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

git-svn-id: trunk@16202 -
This commit is contained in:
sekelsenmat 2010-10-23 10:33:21 +00:00
parent 1811234b72
commit cc2da23316
5 changed files with 246 additions and 99 deletions

View File

@ -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

View File

@ -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>

View File

@ -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);

View File

@ -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;

View File

@ -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;