Selaa lähdekoodia

improve polygon editor

highlight hovered point, arrow to move with fine steps
johann 5 vuotta sitten
vanhempi
commit
5ea5f865c3
1 muutettua tiedostoa jossa 131 lisäystä ja 34 poistoa
  1. 131 34
      lazpaintcontrols/lcvectorpolyshapes.pas

+ 131 - 34
lazpaintcontrols/lcvectorpolyshapes.pas

@@ -61,12 +61,16 @@ type
   TCustomPolypointShape = class(TVectorShape)
   private
     FClosed: boolean;
+    function GetHoverPoint: integer;
     function GetLineCap: TPenEndCap;
     function GetPoint(AIndex: integer): TPointF;
     function GetPointCount: integer;
     procedure SetArrowEndKind(AValue: TArrowKind);
     procedure SetArrowSize(AValue: TPointF);
     procedure SetArrowStartKind(AValue: TArrowKind);
+    procedure SetCenterPoint(AValue: TPointF);
+    procedure SetHoverCenter(AValue: boolean);
+    procedure SetHoverPoint(AValue: integer);
     procedure SetLineCap(AValue: TPenEndCap);
     procedure SetPoint(AIndex: integer; AValue: TPointF);
   protected
@@ -77,8 +81,10 @@ type
     FAddingPoint: boolean;
     FMousePos: TPointF;
     FHoverPoint: integer;
+    FHoverCenter: boolean;
     FArrowStartKind,FArrowEndKind: TArrowKind;
     FArrowSize: TPointF;
+    FViewMatrix, FViewMatrixInverse, FGridMatrix: TAffineMatrix;
     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 OnStartMove({%H-}ASender: TObject; APointIndex: integer; {%H-}AShift: TShiftState);
@@ -104,6 +110,7 @@ type
     function RemovePoint(AIndex: integer): boolean;
     procedure RemovePointRange(AFromIndex, AToIndexPlus1: integer);
     procedure InsertPoint(AIndex: integer; APoint: TPointF);
+    function GetPointBounds(AMatrix: TAffineMatrix): TRectF;
     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;
     procedure KeyDown({%H-}Shift: TShiftState; Key: TSpecialKey; var AHandled: boolean); override;
@@ -117,12 +124,13 @@ type
     property Points[AIndex:integer]: TPointF read GetPoint write SetPoint;
     property PointCount: integer read GetPointCount;
     property Closed: boolean read GetClosed write SetClosed;
-    property HoverPoint: integer read FHoverPoint;
+    property HoverPoint: integer read GetHoverPoint write SetHoverPoint;
+    property HoverCenter: boolean read FHoverCenter write SetHoverCenter;
     property ArrowStartKind: TArrowKind read FArrowStartKind write SetArrowStartKind;
     property ArrowEndKind: TArrowKind read FArrowEndKind write SetArrowEndKind;
     property ArrowSize: TPointF read FArrowSize write SetArrowSize;
     property LineCap: TPenEndCap read GetLineCap write SetLineCap;
-    property Center: TPointF read FCenterPoint;
+    property Center: TPointF read FCenterPoint write SetCenterPoint;
   end;
 
   { TPolylineShape }
@@ -450,6 +458,13 @@ begin
   result := Stroker.LineCap;
 end;
 
+function TCustomPolypointShape.GetHoverPoint: integer;
+begin
+  if (FHoverPoint >= 0) and (FHoverPoint < PointCount) and
+     not Points[FHoverPoint].IsEmpty then
+       result := FHoverPoint else result := -1;
+end;
+
 function TCustomPolypointShape.GetPointCount: integer;
 begin
   result:= length(FPoints);
@@ -479,6 +494,47 @@ begin
   EndUpdate;
 end;
 
+procedure TCustomPolypointShape.SetCenterPoint(AValue: TPointF);
+var
+  i: Integer;
+  delta: TPointF;
+begin
+  if FCenterPoint=AValue then Exit;
+
+  BeginUpdate(TCustomPolypointShapeDiff);
+  delta := AValue - FCenterPoint;
+  for i := 0 to PointCount-1 do
+    Points[i] := Points[i]+delta;
+  if vsfBackFill in Fields then
+    BackFill.Transform(AffineMatrixTranslation(delta.x, delta.y));
+  if vsfPenFill in Fields then
+    PenFill.Transform(AffineMatrixTranslation(delta.x, delta.y));
+  FCenterPoint:=AValue;
+  EndUpdate;
+end;
+
+procedure TCustomPolypointShape.SetHoverCenter(AValue: boolean);
+begin
+  if FHoverCenter=AValue then Exit;
+  BeginEditingUpdate;
+  if AValue then FHoverPoint := -1;
+  FHoverCenter:=AValue;
+  EndEditingUpdate;
+end;
+
+procedure TCustomPolypointShape.SetHoverPoint(AValue: integer);
+begin
+  if (AValue < 0) or (AValue >= PointCount) or
+     Points[AValue].IsEmpty then AValue := -1;
+  if AValue <> FHoverPoint then
+  begin
+    BeginEditingUpdate;
+    FHoverPoint := AValue;
+    if AValue <> -1 then FHoverCenter:= false;
+    EndEditingUpdate;
+  end;
+end;
+
 procedure TCustomPolypointShape.SetLineCap(AValue: TPenEndCap);
 begin
   if Stroker.LineCap=AValue then Exit;
