fpvectorial: Merges the implementation of pen, brush and updates the svg output to use the pen

git-svn-id: trunk@17382 -
This commit is contained in:
sekelsenmat 2011-04-29 11:30:16 +00:00
parent 576072dc17
commit bca76120f6
5 changed files with 229 additions and 99 deletions

View File

@ -13,7 +13,6 @@
<Title Value="fpvwritetest"/>
<ResourceType Value="res"/>
<UseXPManifest Value="True"/>
<Icon Value="0"/>
</General>
<i18n>
<EnableI18N LFM="False"/>
@ -21,6 +20,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)"/>
@ -29,6 +31,7 @@
<RunParams>
<local>
<FormatVersion Value="1"/>
<LaunchingApplication PathPlusParams="\usr\bin\xterm -T 'Lazarus Run Output' -e $(LazarusDir)\tools\runwait.sh $(TargetCmdLine)"/>
</local>
</RunParams>
<Units Count="1">
@ -40,13 +43,13 @@
</Units>
</ProjectOptions>
<CompilerOptions>
<Version Value="9"/>
<Version Value="10"/>
<PathDelim Value="\"/>
<Target>
<Filename Value="fpvwritetest"/>
</Target>
<SearchPaths>
<IncludeFiles Value="$(ProjOutDir)\"/>
<IncludeFiles Value="$(ProjOutDir)"/>
<UnitOutputDirectory Value="lib\$(TargetCPU)-$(TargetOS)"/>
</SearchPaths>
<Other>

View File

@ -22,7 +22,7 @@ program fpvwritetest;
{$mode objfpc}{$H+}
uses
fpvectorial, svgvectorialwriter;
fpvectorial, svgvectorialwriter, fpvutils;
const
cFormat = vfSVG;
@ -134,6 +134,51 @@ begin
Vec.AddText(20, 20, 0, '20, 20 Mówić, cześć, Włosku, Parabéns.');
Vec.AddText(30, 30, 0, '30, 30 森林,是一个高密');
Vec.WriteToFile('multi_test_1' + cExtension, cFormat);
// pen_test_1 Tests the properties of the Pen
Vec.Clear;
Vec.StartPath(0, 20);
Vec.AddLineToPath(30, 30);
Vec.SetPenWidth(10);
Vec.EndPath();
Vec.StartPath(0, 0);
Vec.AddLineToPath(100, 0);
Vec.AddLineToPath(100, 100);
Vec.AddLineToPath(0, 100);
Vec.AddLineToPath(0, 0);
Vec.SetPenWidth(10);
Vec.EndPath();
Vec.StartPath(0, 0);
Vec.AddLineToPath(10, 10);
Vec.AddBezierToPath(10, 20, 20, 20, 20, 10);
Vec.AddLineToPath(30, 0);
Vec.SetPenWidth(10);
Vec.EndPath();
Vec.WriteToFile('pen_test_1' + cExtension, cFormat);
// pen_test_2 Tests the properties of the Pen
Vec.Clear;
Vec.StartPath(0, 20);
Vec.AddLineToPath(30, 30);
Vec.SetPenWidth(10);
Vec.SetPenColor(RGBToVColor(255, 0, 0));
Vec.EndPath();
Vec.StartPath(0, 0);
Vec.AddLineToPath(100, 0);
Vec.AddLineToPath(100, 100);
Vec.AddLineToPath(0, 100);
Vec.AddLineToPath(0, 0);
Vec.SetPenWidth(10);
Vec.SetPenColor(RGBToVColor(0, 255, 0));
Vec.EndPath();
Vec.StartPath(0, 0);
Vec.AddLineToPath(10, 10);
Vec.AddBezierToPath(10, 20, 20, 20, 20, 10);
Vec.AddLineToPath(30, 0);
Vec.SetPenWidth(10);
Vec.SetPenColor(RGBToVColor(0, 0, 255));
Vec.EndPath();
Vec.WriteToFile('pen_test_2' + cExtension, cFormat);
finally
Vec.Free;
end;

View File

