瀏覽代碼

more permissive selection of shape

circular17 6 年之前
父節點
當前提交
7ad349281e

+ 3 - 1
lazpaint/tools/utoolvectorial.pas

@@ -1112,6 +1112,7 @@ var
   handled: boolean;
   handled: boolean;
   m: TAffineMatrix;
   m: TAffineMatrix;
   ptView, ptSel: TPointF;
   ptView, ptSel: TPointF;
+  zoom: Single;
 begin
 begin
   Result:= EmptyRect;
   Result:= EmptyRect;
   if FLeftButton or FRightButton then
   if FLeftButton or FRightButton then
@@ -1173,9 +1174,10 @@ begin
       esmShape, esmNoShape:
       esmShape, esmNoShape:
         begin
         begin
           m := AffineMatrixInverse(Manager.Image.LayerOriginalMatrix[Manager.Image.CurrentLayerIndex]);
           m := AffineMatrixInverse(Manager.Image.LayerOriginalMatrix[Manager.Image.CurrentLayerIndex]);
+          zoom := (VectLen(m[1,1],m[2,1])+VectLen(m[1,2],m[2,2]))/2/Manager.Image.ZoomFactor;
           BindOriginalEvent(true);
           BindOriginalEvent(true);
           try
           try
-            GetVectorOriginal.MouseClick(m*FLastPos);
+            GetVectorOriginal.MouseClick(m*FLastPos, DoScaleX(PointSize, OriginalDPI)*zoom);
           finally
           finally
             BindOriginalEvent(false);
             BindOriginalEvent(false);
           end;
           end;

+ 21 - 3
lazpaintcontrols/lcvectororiginal.pas

@@ -180,6 +180,7 @@ type
     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; virtual;
     function ComputeStroke(APoints: ArrayOfTPointF; AClosed: boolean; AStrokeMatrix: TAffineMatrix): ArrayOfTPointF; virtual;
+    function ComputeStrokeEnvelope(APoints: ArrayOfTPointF; AClosed: boolean; AWidth: single): ArrayOfTPointF; virtual;
     function GetStroker: TBGRAPenStroker;
     function GetStroker: TBGRAPenStroker;
     property Stroker: TBGRAPenStroker read GetStroker;
     property Stroker: TBGRAPenStroker read GetStroker;
     procedure FillChange({%H-}ASender: TObject; var ADiff: TCustomVectorialFillDiff); virtual;
     procedure FillChange({%H-}ASender: TObject; var ADiff: TCustomVectorialFillDiff); virtual;
@@ -202,7 +203,8 @@ type
     procedure Render(ADest: TBGRABitmap; ARenderOffset: TPoint; AMatrix: TAffineMatrix; ADraft: boolean); virtual;
     procedure Render(ADest: TBGRABitmap; ARenderOffset: TPoint; AMatrix: TAffineMatrix; ADraft: boolean); virtual;
     function GetRenderBounds(ADestRect: TRect; AMatrix: TAffineMatrix; AOptions: TRenderBoundsOptions = []): TRectF; virtual; abstract;
     function GetRenderBounds(ADestRect: TRect; AMatrix: TAffineMatrix; AOptions: TRenderBoundsOptions = []): TRectF; virtual; abstract;
     function SuggestGradientBox(AMatrix: TAffineMatrix): TAffineBox; virtual;
     function SuggestGradientBox(AMatrix: TAffineMatrix): TAffineBox; virtual;
-    function PointInShape(APoint: TPointF): boolean; virtual; abstract;
+    function PointInShape(APoint: TPointF): boolean; overload; virtual; abstract;
+    function PointInShape(APoint: TPointF; ARadius: single): boolean; overload; virtual; abstract;
     procedure ConfigureCustomEditor(AEditor: TBGRAOriginalEditor); virtual; abstract;
     procedure ConfigureCustomEditor(AEditor: TBGRAOriginalEditor); virtual; abstract;
     procedure ConfigureEditor(AEditor: TBGRAOriginalEditor); virtual;
     procedure ConfigureEditor(AEditor: TBGRAOriginalEditor); virtual;
     procedure LoadFromStorage(AStorage: TBGRACustomOriginalStorage); virtual;
     procedure LoadFromStorage(AStorage: TBGRACustomOriginalStorage); virtual;
