223 lines
5.3 KiB
ObjectPascal

unit Unit1;
{$mode objfpc}{$H+}
interface
uses
Classes, SysUtils, Forms, Controls, Graphics, Dialogs, StdCtrls, Spin,
ExtCtrls, BGRABitmapTypes, BGRABitmap, BGRACanvas2D;
type
{ TForm1 }
TForm1 = class(TForm)
FloatSpinEdit1: TFloatSpinEdit;
FloatSpinEdit2: TFloatSpinEdit;
Label1: TLabel;
Label2: TLabel;
Label3: TLabel;
Label4: TLabel;
Panel1: TPanel;
procedure FloatSpinEdit1Change(Sender: TObject);
procedure FloatSpinEdit2Change(Sender: TObject);
procedure FormCreate(Sender: TObject);
procedure FormDestroy(Sender: TObject);
procedure FormMouseDown(Sender: TObject; Button: TMouseButton;
Shift: TShiftState; X, Y: Integer);
procedure FormMouseMove(Sender: TObject; Shift: TShiftState; X, Y: Integer);
procedure FormMouseUp(Sender: TObject; Button: TMouseButton;
Shift: TShiftState; X, Y: Integer);
procedure FormPaint(Sender: TObject);
private
Center: TPoint;
B, B2: TRationalQuadraticBezierCurve;
CurPoint: integer;
PrevMouse: TPoint;
Img : TBGRABitmap;
procedure UpdateLength;
public
end;
var
Form1: TForm1;
implementation
{$R *.lfm}
{ TForm1 }
procedure TForm1.FormCreate(Sender: TObject);
begin
B:=BezierCurve(PointF(-150,80), PointF(0,0), PointF(150,80), FloatSpinEdit1.Value);
B2:=BezierCurve(PointF(-150,80), PointF(0,0), PointF(150,80), -FloatSpinEdit1.Value);
UpdateLength;
Img := TBGRABitmap.Create;
CurPoint := -1;
end;
procedure TForm1.FloatSpinEdit1Change(Sender: TObject);
begin
B.weight := FloatSpinEdit1.Value;
B2.weight := -FloatSpinEdit1.Value;
UpdateLength;
invalidate;
end;
procedure TForm1.FloatSpinEdit2Change(Sender: TObject);
begin
Invalidate;
end;
procedure TForm1.FormDestroy(Sender: TObject);
begin
Img.free
end;
procedure TForm1.FormMouseDown(Sender: TObject; Button: TMouseButton;
Shift: TShiftState; X, Y: Integer);
var
MinDist: single;
function TryPoint(APoint: TPointF): boolean;
var
dist: single;
begin
dist:= sqr(APoint.x-x)+sqr(APoint.y-y);
if dist < MinDist then
begin
MinDist := dist;
exit(true)
end
else exit(false);
end;
begin
dec(y, Center.Y);
dec(x, Center.X);
if Button = mbLeft then
begin
CurPoint:= -1;
MinDist := sqr(15);
if TryPoint(B.p1) then CurPoint := 0;
if TryPoint(B.c) then CurPoint := 1;
if TryPoint(B.p2) then CurPoint := 2;
PrevMouse := Point(X,Y);
end;
end;
procedure TForm1.FormMouseMove(Sender: TObject; Shift: TShiftState; X,
Y: Integer);
var
d: TPointF;
begin
dec(y, Center.Y);
dec(x, Center.X);
if CurPoint <> -1 then
begin
d := PointF(X-PrevMouse.x,Y-PrevMouse.y);
case CurPoint of
0: begin B.p1.Offset(d); B2.p1.Offset(d); end;
1: begin B.c.Offset(d); B2.c.Offset(d); end;
2: begin B.p2.Offset(d); B2.p2.Offset(d); end;
end;
PrevMouse := Point(X,Y);
UpdateLength;
Invalidate;
end;
end;
procedure TForm1.FormMouseUp(Sender: TObject; Button: TMouseButton;
Shift: TShiftState; X, Y: Integer);
begin
if Button = mbLeft then CurPoint := -1;
end;
procedure TForm1.FormPaint(Sender: TObject);
var
f: TBGRACanvas2D;
R, boundsF: TrectF;
Aleft, Aright : TRationalQuadraticBezierCurve;
precision: single;
begin
precision := FloatSpinEdit2.Value;
Img.SetSize(ClientWidth,ClientHeight-Panel1.Height);
Img.Fill(clWhite);
f := Img.Canvas2D;
Center := Point(ClientWidth div 2, (ClientHeight - Panel1.Height) div 2 + Panel1.Height);
boundsF := RectF(0,0, Img.Width,Img.Height);
boundsF.Offset(-Center.X, -Center.Y + Panel1.Height);
f.resetTransform;
f.translate(Center.X,Center.Y - Panel1.Height);
f.lineJoinLCL:= pjsBevel;
// arc d'ellipse en rouge, poids 0.4 (petit arc)
f.beginPath;
f.moveto(B.p1);
f.lineTo(B.c);
f.lineTo(B.p2);
f.moveto(B2.p1);
f.lineTo(B2.c);
f.lineTo(B2.p2);
f.moveto(B.p1.x+5,B.p1.y);
f.circle(B.p1.x,B.p1.y,5);
f.moveto(B.c.x+5,B.c.y);
f.circle(B.c.x,B.c.y,5);
f.moveto(B.p2.x+5,B.p2.y);
f.circle(B.p2.x,B.p2.y,5);
f.strokeStyle(clblack);
f.linewidth := 1;
f.stroke();
f.beginPath;
f.lineWidth := 4;
f.strokeStyle(BGRA(255,0,96,255));
f.moveTo(B.p1);
f.polylineTo(B.ToPoints(boundsF,precision));
f.stroke();
// arc d'ellipse en vert, poids -0.4 (grand arc, complétant le précédent)
f.beginPath;
f.strokeStyle(BGRA(96,160,0,255));
f.polylineTo(B2.ToPoints(boundsF,precision));
f.stroke();
if not B2.IsInfinite then
begin
// arc en bleu, c'est la deuxième moitié de l'arc en vert
B2.Split(Aleft, Aright);
f.strokeStyle(BGRA(0,96,255,255));
f.beginPath;
f.moveTo(Aright.p1);
f.polylineTo(Aright.ToPoints(boundsF,precision*2));
f.stroke;
// bounding box de l'arc en vert
R:=B2.GetBounds();
f.beginPath;
f.rect(round(R.Left)-1, round(R.Top)-1, round(R.Width)+2, round(R.Height)+2);
f.strokeStyle(BGRABlack);
f.lineWidth := 1;
f.stroke();
end;
Img.draw(Canvas,0,Panel1.Height)
end;
procedure TForm1.UpdateLength;
var
len: Single;
begin
len := B2.ComputeLength;
if len = EmptySingle then
Label1.caption:='Green arc length = infinity'
else
Label1.caption:='Green arc length = '+FloatToStrF(len, ffFixed, 7,1);
len := B.ComputeLength;
if len = EmptySingle then
Label2.caption:='Red arc length = infinity'
else
Label2.caption:='Red arc length = '+FloatToStrF(len, ffFixed, 7,1);
end;
end.