Browse Source

Initial transformation of fpvectorial segments in classes, as well as making the number of segments dynamic

git-svn-id: trunk@16202 -
sekelsenmat 15 years ago
parent
commit
cc2da23316

+ 14 - 12
packages/fpvectorial/examples/fpvc_mainform.lfm

@@ -10,26 +10,28 @@ object formVectorialConverter: TformVectorialConverter
   LCLVersion = '0.9.29'
   object Label1: TLabel
     Left = 8
-    Height = 14
-    Top = 104
-    Width = 123
+    Height = 17
+    Top = 112
+    Width = 160
     Caption = 'Location of the Input file:'
     ParentColor = False
   end
   object Label2: TLabel
     Left = 11
-    Height = 96
+    Height = 104
     Top = 8
-    Width = 224
+    Width = 229
     AutoSize = False
     Caption = 'This converter application use the fpvectorial library to convert between various different vectorial graphics formats. The type is detected from the extension and the supported types are: PDF (*.pdf), SVG (*.svg) and Corel Draw file (*.cdr).'
+    Font.Height = -12
     ParentColor = False
+    ParentFont = False
     WordWrap = True
   end
   object editInput: TFileNameEdit
     Left = 8
-    Height = 21
-    Top = 120
+    Height = 22
+    Top = 128
     Width = 192
     DialogOptions = []
     FilterIndex = 0
@@ -41,16 +43,16 @@ object formVectorialConverter: TformVectorialConverter
   end
   object Label3: TLabel
     Left = 8
-    Height = 14
-    Top = 144
-    Width = 132
+    Height = 17
+    Top = 152
+    Width = 173
     Caption = 'Full path of the Output file:'
     ParentColor = False
   end
   object editOutput: TFileNameEdit
     Left = 8
-    Height = 21
-    Top = 160
+    Height = 22
+    Top = 168
     Width = 192
     DialogOptions = []
     FilterIndex = 0

+ 5 - 1
packages/fpvectorial/examples/fpvectorialconverter.lpi

@@ -8,6 +8,7 @@
         <AlwaysBuild Value="False"/>
       </Flags>
       <SessionStorage Value="InProjectDir"/>
+      <MainUnit Value="0"/>
       <Title Value="fpvectorialconverter"/>
       <UseXPManifest Value="True"/>
       <Icon Value="0"/>
@@ -18,6 +19,9 @@
     <VersionInfo>
       <StringTable ProductVersion=""/>
     </VersionInfo>
+    <BuildModes Count="1">
+      <Item1 Name="default" Default="True"/>
+    </BuildModes>
     <PublishOptions>
       <Version Value="2"/>
       <IncludeFileFilter Value="*.(pas|pp|inc|lfm|lpr|lrs|lpi|lpk|sh|xml)"/>
@@ -55,7 +59,7 @@
       <Filename Value="fpvectorialconverter"/>
     </Target>
     <SearchPaths>
-      <IncludeFiles Value="$(ProjOutDir)\"/>
+      <IncludeFiles Value="$(ProjOutDir)"/>
       <UnitOutputDirectory Value="lib\$(TargetCPU)-$(TargetOS)"/>
     </SearchPaths>
     <Linking>

+ 195 - 66
packages/fpvectorial/src/fpvectorial.pas

@@ -48,20 +48,64 @@ type
     the starting point is in the bottom-left corner of the document.
     The X grows to the right and the Y grows to the top.
   }
-  TPathSegment = record
+  { TPathSegment }
+
+  TPathSegment = class
+  public
     SegmentType: TSegmentType;
-    X, Y, Z: Double; // Z is ignored in 2D segments
-    X2, Y2, Z2: Double; // Z is ignored in 2D segments
-    X3, Y3, Z3: Double; // Z is ignored in 2D segments
+    // Fields for linking the list
+    Previous: TPathSegment;
+    Next: TPathSegment;
   end;
 
