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:
sekelsenmat
2011-07-28 14:42:21 +00:00
parent 4f7d5dd9af
commit e7119b8f38
4 changed files with 128 additions and 13 deletions

View File

@ -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}

View File

@ -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;

View File

@ -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.

View File

@ -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"/>