mirror of
https://gitlab.com/freepascal.org/lazarus/lazarus.git
synced 2025-07-21 09:26:13 +02:00
508 lines
9.9 KiB
ObjectPascal
508 lines
9.9 KiB
ObjectPascal
//----------------------------------------------------------------------------
|
|
// Anti-Grain Geometry - Version 2.4 (Public License)
|
|
// Copyright (C) 2002-2005 Maxim Shemanarev (http://www.antigrain.com)
|
|
//
|
|
// Anti-Grain Geometry - Version 2.4 Release Milano 3 (AggPas 2.4 RM3)
|
|
// Pascal Port By: Milan Marusinec alias Milano
|
|
// milan@marusinec.sk
|
|
// http://www.aggpas.org
|
|
// Copyright (c) 2005-2006
|
|
//
|
|
// Permission to copy, use, modify, sell and distribute this software
|
|
// is granted provided this copyright notice appears in all copies.
|
|
// This software is provided "as is" without express or implied
|
|
// warranty, and with no claim as to its suitability for any purpose.
|
|
//
|
|
//----------------------------------------------------------------------------
|
|
// Contact: mcseem@antigrain.com
|
|
// mcseemagg@yahoo.com
|
|
// http://www.antigrain.com
|
|
//
|
|
//----------------------------------------------------------------------------
|
|
//
|
|
// Arc generator. Produces at most 4 consecutive cubic bezier curves, i.e.,
|
|
// 4, 7, 10, or 13 vertices.
|
|
//
|
|
// [Pascal Port History] -----------------------------------------------------
|
|
//
|
|
// 23.06.2006-Milano: ptrcomp adjustments
|
|
// 18.12.2005-Milano: Unit port establishment
|
|
//
|
|
{ agg_bezier_arc.pas }
|
|
unit
|
|
agg_bezier_arc ;
|
|
|
|
INTERFACE
|
|
|
|
{$I agg_mode.inc }
|
|
|
|
uses
|
|
Math ,
|
|
agg_basics ,
|
|
agg_vertex_source ,
|
|
agg_trans_affine ;
|
|
|
|
{ TYPES DEFINITION }
|
|
type
|
|
bezier_arc_ptr = ^bezier_arc;
|
|
bezier_arc = object(vertex_source )
|
|
m_vertex ,
|
|
m_num_vertices : unsigned;
|
|
|
|
m_vertices : double_26;
|
|
|
|
m_cmd : unsigned;
|
|
|
|
constructor Construct; overload;
|
|
constructor Construct(x ,y ,rx ,ry ,start_angle ,sweep_angle : double ); overload;
|
|
|
|
procedure init(x ,y ,rx ,ry ,start_angle ,sweep_angle : double );
|
|
|
|
procedure rewind(path_id : unsigned ); virtual;
|
|
function vertex(x ,y : double_ptr ) : unsigned; virtual;
|
|
|
|
// Supplemantary functions. num_vertices() actually returns doubled
|
|
// number of vertices. That is, for 1 vertex it returns 2.
|
|
function num_vertices : unsigned;
|
|
function vertices : double_26_ptr;
|
|
|
|
end;
|
|
|
|
//==========================================================bezier_arc_svg
|
|
// Compute an SVG-style bezier arc.
|
|
//
|
|
// Computes an elliptical arc from (x1, y1) to (x2, y2). The size and
|
|
// orientation of the ellipse are defined by two radii (rx, ry)
|
|
// and an x-axis-rotation, which indicates how the ellipse as a whole
|
|
// is rotated relative to the current coordinate system. The center
|
|
// (cx, cy) of the ellipse is calculated automatically to satisfy the
|
|
// constraints imposed by the other parameters.
|
|
// large-arc-flag and sweep-flag contribute to the automatic calculations
|
|
// and help determine how the arc is drawn.
|
|
bezier_arc_svg_ptr = ^bezier_arc_svg;
|
|
bezier_arc_svg = object(bezier_arc )
|
|
m_radii_ok : boolean;
|
|
|
|
constructor Construct; overload;
|
|
constructor Construct(x1 ,y1 ,rx ,ry ,angle : double; large_arc_flag ,sweep_flag : boolean; x2 ,y2 : double ); overload;
|
|
|
|
procedure init(x0 ,y0 ,rx ,ry ,angle : double; large_arc_flag ,sweep_flag : boolean; x2 ,y2 : double );
|
|
|
|
function radii_ok : boolean;
|
|
|
|
end;
|
|
|
|
{ GLOBAL PROCEDURES }
|
|
procedure arc_to_bezier(cx ,cy ,rx ,ry ,start_angle ,sweep_angle : double; curve : double_8_ptr );
|
|
|
|
|
|
IMPLEMENTATION
|
|
{ LOCAL VARIABLES & CONSTANTS }
|
|
const
|
|
bezier_arc_angle_epsilon = 0.01;
|
|
|
|
{ UNIT IMPLEMENTATION }
|
|
{ ARC_TO_BEZIER }
|
|
procedure arc_to_bezier;
|
|
var
|
|
i : unsigned;
|
|
|
|
sn ,cs ,
|
|
x0 ,y0 ,
|
|
tx ,ty : double;
|
|
px ,py : array[0..3 ] of double;
|
|
|
|
begin
|
|
x0:=Cos(sweep_angle / 2.0 );
|
|
y0:=Sin(sweep_angle / 2.0 );
|
|
tx:=(1.0 - x0 ) * 4.0 / 3.0;
|
|
ty:=y0 - tx * x0 / y0;
|
|
|
|
px[0 ]:=x0;
|
|
py[0 ]:=-y0;
|
|
px[1 ]:=x0 + tx;
|
|
py[1 ]:=-ty;
|
|
px[2 ]:=x0 + tx;
|
|
py[2 ]:=ty;
|
|
px[3 ]:=x0;
|
|
py[3 ]:=y0;
|
|
|
|
sn:=Sin(start_angle + sweep_angle / 2.0 );
|
|
cs:=Cos(start_angle + sweep_angle / 2.0 );
|
|
|
|
for i:=0 to 3 do
|
|
begin
|
|
curve[i * 2 ] :=cx + rx * (px[i ] * cs - py[i ] * sn );
|
|
curve[i * 2 + 1 ]:=cy + ry * (px[i ] * sn + py[i ] * cs );
|
|
|
|
end;
|
|
|
|
end;
|
|
|
|
{ CONSTRUCT }
|
|
constructor bezier_arc.Construct;
|
|
begin
|
|
m_vertex:=26;
|
|
|
|
m_num_vertices:=0;
|
|
|
|
m_cmd:=path_cmd_line_to;
|
|
|
|
end;
|
|
|
|
{ CONSTRUCT }
|
|
constructor bezier_arc.Construct(x ,y ,rx ,ry ,start_angle ,sweep_angle : double );
|
|
begin
|
|
init(x ,y ,rx ,ry ,start_angle ,sweep_angle );
|
|
|
|
end;
|
|
|
|
{ INIT }
|
|
procedure bezier_arc.init;
|
|
var
|
|
i : int;
|
|
f : double;
|
|
|
|
total_sweep ,
|
|
local_sweep ,
|
|
prev_sweep : double;
|
|
|
|
done : boolean;
|
|
|
|
begin
|
|
i:=trunc(start_angle / (2.0 * pi ) );
|
|
f:=start_angle - (i * 2.0 * pi );
|
|
|
|
start_angle:=f;
|
|
|
|
if sweep_angle >= 2.0 * pi then
|
|
sweep_angle:=2.0 * pi;
|
|
|
|
if sweep_angle <= -2.0 * pi then
|
|
sweep_angle:=-2.0 * pi;
|
|
|
|
if Abs(sweep_angle ) < 1e-10 then
|
|
begin
|
|
m_num_vertices:=4;
|
|
|
|
m_cmd:=path_cmd_line_to;
|
|
|
|
m_vertices[0 ]:=x + rx * Cos(start_angle );
|
|
m_vertices[1 ]:=y + ry * Sin(start_angle );
|
|
m_vertices[2 ]:=x + rx * Cos(start_angle + sweep_angle );
|
|
m_vertices[3 ]:=y + ry * Sin(start_angle + sweep_angle );
|
|
|
|
exit;
|
|
|
|
end;
|
|
|
|
total_sweep:=0.0;
|
|
local_sweep:=0.0;
|
|
|
|
m_num_vertices:=2;
|
|
|
|
m_cmd:=path_cmd_curve4;
|
|
done :=false;
|
|
|
|
repeat
|
|
if sweep_angle < 0.0 then
|
|
begin
|
|
prev_sweep :=total_sweep;
|
|
local_sweep:=-pi * 0.5;
|
|
total_sweep:=total_sweep - (pi * 0.5 );
|
|
|
|
if total_sweep <= sweep_angle + bezier_arc_angle_epsilon then
|
|
begin
|
|
local_sweep:=sweep_angle - prev_sweep;
|
|
|
|
done:=true;
|
|
|
|
end;
|
|
|
|
end
|
|
else
|
|
begin
|
|
prev_sweep :=total_sweep;
|
|
local_sweep:=pi * 0.5;
|
|
total_sweep:=total_sweep + (pi * 0.5 );
|
|
|
|
if total_sweep >= sweep_angle - bezier_arc_angle_epsilon then
|
|
begin
|
|
local_sweep:=sweep_angle - prev_sweep;
|
|
|
|
done:=true;
|
|
|
|
end;
|
|
|
|
end;
|
|
|
|
arc_to_bezier(
|
|
x ,y ,rx ,ry ,
|
|
start_angle ,local_sweep ,
|
|
@m_vertices[m_num_vertices - 2 ] );
|
|
|
|
m_num_vertices:=m_num_vertices + 6;
|
|
start_angle :=start_angle + local_sweep;
|
|
|
|
until done or (m_num_vertices >= 26 );
|
|
|
|
end;
|
|
|
|
{ REWIND }
|
|
procedure bezier_arc.rewind;
|
|
begin
|
|
m_vertex:=0;
|
|
|
|
end;
|
|
|
|
{ VERTEX }
|
|
function bezier_arc.vertex;
|
|
begin
|
|
if m_vertex >= m_num_vertices then
|
|
begin
|
|
result:=path_cmd_stop;
|
|
|
|
exit;
|
|
|
|
end;
|
|
|
|
x^:=m_vertices[m_vertex ];
|
|
y^:=m_vertices[m_vertex + 1 ];
|
|
|
|
inc(m_vertex ,2 );
|
|
|
|
if m_vertex = 2 then
|
|
result:=path_cmd_move_to
|
|
else
|
|
result:=m_cmd;
|
|
|
|
end;
|
|
|
|
{ NUM_VERTICES }
|
|
function bezier_arc.num_vertices;
|
|
begin
|
|
result:=m_num_vertices;
|
|
|
|
end;
|
|
|
|
{ VERTICES }
|
|
function bezier_arc.vertices;
|
|
begin
|
|
result:=@m_vertices;
|
|
|
|
end;
|
|
|
|
{ CONSTRUCT }
|
|
constructor bezier_arc_svg.Construct;
|
|
begin
|
|
inherited Construct;
|
|
|
|
m_radii_ok:=false;
|
|
|
|
end;
|
|
|
|
{ CONSTRUCT }
|
|
constructor bezier_arc_svg.Construct(x1 ,y1 ,rx ,ry ,angle : double; large_arc_flag ,sweep_flag : boolean; x2 ,y2 : double );
|
|
begin
|
|
inherited Construct;
|
|
|
|
m_radii_ok:=false;
|
|
|
|
init(x1 ,y1 ,rx ,ry ,angle ,large_arc_flag ,sweep_flag ,x2 ,y2 );
|
|
|
|
end;
|
|
|
|
{ INIT }
|
|
procedure bezier_arc_svg.init;
|
|
var
|
|
i : unsigned;
|
|
|
|
v ,
|
|
p , n ,
|
|
|
|
sq ,
|
|
x1 ,y1 ,
|
|
cx ,cy ,
|
|
ux ,uy ,
|
|
vx ,vy ,
|
|
|
|
dx2 ,dy2 ,
|
|
prx ,pry ,
|
|
px1 ,py1 ,
|
|
cx1 ,cy1 ,
|
|
sx2 ,sy2 ,
|
|
|
|
sign ,coef ,
|
|
|
|
radii_check ,
|
|
start_angle ,
|
|
sweep_angle ,
|
|
|
|
cos_a ,sin_a : double;
|
|
|
|
mtx : trans_affine_rotation;
|
|
trn : trans_affine_translation;
|
|
|
|
begin
|
|
m_radii_ok:=true;
|
|
|
|
if rx < 0.0 then
|
|
rx:=-rx;
|
|
|
|
if ry < 0.0 then
|
|
ry:=-rx;
|
|
|
|
// Calculate the middle point between
|
|
// the current and the final points
|
|
dx2:=(x0 - x2 ) / 2.0;
|
|
dy2:=(y0 - y2 ) / 2.0;
|
|
|
|
// Convert angle from degrees to radians
|
|
cos_a:=Cos(angle );
|
|
sin_a:=Sin(angle );
|
|
|
|
// Calculate (x1, y1)
|
|
x1:= cos_a * dx2 + sin_a * dy2;
|
|
y1:=-sin_a * dx2 + cos_a * dy2;
|
|
|
|
// Ensure radii are large enough
|
|
prx:=rx * rx;
|
|
pry:=ry * ry;
|
|
px1:=x1 * x1;
|
|
py1:=y1 * y1;
|
|
|
|
// Check that radii are large enough
|
|
radii_check:=px1 / prx + py1 / pry;
|
|
|
|
if radii_check > 1.0 then
|
|
begin
|
|
rx :=sqrt(radii_check ) * rx;
|
|
ry :=sqrt(radii_check ) * ry;
|
|
prx:=rx * rx;
|
|
pry:=ry * ry;
|
|
|
|
if radii_check > 10.0 then
|
|
m_radii_ok:=false;
|
|
|
|
end;
|
|
|
|
// Calculate (cx1, cy1)
|
|
if large_arc_flag = sweep_flag then
|
|
sign:=-1.0
|
|
else
|
|
sign:=1.0;
|
|
|
|
sq:=(prx * pry - prx * py1 - pry * px1 ) / (prx * py1 + pry * px1 );
|
|
|
|
if sq < 0 then
|
|
coef:=sign * sqrt(0 )
|
|
else
|
|
coef:=sign * sqrt(sq );
|
|
|
|
cx1:=coef * ((rx * y1 ) / ry );
|
|
cy1:=coef * -((ry * x1 ) / rx );
|
|
|
|
// Calculate (cx, cy) from (cx1, cy1)
|
|
sx2:=(x0 + x2 ) / 2.0;
|
|
sy2:=(y0 + y2 ) / 2.0;
|
|
cx :=sx2 + (cos_a * cx1 - sin_a * cy1 );
|
|
cy :=sy2 + (sin_a * cx1 + cos_a * cy1 );
|
|
|
|
// Calculate the start_angle (angle1) and the sweep_angle (dangle)
|
|
ux:= (x1 - cx1 ) / rx;
|
|
uy:= (y1 - cy1 ) / ry;
|
|
vx:=(-x1 - cx1 ) / rx;
|
|
vy:=(-y1 - cy1 ) / ry;
|
|
|
|
// Calculate the angle start
|
|
n:=sqrt(ux * ux + uy * uy );
|
|
p:=ux; // (1 * ux ) + (0 * uy )
|
|
|
|
if uy < 0 then
|
|
sign:=-1.0
|
|
else
|
|
sign:=1.0;
|
|
|
|
v:=p / n;
|
|
|
|
if v < -1.0 then
|
|
v:=-1.0;
|
|
|
|
if v > 1.0 then
|
|
v:=1.0;
|
|
|
|
start_angle:=sign * ArcCos(v );
|
|
|
|
// Calculate the sweep angle
|
|
n:=sqrt((ux * ux + uy * uy ) * (vx * vx + vy * vy ) );
|
|
p:=ux * vx + uy * vy;
|
|
|
|
if ux * vy - uy * vx < 0 then
|
|
sign:=-1.0
|
|
else
|
|
sign:=1.0;
|
|
|
|
v:=p / n;
|
|
|
|
if v < -1.0 then
|
|
v:=-1.0;
|
|
|
|
if v > 1.0 then
|
|
v:=1.0;
|
|
|
|
sweep_angle:=sign * ArcCos(v );
|
|
|
|
if (not sweep_flag ) and
|
|
(sweep_angle > 0 ) then
|
|
sweep_angle:=sweep_angle - pi * 2.0
|
|
else
|
|
if sweep_flag and
|
|
(sweep_angle < 0 ) then
|
|
sweep_angle:=sweep_angle + pi * 2.0;
|
|
|
|
// We can now build and transform the resulting arc
|
|
inherited init(0.0 ,0.0 ,rx ,ry ,start_angle ,sweep_angle );
|
|
|
|
mtx.Construct(angle );
|
|
trn.Construct(cx ,cy );
|
|
|
|
mtx.multiply(@trn );
|
|
|
|
i:=2;
|
|
|
|
while i < num_vertices - 2 do
|
|
begin
|
|
//mtx.transform(@m_arc.vertices[i ] ,@m_arc.vertices[i + 1 ] );
|
|
mtx.transform(
|
|
@mtx ,
|
|
double_ptr(ptrcomp(vertices ) + i * sizeof(double ) ) ,
|
|
double_ptr(ptrcomp(vertices ) + (i + 1 ) * sizeof(double ) ) );
|
|
|
|
inc(i ,2 );
|
|
|
|
end;
|
|
|
|
// We must make sure that the starting and ending points
|
|
// exactly coincide with the initial (x0,y0) and (x2,y2)
|
|
vertices[0 ]:=x0;
|
|
vertices[1 ]:=y0;
|
|
|
|
if num_vertices > 2 then
|
|
begin
|
|
vertices[num_vertices - 2 ]:=x2;
|
|
vertices[num_vertices - 1 ]:=y2;
|
|
|
|
end;
|
|
|
|
end;
|
|
|
|
{ RADII_OK }
|
|
function bezier_arc_svg.radii_ok;
|
|
begin
|
|
result:=m_radii_ok;
|
|
|
|
end;
|
|
|
|
END.
|
|
|