Browse Source

curve modes

Unknown 6 years ago
parent
commit
a8f2afe15b
4 changed files with 449 additions and 58 deletions
  1. 139 4
      vectoredit/umain.lfm
  2. 102 21
      vectoredit/umain.pas
  3. 2 1
      vectoredit/uvectororiginal.pas
  4. 206 32
      vectoredit/uvectorshapes.pas

+ 139 - 4
vectoredit/umain.lfm

@@ -1,7 +1,7 @@
 object Form1: TForm1
-  Left = 374
+  Left = 375
   Height = 475
-  Top = 0
+  Top = 25
   Width = 981
   Caption = 'Vector Edit'
   ClientHeight = 475
@@ -715,8 +715,8 @@ object Form1: TForm1
         InnerMargin = 0
         OnClick = ButtonPenStyleClick
         ParentColor = False
-        Rounding.RoundX = 3
-        Rounding.RoundY = 3
+        Rounding.RoundX = 5
+        Rounding.RoundY = 5
         Rounding.RoundOptions = []
         RoundingDropDown.RoundX = 1
         RoundingDropDown.RoundY = 1
@@ -42037,4 +42037,139 @@ object Form1: TForm1
       0000000000000000000000000000
     }
   end
+  object CurveImageList: TBGRAImageList
+    left = 680
+    top = 96
+    Bitmap = {
+      4C69040000001000000010000000000000000000000000000000000000000000
+      0000000000000000000000000000000000000000000000000000000000000000
+      00000000000000000000000000000000000001000022010000C5010000FC0100
+      00C5010000220000000000000000000000000000000000000000000000000000
+      000000000000000000000000000000000000010000C4B2B2B2FFFBFBFBFFB2B2
+      B2FF00000ED00000F51400000000000000000000000000000000000000000000
+      000000000000000000000000000000000000010000FBFBFBFBFFFFFFFFFFFBFB
+      FBFF000004FF0000F5FF0000F5E40000F5AB0000F5490000F501000000000000
+      000000000000000000000000000000000000010000C4B2B2B2FFFBFBFBFF0B0B
+      0BFF000014D60000F56F0000F5A30000F5E40000F5FF0000F5C90000F51F0000
+      00000000000000000000000000000000000001000022010000C5010000FC0C0C
+      0CFF161616FD00000000000000000000F5040000F55D0000F5EE0000F5EA0000
+      F520000000000000000000000000000000000000000000000000000000000C0C
+      0CFEC3C3C3FF202020FA0000000000000000000000000000F5260000F5EF0000
+      F5C10000F5020000000000000000000000000000000000000000000000000C0C
+      0CFEEEEEEEFFD6D6D6FF202020FA0000000000000000000000000000F55B0000
+      F5FF0000F5640000000000000000000000000000000000000000000000000C0C
+      0CFEEEEEEEFFFFFFFFFFD6D6D6FF202020FA0000000000000000000000000000
+      F5D60000F5B70000000000000000000000000000000000000000000000000C0C
+      0CFEEEEEEEFFFFFFFFFFFFFFFFFFD6D6D6FF212121F900000000000000000000
+      F58C0000F5F70000F50A00000000000000000000000000000000000000000C0C
+      0CFEDCDCDCFFE0E0E0FFF1F1F1FF4A4A4AFA151515FD080808FC000000000000
+      F5510000F5FF0000F53100000000000000000000000000000000000000000808
+      08FD111111FC131313FEEEEEEEFF5A5A5AF40100007B0100000E000000000000
+      F5310000F5FF0000F55300000000000000000000000000000000000000000100
+      001F010000032B2B2BBAADADADFEB6B6B6FF2D2D2DC500000000000000000000
+      F50D0000F5F80000F56000000000000000000000000000000000000000000000
+      0000000000000100004F434343F6DADADAFF0C0C0CFE01000019000000000000
+      00000000F5170000F50200000000000000000000000000000000000000000000
+      000000000000010000041C1C1CED171717FA2A2A2AD401000040000000000000
+      0000000000000000000000000000000000000000000000000000000000000000
+      0000000000000000000001000016010000020000000000000000000000000000
+      0000000000000000000000000000000000000000000000000000000000000000
+      0000000000000000000000000000000000000000000000000000000000000000
+      00000000000000000000000000000000000001000022010000C5010000FC0100
+      00C5010000220000000000000000000000000000000001000022010000C50100
+      00FC010000C5010000220000000000000000010000C4B2B2B2FFFBFBFBFFB2B2
+      B2FF010000C400000000000000000000000000000000010000C4B2B2B2FFFBFB
+      FBFFB2B2B2FF010000C40000000000000000010000FBFBFBFBFFFFFFFFFFFBFB
+      FBFF010000FB00000000000000000000000000000000000001FCFBFBFBFFFFFF
+      FFFFFBFBFBFF010000FB0000000000000000010000C4B2B2B2FFFBFBFBFFB2B2
+      B2FF010000C400000000000000000000FA370000FABC00003AFFB2B2B2FFFBFB
+      FBFFB2B2B2FF010000C4000000000000000001000022010000C5010000FC0100
+      00C5010000220000FA0E0000FA950000FAFD0000FAE70000B98300000ACD0000
+      03FF00001ADC0100002200000000000000000000000000000000000000000000
+      00000000FA250000FAE00000FAFB0000FA8D0000FA0B000000000000FA050000
+      FAFE0000FA7C0000000000000000000000000000000000000000000000000000
+      FA280000FAE50000FAE80000FA36000000000000000000000000000000000000
+      FAEC0000FA9300000000000000000000000000000000000000000000FA0B0000
+      FADE0000FAE60000FA2900000000000000000000000000000000000000000000
+      FAD50000FAAB00000000000000000000000000000000000000000000FA920000
+      FAFC0000FA3A0000000000000000000000000000000000000000000000000000
+      FABD0000FAC2000000000000000000000000000000000000FA330000FAFD0000
+      FA8F000000000000000000000000000000000000000000000000000000000000
+      FAA60000FAD9000000000000000000000000000000000000FAB70000FAEA0000
+      FA0C000000000000000000000000000000000000000000000000000000000000
+      FA8F0000FAF00000000000000000000000000000FA3D0000FAFF0000FA730000
+      0000000000000000000000000000000000000000000000000000000000000000
+      FA780000FAFF0000FA0A00000000000000000000FABE0000FAE40000FA090000
+      0000000000000000000000000000000000000000000000000000000000000000
+      FA600000FAFF0000FA2000000000000000000000FAA80000FA58000000000000
+      0000000000000000000000000000000000000000000000000000000000000000
+      FA440000FAFF0000FA2D00000000000000000000000000000000000000000000
+      0000000000000000000000000000000000000000000000000000000000000000
+      FA010000FA310000000000000000000000000000000000000000000000000000
+      0000000000000000000000000000000000000000000000000000000000000000
+      0000000000000000000000000000000000000000000000000000000000000000
+      00000000000001000022010000C5010000FC010000C501000022000000000000
+      0000000000000000000000000000000000000000000000000000000000000000
+      000000000000010000C4B2B2B2FFFBFBFBFFB2B2B2FF010000C4000000000000
+      0000000000000000000000000000000000000000000000000000000000000000
+      000000000000010000FBFBFBFBFFFFFFFFFFFBFBFBFF010000FB000000000000
+      0000000000000000000000000000000000000000000000000000000000000000
+      000000000000010000C4B2B2B2FFFBFBFBFFB2B2B2FF010000C4000000000000
+      0000000000000000000000000000000000000000000000000000000000000000
+      00000000000001000022010000C5010000FC010000C501000022000000000000
+      0000000000000000000000000000000000000000000000000000000000000000
+      0000000000000000000000000000000000000000000000000000000000000000
+      0000000000000000000000000000000000000000000000000000000000000000
+      000000000000000000000000F5020000F5160000000000000000000000000000
+      0000000000000000000000000000000000000000000000000000000000000000
+      00000000F50C0000F5960000F5EA0000F5FF0000F5D40000F562000000000000
+      0000000000000000000000000000000000000000000000000000000000000000
+      F51A0000F5CE0000F5FA0000F5A30000F5770000F5C60000F5FE0000F57B0000
+      00000000000000000000000000000000000000000000000000000000F5150000
+      F5DB0000F5F00000F54600000000000000000000F5030000F5A00000F5FF0000
+      F56900000000000000000000000000000000000000000000F50A0000F5C80000
+      F5F40000F536000000000000000000000000000000000000F5010000F5B20000
+      F5F80000F5370000000000000000000000000000F5030000F5B10000F5FC0000
+      F54D0000000000000000000000000000000000000000000000000000F5110000
+      F5DC0000F5E30000F51600000000000000000000F5960000F5FE0000F5680000
+      0000000000000000000000000000000000000000000000000000000000000000
+      F52F0000F5F50000F5BC0000F5030000F5280000F5FF0000F586000000000000
+      0000000000000000000000000000000000000000000000000000000000000000
+      00000000F55D0000F5FE0000F537000000000000F52F00000000000000000000
+      0000000000000000000000000000000000000000000000000000000000000000
+      0000000000000000F51F00000000000000000000000000000000000000000000
+      0000000000000000000000000000000000000000000000000000000000000000
+      0000000000000000000000000000000000000000000000000000000000000000
+      00000000000001000022010000C5010000FC010000C501000022000000000000
+      0000000000000000000000000000000000000000000000000000000000000000
+      000000000000010000C4B2B2B2FFFBFBFBFFB2B2B2FF010000C4000000000000
+      0000000000000000000000000000000000000000000000000000000000000000
+      000000000000010000FBFBFBFBFFFFFFFFFFFBFBFBFF010000FB000000000000
+      0000000000000000000000000000000000000000000000000000000000000000
+      000000000000000001C5B2B2B2FFFBFBFBFFB2B2B2FF010000C4000000000000
+      0000000000000000000000000000000000000000000000000000000000000000
+      0000000000000000B78E000037FF000002FF000036FF00008C51000000000000
+      0000000000000000000000000000000000000000000000000000000000000000
+      00000000F12C0000F1F90000F1A50000F10D0000F1E40000F1CA0000F1010000
+      0000000000000000000000000000000000000000000000000000000000000000
+      F1030000F1CA0000F1E90000F114000000000000F15A0000F1FF0000F1640000
+      0000000000000000000000000000000000000000000000000000000000000000
+      F1770000F1FF0000F15600000000000000000000F1010000F1C10000F1EA0000
+      F1100000000000000000000000000000000000000000000000000000F1270000
+      F1F70000F1AC000000000000000000000000000000000000F12F0000F1FC0000
+      F19100000000000000000000000000000000000000000000F1020000F1C30000
+      F1ED0000F11700000000000000000000000000000000000000000000F1940000
+      F1FB0000F12E000000000000000000000000000000000000F16F0000F1FF0000
+      F15D0000000000000000000000000000000000000000000000000000F1110000
+      F1EB0000F1BF0000000000000000000000000000F1220000F1F40000F1B40000
+      0000000000000000000000000000000000000000000000000000000000000000
+      F1660000F1FF0000F158000000000000F1010000F1BD0000F1F00000F11C0000
+      0000000000000000000000000000000000000000000000000000000000000000
+      F1030000F1CB0000F1E30000F10C0000F1210000F1FC0000F162000000000000
+      0000000000000000000000000000000000000000000000000000000000000000
+      00000000F1380000F1FC0000F147000000000000F11900000000000000000000
+      0000000000000000000000000000000000000000000000000000000000000000
+      0000000000000000F11700000000
+    }
+  end
 end

