Ver Fonte

add vector polyline/curve, miter limit 2, fix arrow end choice

Johann há 6 anos atrás
pai
commit
f162caf88e

+ 0 - 28
lazpaint/lazpaintmainform.lfm

@@ -688,20 +688,6 @@ object FMain: TFMain
       Width = 45
       Width = 45
       DropDownCount = 12
       DropDownCount = 12
       ItemHeight = 17
       ItemHeight = 17
-      Items.Strings = (
-        'None'
-        'Tail'
-        'Tip'
-        'Normal'
-        'Cut'
-        'Flipped'
-        'FlippedCut'
-        'Triangle'
-        'TriangleBack1'
-        'TriangleBack2'
-        'HollowTriangle'
-        'HollowTriangleBack1'
-      )
       Style = csOwnerDrawFixed
       Style = csOwnerDrawFixed
       TabOrder = 1
       TabOrder = 1
     end
     end
@@ -712,20 +698,6 @@ object FMain: TFMain
       Width = 45
       Width = 45
       DropDownCount = 12
       DropDownCount = 12
       ItemHeight = 17
       ItemHeight = 17
-      Items.Strings = (
-        'None'
-        'Tail'
-        'Tip'
-        'Normal'
-        'Cut'
-        'Flipped'
-        'FlippedCut'
-        'Triangle'
-        'TriangleBack1'
-        'TriangleBack2'
-        'HollowTriangle'
-        'HollowTriangleBack1'
-      )
       Style = csOwnerDrawFixed
       Style = csOwnerDrawFixed
       TabOrder = 2
       TabOrder = 2
     end
     end

+ 1 - 1
lazpaint/lazpaintmainform.pas

@@ -820,7 +820,7 @@ implementation
 uses LCLIntf, BGRAUTF8, ugraph, math, umac, uclipboard, ucursors,
 uses LCLIntf, BGRAUTF8, ugraph, math, umac, uclipboard, ucursors,
    ufilters, ULoadImage, ULoading, UFileExtensions, UBrushType,
    ufilters, ULoadImage, ULoading, UFileExtensions, UBrushType,
    ugeometricbrush, UPreviewDialog, UQuestion, BGRALayerOriginal,
    ugeometricbrush, UPreviewDialog, UQuestion, BGRALayerOriginal,
-   BGRATransform;
+   BGRATransform, LCVectorPolyShapes;
 
 
 const PenWidthFactor = 10;
 const PenWidthFactor = 10;
 
 

+ 46 - 17
lazpaint/maintoolbar.inc

@@ -282,6 +282,7 @@ end;
 procedure TFMain.InitToolbarElements;
 procedure TFMain.InitToolbarElements;
 var
 var
   i: Integer;
   i: Integer;
+  ak: TArrowKind;
 begin
 begin
   Panel_Embedded.Visible := LazPaintInstance.Embedded;
   Panel_Embedded.Visible := LazPaintInstance.Embedded;
   Panel_File.Visible := not LazPaintInstance.Embedded;
   Panel_File.Visible := not LazPaintInstance.Embedded;
@@ -292,6 +293,13 @@ begin
   Shape_BackColor.Brush.Color := BGRAToColor(ToolManager.ToolBackColor);
   Shape_BackColor.Brush.Color := BGRAToColor(ToolManager.ToolBackColor);
   SpinEdit_BackOpacity.Value := ToolManager.ToolBackColor.alpha;
   SpinEdit_BackOpacity.Value := ToolManager.ToolBackColor.alpha;
   SpinEdit_PenWidth.Value := Round(ToolManager.ToolPenWidth*PenWidthFactor);
   SpinEdit_PenWidth.Value := Round(ToolManager.ToolPenWidth*PenWidthFactor);
+  for ak := low(TArrowKind) to high(TArrowKind) do
+  begin
+    ComboBox_ArrowStart.Items.Add(ArrowKindToStr[ak]);
+    ComboBox_ArrowEnd.Items.Add(ArrowKindToStr[ak]);
+  end;
+  ComboBox_ArrowStart.ItemIndex := 0;
+  ComboBox_ArrowEnd.ItemIndex := 0;
   SpinEdit_ArrowSizeX.Value := round(ToolManager.ToolArrowSize.x*PenWidthFactor);
   SpinEdit_ArrowSizeX.Value := round(ToolManager.ToolArrowSize.x*PenWidthFactor);
   SpinEdit_ArrowSizeY.Value := round(ToolManager.ToolArrowSize.y*PenWidthFactor);
   SpinEdit_ArrowSizeY.Value := round(ToolManager.ToolArrowSize.y*PenWidthFactor);
   Tool_DrawShapeBorder.Down := ToolManager.ToolOptionDrawShape;
   Tool_DrawShapeBorder.Down := ToolManager.ToolOptionDrawShape;
@@ -305,10 +313,7 @@ begin
   Tool_GridMoveWithoutDeformation.Down := ToolManager.ToolDeformationGridMoveWithoutDeformation;
   Tool_GridMoveWithoutDeformation.Down := ToolManager.ToolDeformationGridMoveWithoutDeformation;
   SpinEdit_GridNbX.Value := ToolManager.ToolDeformationGridNbX-1;
   SpinEdit_GridNbX.Value := ToolManager.ToolDeformationGridNbX-1;
   SpinEdit_GridNbY.Value := ToolManager.ToolDeformationGridNbY-1;
   SpinEdit_GridNbY.Value := ToolManager.ToolDeformationGridNbY-1;
-  if ToolManager.ToolSplineEasyBezier then
-    Combo_SplineStyle.ItemIndex := Combo_SplineStyle.Items.IndexOf('Easy Bézier')
-  else
-    Combo_SplineStyle.ItemIndex:= ord(ToolManager.ToolSplineStyle);
+  Combo_SplineStyle.ItemIndex:= ord(ToolManager.ToolSplineStyle);
   UpdateCurveMode;
   UpdateCurveMode;
   Tool_TextOutline.Down := ToolManager.ToolTextOutline;
   Tool_TextOutline.Down := ToolManager.ToolTextOutline;
   SpinEdit_TextOutlineWidth.Value := round(ToolManager.ToolTextOutlineWidth*PenWidthFactor);
   SpinEdit_TextOutlineWidth.Value := round(ToolManager.ToolTextOutlineWidth*PenWidthFactor);
@@ -821,20 +826,30 @@ end;
 
 
 procedure TFMain.ComboBox_ArrowStartChange(Sender: TObject);
 procedure TFMain.ComboBox_ArrowStartChange(Sender: TObject);
 begin
 begin
-  if initialized then UpdateEditPicture;
+  if initialized then
+  begin
+    ToolManager.ToolArrowStart:= ComboBox_ArrowStart.Text;
+    UpdateEditPicture;
+  end;
 end;
 end;
 
 
 procedure TFMain.ComboBox_ArrowStartDrawItem(Control: TWinControl;
 procedure TFMain.ComboBox_ArrowStartDrawItem(Control: TWinControl;
   Index: Integer; ARect: TRect; State: TOwnerDrawState);
   Index: Integer; ARect: TRect; State: TOwnerDrawState);
+var
+  kind: String;
 begin
 begin
   if Index = -1 then exit;
   if Index = -1 then exit;
-  ToolManager.ToolArrowStart := ComboBox_ArrowStart.Items[Index];
-  DrawArrow(ComboBox_ArrowStart.Canvas,ARect,True,ToolManager.ToolArrowStart,ToolManager.ToolLineCap,State);
+  kind := ComboBox_ArrowStart.Items[Index];
+  DrawArrow(ComboBox_ArrowStart.Canvas,ARect,True,kind,ToolManager.ToolLineCap,State);
 end;
 end;
 
 
 procedure TFMain.ComboBox_ArrowEndChange(Sender: TObject);
 procedure TFMain.ComboBox_ArrowEndChange(Sender: TObject);
 begin
 begin
-  if initialized then UpdateEditPicture;
+  if initialized then
+  begin
+    ToolManager.ToolArrowEnd:= ComboBox_ArrowEnd.Text;
+    UpdateEditPicture;
+  end;
 end;
 end;
 
 
 procedure TFMain.BrushLoadFromFileExecute(Sender: TObject);
 procedure TFMain.BrushLoadFromFileExecute(Sender: TObject);
@@ -860,10 +875,12 @@ end;
 
 
 procedure TFMain.ComboBox_ArrowEndDrawItem(Control: TWinControl;
 procedure TFMain.ComboBox_ArrowEndDrawItem(Control: TWinControl;
   Index: Integer; ARect: TRect; State: TOwnerDrawState);
   Index: Integer; ARect: TRect; State: TOwnerDrawState);
+var
+  kind: String;
 begin
 begin
   if Index = -1 then exit;
   if Index = -1 then exit;
-  ToolManager.ToolArrowEnd := ComboBox_ArrowEnd.Items[Index];
-  DrawArrow(ComboBox_ArrowEnd.Canvas,ARect,False,ToolManager.ToolArrowEnd,ToolManager.ToolLineCap,State);
+  kind := ComboBox_ArrowEnd.Items[Index];
+  DrawArrow(ComboBox_ArrowEnd.Canvas,ARect,False,kind,ToolManager.ToolLineCap,State);
 end;
 end;
 
 
 procedure TFMain.SpinEdit_ArrowSizeChange(Sender: TObject);
 procedure TFMain.SpinEdit_ArrowSizeChange(Sender: TObject);
@@ -974,9 +991,9 @@ end;
 procedure TFMain.SetCurveMode(AMode: TToolSplineMode);
 procedure TFMain.SetCurveMode(AMode: TToolSplineMode);
 begin
 begin
   if (ToolManager.CurrentTool <> nil) and
   if (ToolManager.CurrentTool <> nil) and
-     (ToolManager.CurrentTool is TToolGenericSpline) then
+     (ToolManager.CurrentTool is TToolSpline) then
   begin
   begin
-    (ToolManager.CurrentTool as TToolGenericSpline).CurrentMode := AMode;
+    (ToolManager.CurrentTool as TToolSpline).CurrentMode := AMode;
     UpdateCurveMode;
     UpdateCurveMode;
   end;
   end;
 end;
 end;
@@ -984,11 +1001,11 @@ end;
 procedure TFMain.UpdateCurveMode;
 procedure TFMain.UpdateCurveMode;
 var
 var
   cm: TToolSplineMode;
   cm: TToolSplineMode;
-  splineTool: TToolGenericSpline;
+  splineTool: TToolSpline;
 begin
 begin
-  if (ToolManager.CurrentTool <> nil) and (ToolManager.CurrentTool is TToolGenericSpline) then
+  if (ToolManager.CurrentTool <> nil) and (ToolManager.CurrentTool is TToolSpline) then
   begin
   begin
-    splineTool := ToolManager.CurrentTool as TToolGenericSpline;
+    splineTool := ToolManager.CurrentTool as TToolSpline;
     Tool_CurveMovePoint.Enabled := not splineTool.IsHandDrawing and not splineTool.IsIdle;
     Tool_CurveMovePoint.Enabled := not splineTool.IsHandDrawing and not splineTool.IsIdle;
     cm := splineTool.CurrentMode;
     cm := splineTool.CurrentMode;
     if Tool_CurveMovePoint.Down <> (cm = tsmMovePoint) then
     if Tool_CurveMovePoint.Down <> (cm = tsmMovePoint) then
@@ -1008,7 +1025,6 @@ begin
   if initialized then
   if initialized then
   begin
   begin
     v := Combo_SplineStyle.Text;
     v := Combo_SplineStyle.Text;
-    ToolManager.ToolSplineEasyBezier := false;
     if v = 'Inside' then ToolManager.ToolSplineStyle := ssInside else
     if v = 'Inside' then ToolManager.ToolSplineStyle := ssInside else
     if v = 'Inside + ends' then ToolManager.ToolSplineStyle := ssInsideWithEnds else
     if v = 'Inside + ends' then ToolManager.ToolSplineStyle := ssInsideWithEnds else
     if v = 'Crossing' then ToolManager.ToolSplineStyle := ssCrossing else
     if v = 'Crossing' then ToolManager.ToolSplineStyle := ssCrossing else
@@ -1016,7 +1032,20 @@ begin
     if v = 'Outside' then ToolManager.ToolSplineStyle := ssOutside else
     if v = 'Outside' then ToolManager.ToolSplineStyle := ssOutside else
     if v = 'Round outside' then ToolManager.ToolSplineStyle:= ssRoundOutside else
     if v = 'Round outside' then ToolManager.ToolSplineStyle:= ssRoundOutside else
     if v = 'Vertex to side' then ToolManager.ToolSplineStyle:= ssVertexToSide else
     if v = 'Vertex to side' then ToolManager.ToolSplineStyle:= ssVertexToSide else
-    if v = 'Easy Bézier' then ToolManager.ToolSplineEasyBezier := true;
+    if v = 'Easy Bézier' then ToolManager.ToolSplineStyle := ssEasyBezier;
+    if ToolManager.ToolSplineStyle<>ssEasyBezier then
+    begin
+      Tool_CurveMovePoint.Down := true;
+      SetCurveMode(tsmMovePoint);
+      Tool_CurveModeAuto.Enabled := false;
+      Tool_CurveModeAngle.Enabled := false;
+      Tool_CurveModeCurve.Enabled := false;
+    end else
+    begin
+      Tool_CurveModeAuto.Enabled := true;
+      Tool_CurveModeAngle.Enabled := true;
+      Tool_CurveModeCurve.Enabled := true;
+    end;
     UpdateEditPicture(True);
     UpdateEditPicture(True);
     UpdateCurveMode;
     UpdateCurveMode;
   end;
   end;

+ 9 - 29
lazpaint/ugraph.pas

@@ -51,15 +51,14 @@ function ClearTypeFilter(source: TBGRACustomBitmap): TBGRACustomBitmap;
 function ClearTypeInverseFilter(source: TBGRACustomBitmap): TBGRACustomBitmap;
 function ClearTypeInverseFilter(source: TBGRACustomBitmap): TBGRACustomBitmap;
 
 
 function DoResample(source :TBGRABitmap; newWidth, newHeight: integer; StretchMode: TResampleMode): TBGRABitmap;
 function DoResample(source :TBGRABitmap; newWidth, newHeight: integer; StretchMode: TResampleMode): TBGRABitmap;
-procedure DrawArrow(ACanvas: TCanvas; ARect: TRect; AStart: boolean; AKind: string; ALineCap: TPenEndCap; State: TOwnerDrawState);
-procedure ApplyArrowStyle(AStart: boolean; AKind: string; ABmp: TBGRABitmap; ASize: TPointF);
+procedure DrawArrow(ACanvas: TCanvas; ARect: TRect; AStart: boolean; AKindStr: string; ALineCap: TPenEndCap; State: TOwnerDrawState);
 procedure BCAssignSystemStyle(AButton: TBCButton);
 procedure BCAssignSystemStyle(AButton: TBCButton);
 
 
 implementation
 implementation
 
 
 uses GraphType, math, Types, BGRAUTF8, FileUtil, dialogs, BGRAAnimatedGif,
 uses GraphType, math, Types, BGRAUTF8, FileUtil, dialogs, BGRAAnimatedGif,
   BGRAGradients, BGRATextFX, uresourcestrings, LCScaleDPI, BCTypes,
   BGRAGradients, BGRATextFX, uresourcestrings, LCScaleDPI, BCTypes,
-  BGRAThumbnail;
+  BGRAThumbnail, LCVectorPolyShapes;
 
 
 procedure BCAssignSystemState(AState: TBCButtonState; AFontColor, ATopColor, AMiddleTopColor, AMiddleBottomColor, ABottomColor, ABorderColor: TColor);
 procedure BCAssignSystemState(AState: TBCButtonState; AFontColor, ATopColor, AMiddleTopColor, AMiddleBottomColor, ABottomColor, ABorderColor: TColor);
 begin
 begin
@@ -733,11 +732,13 @@ begin
   result := source.Resample(newWidth,newHeight,StretchMode) as TBGRABitmap;
   result := source.Resample(newWidth,newHeight,StretchMode) as TBGRABitmap;
 end;
 end;
 
 
-procedure DrawArrow(ACanvas: TCanvas; ARect: TRect; AStart: boolean; AKind: string; ALineCap: TPenEndCap; State: TOwnerDrawState);
+procedure DrawArrow(ACanvas: TCanvas; ARect: TRect; AStart: boolean; AKindStr: string; ALineCap: TPenEndCap; State: TOwnerDrawState);
 var bmp : TBGRABitmap;
 var bmp : TBGRABitmap;
   c,c2: TBGRAPixel;
   c,c2: TBGRAPixel;
   x1,x2,xm1,xm2,y,w,temp: single;
   x1,x2,xm1,xm2,y,w,temp: single;
+  kind: TArrowKind;
 begin
 begin
