fpvectorial: More attempts at implementing support for elliptical arcs, not yet working

git-svn-id: trunk@41632 -
This commit is contained in:
sekelsenmat 2013-06-09 13:40:42 +00:00
parent 76cc6180d1
commit 5f6878bdb7
2 changed files with 126 additions and 3 deletions

View File

@ -230,6 +230,9 @@ type
{ T2DEllipticalArcSegment }
T2DEllipticalArcSegment = class(T2DSegment)
private
E1, E2: T3DPoint;
function AlignedEllipseCenterEquationT1(AParam: Double): Double;
public
RX, RY, XRotation: Double;
LeftmostEllipse, ClockwiseArcFlag: Boolean;
@ -1030,10 +1033,24 @@ end;
{ T2DEllipticalArcSegment }
function T2DEllipticalArcSegment.AlignedEllipseCenterEquationT1(
AParam: Double): Double;
var
lLeftSide, lRightSide, lArg: Double;
begin
// E1.Y - RY*sin(t1) = E2.Y - RY*sin(arccos((- E1.X + RX*cos(t1) + E2.X)/RX))
lLeftSide := E1.Y - RY*sin(AParam);
lArg := (- E1.X + RX*cos(AParam) + E2.X)/RX;
lRightSide := E2.Y - RY*sin(arccos(lArg));
Result := lLeftSide - lRightSide;
if Result < 0 then Result := -1* Result;
end;
procedure T2DEllipticalArcSegment.CalculateCenter;
var
XStart, YStart, t: Double;
XStart, YStart, lT1, lT2: Double;
CX1, CY1, CX2, CY2, LeftMostX, LeftMostY, RightMostX, RightMostY: Double;
RotatedCenter: T3DPoint;
begin
// Rotated Ellipse equation:
// (xcosθ+ysinθ)^2 / RX^2 + (ycosθxsinθ)^2 / RY^2 = 1
@ -1096,13 +1113,52 @@ begin
X - A*Cos(t2) + C*Sin(t2) = XStart - A*sqrt(1-Sin(t1)^2) + C*Sin(t1)
}
// Solve by rotating everything to align the ellipse to the axises and then rotating back again
E1 := Rotate3DPointInXY(Make3DPoint(XStart,YStart,0), Make3DPoint(0,0,0),-1*XRotation);
E2 := Rotate3DPointInXY(Make3DPoint(X,Y,0), Make3DPoint(0,0,0),-1*XRotation);
// parametrized:
// CX = E1.X - RX*cos(t1)
// CY = E1.Y - RY*sin(t1)
// CX = E2.X - RX*cos(t2)
// CY = E2.Y - RY*sin(t2)
//
// E1.X - RX*cos(t1) = E2.X - RX*cos(t2)
// E1.Y - RY*sin(t1) = E2.Y - RY*sin(t2)
//
// (- E1.X + RX*cos(t1) + E2.X)/RX = cos(t2)
// arccos((- E1.X + RX*cos(t1) + E2.X)/RX) = t2
//
// E1.Y - RY*sin(t1) = E2.Y - RY*sin(arccos((- E1.X + RX*cos(t1) + E2.X)/RX))
// SolveNumerically
lT1 := SolveNumericallyAngle(AlignedEllipseCenterEquationT1, 0.0001, 20);
// CX = E1.X - RX*cos(t1)
// CY = E1.Y - RY*sin(t1)
CX1 := E1.X - RX*cos(lt1);
CY1 := E1.Y - RY*sin(lt1);
CX2 := E1.X - RX*cos(lt1+Pi);
CY2 := E1.Y - RY*sin(lt1+Pi);
// Rotate back!
RotatedCenter := Rotate3DPointInXY(Make3DPoint(CX1,CY1,0), Make3DPoint(0,0,0),XRotation);
CX1 := RotatedCenter.X;
CY1 := RotatedCenter.Y;
RotatedCenter := Rotate3DPointInXY(Make3DPoint(CX2,CY2,0), Make3DPoint(0,0,0),XRotation);
CX2 := RotatedCenter.X;
CY2 := RotatedCenter.Y;
// errado!!!! Apagar quando achar o correto =(
{
CX := X - RX*Cos(0)*Cos(XRotation) + RY*Sin(0)*Sin(XRotation);
CY := Y - RY*Sin(0)*Cos(XRotation) - RX*Cos(0)*Sin(XRotation);
}
// ativar quando tiver codigo pra calcular CX1, etc
{
if CX1 < CX2 then
begin
LeftMostX := CX1;
@ -1127,7 +1183,7 @@ begin
begin
CX := RightMostX;
CY := RightMostY;
end;}
end;
end;
procedure T2DEllipticalArcSegment.CalculateEllipseBoundingBox(ADest: TFPCustomCanvas;

View File

@ -31,6 +31,8 @@ type
T10Strings = array[0..9] of shortstring;
TPointsArray = array of TPoint;
TNumericalEquation = function (AParameter: Double): Double of object; // return the error
// Color Conversion routines
function FPColorToRGBHexString(AColor: TFPColor): string;
function RGBToFPColor(AR, AG, AB: byte): TFPColor; inline;
@ -49,6 +51,9 @@ procedure AddBezierToPoints(P1, P2, P3, P4: T3DPoint; 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 Rotate3DPointInXY(P, RotCenter: T3DPoint; alpha:double): T3DPoint;
// Numerical Calculus
function SolveNumericallyAngle(ANumericalEquation: TNumericalEquation;
ADesiredMaxError: Double; ADesiredMaxIterations: Integer = 10): Double;
// LCL-related routines
{$ifdef USE_LCL_CANVAS}
function ConvertPathToRegion(APath: TPath; ADestX, ADestY: Integer; AMulX, AMulY: Double): HRGN;
@ -309,6 +314,68 @@ begin
end;
{$ifdef USE_LCL_CANVAS}
function SolveNumericallyAngle(ANumericalEquation: TNumericalEquation;
ADesiredMaxError: Double; ADesiredMaxIterations: Integer = 10): Double;
var
lError, lErr1, lErr2, lErr3, lErr4: Double;
lParam1, lParam2: Double;
lIterations: Integer;
lCount: Integer;
begin
lErr1 := ANumericalEquation(0);
lErr2 := ANumericalEquation(Pi/2);
lErr3 := ANumericalEquation(Pi);
lErr4 := ANumericalEquation(3*Pi/2);
// Choose the place to start
if (lErr1 < lErr2) and (lErr1 < lErr3) and (lErr1 < lErr4) then
begin
lParam1 := -Pi/2;
lParam2 := Pi/2;
end
else if (lErr2 < lErr3) and (lErr2 < lErr4) then
begin
lParam1 := 0;
lParam2 := Pi;
end
else if (lErr2 < lErr3) and (lErr2 < lErr4) then
begin
lParam1 := Pi/2;
lParam2 := 3*Pi/2;
end
else
begin
lParam1 := Pi;
lParam2 := 2*Pi;
end;
// Iterate as many times necessary to get the best answer!
lCount := 0;
lError := $FFFFFFFF;
while ((ADesiredMaxError < 0 ) or (lError > ADesiredMaxError))
and (lParam1 <> lParam2)
and ((ADesiredMaxIterations < 0) or (lCount < ADesiredMaxIterations)) do
begin
lErr1 := ANumericalEquation(lParam1);
lErr2 := ANumericalEquation(lParam2);
if lErr1 < lErr2 then
lParam2 := (lParam1+lParam2)/2
else
lParam1 := (lParam1+lParam2)/2;
lError := Min(lErr1, lErr2);
Inc(lCount);
end;
// Choose the best of the last two
if lErr1 < lErr2 then
Result := lParam1
else
Result := lParam2
end;
function ConvertPathToRegion(APath: TPath; ADestX, ADestY: Integer; AMulX, AMulY: Double): HRGN;
var
WindingMode: Integer;