1 // Copyright (C) 2005, 2006, 2007, 2008, 2009, 2010 Free Software
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.
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.
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
23 #include "SWFMatrix.h"
27 #include <vector> // for path composition
28 #include <cmath> // sqrt
31 // Forward declarations
39 /// Defines an edge with a control point and an anchor point.
41 /// Could be a quadratic bezier curve, or a straight line(degenerated curve).
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
57 Edge(boost::int32_t cx
, boost::int32_t cy
, boost::int32_t ax
,
64 Edge(const Edge
& from
)
70 Edge(const point
& ncp
, const point
& nap
)
81 /// Transform the edge according to the given SWFMatrix.
82 void transform(const SWFMatrix
& mat
)
88 /// Return squared distance between point pt and segment A-B
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
);
108 return p
.squareDistance(A
);
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
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
138 pointOnCurve(const point
& A
, const point
& C
, const point
& B
, float t
)
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
,
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.
170 /// Left fill style index (1-based)
173 /// Right fill style index (1-based)
176 /// Line style index (1-based)
179 /// Start point of the path
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.
191 /// Default constructor
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
),
209 m_edges(from
.m_edges
),
210 m_new_shape(from
.m_new_shape
)
214 /// Initialize a path
217 /// X coordinate of path origin in TWIPS
220 /// Y coordinate in path origin in TWIPS
223 /// Fill style index for left fill (1-based).
224 /// Zero means NO style.
227 /// Fill style index for right fill (1-based)
228 /// Zero means NO style.
231 /// Line style index for right fill (1-based).
232 /// Zero means NO style.
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
,
240 m_new_shape(newShape
)
242 reset(ax
, ay
, fill0
, fill1
, line
);
245 /// Re-initialize a path, maintaining the "new shape" flag untouched
248 /// X coordinate of path origin in TWIPS
251 /// Y coordinate in path origin in TWIPS
254 /// Fill style index for left fill
257 /// Fill style index for right fill
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.
276 /// Expand given SWFRect to include bounds of this path
279 /// The rectangle to expand with our own bounds
282 /// The thickess of our lines, half the thickness will
283 /// be added in all directions in swf8+, all of it will
286 /// @param swfVersion
287 /// SWF version to use.
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
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
);
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.
333 /// X coordinate in TWIPS
336 /// Y coordinate in TWIPS
339 drawLineTo(boost::int32_t dx
, boost::int32_t dy
)
341 m_edges
.push_back(Edge(dx
, dy
, dx
, dy
));
346 /// Offset values are relative to path origin and
347 /// expressed in TWIPS.
350 /// Control point's X coordinate.
353 /// Control point's Y coordinate.
356 /// Anchor point's X ordinate.
359 /// Anchor point's Y ordinate.
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
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
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
);
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.
405 withinSquareDistance(const point
& p
, double dist
) const
407 size_t nedges
= m_edges
.size();
409 if ( ! nedges
) return false;
412 for (size_t i
=0; i
<nedges
; ++i
)
414 const Edge
& e
= m_edges
[i
];
419 double d
= Edge::squareDistancePtSeg(p
, px
, np
);
420 if ( d
<= dist
) return true;
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
433 // TODO: find an apprpriate value for segCount based
434 // on rendering scale ?
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
445 double d
= Edge::squareDistancePtSeg(p
, p0
, p1
);
446 if ( d
<= dist
) return true;
448 p0
.setTo(p1
.x
, p1
.y
);
457 /// Transform all path coordinates according to the given SWFMatrix.
458 void transform(const SWFMatrix
& mat
)
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
474 /// Return true if this path starts a new (sub)shape
475 bool getNewShape() const
480 /// Return true if this path contains no edges
483 return m_edges
.empty();
486 /// Set the fill to use on the left side
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
)
500 unsigned getLeftFill() const
505 /// Set the fill to use on the left side
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
)
519 unsigned getRightFill() const
524 /// Set the line style to use for this path
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
)
538 unsigned getLineStyle() const
543 /// Return the number of edges in this path
546 return m_edges
.size();
549 /// Return a reference to the Nth edge
550 Edge
& operator[] (size_t n
)
555 /// Return a const reference to the Nth edge
556 const Edge
& operator[] (size_t n
) const
561 /// Returns true if this path begins a new subshape. <-- VERIFYME
562 bool isNewShape() const
567 }; // end of class Path
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
581 #endif // GNASH_GEOMETRY_H
588 // indent-tabs-mode: t