+  kind := StrToArrowKind(AKindStr);
   if odSelected in State then
   if odSelected in State then
   begin
   begin
     c2 := ColorToBGRA(ColorToRGB(clHighlight));
     c2 := ColorToBGRA(ColorToRGB(clHighlight));
@@ -748,7 +749,7 @@ begin
     c := ColorToBGRA(ColorToRGB(clWindowText));
     c := ColorToBGRA(ColorToRGB(clWindowText));
   end;
   end;
   with Size(ARect) do bmp:= TBGRABitmap.Create(cx,cy,c2);
   with Size(ARect) do bmp:= TBGRABitmap.Create(cx,cy,c2);
-  ApplyArrowStyle(AStart,AKind,bmp,PointF(1.5,1.5));
+  ApplyArrowStyle(bmp.Arrow,AStart,kind,PointF(1.5,1.5));
   bmp.LineCap := ALineCap;
   bmp.LineCap := ALineCap;
   w := bmp.Height/5;
   w := bmp.Height/5;
   if w > 0 then
   if w > 0 then
@@ -757,8 +758,8 @@ begin
     x2 := 0;
     x2 := 0;
     xm1 := 0;
     xm1 := 0;
     xm2 := w*2.5;
     xm2 := w*2.5;
-    if (AKind = 'Normal') or (AKind = 'Cut') then x1 -= w*0.7 else
-    if (AKind = 'Flipped') or (AKind = 'FlippedCut') then x1 += w*0.7;
+    if kind in[akNone,akCut] then x1 -= w*0.7 else
+    if kind in[akFlipped,akFlippedCut] then x1 += w*0.7;
     if not AStart then
     if not AStart then
     begin
     begin
       temp := x1;
       temp := x1;
@@ -772,7 +773,7 @@ begin
     x1 -= 0.5;
     x1 -= 0.5;
     x2 += bmp.Width-0.5;
     x2 += bmp.Width-0.5;
     y := (bmp.Height-1)/2;
     y := (bmp.Height-1)/2;
-    if (AKind='Tail') or (AKind='None') or (AKind = 'Tip') then w *= 2;
+    if kind in[akTail,akNone,akTip] then w *= 2;
     bmp.DrawLineAntialias(x1,y,x2,y,c,w);
     bmp.DrawLineAntialias(x1,y,x2,y,c,w);
     if bmp.Width > bmp.Height*2 then
     if bmp.Width > bmp.Height*2 then
       bmp.GradientFill(0,0,bmp.width,bmp.height,c2,BGRAPixelTransparent,gtLinear,PointF(xm1,0),PointF(xm2,0),dmDrawWithTransparency);
       bmp.GradientFill(0,0,bmp.width,bmp.height,c2,BGRAPixelTransparent,gtLinear,PointF(xm1,0),PointF(xm2,0),dmDrawWithTransparency);
@@ -781,27 +782,6 @@ begin
   bmp.Free;
   bmp.Free;
 end;
 end;
 
 
-procedure ApplyArrowStyle(AStart: boolean; AKind: string; ABmp: TBGRABitmap; ASize: TPointF);
-var backOfs: single;
-begin
-  backOfs := 0;
-  if (ASize.x = 0) or (ASize.y = 0) then AKind := 'None';
-  if (length(AKind)>0) and (AKind[length(AKind)] in['1'..'9']) then backOfs := (ord(AKind[length(AKind)])-ord('0'))*0.25;
-  case AKind of
-  'Tail': if AStart then ABmp.ArrowStartAsTail else ABmp.ArrowEndAsTail;
-  'Tip': if AStart then ABmp.ArrowStartAsTriangle else ABmp.ArrowEndAsTriangle;
-  'Normal','Cut','Flipped','FlippedCut': if AStart then ABmp.ArrowStartAsClassic((AKind='Flipped') or (AKind='FlippedCut'),(AKind='Cut') or (AKind='FlippedCut'))
-    else ABmp.ArrowEndAsClassic((AKind='Flipped') or (AKind='FlippedCut'),(AKind='Cut') or (AKind='FlippedCut'));
-  'Triangle','TriangleBack1','TriangleBack2': if AStart then ABmp.ArrowStartAsTriangle(backOfs) else ABmp.ArrowEndAsTriangle(backOfs);
-  'HollowTriangle','HollowTriangleBack1','HollowTriangleBack2': if AStart then ABmp.ArrowStartAsTriangle(backOfs,False,True) else ABmp.ArrowEndAsTriangle(backOfs,False,True);
-  else if AStart then ABmp.ArrowStartAsNone else ABmp.ArrowEndAsNone;
-  end;
-  if (AKind = 'Tip') and not ((ASize.x = 0) or (ASize.y = 0)) then
-    ASize := ASize*(0.5/ASize.y);
-  if AStart then ABmp.ArrowStartSize := ASize
-  else ABmp.ArrowEndSize := ASize;
-end;
-
 function CreateMarbleTexture(tx,ty: integer): TBGRABitmap;
 function CreateMarbleTexture(tx,ty: integer): TBGRABitmap;
 var
 var
   colorOscillation: integer;
   colorOscillation: integer;

+ 3 - 5
lazpaint/utool.pas

@@ -187,7 +187,6 @@ type
     ToolArrowSize: TPointF;
     ToolArrowSize: TPointF;
     ToolJoinStyle: TPenJoinStyle;
     ToolJoinStyle: TPenJoinStyle;
     ToolSplineStyle: TSplineStyle;
     ToolSplineStyle: TSplineStyle;
-    ToolSplineEasyBezier: boolean;
     ToolPenStyle: TPenStyle;
     ToolPenStyle: TPenStyle;
     ToolPerspectiveRepeat,ToolPerspectiveTwoPlanes: boolean;
     ToolPerspectiveRepeat,ToolPerspectiveTwoPlanes: boolean;
     ToolDeformationGridMoveWithoutDeformation: boolean;
     ToolDeformationGridMoveWithoutDeformation: boolean;
@@ -917,13 +916,12 @@ begin
   ToolFloodFillOptionProgressive := true;
   ToolFloodFillOptionProgressive := true;
   ToolLineCap := pecRound;
   ToolLineCap := pecRound;
   ToolJoinStyle := pjsRound;
   ToolJoinStyle := pjsRound;
-  ToolArrowStart := 'None';
-  ToolArrowEnd := 'None';
+  ToolArrowStart := 'none';
+  ToolArrowEnd := 'none';
   ToolArrowSize := PointF(2,2);
   ToolArrowSize := PointF(2,2);
   ToolPenStyle := psSolid;
   ToolPenStyle := psSolid;
   ToolEraserAlpha := 255;
   ToolEraserAlpha := 255;
-  ToolSplineStyle := ssRoundOutside;
-  ToolSplineEasyBezier := true;
+  ToolSplineStyle := ssEasyBezier;
   ToolTextOutline := False;
   ToolTextOutline := False;
   ToolTextShadow := false;
   ToolTextShadow := false;
   ToolTextFont := TFont.Create;
   ToolTextFont := TFont.Create;

+ 22 - 1
lazpaint/utoolbasic.pas

@@ -73,6 +73,9 @@ type
   { TVectorialTool }
   { TVectorialTool }
 
 
   TVectorialTool = class(TGenericTool)
   TVectorialTool = class(TGenericTool)
+  private
+    function GetIsHandDrawing: boolean;
+    function GetIsIdle: boolean;
   protected
   protected
     FShape: TVectorShape;
     FShape: TVectorShape;
     FSwapColor: boolean;
     FSwapColor: boolean;
@@ -116,6 +119,8 @@ type
     function ToolKeyPress(var key: TUTF8Char): TRect; override;
     function ToolKeyPress(var key: TUTF8Char): TRect; override;
     function ToolKeyUp(var key: Word): TRect; override;
     function ToolKeyUp(var key: Word): TRect; override;
     function Render(VirtualScreen: TBGRABitmap; {%H-}VirtualScreenWidth, {%H-}VirtualScreenHeight: integer; BitmapToVirtualScreen: TBitmapToVirtualScreenFunction):TRect; override;
     function Render(VirtualScreen: TBGRABitmap; {%H-}VirtualScreenWidth, {%H-}VirtualScreenHeight: integer; BitmapToVirtualScreen: TBitmapToVirtualScreenFunction):TRect; override;
+    property IsIdle: boolean read GetIsIdle;
+    property IsHandDrawing: boolean read GetIsHandDrawing;
     destructor Destroy; override;
     destructor Destroy; override;
   end;
   end;
 
 