-  TPath = record
-    Len: Integer;
-    // ToDo: make the array dynamic
-    Points: array[0..255] of TPathSegment;
+  {@@
+    In a 2D segment, the X and Y coordinates represent usually the
+    final point of the segment, being that it starts where the previous
+    segment ends. The exception is for the first segment of all, which simply
+    holds the starting point for the drawing and should always be of the type
+    stMoveTo.
+  }
+  T2DSegment = class(TPathSegment)
+  public
+    X, Y: Double;
+  end;
+
+  {@@
+    In Bezier segments, we remain using the X and Y coordinates for the ending point.
+    The starting point is where the previous segment ended, so that the intermediary
+    bezier control points are [X2, Y2] and [X3, Y3].
+  }
+  T2DBezierSegment = class(T2DSegment)
+  public
+    X2, Y2: Double;
+    X3, Y3: Double;
   end;
 
-  PPath = ^TPath;
+  T3DSegment = class(TPathSegment)
+  public
+    {@@
+      Coordinates of the end of the segment.
+      For the first segment, this is the starting point.
+    }
+    X, Y, Z: Double;
+  end;
+
+  T3DBezierSegment = class(T3DSegment)
+  public
+    X2, Y2, Z2: Double;
+    X3, Y3, Z3: Double;
+  end;
+
+  TPath = class
+    Len: Integer;
+    Points: TPathSegment; // Beginning of the double-linked list
+    PointsEnd: TPathSegment; // End of the double-linked list
+    CurPoint: TPathSegment; // Used in PrepareForSequentialReading and Next
+    procedure Assign(APath: TPath);
+    function Count(): TPathSegment;
+    procedure PrepareForSequentialReading;
+    function Next(): TPathSegment;
+  end;
 
   {@@
     TvText represents a text in memory.
@@ -69,16 +113,14 @@ type
     At the moment fonts are unsupported, only simple texts
     up to 255 chars are supported.
   }
-
-  TvText = record
+  TvText = class
+  public
     X, Y, Z: Double; // Z is ignored in 2D formats
     FontSize: integer;
     FontName: utf8string;
     Value: utf8string;
   end;
 
-  PText = ^TvText;
-
 type
 
   TvCustomVectorialWriter = class;
@@ -95,6 +137,8 @@ type
     procedure RemoveCallback(data, arg: pointer);
     function CreateVectorialWriter(AFormat: TvVectorialFormat): TvCustomVectorialWriter;
     function CreateVectorialReader(AFormat: TvVectorialFormat): TvCustomVectorialReader;
+    procedure ClearTmpPath();
+    procedure AppendSegmentToTmpPath(ASegment: TPathSegment);
   public
     Name: string;
     Width, Height: Double; // in millimeters
@@ -185,6 +229,9 @@ procedure RegisterVectorialWriter(
 
 implementation
 
+const
+  Str_Error_Nil_Path = ' The program attempted to add a segment before creating a path';
+
 {@@
   Registers a new reader for a format
 }
@@ -276,7 +323,11 @@ end;
 }
 procedure TvVectorialDocument.RemoveCallback(data, arg: pointer);
 begin
