//---------------------------------------------------------------------------- // Anti-Grain Geometry - Version 2.3 // Copyright (C) 2002-2005 Maxim Shemanarev (http://www.antigrain.com) // // 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 // //---------------------------------------------------------------------------- #include #include #include "agg_path_storage.h" #include "agg_math.h" #include "agg_bezier_arc.h" namespace agg { //------------------------------------------------------------------------ path_storage::~path_storage() { if(m_total_blocks) { double** coord_blk = m_coord_blocks + m_total_blocks - 1; while(m_total_blocks--) { delete [] *coord_blk; --coord_blk; } delete [] m_coord_blocks; } } //------------------------------------------------------------------------ path_storage::path_storage() : m_total_vertices(0), m_total_blocks(0), m_max_blocks(0), m_coord_blocks(0), m_cmd_blocks(0), m_iterator(0) { } //------------------------------------------------------------------------ path_storage::path_storage(const path_storage& ps) : m_total_vertices(0), m_total_blocks(0), m_max_blocks(0), m_coord_blocks(0), m_cmd_blocks(0), m_iterator(0) { copy_from(ps); } //------------------------------------------------------------------------ void path_storage::remove_all() { m_total_vertices = 0; m_iterator = 0; } //------------------------------------------------------------------------ void path_storage::copy_from(const path_storage& ps) { remove_all(); unsigned i; for(i = 0; i < ps.total_vertices(); i++) { double x, y; unsigned cmd = ps.vertex(i, &x, &y); add_vertex(x, y, cmd); } } //------------------------------------------------------------------------ void path_storage::allocate_block(unsigned nb) { if(nb >= m_max_blocks) { double** new_coords = new double* [(m_max_blocks + block_pool) * 2]; unsigned char** new_cmds = (unsigned char**)(new_coords + m_max_blocks + block_pool); if(m_coord_blocks) { memcpy(new_coords, m_coord_blocks, m_max_blocks * sizeof(double*)); memcpy(new_cmds, m_cmd_blocks, m_max_blocks * sizeof(unsigned char*)); delete [] m_coord_blocks; } m_coord_blocks = new_coords; m_cmd_blocks = new_cmds; m_max_blocks += block_pool; } m_coord_blocks[nb] = new double [block_size * 2 + block_size / (sizeof(double) / sizeof(unsigned char))]; m_cmd_blocks[nb] = (unsigned char*)(m_coord_blocks[nb] + block_size * 2); m_total_blocks++; } //------------------------------------------------------------------------ void path_storage::rewind(unsigned path_id) { m_iterator = path_id; } //------------------------------------------------------------------------ void path_storage::arc_to(double rx, double ry, double angle, bool large_arc_flag, bool sweep_flag, double x, double y) { if(m_total_vertices && is_vertex(command(m_total_vertices - 1))) { const double epsilon = 1e-30; double x0 = 0.0; double y0 = 0.0; last_vertex(&x0, &y0); rx = fabs(rx); ry = fabs(ry); // Ensure radii are valid //------------------------- if(rx < epsilon || ry < epsilon) { line_to(x, y); return; } if(calc_distance(x0, y0, x, y) < epsilon) { // If the endpoints (x, y) and (x0, y0) are identical, then this // is equivalent to omitting the elliptical arc segment entirely. return; } bezier_arc_svg a(x0, y0, rx, ry, angle, large_arc_flag, sweep_flag, x, y); if(a.radii_ok()) { add_path(a, 0, true); } else { line_to(x, y); } } else { move_to(x, y); } } //------------------------------------------------------------------------ void path_storage::arc_rel(double rx, double ry, double angle, bool large_arc_flag, bool sweep_flag, double dx, double dy) { rel_to_abs(&dx, &dy); arc_to(rx, ry, angle, large_arc_flag, sweep_flag, dx, dy); } //------------------------------------------------------------------------ void path_storage::curve3(double x_ctrl, double y_ctrl, double x_to, double y_to) { add_vertex(x_ctrl, y_ctrl, path_cmd_curve3); add_vertex(x_to, y_to, path_cmd_curve3); } //------------------------------------------------------------------------ void path_storage::curve3_rel(double dx_ctrl, double dy_ctrl, double dx_to, double dy_to) { 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); } //------------------------------------------------------------------------ void path_storage::curve3(double x_to, double y_to) { double x0 = 0; double y0 = 0; if(is_vertex(last_vertex(&x0, &y0))) { double x_ctrl = 0; double y_ctrl = 0; unsigned cmd = prev_vertex(&x_ctrl, &y_ctrl); if(is_curve(cmd)) { x_ctrl = x0 + x0 - x_ctrl; y_ctrl = y0 + y0 - y_ctrl; } else { x_ctrl = x0; y_ctrl = y0; } curve3(x_ctrl, y_ctrl, x_to, y_to); } } //------------------------------------------------------------------------ void path_storage::curve3_rel(double dx_to, double dy_to) { rel_to_abs(&dx_to, &dy_to); curve3(dx_to, dy_to); } //------------------------------------------------------------------------ void path_storage::curve4(double x_ctrl1, double y_ctrl1, double x_ctrl2, double y_ctrl2, double x_to, double y_to) { 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); } //------------------------------------------------------------------------ void path_storage::curve4_rel(double dx_ctrl1, double dy_ctrl1, double dx_ctrl2, double dy_ctrl2, double dx_to, double dy_to) { 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); } //------------------------------------------------------------------------ void path_storage::curve4(double x_ctrl2, double y_ctrl2, double x_to, double y_to) { double x0 = 0; double y0 = 0; if(is_vertex(last_vertex(&x0, &y0))) { double x_ctrl1 = 0; double y_ctrl1 = 0; unsigned cmd = prev_vertex(&x_ctrl1, &y_ctrl1); if(is_curve(cmd)) { x_ctrl1 = x0 + x0 - x_ctrl1; y_ctrl1 = y0 + y0 - y_ctrl1; } else { x_ctrl1 = x0; y_ctrl1 = y0; } curve4(x_ctrl1, y_ctrl1, x_ctrl2, y_ctrl2, x_to, y_to); } } //------------------------------------------------------------------------ void path_storage::curve4_rel(double dx_ctrl2, double dy_ctrl2, double dx_to, double dy_to) { rel_to_abs(&dx_ctrl2, &dy_ctrl2); rel_to_abs(&dx_to, &dy_to); curve4(dx_ctrl2, dy_ctrl2, dx_to, dy_to); } //------------------------------------------------------------------------ void path_storage::end_poly(unsigned flags) { if(m_total_vertices) { if(is_vertex(command(m_total_vertices - 1))) { add_vertex(0.0, 0.0, path_cmd_end_poly | flags); } } } //------------------------------------------------------------------------ unsigned path_storage::start_new_path() { if(m_total_vertices) { if(!is_stop(command(m_total_vertices - 1))) { add_vertex(0.0, 0.0, path_cmd_stop); } } return m_total_vertices; } //------------------------------------------------------------------------ void path_storage::add_poly(const double* vertices, unsigned num, bool solid_path, unsigned end_flags) { if(num) { if(!solid_path) { move_to(vertices[0], vertices[1]); vertices += 2; --num; } while(num--) { line_to(vertices[0], vertices[1]); vertices += 2; } if(end_flags) end_poly(end_flags); } } //------------------------------------------------------------------------ unsigned path_storage::perceive_polygon_orientation(unsigned idx, double xs, double ys, unsigned* orientation) { unsigned i; double sum = 0.0; double x, y, xn, yn; x = xs; y = ys; for(i = idx; i < m_total_vertices; ++i) { if(is_next_poly(vertex(i, &xn, &yn))) break; sum += x * yn - y * xn; x = xn; y = yn; } if(i > idx) sum += x * ys - y * xs; *orientation = path_flags_none; if(sum != 0.0) { *orientation = (sum < 0.0) ? path_flags_cw : path_flags_ccw; } return i; } //------------------------------------------------------------------------ void path_storage::reverse_polygon(unsigned _start, unsigned _end) { unsigned i; unsigned tmp_cmd = command(_start); // Shift all commands to one position for(i = _start; i < _end; i++) { modify_command(i, command(i + 1)); } // Assign starting command to the ending command modify_command(_end, tmp_cmd); // Reverse the polygon while(_end > _start) { unsigned start_nb = _start >> block_shift; unsigned end_nb = _end >> block_shift; double* start_ptr = m_coord_blocks[start_nb] + ((_start & block_mask) << 1); double* end_ptr = m_coord_blocks[end_nb] + ((_end & block_mask) << 1); double tmp_xy; tmp_xy = *start_ptr; *start_ptr++ = *end_ptr; *end_ptr++ = tmp_xy; tmp_xy = *start_ptr; *start_ptr = *end_ptr; *end_ptr = tmp_xy; tmp_cmd = m_cmd_blocks[start_nb][_start & block_mask]; m_cmd_blocks[start_nb][_start & block_mask] = m_cmd_blocks[end_nb][_end & block_mask]; m_cmd_blocks[end_nb][_end & block_mask] = (unsigned char)tmp_cmd; ++_start; --_end; } } //------------------------------------------------------------------------ unsigned path_storage::arrange_orientations(unsigned path_id, path_flags_e new_orientation) { unsigned _end = m_total_vertices; if(m_total_vertices && new_orientation != path_flags_none) { unsigned start = path_id; double xs, ys; unsigned cmd = vertex(start, &xs, &ys); unsigned inc = 0; for(;;) { unsigned orientation; _end = perceive_polygon_orientation(start + 1, xs, ys, &orientation); if(_end > start + 2 && orientation && orientation != unsigned(new_orientation)) { reverse_polygon(start + inc, _end - 1); } if(_end >= m_total_vertices) break; cmd = command(_end); if(is_stop(cmd)) { ++_end; break; } if(is_end_poly(cmd)) { inc = 1; modify_command(_end, set_orientation(cmd, new_orientation)); } else { cmd = vertex(++_end, &xs, &ys); inc = 0; } start = _end; } } return _end; } //------------------------------------------------------------------------ void path_storage::arrange_orientations_all_paths(path_flags_e new_orientation) { if(new_orientation != path_flags_none) { unsigned start = 0; while(start < m_total_vertices) { start = arrange_orientations(start, new_orientation); } } } //------------------------------------------------------------------------ void path_storage::flip_x(double x1, double x2) { unsigned i; double x, y; for(i = 0; i < m_total_vertices; i++) { unsigned cmd = vertex(i, &x, &y); if(is_vertex(cmd)) { modify_vertex(i, x2 - x + x1, y); } } } //------------------------------------------------------------------------ void path_storage::flip_y(double y1, double y2) { unsigned i; double x, y; for(i = 0; i < m_total_vertices; i++) { unsigned cmd = vertex(i, &x, &y); if(is_vertex(cmd)) { modify_vertex(i, x, y2 - y + y1); } } } }