@@ -229,6 +234,7 @@ begin
     if not AlwaysRasterizeShape and Manager.Image.SelectionMaskEmpty then
     if not AlwaysRasterizeShape and Manager.Image.SelectionMaskEmpty then
     begin
     begin
       CancelAction;
       CancelAction;
+      if FShape.Usermode = vsuCreate then FShape.Usermode:= vsuEdit;
       rF := FShape.GetRenderBounds(rect(0,0,Manager.Image.Width,Manager.Image.Height), VectorTransform);
       rF := FShape.GetRenderBounds(rect(0,0,Manager.Image.Width,Manager.Image.Height), VectorTransform);
       if rF.IntersectsWith(rectF(0,0,Manager.Image.Width,Manager.Image.Height)) then
       if rF.IntersectsWith(rectF(0,0,Manager.Image.Width,Manager.Image.Height)) then
       begin
       begin
@@ -276,6 +282,16 @@ begin
   result := OnlyRenderChange;
   result := OnlyRenderChange;
 end;
 end;
 
 
+function TVectorialTool.GetIsHandDrawing: boolean;
+begin
+  result := Assigned(FShape) and (FQuickDefine or (FShape.Usermode = vsuCreate));
+end;
+
+function TVectorialTool.GetIsIdle: boolean;
+begin
+  result := FShape = nil;
+end;
+
 function TVectorialTool.AlwaysRasterizeShape: boolean;
 function TVectorialTool.AlwaysRasterizeShape: boolean;
 begin
 begin
   result := false;
   result := false;
@@ -505,7 +521,12 @@ end;
 
 
 function TVectorialTool.DoToolUpdate(toolDest: TBGRABitmap): TRect;
 function TVectorialTool.DoToolUpdate(toolDest: TBGRABitmap): TRect;
 begin
 begin
-  if Assigned(FShape) then AssignShapeStyle(FLastShapeTransform);
+  if Assigned(FShape) then
+  begin
+    FShape.BeginUpdate;
+    AssignShapeStyle(FLastShapeTransform);
+    FShape.EndUpdate;
+  end;
   result := EmptyRect;
   result := EmptyRect;
 end;
 end;
 
 

+ 146 - 8
lazpaint/utoolpolygon.pas

@@ -5,13 +5,44 @@ unit UToolPolygon;
 interface
 interface
 
 
 uses
 uses
-  Classes, SysUtils, utool, BGRABitmap, BGRABitmapTypes, ULayerAction;
+  Classes, SysUtils, utool, utoolbasic, BGRABitmap, BGRABitmapTypes,
+  LCVectorOriginal, LCLType;
 
 
 const
 const
   EasyBezierMinimumDotProduct = 0.5;
   EasyBezierMinimumDotProduct = 0.5;
 
 
 type
 type
+  TToolSplineMode = (tsmMovePoint, tsmCurveModeAuto, tsmCurveModeAngle, tsmCurveModeSpline);
+
+  { TToolPolygon }
+
+  TToolPolygon = class(TVectorialTool)
+  protected
+    function CreateShape: TVectorShape; override;
+    procedure AssignShapeStyle(AMatrix: TAffineMatrix); override;
+    procedure UpdateUserMode; virtual;
+  end;
+
+  { TToolSpline }
+
+  TToolSpline = class(TToolPolygon)
+  private
+    FCurrentMode: TToolSplineMode;
+    FNextCurveMode: TEasyBezierCurveMode;
+    FCurveModeHintShown: Boolean;
+    function GetCurrentMode: TToolSplineMode;
+    procedure SetCurrentMode(AValue: TToolSplineMode);
+    procedure UpdateUserMode; override;
+  protected
+    function CreateShape: TVectorShape; override;
+    procedure AssignShapeStyle(AMatrix: TAffineMatrix); override;
+  public
+    constructor Create(AManager: TToolManager); override;
+    function ToolKeyPress(var key: TUTF8Char): TRect; override;
+    property CurrentMode: TToolSplineMode read GetCurrentMode write SetCurrentMode;
+  end;
 
 
+{
   { TToolGenericPolygon }
   { TToolGenericPolygon }
 
 
   TToolGenericPolygon = class(TGenericTool)
   TToolGenericPolygon = class(TGenericTool)
@@ -80,8 +111,6 @@ type
     function FinalPolygonView(toolDest: TBGRABitmap): TRect; override;
     function FinalPolygonView(toolDest: TBGRABitmap): TRect; override;
   end;
   end;
 
 
-  TToolSplineMode = (tsmMovePoint, tsmCurveModeAuto, tsmCurveModeAngle, tsmCurveModeSpline);
-
   { TToolGenericSpline }
   { TToolGenericSpline }
 
 
   TToolGenericSpline = class(TToolGenericPolygon)
   TToolGenericSpline = class(TToolGenericPolygon)
@@ -125,14 +154,123 @@ type
     function GetIsSelectingTool: boolean; override;
     function GetIsSelectingTool: boolean; override;
     function HandDrawingPolygonView(toolDest: TBGRABitmap): TRect; override;
     function HandDrawingPolygonView(toolDest: TBGRABitmap): TRect; override;
     function FinalPolygonView({%H-}toolDest: TBGRABitmap):TRect; override;
     function FinalPolygonView({%H-}toolDest: TBGRABitmap):TRect; override;
-  end;
+  end;}
 
 
 implementation
 implementation
 
 
-uses Types, Graphics, LCLType, ugraph, Dialogs, Controls, LazPaintType,
-  BGRAFillInfo, BGRAPath;
+uses LazPaintType, LCVectorPolyShapes;
+
+{ TToolSpline }
+
+function TToolSpline.GetCurrentMode: TToolSplineMode;
+var
+  c: TCurveShape;
+begin
+  if Assigned(FShape) then
+  begin
+    c := TCurveShape(FShape);
+    case c.Usermode of
+    vsuEdit: FCurrentMode := tsmMovePoint;
+    vsuCreate: if c.PointCount > 1 then
+               begin
+                 case c.CurveMode[c.PointCount-2] of
+                   cmAuto: FCurrentMode := tsmCurveModeAuto;
+                   cmAngle: FCurrentMode := tsmCurveModeAngle;
+                   cmCurve: FCurrentMode := tsmCurveModeSpline;
+                 end;
+               end else
+                 result := tsmCurveModeAuto;
+    vsuCurveSetAuto: FCurrentMode := tsmCurveModeAuto;
+    vsuCurveSetAngle: FCurrentMode := tsmCurveModeAngle;
+    vsuCurveSetCurve: FCurrentMode := tsmCurveModeSpline;
+    end;
+  end;
+  result := FCurrentMode;
+end;
+
+procedure TToolSpline.SetCurrentMode(AValue: TToolSplineMode);
+begin
+  if FCurrentMode = AValue then exit;
+  FCurrentMode := AValue;
+  UpdateUserMode;
+end;
+
+procedure TToolSpline.UpdateUserMode;
+var
+  c: TCurveShape;
+begin
+  if FShape = nil then exit;
+  if FQuickDefine then
+  begin
+    FShape.Usermode := vsuCreate;
+    exit;
+  end;
+  c := TCurveShape(FShape);
+  case FCurrentMode of
+  tsmMovePoint: if not (c.Usermode in [vsuEdit,vsuCreate]) then c.Usermode := vsuEdit;
+  tsmCurveModeAuto: if c.Usermode <> vsuCreate then c.Usermode := vsuCurveSetAuto else
+                    if c.PointCount > 1 then c.CurveMode[c.PointCount-2] := cmAuto;
+  tsmCurveModeAngle: if c.Usermode <> vsuCreate then c.Usermode := vsuCurveSetAngle else
+                     if c.PointCount > 1 then c.CurveMode[c.PointCount-2] := cmAngle;
+  tsmCurveModeSpline: if c.Usermode <> vsuCreate then c.Usermode := vsuCurveSetCurve else
+                      if c.PointCount > 1 then c.CurveMode[c.PointCount-2] := cmCurve;
+  end;
+end;
+
+function TToolSpline.CreateShape: TVectorShape;
+begin
+  result := TCurveShape.Create(nil);
+  result.Usermode := vsuCreate;
+  TCurveShape(result).CosineAngle:= EasyBezierMinimumDotProduct;
+  if not FCurveModeHintShown then
+  begin
+    Manager.ToolPopup(tpmCurveModeHint);
+    FCurveModeHintShown := true;
+  end;
+end;
+
+procedure TToolSpline.AssignShapeStyle(AMatrix: TAffineMatrix);
+begin
+  inherited AssignShapeStyle(AMatrix);
+  TCurveShape(FShape).SplineStyle:= Manager.ToolSplineStyle;
+end;
+
+constructor TToolSpline.Create(AManager: TToolManager);
+begin
+  inherited Create(AManager);
+  FNextCurveMode := cmAuto;
+end;
+
+function TToolSpline.ToolKeyPress(var key: TUTF8Char): TRect;
+begin
+  Result:=inherited ToolKeyPress(key);
+  if Key='x' then Key := #0;
+end;
 
 
-{ TToolGenericSpline }
+{ TToolPolygon }
+
+function TToolPolygon.CreateShape: TVectorShape;
+begin
+  result := TPolylineShape.Create(nil);
+end;
+
+procedure TToolPolygon.AssignShapeStyle(AMatrix: TAffineMatrix);
+begin
+  inherited AssignShapeStyle(AMatrix);
+  TCustomPolypointShape(FShape).Closed := Manager.ToolOptionCloseShape;
+  TCustomPolypointShape(FShape).ArrowStartKind := StrToArrowKind(Manager.ToolArrowStart);
+  TCustomPolypointShape(FShape).ArrowEndKind := StrToArrowKind(Manager.ToolArrowEnd);
+  TCustomPolypointShape(FShape).ArrowSize := Manager.ToolArrowSize;
+  UpdateUserMode;
+end;
+
+procedure TToolPolygon.UpdateUserMode;
+begin
+  if FShape = nil then exit;
+  if FQuickDefine then FShape.Usermode := vsuCreate;
+end;
+
+{{ TToolGenericSpline }
 
 
 function TToolGenericSpline.GetCurrentMode: TToolSplineMode;
 function TToolGenericSpline.GetCurrentMode: TToolSplineMode;
 begin
 begin
@@ -962,7 +1100,7 @@ begin
   if length(polygonPoints)<>0 then
   if length(polygonPoints)<>0 then
     ValidatePolygon(GetToolDrawingLayer);
     ValidatePolygon(GetToolDrawingLayer);
   inherited Destroy;
   inherited Destroy;
-end;
+end;}
 
 
 initialization
 initialization
 
 

