mirror of
				https://gitlab.com/freepascal.org/fpc/source.git
				synced 2025-11-04 06:39:25 +01:00 
			
		
		
		
	fpvectorial: Implements support for arc in the postscript reader
git-svn-id: trunk@18030 -
This commit is contained in:
		
							parent
							
								
									391f268bbb
								
							
						
					
					
						commit
						abdc672fe3
					
				@ -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.
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
		Loading…
	
		Reference in New Issue
	
	Block a user