lazarus/components/aggpas/interactive_polygon_.pas
mattias 36a2b1ea07 added aggpas
git-svn-id: trunk@21942 -
2009-10-01 12:24:32 +00:00

639 lines
12 KiB
ObjectPascal

unit
interactive_polygon_ ;
INTERFACE
{$I agg_mode.inc }
uses
Math ,
agg_basics ,
agg_conv_stroke ,
agg_ellipse ,
agg_vertex_source ;
{ TYPES DEFINITION }
type
simple_polygon_vertex_source = object(vertex_source )
m_polygon : double_ptr;
m_num_points ,
m_vertex : unsigned;
m_roundoff ,
m_close : boolean;
constructor Construct(polygon : double_ptr; np : unsigned; roundoff : boolean = false; close : boolean = true );
procedure close_(f : boolean );
function _close : boolean;
procedure rewind(path_id : unsigned ); virtual;
function vertex(x ,y : double_ptr ) : unsigned; virtual;
end;
interactive_polygon = object(vertex_source )
m_polygon : double_ptr;
m_num_points : unsigned;
m_node ,
m_edge : int;
m_vs : simple_polygon_vertex_source;
m_stroke : conv_stroke;
m_ellipse : ellipse;
m_point_radius : double;
m_status : unsigned;
m_dx ,
m_dy : double;
constructor Construct(np : unsigned; point_radius : double );
destructor Destruct; virtual;
function num_points : unsigned;
function xn(n : unsigned ) : double;
function yn(n : unsigned ) : double;
function xn_ptr(n : unsigned ) : double_ptr;
function yn_ptr(n : unsigned ) : double_ptr;
function polygon : double_ptr;
function _node : int;
procedure node_(n : int );
function _close : boolean;
procedure close_(f : boolean );
procedure rewind(path_id : unsigned ); virtual;
function vertex(x ,y : double_ptr ) : unsigned; virtual;
function on_mouse_move(x ,y : double ) : boolean;
function on_mouse_button_down(x ,y : double ) : boolean;
function on_mouse_button_up(x ,y : double ) : boolean;
function check_edge(i : unsigned; x ,y : double ) : boolean;
function point_in_polygon(tx ,ty : double ) : boolean;
end;
{ GLOBAL PROCEDURES }
IMPLEMENTATION
{ LOCAL VARIABLES & CONSTANTS }
{ UNIT IMPLEMENTATION }
{ CONSTRUCT }
constructor simple_polygon_vertex_source.Construct;
begin
inherited Construct;
m_polygon :=polygon;
m_num_points:=np;
m_vertex :=0;
m_roundoff:=roundoff;
m_close :=close;
end;
{ CLOSE_ }
procedure simple_polygon_vertex_source.close_;
begin
m_close:=f;
end;
{ _CLOSE }
function simple_polygon_vertex_source._close;
begin
result:=m_close;
end;
{ REWIND }
procedure simple_polygon_vertex_source.rewind;
begin
m_vertex:=0;
end;
{ VERTEX }
function simple_polygon_vertex_source.vertex;
begin
if m_vertex > m_num_points then
begin
result:=path_cmd_stop;
exit;
end;
if m_vertex = m_num_points then
begin
inc(m_vertex );
if m_close then
result:=path_cmd_end_poly or path_flags_close
else
result:=path_cmd_end_poly or 0;
exit;
end;
x^:=double_ptr(ptrcomp(m_polygon ) + (m_vertex * 2 ) * sizeof(double ) )^;
y^:=double_ptr(ptrcomp(m_polygon ) + (m_vertex * 2 + 1 ) * sizeof(double ) )^;
if m_roundoff then
begin
x^:=Floor(x^ ) + 0.5;
y^:=Floor(y^ ) + 0.5;
end;
inc(m_vertex );
if m_vertex = 1 then
result:=path_cmd_move_to
else
result:=path_cmd_line_to;
end;
{ CONSTRUCT }
constructor interactive_polygon.Construct;
begin
inherited Construct;
agg_getmem(pointer(m_polygon ) ,np * 2 * sizeof(double ) );
m_num_points:=np;
m_node:=-1;
m_edge:=-1;
m_vs.Construct (m_polygon ,m_num_points ,false );
m_stroke.Construct(@m_vs );
m_ellipse.Construct;
m_point_radius:=point_radius;
m_status :=0;
m_dx:=0.0;
m_dy:=0.0;
m_stroke.width_(1.0 );
end;
{ DESTRUCT }
destructor interactive_polygon.Destruct;
begin
agg_freemem(pointer(m_polygon ) ,m_num_points * 2 * sizeof(double ) );
m_stroke.Destruct;
end;
{ NUM_POINTS }
function interactive_polygon.num_points;
begin
result:=m_num_points;
end;
{ XN }
function interactive_polygon.xn;
begin
result:=double_ptr(ptrcomp(m_polygon ) + (n * 2 ) * sizeof(double ) )^;
end;
{ YN }
function interactive_polygon.yn;
begin
result:=double_ptr(ptrcomp(m_polygon ) + (n * 2 + 1 ) * sizeof(double ) )^;
end;
{ XN_PTR }
function interactive_polygon.xn_ptr;
begin
result:=double_ptr(ptrcomp(m_polygon ) + (n * 2 ) * sizeof(double ) );
end;
{ YN_PTR }
function interactive_polygon.yn_ptr;
begin
result:=double_ptr(ptrcomp(m_polygon ) + (n * 2 + 1 ) * sizeof(double ) );
end;
{ POLYGON }
function interactive_polygon.polygon;
begin
result:=m_polygon;
end;
{ _NODE }
function interactive_polygon._node;
begin
result:=m_node;
end;
{ NODE_ }
procedure interactive_polygon.node_;
begin
m_node:=n;
end;
{ _CLOSE }
function interactive_polygon._close;
begin
result:=m_vs._close;
end;
{ CLOSE_ }
procedure interactive_polygon.close_;
begin
m_vs.close_(f );
end;
{ REWIND }
procedure interactive_polygon.rewind;
begin
m_status:=0;
m_stroke.rewind(0 );
end;
{ VERTEX }
function interactive_polygon.vertex;
var
r : double;
cmd : unsigned;
begin
cmd:=path_cmd_stop;
r :=m_point_radius;
if m_status = 0 then
begin
cmd:=m_stroke.vertex(x ,y );
if not is_stop(cmd ) then
begin
result:=cmd;
exit;
end;
if (m_node >= 0 ) and
(m_node = int(m_status ) ) then
r:=r * 1.2;
m_ellipse.init(xn(m_status ) ,yn(m_status ) ,r ,r ,32 );
inc(m_status );
end;
cmd:=m_ellipse.vertex(x ,y );
if not is_stop(cmd ) then
begin
result:=cmd;
exit;
end;
if m_status >= m_num_points then
begin
result:=path_cmd_stop;
exit;
end;
if (m_node >= 0 ) and
(m_node = int(m_status ) ) then
r:=r * 1.2;
m_ellipse.init(xn(m_status ) ,yn(m_status ) ,r ,r ,32 );
inc(m_status );
result:=m_ellipse.vertex(x ,y );
end;
{ ON_MOUSE_MOVE }
function interactive_polygon.on_mouse_move;
var
ret : boolean;
i ,n1 ,n2 : unsigned;
dx ,dy : double;
begin
ret:=false;
if m_node = int(m_num_points ) then
begin
dx:=x - m_dx;
dy:=y - m_dy;
for i:=0 to m_num_points - 1 do
begin
xn_ptr(i )^:=xn_ptr(i )^ + dx;
yn_ptr(i )^:=yn_ptr(i )^ + dy;
end;
m_dx:=x;
m_dy:=y;
ret:=true;
end
else
if m_edge >= 0 then
begin
n1:=m_edge;
n2:=(n1 + m_num_points - 1 ) mod m_num_points;
dx:=x - m_dx;
dy:=y - m_dy;
xn_ptr(n1 )^:=xn_ptr(n1 )^ + dx;
yn_ptr(n1 )^:=yn_ptr(n1 )^ + dy;
xn_ptr(n2 )^:=xn_ptr(n2 )^ + dx;
yn_ptr(n2 )^:=yn_ptr(n2 )^ + dy;
m_dx:=x;
m_dy:=y;
ret:=true;
end
else
if m_node >= 0 then
begin
xn_ptr(m_node )^:=x - m_dx;
yn_ptr(m_node )^:=y - m_dy;
ret:=true;
end;
result:=ret;
end;
{ ON_MOUSE_BUTTON_DOWN }
function interactive_polygon.on_mouse_button_down;
var
i : unsigned;
ret : boolean;
begin
ret:=false;
m_node:=-1;
m_edge:=-1;
for i:=0 to m_num_points - 1 do
if Sqrt((x - xn(i ) ) * (x - xn(i ) ) + (y - yn(i ) ) * (y - yn(i ) ) ) < m_point_radius then
begin
m_dx:=x - xn(i );
m_dy:=y - yn(i );
m_node:=int(i );
ret:=true;
break;
end;
if not ret then
for i:=0 to m_num_points - 1 do
if check_edge(i ,x ,y ) then
begin
m_dx:=x;
m_dy:=y;
m_edge:=int(i );
ret:=true;
break;
end;
if not ret then
if point_in_polygon(x ,y ) then
begin
m_dx:=x;
m_dy:=y;
m_node:=int(m_num_points );
ret:=true;
end;
result:=ret;
end;
{ ON_MOUSE_BUTTON_UP }
function interactive_polygon.on_mouse_button_up;
var
ret : boolean;
begin
ret:=(m_node >= 0 ) or (m_edge >= 0 );
m_node:=-1;
m_edge:=-1;
result:=ret;
end;
{ CHECK_EDGE }
function interactive_polygon.check_edge;
var
ret : boolean;
n1 ,n2 : unsigned;
x1 ,y1 ,x2 ,y2 ,dx ,dy ,x3 ,y3 ,x4 ,y4 ,den ,u1 ,xi ,yi : double;
begin
ret:=false;
n1:= i;
n2:= (i + m_num_points - 1 ) mod m_num_points;
x1:=xn(n1 );
y1:=yn(n1 );
x2:=xn(n2 );
y2:=yn(n2 );
dx:=x2 - x1;
dy:=y2 - y1;
if Sqrt(dx * dx + dy * dy ) > 0.0000001 then
begin
x3:=x;
y3:=y;
x4:=x3 - dy;
y4:=y3 + dx;
den:=(y4 - y3 ) * (x2 - x1 ) - (x4 - x3 ) * (y2 - y1 );
u1 :=((x4 - x3 ) * (y1 - y3 ) - (y4 - y3 ) * (x1 - x3 ) ) / den;
xi:=x1 + u1 * (x2 - x1);
yi:=y1 + u1 * (y2 - y1);
dx:=xi - x;
dy:=yi - y;
if (u1 > 0.0 ) and
(u1 < 1.0 ) and
(Sqrt(dx * dx + dy * dy ) <= m_point_radius ) then
ret:=true;
end;
result:=ret;
end;
{ POINT_IN_POLYGON }
//======= Crossings Multiply algorithm of InsideTest ========================
//
// By Eric Haines, 3D/Eye Inc, erich@eye.com
//
// This version is usually somewhat faster than the original published in
// Graphics Gems IV; by turning the division for testing the X axis crossing
// into a tricky multiplication test this part of the test became faster,
// which had the additional effect of making the test for "both to left or
// both to right" a bit slower for triangles than simply computing the
// intersection each time. The main increase is in triangle testing speed,
// which was about 15% faster; all other polygon complexities were pretty much
// the same as before. On machines where division is very expensive (not the
// case on the HP 9000 series on which I tested) this test should be much
// faster overall than the old code. Your mileage may (in fact, will) vary,
// depending on the machine and the test data, but in general I believe this
// code is both shorter and faster. This test was inspired by unpublished
// Graphics Gems submitted by Joseph Samosky and Mark Haigh-Hutchinson.
// Related work by Samosky is in:
//
// Samosky, Joseph, "SectionView: A system for interactively specifying and
// visualizing sections through three-dimensional medical image data",
// M.S. Thesis, Department of Electrical Engineering and Computer Science,
// Massachusetts Institute of Technology, 1993.
//
// Shoot a test ray along +X axis. The strategy is to compare vertex Y values
// to the testing point's Y and quickly discard edges which are entirely to one
// side of the test ray. Note that CONVEX and WINDING code can be added as
// for the CrossingsTest() code; it is left out here for clarity.
//
// Input 2D polygon _pgon_ with _numverts_ number of vertices and test point
// _point_, returns 1 if inside, 0 if outside.
function interactive_polygon.point_in_polygon;
var
j ,k : unsigned;
yflag0 ,yflag1 ,inside_flag : int;
vtx0 ,vty0 ,vtx1 ,vty1 : double;
begin
if m_num_points < 3 then
begin
result:=false;
exit;
end;
vtx0:=xn(m_num_points - 1 );
vty0:=yn(m_num_points - 1 );
// get test bit for above/below X axis
yflag0:=int(vty0 >= ty );
vtx1:=xn(0 );
vty1:=yn(0 );
inside_flag:=0;
for j:=1 to m_num_points do
begin
yflag1:=int(vty1 >= ty );
// Check if endpoints straddle (are on opposite sides) of X axis
// (i.e. the Y's differ); if so, +X ray could intersect this edge.
// The old test also checked whether the endpoints are both to the
// right or to the left of the test point. However, given the faster
// intersection point computation used below, this test was found to
// be a break-even proposition for most polygons and a loser for
// triangles (where 50% or more of the edges which survive this test
// will cross quadrants and so have to have the X intersection computed
// anyway). I credit Joseph Samosky with inspiring me to try dropping
// the "both left or both right" part of my code.
if yflag0 <> yflag1 then
// Check intersection of pgon segment with +X ray.
// Note if >= point's X; if so, the ray hits it.
// The division operation is avoided for the ">=" test by checking
// the sign of the first vertex wrto the test point; idea inspired
// by Joseph Samosky's and Mark Haigh-Hutchinson's different
// polygon inclusion tests.
if int((vty1 - ty ) * (vtx0 - vtx1 ) >= (vtx1 - tx ) * (vty0 - vty1 ) ) = yflag1 then
inside_flag:=inside_flag xor 1;
// Move to the next pair of vertices, retaining info as possible.
yflag0:=yflag1;
vtx0:=vtx1;
vty0:=vty1;
if j >= m_num_points then
k:=j - m_num_points
else
k:=j;
vtx1:=xn(k );
vty1:=yn(k );
end;
result:=inside_flag <> 0;
end;
END.