+ 51 - 99
lazpaint/utoolselect.pas

@@ -41,22 +41,18 @@ type
 
 
   { TToolSelectPoly }
   { TToolSelectPoly }
 
 
-  TToolSelectPoly = class(TToolGenericPolygon)
+  TToolSelectPoly = class(TToolPolygon)
   protected
   protected
-    function HandDrawingPolygonView({%H-}toolDest: TBGRABitmap): TRect; override;
-    function FinalPolygonView(toolDest: TBGRABitmap): TRect; override;
+    procedure AssignShapeStyle(AMatrix: TAffineMatrix); override;
     function GetIsSelectingTool: boolean; override;
     function GetIsSelectingTool: boolean; override;
-    function GetFillColor: TBGRAPixel; override;
   end;
   end;
 
 
   { TToolSelectSpline }
   { TToolSelectSpline }
 
 
-  TToolSelectSpline = class(TToolGenericSpline)
+  TToolSelectSpline = class(TToolSpline)
   protected
   protected
-    function HandDrawingPolygonView(toolDest: TBGRABitmap): TRect; override;
-    function FinalPolygonView({%H-}toolDest: TBGRABitmap): TRect; override;
+    procedure AssignShapeStyle(AMatrix: TAffineMatrix); override;
     function GetIsSelectingTool: boolean; override;
     function GetIsSelectingTool: boolean; override;
-    function GetFillColor: TBGRAPixel; override;
   end;
   end;
 
 
   { TToolMagicWand }
   { TToolMagicWand }
@@ -133,6 +129,52 @@ implementation
 uses types, ugraph, LCLType, LazPaintType, Math, BGRATransform, BGRAPath,
 uses types, ugraph, LCLType, LazPaintType, Math, BGRATransform, BGRAPath,
   BGRAPen, LCVectorRectShapes;
   BGRAPen, LCVectorRectShapes;
 
 
+procedure AssignSelectShapeStyle(AShape: TVectorShape; ASwapColor: boolean);
+var
+  f: TVectorShapeFields;
+begin
+  f:= AShape.Fields;
+  if vsfPenFill in f then AShape.PenFill.Clear;
+  if vsfPenStyle in f Then AShape.PenStyle := ClearPenStyle;
+  if vsfBackFill in f then
+  begin
+    if ASwapColor then
+      AShape.BackFill.SetSolid(BGRABlack)
+    else
+      AShape.BackFill.SetSolid(BGRAWhite);
+  end;
+end;
+
+{ TToolSelectSpline }
+
+procedure TToolSelectSpline.AssignShapeStyle(AMatrix: TAffineMatrix);
+begin
+  FShape.BeginUpdate;
+  inherited AssignShapeStyle(AMatrix);
+  AssignSelectShapeStyle(FShape, FSwapColor);
+  FShape.EndUpdate;
+end;
+
+function TToolSelectSpline.GetIsSelectingTool: boolean;
+begin
+  Result:= true;
+end;
+
+{ TToolSelectPoly }
+
+procedure TToolSelectPoly.AssignShapeStyle(AMatrix: TAffineMatrix);
+begin
+  FShape.BeginUpdate;
+  inherited AssignShapeStyle(AMatrix);
+  AssignSelectShapeStyle(FShape, FSwapColor);
+  FShape.EndUpdate;
+end;
+
+function TToolSelectPoly.GetIsSelectingTool: boolean;
+begin
+  Result:= true;
+end;
+
 { TVectorialSelectTool }
 { TVectorialSelectTool }
 
 
 function TVectorialSelectTool.GetIsSelectingTool: boolean;
 function TVectorialSelectTool.GetIsSelectingTool: boolean;
@@ -141,19 +183,8 @@ begin
 end;
 end;
 
 
 procedure TVectorialSelectTool.AssignShapeStyle(AMatrix: TAffineMatrix);
 procedure TVectorialSelectTool.AssignShapeStyle(AMatrix: TAffineMatrix);
-var
-  f: TVectorShapeFields;
 begin
 begin
-  f:= FShape.Fields;
-  if vsfPenFill in f then FShape.PenFill.Clear;
-  if vsfPenStyle in f Then FShape.PenStyle := ClearPenStyle;
-  if vsfBackFill in f then
-  begin
-    if FSwapColor then
-      FShape.BackFill.SetSolid(BGRABlack)
-    else
-      FShape.BackFill.SetSolid(BGRAWhite);
-  end;
+  AssignSelectShapeStyle(FShape, FSwapColor);
   if FShape is TCustomRectShape then
   if FShape is TCustomRectShape then
   begin
   begin
     if Manager.ToolRatio = 0 then
     if Manager.ToolRatio = 0 then
@@ -470,49 +501,6 @@ begin
   inherited Destroy;
   inherited Destroy;
 end;
 end;
 
 
-{ TToolSelectSpline }
-
-function TToolSelectSpline.HandDrawingPolygonView(toolDest: TBGRABitmap): TRect;
-var
-  splinePoints: ArrayOfTPointF;
-begin
-  if Manager.ToolSplineEasyBezier then
-  begin
-    NeedCurveMode;
-    splinePoints := EasyBezierCurve(polygonPoints,True,FCurveMode,EasyBezierMinimumDotProduct).ToPoints;
-  end else
-    splinePoints := toolDest.ComputeClosedSpline(polygonPoints,Manager.ToolSplineStyle);
-  FRenderedPolygonPoints := splinePoints;
-
-  if length(splinePoints) > 2 then
-  begin
-    toolDest.FillPolyAntialias(splinePoints, fillColor);
-    result := GetShapeBounds(splinePoints,1);
-  end else
-    result := EmptyRect;
-end;
-
-function TToolSelectSpline.FinalPolygonView(toolDest: TBGRABitmap): TRect;
-begin
-  if FAfterHandDrawing then
-    result := HandDrawingPolygonView(toolDest)
-  else
-    result := EmptyRect;
-end;
-
-function TToolSelectSpline.GetIsSelectingTool: boolean;
-begin
-  Result:= true;
-end;
-
-function TToolSelectSpline.GetFillColor: TBGRAPixel;
-begin
-  if swapedColor then
-    result := BGRABlack
-  else
-    result := BGRAWhite;
-end;
-
 { TToolSelectionPen }
 { TToolSelectionPen }
 
 
 function TToolSelectionPen.GetIsSelectingTool: boolean;
 function TToolSelectionPen.GetIsSelectingTool: boolean;
