#include "agg_basics.h" #include "interactive_polygon.h" namespace agg { interactive_polygon::interactive_polygon(unsigned np, double point_radius) : m_polygon(np * 2), m_num_points(np), m_node(-1), m_edge(-1), m_vs(&m_polygon[0], m_num_points, false), m_stroke(m_vs), m_point_radius(point_radius), m_status(0), m_dx(0.0), m_dy(0.0) { m_stroke.width(1.0); } void interactive_polygon::rewind(unsigned) { m_status = 0; m_stroke.rewind(0); } unsigned interactive_polygon::vertex(double* x, double* y) { unsigned cmd = path_cmd_stop; double r = m_point_radius; if(m_status == 0) { cmd = m_stroke.vertex(x, y); if(!is_stop(cmd)) return cmd; if(m_node >= 0 && m_node == int(m_status)) r *= 1.2; m_ellipse.init(xn(m_status), yn(m_status), r, r, 32); ++m_status; } cmd = m_ellipse.vertex(x, y); if(!is_stop(cmd)) return cmd; if(m_status >= m_num_points) return path_cmd_stop; if(m_node >= 0 && m_node == int(m_status)) r *= 1.2; m_ellipse.init(xn(m_status), yn(m_status), r, r, 32); ++m_status; return m_ellipse.vertex(x, y); } bool interactive_polygon::check_edge(unsigned i, double x, double y) const { bool ret = false; unsigned n1 = i; unsigned n2 = (i + m_num_points - 1) % m_num_points; double x1 = xn(n1); double y1 = yn(n1); double x2 = xn(n2); double y2 = yn(n2); double dx = x2 - x1; double dy = y2 - y1; if(sqrt(dx*dx + dy*dy) > 0.0000001) { double x3 = x; double y3 = y; double x4 = x3 - dy; double y4 = y3 + dx; double den = (y4-y3) * (x2-x1) - (x4-x3) * (y2-y1); double u1 = ((x4-x3) * (y1-y3) - (y4-y3) * (x1-x3)) / den; double xi = x1 + u1 * (x2 - x1); double yi = y1 + u1 * (y2 - y1); dx = xi - x; dy = yi - y; if (u1 > 0.0 && u1 < 1.0 && sqrt(dx*dx + dy*dy) <= m_point_radius) { ret = true; } } return ret; } bool interactive_polygon::on_mouse_button_down(double x, double y) { unsigned i; bool ret = false; m_node = -1; m_edge = -1; for (i = 0; i < m_num_points; i++) { if(sqrt( (x-xn(i)) * (x-xn(i)) + (y-yn(i)) * (y-yn(i)) ) < m_point_radius) { m_dx = x - xn(i); m_dy = y - yn(i); m_node = int(i); ret = true; break; } } if(!ret) { for (i = 0; i < m_num_points; i++) { if(check_edge(i, x, y)) { m_dx = x; m_dy = y; m_edge = int(i); ret = true; break; } } } if(!ret) { if(point_in_polygon(x, y)) { m_dx = x; m_dy = y; m_node = int(m_num_points); ret = true; } } return ret; } bool interactive_polygon::on_mouse_move(double x, double y) { bool ret = false; double dx; double dy; if(m_node == int(m_num_points)) { dx = x - m_dx; dy = y - m_dy; unsigned i; for(i = 0; i < m_num_points; i++) { xn(i) += dx; yn(i) += dy; } m_dx = x; m_dy = y; ret = true; } else { if(m_edge >= 0) { unsigned n1 = m_edge; unsigned n2 = (n1 + m_num_points - 1) % m_num_points; dx = x - m_dx; dy = y - m_dy; xn(n1) += dx; yn(n1) += dy; xn(n2) += dx; yn(n2) += dy; m_dx = x; m_dy = y; ret = true; } else { if(m_node >= 0) { xn(m_node) = x - m_dx; yn(m_node) = y - m_dy; ret = true; } } } return ret; } bool interactive_polygon::on_mouse_button_up(double x, double y) { bool ret = (m_node >= 0) || (m_edge >= 0); m_node = -1; m_edge = -1; return ret; } //======= 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. bool interactive_polygon::point_in_polygon(double tx, double ty) const { if(m_num_points < 3) return false; unsigned j; int yflag0, yflag1, inside_flag; double vtx0, vty0, vtx1, vty1; vtx0 = xn(m_num_points - 1); vty0 = yn(m_num_points - 1); // get test bit for above/below X axis yflag0 = (vty0 >= ty); vtx1 = xn(0); vty1 = yn(0); inside_flag = 0; for (j = 1; j <= m_num_points; ++j) { yflag1 = (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) { // 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 ( ((vty1-ty) * (vtx0-vtx1) >= (vtx1-tx) * (vty0-vty1)) == yflag1 ) { inside_flag ^= 1; } } // Move to the next pair of vertices, retaining info as possible. yflag0 = yflag1; vtx0 = vtx1; vty0 = vty1; unsigned k = (j >= m_num_points) ? j - m_num_points : j; vtx1 = xn(k); vty1 = yn(k); } return inside_flag != 0; } }