1. Proportional Arrow 2. Fixed Head Arrow ================================================================= 1. Proportional Arrow Assume you want an arrow from (x1,y1) to (x2,y2) with the "tip" at (x2,y2). You can find the 90% point from (x1,y1) to (x2,y2): (xBase, yBase) = [x1 + 0.9*(x2-x1), y1 + 0.9*(y2-y1)] The remaining 10% vector from this 90% point to (x2,y2) is (xDelta, yDelta) = (x2-xBase, y2-yBase). So, go out the 10% distance in both directions from (xBase,yBase) and form a triangle from the three points: (x2,y2) (xBase+yDelta, yBase-xDelta) (xBase-yDelta, yBase+xDelta) This technique should work with any points (x1,y2) and (x2,y2). Here's the code. Just put a 512-by-512 Timage (ImageShow) on a form and add the following to a button click: ================================================================== procedure TForm1.ButtonProportionalArrowClick(Sender: TObject); VAR Bitmap: TBitmap; x1,x2 : INTEGER; y1,y2 : INTEGER; xbase : INTEGER; xDelta: INTEGER; ybase : INTEGER; yDelta: INTEGER; begin Bitmap := TBitmap.Create; TRY Bitmap.Width := 512; Bitmap.Height := 512; Bitmap.PixelFormat := pf24bit; Bitmap.Canvas.Pen.Color := clRed; x1 := 100; // first point y1 := 100; x2 := 350; // second point with arrow y2 := 400; Bitmap.Canvas.MoveTo(x1,y1); Bitmap.Canvas.LineTo(x2,y2); xBase := x1 + MulDiv(x2 - x1, 9, 10); // 90% from (x1,y1) to (x2,y2) yBase := y1 + MulDiv(y2 - y1, 9, 10); xDelta := x2 - xBase; yDelta := y2 - yBase; // base of arrow tip is perpendicular to original vector. A normal vector // to the original line can be found by swapping the delta X and delta Y // components and negating one of them. // Draw the arrow tip Bitmap.Canvas.Polygon([Point(x2,y2), Point(xBase+yDelta, yBase-xDelta), Point(xBase-yDelta, yBase+xDelta) ]); ImageShow.Picture.Graphic := Bitmap; FINALLY Bitmap.Free END end; ================================================================= 2. Fixed Head Arrow ================================================================= procedure TForm1.ButtonFixeHeadArrowClick(Sender: TObject); VAR Bitmap : TBitmap; HeadLength : INTEGER; x1,x2 : INTEGER; y1,y2 : INTEGER; xbase : INTEGER; xLineDelta : INTEGER; xLineUnitDelta : Double; xNormalDelta : INTEGER; xNormalUnitDelta: Double; ybase : INTEGER; yLineDelta : INTEGER; yLineUnitDelta : Double; yNormalDelta : INTEGER; yNormalUnitDelta: Double; begin Bitmap := TBitmap.Create; TRY Bitmap.Width := 512; Bitmap.Height := 512; Bitmap.PixelFormat := pf24bit; Bitmap.Canvas.Pen.Color := clRed; x1 := 100; // first point y1 := 200; x2 := 350; // second point with arrow y2 := 400; Bitmap.Canvas.MoveTo(x1,y1); Bitmap.Canvas.LineTo(x2,y2); xLineDelta := x2 - x1; yLineDelta := y2 - y1; xLineUnitDelta := xLineDelta / SQRT( SQR(xLineDelta) + SQR(yLineDelta) ); yLineUnitDelta := yLineDelta / SQRt( SQR(xLineDelta) + SQR(yLineDelta) ); // (xBase,yBase) is where arrow line is perpendicular to base of triangle. HeadLength := 15; // pixels xBase := x2 - ROUND(HeadLength * xLineUnitDelta); yBase := y2 - ROUND(HeadLength * yLineUnitDelta); xNormalDelta := yLineDelta; yNormalDelta := -xLineDelta; xNormalUnitDelta := xNormalDelta / SQRT( SQR(xNormalDelta) + SQR(yNormalDelta) ); yNormalUnitDelta := yNormalDelta / SQRt( SQR(xNormalDelta) + SQR(yNormalDelta) ); // Draw the arrow tip Bitmap.Canvas.Polygon([Point(x2,y2), Point(xBase + ROUND(HeadLength*xNormalUnitDelta), yBase + ROUND(HeadLength*yNormalUnitDelta)), Point(xBase - ROUND(HeadLength*xNormalUnitDelta), yBase - ROUND(HeadLength*yNormalUnitDelta)) ]); ImageShow.Picture.Graphic := Bitmap; FINALLY Bitmap.Free END end; end.