@@ -558,42 +546,6 @@ begin
   ValidateAction;
   ValidateAction;
 end;
 end;
 
 
-{ TToolSelectPoly }
-
-function TToolSelectPoly.HandDrawingPolygonView(toolDest: TBGRABitmap): TRect;
-begin
-  result := EmptyRect;
-  //nothing
-end;
-
-function TToolSelectPoly.FinalPolygonView(toolDest: TBGRABitmap): TRect;
-var
-  i: Integer;
-begin
-  if length(polygonPoints) > 2 then
-  begin
-    toolDest.FillPolyAntialias(polygonPoints, fillColor);
-    result := GetShapeBounds(polygonPoints,1);
-  end else
-    result := EmptyRect;
-  setlength(FRenderedPolygonPoints, length(polygonPoints));
-  for i := 0 to high(polygonPoints) do
-    FRenderedPolygonPoints[i] := polygonPoints[i];
-end;
-
-function TToolSelectPoly.GetIsSelectingTool: boolean;
-begin
-  result := true;
-end;
-
-function TToolSelectPoly.GetFillColor: TBGRAPixel;
-begin
-  if swapedColor then
-    result := BGRABlack
-  else
-    result := BGRAWhite;
-end;
-
 initialization
 initialization
 
 
   RegisterTool(ptMagicWand,TToolMagicWand);
   RegisterTool(ptMagicWand,TToolMagicWand);

+ 2 - 2
lazpaintcontrols/lcvectororiginal.pas

@@ -77,7 +77,7 @@ type
     procedure SetUsermode(AValue: TVectorShapeUsermode); virtual;
     procedure SetUsermode(AValue: TVectorShapeUsermode); virtual;
     procedure LoadFill(AStorage: TBGRACustomOriginalStorage; AObjectName: string; var AValue: TVectorialFill);
     procedure LoadFill(AStorage: TBGRACustomOriginalStorage; AObjectName: string; var AValue: TVectorialFill);
     procedure SaveFill(AStorage: TBGRACustomOriginalStorage; AObjectName: string; AValue: TVectorialFill);
     procedure SaveFill(AStorage: TBGRACustomOriginalStorage; AObjectName: string; AValue: TVectorialFill);
-    function ComputeStroke(APoints: ArrayOfTPointF; AClosed: boolean; AStrokeMatrix: TAffineMatrix): ArrayOfTPointF;
+    function ComputeStroke(APoints: ArrayOfTPointF; AClosed: boolean; AStrokeMatrix: TAffineMatrix): ArrayOfTPointF; virtual;
     function GetStroker: TBGRAPenStroker;
     function GetStroker: TBGRAPenStroker;
     property Stroker: TBGRAPenStroker read GetStroker;
     property Stroker: TBGRAPenStroker read GetStroker;
     procedure FillChange({%H-}ASender: TObject); virtual;
     procedure FillChange({%H-}ASender: TObject); virtual;
@@ -871,7 +871,7 @@ begin
   if FStroker = nil then
   if FStroker = nil then
   begin
   begin
     FStroker := TBGRAPenStroker.Create;
     FStroker := TBGRAPenStroker.Create;
-    FStroker.MiterLimit:= sqrt(2);
+    FStroker.MiterLimit:= 2;
   end;
   end;
   result := FStroker;
   result := FStroker;
 end;
 end;

+ 158 - 10
lazpaintcontrols/lcvectorpolyshapes.pas

@@ -8,6 +8,19 @@ uses
   Classes, SysUtils, Types, LCVectorOriginal, BGRABitmapTypes, BGRALayerOriginal,
   Classes, SysUtils, Types, LCVectorOriginal, BGRABitmapTypes, BGRALayerOriginal,
   BGRABitmap, BGRATransform, BGRAGradients;
   BGRABitmap, BGRATransform, BGRAGradients;
 
 
+type
+  TArrowKind = (akNone, akTail, akTip, akNormal, akCut, akFlipped, akFlippedCut,
+                akTriangle, akTriangleBack1, akTriangleBack2,
+                akHollowTriangle, akHollowTriangleBack1, akHollowTriangleBack2);
+
+const
+  ArrowKindToStr: array[TArrowKind] of string =
+    ('none', 'tail', 'tip', 'normal', 'cut', 'flipped', 'flipped-cut',
+     'triangle', 'triangle-back1', 'triangle-back2',
+     'hollow-triangle', 'hollow-triangle-back1', 'hollow-triangle-back2');
+
+function StrToArrowKind(AStr: string): TArrowKind;
+
 type
 type
   { TCustomPolypointShape }
   { TCustomPolypointShape }
 
 
@@ -16,6 +29,9 @@ type
     FClosed: boolean;
     FClosed: boolean;
     function GetPoint(AIndex: integer): TPointF;
     function GetPoint(AIndex: integer): TPointF;
     function GetPointCount: integer;
     function GetPointCount: integer;
+    procedure SetArrowEndKind(AValue: TArrowKind);
+    procedure SetArrowSize(AValue: TPointF);
+    procedure SetArrowStartKind(AValue: TArrowKind);
     procedure SetPoint(AIndex: integer; AValue: TPointF);
     procedure SetPoint(AIndex: integer; AValue: TPointF);
   protected
   protected
     FPoints: array of record
     FPoints: array of record
@@ -29,6 +45,8 @@ type
     FAddingPoint: boolean;
     FAddingPoint: boolean;
     FMousePos: TPointF;
     FMousePos: TPointF;
     FHoverPoint: integer;
     FHoverPoint: integer;
+    FArrowStartKind,FArrowEndKind: TArrowKind;
+    FArrowSize: TPointF;
     procedure OnMovePoint({%H-}ASender: TObject; {%H-}APrevCoord, ANewCoord: TPointF; {%H-}AShift: TShiftState);
     procedure OnMovePoint({%H-}ASender: TObject; {%H-}APrevCoord, ANewCoord: TPointF; {%H-}AShift: TShiftState);
     procedure OnMoveCenterPoint({%H-}ASender: TObject; {%H-}APrevCoord, ANewCoord: TPointF; {%H-}AShift: TShiftState);
     procedure OnMoveCenterPoint({%H-}ASender: TObject; {%H-}APrevCoord, ANewCoord: TPointF; {%H-}AShift: TShiftState);
     procedure OnStartMove({%H-}ASender: TObject; APointIndex: integer; {%H-}AShift: TShiftState);
     procedure OnStartMove({%H-}ASender: TObject; APointIndex: integer; {%H-}AShift: TShiftState);
@@ -42,11 +60,13 @@ type
     procedure DoClickPoint({%H-}APointIndex: integer; {%H-}AShift: TShiftState); virtual;
     procedure DoClickPoint({%H-}APointIndex: integer; {%H-}AShift: TShiftState); virtual;
     function CanMovePoints: boolean; virtual;
     function CanMovePoints: boolean; virtual;
     procedure InsertPointAuto;
     procedure InsertPointAuto;
+    function ComputeStroke(APoints: ArrayOfTPointF; AClosed: boolean;
+      AStrokeMatrix: TAffineMatrix): ArrayOfTPointF; override;
   public
   public
     constructor Create(AContainer: TVectorOriginal); override;
     constructor Create(AContainer: TVectorOriginal); override;
     procedure Clear;
     procedure Clear;
     destructor Destroy; override;
     destructor Destroy; override;
-    function AddPoint(const APoint: TPointF): integer;
+    function AddPoint(const APoint: TPointF): integer; virtual;
     function RemovePoint(AIndex: integer): boolean;
     function RemovePoint(AIndex: integer): boolean;
     procedure RemovePointRange(AFromIndex, AToIndexPlus1: integer);
     procedure RemovePointRange(AFromIndex, AToIndexPlus1: integer);
     procedure InsertPoint(AIndex: integer; APoint: TPointF);
     procedure InsertPoint(AIndex: integer; APoint: TPointF);
@@ -59,10 +79,14 @@ type
     procedure ConfigureCustomEditor(AEditor: TBGRAOriginalEditor); override;
     procedure ConfigureCustomEditor(AEditor: TBGRAOriginalEditor); override;
     procedure Transform(AMatrix: TAffineMatrix); override;
     procedure Transform(AMatrix: TAffineMatrix); override;
     class function Usermodes: TVectorShapeUsermodes; override;
     class function Usermodes: TVectorShapeUsermodes; override;
