00001 /* 00002 Copyright (c) 2010, The Barbarian Group 00003 All rights reserved. 00004 00005 Redistribution and use in source and binary forms, with or without modification, are permitted provided that 00006 the following conditions are met: 00007 00008 * Redistributions of source code must retain the above copyright notice, this list of conditions and 00009 the following disclaimer. 00010 * Redistributions in binary form must reproduce the above copyright notice, this list of conditions and 00011 the following disclaimer in the documentation and/or other materials provided with the distribution. 00012 00013 THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED 00014 WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A 00015 PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR 00016 ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED 00017 TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) 00018 HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING 00019 NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE 00020 POSSIBILITY OF SUCH DAMAGE. 00021 */ 00022 00023 #pragma once 00024 00025 #include "cinder/Vector.h" 00026 #include "cinder/BSpline.h" 00027 #include "cinder/Rect.h" 00028 #include "cinder/Exception.h" 00029 #include "cinder/MatrixAffine2.h" 00030 00031 #include <vector> 00032 00033 namespace cinder { 00034 00035 class Path2d { 00036 public: 00037 Path2d() {} 00038 explicit Path2d( const BSpline<Vec2f> &spline, float subdivisionStep = 0.01f ); 00039 00041 void moveTo( const Vec2f &p ); 00043 void moveTo( float x, float y ) { moveTo( Vec2f( x, y ) ); } 00044 void lineTo( const Vec2f &p ); 00045 void lineTo( float x, float y ) { lineTo( Vec2f( x, y ) ); } 00046 void quadTo( const Vec2f &p1, const Vec2f &p2 ); 00047 void quadTo( float x1, float y1, float x2, float y2 ) { quadTo( Vec2f( x1, y1 ), Vec2f( x2, y2 ) ); } 00048 void curveTo( const Vec2f &p1, const Vec2f &p2, const Vec2f &p3 ); 00049 void curveTo( float x1, float y1, float x2, float y2, float x3, float y3 ) { curveTo( Vec2f( x1, y1 ), Vec2f( x2, y2 ), Vec2f( x3, y3 ) ); } 00050 void arc( const Vec2f ¢er, float radius, float startRadians, float endRadians, bool forward = true ); 00051 void arc( float centerX, float centerY, float radius, float startRadians, float endRadians, bool forward = true ) { arc( Vec2f( centerX, centerY ), radius, startRadians, endRadians, forward ); } 00052 void arcTo( const Vec2f &p, const Vec2f &t, float radius ); 00053 void arcTo( float x, float y, float tanX, float tanY, float radius) { arcTo( Vec2f( x, y ), Vec2f( tanX, tanY ), radius ); } 00054 00056 void close() { mSegments.push_back( CLOSE ); } 00057 bool isClosed() const { return ( mSegments.size() > 1 ) && mSegments.back() == CLOSE; } 00058 00060 void reverse(); 00061 00062 bool empty() const { return mPoints.empty(); } 00063 void clear() { mSegments.clear(); mPoints.clear(); } 00064 size_t getNumSegments() const { return mSegments.size(); } 00065 size_t getNumPoints() const { return mPoints.size(); } 00066 00068 Vec2f getPosition( float t ) const; 00070 Vec2f getSegmentPosition( size_t segment, float t ) const; 00071 00072 std::vector<Vec2f> subdivide( float approximationScale = 1.0f ) const; 00073 00075 void scale( const Vec2f &amount, Vec2f scaleCenter = Vec2f::zero() ); 00077 void transform( const MatrixAffine2f &matrix ); 00079 Path2d transformCopy( const MatrixAffine2f &matrix ) const; 00080 00081 00082 const std::vector<Vec2f>& getPoints() const { return mPoints; } 00083 std::vector<Vec2f>& getPoints() { return mPoints; } 00084 const Vec2f& getPoint( size_t point ) const { return mPoints[point]; } 00085 Vec2f& getPoint( size_t point ) { return mPoints[point]; } 00086 const Vec2f& getCurrentPoint() const { return mPoints.back(); } 00087 void setPoint( size_t index, const Vec2f &p ) { mPoints[index] = p; } 00088 00089 enum SegmentType { MOVETO, LINETO, QUADTO, CUBICTO, CLOSE }; 00090 static const int sSegmentTypePointCounts[]; 00091 SegmentType getSegmentType( size_t segment ) const { return mSegments[segment]; } 00092 00093 const std::vector<SegmentType>& getSegments() const { return mSegments; } 00094 std::vector<SegmentType>& getSegments() { return mSegments; } 00095 00096 void removeSegment( size_t segment ); 00097 00099 Rectf calcBoundingBox() const; 00101 Rectf calcPreciseBoundingBox() const; 00102 00104 bool contains( const Vec2f &pt ) const; 00105 00106 friend class Shape2d; 00107 friend std::ostream& operator<<( std::ostream &out, const Path2d &p ); 00108 private: 00109 void arcHelper( const Vec2f ¢er, float radius, float startRadians, float endRadians, bool forward ); 00110 void arcSegmentAsCubicBezier( const Vec2f ¢er, float radius, float startRadians, float endRadians ); 00111 void subdivideQuadratic( float distanceToleranceSqr, const Vec2f &p1, const Vec2f &p2, const Vec2f &p3, int level, std::vector<Vec2f> *result ) const; 00112 void subdivideCubic( float distanceToleranceSqr, const Vec2f &p1, const Vec2f &p2, const Vec2f &p3, const Vec2f &p4, int level, std::vector<Vec2f> *result ) const; 00113 00114 std::vector<Vec2f> mPoints; 00115 std::vector<SegmentType> mSegments; 00116 }; 00117 00118 inline std::ostream& operator<<( std::ostream &out, const Path2d &p ) 00119 { 00120 size_t pt = 0; 00121 for( size_t s = 0; s < p.mSegments.size(); ++s ) { 00122 if( p.mSegments[s] == Path2d::MOVETO ) { 00123 out << "M " << p.mPoints[pt].x << " " << p.mPoints[pt].y << " "; 00124 pt++; 00125 } 00126 if( p.mSegments[s] == Path2d::LINETO ) { 00127 out << "L " << p.mPoints[pt].x << " " << p.mPoints[pt].y << " "; 00128 pt++; 00129 } 00130 else if( p.mSegments[s] == Path2d::QUADTO ) { 00131 out << "Q " << p.mPoints[pt].x << " " << p.mPoints[pt].y << " " << p.mPoints[pt+1].x << " " << p.mPoints[pt+1].y << " "; 00132 pt += 2; 00133 } 00134 else if( p.mSegments[s] == Path2d::CUBICTO ) { 00135 out << "C " << p.mPoints[pt].x << " " << p.mPoints[pt].y << " " << p.mPoints[pt+1].x << " " << p.mPoints[pt+1].y << " " << p.mPoints[pt+2].x << " " << p.mPoints[pt+2].y << " "; 00136 pt += 3; 00137 } 00138 else { 00139 out << "Z "; 00140 } 00141 00142 } 00143 00144 return out; 00145 } 00146 00147 class Path2dExc : public Exception { 00148 }; 00149 00150 } // namespace cinder