@@ -362,7 +364,7 @@ type
     procedure SelectShape(AShape: TVectorShape); overload;
     procedure SelectShape(AShape: TVectorShape); overload;
     procedure DeselectShape;
     procedure DeselectShape;
     function GetShapesCost: integer;
     function GetShapesCost: integer;
-    procedure MouseClick(APoint: TPointF);
+    procedure MouseClick(APoint: TPointF; ARadius: single);
     procedure Render(ADest: TBGRABitmap; ARenderOffset: TPoint; AMatrix: TAffineMatrix; ADraft: boolean); override;
     procedure Render(ADest: TBGRABitmap; ARenderOffset: TPoint; AMatrix: TAffineMatrix; ADraft: boolean); override;
     procedure ConfigureEditor(AEditor: TBGRAOriginalEditor); override;
     procedure ConfigureEditor(AEditor: TBGRAOriginalEditor); override;
     function CreateEditor: TBGRAOriginalEditor; override;
     function CreateEditor: TBGRAOriginalEditor; override;
@@ -1658,6 +1660,16 @@ begin
     result := Stroker.ComputePolyline(APoints, PenWidth, PenColor);
     result := Stroker.ComputePolyline(APoints, PenWidth, PenColor);
 end;
 end;
 
 
+function TVectorShape.ComputeStrokeEnvelope(APoints: ArrayOfTPointF;
+  AClosed: boolean; AWidth: single): ArrayOfTPointF;
+var
+  opt: TBGRAPolyLineOptions;
+begin
+  opt := [];
+  if AClosed then include(opt, plCycle);
+  result := ComputeWidePolyPolylinePoints(APoints, AWidth, BGRABlack, pecRound, pjsMiter, SolidPenStyle, opt);
+end;
+
 function TVectorShape.GetStroker: TBGRAPenStroker;
 function TVectorShape.GetStroker: TBGRAPenStroker;
 begin
 begin
   if FStroker = nil then
   if FStroker = nil then
@@ -2611,7 +2623,7 @@ begin
     inc(result, Shape[i].GetGenericCost);
     inc(result, Shape[i].GetGenericCost);
 end;
 end;
 
 
-procedure TVectorOriginal.MouseClick(APoint: TPointF);
+procedure TVectorOriginal.MouseClick(APoint: TPointF; ARadius: single);
 var
 var
   i: LongInt;
   i: LongInt;
 begin
 begin
@@ -2621,6 +2633,12 @@ begin
       SelectShape(i);
       SelectShape(i);
       exit;
       exit;
     end;
     end;
+  for i:= FShapes.Count-1 downto 0 do
+    if FShapes[i].PointInShape(APoint, ARadius) then
+    begin
+      SelectShape(i);
+      exit;
+    end;
   DeselectShape;
   DeselectShape;
 end;
 end;
 
 

+ 12 - 1
lazpaintcontrols/lcvectorpolyshapes.pas

@@ -130,7 +130,8 @@ type
     class function Fields: TVectorShapeFields; override;
     class function Fields: TVectorShapeFields; override;
     procedure Render(ADest: TBGRABitmap; AMatrix: TAffineMatrix; ADraft: boolean); override;
     procedure Render(ADest: TBGRABitmap; AMatrix: TAffineMatrix; ADraft: boolean); override;
     function GetRenderBounds({%H-}ADestRect: TRect; AMatrix: TAffineMatrix; AOptions: TRenderBoundsOptions = []): TRectF; override;
     function GetRenderBounds({%H-}ADestRect: TRect; AMatrix: TAffineMatrix; AOptions: TRenderBoundsOptions = []): TRectF; override;
-    function PointInShape(APoint: TPointF): boolean; override;
+    function PointInShape(APoint: TPointF): boolean; overload; override;
+    function PointInShape(APoint: TPointF; ARadius: single): boolean; overload; override;
     function GetIsSlow(const {%H-}AMatrix: TAffineMatrix): boolean; override;
     function GetIsSlow(const {%H-}AMatrix: TAffineMatrix): boolean; override;
     class function StorageClassName: RawByteString; override;
     class function StorageClassName: RawByteString; override;
   end;
   end;
