Merge branch 'master' of git.sv.gnu.org:/srv/git/gnash
[gnash.git] / libcore / Geometry.h
blobfc20d48909553ca908d0781688a1e27d25c8f41f
1 // Copyright (C) 2005, 2006, 2007, 2008, 2009, 2010 Free Software
2 // Foundation, Inc
3 //
4 // This program is free software; you can redistribute it and/or modify
5 // it under the terms of the GNU General Public License as published by
6 // the Free Software Foundation; either version 3 of the License, or
7 // (at your option) any later version.
8 //
9 // This program is distributed in the hope that it will be useful,
10 // but WITHOUT ANY WARRANTY; without even the implied warranty of
11 // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
12 // GNU General Public License for more details.
13 //
14 // You should have received a copy of the GNU General Public License
15 // along with this program; if not, write to the Free Software
16 // Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
19 #ifndef GNASH_GEOMETRY_H
20 #define GNASH_GEOMETRY_H
22 #include "dsodefs.h"
23 #include "SWFMatrix.h"
24 #include "SWFRect.h"
25 #include "Point2d.h"
27 #include <vector> // for path composition
28 #include <cmath> // sqrt
31 // Forward declarations
32 namespace gnash {
33 class LineStyle;
36 namespace gnash {
38 /// \brief
39 /// Defines an edge with a control point and an anchor point.
40 ///
41 /// Could be a quadratic bezier curve, or a straight line(degenerated curve).
42 ///
43 class Edge
45 public:
47 // Quadratic bezier: point = p0 * t^2 + p1 * 2t(1-t) + p2 * (1-t)^2
48 point cp; // control point, TWIPS
49 point ap; // anchor point, TWIPS
51 Edge()
53 cp(0, 0),
54 ap(0, 0)
57 Edge(boost::int32_t cx, boost::int32_t cy, boost::int32_t ax,
58 boost::int32_t ay)
60 cp(cx, cy),
61 ap(ax, ay)
64 Edge(const Edge& from)
66 cp(from.cp),
67 ap(from.ap)
70 Edge(const point& ncp, const point& nap)
72 cp(ncp),
73 ap(nap)
76 bool straight() const
78 return cp == ap;
81 /// Transform the edge according to the given SWFMatrix.
82 void transform(const SWFMatrix& mat)
84 mat.transform(ap);
85 mat.transform(cp);
88 /// Return squared distance between point pt and segment A-B
89 static double
90 squareDistancePtSeg(const point& p, const point& A, const point& B)
92 boost::int32_t dx = B.x - A.x;
93 boost::int32_t dy = B.y - A.y;
95 if ( dx == 0 && dy == 0 )
97 return p.squareDistance(A);
100 boost::int32_t pdx = p.x - A.x;
101 boost::int32_t pdy = p.y - A.y;
103 double u = (static_cast<double>(pdx) * dx + static_cast<double>(pdy) * dy ) /
104 (static_cast<double>(dx)*dx + static_cast<double>(dy)*dy );
106 if (u <= 0)
108 return p.squareDistance(A);
111 if (u >= 1)
113 return p.squareDistance(B);
116 point px(A, B, u); // FIXME: this interpolation introduce a precision loss (point is int-based)
117 return p.squareDistance(px);
120 /// Return distance between point pt and segment A-B
121 static double
122 distancePtSeg(const point& pt, const point& A, const point& B)
124 double square = squareDistancePtSeg(pt, A, B);
125 return std::sqrt(square);
128 /// Find point of the quadratic curve defined by points A,C,B
130 /// @param A The first point
131 /// @param C The second point (control point)
132 /// @param B The third point (anchor point)
133 /// @param ret The point to write result into
134 /// @param t the step factor between 0 and 1
137 static point
138 pointOnCurve(const point& A, const point& C, const point& B, float t)
140 point Q1(A, C, t);
141 point Q2(C, B, t);
142 point R(Q1, Q2, t);
144 return R;
147 /// Return square distance between point pt and the point on curve found by
148 /// applying the T parameter to the quadratic bezier curve function
150 /// @param A The first point of the bezier curve
151 /// @param C The second point of the bezier curve (control point)
152 /// @param B The third point of the bezier curve (anchor point)
153 /// @param p The point we want to compute distance from
154 /// @param t the step factor between 0 and 1
156 static boost::int64_t squareDistancePtCurve(const point& A,
157 const point& C,
158 const point& B,
159 const point& p, float t)
161 return p.squareDistance( pointOnCurve(A, C, B, t) );
166 /// A subset of a shape, a series of edges sharing a single set of styles.
167 class DSOEXPORT Path
169 public:
170 /// Left fill style index (1-based)
171 unsigned m_fill0;
173 /// Right fill style index (1-based)
174 unsigned m_fill1;
176 /// Line style index (1-based)
177 unsigned m_line;
179 /// Start point of the path
180 point ap;
182 /// Edges forming the path
183 std::vector<Edge> m_edges;
185 /// This flag is set when the path is the first one of a new "sub-shape".
186 /// All paths with a higher index in the list belong to the same
187 /// shape unless they have m_new_shape==true on their own.
188 /// Sub-shapes affect the order in which outlines and shapes are rendered.
189 bool m_new_shape;
191 /// Default constructor
193 /// @param newShape
194 /// True if this path starts a new subshape
196 Path(bool newShape = false)
198 m_new_shape(newShape)
200 reset(0, 0, 0, 0, 0);
203 Path(const Path& from)
205 m_fill0(from.m_fill0),
206 m_fill1(from.m_fill1),
207 m_line(from.m_line),
208 ap(from.ap),
209 m_edges(from.m_edges),
210 m_new_shape(from.m_new_shape)
214 /// Initialize a path
216 /// @param ax
217 /// X coordinate of path origin in TWIPS
219 /// @param ay
220 /// Y coordinate in path origin in TWIPS
222 /// @param fill0
223 /// Fill style index for left fill (1-based).
224 /// Zero means NO style.
226 /// @param fill1
227 /// Fill style index for right fill (1-based)
228 /// Zero means NO style.
230 /// @param line
231 /// Line style index for right fill (1-based).
232 /// Zero means NO style.
234 /// @param newShape
235 /// True if this path starts a new subshape
236 Path(boost::int32_t ax, boost::int32_t ay,
237 unsigned fill0, unsigned fill1, unsigned line,
238 bool newShape)
240 m_new_shape(newShape)
242 reset(ax, ay, fill0, fill1, line);
245 /// Re-initialize a path, maintaining the "new shape" flag untouched
247 /// @param ax
248 /// X coordinate of path origin in TWIPS
250 /// @param ay
251 /// Y coordinate in path origin in TWIPS
253 /// @param fill0
254 /// Fill style index for left fill
256 /// @param fill1
257 /// Fill style index for right fill
259 /// @param line
260 /// Line style index for right fill
262 void reset(boost::int32_t ax, boost::int32_t ay,
263 unsigned fill0, unsigned fill1, unsigned line)
264 // Reset all our members to the given values, and clear our edge list.
266 ap.x = ax;
267 ap.y = ay;
268 m_fill0 = fill0;
269 m_fill1 = fill1;
270 m_line = line;
272 m_edges.resize(0);
273 assert(empty());
276 /// Expand given SWFRect to include bounds of this path
278 /// @param r
279 /// The rectangle to expand with our own bounds
281 /// @param thickness
282 /// The thickess of our lines, half the thickness will
283 /// be added in all directions in swf8+, all of it will
284 /// in swf7-
286 /// @param swfVersion
287 /// SWF version to use.
289 void
290 expandBounds(SWFRect& r, unsigned int thickness, int swfVersion) const
292 const Path& p = *this;
293 size_t nedges = m_edges.size();
295 if ( ! nedges ) return; // this path adds nothing
297 if (thickness)
299 // NOTE: Half of thickness would be enough (and correct) for
300 // radius, but that would not match how Flash calculates the
301 // bounds using the drawing API.
302 unsigned int radius = swfVersion < 8 ? thickness : thickness/2;
304 r.expand_to_circle(ap.x, ap.y, radius);
305 for (unsigned int j = 0; j<nedges; j++)
307 r.expand_to_circle(m_edges[j].ap.x, m_edges[j].ap.y, radius);
308 r.expand_to_circle(m_edges[j].cp.x, m_edges[j].cp.y, radius);
311 else
313 r.expand_to_point(ap.x, ap.y);
314 for (unsigned int j = 0; j<nedges; j++)
316 r.expand_to_point(m_edges[j].ap.x, p.m_edges[j].ap.y);
317 r.expand_to_point(m_edges[j].cp.x, p.m_edges[j].cp.y);
322 /// @{ Primitives for the Drawing API
324 /// Name of these functions track Ming interface
327 /// Draw a straight line.
329 /// Point coordinates are relative to path origin
330 /// and expressed in TWIPS.
332 /// @param x
333 /// X coordinate in TWIPS
335 /// @param y
336 /// Y coordinate in TWIPS
338 void
339 drawLineTo(boost::int32_t dx, boost::int32_t dy)
341 m_edges.push_back(Edge(dx, dy, dx, dy));
344 /// Draw a curve.
346 /// Offset values are relative to path origin and
347 /// expressed in TWIPS.
349 /// @param cx
350 /// Control point's X coordinate.
352 /// @param cy
353 /// Control point's Y coordinate.
355 /// @param ax
356 /// Anchor point's X ordinate.
358 /// @param ay
359 /// Anchor point's Y ordinate.
361 void
362 drawCurveTo(boost::int32_t cdx, boost::int32_t cdy, boost::int32_t adx, boost::int32_t ady)
364 m_edges.push_back(Edge(cdx, cdy, adx, ady));
367 /// Remove all edges and reset style infomation
368 void clear()
370 m_edges.resize(0);
371 m_fill0 = m_fill1 = m_line = 0;
374 /// @} Primitives for the Drawing API
377 /// Returns true if the last and the first point of the path match
378 bool isClosed() const
380 if (m_edges.empty()) return true;
381 return m_edges.back().ap == ap;
384 /// Close this path with a straight line, if not already closed
385 void close()
387 if ( m_edges.empty() ) return;
389 // Close it with a straight edge if needed
390 const Edge& lastedge = m_edges.back();
391 if ( lastedge.ap != ap )
393 Edge newedge(ap, ap);
394 m_edges.push_back(newedge);
398 /// \brief
399 /// Return true if the given point is within the given squared distance
400 /// from this path edges.
402 /// NOTE: if the path is empty, false is returned.
404 bool
405 withinSquareDistance(const point& p, double dist) const
407 size_t nedges = m_edges.size();
409 if ( ! nedges ) return false;
411 point px(ap);
412 for (size_t i=0; i<nedges; ++i)
414 const Edge& e = m_edges[i];
415 point np(e.ap);
417 if (e.straight())
419 double d = Edge::squareDistancePtSeg(p, px, np);
420 if ( d <= dist ) return true;
422 else
425 const point& A = px;
426 const point& C = e.cp;
427 const point& B = e.ap;
429 // Approximate the curve to segCount segments
430 // and compute distance of query point from each
431 // segment.
433 // TODO: find an apprpriate value for segCount based
434 // on rendering scale ?
436 int segCount = 10;
437 point p0(A.x, A.y);
438 for (int i=1; i<=segCount; ++i)
440 float t1 = static_cast<float>(i) / segCount;
441 point p1 = Edge::pointOnCurve(A, C, B, t1);
443 // distance from point and segment being an approximation
444 // of the curve
445 double d = Edge::squareDistancePtSeg(p, p0, p1);
446 if ( d <= dist ) return true;
448 p0.setTo(p1.x, p1.y);
451 px = np;
454 return false;
457 /// Transform all path coordinates according to the given SWFMatrix.
458 void transform(const SWFMatrix& mat)
460 mat.transform(ap);
461 std::vector<Edge>::iterator it = m_edges.begin(), ie = m_edges.end();
462 for(; it != ie; it++)
464 (*it).transform(mat);
468 /// Set this path as the start of a new (sub)shape
469 void setNewShape()
471 m_new_shape=true;
474 /// Return true if this path starts a new (sub)shape
475 bool getNewShape() const
477 return m_new_shape;
480 /// Return true if this path contains no edges
481 bool empty() const
483 return m_edges.empty();
486 /// Set the fill to use on the left side
488 /// @param f
489 /// The fill index (1-based).
490 /// When this path is added to a DefineShapeTag,
491 /// the index (decremented by 1) will reference an element
492 /// in the FillStyle vector defined for that shape.
493 /// If zero, no fill will be active.
495 void setLeftFill(unsigned f)
497 m_fill0 = f;
500 unsigned getLeftFill() const
502 return m_fill0;
505 /// Set the fill to use on the left side
507 /// @param f
508 /// The fill index (1-based).
509 /// When this path is added to a DefineShapeTag,
510 /// the index (decremented by 1) will reference an element
511 /// in the FillStyle vector defined for that shape.
512 /// If zero, no fill will be active.
514 void setRightFill(unsigned f)
516 m_fill1 = f;
519 unsigned getRightFill() const
521 return m_fill1;
524 /// Set the line style to use for this path
526 /// @param f
527 /// The LineStyle index (1-based).
528 /// When this path is added to a DefineShapeTag,
529 /// the index (decremented by 1) will reference an element
530 /// in the LineStyle vector defined for that shape.
531 /// If zero, no fill will be active.
533 void setLineStyle(unsigned i)
535 m_line = i;
538 unsigned getLineStyle() const
540 return m_line;
543 /// Return the number of edges in this path
544 size_t size() const
546 return m_edges.size();
549 /// Return a reference to the Nth edge
550 Edge& operator[] (size_t n)
552 return m_edges[n];
555 /// Return a const reference to the Nth edge
556 const Edge& operator[] (size_t n) const
558 return m_edges[n];
561 /// Returns true if this path begins a new subshape. <-- VERIFYME
562 bool isNewShape() const
564 return m_new_shape;
567 }; // end of class Path
569 namespace geometry
572 bool pointTest(const std::vector<Path>& paths,
573 const std::vector<LineStyle>& lineStyles, boost::int32_t x,
574 boost::int32_t y, const SWFMatrix& wm);
576 } // namespace geometry
579 } // namespace gnash
581 #endif // GNASH_GEOMETRY_H
584 // Local Variables:
585 // mode: C++
586 // c-basic-offset: 8
587 // tab-width: 8
588 // indent-tabs-mode: t
589 // End: