Browse Source

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

git-svn-id: trunk@17382 -
sekelsenmat 14 years ago
parent
commit
bca76120f6

+ 6 - 3
packages/fpvectorial/examples/fpvwritetest.lpi

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

+ 46 - 1
packages/fpvectorial/examples/fpvwritetest.pas

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

+ 77 - 24
packages/fpvectorial/src/fpvectorial.pas

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

+ 12 - 5
packages/fpvectorial/src/fpvtocanvas.pas

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

+ 91 - 69
packages/fpvectorial/src/svgvectorialwriter.pas

@@ -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
-  begin
-    OldPtX := 0;
-    OldPtY := 0;
+  OldPtX := 0;
+  OldPtY := 0;
+  PathStr := '';
 
-    PathStr := '';
-    lPath := AData.GetPath(i);
-    lPath.PrepareForSequentialReading;
+  APath.PrepareForSequentialReading();
 
-    for j := 0 to lPath.Len - 1 do
-    begin
-      segment := TPathSegment(lPath.Next());
+  for j := 0 to APath.Len - 1 do
+  begin
+    segment := TPathSegment(APath.Next());
+
+    if (segment.SegmentType <> st2DLine)
+      and (segment.SegmentType <> stMoveTo)
+      and (segment.SegmentType <> st2DBezier)
+      then Break; // unsupported line type
 
-      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, l2DSegment.X, l2DSegment.Y, PtX, PtY);
+    PtX := PtX - OldPtX;
+    PtY := PtY - OldPtY;
 
-      // Coordinate conversion from fpvectorial to SVG
+    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, l2DSegment.X, l2DSegment.Y, PtX, PtY);
-      PtX := PtX - OldPtX;
-      PtY := PtY - OldPtY;
-
-      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;
-
-        // 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;
+        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;
+
+      // 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;
 
-    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(