@@ -1066,6 +1067,16 @@ begin
   result := false;
   result := false;
 end;
 end;
 
 
+function TPolylineShape.PointInShape(APoint: TPointF; ARadius: single): boolean;
+var
+  pts: ArrayOfTPointF;
+begin
+  if not BackVisible and not PenVisible then exit(false);
+  pts := GetCurve(AffineMatrixIdentity);
+  pts := ComputeStrokeEnvelope(pts, Closed, ARadius*2);
+  result := IsPointInPolygon(pts, APoint, true);
+end;
+
 function TPolylineShape.GetIsSlow(const AMatrix: TAffineMatrix): boolean;
 function TPolylineShape.GetIsSlow(const AMatrix: TAffineMatrix): boolean;
 begin
 begin
   Result:= PointCount > 40;
   Result:= PointCount > 40;

+ 62 - 17
lazpaintcontrols/lcvectorrectshapes.pas

@@ -103,7 +103,8 @@ type
     class function Fields: TVectorShapeFields; override;
     class function Fields: TVectorShapeFields; override;
     procedure Render(ADest: TBGRABitmap; AMatrix: TAffineMatrix; ADraft: boolean); override;
     procedure Render(ADest: TBGRABitmap; AMatrix: TAffineMatrix; ADraft: boolean); override;
     function GetRenderBounds({%H-}ADestRect: TRect; AMatrix: TAffineMatrix; AOptions: TRenderBoundsOptions = []): TRectF; override;
     function GetRenderBounds({%H-}ADestRect: TRect; AMatrix: TAffineMatrix; AOptions: TRenderBoundsOptions = []): TRectF; override;
-    function PointInShape(APoint: TPointF): boolean; override;
+    function PointInShape(APoint: TPointF): boolean; overload; override;
+    function PointInShape(APoint: TPointF; ARadius: single): boolean; overload; override;
     function GetIsSlow(const AMatrix: TAffineMatrix): boolean; override;
     function GetIsSlow(const AMatrix: TAffineMatrix): boolean; override;
     class function StorageClassName: RawByteString; override;
     class function StorageClassName: RawByteString; override;
   end;
   end;
@@ -121,7 +122,8 @@ type
     function GetAlignBounds(const {%H-}ALayoutRect: TRect; const AMatrix: TAffineMatrix): TRectF; override;
     function GetAlignBounds(const {%H-}ALayoutRect: TRect; const AMatrix: TAffineMatrix): TRectF; override;
     procedure Render(ADest: TBGRABitmap; AMatrix: TAffineMatrix; ADraft: boolean); override;
     procedure Render(ADest: TBGRABitmap; AMatrix: TAffineMatrix; ADraft: boolean); override;
     function GetRenderBounds({%H-}ADestRect: TRect; AMatrix: TAffineMatrix; AOptions: TRenderBoundsOptions = []): TRectF; override;
     function GetRenderBounds({%H-}ADestRect: TRect; AMatrix: TAffineMatrix; AOptions: TRenderBoundsOptions = []): TRectF; override;
-    function PointInShape(APoint: TPointF): boolean; override;
+    function PointInShape(APoint: TPointF): boolean; overload; override;
+    function PointInShape(APoint: TPointF; ARadius: single): boolean; overload; override;
     function GetIsSlow(const AMatrix: TAffineMatrix): boolean; override;
     function GetIsSlow(const AMatrix: TAffineMatrix): boolean; override;
     class function StorageClassName: RawByteString; override;
     class function StorageClassName: RawByteString; override;
   end;
   end;
@@ -170,6 +172,7 @@ type
     procedure SetShapeAltitudePercent(AValue: single);
     procedure SetShapeAltitudePercent(AValue: single);
     procedure SetShapeKind(AValue: TPhongShapeKind);
     procedure SetShapeKind(AValue: TPhongShapeKind);
     function BackVisible: boolean;
     function BackVisible: boolean;
+    function GetEnvelope: ArrayOfTPointF;
   protected
   protected
     function AllowShearTransform: boolean; override;
     function AllowShearTransform: boolean; override;
   public
   public
