## Bezier curves

When a shape is described using vector graphics, its outline is often described as a sequence of linear, quadratic, and cubic Bezier curves. You can read about [Bézier curves](https://en.wikipedia.org/wiki/B%C3%A9zier_curve) on Wikipedia. 

You don't need to know much about Bezier curves to solve this kata. Just know that the equations of the linear, quadratic, and cubic curves are given by (respectively):

1. `P(t) = (1 - t) * P0 + t * P1`
2. `P(t) = (1 - t)**2 * P0 + 2 * (1 - t) * t * P1 + t**2 * P2`
3. `P(t) = (1 - t)**3 * P0 + 3 * (1 - t)**2 * t * P1 + 3 * (1 - t) * t**2 * P2 + t**3 * P3`

The points `P0`, `P1`, `P2`, `P3` are called the control points of the curve, and `t` is a variable, which, when taking values from `0` to `1`, will cause `P(t)` to trace the curve.

This should suffice to implement the `point_at(t)` method of the classes you are to implement.

To implement the `sub_segment(t)` method, see, in particular, the section on Constructing Bézier Curves of the above referenced Wikipedia article.

This section shows animations of how the Bézier curves grow. The animations are showing longer and longer subsegments, hence showing how subsegments are constructed. If you look closely at the quad curve animation, you can see how the control points move from starting all at P0 and ending up at P0, P1 and P2 respectively. Now look closely at the animation of the cubic curve. See how the control points of the growing subsection start at P0 and end up at P0, P1, P2, and P3. No need to read anything, just look. At the end of the referenced subsection there is a link to de Casteljau's algorithm, which you might find helpful, but I wouldn't go there. Just look carefully at the animations.

In this kata, you are asked to implement a class for each of the linear, quadratic, and cubic Bézier curves. These classes must extend the following abstract base class and implement the abstract methods:

```Python
from abc import ABCMeta, abstractmethod


class Segment(metaclass=ABCMeta):

    @property
    @abstractmethod
    def control_points(self):
        pass

    @abstractmethod
    def point_at(self, t):
        pass

    @abstractmethod
    def sub_segment(self, t):
        pass
```

`control_points` is a property that returns the coordinates of the points that define the curve. Since the linear curve has two control points (the start and end of the segment), `control_points` will hold 4 floats, which are the x- and y-coordinates of the first point followed by the x- and y-coordinates of the second point.  For the quadratic and cubic curves there are 6 and 8 control points respectively.

The method `point_at(t)` should return the point obtained when inserting `t` in the equation for the curve. This method will be tested for values of `t` in the interval `[0, 1]` only (although it's possible to extrapolate the curve). 

The method `sub_segment(t_0)` should return the curve that starts at the first point and ends at the point returned by `point_at(t_0)` and follows the curve of the object that this method is invoked on otherwise. For example, if `quad` is a quadratic curve, then `quad.sub_segment(t_0)` is a quadratic curve that starts at `quad`'s first point and ends at `quad.point_at(t_0)` and follows `quad`'s curve. More precisely, 

```
quad.point_at(t_0 * t) == quad.sub_segment(t_0).point_at(t)
```
for all values of `t` in the interval `[0, 1]`.

Difficulty: interview

Starter code:

def control_points(self):
	

Solutions:
class Segment:  # Instead of an abstract class, make it the implementation for all three subclasses
    def __init__(self, *coords):
        self.control_points = coords  # IMHO a getter/setter is overkill here

    def control_points_at(self, t):  # Helper function
        p = self.control_points
        result = []
        while p:
            result.extend(p[:2])
            p = [v + (p[i+2] - v) * t for i, v in enumerate(p[:-2])]
        return result

    def point_at(self, t):
        return tuple(self.control_points_at(t)[-2:])

    def sub_segment(self, t):
        return self.__class__(*self.control_points_at(t))

class Line(Segment): pass
class Quad(Segment): pass
class Cubic(Segment): pass

class Segment:
    def __init__(self, *coords):
        self._control_points = list(zip(* [iter(coords)] * 2))

    def _flatten(self, points):
        return tuple(coord for point in points for coord in point)
    
    def _interpolate(self, points, t):
        return [tuple((1 - t) * x + t * y for x, y in zip(p0, p1))
                for p0, p1 in zip(points, points[1:])]
    
    def _reduce(self, points, t):
        while len(points) > 1:
            yield points[0]
            points = self._interpolate(points, t)
        yield points[0]

    @property
    def control_points(self):
        return self._flatten(self._control_points)

    def point_at(self, t):
        return list(self._reduce(self._control_points, t))[-1]

    def sub_segment(self, t):
        return self.__class__(*self._flatten(self._reduce(self._control_points, t)))

class Line(Segment): pass
class Quad(Segment): pass
class Cubic(Segment): pass

from abc import ABCMeta, abstractmethod


class Segment(metaclass=ABCMeta):
    
    @property
    @abstractmethod
    def control_points(self):
        pass

    @abstractmethod
    def point_at(self, t):
        pass

    @abstractmethod
    def sub_segment(self, t):
        pass


class Line(Segment):

    def __init__(self, *coords):
        self._control_points = coords

    @property
    def control_points(self):
        return self._control_points

    def point_at(self, t):
        x, y = self._control_points[::2], self._control_points[1::2]
        x0, y0, x1, y1 = self._control_points
        eq = lambda p, t: (1 - t) * p[0] + t * p[1]
        return eq(x, t), eq(y, t)

    def sub_segment(self, t):
        p0 = self._control_points[:2]
        pt = self.point_at(t)
        return self.__class__(*p0, *pt)


class Quad(Segment): 
    
    def __init__(self, *coords):
        self._control_points = coords

    @property
    def control_points(self):
        return self._control_points

    def point_at(self, t):
        x, y = self._control_points[::2], self._control_points[1::2]
        eq = lambda p, t: (1 - t)**2 * p[0] + 2 * (1 - t) * t * p[1] + t**2 * p[2]
        return eq(x, t), eq(y, t)

    def sub_segment(self, t):
        p0 = self._control_points[:2]
        q0 = Line(*self._control_points[:4]).point_at(t)
        pt = self.point_at(t)
        return self.__class__(*p0, *q0, *pt)


class Cubic(Segment):

    def __init__(self, *coords):
        self._control_points = coords

    @property
    def control_points(self):
        return self._control_points

    def point_at(self, t):
        x, y = self._control_points[::2], self._control_points[1::2]
        eq = lambda p, t: (1 - t)**3 * p[0] + 3 * (1 - t)**2 * t * p[1] + 3 * (1 - t) * t**2 * p[2] + t**3 * p[3]
        return eq(x, t), eq(y, t)

    def sub_segment(self, t):
        p0 = self._control_points[:2]
        q0 = Line(*self._control_points[:4]).point_at(t)
        q1 = Line(*self._control_points[2:6]).point_at(t)
        r0 = Line(*q0, *q1).point_at(t)
        pt = self.point_at(t)
        return self.__class__(*p0, *q0, *r0, *pt)

from math import factorial

class Coordinate(object):
    def __init__(self, x, y):
        self._x = x
        self._y = y

    @property
    def x(self):
        return self._x

    @property
    def y(self):
        return self._y

    def __mul__(self, num):
        return Coordinate(self.x * num, self.y * num)

    __rmul__ = __mul__

    def __sub__(self, other):
        return Coordinate(self.x - other.x, self.y - other.y)

    __rsub__ = __sub__

    def __add__(self, other):
        try:
            return Coordinate(self.x + other.x, self.y + other.y)
        except AttributeError:
            return Coordinate(self.x + other, self.y + other)

    __radd__ = __add__

    def __repr__(self):
        return "Coordinate(" + str(self.x) + ", " + str(self.y) + ")"

    def __str__(self):
        return "C(" + str(self.x) + ", " + str(self.y) + ")"

    def __hash__(self):
        return hash((self.x, self.y))


class Segment:
    def __init__(self, *coords):
        if len(coords) % 2 != 0:
            raise ValueError("Incorrect number of argument values.")
        self._control_points = coords
        self._P = [Coordinate(coords[i], coords[i+1])
                   for i in range(0, len(coords), 2)]
        self._bezier_curve = self._get_bezier_curve(self._P)

    @property
    def control_points(self):
        return self._control_points

    @staticmethod
    def _comb(n, k):
        return factorial(n) // (factorial(k) * factorial(n - k))

    def _get_bezier_curve(self, points):
        n = len(points) - 1
        return lambda t: sum(
            self._comb(n, i) * t ** i * (1 - t) ** (n - i) * points[i]
            for i in range(n + 1)
        )

    def point_at(self, t):
        pt = self._bezier_curve(t)
        return pt.x, pt.y

    def sub_segment(self, t):
        sub_points = [*self._control_points[:2]]
        for i in range(4, len(self._control_points), 2):
            point = Segment(*self._control_points[:i]).point_at(t)
            sub_points.extend(point)
        sub_points.extend(self.point_at(t))
        return Segment(*sub_points)


class Line(Segment):
    pass


class Quad(Segment):
    pass


class Cubic(Segment):
    pass

from abc import ABCMeta, abstractmethod


class Segment(metaclass=ABCMeta):

    @property
    @abstractmethod
    def control_points(self):
        pass

    @abstractmethod
    def point_at(self, t):
        pass

    @abstractmethod
    def sub_segment(self, t):
        pass


class Coordinate(object):
    def __init__(self, x, y):
        self._x = x
        self._y = y

    @property
    def x(self):
        return self._x

    @property
    def y(self):
        return self._y

    def __mul__(self, num):
        return Coordinate(self.x * num, self.y * num)

    def __rmul__(self, num):
        return Coordinate(self.x * num, self.y * num)

    def __sub__(self, other):
        return Coordinate(self.x - other.x, self.y - other.y)

    def __add__(self, other):
        return Coordinate(self.x + other.x, self.y + other.y)

    def __repr__(self):
        return "Coordinate(" + str(self.x) + ", " + str(self.y) + ")"

    def __str__(self):
        return "<" + str(self.x) + ", " + str(self.y) + ">"

    def __hash__(self):
        return hash((self.x, self.y))


class Line(Segment):

    def __init__(self, *coords):
        self._control_points = coords
        self._P0 = Coordinate(coords[0], coords[1])
        self._P1 = Coordinate(coords[2], coords[3])

    @property
    def control_points(self):
        return self._control_points

    def point_at(self, t):
        pt = (1 - t) * self._P0 + t * self._P1
        return pt.x, pt.y

    def sub_segment(self, t):
        return Line(self._P0.x, self._P0.y, *self.point_at(t))


class Quad(Segment):

    def __init__(self, *coords):
        self._control_points = coords
        self._P0 = Coordinate(coords[0], coords[1])
        self._P1 = Coordinate(coords[2], coords[3])
        self._P2 = Coordinate(coords[4], coords[5])

    @property
    def control_points(self):
        return self._control_points

    def point_at(self, t):
        pt = (1 - t)**2 * self._P0 + 2 * (1 - t) * t * self._P1 + \
             t**2 * self._P2
        return pt.x, pt.y

    def sub_segment(self, t):
        sub_line = Line(self._P0.x, self._P0.y, self._P1.x, self._P1.y)
        return Quad(self._P0.x, self._P0.y, *sub_line.point_at(t),
                    *self.point_at(t))


class Cubic(Segment):

    def __init__(self, *coords):
        self._control_points = coords
        self._P0 = Coordinate(coords[0], coords[1])
        self._P1 = Coordinate(coords[2], coords[3])
        self._P2 = Coordinate(coords[4], coords[5])
        self._P3 = Coordinate(coords[6], coords[7])

    @property
    def control_points(self):
        return self._control_points

    def point_at(self, t):
        pt = (1 - t)**3 * self._P0 + 3 * (1 - t)**2 * t * self._P1 + \
             3 * (1 - t) * t**2 * self._P2 + t**3 * self._P3
        return pt.x, pt.y

    def sub_segment(self, t):
        sub_line = Line(self._P0.x, self._P0.y, self._P1.x, self._P1.y)
        sub_quad = Quad(self._P0.x, self._P0.y, self._P1.x, self._P1.y, 
                        self._P2.x, self._P2.y)
        return Cubic(self._P0.x, self._P0.y, *sub_line.point_at(t), 
                     *sub_quad.point_at(t), *self.point_at(t))



from abc import ABCMeta, abstractmethod
class Segment(metaclass=ABCMeta):

    @property
    @abstractmethod
    def control_points(self):
        pass

    @abstractmethod
    def point_at(self, t):
        pass

    @abstractmethod
    def sub_segment(self, t):
        pass

class Line(Segment):
    
    def __init__(self, *coords):
        self._control_points = coords

    @property
    def control_points(self):
        return self._control_points

    def point_at(self, t):
        #P(t) = (1 - t) * P0 + t * P1
        p_0,p_1,p_2,p_3 = self.control_points[0],self.control_points[1],self.control_points[2],self.control_points[3]
        x = (1 - t) *  p_0 + t * p_2
        y = (1 - t) * p_1 + t * p_3
        return (x,y)

    def sub_segment(self, t):
        t_point = self.point_at(t)
        co_ords = (self.control_points[0],self.control_points[1],t_point[0],t_point[1])
        return Line(*co_ords)

class Quad(Segment):

    def __init__(self, *coords):
        self._control_points = coords

    @property
    def control_points(self):
        return self._control_points

    def point_at(self, t):
        #P(t) = (1 - t)**2 * P0 + 2 * (1 - t) * t * P1 + t**2 * P2
        p_1 = (self.control_points[0],self.control_points[1])
        p_2 = (self.control_points[2],self.control_points[3]) 
        p_3 = (self.control_points[4],self.control_points[5]) 
        x = (1 - t)**2 * p_1[0] + 2 * (1 -t) * t * p_2[0] + t**2 * p_3[0]
        y = (1 - t)**2 * p_1[1] + 2 * (1 -t) * t * p_2[1] + t**2 * p_3[1]
        return (x,y)

    def sub_segment(self, t):
        #get p2
        lne = Line(self.control_points[0],self.control_points[1],self.control_points[2],self.control_points[3])
        t_p1 = lne.point_at(t)
        t_point = self.point_at(t)
        co_ords = (self.control_points[0],self.control_points[1],t_p1[0],t_p1[1],t_point[0],t_point[1])
        return Quad(*co_ords)

class Cubic(Segment):

    def __init__(self, *coords):
        self._control_points = coords

    @property
    def control_points(self):
        return self._control_points

    def point_at(self, t):
        #P(t) = (1 - t)**3 * P0 + 3 * (1 - t)**2 * t * P1 + 3 * (1 - t) * t**2 * P2 + t**3 * P3
        p_1 = (self.control_points[0],self.control_points[1])
        p_2 = (self.control_points[2],self.control_points[3]) #x
        p_3 = (self.control_points[4],self.control_points[5])
        p_4 = (self.control_points[6],self.control_points[7]) #y
        x = (1 -t)**3 * p_1[0] + 3 *(1 -t)**2 * t * p_2[0] + 3 * (1-t) * t**2 * p_3[0] + t**3 * p_4[0]
        y = (1 -t)**3 * p_1[1] + 3 *(1 -t)**2 * t * p_2[1] + 3 * (1-t) * t**2 * p_3[1] + t**3 * p_4[1]
        return (x,y)

    def sub_segment(self, t):
        t_point = self.point_at(t)
        lne_1 = Line(self.control_points[0],self.control_points[1],self.control_points[2],self.control_points[3])
        pnt_1 = lne_1.point_at(t)
        lne_2 = Line(self.control_points[2],self.control_points[3],self.control_points[4],self.control_points[5])
        pnt_2 = lne_2.point_at(t)
        lne_3 = Line(self.control_points[4],self.control_points[5],self.control_points[6],self.control_points[7])
        pnt_3 = lne_3.point_at(t)
        #########################################################################################################
        lne_g1 = Line(*pnt_1,*pnt_2)
        pnt_g1 = lne_g1.point_at(t)
        lne_g2 = Line(*pnt_2,*pnt_3)
        #########################################################################################################
        co_ords = (self.control_points[0],self.control_points[1],pnt_1[0],pnt_1[1],pnt_g1[0],pnt_g1[1],t_point[0],t_point[1])
        return Cubic(*co_ords)


from abc import ABCMeta, abstractmethod
from functools import reduce

class Segment(metaclass=ABCMeta):

    @property
    @abstractmethod
    def control_points(self):
        pass

    @abstractmethod
    def point_at(self, t):
        pass

    @abstractmethod
    def sub_segment(self, t):
        pass
    
def mul(a, b):
    return (a[0]*b , a[1]*b)
def add(*args):
    print(args)
    return (sum([args[i][0] + args[i + 1][0] for i in range(len(args) - 1)]),  sum([args[i][1] + args[i + 1][1] for i in range(len(args) - 1)]))  
class Line(Segment):

    def __init__(self, *c):
        self.c = c
        self.points = [(c[i], c[i+1]) for i in range(0, len(c) - 1, 2)]

    @property
    def control_points(self):
        return self.c

    def point_at(self, t):
        p = self.points
        return add(mul(p[0], 1 - t), mul(p[1], t)) 

    def sub_segment(self, t):
        return Line(self.c[0],self.c[1], self.point_at(t)[0], self.point_at(t)[1])


class Quad(Segment):

    def __init__(self, *c):
        self.c = c
        self.points = [(c[i], c[i+1]) for i in range(0, len(c) - 1, 2)]

    @property
    def control_points(self):
        return self.c

    def point_at(self, t):
        p = self.points
        return reduce(add, [mul(p[0], (1 - t)**2), mul(p[1], t*2*(1-t)), mul(p[2], t**2)])

    def sub_segment(self, t):
        a, b = add(mul(self.points[0], 1-t), mul(self.points[1], t))
        return Quad(self.c[0], self.c[1], a, b, self.point_at(t)[0], self.point_at(t)[1])

class Cubic(Segment):
    def __init__(self, *c):
        self.c = c
        self.points = [(c[i], c[i+1]) for i in range(0, len(c) - 1, 2)]

    @property
    def control_points(self):
        return self.c

    def point_at(self, t):
        p = self.points
        return reduce(add,[mul(p[0], (1 - t)**3), mul(p[1], 3*t*(1-t)**2), mul(p[2], 3*t**2*(1-t)), mul(p[3], t**3)])

    def sub_segment(self, t):
        p = self.points
        a, b =  add(mul(p[0], 1 - t), mul(p[1], t)) 
        c, d = reduce(add, [mul(p[0], (1 - t)**2), mul(p[1], t*2*(1-t)), mul(p[2], t**2)])
        return Cubic(self.c[0], self.c[1], a, b, c, d, self.point_at(t)[0], self.point_at(t)[1])

from abc import ABCMeta, abstractmethod


class Segment(metaclass=ABCMeta):

    @property
    @abstractmethod
    def control_points(self):
        pass

    @abstractmethod
    def point_at(self, t):
        pass

    @abstractmethod
    def sub_segment(self, t):
        pass


class Line(Segment):

    def __init__(self, *coords):
        self._control_points = coords

    @property
    def control_points(self):
        return self._control_points

    def point_at(self, t):
        x0, y0, x1, y1 = self.control_points
        point = tuple((1 - t) * p0 + t * p1 for p0, p1 in [(x0, x1), (y0, y1)])
        return point

    def sub_segment(self, t):
        return Line(*self.control_points[:2], *self.point_at(t))


class Quad(Segment):

    def __init__(self, *coords):
        self._control_points = coords

    @property
    def control_points(self):
        return self._control_points

    def point_at(self, t):
        x0, y0, x1, y1, x2, y2 = self.control_points
        point = tuple((1 - t)**2 * p0 + 2 * (1 - t) * t * p1 + t**2 * p2 
                    for p0, p1, p2 in [(x0, x1, x2), (y0, y1, y2)])
        return point

    def sub_segment(self, t):
        x0, y0 = self.control_points[:2]
        x1, y1 = Line(x0, y0, *self.control_points[2:4]).point_at(t)
        x2, y2 = self.point_at(t)
        return Quad(x0, y0, x1, y1, x2, y2)


class Cubic(Segment):

    def __init__(self, *coords):
        self._control_points = coords

    @property
    def control_points(self):
        return self._control_points

    def point_at(self, t):
        x0, y0, x1, y1, x2, y2, x3, y3 = self.control_points
        point = tuple((1 - t)**3 * p0 + 3 * (1 - t)**2 * t * p1 + 3 * (1 - t) * t**2 * p2 + t**3 * p3 
                    for p0, p1, p2, p3 in [(x0, x1, x2, x3), (y0, y1, y2, y3)])
        return point

    def sub_segment(self, t):
        x0, y0 = self.control_points[:2]
        x1, y1 = Line(x0, y0, *self.control_points[2:4]).point_at(t)
        x2, y2 = Quad(*self.control_points[:6]).point_at(t)
        x3, y3 = self.point_at(t)
        return Cubic(x0, y0, x1, y1, x2, y2, x3, y3)


from abc import ABCMeta, abstractmethod
import numpy as np

class Segment(metaclass=ABCMeta):

    @property
    @abstractmethod
    def control_points(self):
        pass

    @abstractmethod
    def point_at(self, t):
        pass

    @abstractmethod
    def sub_segment(self, t):
        pass


class Line(Segment):

    def __init__(self, *coords):
        self._control_points = coords

    @property
    def control_points(self):
        return self._control_points

    def point_at(self, t):
        P0 = np.array(self._control_points[0:2])
        P1 = np.array(self._control_points[2:])
        return (1 - t) * P0 + t * P1


    def sub_segment(self, t):
        P0 = np.array(self._control_points[0:2])
        P1 = self.point_at(t)

        return Line(*P0,*P1)


class Quad(Segment):

    def __init__(self, *coords):
        self._control_points = coords

    @property
    def control_points(self):
        return self._control_points

    def point_at(self, t):
        P0 = np.array(self._control_points[0:2])
        P1 = np.array(self._control_points[2:4])
        P2 = np.array(self._control_points[4:])
        return (1 - t)**2 * P0 + 2 * (1 - t) * t * P1 + t**2 * P2

    def sub_segment(self, t):
        P0 = np.array(self._control_points[0:2])
        P1 = np.array(self._control_points[2:4])
        l1 = Line(*P0,*P1)
        P1_new = l1.point_at(t)
        P2 = np.array(self._control_points[4:])
        P2_new = self.point_at(t)

        return Quad(*P0,*P1_new,*P2_new)


class Cubic(Segment):

    def __init__(self, *coords):
        self._control_points = coords

    @property
    def control_points(self):
        return self._control_points

    def point_at(self, t):
        P0 = np.array(self._control_points[0:2])
        P1 = np.array(self._control_points[2:4])
        P2 = np.array(self._control_points[4:6])
        P3 = np.array(self._control_points[6:])
        return (1 - t)**3 * P0 + 3 * (1 - t)**2 * t * P1 + 3 * (1 - t) * t**2 * P2 + t**3 * P3

    def sub_segment(self, t):
        P0 = np.array(self._control_points[0:2])
        P1 = np.array(self._control_points[2:4])
        P2 = np.array(self._control_points[4:6])
        P3 = np.array(self._control_points[6:])
        l1 = Line(*P0,*P1)
        P1_new = l1.point_at(t)
        l2 = Quad(*P0,*P1,*P2)
        P2_new = l2.point_at(t)
        P3_new = self.point_at(t)
        return Cubic(*P0,*P1_new,*P2_new,*P3_new)


from abc import ABCMeta, abstractmethod


class Segment(metaclass=ABCMeta):

    @property
    @abstractmethod
    def control_points(self):
        pass

    @abstractmethod
    def point_at(self, t):
        pass

    @abstractmethod
    def sub_segment(self, t):
        pass


class Line(Segment):

    def __init__(self, *coords):
        self.cp = coords
        self.P0 = (coords[0], coords[1])
        self.P1 = (coords[2], coords[3])

    @property
    def control_points(self):
        return self.cp

    def point_at(self, t):
        x = (1-t)*self.P0[0] + t*self.P1[0]
        y = (1-t)*self.P0[1] + t*self.P1[1]
        return (x,y)

    def sub_segment(self, t):
        return Line(*self.P0, *self.point_at(t))


class Quad(Segment):

    def __init__(self, *coords):
        self.cp = coords
        self.P = [(coords[0], coords[1]),
                  (coords[2], coords[3]),
                  (coords[4], coords[5])]

    @property
    def control_points(self):
        return self.cp
        
    def get_Q(self, t):
        return (Line(*self.P[0], *self.P[1]).point_at(t), 
                Line(*self.P[1], *self.P[2]).point_at(t))

    def point_at(self, t):
        Q = self.get_Q(t)
        return Line(*Q[0], *Q[1]).point_at(t)

    def sub_segment(self, t):
        Q = self.get_Q(t)[0]
        B = self.point_at(t)
        return Quad(*self.P[0], *Q, *B)


class Cubic(Segment):

    def __init__(self, *coords):
        self.cp = coords
        self.P = [(coords[0], coords[1]),
                  (coords[2], coords[3]),
                  (coords[4], coords[5]),
                  (coords[6], coords[7])]

    @property
    def control_points(self):
        return self.cp
        
    def get_Q(self, t):
        return (Line(*self.P[0], *self.P[1]).point_at(t),
                Line(*self.P[1], *self.P[2]).point_at(t),
                Line(*self.P[2], *self.P[3]).point_at(t))

    def point_at(self, t):
        Q = self.get_Q(t)
        return Quad(*(Q[0]), *(Q[1]), *(Q[2])).point_at(t)

    def sub_segment(self, t):
        Q = self.get_Q(t)
        R = Line(*Q[0], *Q[1]).point_at(t)
        B = self.point_at(t)
        return Cubic(*self.P[0], *Q[0], *R, *B)