Browse Source

Fixes compiling avisocncvectorialwriter, adds a dimension entity and multiple improvements to fpvtocanvas and the DXF vectorial reader

git-svn-id: trunk@17092 -
sekelsenmat 14 years ago
parent
commit
c0f254d706

+ 31 - 13
packages/fpvectorial/src/avisocncgcodewriter.pas

@@ -36,6 +36,10 @@ var
   i, j: Integer;
   i, j: Integer;
   Str: string;
   Str: string;
   APath: TPath;
   APath: TPath;
+  CurSegment: T2DSegment;
+  Cur3DSegment: T3DSegment;
+  Cur2DBezierSegment: T2DBezierSegment;
+  Cur3DBezierSegment: T3DBezierSegment;
 begin
 begin
   AStrings.Clear;
   AStrings.Clear;
 
 
@@ -51,25 +55,39 @@ begin
     // levanta a broca
     // levanta a broca
     AStrings.Add('P01 // Sobe a cabeça de gravação');
     AStrings.Add('P01 // Sobe a cabeça de gravação');
     // vai para o ponto inicial
     // vai para o ponto inicial
+    CurSegment := T2DSegment(APath.Points);
     AStrings.Add(Format('G01 X%f Y%f',
     AStrings.Add(Format('G01 X%f Y%f',
-      [APath.Points[0].X, APath.Points[0].Y]));
+      [CurSegment.X, CurSegment.Y]));
     AStrings.Add('P02 // Abaixa a cabeça de gravação');
     AStrings.Add('P02 // Abaixa a cabeça de gravação');
 
 
     for j := 1 to APath.Len - 1 do
     for j := 1 to APath.Len - 1 do
     begin
     begin
-      case APath.Points[j].SegmentType of
+      CurSegment := T2DSegment(CurSegment.Next);
+      case CurSegment.SegmentType of
       st2DLine: AStrings.Add(Format('G01 X%f Y%f',
       st2DLine: AStrings.Add(Format('G01 X%f Y%f',
-         [APath.Points[j].X, APath.Points[j].Y]));
-      st3DLine: AStrings.Add(Format('G01 X%f Y%f Z%f',
-         [APath.Points[j].X, APath.Points[j].Y, APath.Points[j].Z]));
-      st2DBezier: AStrings.Add(Format('B02 X%f Y%f X%f Y%f X%f Y%f',
-         [APath.Points[j].X2, APath.Points[j].Y2,
-          APath.Points[j].X3, APath.Points[j].Y3,
-          APath.Points[j].X, APath.Points[j].Y]));
-      st3DBezier: AStrings.Add(Format('B03 X%f Y%f Z%f X%f Y%f Z%f X%f Y%f Z%f',
-         [APath.Points[j].X2, APath.Points[j].Y2, APath.Points[j].Z2,
-          APath.Points[j].X3, APath.Points[j].Y3, APath.Points[j].Z3,
-          APath.Points[j].X, APath.Points[j].Y, APath.Points[j].Z]));
+         [CurSegment.X, CurSegment.Y]));
+      st3DLine:
+      begin
+        Cur3DSegment := T3DSegment(CurSegment);
+        AStrings.Add(Format('G01 X%f Y%f Z%f',
+         [Cur3DSegment.X, Cur3DSegment.Y, Cur3DSegment.Z]));
+      end;
+      st2DBezier:
+      begin
+        Cur2DBezierSegment := T2DBezierSegment(CurSegment);
+        AStrings.Add(Format('B02 X%f Y%f X%f Y%f X%f Y%f',
+         [Cur2DBezierSegment.X2, Cur2DBezierSegment.Y2,
+          Cur2DBezierSegment.X3, Cur2DBezierSegment.Y3,
+          Cur2DBezierSegment.X, Cur2DBezierSegment.Y]));
+      end;
+      st3DBezier:
+      begin
+        Cur3DBezierSegment := T3DBezierSegment(CurSegment);
+        AStrings.Add(Format('B03 X%f Y%f Z%f X%f Y%f Z%f X%f Y%f Z%f',
+         [Cur3DBezierSegment.X2, Cur3DBezierSegment.Y2, Cur3DBezierSegment.Z2,
+          Cur3DBezierSegment.X3, Cur3DBezierSegment.Y3, Cur3DBezierSegment.Z3,
+          Cur3DBezierSegment.X, Cur3DBezierSegment.Y, Cur3DBezierSegment.Z]));
+      end;
       end;
       end;
     end;
     end;
   end;
   end;

+ 265 - 5
packages/fpvectorial/src/dxfvectorialreader.pas

@@ -29,11 +29,10 @@ unit dxfvectorialreader;
 interface
 interface
 
 
 uses
 uses
-  Classes, SysUtils,
+  Classes, SysUtils, Math,
   fpvectorial;
   fpvectorial;
 
 
 type
 type
-
   { Used by tcutils.SeparateString }
   { Used by tcutils.SeparateString }
   T10Strings = array[0..9] of shortstring;
   T10Strings = array[0..9] of shortstring;
 
 
@@ -70,6 +69,9 @@ type
     // HEADER data
     // HEADER data
     ANGBASE: Double;
     ANGBASE: Double;
     ANGDIR: Integer;
     ANGDIR: Integer;
+    INSBASE, EXTMIN, EXTMAX, LIMMIN, LIMMAX: T3DPoint;
+    // Calculated HEADER data
+    DOC_OFFSET: T3DPoint;
     //
     //
     function  SeparateString(AString: string; ASeparator: Char): T10Strings;
     function  SeparateString(AString: string; ASeparator: Char): T10Strings;
     procedure ReadHEADER(ATokens: TDXFTokens; AData: TvVectorialDocument);
     procedure ReadHEADER(ATokens: TDXFTokens; AData: TvVectorialDocument);