+ 102 - 21
vectoredit/umain.pas

@@ -37,6 +37,7 @@ type
   TForm1 = class(TForm)
     BackImage: TImage;
     BGRAFillImageList16: TBGRAImageList;
+    CurveImageList: TBGRAImageList;
     ButtonBackGradInterp: TBCButton;
     ButtonBackLoadTex: TBCButton;
     ButtonBackTexRepeat: TBCButton;
@@ -166,11 +167,12 @@ type
     FUpdatingSpinEditPenWidth: boolean;
     FCurrentTool: TPaintTool;
     FSplineStyle: TSplineStyle;
-    FComboboxSplineStyle: TComboBox;
-    FUpdatingComboboxSplineStyle : boolean;
+    FSplineStyleMenu: TPopupMenu;
+    FComboboxSplineStyle: TBCButton;
+    FSplineToolbar: TToolBar;
     FPenStyleMenu: TPopupMenu;
     FInRemoveShapeIfEmpty: Boolean;
-    procedure ComboBoxSplineStyleChange(Sender: TObject);
+    procedure ComboBoxSplineStyleClick(Sender: TObject);
     function GetBackTexture: TBGRABitmap;
     function GetPenColor: TBGRAPixel;
     function GetPenStyle: TBGRAPenStyle;
@@ -178,6 +180,7 @@ type
     function GetSplineStyle: TSplineStyle;
     function GetVectorTransform: TAffineMatrix;
     procedure ImageChange(ARectF: TRectF);
