summaryrefslogtreecommitdiff
path: root/agg/inc/agg_math_stroke.h
diff options
context:
space:
mode:
Diffstat (limited to 'agg/inc/agg_math_stroke.h')
-rwxr-xr-xagg/inc/agg_math_stroke.h596
1 files changed, 391 insertions, 205 deletions
diff --git a/agg/inc/agg_math_stroke.h b/agg/inc/agg_math_stroke.h
index 84d83738a9cd..8cec034e11d2 100755
--- a/agg/inc/agg_math_stroke.h
+++ b/agg/inc/agg_math_stroke.h
@@ -1,5 +1,5 @@
//----------------------------------------------------------------------------
-// Anti-Grain Geometry - Version 2.3
+// Anti-Grain Geometry - Version 2.4
// Copyright (C) 2002-2005 Maxim Shemanarev (http://www.antigrain.com)
//
// Permission to copy, use, modify, sell and distribute this software
@@ -36,297 +36,483 @@ namespace agg
//------------------------------------------------------------line_join_e
enum line_join_e
{
- miter_join,
- miter_join_revert,
- round_join,
- bevel_join
+ miter_join = 0,
+ miter_join_revert = 1,
+ round_join = 2,
+ bevel_join = 3,
+ miter_join_round = 4
};
- // Minimal angle to calculate round joins, less than 0.1 degree.
- const double stroke_theta = 0.001; //----stroke_theta
+ //-----------------------------------------------------------inner_join_e
+ enum inner_join_e
+ {
+ inner_bevel,
+ inner_miter,
+ inner_jag,
+ inner_round
+ };
- //--------------------------------------------------------stroke_calc_arc
- template<class VertexConsumer>
- void stroke_calc_arc(VertexConsumer& out_vertices,
- double x, double y,
- double dx1, double dy1,
- double dx2, double dy2,
- double width,
- double approximation_scale)
+ //------------------------------------------------------------math_stroke
+ template<class VertexConsumer> class math_stroke
{
+ public:
typedef typename VertexConsumer::value_type coord_type;
- //// Check if we actually need the arc (this optimization works bad)
- ////-----------------
- //double dd = calc_distance(dx1, dy1, dx2, dy2);
- //if(dd < 1.0/approximation_scale)
- //{
- // out_vertices.add(coord_type(x + dx1, y + dy1));
- // if(dd > 0.25/approximation_scale)
- // {
- // out_vertices.add(coord_type(x + dx2, y + dy2));
- // }
- // return;
- //}
-
- double a1 = atan2(dy1, dx1);
- double a2 = atan2(dy2, dx2);
- double da = a1 - a2;
+ math_stroke();
+
+ void line_cap(line_cap_e lc) { m_line_cap = lc; }
+ void line_join(line_join_e lj) { m_line_join = lj; }
+ void inner_join(inner_join_e ij) { m_inner_join = ij; }
+
+ line_cap_e line_cap() const { return m_line_cap; }
+ line_join_e line_join() const { return m_line_join; }
+ inner_join_e inner_join() const { return m_inner_join; }
+
+ void width(double w);
+ void miter_limit(double ml) { m_miter_limit = ml; }
+ void miter_limit_theta(double t);
+ void inner_miter_limit(double ml) { m_inner_miter_limit = ml; }
+ void approximation_scale(double as) { m_approx_scale = as; }
+
+ double width() const { return m_width * 2.0; }
+ double miter_limit() const { return m_miter_limit; }
+ double inner_miter_limit() const { return m_inner_miter_limit; }
+ double approximation_scale() const { return m_approx_scale; }
+
+ void calc_cap(VertexConsumer& vc,
+ const vertex_dist& v0,
+ const vertex_dist& v1,
+ double len);
+
+ void calc_join(VertexConsumer& vc,
+ const vertex_dist& v0,
+ const vertex_dist& v1,
+ const vertex_dist& v2,
+ double len1,
+ double len2);
+
+ private:
+ AGG_INLINE void add_vertex(VertexConsumer& vc, double x, double y)
+ {
+ vc.add(coord_type(x, y));
+ }
+
+ void calc_arc(VertexConsumer& vc,
+ double x, double y,
+ double dx1, double dy1,
+ double dx2, double dy2);
+
+ void calc_miter(VertexConsumer& vc,
+ const vertex_dist& v0,
+ const vertex_dist& v1,
+ const vertex_dist& v2,
+ double dx1, double dy1,
+ double dx2, double dy2,
+ line_join_e lj,
+ double mlimit,
+ double dbevel);
+
+ double m_width;
+ double m_width_abs;
+ double m_width_eps;
+ int m_width_sign;
+ double m_miter_limit;
+ double m_inner_miter_limit;
+ double m_approx_scale;
+ line_cap_e m_line_cap;
+ line_join_e m_line_join;
+ inner_join_e m_inner_join;
+ };
- if(fabs(da) < stroke_theta)
+ //-----------------------------------------------------------------------
+ template<class VC> math_stroke<VC>::math_stroke() :
+ m_width(0.5),
+ m_width_abs(0.5),
+ m_width_eps(0.5/1024.0),
+ m_width_sign(1),
+ m_miter_limit(4.0),
+ m_inner_miter_limit(1.01),
+ m_approx_scale(1.0),
+ m_line_cap(butt_cap),
+ m_line_join(miter_join),
+ m_inner_join(inner_miter)
+ {
+ }
+
+ //-----------------------------------------------------------------------
+ template<class VC> void math_stroke<VC>::width(double w)
+ {
+ m_width = w * 0.5;
+ if(m_width < 0)
+ {
+ m_width_abs = -m_width;
+ m_width_sign = -1;
+ }
+ else
{
- out_vertices.add(coord_type((x + x + dx1 + dx2) * 0.5,
- (y + y + dy1 + dy2) * 0.5));
- return;
+ m_width_abs = m_width;
+ m_width_sign = 1;
}
+ m_width_eps = m_width / 1024.0;
+ }
+
+ //-----------------------------------------------------------------------
+ template<class VC> void math_stroke<VC>::miter_limit_theta(double t)
+ {
+ m_miter_limit = 1.0 / sin(t * 0.5) ;
+ }
+
+ //-----------------------------------------------------------------------
+ template<class VC>
+ void math_stroke<VC>::calc_arc(VC& vc,
+ double x, double y,
+ double dx1, double dy1,
+ double dx2, double dy2)
+ {
+ double a1 = atan2(dy1 * m_width_sign, dx1 * m_width_sign);
+ double a2 = atan2(dy2 * m_width_sign, dx2 * m_width_sign);
+ double da = a1 - a2;
+ int i, n;
- bool ccw = da > 0.0 && da < pi;
+ da = acos(m_width_abs / (m_width_abs + 0.125 / m_approx_scale)) * 2;
- if(width < 0) width = -width;
- da = fabs(1.0 / (width * approximation_scale));
- if(!ccw)
+ add_vertex(vc, x + dx1, y + dy1);
+ if(m_width_sign > 0)
{
if(a1 > a2) a2 += 2 * pi;
- while(a1 < a2)
+ n = int((a2 - a1) / da);
+ da = (a2 - a1) / (n + 1);
+ a1 += da;
+ for(i = 0; i < n; i++)
{
- out_vertices.add(coord_type(x + cos(a1) * width, y + sin(a1) * width));
+ add_vertex(vc, x + cos(a1) * m_width, y + sin(a1) * m_width);
a1 += da;
}
}
else
{
if(a1 < a2) a2 -= 2 * pi;
- while(a1 > a2)
+ n = int((a1 - a2) / da);
+ da = (a1 - a2) / (n + 1);
+ a1 -= da;
+ for(i = 0; i < n; i++)
{
- out_vertices.add(coord_type(x + cos(a1) * width, y + sin(a1) * width));
+ add_vertex(vc, x + cos(a1) * m_width, y + sin(a1) * m_width);
a1 -= da;
}
}
- out_vertices.add(coord_type(x + dx2, y + dy2));
+ add_vertex(vc, x + dx2, y + dy2);
}
-
-
- //-------------------------------------------------------stroke_calc_miter
- template<class VertexConsumer>
- void stroke_calc_miter(VertexConsumer& out_vertices,
- const vertex_dist& v0,
- const vertex_dist& v1,
- const vertex_dist& v2,
- double dx1, double dy1,
- double dx2, double dy2,
- double width,
- bool revert_flag,
- double miter_limit)
+ //-----------------------------------------------------------------------
+ template<class VC>
+ void math_stroke<VC>::calc_miter(VC& vc,
+ const vertex_dist& v0,
+ const vertex_dist& v1,
+ const vertex_dist& v2,
+ double dx1, double dy1,
+ double dx2, double dy2,
+ line_join_e lj,
+ double mlimit,
+ double dbevel)
{
- typedef typename VertexConsumer::value_type coord_type;
-
- double xi = v1.x;
- double yi = v1.y;
-
- if(!calc_intersection(v0.x + dx1, v0.y - dy1,
- v1.x + dx1, v1.y - dy1,
- v1.x + dx2, v1.y - dy2,
- v2.x + dx2, v2.y - dy2,
- &xi, &yi))
+ double xi = v1.x;
+ double yi = v1.y;
+ double di = 1;
+ double lim = m_width_abs * mlimit;
+ bool miter_limit_exceeded = true; // Assume the worst
+ bool intersection_failed = true; // Assume the worst
+
+ if(calc_intersection(v0.x + dx1, v0.y - dy1,
+ v1.x + dx1, v1.y - dy1,
+ v1.x + dx2, v1.y - dy2,
+ v2.x + dx2, v2.y - dy2,
+ &xi, &yi))
{
- // The calculation didn't succeed, most probaly
- // the three points lie one straight line
+ // Calculation of the intersection succeeded
+ //---------------------
+ di = calc_distance(v1.x, v1.y, xi, yi);
+ if(di <= lim)
+ {
+ // Inside the miter limit
+ //---------------------
+ add_vertex(vc, xi, yi);
+ miter_limit_exceeded = false;
+ }
+ intersection_failed = false;
+ }
+ else
+ {
+ // Calculation of the intersection failed, most probably
+ // the three points lie one straight line.
+ // First check if v0 and v2 lie on the opposite sides of vector:
+ // (v1.x, v1.y) -> (v1.x+dx1, v1.y-dy1), that is, the perpendicular
+ // to the line determined by vertices v0 and v1.
+ // This condition determines whether the next line segments continues
+ // the previous one or goes back.
//----------------
- if(calc_distance(dx1, -dy1, dx2, -dy2) < width * 0.025)
+ double x2 = v1.x + dx1;
+ double y2 = v1.y - dy1;
+ if((cross_product(v0.x, v0.y, v1.x, v1.y, x2, y2) < 0.0) ==
+ (cross_product(v1.x, v1.y, v2.x, v2.y, x2, y2) < 0.0))
{
// This case means that the next segment continues
// the previous one (straight line)
//-----------------
- out_vertices.add(coord_type(v1.x + dx1, v1.y - dy1));
- }
- else
- {
- // This case means that the next segment goes back
- //-----------------
- if(revert_flag)
- {
- out_vertices.add(coord_type(v1.x + dx1, v1.y - dy1));
- out_vertices.add(coord_type(v1.x + dx2, v1.y - dy2));
- }
- else
- {
- // If no miter-revert, calcuate new dx1, dy1, dx2, dy2
- out_vertices.add(coord_type(v1.x + dx1 + dy1 * miter_limit,
- v1.y - dy1 + dx1 * miter_limit));
- out_vertices.add(coord_type(v1.x + dx2 - dy2 * miter_limit,
- v1.y - dy2 - dx2 * miter_limit));
- }
+ add_vertex(vc, v1.x + dx1, v1.y - dy1);
+ miter_limit_exceeded = false;
}
}
- else
+
+ if(miter_limit_exceeded)
{
- double d1 = calc_distance(v1.x, v1.y, xi, yi);
- double lim = width * miter_limit;
- if(d1 > lim)
+ // Miter limit exceeded
+ //------------------------
+ switch(lj)
{
- // Miter limit exceeded
- //------------------------
- if(revert_flag)
+ case miter_join_revert:
+ // For the compatibility with SVG, PDF, etc,
+ // we use a simple bevel join instead of
+ // "smart" bevel
+ //-------------------
+ add_vertex(vc, v1.x + dx1, v1.y - dy1);
+ add_vertex(vc, v1.x + dx2, v1.y - dy2);
+ break;
+
+ case miter_join_round:
+ calc_arc(vc, v1.x, v1.y, dx1, -dy1, dx2, -dy2);
+ break;
+
+ default:
+ // If no miter-revert, calculate new dx1, dy1, dx2, dy2
+ //----------------
+ if(intersection_failed)
{
- // For the compatibility with SVG, PDF, etc,
- // we use a simple bevel join instead of
- // "smart" bevel
- //-------------------
- out_vertices.add(coord_type(v1.x + dx1, v1.y - dy1));
- out_vertices.add(coord_type(v1.x + dx2, v1.y - dy2));
+ mlimit *= m_width_sign;
+ add_vertex(vc, v1.x + dx1 + dy1 * mlimit,
+ v1.y - dy1 + dx1 * mlimit);
+ add_vertex(vc, v1.x + dx2 - dy2 * mlimit,
+ v1.y - dy2 - dx2 * mlimit);
}
else
{
- // Smart bevel that cuts the miter at the limit point
- //-------------------
- d1 = lim / d1;
double x1 = v1.x + dx1;
double y1 = v1.y - dy1;
double x2 = v1.x + dx2;
double y2 = v1.y - dy2;
-
- x1 += (xi - x1) * d1;
- y1 += (yi - y1) * d1;
- x2 += (xi - x2) * d1;
- y2 += (yi - y2) * d1;
- out_vertices.add(coord_type(x1, y1));
- out_vertices.add(coord_type(x2, y2));
+ di = (lim - dbevel) / (di - dbevel);
+ add_vertex(vc, x1 + (xi - x1) * di,
+ y1 + (yi - y1) * di);
+ add_vertex(vc, x2 + (xi - x2) * di,
+ y2 + (yi - y2) * di);
}
- }
- else
- {
- // Inside the miter limit
- //---------------------
- out_vertices.add(coord_type(xi, yi));
+ break;
}
}
}
-
-
-
-
-
//--------------------------------------------------------stroke_calc_cap
- template<class VertexConsumer>
- void stroke_calc_cap(VertexConsumer& out_vertices,
- const vertex_dist& v0,
- const vertex_dist& v1,
- double len,
- line_cap_e line_cap,
- double width,
- double approximation_scale)
+ template<class VC>
+ void math_stroke<VC>::calc_cap(VC& vc,
+ const vertex_dist& v0,
+ const vertex_dist& v1,
+ double len)
{
- typedef typename VertexConsumer::value_type coord_type;
+ vc.remove_all();
- out_vertices.remove_all();
-
- double dx1 = width * (v1.y - v0.y) / len;
- double dy1 = width * (v1.x - v0.x) / len;
+ double dx1 = (v1.y - v0.y) / len;
+ double dy1 = (v1.x - v0.x) / len;
double dx2 = 0;
double dy2 = 0;
- if(line_cap == square_cap)
- {
- dx2 = dy1;
- dy2 = dx1;
- }
+ dx1 *= m_width;
+ dy1 *= m_width;
- if(line_cap == round_cap)
+ if(m_line_cap != round_cap)
{
- double a1 = atan2(dy1, -dx1);
- double a2 = a1 + pi;
- double da = fabs(1.0 / (width * approximation_scale));
- while(a1 < a2)
+ if(m_line_cap == square_cap)
{
- out_vertices.add(coord_type(v0.x + cos(a1) * width,
- v0.y + sin(a1) * width));
- a1 += da;
+ dx2 = dy1 * m_width_sign;
+ dy2 = dx1 * m_width_sign;
}
- out_vertices.add(coord_type(v0.x + dx1, v0.y - dy1));
+ add_vertex(vc, v0.x - dx1 - dx2, v0.y + dy1 - dy2);
+ add_vertex(vc, v0.x + dx1 - dx2, v0.y - dy1 - dy2);
}
else
{
- out_vertices.add(coord_type(v0.x - dx1 - dx2, v0.y + dy1 - dy2));
- out_vertices.add(coord_type(v0.x + dx1 - dx2, v0.y - dy1 - dy2));
+ double da = acos(m_width_abs / (m_width_abs + 0.125 / m_approx_scale)) * 2;
+ double a1;
+ int i;
+ int n = int(pi / da);
+
+ da = pi / (n + 1);
+ add_vertex(vc, v0.x - dx1, v0.y + dy1);
+ if(m_width_sign > 0)
+ {
+ a1 = atan2(dy1, -dx1);
+ a1 += da;
+ for(i = 0; i < n; i++)
+ {
+ add_vertex(vc, v0.x + cos(a1) * m_width,
+ v0.y + sin(a1) * m_width);
+ a1 += da;
+ }
+ }
+ else
+ {
+ a1 = atan2(-dy1, dx1);
+ a1 -= da;
+ for(i = 0; i < n; i++)
+ {
+ add_vertex(vc, v0.x + cos(a1) * m_width,
+ v0.y + sin(a1) * m_width);
+ a1 -= da;
+ }
+ }
+ add_vertex(vc, v0.x + dx1, v0.y - dy1);
}
}
-
-
- //-------------------------------------------------------stroke_calc_join
- template<class VertexConsumer>
- void stroke_calc_join(VertexConsumer& out_vertices,
- const vertex_dist& v0,
- const vertex_dist& v1,
- const vertex_dist& v2,
- double len1,
- double len2,
- double width,
- line_join_e line_join,
- line_join_e inner_line_join,
- double miter_limit,
- double inner_miter_limit,
- double approximation_scale)
+ //-----------------------------------------------------------------------
+ template<class VC>
+ void math_stroke<VC>::calc_join(VC& vc,
+ const vertex_dist& v0,
+ const vertex_dist& v1,
+ const vertex_dist& v2,
+ double len1,
+ double len2)
{
- typedef typename VertexConsumer::value_type coord_type;
-
- double dx1, dy1, dx2, dy2;
-
- dx1 = width * (v1.y - v0.y) / len1;
- dy1 = width * (v1.x - v0.x) / len1;
-
- dx2 = width * (v2.y - v1.y) / len2;
- dy2 = width * (v2.x - v1.x) / len2;
+ double dx1 = m_width * (v1.y - v0.y) / len1;
+ double dy1 = m_width * (v1.x - v0.x) / len1;
+ double dx2 = m_width * (v2.y - v1.y) / len2;
+ double dy2 = m_width * (v2.x - v1.x) / len2;
- out_vertices.remove_all();
+ vc.remove_all();
- if(calc_point_location(v0.x, v0.y, v1.x, v1.y, v2.x, v2.y) > 0.0)
+ double cp = cross_product(v0.x, v0.y, v1.x, v1.y, v2.x, v2.y);
+ if(cp != 0 && (cp > 0) == (m_width > 0))
{
// Inner join
//---------------
- stroke_calc_miter(out_vertices,
- v0, v1, v2, dx1, dy1, dx2, dy2,
- width,
- inner_line_join == miter_join_revert,
- inner_miter_limit);
+ double limit = ((len1 < len2) ? len1 : len2) / m_width_abs;
+ if(limit < m_inner_miter_limit)
+ {
+ limit = m_inner_miter_limit;
+ }
+
+ switch(m_inner_join)
+ {
+ default: // inner_bevel
+ add_vertex(vc, v1.x + dx1, v1.y - dy1);
+ add_vertex(vc, v1.x + dx2, v1.y - dy2);
+ break;
+
+ case inner_miter:
+ calc_miter(vc,
+ v0, v1, v2, dx1, dy1, dx2, dy2,
+ miter_join_revert,
+ limit, 0);
+ break;
+
+ case inner_jag:
+ case inner_round:
+ cp = (dx1-dx2) * (dx1-dx2) + (dy1-dy2) * (dy1-dy2);
+ if(cp < len1 * len1 && cp < len2 * len2)
+ {
+ calc_miter(vc,
+ v0, v1, v2, dx1, dy1, dx2, dy2,
+ miter_join_revert,
+ limit, 0);
+ }
+ else
+ {
+ if(m_inner_join == inner_jag)
+ {
+ add_vertex(vc, v1.x + dx1, v1.y - dy1);
+ add_vertex(vc, v1.x, v1.y );
+ add_vertex(vc, v1.x + dx2, v1.y - dy2);
+ }
+ else
+ {
+ add_vertex(vc, v1.x + dx1, v1.y - dy1);
+ add_vertex(vc, v1.x, v1.y );
+ calc_arc(vc, v1.x, v1.y, dx2, -dy2, dx1, -dy1);
+ add_vertex(vc, v1.x, v1.y );
+ add_vertex(vc, v1.x + dx2, v1.y - dy2);
+ }
+ }
+ break;
+ }
}
else
{
// Outer join
//---------------
- switch(line_join)
+
+ // Calculate the distance between v1 and
+ // the central point of the bevel line segment
+ //---------------
+ double dx = (dx1 + dx2) / 2;
+ double dy = (dy1 + dy2) / 2;
+ double dbevel = sqrt(dx * dx + dy * dy);
+
+ if(m_line_join == round_join || m_line_join == bevel_join)
{
- case miter_join:
- stroke_calc_miter(out_vertices,
- v0, v1, v2, dx1, dy1, dx2, dy2,
- width,
- false,
- miter_limit);
- break;
+ // This is an optimization that reduces the number of points
+ // in cases of almost collinear segments. If there's no
+ // visible difference between bevel and miter joins we'd rather
+ // use miter join because it adds only one point instead of two.
+ //
+ // Here we calculate the middle point between the bevel points
+ // and then, the distance between v1 and this middle point.
+ // At outer joins this distance always less than stroke width,
+ // because it's actually the height of an isosceles triangle of
+ // v1 and its two bevel points. If the difference between this
+ // width and this value is small (no visible bevel) we can
+ // add just one point.
+ //
+ // The constant in the expression makes the result approximately
+ // the same as in round joins and caps. You can safely comment
+ // out this entire "if".
+ //-------------------
+ if(m_approx_scale * (m_width_abs - dbevel) < m_width_eps)
+ {
+ if(calc_intersection(v0.x + dx1, v0.y - dy1,
+ v1.x + dx1, v1.y - dy1,
+ v1.x + dx2, v1.y - dy2,
+ v2.x + dx2, v2.y - dy2,
+ &dx, &dy))
+ {
+ add_vertex(vc, dx, dy);
+ }
+ else
+ {
+ add_vertex(vc, v1.x + dx1, v1.y - dy1);
+ }
+ return;
+ }
+ }
+ switch(m_line_join)
+ {
+ case miter_join:
case miter_join_revert:
- stroke_calc_miter(out_vertices,
- v0, v1, v2, dx1, dy1, dx2, dy2,
- width,
- true,
- miter_limit);
+ case miter_join_round:
+ calc_miter(vc,
+ v0, v1, v2, dx1, dy1, dx2, dy2,
+ m_line_join,
+ m_miter_limit,
+ dbevel);
break;
case round_join:
- stroke_calc_arc(out_vertices,
- v1.x, v1.y, dx1, -dy1, dx2, -dy2,
- width, approximation_scale);
+ calc_arc(vc, v1.x, v1.y, dx1, -dy1, dx2, -dy2);
break;
default: // Bevel join
- out_vertices.add(coord_type(v1.x + dx1, v1.y - dy1));
- if(calc_distance(dx1, dy1, dx2, dy2) > approximation_scale * 0.25)
- {
- out_vertices.add(coord_type(v1.x + dx2, v1.y - dy2));
- }
+ add_vertex(vc, v1.x + dx1, v1.y - dy1);
+ add_vertex(vc, v1.x + dx2, v1.y - dy2);
break;
}
}