@@ -77,6 +79,7 @@ type
     procedure ReadENTITIES_LINE(ATokens: TDXFTokens; AData: TvVectorialDocument);
     procedure ReadENTITIES_LINE(ATokens: TDXFTokens; AData: TvVectorialDocument);
     procedure ReadENTITIES_ARC(ATokens: TDXFTokens; AData: TvVectorialDocument);
     procedure ReadENTITIES_ARC(ATokens: TDXFTokens; AData: TvVectorialDocument);
     procedure ReadENTITIES_CIRCLE(ATokens: TDXFTokens; AData: TvVectorialDocument);
     procedure ReadENTITIES_CIRCLE(ATokens: TDXFTokens; AData: TvVectorialDocument);
+    procedure ReadENTITIES_DIMENSION(ATokens: TDXFTokens; AData: TvVectorialDocument);
     procedure ReadENTITIES_ELLIPSE(ATokens: TDXFTokens; AData: TvVectorialDocument);
     procedure ReadENTITIES_ELLIPSE(ATokens: TDXFTokens; AData: TvVectorialDocument);
     procedure ReadENTITIES_TEXT(ATokens: TDXFTokens; AData: TvVectorialDocument);
     procedure ReadENTITIES_TEXT(ATokens: TDXFTokens; AData: TvVectorialDocument);
     function  GetCoordinateValue(AStr: shortstring): Double;
     function  GetCoordinateValue(AStr: shortstring): Double;
@@ -326,8 +329,9 @@ end;
 procedure TvDXFVectorialReader.ReadHEADER(ATokens: TDXFTokens;
 procedure TvDXFVectorialReader.ReadHEADER(ATokens: TDXFTokens;
   AData: TvVectorialDocument);
   AData: TvVectorialDocument);
 var
 var
-  i: Integer;
+  i, j: Integer;
   CurToken: TDXFToken;
   CurToken: TDXFToken;
+  CurField: P3DPoint;
 begin
 begin
   i := 0;
   i := 0;
   while i < ATokens.Count do
   while i < ATokens.Count do
@@ -344,10 +348,71 @@ begin
       CurToken := TDXFToken(ATokens.Items[i+1]);
       CurToken := TDXFToken(ATokens.Items[i+1]);
       ANGDIR := StrToInt(CurToken.StrValue);
       ANGDIR := StrToInt(CurToken.StrValue);
       Inc(i);
       Inc(i);
+    end
+    // This indicates the size of the document
+    else if (CurToken.StrValue = '$INSBASE') or
+      (CurToken.StrValue = '$EXTMIN') or (CurToken.StrValue = '$EXTMAX') or
+      (CurToken.StrValue = '$LIMMIN') or (CurToken.StrValue = '$LIMMAX') then
+    begin
+      if (CurToken.StrValue = '$INSBASE') then CurField := @INSBASE
+      else if (CurToken.StrValue = '$EXTMIN') then CurField := @EXTMIN
+      else if (CurToken.StrValue = '$EXTMAX') then CurField := @EXTMAX
+      else if (CurToken.StrValue = '$LIMMIN') then CurField := @LIMMIN
+      else if (CurToken.StrValue = '$LIMMAX') then CurField := @LIMMAX;
+
+      // Check the next 2 items and verify if they are the values of the size of the document
+      for j := 0 to 1 do
+      begin
+        CurToken := TDXFToken(ATokens.Items[i+1]);
+        case CurToken.GroupCode of
+        10:
+        begin;
+          CurField^.X := StrToFloat(CurToken.StrValue, FPointSeparator);
+          Inc(i);
+        end;
+        20:
+        begin
+          CurField^.Y := StrToFloat(CurToken.StrValue, FPointSeparator);
+          Inc(i);
+        end;
+        end;
+      end;
     end;
     end;
 
 
     Inc(i);
     Inc(i);
   end;
   end;