+    procedure OnClickSplineStyleItem(ASender: TObject);
     procedure OnEditingChange({%H-}ASender: TObject; AOriginal: TBGRALayerCustomOriginal);
     procedure OnOriginalChange({%H-}ASender: TObject; AOriginal: TBGRALayerCustomOriginal);
     procedure OnSelectShape(ASender: TObject; AShape: TVectorShape; APreviousShape: TVectorShape);
@@ -196,6 +199,7 @@ type
     procedure SetPenStyle(AValue: TBGRAPenStyle);
     procedure SetPenWidth(AValue: single);
     procedure SetSplineStyle(AValue: TSplineStyle);
+    procedure SplineToolbarClick(Sender: TObject);
     procedure UpdateViewCursor(ACursor: TOriginalEditorCursor);
     procedure RenderAndUpdate(ADraft: boolean);
     procedure UpdateFlattenedImage(ARect: TRect; AUpdateView: boolean = true);
@@ -252,6 +256,22 @@ implementation
 
 uses math, BGRAPen, BGRAThumbnail, BGRAGradientOriginal, uvectorclipboard;
 
+procedure AddToolbarCheckButton(AToolbar: TToolbar; ACaption: string; AImageIndex: integer;
+          AOnClick: TNotifyEvent; ADown: boolean; AGrouped: boolean = true);
+var
+  btn: TToolButton;
+begin
+  btn := TToolButton.Create(AToolbar);
+  btn.Style := tbsCheck;
+  btn.Caption := ACaption;
+  btn.Hint := ACaption;
+  btn.ImageIndex := AImageIndex;
+  btn.Down:= ADown;
+  btn.Grouped := AGrouped;
+  btn.OnClick:= AOnClick;
+  btn.Parent := AToolbar;
+end;
+
 function LCLKeyToSpecialKey(Key: Word): TSpecialKey;
 var
   sk: TSpecialKey;
@@ -277,6 +297,7 @@ var
   gr: TBGRAGradientRepetition;
   ci: TBGRAColorInterpolation;
   tr: TTextureRepetition;
+  ss: TSplineStyle;
 begin
   baseCaption:= Caption;
 
@@ -321,6 +342,15 @@ begin
     FBackGradInterpMenu.Items.Add(item);
   end;
 
+  FSplineStyleMenu := TPopupMenu.Create(nil);
+  for ss := low(TSplineStyle) to high(TSplineStyle) do
+  begin
+    item := TMenuItem.Create(FSplineStyleMenu); item.Caption := SplineStyleToStr[ss];
+    item.OnClick:=@OnClickSplineStyleItem;
+        item.Tag := ord(ss);
+    FSplineStyleMenu.Items.Add(item);
+  end;
+
   FBackTexRepetitionMenu := TPopupMenu.Create(nil);
   FBackTexRepetitionMenu.Images := BGRAFillImageList16;
   for tr := low(TTextureRepetition) to high(TTextureRepetition) do
