diff options
Diffstat (limited to 'src/cython/_cy_affine.pyx')
-rw-r--r-- | src/cython/_cy_affine.pyx | 736 |
1 files changed, 736 insertions, 0 deletions
diff --git a/src/cython/_cy_affine.pyx b/src/cython/_cy_affine.pyx new file mode 100644 index 0000000..19aa9ef --- /dev/null +++ b/src/cython/_cy_affine.pyx @@ -0,0 +1,736 @@ +from cython.operator cimport dereference as deref + +from numbers import Number + + +cdef class cy_Affine: + + """Class representing affine transform in 2D plane. + + Corresponds to Affine class in 2geom. + """ + + def __cinit__(self, c0=None, + Coord c1=0, + Coord c2=0, + Coord c3=1, + Coord c4=0, + Coord c5=0): + """Create Affine instance from either transform or from coefficients.""" + if c0 is None: + self.thisptr = new Affine() + elif is_transform(c0): + self.thisptr = new Affine( get_Affine(c0) ) + else: + self.thisptr = new Affine(<Coord> float(c0) ,c1 ,c2 ,c3 ,c4 ,c5) + + def __str__(self): + """str(self)""" + return "Affine({}, {}, {}, {}, {}, {})".format( self[0], + self[1], + self[2], + self[3], + self[4], + self[5], + ) + def __repr__(self): + """repr(self)""" + return str(self) + + def __dealloc__(self): + del self.thisptr + + def __getitem__(self, int i): + """Get coefficients.""" + if i >= 6: + raise IndexError("Affine has only 6 coefficients.") + return deref(self.thisptr) [i] + + def __mul__(cy_Affine self, other): + """Compose with another transformation.""" + if isinstance(other, cy_Affine): + return wrap_Affine( deref(self.thisptr) * deref( (<cy_Affine> other).thisptr ) ) + elif isinstance(other, cy_Translate): + return wrap_Affine( deref(self.thisptr) * deref( (<cy_Translate> other).thisptr ) ) + elif isinstance(other, cy_Scale): + return wrap_Affine( deref(self.thisptr) * deref( (<cy_Scale> other).thisptr ) ) + elif isinstance(other, cy_Rotate): + return wrap_Affine( deref(self.thisptr) * deref( (<cy_Rotate> other).thisptr ) ) + elif isinstance(other, cy_HShear): + return wrap_Affine( deref(self.thisptr) * deref( (<cy_HShear> other).thisptr ) ) + elif isinstance(other, cy_VShear): + return wrap_Affine( deref(self.thisptr) * deref( (<cy_VShear> other).thisptr ) ) + elif isinstance(other, cy_Zoom): + return wrap_Affine( deref(self.thisptr) * deref( (<cy_Zoom> other).thisptr ) ) + + def __pow__(cy_Affine self, int n, z): + """Compose with self n times.""" + return wrap_Affine(pow( deref(self.thisptr), n )) + + def __richcmp__(cy_Affine self, cy_Affine other, int op): + if op == 2: + return deref(self.thisptr) == deref(other.thisptr) + elif op == 3: + return deref(self.thisptr) != deref(other.thisptr) + + def x_axis(self): + """Transformation of unit x vector without translation.""" + return wrap_Point(self.thisptr.xAxis()) + + def y_axis(self): + """Transformation of unit y vector without translation.""" + return wrap_Point(self.thisptr.yAxis()) + + def translation(self): + """Translation of transformation.""" + return wrap_Point(self.thisptr.translation()) + + def expansion_X(self): + """Expansion of unit x vector.""" + return self.thisptr.expansionX() + + def expansion_Y(self): + """Expansion of unit y vector.""" + return self.thisptr.expansionY() + + def expansion(self): + """Point( self.expansion_X(), self.expansion_Y() )""" + return wrap_Point(self.thisptr.expansion()) + + def set_X_axis(self, cy_Point vec): + """Set transformation of x unit vector without translation.""" + self.thisptr.setXAxis(deref( vec.thisptr )) + + def set_Y_axis(self, cy_Point vec): + """Set transformation of y unit vector without translation.""" + self.thisptr.setYAxis(deref( vec.thisptr )) + + def set_translation(self, cy_Point loc): + """Set translation of origin.""" + self.thisptr.setTranslation(deref( loc.thisptr )) + + def set_expansion_X(self, Coord val): + """Set expansion of x unit vector.""" + self.thisptr.setExpansionX(val) + + def set_expansion_Y(self, Coord val): + """Set expansion of y unit vector.""" + self.thisptr.setExpansionY(val) + + def set_identity(self): + """Set self to identity transformation.""" + self.thisptr.setIdentity() + + def is_identity(self, Coord eps=EPSILON): + """Return true if self is close to identity transform. + + Use second argument eps to specify tolerance. + """ + return self.thisptr.isIdentity(eps) + + def is_translation(self, Coord eps=EPSILON): + """Return true if self is close to transformation. + + Use second argument eps to specify tolerance. + """ + return self.thisptr.isTranslation(eps) + + def is_scale(self, Coord eps=EPSILON): + """Return true if self is close to scale. + + Use second argument eps to specify tolerance. + """ + return self.thisptr.isScale(eps) + + def is_uniform_scale(self, Coord eps=EPSILON): + """Return true if self is close to uniform scale. + + Use second argument eps to specify tolerance. + """ + return self.thisptr.isUniformScale(eps) + + def is_rotation(self, Coord eps=EPSILON): + """Return true if self is close to rotation. + + Use second argument eps to specify tolerance. + """ + return self.thisptr.isRotation(eps) + + def is_HShear(self, Coord eps=EPSILON): + """Return true if self is close to horizontal shear. + + Use second argument eps to specify tolerance. + """ + return self.thisptr.isHShear(eps) + + def is_VShear(self, Coord eps=EPSILON): + """Return true if self is close to vertical shear. + + Use second argument eps to specify tolerance. + """ + return self.thisptr.isVShear(eps) + + def is_nonzero_translation(self, Coord eps=EPSILON): + """Return true if self is close to translation and is identity. + + Use second argument eps to specify tolerance. + """ + return self.thisptr.isNonzeroTranslation(eps) + + def is_nonzero_scale(self, Coord eps=EPSILON): + """Return true if self is close to scale and is identity. + + Use second argument eps to specify tolerance. + """ + return self.thisptr.isNonzeroScale(eps) + + def is_nonzero_uniform_scale(self, Coord eps=EPSILON): + """Return true if self is close to scale and is identity. + + Use second argument eps to specify tolerance. + """ + return self.thisptr.isNonzeroUniformScale(eps) + + def is_nonzero_rotation(self, Coord eps=EPSILON): + """Return true if self is close to rotation and is identity. + + Use second argument eps to specify tolerance. + """ + return self.thisptr.isNonzeroRotation(eps) + + def is_nonzero_HShear(self, Coord eps=EPSILON): + """Return true if self is close to horizontal shear and is not identity. + + Use second argument eps to specify tolerance. + """ + return self.thisptr.isNonzeroHShear(eps) + + def is_nonzero_VShear(self, Coord eps=EPSILON): + """Return true if self is close to vertical shear and is not identity. + + Use second argument eps to specify tolerance. + """ + return self.thisptr.isNonzeroVShear(eps) + + def is_zoom(self, Coord eps=EPSILON): + """Return true if self is close to zoom. + + Use second argument eps to specify tolerance. + """ + return self.thisptr.isZoom(eps) + + def preserves_area(self, Coord eps=EPSILON): + """Return true if areas are preserved after transformation + + Use second argument eps to specify tolerance. + """ + return self.thisptr.preservesArea(eps) + + def preserves_angles(self, Coord eps=EPSILON): + """Return true if angles are preserved after transformation + + Use second argument eps to specify tolerance. + """ + return self.thisptr.preservesAngles(eps) + + def preserves_distances(self, Coord eps=EPSILON): + """Return true if distances are preserved after transformation + + Use second argument eps to specify tolerance. + """ + return self.thisptr.preservesDistances(eps) + + def flips(self): + """Return true if transformation flips - it has negative scaling.""" + return self.thisptr.flips() + + def is_singular(self, Coord eps=EPSILON): + """Check whether transformation matrix is singular.""" + return self.thisptr.isSingular(eps) + + def without_translation(self): + """Return transformation without translation part.""" + return wrap_Affine(self.thisptr.withoutTranslation()) + + def inverse(self): + """Return inverse transformation.""" + return wrap_Affine(self.thisptr.inverse()) + + def det(self): + """Return determinant of transformation matrix.""" + return self.thisptr.det() + + def descrim2(self): + """Return absolute value of self.det()""" + return self.thisptr.descrim2() + + def descrim(self): + """Return square root of self.descrim2()""" + return self.thisptr.descrim() + + @classmethod + def identity(self): + """Create identity transformation.""" + return wrap_Affine(a_identity()) + + @classmethod + def are_near(cls, A, B, Coord eps=EPSILON): + """Test if two transforms are near.""" + if is_transform(A) & is_transform(B): + return are_near(get_Affine(A), get_Affine(B), eps) + + @classmethod + def reflection(cls, cy_Point vector, cy_Point origin): + """Create transformation reflecting along line specified by vector and origin.""" + return wrap_Affine( reflection( deref(vector.thisptr), deref(origin.thisptr) ) ) + +cdef cy_Affine wrap_Affine(Affine p): + cdef Affine * retp = new Affine() + retp[0] = p + cdef cy_Affine r = cy_Affine.__new__(cy_Affine) + r.thisptr = retp + return r + +cdef Affine get_Affine(t): + if isinstance(t, cy_Affine ): + return deref( (<cy_Affine> t).thisptr ) + elif isinstance(t, cy_Translate): + return <Affine> deref( (<cy_Translate> t).thisptr ) + elif isinstance(t, cy_Scale): + return <Affine> deref( (<cy_Scale> t).thisptr ) + elif isinstance(t, cy_Rotate): + return <Affine> deref( (<cy_Rotate> t).thisptr ) + elif isinstance(t, cy_HShear): + return <Affine> deref( (<cy_HShear> t).thisptr ) + elif isinstance(t, cy_VShear): + return <Affine> deref( (<cy_VShear> t).thisptr ) + elif isinstance(t, cy_Zoom): + return <Affine> deref( (<cy_Zoom> t).thisptr ) + +cdef bint is_transform(t): + return any([isinstance(t, cy_Affine), + isinstance(t, cy_Translate), + isinstance(t, cy_Scale), + isinstance(t, cy_Rotate), + isinstance(t, cy_HShear), + isinstance(t, cy_VShear), + isinstance(t, cy_Zoom) + ]) + + +cdef class cy_Translate: + + """Translation in 2D plane + + Corresponds to Translate class in 2geom. + """ + + def __cinit__(self, *args): + """Create Translate instance form point or it's two coordinates.""" + if len(args) == 0: + self.thisptr = new Translate() + elif len(args) == 1: + self.thisptr = new Translate( deref( (<cy_Point> args[0]).thisptr ) ) + elif len(args) == 2: + self.thisptr = new Translate(float(args[0]), float(args[1])) + + def __dealloc__(self): + del self.thisptr + + + def __getitem__(self, Dim2 dim): + """Get components of displacement vector.""" + return deref( self.thisptr ) [dim] + + def __mul__(cy_Translate self, o): + """Compose with another transformation.""" + if isinstance(o, cy_Translate): + return wrap_Translate(deref( self.thisptr ) * deref( (<cy_Translate>o).thisptr )) + elif is_transform(o): + return wrap_Affine(deref(self.thisptr) * get_Affine(o)) + + def __pow__(cy_Translate self, int n, z): + """Compose with self n times.""" + return wrap_Translate(pow( deref(self.thisptr), n )) + + def vector(self): + """Get displacement vector.""" + return wrap_Point(self.thisptr.vector()) + + def inverse(self): + """Return inverse transformation.""" + return wrap_Translate(self.thisptr.inverse()) + + @classmethod + def identity(self): + """Create identity translation.""" + return wrap_Translate(t_identity()) + + def __richcmp__(cy_Translate self, cy_Translate t, op): + if op == 2: + return deref(self.thisptr) == deref(t.thisptr) + elif op == 3: + return deref(self.thisptr) != deref(t.thisptr) + +cdef cy_Translate wrap_Translate(Translate p): + cdef Translate * retp = new Translate() + retp[0] = p + cdef cy_Translate r = cy_Translate.__new__(cy_Translate) + r.thisptr = retp + return r + + +cdef class cy_Scale: + + """Scale in 2D plane. + + Corresponds to Scale in 2geom. + """ + + def __cinit__(self, *args): + """Create scale from number, point or it's two coordinates. + + One number creates uniform scale, point or two numbers create + scale with different x and y scale factor. + """ + if len(args) == 0: + self.thisptr = new Scale() + elif len(args) == 1: + if isinstance(args[0], Number): + self.thisptr = new Scale(<Coord> float(args[0])) + elif isinstance(args[0], cy_Point): + self.thisptr = new Scale( deref( (<cy_Point> args[0]).thisptr ) ) + elif len(args) == 2: + self.thisptr = new Scale(float(args[0]), float(args[1])) + + def __dealloc__(self): + del self.thisptr + + def __getitem__(self, Dim2 d): + """Get scale factors for each axis.""" + return deref( self.thisptr ) [d] + + def __mul__(cy_Scale self, o): + """Compose with another transformation.""" + if isinstance(o, cy_Scale): + return wrap_Scale(deref( self.thisptr ) * deref( (<cy_Scale>o).thisptr )) + elif is_transform(o): + return wrap_Affine(deref(self.thisptr) * get_Affine(o)) + + def __pow__(cy_Scale self, int n, z): + """Compose with self n times.""" + return wrap_Scale(pow( deref(self.thisptr), n )) + + def vector(self): + """Get both scale factors as a point.""" + return wrap_Point(self.thisptr.vector()) + + def inverse(self): + """Return inverse transformation.""" + return wrap_Scale(self.thisptr.inverse()) + + @classmethod + def identity(self): + """Create identity scale.""" + return wrap_Scale(s_identity()) + + def __richcmp__(cy_Scale self, cy_Scale s, op): + if op == 2: + return deref(self.thisptr) == deref(s.thisptr) + elif op == 3: + return deref(self.thisptr) != deref(s.thisptr) + +cdef cy_Scale wrap_Scale(Scale p): + cdef Scale * retp = new Scale() + retp[0] = p + cdef cy_Scale r = cy_Scale.__new__(cy_Scale) + r.thisptr = retp + return r + + +cdef class cy_Rotate: + + """Rotation in 2D plane. + + Corresponds to Rotate in 2geom. + """ + + def __cinit__(self, *args): + """Create new Rotate instance, specifying angle. + + Use one number to set the angle, or point/its two coordinates, + using point's angle with x-axis as a rotation angle. + """ + if len(args) == 0: + self.thisptr = new Rotate() + elif len(args) == 1: + if isinstance(args[0], Number): + self.thisptr = new Rotate(<Coord> float(args[0])) + elif isinstance(args[0], cy_Point): + self.thisptr = new Rotate( deref( (<cy_Point> args[0]).thisptr ) ) + elif len(args) == 2: + self.thisptr = new Rotate(float(args[0]), float(args[1])) + + def __dealloc__(self): + del self.thisptr + + def vector(self): + """Return Point(1, 0)*self.""" + return wrap_Point(self.thisptr.vector()) + + def __getitem__(self, Dim2 dim): + """Get components of self.vector()""" + return deref( self.thisptr ) [dim] + + def __mul__(cy_Rotate self, o): + """Compose with another transformation.""" + if isinstance(o, cy_Rotate): + return wrap_Rotate(deref( self.thisptr ) * deref( (<cy_Rotate>o).thisptr )) + elif is_transform(o): + return wrap_Affine(deref(self.thisptr) * get_Affine(o)) + + def __pow__(cy_Rotate self, int n, z): + """Compose with self n times.""" + return wrap_Rotate(pow( deref(self.thisptr), n )) + + def inverse(self): + """Return inverse transformation.""" + return wrap_Rotate(self.thisptr.inverse()) + + @classmethod + def identity(cls): + """Create identity rotation.""" + return wrap_Rotate(r_identity()) + + @classmethod + def from_degrees(cls, Coord deg): + """Create rotation from angle in degrees.""" + return wrap_Rotate(from_degrees(deg)) + + def __richcmp__(cy_Rotate self, cy_Rotate r, op): + if op == 2: + return deref(self.thisptr) == deref(r.thisptr) + elif op == 3: + return deref(self.thisptr) != deref(r.thisptr) + +cdef cy_Rotate wrap_Rotate(Rotate p): + cdef Rotate * retp = new Rotate() + retp[0] = p + cdef cy_Rotate r = cy_Rotate.__new__(cy_Rotate) + r.thisptr = retp + return r + + +cdef class cy_VShear: + + """Vertical shear in 2D plane + + Corresponds to VShear in 2geom. + """ + + def __cinit__(self, Coord h): + """Create VShear instance form shearing factor.""" + self.thisptr = new VShear(h) + + def __dealloc__(self): + del self.thisptr + + def factor(self): + """Get shearing factor.""" + return self.thisptr.factor() + + def set_factor(self, Coord nf): + """Set shearing factor.""" + self.thisptr.setFactor(nf) + + def __mul__(cy_VShear self, o): + """Compose with another transformation.""" + if isinstance(o, cy_VShear): + return wrap_VShear(deref( self.thisptr ) * deref( (<cy_VShear>o).thisptr )) + elif is_transform(o): + return wrap_Affine(deref(self.thisptr) * get_Affine(o)) + + def __pow__(cy_VShear self, int n, z): + """Compose with self n times.""" + return wrap_VShear(pow( deref(self.thisptr), n )) + + def inverse(self): + """Return inverse transformation.""" + return wrap_VShear(self.thisptr.inverse()) + + @classmethod + def identity(cls): + """Create identity VShear.""" + return wrap_VShear( vs_identity() ) + + def __richcmp__(cy_VShear self, cy_VShear hs, op): + if op == 2: + return deref(self.thisptr) == deref(hs.thisptr) + elif op == 3: + return deref(self.thisptr) != deref(hs.thisptr) + + + +cdef cy_VShear wrap_VShear(VShear p): + cdef VShear * retp = new VShear(0) + retp[0] = p + cdef cy_VShear r = cy_VShear.__new__(cy_VShear, 0) + r.thisptr = retp + return r + +cdef class cy_HShear: + + """Horizontal shear in 2D plane + + Corresponds to HShear in 2geom. + """ + + def __cinit__(self, Coord h): + """Create HShear instance form shearing factor.""" + self.thisptr = new HShear(h) + + def __dealloc__(self): + del self.thisptr + + def factor(self): + """Get shearing factor.""" + return self.thisptr.factor() + + def set_factor(self, Coord nf): + """Set shearing factor.""" + self.thisptr.setFactor(nf) + + def __mul__(cy_HShear self, o): + """Compose with another transformation.""" + if isinstance(o, cy_HShear): + return wrap_HShear(deref( self.thisptr ) * deref( (<cy_HShear>o).thisptr )) + elif is_transform(o): + return wrap_Affine(deref(self.thisptr) * get_Affine(o)) + + def __pow__(cy_HShear self, int n, z): + """Compose with self n times.""" + return wrap_HShear(pow( deref(self.thisptr), n )) + + def inverse(self): + """Return inverse transformation.""" + return wrap_HShear(self.thisptr.inverse()) + + @classmethod + def identity(cls): + """Create identity HShear.""" + return wrap_HShear( hs_identity() ) + + def __richcmp__(cy_HShear self, cy_HShear hs, op): + if op == 2: + return deref(self.thisptr) == deref(hs.thisptr) + elif op == 3: + return deref(self.thisptr) != deref(hs.thisptr) + +cdef cy_HShear wrap_HShear(HShear p): + cdef HShear * retp = new HShear(0) + retp[0] = p + cdef cy_HShear r = cy_HShear.__new__(cy_HShear, 0) + r.thisptr = retp + return r + + +cdef class cy_Zoom: + + """Zoom in 2D plane, consisting of uniform scale and translation. + + Corresponds to Zoom in 2geom. + """ + + def __cinit__(self, Coord scale=1, cy_Translate translate=cy_Translate()): + """Create Zoom from scale factor and translation""" + self.thisptr = new Zoom( scale, deref( translate.thisptr ) ) + + def __dealloc__(self): + del self.thisptr + + def __mul__(cy_Zoom self, cy_Zoom z): + """Compose with another transformation.""" + return wrap_Zoom( deref(self.thisptr) * deref( z.thisptr )) + + def __pow__(cy_Zoom self, int n, z): + """Compose with self n times.""" + return wrap_Zoom(pow( deref(self.thisptr), n )) + + def __richcmp__(cy_Zoom self, cy_Zoom z, op): + if op == 2: + return deref(self.thisptr) == deref(z.thisptr) + elif op == 3: + return deref(self.thisptr) != deref(z.thisptr) + + def scale(self): + """Get scale factor.""" + return self.thisptr.scale() + + def set_scale(self, Coord s): + """Set scale factor.""" + self.thisptr.setScale(s) + + def translation(self): + """Get translation as a point.""" + return wrap_Point(self.thisptr.translation()) + + def set_translation(self, cy_Point p): + """Set translation.""" + self.thisptr.setTranslation(deref( p.thisptr )) + + def inverse(self): + """Return inverse transformation.""" + return wrap_Zoom(self.thisptr.inverse()) + + @classmethod + def identity(cls): + """Create identity zoom.""" + return wrap_Zoom(z_identity()) + + @classmethod + def map_rect(self, cy_Rect old_r, cy_Rect new_r): + """Create zooming used to go from old rectangle to new.""" + return wrap_Zoom(map_rect(deref( old_r.thisptr ) ,deref( new_r.thisptr ))) + +cdef cy_Zoom wrap_Zoom(Zoom p): + cdef Zoom * retp = new Zoom(0) + retp[0] = p + cdef cy_Zoom r = cy_Zoom.__new__(cy_Zoom, 0) + r.thisptr = retp + return r + + +cdef class cy_Eigen: + + """Class computing eigenvalues and eigenvectors of 2x2 matrix. + + Corresponds to Eigen class in 2geom. + """ + + cdef Eigen* thisptr + + def __cinit__(self, a): + """Create Eigen form 2D transform or 2x2 list - matrix.""" + cdef Affine at + cdef double m[2][2] + if is_transform(a): + at = get_Affine(a) + self.thisptr = new Eigen(at) + else: + for i in range(2): + for j in range(2): + m[i][j] = a[i][j] + self.thisptr = new Eigen(m) + + def __dealloc__(self): + del self.thisptr + + @property + def vectors(self): + """Eigenvectors of matrix.""" + return (wrap_Point(self.thisptr.vectors[0]), wrap_Point(self.thisptr.vectors[1])) + + @property + def values(self): + """Eigenvalues of matrix.""" + return (self.thisptr.values[0], self.thisptr.values[1]) |