@@ -516,26 +572,13 @@ procedure TCustomPolypointShape.OnMovePoint(ASender: TObject; APrevCoord,
   ANewCoord: TPointF; AShift: TShiftState);
 begin
   if FCurPoint = -1 then exit;
-  BeginUpdate(TCustomPolypointShapeDiff);
   Points[FCurPoint] := ANewCoord;
-  EndUpdate;
 end;
 
 procedure TCustomPolypointShape.OnMoveCenterPoint(ASender: TObject; APrevCoord,
   ANewCoord: TPointF; AShift: TShiftState);
-var
-  i: Integer;
-  delta: TPointF;
 begin
-  BeginUpdate(TCustomPolypointShapeDiff);
-  delta := ANewCoord - APrevCoord;
-  for i := 0 to PointCount-1 do
-    Points[i] := Points[i]+delta;
-  if vsfBackFill in Fields then
-    BackFill.Transform(AffineMatrixTranslation(delta.x, delta.y));
-  if vsfPenFill in Fields then
-    PenFill.Transform(AffineMatrixTranslation(delta.x, delta.y));
-  EndUpdate;
+  Center := ANewCoord;
 end;
 
 procedure TCustomPolypointShape.OnStartMove(ASender: TObject; APointIndex: integer;
@@ -610,18 +653,25 @@ end;
 procedure TCustomPolypointShape.OnHoverPoint(ASender: TObject;
   APointIndex: integer);
 var
-  i: Integer;
+  i, newHoverPoint: Integer;
 begin
-  FHoverPoint:= -1;
+  if APointIndex = FCenterPointEditorIndex then
+  begin
+    HoverCenter := true;
+    exit;
+  end;
+  newHoverPoint:= -1;
   if APointIndex <> -1 then
   begin
     for i:= 0 to PointCount-1 do
       if FPoints[i].editorIndex = APointIndex then
       begin
-        FHoverPoint:= i;
+        newHoverPoint:= i;
         break;
       end;
   end;
+  HoverPoint := newHoverPoint;
+  HoverCenter:= false;
 end;
 
 procedure TCustomPolypointShape.OnClickPoint(ASender: TObject;
@@ -749,7 +799,7 @@ begin
   if (bestPointIndex <> -1) and ((bestSegmentIndex = -1) or (bestPointDist < bestSegmentDist)) then
   begin
     InsertPoint(bestPointIndex+1, FMousePos);
-    FHoverPoint := bestPointIndex+1;
+    HoverPoint := bestPointIndex+1;
   end else
   if bestSegmentIndex <> -1 then
   begin
@@ -757,7 +807,7 @@ begin
       InsertPoint(bestSegmentIndex+1, bestProjection)
     else
       InsertPoint(bestSegmentIndex+1, FMousePos);
-    FHoverPoint:= bestSegmentIndex+1;
+    HoverPoint:= bestSegmentIndex+1;
   end;
 end;
 
@@ -835,6 +885,8 @@ begin
   for i := AFromIndex to PointCount-DelCount-1 do
     FPoints[i] := FPoints[i+delCount];
   setlength(FPoints, PointCount-delCount);
+  if (HoverPoint >= AFromIndex) and (HoverPoint < AToIndexPlus1) then HoverPoint := -1
+  else if (HoverPoint <> -1) and (HoverPoint >= AToIndexPlus1) then HoverPoint := HoverPoint - delCount;
   EndUpdate;
 end;
 
@@ -850,9 +902,15 @@ begin
   FPoints[AIndex].coord := APoint;
   FPoints[AIndex].editorIndex:= -1;
   FPoints[AIndex].data := 0;
+  if (HoverPoint <> -1) and (HoverPoint >= AIndex) then HoverPoint := HoverPoint + 1;
   EndUpdate;
 end;
 
+function TCustomPolypointShape.GetPointBounds(AMatrix: TAffineMatrix): TRectF;
+begin
+  result := GetPointsBoundsF(GetCurve(AMatrix));
+end;
+
 procedure TCustomPolypointShape.MouseMove(Shift: TShiftState; X, Y: single; var
   ACursor: TOriginalEditorCursor; var AHandled: boolean);
 begin
@@ -907,21 +965,23 @@ end;
 procedure TCustomPolypointShape.KeyDown(Shift: TShiftState; Key: TSpecialKey;
   var AHandled: boolean);
 var
-  nb: Integer;
+  nb, idx: Integer;
+  dx, dy, d: TPointF;
 begin
-  if (Key = skDelete) and (FAddingPoint or ((FHoverPoint >= 0) and (FHoverPoint < PointCount))) then
+  if (Key = skDelete) and (FAddingPoint or (HoverPoint <> -1)) then
   begin
-    if (FHoverPoint >= 0) and (FHoverPoint < PointCount) then
+    if (HoverPoint <> -1) then
     begin
       BeginUpdate(TCustomPolypointShapeDiff);
-      RemovePoint(FHoverPoint);
-      if ((FHoverPoint = PointCount) or IsEmptyPointF(Points[FHoverPoint])) and
-         ((FHoverPoint = 0) or IsEmptyPointF(Points[FHoverPoint-1])) then
+      idx := HoverPoint;
+      RemovePoint(idx);
+      if ((idx = PointCount) or IsEmptyPointF(Points[idx])) and
+         ((idx = 0) or IsEmptyPointF(Points[idx-1])) then
       begin
-        if FHoverPoint < PointCount then
-          RemovePoint(FHoverPoint)
-        else if FHoverPoint > 0 then
-          RemovePoint(FHoverPoint-1);
+        if idx < PointCount then
+          RemovePoint(idx)
+        else if idx > 0 then
+          RemovePoint(idx-1);
       end;
       EndUpdate;
       if PointCount = 0 then self.Remove;
@@ -944,7 +1004,34 @@ begin
     end;
     AHandled:= true;
   end else
-  if (Key = skInsert) then InsertPointAuto(Shift) else
+  if (Key = skInsert) then
+  begin
+    InsertPointAuto(Shift);
+    AHandled := true;
+  end else
+  if (Key in [skLeft,skUp,skRight,skDown]) and ((HoverPoint <> -1) or HoverCenter) then
+  begin
+    if ssCtrl in Shift then
+    begin
+      dx := PointF(FGridMatrix[1,1], FGridMatrix[2,1]);
+      dy := PointF(FGridMatrix[1,2], FGridMatrix[2,2]);
+    end else
+    begin
+      dx := PointF(FViewMatrixInverse[1,1], FViewMatrixInverse[2,1]);
+      dy := PointF(FViewMatrixInverse[1,2], FViewMatrixInverse[2,2]);
+    end;
+    case Key of
+    skLeft: d := -dx;
+    skRight: d := dx;
+    skUp: d := -dy;
+    skDown: d := dy;
+    end;
+    if HoverCenter then
+      Center := Center + d
+    else
+      Points[HoverPoint] := Points[HoverPoint] + d;
+    AHandled := true;
+  end else
     inherited KeyDown(Shift, Key, AHandled);
 end;
 
@@ -1015,6 +1102,10 @@ procedure TCustomPolypointShape.ConfigureCustomEditor(AEditor: TBGRAOriginalEdit
 var
   i, nbTotal: Integer;
 begin
+  FViewMatrix := AEditor.Matrix;
+  FViewMatrixInverse := AffineMatrixInverse(FViewMatrix);
+  FGridMatrix := AEditor.GridMatrix;
+
   AEditor.AddStartMoveHandler(@OnStartMove);
   AEditor.AddClickPointHandler(@OnClickPoint);
   AEditor.AddHoverPointHandler(@OnHoverPoint);
@@ -1037,6 +1128,8 @@ begin
       else
         FPoints[i].editorIndex := AEditor.AddFixedPoint(Points[i], false);
       FCenterPoint += Points[i];
+      if i = HoverPoint then
+        AEditor.PointHighlighted[FPoints[i].editorIndex] := true;
       inc(nbTotal);
     end;
 
@@ -1045,7 +1138,11 @@ begin
     else FCenterPoint := EmptyPointF;
 
   if (FAddingPoint and (nbTotal > 2)) or (not FAddingPoint and (nbTotal > 1)) then
+  begin
     FCenterPointEditorIndex := AEditor.AddPoint(FCenterPoint, @OnMoveCenterPoint, true);
+    AEditor.PointHighlighted[FCenterPointEditorIndex] := HoverCenter;
+  end else
+    FCenterPointEditorIndex := -1;
 end;
 
 procedure TCustomPolypointShape.Transform(const AMatrix: TAffineMatrix);
@@ -1413,8 +1510,8 @@ procedure TCurveShape.KeyPress(UTF8Key: string; var AHandled: boolean);
 var
   targetPoint: Integer;
 begin
-  if FHoverPoint<>-1 then
-    targetPoint := FHoverPoint
+  if HoverPoint<>-1 then
+    targetPoint := HoverPoint
   else if FAddingPoint and (PointCount > 1) then
     targetPoint := PointCount-2
   else