@@ -736,6 +766,7 @@ begin
   FBackGradRepetitionMenu.Free;
   FBackGradInterpMenu.Free;
   FBackTexRepetitionMenu.Free;
+  FSplineStyleMenu.Free;
 end;
 
 procedure TForm1.FormKeyDown(Sender: TObject; var Key: Word; Shift: TShiftState);
@@ -883,11 +914,11 @@ begin
   end;
 end;
 
-procedure TForm1.ComboBoxSplineStyleChange(Sender: TObject);
+procedure TForm1.ComboBoxSplineStyleClick(Sender: TObject);
 begin
-  if FUpdatingComboboxSplineStyle then exit;
-  if FComboboxSplineStyle.ItemIndex <> -1 then
-    splineStyle:= TSplineStyle(FComboboxSplineStyle.ItemIndex);
+  if Assigned(FSplineStyleMenu) then
+    with FComboboxSplineStyle.ClientToScreen(Point(0,FComboboxSplineStyle.Height)) do
+      FSplineStyleMenu.PopUp(X,Y);
 end;
 
 function TForm1.GetBackTexture: TBGRABitmap;
@@ -934,6 +965,11 @@ begin
   end;
 end;
 
+procedure TForm1.OnClickSplineStyleItem(ASender: TObject);
+begin
+  splineStyle := TSplineStyle((ASender as TMenuItem).Tag);
+end;
+
 procedure TForm1.OnEditingChange(ASender: TObject;
   AOriginal: TBGRALayerCustomOriginal);
 begin
@@ -1174,16 +1210,32 @@ procedure TForm1.SetSplineStyle(AValue: TSplineStyle);
 begin
   FSplineStyle := AValue;
   if Assigned(FComboboxSplineStyle) then
-  begin
-    FUpdatingComboboxSplineStyle := true;
-    FComboboxSplineStyle.ItemIndex:= ord(FSplineStyle);
-    FUpdatingComboboxSplineStyle := false;
-  end;
+    FComboboxSplineStyle.Caption:= SplineStyleToStr[FSplineStyle];
   if not FUpdatingFromShape and Assigned(vectorOriginal) and Assigned(vectorOriginal.SelectedShape) and
     (vectorOriginal.SelectedShape is TCurveShape) then
     TCurveShape(vectorOriginal.SelectedShape).SplineStyle := FSplineStyle;
 end;
 
+procedure TForm1.SplineToolbarClick(Sender: TObject);
+var
+  btn: TToolButton;
+  mode: TVectorShapeUsermode;
+begin
+  if Assigned(vectorOriginal) and Assigned(vectorOriginal.SelectedShape) and
+     (vectorOriginal.SelectedShape is TCurveShape) then
+  begin
+    btn := Sender as TToolButton;
+    if btn.Down then
+    begin
+      if btn.ImageIndex = 0 then mode := vsuEdit
+      else if btn.ImageIndex = 1 then mode := vsuCurveSetAuto
+      else if btn.ImageIndex = 2 then mode := vsuCurveSetCurve
+      else if btn.ImageIndex = 3 then mode := vsuCurveSetAngle;
+      vectorOriginal.SelectedShape.Usermode := mode;
+    end;
+  end;
+end;
+
 procedure TForm1.UpdateViewCursor(ACursor: TOriginalEditorCursor);
 begin
   case ACursor of
@@ -1197,6 +1249,7 @@ begin
     oecMoveSW: BGRAVirtualScreen1.Cursor := crSizeSW;
     oecMoveNW: BGRAVirtualScreen1.Cursor := crSizeNW;
     oecMoveSE: BGRAVirtualScreen1.Cursor := crSizeSE;
+    oecHandPoint: BGRAVirtualScreen1.Cursor := crHandPoint;
   end;
 end;
 
@@ -1281,19 +1334,22 @@ begin
 end;
 
 procedure TForm1.UpdateToolbarFromShape(AShape: TVectorShape);
-const ControlMargin = 8;
+const ControlMargin = 6;
 var
   f: TVectorShapeFields;
   showSplineStyle: boolean;
   nextControlPos: TPoint;
   s: TSplineStyle;
   texSource: TBGRABitmap;
+  btn: TToolButton;
+  mode: TVectorShapeUsermode;
 begin
   RemoveExtendedStyleControls;
 
   if AShape <> nil then
   begin
     FUpdatingFromShape := true;
+    mode := AShape.Usermode;
     f := AShape.Fields;
     if vsfPenColor in f then penColor := AShape.PenColor;
     if vsfPenWidth in f then penWidth:= AShape.PenWidth;
@@ -1340,6 +1396,7 @@ begin
     FUpdatingFromShape := false;
   end else
   begin
+    mode := vsuEdit;
     if IsCreateShapeTool(currentTool) then
     begin
       f := PaintToolClass[currentTool].Fields;
@@ -1355,19 +1412,38 @@ begin
   UpDownPenWidth.Enabled := vsfPenWidth in f;
   ButtonPenStyle.Enabled:= vsfPenStyle in f;
 
-  nextControlPos := Point(ControlMargin,ShapeBackColor.Top);
+  nextControlPos := Point(ControlMargin,4);
   if showSplineStyle then
   begin
