mirror of
https://gitlab.com/freepascal.org/lazarus/lazarus.git
synced 2025-04-10 06:08:17 +02:00
Merged revision(s) 51004 #884fddbf00, 51021 #3cab480c11, 51058 #c224456e7f, 51060 #c689e1582c from trunk:
fpvectorial: Correct calculation of ellipse center of elliptical arc path segment ........ fpvectorial: Fix TPath to avoid rendering of internal lines for bezier segments. Fix elliptic path segment with rotated axis. ........ fpvectorial: Reorganize TPath.Render. Support polygon even-odd and non-zero winding rules for brush fill. ........ fpvectorial: Fix svg reader memory leaks related to BrushDefs and tokenizer. Declare package as runtime package. ........ git-svn-id: branches/fixes_1_6@51074 -
This commit is contained in:
parent
b2b3cd958d
commit
2dc436febb
@ -24,6 +24,7 @@ unit fpvectorial;
|
||||
{.$define FPVECTORIAL_TOCANVAS_DEBUG}
|
||||
{.$define FPVECTORIAL_DEBUG_BLOCKS}
|
||||
{$define FPVECTORIAL_AUTOFIT_DEBUG}
|
||||
{.$define FPVECTORIAL_TOCANVAS_ELLIPSE_VISUALDEBUG}
|
||||
|
||||
interface
|
||||
|
||||
@ -100,6 +101,7 @@ const
|
||||
// Convenience constant to convert text size points to mm
|
||||
FPV_TEXT_POINT_TO_MM = 0.35278;
|
||||
|
||||
TWO_PI = 2.0 * pi;
|
||||
|
||||
type
|
||||
TvCustomVectorialWriter = class;
|
||||
@ -272,6 +274,9 @@ type
|
||||
|
||||
P3DPoint = ^T3DPoint;
|
||||
|
||||
T3DPointsArray = array of T3DPoint;
|
||||
TPointsArray = array of TPoint;
|
||||
|
||||
TSegmentType = (
|
||||
st2DLine, st2DLineWithPen, st2DBezier,
|
||||
st3DLine, st3DBezier, stMoveTo,
|
||||
@ -299,6 +304,8 @@ type
|
||||
procedure Rotate(AAngle: Double; ABase: T3DPoint); virtual; // Angle in radians
|
||||
procedure CalculateBoundingBox(ADest: TFPCustomCanvas; var ALeft, ATop, ARight, ABottom: Double); virtual;
|
||||
function GenerateDebugTree(ADestRoutine: TvDebugAddItemProc; APageItem: Pointer): Pointer; virtual;
|
||||
// rendering
|
||||
procedure AddToPoints(ADestX, ADestY: Integer; AMulX, AMulY: Double; var Points: TPointsArray); virtual;
|
||||
end;
|
||||
|
||||
{@@
|
||||
@ -321,6 +328,8 @@ type
|
||||
procedure Move(ADeltaX, ADeltaY: Double); override;
|
||||
procedure Rotate(AAngle: Double; ABase: T3DPoint); override;
|
||||
function GenerateDebugTree(ADestRoutine: TvDebugAddItemProc; APageItem: Pointer): Pointer; override;
|
||||
// rendering
|
||||
procedure AddToPoints(ADestX, ADestY: Integer; AMulX, AMulY: Double; var Points: TPointsArray); override;
|
||||
end;
|
||||
|
||||
T2DSegmentWithPen = class(T2DSegment)
|
||||
@ -352,6 +361,8 @@ type
|
||||
// edition methods
|
||||
procedure Move(ADeltaX, ADeltaY: Double); override;
|
||||
function GenerateDebugTree(ADestRoutine: TvDebugAddItemProc; APageItem: Pointer): Pointer; override;
|
||||
// rendering
|
||||
procedure AddToPoints(ADestX, ADestY: Integer; AMulX, AMulY: Double; var Points: TPointsArray); override;
|
||||
end;
|
||||
|
||||
{ T3DSegment }
|
||||
@ -364,6 +375,8 @@ type
|
||||
}
|
||||
X, Y, Z: Double;
|
||||
procedure Move(ADeltaX, ADeltaY: Double); override;
|
||||
// rendering
|
||||
procedure AddToPoints(ADestX, ADestY: Integer; AMulX, AMulY: Double; var Points: TPointsArray); override;
|
||||
end;
|
||||
|
||||
{ T3DBezierSegment }
|
||||
@ -389,9 +402,12 @@ type
|
||||
LeftmostEllipse, ClockwiseArcFlag: Boolean;
|
||||
CX, CY: Double; // Ellipse center
|
||||
CenterSetByUser: Boolean; // defines if we should use LeftmostEllipse to calculate the center, or if CX, CY is set directly
|
||||
procedure BezierApproximate(var Points: T3dPointsArray);
|
||||
procedure PolyApproximate(var Points: T3dPointsArray);
|
||||
procedure CalculateCenter;
|
||||
procedure CalculateEllipseBoundingBox(ADest: TFPCustomCanvas; var ALeft, ATop, ARight, ABottom: Double);
|
||||
procedure CalculateEllipseBoundingBox(ADest: TFPCustomCanvas; out ALeft, ATop, ARight, ABottom: Double);
|
||||
function GenerateDebugTree(ADestRoutine: TvDebugAddItemProc; APageItem: Pointer): Pointer; override;
|
||||
procedure AddToPoints(ADestX, ADestY: Integer; AMulX, AMulY: Double; var Points: TPointsArray); override;
|
||||
end;
|
||||
|
||||
TvFindEntityResult = (vfrNotFound, vfrFound, vfrSubpartFound);
|
||||
@ -491,11 +507,14 @@ type
|
||||
|
||||
{ TvEntityWithPenAndBrush }
|
||||
|
||||
TvClipMode = (vcmNonzeroWindingRule, vcmEvenOddRule);
|
||||
|
||||
TvEntityWithPenAndBrush = class(TvEntityWithPen)
|
||||
public
|
||||
{@@ The global Brush for the entire entity. In the case of paths, individual
|
||||
elements might be able to override this setting. }
|
||||
Brush: TvBrush;
|
||||
WindingRule: TvClipMode;
|
||||
constructor Create(APage: TvPage); override;
|
||||
procedure ApplyBrushToCanvas(ADest: TFPCustomCanvas); overload;
|
||||
procedure ApplyBrushToCanvas(ADest: TFPCustomCanvas; ABrush: TvBrush); overload;
|
||||
@ -536,8 +555,6 @@ type
|
||||
ADestY: Integer = 0; AMulX: Double = 1.0; AMulY: Double = 1.0; ADoDraw: Boolean = True); override;
|
||||
end;
|
||||
|
||||
TvClipMode = (vcmNonzeroWindingRule, vcmEvenOddRule);
|
||||
|
||||
TPath = class(TvEntityWithPenAndBrush)
|
||||
private
|
||||
// Used to speed up sequencial access in MoveSubpart
|
||||
@ -1452,6 +1469,7 @@ type
|
||||
procedure AddBezierToPath(AX1, AY1, AX2, AY2, AX3, AY3: Double); overload;
|
||||
procedure AddBezierToPath(AX1, AY1, AZ1, AX2, AY2, AZ2, AX3, AY3, AZ3: Double); overload;
|
||||
procedure AddEllipticalArcToPath(ARadX, ARadY, AXAxisRotation, ADestX, ADestY: Double; ALeftmostEllipse, AClockwiseArcFlag: Boolean); // See http://www.w3.org/TR/SVG/paths.html#PathDataEllipticalArcCommands
|
||||
procedure AddEllipticalArcWithCenterToPath(ARadX, ARadY, AXAxisRotation, ADestX, ADestY, ACenterX, ACenterY: Double; AClockwiseArcFlag: Boolean);
|
||||
procedure SetBrushColor(AColor: TFPColor);
|
||||
procedure SetBrushStyle(AStyle: TFPBrushStyle);
|
||||
procedure SetPenColor(AColor: TFPColor);
|
||||
@ -2625,6 +2643,7 @@ end;
|
||||
|
||||
{ T2DEllipticalArcSegment }
|
||||
|
||||
// wp: no longer needed...
|
||||
function T2DEllipticalArcSegment.AlignedEllipseCenterEquationT1(
|
||||
AParam: Double): Double;
|
||||
var
|
||||
@ -2639,6 +2658,94 @@ begin
|
||||
if Result < 0 then Result := -1* Result;
|
||||
end;
|
||||
|
||||
procedure T2DEllipticalArcSegment.BezierApproximate(var Points: T3dPointsArray);
|
||||
var
|
||||
P1, P2, P3, P4: T3dPoint;
|
||||
startangle, endangle: Double;
|
||||
startanglePi2, endanglePi2: Double;
|
||||
xstart, ystart: Double;
|
||||
nextx, nexty: Double;
|
||||
angle: Double;
|
||||
n: Integer;
|
||||
begin
|
||||
SetLength(Points, 30);
|
||||
n := 0;
|
||||
|
||||
xstart := T2DSegment(Previous).X;
|
||||
ystart := T2DSegment(Previous).Y;
|
||||
startangle := CalcEllipsePointAngle(xstart, ystart, RX, RY, CX, CY, XRotation);
|
||||
endangle := CalcEllipsePointAngle(X, Y, RX, RY, CX, CY, XRotation);
|
||||
if endangle < 0 then endangle := 2*pi + endangle;
|
||||
|
||||
angle := arctan2(-1,1);
|
||||
angle := radtodeg(angle);
|
||||
|
||||
angle := radtodeg(startangle);
|
||||
angle := radtodeg(endangle);
|
||||
|
||||
// Since the algorithm for bezier approximation requires that the angle
|
||||
// between start and end is at most pi/3 we have to progress in pi/3 steps.
|
||||
angle := startangle + pi/3;
|
||||
while true do
|
||||
begin
|
||||
if angle >= endangle then begin
|
||||
EllipticalArcToBezier(CX, CY, RX, RY, startAngle, endangle, Points[n], Points[n+1], Points[n+2], Points[n+3]);
|
||||
inc(n, 4);
|
||||
break;
|
||||
end else
|
||||
EllipticalArcToBezier(CX, CY, RX, RY, startangle, angle, Points[n], Points[n+1], Points[n+2], Points[n+3]);
|
||||
inc(n, 4);
|
||||
startangle := angle;
|
||||
angle := angle + pi/2;
|
||||
end;
|
||||
SetLength(Points, n);
|
||||
end;
|
||||
|
||||
procedure T2DEllipticalArcSegment.PolyApproximate(var Points: T3dPointsArray);
|
||||
const
|
||||
BUFSIZE = 100;
|
||||
var
|
||||
t, tstart, tend, dt: Double;
|
||||
xstart, ystart: Double;
|
||||
n: Integer;
|
||||
done: Boolean;
|
||||
begin
|
||||
n := 0;
|
||||
SetLength(Points, BUFSIZE);
|
||||
|
||||
dt := DegToRad(2.0); // 2-degree increments
|
||||
|
||||
xstart := T2DSegment(Previous).X;
|
||||
ystart := T2DSegment(Previous).Y;
|
||||
tstart := CalcEllipsePointAngle(xstart, ystart, RX, RY, CX, CY, XRotation);
|
||||
tend := CalcEllipsePointAngle(X, Y, RX, RY, CX, CY, XRotation);
|
||||
if ClockwiseArcFlag then
|
||||
begin // tend must be smaller than tstart
|
||||
if tend < tstart then tend := TWO_PI + tend;
|
||||
end else begin // tstart must be smaller than tend
|
||||
if tstart < tend then tstart := TWO_PI + tstart;
|
||||
dt := -dt;
|
||||
end;
|
||||
|
||||
done := false;
|
||||
t := tstart;
|
||||
while not done do begin
|
||||
if (ClockwiseArcFlag and (t > tend)) or
|
||||
(not ClockwiseArcFlag and (t < tend)) then
|
||||
begin
|
||||
t := tend;
|
||||
done := true;
|
||||
end;
|
||||
if n >= Length(Points) then
|
||||
SetLength(Points, Length(Points) + BUFSIZE);
|
||||
CalcEllipsePoint(t, RX, RY, CX, CY, XRotation, Points[n].x, Points[n].y);
|
||||
inc(n);
|
||||
t := t + dt; // Note: dt is <0 in counter-clockwise case
|
||||
end;
|
||||
SetLength(Points, n);
|
||||
end;
|
||||
|
||||
// wp: no longer needed...
|
||||
procedure T2DEllipticalArcSegment.CalculateCenter;
|
||||
var
|
||||
XStart, YStart, lT1: Double;
|
||||
@ -2694,7 +2801,7 @@ begin
|
||||
CX1 := RotatedCenter.X;
|
||||
CY1 := RotatedCenter.Y;
|
||||
|
||||
// The other ellipse is simetrically positioned
|
||||
// The other ellipse is symmetrically positioned
|
||||
if (CX1 > Xstart) then
|
||||
CX2 := X - (CX1-Xstart)
|
||||
else
|
||||
@ -2734,7 +2841,7 @@ begin
|
||||
end;
|
||||
|
||||
procedure T2DEllipticalArcSegment.CalculateEllipseBoundingBox(ADest: TFPCustomCanvas;
|
||||
var ALeft, ATop, ARight, ABottom: Double);
|
||||
out ALeft, ATop, ARight, ABottom: Double);
|
||||
var
|
||||
t1, t2, t3: Double;
|
||||
x1, x2, x3: Double;
|
||||
@ -2777,41 +2884,49 @@ begin
|
||||
begin
|
||||
ALeft := CX-RX;
|
||||
ARight := CX+RX;
|
||||
ATop := CY-RY;
|
||||
ABottom := CY+RY;
|
||||
ATop := CY+RY;
|
||||
ABottom := CY-RY;
|
||||
end
|
||||
else
|
||||
begin
|
||||
// Search for the minimum and maximum X
|
||||
// There are two solutions in each 2pi range
|
||||
t1 := arctan(-RY*tan(XRotation)/RX);
|
||||
t2 := arctan(-RY*tan(XRotation)/RX) + Pi/2;
|
||||
t3 := arctan(-RY*tan(XRotation)/RX) + Pi;
|
||||
t2 := arctan(-RY*tan(XRotation)/RX) + pi; //Pi/2; // why add pi/2 ??
|
||||
// t3 := arctan(-RY*tan(XRotation)/RX) + Pi;
|
||||
|
||||
x1 := Cx + RX*Cos(t1)*Cos(XRotation)-RY*Sin(t1)*Sin(XRotation);
|
||||
x2 := Cx + RX*Cos(t2)*Cos(XRotation)-RY*Sin(t2)*Sin(XRotation);
|
||||
x3 := Cx + RX*Cos(t3)*Cos(XRotation)-RY*Sin(t3)*Sin(XRotation);
|
||||
// x3 := Cx + RX*Cos(t3)*Cos(XRotation)-RY*Sin(t3)*Sin(XRotation);
|
||||
|
||||
ALeft := Min(x1, x2);
|
||||
ALeft := Min(ALeft, x3);
|
||||
// ALeft := Min(ALeft, x3);
|
||||
|
||||
ARight := Max(x1, x2);
|
||||
ARight := Max(ARight, x3);
|
||||
//ARight := Max(ARight, x3);
|
||||
|
||||
// Now the same for Y
|
||||
|
||||
t1 := arctan(RY*cotan(XRotation)/RX);
|
||||
t2 := arctan(RY*cotan(XRotation)/RX) + Pi/2;
|
||||
t3 := arctan(RY*cotan(XRotation)/RX) + 3*Pi/2;
|
||||
t2 := arctan(RY*cotan(XRotation)/RX) + pi; //Pi/2; // why add pi/2 ??
|
||||
// t3 := arctan(RY*cotan(XRotation)/RX) + 3*Pi/2;
|
||||
|
||||
y1 := CY + RY*Sin(t1)*Cos(XRotation)+RX*Cos(t1)*Sin(XRotation);
|
||||
y2 := CY + RY*Sin(t2)*Cos(XRotation)+RX*Cos(t2)*Sin(XRotation);
|
||||
y3 := CY + RY*Sin(t3)*Cos(XRotation)+RX*Cos(t3)*Sin(XRotation);
|
||||
// y3 := CY + RY*Sin(t3)*Cos(XRotation)+RX*Cos(t3)*Sin(XRotation);
|
||||
|
||||
ATop := Max(y1, y2);
|
||||
// ATop := Max(ATop, y3);
|
||||
|
||||
ABottom := Min(y1, y2);
|
||||
// ABottom := Min(ABottom, y3);
|
||||
{
|
||||
ATop := Min(y1, y2);
|
||||
ATop := Min(ATop, y3);
|
||||
|
||||
ABottom := Max(y1, y2);
|
||||
ABottom := Max(ABottom, y3);
|
||||
}
|
||||
end;
|
||||
end;
|
||||
|
||||
@ -2830,6 +2945,24 @@ begin
|
||||
Result := ADestRoutine(lStr, APageItem);
|
||||
end;
|
||||
|
||||
procedure T2DEllipticalArcSegment.AddToPoints(ADestX, ADestY: Integer;
|
||||
AMulX, AMulY: Double; var Points: TPointsArray);
|
||||
var
|
||||
pts3D: T3DPointsArray;
|
||||
i, n: Integer;
|
||||
begin
|
||||
SetLength(pts3d, 0);
|
||||
PolyApproximate(pts3D);
|
||||
n := Length(Points);
|
||||
SetLength(Points, n + Length(pts3D) - 1); // we don't need the start point --> -1
|
||||
for i:=1 to High(pts3D) do // i=0 is end point of prev segment -> we can skip it.
|
||||
begin
|
||||
Points[n].X := CoordToCanvasX(pts3D[i].X, ADestX, AMulX);
|
||||
Points[n].Y := CoordToCanvasY(pts3D[i].Y, ADestY, AMulY);
|
||||
inc(n);
|
||||
end;
|
||||
end;
|
||||
|
||||
{ TvVerticalFormulaStack }
|
||||
|
||||
function TvVerticalFormulaStack.CalculateHeight(ADest: TFPCustomCanvas): Double;
|
||||
@ -2970,6 +3103,13 @@ begin
|
||||
Result := ADestRoutine(lStr, APageItem);
|
||||
end;
|
||||
|
||||
procedure TPathSegment.AddToPoints(ADestX, ADestY: Integer; AMulX, AMulY: Double;
|
||||
var Points: TPointsArray);
|
||||
begin
|
||||
// Override by descendants
|
||||
end;
|
||||
|
||||
|
||||
{ T2DSegment }
|
||||
|
||||
function T2DSegment.GetLength: Double;
|
||||
@ -3018,6 +3158,17 @@ begin
|
||||
Result := ADestRoutine(lStr, APageItem);
|
||||
end;
|
||||
|
||||
procedure T2DSegment.AddToPoints(ADestX, ADestY: Integer; AMulX, AMulY: Double;
|
||||
var Points: TPointsArray);
|
||||
var
|
||||
n: Integer;
|
||||
begin
|
||||
n := Length(Points);
|
||||
SetLength(Points, n + 1);
|
||||
Points[n].X := CoordToCanvasX(Points[n].X, ADestX, AMulX);
|
||||
Points[n].Y := CoordToCanvasY(Points[n].Y, ADestY, AMulY);
|
||||
end;
|
||||
|
||||
{ T2DBezierSegment }
|
||||
|
||||
function T2DBezierSegment.GetLength: Double;
|
||||
@ -3060,6 +3211,42 @@ begin
|
||||
Result := ADestRoutine(lStr, APageItem);
|
||||
end;
|
||||
|
||||
procedure T2DBezierSegment.AddToPoints(ADestX, ADestY: Integer; AMulX, AMulY: Double;
|
||||
var Points: TPointsArray);
|
||||
var
|
||||
pts: TPointsArray;
|
||||
coordX, coordY, coord2X, coord2Y, coord3X, coord3Y, coord4X, coord4Y: Integer;
|
||||
i, n: Integer;
|
||||
begin
|
||||
if not (Previous is T2DSegment) then
|
||||
raise Exception.Create('T2DBezierSegment must follow a T2DSegment.');
|
||||
|
||||
coordX := CoordToCanvasX(T2DSegment(Previous).X, ADestX, AMulX); // start pt
|
||||
coordY := CoordToCanvasY(T2DSegment(Previous).Y, ADestY, AMulY);
|
||||
coord4X := CoordToCanvasX(X, ADestX, AMulX); // end pt
|
||||
coord4Y := CoordToCanvasY(Y, ADestY, AMulY);
|
||||
coord2X := CoordToCanvasX(X2, ADestX, AMulX); // ctrl pt 1
|
||||
coord2Y := CoordToCanvasY(Y2, ADestY, AMulY);
|
||||
coord3X := CoordToCanvasX(X3, ADestX, AMulX); // ctrl pt 2
|
||||
coord3Y := CoordToCanvasY(Y3, ADestY, AMulY);
|
||||
|
||||
SetLength(pts, 0);
|
||||
AddBezierToPoints(
|
||||
Make2DPoint(coordX, coordY),
|
||||
Make2DPoint(coord2X, coord2Y),
|
||||
Make2DPoint(coord3X, coord3Y),
|
||||
Make2DPoint(coord4X, coord4Y),
|
||||
pts);
|
||||
|
||||
n := Length(Points);
|
||||
SetLength(Points, n + Length(pts) - 1); // we don't need the start point --> -1
|
||||
for i:=1 to High(pts) do // begin at 1 to skip the start point
|
||||
begin
|
||||
Points[n] := pts[i];
|
||||
inc(n);
|
||||
end;
|
||||
end;
|
||||
|
||||
{ T3DSegment }
|
||||
|
||||
procedure T3DSegment.Move(ADeltaX, ADeltaY: Double);
|
||||
@ -3068,6 +3255,18 @@ begin
|
||||
Y := Y + ADeltaY;
|
||||
end;
|
||||
|
||||
{ This is preliminary... }
|
||||
procedure T3DSegment.AddToPoints(ADestX, ADestY: Integer; AMulX, AMulY: Double;
|
||||
var Points: TPointsArray);
|
||||
var
|
||||
n: Integer;
|
||||
begin
|
||||
n := Length(Points);
|
||||
SetLength(Points, n + 1);
|
||||
Points[n].X := CoordToCanvasX(Points[n].X, ADestX, AMulX);
|
||||
Points[n].Y := CoordToCanvasY(Points[n].Y, ADestY, AMulY);
|
||||
end;
|
||||
|
||||
{ T3DBezierSegment }
|
||||
|
||||
procedure T3DBezierSegment.Move(ADeltaX, ADeltaY: Double);
|
||||
@ -4001,10 +4200,116 @@ begin
|
||||
SetLength(Result, n);
|
||||
end;
|
||||
|
||||
procedure TPath.Render(ADest: TFPCustomCanvas; var ARenderInfo: TvRenderInfo;
|
||||
ADestX, ADestY: Integer; AMulX, AMulY: Double; ADoDraw: Boolean);
|
||||
var
|
||||
polygonPoints: TPointsArray;
|
||||
polygonStart: TIntegerDynArray;
|
||||
i: Integer;
|
||||
j, n: Integer;
|
||||
x1, y1, x2, y2: Integer;
|
||||
ACanvas: TCanvas absolute ADest;
|
||||
coordX, coordY: Integer;
|
||||
curSegment: TPathSegment;
|
||||
cur2DSegment: T2DSegment absolute curSegment;
|
||||
begin
|
||||
inherited Render(ADest, ARenderInfo, ADestX, ADestY, AMulX, AMulY, ADoDraw);
|
||||
|
||||
ConvertPathToPolygons(self, ADestX, ADestY, AMulX, AMulY, polygonPoints, polygonStart);
|
||||
x1 := MaxInt;
|
||||
y1 := maxInt;
|
||||
x2 := -MaxInt;
|
||||
y2 := -MaxInt;
|
||||
for i := 0 to High(polygonPoints) do
|
||||
begin
|
||||
x1 := min(x1, polygonPoints[i].X);
|
||||
y1 := min(y1, polygonPoints[i].Y);
|
||||
x2 := max(x2, polygonPoints[i].X);
|
||||
y2 := max(y2, polygonPoints[i].Y);
|
||||
end;
|
||||
CalcEntityCanvasMinMaxXY_With2Points(ARenderInfo, x1, y1, x2, y2);
|
||||
|
||||
if ADoDraw then
|
||||
begin
|
||||
// (1) draw background only
|
||||
ADest.Pen.Style := psClear;
|
||||
if (Length(polygonPoints) > 2) then
|
||||
case Brush.Kind of
|
||||
bkSimpleBrush:
|
||||
if Brush.Style <> bsClear then
|
||||
begin
|
||||
{$IFDEF USE_LCL_CANVAS}
|
||||
for i := 0 to High(polygonStart) do
|
||||
begin
|
||||
j := polygonStart[i];
|
||||
if i = High(polygonStart) then
|
||||
n := Length(polygonPoints) - j
|
||||
else
|
||||
n := polygonStart[i+1] - polygonStart[i] + 1;
|
||||
end;
|
||||
ACanvas.Polygon(@polygonPoints[j], n, WindingRule = vcmNonZeroWindingRule);
|
||||
{$ELSE}
|
||||
ADest.Polygon(polygonPoints);
|
||||
{$ENDIF}
|
||||
end;
|
||||
else // gradients
|
||||
DrawBrushGradient(ADest, ARenderInfo, x1, y1, x2, y2, ADestX, ADestY, AMulX, AMulY);
|
||||
// to do: multiple polygons!
|
||||
end;
|
||||
|
||||
// (2) draw border, take care of the segments with modified pen
|
||||
ADest.Brush.Style := bsClear; // We will paint no background
|
||||
ApplyPenToCanvas(ADest, ARenderInfo, Pen); // Restore pen
|
||||
|
||||
PrepareForSequentialReading;
|
||||
for j := 0 to Len - 1 do
|
||||
begin
|
||||
curSegment := TPathSegment(Next);
|
||||
case curSegment.SegmentType of
|
||||
stMoveTo:
|
||||
begin
|
||||
inc(i);
|
||||
coordX := CoordToCanvasX(cur2DSegment.X, ADestX, AMulX);
|
||||
coordY := CoordToCanvasY(cur2DSegment.Y, ADestY, AMulY);
|
||||
ADest.MoveTo(coordX, coordY);
|
||||
end;
|
||||
st2DLineWithPen, st2DLine, st3DLine:
|
||||
begin
|
||||
coordX := CoordToCanvasX(cur2DSegment.X, ADestX, AMulX);
|
||||
coordY := CoordToCanvasY(cur2DSegment.Y, ADestY, AMulY);
|
||||
if curSegment.SegmentType = st2DLineWithPen then
|
||||
begin
|
||||
ADest.Pen.FPColor := AdjustColorToBackground(T2DSegmentWithPen(Cur2DSegment).Pen.Color, ARenderInfo);
|
||||
ADest.Pen.Width := T2DSegmentWithPen(cur2DSegment).Pen.Width;
|
||||
ADest.Pen.Style := T2DSegmentWithPen(cur2DSegment).Pen.Style;
|
||||
ADest.LineTo(coordX, coordY);
|
||||
ApplyPenToCanvas(ADest, ARenderInfo, Pen);
|
||||
end else
|
||||
ADest.LineTo(coordX, coordY);
|
||||
end;
|
||||
st2DBezier, st3DBezier, st2DEllipticalArc:
|
||||
begin
|
||||
coordX := CoordToCanvasX(T2DSegment(curSegment.Previous).X, ADestX, AMulX);
|
||||
coordY := CoordToCanvasY(T2DSegment(curSegment.Previous).Y, ADestY, AMulY);
|
||||
SetLength(PolygonPoints, 1);
|
||||
PolygonPoints[0] := Point(coordX, coordY);
|
||||
curSegment.AddToPoints(ADestX, ADestY, AMulX, AMulY, PolygonPoints);
|
||||
ADest.PolyLine(PolygonPoints);
|
||||
coordX := PolygonPoints[High(PolygonPoints)].X;
|
||||
coordY := PolygonPoints[High(PolygonPoints)].Y;
|
||||
ADest.MoveTo(coordX, coordY);
|
||||
end;
|
||||
end;
|
||||
end;
|
||||
end;
|
||||
end;
|
||||
|
||||
|
||||
(*
|
||||
procedure TPath.Render(ADest: TFPCustomCanvas; var ARenderInfo: TvRenderInfo; ADestX: Integer;
|
||||
ADestY: Integer; AMulX: Double; AMulY: Double; ADoDraw: Boolean);
|
||||
|
||||
function HasStraightSegmentsOnly: Boolean;
|
||||
function CanFill: Boolean;
|
||||
var
|
||||
seg: TPathSegment;
|
||||
j: Integer;
|
||||
@ -4022,8 +4327,10 @@ procedure TPath.Render(ADest: TFPCustomCanvas; var ARenderInfo: TvRenderInfo; AD
|
||||
end;
|
||||
end;
|
||||
|
||||
const
|
||||
POINT_BUFFER = 100;
|
||||
var
|
||||
j: Integer;
|
||||
i, j: Integer;
|
||||
PosX, PosY: Double; // Not modified by ADestX, etc
|
||||
CoordX, CoordY: Integer;
|
||||
CurSegment: TPathSegment;
|
||||
@ -4032,10 +4339,11 @@ var
|
||||
Cur2DArcSegment: T2DEllipticalArcSegment absolute CurSegment;
|
||||
x1, y1, x2, y2: Integer;
|
||||
// For bezier
|
||||
CoordX2, CoordY2, CoordX3, CoordY3, CoordX4, CoordY4: Integer;
|
||||
//t: Double;
|
||||
CoordX2, CoordY2, CoordX3, CoordY3, CoordX4, CoordY4, CoordX5, CoordY5: Integer;
|
||||
// For polygons
|
||||
lPoints: array of TPoint;
|
||||
lPoints, pts: array of TPoint;
|
||||
NumPoints: Integer;
|
||||
pts3d: T3dPointsArray = nil;
|
||||
// for elliptical arcs
|
||||
BoxLeft, BoxTop, BoxRight, BoxBottom: Double;
|
||||
EllipseRect: TRect;
|
||||
@ -4084,38 +4392,46 @@ begin
|
||||
{$endif}
|
||||
|
||||
// useful in some paths, like stars!
|
||||
{ -- wp: causes artifacts in case of concave path
|
||||
if ADoDraw then
|
||||
RenderInternalPolygon(ADest, ARenderInfo, ADestX, ADestY, AMulX, AMulY);
|
||||
}
|
||||
|
||||
{$IFDEF USE_LCL_CANVAS}
|
||||
if ADoDraw and (Brush.Kind in [bkHorizontalGradient, bkVerticalGradient]) and
|
||||
HasStraightSegmentsOnly then
|
||||
if CanFill then
|
||||
begin
|
||||
x1 := MaxInt;
|
||||
y1 := MaxInt;
|
||||
x2 := -MaxInt;
|
||||
y2 := -MaxInt;
|
||||
PrepareForSequentialReading;
|
||||
for j := 0 to Len - 1 do
|
||||
// Manually fill polygon with gradient
|
||||
{$IFDEF USE_LCL_CANVAS}
|
||||
if ADoDraw and (Brush.Kind in [bkHorizontalGradient, bkVerticalGradient]) then
|
||||
begin
|
||||
CurSegment := TPathSegment(Next);
|
||||
CoordX := CoordToCanvasX(Cur2DSegment.X, ADestX, AMulX);
|
||||
CoordY := CoordToCanvasY(Cur2DSegment.Y, ADestY, AMulY);
|
||||
x1 := Min(x1, CoordX);
|
||||
y1 := Min(y1, CoordY);
|
||||
x2 := Max(x2, CoordX);
|
||||
y2 := Max(y2, CoordY);
|
||||
x1 := MaxInt;
|
||||
y1 := MaxInt;
|
||||
x2 := -MaxInt;
|
||||
y2 := -MaxInt;
|
||||
PrepareForSequentialReading;
|
||||
for j := 0 to Len - 1 do
|
||||
begin
|
||||
CurSegment := TPathSegment(Next);
|
||||
CoordX := CoordToCanvasX(Cur2DSegment.X, ADestX, AMulX);
|
||||
CoordY := CoordToCanvasY(Cur2DSegment.Y, ADestY, AMulY);
|
||||
x1 := Min(x1, CoordX);
|
||||
y1 := Min(y1, CoordY);
|
||||
x2 := Max(x2, CoordX);
|
||||
y2 := Max(y2, CoordY);
|
||||
end;
|
||||
DrawBrushGradient(ADest, ARenderInfo, x1, y1, x2, y2, ADestX, ADestY, AMulX, AMulY);
|
||||
end;
|
||||
DrawBrushGradient(ADest, ARenderInfo, x1, y1, x2, y2, ADestX, ADestY, AMulX, AMulY);
|
||||
{$ENDIF}
|
||||
end;
|
||||
{$ENDIF}
|
||||
|
||||
//
|
||||
// For other paths, draw more carefully
|
||||
//
|
||||
ApplyPenToCanvas(ADest, ARenderInfo, Pen);
|
||||
ApplyPenToCanvas(ADest, ARenderInfo, Pen); // Restore pen
|
||||
PrepareForSequentialReading;
|
||||
|
||||
SetLength(lPoints, POINT_BUFFER);
|
||||
NumPoints := 0;
|
||||
|
||||
for j := 0 to Len - 1 do
|
||||
begin
|
||||
//WriteLn('j = ', j);
|
||||
@ -4126,8 +4442,32 @@ begin
|
||||
begin
|
||||
CoordX := CoordToCanvasX(Cur2DSegment.X, ADestX, AMulX);
|
||||
CoordY := CoordToCanvasY(Cur2DSegment.Y, ADestY, AMulY);
|
||||
|
||||
if ADoDraw then
|
||||
begin
|
||||
// Draw previous polygon
|
||||
if NumPoints > 0 then
|
||||
begin
|
||||
SetLength(lPoints, NumPoints);
|
||||
if Length(lPoints) = 2 then
|
||||
ADest.Line(lPoints[0].X, lPoints[0].Y, lPoints[1].X, lPoints[1].Y)
|
||||
else
|
||||
ADest.Polygon(lPoints);
|
||||
// Start new polygon
|
||||
SetLength(lPoints, POINT_BUFFER);
|
||||
NumPoints := 0;
|
||||
end;
|
||||
|
||||
lPoints[0].X := CoordX;
|
||||
lPoints[0].Y := CoordY;
|
||||
NumPoints := 1;
|
||||
end;
|
||||
|
||||
|
||||
{
|
||||
if ADoDraw then
|
||||
ADest.MoveTo(CoordX, CoordY);
|
||||
}
|
||||
CalcEntityCanvasMinMaxXY(ARenderInfo, CoordX, CoordY);
|
||||
PosX := Cur2DSegment.X;
|
||||
PosY := Cur2DSegment.Y;
|
||||
@ -4135,7 +4475,12 @@ begin
|
||||
Write(Format(' M%d,%d', [CoordX, CoordY]));
|
||||
{$endif}
|
||||
end;
|
||||
|
||||
// This element can override temporarely the Pen
|
||||
|
||||
// TO DO: Paint these segments with correct pen at end !!!!
|
||||
|
||||
|
||||
st2DLineWithPen:
|
||||
begin
|
||||
ADest.Pen.FPColor := AdjustColorToBackground(T2DSegmentWithPen(Cur2DSegment).Pen.Color, ARenderInfo);
|
||||
@ -4145,7 +4490,14 @@ begin
|
||||
CoordY2 := CoordToCanvasY(Cur2DSegment.Y, ADestY, AMulY);
|
||||
CalcEntityCanvasMinMaxXY_With2Points(ARenderInfo, CoordX, CoordY, CoordX2, CoordY2);
|
||||
if ADoDraw then
|
||||
ADest.Line(CoordX, CoordY, CoordX2, CoordY2);
|
||||
begin
|
||||
if NumPoints >= Length(lPoints) then
|
||||
SetLength(lPoints, Length(lPoints) + POINT_BUFFER);
|
||||
lPoints[NumPoints].X := CoordX2;
|
||||
lPoints[NumPoints].Y := CoordY2;
|
||||
inc(NumPoints);
|
||||
// ADest.Line(CoordX, CoordY, CoordX2, CoordY2);
|
||||
end;
|
||||
|
||||
PosX := Cur2DSegment.X;
|
||||
PosY := Cur2DSegment.Y;
|
||||
@ -4156,6 +4508,7 @@ begin
|
||||
Write(Format(' L%d,%d', [CoordX2, CoordY2]));
|
||||
{$endif}
|
||||
end;
|
||||
|
||||
st2DLine, st3DLine:
|
||||
begin
|
||||
CoordX := CoordToCanvasX(PosX, ADestX, AMulX);
|
||||
@ -4164,13 +4517,21 @@ begin
|
||||
CoordY2 := CoordToCanvasY(Cur2DSegment.Y, ADestY, AMulY);
|
||||
CalcEntityCanvasMinMaxXY_With2Points(ARenderInfo, CoordX, CoordY, CoordX2, CoordY2);
|
||||
if ADoDraw then
|
||||
ADest.Line(CoordX, CoordY, CoordX2, CoordY2);
|
||||
begin
|
||||
if NumPoints >= Length(lPoints) then
|
||||
SetLength(lPoints, Length(lPoints) + POINT_BUFFER);
|
||||
lPoints[NumPoints].X := CoordX2;
|
||||
lPoints[NumPoints].Y := CoordY2;
|
||||
inc(NumPoints);
|
||||
// ADest.Line(CoordX, CoordY, CoordX2, CoordY2);
|
||||
end;
|
||||
PosX := Cur2DSegment.X;
|
||||
PosY := Cur2DSegment.Y;
|
||||
{$ifdef FPVECTORIAL_TOCANVAS_DEBUG}
|
||||
Write(Format(' L%d,%d', [CoordX2, CoordY2]));
|
||||
{$endif}
|
||||
end;
|
||||
|
||||
{ To draw a bezier we need to divide the interval in parts and make
|
||||
lines between this parts }
|
||||
st2DBezier, st3DBezier:
|
||||
@ -4183,21 +4544,35 @@ begin
|
||||
CoordY3 := CoordToCanvasY(Cur2DBSegment.Y3, ADestY, AMulY);
|
||||
CoordX4 := CoordToCanvasX(Cur2DBSegment.X, ADestX, AMulX);
|
||||
CoordY4 := CoordToCanvasY(Cur2DBSegment.Y, ADestY, AMulY);
|
||||
SetLength(lPoints, 0);
|
||||
// SetLength(lPoints, 0);
|
||||
CalcEntityCanvasMinMaxXY_With2Points(ARenderInfo, CoordX, CoordY, CoordX2, CoordY2);
|
||||
CalcEntityCanvasMinMaxXY_With2Points(ARenderInfo, CoordX3, CoordY3, CoordX4, CoordY4);
|
||||
SetLength(pts, 0);
|
||||
AddBezierToPoints(
|
||||
Make2DPoint(CoordX, CoordY),
|
||||
Make2DPoint(CoordX2, CoordY2),
|
||||
Make2DPoint(CoordX3, CoordY3),
|
||||
Make2DPoint(CoordX4, CoordY4),
|
||||
lPoints
|
||||
pts //lPoints
|
||||
);
|
||||
|
||||
if ADoDraw then
|
||||
begin
|
||||
if NumPoints + Length(pts) >= POINT_BUFFER then
|
||||
SetLength(lPoints, NumPoints + Length(pts));
|
||||
for i:=0 to High(pts) do
|
||||
begin
|
||||
lPoints[NumPoints].X := pts[i].X;
|
||||
lPoints[NumPoints].Y := pts[i].Y;
|
||||
inc(numPoints);
|
||||
end;
|
||||
end;
|
||||
|
||||
ADest.Brush.Style := Brush.Style;
|
||||
{
|
||||
if (Length(lPoints) >= 3) and ADoDraw then
|
||||
ADest.Polygon(lPoints);
|
||||
|
||||
}
|
||||
PosX := Cur2DSegment.X;
|
||||
PosY := Cur2DSegment.Y;
|
||||
|
||||
@ -4209,34 +4584,19 @@ begin
|
||||
CoordToCanvasX(Cur2DBSegment.X, ADestX, AMulX), CoordToCanvasY(Cur2DBSegment.Y, ADestY, AMulY)]));
|
||||
{$endif}
|
||||
end;
|
||||
// Alligned Ellipse equation:
|
||||
// x^2 / RX^2 + Y^2 / RY^2 = 1
|
||||
//
|
||||
// Rotated Ellipse equation:
|
||||
// (xcosθ+ysinθ)^2 / RX^2 + (ycosθ−xsinθ)^2 / RY^2 = 1
|
||||
//
|
||||
// parametrized:
|
||||
// x = Cx + a*cos(t)*cos(phi) - b*sin(t)*sin(phi) [1]
|
||||
// y = Cy + b*sin(t)*cos(phi) + a*cos(t)*sin(phi) [2]
|
||||
// ...where ellipse has centre (h,k) semimajor axis a and semiminor axis b, and is rotated through angle phi.
|
||||
//
|
||||
// You can then differentiate and solve for gradient = 0:
|
||||
// 0 = dx/dt = -a*sin(t)*cos(phi) - b*cos(t)*sin(phi)
|
||||
// => tan(t) = -b*tan(phi)/a [3]
|
||||
// => t = arctan(-b*tan(phi)/a) + n*Pi [4]
|
||||
//
|
||||
// calculate some values of t for n in -2, -1, 0, 1, 2 and see which are the smaller, bigger ones
|
||||
// done!
|
||||
|
||||
st2DEllipticalArc:
|
||||
begin
|
||||
CoordX := CoordToCanvasX(PosX, ADestX, AMulX);
|
||||
CoordX := CoordToCanvasX(PosX, ADestX, AMulX); // start point of segment
|
||||
CoordY := CoordToCanvasY(PosY, ADestY, AMulY);
|
||||
CoordX2 := CoordToCanvasX(Cur2DArcSegment.RX, ADestX, AMulX);
|
||||
CoordY2 := CoordToCanvasY(Cur2DArcSegment.RY, ADestY, AMulY);
|
||||
CoordX3 := CoordToCanvasX(Cur2DArcSegment.XRotation, ADestX, AMulX);
|
||||
CoordX4 := CoordToCanvasX(Cur2DArcSegment.X, ADestX, AMulX);
|
||||
CoordX2 := CoordToCanvasX(Cur2DArcSegment.RX, ADestX, AMulX); // major axis radius
|
||||
CoordY2 := CoordToCanvasY(Cur2DArcSegment.RY, ADestY, AMulY); // minor axis radius
|
||||
CoordX3 := CoordToCanvasX(Cur2DArcSegment.XRotation, 0, sign(AMulX)); // axis rotation angle
|
||||
CoordX4 := CoordToCanvasX(Cur2DArcSegment.X, ADestX, AMulX); // end point of segment
|
||||
CoordY4 := CoordToCanvasY(Cur2DArcSegment.Y, ADestY, AMulY);
|
||||
SetLength(lPoints, 0);
|
||||
CoordX5 := CoordToCanvasX(Cur2DArcSegment.Cx, ADestX, AMulX); // Ellipse center
|
||||
CoordY5 := CoordToCanvasY(Cur2DArcSegment.Cy, ADestY, AMulY);
|
||||
// SetLength(lPoints, 0);
|
||||
|
||||
Cur2DArcSegment.CalculateEllipseBoundingBox(nil, BoxLeft, BoxTop, BoxRight, BoxBottom);
|
||||
|
||||
@ -4248,27 +4608,63 @@ begin
|
||||
{$ifdef FPVECTORIAL_TOCANVAS_ELLIPSE_VISUALDEBUG}
|
||||
ACanvas.Pen.Color := clRed;
|
||||
ACanvas.Brush.Style := bsClear;
|
||||
ACanvas.Rectangle(
|
||||
ACanvas.Rectangle( // Ellipse bounding box
|
||||
EllipseRect.Left, EllipseRect.Top, EllipseRect.Right, EllipseRect.Bottom);
|
||||
ACanvas.Line(CoordX5-5, CoordY5, CoordX5+5, CoordY5); // Ellipse center
|
||||
ACanvas.Line(CoordX5, CoordY5-5, CoordX5, CoordY5+5);
|
||||
ACanvas.Pen.Color := clBlue;
|
||||
ACanvas.Line(CoordX-5, CoordY, CoordX+5, CoordY); // Start point
|
||||
ACanvas.Line(CoordX, CoordY-5, CoordX, CoordY+5);
|
||||
ACanvas.Line(CoordX4-5, CoordY4, CoordX4+5, CoordY4); // End point
|
||||
ACanvas.Line(CoordX4, CoordY4-5, CoordX4, CoordY4+5);
|
||||
|
||||
{$endif}
|
||||
|
||||
ADest.Brush.Style := Brush.Style;
|
||||
CalcEntityCanvasMinMaxXY_With2Points(ARenderInfo, CoordX, CoordY, CoordX4, CoordY4);
|
||||
// ADest.Brush.Style := Brush.Style;
|
||||
CalcEntityCanvasMinMaxXY_With2Points(ARenderInfo,
|
||||
EllipseRect.Left, EllipseRect.Top, EllipseRect.Right, EllipseRect.Bottom);
|
||||
|
||||
if ADoDraw then
|
||||
begin
|
||||
// Arc draws counterclockwise
|
||||
if Cur2DArcSegment.ClockwiseArcFlag then
|
||||
Cur2DArcSegment.PolyApproximate(pts3D);
|
||||
// Cur2DArcSegment.BezierApproximate(pts3D);
|
||||
if NumPoints + Length(pts3D) >= POINT_BUFFER then
|
||||
SetLength(lPoints, NumPoints + Length(pts3D));
|
||||
for i:=1 to High(pts3D) do // i=0 is end point of prev segment -> we can skip it.
|
||||
begin
|
||||
lPoints[NumPoints].X := CoordToCanvasX(pts3D[i].X, ADestX, AMulX);
|
||||
lPoints[NumPoints].Y := CoordToCanvasY(pts3D[i].Y, ADestY, AMulY);
|
||||
inc(numPoints);
|
||||
end;
|
||||
{
|
||||
SetLength(lPoints, Length(pts3D));
|
||||
for i:=0 to High(pts3D) do
|
||||
begin
|
||||
lPoints[i].X := CoordToCanvasX(pts3D[i].X, ADestX, AMulX);
|
||||
lPoints[i].Y := CoordToCanvasY(pts3D[i].Y, ADestY, AMulY);
|
||||
end;
|
||||
ADest.Polygon(lPoints);
|
||||
}
|
||||
{
|
||||
i := 0;
|
||||
while i < Length(lPoints) do
|
||||
begin
|
||||
ADest.Polygon([lPoints[i], lPoints[i+1], lPoints[i+2], lPoints[i+3]]);
|
||||
inc(i, 4);
|
||||
end;
|
||||
}
|
||||
{
|
||||
// Arc draws counterclockwise
|
||||
if Cur2DArcSegment.ClockwiseArcFlag then
|
||||
ACanvas.Arc(
|
||||
EllipseRect.Left, EllipseRect.Top, EllipseRect.Right, EllipseRect.Bottom,
|
||||
CoordX4, CoordY4, CoordX, CoordY);
|
||||
end else
|
||||
begin
|
||||
CoordX4, CoordY4, CoordX, CoordY)
|
||||
else
|
||||
ACanvas.Arc(
|
||||
EllipseRect.Left, EllipseRect.Top, EllipseRect.Right, EllipseRect.Bottom,
|
||||
CoordX, CoordY, CoordX4, CoordY4);
|
||||
end;
|
||||
}
|
||||
end;
|
||||
PosX := Cur2DArcSegment.X;
|
||||
PosY := Cur2DArcSegment.Y;
|
||||
@ -4287,6 +4683,15 @@ begin
|
||||
WriteLn('');
|
||||
{$endif}
|
||||
|
||||
// Draw polygon
|
||||
if ADoDraw then begin
|
||||
SetLength(lPoints, NumPoints);
|
||||
if Length(lPoints) = 2 then
|
||||
ADest.Line(lPoints[0].X, lPoints[0].Y, lPoints[1].X, lPoints[1].Y)
|
||||
else
|
||||
ADest.Polygon(lPoints);
|
||||
end;
|
||||
|
||||
// Restores the previous Clip Region
|
||||
{$ifdef USE_CANVAS_CLIP_REGION}
|
||||
if ClipPath <> nil then
|
||||
@ -4295,7 +4700,7 @@ begin
|
||||
end;
|
||||
{$endif}
|
||||
end;
|
||||
|
||||
*)
|
||||
procedure TPath.RenderInternalPolygon(ADest: TFPCustomCanvas;
|
||||
ARenderInfo: TvRenderInfo; ADestX: Integer; ADestY: Integer; AMulX: Double;
|
||||
AMulY: Double);
|
||||
@ -7692,6 +8097,27 @@ begin
|
||||
AppendSegmentToTmpPath(segment);
|
||||
end;
|
||||
|
||||
procedure TvVectorialPage.AddEllipticalArcWithCenterToPath(ARadX, ARadY,
|
||||
AXAxisRotation, ADestX, ADestY, ACenterX, ACenterY: Double;
|
||||
AClockwiseArcFlag: Boolean);
|
||||
var
|
||||
segment: T2DEllipticalArcSegment;
|
||||
begin
|
||||
segment := T2DEllipticalArcSegment.Create;
|
||||
segment.SegmentType := st2DEllipticalArc;
|
||||
segment.X := ADestX;
|
||||
segment.Y := ADestY;
|
||||
segment.RX := ARadX;
|
||||
segment.RY := ARadY;
|
||||
segment.XRotation := AXAxisRotation;
|
||||
segment.CX := ACenterX;
|
||||
segment.CY := ACenterY;
|
||||
segment.ClockwiseArcFlag := AClockwiseArcFlag;
|
||||
segment.CenterSetByUser := true;
|
||||
|
||||
AppendSegmentToTmpPath(segment);
|
||||
end;
|
||||
|
||||
procedure TvVectorialPage.SetBrushColor(AColor: TFPColor);
|
||||
begin
|
||||
FTmPPath.Brush.Color := AColor;
|
||||
|
@ -8,9 +8,6 @@
|
||||
<SearchPaths>
|
||||
<UnitOutputDirectory Value="lib/$(TargetCPU)-$(TargetOS)"/>
|
||||
</SearchPaths>
|
||||
<Other>
|
||||
<CompilerPath Value="$(CompPath)"/>
|
||||
</Other>
|
||||
</CompilerOptions>
|
||||
<Files Count="21">
|
||||
<Item1>
|
||||
@ -98,7 +95,6 @@
|
||||
<UnitName Value="htmlvectorialreader"/>
|
||||
</Item21>
|
||||
</Files>
|
||||
<Type Value="RunAndDesignTime"/>
|
||||
<RequiredPkgs Count="2">
|
||||
<Item1>
|
||||
<PackageName Value="LCL"/>
|
||||
@ -114,5 +110,8 @@
|
||||
<PublishOptions>
|
||||
<Version Value="2"/>
|
||||
</PublishOptions>
|
||||
<CustomOptions Items="ExternHelp" Version="2">
|
||||
<_ExternHelp Items="Count"/>
|
||||
</CustomOptions>
|
||||
</Package>
|
||||
</CONFIG>
|
||||
|
@ -13,14 +13,8 @@ uses
|
||||
lazvectorialreader, mathmlvectorialreader, odgvectorialreader,
|
||||
rawvectorialreadwrite, svgvectorialreader, svgvectorialwriter,
|
||||
svgzvectorialreader, odtvectorialwriter, docxvectorialwriter,
|
||||
htmlvectorialreader, LazarusPackageIntf;
|
||||
htmlvectorialreader;
|
||||
|
||||
implementation
|
||||
|
||||
procedure Register;
|
||||
begin
|
||||
end;
|
||||
|
||||
initialization
|
||||
RegisterPackage('fpvectorialpkg', @Register);
|
||||
end.
|
||||
|
@ -22,7 +22,7 @@ unit fpvutils;
|
||||
interface
|
||||
|
||||
uses
|
||||
Classes, SysUtils, Math,
|
||||
Classes, SysUtils, Math, Types,
|
||||
{$ifdef USE_LCL_CANVAS}
|
||||
Graphics, LCLIntf, LCLType,
|
||||
{$endif}
|
||||
@ -31,7 +31,7 @@ uses
|
||||
|
||||
type
|
||||
T10Strings = array[0..9] of shortstring;
|
||||
TPointsArray = array of TPoint;
|
||||
// TPointsArray = array of TPoint;
|
||||
TFPVUByteArray = array of Byte;
|
||||
|
||||
TNumericalEquation = function (AParameter: Double): Double of object; // return the error
|
||||
@ -60,6 +60,12 @@ function BezierEquation_GetLength(P1, P2, P3, P4: T3DPoint; AMaxT: Double = 1; A
|
||||
function BezierEquation_GetT_ForLength(P1, P2, P3, P4: T3DPoint; ALength: Double; ASteps: Integer = 30): Double;
|
||||
function BezierEquation_GetPointAndTangentForLength(P1, P2, P3, P4: T3DPoint;
|
||||
ADistance: Double; out AX, AY, ATangentAngle: Double; ASteps: Integer = 30): Boolean;
|
||||
function CalcEllipseCenter(x1,y1, x2,y2, rx,ry, phi: Double; fa, fs: Boolean;
|
||||
out cx,cy, lambda: Double): Boolean;
|
||||
function CalcEllipsePointAngle(x,y, rx,ry, cx,cy, phi: Double): Double;
|
||||
procedure CalcEllipsePoint(angle, rx,ry, cx,cy, phi: Double; out x,y: Double);
|
||||
procedure ConvertPathToPolygons(APath: TPath; ADestX, ADestY: Integer; AMulX, AMulY: Double;
|
||||
var PolygonPoints: TPointsArray; var PolygonStartIndexes: TIntegerDynArray);
|
||||
procedure ConvertPathToPoints(APath: TPath; ADestX, ADestY: Integer; AMulX, AMulY: Double; var Points: TPointsArray);
|
||||
function Rotate2DPoint(P, RotCenter: TPoint; alpha:double): TPoint;
|
||||
function Rotate3DPointInXY(P, RotCenter: T3DPoint; alpha:double): T3DPoint;
|
||||
@ -376,6 +382,197 @@ begin
|
||||
Result := True;
|
||||
end;
|
||||
|
||||
// Calculate center of ellipse defined by two points on its perimeter, the
|
||||
// major and minor axes, and the "sweep" and "large-angle" flags.
|
||||
// Calculation follows the SVG implementation notes
|
||||
// see: http://www.w3.org/TR/SVG/implnote.html#ArcImplementationNotes
|
||||
// - (x1, y1) absolute coordinates of start point of arc
|
||||
// - (x2, y2) absolute coordinates of end point of arc
|
||||
// - rx, ry: radii of major and minor ellipse axes. Must be > 0. Use abs() if necessary.
|
||||
// - phi: rotation angle of ellipse
|
||||
// - fa: large arc flag (false = small arc, true = large arc)
|
||||
// - fs: sweep flag (false = counterclockwise, true = clockwise)
|
||||
// - cx, cy: Center coordinates of ellipse
|
||||
// - Function result is false if the center cannot be calculated
|
||||
function CalcEllipseCenter(x1,y1, x2,y2, rx,ry, phi: Double; fa, fs: Boolean;
|
||||
out cx,cy, lambda: Double): Boolean;
|
||||
const
|
||||
EPS = 1E-9;
|
||||
var
|
||||
sinphi, cosphi: Extended;
|
||||
x1p, x2p, y1p, y2p: Double; // x1', x2', y1', y2'
|
||||
cxp, cyp: Double; // cx', cy'
|
||||
m: Double;
|
||||
begin
|
||||
Result := false;
|
||||
if (rx = 0) or (ry = 0) then
|
||||
exit;
|
||||
|
||||
rx := abs(rx); // only positive radii!
|
||||
ry := abs(ry);
|
||||
SinCos(phi, sinphi, cosphi);
|
||||
|
||||
// (F.6.5.1) in above document
|
||||
x1p := ( cosphi*(x1-x2) + sinphi*(y1-y2)) / 2;
|
||||
y1p := (-sinphi*(x1-x2) + cosphi*(y1-y2)) / 2;
|
||||
|
||||
lambda := sqr(x1p/rx) + sqr(y1p/ry);
|
||||
if lambda > 1 then
|
||||
begin
|
||||
// If the distance of the points is too large in relation to the ellipse
|
||||
// size there is no solution. SVG Implemantation Notes request in this case
|
||||
// that the ellipse is magnified so much that a solution exists.
|
||||
lambda := sqrt(lambda);
|
||||
rx := rx * lambda;
|
||||
ry := ry * lambda;
|
||||
end else
|
||||
lambda := 1.0;
|
||||
|
||||
// (F.6.5.2)
|
||||
m := (sqr(rx*ry) - sqr(rx*y1p) - sqr(ry*x1p)) / (sqr(rx*y1p) + sqr(ry*x1p));
|
||||
if SameValue(m, 0.0, EPS) then
|
||||
// Prevent a crash caused by a tiny negative sqrt argument due to rounding error.
|
||||
m := 0
|
||||
else if m < 0 then
|
||||
exit;
|
||||
// Exit if point distance is too large and return "false" - but this
|
||||
// should no happen after having applied lambda!
|
||||
m := sqrt(m); // Positive root for fa <> fs
|
||||
if fa = fs then m := -m; // Negative root for fa = fs.
|
||||
cxp := m * rx / ry * y1p;
|
||||
cyp := -m * ry / rx * x1p;
|
||||
|
||||
// (F.6.5.3)
|
||||
cx := cosphi*cxp - sinphi*cyp + (x1 + x2) / 2;
|
||||
cy := sinphi*cxp + cosphi*cyp + (y1 + y2) / 2;
|
||||
|
||||
// If the function gets here we have a valid ellipse center in cx,cy
|
||||
Result := true;
|
||||
end;
|
||||
|
||||
{ Calculates the arc angle (in radians) of the point (x,y) on the perimeter of
|
||||
an ellipse with radii rx,ry and center cx,cy. phi is the rotation angle of
|
||||
the ellipse major axis with the x axis.
|
||||
The result is in the range 0 .. 2pi}
|
||||
function CalcEllipsePointAngle(x,y, rx,ry, cx,cy, phi: Double): Double;
|
||||
var
|
||||
p: T3DPoint;
|
||||
begin
|
||||
// Rotate ellipse back to align its major axis with the x axis
|
||||
P := Rotate3dPointInXY(Make3dPoint(x-cx, y-cy, 0), Make3dPoint(0, 0, 0), phi);
|
||||
// Correctly speaking, above line should use -phi, instead of phi. But
|
||||
// Make3DPointInXY seems to define the angle in the opposite way.
|
||||
Result := arctan2(P.Y, P.X);
|
||||
if Result < 0 then Result := TWO_PI + Result;
|
||||
end;
|
||||
|
||||
{ Calculates the x,y coordinates of a point on an ellipse defined by these
|
||||
parameters:
|
||||
- rx, ry: major and minor radius
|
||||
- phi: rotation angle of the ellipse (angle between major axis and x axis)
|
||||
- angle: angle from ellipse center between x axis and the point
|
||||
|
||||
parameterized:
|
||||
x = Cx + RX*cos(t)*cos(phi) - RY*sin(t)*sin(phi) [1]
|
||||
y = Cy + RY*sin(t)*cos(phi) + RX*cos(t)*sin(phi) [2] }
|
||||
procedure CalcEllipsePoint(angle, rx,ry, cx,cy, phi: Double; out x,y: Double);
|
||||
var
|
||||
P: T3dPoint;
|
||||
cost, sint: Extended;
|
||||
cosphi, sinphi: Extended;
|
||||
begin
|
||||
SinCos(angle, sint, cost);
|
||||
SinCos(phi, sinphi, cosphi);
|
||||
x := cx + rx*cost*cosphi - ry*sint*sinphi;
|
||||
y := cy + ry*sint*cosphi + rx*cost*sinphi;
|
||||
end;
|
||||
|
||||
{ Converts a path to one or more polygons. The polygon vertices are returned
|
||||
in "PolygonPoints"; they are given in canvas units (pixels).
|
||||
Since the path can contain several polygons the start index of each polygon
|
||||
is returned in "PolygonStartIndexes". }
|
||||
procedure ConvertPathToPolygons(APath: TPath;
|
||||
ADestX, ADestY: Integer; AMulX, AMulY: Double;
|
||||
var PolygonPoints: TPointsArray;
|
||||
var PolygonStartIndexes: TIntegerDynArray);
|
||||
const
|
||||
POINT_BUFFER = 100;
|
||||
var
|
||||
i, j: Integer;
|
||||
numPoints: Integer;
|
||||
numPolygons: Integer;
|
||||
coordX, coordY: Integer;
|
||||
coordX2, coordY2, coordX3, coordY3, coordX4, coordY4: Integer;
|
||||
// temporary point arrays
|
||||
pts: array of TPoint;
|
||||
pts3D: T3dPointsArray;
|
||||
// Segments
|
||||
curSegment: TPathSegment;
|
||||
cur2DSegment: T2DSegment absolute curSegment;
|
||||
cur2DBSegment: T2DBezierSegment absolute curSegment;
|
||||
cur2DArcSegment: T2DEllipticalArcSegment absolute curSegment;
|
||||
begin
|
||||
if (APath = nil) then
|
||||
begin
|
||||
SetLength(PolygonPoints, 0);
|
||||
SetLength(PolygonStartIndexes, 0);
|
||||
exit;
|
||||
end;
|
||||
|
||||
SetLength(PolygonPoints, POINT_BUFFER);
|
||||
SetLength(PolygonStartIndexes, POINT_BUFFER);
|
||||
numPoints := 0;
|
||||
numPolygons := 0;
|
||||
|
||||
APath.PrepareForSequentialReading;
|
||||
for i := 0 to APath.Len - 1 do
|
||||
begin
|
||||
curSegment := TPathSegment(APath.Next);
|
||||
|
||||
case curSegment.SegmentType of
|
||||
stMoveTo:
|
||||
begin
|
||||
if i <> 0 then
|
||||
raise Exception.Create('Path must start with a "MoveTo" command');
|
||||
|
||||
// Store current length of points array as polygon start index
|
||||
if numPolygons >= Length(PolygonStartIndexes) then
|
||||
SetLength(PolygonstartIndexes, Length(PolygonStartIndexes) + POINT_BUFFER);
|
||||
PolygonStartIndexes[numPolygons] := numPoints;
|
||||
inc(numPolygons);
|
||||
|
||||
// Store current point as first point of a new polygon
|
||||
coordX := CoordToCanvasX(cur2DSegment.X, ADestX, AMulX);
|
||||
coordY := CoordToCanvasY(cur2DSegment.Y, ADestY, AMulY);
|
||||
if numPoints >= Length(PolygonPoints) then
|
||||
SetLength(PolygonPoints, Length(PolygonPoints) + POINT_BUFFER);
|
||||
PolygonPoints[numPoints] := Point(coordX, coordY);
|
||||
inc(numPoints);
|
||||
end;
|
||||
|
||||
st2DLine, st3DLine, st2DLineWithPen:
|
||||
begin
|
||||
// Add current point to current polygon
|
||||
coordX := CoordToCanvasX(cur2DSegment.X, ADestX, AMulX);
|
||||
coordY := CoordToCanvasY(cur2DSegment.Y, ADestY, AMulY);
|
||||
if numPoints >= Length(PolygonPoints) then
|
||||
SetLength(PolygonPoints, Length(PolygonPoints) + POINT_BUFFER);
|
||||
PolygonPoints[numPoints] := Point(coordX, coordY);
|
||||
inc(numPoints);
|
||||
end;
|
||||
|
||||
st2DBezier, st3DBezier, st2DEllipticalArc:
|
||||
begin
|
||||
SetLength(PolygonPoints, numPoints);
|
||||
curSegment.AddToPoints(ADestX, ADestY, AMulX, AMulY, PolygonPoints);
|
||||
numPoints := Length(PolygonPoints);
|
||||
end;
|
||||
end;
|
||||
end;
|
||||
SetLength(PolygonPoints, numPoints);
|
||||
SetLength(PolygonStartIndexes, numPolygons);
|
||||
end;
|
||||
|
||||
procedure ConvertPathToPoints(APath: TPath; ADestX, ADestY: Integer; AMulX, AMulY: Double; var Points: TPointsArray);
|
||||
var
|
||||
i, LastPoint: Integer;
|
||||
@ -439,6 +636,8 @@ end;
|
||||
|
||||
// Rotates a point P around RotCenter
|
||||
// alpha angle in radians
|
||||
// Be CAREFUL: the angle used here grows in clockwise direction. This is
|
||||
// against mathematical convention!
|
||||
function Rotate3DPointInXY(P, RotCenter: T3DPoint; alpha:double): T3DPoint;
|
||||
var
|
||||
sinus, cosinus : Extended;
|
||||
@ -508,14 +707,12 @@ function SolveNumericallyAngle(ANumericalEquation: TNumericalEquation;
|
||||
var
|
||||
lError, lErr1, lErr2, lErr3, lErr4: Double;
|
||||
lParam1, lParam2: Double;
|
||||
lIterations: Integer;
|
||||
lCount: Integer;
|
||||
begin
|
||||
lErr1 := ANumericalEquation(0);
|
||||
lErr2 := ANumericalEquation(Pi/2);
|
||||
lErr3 := ANumericalEquation(Pi);
|
||||
lErr4 := ANumericalEquation(3*Pi/2);
|
||||
|
||||
// Choose the place to start
|
||||
if (lErr1 < lErr2) and (lErr1 < lErr3) and (lErr1 < lErr4) then
|
||||
begin
|
||||
@ -527,7 +724,7 @@ begin
|
||||
lParam1 := 0;
|
||||
lParam2 := Pi;
|
||||
end
|
||||
else if (lErr2 < lErr3) and (lErr2 < lErr4) then
|
||||
else if (lErr2 < lErr3) and (lErr2 < lErr4) then // wp: same as above!
|
||||
begin
|
||||
lParam1 := Pi/2;
|
||||
lParam2 := 3*Pi/2;
|
||||
@ -535,7 +732,7 @@ begin
|
||||
else
|
||||
begin
|
||||
lParam1 := Pi;
|
||||
lParam2 := 2*Pi;
|
||||
lParam2 := TWO_PI;
|
||||
end;
|
||||
|
||||
// Iterate as many times necessary to get the best answer!
|
||||
|
@ -67,13 +67,15 @@ type
|
||||
{ TSVGPathTokenizer }
|
||||
|
||||
TSVGPathTokenizer = class
|
||||
protected
|
||||
Tokens: TSVGTokenList;
|
||||
public
|
||||
FPointSeparator, FCommaSeparator: TFormatSettings;
|
||||
Tokens: TSVGTokenList;
|
||||
ExtraDebugStr: string;
|
||||
constructor Create;
|
||||
Destructor Destroy; override;
|
||||
destructor Destroy; override;
|
||||
procedure AddToken(AStr: string);
|
||||
procedure ClearTokens;
|
||||
procedure TokenizePathString(AStr: string);
|
||||
procedure TokenizeFunctions(AStr: string);
|
||||
function DebugOutTokensAsString: string;
|
||||
@ -208,13 +210,9 @@ begin
|
||||
end;
|
||||
|
||||
destructor TSVGPathTokenizer.Destroy;
|
||||
var
|
||||
i: Integer;
|
||||
begin
|
||||
for i:=Tokens.Count-1 downto 0 do
|
||||
Tokens[i].Free;
|
||||
ClearTokens;
|
||||
Tokens.Free;
|
||||
|
||||
inherited Destroy;
|
||||
end;
|
||||
|
||||
@ -278,6 +276,15 @@ begin
|
||||
Tokens.Add(lToken);
|
||||
end;
|
||||
|
||||
procedure TSVGPathTokenizer.ClearTokens;
|
||||
var
|
||||
i: Integer;
|
||||
begin
|
||||
for i := Tokens.Count-1 downto 0 do
|
||||
Tokens[i].Free;
|
||||
Tokens.Clear;
|
||||
end;
|
||||
|
||||
procedure TSVGPathTokenizer.TokenizePathString(AStr: string);
|
||||
const
|
||||
Str_Space: Char = ' ';
|
||||
@ -879,6 +886,13 @@ begin
|
||||
|
||||
Result := Result + [spbfBrushColor, spbfBrushStyle];
|
||||
end
|
||||
else if AKey = 'fill-rule' then
|
||||
begin
|
||||
if AValue = 'evenodd' then
|
||||
ADestEntity.WindingRule := vcmEvenOddRule else
|
||||
if AValue = 'nonzero' then
|
||||
ADestEntity.WindingRule := vcmNonzeroWindingRule; // to do: "inherit" missing here
|
||||
end
|
||||
else if AKey = 'fill-opacity' then
|
||||
ADestEntity.Brush.Color.Alpha := StringFloatZeroToOneToWord(AValue)
|
||||
// For linear gradient => stop-color:rgb(255,255,0);stop-opacity:1
|
||||
@ -1947,7 +1961,7 @@ var
|
||||
lDebugStr: String;
|
||||
lTmpTokenType: TSVGTokenType;
|
||||
begin
|
||||
FSVGPathTokenizer.Tokens.Clear;
|
||||
FSVGPathTokenizer.ClearTokens;
|
||||
FSVGPathTokenizer.TokenizePathString(AStr);
|
||||
//lDebugStr := FSVGPathTokenizer.DebugOutTokensAsString();
|
||||
CurX := 0;
|
||||
@ -1981,7 +1995,7 @@ procedure TvSVGVectorialReader.ReadNextPathCommand(ACurTokenType: TSVGTokenType;
|
||||
var i: Integer; var CurX, CurY: Double; AData: TvVectorialPage;
|
||||
ADoc: TvVectorialDocument);
|
||||
var
|
||||
X, Y, X2, Y2, X3, Y3, XQ, YQ, tmp: Double;
|
||||
X, Y, X2, Y2, X3, Y3, XQ, YQ, Xnew, Ynew, cx, cy, phi, tmp: Double;
|
||||
LargeArcFlag, SweepFlag, LeftmostEllipse, ClockwiseArc: Boolean;
|
||||
lCurTokenType: TSVGTokenType;
|
||||
lDebugStr: String;
|
||||
@ -2225,29 +2239,68 @@ begin
|
||||
begin
|
||||
X2 := FSVGPathTokenizer.Tokens.Items[i+1].Value; // RX
|
||||
Y2 := FSVGPathTokenizer.Tokens.Items[i+2].Value; // RY
|
||||
X3 := FSVGPathTokenizer.Tokens.Items[i+3].Value; // RotationX
|
||||
X3 := X3 * Pi / 180; // degrees to radians conversion
|
||||
phi := FSVGPathTokenizer.Tokens.Items[i+3].Value; // RotationX
|
||||
phi := DegToRad(phi); // degrees to radians conversion
|
||||
LargeArcFlag := Round(FSVGPathTokenizer.Tokens.Items[i+4].Value) = 1;
|
||||
SweepFlag := Round(FSVGPathTokenizer.Tokens.Items[i+5].Value) = 1;
|
||||
X := FSVGPathTokenizer.Tokens.Items[i+6].Value; // X
|
||||
Y := FSVGPathTokenizer.Tokens.Items[i+7].Value; // Y
|
||||
|
||||
// non-coordinate values
|
||||
{
|
||||
if lCurTokenType = sttRelativeEllipticArcTo then
|
||||
begin
|
||||
Xnew := CurX + X;
|
||||
Ynew := CurY + Y;
|
||||
end else
|
||||
begin
|
||||
Xnew := CurX;
|
||||
Ynew := CurY;
|
||||
end;
|
||||
|
||||
CalcEllipseCenter(CurX, CurY, Xnew, Ynew, X2, Y2, phi, LargeArcFlag, SweepFlag, cx, cy, tmp);
|
||||
ConvertSVGCoordinatesToFPVCoordinates(AData, cx, cy, cx, cy);
|
||||
}
|
||||
// non-coordinate values (radii)
|
||||
ConvertSVGDeltaToFPVDelta(AData, X2, Y2, X2, Y2);
|
||||
if X2 < 0 then X2 := -X2;
|
||||
if Y2 < 0 then Y2 := -Y2;
|
||||
|
||||
// Careful that absolute coordinates require using ConvertSVGCoordinatesToFPVCoordinates
|
||||
if lCurTokenType in [sttRelativeEllipticArcTo] then
|
||||
begin
|
||||
ConvertSVGDeltaToFPVDelta(AData, X, Y, X, Y);
|
||||
end
|
||||
ConvertSVGDeltaToFPVDelta(AData, X, Y, X, Y)
|
||||
else
|
||||
begin
|
||||
ConvertSVGCoordinatesToFPVCoordinates(AData, X, Y, X, Y);
|
||||
|
||||
if lCurTokenType = sttRelativeEllipticArcTo then
|
||||
begin
|
||||
Xnew := CurX + X;
|
||||
Ynew := CurY + Y;
|
||||
end else
|
||||
begin
|
||||
Xnew := X;
|
||||
Ynew := Y;
|
||||
end;
|
||||
|
||||
// in svg the y axis increases downward, in fpv upward. Therefore, angles
|
||||
// change their sign!
|
||||
phi := -phi;
|
||||
SweepFlag := not SweepFlag; // i.e. "clockwise" turns into "counter-clockwise"!
|
||||
|
||||
if CalcEllipseCenter(CurX, CurY, Xnew, Ynew, X2, Y2, phi, LargeArcFlag, SweepFlag, cx, cy, tmp) then
|
||||
AData.AddEllipticalArcWithCenterToPath(X2*tmp, Y2*tmp, phi, Xnew, Ynew, cx, cy, SweepFlag)
|
||||
else
|
||||
// Use a straight segment in case of no solution existing for the ellipse center
|
||||
AData.AddLineToPath(Xnew, Ynew);
|
||||
|
||||
CurX := Xnew;
|
||||
CurY := Ynew;
|
||||
{
|
||||
// Convert SVG flags to fpvectorial flags
|
||||
LeftmostEllipse := (LargeArcFlag and (not SweepFlag))
|
||||
or ((not LargeArcFlag) and SweepFlag);
|
||||
if (Y > CurY) or ((Y = CurY) and (X > CurX)) then
|
||||
LeftMostEllipse := not LeftMostEllipse;
|
||||
// if Y = CurY then "LeftMost" is to be understood as "TopMost"
|
||||
ClockwiseArc := SweepFlag;
|
||||
|
||||
if lCurTokenType = sttRelativeEllipticArcTo then
|
||||
@ -2262,6 +2315,7 @@ begin
|
||||
CurX := X;
|
||||
CurY := Y;
|
||||
end;
|
||||
}
|
||||
|
||||
Inc(i, 8);
|
||||
end
|
||||
@ -2278,7 +2332,7 @@ var
|
||||
X, Y: Double;
|
||||
FirstPtX, FirstPtY, CurX, CurY: Double;
|
||||
begin
|
||||
FSVGPathTokenizer.Tokens.Clear;
|
||||
FSVGPathTokenizer.ClearTokens;
|
||||
FSVGPathTokenizer.TokenizePathString(AStr);
|
||||
CurX := 0;
|
||||
CurY := 0;
|
||||
@ -3012,9 +3066,13 @@ begin
|
||||
end;
|
||||
|
||||
destructor TvSVGVectorialReader.Destroy;
|
||||
var
|
||||
i: Integer;
|
||||
begin
|
||||
FLayerStylesKeys.Free;
|
||||
FLayerStylesValues.Free;
|
||||
|
||||
for i:=FBrushDefs.Count-1 downto 0 do TObject(FBrushDefs[i]).Free;
|
||||
FBrushDefs.Free;
|
||||
FSVGPathTokenizer.Free;
|
||||
|
||||
|
Loading…
Reference in New Issue
Block a user