1from cython.operator cimport dereference as deref 2from numbers import Number 3from _cy_rectangle cimport cy_OptInterval, wrap_OptInterval, wrap_Rect, OptRect, wrap_OptRect 4from _cy_rectangle cimport cy_Interval, wrap_Interval 5 6from _cy_affine cimport cy_Affine, wrap_Affine, get_Affine, is_transform 7 8from _cy_curves cimport is_Curve, get_Curve_p 9 10 11cdef class cy_Iterator: 12 cdef Iterator* thisptr 13 cdef ConstIterator* startptr 14 cdef ConstIterator* endptr 15 16 def __cinit__(self): 17 self.thisptr = new Iterator() 18 19 def __dealloc__(self): 20 del self.thisptr 21 22 def __iter__(self): 23 return self 24 25 def __next__(self): 26 if deref(<BaseIterator[ConstIterator, Path] *> self.endptr) == deref(<BaseIterator[ConstIterator, Path] *> self.thisptr): 27 raise StopIteration 28 cdef Curve * r = <Curve *> &(<BaseIterator[Iterator, Path] * > self.thisptr).c_item() 29 (<BaseIterator[Iterator, Path] *> self.thisptr).c_next() 30 return wrap_Curve_p ( r ) 31 32 33 34 35cdef cy_Iterator wrap_Iterator(Iterator i, ConstIterator starti, ConstIterator endi): 36 cdef Iterator * retp = new Iterator() 37 retp[0] = i 38 cdef cy_Iterator r = cy_Iterator.__new__(cy_Iterator) 39 r.thisptr = retp 40 41 cdef ConstIterator * endp = new ConstIterator() 42 endp[0] = endi 43 r.endptr = endp 44 45 cdef ConstIterator * startp = new ConstIterator() 46 startp[0] = starti 47 r.startptr = startp 48 49 return r 50 51cdef class cy_Path: 52 53 """Path is a ordered sequence of curves. 54 55 You can iterate this class, accessing curves one at time, or access 56 them using indices. 57 58 Two constants, NO_STITCHING and STITCH_DISCONTINUOUS are members of 59 Path namespace, and are used to specify type of stitching, if 60 necessary. 61 62 Path is either open or closed, but in both cases carries closing 63 segment, connecting last and first point. 64 65 Corresponds to Path class in 2geom. 66 """ 67 68 NO_STITCHING = c_NO_STITCHING 69 STITCH_DISCONTINUOUS = c_STITCH_DISCONTINUOUS 70 71 def __cinit__(self, cy_Point p=cy_Point()): 72 """Create Path containing only one point.""" 73 self.thisptr = new Path(deref( p.thisptr )) 74 75 @classmethod 76 def fromList(cls, l, Stitching stitching=NO_STITCHING, closed=False): 77 """Create path from list of curves. 78 79 Specify stithing and closed flag in additional arguments. 80 """ 81 p = cy_Path() 82 for curve in l: 83 p.append_curve(curve, stitching) 84 p.close(closed) 85 return p 86 87 @classmethod 88 def fromPath(cls, cy_Path p, fr=-1, to = 1, bint closed=False): 89 """Create path copying another's path curves. 90 91 Either copy all curves, or ones from 92 fr - index of first curve copied 93 to 94 to - index of first curve not copied. 95 96 Also takes closed flag. 97 """ 98 if fr == -1: 99 return wrap_Path( Path(p.thisptr.begin_const(), p.thisptr.end_default(), closed) ) 100 else: 101 return wrap_Path( Path(p._const_iterator_at_index(fr), p._const_iterator_at_index(to), closed) ) 102 103 def __dealloc__(self): 104 del self.thisptr 105 106 def __getitem__(self, unsigned int i): 107 """Get curve with index i.""" 108 cdef Curve * r = <Curve *> & deref(self.thisptr)[i] 109 return wrap_Curve_p(r) 110 111 def __call__(self, double t): 112 """Evaluate path at time t. 113 114 Note: t can be greater than 1 here, it can go to self.size() 115 """ 116 return wrap_Point(deref( self.thisptr ) (t) ) 117 118 def __richcmp__(cy_Path self, cy_Path other, int op): 119 if op == 2: 120 return deref( self.thisptr ) == deref( other.thisptr ) 121 elif op == 3: 122 return deref( self.thisptr ) != deref( other.thisptr ) 123 124 def __mul__(cy_Path self, m): 125 """Transform path with a transform.""" 126 cdef Affine at 127 if is_transform(m): 128 at = get_Affine(m) 129 return wrap_Path( deref(self.thisptr) * at ) 130 131 #This is not the fastest way, but it's pretty nice from python's perspective 132 #Anyway, I would expect that performance hit is minimal, since i is generally really small 133 cdef ConstIterator _const_iterator_at_index(self, int i): 134 cdef ConstIterator ci = self.thisptr.begin_const() 135 cdef ConstIterator * cip = &ci 136 for ii in range(i): 137 (<BaseIteratorConst *> cip).c_next() 138 return ci 139 140 cdef Iterator _iterator_at_index(self, int i): 141 cdef Iterator ci = self.thisptr.begin() 142 cdef Iterator * cip = &ci 143 for ii in range(i): 144 (<BaseIterator[Iterator, Path] *> cip).c_next() 145 return ci 146 147 def swap(self, cy_Path other): 148 """Swap curves with another path.""" 149 self.thisptr.swap(deref( other.thisptr )) 150 151#This is the same as __getitem__ 152#~ def at_index(self, unsigned int i): 153#~ return wrap_Curve(self.thisptr.at_index(i)) 154 155 def front(self): 156 """Get first curve.""" 157 #this is AFAIK the shortest way to do this 158 cdef Curve * r = <Curve *> &self.thisptr.front() 159 return wrap_Curve_p(r) 160 161 def back(self): 162 """Same as back_open.""" 163 cdef Curve * r = <Curve *> &self.thisptr.back() 164 return wrap_Curve_p(r) 165 166 def back_open(self): 167 """Get last curve, treating self as open.""" 168 cdef Curve * r = <Curve *> &self.thisptr.back_open() 169 return wrap_Curve_p(r) 170 171 def back_closed(self): 172 """Get last curve, treating self as closed.""" 173 cdef Curve * r = <Curve *> &self.thisptr.back_closed() 174 return wrap_Curve_p(r) 175 176 def back_default(self): 177 """Get last curve.""" 178 cdef Curve * r = <Curve *> &self.thisptr.back_default() 179 return wrap_Curve_p(r) 180 181 def curves(self): 182 """Same as curves_open""" 183 return wrap_Iterator(self.thisptr.begin(), self.thisptr.begin_const(), self.thisptr.end()) 184 185 def curves_open(self): 186 """Return all curves as iterable, treating self as open.""" 187 return wrap_Iterator(self.thisptr.begin(), self.thisptr.begin_const(), self.thisptr.end_open()) 188 189 def curves_closed(self): 190 """Return all curves as iterable, treating self as closed.""" 191 return wrap_Iterator(self.thisptr.begin(), self.thisptr.begin_const(), self.thisptr.end_closed()) 192 193 def curves_default(self): 194 """Return all curves as iterable.""" 195 return wrap_Iterator(self.thisptr.begin(), self.thisptr.begin_const(), self.thisptr.end_default()) 196 197 def __iter__(self): 198 return self.curves_default() 199 200 def size_open(self): 201 """Get number of curves, treating self as open.""" 202 return self.thisptr.size_open() 203 204 def size_closed(self): 205 """Get number of curves, treating self as closed.""" 206 return self.thisptr.size_closed() 207 208 def size_default(self): 209 """Get number of curves.""" 210 return self.thisptr.size_default() 211 212 def size(self): 213 """Same as size_open.""" 214 return self.thisptr.size() 215 216#Does the same as size_open, which doesn't correspond with name. 217#~ def max_size(self): 218#~ return self.thisptr.max_size() 219 220 def empty(self): 221 """Test whether path contains no curves.""" 222 return self.thisptr.empty() 223 224 def closed(self): 225 """Return state of closed flag.""" 226 return self.thisptr.closed() 227 228 def close(self, bint closed): 229 """Set closed flag.""" 230 self.thisptr.close(closed) 231 232 def bounds_fast(self): 233 """Return fast bounding rectangle for path. 234 235 It's not guaranteed to give the tighest bound. 236 """ 237 return wrap_OptRect(self.thisptr.boundsFast()) 238 239 def bounds_exact(self): 240 """Give the tighest bounding rectangle for path.""" 241 return wrap_OptRect(self.thisptr.boundsExact()) 242 243#~ def toPwSb(self): 244#~ return wrap_Piecewise<Geom::D2<Geom::SBasis> >(self.thisptr.toPwSb()) 245 246 def point_at(self, double t): 247 """Same as self(t).""" 248 return wrap_Point(self.thisptr.pointAt(t)) 249 250 def value_at(self, double t, Dim2 d): 251 """Same as self(t)[d].""" 252 return self.thisptr.valueAt(t, d) 253 254 def __call__(self, double t): 255 """Evaluate path at time t. 256 257 Equivalent to self[floor(t)](t-floor(t)) 258 """ 259 return wrap_Point(deref( self.thisptr ) (t) ) 260 261 def roots(self, double v, Dim2 d): 262 """Find time values where self(t)[d] == v""" 263 return wrap_vector_double(self.thisptr.roots(v, d)) 264 265 def all_nearest_times(self, cy_Point _point, double fr=-1, double to=1): 266 """Return all values of t that |self(t) - point| is minimized.""" 267 if fr == -1: 268 return wrap_vector_double(self.thisptr.allNearestTimes(deref( _point.thisptr ))) 269 return wrap_vector_double(self.thisptr.allNearestTimes(deref( _point.thisptr ), fr, to)) 270 271 272 def nearest_time_per_curve(self, cy_Point _point): 273 """Find nearest points, return one time value per each curve.""" 274 return wrap_vector_double(self.thisptr.nearestTimePerCurve(deref( _point.thisptr ))) 275 276 def nearest_time(self, cy_Point _point, double fr=-1, double to=1):#, cy_double * distance_squared): 277 """Return such t that |self(t) - point| is minimized.""" 278 if fr == -1: 279 return self.thisptr.nearestTime(deref( _point.thisptr ), NULL) 280 return self.thisptr.nearestTime(deref( _point.thisptr ), fr, to, NULL) 281 282 def nearest_time_and_dist_sq(self, cy_Point _point, double fr=-1, double to=1): 283 """Return such t that |self(t) - point| is minimized and square of that distance.""" 284 cdef double t, dist 285 if fr == -1: 286 t = self.thisptr.nearestTime(deref( _point.thisptr ), &dist ) 287 else: 288 t = self.thisptr.nearestTime(deref( _point.thisptr ), fr, to, &dist) 289 return (t, dist) 290 291 def append_portion_to(self, cy_Path p, double f, double t): 292 """Append portion of path to self.""" 293 self.thisptr.appendPortionTo(deref( p.thisptr ), f, t) 294 295 def portion(self, Coord fr=0, Coord to=1, cy_Interval interval=None): 296 """Return portion of curve between two time values. 297 298 Alternatively use argument interval. 299 """ 300 if interval is None: 301 return wrap_Path(self.thisptr.portion(fr, to)) 302 else: 303 return wrap_Path(self.thisptr.portion(deref( interval.thisptr ))) 304 305 def reversed(self): 306 """Return reversed curve.""" 307 return wrap_Path(self.thisptr.reversed()) 308 309 def insert(self, int pos, curve, Stitching stitching=NO_STITCHING): 310 """Insert curve into position pos. 311 312 Args: 313 pos: Position of inserted curve. 314 curve: Curve to insert. 315 stitching=NO_STITCHING 316 """ 317 cdef Curve * cptr = get_Curve_p(curve) 318 if cptr: 319 self.thisptr.insert( self._iterator_at_index(pos), deref( cptr ), stitching ) 320 else: 321 raise TypeError("passed curve is not C++ Curve") 322 323 def insert_slice(self, int pos, cy_Path p, int first, int last, Stitching stitching=NO_STITCHING): 324 """Insert curves to position pos. 325 326 Args: 327 pos: Position of inserted slice. 328 p: Path from which slice is inserted. 329 first: First inserted curve position (in p). 330 last: Fist not inserted curve position (in p). 331 stiching=NO_STITCHING 332 """ 333 self.thisptr.insert(self._iterator_at_index(pos), p._const_iterator_at_index(first), p._const_iterator_at_index(last), stitching) 334 335 def clear(self): 336 """Clear all curves.""" 337 self.thisptr.clear() 338 339 def erase(self, int pos, Stitching stitching=NO_STITCHING): 340 """Erase curve at position pos. 341 342 Args: 343 pos: Position of erased curve. 344 stitching=NO_STITCHING 345 """ 346 self.thisptr.erase(self._iterator_at_index(pos), stitching) 347 348 def erase_slice(self, int start, int end, Stitching stitching=NO_STITCHING): 349 """Erase curves with indices [start, end). 350 351 Args: 352 start, end: Curves with indices start...end-1 are erased 353 stitching=NO_STITCHING 354 """ 355 self.thisptr.erase(self._iterator_at_index(start), self._iterator_at_index(end), stitching) 356 357 def erase_last(self): 358 """Erase last curve.""" 359 self.thisptr.erase_last() 360 361 def replace(self, int replaced, curve, Stitching stitching=NO_STITCHING): 362 """Replace curve at position replaced with another curve. 363 364 Args: 365 replaced: Position of replaced curve. 366 curve: New curve. 367 stitching=NO_STITCHING 368 """ 369 cdef Curve * cptr = get_Curve_p(curve) 370 if cptr: 371 self.thisptr.replace(self._iterator_at_index(replaced), deref( cptr ), stitching) 372 else: 373 raise TypeError("passed curve is not C++ Curve") 374 375 def replace_slice(self, int first_replaced, int last_replaced, curve, Stitching stitching=NO_STITCHING): 376 """Replace slice of curves by new curve. 377 378 Args: 379 first_replaced, last_replace: Curves with indices 380 first_replaced ... last_replaced 381 curve: New curve. 382 stitching=NO_STITCHING 383 """ 384 cdef Curve * cptr = get_Curve_p(curve) 385 if cptr: 386 self.thisptr.replace(self._iterator_at_index(first_replaced), self._iterator_at_index(last_replaced), deref( cptr ), stitching) 387 else: 388 raise TypeError("passed curve is not C++ Curve") 389 390#How to implement this nicely? 391#~ def replaceByList(self, int replaced, cy_ConstIterator first, cy_ConstIterator last, Stitching stitching): 392#~ self.thisptr.replace(deref( replaced.thisptr ), deref( first.thisptr ), deref( last.thisptr ), stitching) 393#~ def replace(self, cy_Iterator first_replaced, cy_Iterator last_replaced, cy_ConstIterator first, cy_ConstIterator last, Stitching stitching): 394#~ self.thisptr.replace(deref( first_replaced.thisptr ), deref( last_replaced.thisptr ), deref( first.thisptr ), deref( last.thisptr ), stitching) 395 396 def start(self, cy_Point p): 397 """Erase all curves and set first point.""" 398 self.thisptr.start(deref( p.thisptr )) 399 400 def initial_point(self): 401 """Get initial point.""" 402 return wrap_Point(self.thisptr.initialPoint()) 403 404 def final_point(self): 405 """Get final point.""" 406 return wrap_Point(self.thisptr.finalPoint()) 407 408 def set_initial(self, cy_Point p): 409 """Set initial point.""" 410 self.thisptr.setInitial(deref( p.thisptr )) 411 412 def set_final(self, cy_Point p): 413 """Set final point.""" 414 self.thisptr.setFinal(deref( p.thisptr )) 415 416 def append_curve(self, curve, Stitching stitching=NO_STITCHING): 417 """Append curve to path. 418 419 Args: 420 curve: Curve to append. 421 stitching=NO_STITCHING 422 """ 423 cdef Curve * cptr = get_Curve_p(curve) 424 if cptr: 425 self.thisptr.append( deref( cptr ), stitching) 426 else: 427 raise TypeError("passed curve is not C++ Curve") 428 429 def append_SBasis(self, cy_SBasis x, cy_SBasis y, Stitching stitching=NO_STITCHING): 430 """Append two SBasis functions to path. 431 432 Args: 433 x, y: SBasis functions to append. 434 stitching=NO_STITCHING 435 """ 436 cdef D2[SBasis] sb = D2[SBasis]( deref(x.thisptr), deref(y.thisptr) ) 437 self.thisptr.append(sb, stitching) 438 439 def append_path(self, cy_Path other, Stitching stitching=NO_STITCHING): 440 """Append another path to path. 441 442 Args: 443 other: Path to append. 444 stitching=NO_STITCHING 445 """ 446 self.thisptr.append(deref( other.thisptr ), stitching) 447 448 def stitch_to(self, cy_Point p): 449 """Set last point to p, creating stitching segment to it.""" 450 self.thisptr.stitchTo(deref( p.thisptr )) 451 452cdef cy_Path wrap_Path(Path p): 453 cdef Path * retp = new Path(Point()) 454 retp[0] = p 455 cdef cy_Path r = cy_Path.__new__(cy_Path) 456 r.thisptr = retp 457 return r 458