mirror of
https://gitlab.com/freepascal.org/lazarus/lazarus.git
synced 2025-06-26 17:48:33 +02:00
1263 lines
25 KiB
ObjectPascal
1263 lines
25 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
|
|
//
|
|
//----------------------------------------------------------------------------
|
|
//
|
|
// Class path_storage
|
|
//
|
|
// [Pascal Port History] -----------------------------------------------------
|
|
//
|
|
// 09.10.2007-Milano: Path Affine Transformations
|
|
// 23.06.2006-Milano: ptrcomp adjustments
|
|
// 19.12.2005-Milano: Unit port establishment
|
|
//
|
|
{ agg_path_storage.pas }
|
|
unit
|
|
agg_path_storage ;
|
|
|
|
INTERFACE
|
|
|
|
{$I agg_mode.inc }
|
|
|
|
uses
|
|
agg_basics ,
|
|
agg_math ,
|
|
agg_bezier_arc ,
|
|
agg_vertex_source ,
|
|
agg_trans_affine ;
|
|
|
|
{ TYPES DEFINITION }
|
|
// Allocation parameters
|
|
const
|
|
block_shift = 8;
|
|
block_size = 1 shl block_shift;
|
|
block_mask = block_size - 1;
|
|
block_pool = 256;
|
|
|
|
type
|
|
path_storage_ptr = ^path_storage;
|
|
|
|
ps_vertex_source_ptr = ^ps_vertex_source;
|
|
ps_vertex_source = object(vertex_source )
|
|
m_path : path_storage_ptr;
|
|
|
|
m_vertex_idx : unsigned;
|
|
|
|
constructor Construct; overload;
|
|
constructor Construct(p : path_storage_ptr ); overload;
|
|
|
|
procedure rewind(path_id : unsigned ); virtual;
|
|
function vertex(x ,y : double_ptr ) : unsigned; virtual;
|
|
|
|
end;
|
|
|
|
//------------------------------------------------------------path_storage
|
|
// A container to store vertices with their flags.
|
|
// A path consists of a number of contours separated with "move_to"
|
|
// commands. The path storage can keep and maintain more than one
|
|
// path.
|
|
// To navigate to the beginning of a particular path, use rewind(path_id);
|
|
// Where path_id is what start_new_path() returns. So, when you call
|
|
// start_new_path() you need to store its return value somewhere else
|
|
// to navigate to the path afterwards.
|
|
path_storage = object(vertex_source )
|
|
m_total_vertices ,
|
|
m_total_blocks ,
|
|
m_max_blocks : unsigned;
|
|
|
|
m_coord_blocks : double_ptr_ptr;
|
|
m_cmd_blocks : int8u_ptr_ptr;
|
|
|
|
m_iterator : unsigned;
|
|
|
|
constructor Construct; overload;
|
|
constructor Construct(ps : path_storage_ptr ); overload;
|
|
destructor Destruct; virtual;
|
|
|
|
procedure remove_all; virtual;
|
|
|
|
function last_vertex(x ,y : double_ptr ) : unsigned;
|
|
function prev_vertex(x ,y : double_ptr ) : unsigned;
|
|
|
|
function last_x : double;
|
|
function last_y : double;
|
|
|
|
procedure rel_to_abs(x ,y : double_ptr );
|
|
|
|
procedure move_to (x ,y : double );
|
|
procedure move_rel(dx ,dy : double );
|
|
|
|
procedure line_to (x ,y : double );
|
|
procedure line_rel(dx ,dy : double );
|
|
|
|
procedure hline_to (x : double );
|
|
procedure hline_rel(dx : double );
|
|
|
|
procedure vline_to (y : double );
|
|
procedure vline_rel(dy : double );
|
|
|
|
procedure arc_to (rx ,ry ,angle : double; large_arc_flag ,sweep_flag : boolean; x ,y : double );
|
|
procedure arc_rel(rx ,ry ,angle : double; large_arc_flag ,sweep_flag : boolean; dx ,dy : double );
|
|
|
|
procedure curve3 (x_ctrl ,y_ctrl ,x_to ,y_to : double ); overload;
|
|
procedure curve3_rel(dx_ctrl ,dy_ctrl ,dx_to ,dy_to : double ); overload;
|
|
|
|
procedure curve3 (x_to ,y_to : double ); overload;
|
|
procedure curve3_rel(dx_to ,dy_to : double ); overload;
|
|
|
|
procedure curve4 (x_ctrl1 ,y_ctrl1 ,x_ctrl2 ,y_ctrl2 ,x_to ,y_to : double ); overload;
|
|
procedure curve4_rel(dx_ctrl1 ,dy_ctrl1 ,dx_ctrl2 ,dy_ctrl2 ,dx_to ,dy_to : double ); overload;
|
|
|
|
procedure curve4 (x_ctrl2 ,y_ctrl2 ,x_to ,y_to : double ); overload;
|
|
procedure curve4_rel(dx_ctrl2 ,dy_ctrl2 ,dx_to ,dy_to : double ); overload;
|
|
|
|
procedure end_poly (flags : unsigned = path_flags_close );
|
|
procedure close_polygon(flags : unsigned = path_flags_none );
|
|
|
|
procedure add_poly(vertices : double_2_ptr; num : unsigned; solid_path : boolean = false; end_flags : unsigned = path_flags_none );
|
|
procedure add_path(vs : vertex_source_ptr; path_id : unsigned = 0; solid_path : boolean = true );
|
|
|
|
function start_new_path : unsigned;
|
|
|
|
procedure copy_from(ps : path_storage_ptr );
|
|
|
|
function total_vertices : unsigned;
|
|
|
|
function vertex_(idx : unsigned; x ,y : double_ptr ) : unsigned;
|
|
function command(idx : unsigned ) : unsigned;
|
|
procedure rewind (path_id : unsigned ); virtual;
|
|
function vertex (x ,y : double_ptr ) : unsigned; virtual;
|
|
|
|
// Arrange the orientation of a polygon, all polygons in a path,
|
|
// or in all paths. After calling arrange_orientations() or
|
|
// arrange_orientations_all_paths(), all the polygons will have
|
|
// the same orientation, i.e. path_flags_cw or path_flags_ccw
|
|
function arrange_polygon_orientation (start ,orientation : unsigned ) : unsigned;
|
|
function arrange_orientations (start ,orientation : unsigned ) : unsigned;
|
|
procedure arrange_orientations_all_paths(orientation : unsigned );
|
|
|
|
// Flip all the vertices horizontally or vertically
|
|
procedure flip_x(x1 ,x2 : double );
|
|
procedure flip_y(y1 ,y2 : double );
|
|
|
|
// This function adds a vertex with its flags directly. Since there's no
|
|
// checking for errors, keeping proper path integrity is the responsibility
|
|
// of the caller. It can be said the function is "not very public".
|
|
procedure add_vertex(x ,y : double; cmd : unsigned ); virtual;
|
|
|
|
// Allows you to modify vertex coordinates. The caller must know
|
|
// the index of the vertex.
|
|
procedure modify_vertex(idx : unsigned; x ,y : double );
|
|
|
|
// Allows you to modify vertex command. The caller must know
|
|
// the index of the vertex.
|
|
procedure modify_command(idx ,cmd : unsigned );
|
|
|
|
// Path Affine Transformations
|
|
procedure transform (trans : trans_affine_ptr; path_id : unsigned = 0 );
|
|
procedure transform_all_paths(trans : trans_affine_ptr );
|
|
|
|
// Private
|
|
procedure allocate_block(nb : unsigned );
|
|
function storage_ptrs (xy_ptr : double_ptr_ptr ) : int8u_ptr;
|
|
|
|
function perceive_polygon_orientation(start ,end_ : unsigned ) : unsigned;
|
|
|
|
procedure invert_polygon(start ,end_ : unsigned ); overload;
|
|
procedure invert_polygon(start : unsigned ); overload;
|
|
|
|
// from 2.4
|
|
procedure concat_path(vs : vertex_source_ptr; path_id : unsigned = 0 );
|
|
|
|
|
|
end;
|
|
|
|
{ GLOBAL PROCEDURES }
|
|
|
|
|
|
IMPLEMENTATION
|
|
{ LOCAL VARIABLES & CONSTANTS }
|
|
{ UNIT IMPLEMENTATION }
|
|
{ CONSTRUCT }
|
|
constructor ps_vertex_source.Construct;
|
|
begin
|
|
m_path:=NIL;
|
|
|
|
m_vertex_idx:=0;
|
|
|
|
end;
|
|
|
|
{ CONSTRUCT }
|
|
constructor ps_vertex_source.Construct(p : path_storage_ptr );
|
|
begin
|
|
m_path:=p;
|
|
|
|
m_vertex_idx:=0;
|
|
|
|
end;
|
|
|
|
{ REWIND }
|
|
procedure ps_vertex_source.rewind;
|
|
begin
|
|
m_vertex_idx:=path_id;
|
|
|
|
end;
|
|
|
|
{ VERTEX }
|
|
function ps_vertex_source.vertex;
|
|
begin
|
|
if m_vertex_idx < m_path.total_vertices then
|
|
begin
|
|
result:=m_path.vertex_(m_vertex_idx ,x ,y );
|
|
|
|
inc(m_vertex_idx );
|
|
|
|
end
|
|
else
|
|
result:=path_cmd_stop;
|
|
|
|
end;
|
|
|
|
{ CONSTRUCT }
|
|
constructor path_storage.Construct;
|
|
begin
|
|
m_total_vertices:=0;
|
|
m_total_blocks :=0;
|
|
m_max_blocks :=0;
|
|
|
|
m_coord_blocks:=NIL;
|
|
m_cmd_blocks :=NIL;
|
|
|
|
m_iterator:=0;
|
|
|
|
end;
|
|
|
|
{ CONSTRUCT }
|
|
constructor path_storage.Construct(ps : path_storage_ptr );
|
|
begin
|
|
m_total_vertices:=0;
|
|
m_total_blocks :=0;
|
|
m_max_blocks :=0;
|
|
|
|
m_coord_blocks:=NIL;
|
|
m_cmd_blocks :=NIL;
|
|
|
|
m_iterator:=0;
|
|
|
|
copy_from(ps );
|
|
|
|
end;
|
|
|
|
{ DESTRUCT }
|
|
destructor path_storage.Destruct;
|
|
var
|
|
coord_blk : double_ptr_ptr;
|
|
|
|
begin
|
|
if m_total_blocks <> 0 then
|
|
begin
|
|
coord_blk:=double_ptr_ptr(ptrcomp(m_coord_blocks ) + (m_total_blocks - 1 ) * sizeof(double_ptr ) );
|
|
|
|
while m_total_blocks > 0 do
|
|
begin
|
|
agg_freemem(
|
|
p32(coord_blk^ ).ptr ,
|
|
(block_size * 2 + block_size div (sizeof(double ) div sizeof(int8u ) ) ) * sizeof(double ) );
|
|
|
|
dec(ptrcomp(coord_blk ) ,sizeof(double_ptr ) );
|
|
dec(m_total_blocks );
|
|
|
|
end;
|
|
|
|
agg_freemem(pointer(m_coord_blocks ) ,m_max_blocks * 2 * sizeof(double_ptr ) );
|
|
|
|
end;
|
|
|
|
end;
|
|
|
|
{ REMOVE_ALL }
|
|
procedure path_storage.remove_all;
|
|
begin
|
|
m_total_vertices:=0;
|
|
m_iterator :=0;
|
|
|
|
end;
|
|
|
|
{ LAST_VERTEX }
|
|
function path_storage.last_vertex;
|
|
begin
|
|
if m_total_vertices <> 0 then
|
|
result:=vertex_(m_total_vertices - 1 ,x ,y )
|
|
else
|
|
result:=path_cmd_stop;
|
|
|
|
end;
|
|
|
|
{ PREV_VERTEX }
|
|
function path_storage.prev_vertex;
|
|
begin
|
|
if m_total_vertices > 1 then
|
|
result:=vertex_(m_total_vertices - 2 ,x ,y )
|
|
else
|
|
result:=path_cmd_stop;
|
|
|
|
end;
|
|
|
|
{ LAST_X }
|
|
function path_storage.last_x;
|
|
var
|
|
idx : unsigned;
|
|
|
|
begin
|
|
if m_total_vertices <> 0 then
|
|
begin
|
|
idx:=m_total_vertices - 1;
|
|
|
|
result:=
|
|
double_ptr(
|
|
ptrcomp(
|
|
p32_ptr(ptrcomp(m_coord_blocks ) + (idx shr block_shift ) * sizeof(double_ptr ) ).ptr ) +
|
|
((idx and block_mask) shl 1 ) * sizeof(double ) )^;
|
|
|
|
end
|
|
else
|
|
result:=0.0;
|
|
|
|
end;
|
|
|
|
{ LAST_Y }
|
|
function path_storage.last_y;
|
|
var
|
|
idx : unsigned;
|
|
|
|
begin
|
|
if m_total_vertices <> 0 then
|
|
begin
|
|
idx:=m_total_vertices - 1;
|
|
|
|
result:=
|
|
double_ptr(
|
|
ptrcomp(
|
|
p32_ptr(ptrcomp(m_coord_blocks ) + (idx shr block_shift ) * sizeof(double_ptr ) ).ptr ) +
|
|
(((idx and block_mask) shl 1 ) + 1 ) * sizeof(double ) )^;
|
|
|
|
end
|
|
else
|
|
result:=0.0;
|
|
|
|
end;
|
|
|
|
{ REL_TO_ABS }
|
|
procedure path_storage.rel_to_abs;
|
|
var
|
|
x2 ,y2 : double;
|
|
|
|
begin
|
|
if m_total_vertices <> 0 then
|
|
if is_vertex(vertex_(m_total_vertices - 1 ,@x2 ,@y2 ) ) then
|
|
begin
|
|
x^:=x^ + x2;
|
|
y^:=y^ + y2;
|
|
|
|
end;
|
|
|
|
end;
|
|
|
|
{ MOVE_TO }
|
|
procedure path_storage.move_to;
|
|
begin
|
|
add_vertex(x ,y ,path_cmd_move_to );
|
|
|
|
end;
|
|
|
|
{ MOVE_REL }
|
|
procedure path_storage.move_rel;
|
|
begin
|
|
rel_to_abs(@dx ,@dy );
|
|
add_vertex(dx ,dy ,path_cmd_move_to );
|
|
|
|
end;
|
|
|
|
{ LINE_TO }
|
|
procedure path_storage.line_to;
|
|
begin
|
|
add_vertex(x ,y ,path_cmd_line_to );
|
|
|
|
end;
|
|
|
|
{ LINE_REL }
|
|
procedure path_storage.line_rel;
|
|
begin
|
|
rel_to_abs(@dx ,@dy);
|
|
add_vertex(dx ,dy ,path_cmd_line_to );
|
|
|
|
end;
|
|
|
|
{ HLINE_TO }
|
|
procedure path_storage.hline_to;
|
|
begin
|
|
add_vertex(x ,last_y ,path_cmd_line_to );
|
|
|
|
end;
|
|
|
|
{ HLINE_REL }
|
|
procedure path_storage.hline_rel;
|
|
var
|
|
dy : double;
|
|
|
|
begin
|
|
dy:=0;
|
|
|
|
rel_to_abs(@dx ,@dy);
|
|
add_vertex(dx ,dy ,path_cmd_line_to );
|
|
|
|
end;
|
|
|
|
{ VLINE_TO }
|
|
procedure path_storage.vline_to;
|
|
begin
|
|
add_vertex(last_x ,y ,path_cmd_line_to );
|
|
|
|
end;
|
|
|
|
{ VLINE_REL }
|
|
procedure path_storage.vline_rel;
|
|
var
|
|
dx : double;
|
|
|
|
begin
|
|
dx:=0;
|
|
|
|
rel_to_abs(@dx, @dy);
|
|
add_vertex(dx ,dy ,path_cmd_line_to );
|
|
|
|
end;
|
|
|
|
{ ARC_TO }
|
|
procedure path_storage.arc_to;
|
|
var
|
|
a : bezier_arc_svg_ptr;
|
|
|
|
x0 ,y0 ,epsilon : double;
|
|
|
|
begin
|
|
a:=NIL;
|
|
|
|
if (m_total_vertices <> 0 ) and
|
|
is_vertex(command(m_total_vertices - 1 ) ) then
|
|
begin
|
|
epsilon:=1e-30;
|
|
|
|
x0:=0.0;
|
|
y0:=0.0;
|
|
|
|
last_vertex(@x0 ,@y0 );
|
|
|
|
rx:=Abs(rx );
|
|
ry:=Abs(ry );
|
|
|
|
// Ensure radii are valid
|
|
if (rx < epsilon ) or
|
|
(ry < epsilon ) then
|
|
begin
|
|
line_to(x ,y );
|
|
exit;
|
|
|
|
end;
|
|
|
|
// If the endpoints (x, y) and (x0, y0) are identical, then this
|
|
// is equivalent to omitting the elliptical arc segment entirely.
|
|
if calc_distance(x0 ,y0 ,x ,y ) < epsilon then
|
|
exit;
|
|
|
|
new(a ,Construct(x0 ,y0 ,rx ,ry ,angle ,large_arc_flag ,sweep_flag ,x ,y ) );
|
|
|
|
if a.radii_ok then
|
|
add_path(a ,0 ,true )
|
|
else
|
|
line_to(x ,y );
|
|
|
|
end
|
|
else
|
|
move_to(x ,y );
|
|
|
|
if a <> NIL then
|
|
dispose(a ,Destruct );
|
|
|
|
end;
|
|
|
|
{ ARC_REL }
|
|
procedure path_storage.arc_rel;
|
|
begin
|
|
rel_to_abs(@dx ,@dy );
|
|
arc_to (rx ,ry ,angle ,large_arc_flag ,sweep_flag ,dx ,dy );
|
|
|
|
end;
|
|
|
|
{ CURVE3 }
|
|
procedure path_storage.curve3(x_ctrl ,y_ctrl ,x_to ,y_to : double );
|
|
begin
|
|
add_vertex(x_ctrl ,y_ctrl ,path_cmd_curve3 );
|
|
add_vertex(x_to ,y_to ,path_cmd_curve3 );
|
|
|
|
end;
|
|
|
|
{ CURVE3_REL }
|
|
procedure path_storage.curve3_rel(dx_ctrl ,dy_ctrl ,dx_to ,dy_to : double );
|
|
begin
|
|
rel_to_abs(@dx_ctrl ,@dy_ctrl );
|
|
rel_to_abs(@dx_to ,@dy_to );
|
|
add_vertex(dx_ctrl ,dy_ctrl ,path_cmd_curve3 );
|
|
add_vertex(dx_to ,dy_to ,path_cmd_curve3 );
|
|
|
|
end;
|
|
|
|
{ CURVE3 }
|
|
procedure path_storage.curve3(x_to ,y_to : double );
|
|
var
|
|
cmd : unsigned;
|
|
|
|
x0 ,y0 ,x_ctrl ,y_ctrl : double;
|
|
|
|
begin
|
|
if is_vertex(last_vertex(@x0 ,@y0 ) ) then
|
|
begin
|
|
cmd:=prev_vertex(@x_ctrl ,@y_ctrl );
|
|
|
|
if is_curve(cmd ) then
|
|
begin
|
|
x_ctrl:=x0 + x0 - x_ctrl;
|
|
y_ctrl:=y0 + y0 - y_ctrl;
|
|
|
|
end
|
|
else
|
|
begin
|
|
x_ctrl:=x0;
|
|
y_ctrl:=y0;
|
|
|
|
end;
|
|
|
|
curve3(x_ctrl ,y_ctrl ,x_to ,y_to );
|
|
|
|
end;
|
|
|
|
end;
|
|
|
|
{ CURVE3_REL }
|
|
procedure path_storage.curve3_rel(dx_to ,dy_to : double );
|
|
begin
|
|
rel_to_abs(@dx_to ,@dy_to );
|
|
curve3 (dx_to ,dy_to );
|
|
|
|
end;
|
|
|
|
{ CURVE4 }
|
|
procedure path_storage.curve4(x_ctrl1 ,y_ctrl1 ,x_ctrl2 ,y_ctrl2 ,x_to ,y_to : double );
|
|
begin
|
|
add_vertex(x_ctrl1 ,y_ctrl1 ,path_cmd_curve4 );
|
|
add_vertex(x_ctrl2 ,y_ctrl2 ,path_cmd_curve4 );
|
|
add_vertex(x_to ,y_to ,path_cmd_curve4 );
|
|
|
|
end;
|
|
|
|
{ CURVE4_REL }
|
|
procedure path_storage.curve4_rel(dx_ctrl1 ,dy_ctrl1 ,dx_ctrl2 ,dy_ctrl2 ,dx_to ,dy_to : double );
|
|
begin
|
|
rel_to_abs(@dx_ctrl1 ,@dy_ctrl1 );
|
|
rel_to_abs(@dx_ctrl2 ,@dy_ctrl2 );
|
|
rel_to_abs(@dx_to ,@dy_to );
|
|
add_vertex(dx_ctrl1 ,dy_ctrl1 ,path_cmd_curve4 );
|
|
add_vertex(dx_ctrl2 ,dy_ctrl2 ,path_cmd_curve4 );
|
|
add_vertex(dx_to ,dy_to ,path_cmd_curve4 );
|
|
|
|
end;
|
|
|
|
{ CURVE4 }
|
|
procedure path_storage.curve4(x_ctrl2 ,y_ctrl2 ,x_to ,y_to : double );
|
|
var
|
|
cmd : unsigned;
|
|
|
|
x0 ,y0 ,x_ctrl1 ,y_ctrl1 : double;
|
|
|
|
begin
|
|
if is_vertex(last_vertex(@x0 ,@y0 ) ) then
|
|
begin
|
|
cmd:=prev_vertex(@x_ctrl1 ,@y_ctrl1 );
|
|
|
|
if is_curve(cmd ) then
|
|
begin
|
|
x_ctrl1:=x0 + x0 - x_ctrl1;
|
|
y_ctrl1:=y0 + y0 - y_ctrl1;
|
|
|
|
end
|
|
else
|
|
begin
|
|
x_ctrl1:=x0;
|
|
y_ctrl1:=y0;
|
|
|
|
end;
|
|
|
|
curve4(x_ctrl1 ,y_ctrl1 ,x_ctrl2 ,y_ctrl2 ,x_to ,y_to );
|
|
|
|
end;
|
|
|
|
end;
|
|
|
|
{ CURVE4_REL }
|
|
procedure path_storage.curve4_rel(dx_ctrl2 ,dy_ctrl2 ,dx_to ,dy_to : double );
|
|
begin
|
|
rel_to_abs(@dx_ctrl2 ,@dy_ctrl2 );
|
|
rel_to_abs(@dx_to ,@dy_to );
|
|
|
|
curve4(dx_ctrl2 ,dy_ctrl2 ,dx_to ,dy_to );
|
|
|
|
end;
|
|
|
|
{ END_POLY }
|
|
procedure path_storage.end_poly;
|
|
begin
|
|
if m_total_vertices <> 0 then
|
|
if is_vertex(command(m_total_vertices - 1 ) ) then
|
|
add_vertex(0.0 ,0.0 ,path_cmd_end_poly or flags );
|
|
|
|
end;
|
|
|
|
{ CLOSE_POLYGON }
|
|
procedure path_storage.close_polygon;
|
|
begin
|
|
end_poly(path_flags_close or flags );
|
|
|
|
end;
|
|
|
|
{ ADD_POLY }
|
|
procedure path_storage.add_poly;
|
|
begin
|
|
if num <> 0 then
|
|
begin
|
|
if not solid_path then
|
|
begin
|
|
move_to(vertices[0 ] ,vertices[1 ] );
|
|
|
|
inc(ptrcomp(vertices ) ,2 * sizeof(double ) );
|
|
dec(num );
|
|
|
|
end;
|
|
|
|
while num > 0 do
|
|
begin
|
|
line_to(vertices[0 ] ,vertices[1 ] );
|
|
|
|
inc(ptrcomp(vertices ) ,2 * sizeof(double ) );
|
|
dec(num );
|
|
|
|
end;
|
|
|
|
if end_flags <> 0 then
|
|
end_poly(end_flags );
|
|
|
|
end;
|
|
|
|
end;
|
|
|
|
{ ADD_PATH }
|
|
procedure path_storage.add_path;
|
|
var
|
|
cmd : unsigned;
|
|
|
|
x ,y : double;
|
|
|
|
begin
|
|
vs.rewind(path_id );
|
|
|
|
cmd:=vs.vertex(@x ,@y );
|
|
|
|
while not is_stop(cmd ) do
|
|
begin
|
|
if is_move_to(cmd ) and
|
|
solid_path and
|
|
(m_total_vertices <> 0 ) then
|
|
cmd:=path_cmd_line_to;
|
|
|
|
add_vertex(x ,y ,cmd );
|
|
|
|
cmd:=vs.vertex(@x ,@y );
|
|
|
|
end;
|
|
|
|
end;
|
|
|
|
{ START_NEW_PATH }
|
|
function path_storage.start_new_path;
|
|
begin
|
|
if m_total_vertices <> 0 then
|
|
if not is_stop(command(m_total_vertices - 1 ) ) then
|
|
add_vertex(0.0 ,0.0 ,path_cmd_stop );
|
|
|
|
result:=m_total_vertices;
|
|
|
|
end;
|
|
|
|
{ COPY_FROM }
|
|
procedure path_storage.copy_from;
|
|
var
|
|
i ,cmd : unsigned;
|
|
x ,y : double;
|
|
|
|
begin
|
|
remove_all;
|
|
|
|
for i:=0 to ps.total_vertices - 1 do
|
|
begin
|
|
cmd:=ps.vertex_(i ,@x ,@y );
|
|
|
|
add_vertex(x ,y ,cmd );
|
|
|
|
end;
|
|
|
|
end;
|
|
|
|
{ TOTAL_VERTICES }
|
|
function path_storage.total_vertices;
|
|
begin
|
|
result:=m_total_vertices
|
|
|
|
end;
|
|
|
|
{ VERTEX_ }
|
|
function path_storage.vertex_;
|
|
var
|
|
nb : unsigned;
|
|
pv : double_ptr;
|
|
|
|
begin
|
|
nb:=idx shr block_shift;
|
|
|
|
pv:=
|
|
double_ptr(
|
|
ptrcomp(
|
|
p32_ptr(ptrcomp(m_coord_blocks ) + nb * sizeof(double_ptr ) ).ptr ) +
|
|
((idx and block_mask ) shl 1 ) * sizeof(double ) );
|
|
|
|
x^:=pv^; inc(ptrcomp(pv ) ,sizeof(double ) );
|
|
y^:=pv^;
|
|
|
|
result:=
|
|
int8u_ptr(
|
|
ptrcomp(
|
|
p32_ptr(ptrcomp(m_cmd_blocks ) + nb * sizeof(int8u_ptr ) ).ptr ) +
|
|
(idx and block_mask ) * sizeof(int8u ) )^;
|
|
|
|
end;
|
|
|
|
{ COMMAND }
|
|
function path_storage.command;
|
|
begin
|
|
result:=
|
|
int8u_ptr(
|
|
ptrcomp(
|
|
p32_ptr(ptrcomp(m_cmd_blocks ) + (idx shr block_shift ) * sizeof(int8u_ptr ) ).ptr ) +
|
|
(idx and block_mask ) * sizeof(int8u ) )^;
|
|
|
|
end;
|
|
|
|
{ REWIND }
|
|
procedure path_storage.rewind;
|
|
begin
|
|
m_iterator:=path_id;
|
|
|
|
end;
|
|
|
|
{ VERTEX }
|
|
function path_storage.vertex;
|
|
begin
|
|
if m_iterator >= m_total_vertices then
|
|
result:=path_cmd_stop
|
|
else
|
|
begin
|
|
result:=vertex_(m_iterator ,x ,y );
|
|
|
|
inc(m_iterator );
|
|
|
|
end;
|
|
|
|
end;
|
|
|
|
{ ARRANGE_POLYGON_ORIENTATION }
|
|
function path_storage.arrange_polygon_orientation;
|
|
var
|
|
cmd ,
|
|
end_ : unsigned;
|
|
|
|
begin
|
|
if orientation = path_flags_none then
|
|
begin
|
|
result:=start;
|
|
|
|
exit;
|
|
|
|
end;
|
|
|
|
// Skip all non-vertices at the beginning
|
|
while (start < m_total_vertices ) and
|
|
not is_vertex(command(start ) ) do
|
|
inc(start );
|
|
|
|
// Skip all insignificant move_to
|
|
while (start + 1 < m_total_vertices ) and
|
|
is_move_to(command(start ) ) and
|
|
is_move_to(command(start + 1 ) ) do
|
|
inc(start );
|
|
|
|
// Find the last vertex
|
|
end_:=start + 1;
|
|
|
|
while (end_ < m_total_vertices ) and
|
|
not is_next_poly(command(end_ ) ) do
|
|
inc(end_ );
|
|
|
|
if end_ - start > 2 then
|
|
if perceive_polygon_orientation(start ,end_ ) <> orientation then
|
|
begin
|
|
// Invert polygon, set orientation flag, and skip all end_poly
|
|
invert_polygon(start ,end_ );
|
|
|
|
cmd:=command(end_ );
|
|
|
|
while (end_ < m_total_vertices ) and
|
|
is_end_poly(cmd ) do
|
|
begin
|
|
modify_command(end_ ,set_orientation(cmd ,orientation ) );
|
|
|
|
inc(end_ );
|
|
|
|
cmd:=command(end_ );
|
|
|
|
end;
|
|
|
|
end;
|
|
|
|
result:=end_;
|
|
|
|
end;
|
|
|
|
{ ARRANGE_ORIENTATIONS }
|
|
function path_storage.arrange_orientations;
|
|
begin
|
|
if orientation <> path_flags_none then
|
|
while start < m_total_vertices do
|
|
begin
|
|
start:=arrange_polygon_orientation(start ,orientation );
|
|
|
|
if is_stop(command(start ) ) then
|
|
begin
|
|
inc(start );
|
|
|
|
break;
|
|
|
|
end;
|
|
|
|
end;
|
|
|
|
result:=start;
|
|
|
|
end;
|
|
|
|
{ ARRANGE_ORIENTATIONS_ALL_PATHS }
|
|
procedure path_storage.arrange_orientations_all_paths;
|
|
var
|
|
start : unsigned;
|
|
|
|
begin
|
|
if orientation <> path_flags_none then
|
|
begin
|
|
start:=0;
|
|
|
|
while start < m_total_vertices do
|
|
start:=arrange_orientations(start ,orientation );
|
|
|
|
end;
|
|
|
|
end;
|
|
|
|
{ FLIP_X }
|
|
procedure path_storage.flip_x;
|
|
var
|
|
i ,cmd : unsigned;
|
|
x ,y : double;
|
|
|
|
begin
|
|
if m_total_vertices > 0 then
|
|
for i:=0 to m_total_vertices - 1 do
|
|
begin
|
|
cmd:=vertex_(i ,@x ,@y );
|
|
|
|
if is_vertex(cmd ) then
|
|
modify_vertex(i ,x2 - x + x1 ,y );
|
|
|
|
end;
|
|
|
|
end;
|
|
|
|
{ FLIP_Y }
|
|
procedure path_storage.flip_y;
|
|
var
|
|
i ,cmd : unsigned;
|
|
x ,y : double;
|
|
|
|
begin
|
|
if m_total_vertices > 0 then
|
|
for i:=0 to m_total_vertices - 1 do
|
|
begin
|
|
cmd:=vertex_(i ,@x ,@y );
|
|
|
|
if is_vertex(cmd ) then
|
|
modify_vertex(i ,x ,y2 - y + y1 );
|
|
|
|
end;
|
|
|
|
end;
|
|
|
|
{ ADD_VERTEX }
|
|
procedure path_storage.add_vertex;
|
|
var
|
|
coord_ptr : double_ptr;
|
|
cmd_ptr : int8u_ptr;
|
|
|
|
begin
|
|
coord_ptr:=NIL;
|
|
|
|
cmd_ptr:=storage_ptrs(@coord_ptr );
|
|
|
|
cmd_ptr^:=int8u(cmd );
|
|
|
|
coord_ptr^:=x; inc(ptrcomp(coord_ptr ) ,sizeof(double ) );
|
|
coord_ptr^:=y;
|
|
|
|
inc(m_total_vertices );
|
|
|
|
end;
|
|
|
|
{ MODIFY_VERTEX }
|
|
procedure path_storage.modify_vertex;
|
|
var
|
|
pv : double_ptr;
|
|
|
|
begin
|
|
pv:=
|
|
double_ptr(
|
|
ptrcomp(
|
|
p32_ptr(ptrcomp(m_coord_blocks ) + (idx shr block_shift ) * sizeof(double_ptr ) ).ptr ) +
|
|
((idx and block_mask ) shl 1 ) * sizeof(double ) );
|
|
|
|
pv^:=x; inc(ptrcomp(pv ) ,sizeof(double ) );
|
|
pv^:=y;
|
|
|
|
end;
|
|
|
|
{ MODIFY_COMMAND }
|
|
procedure path_storage.modify_command;
|
|
begin
|
|
int8u_ptr(
|
|
ptrcomp(
|
|
p32_ptr(ptrcomp(m_cmd_blocks ) + (idx shr block_shift ) * sizeof(int8u_ptr ) ).ptr ) +
|
|
(idx and block_mask ) * sizeof(int8u ) )^:=int8u(cmd );
|
|
|
|
end;
|
|
|
|
{ TRANSFORM }
|
|
procedure path_storage.transform(trans : trans_affine_ptr; path_id : unsigned = 0 );
|
|
var
|
|
x ,y : double;
|
|
cmd : unsigned;
|
|
|
|
begin
|
|
while path_id < m_total_vertices do
|
|
begin
|
|
cmd:=vertex_(path_id ,@x ,@y );
|
|
|
|
if is_stop(cmd ) then
|
|
break;
|
|
|
|
if is_vertex(cmd ) then
|
|
begin
|
|
trans.transform(trans ,@x ,@y );
|
|
modify_vertex (path_id ,x ,y );
|
|
|
|
end;
|
|
|
|
inc(path_id );
|
|
|
|
end;
|
|
|
|
end;
|
|
|
|
{ TRANSFORM_ALL_PATHS }
|
|
procedure path_storage.transform_all_paths(trans : trans_affine_ptr );
|
|
var
|
|
x ,y : double;
|
|
idx : unsigned;
|
|
|
|
begin
|
|
idx:=0;
|
|
|
|
while idx < m_total_vertices do
|
|
begin
|
|
if is_vertex(vertex_(idx ,@x ,@y ) ) then
|
|
begin
|
|
trans.transform(trans ,@x ,@y );
|
|
modify_vertex (idx ,x ,y );
|
|
|
|
end;
|
|
|
|
inc(idx );
|
|
|
|
end;
|
|
|
|
end;
|
|
|
|
{ ALLOCATE_BLOCK }
|
|
procedure path_storage.allocate_block;
|
|
var
|
|
new_coords : double_ptr_ptr;
|
|
new_cmds : int8u_ptr_ptr;
|
|
|
|
begin
|
|
if nb >= m_max_blocks then
|
|
begin
|
|
agg_getmem(pointer(new_coords ) ,((m_max_blocks + block_pool ) * 2 ) * sizeof(double_ptr ) );
|
|
|
|
new_cmds:=int8u_ptr_ptr(ptrcomp(new_coords ) + (m_max_blocks + block_pool ) * sizeof(double_ptr ) );
|
|
|
|
if m_coord_blocks <> NIL then
|
|
begin
|
|
move(m_coord_blocks^ ,new_coords^ ,m_max_blocks * sizeof(double_ptr ) );
|
|
move(m_cmd_blocks^ ,new_cmds^ ,m_max_blocks * sizeof(int8u_ptr ) );
|
|
|
|
agg_freemem(pointer(m_coord_blocks ) ,m_max_blocks * 2 * sizeof(double_ptr ) );
|
|
|
|
end;
|
|
|
|
m_coord_blocks:=new_coords;
|
|
m_cmd_blocks :=new_cmds;
|
|
|
|
inc(m_max_blocks ,block_pool );
|
|
|
|
end;
|
|
|
|
agg_getmem(
|
|
p32_ptr(ptrcomp(m_coord_blocks ) + nb * sizeof(double_ptr ) ).ptr ,
|
|
(block_size * 2 + block_size div (sizeof(double ) div sizeof(int8u ) ) ) * sizeof(double ) );
|
|
|
|
p32_ptr(ptrcomp(m_cmd_blocks ) + nb * sizeof(int8u_ptr ) ).ptr:=
|
|
pointer(
|
|
ptrcomp(p32_ptr(ptrcomp(m_coord_blocks ) + nb * sizeof(double_ptr ) ).ptr ) +
|
|
block_size * 2 * sizeof(double ) );
|
|
|
|
inc(m_total_blocks );
|
|
|
|
end;
|
|
|
|
{ STORAGE_PTRS }
|
|
function path_storage.storage_ptrs;
|
|
var
|
|
nb : unsigned;
|
|
|
|
begin
|
|
nb:=m_total_vertices shr block_shift;
|
|
|
|
if nb >= m_total_blocks then
|
|
allocate_block(nb );
|
|
|
|
xy_ptr^:=
|
|
double_ptr(
|
|
ptrcomp(
|
|
p32_ptr(ptrcomp(m_coord_blocks ) + nb * sizeof(double_ptr ) ).ptr ) +
|
|
((m_total_vertices and block_mask ) shl 1 ) * sizeof(double ) );
|
|
|
|
result:=
|
|
int8u_ptr(
|
|
ptrcomp(
|
|
p32_ptr(ptrcomp(m_cmd_blocks ) + nb * sizeof(int8u_ptr ) ).ptr ) +
|
|
(m_total_vertices and block_mask ) * sizeof(int8u ) );
|
|
|
|
end;
|
|
|
|
{ PERCEIVE_POLYGON_ORIENTATION }
|
|
function path_storage.perceive_polygon_orientation;
|
|
var
|
|
np ,i : unsigned;
|
|
|
|
area ,x1 ,y1 ,x2 ,y2 : double;
|
|
|
|
begin
|
|
// Calculate signed area (double area to be exact)
|
|
np :=end_ - start;
|
|
area:=0.0;
|
|
|
|
if np > 0 then
|
|
for i:=0 to np - 1 do
|
|
begin
|
|
vertex_(start + i ,@x1 ,@y1 );
|
|
vertex_(start + (i + 1 ) mod np ,@x2 ,@y2 );
|
|
|
|
area:=area + (x1 * y2 - y1 * x2 );
|
|
|
|
end;
|
|
|
|
if area < 0.0 then
|
|
result:=path_flags_cw
|
|
else
|
|
result:=path_flags_ccw;
|
|
|
|
end;
|
|
|
|
{ INVERT_POLYGON }
|
|
procedure path_storage.invert_polygon(start ,end_ : unsigned );
|
|
var
|
|
i ,tmp_cmd ,start_nb ,end_nb : unsigned;
|
|
|
|
start_ptr ,end_ptr : double_ptr;
|
|
|
|
tmp_xy : double;
|
|
|
|
begin
|
|
tmp_cmd:=command(start );
|
|
|
|
dec(end_ ); // Make "end" inclusive
|
|
|
|
// Shift all commands to one position
|
|
i:=start;
|
|
|
|
while i < end_ do
|
|
begin
|
|
modify_command(i ,command(i + 1 ) );
|
|
|
|
inc(i );
|
|
|
|
end;
|
|
|
|
// Assign starting command to the ending command
|
|
modify_command(end_ ,tmp_cmd );
|
|
|
|
// Reverse the polygon
|
|
while end_ > start do
|
|
begin
|
|
start_nb:=start shr block_shift;
|
|
end_nb :=end_ shr block_shift;
|
|
|
|
start_ptr:=
|
|
double_ptr(
|
|
ptrcomp(
|
|
p32_ptr(ptrcomp(m_coord_blocks ) + start_nb * sizeof(double_ptr ) ).ptr ) +
|
|
((start and block_mask ) shl 1 ) * sizeof(double ) );
|
|
|
|
end_ptr:=
|
|
double_ptr(
|
|
ptrcomp(
|
|
p32_ptr(ptrcomp(m_coord_blocks ) + end_nb * sizeof(double_ptr ) ).ptr ) +
|
|
((end_ and block_mask ) shl 1 ) * sizeof(double ) );
|
|
|
|
tmp_xy :=start_ptr^;
|
|
start_ptr^:=end_ptr^; inc(ptrcomp(start_ptr ) ,sizeof(double ) );
|
|
end_ptr^ :=tmp_xy; inc(ptrcomp(end_ptr ) ,sizeof(double ) );
|
|
|
|
tmp_xy :=start_ptr^;
|
|
start_ptr^:=end_ptr^;
|
|
end_ptr^ :=tmp_xy;
|
|
|
|
tmp_cmd:=
|
|
int8u_ptr(
|
|
ptrcomp(
|
|
p32_ptr(ptrcomp(m_cmd_blocks ) + start_nb * sizeof(int8u_ptr ) ).ptr ) +
|
|
(start and block_mask ) * sizeof(int8u ) )^;
|
|
|
|
int8u_ptr(
|
|
ptrcomp(
|
|
p32_ptr(ptrcomp(m_cmd_blocks ) + start_nb * sizeof(int8u_ptr ) ).ptr ) +
|
|
(start and block_mask ) * sizeof(int8u ) )^:=
|
|
int8u_ptr(
|
|
ptrcomp(
|
|
p32_ptr(ptrcomp(m_cmd_blocks ) + end_nb * sizeof(int8u_ptr ) ).ptr ) +
|
|
(end_ and block_mask ) * sizeof(int8u ) )^;
|
|
|
|
int8u_ptr(
|
|
ptrcomp(
|
|
p32_ptr(ptrcomp(m_cmd_blocks ) + end_nb * sizeof(int8u_ptr ) ).ptr ) +
|
|
(end_ and block_mask ) * sizeof(int8u ) )^:=int8u(tmp_cmd );
|
|
|
|
inc(start );
|
|
dec(end_ );
|
|
|
|
end;
|
|
|
|
end;
|
|
|
|
{ INVERT_POLYGON }
|
|
procedure path_storage.invert_polygon(start : unsigned );
|
|
var
|
|
end_ : unsigned;
|
|
|
|
begin
|
|
// Skip all non-vertices at the beginning
|
|
while (start < m_total_vertices ) and
|
|
not is_vertex(command(start ) ) do
|
|
inc(start );
|
|
|
|
// Skip all insignificant move_to
|
|
while (start + 1 < m_total_vertices ) and
|
|
is_move_to(command(start ) ) and
|
|
is_move_to(command(start + 1 ) ) do
|
|
inc(start );
|
|
|
|
// Find the last vertex
|
|
end_:=start + 1;
|
|
|
|
while (end_ < m_total_vertices ) and
|
|
not is_next_poly(command(end_ ) ) do
|
|
inc(end_ );
|
|
|
|
invert_polygon(start ,end_ );
|
|
|
|
end;
|
|
|
|
{ CONCAT_PATH }
|
|
procedure path_storage.concat_path(vs : vertex_source_ptr; path_id : unsigned = 0 );
|
|
var
|
|
x ,y : double;
|
|
cmd : unsigned;
|
|
|
|
begin
|
|
vs.rewind(path_id );
|
|
|
|
cmd:=vs.vertex(@x ,@y );
|
|
|
|
while not is_stop(cmd ) do
|
|
begin
|
|
add_vertex(x ,y ,cmd );
|
|
|
|
cmd:=vs.vertex(@x ,@y );
|
|
|
|
end;
|
|
|
|
end;
|
|
|
|
END.
|
|
|