diff options
author | Daniel Baumann <daniel.baumann@progress-linux.org> | 2024-04-13 11:57:42 +0000 |
---|---|---|
committer | Daniel Baumann <daniel.baumann@progress-linux.org> | 2024-04-13 11:57:42 +0000 |
commit | 61f3ab8f23f4c924d455757bf3e65f8487521b5a (patch) | |
tree | 885599a36a308f422af98616bc733a0494fe149a /src/cython/_cy_curves.pyx | |
parent | Initial commit. (diff) | |
download | lib2geom-upstream.tar.xz lib2geom-upstream.zip |
Adding upstream version 1.3.upstream/1.3upstream
Signed-off-by: Daniel Baumann <daniel.baumann@progress-linux.org>
Diffstat (limited to '')
-rw-r--r-- | src/cython/_cy_curves.pyx | 1945 |
1 files changed, 1945 insertions, 0 deletions
diff --git a/src/cython/_cy_curves.pyx b/src/cython/_cy_curves.pyx new file mode 100644 index 0000000..3584a87 --- /dev/null +++ b/src/cython/_cy_curves.pyx @@ -0,0 +1,1945 @@ +from numbers import Number + +from cython.operator cimport dereference as deref + +from _cy_rectangle cimport cy_OptInterval, wrap_OptInterval, wrap_Rect, OptRect, wrap_OptRect +from _cy_rectangle cimport cy_Interval, wrap_Interval + +from _cy_affine cimport cy_Translate, cy_Rotate, cy_Scale +from _cy_affine cimport cy_VShear, cy_HShear, cy_Zoom +from _cy_affine cimport cy_Affine, wrap_Affine, get_Affine, is_transform + + +cdef class cy_Curve: + + """Class representing generic curve. + + Curve maps unit interval to real plane. All curves should implement + these methods. + + This class corresponds to Curve class in 2geom. It's children in cython + aren't actually derived from it, it would make code more unreadable. + """ + + def __cinit__(self): + """Create new Curve. + + You shouldn't create Curve this way, it usually wraps existing + curves (f. e. in Path). + """ + self.thisptr = <Curve *> new SBasisCurve(D2[SBasis]( SBasis(0), SBasis(0) )) + + def __call__(self, Coord t): + """Get point at time value t.""" + return wrap_Point( deref(self.thisptr)(t) ) + + def initial_point(self): + """Get self(0).""" + return wrap_Point(self.thisptr.initialPoint()) + + def final_point(self): + """Get self(1).""" + return wrap_Point(self.thisptr.finalPoint()) + def is_degenerate(self): + """Curve is degenerate if it's length is zero.""" + return self.thisptr.isDegenerate() + + def point_at(self, Coord t): + """Equivalent to self(t).""" + return wrap_Point(self.thisptr.pointAt(t)) + + def value_at(self, Coord t, Dim2 d): + """Equivalent to self(t)[d].""" + return self.thisptr.valueAt(t, d) + + def point_and_derivatives(self, Coord t, unsigned int n): + """Return point and at least first n derivatives at point t in list.""" + return wrap_vector_point(self.thisptr.pointAndDerivatives(t, n)) + + def set_initial(self, cy_Point v): + """Set initial point of curve.""" + self.thisptr.setInitial(deref( v.thisptr )) + + def set_final(self, cy_Point v): + """Set final point of curve.""" + self.thisptr.setFinal(deref( v.thisptr )) + + def bounds_fast(self): + """Return bounding rectangle for curve. + + This method is fast, but does not guarantee to give smallest + rectangle. + """ + return wrap_Rect(self.thisptr.boundsFast()) + + def bounds_exact(self): + """Return exact bounding rectangle for curve. + + This may take a while. + """ + return wrap_Rect(self.thisptr.boundsExact()) + + def bounds_local(self, cy_OptInterval i, unsigned int deg=0): + """Return bounding rectangle to portion of curve.""" + return wrap_OptRect(self.thisptr.boundsLocal(deref( i.thisptr ), deg)) + + #TODO rewrite all duplicates to copy.""" + def duplicate(self): + """Duplicate the curve.""" + return wrap_Curve_p( self.thisptr.duplicate() ) + + def transformed(self, m): + """Transform curve by affine transform.""" + cdef Affine at + if is_transform(m): + at = get_Affine(m) + return wrap_Curve_p( self.thisptr.transformed(at) ) + + def portion(self, Coord fr=0, Coord to=1, cy_Interval interval=None): + """Return portion of curve, specified by endpoints or interval.""" + if interval is None: + return wrap_Curve_p( self.thisptr.portion(deref( interval.thisptr )) ) + else: + return wrap_Curve_p( self.thisptr.portion(fr, to) ) + + def reverse(self): + """Return curve with reversed time.""" + return wrap_Curve_p( self.thisptr.reverse() ) + + def derivative(self): + """Return curve's derivative.""" + return wrap_Curve_p( self.thisptr.derivative() ) + + def nearest_time(self, cy_Point p, Coord fr=0, Coord to=1, cy_Interval interval=None): + """Return such t that |self(t) - point| is minimized.""" + if interval is None: + return self.thisptr.nearestTime(deref( p.thisptr ), fr, to) + else: + return self.thisptr.nearestTime(deref( p.thisptr ), deref( interval.thisptr )) + + def all_nearest_times(self, cy_Point p, Coord fr=0, Coord to=1, cy_Interval interval=None): + """Return all values of t that |self(t) - point| is minimized.""" + if interval is None: + return wrap_vector_double(self.thisptr.allNearestTimes(deref( p.thisptr ), fr, to)) + else: + return wrap_vector_double(self.thisptr.allNearestTimes(deref( p.thisptr ), + deref( interval.thisptr ))) + + def length(self, Coord tolerance): + """Return length of curve, within give tolerance.""" + return self.thisptr.length(tolerance) + + def roots(self, Coord v, Dim2 d): + """Find time values where self(t)[d] == v.""" + return wrap_vector_double(self.thisptr.roots(v, d)) + + def winding(self, cy_Point p): + """Return winding number around specified point.""" + return self.thisptr.winding(deref( p.thisptr )) + + def unit_tangent_at(self, Coord t, unsigned int n): + """Return tangent at self(t). + + Parameter n specifies how many derivatives to take into account.""" + return wrap_Point(self.thisptr.unitTangentAt(t, n)) + + def to_SBasis(self): + """Return tuple of SBasis functions.""" + cdef D2[SBasis] ret = self.thisptr.toSBasis() + return ( wrap_SBasis(ret[0]), wrap_SBasis(ret[1]) ) + + def degrees_of_freedom(self): + """Return number of independent parameters needed to specify the curve.""" + return self.thisptr.degreesOfFreedom() +#~ def operator==(self, cy_Curve c): +#~ return deref( self.thisptr ) == deref( c.thisptr ) + +#~ cdef cy_Curve wrap_Curve(Curve & p): +#~ cdef Curve * retp = <Curve *> new SBasisCurve(D2[SBasis]( SBasis(), SBasis() )) +#~ retp[0] = p +#~ cdef cy_Curve r = cy_Curve.__new__(cy_Curve) +#~ r.thisptr = retp +#~ return r + +cdef cy_Curve wrap_Curve_p(Curve * p): + cdef cy_Curve r = cy_Curve.__new__(cy_Curve) + r.thisptr = p + return r + +cdef class cy_Linear: + """Function mapping linearly between two values. + + Corresponds to Linear class in 2geom. + """ + + cdef Linear* thisptr + + def __cinit__(self, aa = None, b = None): + """Create new Linear from two end values. + + No arguments create zero constant, one value creates constant. + """ + if aa is None: + self.thisptr = new Linear() + elif b is None: + self.thisptr = new Linear(float(aa)) + else: + self.thisptr = new Linear(float(aa), float(b)) + + def __dealloc__(self): + del self.thisptr + + def __call__(self, Coord t): + """Get value at time value t.""" + return deref(self.thisptr)(t) + + def __getitem__(self, i): + """Get end values.""" + return deref( self.thisptr ) [i] + + def __richcmp__(cy_Linear self, cy_Linear other, int op): + if op == 2: + return deref(self.thisptr) == deref(other.thisptr) + elif op == 3: + return deref(self.thisptr) != deref(other.thisptr) + + + def __neg__(cy_Linear self): + """Negate all values of self.""" + return wrap_Linear( L_neg(deref(self.thisptr)) ) + + + def __add__(cy_Linear self, other): + """Add number or other linear.""" + if isinstance(other, Number): + return wrap_Linear( deref(self.thisptr) + float(other) ) + elif isinstance(other, cy_Linear): + return wrap_Linear( deref(self.thisptr) + deref( (<cy_Linear> other).thisptr ) ) + + def __sub__(cy_Linear self, other): + """Substract number or other linear.""" + if isinstance(other, Number): + return wrap_Linear( L_sub_Ld(deref(self.thisptr), float(other)) ) + elif isinstance(other, cy_Linear): + return wrap_Linear( L_sub_LL(deref(self.thisptr), deref( (<cy_Linear> other).thisptr )) ) + + + def __mul__(cy_Linear self, double b): + """Multiply linear by number.""" + return wrap_Linear(deref( self.thisptr ) * b) + + def __div__(cy_Linear self, double b): + """Divide linear by value.""" + return wrap_Linear(deref( self.thisptr ) / b) + + def is_zero(self, double eps = EPSILON): + """Test whether linear is zero within given tolerance.""" + return self.thisptr.isZero(eps) + + def is_constant(self, double eps = EPSILON): + """Test whether linear is constant within given tolerance.""" + return self.thisptr.isConstant(eps) + + def is_finite(self): + """Test whether linear is finite.""" + return self.thisptr.isFinite() + + def at0(self): + """Equivalent to self(0).""" + return self.thisptr.at0() + + def at1(self): + """Equivalent to self(1).""" + return self.thisptr.at1() + + def value_at(self, double t): + """Equivalent to self(t).""" + return self.thisptr.valueAt(t) + + def to_SBasis(self): + """Convert to SBasis.""" + return wrap_SBasis(self.thisptr.toSBasis()) + + def bounds_exact(self): + """Return exact bounding interval + + This may take a while. + """ + return wrap_OptInterval(self.thisptr.bounds_exact()) + + def bounds_fast(self): + """Return bounding interval + + This method is fast, but does not guarantee to give smallest + interval. + """ + return wrap_OptInterval(self.thisptr.bounds_fast()) + + def bounds_local(self, double u, double v): + """Return bounding interval to the portion of Linear.""" + return wrap_OptInterval(self.thisptr.bounds_local(u, v)) + + def tri(self): + """Return difference between end values.""" + return self.thisptr.tri() + + def hat(self): + """Return value at (0.5).""" + return self.thisptr.hat() + + @classmethod + def sin(cls, cy_Linear bo, int k): + """Return sine of linear.""" + return wrap_SBasis(sin(deref( bo.thisptr ), k)) + + @classmethod + def cos(cls, cy_Linear bo, int k): + """Return cosine of linear.""" + return wrap_SBasis(cos(deref( bo.thisptr ), k)) + + @classmethod + def reciprocal(cls, cy_Linear a, int k): + """Return reciprocical of linear.""" + return wrap_SBasis(reciprocal(deref( a.thisptr ), k)) + + @classmethod + def shift(cls, cy_Linear a, int sh): + """Multiply by x**sh.""" + return wrap_SBasis(shift(deref( a.thisptr ), sh)) + +#leave these in cy2geom napespace? +def cy_lerp(double t, double a, double b): + return lerp(t, a, b) + +cdef cy_Linear wrap_Linear(Linear p): + cdef Linear * retp = new Linear() + retp[0] = p + cdef cy_Linear r = cy_Linear.__new__(cy_Linear) + r.thisptr = retp + return r + +cdef vector[Linear] make_vector_linear(object l): + cdef vector[Linear] ret + for i in l: + ret.push_back( deref( (<cy_Linear> i).thisptr ) ) + return ret + + +cdef class cy_SBasis: + + """Class representing SBasis polynomial. + + Corresponds to SBasis class in 2geom.""" + + def __cinit__(self, a=None, b=None): + """Create new SBasis. + + This constructor only creates linear SBasis, specifying endpoints. + """ + if a is None: + self.thisptr = new SBasis() + elif b is None: + self.thisptr = new SBasis( float(a) ) + else: + self.thisptr = new SBasis( float(a), float(b) ) + + def __dealloc__(self): + del self.thisptr + + @classmethod + def from_linear(cls, cy_Linear l): + """Create SBasis from Linear.""" + return wrap_SBasis( SBasis(deref( l.thisptr )) ) + + @classmethod + def from_linears(cls, lst): + """Create SBasis from list of Linears.""" + return wrap_SBasis( SBasis( make_vector_linear(lst) ) ) + + def size(self): + """Return number of linears SBasis consists of.""" + return self.thisptr.size() + + def __call__(self, o): + """Get point at time value t.""" + if isinstance(o, Number): + return deref(self.thisptr)(float(o)) + elif isinstance(self, cy_SBasis): + return wrap_SBasis(deref(self.thisptr)( deref( (<cy_SBasis> o).thisptr ) )) + + def __getitem__(self, unsigned int i): + """Get Linear at i th position.""" + if i>=self.size(): + raise IndexError + else: + return wrap_Linear(deref( self.thisptr ) [i]) + + def __neg__(self): + """Return SBasis with negated values.""" + return wrap_SBasis( SB_neg(deref(self.thisptr)) ) + + #cython doesn't use __rmul__, it switches the arguments instead + def __add__(cy_SBasis self, other): + """Add number or other SBasis to SBasis.""" + if isinstance(other, Number): + return wrap_SBasis( deref(self.thisptr) + float(other) ) + elif isinstance(other, cy_SBasis): + return wrap_SBasis( deref(self.thisptr) + deref( (<cy_SBasis> other).thisptr ) ) + + def __sub__(cy_SBasis self, other): + """Substract number or other SBasis from SBasis.""" + if isinstance(other, Number): + return wrap_SBasis( SB_sub_Sd(deref(self.thisptr), float(other) ) ) + elif isinstance(other, cy_SBasis): + return wrap_SBasis( SB_sub_SS(deref(self.thisptr), deref( (<cy_SBasis> other).thisptr ) ) ) + + def __mul__(self, other): + """Multiply SBasis by number or other SBasis.""" + if isinstance(other, Number): + return wrap_SBasis( deref( (<cy_SBasis> self).thisptr ) * float(other) ) + elif isinstance(other, cy_SBasis): + if isinstance(self, cy_SBasis): + return wrap_SBasis( deref( (<cy_SBasis> self).thisptr ) * deref( (<cy_SBasis> other).thisptr ) ) + elif isinstance(self, Number): + return wrap_SBasis( float(self) * deref( (<cy_SBasis> other).thisptr ) ) + + def __div__(cy_SBasis self, double other): + """Divide SBasis by number.""" + return wrap_SBasis( deref(self.thisptr)/other ) + + + def empty(self): + """Test whether SBasis has no linears.""" + return self.thisptr.empty() + + def back(self): + """Return last linear in SBasis.""" + return wrap_Linear(self.thisptr.back()) + + def pop_back(self): + """Remove last linear in SBasis.""" + self.thisptr.pop_back() + + def resize(self, unsigned int n, cy_Linear l = None): + """Resize SBasis, optionally filling created slots with linear.""" + if l is None: + self.thisptr.resize(n) + else: + self.thisptr.resize(n, deref( l.thisptr )) + +#~ def reserve(self, unsigned int n): +#~ self.thisptr.reserve(n) + + def clear(self): + """Make SBasis empty.""" + self.thisptr.clear() +#~ def insert(self, cy_::__gnu_cxx::__normal_iterator<Geom::Linear*, std::vector<Geom::Linear, std::allocator<Geom::Linear> > > before, cy_::__gnu_cxx::__normal_iterator<Geom::Linear const*, std::vector<Geom::Linear, std::allocator<Geom::Linear> > > src_begin, cy_::__gnu_cxx::__normal_iterator<Geom::Linear const*, std::vector<Geom::Linear, std::allocator<Geom::Linear> > > src_end): +#~ self.thisptr.insert(deref( before.thisptr ), deref( src_begin.thisptr ), deref( src_end.thisptr )) + + def at(self, unsigned int i): + """Equivalent to self[i].""" + return wrap_Linear(self.thisptr.at(i)) + + def __richcmp__(cy_SBasis self, cy_SBasis B, int op): + if op == 2: + return deref( self.thisptr ) == deref( B.thisptr ) + elif op == 3: + return deref( self.thisptr ) != deref( B.thisptr ) + + def is_zero(self, double eps = EPSILON): + """Test whether linear is zero within given tolerance.""" + return self.thisptr.isZero(eps) + + def is_constant(self, double eps = EPSILON): + """Test whether linear is constant within given tolerance.""" + return self.thisptr.isConstant(eps) + + def is_finite(self): + """Test whether linear is finite.""" + return self.thisptr.isFinite() + + def at0(self): + """Equivalent to self(0).""" + return self.thisptr.at0() + + def at1(self): + """Equivalent to self(1).""" + return self.thisptr.at1() + + def degrees_of_freedom(self): + """Return number of independent parameters needed to specify the curve.""" + return self.thisptr.degreesOfFreedom() + + def value_at(self, double t): + """Equivalent to self(t)[d].""" + return self.thisptr.valueAt(t) + + def value_and_derivatives(self, double t, unsigned int n): + """Return value and at least n derivatives at time t.""" + return wrap_vector_double (self.thisptr.valueAndDerivatives(t, n)) + + def to_SBasis(self): + """Just return self.""" + return wrap_SBasis(self.thisptr.toSBasis()) + + def tail_error(self, unsigned int tail): + """Return largest error after truncating linears from tail.""" + return self.thisptr.tailError(tail) + + def normalize(self): + """Remove zero linears at the end.""" + self.thisptr.normalize() + + def truncate(self, unsigned int k): + """Truncate SBasis to have k elements.""" + self.thisptr.truncate(k) + + @classmethod + def sqrt(cls, cy_SBasis a, int k): + """Return square root of SBasis. + + Use k to specify degree of resulting SBasis. + """ + return wrap_SBasis(sqrt(deref( a.thisptr ), k)) + + @classmethod + def inverse(cls, cy_SBasis a, int k): + """Return inverse function to SBasis. + + Passed SBasis must be function [1-1] -> [1-1] bijection. + """ + return wrap_SBasis(inverse(deref( a.thisptr ), k)) + + @classmethod + def valuation(cls, cy_SBasis a, double tol = 0): + """Return the degree of the first non zero coefficient.""" + return valuation(deref( a.thisptr ), tol) + + #call with level_set(SBasis(1, 5), 2, a = 0.2, b = 0.4, tol = 0.02) + @classmethod + def level_set(cls, cy_SBasis f, level, a = 0, b = 1, tol = 1e-5, vtol = 1e-5): + """Return intervals where SBasis is in specified level. + + Specify range and tolerance in other arguments. + """ + if isinstance(level, cy_Interval): + return wrap_vector_interval(level_set(deref( f.thisptr ), deref( (<cy_Interval> level).thisptr ), a, b, tol)) #a, b, tol + else: + return wrap_vector_interval(level_set(deref( f.thisptr ), float(level), vtol, a, b, tol)) #vtol, a, b, tol + + @classmethod + def shift(cls, cy_SBasis a, int sh): + """Multiply by x**sh.""" + return wrap_SBasis(shift(deref( a.thisptr ), sh)) + + @classmethod + def compose(cls, cy_SBasis a, cy_SBasis b, k = None): + """Compose two SBasis. + + Specify order of resulting SBasis by parameter k. + """ + if k is None: + return wrap_SBasis(compose(deref( a.thisptr ), deref( b.thisptr ))) + else: + return wrap_SBasis(compose(deref( a.thisptr ), deref( b.thisptr ), int(k))) + + @classmethod + def roots(cls, cy_SBasis s, cy_Interval inside = None): + """Return time values where self equals 0. + + inside intervals specifies subset of domain. + """ + if inside is None: + return wrap_vector_double(roots(deref( s.thisptr ))) + else: + return wrap_vector_double(roots(deref( s.thisptr ), deref( inside.thisptr ))) + + @classmethod + def multi_roots(cls, cy_SBasis f, levels, double htol = 1e-7, double vtol = 1e-7, double a = 0, double b = 1): + """Return lists of roots for different levels.""" + cdef vector[double] l = make_vector_double(levels) + cdef vector[ vector[double] ] r = multi_roots(deref( f.thisptr ), l, htol, vtol, a, b) + lst = [] + for i in range(r.size()): + lst.append( wrap_vector_double(r[i]) ) + return lst + + @classmethod + def multiply_add(cls, cy_SBasis a, cy_SBasis b, cy_SBasis c): + """Return a*b+c.""" + return wrap_SBasis(multiply_add(deref( a.thisptr ), deref( b.thisptr ), deref( c.thisptr ))) + + @classmethod + def divide(cls, cy_SBasis a, cy_SBasis b, int k): + """Divide two SBasis functions. + + Use k to specify degree of resulting SBasis. + """ + return wrap_SBasis(divide(deref( a.thisptr ), deref( b.thisptr ), k)) + + @classmethod + def compose_inverse(cls, cy_SBasis f, cy_SBasis g, unsigned int order, double tol): + """Compose f with g's inverse. + + Requires g to be bijection g: [0, 1] -> [0, 1] + """ + return wrap_SBasis(compose_inverse(deref( f.thisptr ), deref( g.thisptr ), order, tol)) + + @classmethod + def multiply(cls, cy_SBasis a, cy_SBasis b): + """Multiply two SBasis functions.""" + return wrap_SBasis(multiply(deref( (<cy_SBasis> a).thisptr ), deref( (<cy_SBasis> b).thisptr ))) + + @classmethod + def derivative(cls, cy_SBasis a): + """Return derivative os SBasis.""" + return wrap_SBasis(derivative(deref( (<cy_SBasis> a).thisptr ))) + + @classmethod + def integral(cls, a): + """Return integral of SBasis.""" + return wrap_SBasis(integral(deref( (<cy_SBasis> a).thisptr ))) + + @classmethod + def portion(cls, cy_SBasis a, Coord fr=0, Coord to=1, cy_Interval interval=None): + """Return portion of SBasis, specified by endpoints or interval.""" + if interval is None: + return wrap_SBasis( portion( deref( a.thisptr ), fr, to ) ) + else: + return wrap_SBasis( portion( deref( a.thisptr ), deref( interval.thisptr ) ) ) + + @classmethod + def bounds_fast(cls, cy_SBasis a, int order = 0): + """Return bounding interval + + This method is fast, but does not guarantee to give smallest + interval. + """ + return wrap_OptInterval(bounds_fast(deref( a.thisptr ), order)) + + @classmethod + def bounds_exact(cls, cy_SBasis a): + """Return exact bounding interval + + This may take a while. + """ + return wrap_OptInterval(bounds_exact(deref( a.thisptr ))) + + @classmethod + def bounds_local(cls, cy_SBasis a, cy_OptInterval t, int order = 0): + """Return bounding interval to the portion of SBasis.""" + return wrap_OptInterval(bounds_local(deref( a.thisptr ), deref( t.thisptr ), order)) + +#~ def cy_level_sets(cy_SBasis f, vector[Interval] levels, double a, double b, double tol): +#~ return wrap_::std::vector<std::vector<Geom::Interval, std::allocator<Geom::Interval> >,std::allocator<std::vector<Geom::Interval, std::allocator<Geom::Interval> > > >(level_sets(deref( f.thisptr ), deref( levels.thisptr ), a, b, tol)) +#~ def cy_level_sets(cy_SBasis f, vector[vector] levels, double a, double b, double vtol, double tol): +#~ return wrap_::std::vector<std::vector<Geom::Interval, std::allocator<Geom::Interval> >,std::allocator<std::vector<Geom::Interval, std::allocator<Geom::Interval> > > >(level_sets(deref( f.thisptr ), deref( levels.thisptr ), a, b, vtol, tol)) + +def cy_reverse(a): + if isinstance(a, cy_Linear): + return wrap_Linear( reverse(deref( (<cy_Linear> a).thisptr ))) + elif isinstance(a, cy_SBasis): + return wrap_SBasis( reverse(deref( (<cy_SBasis> a).thisptr ))) + elif isinstance(a, cy_Bezier): + return wrap_Bezier( reverse(deref( (<cy_Bezier> a).thisptr ))) + +#already implemented +#~ def cy_truncate(cy_SBasis a, unsigned int terms): +#~ return wrap_SBasis(truncate(deref( a.thisptr ), terms)) + +cdef cy_SBasis wrap_SBasis(SBasis p): + cdef SBasis * retp = new SBasis() + retp[0] = p + cdef cy_SBasis r = cy_SBasis.__new__(cy_SBasis, 0, 0) + r.thisptr = retp + return r + + +cdef class cy_SBasisCurve: + + """Curve mapping two SBasis functions to point (s1(t), s2(t)). + + Corresponds to SBasisCurve in 2geom. + """ + + cdef SBasisCurve* thisptr + +#~ def __init__(self, cy_Curve other): +#~ self.thisptr = self.thisptr.SBasisCurve(deref( other.thisptr )) + + def __cinit__(self, cy_SBasis s1, cy_SBasis s2): + """Create new SBasisCurve from two SBasis functions.""" + self.thisptr = new SBasisCurve( D2[SBasis]( + deref( s1.thisptr ), + deref( s2.thisptr ) ) ) + + def __dealloc__(self): + del self.thisptr + + def __call__(self, double t): + """Get point at time value t.""" + return wrap_Point(deref(self.thisptr)(t)) + + def duplicate(self): + """Duplicate the curve.""" + return wrap_SBasisCurve( <SBasisCurve> deref(self.thisptr.duplicate()) ) + + def initial_point(self): + """Get self(0).""" + return wrap_Point(self.thisptr.initialPoint()) + + def final_point(self): + """Get self(1).""" + return wrap_Point(self.thisptr.finalPoint()) + + def is_degenerate(self): + """Curve is degenerate if it's length is zero.""" + return self.thisptr.isDegenerate() + + def point_at(self, Coord t): + """Equivalent to self(t).""" + return wrap_Point(self.thisptr.pointAt(t)) + + def point_and_derivatives(self, Coord t, unsigned int n): + """Return point and at least first n derivatives at point t in list.""" + return wrap_vector_point(self.thisptr.pointAndDerivatives(t, n)) + + def value_at(self, Coord t, Dim2 d): + """Equivalent to self(t)[d].""" + return self.thisptr.valueAt(t, d) + + def set_initial(self, cy_Point v): + """Set initial point of curve.""" + self.thisptr.setInitial(deref( v.thisptr )) + + def set_final(self, cy_Point v): + """Set final point of curve.""" + self.thisptr.setFinal(deref( v.thisptr )) + + def bounds_fast(self): + """Return bounding rectangle for curve. + + This method is fast, but does not guarantee to give smallest + rectangle. + """ + return wrap_Rect(self.thisptr.boundsFast()) + + def bounds_exact(self): + """Return exact bounding rectangle for curve. + + This may take a while. + """ + return wrap_Rect(self.thisptr.boundsExact()) + + def bounds_local(self, cy_OptInterval i, unsigned int deg): + """Return bounding rectangle to portion of curve.""" + return wrap_OptRect(self.thisptr.boundsLocal(deref( i.thisptr ), deg)) + + def roots(self, Coord v, Dim2 d): + """Find time values where self(t)[d] == v.""" + return wrap_vector_double( self.thisptr.roots(v, d) ) + + def nearest_time(self, cy_Point p, Coord fr=0, Coord to=1, cy_Interval interval=None): + """Return such t that |self(t) - point| is minimized.""" + if interval is None: + return self.thisptr.nearestTime(deref( p.thisptr ), fr, to) + else: + return (<Curve *> self.thisptr).nearestTime(deref( p.thisptr ), deref( interval.thisptr ) ) + + def all_nearest_times(self, cy_Point p, Coord fr=0, Coord to=1, cy_Interval interval=None): + """Return all values of t that |self(t) - point| is minimized.""" + if interval is None: + return wrap_vector_double(self.thisptr.allNearestTimes(deref( p.thisptr ), fr, to)) + else: + return wrap_vector_double((<Curve *> self.thisptr).allNearestTimes(deref( p.thisptr ), + deref( interval.thisptr ) )) + + def length(self, Coord tolerance = 0.01): + """Return length of curve, within give tolerance.""" + return self.thisptr.length(tolerance) + + + def portion(self, Coord fr=0, Coord to=1, cy_Interval interval=None): + """Return portion of curve, specified by endpoints or interval.""" + if interval is None: + return wrap_SBasisCurve( <SBasisCurve> deref(self.thisptr.portion( fr, to ) ) ) + else: + return wrap_SBasisCurve( <SBasisCurve> + deref( (<Curve *> self.thisptr).portion( deref( interval.thisptr ))) ) + + def transformed(self, t): + """Transform curve by affine transform.""" + cdef Affine at + if is_transform(t): + at = get_Affine(t) + return wrap_SBasisCurve( <SBasisCurve> deref(self.thisptr.transformed( at ))) + + def reverse(self): + """Return curve with reversed time.""" + return wrap_SBasisCurve( <SBasisCurve> deref( (<Curve *> self.thisptr).reverse() ) ) + + def derivative(self): + """Return curve's derivative.""" + return wrap_SBasisCurve( <SBasisCurve> deref(self.thisptr.derivative()) ) + + + def winding(self, cy_Point p): + """Return winding number around specified point.""" + return (<Curve *> self.thisptr).winding(deref(p.thisptr)) + + def unit_tangent_at(self, Coord t, int n = 3): + """Return tangent at self(t). + + Parameter n specifies how many derivatives to take into account.""" + return wrap_Point((<Curve *> self.thisptr).unitTangentAt(t, n)) + + def to_SBasis(self): + """Return tuple containing it's SBasis functions.""" + return wrap_D2_SBasis(self.thisptr.toSBasis()) + + def degrees_of_freedom(self): + """Return number of independent parameters needed to specify the curve.""" + return self.thisptr.degreesOfFreedom() + +cdef object wrap_D2_SBasis(D2[SBasis] p): + return ( wrap_SBasis(p[0]), wrap_SBasis(p[1]) ) + +cdef cy_SBasisCurve wrap_SBasisCurve(SBasisCurve p): + cdef SBasisCurve * retp = new SBasisCurve(D2[SBasis]( SBasis(), SBasis() )) + retp[0] = p + cdef cy_SBasisCurve r = cy_SBasisCurve.__new__(cy_SBasisCurve, cy_SBasis(), cy_SBasis()) + r.thisptr = retp + return r + + +cdef class cy_Bezier: + + """Bezier polynomial. + + Corresponds to Bezier class in 2geom. + """ + + cdef Bezier* thisptr + + def __cinit__(self, *args): + """Create Bezier polynomial specifying it's coeffincients + + This constructor takes up to four coefficients. + """ + if len(args) == 0: + #new Bezier() causes segfault + self.thisptr = new Bezier(0) + elif len(args) == 1: + self.thisptr = new Bezier( float(args[0]) ) + elif len(args) == 2: + self.thisptr = new Bezier( float(args[0]), float(args[1]) ) + elif len(args) == 3: + self.thisptr = new Bezier( float(args[0]), float(args[1]), float(args[2]) ) + elif len(args) == 4: + self.thisptr = new Bezier( float(args[0]), float(args[1]), float(args[2]), float(args[3]) ) + else: + raise ValueError("Passed list has too many points") + + def __dealloc__(self): + del self.thisptr + + def __call__(self, double t): + """Get point at time value t.""" + return deref( self.thisptr ) (t) + + + def __getitem__(self, unsigned int ix): + """Get coefficient by accessing list.""" + if ix >= self.size(): + raise IndexError + return deref( self.thisptr ) [ix] + + def order(self): + """Return order of Bezier.""" + return self.thisptr.order() + + def size(self): + """Return number of coefficients.""" + return self.thisptr.size() + + def __mul__( cy_Bezier self, double v): + """Multiply Bezier by number.""" + return wrap_Bezier(deref( self.thisptr ) * v) + + def __add__( cy_Bezier self, double v): + """Add number to Bezier.""" + return wrap_Bezier(deref( self.thisptr ) + v) + + def __sub__( cy_Bezier self, double v): + """Substract number from Bezier.""" + return wrap_Bezier(deref( self.thisptr ) - v) + + def __div__( cy_Bezier self, double v): + """Divide Bezier number.""" + return wrap_Bezier(deref( self.thisptr ) / v) + + + def resize(self, unsigned int n, Coord v): + """Change order of Bezier.""" + self.thisptr.resize(n, v) + + def clear(self): + """Create empty Bezier.""" + self.thisptr.clear() + + def degree(self): + """Return degree of Bezier polynomial.""" + return self.thisptr.degree() + + def is_zero(self, double eps = EPSILON): + """Test whether linear is zero within given tolerance.""" + return self.thisptr.isZero(eps) + + def is_constant(self, double eps = EPSILON): + """Test whether linear is constant within given tolerance.""" + return self.thisptr.isConstant(eps) + + def is_finite(self): + """Test whether linear is finite.""" + return self.thisptr.isFinite() + + def at0(self): + """Equivalent to self(0).""" + return self.thisptr.at0() + + def at1(self): + """Equivalent to self(1).""" + return self.thisptr.at1() + + def value_at(self, double t): + """Equivalent to self(t).""" + return self.thisptr.valueAt(t) + + def to_SBasis(self): + """Convert to SBasis.""" + return wrap_SBasis(self.thisptr.toSBasis()) + + def set_point(self, unsigned int ix, double val): + """Set self[ix] to val.""" + self.thisptr.setPoint(ix, val) + + def value_and_derivatives(self, Coord t, unsigned int n_derivs): + """Return value and at least n derivatives at time t.""" + return wrap_vector_double(self.thisptr.valueAndDerivatives(t, n_derivs)) + + def subdivide(self, Coord t): + """Get two beziers, from 0 to t and from t to 1.""" + cdef pair[Bezier, Bezier] p = self.thisptr.subdivide(t) + return ( wrap_Bezier(p.first), wrap_Bezier(p.second) ) + + def roots(self, cy_Interval ivl = None): + """Find time values where self(t)[d] == v.""" + if ivl is None: + return wrap_vector_double(self.thisptr.roots()) + else: + return wrap_vector_double(self.thisptr.roots(deref( ivl.thisptr ))) + + def forward_difference(self, unsigned int k): +#TODO: ask someone what this function does. +#~ """Compute forward difference of degree k. +#~ +#~ First forward difference of B is roughly function B'(t) = B(t+h)-B(t) +#~ for fixed step h""" + return wrap_Bezier(self.thisptr.forward_difference(k)) + + def elevate_degree(self): + """Increase degree of Bezier by 1.""" + return wrap_Bezier(self.thisptr.elevate_degree()) + + def reduce_degree(self): + """Decrease degree of Bezier by 1.""" + return wrap_Bezier(self.thisptr.reduce_degree()) + + def elevate_to_degree(self, unsigned int new_degree): + """Increase degree of Bezier to new_degree.""" + return wrap_Bezier(self.thisptr.elevate_to_degree(new_degree)) + + def deflate(self): +#TODO: ask someone what this function does. + #It looks like integral(self)*self.size() + return wrap_Bezier(self.thisptr.deflate()) + + @classmethod + def bezier_points(cls, cy_Bezier a, cy_Bezier b): + """Return control points of BezierCurve consisting of two beziers. + + Passed bezier must have same degree.""" + return wrap_vector_point(bezier_points( D2[Bezier]( deref(a.thisptr), deref(b.thisptr) ) )) + + @classmethod + def multiply(cls, cy_Bezier a, cy_Bezier b): + """Multiply two Bezier functions.""" + return wrap_Bezier(multiply(deref( (<cy_Bezier> a).thisptr ), + deref( (<cy_Bezier> b).thisptr ))) + + @classmethod + def portion(cls, cy_Bezier a, Coord fr=0, Coord to=1, interval=None): + """Return portion of bezier, specified by endpoints or interval.""" + if interval is None: + return wrap_Bezier(portion(deref( a.thisptr ), fr, to)) + else: + return wrap_Bezier(portion(deref( a.thisptr ), float(interval.min()), + float(interval.max()) )) + + @classmethod + def derivative(cls, cy_Bezier a): + """Return derivative of a bezier.""" + return wrap_Bezier(derivative(deref( a.thisptr ))) + + @classmethod + def integral(cls, cy_Bezier a): + """Return derivative of a bezier.""" + return wrap_Bezier(integral(deref( a.thisptr ))) + + @classmethod + def bounds_fast(cls, cy_Bezier a): + """Return bounding interval + + This method is fast, but does not guarantee to give smallest + interval. + """ + return wrap_OptInterval(bounds_fast(deref( a.thisptr ))) + + @classmethod + def bounds_exact(cls, cy_Bezier a): + """Return exact bounding interval + + This may take a while. + """ + return wrap_OptInterval(bounds_exact(deref( a.thisptr ))) + + @classmethod + def bounds_local(cls, cy_Bezier a, cy_OptInterval t): + """Return bounding interval to the portion of bezier.""" + return wrap_OptInterval(bounds_local(deref( a.thisptr ), deref( t.thisptr ))) + +#This is the same as bz.to_SBasis() +#~ def cy_bezier_to_sbasis(cy_SBasis sb, cy_Bezier bz): +#~ bezier_to_sbasis(deref( sb.thisptr ), deref( bz.thisptr )) + +#These are look like internal functions. +#~ def cy_casteljau_subdivision(Coord t, cy_Coord * v, cy_Coord * left, cy_Coord * right, unsigned int order): +#~ return subdivideArr(t, v.thisptr, left.thisptr, right.thisptr, order) +#~ def cy_bernsteinValueAt(double t, cy_double * c_, unsigned int n): +#~ return bernsteinValueAt(t, c_.thisptr, n) + +cdef cy_Bezier wrap_Bezier(Bezier p): + cdef Bezier * retp = new Bezier() + retp[0] = p + cdef cy_Bezier r = cy_Bezier.__new__(cy_Bezier) + r.thisptr = retp + return r + + +cdef class cy_BezierCurve: + + """Bezier curve, consisting of two Bezier functions. + + Corresponds to BezierCurve class in 2geom. + """ + + #This flag is due to this class children + def __cinit__(self, *args, **kwargs): + """Don't use this constructor, use create instead.""" + pass + + def __dealloc__(self): + del self.thisptr + + def __call__(self, Coord t): + """Get point at time value t.""" + return wrap_Point(deref( <Curve *> self.thisptr )(t)) + + def __getitem__(self, unsigned int ix): + """Get control point by list access.""" + return wrap_Point(deref( self.thisptr ) [ix]) + + @classmethod + def create(cls, pts): + """Create new BezierCurve from control points.""" + return wrap_BezierCurve( deref( create( make_vector_point(pts) ) ) ) + + def order(self): + """Get order of curve.""" + return self.thisptr.order() + + def control_points(self): + """Get control points.""" + return wrap_vector_point(self.thisptr.controlPoints()) + + def set_point(self, unsigned int ix, cy_Point v): + """Set control point.""" + self.thisptr.setPoint(ix, deref( v.thisptr )) + + def set_points(self, ps): + """Set control points""" + self.thisptr.setPoints( make_vector_point(ps) ) + + def initial_point(self): + """Get self(0).""" + return wrap_Point(self.thisptr.initialPoint()) + + def final_point(self): + """Get self(1).""" + return wrap_Point(self.thisptr.finalPoint()) + + def is_degenerate(self): + """Curve is degenerate if it's length is zero.""" + return self.thisptr.isDegenerate() + + def set_initial(self, cy_Point v): + """Set initial point of curve.""" + self.thisptr.setInitial(deref( v.thisptr )) + + def set_final(self, cy_Point v): + """Set final point of curve.""" + self.thisptr.setFinal(deref( v.thisptr )) + + def bounds_fast(self): + """Return bounding rectangle for curve. + + This method is fast, but does not guarantee to give smallest + rectangle. + """ + return wrap_Rect(self.thisptr.boundsFast()) + + def bounds_exact(self): + """Return exact bounding rectangle for curve. + + This may take a while. + """ + return wrap_Rect(self.thisptr.boundsExact()) + + def bounds_local(cy_BezierCurve self, cy_OptInterval i, unsigned int deg): + """Return bounding rectangle to portion of curve.""" + return wrap_OptRect(self.thisptr.boundsLocal(deref( i.thisptr ), deg)) + + def nearest_time(self, cy_Point p, Coord fr=0, Coord to=1, cy_Interval interval=None): + """Return such t that |self(t) - point| is minimized.""" + if interval is None: + return (<Curve *> self.thisptr).nearestTime(deref( p.thisptr ), fr, to) + else: + return (<Curve *> self.thisptr).nearestTime(deref( p.thisptr ), deref( interval.thisptr ) ) + + def all_nearest_times(self, cy_Point p, Coord fr=0, Coord to=1, cy_Interval interval=None): + """Return all values of t that |self(t) - point| is minimized.""" + if interval is None: + return wrap_vector_double((<Curve *> self.thisptr).allNearestTimes(deref( p.thisptr ), fr, to)) + else: + return wrap_vector_double((<Curve *> self.thisptr).allNearestTimes(deref( p.thisptr ), + deref( interval.thisptr ) )) + + def portion(self, Coord fr=0, Coord to=1, cy_Interval interval=None): + """Return portion of curve, specified by endpoints or interval.""" + if interval is None: + return wrap_BezierCurve( <BezierCurve> deref(<BezierCurve *> + (<Curve *> self.thisptr).portion( fr, to ) + ) ) + else: + return wrap_BezierCurve( <BezierCurve> deref(<BezierCurve *> + (<Curve *> self.thisptr).portion(deref( interval.thisptr )) + ) ) + + def duplicate(self): + """Duplicate the curve.""" + return wrap_BezierCurve( deref( <BezierCurve *> self.thisptr.duplicate())) + + def reverse(self): + """Return curve with reversed time.""" + return wrap_BezierCurve( deref( <BezierCurve *> self.thisptr.reverse())) + + def transformed(self, t): + """Transform curve by affine transform.""" + cdef Affine at + if is_transform(t): + at = get_Affine(t) + return wrap_BezierCurve( deref( <BezierCurve *> self.thisptr.transformed( at ))) + + def derivative(self): + """Return curve's derivative.""" + return wrap_BezierCurve( deref( <BezierCurve *> self.thisptr.derivative())) + + def degrees_of_freedom(self): + """Return number of independent parameters needed to specify the curve.""" + return self.thisptr.degreesOfFreedom() + + def roots(self, Coord v, Dim2 d): + """Find time values where self(t)[d] == v.""" + return wrap_vector_double(self.thisptr.roots(v, d)) + + def length(self, Coord tolerance = 0.01): + """Return length of curve, within give tolerance.""" + return self.thisptr.length(tolerance) + + def point_at(self, Coord t): + """Equivalent to self(t).""" + return wrap_Point(self.thisptr.pointAt(t)) + + def point_and_derivatives(self, Coord t, unsigned int n): + """Return point and at least first n derivatives at point t in list.""" + return wrap_vector_point(self.thisptr.pointAndDerivatives(t, n)) + + def value_at(self, Coord t, Dim2 d): + """Equivalent to self(t)[d].""" + return self.thisptr.valueAt(t, d) + + def to_SBasis(self): + """Convert self to pair of SBasis functions.""" + return wrap_D2_SBasis(self.thisptr.toSBasis()) + + def winding(self, cy_Point p): + """Return winding number around specified point.""" + return (<Curve *> self.thisptr).winding(deref(p.thisptr)) + + def unit_tangent_at(self, Coord t, int n = 3): + """Return tangent at self(t). + + Parameter n specifies how many derivatives to take into account.""" + return wrap_Point((<Curve *> self.thisptr).unitTangentAt(t, n)) + +cdef cy_BezierCurve wrap_BezierCurve(BezierCurve p): + cdef vector[Point] points = make_vector_point([cy_Point(), cy_Point()]) + cdef BezierCurve * retp = create(p.controlPoints()) + cdef cy_BezierCurve r = cy_BezierCurve.__new__(cy_BezierCurve, [cy_Point(), cy_Point()]) + r.thisptr = retp + return r + + +cdef class cy_LineSegment(cy_BezierCurve): + + """Bezier curve with fixed order 1. + + This class inherits from BezierCurve. + + Corresponds to LineSegment in 2geom. BezierCurveN is not wrapped. + """ + + def __cinit__(self, cy_Point p0=None, + cy_Point p1=cy_Point()): + """Create new LineSegment from it's endpoints.""" + if p0 is None: + self.thisptr = <BezierCurve *> new LineSegment() + else: + self.thisptr = <BezierCurve *> new LineSegment( deref(p0.thisptr), + deref(p1.thisptr)) + + @classmethod + def from_beziers(cls, cy_Bezier b0, cy_Bezier b1): + """Create LineSegment from two linear beziers.""" + return wrap_LineSegment( LineSegment(deref(b0.thisptr), deref(b1.thisptr)) ) + + def subdivide(self, Coord t): + """Get two LineSegments, from 0 to t and from t to 1.""" + cdef pair[LineSegment, LineSegment] p = (<LineSegment *> self.thisptr).subdivide(t) + return ( wrap_LineSegment(p.first), wrap_LineSegment(p.second) ) + + def duplicate(self): + """Duplicate the curve.""" + return wrap_LineSegment( deref( <LineSegment *> self.thisptr.duplicate())) + + def portion(self, double fr=0, double to=1, cy_Interval interval=None): + """Return portion of curve, specified by endpoints or interval.""" + if interval is None: + return wrap_LineSegment( deref( <LineSegment *> self.thisptr.portion( fr, to ) ) ) + else: + return wrap_LineSegment( deref( <LineSegment *> + (<Curve *> self.thisptr).portion( deref( interval.thisptr )) + ) ) + + def reverse(self): + """Return curve with reversed time.""" + return wrap_LineSegment( deref( <LineSegment *> self.thisptr.reverse())) + + def transformed(self, t): + """Transform curve by affine transform.""" + cdef Affine at + if is_transform(t): + at = get_Affine(t) + return wrap_LineSegment( deref( <LineSegment *> self.thisptr.transformed( at ))) + + def derivative(self): + """Return curve's derivative.""" + return wrap_LineSegment( deref( <LineSegment *> self.thisptr.derivative())) + +cdef cy_LineSegment wrap_LineSegment(LineSegment p): + cdef LineSegment * retp = new LineSegment() + retp[0] = p + cdef cy_LineSegment r = cy_LineSegment.__new__(cy_LineSegment) + r.thisptr = <BezierCurve* > retp + return r + + +cdef class cy_QuadraticBezier(cy_BezierCurve): + + """Bezier curve with fixed order 2. + + This class inherits from BezierCurve. + + Corresponds to QuadraticBezier in 2geom. BezierCurveN is not wrapped. + """ + + def __cinit__(self, cy_Point p0=None, + cy_Point p1=cy_Point(), + cy_Point p2=cy_Point()): + """Create new QuadraticBezier from three control points.""" + if p0 is None: + self.thisptr = <BezierCurve *> new QuadraticBezier() + else: + self.thisptr = <BezierCurve *> new QuadraticBezier( deref( p0.thisptr ), + deref( p1.thisptr ), + deref( p2.thisptr ) ) + + @classmethod + def from_beziers(cls, cy_Bezier b0, cy_Bezier b1): + """Create QuadraticBezier from two quadratic bezier functions.""" + return wrap_QuadraticBezier( QuadraticBezier(deref(b0.thisptr), deref(b1.thisptr)) ) + + def subdivide(self, Coord t): + """Get two QuadraticBeziers, from 0 to t and from t to 1.""" + cdef pair[QuadraticBezier, QuadraticBezier] p = (<QuadraticBezier *> self.thisptr).subdivide(t) + return ( wrap_QuadraticBezier(p.first), wrap_QuadraticBezier(p.second) ) + + def duplicate(self): + """Duplicate the curve.""" + return wrap_QuadraticBezier( deref( <QuadraticBezier *> self.thisptr.duplicate())) + + def portion(self, double fr=0, double to=1, cy_Interval interval=None): + """Return portion of curve, specified by endpoints or interval.""" + if interval is None: + return wrap_QuadraticBezier( deref( <QuadraticBezier *> self.thisptr.portion( fr, to ) ) ) + else: + return wrap_QuadraticBezier( deref( <QuadraticBezier *> + (<Curve *> self.thisptr).portion( deref( interval.thisptr )) + ) ) + + def reverse(self): + """Return curve with reversed time.""" + return wrap_QuadraticBezier( deref( <QuadraticBezier *> self.thisptr.reverse())) + + def transformed(self, t): + """Transform curve by affine transform.""" + cdef Affine at + if is_transform(t): + at = get_Affine(t) + return wrap_QuadraticBezier( deref( <QuadraticBezier *> self.thisptr.transformed( at ))) + + def derivative(self): + """Return curve's derivative.""" + return wrap_LineSegment( deref( <LineSegment *> self.thisptr.derivative())) + +cdef cy_QuadraticBezier wrap_QuadraticBezier(QuadraticBezier p): + cdef QuadraticBezier * retp = new QuadraticBezier() + retp[0] = p + cdef cy_QuadraticBezier r = cy_QuadraticBezier.__new__(cy_QuadraticBezier) + r.thisptr = <BezierCurve* > retp + return r + +cdef class cy_CubicBezier(cy_BezierCurve): + + """Bezier curve with fixed order 2. + + This class inherits from BezierCurve. + + Corresponds to QuadraticBezier in 2geom. BezierCurveN is not wrapped. + """ + + def __cinit__(self, cy_Point p0=None, + cy_Point p1=cy_Point(), + cy_Point p2=cy_Point(), + cy_Point p3=cy_Point()): + """Create new CubicBezier from four control points.""" + if p0 is None: + self.thisptr = <BezierCurve *> new CubicBezier() + else: + self.thisptr = <BezierCurve *> new CubicBezier( deref( p0.thisptr ), + deref( p1.thisptr ), + deref( p2.thisptr ), + deref( p3.thisptr ) ) + + @classmethod + def from_beziers(cls, cy_Bezier b0, cy_Bezier b1): + """Create CubicBezier from two cubic bezier functions.""" + return wrap_CubicBezier( CubicBezier(deref(b0.thisptr), deref(b1.thisptr)) ) + + def subdivide(self, Coord t): + """Get two CubicBeziers, from 0 to t and from t to 1.""" + cdef pair[CubicBezier, CubicBezier] p = (<CubicBezier *> self.thisptr).subdivide(t) + return ( wrap_CubicBezier(p.first), wrap_CubicBezier(p.second) ) + + def duplicate(self): + """Duplicate the curve.""" + return wrap_CubicBezier( deref( <CubicBezier *> self.thisptr.duplicate())) + + def portion(self, double fr=0, double to=1, cy_Interval interval=None): + """Return portion of curve, specified by endpoints or interval.""" + if interval is None: + return wrap_CubicBezier( deref( <CubicBezier *> self.thisptr.portion( fr, to ) ) ) + else: + return wrap_CubicBezier( deref( <CubicBezier *> + (<Curve *> self.thisptr).portion( deref( interval.thisptr )) + ) ) + + def reverse(self): + """Return curve with reversed time.""" + return wrap_CubicBezier( deref( <CubicBezier *> self.thisptr.reverse())) + + def transformed(self, t): + """Transform curve by affine transform.""" + cdef Affine at + if is_transform(t): + at = get_Affine(t) + return wrap_CubicBezier( deref( <CubicBezier *> self.thisptr.transformed( at ))) + + def derivative(self): + """Return curve's derivative.""" + return wrap_QuadraticBezier( deref( <QuadraticBezier *> self.thisptr.derivative())) + +cdef cy_CubicBezier wrap_CubicBezier(CubicBezier p): + cdef CubicBezier * retp = new CubicBezier() + retp[0] = p + cdef cy_CubicBezier r = cy_CubicBezier.__new__(cy_CubicBezier) + r.thisptr = <BezierCurve* > retp + return r + +#~ cdef class cy_BezierCurveN(cy_BezierCurve): + + +cdef class cy_HLineSegment(cy_LineSegment): + + """Horizontal line segment. + + This class corresponds to HLineSegment in 2geom. + """ + + def __cinit__(self, cy_Point p0=None, cy_Point p1=cy_Point()): + """Create HLineSegment from it's endpoints.""" + if p0 is None: + self.thisptr = <BezierCurve *> new HLineSegment() + else: + self.thisptr = <BezierCurve *> new HLineSegment( deref( p0.thisptr ), deref( p1.thisptr ) ) + + @classmethod + def from_points(cls, cy_Point p0, cy_Point p1): + """Create HLineSegment from it's endpoints.""" + return wrap_HLineSegment( HLineSegment( deref(p0.thisptr), + deref(p1.thisptr)) ) + + @classmethod + def from_point_length(cls, cy_Point p, Coord length): + return wrap_HLineSegment( HLineSegment( deref( p.thisptr ), length ) ) + + def set_initial(self, cy_Point p): + """Set initial point of curve.""" + (<AxisLineSegment_X *> self.thisptr).setInitial( deref(p.thisptr) ) + + def set_final(self, cy_Point p): + """Set final point of curve.""" + (<AxisLineSegment_X *> self.thisptr).setFinal( deref(p.thisptr) ) + + def bounds_fast(self): + """Return bounding rectangle for curve. + + This method is fast, but does not guarantee to give smallest + rectangle. + """ + return wrap_Rect( (<AxisLineSegment_X *> self.thisptr).boundsFast() ) + + def bounds_exact(self): + """Return exact bounding rectangle for curve. + + This may take a while. + """ + return wrap_Rect( (<AxisLineSegment_X *> self.thisptr).boundsExact() ) + + def degrees_of_freedom(self): + """Return number of independent parameters needed to specify the curve.""" + return (<AxisLineSegment_X *> self.thisptr).degreesOfFreedom() + + def roots(self, Coord v, Dim2 d): + """Find time values where self(t)[d] == v.""" + return wrap_vector_double( (<AxisLineSegment_X *> self.thisptr).roots(v, d) ) + + def nearest_time(self, cy_Point p, Coord fr=0, Coord to=1, cy_Interval interval=None): + """Return such t that |self(t) - point| is minimized.""" + if interval is None: + return (<AxisLineSegment_X *> self.thisptr).nearestTime(deref( p.thisptr ), fr, to) + else: + return (<Curve *> self.thisptr).nearestTime(deref( p.thisptr ), + deref( ( interval.thisptr ) ) ) + + def point_at(self, Coord t): + """Equivalent to self(t).""" + return wrap_Point((<AxisLineSegment_X *> self.thisptr).pointAt(t)) + + def value_at(self, Coord t, Dim2 d): + """Equivalent to self(t)[d].""" + return (<AxisLineSegment_X *> self.thisptr).valueAt(t, d) + + def point_and_derivatives(self, Coord t, unsigned n): + """Return point and at least first n derivatives at point t in list.""" + return wrap_vector_point( (<AxisLineSegment_X *> self.thisptr).pointAndDerivatives(t, n) ) + + def get_Y(self): + """Get distance of self from y-axis.""" + return (<HLineSegment *> self.thisptr).getY() + + def set_initial_X(self, Coord x): + """Set initial point's X coordinate.""" + (<HLineSegment *> self.thisptr).setInitialX(x) + + def set_final_X(self, Coord x): + """Set final point's X coordinate.""" + (<HLineSegment *> self.thisptr).setFinalX(x) + + def set_Y(self, Coord y): + """Set Y coordinate of points.""" + (<HLineSegment *> self.thisptr).setY(y) + + def subdivide(self, Coord t): + """Return two HLineSegments subdivided at t.""" + cdef pair[HLineSegment, HLineSegment] p = (<HLineSegment *> self.thisptr).subdivide(t) + return (wrap_HLineSegment(p.first), wrap_HLineSegment(p.second)) + + def duplicate(self): + """Duplicate the curve.""" + return wrap_HLineSegment( deref(<HLineSegment *> self.thisptr.duplicate()) ) + + def portion(self, Coord fr=0, Coord to=1, cy_Interval interval=None): + """Return portion of curve, specified by endpoints or interval.""" + if interval is None: + return wrap_HLineSegment( deref( <HLineSegment *> self.thisptr.portion( fr, to ) ) ) + else: + return wrap_HLineSegment( deref( <HLineSegment *> + (<Curve *> self.thisptr).portion( deref( interval.thisptr ) ) + ) ) + + def reverse(self): + """Return curve with reversed time.""" + return wrap_HLineSegment( deref(<HLineSegment *> self.thisptr.reverse()) ) + + def transformed(self, t): + """Transform curve by affine transform.""" + cdef Affine at + if is_transform(t): + at = get_Affine(t) + return wrap_LineSegment( deref(<LineSegment *> self.thisptr.transformed( at )) ) + + def derivative(self): + """Return curve's derivative.""" + return wrap_HLineSegment( deref(<HLineSegment *> self.thisptr.derivative()) ) + +cdef cy_HLineSegment wrap_HLineSegment(HLineSegment p): + cdef HLineSegment * retp = new HLineSegment() + retp[0] = p + cdef cy_HLineSegment r = cy_HLineSegment.__new__(cy_HLineSegment) + r.thisptr = <BezierCurve *> retp + return r + +cdef class cy_VLineSegment(cy_LineSegment): + + """Vertical line segment. + + This class corresponds to HLineSegment in 2geom. + """ + + def __cinit__(self, cy_Point p0=None, cy_Point p1=cy_Point()): + """Create VLineSegment from it's endpoints.""" + if p0 is None: + self.thisptr = <BezierCurve *> new VLineSegment() + else: + self.thisptr = <BezierCurve *> new VLineSegment( deref( p0.thisptr ), deref( p1.thisptr ) ) + + @classmethod + def from_points(cls, cy_Point p0, cy_Point p1): + """Create VLineSegment from it's endpoints.""" + return wrap_VLineSegment( VLineSegment( deref(p0.thisptr), + deref(p1.thisptr)) ) + + @classmethod + def from_point_length(cls, cy_Point p, Coord length): + return wrap_VLineSegment( VLineSegment( deref( p.thisptr ), length ) ) + + def set_initial(self, cy_Point p): + """Set initial point of curve.""" + (<AxisLineSegment_Y *> self.thisptr).setInitial( deref(p.thisptr) ) + + def set_final(self, cy_Point p): + """Set final point of curve.""" + (<AxisLineSegment_Y *> self.thisptr).setFinal( deref(p.thisptr) ) + + def bounds_fast(self): + """Return bounding rectangle for curve. + + This method is fast, but does not guarantee to give smallest + rectangle. + """ + return wrap_Rect( (<AxisLineSegment_Y *> self.thisptr).boundsFast() ) + + def bounds_exact(self): + """Return exact bounding rectangle for curve. + + This may take a while. + """ + return wrap_Rect( (<AxisLineSegment_Y *> self.thisptr).boundsExact() ) + + def degrees_of_freedom(self): + """Return number of independent parameters needed to specify the curve.""" + return (<AxisLineSegment_Y *> self.thisptr).degreesOfFreedom() + + def roots(self, Coord v, Dim2 d): + """Find time values where self(t)[d] == v.""" + return wrap_vector_double( (<AxisLineSegment_Y *> self.thisptr).roots(v, d) ) + + def nearest_time(self, cy_Point p, Coord fr=0, Coord to=1, cy_Interval interval=None): + """Return such t that |self(t) - point| is minimized.""" + if interval is None: + return (<AxisLineSegment_Y *> self.thisptr).nearestTime(deref( p.thisptr ), fr, to) + else: + return (<Curve *> self.thisptr).nearestTime(deref( p.thisptr ), + deref( ( interval.thisptr ) ) ) + + def point_at(self, Coord t): + """Equivalent to self(t).""" + return wrap_Point((<AxisLineSegment_Y *> self.thisptr).pointAt(t)) + + def value_at(self, Coord t, Dim2 d): + """Equivalent to self(t)[d].""" + return (<AxisLineSegment_Y *> self.thisptr).valueAt(t, d) + + def point_and_derivatives(self, Coord t, unsigned n): + """Return point and at least first n derivatives at point t in list.""" + return wrap_vector_point( (<AxisLineSegment_Y *> self.thisptr).pointAndDerivatives(t, n) ) + + def get_X(self): + return (<VLineSegment *> self.thisptr).getX() + + def set_initial_Y(self, Coord y): + (<VLineSegment *> self.thisptr).setInitialY(y) + + def set_final_Y(self, Coord y): + (<VLineSegment *> self.thisptr).setFinalY(y) + + def set_X(self, Coord x): + (<VLineSegment *> self.thisptr).setX(x) + + def subdivide(self, Coord t): + """Return two HLineSegments subdivided at t.""" + cdef pair[VLineSegment, VLineSegment] p = (<VLineSegment *> self.thisptr).subdivide(t) + return (wrap_VLineSegment(p.first), wrap_VLineSegment(p.second)) + + def duplicate(self): + """Duplicate the curve.""" + return wrap_VLineSegment( deref(<VLineSegment *> self.thisptr.duplicate()) ) + + def portion(self, Coord fr=0, Coord to=1, cy_Interval interval=None): + """Return portion of curve, specified by endpoints or interval.""" + if interval is None: + return wrap_VLineSegment( deref( <VLineSegment *> self.thisptr.portion( fr, to ) ) ) + else: + return wrap_VLineSegment( deref( <VLineSegment *> + (<Curve *> self.thisptr).portion( deref( interval.thisptr ) ) + ) ) + + def reverse(self): + """Return curve with reversed time.""" + return wrap_VLineSegment( deref(<VLineSegment *> self.thisptr.reverse()) ) + + def transformed(self, t): + """Transform curve by affine transform.""" + cdef Affine at + if is_transform(t): + at = get_Affine(t) + return wrap_LineSegment( deref(<LineSegment *> self.thisptr.transformed( at )) ) + + def derivative(self): + """Return curve's derivative.""" + return wrap_VLineSegment( deref(<VLineSegment *> self.thisptr.derivative()) ) + +cdef cy_VLineSegment wrap_VLineSegment(VLineSegment p): + cdef VLineSegment * retp = new VLineSegment() + retp[0] = p + cdef cy_VLineSegment r = cy_VLineSegment.__new__(cy_VLineSegment) + r.thisptr = <BezierCurve *> retp + return r + +cdef class cy_EllipticalArc: + + """Elliptical arc. + + Corresponds to EllipticalArc class in 2geom. + """ + + def __cinit__(self, cy_Point ip = cy_Point(0, 0), + Coord rx = 0, + Coord ry = 0, + Coord rot_angle = 0, + bint large_arc = True, + bint sweep = True, + cy_Point fp = cy_Point(0, 0)): + """Create Elliptical arc from it's major axis and rays.""" + self.thisptr = new EllipticalArc(deref( ip.thisptr ), rx, ry, rot_angle, large_arc, sweep, deref( fp.thisptr )) + + def __dealloc__(self): + del self.thisptr + + def __call__(self, Coord t): + """Get point at time value t.""" + return wrap_Point( deref(<Curve *> self.thisptr)(t) ) + #Curve methods + + def length(self, Coord tolerance = 0.01): + """Return length of curve, within give tolerance.""" + return (<Curve *> self.thisptr).length(tolerance) + + #AngleInterval methods + + def initial_angle(self): + """Get initial Angle of arc.""" + return wrap_Angle((<AngleInterval *> self.thisptr).initialAngle()) + + def final_angle(self): + """Get final Angle of arc.""" + return wrap_Angle((<AngleInterval *> self.thisptr).finalAngle()) + + def angle_at(self, Coord t): + """Get Angle from time value.""" + return wrap_Angle((<AngleInterval *> self.thisptr).angleAt(t)) + + def contains(self, cy_Angle a): + """Test whether arc contains angle.""" + return (<AngleInterval *> self.thisptr).contains(deref( a.thisptr )) + + def extent(self): + """Get extent of angle interval.""" + return (<AngleInterval *> self.thisptr).extent() + + def angle_interval(self): + """Get underlying angle Interval.""" + return wrap_Interval(self.thisptr.angleInterval()) + + def rotation_angle(self): + """Return rotation angle of major axis.""" + return wrap_Angle(self.thisptr.rotationAngle()) + + def ray(self, Dim2 d): + """Access rays with X or Y.""" + return self.thisptr.ray(d) + + def rays(self): + """Get rays as a point.""" + return wrap_Point(self.thisptr.rays()) + + def large_arc(self): + """Check if large arc flag is set.""" + return self.thisptr.largeArc() + + def sweep(self): + """Check if sweep flag is set.""" + return self.thisptr.sweep() + + def chord(self): + """Return chord of arc.""" + return wrap_LineSegment(self.thisptr.chord()) + + def set(self, cy_Point ip, double rx, double ry, double rot_angle, bint large_arc, bint sweep, cy_Point fp): + """Set arc's properties.""" + self.thisptr.set(deref( ip.thisptr ), rx, ry, rot_angle, large_arc, sweep, deref( fp.thisptr )) + + def set_extremes(self, cy_Point ip, cy_Point fp): + """Set endpoints of arc.""" + self.thisptr.setExtremes(deref( ip.thisptr ), deref( fp.thisptr )) + + def center(self, coordinate=None): + """Return center of ellipse, or it's coordinate.""" + if coordinate is None: + return wrap_Point(self.thisptr.center()) + else: + return self.thisptr.center(int(coordinate)) + + def sweep_angle(self): + """Equivalent to self.extent()""" + return self.thisptr.sweepAngle() + + def contains_angle(self, Coord angle): + """Test whether arc contains angle. + + Equivalent to self.contains(Angle(a)) + """ + return self.thisptr.containsAngle(angle) + + def point_at_angle(self, Coord a): + """Get point of arc at specified angle.""" + return wrap_Point(self.thisptr.pointAtAngle(a)) + + def value_at_angle(self, Coord a, Dim2 d): + """Equivalent to self.point_at_angle(a)[d]""" + return self.thisptr.valueAtAngle(a, d) + + def unit_circle_transform(self): + """Get Affine transform needed to transform unit circle to ellipse.""" + return wrap_Affine(self.thisptr.unitCircleTransform()) + + def is_SVG_compliant(self): + """Check whether arc is SVG compliant + + SVG has special specification for degenerated ellipse.""" + return self.thisptr.isSVGCompliant() + + def subdivide(self, Coord t): + """Return two arcs, subdivided at time t.""" + cdef pair[EllipticalArc, EllipticalArc] r = self.thisptr.subdivide(t) + return (wrap_EllipticalArc(r.first), wrap_EllipticalArc(r.second)) + + def initial_point(self): + """Get self(0).""" + return wrap_Point(self.thisptr.initialPoint()) + + def final_point(self): + """Get self(1).""" + return wrap_Point(self.thisptr.finalPoint()) + + def duplicate(self): + """Duplicate the curve.""" + return wrap_EllipticalArc( deref(<EllipticalArc *> self.thisptr.duplicate()) ) + + def set_initial(self, cy_Point p): + """Set initial point of curve.""" + self.thisptr.setInitial(deref( p.thisptr )) + + def set_final(self, cy_Point p): + """Set final point of curve.""" + self.thisptr.setFinal(deref( p.thisptr )) + + def is_degenerate(self): + """Curve is degenerate if its length is zero.""" + return self.thisptr.isDegenerate() + + def bounds_fast(self): + """Return bounding rectangle for curve. + + This method is fast, but does not guarantee to give smallest + rectangle. + """ + return wrap_Rect(self.thisptr.boundsFast()) + + def bounds_exact(self): + """Return exact bounding rectangle for curve. + + This may take a while. + """ + return wrap_Rect(self.thisptr.boundsExact()) + + def bounds_local(self, cy_OptInterval i, unsigned int deg): + """Return bounding rectangle to portion of curve.""" + return wrap_OptRect(self.thisptr.boundsLocal(deref( i.thisptr ), deg)) + + def roots(self, double v, Dim2 d): + """Find time values where self(t)[d] == v.""" + return wrap_vector_double(self.thisptr.roots(v, d)) + + def nearest_time(self, cy_Point p, Coord fr=0, Coord to=1, cy_Interval interval=None): + """Return such t that |self(t) - point| is minimized.""" + if interval is None: + return self.thisptr.nearestTime(deref( p.thisptr ), fr, to) + else: + return (<Curve *> self.thisptr).nearestTime(deref( p.thisptr ), + deref( interval.thisptr ) ) + + def all_nearest_times(self, cy_Point p, Coord fr=0, Coord to=1, cy_Interval interval=None): + """Return all values of t that |self(t) - point| is minimized.""" + if interval is None: + return wrap_vector_double( (<Curve *> self.thisptr).allNearestTimes(deref( p.thisptr ), fr, to)) + else: + return wrap_vector_double( (<Curve *> self.thisptr).allNearestTimes(deref( p.thisptr ), deref( interval.thisptr ) )) + + def degrees_of_freedom(self): + """Return number of independent parameters needed to specify the curve.""" + return self.thisptr.degreesOfFreedom() + + def derivative(self): + """Return curve's derivative.""" + return wrap_EllipticalArc( deref(<EllipticalArc *> self.thisptr.derivative()) ) + + def transformed(self, cy_Affine m): + """Transform curve by affine transform.""" + return wrap_EllipticalArc( deref(<EllipticalArc *> self.thisptr.transformed(deref( m.thisptr ))) ) + + def point_and_derivatives(self, Coord t, unsigned int n): + """Return point and at least first n derivatives at point t in list.""" + return wrap_vector_point(self.thisptr.pointAndDerivatives(t, n)) + + def to_SBasis(self): + """Convert to pair of SBasis polynomials.""" + cdef D2[SBasis] r = self.thisptr.toSBasis() + return ( wrap_SBasis(r[0]), wrap_SBasis(r[1]) ) + + def value_at(self, Coord t, Dim2 d): + """Equivalent to self(t)[d].""" + return self.thisptr.valueAt(t, d) + + def point_at(self, Coord t): + """Equivalent to self(t).""" + return wrap_Point(self.thisptr.pointAt(t)) + + + def portion(self, Coord fr=0, Coord to=1, cy_Interval interval=None): + """Return portion of curve, specified by endpoints or interval.""" + if interval is None: + return wrap_EllipticalArc( deref( <EllipticalArc *> self.thisptr.portion( fr, to ) ) ) + else: + return wrap_EllipticalArc( deref( <EllipticalArc *> (<Curve *> self.thisptr).portion( deref( interval.thisptr ) ) ) ) + + def reverse(self): + """Return curve with reversed time.""" + return wrap_EllipticalArc( deref(<EllipticalArc *> self.thisptr.reverse()) ) + + def winding(self, cy_Point p): + """Return winding number around specified point.""" + return (<Curve *> self.thisptr).winding(deref(p.thisptr)) + + def unit_tangent_at(self, Coord t, int n = 3): + """Return tangent at self(t). + + Parameter n specifies how many derivatives to take into account.""" + return wrap_Point((<Curve *> self.thisptr).unitTangentAt(t, n)) + +cdef cy_EllipticalArc wrap_EllipticalArc(EllipticalArc p): + cdef EllipticalArc * retp = new EllipticalArc() + retp[0] = p + cdef cy_EllipticalArc r = cy_EllipticalArc.__new__(cy_EllipticalArc) + r.thisptr = retp + return r + +#TODO move somewhere else + +cdef object wrap_vector_interval(vector[Interval] v): + r = [] + cdef unsigned int i + for i in range(v.size()): + r.append( wrap_Interval(v[i])) + return r + + +cdef bint is_Curve(object c): + return any([ + isinstance(c, cy_Curve), + isinstance(c, cy_SBasisCurve), + isinstance(c, cy_BezierCurve), + isinstance(c, cy_EllipticalArc)]) + +cdef Curve * get_Curve_p(object c): + if isinstance(c, cy_Curve): + return (<cy_Curve> c).thisptr + elif isinstance(c, cy_SBasisCurve): + return <Curve *> (<cy_SBasisCurve> c).thisptr + elif isinstance(c, cy_BezierCurve): + return <Curve *> (<cy_BezierCurve> c).thisptr + elif isinstance(c, cy_EllipticalArc): + return <Curve *> (<cy_EllipticalArc> c).thisptr + return NULL + |