mirror of
https://gitlab.com/freepascal.org/lazarus/lazarus.git
synced 2025-08-26 13:19:29 +02:00
fpvectorial: Correct rendering of "path with holes"
git-svn-id: trunk@52803 -
This commit is contained in:
parent
2dc04023df
commit
08e9be93a9
@ -561,8 +561,11 @@ type
|
||||
procedure CalcGradientVector(out AGradientStart, AGradientEnd: T2dPoint;
|
||||
const ARect: TRect; ADestX: Integer = 0; ADestY: Integer = 0;
|
||||
AMulX: Double = 1.0; AMulY: Double = 1.0);
|
||||
procedure DrawPolygon(ADest: TFPCustomCanvas; var RenderInfo: TvRenderInfo;
|
||||
const APoints: TPointsArray; const APolyStarts: TIntegerDynArray; ARect: TRect);
|
||||
procedure DrawPolygonBrushGradient(ADest: TFPCustomCanvas; var ARenderInfo: TvRenderInfo;
|
||||
const APoints: TPointsArray; ARect: TRect; AGradientStart, AGradientEnd: T2DPoint);
|
||||
const APoints: TPointsArray; const APolyStarts: TIntegerDynArray;
|
||||
ARect: TRect; AGradientStart, AGradientEnd: T2DPoint);
|
||||
procedure DrawPolygonBrushRadialGradient(ADest: TFPCustomCanvas; var ARenderInfo: TvRenderInfo;
|
||||
const APoints: TPointsArray; ARect: TRect);
|
||||
procedure DrawNativePolygonBrushRadialGradient(ADest: TFPCustomCanvas; var ARenderInfo: TvRenderInfo;
|
||||
@ -3944,13 +3947,69 @@ begin
|
||||
end;
|
||||
end;
|
||||
|
||||
{ Fills a polygon with the color of the current brush. The routine can handle
|
||||
non-contiguous polygons (holes!) correctly using the ScanLine algorithm and
|
||||
the even-odd rule
|
||||
http://www.tutorialspoint.com/computer_graphics/polygon_filling_algorithm.htm
|
||||
|
||||
NOTE: The method only performs a solid fill, i.e. Brush.Style is ignored }
|
||||
procedure TvEntityWithPenAndBrush.DrawPolygon(ADest: TFPCustomCanvas;
|
||||
var RenderInfo: TvRenderInfo; const APoints: TPointsArray;
|
||||
const APolyStarts: TIntegerDynArray; ARect: TRect);
|
||||
var
|
||||
scanlineY, scanLineY1, scanLineY2: Integer;
|
||||
lPoints, pts: T2DPointsArray;
|
||||
j: Integer;
|
||||
begin
|
||||
if ARect.Top < ARect.Bottom then
|
||||
begin
|
||||
scanLineY1 := ARect.Top;
|
||||
scanLineY2 := ARect.Bottom;
|
||||
end else
|
||||
begin
|
||||
scanLineY1 := ARect.Bottom;
|
||||
scanLineY2 := ARect.Top;
|
||||
end;
|
||||
|
||||
// Prepare points as needed by the GetLinePolygonIntersectionPoints procedure
|
||||
SetLength(pts, Length(APoints));
|
||||
for j := 0 to High(APoints) do
|
||||
pts[j] := Point2D(APoints[j].X, APoints[j].Y);
|
||||
|
||||
// Prepare parameters and polygon points
|
||||
ADest.Pen.Style := psSolid;
|
||||
ADest.Pen.Width := 1;
|
||||
ADest.Pen.FPColor := Brush.Color;
|
||||
|
||||
// Fill polygon by drawing horizontal line segments
|
||||
scanlineY := scanlineY1;
|
||||
while (scanlineY <= scanlineY2) do begin
|
||||
// Find intersection points of horizontal scan line with polygon
|
||||
// with polygon
|
||||
lPoints := GetLinePolygonIntersectionPoints(scanlineY, pts, APolyStarts, false);
|
||||
if Length(lPoints) < 2 then begin
|
||||
inc(scanlineY);
|
||||
Continue;
|
||||
end;
|
||||
// Draw lines between intersection points, skip every second pair
|
||||
j := 0;
|
||||
while j < High(lPoints) do
|
||||
begin
|
||||
ADest.Line(round(lPoints[j].X), round(lPoints[j].Y), round(lPoints[j+1].X), round(lPoints[j+1].Y));
|
||||
inc(j, 2);
|
||||
end;
|
||||
// Proceed to next scan line
|
||||
inc(scanlineY);
|
||||
end;
|
||||
end;
|
||||
|
||||
{ Fills the entity with a gradient.
|
||||
Assumes that the boundary is already in canvas units and is specified by
|
||||
polygon APoints. }
|
||||
procedure TvEntityWithPenAndBrush.DrawPolygonBrushGradient(
|
||||
ADest: TFPCustomCanvas; var ARenderInfo: TvRenderInfo;
|
||||
const APoints: TPointsArray; ARect: TRect; AGradientStart,
|
||||
AGradientEnd: T2DPoint);
|
||||
const APoints: TPointsArray;const APolyStarts: TIntegerDynArray;
|
||||
ARect: TRect; AGradientStart, AGradientEnd: T2DPoint);
|
||||
var
|
||||
lPoints, pts: T2DPointsArray;
|
||||
i, j: Integer;
|
||||
@ -4072,7 +4131,7 @@ begin
|
||||
begin
|
||||
// Find intersection points of gradient line (normal to gradient vector)
|
||||
// with polygon
|
||||
lPoints := GetLinePolygonIntersectionPoints(coord, pts, coordIsX);
|
||||
lPoints := GetLinePolygonIntersectionPoints(coord, pts, APolyStarts, coordIsX);
|
||||
if Length(lPoints) < 2 then begin
|
||||
coord := coord + dcoord;
|
||||
Continue;
|
||||
@ -4285,8 +4344,7 @@ begin
|
||||
end
|
||||
else
|
||||
{$endif}
|
||||
DrawPolygonBrushGradient(ADest, ARenderInfo, polypoints, lRect, gv1, gv2);
|
||||
// to do: multiple polygons!
|
||||
DrawPolygonBrushGradient(ADest, ARenderInfo, polypoints, polystarts, lRect, gv1, gv2);
|
||||
|
||||
// Paint outline
|
||||
if ADest.Pen.Style <> psClear then
|
||||
@ -4969,19 +5027,26 @@ begin
|
||||
bkSimpleBrush:
|
||||
if Brush.Style <> bsClear then
|
||||
begin
|
||||
{$IFDEF USE_LCL_CANVAS}
|
||||
for i := 0 to High(FPolyStarts) do
|
||||
begin
|
||||
j := FPolyStarts[i];
|
||||
if i = High(FPolyStarts) then
|
||||
n := Length(FPolyPoints) - j
|
||||
else
|
||||
n := FPolyStarts[i+1] - FPolyStarts[i]; // + 1;
|
||||
ACanvas.Polygon(@FPolyPoints[j], n, WindingRule = vcmNonZeroWindingRule);
|
||||
end;
|
||||
{$ELSE}
|
||||
ADest.Polygon(FPolyPoints);
|
||||
{$ENDIF}
|
||||
if (Brush.Style = bsSolid) and (Length(FPolyStarts) > 1) then begin
|
||||
// Non-contiguous polygon (polygon with "holes") --> use special procedure
|
||||
// Disadvantage: it can oly do solid fills!
|
||||
lRect := Rect(x1, y1, x2, y2);
|
||||
DrawPolygon(ADest, ARenderInfo, FPolyPoints, FPolyStarts, lRect)
|
||||
end
|
||||
else
|
||||
{$IFDEF USE_LCL_CANVAS}
|
||||
for i := 0 to High(FPolyStarts) do
|
||||
begin
|
||||
j := FPolyStarts[i];
|
||||
if i = High(FPolyStarts) then
|
||||
n := Length(FPolyPoints) - j
|
||||
else
|
||||
n := FPolyStarts[i+1] - FPolyStarts[i]; // + 1;
|
||||
ACanvas.Polygon(@FPolyPoints[j], n, WindingRule = vcmNonZeroWindingRule);
|
||||
end;
|
||||
{$ELSE}
|
||||
ADest.Polygon(FPolyPoints);
|
||||
{$ENDIF}
|
||||
end;
|
||||
else // gradients
|
||||
// Boundary rect of shape filled with a gradient
|
||||
@ -4989,7 +5054,7 @@ begin
|
||||
// calculate gradient vector
|
||||
CalcGradientVector(gv1, gv2, lRect, ADestX, ADestY, AMulX, AMulY);
|
||||
// Draw the gradient
|
||||
DrawPolygonBrushGradient(ADest, ARenderInfo, FPolyPoints, lRect, gv1, gv2);
|
||||
DrawPolygonBrushGradient(ADest, ARenderInfo, FPolyPoints, FPolyStarts, lRect, gv1, gv2);
|
||||
// to do: multiple polygons!
|
||||
end;
|
||||
|
||||
|
@ -73,7 +73,10 @@ procedure ConvertPathToPolygons(APath: TPath; ADestX, ADestY: Integer; AMulX, AM
|
||||
var PolygonPoints: TPointsArray; var PolygonStartIndexes: TIntegerDynArray);
|
||||
procedure ConvertPathToPoints(APath: TPath; ADestX, ADestY: Integer; AMulX, AMulY: Double; var Points: TPointsArray);
|
||||
function GetLinePolygonIntersectionPoints(ACoord: Double;
|
||||
const APoints: T2DPointsArray; ACoordIsX: Boolean): T2DPointsArray;
|
||||
const APoints: T2DPointsArray; const APolyStarts: TIntegerDynArray;
|
||||
ACoordIsX: Boolean): T2DPointsArray; overload;
|
||||
function GetLinePolygonIntersectionPoints(ACoord: Double;
|
||||
const APoints: T2DPointsArray; ACoordIsX: Boolean): T2DPointsArray; overload;
|
||||
function Rotate2DPoint(P, RotCenter: TPoint; alpha:double): TPoint;
|
||||
function Rotate3DPointInXY(P, RotCenter: T3DPoint; alpha:double): T3DPoint;
|
||||
procedure NormalizeRect(var ARect: TRect);
|
||||
@ -711,15 +714,27 @@ begin
|
||||
Result := CompareValue(val1^, val2^);
|
||||
end;
|
||||
|
||||
function GetLinePolygonIntersectionPoints(ACoord: Double;
|
||||
const APoints: T2DPointsArray; ACoordIsX: Boolean): T2DPointsArray;
|
||||
var
|
||||
polystarts: TIntegerDynArray;
|
||||
begin
|
||||
SetLength(polystarts, 1);
|
||||
polystarts[0] := 0;
|
||||
Result := GetLinePolygonIntersectionPoints(ACoord, APoints, ACoordIsX);
|
||||
end;
|
||||
|
||||
{@@ Calculates the intersection points of a vertical (ACoordIsX = true) or
|
||||
horizontal (ACoordIsX = false) line with border of the polygon specified
|
||||
by APoints. Returns the coordinates of the intersection points }
|
||||
function GetLinePolygonIntersectionPoints(ACoord: Double;
|
||||
const APoints: T2DPointsArray; ACoordIsX: Boolean): T2DPointsArray;
|
||||
const APoints: T2DPointsArray; const APolyStarts: TIntegerDynArray;
|
||||
ACoordIsX: Boolean): T2DPointsArray;
|
||||
const
|
||||
EPS = 1e-9;
|
||||
var
|
||||
j: Integer;
|
||||
j, p: Integer;
|
||||
firstj,lastj: Integer;
|
||||
dx, dy: Double;
|
||||
xval, yval: Double;
|
||||
val: ^Double;
|
||||
@ -728,41 +743,72 @@ begin
|
||||
list := TFPList.Create;
|
||||
if ACoordIsX then
|
||||
begin
|
||||
for j:=0 to High(APoints) - 1 do
|
||||
begin
|
||||
if ((APoints[j].X <= ACoord) and (ACoord < APoints[j+1].X)) or
|
||||
((APoints[j+1].X <= ACoord) and (ACoord < APoints[j].X)) then
|
||||
begin
|
||||
dx := APoints[j+1].X - APoints[j].X; // can't be zero here
|
||||
dy := APoints[j+1].Y - APoints[j].Y;
|
||||
New(val);
|
||||
val^ := APoints[j].Y + (ACoord - APoints[j].X) * dy / dx;
|
||||
list.Add(val);
|
||||
for p := 0 to High(APolyStarts) do begin
|
||||
firstj := APolyStarts[p];
|
||||
lastj := IfThen(p = High(APolyStarts), High(APoints), APolyStarts[p+1]-1);
|
||||
// Skip non-closed polygons
|
||||
if (APoints[firstj].X <> APoints[lastj].x) or (APoints[lastj].Y <> APoints[lastj].Y) then
|
||||
continue;
|
||||
for j := firstj to lastj-1 do
|
||||
if ((APoints[j].X <= ACoord) and (ACoord < APoints[j+1].X)) or
|
||||
((APoints[j+1].X <= ACoord) and (ACoord < APoints[j].X)) then
|
||||
begin
|
||||
dx := APoints[j+1].X - APoints[j].X; // can't be zero here
|
||||
dy := APoints[j+1].Y - APoints[j].Y;
|
||||
New(val);
|
||||
val^ := APoints[j].Y + (ACoord - APoints[j].X) * dy / dx;
|
||||
list.Add(val);
|
||||
end;
|
||||
end;
|
||||
end;
|
||||
end else
|
||||
begin
|
||||
for j:=0 to High(APoints) - 1 do
|
||||
if ((APoints[j].Y <= ACoord) and (ACoord < APoints[j+1].Y)) or
|
||||
((APoints[j+1].Y <= ACoord) and (ACoord < APoints[j].Y)) then
|
||||
begin
|
||||
dy := APoints[j+1].Y - APoints[j].Y; // can't be zero here
|
||||
dx := APoints[j+1].X - APoints[j].X;
|
||||
New(val);
|
||||
val^ := APoints[j].X + (ACoord - APoints[j].Y) * dx / dy;
|
||||
list.Add(val);
|
||||
end;
|
||||
for p := 0 to High(APolyStarts) do begin
|
||||
firstj := APolyStarts[p];
|
||||
lastj := IfThen(p = High(APolyStarts), High(APoints), APolyStarts[p+1]-1);
|
||||
// Skip non-closed polygons
|
||||
if (APoints[firstj].X <> APoints[lastj].x) or (APoints[lastj].Y <> APoints[lastj].Y) then
|
||||
continue;
|
||||
for j := firstj to lastj-1 do
|
||||
if ((APoints[j].Y <= ACoord) and (ACoord < APoints[j+1].Y)) or
|
||||
((APoints[j+1].Y <= ACoord) and (ACoord < APoints[j].Y)) then
|
||||
begin
|
||||
dy := APoints[j+1].Y - APoints[j].Y; // can't be zero here
|
||||
dx := APoints[j+1].X - APoints[j].X;
|
||||
New(val);
|
||||
val^ := APoints[j].X + (ACoord - APoints[j].Y) * dx / dy;
|
||||
list.Add(val);
|
||||
end;
|
||||
end;
|
||||
end;
|
||||
|
||||
// Sort intersection coordinates in ascending order
|
||||
list.Sort(@CompareDbl);
|
||||
SetLength(Result, list.Count);
|
||||
if ACoordIsX then
|
||||
for j:=0 to list.Count-1 do
|
||||
Result[j] := Point2D(ACoord, Double(list[j]^))
|
||||
else
|
||||
for j:=0 to list.Count-1 do
|
||||
Result[j] := Point2D(Double(list[j]^), ACoord);
|
||||
|
||||
// When scanning across an non-contiguous polygon the scan may produce an
|
||||
// odd number of points where the scan finds irregular points due to interaction
|
||||
// with the other polygon curves. I don't have a general solution, only for
|
||||
// the case of 3 points.
|
||||
(*
|
||||
if list.Count = 3 then begin // this can't be --> use ony outer points
|
||||
SetLength(Result, 2);
|
||||
if ACoordIsX then begin
|
||||
Result[0] := Point2D(ACoord, Double(list[0]^));
|
||||
Result[1] := Point2D(ACoord, Double(list[2]^));
|
||||
end else begin
|
||||
Result[0] := Point2D(Double(list[0]^), ACoord);
|
||||
Result[1] := Point2D(Double(list[2]^), ACoord);
|
||||
end;
|
||||
end else
|
||||
*)
|
||||
begin // regular case
|
||||
SetLength(Result, list.Count);
|
||||
if ACoordIsX then
|
||||
for j:=0 to list.Count-1 do
|
||||
Result[j] := Point2D(ACoord, Double(list[j]^))
|
||||
else
|
||||
for j:=0 to list.Count-1 do
|
||||
Result[j] := Point2D(Double(list[j]^), ACoord);
|
||||
end;
|
||||
|
||||
// Clean-up
|
||||
for j:=list.Count-1 downto 0 do
|
||||
|
Loading…
Reference in New Issue
Block a user