summaryrefslogtreecommitdiffstats
path: root/src/cython/_cy_primitives.pyx
diff options
context:
space:
mode:
Diffstat (limited to 'src/cython/_cy_primitives.pyx')
-rw-r--r--src/cython/_cy_primitives.pyx846
1 files changed, 846 insertions, 0 deletions
diff --git a/src/cython/_cy_primitives.pyx b/src/cython/_cy_primitives.pyx
new file mode 100644
index 0000000..62f7f41
--- /dev/null
+++ b/src/cython/_cy_primitives.pyx
@@ -0,0 +1,846 @@
+from numbers import Number
+
+from _common_decl cimport *
+from cython.operator cimport dereference as deref
+
+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, get_Affine, is_transform
+
+from _cy_curves cimport cy_Curve, wrap_Curve_p
+from _cy_curves cimport cy_LineSegment, wrap_LineSegment
+
+cdef class cy_Angle:
+
+ """Class representig angle.
+
+ Angles can be tested for equality, but they are not ordered.
+
+ Corresponds to Angle class in 2geom. Most members are direct
+ calls to Angle methods, otherwise C++ call is specified.
+ """
+
+ def __cinit__(self, double x):
+ """Create new angle from value in radians."""
+ self.thisptr = new Angle(x)
+
+ def __repr__(self):
+ """repr(self)"""
+ return "Angle({0:2.f})".format(self.radians())
+
+ def __str__(self):
+ """str(self)"""
+ return "{0:.2f} radians".format(self.radians())
+
+ def __dealloc__(self):
+ del self.thisptr
+
+ @classmethod
+ def from_radians(cls, rad):
+ """Construnct angle from radians."""
+ return wrap_Angle(from_radians(rad))
+
+ @classmethod
+ def from_degrees(cls, d):
+ """Construnct angle from degrees."""
+ return wrap_Angle(from_degrees(d))
+
+ @classmethod
+ def from_degrees_clock(cls, d):
+ """Construnct angle from degrees in clock convention."""
+ return wrap_Angle(from_degrees_clock(d))
+
+ @classmethod
+ def from_Point(cls, cy_Point p):
+ """Construct angle from Point. Calls Angle(Point) in 2geom."""
+ cdef Point * pp = p.thisptr
+ return wrap_Angle( Angle( deref(p.thisptr) ))
+
+ def __float__(self):
+ """float(self)"""
+ return <Coord> deref(self.thisptr)
+
+ def __add__(cy_Angle self, cy_Angle other):
+ """alpha + beta"""
+ return wrap_Angle(deref(other.thisptr) + deref(self.thisptr))
+
+ def __sub__(cy_Angle self, cy_Angle other):
+ """alpha - beta"""
+ return wrap_Angle(deref(other.thisptr) - deref(self.thisptr))
+
+ def __richcmp__(cy_Angle self, cy_Angle other, int op):
+ """Test equality of angles. Note: angles are not ordered."""
+ if op == 2:
+ return deref(other.thisptr) == deref(self.thisptr)
+ elif op == 3:
+ return deref(other.thisptr) != deref(self.thisptr)
+ return NotImplemented
+
+ def radians(self):
+ """Return the angle in radians."""
+ return self.thisptr.radians()
+
+ def radians0(self):
+ """Return the angle in positive radians."""
+ return self.thisptr.radians0()
+
+ def degrees(self):
+ """Return the angle in degrees."""
+ return self.thisptr.degrees()
+
+ def degrees_clock(self):
+ """Return the angle in clock convention. Calls degreesClock() in 2geom."""
+ return self.thisptr.degreesClock()
+
+ @classmethod
+ def rad_from_deg(cls, deg):
+ """Convert degrees to radians."""
+ return rad_from_deg(deg)
+
+ @classmethod
+ def deg_from_rad(cls, rad):
+ """Convert radians to degrees."""
+ return deg_from_rad(rad)
+
+cdef cy_Angle wrap_Angle(Angle p):
+ cdef Angle * retp = new Angle()
+ retp[0] = p
+ cdef cy_Angle r = cy_Angle.__new__(cy_Angle, 0)
+ r.thisptr = retp
+ return r
+
+
+cdef class cy_AngleInterval:
+
+ """ Class representing interval of angles.
+
+ Corresponds to AngleInterval class in 2geom. Most members are direct
+ calls to AngleInterval methods, otherwise C++ call is specified.
+ """
+
+ cdef AngleInterval* thisptr
+
+ def __cinit__(self, start, end, bint cw=False):
+ """Create AngleInterval from starting and ending value.
+
+ Optional argument cw specifies direction - counter-clockwise
+ is default.
+ """
+ self.thisptr = new AngleInterval(float(start), float(end), cw)
+
+ def __call__(self, Coord t):
+ """A(t), maps unit interval to Angle."""
+ return wrap_Angle(deref( self.thisptr ) (t))
+
+ def initial_angle(self):
+ """Return initial angle as Angle instance."""
+ return wrap_Angle(self.thisptr.initialAngle())
+
+ def final_angle(self):
+ """Return final angle as Angle instance."""
+ return wrap_Angle(self.thisptr.finalAngle())
+
+ def is_degenerate(self):
+ """Test for empty interval."""
+ return self.thisptr.isDegenerate()
+
+ def angle_at(self, Coord t):
+ """A.angle_at(t) <==> A(t)"""
+ return wrap_Angle(self.thisptr.angleAt(t))
+
+ def contains(self, cy_Angle a):
+ """Test whether interval contains Angle."""
+ return self.thisptr.contains(deref( a.thisptr ))
+
+ def extent(self):
+ """Calculate interval's extent."""
+ return self.thisptr.extent()
+
+cdef cy_AngleInterval wrap_AngleInterval(AngleInterval p):
+ cdef AngleInterval * retp = new AngleInterval(0, 0, 0)
+ retp[0] = p
+ cdef cy_AngleInterval r = cy_AngleInterval.__new__(cy_AngleInterval)
+ r.thisptr = retp
+ return r
+
+
+cdef class cy_Point:
+
+ """Represents point or vector in 2D plane.
+
+ Points are ordered lexicographically, with y coordinate being
+ more significant.
+
+ Corresponds to Point class in 2geom. Most members are direct
+ calls to Point methods, otherwise C++ call is specified.
+ """
+
+ def __cinit__(self, double x=0.0, double y=0.0):
+ """Create Point from it's cartesian coordinates."""
+ self.thisptr = new Point(x, y)
+
+ def __repr__(self):
+ """repr(self)"""
+ return "Point ({0:.3f}, {1:.3f})".format(self[0], self[1])
+
+ def __str__(self):
+ """str(self)"""
+ return "[{0:.3f}, {1:.3f}]".format(self[0], self[1])
+
+ def __dealloc__(self):
+ del self.thisptr
+
+ @classmethod
+ def polar(cls, Coord angle, Coord radius = 1.0):
+ """Create Point from polar coordinates."""
+ return wrap_Point(polar(angle, radius))
+
+ def length(self):
+ """Return distance from origin or length of vector."""
+ return self.thisptr.length()
+
+ def ccw(self):
+ """Return point rotated counter-clockwise."""
+ return wrap_Point(self.thisptr.ccw())
+
+ def cw(self):
+ """Return point rotated clockwise."""
+ return wrap_Point(self.thisptr.cw())
+
+ def __getitem__(self, key):
+ """Access coordinates of point."""
+ return deref(self.thisptr)[key]
+
+ @property
+ def x(self):
+ """First coordinate of point."""
+ return self.thisptr.x()
+
+ @property
+ def y(self):
+ """Second coordinate of point."""
+ return self.thisptr.y()
+
+ def round(self):
+ """Create IntPoint rounding coordinates."""
+ return wrap_IntPoint(self.thisptr.round())
+
+ def floor(self):
+ """Create IntPoint flooring coordinates."""
+ return wrap_IntPoint(self.thisptr.floor())
+
+ def ceil(self):
+ """Create IntPoint ceiling coordinates."""
+ return wrap_IntPoint(self.thisptr.ceil())
+
+ def __neg__(self):
+ """-P"""
+ return(wrap_Point(-deref(self.thisptr)))
+
+ def __abs__(self):
+ """abs(P)"""
+ return self.length()
+
+ def __add__(cy_Point self, cy_Point other):
+ """P + Q"""
+ return wrap_Point(deref(self.thisptr) + deref(other.thisptr))
+
+ def __sub__(cy_Point self, cy_Point other):
+ """P - Q"""
+ return wrap_Point(deref(self.thisptr) - deref(other.thisptr))
+
+ #TODO exceptions
+ def __mul__(cy_Point self, s):
+ """Multiply point by number or Affine transform."""
+ if isinstance(s, Number):
+ return wrap_Point(deref(self.thisptr)* (<Coord> float(s)))
+ elif isinstance(s, cy_Affine):
+ return wrap_Point( deref(self.thisptr) * <Affine &> deref( (<cy_Affine> s).thisptr ) )
+ elif isinstance(s, cy_Translate):
+ return wrap_Point( deref(self.thisptr) * <Translate &> deref( (<cy_Translate> s).thisptr ) )
+ elif isinstance(s, cy_Scale):
+ return wrap_Point( deref(self.thisptr) * <Scale &> deref( (<cy_Scale> s).thisptr ) )
+ elif isinstance(s, cy_Rotate):
+ return wrap_Point( deref(self.thisptr) * <Rotate &> deref( (<cy_Rotate> s).thisptr ) )
+ elif isinstance(s, cy_HShear):
+ return wrap_Point( deref(self.thisptr) * <HShear &> deref( (<cy_HShear> s).thisptr ) )
+ elif isinstance(s, cy_VShear):
+ return wrap_Point( deref(self.thisptr) * <VShear &> deref( (<cy_VShear> s).thisptr ) )
+ elif isinstance(s, cy_Zoom):
+ return wrap_Point( deref(self.thisptr) * <Zoom &> deref( (<cy_Zoom> s).thisptr ) )
+ return NotImplemented
+
+ def __div__(cy_Point self, Coord s):
+ """P / s"""
+ return wrap_Point(deref(self.thisptr)/s)
+
+ def __richcmp__(cy_Point self, cy_Point other, int op):
+ if op == 0:
+ return deref(self.thisptr) < deref(other.thisptr)
+ if op == 1:
+ return deref(self.thisptr) <= deref(other.thisptr)
+ if op == 2:
+ return deref(self.thisptr) == deref(other.thisptr)
+ if op == 3:
+ return deref(self.thisptr) != deref(other.thisptr)
+ if op == 4:
+ return deref(self.thisptr) > deref(other.thisptr)
+ if op == 5:
+ return deref(self.thisptr) >= deref(other.thisptr)
+
+ def isFinite(self):
+ """Test whether point is finite."""
+ return self.thisptr.isFinite()
+
+ def isZero(self):
+ """Test whether point is origin"""
+ return self.thisptr.isZero()
+
+ def isNormalized(self, eps=EPSILON):
+ """Test whether point's norm is close to 1."""
+ return self.thisptr.isNormalized(eps)
+
+ @classmethod
+ def L2(cls, cy_Point p):
+ """Compute L2 (Euclidean) norm of point.
+
+ L2(P) = sqrt( P.x**2 + P.y**2 )
+ """
+ return L2(deref(p.thisptr))
+
+ @classmethod
+ def L2sq(cls, cy_Point p):
+ """Compute square of L2 (Euclidean) norm."""
+ return L2sq(deref(p.thisptr))
+
+ @classmethod
+ def are_near(cls, cy_Point a, cy_Point b, double eps=EPSILON):
+ """Test if two points are close."""
+ return are_near(deref(a.thisptr), deref(b.thisptr), eps)
+
+ @classmethod
+ def middle_point(cls, cy_Point a, cy_Point b):
+ """Return point between two points."""
+ return wrap_Point(middle_point(deref(a.thisptr), deref(b.thisptr)))
+
+ @classmethod
+ def rot90(cls, cy_Point a):
+ """Rotate point by 90 degrees."""
+ return wrap_Point(rot90(deref(a.thisptr)))
+
+ @classmethod
+ def lerp(cls, double t, cy_Point a, cy_Point b):
+ """Linearly interpolate between too points."""
+ return wrap_Point(lerp(t, deref(a.thisptr), deref(b.thisptr)))
+
+ @classmethod
+ def dot(cls, cy_Point a, cy_Point b):
+ """Calculate dot product of two points."""
+ return dot(deref(a.thisptr), deref(b.thisptr))
+
+ @classmethod
+ def cross(cls, cy_Point a, cy_Point b):
+ """Calculate (z-coordinate of) cross product of two points."""
+ return cross(deref(a.thisptr), deref(b.thisptr))
+
+ @classmethod
+ def distance(cls, cy_Point a, cy_Point b):
+ """Compute distance between two points."""
+ return distance(deref(a.thisptr), deref(b.thisptr))
+
+ @classmethod
+ def distanceSq(cls, cy_Point a, cy_Point b):
+ """Compute square of distance between two points."""
+ return distanceSq(deref(a.thisptr), deref(b.thisptr))
+
+ @classmethod
+ def unit_vector(cls, cy_Point p):
+ """Normalise point."""
+ return wrap_Point(unit_vector(deref(p.thisptr)))
+
+ @classmethod
+ def L1(cls, cy_Point p):
+ """Compute L1 (Manhattan) norm of a point.
+
+ L1(P) = |P.x| + |P.y|
+ """
+ return L1(deref(p.thisptr))
+
+ @classmethod
+ def LInfty(cls, cy_Point p):
+ """Compute Infinity norm of a point.
+
+ LInfty(P) = max(|P.x|, |P.y|)
+ """
+ return LInfty(deref(p.thisptr))
+
+ @classmethod
+ def is_zero(cls, cy_Point p):
+ """Test whether point is origin."""
+ return is_zero(deref(p.thisptr))
+
+ @classmethod
+ def is_unit_vector(cls, cy_Point p):
+ """Test whether point's length equal 1."""
+ return is_unit_vector(deref(p.thisptr))
+
+ @classmethod
+ def atan2(cls, cy_Point p):
+ """Return angle between point and x-axis."""
+ return atan2(deref(p.thisptr))
+
+ @classmethod
+ def angle_between(cls, cy_Point a, cy_Point b):
+ """Return angle between two point."""
+ return angle_between(deref(a.thisptr), deref(b.thisptr))
+
+ @classmethod
+ def abs(cls, cy_Point p):
+ """Return length of a point."""
+ return wrap_Point(abs(deref(p.thisptr)))
+
+ @classmethod
+ def constrain_angle(cls, cy_Point a, cy_Point b, unsigned int n, cy_Point direction):
+ """Rotate B around A to have specified angle wrt. direction."""
+ return wrap_Point(constrain_angle(deref(a.thisptr), deref(b.thisptr), n, deref(direction.thisptr)))
+
+cdef cy_Point wrap_Point(Point p):
+ cdef Point * retp = new Point()
+ retp[0] = p
+ cdef cy_Point r = cy_Point.__new__(cy_Point)
+ r.thisptr = retp
+ return r
+
+cdef object wrap_vector_point(vector[Point] v):
+ r = []
+ cdef unsigned int i
+ for i in range(v.size()):
+ r.append( wrap_Point(v[i]) )
+ return r
+
+cdef vector[Point] make_vector_point(object l):
+ cdef vector[Point] ret
+ for i in l:
+ ret.push_back( deref( (<cy_Point> i).thisptr ) )
+ return ret
+
+
+cdef class cy_IntPoint:
+
+ """Represents point with integer coordinates
+
+ IntPoints are ordered lexicographically, with y coordinate being
+ more significant.
+
+ Corresponds to IntPoint class in 2geom. Most members are direct
+ calls to IntPoint methods, otherwise C++ call is specified.
+ """
+
+ def __init__(self, IntCoord x = 0, IntCoord y = 0):
+ """Create new IntPoint from it's cartesian coordinates."""
+ self.thisptr = new IntPoint(x ,y)
+
+ def __getitem__(self, key):
+ """Get coordinates of IntPoint."""
+ return deref(self.thisptr)[key]
+
+ def __repr__(self):
+ """repr(self)"""
+ return "IntPoint ({0}, {1})".format(self[0], self[1])
+
+ def __str__(self):
+ """str(self)"""
+ return "[{0}, {1}]".format(self[0], self[1])
+
+ def __dealloc__(self):
+ del self.thisptr
+
+ @property
+ def x(self):
+ """First coordinate of IntPoint."""
+ return self.thisptr.x()
+
+ @property
+ def y(self):
+ """Second coordinate of IntPoint."""
+ return self.thisptr.y()
+
+ def __add__(cy_IntPoint self, cy_IntPoint o):
+ """P + Q"""
+ return wrap_IntPoint(deref(self.thisptr)+deref( o.thisptr ))
+
+ def __sub__(cy_IntPoint self, cy_IntPoint o):
+ """P - Q"""
+ return wrap_IntPoint(deref(self.thisptr)-deref( o.thisptr ))
+
+ def __richcmp__(cy_IntPoint self, cy_IntPoint other, int op):
+ if op == 0:
+ return deref(self.thisptr) < deref(other.thisptr)
+ if op == 1:
+ return deref(self.thisptr) <= deref(other.thisptr)
+ if op == 2:
+ return deref(self.thisptr) == deref(other.thisptr)
+ if op == 3:
+ return deref(self.thisptr) != deref(other.thisptr)
+ if op == 4:
+ return deref(self.thisptr) > deref(other.thisptr)
+ if op == 5:
+ return deref(self.thisptr) >= deref(other.thisptr)
+
+cdef cy_IntPoint wrap_IntPoint(IntPoint p):
+ cdef IntPoint * retp = new IntPoint()
+ retp[0] = p
+ cdef cy_IntPoint r = cy_IntPoint.__new__(cy_IntPoint)
+ r.thisptr = retp
+ return r
+
+
+cdef class cy_Line:
+
+ """Class representing line in plane.
+
+ Corresponds to Line class in 2geom. Most members are direct
+ calls to Line methods, otherwise C++ call is specified.
+ """
+
+ cdef Line* thisptr
+
+ def __cinit__(self, cy_Point cp = None, double x = 0):
+ """Create Line from point and angle to x-axis.
+
+ Constructor with no arguments calls Line() in 2geom, otherwise
+ Line(Point &, double) is called.
+ """
+ if cp is None:
+ self.thisptr = new Line()
+ else:
+ self.thisptr = new Line( deref(cp.thisptr), x)
+
+ def __repr__(self):
+ """repr(self)."""
+ return "Line({0}, {1:3f})".format(repr(self.origin()), self.angle())
+
+ def __str__(self):
+ """str(self)"""
+ return repr(self)
+
+ def __dealloc__(self):
+ del self.thisptr
+
+ @classmethod
+ def from_points(cls, cy_Point cp, cy_Point cq):
+ """Create Line passing through two points.
+
+ Calls Line(Point &, Point &) in 2geom.
+ """
+ return wrap_Line( Line( deref(cp.thisptr), deref(cq.thisptr) ) )
+
+ @classmethod
+ def from_origin_and_versor(cls, cy_Point o, cy_Point v):
+ """Create Line passing through point with specified versor."""
+ return wrap_Line( from_origin_and_versor(deref(o.thisptr), deref(v.thisptr)) )
+
+ @classmethod
+ def from_normal_distance(cls, cy_Point normal, double dist):
+ """Create Line from it's normal and distance from origin."""
+ return wrap_Line( from_normal_distance( deref(normal.thisptr), dist ) )
+
+ @classmethod
+ def from_LineSegment(cls, cy_LineSegment LS):
+ """Create Line from LineSegment.
+
+ Calls Line(LineSegment &) in 2geom.
+ """
+ return wrap_Line( Line( deref(<LineSegment *> LS.thisptr) ) )
+
+ @classmethod
+ def from_Ray(cls, cy_Ray R):
+ """Create Line from Ray.
+
+ Calls Line(Ray &) in 2geom.
+ """
+ return wrap_Line( Line( deref(R.thisptr) ) )
+
+ #maybe implement as properties.
+
+ def origin(self):
+ """Return origin of line."""
+ return wrap_Point(self.thisptr.origin())
+
+ def versor(self):
+ """Return versor of line."""
+ return wrap_Point(self.thisptr.versor())
+
+ def angle(self):
+ """Return angle between line and x-axis."""
+ return self.thisptr.angle()
+
+ def set_origin(self, cy_Point origin):
+ """Set origin."""
+ self.thisptr.setOrigin( deref(origin.thisptr) )
+
+ def set_versor(self, cy_Point versor):
+ """Set versor."""
+ self.thisptr.setVersor( deref(versor.thisptr) )
+
+ def set_angle(self, Coord a):
+ """Set angle."""
+ self.thisptr.setAngle(a)
+
+ def set_points(self, cy_Point cp, cy_Point cq):
+ """Set two points line passes through."""
+ self.thisptr.setPoints( deref(cp.thisptr), deref(cq.thisptr) )
+
+ def set_coefficients(self, a, b, c):
+ """Set coefficients in parametric equation of line."""
+ self.thisptr.setCoefficients(a, b, c)
+
+ def is_degenerate(self):
+ """Test whether line's versor is zero vector."""
+ return self.thisptr.isDegenerate()
+
+ def point_at(self, t):
+ """origin + t*versor"""
+ return wrap_Point(self.thisptr.pointAt(t))
+
+ def value_at(self, t, Dim2 d):
+ """Coordinates of point_at(t)."""
+ return self.thisptr.valueAt(t, d)
+
+ def time_at(self, cy_Point cp):
+ """Find time value corresponding to point on line."""
+ return self.thisptr.timeAt( deref(cp.thisptr) )
+
+ def time_at_projection(self, cy_Point cp):
+ """Find time value corresponding to orthogonal projection of point."""
+ return self.thisptr.timeAtProjection( deref(cp.thisptr) )
+
+ def nearest_time(self, cy_Point cp):
+ """Alias for time_at_projection."""
+ return self.thisptr.nearestTime( deref(cp.thisptr) )
+
+ def roots(self, Coord v, Dim2 d):
+ """Return time values where self.value_at(t, dim) == v."""
+ return wrap_vector_double( self.thisptr.roots(v, d) )
+
+ def reverse(self):
+ """Reverse line."""
+ return wrap_Line( self.thisptr.reverse() )
+
+ def derivative(self):
+ """Take line's derivative."""
+ return wrap_Line( self.thisptr.derivative() )
+
+ def normal(self):
+ """Return line's normal."""
+ return wrap_Point( self.thisptr.normal() )
+
+ def normal_and_dist(self):
+ """return tuple containing normal vector and distance from origin.
+
+ Calls normal_and_dist(x) and return it's result and x as a tuple.
+ """
+ cdef double x = 0
+ cdef Point p = self.thisptr.normalAndDist(x)
+ return (wrap_Point(p), x)
+
+ def portion(self, Coord f, Coord t):
+ """Return Curve corresponding to portion of line."""
+ return wrap_Curve_p( self.thisptr.portion(f, t) )
+
+ def ray(self, Coord t):
+ """Return Ray continuing from time value t."""
+ return wrap_Ray( self.thisptr.ray(t) )
+
+ def segment(self, Coord f, Coord t):
+ """Return LineSegment corresponding to portion of line."""
+ return wrap_LineSegment( self.thisptr.segment(f, t) )
+
+ def transformed(self, t):
+ """Return line transformed by transform."""
+ #doing this because transformed(t) takes reference
+ cdef Affine at
+ if is_transform(t):
+ at = get_Affine(t)
+ return wrap_Line(self.thisptr.transformed( at ))
+
+ @classmethod
+ def distance(cls, cy_Point cp, cy_Line cl):
+ """Calculate distance between point and line."""
+ return distance( deref(cp.thisptr), deref(cl.thisptr))
+
+ @classmethod
+ def are_near(cls, cy_Point cp, cy_Line cl, double eps=EPSILON):
+ """Test if point is near line."""
+ return are_near( deref(cp.thisptr), deref(cl.thisptr), eps)
+
+ @classmethod
+ def are_parallel(cls, cy_Line cl, cy_Line ck, eps=EPSILON):
+ """Test if lines are almost parallel."""
+ return are_parallel( deref(cl.thisptr), deref(ck.thisptr), eps)
+
+ @classmethod
+ def are_same(cls, cy_Line cl, cy_Line ck, double eps=EPSILON):
+ """Test if lines represent the same line."""
+ return are_same( deref(cl.thisptr), deref(ck.thisptr), eps)
+
+ @classmethod
+ def are_orthogonal(cls, cy_Line cl, cy_Line ck, eps=EPSILON):
+ """Test two lines for orthogonality."""
+ return are_orthogonal( deref(cl.thisptr), deref(ck.thisptr), eps)
+
+ @classmethod
+ def are_collinear(cls, cy_Point cp, cy_Point cq, cy_Point cr, eps=EPSILON):
+ """Test for collinearity of vectors (cq-cp) and (cr-cp)"""
+ return are_collinear( deref(cp.thisptr), deref(cq.thisptr), deref(cr.thisptr), eps)
+
+ @classmethod
+ def angle_between(cls, cy_Line cl, cy_Line ck):
+ """Calculate angle between two lines"""
+ return angle_between( deref(cl.thisptr), deref(ck.thisptr) )
+
+cdef cy_Line wrap_Line(Line p):
+ cdef Line * retp = new Line()
+ retp[0] = p
+ cdef cy_Line r = cy_Line.__new__(cy_Line)
+ r.thisptr = retp
+ return r
+
+#-- Ray --
+
+cdef class cy_Ray:
+
+ """Ray represents half of line, starting at origin and going to
+ infinity.
+
+ Corresponds to Ray class in 2geom. Most members are direct
+ calls to Ray methods, otherwise C++ call is specified.
+ """
+
+ cdef Ray* thisptr
+
+ def __cinit__(self, cy_Point cp = None, double x = 0):
+ """Create Ray from origin and angle with x-axis.
+
+ Empty constructor calls Ray() in 2geom.
+ """
+ if cp is None:
+ self.thisptr = new Ray()
+ else:
+ self.thisptr = new Ray( deref(cp.thisptr), x)
+
+ def __repr__(self):
+ """repr(self)."""
+ return "Ray({0}, {1:3f})".format(repr(self.origin()), self.angle())
+
+ def __str__(self):
+ """str(self)"""
+ return repr(self)
+
+ def __dealloc__(self):
+ del self.thisptr
+
+ @classmethod
+ def from_points(cls, cy_Point cp, cy_Point cq):
+ """Create ray passing through two points, starting at first one."""
+ return wrap_Ray( (Ray( deref(cp.thisptr), deref(cq.thisptr) )) )
+
+ def origin(self):
+ """Return origin."""
+ return wrap_Point(self.thisptr.origin())
+
+ def versor(self):
+ """Return versor."""
+ return wrap_Point(self.thisptr.versor())
+
+ def angle(self):
+ """Return angle between ray and x-axis."""
+ return self.thisptr.angle()
+
+ def set_origin(self, cy_Point cp):
+ """Set origin."""
+ self.thisptr.setOrigin( deref(cp.thisptr) )
+
+ def set_versor(self, cy_Point cp):
+ """Set versor."""
+ self.thisptr.setVersor( deref(cp.thisptr) )
+
+ def set_angle(self, Coord a):
+ """Set angle."""
+ self.thisptr.setAngle(a)
+
+ def set_points(self, cy_Point cp, cy_Point cq):
+ """Set origin and second point of ray."""
+ self.thisptr.setPoints( deref(cp.thisptr), deref(cq.thisptr) )
+
+ def is_degenerate(self):
+ """Check for zero versor."""
+ return self.thisptr.isDegenerate()
+
+ def point_at(self, t):
+ """origin + t * versor"""
+ return wrap_Point(self.thisptr.pointAt(t))
+ def value_at(self, t, Dim2 d):
+ """Access coordinates of point_at(t)."""
+ return self.thisptr.valueAt(t, d)
+
+ def nearest_time(self, cy_Point cp):
+ """Get time value of nearest point of ray."""
+ return self.thisptr.nearestTime( deref(cp.thisptr) )
+ def reverse(self):
+ """Reverse the ray."""
+ return wrap_Ray( self.thisptr.reverse() )
+
+ def roots(self, Coord v, Dim2 d):
+ """Return time values for which self.value_at(t, d) == v."""
+ return wrap_vector_double( self.thisptr.roots(v, d) )
+
+ def transformed(self, t):
+ """Return ray transformed by affine transform."""
+ cdef Affine at
+ if is_transform(t):
+ at = get_Affine(t)
+ return wrap_Ray(self.thisptr.transformed( at ))
+
+ def portion(self, Coord f, Coord t):
+ """Return Curve corresponding to portion of ray."""
+ return wrap_Curve_p( self.thisptr.portion(f, t) )
+
+ def segment(self, Coord f, Coord t):
+ """Return LineSegment corresponding to portion of ray."""
+ return wrap_LineSegment( self.thisptr.segment(f, t) )
+
+ @classmethod
+ def distance(cls, cy_Point cp, cy_Ray cl):
+ """Compute distance between point and ray."""
+ return distance( deref(cp.thisptr), deref(cl.thisptr))
+
+ @classmethod
+ def are_near(cls, cy_Point cp, cy_Ray cl, double eps=EPSILON):
+ """Check if distance between point and ray is small."""
+ return are_near( deref(cp.thisptr), deref(cl.thisptr), eps)
+
+ @classmethod
+ def are_same(cls, cy_Ray cl, cy_Ray ck, double eps=EPSILON):
+ """Check if two ray are same."""
+ return are_same( deref(cl.thisptr), deref(ck.thisptr), eps)
+
+ @classmethod
+ def angle_between(cls, cy_Ray cl, cy_Ray ck, bint cw=True):
+ """Compute angle between two rays.
+
+ Can specify direction using parameter cw.
+ """
+ return angle_between( deref(cl.thisptr), deref(ck.thisptr), cw)
+
+ @classmethod
+ def make_angle_bisector_ray(cls, cy_Ray cl, cy_Ray ck):
+ """Make ray bisecting smaller angle formed by two rays."""
+ return wrap_Ray( make_angle_bisector_ray(deref(cl.thisptr), deref(ck.thisptr) ))
+
+cdef cy_Ray wrap_Ray(Ray p):
+ cdef Ray * retp = new Ray()
+ retp[0] = p
+ cdef cy_Ray r = cy_Ray.__new__(cy_Ray)
+ r.thisptr = retp
+ return r