@@ -185,7 +188,8 @@ type
     procedure SaveToStorage(AStorage: TBGRACustomOriginalStorage); override;
     procedure SaveToStorage(AStorage: TBGRACustomOriginalStorage); override;
     procedure Render(ADest: TBGRABitmap; AMatrix: TAffineMatrix; ADraft: boolean); override;
     procedure Render(ADest: TBGRABitmap; AMatrix: TAffineMatrix; ADraft: boolean); override;
     function GetRenderBounds({%H-}ADestRect: TRect; AMatrix: TAffineMatrix; AOptions: TRenderBoundsOptions = []): TRectF; override;
     function GetRenderBounds({%H-}ADestRect: TRect; AMatrix: TAffineMatrix; AOptions: TRenderBoundsOptions = []): TRectF; override;
-    function PointInShape(APoint: TPointF): boolean; override;
+    function PointInShape(APoint: TPointF): boolean; overload; override;
+    function PointInShape(APoint: TPointF; ARadius: single): boolean; overload; override;
     function GetIsSlow(const AMatrix: TAffineMatrix): boolean; override;
     function GetIsSlow(const AMatrix: TAffineMatrix): boolean; override;
     function GetGenericCost: integer; override;
     function GetGenericCost: integer; override;
     procedure Transform(const AMatrix: TAffineMatrix); override;
     procedure Transform(const AMatrix: TAffineMatrix); override;
@@ -1091,6 +1095,20 @@ begin
     result := false;
     result := false;
 end;
 end;
 
 
+function TRectShape.PointInShape(APoint: TPointF; ARadius: single): boolean;
+var
+  pts: ArrayOfTPointF;
+  box: TAffineBox;
+begin
+  if PenVisible or BackVisible then
+  begin
+    box := GetAffineBox(AffineMatrixIdentity, true);
+    pts := ComputeStrokeEnvelope(box.AsPolygon, true, ARadius*2);
+    result:= IsPointInPolygon(pts, APoint, true);
+  end
+  else result := false;
+end;
+
 class function TRectShape.StorageClassName: RawByteString;
 class function TRectShape.StorageClassName: RawByteString;
 begin
 begin
   result := 'rect';
   result := 'rect';
@@ -1323,6 +1341,19 @@ begin
     result := false;
     result := false;
 end;
 end;
 
 
+function TEllipseShape.PointInShape(APoint: TPointF; ARadius: single): boolean;
+var
+  pts: ArrayOfTPointF;
+begin
+  if PenVisible or BackVisible then
+  begin
+    pts := ComputeEllipse(FOrigin, FXAxis, FYAxis);
+    pts := ComputeStrokeEnvelope(pts, true, ARadius*2);
+    result:= IsPointInPolygon(pts, APoint, true);
+  end else
+    result := false;
+end;
+
 function TEllipseShape.GetIsSlow(const AMatrix: TAffineMatrix): boolean;
 function TEllipseShape.GetIsSlow(const AMatrix: TAffineMatrix): boolean;
 var
 var
   ab: TAffineBox;
   ab: TAffineBox;
@@ -1397,6 +1428,21 @@ begin
   result := not BackFill.IsFullyTransparent;
   result := not BackFill.IsFullyTransparent;
 end;
 end;
 
 
+function TPhongShape.GetEnvelope: ArrayOfTPointF;
+var
+  box: TAffineBox;
+begin
+  case ShapeKind of
+    pskHalfSphere, pskConeTop: result := ComputeEllipse(FOrigin, FXAxis, FYAxis);
+    pskConeSide: result := PointsF([FOrigin - (FYAxis-FOrigin), FYAxis + (FXAxis-FOrigin), FYAxis - (FXAxis-FOrigin)]);
+  else
+    begin
+      box := GetAffineBox(AffineMatrixIdentity, true);
+      result := box.AsPolygon;
+    end;
+  end;
+end;
+
 function TPhongShape.AllowShearTransform: boolean;
 function TPhongShape.AllowShearTransform: boolean;
 begin
 begin
   Result:= false;
   Result:= false;
@@ -1691,24 +1737,23 @@ end;
 
 
 function TPhongShape.PointInShape(APoint: TPointF): boolean;
 function TPhongShape.PointInShape(APoint: TPointF): boolean;
 var
 var