-    FComboboxSplineStyle := TComboBox.Create(nil);
-    FComboboxSplineStyle.Style := csDropDownList;
+    FSplineToolbar := TToolBar.Create(nil);
+    FSplineToolbar.Align := alNone;
+    FSplineToolbar.Left := nextControlPos.X;
+    FSplineToolbar.Top := nextControlPos.Y;
+    FSplineToolbar.Height := 25;
+    FSplineToolbar.Width := 120;
+    FSplineToolbar.ShowHint:= true;
+    FSplineToolbar.ShowCaptions:= false;
+    FSplineToolbar.Images := CurveImageList;
+    AddToolbarCheckButton(FSplineToolbar, 'Move spline points', 0, @SplineToolbarClick, mode in [vsuEdit, vsuCreate]);
+    AddToolbarCheckButton(FSplineToolbar, 'Set to autodetect angle (A)', 1, @SplineToolbarClick, mode = vsuCurveSetAuto);
+    AddToolbarCheckButton(FSplineToolbar, 'Set to curve (S)', 2, @SplineToolbarClick, mode = vsuCurveSetCurve);
+    AddToolbarCheckButton(FSplineToolbar, 'Set to angle (X)', 3, @SplineToolbarClick, mode = vsuCurveSetAngle);
+    PanelExtendedStyle.InsertControl(FSplineToolbar);
+
+    FComboboxSplineStyle := TBCButton.Create(nil);
+    FComboboxSplineStyle.Style := bbtButton;
     FComboboxSplineStyle.Left := nextControlPos.X;
-    FComboboxSplineStyle.Top := nextControlPos.Y;
-    for s := low(SplineStyleToStr) to high(SplineStyleToStr) do
-      FComboboxSplineStyle.Items.Add(SplineStyleToStr[s]);
-    FComboboxSplineStyle.ItemIndex := ord(splineStyle);
+    FComboboxSplineStyle.Top := nextControlPos.Y + FSplineToolbar.Height;
+    FComboboxSplineStyle.Caption:= SplineStyleToStr[splineStyle];
     FComboboxSplineStyle.Width := 120;
-    FComboboxSplineStyle.OnChange:= @ComboBoxSplineStyleChange;
+    FComboboxSplineStyle.Height := ButtonPenStyle.Height;
+    FComboboxSplineStyle.OnClick:=@ComboBoxSplineStyleClick;
+    FComboboxSplineStyle.StateNormal.Assign(ButtonPenStyle.StateNormal);
+    FComboboxSplineStyle.StateHover.Assign(ButtonPenStyle.StateHover);
+    FComboboxSplineStyle.StateClicked.Assign(ButtonPenStyle.StateClicked);
+    FComboboxSplineStyle.Rounding.Assign(ButtonPenStyle.Rounding);
     PanelExtendedStyle.InsertControl(FComboboxSplineStyle);
+
     nextControlPos.X := FComboboxSplineStyle.Left + FComboboxSplineStyle.Width + ControlMargin;
   end;
 end;
@@ -1476,6 +1552,11 @@ begin
     PanelExtendedStyle.RemoveControl(FComboboxSplineStyle);
     FreeAndNil(FComboboxSplineStyle);
   end;
+  if Assigned(FSplineToolbar) then
+  begin
+    PanelExtendedStyle.RemoveControl(FSplineToolbar);
+    FreeAndNil(FSplineToolbar);
+  end;
 end;
 
 procedure TForm1.UpdateBackComponentsVisibility;

+ 2 - 1
vectoredit/uvectororiginal.pas

@@ -22,7 +22,8 @@ type
   TRenderBoundsOptions = set of TRenderBoundsOption;
   TVectorShapeField = (vsfPenColor, vsfPenWidth, vsfPenStyle, vsfJoinStyle, vsfBackFill);
   TVectorShapeFields = set of TVectorShapeField;
-  TVectorShapeUsermode = (vsuEdit, vsuCreate, vsuEditBackFill);
+  TVectorShapeUsermode = (vsuEdit, vsuCreate, vsuEditBackFill,
+                          vsuCurveSetAuto, vsuCurveSetCurve, vsuCurveSetAngle);
   TVectorShapeUsermodes = set of TVectorShapeUsermode;
 
   { TVectorShape }

+ 206 - 32
vectoredit/uvectorshapes.pas

@@ -85,6 +85,7 @@ type
     FPoints: array of record
                coord: TPointF;
                editorIndex: integer;
+               data: pointer;
              end;
     FCenterPoint: TPointF;
     FCenterPointEditorIndex: integer;
@@ -101,11 +102,17 @@ type
     procedure SetClosed(AValue: boolean); virtual;
     function PointsEqual(const APoint1, APoint2: TPointF): boolean;
     procedure OnHoverPoint({%H-}ASender: TObject; APointIndex: integer); virtual;
+    procedure OnClickPoint({%H-}ASender: TObject; APointIndex: integer; {%H-}AShift: TShiftState); virtual;
+    procedure DoClickPoint(APointIndex: integer; {%H-}AShift: TShiftState); virtual;
+    function CanMovePoints: boolean; virtual;
     procedure InsertPointAuto;
   public
     constructor Create(AContainer: TVectorOriginal); override;
-    procedure AddPoint(const APoint: TPointF);
+    procedure Clear;
+    destructor Destroy; override;
+    function AddPoint(const APoint: TPointF): integer;
     function RemovePoint(AIndex: integer): boolean;