+    class function DefaultArrowSize: TPointF;
     property Points[AIndex:integer]: TPointF read GetPoint write SetPoint;
     property Points[AIndex:integer]: TPointF read GetPoint write SetPoint;
     property PointCount: integer read GetPointCount;
     property PointCount: integer read GetPointCount;
     property Closed: boolean read GetClosed write SetClosed;
     property Closed: boolean read GetClosed write SetClosed;
     property HoverPoint: integer read FHoverPoint;
     property HoverPoint: integer read FHoverPoint;
+    property ArrowStartKind: TArrowKind read FArrowStartKind write SetArrowStartKind;
+    property ArrowEndKind: TArrowKind read FArrowEndKind write SetArrowEndKind;
+    property ArrowSize: TPointF read FArrowSize write SetArrowSize;
   end;
   end;
 
 
   { TPolylineShape }
   { TPolylineShape }
@@ -84,8 +108,10 @@ type
 
 
   TCurveShape = class(TPolylineShape)
   TCurveShape = class(TPolylineShape)
   private
   private
+    FCosineAngle: single;
     FSplineStyle: TSplineStyle;
     FSplineStyle: TSplineStyle;
     function GetCurveMode(AIndex: integer): TEasyBezierCurveMode;
     function GetCurveMode(AIndex: integer): TEasyBezierCurveMode;
+    procedure SetCosineAngle(AValue: single);
     procedure SetCurveMode(AIndex: integer; AValue: TEasyBezierCurveMode);
     procedure SetCurveMode(AIndex: integer; AValue: TEasyBezierCurveMode);
     procedure SetSplineStyle(AValue: TSplineStyle);
     procedure SetSplineStyle(AValue: TSplineStyle);
   protected
   protected
@@ -95,17 +121,55 @@ type
   public
   public
     class function Usermodes: TVectorShapeUsermodes; override;
     class function Usermodes: TVectorShapeUsermodes; override;
     constructor Create(AContainer: TVectorOriginal); override;
     constructor Create(AContainer: TVectorOriginal); override;
+    function AddPoint(const APoint: TPointF): integer; override;
     procedure KeyPress(UTF8Key: string; var AHandled: boolean); override;
     procedure KeyPress(UTF8Key: string; var AHandled: boolean); override;
     procedure LoadFromStorage(AStorage: TBGRACustomOriginalStorage); override;
     procedure LoadFromStorage(AStorage: TBGRACustomOriginalStorage); override;
     procedure SaveToStorage(AStorage: TBGRACustomOriginalStorage); override;
     procedure SaveToStorage(AStorage: TBGRACustomOriginalStorage); override;
     class function StorageClassName: RawByteString; override;
     class function StorageClassName: RawByteString; override;
     property SplineStyle: TSplineStyle read FSplineStyle write SetSplineStyle;
     property SplineStyle: TSplineStyle read FSplineStyle write SetSplineStyle;
     property CurveMode[AIndex: integer]: TEasyBezierCurveMode read GetCurveMode write SetCurveMode;
     property CurveMode[AIndex: integer]: TEasyBezierCurveMode read GetCurveMode write SetCurveMode;
+    property CosineAngle: single read FCosineAngle write SetCosineAngle;
   end;
   end;
 
 
+procedure ApplyArrowStyle(AArrow: TBGRACustomArrow; AStart: boolean; AKind: TArrowKind; ASize: TPointF);
+
 implementation
 implementation
 
 
-uses BGRAPen, BGRAGraphics, BGRAFillInfo, BGRAPath, math, LCVectorialFill;
+uses BGRAPen, BGRAGraphics, BGRAFillInfo, BGRAPath, math, LCVectorialFill,
+  BGRAArrow;
+
+function StrToArrowKind(AStr: string): TArrowKind;
+var
+  ak: TArrowKind;
+begin
+  for ak := low(TArrowKind) to high(TArrowKind) do
+    if CompareText(AStr, ArrowKindToStr[ak])=0 then exit(ak);
+  result := akNone;
+end;
+
+procedure ApplyArrowStyle(AArrow: TBGRACustomArrow; AStart: boolean; AKind: TArrowKind; ASize: TPointF);
+var backOfs: single;
+begin
+  backOfs := 0;
+  if (ASize.x = 0) or (ASize.y = 0) then AKind := akNone;
+  if AKind in[akTriangleBack1,akHollowTriangleBack1] then backOfs := 0.25;
+  if AKind in[akTriangleBack2,akHollowTriangleBack2] then backOfs := 0.50;
+  case AKind of
+  akTail: if AStart then AArrow.StartAsTail else AArrow.EndAsTail;
+  akTip: if AStart then AArrow.StartAsTriangle else AArrow.EndAsTriangle;
+  akNormal,akCut,akFlipped,akFlippedCut:
+    if AStart then AArrow.StartAsClassic(AKind in[akFlipped,akFlippedCut], AKind in[akCut,akFlippedCut])
+    else AArrow.EndAsClassic(AKind in[akFlipped,akFlippedCut], AKind in[akCut,akFlippedCut]);
+  akTriangle,akTriangleBack1,akTriangleBack2:
+    if AStart then AArrow.StartAsTriangle(backOfs) else AArrow.EndAsTriangle(backOfs);
+  akHollowTriangle,akHollowTriangleBack1,akHollowTriangleBack2:
+    if AStart then AArrow.StartAsTriangle(backOfs,False,True) else AArrow.EndAsTriangle(backOfs,False,True);
+  else if AStart then AArrow.StartAsNone else AArrow.EndAsNone;
+  end;
+  if (AKind = akTip) and not ((ASize.x = 0) or (ASize.y = 0)) then
+    ASize := ASize*(0.5/ASize.y);
+  if AStart then AArrow.StartSize := ASize else AArrow.EndSize := ASize;
+end;
 
 
 procedure IncludePointF(var ARectF: TRectF; APointF: TPointF);
 procedure IncludePointF(var ARectF: TRectF; APointF: TPointF);
 begin
 begin
@@ -154,6 +218,30 @@ begin
   result:= length(FPoints);
   result:= length(FPoints);
 end;
 end;
 
 
+procedure TCustomPolypointShape.SetArrowEndKind(AValue: TArrowKind);
+begin
+  if FArrowEndKind=AValue then Exit;
+  BeginUpdate;
+  FArrowEndKind:=AValue;
+  EndUpdate;
+end;
+
+procedure TCustomPolypointShape.SetArrowSize(AValue: TPointF);
+begin
+  if FArrowSize=AValue then Exit;
+  BeginUpdate;
+  FArrowSize:=AValue;
+  EndUpdate;
+end;
+
+procedure TCustomPolypointShape.SetArrowStartKind(AValue: TArrowKind);
+begin
+  if FArrowStartKind=AValue then Exit;
+  BeginUpdate;
+  FArrowStartKind:=AValue;
+  EndUpdate;
+end;
+
 procedure TCustomPolypointShape.SetClosed(AValue: boolean);
 procedure TCustomPolypointShape.SetClosed(AValue: boolean);
 begin
 begin
   if AValue = FClosed then exit;
   if AValue = FClosed then exit;
@@ -235,6 +323,11 @@ begin
   Result:= inherited Usermodes + [vsuCreate];
   Result:= inherited Usermodes + [vsuCreate];
 end;
 end;
 
 
+class function TCustomPolypointShape.DefaultArrowSize: TPointF;
+begin
+  result := PointF(2,2);
+end;
+
 procedure TCustomPolypointShape.SetUsermode(AValue: TVectorShapeUsermode);
 procedure TCustomPolypointShape.SetUsermode(AValue: TVectorShapeUsermode);
 var
 var
   add: Boolean;
   add: Boolean;
@@ -356,6 +449,17 @@ begin
   end;
   end;
 end;
 end;
 
 
+function TCustomPolypointShape.ComputeStroke(APoints: ArrayOfTPointF;
+  AClosed: boolean; AStrokeMatrix: TAffineMatrix): ArrayOfTPointF;
+begin
+  if Stroker.Arrow = nil then Stroker.Arrow := TBGRAArrow.Create;
+  ApplyArrowStyle(Stroker.Arrow, true, ArrowStartKind, ArrowSize);
+  ApplyArrowStyle(Stroker.Arrow, false, ArrowEndKind, ArrowSize);
+  Result:=inherited ComputeStroke(APoints, AClosed, AStrokeMatrix);
+  Stroker.Arrow.StartAsNone;
+  Stroker.Arrow.EndAsNone;
+end;
+
 constructor TCustomPolypointShape.Create(AContainer: TVectorOriginal);
 constructor TCustomPolypointShape.Create(AContainer: TVectorOriginal);
 begin
 begin
   inherited Create(AContainer);
   inherited Create(AContainer);
