path.h revision a16a494f042310ee849a6f717ffea70846f1f22c
/** @file
* @brief Path - a sequence of contiguous curves
*//*
* Authors:
* MenTaLguY <mental@rydia.net>
* Marco Cecchetti <mrcekets at gmail.com>
* Krzysztof KosiĆski <tweenk.pl@gmail.com>
*
* Copyright 2007-2014 Authors
*
* modify it either under the terms of the GNU Lesser General Public
* License version 2.1 as published by the Free Software Foundation
* (the "LGPL") or, at your option, under the terms of the Mozilla
* Public License Version 1.1 (the "MPL"). If you do not alter this
* notice, a recipient may use your version of this file under either
* the MPL or the LGPL.
*
* You should have received a copy of the LGPL along with this library
* in the file COPYING-LGPL-2.1; if not, write to the Free Software
* Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
* You should have received a copy of the MPL along with this library
* in the file COPYING-MPL-1.1
*
* The contents of this file are subject to the Mozilla Public License
* Version 1.1 (the "License"); you may not use this file except in
* compliance with the License. You may obtain a copy of the License at
*
* This software is distributed on an "AS IS" basis, WITHOUT WARRANTY
* OF ANY KIND, either express or implied. See the LGPL or the MPL for
* the specific language governing rights and limitations.
*/
#ifndef LIB2GEOM_SEEN_PATH_H
#define LIB2GEOM_SEEN_PATH_H
#include <iterator>
#include <algorithm>
#include <iostream>
#include <boost/operators.hpp>
#include <boost/shared_ptr.hpp>
struct PathData {
};
< BaseIterator<P>
, Curve const
, Curve const *
, Curve const &
>
{
// default copy, default assign
typedef BaseIterator<P> Self;
}
}
}
++index;
return *this;
}
--index;
return *this;
}
index += d;
return *this;
}
index -= d;
return *this;
}
P *path;
unsigned index;
};
}
/** @brief Generalized time value in the path.
*
* This class exists because when mapping the range of multiple curves onto the same interval
* as the curve index, we lose some precision. For instance, a path with 16 curves will
* have 4 bits less precision than a path with 1 curve. If you need high precision results
* in long paths, either use this class and related methods instead of the standard methods
* pointAt(), nearestTime() and so on, or use curveAt() to first obtain the curve, then
* call the method again to obtain a high precision result.
*
* @ingroup Paths */
struct PathTime
{
Coord t; ///< Time value in the curve
PathTime() : t(0), curve_index(0) {}
return t < other.t;
}
return false;
}
}
/// Convert times at or beyond 1 to 0 on the next curve.
if (t >= 1) {
t = 0;
}
}
/// Convert times at or before 0 to 1 on the previous curve.
if (t <= 0) {
t = 1;
}
}
};
return os;
}
/** @brief Contiguous subset of the path's parameter domain.
* This is a directed interval, which allows one to specify any contiguous subset
* of the path's domain, including subsets that wrap around the initial point
* of the path.
* @ingroup Paths */
/** @brief Default interval.
* Default-constructed PathInterval includes only the initial point of the initial segment. */
PathInterval();
/** @brief Construct an interval in the path's parameter domain.
* @param from Initial time
* @param to Final time
* @param cross_start If true, the interval will proceed from the initial to final
* time through the initial point of the path, wrapping around the closing segment;
* otherwise it will not wrap around the closing segment.
* @param path_size Size of the path to which this interval applies, required
* to clean up degenerate cases */
/// Get the time value of the initial point.
/// Get the time value of the final point.
/// Check whether the interval has only one point.
/// True if the interval goes in the direction of decreasing time values.
/// True if the interior of the interval contains the initial point of the path.
bool crossesStart() const { return _cross_start; }
/// Test a path time for inclusion.
/// Get a time at least @a min_dist away in parameter space from the ends.
/// If no such time exists, the middle point is returned.
/// Select one of two intervals with given endpoints by parameter direction.
/// Select one of two intervals with given endpoints by whether it includes the initial point.
return result;
}
size_type curveCount() const;
bool _cross_start, _reverse;
};
/// Create an interval in the direction of increasing time value.
/// @relates PathInterval
{
return result;
}
/// Create an interval in the direction of decreasing time value.
/// @relates PathInterval
{
return result;
}
/// Output an interval in the path's domain.
/// @relates PathInterval
os << "PathInterval[";
if (ival.crossesStart()) {
} else {
}
os << "]";
return os;
}
template <>
struct ShapeTraits<Path> {
typedef PathInterval IntervalType;
typedef Path AffineClosureType;
typedef PathIntersection IntersectionType;
};
/** @brief Sequence of contiguous curves, aka spline.
*
* Path represents a sequence of contiguous curves, also known as a spline.
* It corresponds to a "subpath" in SVG terminology. It can represent both
* open and closed subpaths. The final point of each curve is exactly
* equal to the initial point of the next curve.
*
* The path always contains a linear closing segment that connects
* the final point of the last "real" curve to the initial point of the
* first curve. This way the curves form a closed loop even for open paths.
* If the closing segment has nonzero length and the path is closed, it is
* considered a normal part of the path data. There are three distinct sets
* of end iterators one can use to iterate over the segments:
*
* - Iterating between @a begin() and @a end() will iterate over segments
* which are part of the path.
* - Iterating between @a begin() and @a end_closed()
* will always iterate over a closed loop of segments.
* - Iterating between @a begin() and @a end_open() will always skip
* the final linear closing segment.
*
* If the final point of the last "real" segment coincides exactly with the initial
* point of the first segment, the closing segment will be absent from both
* [begin(), end_open()) and [begin(), end_closed()).
*
* Normally, an exception will be thrown when you try to insert a curve
* that makes the path non-continuous. If you are working with unsanitized
* curve data, you can call setStitching(true), which will insert line segments
* to make the path continuous.
*
* Internally, Path uses copy-on-write data. This is done for two reasons: first,
* copying a Curve requires calling a virtual function, so it's a little more expensive
* that normal copying; and second, it reduces the memory cost of copying the path.
* Therefore you can return Path and PathVector from functions without worrying
* about temporary copies.
*
* Note that this class cannot represent arbitrary shapes, which may contain holes.
* To do that, use PathVector, which is more generic.
*
* It's not very convenient to create a Path directly. To construct paths more easily,
* use PathBuilder.
*
* @ingroup Paths */
{
ClosingSegment() : LineSegment() {}
};
StitchSegment() : LineSegment() {}
};
// Path(Path const &other) - use default copy constructor
/// Construct an empty path starting at the specified point.
, _closed(false)
, _exception_on_stitch(true)
{
}
/// Construct a path containing a range of curves.
{
}
} else {
}
}
/// Create a path from a rectangle.
/// Create a path from a convex hull.
/// Create a path from a circle, using two elliptical arcs.
/// Create a path from an ellipse, using two elliptical arcs.
// Path &operator=(Path const &other) - use default assignment operator
/** @brief Swap contents with another path
* @todo Add noexcept specifiers for C++11 */
}
/** @brief Swap contents of two paths.
* @relates Path */
/** @brief Access a curve by index */
/** @brief Access a curve by index */
/** @brief Access the first curve in the path.
* Since the curve always contains at least a degenerate closing segment,
* it is always safe to use this method. */
/// Alias for front().
/** @brief Access the last curve in the path. */
}
Curve const &back_closed() const {
return _closing_seg->isDegenerate()
}
Curve const &back_default() const {
return _includesClosingSegment()
? back_closed()
: back_open();
}
/// Size without the closing segment, even if the path is closed.
/** @brief Size with the closing segment, if it makes a difference.
* If the closing segment is degenerate, i.e. its initial and final points
* are exactly equal, then it is not included in this size. */
size_type size_closed() const {
}
/// Natural size of the path.
size_type size_default() const {
}
/// Natural size of the path.
/** @brief Check whether path is empty.
* The path is empty if it contains only the closing segment, which according
* to the continuity invariant must be degenerate. Note that unlike standard
* containers, two empty paths are not necessarily identical, because the
* degenerate closing segment may be at a different point, affecting the operation
* of methods such as appendNew(). */
/// Check whether the path is closed.
/** @brief Set whether the path is closed.
* When closing a path where the last segment can be represented as a closing
* segment, the last segment will be removed. When opening a path, the closing
* segment will be erased. This means that closing and then opening a path
* will not always give back the original path. */
/** @brief Remove all curves from the path.
* The initial and final points of the closing segment are set to (0,0).
* The stitching flag remains unchanged. */
void clear();
/** @brief Get the approximate bounding box.
* The rectangle returned by this method will contain all the curves, but it's not
* guaranteed to be the smallest possible one */
OptRect boundsFast() const;
/** @brief Get a tight-fitting bounding box.
* This will return the smallest possible axis-aligned rectangle containing
* all the curves in the path. */
OptRect boundsExact() const;
/// Test paths for exact equality.
/// Apply a transform to each curve.
_unshare();
}
return *this;
}
return result;
}
/** @brief Get the allowed range of time values.
* @return Values for which pointAt() and valueAt() yield valid results. */
/** Get the curve at the specified time value.
* @param t Time value
* @param rest Optional storage for the corresponding time value in the curve */
/// Get the closing segment of the path.
/** @brief Get the point at the specified time value.
* Note that this method has reduced precision with respect to calling pointAt()
* directly on the curve. If you want high precision results, use the version
* that takes a PathTime parameter.
*
* Allowed time values range from zero to the number of curves; you can retrieve
* the allowed range of values with timeRange(). */
/// Get one coordinate (X or Y) at the specified time value.
/// Get the curve at the specified path time.
/// Get the point at the specified path time.
/// Get one coordinate at the specified path time.
/// Compute intersections with axis-aligned line.
/// Compute intersections with another path.
/** @brief Determine the winding number at the specified point.
*
* The winding number is the number of full turns made by a ray that connects the passed
* point and the path's value (i.e. the result of the pointAt() method) as the time increases
* from 0 to the maximum valid value. Positive numbers indicate turns in the direction
* of increasing angles.
*
* Winding numbers are often used as the definition of what is considered "inside"
* the shape. Typically points with either nonzero winding or odd winding are
* considered to be inside the path. */
return allNearestTimes(p, 0, size_default());
}
/** @brief Append a subset of this path to another path.
* An extra stitching segment will be inserted if the start point of the portion
* and the final point of the target path do not match exactly.
* The closing segment of the target path will be modified. */
void appendPortionTo(Path &p, PathTime const &from, PathTime const &to, bool cross_start = false) const {
}
/** @brief Append a subset of this path to another path.
* This version allows you to explicitly pass a PathInterval. */
}
/** @brief Append a subset of this path to another path, specifying endpoints.
* This method is for use in situations where endpoints of the portion segments
* have to be set exactly, for instance when computing Boolean operations. */
appendPortionTo(ret, f, t);
return ret;
}
/** @brief Get a subset of the current path with full precision.
* When @a from is larger (later in the path) than @a to, the returned portion
* will be reversed. If @a cross_start is true, the portion will be reversed
* and will cross the initial point of the path. Therefore, when @a from is larger
* than @a to and @a cross_start is true, the returned portion will not be reversed,
* but will "wrap around" the end of the path. */
return ret;
}
/** @brief Get a subset of the current path with full precision.
* This version allows you to explicitly pass a PathInterval. */
return ret;
}
/** @brief Obtain a reversed version of the current path.
* The final point of the current path will become the initial point
* of the reversed path, unless it is closed and has a non-degenerate
* closing segment. In that case, the new initial point will be the final point
* of the last "real" segment. */
_unshare();
}
}
// erase last segment of path
/** @brief Get the first point in the path. */
/** @brief Get the last point in the path.
* If the path is closed, this is always the same as the initial point. */
void setInitial(Point const &p) {
_unshare();
_closed = false;
_closing_seg->setFinal(p);
}
_unshare();
_closed = false;
_closing_seg->setInitial(p);
}
/** @brief Add a new curve to the end of the path.
* This inserts the new curve right before the closing segment.
* The path takes ownership of the passed pointer, which should not be freed. */
_unshare();
}
_unshare();
}
_unshare();
}
}
}
_unshare();
}
}
/** @brief Append a new curve to the path.
*
* This family of methods will automaticaly use the current final point of the path
* as the first argument of the new curve's constructor. To call this method,
* you'll need to write e.g.:
* @code
path.template appendNew<CubicBezier>(control1, control2, end_point);
@endcode
* It is important to note that the coordinates passed to appendNew should be finite!
* If one of the coordinates is infinite, 2geom will throw a ContinuityError exception.
*/
void appendNew(A a) {
_unshare();
}
void appendNew(A a, B b) {
_unshare();
}
void appendNew(A a, B b, C c) {
_unshare();
}
void appendNew(A a, B b, C c, D d) {
_unshare();
}
void appendNew(A a, B b, C c, D d, E e) {
_unshare();
}
template <typename CurveType, typename A, typename B, typename C, typename D, typename E, typename F>
void appendNew(A a, B b, C c, D d, E e, F f) {
_unshare();
}
template <typename CurveType, typename A, typename B, typename C, typename D, typename E, typename F, typename G>
void appendNew(A a, B b, C c, D d, E e, F f, G g) {
_unshare();
}
template <typename CurveType, typename A, typename B, typename C, typename D, typename E, typename F, typename G,
typename H>
void appendNew(A a, B b, C c, D d, E e, F f, G g, H h) {
_unshare();
}
template <typename CurveType, typename A, typename B, typename C, typename D, typename E, typename F, typename G,
void appendNew(A a, B b, C c, D d, E e, F f, G g, H h, I i) {
_unshare();
}
/** @brief Reduce the closing segment to a point if it's shorter than precision.
* Do this by moving the final point. */
/// Append a stitching segment ending at the specified point.
/** @brief Verify the continuity invariant.
* If the path is not contiguous, this will throw a CountinuityError. */
void checkContinuity() const;
/** @brief Enable or disable the throwing of exceptions when stitching discontinuities.
* Normally stitching will cause exceptions, but when you are working with unsanitized
* curve data, you can disable these exceptions. */
void setStitching(bool x) {
_exception_on_stitch = !x;
}
}
}
// whether the closing segment is part of the path
bool _includesClosingSegment() const {
}
void _unshare() {
// Called before every mutation.
// Ensure we have our own copy of curve data and reset cached values
}
}
void stitch(Sequence::iterator first_replaced, Sequence::iterator last_replaced, Sequence &sequence);
// n.b. takes ownership of curve object
bool _closed;
bool _exception_on_stitch;
}; // end class Path
}
} // end namespace Geom
#endif // LIB2GEOM_SEEN_PATH_H
/*
Local Variables:
mode:c++
c-file-style:"stroustrup"
c-file-offsets:((innamespace . 0)(inline-open . 0)(case-label . +))
indent-tabs-mode:nil
fill-column:99
End:
*/
// vim: filetype=cpp:expandtab:shiftwidth=4:tabstop=8:softtabstop=4:fileencoding=utf-8:textwidth=99 :