+    procedure RemovePointRange(AFromIndex, AToIndexPlus1: integer);
     procedure InsertPoint(AIndex: integer; APoint: TPointF);
     procedure MouseMove({%H-}Shift: TShiftState; X, Y: single; var {%H-}ACursor: TOriginalEditorCursor; var AHandled: boolean); override;
     procedure MouseDown(RightButton: boolean; {%H-}Shift: TShiftState; {%H-}X, {%H-}Y: single; var {%H-}ACursor: TOriginalEditorCursor; var AHandled: boolean); override;
@@ -141,15 +148,22 @@ type
   TCurveShape = class(TPolylineShape)
   private
     FSplineStyle: TSplineStyle;
+    function GetCurveMode(AIndex: integer): TEasyBezierCurveMode;
+    procedure SetCurveMode(AIndex: integer; AValue: TEasyBezierCurveMode);
     procedure SetSplineStyle(AValue: TSplineStyle);
   protected
     function GetCurve(AMatrix: TAffineMatrix): ArrayOfTPointF; override;
+    function CanMovePoints: boolean; override;
+    procedure DoClickPoint(APointIndex: integer; {%H-}AShift: TShiftState); override;
   public
+    class function Usermodes: TVectorShapeUsermodes; override;
     constructor Create(AContainer: TVectorOriginal); override;
+    procedure KeyPress(UTF8Key: string; var AHandled: boolean); override;
     procedure LoadFromStorage(AStorage: TBGRACustomOriginalStorage); override;
     procedure SaveToStorage(AStorage: TBGRACustomOriginalStorage); override;
     class function StorageClassName: RawByteString; override;
     property SplineStyle: TSplineStyle read FSplineStyle write SetSplineStyle;
+    property CurveMode[AIndex: integer]: TEasyBezierCurveMode read GetCurveMode write SetCurveMode;
   end;
 
 implementation
@@ -840,8 +854,9 @@ begin
   if AIndex = length(FPoints) then
   begin
     setlength(FPoints, length(FPoints)+1);
-    FPoints[high(FPoints)].coord := AValue;
-    FPoints[high(FPoints)].editorIndex := -1;
+    FPoints[AIndex].coord := AValue;
+    FPoints[AIndex].editorIndex := -1;
+    FPoints[AIndex].data := nil;
   end
   else
     FPoints[AIndex].coord := AValue;
@@ -878,7 +893,7 @@ var
   i: Integer;
 begin
   FCurPoint:= -1;
-  for i:= 0 to high(FPoints) do
+  for i:= 0 to PointCount-1 do
     if FPoints[i].editorIndex = APointIndex then
     begin
       FCurPoint:= i;
@@ -907,15 +922,11 @@ var
   add: Boolean;
 begin
   add := AValue = vsuCreate;
-  if add and (length(FPoints) = 0) then exit;
+  if add and (PointCount = 0) then exit;
   if FAddingPoint and not add then
   begin
-    if (length(FPoints)>1) and PointsEqual(FPoints[high(FPoints)].coord,FPoints[high(FPoints)-1].coord) then
-    begin
-      BeginUpdate;
-      setlength(FPoints, length(FPoints)-1);
-      EndUpdate;
-    end;
+    if (PointCount>1) and PointsEqual(Points[PointCount-1],Points[PointCount-2]) then
+      RemovePoint(PointCount-1);
     FAddingPoint:= add;
   end else
   if not FAddingPoint and add then
@@ -948,7 +959,7 @@ begin
   FHoverPoint:= -1;
   if APointIndex <> -1 then
   begin
-    for i:= 0 to high(FPoints) do
+    for i:= 0 to PointCount-1 do
       if FPoints[i].editorIndex = APointIndex then
       begin
         FHoverPoint:= i;
@@ -957,6 +968,33 @@ begin
   end;
 end;
 
+procedure TCustomPolypointShape.OnClickPoint(ASender: TObject;
+  APointIndex: integer; AShift: TShiftState);
+var
+  i: Integer;
+begin
+  if APointIndex <> -1 then
+  begin
+    for i:= 0 to PointCount-1 do
+      if FPoints[i].editorIndex = APointIndex then
+      begin
+        DoClickPoint(i, AShift);
+        break;
+      end;
+  end;
+end;
+
+procedure TCustomPolypointShape.DoClickPoint(APointIndex: integer;
+  AShift: TShiftState);
+begin
+  //nothing
+end;
+
+function TCustomPolypointShape.CanMovePoints: boolean;
+begin
+  result := true;
+end;
+
 procedure TCustomPolypointShape.InsertPointAuto;
 var
   bestSegmentIndex, i: Integer;
@@ -1008,22 +1046,54 @@ begin
   FHoverPoint:= -1;
 end;
 
-procedure TCustomPolypointShape.AddPoint(const APoint: TPointF);
+procedure TCustomPolypointShape.Clear;
 begin
-  Points[PointCount] := APoint;
+  RemovePointRange(0, PointCount);
 end;
 
-function TCustomPolypointShape.RemovePoint(AIndex: integer): boolean;
+destructor TCustomPolypointShape.Destroy;
 var
   i: Integer;
