223 lines
5.3 KiB
ObjectPascal
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.
|
|
|