+
+  // After getting all the data, we can try to make some sense out of it
+
+  // Sometimes EXTMIN comes as 10^20 and EXTMAX as -10^20, which makes no sence
+  // In these cases we need to ignore them.
+  if (EXTMIN.X > 10000000000) or (EXTMIN.X < -10000000000)
+  or (EXTMAX.X > 10000000000) or (EXTMAX.X < -10000000000) then
+  begin
+    DOC_OFFSET.X := 0;
+    DOC_OFFSET.Y := 0;
+
+    AData.Width := LIMMAX.X;
+    AData.Height := LIMMAX.Y;
+  end
+  else
+  begin
+    // The size of the document seams to be given by:
+    // DOC_SIZE = min(EXTMAX, LIMMAX) - DOC_OFFSET;
+    // if EXTMIN is <> -infinite then DOC_OFFSET = EXTMIN else DOC_OFFSET = (0, 0)
+    // We will shift the whole document so that it has only positive coordinates and
+    // DOC_OFFSET will be utilized for that
+
+    if EXTMIN.X > -100 then
+    begin
+      DOC_OFFSET.X := EXTMIN.X;
+      DOC_OFFSET.Y := EXTMIN.Y;
+    end
+    else FillChar(DOC_OFFSET, sizeof(T3DPoint), #0);
+
+    AData.Width := min(EXTMAX.X, LIMMAX.X) - DOC_OFFSET.X;
+    AData.Height := min(EXTMAX.Y, LIMMAX.Y) - DOC_OFFSET.Y;
+  end;
 end;
 end;
 
 
 procedure TvDXFVectorialReader.ReadENTITIES(ATokens: TDXFTokens; AData: TvVectorialDocument);
 procedure TvDXFVectorialReader.ReadENTITIES(ATokens: TDXFTokens; AData: TvVectorialDocument);
@@ -360,9 +425,11 @@ begin
     CurToken := TDXFToken(ATokens.Items[i]);
     CurToken := TDXFToken(ATokens.Items[i]);
     if CurToken.StrValue = 'ARC' then ReadENTITIES_ARC(CurToken.Childs, AData)
     if CurToken.StrValue = 'ARC' then ReadENTITIES_ARC(CurToken.Childs, AData)
     else if CurToken.StrValue = 'CIRCLE' then ReadENTITIES_CIRCLE(CurToken.Childs, AData)
     else if CurToken.StrValue = 'CIRCLE' then ReadENTITIES_CIRCLE(CurToken.Childs, AData)
+    else if CurToken.StrValue = 'DIMENSION' then ReadENTITIES_DIMENSION(CurToken.Childs, AData)
     else if CurToken.StrValue = 'ELLIPSE' then ReadENTITIES_ELLIPSE(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 = 'LINE' then ReadENTITIES_LINE(CurToken.Childs, AData)
-    else if CurToken.StrValue = 'TEXT' then
+    else if CurToken.StrValue = 'TEXT' then ReadENTITIES_TEXT(CurToken.Childs, AData)
+    else
     begin
     begin
       // ...
       // ...
     end;
     end;
@@ -406,9 +473,15 @@ begin
     end;
     end;
   end;
   end;
 
 
+  // Position fixing for documents with negative coordinates
+  LineStartX := LineStartX - DOC_OFFSET.X;
+  LineStartY := LineStartY - DOC_OFFSET.Y;
+  LineEndX := LineEndX - DOC_OFFSET.X;
+  LineEndY := LineEndY - DOC_OFFSET.Y;
+
   // And now write it
   // And now write it
   {$ifdef FPVECTORIALDEBUG}
   {$ifdef FPVECTORIALDEBUG}
-  WriteLn(Format('Adding Line from %f,%f to %f,%f', [LineStartX, LineStartY, LineEndX, LineEndY]));
+ // WriteLn(Format('Adding Line from %f,%f to %f,%f', [LineStartX, LineStartY, LineEndX, LineEndY]));
   {$endif}
   {$endif}
   AData.StartPath(LineStartX, LineStartY);
   AData.StartPath(LineStartX, LineStartY);
   AData.AddLineToPath(LineEndX, LineEndY);
   AData.AddLineToPath(LineEndX, LineEndY);
@@ -416,6 +489,8 @@ begin
 end;
 end;
 
 
 {
 {
+Arcs are always counter-clockwise in DXF
+
 100 Subclass marker (AcDbCircle)
 100 Subclass marker (AcDbCircle)
 39 Thickness (optional; default = 0)
 39 Thickness (optional; default = 0)
 10 Center point (in OCS) DXF: X value; APP: 3D point
 10 Center point (in OCS) DXF: X value; APP: 3D point
@@ -462,6 +537,18 @@ begin
     end;
     end;
   end;
   end;
 
 
+  // In DXF the EndAngle is always greater then the StartAngle.
+  // If it isn't then sum 360 to it to make sure we don't get wrong results
+  if EndAngle < StartAngle then EndAngle := EndAngle + 360;
+
+  // Position fixing for documents with negative coordinates
+  CenterX := CenterX - DOC_OFFSET.X;
+  CenterY := CenterY - DOC_OFFSET.Y;
+
+  {$ifdef FPVECTORIALDEBUG}
+  WriteLn(Format('Adding Arc Center=%f,%f Radius=%f StartAngle=%f EndAngle=%f',
+    [CenterX, CenterY, Radius, StartAngle, EndAngle]));
+  {$endif}
   AData.AddCircularArc(CenterX, CenterY, CenterZ, Radius, StartAngle, EndAngle);
   AData.AddCircularArc(CenterX, CenterY, CenterZ, Radius, StartAngle, EndAngle);
 end;
 end;
 
 
@@ -506,10 +593,175 @@ begin
     end;
     end;
   end;
   end;
 
 
+  // Position fixing for documents with negative coordinates
+  CircleCenterX := CircleCenterX - DOC_OFFSET.X;
+  CircleCenterY := CircleCenterY - DOC_OFFSET.Y;
+
   AData.AddCircle(CircleCenterX, CircleCenterY,
   AData.AddCircle(CircleCenterX, CircleCenterY,
     CircleCenterZ, CircleRadius);
     CircleCenterZ, CircleRadius);
 end;
 end;
 
 
+{
+Group codes Description
+100 Subclass marker (AcDbDimension)
+2 Name of the block that contains the entities that make up the dimension picture
+10 Definition point (in WCS) DXF: X value; APP: 3D point
+20, 30 DXF: Y and Z values of definition point (in WCS)
+11 Middle point of dimension text (in OCS) DXF: X value; APP: 3D point
+21, 31 DXF: Y and Z values of middle point of dimension text (in OCS)
+70 Dimension type.
+  Values 0-6 are integer values that represent the dimension type.
+  Values 32, 64, and 128 are bit values, which are added to the integer values
+  (value 32 is always set in R13 and later releases).
+  0 = Rotated, horizontal, or vertical; 1 = Aligned;
+  2 = Angular; 3 = Diameter; 4 = Radius;
+  5 = Angular 3 point; 6 = Ordinate;
+  32 = Indicates that the block reference (group code 2) is referenced by this dimension only.
+  64 = Ordinate type. This is a bit value (bit 7) used only with integer value 6.
+    If set, ordinate is X-type; if not set, ordinate is Y-type.
+  128 = This is a bit value (bit 8) added to the other group 70 values
+    if the dimension text has been positioned at a user-defined location
+    rather than at the default location.
+71 Attachment point:
+  1 = Top left; 2 = Top center; 3 = Top right;
+  4 = Middle left; 5 = Middle center; 6 = Middle right;
+  7 = Bottom left; 8 = Bottom center; 9 = Bottom right
+72 Dimension text line spacing style (optional):
+  1(or missing) = At least (taller characters will override)
+  2 = Exact (taller characters will not override)
+41 Dimension text line spacing factor (optional):
+  Percentage of default (3-on-5) line spacing to be applied. Valid values range from 0.25 to 4.00.
+42 Actual measurement (optional; read-only value)
+1 Dimension text explicitly entered by the user. Optional; default is the measurement.
+  If null or "<>", the dimension measurement is drawn as the text,
+  if " " (one blank space), the text is suppressed. Anything else is drawn as the text.
+53 The optional group code 53 is the rotation angle of the dimension
+  text away from its default orientation (the direction of the dimension line)  (optional).
+51 All dimension types have an optional 51 group code, which indicates the
+  horizontal direction for the dimension entity. The dimension entity determines
+  the orientation of dimension text and lines for horizontal, vertical, and
+  rotated linear dimensions.
+  This group value is the negative of the angle between the OCS X axis
+  and the UCS X axis. It is always in the XY plane of the OCS.
+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)
+3 Dimension style name
+
+Aligned Dimension Group Codes
+
+100 Subclass marker (AcDbAlignedDimension)
+12 Insertion point for clones of a dimension-Baseline and Continue (in OCS) DXF: X value; APP: 3D point
+22, 32 DXF: Y and Z values of insertion point for clones of a dimension-Baseline and Continue (in OCS)
+13 Definition point for linear and angular dimensions (in WCS) DXF: X value; APP: 3D point
+23, 33 DXF: Y and Z values of definition point for linear and angular dimensions (in WCS)
+14 Definition point for linear and angular dimensions (in WCS) DXF: X value; APP: 3D point
+24, 34 DXF: Y and Z values of definition point for linear and angular dimensions (in WCS)
+
+  |--text--|->10,20
+  |        |
+  |        |
+  X->14,24 X->13,23
+}
+procedure TvDXFVectorialReader.ReadENTITIES_DIMENSION(ATokens: TDXFTokens;
+  AData: TvVectorialDocument);
+var
+  CurToken: TDXFToken;
+  i: Integer;
+  // DIMENSION
+  BaseLeft, BaseRight, DimensionRight, DimensionLeft, TmpPoint: T3DPoint;
+  IsAlignedDimension: Boolean = False;
+begin
+  // Initial values
+  BaseLeft.X := 0;
+  BaseLeft.Y := 0;
+  BaseRight.X := 0;
+  BaseRight.X := 0;
+  DimensionRight.X := 0;
+  DimensionRight.Y := 0;
+  DimensionLeft.X := 0;
+  DimensionLeft.Y := 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 in [10, 20, 30, 11, 21, 31, 13, 23, 33, 14, 24, 34] then
+    begin
+      CurToken.FloatValue :=  StrToFloat(Trim(CurToken.StrValue), FPointSeparator);
+    end;
+
+    case CurToken.GroupCode of
+      10: DimensionRight.X := CurToken.FloatValue;
+      20: DimensionRight.Y := CurToken.FloatValue;
+      30: DimensionRight.Z := CurToken.FloatValue;
+      13: BaseRight.X := CurToken.FloatValue;
+      23: BaseRight.Y := CurToken.FloatValue;
+      33: BaseRight.Z := CurToken.FloatValue;
+      14: BaseLeft.X := CurToken.FloatValue;
+      24: BaseLeft.Y := CurToken.FloatValue;
+      34: BaseLeft.Z := CurToken.FloatValue;
+      100:
+      begin
+        if CurToken.StrValue = 'AcDbAlignedDimension' then IsAlignedDimension := True;
+      end;
+    end;
+  end;
+
+  // And now write it
+  {$ifdef FPVECTORIALDEBUG}
+//  WriteLn(Format('Adding Line from %f,%f to %f,%f', [LineStartX, LineStartY, LineEndX, LineEndY]));
+  {$endif}
+  if IsAlignedDimension then
+  begin
+    // Now make sure that we actually that BaseLeft is to the left of BaseRight
+    if BaseRight.X < BaseLeft.X then
+    begin
+      TmpPoint := BaseRight;
+      BaseRight := BaseLeft;
+      BaseLeft := TmpPoint;
+    end;
+
+    // Now check if we are a horizontal or vertical dimension
+
+    // horizontal
+    //
+    //DL____ DR
+    //  |  |
+    //  |  |
+    // BL  BR
+    if DimensionRight.X = BaseRight.X then
+    begin
+      DimensionLeft.X := BaseLeft.X;
+      DimensionLeft.Y := DimensionRight.Y;
+    end
+    // vertical
+    //
+    // BL ----|DR
+    //  BR  --|DL
+    //
+    // In this case we invert then DR and DL
+    else if DimensionRight.Y = BaseLeft.Y then
+    begin
+      DimensionLeft := DimensionRight;
+      DimensionRight.Y := BaseRight.Y;
+    end
+    // vertical
+    //
+    // BL ----|DL
+    //  BR  --|DR
+    //
+    else if DimensionRight.Y = BaseRight.Y then
+    begin
+      DimensionLeft.X := DimensionRight.X;
+      DimensionLeft.Y := BaseLeft.Y;
+    end;
+
+    AData.AddAlignedDimension(BaseLeft, BaseRight, DimensionLeft, DimensionRight);
+  end;
+end;
+
 {
 {
 100 Subclass marker (AcDbEllipse)
 100 Subclass marker (AcDbEllipse)
 10 Center point (in WCS) DXF: X value; APP: 3D point
 10 Center point (in WCS) DXF: X value; APP: 3D point
@@ -547,6 +799,10 @@ begin
     end;
     end;
   end;
   end;
 
 
+  // Position fixing for documents with negative coordinates
+  CenterX := CenterX - DOC_OFFSET.X;
+  CenterY := CenterY - DOC_OFFSET.Y;
+
   //
   //
   AData.AddEllipse(CenterX, CenterY, CenterZ, MajorHalfAxis, MinorHalfAxis, Angle);
   AData.AddEllipse(CenterX, CenterY, CenterZ, MajorHalfAxis, MinorHalfAxis, Angle);
 end;
 end;
@@ -614,6 +870,10 @@ begin
     end;
     end;
   end;
   end;
 
 
+  // Position fixing for documents with negative coordinates
+  PosX := PosX - DOC_OFFSET.X;
+  PosY := PosY - DOC_OFFSET.Y;
+
   //
   //
   AData.AddText(PosX, PosY, PosZ, '', Round(FontSize), Str);
   AData.AddText(PosX, PosY, PosZ, '', Round(FontSize), Str);
 end;
 end;

+ 32 - 0
packages/fpvectorial/src/fpvectorial.pas

@@ -39,6 +39,12 @@ const
   STR_WINMETAFILE_EXTENSION = '.wmf';
   STR_WINMETAFILE_EXTENSION = '.wmf';
 
 
 type
 type
+  T3DPoint = record
+    X, Y, Z: Double;
+  end;
+
+  P3DPoint = ^T3DPoint;
+
   TSegmentType = (
   TSegmentType = (
     st2DLine, st2DBezier,
     st2DLine, st2DBezier,
     st3DLine, st3DBezier, stMoveTo);
     st3DLine, st3DBezier, stMoveTo);
@@ -159,6 +165,17 @@ type
     procedure CalculateBoundingRectangle;
     procedure CalculateBoundingRectangle;
   end;
   end;
 
 
+  {@@
+  }
+
+  { TvAlignedDimension }
+
+  TvAlignedDimension = class(TvEntity)
+  public
+    // Mandatory fields
+    BaseLeft, BaseRight, DimensionLeft, DimensionRight: T3DPoint;
+  end;
+
 type
 type
 
 
   TvCustomVectorialWriter = class;
   TvCustomVectorialWriter = class;
@@ -216,6 +233,8 @@ type
     procedure AddCircle(ACenterX, ACenterY, ACenterZ, ARadius: Double);
     procedure AddCircle(ACenterX, ACenterY, ACenterZ, ARadius: Double);
     procedure AddCircularArc(ACenterX, ACenterY, ACenterZ, ARadius, AStartAngle, AEndAngle: Double);
     procedure AddCircularArc(ACenterX, ACenterY, ACenterZ, ARadius, AStartAngle, AEndAngle: Double);
     procedure AddEllipse(CenterX, CenterY, CenterZ, MajorHalfAxis, MinorHalfAxis, Angle: Double);
     procedure AddEllipse(CenterX, CenterY, CenterZ, MajorHalfAxis, MinorHalfAxis, Angle: Double);
+    // Dimensions
+    procedure AddAlignedDimension(BaseLeft, BaseRight, DimLeft, DimRight: T3DPoint);
     { properties }
     { properties }
     property PathCount: Integer read GetPathCount;
     property PathCount: Integer read GetPathCount;
     property Paths[Index: Cardinal]: TPath read GetPath;
     property Paths[Index: Cardinal]: TPath read GetPath;
@@ -642,6 +661,19 @@ begin
   FEntities.Add(lEllipse);
   FEntities.Add(lEllipse);
 end;
 end;
 
 
+procedure TvVectorialDocument.AddAlignedDimension(BaseLeft, BaseRight,
+  DimLeft, DimRight: T3DPoint);
+var
+  lDim: TvAlignedDimension;
+begin
+  lDim := TvAlignedDimension.Create;
+  lDim.BaseLeft := BaseLeft;
+  lDim.BaseRight := BaseRight;
+  lDim.DimensionLeft := DimLeft;
+  lDim.DimensionRight := DimRight;
+  FEntities.Add(lDim);
+end;
+
 {@@
 {@@
   Convenience method which creates the correct
   Convenience method which creates the correct
   writer object for a given vector graphics document format.
   writer object for a given vector graphics document format.

+ 185 - 24
packages/fpvectorial/src/fpvtocanvas.pas

@@ -11,7 +11,7 @@ uses
   {$ifdef USE_LCL_CANVAS}
   {$ifdef USE_LCL_CANVAS}
   Graphics, LCLIntf,
   Graphics, LCLIntf,
   {$else}
   {$else}
-  fpcanvas,
+  fpcanvas, fpimage,
   {$endif}
   {$endif}
   fpvectorial;
   fpvectorial;
 
 
@@ -25,6 +25,10 @@ procedure DrawFPVectorialToCanvas(ASource: TvVectorialDocument;
 
 
 implementation
 implementation
 
 
+{$ifndef Windows}
+{$define FPVECTORIALDEBUG}
+{$endif}
+
 function Rotate2DPoint(P,Fix :TPoint; alpha:double): TPoint;
 function Rotate2DPoint(P,Fix :TPoint; alpha:double): TPoint;
 var
 var
   sinus, cosinus : Extended;
   sinus, cosinus : Extended;
@@ -97,6 +101,17 @@ procedure DrawFPVectorialToCanvas(ASource: TvVectorialDocument;
   ADest: TFPCustomCanvas;
   ADest: TFPCustomCanvas;
   {$endif}
   {$endif}
   ADestX: Integer = 0; ADestY: Integer = 0; AMulX: Double = 1.0; AMulY: Double = 1.0);
   ADestX: Integer = 0; ADestY: Integer = 0; AMulX: Double = 1.0; AMulY: Double = 1.0);
+
+  function CoordToCanvasX(ACoord: Double): Integer;
+  begin
+    Result := Round(ADestX + AmulX * ACoord);
+  end;
+
+  function CoordToCanvasY(ACoord: Double): Integer;
+  begin
+    Result := Round(ADestY + AmulY * ACoord);
+  end;
+
 var
 var
   i, j, k: Integer;
   i, j, k: Integer;
   PosX, PosY: Integer; // Not modified by ADestX, etc
   PosX, PosY: Integer; // Not modified by ADestX, etc
@@ -113,7 +128,15 @@ var
   CurEntity: TvEntity;
   CurEntity: TvEntity;
   CurCircle: TvCircle;
   CurCircle: TvCircle;
   CurEllipse: TvEllipse;
   CurEllipse: TvEllipse;
+  //
   CurArc: TvCircularArc;
   CurArc: TvCircularArc;
+  FinalStartAngle, FinalEndAngle, tmpAngle: double;
+  BoundsLeft, BoundsTop, BoundsRight, BoundsBottom,
+   IntStartAngle, IntAngleLength, IntTmp: Integer;
+  //
+  CurDim: TvAlignedDimension;
+  Points: array of TPoint;
+  UpperDim, LowerDim: T3DPoint;
 begin
 begin
   {$ifdef FPVECTORIALDEBUG}
   {$ifdef FPVECTORIALDEBUG}
   WriteLn(':>DrawFPVectorialToCanvas');
   WriteLn(':>DrawFPVectorialToCanvas');
@@ -121,6 +144,7 @@ begin
 
 
   PosX := 0;
   PosX := 0;
   PosY := 0;
   PosY := 0;
+  ADest.Brush.Style := bsClear;
 
 
   ADest.MoveTo(ADestX, ADestY);
   ADest.MoveTo(ADestX, ADestY);
 
 
@@ -138,17 +162,11 @@ begin
       case CurSegment.SegmentType of
       case CurSegment.SegmentType of
       stMoveTo:
       stMoveTo:
       begin
       begin
-        ADest.MoveTo(
-          Round(ADestX + AMulX * Cur2DSegment.X),
-          Round(ADestY + AMulY * Cur2DSegment.Y)
-          );
+        ADest.MoveTo(CoordToCanvasX(Cur2DSegment.X), CoordToCanvasY(Cur2DSegment.Y));
       end;
       end;
       st2DLine, st3DLine:
       st2DLine, st3DLine:
       begin
       begin
-        ADest.LineTo(
-          Round(ADestX + AMulX * Cur2DSegment.X),
-          Round(ADestY + AMulY * Cur2DSegment.Y)
-          );
+        ADest.LineTo(CoordToCanvasX(Cur2DSegment.X), CoordToCanvasY(Cur2DSegment.Y));
       end;
       end;
       { To draw a bezier we need to divide the interval in parts and make
       { To draw a bezier we need to divide the interval in parts and make
         lines between this parts }
         lines between this parts }
@@ -164,9 +182,7 @@ begin
           t := k / CurveLength;
           t := k / CurveLength;
           CurX := Round(sqr(1 - t) * (1 - t) * PosX + 3 * t * sqr(1 - t) * Cur2DBSegment.X2 + 3 * t * t * (1 - t) * Cur2DBSegment.X3 + t * t * t * Cur2DBSegment.X);
           CurX := Round(sqr(1 - t) * (1 - t) * PosX + 3 * t * sqr(1 - t) * Cur2DBSegment.X2 + 3 * t * t * (1 - t) * Cur2DBSegment.X3 + t * t * t * Cur2DBSegment.X);
           CurY := Round(sqr(1 - t) * (1 - t) * PosY + 3 * t * sqr(1 - t) * Cur2DBSegment.Y2 + 3 * t * t * (1 - t) * Cur2DBSegment.Y3 + t * t * t * Cur2DBSegment.Y);
           CurY := Round(sqr(1 - t) * (1 - t) * PosY + 3 * t * sqr(1 - t) * Cur2DBSegment.Y2 + 3 * t * t * (1 - t) * Cur2DBSegment.Y3 + t * t * t * Cur2DBSegment.Y);
-          ADest.LineTo(
-            Round(ADestX + AMulX * CurX),
-            Round(ADestY + AMulY * CurY));
+          ADest.LineTo(CoordToCanvasX(CurX), CoordToCanvasY(CurY));
         end;
         end;
         PosX := Round(Cur2DBSegment.X);
         PosX := Round(Cur2DBSegment.X);
         PosY := Round(Cur2DBSegment.Y);
         PosY := Round(Cur2DBSegment.Y);
@@ -183,10 +199,10 @@ begin
     begin
     begin
       CurCircle := CurEntity as TvCircle;
       CurCircle := CurEntity as TvCircle;
       ADest.Ellipse(
       ADest.Ellipse(
-        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))
+        CoordToCanvasX(CurCircle.CenterX - CurCircle.Radius),
+        CoordToCanvasY(CurCircle.CenterY - CurCircle.Radius),
+        CoordToCanvasX(CurCircle.CenterX + CurCircle.Radius),
+        CoordToCanvasY(CurCircle.CenterY + CurCircle.Radius)
         );
         );
     end
     end
     else if CurEntity is TvEllipse then
     else if CurEntity is TvEllipse then
@@ -198,16 +214,155 @@ begin
     begin
     begin
       CurArc := CurEntity as TvCircularArc;
       CurArc := CurEntity as TvCircularArc;
       {$ifdef USE_LCL_CANVAS}
       {$ifdef USE_LCL_CANVAS}
+      // ToDo: Consider a X axis inversion
+      // If the Y axis is inverted, then we need to mirror our angles as well
+      BoundsLeft := CoordToCanvasX(CurArc.CenterX - CurArc.Radius);
+      BoundsTop := CoordToCanvasY(CurArc.CenterY - CurArc.Radius);
+      BoundsRight := CoordToCanvasX(CurArc.CenterX + CurArc.Radius);
+      BoundsBottom := CoordToCanvasY(CurArc.CenterY + CurArc.Radius);
+      {if AMulY > 0 then
+      begin}
+        FinalStartAngle := CurArc.StartAngle;
+        FinalEndAngle := CurArc.EndAngle;
+      {end
+      else // AMulY is negative
+      begin
+        // Inverting the angles generates the correct result for Y axis inversion
+        if CurArc.EndAngle = 0 then FinalStartAngle := 0
+        else FinalStartAngle := 360 - 1* CurArc.EndAngle;
+        if CurArc.StartAngle = 0 then FinalEndAngle := 0
+        else FinalEndAngle := 360 - 1* CurArc.StartAngle;
+      end;}
+      IntStartAngle := Round(16*FinalStartAngle);
+      IntAngleLength := Round(16*(FinalEndAngle - FinalStartAngle));
+      // On Gtk2 and Carbon, the Left really needs to be to the Left of the Right position
+      // The same for the Top and Bottom
+      // On Windows it works fine either way
+      // On Gtk2 if the positions are inverted then the arcs are screwed up
+      // In Carbon if the positions are inverted, then the arc is inverted
+      if BoundsLeft > BoundsRight then
+      begin
+        IntTmp := BoundsLeft;
+        BoundsLeft := BoundsRight;
+        BoundsRight := IntTmp;
+      end;
+      if BoundsTop > BoundsBottom then
+      begin
+        IntTmp := BoundsTop;
+        BoundsTop := BoundsBottom;
+        BoundsBottom := IntTmp;
+      end;
       // Arc(ALeft, ATop, ARight, ABottom, Angle16Deg, Angle16DegLength: Integer);
       // Arc(ALeft, ATop, ARight, ABottom, Angle16Deg, Angle16DegLength: Integer);
+      {$ifdef FPVECTORIALDEBUG}
+      WriteLn(Format('Drawing Arc Center=%f,%f Radius=%f StartAngle=%f AngleLength=%f',
+        [CurArc.CenterX, CurArc.CenterY, CurArc.Radius, IntStartAngle/16, IntAngleLength/16]));
+      {$endif}
       ADest.Arc(
       ADest.Arc(
-        Round(ADestX + AmulX * (CurArc.CenterX - CurArc.Radius)),
-        Round(ADestY + AmulY * (CurArc.CenterY - CurArc.Radius)),
-        Round(ADestX + AmulX * (CurArc.CenterX + CurArc.Radius)),
-        Round(ADestY + AmulY * (CurArc.CenterY + CurArc.Radius)),
-        Round(16*CurArc.StartAngle),
-        Round(16*CurArc.EndAngle - CurArc.StartAngle)
+        BoundsLeft, BoundsTop, BoundsRight, BoundsBottom,
+        IntStartAngle, IntAngleLength
         );
         );
+      // Debug info
+//      {$define FPVECTORIALDEBUG}
+//      {$ifdef FPVECTORIALDEBUG}
+//      WriteLn(Format('Drawing Arc x1y1=%d,%d x2y2=%d,%d start=%d end=%d',
+//        [BoundsLeft, BoundsTop, BoundsRight, BoundsBottom, IntStartAngle, IntAngleLength]));
+//      {$endif}
+{      ADest.TextOut(CoordToCanvasX(CurArc.CenterX), CoordToCanvasY(CurArc.CenterY),
+        Format('R=%d S=%d L=%d', [Round(CurArc.Radius*AMulX), Round(FinalStartAngle),
+        Abs(Round((FinalEndAngle - FinalStartAngle)))]));
+      ADest.Pen.Color := TColor($DDDDDD);
+      ADest.Rectangle(
+        BoundsLeft, BoundsTop, BoundsRight, BoundsBottom);
+      ADest.Pen.Color := clBlack;}
       {$endif}
       {$endif}
+    end
+    else if CurEntity is TvAlignedDimension then
+    begin
+      CurDim := CurEntity as TvAlignedDimension;
+      //
+      // Draws this shape:
+      // vertical     horizontal
+      // ___
+      // | |     or   ---| X cm
+      //   |           --|
+      // Which marks the dimension
+      ADest.MoveTo(CoordToCanvasX(CurDim.BaseRight.X), CoordToCanvasY(CurDim.BaseRight.Y));
+      ADest.LineTo(CoordToCanvasX(CurDim.DimensionRight.X), CoordToCanvasY(CurDim.DimensionRight.Y));
+      ADest.LineTo(CoordToCanvasX(CurDim.DimensionLeft.X), CoordToCanvasY(CurDim.DimensionLeft.Y));
+      ADest.LineTo(CoordToCanvasX(CurDim.BaseLeft.X), CoordToCanvasY(CurDim.BaseLeft.Y));
+      // Now the arrows
+      // horizontal
+      SetLength(Points, 3);
+      if CurDim.DimensionRight.Y = CurDim.DimensionLeft.Y then
+      begin
+        {$ifdef USE_LCL_CANVAS}
+        ADest.Brush.Color := clBlack;
+        {$else}
+        ADest.Brush.FPColor := colBlack;
+        {$endif}
+        ADest.Brush.Style := bsSolid;
+        // Left arrow
+        Points[0] := Point(CoordToCanvasX(CurDim.DimensionLeft.X), CoordToCanvasY(CurDim.DimensionLeft.Y));
+        Points[1] := Point(Points[0].X + 7, Points[0].Y - 3);
+        Points[2] := Point(Points[0].X + 7, Points[0].Y + 3);
+        ADest.Polygon(Points);
+        // Right arrow
+        Points[0] := Point(CoordToCanvasX(CurDim.DimensionRight.X), CoordToCanvasY(CurDim.DimensionRight.Y));
+        Points[1] := Point(Points[0].X - 7, Points[0].Y - 3);
+        Points[2] := Point(Points[0].X - 7, Points[0].Y + 3);
+        ADest.Polygon(Points);
+        ADest.Brush.Style := bsClear;
+        // Dimension text
+        Points[0].X := CoordToCanvasX((CurDim.DimensionLeft.X+CurDim.DimensionRight.X)/2);
+        Points[0].Y := CoordToCanvasY(CurDim.DimensionLeft.Y);
+        LowerDim.X := CurDim.DimensionRight.X-CurDim.DimensionLeft.X;
+        ADest.Font.Size := 10;
+        ADest.TextOut(Points[0].X, Points[0].Y, Format('%.1f', [LowerDim.X]));
+      end
+      else
+      begin
+        {$ifdef USE_LCL_CANVAS}
+        ADest.Brush.Color := clBlack;
+        {$else}
+        ADest.Brush.FPColor := colBlack;
+        {$endif}
+        ADest.Brush.Style := bsSolid;
+        // There is no upper/lower preference for DimensionLeft/Right, so we need to check
+        if CurDim.DimensionLeft.Y > CurDim.DimensionRight.Y then
+        begin
+          UpperDim := CurDim.DimensionLeft;
+          LowerDim := CurDim.DimensionRight;
+        end
+        else
+        begin
+          UpperDim := CurDim.DimensionRight;
+          LowerDim := CurDim.DimensionLeft;
+        end;
+        // Upper arrow
+        Points[0] := Point(CoordToCanvasX(UpperDim.X), CoordToCanvasY(UpperDim.Y));
+        Points[1] := Point(Points[0].X + Round(AMulX), Points[0].Y - Round(AMulY*3));
+        Points[2] := Point(Points[0].X - Round(AMulX), Points[0].Y - Round(AMulY*3));
+        ADest.Polygon(Points);
+        // Lower arrow
+        Points[0] := Point(CoordToCanvasX(LowerDim.X), CoordToCanvasY(LowerDim.Y));
+        Points[1] := Point(Points[0].X + Round(AMulX), Points[0].Y + Round(AMulY*3));
+        Points[2] := Point(Points[0].X - Round(AMulX), Points[0].Y + Round(AMulY*3));
+        ADest.Polygon(Points);
+        ADest.Brush.Style := bsClear;
+        // Dimension text
+        Points[0].X := CoordToCanvasX(CurDim.DimensionLeft.X);
+        Points[0].Y := CoordToCanvasY((CurDim.DimensionLeft.Y+CurDim.DimensionRight.Y)/2);
+        LowerDim.Y := CurDim.DimensionRight.Y-CurDim.DimensionLeft.Y;
+        if LowerDim.Y < 0 then LowerDim.Y := -1 * LowerDim.Y;
+        ADest.Font.Size := 10;
+        ADest.TextOut(Points[0].X, Points[0].Y, Format('%.1f', [LowerDim.Y]));
+      end;
+      SetLength(Points, 0);
+{      // Debug info
+      ADest.TextOut(CoordToCanvasX(CurDim.BaseRight.X), CoordToCanvasY(CurDim.BaseRight.Y), 'BR');
+      ADest.TextOut(CoordToCanvasX(CurDim.DimensionRight.X), CoordToCanvasY(CurDim.DimensionRight.Y), 'DR');
+      ADest.TextOut(CoordToCanvasX(CurDim.DimensionLeft.X), CoordToCanvasY(CurDim.DimensionLeft.Y), 'DL');
+      ADest.TextOut(CoordToCanvasX(CurDim.BaseLeft.X), CoordToCanvasY(CurDim.BaseLeft.Y), 'BL');}
     end;
     end;
   end;
   end;
 
 
@@ -215,10 +370,16 @@ begin
   for i := 0 to ASource.GetTextCount - 1 do
   for i := 0 to ASource.GetTextCount - 1 do
   begin
   begin
     CurText := ASource.GetText(i);
     CurText := ASource.GetText(i);
-    ADest.Font.Height := Round(AmulY * CurText.FontSize);
+    ADest.Font.Size := Round(AmulX * CurText.FontSize);
     ADest.Pen.Style := psSolid;
     ADest.Pen.Style := psSolid;
+    {$ifdef USE_LCL_CANVAS}
     ADest.Pen.Color := clBlack;
     ADest.Pen.Color := clBlack;
-    ADest.TextOut(Round(CurText.X), Round(CurText.Y), CurText.Value);
+    {$else}
+    ADest.Pen.FPColor := colBlack;
+    {$endif}
+    ADest.Brush.Style := bsClear;
+    LowerDim.Y := CurText.Y + CurText.FontSize;
+    ADest.TextOut(CoordToCanvasX(CurText.X), CoordToCanvasY(LowerDim.Y), CurText.Value);
   end;
   end;
 
 
   {$ifdef FPVECTORIALDEBUG}
   {$ifdef FPVECTORIALDEBUG}