diff --git a/packages/fpvectorial/examples/fpvc_mainform.lfm b/packages/fpvectorial/examples/fpvc_mainform.lfm
index 7e9613739d..2ed06cdea4 100644
--- a/packages/fpvectorial/examples/fpvc_mainform.lfm
+++ b/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
diff --git a/packages/fpvectorial/examples/fpvectorialconverter.lpi b/packages/fpvectorial/examples/fpvectorialconverter.lpi
index 2d8003a7b9..21b7cae3c8 100644
--- a/packages/fpvectorial/examples/fpvectorialconverter.lpi
+++ b/packages/fpvectorial/examples/fpvectorialconverter.lpi
@@ -8,6 +8,7 @@
+
@@ -18,6 +19,9 @@
+
+
+
@@ -55,7 +59,7 @@
-
+
diff --git a/packages/fpvectorial/src/fpvectorial.pas b/packages/fpvectorial/src/fpvectorial.pas
index c0b49f39ad..9fd35acc09 100644
--- a/packages/fpvectorial/src/fpvectorial.pas
+++ b/packages/fpvectorial/src/fpvectorial.pas
@@ -48,37 +48,79 @@ 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
+ {@@
+ 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;
+
+ 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;
- // ToDo: make the array dynamic
- Points: array[0..255] of TPathSegment;
+ 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;
- PPath = ^TPath;
-
{@@
TvText represents a text in memory.
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);
diff --git a/packages/fpvectorial/src/fpvtocanvas.pas b/packages/fpvectorial/src/fpvtocanvas.pas
index 396b6de7c5..d5977019a1 100644
--- a/packages/fpvectorial/src/fpvtocanvas.pas
+++ b/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;
diff --git a/packages/fpvectorial/src/svgvectorialwriter.pas b/packages/fpvectorial/src/svgvectorialwriter.pas
index 765dc78efd..a1ae21495d 100644
--- a/packages/fpvectorial/src/svgvectorialwriter.pas
+++ b/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;