mirror of
https://gitlab.com/freepascal.org/lazarus/lazarus.git
synced 2025-08-27 16:20:35 +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_TOCANVAS_DEBUG}
|
||||||
{.$define FPVECTORIAL_DEBUG_BLOCKS}
|
{.$define FPVECTORIAL_DEBUG_BLOCKS}
|
||||||
{$define FPVECTORIAL_AUTOFIT_DEBUG}
|
{$define FPVECTORIAL_AUTOFIT_DEBUG}
|
||||||
|
{.$define FPVECTORIAL_TOCANVAS_ELLIPSE_VISUALDEBUG}
|
||||||
|
|
||||||
interface
|
interface
|
||||||
|
|
||||||
@ -100,6 +101,7 @@ const
|
|||||||
// Convenience constant to convert text size points to mm
|
// Convenience constant to convert text size points to mm
|
||||||
FPV_TEXT_POINT_TO_MM = 0.35278;
|
FPV_TEXT_POINT_TO_MM = 0.35278;
|
||||||
|
|
||||||
|
TWO_PI = 2.0 * pi;
|
||||||
|
|
||||||
type
|
type
|
||||||
TvCustomVectorialWriter = class;
|
TvCustomVectorialWriter = class;
|
||||||
@ -272,6 +274,9 @@ type
|
|||||||
|
|
||||||
P3DPoint = ^T3DPoint;
|
P3DPoint = ^T3DPoint;
|
||||||
|
|
||||||
|
T3DPointsArray = array of T3DPoint;
|
||||||
|
TPointsArray = array of TPoint;
|
||||||
|
|
||||||
TSegmentType = (
|
TSegmentType = (
|
||||||
st2DLine, st2DLineWithPen, st2DBezier,
|
st2DLine, st2DLineWithPen, st2DBezier,
|
||||||
st3DLine, st3DBezier, stMoveTo,
|
st3DLine, st3DBezier, stMoveTo,
|
||||||
@ -299,6 +304,8 @@ type
|
|||||||
procedure Rotate(AAngle: Double; ABase: T3DPoint); virtual; // Angle in radians
|
procedure Rotate(AAngle: Double; ABase: T3DPoint); virtual; // Angle in radians
|
||||||
procedure CalculateBoundingBox(ADest: TFPCustomCanvas; var ALeft, ATop, ARight, ABottom: Double); virtual;
|
procedure CalculateBoundingBox(ADest: TFPCustomCanvas; var ALeft, ATop, ARight, ABottom: Double); virtual;
|
||||||
function GenerateDebugTree(ADestRoutine: TvDebugAddItemProc; APageItem: Pointer): Pointer; virtual;
|
function GenerateDebugTree(ADestRoutine: TvDebugAddItemProc; APageItem: Pointer): Pointer; virtual;
|
||||||
|
// rendering
|
||||||
|
procedure AddToPoints(ADestX, ADestY: Integer; AMulX, AMulY: Double; var Points: TPointsArray); virtual;
|
||||||
end;
|
end;
|
||||||
|
|
||||||
{@@
|
{@@
|
||||||
@ -321,6 +328,8 @@ type
|
|||||||
procedure Move(ADeltaX, ADeltaY: Double); override;
|
procedure Move(ADeltaX, ADeltaY: Double); override;
|
||||||
procedure Rotate(AAngle: Double; ABase: T3DPoint); override;
|
procedure Rotate(AAngle: Double; ABase: T3DPoint); override;
|
||||||
function GenerateDebugTree(ADestRoutine: TvDebugAddItemProc; APageItem: Pointer): Pointer; override;
|
function GenerateDebugTree(ADestRoutine: TvDebugAddItemProc; APageItem: Pointer): Pointer; override;
|
||||||
|
// rendering
|
||||||
|
procedure AddToPoints(ADestX, ADestY: Integer; AMulX, AMulY: Double; var Points: TPointsArray); override;
|
||||||
end;
|
end;
|
||||||
|
|
||||||
T2DSegmentWithPen = class(T2DSegment)
|
T2DSegmentWithPen = class(T2DSegment)
|
||||||
@ -352,6 +361,8 @@ type
|
|||||||
// edition methods
|
// edition methods
|
||||||
procedure Move(ADeltaX, ADeltaY: Double); override;
|
procedure Move(ADeltaX, ADeltaY: Double); override;
|
||||||
function GenerateDebugTree(ADestRoutine: TvDebugAddItemProc; APageItem: Pointer): Pointer; override;
|
function GenerateDebugTree(ADestRoutine: TvDebugAddItemProc; APageItem: Pointer): Pointer; override;
|
||||||
|
// rendering
|
||||||
|
procedure AddToPoints(ADestX, ADestY: Integer; AMulX, AMulY: Double; var Points: TPointsArray); override;
|
||||||
end;
|
end;
|
||||||
|
|
||||||
{ T3DSegment }
|
{ T3DSegment }
|
||||||
@ -364,6 +375,8 @@ type
|
|||||||
}
|
}
|
||||||
X, Y, Z: Double;
|
X, Y, Z: Double;
|
||||||
procedure Move(ADeltaX, ADeltaY: Double); override;
|
procedure Move(ADeltaX, ADeltaY: Double); override;
|
||||||
|
// rendering
|
||||||
|
procedure AddToPoints(ADestX, ADestY: Integer; AMulX, AMulY: Double; var Points: TPointsArray); override;
|
||||||
end;
|
end;
|
||||||
|
|
||||||
{ T3DBezierSegment }
|
{ T3DBezierSegment }
|
||||||
@ -389,9 +402,12 @@ type
|
|||||||
LeftmostEllipse, ClockwiseArcFlag: Boolean;
|
LeftmostEllipse, ClockwiseArcFlag: Boolean;
|
||||||
CX, CY: Double; // Ellipse center
|
CX, CY: Double; // Ellipse center
|
||||||
CenterSetByUser: Boolean; // defines if we should use LeftmostEllipse to calculate the center, or if CX, CY is set directly
|
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 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;
|
function GenerateDebugTree(ADestRoutine: TvDebugAddItemProc; APageItem: Pointer): Pointer; override;
|
||||||
|
procedure AddToPoints(ADestX, ADestY: Integer; AMulX, AMulY: Double; var Points: TPointsArray); override;
|
||||||
end;
|
end;
|
||||||
|
|
||||||
TvFindEntityResult = (vfrNotFound, vfrFound, vfrSubpartFound);
|
TvFindEntityResult = (vfrNotFound, vfrFound, vfrSubpartFound);
|
||||||
@ -491,11 +507,14 @@ type
|
|||||||
|
|
||||||
{ TvEntityWithPenAndBrush }
|
{ TvEntityWithPenAndBrush }
|
||||||
|
|
||||||
|
TvClipMode = (vcmNonzeroWindingRule, vcmEvenOddRule);
|
||||||
|
|
||||||
TvEntityWithPenAndBrush = class(TvEntityWithPen)
|
TvEntityWithPenAndBrush = class(TvEntityWithPen)
|
||||||
public
|
public
|
||||||
{@@ The global Brush for the entire entity. In the case of paths, individual
|
{@@ The global Brush for the entire entity. In the case of paths, individual
|
||||||
elements might be able to override this setting. }
|
elements might be able to override this setting. }
|
||||||
Brush: TvBrush;
|
Brush: TvBrush;
|
||||||
|
WindingRule: TvClipMode;
|
||||||
constructor Create(APage: TvPage); override;
|
constructor Create(APage: TvPage); override;
|
||||||
procedure ApplyBrushToCanvas(ADest: TFPCustomCanvas); overload;
|
procedure ApplyBrushToCanvas(ADest: TFPCustomCanvas); overload;
|
||||||
procedure ApplyBrushToCanvas(ADest: TFPCustomCanvas; ABrush: TvBrush); 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;
|
ADestY: Integer = 0; AMulX: Double = 1.0; AMulY: Double = 1.0; ADoDraw: Boolean = True); override;
|
||||||
end;
|
end;
|
||||||
|
|
||||||
TvClipMode = (vcmNonzeroWindingRule, vcmEvenOddRule);
|
|
||||||
|
|
||||||
TPath = class(TvEntityWithPenAndBrush)
|
TPath = class(TvEntityWithPenAndBrush)
|
||||||
private
|
private
|
||||||
// Used to speed up sequencial access in MoveSubpart
|
// 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, AX2, AY2, AX3, AY3: Double); overload;
|
||||||
procedure AddBezierToPath(AX1, AY1, AZ1, AX2, AY2, AZ2, AX3, AY3, AZ3: 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 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 SetBrushColor(AColor: TFPColor);
|
||||||
procedure SetBrushStyle(AStyle: TFPBrushStyle);
|
procedure SetBrushStyle(AStyle: TFPBrushStyle);
|
||||||
procedure SetPenColor(AColor: TFPColor);
|
procedure SetPenColor(AColor: TFPColor);
|
||||||
@ -2625,6 +2643,7 @@ end;
|
|||||||
|
|
||||||
{ T2DEllipticalArcSegment }
|
{ T2DEllipticalArcSegment }
|
||||||
|
|
||||||
|
// wp: no longer needed...
|
||||||
function T2DEllipticalArcSegment.AlignedEllipseCenterEquationT1(
|
function T2DEllipticalArcSegment.AlignedEllipseCenterEquationT1(
|
||||||
AParam: Double): Double;
|
AParam: Double): Double;
|
||||||
var
|
var
|
||||||
@ -2639,6 +2658,94 @@ begin
|
|||||||
if Result < 0 then Result := -1* Result;
|
if Result < 0 then Result := -1* Result;
|
||||||
end;
|
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;
|
procedure T2DEllipticalArcSegment.CalculateCenter;
|
||||||
var
|
var
|
||||||
XStart, YStart, lT1: Double;
|
XStart, YStart, lT1: Double;
|
||||||
@ -2694,7 +2801,7 @@ begin
|
|||||||
CX1 := RotatedCenter.X;
|
CX1 := RotatedCenter.X;
|
||||||
CY1 := RotatedCenter.Y;
|
CY1 := RotatedCenter.Y;
|
||||||
|
|
||||||
// The other ellipse is simetrically positioned
|
// The other ellipse is symmetrically positioned
|
||||||
if (CX1 > Xstart) then
|
if (CX1 > Xstart) then
|
||||||
CX2 := X - (CX1-Xstart)
|
CX2 := X - (CX1-Xstart)
|
||||||
else
|
else
|
||||||
@ -2734,7 +2841,7 @@ begin
|
|||||||
end;
|
end;
|
||||||
|
|
||||||
procedure T2DEllipticalArcSegment.CalculateEllipseBoundingBox(ADest: TFPCustomCanvas;
|
procedure T2DEllipticalArcSegment.CalculateEllipseBoundingBox(ADest: TFPCustomCanvas;
|
||||||
var ALeft, ATop, ARight, ABottom: Double);
|
out ALeft, ATop, ARight, ABottom: Double);
|
||||||
var
|
var
|
||||||
t1, t2, t3: Double;
|
t1, t2, t3: Double;
|
||||||
x1, x2, x3: Double;
|
x1, x2, x3: Double;
|
||||||
@ -2777,41 +2884,49 @@ begin
|
|||||||
begin
|
begin
|
||||||
ALeft := CX-RX;
|
ALeft := CX-RX;
|
||||||
ARight := CX+RX;
|
ARight := CX+RX;
|
||||||
ATop := CY-RY;
|
ATop := CY+RY;
|
||||||
ABottom := CY+RY;
|
ABottom := CY-RY;
|
||||||
end
|
end
|
||||||
else
|
else
|
||||||
begin
|
begin
|
||||||
// Search for the minimum and maximum X
|
// Search for the minimum and maximum X
|
||||||
|
// There are two solutions in each 2pi range
|
||||||
t1 := arctan(-RY*tan(XRotation)/RX);
|
t1 := arctan(-RY*tan(XRotation)/RX);
|
||||||
t2 := arctan(-RY*tan(XRotation)/RX) + Pi/2;
|
t2 := arctan(-RY*tan(XRotation)/RX) + pi; //Pi/2; // why add pi/2 ??
|
||||||
t3 := arctan(-RY*tan(XRotation)/RX) + Pi;
|
// t3 := arctan(-RY*tan(XRotation)/RX) + Pi;
|
||||||
|
|
||||||
x1 := Cx + RX*Cos(t1)*Cos(XRotation)-RY*Sin(t1)*Sin(XRotation);
|
x1 := Cx + RX*Cos(t1)*Cos(XRotation)-RY*Sin(t1)*Sin(XRotation);
|
||||||
x2 := Cx + RX*Cos(t2)*Cos(XRotation)-RY*Sin(t2)*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(x1, x2);
|
||||||
ALeft := Min(ALeft, x3);
|
// ALeft := Min(ALeft, x3);
|
||||||
|
|
||||||
ARight := Max(x1, x2);
|
ARight := Max(x1, x2);
|
||||||
ARight := Max(ARight, x3);
|
//ARight := Max(ARight, x3);
|
||||||
|
|
||||||
// Now the same for Y
|
// Now the same for Y
|
||||||
|
|
||||||
t1 := arctan(RY*cotan(XRotation)/RX);
|
t1 := arctan(RY*cotan(XRotation)/RX);
|
||||||
t2 := arctan(RY*cotan(XRotation)/RX) + Pi/2;
|
t2 := arctan(RY*cotan(XRotation)/RX) + pi; //Pi/2; // why add pi/2 ??
|
||||||
t3 := arctan(RY*cotan(XRotation)/RX) + 3*Pi/2;
|
// t3 := arctan(RY*cotan(XRotation)/RX) + 3*Pi/2;
|
||||||
|
|
||||||
y1 := CY + RY*Sin(t1)*Cos(XRotation)+RX*Cos(t1)*Sin(XRotation);
|
y1 := CY + RY*Sin(t1)*Cos(XRotation)+RX*Cos(t1)*Sin(XRotation);
|
||||||
y2 := CY + RY*Sin(t2)*Cos(XRotation)+RX*Cos(t2)*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(y1, y2);
|
||||||
ATop := Min(ATop, y3);
|
ATop := Min(ATop, y3);
|
||||||
|
|
||||||
ABottom := Max(y1, y2);
|
ABottom := Max(y1, y2);
|
||||||
ABottom := Max(ABottom, y3);
|
ABottom := Max(ABottom, y3);
|
||||||
|
}
|
||||||
end;
|
end;
|
||||||
end;
|
end;
|
||||||
|
|
||||||
@ -2830,6 +2945,24 @@ begin
|
|||||||
Result := ADestRoutine(lStr, APageItem);
|
Result := ADestRoutine(lStr, APageItem);
|
||||||
end;
|
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 }
|
{ TvVerticalFormulaStack }
|
||||||
|
|
||||||
function TvVerticalFormulaStack.CalculateHeight(ADest: TFPCustomCanvas): Double;
|
function TvVerticalFormulaStack.CalculateHeight(ADest: TFPCustomCanvas): Double;
|
||||||
@ -2970,6 +3103,13 @@ begin
|
|||||||
Result := ADestRoutine(lStr, APageItem);
|
Result := ADestRoutine(lStr, APageItem);
|
||||||
end;
|
end;
|
||||||
|
|
||||||
|
procedure TPathSegment.AddToPoints(ADestX, ADestY: Integer; AMulX, AMulY: Double;
|
||||||
|
var Points: TPointsArray);
|
||||||
|
begin
|
||||||
|
// Override by descendants
|
||||||
|
end;
|
||||||
|
|
||||||
|
|
||||||
{ T2DSegment }
|
{ T2DSegment }
|
||||||
|
|
||||||
function T2DSegment.GetLength: Double;
|
function T2DSegment.GetLength: Double;
|
||||||
@ -3018,6 +3158,17 @@ begin
|
|||||||
Result := ADestRoutine(lStr, APageItem);
|
Result := ADestRoutine(lStr, APageItem);
|
||||||
end;
|
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 }
|
{ T2DBezierSegment }
|
||||||
|
|
||||||
function T2DBezierSegment.GetLength: Double;
|
function T2DBezierSegment.GetLength: Double;
|
||||||
@ -3060,6 +3211,42 @@ begin
|
|||||||
Result := ADestRoutine(lStr, APageItem);
|
Result := ADestRoutine(lStr, APageItem);
|
||||||
end;
|
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 }
|
{ T3DSegment }
|
||||||
|
|
||||||
procedure T3DSegment.Move(ADeltaX, ADeltaY: Double);
|
procedure T3DSegment.Move(ADeltaX, ADeltaY: Double);
|
||||||
@ -3068,6 +3255,18 @@ begin
|
|||||||
Y := Y + ADeltaY;
|
Y := Y + ADeltaY;
|
||||||
end;
|
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 }
|
{ T3DBezierSegment }
|
||||||
|
|
||||||
procedure T3DBezierSegment.Move(ADeltaX, ADeltaY: Double);
|
procedure T3DBezierSegment.Move(ADeltaX, ADeltaY: Double);
|
||||||
@ -4001,10 +4200,116 @@ begin
|
|||||||
SetLength(Result, n);
|
SetLength(Result, n);
|
||||||
end;
|
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;
|
procedure TPath.Render(ADest: TFPCustomCanvas; var ARenderInfo: TvRenderInfo; ADestX: Integer;
|
||||||
ADestY: Integer; AMulX: Double; AMulY: Double; ADoDraw: Boolean);
|
ADestY: Integer; AMulX: Double; AMulY: Double; ADoDraw: Boolean);
|
||||||
|
|
||||||
function HasStraightSegmentsOnly: Boolean;
|
function CanFill: Boolean;
|
||||||
var
|
var
|
||||||
seg: TPathSegment;
|
seg: TPathSegment;
|
||||||
j: Integer;
|
j: Integer;
|
||||||
@ -4022,8 +4327,10 @@ procedure TPath.Render(ADest: TFPCustomCanvas; var ARenderInfo: TvRenderInfo; AD
|
|||||||
end;
|
end;
|
||||||
end;
|
end;
|
||||||
|
|
||||||
|
const
|
||||||
|
POINT_BUFFER = 100;
|
||||||
var
|
var
|
||||||
j: Integer;
|
i, j: Integer;
|
||||||
PosX, PosY: Double; // Not modified by ADestX, etc
|
PosX, PosY: Double; // Not modified by ADestX, etc
|
||||||
CoordX, CoordY: Integer;
|
CoordX, CoordY: Integer;
|
||||||
CurSegment: TPathSegment;
|
CurSegment: TPathSegment;
|
||||||
@ -4032,10 +4339,11 @@ var
|
|||||||
Cur2DArcSegment: T2DEllipticalArcSegment absolute CurSegment;
|
Cur2DArcSegment: T2DEllipticalArcSegment absolute CurSegment;
|
||||||
x1, y1, x2, y2: Integer;
|
x1, y1, x2, y2: Integer;
|
||||||
// For bezier
|
// For bezier
|
||||||
CoordX2, CoordY2, CoordX3, CoordY3, CoordX4, CoordY4: Integer;
|
CoordX2, CoordY2, CoordX3, CoordY3, CoordX4, CoordY4, CoordX5, CoordY5: Integer;
|
||||||
//t: Double;
|
|
||||||
// For polygons
|
// For polygons
|
||||||
lPoints: array of TPoint;
|
lPoints, pts: array of TPoint;
|
||||||
|
NumPoints: Integer;
|
||||||
|
pts3d: T3dPointsArray = nil;
|
||||||
// for elliptical arcs
|
// for elliptical arcs
|
||||||
BoxLeft, BoxTop, BoxRight, BoxBottom: Double;
|
BoxLeft, BoxTop, BoxRight, BoxBottom: Double;
|
||||||
EllipseRect: TRect;
|
EllipseRect: TRect;
|
||||||
@ -4084,38 +4392,46 @@ begin
|
|||||||
{$endif}
|
{$endif}
|
||||||
|
|
||||||
// useful in some paths, like stars!
|
// useful in some paths, like stars!
|
||||||
|
{ -- wp: causes artifacts in case of concave path
|
||||||
if ADoDraw then
|
if ADoDraw then
|
||||||
RenderInternalPolygon(ADest, ARenderInfo, ADestX, ADestY, AMulX, AMulY);
|
RenderInternalPolygon(ADest, ARenderInfo, ADestX, ADestY, AMulX, AMulY);
|
||||||
|
}
|
||||||
|
|
||||||
{$IFDEF USE_LCL_CANVAS}
|
if CanFill then
|
||||||
if ADoDraw and (Brush.Kind in [bkHorizontalGradient, bkVerticalGradient]) and
|
|
||||||
HasStraightSegmentsOnly then
|
|
||||||
begin
|
begin
|
||||||
x1 := MaxInt;
|
// Manually fill polygon with gradient
|
||||||
y1 := MaxInt;
|
{$IFDEF USE_LCL_CANVAS}
|
||||||
x2 := -MaxInt;
|
if ADoDraw and (Brush.Kind in [bkHorizontalGradient, bkVerticalGradient]) then
|
||||||
y2 := -MaxInt;
|
|
||||||
PrepareForSequentialReading;
|
|
||||||
for j := 0 to Len - 1 do
|
|
||||||
begin
|
begin
|
||||||
CurSegment := TPathSegment(Next);
|
x1 := MaxInt;
|
||||||
CoordX := CoordToCanvasX(Cur2DSegment.X, ADestX, AMulX);
|
y1 := MaxInt;
|
||||||
CoordY := CoordToCanvasY(Cur2DSegment.Y, ADestY, AMulY);
|
x2 := -MaxInt;
|
||||||
x1 := Min(x1, CoordX);
|
y2 := -MaxInt;
|
||||||
y1 := Min(y1, CoordY);
|
PrepareForSequentialReading;
|
||||||
x2 := Max(x2, CoordX);
|
for j := 0 to Len - 1 do
|
||||||
y2 := Max(y2, CoordY);
|
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;
|
end;
|
||||||
DrawBrushGradient(ADest, ARenderInfo, x1, y1, x2, y2, ADestX, ADestY, AMulX, AMulY);
|
{$ENDIF}
|
||||||
end;
|
end;
|
||||||
{$ENDIF}
|
|
||||||
|
|
||||||
//
|
//
|
||||||
// For other paths, draw more carefully
|
// For other paths, draw more carefully
|
||||||
//
|
//
|
||||||
ApplyPenToCanvas(ADest, ARenderInfo, Pen);
|
ApplyPenToCanvas(ADest, ARenderInfo, Pen); // Restore pen
|
||||||
PrepareForSequentialReading;
|
PrepareForSequentialReading;
|
||||||
|
|
||||||
|
SetLength(lPoints, POINT_BUFFER);
|
||||||
|
NumPoints := 0;
|
||||||
|
|
||||||
for j := 0 to Len - 1 do
|
for j := 0 to Len - 1 do
|
||||||
begin
|
begin
|
||||||
//WriteLn('j = ', j);
|
//WriteLn('j = ', j);
|
||||||
@ -4126,8 +4442,32 @@ begin
|
|||||||
begin
|
begin
|
||||||
CoordX := CoordToCanvasX(Cur2DSegment.X, ADestX, AMulX);
|
CoordX := CoordToCanvasX(Cur2DSegment.X, ADestX, AMulX);
|
||||||
CoordY := CoordToCanvasY(Cur2DSegment.Y, ADestY, AMulY);
|
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
|
if ADoDraw then
|
||||||
ADest.MoveTo(CoordX, CoordY);
|
ADest.MoveTo(CoordX, CoordY);
|
||||||
|
}
|
||||||
CalcEntityCanvasMinMaxXY(ARenderInfo, CoordX, CoordY);
|
CalcEntityCanvasMinMaxXY(ARenderInfo, CoordX, CoordY);
|
||||||
PosX := Cur2DSegment.X;
|
PosX := Cur2DSegment.X;
|
||||||
PosY := Cur2DSegment.Y;
|
PosY := Cur2DSegment.Y;
|
||||||
@ -4135,7 +4475,12 @@ begin
|
|||||||
Write(Format(' M%d,%d', [CoordX, CoordY]));
|
Write(Format(' M%d,%d', [CoordX, CoordY]));
|
||||||
{$endif}
|
{$endif}
|
||||||
end;
|
end;
|
||||||
|
|
||||||
// This element can override temporarely the Pen
|
// This element can override temporarely the Pen
|
||||||
|
|
||||||
|
// TO DO: Paint these segments with correct pen at end !!!!
|
||||||
|
|
||||||
|
|
||||||
st2DLineWithPen:
|
st2DLineWithPen:
|
||||||
begin
|
begin
|
||||||
ADest.Pen.FPColor := AdjustColorToBackground(T2DSegmentWithPen(Cur2DSegment).Pen.Color, ARenderInfo);
|
ADest.Pen.FPColor := AdjustColorToBackground(T2DSegmentWithPen(Cur2DSegment).Pen.Color, ARenderInfo);
|
||||||
@ -4145,7 +4490,14 @@ begin
|
|||||||
CoordY2 := CoordToCanvasY(Cur2DSegment.Y, ADestY, AMulY);
|
CoordY2 := CoordToCanvasY(Cur2DSegment.Y, ADestY, AMulY);
|
||||||
CalcEntityCanvasMinMaxXY_With2Points(ARenderInfo, CoordX, CoordY, CoordX2, CoordY2);
|
CalcEntityCanvasMinMaxXY_With2Points(ARenderInfo, CoordX, CoordY, CoordX2, CoordY2);
|
||||||
if ADoDraw then
|
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;
|
PosX := Cur2DSegment.X;
|
||||||
PosY := Cur2DSegment.Y;
|
PosY := Cur2DSegment.Y;
|
||||||
@ -4156,6 +4508,7 @@ begin
|
|||||||
Write(Format(' L%d,%d', [CoordX2, CoordY2]));
|
Write(Format(' L%d,%d', [CoordX2, CoordY2]));
|
||||||
{$endif}
|
{$endif}
|
||||||
end;
|
end;
|
||||||
|
|
||||||
st2DLine, st3DLine:
|
st2DLine, st3DLine:
|
||||||
begin
|
begin
|
||||||
CoordX := CoordToCanvasX(PosX, ADestX, AMulX);
|
CoordX := CoordToCanvasX(PosX, ADestX, AMulX);
|
||||||
@ -4164,13 +4517,21 @@ begin
|
|||||||
CoordY2 := CoordToCanvasY(Cur2DSegment.Y, ADestY, AMulY);
|
CoordY2 := CoordToCanvasY(Cur2DSegment.Y, ADestY, AMulY);
|
||||||
CalcEntityCanvasMinMaxXY_With2Points(ARenderInfo, CoordX, CoordY, CoordX2, CoordY2);
|
CalcEntityCanvasMinMaxXY_With2Points(ARenderInfo, CoordX, CoordY, CoordX2, CoordY2);
|
||||||
if ADoDraw then
|
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;
|
PosX := Cur2DSegment.X;
|
||||||
PosY := Cur2DSegment.Y;
|
PosY := Cur2DSegment.Y;
|
||||||
{$ifdef FPVECTORIAL_TOCANVAS_DEBUG}
|
{$ifdef FPVECTORIAL_TOCANVAS_DEBUG}
|
||||||
Write(Format(' L%d,%d', [CoordX2, CoordY2]));
|
Write(Format(' L%d,%d', [CoordX2, CoordY2]));
|
||||||
{$endif}
|
{$endif}
|
||||||
end;
|
end;
|
||||||
|
|
||||||
{ To draw a bezier we need to divide the interval in parts and make
|
{ To draw a bezier we need to divide the interval in parts and make
|
||||||
lines between this parts }
|
lines between this parts }
|
||||||
st2DBezier, st3DBezier:
|
st2DBezier, st3DBezier:
|
||||||
@ -4183,21 +4544,35 @@ begin
|
|||||||
CoordY3 := CoordToCanvasY(Cur2DBSegment.Y3, ADestY, AMulY);
|
CoordY3 := CoordToCanvasY(Cur2DBSegment.Y3, ADestY, AMulY);
|
||||||
CoordX4 := CoordToCanvasX(Cur2DBSegment.X, ADestX, AMulX);
|
CoordX4 := CoordToCanvasX(Cur2DBSegment.X, ADestX, AMulX);
|
||||||
CoordY4 := CoordToCanvasY(Cur2DBSegment.Y, ADestY, AMulY);
|
CoordY4 := CoordToCanvasY(Cur2DBSegment.Y, ADestY, AMulY);
|
||||||
SetLength(lPoints, 0);
|
// SetLength(lPoints, 0);
|
||||||
CalcEntityCanvasMinMaxXY_With2Points(ARenderInfo, CoordX, CoordY, CoordX2, CoordY2);
|
CalcEntityCanvasMinMaxXY_With2Points(ARenderInfo, CoordX, CoordY, CoordX2, CoordY2);
|
||||||
CalcEntityCanvasMinMaxXY_With2Points(ARenderInfo, CoordX3, CoordY3, CoordX4, CoordY4);
|
CalcEntityCanvasMinMaxXY_With2Points(ARenderInfo, CoordX3, CoordY3, CoordX4, CoordY4);
|
||||||
|
SetLength(pts, 0);
|
||||||
AddBezierToPoints(
|
AddBezierToPoints(
|
||||||
Make2DPoint(CoordX, CoordY),
|
Make2DPoint(CoordX, CoordY),
|
||||||
Make2DPoint(CoordX2, CoordY2),
|
Make2DPoint(CoordX2, CoordY2),
|
||||||
Make2DPoint(CoordX3, CoordY3),
|
Make2DPoint(CoordX3, CoordY3),
|
||||||
Make2DPoint(CoordX4, CoordY4),
|
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;
|
ADest.Brush.Style := Brush.Style;
|
||||||
|
{
|
||||||
if (Length(lPoints) >= 3) and ADoDraw then
|
if (Length(lPoints) >= 3) and ADoDraw then
|
||||||
ADest.Polygon(lPoints);
|
ADest.Polygon(lPoints);
|
||||||
|
}
|
||||||
PosX := Cur2DSegment.X;
|
PosX := Cur2DSegment.X;
|
||||||
PosY := Cur2DSegment.Y;
|
PosY := Cur2DSegment.Y;
|
||||||
|
|
||||||
@ -4209,34 +4584,19 @@ begin
|
|||||||
CoordToCanvasX(Cur2DBSegment.X, ADestX, AMulX), CoordToCanvasY(Cur2DBSegment.Y, ADestY, AMulY)]));
|
CoordToCanvasX(Cur2DBSegment.X, ADestX, AMulX), CoordToCanvasY(Cur2DBSegment.Y, ADestY, AMulY)]));
|
||||||
{$endif}
|
{$endif}
|
||||||
end;
|
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:
|
st2DEllipticalArc:
|
||||||
begin
|
begin
|
||||||
CoordX := CoordToCanvasX(PosX, ADestX, AMulX);
|
CoordX := CoordToCanvasX(PosX, ADestX, AMulX); // start point of segment
|
||||||
CoordY := CoordToCanvasY(PosY, ADestY, AMulY);
|
CoordY := CoordToCanvasY(PosY, ADestY, AMulY);
|
||||||
CoordX2 := CoordToCanvasX(Cur2DArcSegment.RX, ADestX, AMulX);
|
CoordX2 := CoordToCanvasX(Cur2DArcSegment.RX, ADestX, AMulX); // major axis radius
|
||||||
CoordY2 := CoordToCanvasY(Cur2DArcSegment.RY, ADestY, AMulY);
|
CoordY2 := CoordToCanvasY(Cur2DArcSegment.RY, ADestY, AMulY); // minor axis radius
|
||||||
CoordX3 := CoordToCanvasX(Cur2DArcSegment.XRotation, ADestX, AMulX);
|
CoordX3 := CoordToCanvasX(Cur2DArcSegment.XRotation, 0, sign(AMulX)); // axis rotation angle
|
||||||
CoordX4 := CoordToCanvasX(Cur2DArcSegment.X, ADestX, AMulX);
|
CoordX4 := CoordToCanvasX(Cur2DArcSegment.X, ADestX, AMulX); // end point of segment
|
||||||
CoordY4 := CoordToCanvasY(Cur2DArcSegment.Y, ADestY, AMulY);
|
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);
|
Cur2DArcSegment.CalculateEllipseBoundingBox(nil, BoxLeft, BoxTop, BoxRight, BoxBottom);
|
||||||
|
|
||||||
@ -4248,27 +4608,63 @@ begin
|
|||||||
{$ifdef FPVECTORIAL_TOCANVAS_ELLIPSE_VISUALDEBUG}
|
{$ifdef FPVECTORIAL_TOCANVAS_ELLIPSE_VISUALDEBUG}
|
||||||
ACanvas.Pen.Color := clRed;
|
ACanvas.Pen.Color := clRed;
|
||||||
ACanvas.Brush.Style := bsClear;
|
ACanvas.Brush.Style := bsClear;
|
||||||
ACanvas.Rectangle(
|
ACanvas.Rectangle( // Ellipse bounding box
|
||||||
EllipseRect.Left, EllipseRect.Top, EllipseRect.Right, EllipseRect.Bottom);
|
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}
|
{$endif}
|
||||||
|
|
||||||
ADest.Brush.Style := Brush.Style;
|
// ADest.Brush.Style := Brush.Style;
|
||||||
CalcEntityCanvasMinMaxXY_With2Points(ARenderInfo, CoordX, CoordY, CoordX4, CoordY4);
|
CalcEntityCanvasMinMaxXY_With2Points(ARenderInfo,
|
||||||
|
EllipseRect.Left, EllipseRect.Top, EllipseRect.Right, EllipseRect.Bottom);
|
||||||
|
|
||||||
if ADoDraw then
|
if ADoDraw then
|
||||||
begin
|
begin
|
||||||
// Arc draws counterclockwise
|
Cur2DArcSegment.PolyApproximate(pts3D);
|
||||||
if Cur2DArcSegment.ClockwiseArcFlag then
|
// 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
|
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(
|
ACanvas.Arc(
|
||||||
EllipseRect.Left, EllipseRect.Top, EllipseRect.Right, EllipseRect.Bottom,
|
EllipseRect.Left, EllipseRect.Top, EllipseRect.Right, EllipseRect.Bottom,
|
||||||
CoordX4, CoordY4, CoordX, CoordY);
|
CoordX4, CoordY4, CoordX, CoordY)
|
||||||
end else
|
else
|
||||||
begin
|
|
||||||
ACanvas.Arc(
|
ACanvas.Arc(
|
||||||
EllipseRect.Left, EllipseRect.Top, EllipseRect.Right, EllipseRect.Bottom,
|
EllipseRect.Left, EllipseRect.Top, EllipseRect.Right, EllipseRect.Bottom,
|
||||||
CoordX, CoordY, CoordX4, CoordY4);
|
CoordX, CoordY, CoordX4, CoordY4);
|
||||||
end;
|
end;
|
||||||
|
}
|
||||||
end;
|
end;
|
||||||
PosX := Cur2DArcSegment.X;
|
PosX := Cur2DArcSegment.X;
|
||||||
PosY := Cur2DArcSegment.Y;
|
PosY := Cur2DArcSegment.Y;
|
||||||
@ -4287,6 +4683,15 @@ begin
|
|||||||
WriteLn('');
|
WriteLn('');
|
||||||
{$endif}
|
{$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
|
// Restores the previous Clip Region
|
||||||
{$ifdef USE_CANVAS_CLIP_REGION}
|
{$ifdef USE_CANVAS_CLIP_REGION}
|
||||||
if ClipPath <> nil then
|
if ClipPath <> nil then
|
||||||
@ -4295,7 +4700,7 @@ begin
|
|||||||
end;
|
end;
|
||||||
{$endif}
|
{$endif}
|
||||||
end;
|
end;
|
||||||
|
*)
|
||||||
procedure TPath.RenderInternalPolygon(ADest: TFPCustomCanvas;
|
procedure TPath.RenderInternalPolygon(ADest: TFPCustomCanvas;
|
||||||
ARenderInfo: TvRenderInfo; ADestX: Integer; ADestY: Integer; AMulX: Double;
|
ARenderInfo: TvRenderInfo; ADestX: Integer; ADestY: Integer; AMulX: Double;
|
||||||
AMulY: Double);
|
AMulY: Double);
|
||||||
@ -7692,6 +8097,27 @@ begin
|
|||||||
AppendSegmentToTmpPath(segment);
|
AppendSegmentToTmpPath(segment);
|
||||||
end;
|
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);
|
procedure TvVectorialPage.SetBrushColor(AColor: TFPColor);
|
||||||
begin
|
begin
|
||||||
FTmPPath.Brush.Color := AColor;
|
FTmPPath.Brush.Color := AColor;
|
||||||
|
@ -8,9 +8,6 @@
|
|||||||
<SearchPaths>
|
<SearchPaths>
|
||||||
<UnitOutputDirectory Value="lib/$(TargetCPU)-$(TargetOS)"/>
|
<UnitOutputDirectory Value="lib/$(TargetCPU)-$(TargetOS)"/>
|
||||||
</SearchPaths>
|
</SearchPaths>
|
||||||
<Other>
|
|
||||||
<CompilerPath Value="$(CompPath)"/>
|
|
||||||
</Other>
|
|
||||||
</CompilerOptions>
|
</CompilerOptions>
|
||||||
<Files Count="21">
|
<Files Count="21">
|
||||||
<Item1>
|
<Item1>
|
||||||
@ -98,7 +95,6 @@
|
|||||||
<UnitName Value="htmlvectorialreader"/>
|
<UnitName Value="htmlvectorialreader"/>
|
||||||
</Item21>
|
</Item21>
|
||||||
</Files>
|
</Files>
|
||||||
<Type Value="RunAndDesignTime"/>
|
|
||||||
<RequiredPkgs Count="2">
|
<RequiredPkgs Count="2">
|
||||||
<Item1>
|
<Item1>
|
||||||
<PackageName Value="LCL"/>
|
<PackageName Value="LCL"/>
|
||||||
@ -114,5 +110,8 @@
|
|||||||
<PublishOptions>
|
<PublishOptions>
|
||||||
<Version Value="2"/>
|
<Version Value="2"/>
|
||||||
</PublishOptions>
|
</PublishOptions>
|
||||||
|
<CustomOptions Items="ExternHelp" Version="2">
|
||||||
|
<_ExternHelp Items="Count"/>
|
||||||
|
</CustomOptions>
|
||||||
</Package>
|
</Package>
|
||||||
</CONFIG>
|
</CONFIG>
|
||||||
|
@ -13,14 +13,8 @@ uses
|
|||||||
lazvectorialreader, mathmlvectorialreader, odgvectorialreader,
|
lazvectorialreader, mathmlvectorialreader, odgvectorialreader,
|
||||||
rawvectorialreadwrite, svgvectorialreader, svgvectorialwriter,
|
rawvectorialreadwrite, svgvectorialreader, svgvectorialwriter,
|
||||||
svgzvectorialreader, odtvectorialwriter, docxvectorialwriter,
|
svgzvectorialreader, odtvectorialwriter, docxvectorialwriter,
|
||||||
htmlvectorialreader, LazarusPackageIntf;
|
htmlvectorialreader;
|
||||||
|
|
||||||
implementation
|
implementation
|
||||||
|
|
||||||
procedure Register;
|
|
||||||
begin
|
|
||||||
end;
|
|
||||||
|
|
||||||
initialization
|
|
||||||
RegisterPackage('fpvectorialpkg', @Register);
|
|
||||||
end.
|
end.
|
||||||
|
@ -22,7 +22,7 @@ unit fpvutils;
|
|||||||
interface
|
interface
|
||||||
|
|
||||||
uses
|
uses
|
||||||
Classes, SysUtils, Math,
|
Classes, SysUtils, Math, Types,
|
||||||
{$ifdef USE_LCL_CANVAS}
|
{$ifdef USE_LCL_CANVAS}
|
||||||
Graphics, LCLIntf, LCLType,
|
Graphics, LCLIntf, LCLType,
|
||||||
{$endif}
|
{$endif}
|
||||||
@ -31,7 +31,7 @@ uses
|
|||||||
|
|
||||||
type
|
type
|
||||||
T10Strings = array[0..9] of shortstring;
|
T10Strings = array[0..9] of shortstring;
|
||||||
TPointsArray = array of TPoint;
|
// TPointsArray = array of TPoint;
|
||||||
TFPVUByteArray = array of Byte;
|
TFPVUByteArray = array of Byte;
|
||||||
|
|
||||||
TNumericalEquation = function (AParameter: Double): Double of object; // return the error
|
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_GetT_ForLength(P1, P2, P3, P4: T3DPoint; ALength: Double; ASteps: Integer = 30): Double;
|
||||||
function BezierEquation_GetPointAndTangentForLength(P1, P2, P3, P4: T3DPoint;
|
function BezierEquation_GetPointAndTangentForLength(P1, P2, P3, P4: T3DPoint;
|
||||||
ADistance: Double; out AX, AY, ATangentAngle: Double; ASteps: Integer = 30): Boolean;
|
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);
|
procedure ConvertPathToPoints(APath: TPath; ADestX, ADestY: Integer; AMulX, AMulY: Double; var Points: TPointsArray);
|
||||||
function Rotate2DPoint(P, RotCenter: TPoint; alpha:double): TPoint;
|
function Rotate2DPoint(P, RotCenter: TPoint; alpha:double): TPoint;
|
||||||
function Rotate3DPointInXY(P, RotCenter: T3DPoint; alpha:double): T3DPoint;
|
function Rotate3DPointInXY(P, RotCenter: T3DPoint; alpha:double): T3DPoint;
|
||||||
@ -376,6 +382,197 @@ begin
|
|||||||
Result := True;
|
Result := True;
|
||||||
end;
|
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);
|
procedure ConvertPathToPoints(APath: TPath; ADestX, ADestY: Integer; AMulX, AMulY: Double; var Points: TPointsArray);
|
||||||
var
|
var
|
||||||
i, LastPoint: Integer;
|
i, LastPoint: Integer;
|
||||||
@ -439,6 +636,8 @@ end;
|
|||||||
|
|
||||||
// Rotates a point P around RotCenter
|
// Rotates a point P around RotCenter
|
||||||
// alpha angle in radians
|
// 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;
|
function Rotate3DPointInXY(P, RotCenter: T3DPoint; alpha:double): T3DPoint;
|
||||||
var
|
var
|
||||||
sinus, cosinus : Extended;
|
sinus, cosinus : Extended;
|
||||||
@ -508,14 +707,12 @@ function SolveNumericallyAngle(ANumericalEquation: TNumericalEquation;
|
|||||||
var
|
var
|
||||||
lError, lErr1, lErr2, lErr3, lErr4: Double;
|
lError, lErr1, lErr2, lErr3, lErr4: Double;
|
||||||
lParam1, lParam2: Double;
|
lParam1, lParam2: Double;
|
||||||
lIterations: Integer;
|
|
||||||
lCount: Integer;
|
lCount: Integer;
|
||||||
begin
|
begin
|
||||||
lErr1 := ANumericalEquation(0);
|
lErr1 := ANumericalEquation(0);
|
||||||
lErr2 := ANumericalEquation(Pi/2);
|
lErr2 := ANumericalEquation(Pi/2);
|
||||||
lErr3 := ANumericalEquation(Pi);
|
lErr3 := ANumericalEquation(Pi);
|
||||||
lErr4 := ANumericalEquation(3*Pi/2);
|
lErr4 := ANumericalEquation(3*Pi/2);
|
||||||
|
|
||||||
// Choose the place to start
|
// Choose the place to start
|
||||||
if (lErr1 < lErr2) and (lErr1 < lErr3) and (lErr1 < lErr4) then
|
if (lErr1 < lErr2) and (lErr1 < lErr3) and (lErr1 < lErr4) then
|
||||||
begin
|
begin
|
||||||
@ -527,7 +724,7 @@ begin
|
|||||||
lParam1 := 0;
|
lParam1 := 0;
|
||||||
lParam2 := Pi;
|
lParam2 := Pi;
|
||||||
end
|
end
|
||||||
else if (lErr2 < lErr3) and (lErr2 < lErr4) then
|
else if (lErr2 < lErr3) and (lErr2 < lErr4) then // wp: same as above!
|
||||||
begin
|
begin
|
||||||
lParam1 := Pi/2;
|
lParam1 := Pi/2;
|
||||||
lParam2 := 3*Pi/2;
|
lParam2 := 3*Pi/2;
|
||||||
@ -535,7 +732,7 @@ begin
|
|||||||
else
|
else
|
||||||
begin
|
begin
|
||||||
lParam1 := Pi;
|
lParam1 := Pi;
|
||||||
lParam2 := 2*Pi;
|
lParam2 := TWO_PI;
|
||||||
end;
|
end;
|
||||||
|
|
||||||
// Iterate as many times necessary to get the best answer!
|
// Iterate as many times necessary to get the best answer!
|
||||||
|
@ -67,13 +67,15 @@ type
|
|||||||
{ TSVGPathTokenizer }
|
{ TSVGPathTokenizer }
|
||||||
|
|
||||||
TSVGPathTokenizer = class
|
TSVGPathTokenizer = class
|
||||||
|
protected
|
||||||
|
Tokens: TSVGTokenList;
|
||||||
public
|
public
|
||||||
FPointSeparator, FCommaSeparator: TFormatSettings;
|
FPointSeparator, FCommaSeparator: TFormatSettings;
|
||||||
Tokens: TSVGTokenList;
|
|
||||||
ExtraDebugStr: string;
|
ExtraDebugStr: string;
|
||||||
constructor Create;
|
constructor Create;
|
||||||
Destructor Destroy; override;
|
destructor Destroy; override;
|
||||||
procedure AddToken(AStr: string);
|
procedure AddToken(AStr: string);
|
||||||
|
procedure ClearTokens;
|
||||||
procedure TokenizePathString(AStr: string);
|
procedure TokenizePathString(AStr: string);
|
||||||
procedure TokenizeFunctions(AStr: string);
|
procedure TokenizeFunctions(AStr: string);
|
||||||
function DebugOutTokensAsString: string;
|
function DebugOutTokensAsString: string;
|
||||||
@ -208,13 +210,9 @@ begin
|
|||||||
end;
|
end;
|
||||||
|
|
||||||
destructor TSVGPathTokenizer.Destroy;
|
destructor TSVGPathTokenizer.Destroy;
|
||||||
var
|
|
||||||
i: Integer;
|
|
||||||
begin
|
begin
|
||||||
for i:=Tokens.Count-1 downto 0 do
|
ClearTokens;
|
||||||
Tokens[i].Free;
|
|
||||||
Tokens.Free;
|
Tokens.Free;
|
||||||
|
|
||||||
inherited Destroy;
|
inherited Destroy;
|
||||||
end;
|
end;
|
||||||
|
|
||||||
@ -278,6 +276,15 @@ begin
|
|||||||
Tokens.Add(lToken);
|
Tokens.Add(lToken);
|
||||||
end;
|
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);
|
procedure TSVGPathTokenizer.TokenizePathString(AStr: string);
|
||||||
const
|
const
|
||||||
Str_Space: Char = ' ';
|
Str_Space: Char = ' ';
|
||||||
@ -879,6 +886,13 @@ begin
|
|||||||
|
|
||||||
Result := Result + [spbfBrushColor, spbfBrushStyle];
|
Result := Result + [spbfBrushColor, spbfBrushStyle];
|
||||||
end
|
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
|
else if AKey = 'fill-opacity' then
|
||||||
ADestEntity.Brush.Color.Alpha := StringFloatZeroToOneToWord(AValue)
|
ADestEntity.Brush.Color.Alpha := StringFloatZeroToOneToWord(AValue)
|
||||||
// For linear gradient => stop-color:rgb(255,255,0);stop-opacity:1
|
// For linear gradient => stop-color:rgb(255,255,0);stop-opacity:1
|
||||||
@ -1947,7 +1961,7 @@ var
|
|||||||
lDebugStr: String;
|
lDebugStr: String;
|
||||||
lTmpTokenType: TSVGTokenType;
|
lTmpTokenType: TSVGTokenType;
|
||||||
begin
|
begin
|
||||||
FSVGPathTokenizer.Tokens.Clear;
|
FSVGPathTokenizer.ClearTokens;
|
||||||
FSVGPathTokenizer.TokenizePathString(AStr);
|
FSVGPathTokenizer.TokenizePathString(AStr);
|
||||||
//lDebugStr := FSVGPathTokenizer.DebugOutTokensAsString();
|
//lDebugStr := FSVGPathTokenizer.DebugOutTokensAsString();
|
||||||
CurX := 0;
|
CurX := 0;
|
||||||
@ -1981,7 +1995,7 @@ procedure TvSVGVectorialReader.ReadNextPathCommand(ACurTokenType: TSVGTokenType;
|
|||||||
var i: Integer; var CurX, CurY: Double; AData: TvVectorialPage;
|
var i: Integer; var CurX, CurY: Double; AData: TvVectorialPage;
|
||||||
ADoc: TvVectorialDocument);
|
ADoc: TvVectorialDocument);
|
||||||
var
|
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;
|
LargeArcFlag, SweepFlag, LeftmostEllipse, ClockwiseArc: Boolean;
|
||||||
lCurTokenType: TSVGTokenType;
|
lCurTokenType: TSVGTokenType;
|
||||||
lDebugStr: String;
|
lDebugStr: String;
|
||||||
@ -2225,29 +2239,68 @@ begin
|
|||||||
begin
|
begin
|
||||||
X2 := FSVGPathTokenizer.Tokens.Items[i+1].Value; // RX
|
X2 := FSVGPathTokenizer.Tokens.Items[i+1].Value; // RX
|
||||||
Y2 := FSVGPathTokenizer.Tokens.Items[i+2].Value; // RY
|
Y2 := FSVGPathTokenizer.Tokens.Items[i+2].Value; // RY
|
||||||
X3 := FSVGPathTokenizer.Tokens.Items[i+3].Value; // RotationX
|
phi := FSVGPathTokenizer.Tokens.Items[i+3].Value; // RotationX
|
||||||
X3 := X3 * Pi / 180; // degrees to radians conversion
|
phi := DegToRad(phi); // degrees to radians conversion
|
||||||
LargeArcFlag := Round(FSVGPathTokenizer.Tokens.Items[i+4].Value) = 1;
|
LargeArcFlag := Round(FSVGPathTokenizer.Tokens.Items[i+4].Value) = 1;
|
||||||
SweepFlag := Round(FSVGPathTokenizer.Tokens.Items[i+5].Value) = 1;
|
SweepFlag := Round(FSVGPathTokenizer.Tokens.Items[i+5].Value) = 1;
|
||||||
X := FSVGPathTokenizer.Tokens.Items[i+6].Value; // X
|
X := FSVGPathTokenizer.Tokens.Items[i+6].Value; // X
|
||||||
Y := FSVGPathTokenizer.Tokens.Items[i+7].Value; // Y
|
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);
|
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
|
// Careful that absolute coordinates require using ConvertSVGCoordinatesToFPVCoordinates
|
||||||
if lCurTokenType in [sttRelativeEllipticArcTo] then
|
if lCurTokenType in [sttRelativeEllipticArcTo] then
|
||||||
begin
|
ConvertSVGDeltaToFPVDelta(AData, X, Y, X, Y)
|
||||||
ConvertSVGDeltaToFPVDelta(AData, X, Y, X, Y);
|
|
||||||
end
|
|
||||||
else
|
else
|
||||||
begin
|
|
||||||
ConvertSVGCoordinatesToFPVCoordinates(AData, X, Y, X, Y);
|
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;
|
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
|
// Convert SVG flags to fpvectorial flags
|
||||||
LeftmostEllipse := (LargeArcFlag and (not SweepFlag))
|
LeftmostEllipse := (LargeArcFlag and (not SweepFlag))
|
||||||
or ((not LargeArcFlag) and 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;
|
ClockwiseArc := SweepFlag;
|
||||||
|
|
||||||
if lCurTokenType = sttRelativeEllipticArcTo then
|
if lCurTokenType = sttRelativeEllipticArcTo then
|
||||||
@ -2262,6 +2315,7 @@ begin
|
|||||||
CurX := X;
|
CurX := X;
|
||||||
CurY := Y;
|
CurY := Y;
|
||||||
end;
|
end;
|
||||||
|
}
|
||||||
|
|
||||||
Inc(i, 8);
|
Inc(i, 8);
|
||||||
end
|
end
|
||||||
@ -2278,7 +2332,7 @@ var
|
|||||||
X, Y: Double;
|
X, Y: Double;
|
||||||
FirstPtX, FirstPtY, CurX, CurY: Double;
|
FirstPtX, FirstPtY, CurX, CurY: Double;
|
||||||
begin
|
begin
|
||||||
FSVGPathTokenizer.Tokens.Clear;
|
FSVGPathTokenizer.ClearTokens;
|
||||||
FSVGPathTokenizer.TokenizePathString(AStr);
|
FSVGPathTokenizer.TokenizePathString(AStr);
|
||||||
CurX := 0;
|
CurX := 0;
|
||||||
CurY := 0;
|
CurY := 0;
|
||||||
@ -3012,9 +3066,13 @@ begin
|
|||||||
end;
|
end;
|
||||||
|
|
||||||
destructor TvSVGVectorialReader.Destroy;
|
destructor TvSVGVectorialReader.Destroy;
|
||||||
|
var
|
||||||
|
i: Integer;
|
||||||
begin
|
begin
|
||||||
FLayerStylesKeys.Free;
|
FLayerStylesKeys.Free;
|
||||||
FLayerStylesValues.Free;
|
FLayerStylesValues.Free;
|
||||||
|
|
||||||
|
for i:=FBrushDefs.Count-1 downto 0 do TObject(FBrushDefs[i]).Free;
|
||||||
FBrushDefs.Free;
|
FBrushDefs.Free;
|
||||||
FSVGPathTokenizer.Free;
|
FSVGPathTokenizer.Free;
|
||||||
|
|
||||||
|
Loading…
Reference in New Issue
Block a user