[英]how can I create a custom draw line in delphi
我想在我的 delphi 应用程序中的画布上画一条线,但需要它是一条 ****** 如何使用 * char 而不是破折号或点创建自定义线。
您可以使用Skia4Delphi库来获得该问题的通用解决方案。 下面是使用 TSkPaintBox 控件和 TSkPaintBox.OnDraw 事件的示例:
uses
System.Math.Vectors, FMX.TextLayout, Skia, Skia.FMX;
procedure TForm1.SkPaintBox1Draw(ASender: TObject; const ACanvas: ISkCanvas;
const ADest: TRectF; const AOpacity: Single);
function GetTextPath(const AText: string): ISkPath;
var
LTextLayout: TTextLayout;
LPathData: TPathData;
begin
LTextLayout := TTextLayoutManager.DefaultTextLayout.Create;
try
LTextLayout.BeginUpdate;
try
LTextLayout.Font.Size := 30;
LTextLayout.Font.Style := [TFontStyle.fsBold];
LTextLayout.Text := AText;
finally
LTextLayout.EndUpdate;
end;
LPathData := TPathData.Create;
try
LTextLayout.ConvertToPath(LPathData);
Result := LPathData.ToSkPath;
finally
LPathData.Free;
end;
finally
LTextLayout.Free;
end;
end;
var
LPaint: ISkPaint;
LTextPath: ISkPath;
LPathBuilder: ISkPathBuilder;
begin
LTextPath := GetTextPath('*');
LPaint := TSkPaint.Create(TSkPaintStyle.Stroke);
LPaint.AntiAlias := True;
LPaint.Color := TAlphaColors.Black;
LPaint.PathEffect := TSkPathEffect.Make1DPath(LTextPath, LTextPath.Bounds.Width + 2, 0, TSkPathEffect1DStyle.Rotate);
LPathBuilder := TSkPathBuilder.Create;
LPathBuilder.MoveTo(PointF(50, 100));
LPathBuilder.LineTo(PointF(400, 290));
ACanvas.DrawPath(LPathBuilder.Detach, LPaint);
end;
结果:
此解决方案不仅限于星号和线条。 使用“@”和一个圆圈查看结果:
一条线有一个方程,其形式为: Y = A * X + B
A
是斜率, B
是原点处的偏移量。
如果要从点(X1, Y1)
到点(X2, Y2)
画一条线,首先要确定方程的A
和B
常数:
A = (Y2 - Y1) / (X2 - X1)
有了 A 后,将 B 计算为:
B = Y1 - A * X1
现在您有了A
和B
,您可以使用它来计算X1
和X2
之间的中间点。 一个简单的循环就可以了。 将X
增加您希望*
分隔的值。
注意:如果Y2 - Y1
大于X2 - X1
,则必须迭代Y
而不是X
。
作为练习,我让你编写代码......
我将使用线条的参数表示,参数是到目前为止绘制的线条长度。 这样就可以画出垂直线,并且可以实现星星的绘制等距,与线的斜率无关。
更准确地说:要从 A 点到 B 点画一条线,计算线 L 的长度,然后计算线方向的单位向量 Dir。 在线上点 P 的公式为 P = A + t*Dir,其中 t 从 0 到 L。(这是伪代码,可作为矢量符号读取。)
这是一个执行此操作的简单例程。
procedure DrawStarAt(P: TPointF; Radius: Single; aCanvas: TCanvas);
begin
var
r := RectF(P.X - Radius, P.Y - Radius, P.X + Radius, P.Y + Radius);
aCanvas.FillText(r, '*', false, 1, [], TTextAlign.Center, TTextAlign.Center);
end;
procedure DrawStarLine(A, B: TPointF; aCanvas: TCanvas);
var
// line length
L,
// line parameter
t,
// step for t
dt,
// Radius of the text rectangle
Radius: Single;
// Point to be drawn
P,
// unit vector for line direction
Direction: TPointF;
n: integer;
begin
aCanvas.BeginScene;
aCanvas.Fill.Color := TAlphaColorRec.Black;
Radius := aCanvas.TextWidth('*');
L := sqrt(sqr(B.X - A.X) + sqr(B.Y - A.Y));
n:=trunc(L/Radius);
//adjust dt so the last star is drawn exactly at B
dt:=L/n;
if L = 0 then
begin
DrawStarAt(A, Radius, aCanvas);
aCanvas.EndScene;
exit;
end;
Direction := PointF((B.X - A.X) / L, (B.Y - A.Y) / L);
t := 0;
while t < L do
begin
P := PointF(A.X + t * Direction.X, A.Y + t * Direction.Y);
DrawStarAt(P, Radius, aCanvas);
t := t + dt;
end;
DrawStarAt(B, Radius, aCanvas);
aCanvas.EndScene;
end;
计算机科学家 Jack Bresenham 设计了一种算法,可以在整数网格上快速绘制直线。 该算法仅使用整数变量,不需要除法或乘法。
您可以直接在 Bresenham 代码中编写星号,但使用回调过程要简洁得多:您可以使用回调函数作为额外参数调用 Bresenham 过程。 每次算法计算出线上的一个点时,它都会调用回调过程,传递 X 和 Y 坐标。
最大的优点是您可以编写一个通用的 Bresenham 过程,并且只在回调中编写不同的操作,具体取决于您是要绘制点还是星号。
它是这样的。
您定义一个回调过程类型:
type
TCallbackProc = procedure(X, Y: Integer) of Object;
写出回调过程的动作。 这里我画了一个像素。 你改变这条线来画一个星号:
procedure TForm1.DrawPixel(X, Y: Integer);
begin
Image1.Canvas.Pixels[X, Y] := clBlack;
end;
然后是 Bresenham 程序本身。 回想一下,该过程找到了线上的所有点,但不知道如何处理它们,因此它将它们传递给回调过程:
procedure TForm1.Bresenham(X1, Y1, X2, Y2: Integer; CallbackProc: TCallBackProc);
var
Dx, Dy, Sx, Sy, Error, E2: Integer;
Done: Boolean;
begin
Dx := Abs(X2 - X1);
if X1 < X2 then
Sx := 1
else
Sx := -1;
Dy := -Abs(Y2 - Y1);
if Y1 < Y2 then
Sy := 1
else
Sy := -1;
Error := Dx + Dy;
while True do
begin
if Assigned(CallbackProc) then
CallbackProc(X1, Y1);
if (X1 = X2) and (Y1 = Y2) then
Exit;
E2 := 2 * Error;
if E2 >= Dy then
begin
if X1 = X2 then
Exit;
Error := Error + Dy;
X1 := X1 + Sx;
end;
if E2 <= Dx then
begin
if Y1 = Y2 then
Exit;
Error := Error + Dx;
Y1 := Y1 + Sy;
end;
end;
end;
用法:
procedure TForm1.Button1Click(Sender: TObject);
begin
Bresenham(100, 50, 250, 350, DrawPixel);
Bresenham(100, 50, 250, 350, DrawDaisies);
Bresenham(100, 50, 250, 350, DrawSquirrels);
end;
声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.