📄 agg_bezier_arc.cpp
字号:
//----------------------------------------------------------------------------// 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//----------------------------------------------------------------------------//// Arc generator. Produces at most 4 consecutive cubic bezier curves, i.e., // 4, 7, 10, or 13 vertices.////----------------------------------------------------------------------------#include <math.h>#include "agg_bezier_arc.h"namespace agg{ //------------------------------------------------------------arc_to_bezier void arc_to_bezier(double cx, double cy, double rx, double ry, double start_angle, double sweep_angle, double* curve) { double x0 = cos(sweep_angle / 2.0); double y0 = sin(sweep_angle / 2.0); double tx = (1.0 - x0) * 4.0 / 3.0; double ty = y0 - tx * x0 / y0; double px[4]; double py[4]; 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; double sn = sin(start_angle + sweep_angle / 2.0); double cs = cos(start_angle + sweep_angle / 2.0); unsigned i; for(i = 0; i < 4; i++) { curve[i * 2] = cx + rx * (px[i] * cs - py[i] * sn); curve[i * 2 + 1] = cy + ry * (px[i] * sn + py[i] * cs); } } //------------------------------------------------------------------------ void bezier_arc::init(double x, double y, double rx, double ry, double start_angle, double sweep_angle) { start_angle = fmod(start_angle, 2.0 * pi); if(sweep_angle >= 2.0 * pi) sweep_angle = 2.0 * pi; if(sweep_angle <= -2.0 * pi) sweep_angle = -2.0 * pi; double total_sweep = 0.0; double local_sweep = 0.0; m_num_vertices = 2; bool done = false; do { if(sweep_angle < 0.0) { local_sweep = -pi * 0.5; total_sweep -= pi * 0.5; if(total_sweep <= sweep_angle) { local_sweep = sweep_angle - (total_sweep + pi * 0.5); done = true; } } else { local_sweep = pi * 0.5; total_sweep += pi * 0.5; if(total_sweep >= sweep_angle) { local_sweep = sweep_angle - (total_sweep - pi * 0.5); done = true; } } arc_to_bezier(x, y, rx, ry, start_angle, local_sweep, m_vertices + m_num_vertices - 2); m_num_vertices += 6; start_angle += local_sweep; } while(!done && m_num_vertices < 26); } //-------------------------------------------------------------------- void bezier_arc_svg::init(double x0, double y0, double rx, double ry, double angle, bool large_arc_flag, bool sweep_flag, double x2, double y2) { m_radii_ok = true; if(rx < 0.0) rx = -rx; if(ry < 0.0) ry = -rx; // Calculate the middle point between // the current and the final points //------------------------ double dx2 = (x0 - x2) / 2.0; double dy2 = (y0 - y2) / 2.0; // Convert angle from degrees to radians //------------------------ double cos_a = cos(angle); double sin_a = sin(angle); // Calculate (x1, y1) //------------------------ double x1 = cos_a * dx2 + sin_a * dy2; double y1 = -sin_a * dx2 + cos_a * dy2; // Ensure radii are large enough //------------------------ double prx = rx * rx; double pry = ry * ry; double px1 = x1 * x1; double py1 = y1 * y1; // Check that radii are large enough //------------------------ double radii_check = px1/prx + py1/pry; if(radii_check > 1.0) { rx = sqrt(radii_check) * rx; ry = sqrt(radii_check) * ry; prx = rx * rx; pry = ry * ry; if(radii_check > 10.0) m_radii_ok = false; } // Calculate (cx1, cy1) //------------------------ double sign = (large_arc_flag == sweep_flag) ? -1.0 : 1.0; double sq = (prx*pry - prx*py1 - pry*px1) / (prx*py1 + pry*px1); double coef = sign * sqrt((sq < 0) ? 0 : sq); double cx1 = coef * ((rx * y1) / ry); double cy1 = coef * -((ry * x1) / rx); // // Calculate (cx, cy) from (cx1, cy1) //------------------------ double sx2 = (x0 + x2) / 2.0; double sy2 = (y0 + y2) / 2.0; double cx = sx2 + (cos_a * cx1 - sin_a * cy1); double cy = sy2 + (sin_a * cx1 + cos_a * cy1); // Calculate the start_angle (angle1) and the sweep_angle (dangle) //------------------------ double ux = (x1 - cx1) / rx; double uy = (y1 - cy1) / ry; double vx = (-x1 - cx1) / rx; double vy = (-y1 - cy1) / ry; double p, n; // Calculate the angle start //------------------------ n = sqrt(ux*ux + uy*uy); p = ux; // (1 * ux) + (0 * uy) sign = (uy < 0) ? -1.0 : 1.0; double v = p / n; if(v < -1.0) v = -1.0; if(v > 1.0) v = 1.0; double start_angle = sign * acos(v); // Calculate the sweep angle //------------------------ n = sqrt((ux*ux + uy*uy) * (vx*vx + vy*vy)); p = ux * vx + uy * vy; sign = (ux * vy - uy * vx < 0) ? -1.0 : 1.0; v = p / n; if(v < -1.0) v = -1.0; if(v > 1.0) v = 1.0; double sweep_angle = sign * acos(v); if(!sweep_flag && sweep_angle > 0) { sweep_angle -= pi * 2.0; } else if (sweep_flag && sweep_angle < 0) { sweep_angle += pi * 2.0; } // We can now build and transform the resulting arc //------------------------ m_arc.init(0.0, 0.0, rx, ry, start_angle, sweep_angle); trans_affine mtx = trans_affine_rotation(angle); mtx *= trans_affine_translation(cx, cy); for(unsigned i = 2; i < m_arc.num_vertices()-2; i += 2) { mtx.transform(m_arc.vertices() + i, m_arc.vertices() + i + 1); } // We must make sure that the starting and ending points // exactly coincide with the initial (x0,y0) and (x2,y2) m_arc.vertices()[0] = x0; m_arc.vertices()[1] = y0; if(m_arc.num_vertices() > 2) { m_arc.vertices()[m_arc.num_vertices() - 2] = x2; m_arc.vertices()[m_arc.num_vertices() - 1] = y2; } }}
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -