mirror of
https://gitlab.com/freepascal.org/lazarus/lazarus.git
synced 2025-08-09 21:35:57 +02:00
fpvectorial: Reorganize TPath.Render. Support polygon even-odd and non-zero winding rules for brush fill.
git-svn-id: trunk@51058 -
This commit is contained in:
parent
e8bf2b6e97
commit
c224456e7f
@ -275,6 +275,7 @@ type
|
|||||||
P3DPoint = ^T3DPoint;
|
P3DPoint = ^T3DPoint;
|
||||||
|
|
||||||
T3DPointsArray = array of T3DPoint;
|
T3DPointsArray = array of T3DPoint;
|
||||||
|
TPointsArray = array of TPoint;
|
||||||
|
|
||||||
TSegmentType = (
|
TSegmentType = (
|
||||||
st2DLine, st2DLineWithPen, st2DBezier,
|
st2DLine, st2DLineWithPen, st2DBezier,
|
||||||
@ -303,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;
|
||||||
|
|
||||||
{@@
|
{@@
|
||||||
@ -325,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)
|
||||||
@ -356,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 }
|
||||||
@ -368,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 }
|
||||||
@ -398,6 +407,7 @@ type
|
|||||||
procedure CalculateCenter;
|
procedure CalculateCenter;
|
||||||
procedure CalculateEllipseBoundingBox(ADest: TFPCustomCanvas; out 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);
|
||||||
@ -497,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;
|
||||||
@ -542,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
|
||||||
@ -2934,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;
|
||||||
@ -3074,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;
|
||||||
@ -3122,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;
|
||||||
@ -3164,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);
|
||||||
@ -3172,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);
|
||||||
@ -4105,6 +4200,112 @@ 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);
|
||||||
|
|
||||||
@ -4159,7 +4360,7 @@ begin
|
|||||||
// ADest.Brush.Style := bsClear;
|
// ADest.Brush.Style := bsClear;
|
||||||
|
|
||||||
ADest.MoveTo(ADestX, ADestY);
|
ADest.MoveTo(ADestX, ADestY);
|
||||||
(*
|
{
|
||||||
// Set the path Pen and Brush options
|
// Set the path Pen and Brush options
|
||||||
ADest.Pen.Style := Pen.Style;
|
ADest.Pen.Style := Pen.Style;
|
||||||
ADest.Pen.Width := Round(Pen.Width * AMulX);
|
ADest.Pen.Width := Round(Pen.Width * AMulX);
|
||||||
@ -4172,7 +4373,7 @@ begin
|
|||||||
ACanvas.Pen.SetPattern(Pen.Pattern);
|
ACanvas.Pen.SetPattern(Pen.Pattern);
|
||||||
{$endif}
|
{$endif}
|
||||||
ADest.Brush.FPColor := Brush.Color;
|
ADest.Brush.FPColor := Brush.Color;
|
||||||
*)
|
}
|
||||||
// Prepare the Clipping Region, if any
|
// Prepare the Clipping Region, if any
|
||||||
{$ifdef USE_CANVAS_CLIP_REGION}
|
{$ifdef USE_CANVAS_CLIP_REGION}
|
||||||
if ClipPath <> nil then
|
if ClipPath <> nil then
|
||||||
@ -4499,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);
|
||||||
|
@ -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
|
||||||
@ -64,6 +64,8 @@ function CalcEllipseCenter(x1,y1, x2,y2, rx,ry, phi: Double; fa, fs: Boolean;
|
|||||||
out cx,cy, lambda: Double): Boolean;
|
out cx,cy, lambda: Double): Boolean;
|
||||||
function CalcEllipsePointAngle(x,y, rx,ry, cx,cy, phi: Double): Double;
|
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 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;
|
||||||
@ -485,6 +487,92 @@ begin
|
|||||||
y := cy + ry*sint*cosphi + rx*cost*sinphi;
|
y := cy + ry*sint*cosphi + rx*cost*sinphi;
|
||||||
end;
|
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;
|
||||||
|
@ -879,6 +879,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
|
||||||
|
Loading…
Reference in New Issue
Block a user