@@ -435,7 +539,10 @@ begin
   FMousePos := PointF(X,Y);
   FMousePos := PointF(X,Y);
   if FAddingPoint then
   if FAddingPoint then
   begin
   begin
-    Points[PointCount-1] := FMousePos;
+    if (PointCount = 1) and (FMousePos <> Points[PointCount-1]) then
+      Points[PointCount] := FMousePos
+    else
+      Points[PointCount-1] := FMousePos;
     AHandled:= true;
     AHandled:= true;
   end;
   end;
 end;
 end;
@@ -514,6 +621,11 @@ begin
     FPoints[i].data := nil;
     FPoints[i].data := nil;
   end;
   end;
   FClosed:= AStorage.Bool['closed'];
   FClosed:= AStorage.Bool['closed'];
+  if AStorage.HasAttribute('arrow-size') then
+    FArrowSize := AStorage.PointF['arrow-size']
+  else FArrowSize := DefaultArrowSize;
+  FArrowStartKind:= StrToArrowKind(AStorage.RawString['arrow-start-kind']);
+  FArrowEndKind:= StrToArrowKind(AStorage.RawString['arrow-end-kind']);
   EndUpdate;
   EndUpdate;
 end;
 end;
 
 
@@ -533,6 +645,12 @@ begin
   AStorage.FloatArray['x'] := x;
   AStorage.FloatArray['x'] := x;
   AStorage.FloatArray['y'] := y;
   AStorage.FloatArray['y'] := y;
   AStorage.Bool['closed'] := Closed;
   AStorage.Bool['closed'] := Closed;
+  if ArrowStartKind=akNone then AStorage.RemoveAttribute('arrow-start-kind')
+  else AStorage.RawString['arrow-start-kind'] := ArrowKindToStr[ArrowStartKind];
+  if ArrowEndKind=akNone then AStorage.RemoveAttribute('arrow-end-kind')
+  else AStorage.RawString['arrow-end-kind'] := ArrowKindToStr[ArrowEndKind];
+  if (ArrowStartKind=akNone) and (ArrowEndKind=akNone) then AStorage.RemoveAttribute('arrow-size')
+  else AStorage.PointF['arrow-size'] := FArrowSize;
 end;
 end;
 
 
 procedure TCustomPolypointShape.ConfigureCustomEditor(AEditor: TBGRAOriginalEditor);
 procedure TCustomPolypointShape.ConfigureCustomEditor(AEditor: TBGRAOriginalEditor);
@@ -547,7 +665,7 @@ begin
   for i:= 0 to PointCount-1 do
   for i:= 0 to PointCount-1 do
     if isEmptyPointF(Points[i]) then
     if isEmptyPointF(Points[i]) then
       FPoints[i].editorIndex := -1
       FPoints[i].editorIndex := -1
-    else if (FAddingPoint and ((i = 0) or (i = PointCount-1))) then
+    else if (FAddingPoint and (i = PointCount-1)) then
     begin
     begin
       FPoints[i].editorIndex := -1;
       FPoints[i].editorIndex := -1;
       FCenterPoint += Points[i];
       FCenterPoint += Points[i];
@@ -660,7 +778,7 @@ begin
     pts := GetCurve(AMatrix);
     pts := GetCurve(AMatrix);
     if PenVisible(rboAssumePenFill in AOptions) then
     if PenVisible(rboAssumePenFill in AOptions) then
     begin
     begin
-      if JoinStyle = pjsRound then
+      if (JoinStyle = pjsRound) and (ArrowStartKind = akNone) and (ArrowEndKind = akNone) then
       begin
       begin
         xMargin := (abs(AMatrix[1,1])+abs(AMatrix[1,2]))*PenWidth*0.5;
         xMargin := (abs(AMatrix[1,1])+abs(AMatrix[1,2]))*PenWidth*0.5;
         yMargin := (abs(AMatrix[2,1])+abs(AMatrix[2,2]))*PenWidth*0.5;
         yMargin := (abs(AMatrix[2,1])+abs(AMatrix[2,2]))*PenWidth*0.5;
@@ -728,6 +846,14 @@ begin
     result := cmAuto;
     result := cmAuto;
 end;
 end;
 
 
+procedure TCurveShape.SetCosineAngle(AValue: single);
+begin
+  if FCosineAngle=AValue then Exit;
+  BeginUpdate;
+  FCosineAngle:=AValue;
+  EndUpdate;
+end;
+
 procedure TCurveShape.SetCurveMode(AIndex: integer; AValue: TEasyBezierCurveMode);
 procedure TCurveShape.SetCurveMode(AIndex: integer; AValue: TEasyBezierCurveMode);
 begin
 begin
   if (AIndex < 0) or (AIndex >= PointCount) then exit;
   if (AIndex < 0) or (AIndex >= PointCount) then exit;
@@ -751,7 +877,7 @@ begin
     setlength(cm, PointCount);
     setlength(cm, PointCount);
     for i := 0 to PointCount-1 do
     for i := 0 to PointCount-1 do
       cm[i] := CurveMode[i];
       cm[i] := CurveMode[i];
-    eb := EasyBezierCurve(pts, Closed, cm);
+    eb := EasyBezierCurve(pts, Closed, cm, CosineAngle);
     result := eb.ToPoints;
     result := eb.ToPoints;
   end else
   end else
   begin
   begin
@@ -787,23 +913,43 @@ begin
   FSplineStyle:= ssEasyBezier;
   FSplineStyle:= ssEasyBezier;
 end;
 end;
 
 
+function TCurveShape.AddPoint(const APoint: TPointF): integer;
+begin
+  if (PointCount > 1) and (APoint = Points[PointCount-1]) then
+  begin
+    BeginUpdate;
+    CurveMode[PointCount-1] := CurveMode[PointCount-2];
+    Result:=inherited AddPoint(APoint);
+    EndUpdate;
+  end
+  else Result:=inherited AddPoint(APoint);
+end;
+
 procedure TCurveShape.KeyPress(UTF8Key: string; var AHandled: boolean);
 procedure TCurveShape.KeyPress(UTF8Key: string; var AHandled: boolean);
+var
+  targetPoint: Integer;
 begin
 begin
-  if (FHoverPoint >= 0) and (FHoverPoint < PointCount) then
+  if FHoverPoint<>-1 then
+    targetPoint := FHoverPoint
+  else if FAddingPoint and (PointCount > 1) then
+    targetPoint := PointCount-2
+  else
+    targetPoint := -1;
+  if (targetPoint >= 0) and (targetPoint < PointCount) then
   begin
   begin
     if (UTF8Key = 'A') or (UTF8Key = 'a') then
     if (UTF8Key = 'A') or (UTF8Key = 'a') then
     begin
     begin
-      CurveMode[FHoverPoint] := cmAuto;
+      CurveMode[targetPoint] := cmAuto;
       AHandled := true;
       AHandled := true;
     end else
     end else
     if (UTF8Key = 'S') or (UTF8Key = 's') then
     if (UTF8Key = 'S') or (UTF8Key = 's') then
     begin
     begin
-      CurveMode[FHoverPoint] := cmCurve;
+      CurveMode[targetPoint] := cmCurve;
       AHandled:= true;
       AHandled:= true;
     end else
     end else
     if (UTF8Key = 'X') or (UTF8Key = 'x') then
     if (UTF8Key = 'X') or (UTF8Key = 'x') then
     begin
     begin
-      CurveMode[FHoverPoint] := cmAngle;
+      CurveMode[targetPoint] := cmAngle;
       AHandled:= true;
       AHandled:= true;
     end;
     end;
   end;
   end;
@@ -841,6 +987,7 @@ begin
       for i:= length(cm) to PointCount-1 do
       for i:= length(cm) to PointCount-1 do
         CurveMode[i] := cmCurve;
         CurveMode[i] := cmCurve;
   end;
   end;
+  CosineAngle := AStorage.FloatDef['cosine-angle', EasyBezierDefaultMinimumDotProduct];
   EndUpdate;
   EndUpdate;
 end;
 end;
 
 
@@ -869,6 +1016,7 @@ begin
       cm[i] := ord(CurveMode[i]);
       cm[i] := ord(CurveMode[i]);
     AStorage.FloatArray['curve-mode'] := cm;
     AStorage.FloatArray['curve-mode'] := cm;
   end;
   end;
+  AStorage.Float['cosine-angle'] := CosineAngle;
 end;
 end;
 
 
 class function TCurveShape.StorageClassName: RawByteString;
 class function TCurveShape.StorageClassName: RawByteString;