-  box: TAffineBox;
   pts: ArrayOfTPointF;
   pts: ArrayOfTPointF;
 begin
 begin
   if not BackVisible then exit(false);
   if not BackVisible then exit(false);
-  if ShapeKind in [pskHalfSphere, pskConeTop] then
-  begin
-    pts := ComputeEllipse(FOrigin, FXAxis, FYAxis);
-    result := IsPointInPolygon(pts, APoint, true);
-  end else
-  if ShapeKind = pskConeSide then
-  begin
-    pts:= PointsF([FOrigin - (FYAxis-FOrigin), FYAxis + (FXAxis-FOrigin), FYAxis - (FXAxis-FOrigin)]);
-    result := IsPointInPolygon(pts, APoint, true);
-  end else
+  pts := GetEnvelope;
+  result := IsPointInPolygon(pts, APoint, true);
+end;
+
+function TPhongShape.PointInShape(APoint: TPointF; ARadius: single): boolean;
+var
+  pts: ArrayOfTPointF;
+begin
+  if BackVisible then
   begin
   begin
-    box := GetAffineBox(AffineMatrixIdentity, true);
-    result:= box.Contains(APoint);
-  end;
+    pts := ComputeStrokeEnvelope(GetEnvelope, true, ARadius*2);
+    result:= IsPointInPolygon(pts, APoint, true);
+  end
+    else result := false;
 end;
 end;
 
 
 function TPhongShape.GetIsSlow(const AMatrix: TAffineMatrix): boolean;
 function TPhongShape.GetIsSlow(const AMatrix: TAffineMatrix): boolean;

+ 7 - 1
lazpaintcontrols/lcvectortextshapes.pas

@@ -151,7 +151,8 @@ type
     procedure ConfigureCustomEditor(AEditor: TBGRAOriginalEditor); override;
     procedure ConfigureCustomEditor(AEditor: TBGRAOriginalEditor); override;
     procedure Render(ADest: TBGRABitmap; ARenderOffset: TPoint; AMatrix: TAffineMatrix; ADraft: boolean); override;
     procedure Render(ADest: TBGRABitmap; ARenderOffset: TPoint; AMatrix: TAffineMatrix; ADraft: boolean); override;
     function GetRenderBounds({%H-}ADestRect: TRect; AMatrix: TAffineMatrix; AOptions: TRenderBoundsOptions = []): TRectF; override;
     function GetRenderBounds({%H-}ADestRect: TRect; AMatrix: TAffineMatrix; AOptions: TRenderBoundsOptions = []): TRectF; override;
-    function PointInShape(APoint: TPointF): boolean; override;
+    function PointInShape(APoint: TPointF): boolean; overload; override;
+    function PointInShape(APoint: TPointF; ARadius: single): boolean; overload; override;
     function GetIsSlow(const {%H-}AMatrix: TAffineMatrix): boolean; override;
     function GetIsSlow(const {%H-}AMatrix: TAffineMatrix): boolean; override;
     function GetGenericCost: integer; override;
     function GetGenericCost: integer; override;
     procedure MouseMove({%H-}Shift: TShiftState; {%H-}X, {%H-}Y: single; var {%H-}ACursor: TOriginalEditorCursor; var {%H-}AHandled: boolean); override;
     procedure MouseMove({%H-}Shift: TShiftState; {%H-}X, {%H-}Y: single; var {%H-}ACursor: TOriginalEditorCursor; var {%H-}AHandled: boolean); override;
@@ -1468,6 +1469,11 @@ begin
   result := GetAffineBox(AffineMatrixIdentity,true).Contains(APoint);
   result := GetAffineBox(AffineMatrixIdentity,true).Contains(APoint);
 end;
 end;
 
 
+function TTextShape.PointInShape(APoint: TPointF; ARadius: single): boolean;
+begin
+  result := false;
+end;
+
 function TTextShape.GetIsSlow(const AMatrix: TAffineMatrix): boolean;
 function TTextShape.GetIsSlow(const AMatrix: TAffineMatrix): boolean;
 begin
 begin
   Result:= true;
   Result:= true;