GDI+绘制多边形路径出现内部多余连线问题咨询
GDI+绘制八角星路径异常问题
我尝试绘制如下多边形:
实现代码如下:
procedure TForm1.Paint; var g: TGPGraphics; pen: TGPPen; i: Integer; p: TGPGraphicsPath; m: TGPMatrix; br: TGpSolidBrush; begin inherited; g := TGPGraphics.Create(Canvas.Handle); pen := TGPPen.Create(MakeColor(141, 63, 0), 3); p := TGPGraphicsPath.Create(); m := TGPMatrix.Create; br := TGpSolidBrush.Create(MakeColor(244, 146, 32)); try g.SetSmoothingMode(SmoothingModeHighQuality); g.Clear(MakeColor(255, 255, 255)); g.TranslateTransform(ClientWidth div 2, ClientHeight div 2); m.Rotate(45); p.StartFigure; for i := 0 to 7 do begin p.AddLine(-14, -33, 0, -55); // 到右上 p.AddLine(0, -55, 14, -33); // 到右下 p.Transform(m); // 旋转45度 end; p.CloseFigure; g.DrawPath(pen, p); finally br.Free; m.Free; p.Free; pen.Free; g.Free; end; end;
但运行后GDI+会自动闭合每一条星射线的路径,生成了存在多余内部连线的错误效果,实际绘制结果如下:
现有三个咨询问题:
- 上述代码存在什么问题?
- 备注:我必须使用
AddLine方法(每次调用传入两组坐标点)绘制射线的两条边,GDI+中是否存在类似LineTo的方法,仅需传入一组X、Y坐标,自动以上一个路径点作为线段起点? - 是否存在无需执行绘制操作,即可直接对
TGPPointF围绕指定点做旋转变换的方法?
问题解答
1. 代码核心问题
两处逻辑错误直接导致绘制结果不符合预期:
- 路径的
Transform方法会对路径内所有已存储的点全部生效,不是仅对后续新增的点做变换。第一次循环后路径存了3个点(2条线段),旋转45度;第二次循环新增线段后再调用旋转,第一次存的3个点会累计旋转90度,循环执行8次后,最早存入的点累计旋转360度,所有点位置完全错位,根本不是均匀分布的八角结构。 - 路径绘制时没有做子图分割:每画完一个星角的两条边,既没有回到中心起点,也没有调用
StartFigure开启新的子路径,GDI+会自动把上一段的终点和下一段的起点连线,最后调用CloseFigure还会强制连接整个路径的首尾,自然会出现大量多余的内部连线。
2. 类LineTo效果的实现方式
GDI+没有提供只接收单个坐标点的LineTo同名方法,但完全可以基于AddLine实现同等效果,不需要使用其他接口:TGPGraphicsPath提供了GetLastPoint方法,可以直接获取路径当前的最后一个端点坐标,封装一个简单的辅助逻辑即可:每次要追加线段到新坐标点时,先取出路径最后一个点作为线段起点,和新的目标点一起传入AddLine,效果和传统GDI的LineTo完全一致,也符合必须使用AddLine的要求。
不要依赖GDI+路径的隐式自动连线规则,显式传入线段起止点的写法更稳定,不会因为子图闭合、路径变换等操作出现意外连线。
3. 独立点旋转变换方法
不需要绑定任何绘制操作,直接用矩阵类的点变换能力即可实现:TGPMatrix自带TransformPoints方法,先构造好符合旋转规则的矩阵,把要变换的TGPPointF指针传入,就能直接把点的坐标更新为变换后的值,全程不需要创建绘图路径、不需要调用任何绘制接口。
如果要围绕任意指定点旋转,构造矩阵时按「平移坐标系到旋转中心→旋转指定角度→平移回原坐标系」的顺序设置参数即可,参考实现:
// 围绕指定中心旋转单个TGPPointF点 function RotatePointF(const P: TGPPointF; const Center: TGPPointF; AngleDeg: Single): TGPPointF; var mat: TGPMatrix; begin mat := TGPMatrix.Create; try mat.Translate(-Center.X, -Center.Y); mat.Rotate(AngleDeg); mat.Translate(Center.X, Center.Y); Result := P; mat.TransformPoints(@Result, 1); finally mat.Free; end; end;
可以提前用这个方法算好8个星角所有端点的坐标,再按顺序一次性加入路径,就能完全避免之前反复变换路径导致的点错位问题。
内容的提问来源于stack exchange,提问作者Paul
相关产品推荐
相关产品推荐