-  if data <> nil then FreeMem(data);
+{  if data <> nil then
+  begin
+    ldata := PObject(data);
+    ldata^.Free;
+  end;}
 end;
 
 {@@
@@ -288,6 +339,7 @@ begin
 
   FPaths := TFPList.Create;
   FTexts := TFPList.Create;
+  FTmpPath := TPath.Create;
 end;
 
 {@@
@@ -308,28 +360,27 @@ end;
 }
 procedure TvVectorialDocument.RemoveAllPaths;
 begin
-  FPaths.ForEachCall(RemoveCallback, nil);
+//  FPaths.ForEachCall(RemoveCallback, nil);
   FPaths.Clear;
 end;
 
 procedure TvVectorialDocument.RemoveAllTexts;
 begin
-  FTexts.ForEachCall(RemoveCallback, nil);
+//  FTexts.ForEachCall(RemoveCallback, nil);
   FTexts.Clear;
 end;
 
 procedure TvVectorialDocument.AddPath(APath: TPath);
 var
-  Path: PPath;
+  lPath: TPath;
   Len: Integer;
 begin
-  Len := SizeOf(TPath);
+  lPath := TPath.Create;
+  lPath.Assign(APath);
+  FPaths.Add(Pointer(lPath));
   //WriteLn(':>TvVectorialDocument.AddPath 1 Len = ', Len);
-  Path := GetMem(Len);
   //WriteLn(':>TvVectorialDocument.AddPath 2');
-  Move(APath, Path^, Len);
   //WriteLn(':>TvVectorialDocument.AddPath 3');
-  FPaths.Add(Path);
   //WriteLn(':>TvVectorialDocument.AddPath 4');
 end;
 
@@ -341,11 +392,19 @@ end;
   @see    StartPath, AddPointToPath
 }
 procedure TvVectorialDocument.StartPath(AX, AY: Double);
+var
+  segment: T2DSegment;
 begin
+  ClearTmpPath();
+
   FTmpPath.Len := 1;
-  FTmpPath.Points[0].SegmentType := stMoveTo;
-  FTmpPath.Points[0].X := AX;
-  FTmpPath.Points[0].Y := AY;
+  segment := T2DSegment.Create;
+  segment.SegmentType := stMoveTo;
+  segment.X := AX;
+  segment.Y := AY;
+
+  FTmpPath.Points := segment;
+  FTmpPath.PointsEnd := segment;
 end;
 
 {@@
@@ -360,60 +419,69 @@ end;
 }
 procedure TvVectorialDocument.AddLineToPath(AX, AY: Double);
 var
-  L: Integer;
+  segment: T2DSegment;
 begin
-  L := FTmpPath.Len;
-  Inc(FTmpPath.Len);
-  FTmpPath.Points[L].SegmentType := st2DLine;
-  FTmpPath.Points[L].X := AX;
-  FTmpPath.Points[L].Y := AY;
+  segment := T2DSegment.Create;
+  segment.SegmentType := st2DLine;
+  segment.X := AX;
+  segment.Y := AY;
+
+  AppendSegmentToTmpPath(segment);
 end;
 
 procedure TvVectorialDocument.AddLineToPath(AX, AY, AZ: Double);
 var
-  L: Integer;
+  segment: T3DSegment;
 begin
-  L := FTmPPath.Len;
-  Inc(FTmPPath.Len);
-  FTmPPath.Points[L].SegmentType := st3DLine;
-  FTmPPath.Points[L].X := AX;
-  FTmPPath.Points[L].Y := AY;
-  FTmPPath.Points[L].Z := AZ;
+  segment := T3DSegment.Create;
+  segment.SegmentType := st3DLine;
+  segment.X := AX;
+  segment.Y := AY;
+  segment.Z := AZ;
+
+  AppendSegmentToTmpPath(segment);
 end;
 
+{@@
+  Adds a bezier element to the path. It starts where the previous element ended
+  and it goes throw the control points [AX1, AY1] and [AX2, AY2] and ends
+  in [AX3, AY3].
+}
 procedure TvVectorialDocument.AddBezierToPath(AX1, AY1, AX2, AY2, AX3,
   AY3: Double);
 var
-  L: Integer;
+  segment: T2DBezierSegment;
 begin
-  L := FTmPPath.Len;
-  Inc(FTmPPath.Len);
-  FTmPPath.Points[L].SegmentType := st2DBezier;
-  FTmPPath.Points[L].X := AX3;
-  FTmPPath.Points[L].Y := AY3;
-  FTmPPath.Points[L].X2 := AX1;
-  FTmPPath.Points[L].Y2 := AY1;
-  FTmPPath.Points[L].X3 := AX2;
-  FTmPPath.Points[L].Y3 := AY2;
+  segment := T2DBezierSegment.Create;
+  segment.SegmentType := st2DBezier;
+  segment.X := AX3;
+  segment.Y := AY3;
+  segment.X2 := AX1;
+  segment.Y2 := AY1;
+  segment.X3 := AX2;
+  segment.Y3 := AY2;
+
+  AppendSegmentToTmpPath(segment);
 end;
 
 procedure TvVectorialDocument.AddBezierToPath(AX1, AY1, AZ1, AX2, AY2, AZ2,
   AX3, AY3, AZ3: Double);
 var