@ -45,6 +45,17 @@ type
Red, Green, Blue, Alpha: Byte;
end;
TvPen = record
Color: TvColor;
Style: TFPPenStyle;
Width: Integer;
end;
TvBrush = record
Color: TvColor;
Style: TFPBrushStyle;
end;
const
FPValphaTransparent = $00;
FPValphaOpaque = $FF;
@ -59,7 +70,7 @@ type
P3DPoint = ^T3DPoint;
TSegmentType = (
st2DLine, st2DBezier,
st2DLine, st2DLineWithPen, st2DBezier,
st3DLine, st3DBezier, stMoveTo);
{@@
@ -75,10 +86,6 @@ type
// Fields for linking the list
Previous: TPathSegment;
Next: TPathSegment;
// Data fields
PenColor: TvColor;
PenStyle: TFPPenStyle;
PenWidth: Integer;
end;
{@@
@ -93,6 +100,11 @@ type
X, Y: Double;
end;
T2DSegmentWithPen = class(T2DSegment)
public
Pen: TvPen;
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
@ -124,6 +136,13 @@ type
Points: TPathSegment; // Beginning of the double-linked list
PointsEnd: TPathSegment; // End of the double-linked list
CurPoint: TPathSegment; // Used in PrepareForSequentialReading and Next
{@@ The global Pen for the entire path. This Pen might be overriden by
individual elements of the polyline. }
Pen: TvPen;
{@@ Sets a Brush to paint the inner area inside the path.
There is no inner area if Brush.Style = bsClear, which is the default. }
Brush: TvBrush;
constructor Create();
procedure Assign(APath: TPath);
function Count(): TPathSegment;
procedure PrepareForSequentialReading;
@ -139,23 +158,18 @@ type
TvText = class
public
X, Y, Z: Double; // Z is ignored in 2D formats
Value: utf8string;
FontColor: TvColor;
FontSize: integer;
FontName: utf8string;
Value: utf8string;
Color: TvColor;
end;
{@@
}
TvEntity = class
public
// Pen
PenColor: TvColor;
PenStyle: TFPPenStyle;
PenWidth: Integer;
// Brush
BrushStyle: TFPBrushStyle;
BrushColor: TvColor;
Pen: TvPen;
Brush: TvBrush;
end;
{@@
@ -253,6 +267,11 @@ type
procedure AddLineToPath(AX, AY, AZ: Double); overload;
procedure AddBezierToPath(AX1, AY1, AX2, AY2, AX3, AY3: Double); overload;
procedure AddBezierToPath(AX1, AY1, AZ1, AX2, AY2, AZ2, AX3, AY3, AZ3: Double); overload;
procedure SetBrushColor(AColor: TvColor);
procedure SetBrushStyle(AStyle: TFPBrushStyle);
procedure SetPenColor(AColor: TvColor);
procedure SetPenStyle(AStyle: TFPPenStyle);
procedure SetPenWidth(AWidth: Integer);
procedure EndPath();
procedure AddText(AX, AY, AZ: Double; FontName: string; FontSize: integer; AText: utf8string); overload;
procedure AddText(AX, AY, AZ: Double; AStr: utf8string); overload;
@ -550,20 +569,19 @@ begin
segment.SegmentType := st2DLine;
segment.X := AX;
segment.Y := AY;
segment.PenColor := clvBlack;
AppendSegmentToTmpPath(segment);
end;
procedure TvVectorialDocument.AddLineToPath(AX, AY: Double; AColor: TvColor);
var
segment: T2DSegment;
segment: T2DSegmentWithPen;
begin
segment := T2DSegment.Create;
segment.SegmentType := st2DLine;
segment := T2DSegmentWithPen.Create;
segment.SegmentType := st2DLineWithPen;
segment.X := AX;
segment.Y := AY;
segment.PenColor := AColor;
segment.Pen.Color := AColor;
AppendSegmentToTmpPath(segment);
end;
@ -623,6 +641,31 @@ begin
AppendSegmentToTmpPath(segment);
end;
procedure TvVectorialDocument.SetBrushColor(AColor: TvColor);
begin
FTmPPath.Brush.Color := AColor;
end;
procedure TvVectorialDocument.SetBrushStyle(AStyle: TFPBrushStyle);
begin
FTmPPath.Brush.Style := AStyle;
end;
procedure TvVectorialDocument.SetPenColor(AColor: TvColor);
begin
FTmPPath.Pen.Color := AColor;
end;
procedure TvVectorialDocument.SetPenStyle(AStyle: TFPPenStyle);
begin
FTmPPath.Pen.Style := AStyle;
end;
procedure TvVectorialDocument.SetPenWidth(AWidth: Integer);
begin
FTmPPath.Pen.Width := AWidth;
end;
{@@
Finishes writing a Path, which was created in multiple
steps using StartPath and AddPointToPath,
@ -683,7 +726,7 @@ begin
lCircularArc.Radius := ARadius;
lCircularArc.StartAngle := AStartAngle;
lCircularArc.EndAngle := AEndAngle;
lCircularArc.PenColor := AColor;
lCircularArc.Pen.Color := AColor;
FEntities.Add(lCircularArc);
end;
@ -728,12 +771,13 @@ begin
for i := 0 to Length(GvVectorialFormats) - 1 do
if GvVectorialFormats[i].Format = AFormat then
begin
Result := GvVectorialFormats[i].WriterClass.Create;
if GvVectorialFormats[i].WriterClass <> nil then
Result := GvVectorialFormats[i].WriterClass.Create;
Break;
end;
if Result = nil then raise Exception.Create('Unsuported vector graphics format.');
if Result = nil then raise Exception.Create('Unsupported vector graphics format.');
end;
{@@
@ -749,12 +793,13 @@ begin
for i := 0 to Length(GvVectorialFormats) - 1 do
if GvVectorialFormats[i].Format = AFormat then
begin
Result := GvVectorialFormats[i].ReaderClass.Create;
if GvVectorialFormats[i].ReaderClass <> nil then
Result := GvVectorialFormats[i].ReaderClass.Create;
Break;
end;
if Result = nil then raise Exception.Create('Unsuported vector graphics format.');
if Result = nil then raise Exception.Create('Unsupported vector graphics format.');
end;
procedure TvVectorialDocument.ClearTmpPath();
@ -1073,12 +1118,20 @@ end;
{ TPath }
constructor TPath.Create();
begin
Brush.Style := bsClear;
inherited Create();
end;
procedure TPath.Assign(APath: TPath);
begin
Len := APath.Len;
Points := APath.Points;
PointsEnd := APath.PointsEnd;
CurPoint := APath.CurPoint;
Pen := APath.Pen;
Brush := APath.Brush;
end;
function TPath.Count(): TPathSegment;

View File

@ -4,7 +4,7 @@ unit fpvtocanvas;
interface
{.$define USE_LCL_CANVAS}
{$define USE_LCL_CANVAS}
uses
Classes, SysUtils, Math,
@ -115,7 +115,7 @@ end;
DrawFPVectorialToCanvas(ASource, ADest, 0, ASource.Height, 1.0, -1.0);
}
{$define FPVECTORIAL_TOCANVAS_DEBUG}
{.$define FPVECTORIAL_TOCANVAS_DEBUG}
procedure DrawFPVectorialToCanvas(ASource: TvVectorialDocument;
{$ifdef USE_LCL_CANVAS}ADest: TCanvas;{$else}ADest: TFPCustomCanvas;{$endif}
ADestX: Integer = 0; ADestY: Integer = 0; AMulX: Double = 1.0; AMulY: Double = 1.0);
@ -187,15 +187,22 @@ begin
Write(Format(' M%d,%d', [CoordToCanvasX(Cur2DSegment.X), CoordToCanvasY(Cur2DSegment.Y)]));
{$endif}
end;
st2DLine, st3DLine:
st2DLineWithPen:
begin
{$ifdef USE_LCL_CANVAS}ADest.Pen.Color := VColorToTColor(Cur2DSegment.PenColor);{$endif}
{$ifdef USE_LCL_CANVAS}ADest.Pen.Color := VColorToTColor(T2DSegmentWithPen(Cur2DSegment).Pen.Color);{$endif}
ADest.LineTo(CoordToCanvasX(Cur2DSegment.X), CoordToCanvasY(Cur2DSegment.Y));
{$ifdef USE_LCL_CANVAS}ADest.Pen.Color := clBlack;{$endif}
{$ifdef FPVECTORIAL_TOCANVAS_DEBUG}
Write(Format(' L%d,%d', [CoordToCanvasX(Cur2DSegment.X), CoordToCanvasY(Cur2DSegment.Y)]));
{$endif}
end;
st2DLine, st3DLine:
begin
ADest.LineTo(CoordToCanvasX(Cur2DSegment.X), CoordToCanvasY(Cur2DSegment.Y));
{$ifdef FPVECTORIAL_TOCANVAS_DEBUG}
Write(Format(' L%d,%d', [CoordToCanvasX(Cur2DSegment.X), CoordToCanvasY(Cur2DSegment.Y)]));
{$endif}
end;
{ To draw a bezier we need to divide the interval in parts and make
lines between this parts }
st2DBezier, st3DBezier:
@ -321,7 +328,7 @@ begin
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.Pen.Color := {$ifdef USE_LCL_CANVAS}VColorToTColor(CurArc.PenColor);{$else}VColorToFPColor(CurArc.PenColor);{$endif}
ADest.Pen.Color := {$ifdef USE_LCL_CANVAS}VColorToTColor(CurArc.Pen.Color);{$else}VColorToFPColor(CurArc.Pen.Color);{$endif}
ADest.Arc(
BoundsLeft, BoundsTop, BoundsRight, BoundsBottom,
IntStartAngle, IntAngleLength

View File

@ -13,7 +13,7 @@ unit svgvectorialwriter;
interface
uses
Classes, SysUtils, math, fpvectorial;
Classes, SysUtils, math, fpvectorial, fpvutils;
type
{ TvSVGVectorialWriter }
@ -24,6 +24,7 @@ type
procedure WriteDocumentSize(AStrings: TStrings; AData: TvVectorialDocument);
procedure WriteDocumentName(AStrings: TStrings; AData: TvVectorialDocument);
procedure WritePaths(AStrings: TStrings; AData: TvVectorialDocument);
procedure WritePath(AIndex: Integer; APath: TPath; AStrings: TStrings; AData: TvVectorialDocument);
procedure WriteTexts(AStrings: TStrings; AData: TvVectorialDocument);
procedure ConvertFPVCoordinatesToSVGCoordinates(
const AData: TvVectorialDocument;
@ -60,6 +61,19 @@ begin
AStrings.Add(' sodipodi:docname="New document 1">');
end;
procedure TvSVGVectorialWriter.WritePaths(AStrings: TStrings; AData: TvVectorialDocument);
var
i: Integer;
lPath: TPath;
begin
for i := 0 to AData.GetPathCount() - 1 do
begin
lPath := AData.GetPath(i);
lPath.PrepareForSequentialReading;
WritePath(i ,lPath, AStrings, AData);
end;
end;
{@@
SVG Coordinate system measures things only in pixels, so that we have to
hardcode a DPI value for the screen, which is usually 72.
@ -74,90 +88,98 @@ end;
SVG uses commas "," to separate the X,Y coordinates, so it always uses points
"." as decimal separators and uses no thousand separators
}
procedure TvSVGVectorialWriter.WritePaths(AStrings: TStrings; AData: TvVectorialDocument);
procedure TvSVGVectorialWriter.WritePath(AIndex: Integer; APath: TPath; AStrings: TStrings;
AData: TvVectorialDocument);
var
i, j: Integer;
j: Integer;
PathStr: string;
lPath: TPath;
PtX, PtY, OldPtX, OldPtY: double;
BezierCP1X, BezierCP1Y, BezierCP2X, BezierCP2Y: double;
segment: TPathSegment;
l2DSegment: T2DSegment absolute segment;
l2DBSegment: T2DBezierSegment absolute segment;
// Pen properties
lPenWidth: Integer;
lPenColor: string;
begin
for i := 0 to AData.GetPathCount() - 1 do
OldPtX := 0;
OldPtY := 0;
PathStr := '';
APath.PrepareForSequentialReading();
for j := 0 to APath.Len - 1 do
begin
OldPtX := 0;
OldPtY := 0;
segment := TPathSegment(APath.Next());
PathStr := '';
lPath := AData.GetPath(i);
lPath.PrepareForSequentialReading;
if (segment.SegmentType <> st2DLine)
and (segment.SegmentType <> stMoveTo)
and (segment.SegmentType <> st2DBezier)
then Break; // unsupported line type
for j := 0 to lPath.Len - 1 do
// Coordinate conversion from fpvectorial to SVG
ConvertFPVCoordinatesToSVGCoordinates(
AData, l2DSegment.X, l2DSegment.Y, PtX, PtY);
PtX := PtX - OldPtX;
PtY := PtY - OldPtY;
if (segment.SegmentType = stMoveTo) then
begin
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
PathStr := PathStr + 'm '
+ FloatToStr(PtX, FPointSeparator) + ','
+ FloatToStr(PtY, FPointSeparator) + ' ';
end
else if (segment.SegmentType = st2DLine) then
begin
PathStr := PathStr + 'l '
+ FloatToStr(PtX, FPointSeparator) + ','
+ FloatToStr(PtY, FPointSeparator) + ' ';
end
else if (segment.SegmentType = st2DBezier) then
begin
// Converts all coordinates to absolute values
ConvertFPVCoordinatesToSVGCoordinates(
AData, l2DSegment.X, l2DSegment.Y, PtX, PtY);
PtX := PtX - OldPtX;
PtY := PtY - OldPtY;
AData, l2DBSegment.X2, l2DBSegment.Y2, BezierCP1X, BezierCP1Y);
ConvertFPVCoordinatesToSVGCoordinates(
AData, l2DBSegment.X3, l2DBSegment.Y3, BezierCP2X, BezierCP2Y);
if (segment.SegmentType = stMoveTo) then
begin
PathStr := PathStr + 'm '
+ FloatToStr(PtX, FPointSeparator) + ','
+ FloatToStr(PtY, FPointSeparator) + ' ';
end
else if (segment.SegmentType = st2DLine) then
begin
PathStr := PathStr + 'l '
+ FloatToStr(PtX, FPointSeparator) + ','
+ FloatToStr(PtY, FPointSeparator) + ' ';
end
else if (segment.SegmentType = st2DBezier) then
begin
// Converts all coordinates to absolute values
ConvertFPVCoordinatesToSVGCoordinates(
AData, l2DBSegment.X2, l2DBSegment.Y2, BezierCP1X, BezierCP1Y);
ConvertFPVCoordinatesToSVGCoordinates(
AData, l2DBSegment.X3, l2DBSegment.Y3, BezierCP2X, BezierCP2Y);
// Transforms them into values relative to the initial point
BezierCP1X := BezierCP1X - OldPtX;
BezierCP1Y := BezierCP1Y - OldPtY;
BezierCP2X := BezierCP2X - OldPtX;
BezierCP2Y := BezierCP2Y - OldPtY;
// Transforms them into values relative to the initial point
BezierCP1X := BezierCP1X - OldPtX;
BezierCP1Y := BezierCP1Y - OldPtY;
BezierCP2X := BezierCP2X - OldPtX;
BezierCP2Y := BezierCP2Y - OldPtY;
// PtX and PtY already contains the destination point
// PtX and PtY already contains the destination point
// Now render our 2D cubic bezier
PathStr := PathStr + 'c '
+ FloatToStr(BezierCP1X, FPointSeparator) + ','
+ FloatToStr(BezierCP1Y, FPointSeparator) + ' '
+ FloatToStr(BezierCP2X, FPointSeparator) + ','
+ FloatToStr(BezierCP2Y, FPointSeparator) + ' '
+ FloatToStr(PtX, FPointSeparator) + ','
+ FloatToStr(PtY, FPointSeparator) + ' '
;
end;
// Store the current position for future points
OldPtX := OldPtX + PtX;
OldPtY := OldPtY + PtY;
// Now render our 2D cubic bezier
PathStr := PathStr + 'c '
+ FloatToStr(BezierCP1X, FPointSeparator) + ','
+ FloatToStr(BezierCP1Y, FPointSeparator) + ' '
+ FloatToStr(BezierCP2X, FPointSeparator) + ','
+ FloatToStr(BezierCP2Y, FPointSeparator) + ' '
+ FloatToStr(PtX, FPointSeparator) + ','
+ FloatToStr(PtY, FPointSeparator) + ' '
;
end;
AStrings.Add(' <path');
AStrings.Add(' style="fill:none;stroke:#000000;stroke-width:1px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1"');
AStrings.Add(' d="' + PathStr + '"');
AStrings.Add(' id="path' + IntToStr(i) + '" />');
// Store the current position for future points
OldPtX := OldPtX + PtX;
OldPtY := OldPtY + PtY;
end;
// Get the Pen Width
if APath.Pen.Width >= 1 then lPenWidth := APath.Pen.Width
else lPenWidth := 1;
// Get the Pen Color
lPenColor := VColorToRGBHexString(APath.Pen.Color);
AStrings.Add(' <path');
AStrings.Add(Format(' style="fill:none;stroke:#%s;stroke-width:%dpx;'
+ 'stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1"',
[lPenColor, lPenWidth]));
AStrings.Add(' d="' + PathStr + '"');
AStrings.Add(' id="path' + IntToStr(AIndex) + '" />');
end;
procedure TvSVGVectorialWriter.ConvertFPVCoordinatesToSVGCoordinates(