+begin
+  for i := 0 to PointCount-1 do
+  begin
+    FreeMem(FPoints[i].data);
+    FPoints[i].data := nil;
+  end;
+  inherited Destroy;
+end;
+
+function TCustomPolypointShape.AddPoint(const APoint: TPointF): integer;
+begin
+  result := PointCount;
+  Points[result] := APoint;
+end;
+
+function TCustomPolypointShape.RemovePoint(AIndex: integer): boolean;
 begin
   if (AIndex < 0) or (AIndex >= PointCount) then exit(false);
+  RemovePointRange(AIndex,AIndex+1);
+  result := true;
+end;
+
+procedure TCustomPolypointShape.RemovePointRange(AFromIndex, AToIndexPlus1: integer);
+var
+  i, delCount: Integer;
+begin
+  if AFromIndex < 0 then AFromIndex:= 0;
+  if AToIndexPlus1 > PointCount then AToIndexPlus1:= PointCount;
+  if AFromIndex >= AToIndexPlus1 then exit;
   BeginUpdate;
-  for i := AIndex to PointCount-2 do
-    FPoints[i] := FPoints[i+1];
-  setlength(FPoints, PointCount-1);
+  for i := AFromIndex to AToIndexPlus1-1 do
+  begin
+    freemem(FPoints[i].data);
+    FPoints[i].data := nil;
+  end;
+  delCount := AToIndexPlus1-AFromIndex;
+  for i := AFromIndex to PointCount-DelCount-1 do
+    FPoints[i] := FPoints[i+delCount];
+  setlength(FPoints, PointCount-delCount);
   EndUpdate;
-  result := true;
 end;
 
 procedure TCustomPolypointShape.InsertPoint(AIndex: integer; APoint: TPointF);
@@ -1037,6 +1107,7 @@ begin
     FPoints[i] := FPoints[i-1];
   FPoints[AIndex].coord := APoint;
   FPoints[AIndex].editorIndex:= -1;
+  FPoints[AIndex].data := nil;
   EndUpdate;
 end;
 
@@ -1046,9 +1117,7 @@ begin
   FMousePos := PointF(X,Y);
   if FAddingPoint then
   begin
-    BeginUpdate;
-    FPoints[high(FPoints)].coord := FMousePos;
-    EndUpdate;
+    Points[PointCount-1] := FMousePos;
     AHandled:= true;
   end;
 end;
@@ -1061,8 +1130,8 @@ begin
   begin
     if not RightButton then
     begin
-      if (length(FPoints)>1) and not PointsEqual(FPoints[high(FPoints)].coord,FPoints[high(FPoints)-1].coord) then
-        AddPoint(FPoints[high(FPoints)].coord);
+      if (PointCount>1) and not PointsEqual(Points[PointCount-1],Points[PointCount-2]) then
+        AddPoint(Points[PointCount-1]);
     end else
       Usermode := vsuEdit;
     AHandled:= true;
@@ -1078,7 +1147,7 @@ begin
     begin
       BeginUpdate;
       RemovePoint(FHoverPoint);
-      if (FHoverPoint < PointCount) and IsEmptyPointF(FPoints[FHoverPoint].coord) then RemovePoint(FHoverPoint);
+      if (FHoverPoint < PointCount) and IsEmptyPointF(Points[FHoverPoint]) then RemovePoint(FHoverPoint);
       EndUpdate;
       if PointCount = 0 then self.Remove;
     end;
@@ -1087,11 +1156,9 @@ begin
   if (Key = skBackspace) and FAddingPoint then
   begin
     If PointCount <= 2 then self.Remove else
-    If isEmptyPointF(FPoints[PointCount-3].coord) then
+    If isEmptyPointF(Points[PointCount-3]) then
     begin
-      BeginUpdate;
-      setlength(FPoints, PointCount-3);
-      EndUpdate;
+      RemovePointRange(PointCount-3, PointCount);
       Usermode:= vsuEdit;
     end else
       RemovePoint(PointCount-2);
@@ -1118,6 +1185,7 @@ var
 begin
   BeginUpdate;
   inherited LoadFromStorage(AStorage);
+  Clear;
   x := AStorage.FloatArray['x'];
   y := AStorage.FloatArray['y'];
   setlength(FPoints, max(length(x),length(y)));
@@ -1125,6 +1193,7 @@ begin
   begin
     FPoints[i].coord := PointF(x[i],y[i]);
     FPoints[i].editorIndex := -1;
+    FPoints[i].data := nil;
   end;
   FClosed:= AStorage.Bool['closed'];
   EndUpdate;
@@ -1153,6 +1222,7 @@ var
   i, nb: Integer;
 begin
   AEditor.AddStartMoveHandler(@OnStartMove);
+  AEditor.AddClickPointHandler(@OnClickPoint);
   AEditor.AddHoverPointHandler(@OnHoverPoint);
   nb := 0;
   FCenterPoint := PointF(0,0);
@@ -1167,7 +1237,10 @@ begin
     end
     else
     begin
-      FPoints[i].editorIndex := AEditor.AddPoint(Points[i], @OnMovePoint, false);
+      if CanMovePoints then
+        FPoints[i].editorIndex := AEditor.AddPoint(Points[i], @OnMovePoint, false)
+      else
+        FPoints[i].editorIndex := AEditor.AddFixedPoint(Points[i], false);
       FCenterPoint += Points[i];
       inc(nb);
     end;
@@ -1307,13 +1380,66 @@ begin
   EndUpdate;
 end;
 
