From c079c6138bdcf09398205dbfa092314e6b5be459 Mon Sep 17 00:00:00 2001 From: sekelsenmat Date: Wed, 26 Jan 2011 16:05:00 +0000 Subject: [PATCH] Advances the reading support of DXF in fpvviewer git-svn-id: https://svn.code.sf.net/p/lazarus-ccr/svn@1462 8e941d3f-bd1b-0410-a28a-d453659cc2b4 --- .../fpvectorialsrc/dxfvectorialreader.pas | 156 +++++++++++++----- .../fpvviewer/fpvectorialsrc/fpvectorial.pas | 106 +++++++++++- .../fpvviewer/fpvectorialsrc/fpvtocanvas.pas | 58 ++++++- 3 files changed, 264 insertions(+), 56 deletions(-) diff --git a/applications/fpvviewer/fpvectorialsrc/dxfvectorialreader.pas b/applications/fpvviewer/fpvectorialsrc/dxfvectorialreader.pas index ca3d7fd6d..b9897dd5b 100644 --- a/applications/fpvviewer/fpvectorialsrc/dxfvectorialreader.pas +++ b/applications/fpvviewer/fpvectorialsrc/dxfvectorialreader.pas @@ -66,15 +66,11 @@ type TvDXFVectorialReader = class(TvCustomVectorialReader) private - // CIRCLE - CircleCenterX, CircleCenterY, CircleCenterZ, CircleRadius: Double; - // LINE - LineStartX, LineStartY, LineStartZ: Double; - LineEndX, LineEndY, LineEndZ: Double; // function SeparateString(AString: string; ASeparator: Char): T10Strings; procedure ReadENTITIES(ATokens: TDXFTokens; AData: TvVectorialDocument); procedure ReadENTITIES_LINE(ATokens: TDXFTokens; AData: TvVectorialDocument); + procedure ReadENTITIES_ARC(ATokens: TDXFTokens; AData: TvVectorialDocument); procedure ReadENTITIES_CIRCLE(ATokens: TDXFTokens; AData: TvVectorialDocument); procedure ReadENTITIES_ELLIPSE(ATokens: TDXFTokens; AData: TvVectorialDocument); procedure ReadENTITIES_TEXT(ATokens: TDXFTokens; AData: TvVectorialDocument); @@ -318,44 +314,9 @@ begin for i := 0 to ATokens.Count - 1 do begin CurToken := TDXFToken(ATokens.Items[i]); - if CurToken.StrValue = 'CIRCLE' then - begin - CircleCenterX := 0.0; - CircleCenterY := 0.0; - CircleCenterZ := 0.0; - CircleRadius := 0.0; - - ReadENTITIES_CIRCLE(CurToken.Childs, AData); - - AData.AddCircle(CircleCenterX, CircleCenterY, - CircleCenterZ, CircleRadius); - end - else if CurToken.StrValue = 'ELLIPSE' then - begin - // ... - ReadENTITIES_ELLIPSE(CurToken.Childs, AData); - end - else if CurToken.StrValue = 'LINE' then - begin - // Initial values - LineStartX := 0; - LineStartY := 0; - LineStartZ := 0; - LineEndX := 0; - LineEndY := 0; - LineEndZ := 0; - - // Read the data of the line - ReadENTITIES_LINE(CurToken.Childs, AData); - - // And now write it - {$ifdef FPVECTORIALDEBUG} - WriteLn(Format('Adding Line from %f,%f to %f,%f', [LineStartX, LineStartY, LineEndX, LineEndY])); - {$endif} - AData.StartPath(LineStartX, LineStartY); - AData.AddLineToPath(LineEndX, LineEndY); - AData.EndPath(); - end + if CurToken.StrValue = 'CIRCLE' then ReadENTITIES_CIRCLE(CurToken.Childs, AData) + else if CurToken.StrValue = 'ELLIPSE' then ReadENTITIES_ELLIPSE(CurToken.Childs, AData) + else if CurToken.StrValue = 'LINE' then ReadENTITIES_LINE(CurToken.Childs, AData) else if CurToken.StrValue = 'TEXT' then begin // ... @@ -367,7 +328,18 @@ procedure TvDXFVectorialReader.ReadENTITIES_LINE(ATokens: TDXFTokens; AData: TvV var CurToken: TDXFToken; i: Integer; + // LINE + LineStartX, LineStartY, LineStartZ: Double; + LineEndX, LineEndY, LineEndZ: Double; begin + // Initial values + LineStartX := 0; + LineStartY := 0; + LineStartZ := 0; + LineEndX := 0; + LineEndY := 0; + LineEndZ := 0; + for i := 0 to ATokens.Count - 1 do begin // Now read and process the item name @@ -388,6 +360,62 @@ begin 31: LineEndZ := CurToken.FloatValue; end; end; + + // And now write it + {$ifdef FPVECTORIALDEBUG} + WriteLn(Format('Adding Line from %f,%f to %f,%f', [LineStartX, LineStartY, LineEndX, LineEndY])); + {$endif} + AData.StartPath(LineStartX, LineStartY); + AData.AddLineToPath(LineEndX, LineEndY); + AData.EndPath(); +end; + +{ +100 Subclass marker (AcDbCircle) +39 Thickness (optional; default = 0) +10 Center point (in OCS) DXF: X value; APP: 3D point +20, 30 DXF: Y and Z values of center point (in OCS) +40 Radius +100 Subclass marker (AcDbArc) +50 Start angle +51 End angle +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) +} +procedure TvDXFVectorialReader.ReadENTITIES_ARC(ATokens: TDXFTokens; + AData: TvVectorialDocument); +var + CurToken: TDXFToken; + i: Integer; + CenterX, CenterY, CenterZ, Radius, StartAngle, EndAngle: Double; +begin + CenterX := 0.0; + CenterY := 0.0; + CenterZ := 0.0; + Radius := 0.0; + StartAngle := 0.0; + EndAngle := 0.0; + + for i := 0 to ATokens.Count - 1 do + begin + // Now read and process the item name + CurToken := TDXFToken(ATokens.Items[i]); + + // Avoid an exception by previously checking if the conversion can be made + if (CurToken.GroupCode = DXF_ENTITIES_HANDLE) or + (CurToken.GroupCode = DXF_ENTITIES_AcDbEntity) then Continue; + + CurToken.FloatValue := StrToFloat(Trim(CurToken.StrValue)); + + case CurToken.GroupCode of + 10: CenterX := CurToken.FloatValue; + 20: CenterY := CurToken.FloatValue; + 30: CenterZ := CurToken.FloatValue; + 40: Radius := CurToken.FloatValue; + end; + end; + + AData.AddCircularArc(CenterX, CenterY, CenterZ, Radius, StartAngle, EndAngle); end; { @@ -405,7 +433,13 @@ procedure TvDXFVectorialReader.ReadENTITIES_CIRCLE(ATokens: TDXFTokens; var CurToken: TDXFToken; i: Integer; + CircleCenterX, CircleCenterY, CircleCenterZ, CircleRadius: Double; begin + CircleCenterX := 0.0; + CircleCenterY := 0.0; + CircleCenterZ := 0.0; + CircleRadius := 0.0; + for i := 0 to ATokens.Count - 1 do begin // Now read and process the item name @@ -424,6 +458,9 @@ begin 40: CircleRadius := CurToken.FloatValue; end; end; + + AData.AddCircle(CircleCenterX, CircleCenterY, + CircleCenterZ, CircleRadius); end; { @@ -443,7 +480,7 @@ procedure TvDXFVectorialReader.ReadENTITIES_ELLIPSE(ATokens: TDXFTokens; var CurToken: TDXFToken; i: Integer; - CenterX, CenterY, CenterZ: Double; + CenterX, CenterY, CenterZ, MajorHalfAxis, MinorHalfAxis, Angle: Double; begin for i := 0 to ATokens.Count - 1 do begin @@ -461,7 +498,11 @@ begin 20: CenterY := CurToken.FloatValue; 30: CenterZ := CurToken.FloatValue; end; + end; + + // + AData.AddEllipse(CenterX, CenterY, CenterZ, MajorHalfAxis, MinorHalfAxis, Angle); end; { @@ -498,8 +539,35 @@ end; } procedure TvDXFVectorialReader.ReadENTITIES_TEXT(ATokens: TDXFTokens; AData: TvVectorialDocument); +var + CurToken: TDXFToken; + i: Integer; + PosX, PosY, PosZ: Double; + Str: string; begin + for i := 0 to ATokens.Count - 1 do + begin + // Now read and process the item name + CurToken := TDXFToken(ATokens.Items[i]); + // Avoid an exception by previously checking if the conversion can be made + if (CurToken.GroupCode = DXF_ENTITIES_HANDLE) or + (CurToken.GroupCode = 1) or + (CurToken.GroupCode = DXF_ENTITIES_AcDbEntity) then Continue; + + CurToken.FloatValue := StrToFloat(Trim(CurToken.StrValue)); + + case CurToken.GroupCode of + 1: Str := CurToken.StrValue; + 10: PosX := CurToken.FloatValue; + 20: PosY := CurToken.FloatValue; + 30: PosZ := CurToken.FloatValue; + end; + + end; + + // +// AData.AddEllipse(CenterX, CenterY, CenterZ, MajorHalfAxis, MinorHalfAxis, Angle); end; function TvDXFVectorialReader.GetCoordinateValue(AStr: shortstring): Double; diff --git a/applications/fpvviewer/fpvectorialsrc/fpvectorial.pas b/applications/fpvviewer/fpvectorialsrc/fpvectorial.pas index cd8e58f1b..6c5119184 100644 --- a/applications/fpvviewer/fpvectorialsrc/fpvectorial.pas +++ b/applications/fpvviewer/fpvectorialsrc/fpvectorial.pas @@ -18,7 +18,7 @@ unit fpvectorial; interface uses - Classes, SysUtils; + Classes, SysUtils, Math; type TvVectorialFormat = ( @@ -121,13 +121,41 @@ type Value: utf8string; end; + {@@ + } TvEntity = class public end; + {@@ + } TvCircle = class(TvEntity) public - X, Y, Z, Radius: Double; + CenterX, CenterY, CenterZ, Radius: Double; + end; + + {@@ + } + TvCircularArc = class(TvEntity) + public + CenterX, CenterY, CenterZ, Radius: Double; + StartAngle, EndAngle: Double; + end; + + {@@ + } + + { TvEllipse } + + TvEllipse = class(TvEntity) + public + // Mandatory fields + CenterX, CenterY, CenterZ, MajorHalfAxis, MinorHalfAxis: Double; + {@@ The Angle is measured in radians in relation to the positive X axis } + Angle: Double; + // Calculated fields + BoundingRect: TRect; + procedure CalculateBoundingRectangle; end; type @@ -184,7 +212,9 @@ type procedure EndPath(); procedure AddText(AX, AY, AZ: Double; FontName: string; FontSize: integer; AText: utf8string); overload; procedure AddText(AX, AY, AZ: Double; AStr: utf8string); overload; - procedure AddCircle(AX, AY, AZ, ARadius: Double); + procedure AddCircle(ACenterX, ACenterY, ACenterZ, ARadius: Double); + procedure AddCircularArc(ACenterX, ACenterY, ACenterZ, ARadius, AStartAngle, AEndAngle: Double); + procedure AddEllipse(CenterX, CenterY, CenterZ, MajorHalfAxis, MinorHalfAxis, Angle: Double); { properties } property PathCount: Integer read GetPathCount; property Paths[Index: Cardinal]: TPath read GetPath; @@ -331,6 +361,38 @@ begin end; end; +{ TvEllipse } + +procedure TvEllipse.CalculateBoundingRectangle; +var + t, tmp: Double; +begin + { + To calculate the bounding rectangle we can do this: + + Ellipse equations:You could try using the parametrized equations for an ellipse rotated at an arbitrary angle: + + x = CenterX + MajorHalfAxis*cos(t)*cos(Angle) - MinorHalfAxis*sin(t)*sin(Angle) + y = CenterY + MinorHalfAxis*sin(t)*cos(Angle) + MajorHalfAxis*cos(t)*sin(Angle) + + You can then differentiate and solve for gradient = 0: + 0 = dx/dt = -MajorHalfAxis*sin(t)*cos(Angle) - MinorHalfAxis*cos(t)*sin(Angle) + => + tan(t) = -MinorHalfAxis*tan(Angle)/MajorHalfAxis + => + t = cotang(-MinorHalfAxis*tan(Angle)/MajorHalfAxis) + + On the other axis: + + 0 = dy/dt = b*cos(t)*cos(phi) - a*sin(t)*sin(phi) + => + tan(t) = b*cot(phi)/a + } + t := cotan(-MinorHalfAxis*tan(Angle)/MajorHalfAxis); + tmp := CenterX + MajorHalfAxis*cos(t)*cos(Angle) - MinorHalfAxis*sin(t)*sin(Angle); + BoundingRect.Right := Round(tmp); +end; + { TsWorksheet } {@@ @@ -537,18 +599,48 @@ begin AddText(AX, AY, AZ, '', 10, AStr); end; -procedure TvVectorialDocument.AddCircle(AX, AY, AZ, ARadius: Double); +procedure TvVectorialDocument.AddCircle(ACenterX, ACenterY, ACenterZ, ARadius: Double); var lCircle: TvCircle; begin lCircle := TvCircle.Create; - lCircle.X := AX; - lCircle.Y := AY; - lCircle.Z := AZ; + lCircle.CenterX := ACenterX; + lCircle.CenterY := ACenterY; + lCircle.CenterZ := ACenterZ; lCircle.Radius := ARadius; FEntities.Add(lCircle); end; +procedure TvVectorialDocument.AddCircularArc(ACenterX, ACenterY, ACenterZ, + ARadius, AStartAngle, AEndAngle: Double); +var + lCircularArc: TvCircularArc; +begin + lCircularArc := TvCircularArc.Create; + lCircularArc.CenterX := ACenterX; + lCircularArc.CenterY := ACenterY; + lCircularArc.CenterZ := ACenterZ; + lCircularArc.Radius := ARadius; + lCircularArc.StartAngle := AStartAngle; + lCircularArc.EndAngle := AEndAngle; + FEntities.Add(lCircularArc); +end; + +procedure TvVectorialDocument.AddEllipse(CenterX, CenterY, CenterZ, + MajorHalfAxis, MinorHalfAxis, Angle: Double); +var + lEllipse: TvEllipse; +begin + lEllipse := TvEllipse.Create; + lEllipse.CenterX := CenterX; + lEllipse.CenterY := CenterY; + lEllipse.CenterZ := CenterZ; + lEllipse.MajorHalfAxis := MajorHalfAxis; + lEllipse.MinorHalfAxis := MinorHalfAxis; + lEllipse.Angle := Angle; + FEntities.Add(lEllipse); +end; + {@@ Convenience method which creates the correct writer object for a given vector graphics document format. diff --git a/applications/fpvviewer/fpvectorialsrc/fpvtocanvas.pas b/applications/fpvviewer/fpvectorialsrc/fpvtocanvas.pas index d77933768..f80ab900e 100644 --- a/applications/fpvviewer/fpvectorialsrc/fpvtocanvas.pas +++ b/applications/fpvviewer/fpvectorialsrc/fpvtocanvas.pas @@ -14,6 +14,42 @@ procedure DrawFPVectorialToCanvas(ASource: TvVectorialDocument; ADest: TFPCustom implementation +{function Rotate2DPoint(P,Fix :TPoint; alpha:double): TPoint; +var + sinus, cosinus : Extended; +begin + SinCos(alpha, sinus, cosinus); + P.x := P.x - Fix.x; + P.y := P.y - Fix.y; + result.x := Round(p.x*cosinus + p.y*sinus) + fix.x ; + result.y := Round(-p.x*sinus + p.y*cosinus) + Fix.y; +end;} + +procedure DrawRotatedEllipse(ADest: TFPCustomCanvas; CurEllipse: TvEllipse); +{var + PointList: array[0..6] of TPoint; + f: TPoint; + dk: Integer;} +begin +{ dk := Round(0.654 * Abs(y2-y1)); + f.x := CurEllipse.CenterX; + f.y := CurEllipse.CenterY - 1; + PointList[0] := Rotate2DPoint(Point(x1, f.y), f, Alpha) ; // Startpoint + PointList[1] := Rotate2DPoint(Point(x1, f.y - dk), f, Alpha); + //Controlpoint of Startpoint first part + PointList[2] := Rotate2DPoint(Point(x2- 1, f.y - dk), f, Alpha); + //Controlpoint of secondpoint first part + PointList[3] := Rotate2DPoint(Point(x2 -1 , f.y), f, Alpha); + // Firstpoint of secondpart + PointList[4] := Rotate2DPoint(Point(x2-1 , f.y + dk), f, Alpha); + // Controllpoint of secondpart firstpoint + PointList[5] := Rotate2DPoint(Point(x1, f.y + dk), f, Alpha); + // Conrollpoint of secondpart endpoint + PointList[6] := PointList[0]; // Endpoint of + // Back to the startpoint + PolyBezier(canvas.handle, Pointlist[0], 7);} +end; + {@@ This function draws a FPVectorial vectorial image to a TFPCustomCanvas descendent, such as TCanvas from the LCL. @@ -43,6 +79,8 @@ var // For entities CurEntity: TvEntity; CurCircle: TvCircle; + CurEllipse: TvEllipse; + CurCircularArc: TvCircularArc; begin {$ifdef FPVECTORIALDEBUG} WriteLn(':>DrawFPVectorialToCanvas'); @@ -106,15 +144,25 @@ begin for i := 0 to ASource.GetEntityCount - 1 do begin CurEntity := ASource.GetEntity(i); - CurCircle := CurEntity as TvCircle; if CurEntity is TvCircle then begin + CurCircle := CurEntity as TvCircle; ADest.Ellipse( - Round(ADestX + AmulX * (CurCircle.X - CurCircle.Radius)), - Round(ADestY + AMulY * (CurCircle.Y - CurCircle.Radius)), - Round(ADestX + AmulX * (CurCircle.X + CurCircle.Radius)), - Round(ADestY + AMulY * (CurCircle.Y + CurCircle.Radius)) + Round(ADestX + AmulX * (CurCircle.CenterX - CurCircle.Radius)), + Round(ADestY + AMulY * (CurCircle.CenterY - CurCircle.Radius)), + Round(ADestX + AmulX * (CurCircle.CenterX + CurCircle.Radius)), + Round(ADestY + AMulY * (CurCircle.CenterY + CurCircle.Radius)) ); + end + else if CurEntity is TvEllipse then + begin + CurEllipse := CurEntity as TvEllipse; + DrawRotatedEllipse(ADest, CurEllipse); + end + else if CurEntity is TvCircularArc then + begin + CurCircularArc := CurEntity as TvCircularArc; +// ADest.Arc(ADest, CurEllipse); end; end;