You've already forked lazarus-ccr
fpvectorial: Implements support for arc in the postscript reader
git-svn-id: https://svn.code.sf.net/p/lazarus-ccr/svn@1766 8e941d3f-bd1b-0410-a28a-d453659cc2b4
This commit is contained in:
@ -1455,6 +1455,9 @@ function TvEPSVectorialReader.ExecutePathConstructionOperator(
|
||||
var
|
||||
Param1, Param2, Param3, Param4, Param5, Param6: TPSToken;
|
||||
PosX, PosY, PosX2, PosY2, PosX3, PosY3, BaseX, BaseY: Double;
|
||||
// For Arc
|
||||
P1, P2, P3, P4: T3DPoint;
|
||||
startAngle, endAngle: Double;
|
||||
begin
|
||||
Result := False;
|
||||
|
||||
@ -1506,7 +1509,7 @@ begin
|
||||
Param1 := TPSToken(Stack.Pop);
|
||||
Param2 := TPSToken(Stack.Pop);
|
||||
PostScriptCoordsToFPVectorialCoords(Param1, Param2, PosX, PosY);
|
||||
AData.GetCurrenPathPenPos(BaseX, BaseY);
|
||||
AData.GetCurrentPathPenPos(BaseX, BaseY);
|
||||
PosX := PosX + CurrentGraphicState.TranslateX;
|
||||
PosY := PosY + CurrentGraphicState.TranslateY;
|
||||
{$ifdef FPVECTORIALDEBUG_PATHS}
|
||||
@ -1534,7 +1537,8 @@ begin
|
||||
PostScriptCoordsToFPVectorialCoords(Param5, Param6, PosX, PosY);
|
||||
PostScriptCoordsToFPVectorialCoords(Param3, Param4, PosX2, PosY2);
|
||||
PostScriptCoordsToFPVectorialCoords(Param1, Param2, PosX3, PosY3);
|
||||
AData.GetCurrenPathPenPos(BaseX, BaseY);
|
||||
AData.GetCurrentPathPenPos(BaseX, BaseY);
|
||||
// First move to the start of the arc
|
||||
BaseX := BaseX + CurrentGraphicState.TranslateX;
|
||||
BaseY := BaseY + CurrentGraphicState.TranslateY;
|
||||
{$ifdef FPVECTORIALDEBUG_PATHS}
|
||||
@ -1558,19 +1562,47 @@ begin
|
||||
|
||||
Exit(True);
|
||||
end;
|
||||
// x y r angle1 angle2 arc – Append counterclockwise arc
|
||||
{
|
||||
x y r angle1 angle2 arc – Append counterclockwise arc
|
||||
|
||||
Arcs in PostScript are described by a center (x, y), a radius r and
|
||||
two angles, angle1 for the start and angle2 for the end. These two
|
||||
angles are relative to the X axis growing to the right (positive direction).
|
||||
|
||||
}
|
||||
if AToken.StrValue = 'arc' then
|
||||
begin
|
||||
Param1 := TPSToken(Stack.Pop);
|
||||
Param2 := TPSToken(Stack.Pop);
|
||||
Param3 := TPSToken(Stack.Pop);
|
||||
Param4 := TPSToken(Stack.Pop);
|
||||
Param5 := TPSToken(Stack.Pop);
|
||||
Param1 := TPSToken(Stack.Pop); // angle2
|
||||
Param2 := TPSToken(Stack.Pop); // angle1
|
||||
Param3 := TPSToken(Stack.Pop); // r
|
||||
Param4 := TPSToken(Stack.Pop); // y
|
||||
Param5 := TPSToken(Stack.Pop); // x
|
||||
PostScriptCoordsToFPVectorialCoords(Param4, Param5, PosX, PosY);
|
||||
PosX := PosX + CurrentGraphicState.TranslateX;
|
||||
PosY := PosY + CurrentGraphicState.TranslateY;
|
||||
startAngle := Param2.FloatValue * Pi / 180;
|
||||
endAngle := Param1.FloatValue * Pi / 180;
|
||||
|
||||
// If the angle is too big we need to use two beziers
|
||||
if endAngle - startAngle > Pi then
|
||||
begin
|
||||
CircularArcToBezier(PosX, PosY, Param3.FloatValue, startAngle, endAngle - Pi, P1, P2, P3, P4);
|
||||
AData.AddMoveToPath(P1.X, P1.Y);
|
||||
AData.AddBezierToPath(P2.X, P2.Y, P3.X, P3.Y, P4.X, P4.Y);
|
||||
|
||||
CircularArcToBezier(PosX, PosY, Param3.FloatValue, startAngle + Pi, endAngle, P1, P2, P3, P4);
|
||||
AData.AddMoveToPath(P1.X, P1.Y);
|
||||
AData.AddBezierToPath(P2.X, P2.Y, P3.X, P3.Y, P4.X, P4.Y);
|
||||
end
|
||||
else
|
||||
begin
|
||||
CircularArcToBezier(PosX, PosY, Param3.FloatValue, startAngle, endAngle, P1, P2, P3, P4);
|
||||
AData.AddMoveToPath(P1.X, P1.Y);
|
||||
AData.AddBezierToPath(P2.X, P2.Y, P3.X, P3.Y, P4.X, P4.Y);
|
||||
end;
|
||||
// {$ifdef FPVECTORIALDEBUG}
|
||||
// WriteLn(Format('[TvEPSVectorialReader.ExecutePathConstructionOperator] rcurveto %f, %f', [BaseX + PosX, BaseY + PosY]));
|
||||
// {$endif}
|
||||
// AData.AddBezierToPath(BaseX + PosX, BaseY + PosY, BaseX + PosX2, BaseY + PosY2, BaseX + PosX3, BaseY + PosY3);
|
||||
{$ifdef FPVECTORIALDEBUG_PATHS}
|
||||
WriteLn(Format('[TvEPSVectorialReader.ExecutePathConstructionOperator] arc %f, %f', [PosX, PosY]));
|
||||
{$endif}
|
||||
|
@ -295,7 +295,7 @@ type
|
||||
procedure AddLineToPath(AX, AY: Double); overload;
|
||||
procedure AddLineToPath(AX, AY: Double; AColor: TFPColor); overload;
|
||||
procedure AddLineToPath(AX, AY, AZ: Double); overload;
|
||||
procedure GetCurrenPathPenPos(var AX, AY: Double);
|
||||
procedure GetCurrentPathPenPos(var AX, AY: Double);
|
||||
procedure AddBezierToPath(AX1, AY1, AX2, AY2, AX3, AY3: Double); overload;
|
||||
procedure AddBezierToPath(AX1, AY1, AZ1, AX2, AY2, AZ2, AX3, AY3, AZ3: Double); overload;
|
||||
procedure SetBrushColor(AColor: TFPColor);
|
||||
@ -661,10 +661,10 @@ end;
|
||||
{@@
|
||||
Gets the current Pen Pos in the temporary path
|
||||
}
|
||||
procedure TvVectorialDocument.GetCurrenPathPenPos(var AX, AY: Double);
|
||||
procedure TvVectorialDocument.GetCurrentPathPenPos(var AX, AY: Double);
|
||||
begin
|
||||
// Check if we are the first segment in the tmp path
|
||||
if FTmpPath.PointsEnd = nil then raise Exception.Create('[TvVectorialDocument.GetCurrenPathPenPos] One cannot obtain the Pen Pos if there are no segments in the temporary path');
|
||||
if FTmpPath.PointsEnd = nil then raise Exception.Create('[TvVectorialDocument.GetCurrentPathPenPos] One cannot obtain the Pen Pos if there are no segments in the temporary path');
|
||||
|
||||
AX := T2DSegment(FTmpPath.PointsEnd).X;
|
||||
AY := T2DSegment(FTmpPath.PointsEnd).Y;
|
||||
|
@ -31,6 +31,9 @@ function RGBToFPColor(AR, AG, AB: byte): TFPColor; inline;
|
||||
function CanvasCoordsToFPVectorial(AY: Integer; AHeight: Integer): Integer; inline;
|
||||
function CanvasTextPosToFPVectorial(AY: Integer; ACanvasHeight, ATextHeight: Integer): Integer;
|
||||
function SeparateString(AString: string; ASeparator: char): T10Strings;
|
||||
// Mathematical routines
|
||||
procedure EllipticalArcToBezier(Xc, Yc, Rx, Ry, startAngle, endAngle: Double; var P1, P2, P3, P4: T3DPoint);
|
||||
procedure CircularArcToBezier(Xc, Yc, R, startAngle, endAngle: Double; var P1, P2, P3, P4: T3DPoint);
|
||||
|
||||
implementation
|
||||
|
||||
@ -110,5 +113,56 @@ begin
|
||||
end;
|
||||
end;
|
||||
|
||||
{ Considering a counter-clockwise arc, elliptical and alligned to the axises
|
||||
|
||||
An elliptical Arc can be converted to
|
||||
the following Cubic Bezier control points:
|
||||
|
||||
P1 = E(startAngle) <- start point
|
||||
P2 = P1+alfa * dE(startAngle) <- control point
|
||||
P3 = P4−alfa * dE(endAngle) <- control point
|
||||
P4 = E(endAngle) <- end point
|
||||
|
||||
source: http://www.spaceroots.org/documents/ellipse/elliptical-arc.pdf
|
||||
|
||||
The equation of an elliptical arc is:
|
||||
|
||||
X(t) = Xc + Rx * cos(t)
|
||||
Y(t) = Yc + Ry * sin(t)
|
||||
|
||||
dX(t)/dt = - Rx * sin(t)
|
||||
dY(t)/dt = + Ry * cos(t)
|
||||
}
|
||||
procedure EllipticalArcToBezier(Xc, Yc, Rx, Ry, startAngle, endAngle: Double;
|
||||
var P1, P2, P3, P4: T3DPoint);
|
||||
var
|
||||
halfLength, arcLength, alfa: Double;
|
||||
begin
|
||||
arcLength := endAngle - startAngle;
|
||||
halfLength := (endAngle - startAngle) / 2;
|
||||
alfa := sin(arcLength) * (Sqrt(4 + 3*sqr(tan(halfLength))) - 1) / 3;
|
||||
|
||||
// Start point
|
||||
P1.X := Xc + Rx * cos(startAngle);
|
||||
P1.Y := Yc + Ry * sin(startAngle);
|
||||
|
||||
// End point
|
||||
P4.X := Xc + Rx * cos(endAngle);
|
||||
P4.Y := Yc + Ry * sin(endAngle);
|
||||
|
||||
// Control points
|
||||
P2.X := P1.X + alfa * -1 * Rx * sin(startAngle);
|
||||
P2.Y := P1.Y + alfa * Ry * cos(startAngle);
|
||||
|
||||
P3.X := P4.X - alfa * -1 * Rx * sin(endAngle);
|
||||
P3.Y := P4.Y - alfa * Ry * cos(endAngle);
|
||||
end;
|
||||
|
||||
procedure CircularArcToBezier(Xc, Yc, R, startAngle, endAngle: Double; var P1,
|
||||
P2, P3, P4: T3DPoint);
|
||||
begin
|
||||
EllipticalArcToBezier(Xc, Yc, R, R, startAngle, endAngle, P1, P2, P3, P4);
|
||||
end;
|
||||
|
||||
end.
|
||||
|
||||
|
@ -14,8 +14,37 @@
|
||||
<VersionInfo>
|
||||
<StringTable ProductVersion=""/>
|
||||
</VersionInfo>
|
||||
<BuildModes Count="1">
|
||||
<BuildModes Count="2">
|
||||
<Item1 Name="default" Default="True"/>
|
||||
<Item2 Name="qt">
|
||||
<MacroValues Count="1">
|
||||
<Macro1 Name="LCLWidgetType" Value="qt"/>
|
||||
</MacroValues>
|
||||
<CompilerOptions>
|
||||
<Version Value="10"/>
|
||||
<Target>
|
||||
<Filename Value="fpvviewer"/>
|
||||
</Target>
|
||||
<SearchPaths>
|
||||
<IncludeFiles Value="$(ProjOutDir)"/>
|
||||
<OtherUnitFiles Value="fpvectorialsrc"/>
|
||||
<UnitOutputDirectory Value="lib/$(TargetCPU)-$(TargetOS)"/>
|
||||
</SearchPaths>
|
||||
<Linking>
|
||||
<Options>
|
||||
<Win32>
|
||||
<GraphicApplication Value="True"/>
|
||||
</Win32>
|
||||
</Options>
|
||||
</Linking>
|
||||
<Other>
|
||||
<CompilerMessages>
|
||||
<UseMsgFile Value="True"/>
|
||||
</CompilerMessages>
|
||||
<CompilerPath Value="$(CompPath)"/>
|
||||
</Other>
|
||||
</CompilerOptions>
|
||||
</Item2>
|
||||
</BuildModes>
|
||||
<PublishOptions>
|
||||
<Version Value="2"/>
|
||||
|
Reference in New Issue
Block a user