+function TCurveShape.GetCurveMode(AIndex: integer): TEasyBezierCurveMode;
+begin
+  if (AIndex < 0) or (AIndex >= PointCount) then exit(cmCurve);
+  if Assigned(FPoints[AIndex].data) then
+    result := TEasyBezierCurveMode(FPoints[AIndex].data^)
+  else
+    result := cmAuto;
+end;
+
+procedure TCurveShape.SetCurveMode(AIndex: integer; AValue: TEasyBezierCurveMode);
+begin
+  if (AIndex < 0) or (AIndex >= PointCount) then exit;
+  if CurveMode[AIndex] = AValue then exit;
+  BeginUpdate;
+  if FPoints[AIndex].data = nil then FPoints[AIndex].data := getmem(sizeof(TEasyBezierCurveMode));
+  TEasyBezierCurveMode(FPoints[AIndex].data^) := AValue;
+  EndUpdate
+end;
+
 function TCurveShape.GetCurve(AMatrix: TAffineMatrix): ArrayOfTPointF;
 var
   pts: array of TPointF;
+  cm: array of TEasyBezierCurveMode;
+  i: Integer;
+  eb: TEasyBezierCurve;
 begin
   pts := inherited GetCurve(AMatrix);
-  if Closed then result := ComputeClosedSpline(pts, FSplineStyle)
-  else result := ComputeOpenedSpline(pts, FSplineStyle);
+  if FSplineStyle = ssEasyBezier then
+  begin
+    setlength(cm, PointCount);
+    for i := 0 to PointCount-1 do
+      cm[i] := CurveMode[i];
+    eb := EasyBezierCurve(pts, Closed, cm);
+    result := eb.ToPoints;
+  end else
+  begin
+    if Closed then result := ComputeClosedSpline(pts, FSplineStyle)
+    else result := ComputeOpenedSpline(pts, FSplineStyle);
+  end;
+end;
+
+function TCurveShape.CanMovePoints: boolean;
+begin
+  Result:= Usermode in [vsuCreate,vsuEdit];
+end;
+
+procedure TCurveShape.DoClickPoint(APointIndex: integer; AShift: TShiftState);
+begin
+  case Usermode of
+  vsuCurveSetAuto: CurveMode[APointIndex] := cmAuto;
+  vsuCurveSetCurve: CurveMode[APointIndex] := cmCurve;
+  vsuCurveSetAngle: CurveMode[APointIndex] := cmAngle;
+  else
+    inherited DoClickPoint(APointIndex, AShift);
+  end;
+end;
+
+class function TCurveShape.Usermodes: TVectorShapeUsermodes;
+begin
+  Result:=inherited Usermodes + [vsuCurveSetAuto, vsuCurveSetCurve, vsuCurveSetAngle];
 end;
 
 constructor TCurveShape.Create(AContainer: TVectorOriginal);
@@ -1322,7 +1448,34 @@ begin
   FSplineStyle:= ssEasyBezier;
 end;
 
+procedure TCurveShape.KeyPress(UTF8Key: string; var AHandled: boolean);
+begin
+  if (FHoverPoint >= 0) and (FHoverPoint < PointCount) then
+  begin
+    if (UTF8Key = 'A') or (UTF8Key = 'a') then
+    begin
+      CurveMode[FHoverPoint] := cmAuto;
+      AHandled := true;
+    end else
+    if (UTF8Key = 'S') or (UTF8Key = 's') then
+    begin
+      CurveMode[FHoverPoint] := cmCurve;
+      AHandled:= true;
+    end else
+    if (UTF8Key = 'X') or (UTF8Key = 'x') then
+    begin
+      CurveMode[FHoverPoint] := cmAngle;
+      AHandled:= true;
+    end;
+  end;
+  if not AHandled then
+    inherited KeyPress(UTF8Key, AHandled);
+end;
+
 procedure TCurveShape.LoadFromStorage(AStorage: TBGRACustomOriginalStorage);
+var
+  i: Integer;
+  cm: array of Single;
 begin
   BeginUpdate;
   inherited LoadFromStorage(AStorage);
@@ -1337,11 +1490,25 @@ begin
   else
     {'easy-bezier'} SplineStyle := ssEasyBezier;
   end;
+  if SplineStyle = ssEasyBezier then
+  begin
+    cm := AStorage.FloatArray['curve-mode'];
+    for i := 0 to min(high(cm),PointCount-1) do
+      case round(cm[i]) of
+      1: CurveMode[i] := cmCurve;
+      2: CurveMode[i] := cmAngle;
+      end;
+    if length(cm) < PointCount then
+      for i:= length(cm) to PointCount-1 do
+        CurveMode[i] := cmCurve;
+  end;
   EndUpdate;
 end;
 
 procedure TCurveShape.SaveToStorage(AStorage: TBGRACustomOriginalStorage);
 var s: string;
+  cm: array of single;
+  i: Integer;
 begin
   inherited SaveToStorage(AStorage);
   case SplineStyle of
@@ -1356,6 +1523,13 @@ begin
   else s := '';
   end;
   AStorage.RawString['spline-style'] := s;
+  if SplineStyle = ssEasyBezier then
+  begin
+    setlength(cm, PointCount);
+    for i := 0 to PointCount-1 do
+      cm[i] := ord(CurveMode[i]);
+    AStorage.FloatArray['curve-mode'] := cm;
+  end;
 end;
 
 class function TCurveShape.StorageClassName: RawByteString;