diff --git a/components/fpvectorial/dxfvectorialreader.pas b/components/fpvectorial/dxfvectorialreader.pas index 2e1b55d5ff..4143ddfe9f 100644 --- a/components/fpvectorial/dxfvectorialreader.pas +++ b/components/fpvectorial/dxfvectorialreader.pas @@ -1622,7 +1622,7 @@ var lName: string; lBlock: TvBlock; PosX, PosY, PosZ: Double; - lRotationAngle: Double; + lRotationAngle: Double = 0.0; begin PosX := 0.0; PosY := 0.0; @@ -1645,7 +1645,7 @@ begin 10: PosX := CurToken.FloatValue; 20: PosY := CurToken.FloatValue; 30: PosZ := CurToken.FloatValue; - 50: lRotationAngle := CurToken.FloatValue; + 50: lRotationAngle := -1 * CurToken.FloatValue * Pi / 180; end; end; @@ -1744,6 +1744,24 @@ begin end; {.$define FPVECTORIALDEBUG_LWPOLYLINE} +{ +100 Subclass marker (AcDbPolyline) +90 Number of vertices +70 Polyline flag (bit-coded); default is 0: + 1 = Closed; 128 = Plinegen +43 Constant width (optional; default = 0). Not used if variable width (codes 40 and/or 41) is set +38 Elevation (optional; default = 0) +39 Thickness (optional; default = 0) +10 Vertex coordinates (in OCS), multiple entries; one entry for each vertex + DXF: X value; APP: 2D point +20 DXF: Y value of vertex coordinates (in OCS), multiple entries; one entry for each vertex +40 Starting width (multiple entries; one entry for each vertex) (optional; default = 0; multiple entries). Not used if constant width (code 43) is set +41 End width (multiple entries; one entry for each vertex) (optional; default = 0; multiple entries). Not used if constant width (code 43) is set +42 Bulge (multiple entries; one entry for each vertex) (optional; default = 0) +210 Extrusion direction (optional; default = 0, 0, 1) + DXF: X value; APP: 3D vector +220, 230 DXF: Y and Z values of extrusion direction (optional) +} function TvDXFVectorialReader.ReadENTITIES_LWPOLYLINE(ATokens: TDXFTokens; AData: TvVectorialPage; ADoc: TvVectorialDocument; AOnlyCreate: Boolean = False): TPath; var @@ -1751,6 +1769,7 @@ var i, curPoint: Integer; // LINE LWPolyline: array of TLWPOLYLINEElement; + LWFlags: Integer = 0; begin curPoint := -1; Result := nil; @@ -1761,7 +1780,7 @@ begin CurToken := TDXFToken(ATokens.Items[i]); // Avoid an exception by previously checking if the conversion can be made - if CurToken.GroupCode in [10, 20, 30, 11, 21, 31] then + if CurToken.GroupCode in [10, 20, 30, 11, 21, 31, 70] then begin CurToken.FloatValue := StrToFloat(Trim(CurToken.StrValue), FPointSeparator); end; @@ -1778,9 +1797,19 @@ begin LWPolyline[curPoint].X := CurToken.FloatValue - DOC_OFFSET.X; end; 20: LWPolyline[curPoint].Y := CurToken.FloatValue - DOC_OFFSET.Y; + 70: LWFlags := Round(CurToken.FloatValue); end; end; + // In case of a Flag="Closed" then we need to close the line + if LWFlags = 1 then + begin + Inc(curPoint); + SetLength(LWPolyline, curPoint+1); + LWPolyline[curPoint].X := LWPolyline[0].X; + LWPolyline[curPoint].Y := LWPolyline[0].Y; + end; + // And now write it if curPoint >= 0 then // otherwise the polyline is empty of points begin diff --git a/components/fpvectorial/fpvectorial.pas b/components/fpvectorial/fpvectorial.pas index ef6d5c1f01..c50bd54ed4 100644 --- a/components/fpvectorial/fpvectorial.pas +++ b/components/fpvectorial/fpvectorial.pas @@ -151,6 +151,7 @@ type Previous: TPathSegment; Next: TPathSegment; procedure Move(ADeltaX, ADeltaY: Double); virtual; + procedure Rotate(AAngle: Double; ABase: T3DPoint); virtual; // Angle in radians function GenerateDebugTree(ADestRoutine: TvDebugAddItemProc; APageItem: Pointer): Pointer; virtual; end; @@ -168,6 +169,7 @@ type public X, Y: Double; procedure Move(ADeltaX, ADeltaY: Double); override; + procedure Rotate(AAngle: Double; ABase: T3DPoint); override; function GenerateDebugTree(ADestRoutine: TvDebugAddItemProc; APageItem: Pointer): Pointer; override; end; @@ -246,7 +248,7 @@ type procedure MoveSubpart(ADeltaX, ADeltaY: Double; ASubpart: Cardinal); virtual; function GetSubpartCount: Integer; virtual; procedure PositionSubparts(ADest: TFPCustomCanvas; ABaseX, ABaseY: Double); virtual; - procedure Rotate(AAngle: Double); virtual; + procedure Rotate(AAngle: Double; ABase: T3DPoint); virtual; // Angle in radians procedure Render(ADest: TFPCustomCanvas; ARenderInfo: TvRenderInfo; ADestX: Integer = 0; ADestY: Integer = 0; AMulX: Double = 1.0; AMulY: Double = 1.0); virtual; function AdjustColorToBackground(AColor: TFPColor; ARenderInfo: TvRenderInfo): TFPColor; @@ -260,6 +262,8 @@ type { TvNamedEntity } TvNamedEntity = class(TvEntity) + protected + FExtraDebugStr: string; public Name: string; function GenerateDebugTree(ADestRoutine: TvDebugAddItemProc; APageItem: Pointer): Pointer; override; @@ -336,6 +340,7 @@ type procedure MoveSubpart(ADeltaX, ADeltaY: Double; ASubpart: Cardinal); override; function MoveToSubpart(ASubpart: Cardinal): TPathSegment; function GetSubpartCount: Integer; override; + procedure Rotate(AAngle: Double; ABase: T3DPoint); override; procedure Render(ADest: TFPCustomCanvas; ARenderInfo: TvRenderInfo; ADestX: Integer = 0; ADestY: Integer = 0; AMulX: Double = 1.0; AMulY: Double = 1.0); override; function GenerateDebugTree(ADestRoutine: TvDebugAddItemProc; APageItem: Pointer): Pointer; override; @@ -661,7 +666,7 @@ type function GetFirstEntity: TvEntity; function GetNextEntity: TvEntity; procedure AddEntity(AEntity: TvEntity); - procedure Rotate(AAngle: Double); + procedure Rotate(AAngle: Double; ABase: T3DPoint); override; procedure Clear; override; procedure Render(ADest: TFPCustomCanvas; ARenderInfo: TvRenderInfo; ADestX: Integer = 0; ADestY: Integer = 0; AMulX: Double = 1.0; AMulY: Double = 1.0); override; @@ -691,9 +696,10 @@ type TvInsert = class(TvNamedEntity) public InsertEntity: TvEntity; // The entity to be inserted - RotationAngle: Double; // in degrees, normal is zero + RotationAngle: Double; // in angles, normal is zero procedure Render(ADest: TFPCustomCanvas; ARenderInfo: TvRenderInfo; ADestX: Integer = 0; ADestY: Integer = 0; AMulX: Double = 1.0; AMulY: Double = 1.0); override; + function GenerateDebugTree(ADestRoutine: TvDebugAddItemProc; APageItem: Pointer): Pointer; override; end; {@@ @@ -1077,6 +1083,11 @@ begin end; +procedure TPathSegment.Rotate(AAngle: Double; ABase: T3DPoint); +begin + +end; + function TPathSegment.GenerateDebugTree(ADestRoutine: TvDebugAddItemProc; APageItem: Pointer): Pointer; var @@ -1094,6 +1105,16 @@ begin Y := Y + ADeltaY; end; +procedure T2DSegment.Rotate(AAngle: Double; ABase: T3DPoint); +var + lRes: T3DPoint; +begin + inherited Rotate(AAngle, ABase); + lRes := fpvutils.Rotate3DPointInXY(Make3DPoint(X, Y, 0), ABase, AAngle); + X := lRes.X; + Y := lRes.Y; +end; + function T2DSegment.GenerateDebugTree(ADestRoutine: TvDebugAddItemProc; APageItem: Pointer): Pointer; var @@ -1192,7 +1213,7 @@ begin end; -procedure TvEntity.Rotate(AAngle: Double); +procedure TvEntity.Rotate(AAngle: Double; ABase: T3DPoint); begin end; @@ -1249,7 +1270,7 @@ function TvNamedEntity.GenerateDebugTree(ADestRoutine: TvDebugAddItemProc; var lStr: string; begin - lStr := Format('[%s] Name=%s X=%f Y=%f', [Self.ClassName, Name, X, Y]); + lStr := Format('[%s] Name="%s" X=%f Y=%f' + FExtraDebugStr, [Self.ClassName, Name, X, Y]); Result := ADestRoutine(lStr, APageItem); end; @@ -1622,6 +1643,21 @@ begin Result := Len; end; +procedure TPath.Rotate(AAngle: Double; ABase: T3DPoint); +var + i: Integer; + lCurPart: TPathSegment; +begin + inherited Rotate(AAngle, ABase); + for i := 0 to GetSubpartCount()-1 do + begin + // Move to the subpart + lCurPart := MoveToSubpart(i); + // Rotate it + lCurPart.Rotate(AAngle, ABase); + end; +end; + procedure TPath.Render(ADest: TFPCustomCanvas; ARenderInfo: TvRenderInfo; ADestX: Integer; ADestY: Integer; AMulX: Double; AMulY: Double); @@ -3278,13 +3314,13 @@ begin FElements.Add(AEntity); end; -procedure TvEntityWithSubEntities.Rotate(AAngle: Double); +procedure TvEntityWithSubEntities.Rotate(AAngle: Double; ABase: T3DPoint); var i: Integer; begin for i := 0 to FElements.Count-1 do begin - TvEntity(FElements.Items[i]).Rotate(AAngle); + TvEntity(FElements.Items[i]).Rotate(AAngle, ABase); end; end; @@ -3322,7 +3358,7 @@ var lStr: string; lCurEntity: TvEntity; begin - lStr := Format('[%s] Name="%s"', [Self.ClassName, Self.Name]); + lStr := Format('[%s] Name="%s"' + FExtraDebugStr, [Self.ClassName, Self.Name]); // Add styles // Pen @@ -3367,25 +3403,34 @@ begin // If we are inserting a block, make sure it will render its contents OldForceRenderBlock := ARenderInfo.ForceRenderBlock; ARenderInfo.ForceRenderBlock := True; - // Alter the position of the elements to consider the positioning of the BLOCK and of the INSERT - InsertEntity.Move(X, Y); // If necessary rotate the canvas if RotationAngle <> 0 then begin - + InsertEntity.Rotate(RotationAngle, Make3DPoint(0, 0, 0)); end; + // Alter the position of the elements to consider the positioning of the BLOCK and of the INSERT + InsertEntity.Move(X, Y); // Render InsertEntity.Render(ADest, ARenderInfo, ADestX, ADestY, AMulX, AMuly); + // Change them back + InsertEntity.Move(-X, -Y); // And unrotate it back again if RotationAngle <> 0 then begin - + InsertEntity.Rotate(-1 * RotationAngle, Make3DPoint(0, 0, 0)); end; - // Change them back - InsertEntity.Move(-X, -Y); ARenderInfo.ForceRenderBlock := OldForceRenderBlock; end; +function TvInsert.GenerateDebugTree(ADestRoutine: TvDebugAddItemProc; + APageItem: Pointer): Pointer; +begin + FExtraDebugStr := Format(' RotationAngle(degrees)=%f', [RotationAngle * 180 / Pi]); + if (InsertEntity <> nil) and (InsertEntity is TvNamedEntity) then + FExtraDebugStr := FExtraDebugStr + Format(' InsertEntity="%s"', [TvNamedEntity(InsertEntity).Name]); + Result:=inherited GenerateDebugTree(ADestRoutine, APageItem); +end; + { TvBlock } procedure TvBlock.Render(ADest: TFPCustomCanvas; ARenderInfo: TvRenderInfo; ADestX: Integer; diff --git a/components/fpvectorial/fpvutils.pas b/components/fpvectorial/fpvutils.pas index d08fde3849..2c03fd113b 100644 --- a/components/fpvectorial/fpvutils.pas +++ b/components/fpvectorial/fpvutils.pas @@ -41,6 +41,7 @@ function CoordToCanvasX(ACoord: Double; ADestX: Integer; AMulX: Double): Integer function CoordToCanvasY(ACoord: Double; ADestY: Integer; AMulY: Double): Integer; inline; // Other routines function SeparateString(AString: string; ASeparator: char): T10Strings; +function Make3DPoint(AX, AY, AZ: Double): T3DPoint; // Mathematical routines procedure EllipticalArcToBezier(Xc, Yc, Rx, Ry, startAngle, endAngle: Double; var P1, P2, P3, P4: T3DPoint); procedure CircularArcToBezier(Xc, Yc, R, startAngle, endAngle: Double; var P1, P2, P3, P4: T3DPoint); @@ -141,6 +142,13 @@ begin end; end; +function Make3DPoint(AX, AY, AZ: Double): T3DPoint; +begin + Result.X := AX; + Result.Y := AY; + Result.Z := AZ; +end; + { Considering a counter-clockwise arc, elliptical and alligned to the axises An elliptical Arc can be converted to @@ -288,6 +296,7 @@ begin end; // Rotates a point P around RotCenter +// alpha angle in radians function Rotate3DPointInXY(P, RotCenter: T3DPoint; alpha:double): T3DPoint; var sinus, cosinus : Extended;