-  L: Integer;
+  segment: T3DBezierSegment;
 begin
-  L := FTmPPath.Len;
-  Inc(FTmPPath.Len);
-  FTmPPath.Points[L].SegmentType := st3DBezier;
-  FTmPPath.Points[L].X := AX3;
-  FTmPPath.Points[L].Y := AY3;
-  FTmPPath.Points[L].Z := AZ3;
-  FTmPPath.Points[L].X2 := AX1;
-  FTmPPath.Points[L].Y2 := AY1;
-  FTmPPath.Points[L].Z2 := AZ1;
-  FTmPPath.Points[L].X3 := AX2;
-  FTmPPath.Points[L].Y3 := AY2;
-  FTmPPath.Points[L].Z3 := AZ2;
+  segment := T3DBezierSegment.Create;
+  segment.SegmentType := st3DBezier;
+  segment.X := AX3;
+  segment.Y := AY3;
+  segment.Z := AZ3;
+  segment.X2 := AX1;
+  segment.Y2 := AY1;
+  segment.Z2 := AZ1;
+  segment.X3 := AX2;
+  segment.Y3 := AY2;
+  segment.Z3 := AZ2;
+
+  AppendSegmentToTmpPath(segment);
 end;
 
 {@@
@@ -430,15 +498,14 @@ procedure TvVectorialDocument.EndPath();
 begin
   if FTmPPath.Len = 0 then Exit;
   AddPath(FTmPPath);
-  FTmPPath.Len := 0;
+  ClearTmpPath();
 end;
 
 procedure TvVectorialDocument.AddText(AX, AY, AZ: Double; FontName: string; FontSize: integer; AText: utf8string);
 var
-  lText: PText;
+  lText: TvText;
 begin
-  lText := GetMem(SizeOf(TvText));
-  FillChar(lText^, SizeOf(TvText), 0);
+  lText := TvText.Create;
   lText.Value := AText;
   lText.X := AX;
   lText.Y := AY;
@@ -495,6 +562,40 @@ begin
   if Result = nil then raise Exception.Create('Unsuported vector graphics format.');
 end;
 
+procedure TvVectorialDocument.ClearTmpPath();
+var
+  segment, oldsegment: TPathSegment;
+begin
+//  segment := FTmpPath.Points;
+// Don't free segments, because they are used when the path is added
+//  while segment <> nil do
+//  begin
+//    oldsegment := segment;
+//    segment := segment^.Next;
+//    oldsegment^.Free;
+//  end;
+
+  FTmpPath.Points := nil;
+  FTmpPath.PointsEnd := nil;
+  FTmpPath.Len := 0;
+end;
+
+procedure TvVectorialDocument.AppendSegmentToTmpPath(ASegment: TPathSegment);
+var
+  L: Integer;
+begin
+  if FTmpPath.PointsEnd = nil then
+    Exception.Create('[TvVectorialDocument.AppendSegmentToTmpPath]' + Str_Error_Nil_Path);
+
+  L := FTmpPath.Len;
+  Inc(FTmpPath.Len);
+
+  // Adds the element to the end of the list
+  FTmpPath.PointsEnd.Next := ASegment;
+  ASegment.Previous := FTmpPath.PointsEnd;
+  FTmpPath.PointsEnd := ASegment;
+end;
+
 {@@
   Writes the document to a file.
 
@@ -624,7 +725,7 @@ begin
 
   if FPaths.Items[ANum] = nil then raise Exception.Create('TvVectorialDocument.GetPath: Invalid Path number');
 
-  Result := PPath(FPaths.Items[ANum])^;
+  Result := TPath(FPaths.Items[ANum]);
 end;
 
 function TvVectorialDocument.GetPathCount: Integer;
@@ -638,7 +739,7 @@ begin
 
   if FTexts.Items[ANum] = nil then raise Exception.Create('TvVectorialDocument.GetText: Invalid Text number');
 
-  Result := PText(FTexts.Items[ANum])^;
+  Result := TvText(FTexts.Items[ANum]);
 end;
 
 function TvVectorialDocument.GetTextCount: Integer;
@@ -751,6 +852,34 @@ begin
 
 end;
 
+{ TPath }
+
+procedure TPath.Assign(APath: TPath);
+begin
+  Len := APath.Len;
+  Points := APath.Points;
+  PointsEnd := APath.PointsEnd;
+  CurPoint := APath.CurPoint;
+end;
+
+function TPath.Count(): TPathSegment;
+begin
+
+end;
+
+procedure TPath.PrepareForSequentialReading;
+begin
+  CurPoint := nil;
+end;
+
+function TPath.Next(): TPathSegment;
+begin
+  if CurPoint = nil then Result := Points
+  else Result := CurPoint.Next;
+
+  CurPoint := Result;
+end;
+
 finalization
 
   SetLength(GvVectorialFormats, 0);

+ 16 - 11
packages/fpvectorial/src/fpvtocanvas.pas

@@ -34,6 +34,8 @@ var
   i, j, k: Integer;
   PosX, PosY: Integer; // Not modified by ADestX, etc
   CurSegment: TPathSegment;
+  Cur2DSegment: T2DSegment absolute CurSegment;
+  Cur2DBSegment: T2DBezierSegment absolute CurSegment;
   // For bezier
   CurX, CurY: Integer; // Not modified by ADestX, etc
   CurveLength: Integer;
@@ -51,15 +53,18 @@ begin
   for i := 0 to ASource.PathCount - 1 do
   begin
     //WriteLn('i = ', i);
-    for j := 0 to Length(ASource.Paths[i].Points) - 1 do
+    ASource.Paths[i].PrepareForSequentialReading;
+
+    for j := 0 to ASource.Paths[i].Len - 1 do
     begin
       //WriteLn('j = ', j);
-      CurSegment := ASource.Paths[i].Points[j];
+      CurSegment := TPathSegment(ASource.Paths[i].Next());
+
       case CurSegment.SegmentType of
       st2DLine, st3DLine:
       begin
-        PosX := Round(CurSegment.X);
-        PosY := Round(CurSegment.Y);
+        PosX := Round(Cur2DSegment.X);
+        PosY := Round(Cur2DSegment.Y);
         ADest.LineTo(
           Round(ADestX + AMulX * PosX),
           Round(ADestY + AMulY * PosY)
@@ -70,21 +75,21 @@ begin
       st2DBezier, st3DBezier:
       begin
         CurveLength :=
-          Round(sqrt(sqr(CurSegment.X3 - PosX) + sqr(CurSegment.Y3 - PosY))) +
-          Round(sqrt(sqr(CurSegment.X2 - CurSegment.X3) + sqr(CurSegment.Y2 - CurSegment.Y3))) +
-          Round(sqrt(sqr(CurSegment.X - CurSegment.X3) + sqr(CurSegment.Y - CurSegment.Y3)));
+          Round(sqrt(sqr(Cur2DBSegment.X3 - PosX) + sqr(Cur2DBSegment.Y3 - PosY))) +
+          Round(sqrt(sqr(Cur2DBSegment.X2 - Cur2DBSegment.X3) + sqr(Cur2DBSegment.Y2 - Cur2DBSegment.Y3))) +
+          Round(sqrt(sqr(Cur2DBSegment.X - Cur2DBSegment.X3) + sqr(Cur2DBSegment.Y - Cur2DBSegment.Y3)));
 
         for k := 1 to CurveLength do
         begin
           t := k / CurveLength;
-          CurX := Round(sqr(1 - t) * (1 - t) * PosX + 3 * t * sqr(1 - t) * CurSegment.X2 + 3 * t * t * (1 - t) * CurSegment.X3 + t * t * t * CurSegment.X);
-          CurY := Round(sqr(1 - t) * (1 - t) * PosY + 3 * t * sqr(1 - t) * CurSegment.Y2 + 3 * t * t * (1 - t) * CurSegment.Y3 + t * t * t * CurSegment.Y);
+          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);
           ADest.LineTo(
             Round(ADestX + AMulX * CurX),
             Round(ADestY + AMulY * CurY));
         end;
-        PosX := Round(CurSegment.X);
-        PosY := Round(CurSegment.Y);
+        PosX := Round(Cur2DBSegment.X);
+        PosY := Round(Cur2DBSegment.Y);
       end;
       end;
     end;

+ 16 - 9
packages/fpvectorial/src/svgvectorialwriter.pas

@@ -81,6 +81,9 @@ var
   lPath: TPath;
   PtX, PtY, OldPtX, OldPtY: double;
   BezierCP1X, BezierCP1Y, BezierCP2X, BezierCP2Y: double;
+  segment: TPathSegment;
+  l2DSegment: T2DSegment absolute segment;
+  l2DBSegment: T2DBezierSegment absolute segment;
 begin
   for i := 0 to AData.GetPathCount() - 1 do
   begin
@@ -89,38 +92,42 @@ begin
 
     PathStr := '';
     lPath := AData.GetPath(i);
+    lPath.PrepareForSequentialReading;
+
     for j := 0 to lPath.Len - 1 do
     begin
-      if (lPath.Points[j].SegmentType <> st2DLine)
-        and (lPath.Points[j].SegmentType <> stMoveTo)
-        and (lPath.Points[j].SegmentType <> st2DBezier)
+      segment := TPathSegment(lPath.Next());
+
+      if (segment.SegmentType <> st2DLine)
+        and (segment.SegmentType <> stMoveTo)
+        and (segment.SegmentType <> st2DBezier)
         then Break; // unsupported line type
 
       // Coordinate conversion from fpvectorial to SVG
       ConvertFPVCoordinatesToSVGCoordinates(
-        AData, lPath.Points[j].X, lPath.Points[j].Y, PtX, PtY);
+        AData, l2DSegment.X, l2DSegment.Y, PtX, PtY);
       PtX := PtX - OldPtX;
       PtY := PtY - OldPtY;
 
-      if (lPath.Points[j].SegmentType = stMoveTo) then
+      if (segment.SegmentType = stMoveTo) then
       begin
         PathStr := PathStr + 'm '
           + FloatToStr(PtX, FPointSeparator) + ','
           + FloatToStr(PtY, FPointSeparator) + ' ';
       end
-      else if (lPath.Points[j].SegmentType = st2DLine) then
+      else if (segment.SegmentType = st2DLine) then
       begin
         PathStr := PathStr + 'l '
           + FloatToStr(PtX, FPointSeparator) + ','
           + FloatToStr(PtY, FPointSeparator) + ' ';
       end
-      else if (lPath.Points[j].SegmentType = st2DBezier) then
+      else if (segment.SegmentType = st2DBezier) then
       begin
         // Converts all coordinates to absolute values
         ConvertFPVCoordinatesToSVGCoordinates(
-          AData, lPath.Points[j].X2, lPath.Points[j].Y2, BezierCP1X, BezierCP1Y);
+          AData, l2DBSegment.X2, l2DBSegment.Y2, BezierCP1X, BezierCP1Y);
         ConvertFPVCoordinatesToSVGCoordinates(
-          AData, lPath.Points[j].X3, lPath.Points[j].Y3, BezierCP2X, BezierCP2Y);
+          AData, l2DBSegment.X3, l2DBSegment.Y3, BezierCP2X, BezierCP2Y);
 
         // Transforms them into values relative to the initial point
         BezierCP1X := BezierCP1X - OldPtX;