Browse Source

Improves the DXF reader

git-svn-id: trunk@16820 -
sekelsenmat 14 years ago
parent
commit
1b8372d750

+ 112 - 44
packages/fpvectorial/src/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;

+ 99 - 7
packages/fpvectorial/src/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.

+ 53 - 5
packages/fpvectorial/src/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;