示例#1
0
    def dividing_points(self, n=None, prefix='a'):
        """
        Create the list of Points that divide the Bipoint in n parts.

        :param n: the number of parts (so it will create n - 1 points)
        n must be greater or equal to 1
        :type n: int
        """
        if not (is_number(n) and is_integer(n)):
            raise TypeError('n must be an integer')
        if not n >= 1:
            raise ValueError('n must be greater or equal to 1')
        x0 = self.points[0].x
        x1 = self.points[1].x
        xstep = (x1 - x0) / n
        x_list = [x0 + (i + 1) * xstep for i in range(int(n - 1))]
        y0 = self.points[0].y
        y1 = self.points[1].y
        ystep = (y1 - y0) / n
        y_list = [y0 + (i + 1) * ystep for i in range(int(n - 1))]
        if self.three_dimensional:
            z0 = self.points[0].z
            z1 = self.points[1].z
            zstep = (z1 - z0) / n
            z_list = [z0 + (i + 1) * zstep for i in range(int(n - 1))]
        else:
            z_list = ['undefined' for i in range(int(n - 1))]
        return [
            Point(x, y, z, prefix + str(i + 1))
            for i, (x, y, z) in enumerate(zip(x_list, y_list, z_list))
        ]
 def project(point, matrix):
     x = sum([
         pc * coord for pc, coord in zip(matrix[0], point.coordinates)
     ])
     y = sum([
         pc * coord for pc, coord in zip(matrix[1], point.coordinates)
     ])
     return Point(x, y, point.name)
示例#3
0
 def isobarycenter(self, name='automatic'):
     if self.three_dimensional:
         zval = mean([v.z for v in self.vertices])
     else:
         zval = 'undefined'
     return Point(mean([v.x for v in self.vertices]),
                  mean([v.y for v in self.vertices]),
                  z=zval,
                  name=name)
示例#4
0
 def midpoint(self, name='automatic'):
     """Bipoint's midpoint."""
     if self.three_dimensional:
         zval = (self.points[0].z + self.points[1].z) / 2
     else:
         zval = 'undefined'
     return Point((self.points[0].x + self.points[1].x) / 2,
                  (self.points[0].y + self.points[1].y) / 2,
                  z=zval,
                  name=name)
示例#5
0
 def add(self, other, new_endpoint_name='automatic'):
     if not isinstance(other, Bipoint):
         raise TypeError('Can only add a Bipoint to another Bipoint. '
                         'Found {} instead.'.format(repr(other)))
     if self.three_dimensional:
         zval = self.points[1].z + other.Δz
     else:
         zval = 'undefined'
     return Bipoint(
         self.points[0],
         Point(self.points[1].x + other.Δx,
               self.points[1].y + other.Δy,
               z=zval,
               name=new_endpoint_name))
示例#6
0
    def __init__(self, tail, head, allow_zero_length=True):
        """
        A Bipoint can be created from a pair of Points or a Point + a Vector.

        :param tail: the first Point of the Bipoint
        :type tail: Point
        :param head: the second Point of the Bipoint. If a Vector is provided,
        the second Point will be calculated using the first Point and this
        Vector.
        :type head: Point or Vector
        :param allow_zero_length: whether zero length Bipoints are allowed or
        not (default True).
        :type allow_zero_length: bool
        """
        if not isinstance(tail, Point):
            raise TypeError('First argument must be a Point, found {} '
                            'instead.'.format(repr(tail)))
        if not isinstance(head, (Point, Vector)):
            raise TypeError('Second argument must be a Point or a Vector, '
                            'found {} instead.'.format(repr(head)))
        self._three_dimensional = tail.three_dimensional \
            or head.three_dimensional
        if isinstance(head, Vector):
            if self._three_dimensional:
                zval = tail.z + head.z
            else:
                zval = 'undefined'
            head = Point(tail.x + head.x, tail.y + head.y, zval)
        if (not allow_zero_length and tail.coordinates == head.coordinates):
            msg = 'Explicitly disallowed creation of a zero-length {}.'\
                .format(type(self).__name__)
            raise ZERO_OBJECTS_ERRORS[type(self).__name__](msg)
        self._points = [tail, head]
        self._Δx = self.points[1].x - self.points[0].x
        self._Δy = self.points[1].y - self.points[0].y
        self._Δz = self.points[1].z - self.points[0].z
示例#7
0
    def point_at(self, position, name='automatic'):
        """
        A Point aligned with the Bipoint, at provided position.

        The Bipoint's length is the length unit of position.
        Hence, position 0 matches points[0], position 1 matches points[1],
        position 0.5 matches the midpoint, position 0.75 is three quarters
        on the way from points[0] to points[1], position 2 is a Point that
        makes points[1] the middle between it and points[0], position -1 makes
        points[0] the middle between it and points[1].

        :param position: a number
        :type position: number
        :param name: the name to give to the Point
        :type name: str
        """
        if not is_number(position):
            raise TypeError(
                'position must be a number, found {} instead.'.format(
                    type(position)))
        k = Number(position)
        if k == 0:
            return self.points[0]
        elif k == 1:
            return self.points[1]
        else:
            if self.three_dimensional:
                zval = (self.points[0].z +
                        (self.points[1].z - self.points[0].z) * k)
            else:
                zval = 'undefined'
            return Point(
                (self.points[0].x + (self.points[1].x - self.points[0].x) * k),
                (self.points[0].y + (self.points[1].y - self.points[0].y) * k),
                z=zval,
                name=name)
示例#8
0
    def __init__(self,
                 start_vertex=None,
                 name=None,
                 base_length=Number('1.5'),
                 equal_legs_length=Number('1'),
                 mark_equal_sides=True,
                 use_mark='||',
                 draw_vertices=False,
                 label_vertices=True,
                 thickness='thick',
                 color=None,
                 rotation_angle=0,
                 winding=None,
                 sloped_sides_labels=True):
        r"""
        Initialize Isosceles Triangle

        :param start_vertex: the vertex to start to draw the Right Triangle
        (default (0; 0))
        :type start_vertex: Point
        :param name: the name of the Triangle, like ABC.
        Can be either None (the names will be automatically created), or a
        string of the letters to use to name the vertices. Only single letters
        are supported as Points' names so far (at Polygon's creation).
        See issue #3.
        :type name: None or str
        :param base_length: the length of the base of the IsoscelesTriangle,
        that will be used to calculate the coordinates of its vertices
        :type base_length: a number
        :param equal_legs_length: the length of the equal legs of the
        IsoscelesTriangle, that will be used to calculate the coordinates of
        its vertices
        :type equal_legs_length: a number
        :param mark_equal_sides: if True (default), all three sides will be
        automatically marked with the same symbol.
        :type mark_equal_sides: bool
        :param draw_vertices: whether to actually draw, or not, the vertices
        :type draw_vertices: bool
        :param label_vertices: whether to label, or not, the vertices
        :type label_vertices: bool
        :param thickness: the thickness of the Triangle's sides
        :type thickness: str
        :param color: the color of the Triangle's sides
        :type color: str
        :param rotate: the angle of rotation around isobarycenter
        :type rotate: int
        """
        if start_vertex is None:
            start_vertex = Point(0, 0)
        self._base_length = Number(base_length)
        self._equal_legs_length = Number(equal_legs_length)
        v1 = Point(base_length + start_vertex.x, start_vertex.y)
        v2 = Point(
            base_length / 2,
            (equal_legs_length**2 -
             Number('0.25') * base_length**2).sqrt().rounded(Number('0.001')) +
            start_vertex.y)
        if (winding == 'clockwise'
                or (winding is None
                    and config.polygons.DEFAULT_WINDING == 'clockwise')):
            start_vertex, v1 = v1, start_vertex
        Triangle.__init__(self,
                          start_vertex,
                          v1,
                          v2,
                          name=name,
                          draw_vertices=draw_vertices,
                          label_vertices=label_vertices,
                          thickness=thickness,
                          color=color,
                          rotation_angle=rotation_angle,
                          winding=winding,
                          sloped_sides_labels=sloped_sides_labels)
        self._type = 'IsoscelesTriangle'
        if mark_equal_sides:
            self.sides[1].mark = self.sides[2].mark = use_mark
示例#9
0
    def __init__(self,
                 *vertices,
                 name=None,
                 draw_vertices=False,
                 label_vertices=True,
                 thickness='thick',
                 color=None,
                 rotation_angle=0,
                 winding=None,
                 do_cycle=True,
                 sloped_sides_labels=True):
        r"""
        Initialize Polygon

        :param vertices: the vertices of the Polygon
        :type vertices: a list of at least three Points
        :param name: the name of the Polygon, like ABCDE for a pentagon. Can
        be either None (the names of the provided Points will be kept), or a
        string of the letters to use to rename the provided Points. Only
        single letters are supported as Points' names so far (at Polygon's
        creation). See issue #3.
        :type name: None or str
        :param draw_vertices: whether to actually draw, or not, the vertices
        :type draw_vertices: bool
        :param label_vertices: whether to label, or not, the vertices
        :type label_vertices: bool
        :param thickness: the thickness of the Polygon's sides
        :type thickness: str
        :param color: the color of the Polygon's sides
        :type color: str
        :param rotate: the angle of rotation around isobarycenter
        :type rotate: int
        :param winding: force the winding to be either 'clockwise' or
        'anticlockwise'. If left to None (default), doesn't force anything,
        the winding will be either forced by the value of
        config.DEFAULT_POLYGON_WINDING, or if it is None too, then the
        winding will be deduced from the given vertices' order.
        :type winding: None or a str ('clockwise' or 'anticlockwise')
        """
        self.thickness = thickness
        self.color = color
        self.do_cycle = do_cycle
        self.draw_vertices = draw_vertices
        self.label_vertices = label_vertices
        if len(vertices) <= 2:
            raise ValueError('At least three Points are required to be able '
                             'to build a Polygon. Got only {} positional '
                             'arguments, though.'.format(len(vertices)))
        if any([not isinstance(v, Point) for v in vertices]):
            for i, v in enumerate(vertices):
                if not isinstance(v, Point):
                    raise TypeError('Only Points must be provided in order to '
                                    'build a Polygon. Got a {} as positional '
                                    'argument #{}.'.format(type(v), i))
        if not is_number(rotation_angle):
            raise TypeError('Expected a number as rotation angle, got a {} '
                            'instead.'.format(type(rotation_angle)))
        if name is not None:
            if len(name) != len(vertices):
                raise ValueError('The number of provided vertices ({}) does '
                                 'not match the number of Points\' names '
                                 '({}).'.format(len(vertices), len(name)))

        if (winding is None and config.polygons.DEFAULT_WINDING is not None):
            winding = config.polygons.DEFAULT_WINDING

        if winding is not None:
            check_winding(winding)

        self._reverted_winding = False

        if shoelace_formula(*vertices) < 0:
            if winding == 'anticlockwise':
                vertices = vertices[::-1]
                self._reverted_winding = True
                self.winding = 'anticlockwise'
            else:
                self.winding = 'clockwise'
        else:
            if winding == 'clockwise':
                vertices = vertices[::-1]
                self._reverted_winding = True
                self.winding = 'clockwise'
            else:
                self.winding = 'anticlockwise'

        self._vertices = []
        self._three_dimensional = False
        for i, v in enumerate(vertices):
            if name is None:
                vname = v.name
            else:
                vname = name[i]
            if v.name == v.label:
                lbl = 'default'
            else:
                lbl = v.label
            if v.three_dimensional:
                self._three_dimensional = True
                zval = v.z
            else:
                zval = 'undefined'
            self._vertices.append(
                Point(v.x,
                      v.y,
                      z=zval,
                      name=vname,
                      shape=v.shape,
                      label=lbl,
                      color=v.color,
                      shape_scale=v.shape_scale))
        if rotation_angle:
            center = self.isobarycenter()
            for i in range(len(self._vertices)):
                self._vertices[i] = self._vertices[i].rotate(
                    center=center, angle=rotation_angle, rename='keep_name')

        self._sides = []
        shifted_vertices = deepcopy(self._vertices)
        shifted_vertices += [shifted_vertices.pop(0)]
        for (v0, v1) in zip(self._vertices, shifted_vertices):
            self._sides += [
                LineSegment(v0,
                            v1,
                            label_winding=self.winding,
                            locked_label=True)
            ]
        self._angles = []
        left_shifted_vertices = deepcopy(self._vertices)
        left_shifted_vertices = \
            [left_shifted_vertices.pop(-1)] + left_shifted_vertices
        for (v0, v1, v2) in zip(left_shifted_vertices, self._vertices,
                                shifted_vertices):
            self._angles += [Angle(v2, v1, v0)]
        for i in range(len(self._vertices)):
            u = Vector(self._vertices[i], left_shifted_vertices[i])
            v = Vector(self._vertices[i], shifted_vertices[i])
            if self.winding == 'clockwise':
                u, v = v, u
            self._vertices[i].label_position = \
                tikz_approx_position(u.bisector(v).slope360)

        if len(self._sides) in POLYGONS_TYPES:
            self._type = POLYGONS_TYPES[len(self._sides)]
        else:
            self._type = \
                '{n}-sided Polygon'.format(n=str(len(self._sides)))

        self.sloped_sides_labels = sloped_sides_labels

        if (self._reverted_winding
                and config.polygons.ENABLE_MISMATCH_WINDING_WARNING):
            warnings.warn('Changed the order of Points to comply with forced '
                          'winding ({}) for {}.'.format(winding, repr(self)))
示例#10
0
    def __init__(self, *vertices, name=None,
                 draw_vertices=False, label_vertices=True,
                 thickness='thick', color=None):
        r"""
        Initialize Polyhedron.

        :param vertices: the vertices of the Polyhedron
        :type vertices: a list of at least three Points
        :param name: the name of the Polyhedron, like ABCDEFGH.
        Can be either None (the names will be automatically created), or a
        string of the letters to use to name the vertices. Only single letters
        are supported as Points' names so far (at Polyhedron's creation).
        See issue #3.
        :type name: None or str
        :param draw_vertices: whether to actually draw, or not, the vertices
        :type draw_vertices: bool
        :param label_vertices: whether to label, or not, the vertices
        :type label_vertices: bool
        :param thickness: the thickness of the Polyhedron's edges
        :type thickness: str
        :param color: the color of the Polyhedron's edges
        :type color: str
        """
        if len(vertices) <= 3:
            raise ValueError('At least four Points are required to be able '
                             'to build a Polyhedron. Found only {} positional '
                             'arguments, though.'.format(len(vertices)))
        if any([not (isinstance(v, Point) and v.three_dimensional)
                for v in vertices]):
            for i, v in enumerate(vertices):
                if not isinstance(v, Point):
                    raise TypeError('Only Points must be provided in order to '
                                    'build a Polyhedron. Yet found {} as '
                                    'positional argument #{}.'
                                    .format(type(v), i))
                if not v.three_dimensional:
                    raise TypeError('Points used to build a Polyhedron must '
                                    'be three-dimensional Points. Found a '
                                    'two-dimensional Point instead: {}.'
                                    .format(repr(v)))
        if name is not None:
            if not isinstance(name, str):
                raise TypeError('name must be a str, found {} instead.'
                                .format(repr(name)))
            if not len(name) == len(vertices):
                raise ValueError('A polyhedron\'s name must contain as many '
                                 'letters as the polyhedron\'s number of '
                                 'vertices, yet found {} letters (name: {}) '
                                 'and {} vertices.'
                                 ''.format(len(name), repr(name),
                                           len(vertices)))
        self.thickness = thickness
        self.color = color
        self.draw_vertices = draw_vertices
        self.label_vertices = label_vertices
        self._vertices = []
        for i, v in enumerate(vertices):
            if name is None:
                vname = v.name
            else:
                vname = name[i]
            if v.name == v.label:
                lbl = 'default'
            else:
                lbl = v.label
            self._vertices.append(Point(v.x, v.y, v.z, name=vname,
                                        shape=v.shape,
                                        label=lbl, color=v.color,
                                        shape_scale=v.shape_scale))
        self._init_faces()
        self._edges = []
        for F in self.faces:
            for s in F.sides:
                if s not in self._edges:
                    self._edges.append(s)
示例#11
0
    def __init__(self,
                 start_vertex=None,
                 name=None,
                 side_length=Number('1'),
                 build_angle=60,
                 mark_equal_sides=True,
                 use_mark='||',
                 draw_vertices=False,
                 label_vertices=True,
                 thickness='thick',
                 color=None,
                 rotation_angle=0,
                 winding=None,
                 sloped_sides_labels=True):
        r"""
        Initialize Rhombus

        :param start_vertex: the vertex to start to draw the Rhombus
        (default (0; 0))
        :type start_vertex: Point
        :param name: the name of the Rhombus, like ABCD.
        Can be either None (the names will be automatically created), or a
        string of the letters to use to name the vertices. Only single letters
        are supported as Points' names so far (at Polygon's creation).
        See issue #3.
        :type name: None or str
        :param side_length: the length that will be used to calculate the
        coordinates of the vertices used to build the Rhombus
        :type side_length: a number
        :param build_angle: one of the interior angles of the Rhombus.
        :type build_angle: any number
        :param mark_equal_sides: if True (default), all four sides will be
        automatically marked with the same symbol.
        :type mark_equal_sides: bool
        :param draw_vertices: whether to actually draw, or not, the vertices
        :type draw_vertices: bool
        :param label_vertices: whether to label, or not, the vertices
        :type label_vertices: bool
        :param thickness: the thickness of the Quadrilateral's sides
        :type thickness: str
        :param color: the color of the Quadrilateral's sides
        :type color: str
        :param rotate: the angle of rotation around isobarycenter
        :type rotate: int
        """
        if start_vertex is None:
            start_vertex = Point(0, 0)
        self._side_length = Number(side_length)
        if is_number(build_angle):
            self._build_angle = build_angle
        else:
            raise TypeError(
                'Expected an integer as build_angle, found {}.'.format(
                    type(build_angle)))
        x = (side_length * Number(str(cos(radians(build_angle / 2)))))\
            .rounded(Number('0.001'))
        y = (side_length * Number(str(sin(radians(build_angle / 2)))))\
            .rounded(Number('0.001'))
        v1 = Point(x + start_vertex.x, -y + start_vertex.y)
        v2 = Point(2 * x + start_vertex.x, start_vertex.y)
        v3 = Point(x + start_vertex.x, y + start_vertex.y)
        Quadrilateral.__init__(self,
                               start_vertex,
                               v1,
                               v2,
                               v3,
                               name=name,
                               draw_vertices=draw_vertices,
                               label_vertices=label_vertices,
                               thickness=thickness,
                               color=color,
                               rotation_angle=rotation_angle,
                               winding=winding,
                               sloped_sides_labels=sloped_sides_labels)
        self._type = 'Rhombus'
        Equilateral.__init__(self,
                             mark_equal_sides=mark_equal_sides,
                             use_mark=use_mark)
示例#12
0
    def __init__(self,
                 start_vertex=None,
                 dimensions=None,
                 name=None,
                 draw_vertices=False,
                 label_vertices=True,
                 thickness='thick',
                 color=None):
        r"""
        Initialize Right Cuboid

        :param start_vertex: the vertex to start to draw the Right Cuboid
        (default (0; 0))
        :type start_vertex: Point (must be a 3D Point)
        :param dimensions: (width, depth, height)
        :type dimensions: tuple (of numbers)
        :param name: the name of the RightCuboid, like ABCDEFGH.
        Can be either None (the names will be automatically created), or a
        string of the letters to use to name the vertices. Only single letters
        are supported as Points' names so far (at Polyhedra's creation).
        See issue #3.
        :type name: None or str
        :param draw_vertices: whether to actually draw, or not, the vertices
        :type draw_vertices: bool
        :param label_vertices: whether to label, or not, the vertices
        :type label_vertices: bool
        :param thickness: the thickness of the RightCuboid's edges
        :type thickness: str
        :param color: the color of the RightCuboid's edges
        :type color: str
        """
        if start_vertex is None:
            start_vertex = Point(0, 0, 0)
        if not isinstance(start_vertex, Point):
            raise TypeError(
                'start_vertex must be a Point; found {} instead.'.format(
                    repr(start_vertex)))
        if not start_vertex.three_dimensional:
            raise TypeError(
                'start_vertex must be a three-dimensional Point. '
                'Found this two-dimensional Point instead: {}.'.format(
                    repr(start_vertex)))
        if not isinstance(dimensions, (tuple, list)):
            raise TypeError('dimensions must be a tuple or a list. Found {} '
                            'instead.'.format(dimensions))
        if not len(dimensions) == 3:
            raise TypeError('dimensions must have a length of 3. Found {} '
                            'instead.'.format(dimensions))
        x, y, z = start_vertex.coordinates
        width, depth, height = dimensions
        vertices = [
            start_vertex,
            Point(x + width, y, z),
            Point(x + width, y + depth, z),
            Point(x, y + depth, z),
            Point(x, y, z + height),
            Point(x + width, y, z + height),
            Point(x + width, y + depth, z + height),
            Point(x, y + depth, z + height)
        ]
        Polyhedron.__init__(self,
                            *vertices,
                            name=name,
                            draw_vertices=draw_vertices,
                            label_vertices=label_vertices,
                            thickness=thickness,
                            color=color)
        self._width, self._depth, self._height = width, depth, height
        self._labels = None
        self._edges_to_label = {}
示例#13
0
    def __init__(self,
                 start_vertex=None,
                 name=None,
                 leg1_length=Number(2),
                 leg2_length=Number(1),
                 mark_right_angle=True,
                 draw_vertices=False,
                 label_vertices=True,
                 thickness='thick',
                 color=None,
                 rotation_angle=0,
                 winding=None,
                 sloped_sides_labels=True):
        r"""
        Initialize Right Triangle

        :param start_vertex: the vertex to start to draw the Right Triangle
        (default (0; 0))
        :type start_vertex: Point
        :param name: the name of the Triangle, like ABC.
        Can be either None (the names will be automatically created), or a
        string of the letters to use to name the vertices. Only single letters
        are supported as Points' names so far (at Polygon's creation).
        See issue #3.
        :type name: None or str
        :param leg1_length: the leg1's length that will be used to calculate
        the coordinates of the vertices used to build the RightTriangle
        :type leg1_length: a number
        :param leg2_length: the leg2's length that will be used to calculate
        the coordinates of the vertices used to build the RightTriangle
        :type leg2_length: a number
        :param mark_right_angle: if True (default), the right angle will be
        automatically marked as right angle.
        :type mark_right_angle: bool
        :param draw_vertices: whether to actually draw, or not, the vertices
        :type draw_vertices: bool
        :param label_vertices: whether to label, or not, the vertices
        :type label_vertices: bool
        :param thickness: the thickness of the Triangle's sides
        :type thickness: str
        :param color: the color of the Triangle's sides
        :type color: str
        :param rotate: the angle of rotation around isobarycenter
        :type rotate: int
        """
        if start_vertex is None:
            start_vertex = Point(0, 0)
        # Accepted type for leg1's and leg2's lengths is number, will be
        # checked at vertices' instanciations.
        v1 = Point(leg1_length + start_vertex.x, start_vertex.y)
        v2 = Point(leg1_length + start_vertex.x, leg2_length + start_vertex.y)
        if (winding == 'clockwise'
                or (winding is None
                    and config.polygons.DEFAULT_WINDING == 'clockwise')):
            start_vertex, v2 = v2, start_vertex
        Triangle.__init__(self,
                          start_vertex,
                          v1,
                          v2,
                          name=name,
                          draw_vertices=draw_vertices,
                          label_vertices=label_vertices,
                          thickness=thickness,
                          color=color,
                          rotation_angle=rotation_angle,
                          winding=winding,
                          sloped_sides_labels=sloped_sides_labels)
        self._type = 'RightTriangle'
        if mark_right_angle:
            self.right_angle.decoration = AngleDecoration(thickness=thickness)
            self.right_angle.mark_right = True
示例#14
0
    def __init__(self,
                 start_vertex=None,
                 name=None,
                 side_length=Number('1'),
                 mark_equal_sides=True,
                 use_mark='||',
                 draw_vertices=False,
                 label_vertices=True,
                 thickness='thick',
                 color=None,
                 rotation_angle=0,
                 winding=None,
                 sloped_sides_labels=True):
        r"""
        Initialize Equilateral Triangle

        :param start_vertex: the vertex to start to draw the Right Triangle
        (default (0; 0))
        :type start_vertex: Point
        :param name: the name of the Triangle, like ABC.
        Can be either None (the names will be automatically created), or a
        string of the letters to use to name the vertices. Only single letters
        are supported as Points' names so far (at Polygon's creation).
        See issue #3.
        :type name: None or str
        :param side_length: the length that will be used to calculate the
        coordinates of the vertices used to build the EquilateralTriangle
        :type side_length: a number
        :param mark_equal_sides: if True (default), all three sides will be
        automatically marked with the same symbol.
        :type mark_equal_sides: bool
        :param draw_vertices: whether to actually draw, or not, the vertices
        :type draw_vertices: bool
        :param label_vertices: whether to label, or not, the vertices
        :type label_vertices: bool
        :param thickness: the thickness of the Triangle's sides
        :type thickness: str
        :param color: the color of the Triangle's sides
        :type color: str
        :param rotate: the angle of rotation around isobarycenter
        :type rotate: int
        """
        if start_vertex is None:
            start_vertex = Point(0, 0)
        self._side_length = Number(side_length)
        v1 = Point(side_length + start_vertex.x, start_vertex.y)
        v2 = Point(side_length / 2 + start_vertex.x,
                   (side_length * Number(3).sqrt() * Number('0.5')).rounded(
                       Number('0.001')) + start_vertex.y)
        Triangle.__init__(self,
                          start_vertex,
                          v1,
                          v2,
                          name=name,
                          draw_vertices=draw_vertices,
                          label_vertices=label_vertices,
                          thickness=thickness,
                          color=color,
                          rotation_angle=rotation_angle,
                          winding=winding,
                          sloped_sides_labels=sloped_sides_labels)
        Equilateral.__init__(self,
                             mark_equal_sides=mark_equal_sides,
                             use_mark=use_mark)
        self._type = 'EquilateralTriangle'
示例#15
0
    def __init__(self, point, vertex, point_or_measure, decoration=None,
                 mark_right=False, second_point_name='auto', label=None,
                 color=None, thickness='thick', armspoints=None,
                 label_vertex=False, draw_vertex=False,
                 label_armspoints=False, draw_armspoints=False,
                 label_endpoints=False, draw_endpoints=False,
                 naming_mode='from_endpoints', decoration2=None):
        """
        :param point: a Point of an arm of the Angle
        :type point: Point
        :param vertex: the Angle's vertex
        :type vertex: Point
        :param point_or_measure: either a Point of the other arm of the Angle,
        or the measure of the Angle
        :type point_or_measure: Point or number
        :param decoration: the decoration of the Angle
        :type decoration: None or AngleDecoration
        :param mark_right: to tell whether to mark the angle as a right angle
        :type mark_right: bool
        :param second_point_name: Only used if point_or_measure is a measure,
        this is the name of the 2d arm's Point. If set to 'auto', then the name
        of the first Point will be used, concatenated to a '.
        :type second_point_name: str
        :param thickness: the Angle's arms' thickness. Available values are
        TikZ's ones.
        :type thickness: str
        :param color: the color of the Angle's arms.
        :type color: str
        :param naming_mode: how to build the name. Possible modes are:
        'from_endpoints', 'from_armspoints', 'from_vertex'. Note that if no
        armspoints are defined, then trying to get the Angle.name will raise an
        error
        :type naming_mode: str
        """
        self.color = color
        self.thickness = thickness
        self.naming_mode = naming_mode
        self.decoration = decoration
        self.decoration2 = decoration2
        # The label must be set *after* the possible decoration, because it
        # will actually be handled by self.decoration
        if (self.decoration is None
            or self.decoration.label in [None, 'default']):
            self.label = label
        else:
            if label is not None:
                raise ValueError('The label has been set twice, as Angle\'s '
                                 'keyword argument ({}) and as its '
                                 'AngleDecoration\'s keyword argument ({}).'
                                 .format(repr(label),
                                         repr(self.decoration.label_value)))
        self.mark_right = mark_right
        self.label_vertex = label_vertex
        self.label_endpoints = label_endpoints
        self.draw_endpoints = draw_endpoints
        self.label_armspoints = label_armspoints
        self.draw_armspoints = draw_armspoints
        self.draw_vertex = draw_vertex
        if not (isinstance(point, Point)
                and isinstance(vertex, Point)
                and (isinstance(point_or_measure, Point)
                     or is_number(point_or_measure))):
            raise TypeError('Three Points, or two Points and the measure of '
                            'the angle are required to build an Angle. '
                            'Found instead: {}, {} and {}.'
                            .format(type(point), type(vertex),
                                    type(point_or_measure)))
        self._points = [point, vertex]
        if isinstance(point_or_measure, Point):
            self._points.append(point_or_measure)
        else:
            self._points.append(point.rotate(vertex, point_or_measure,
                                             rename=second_point_name))

        if any([p.three_dimensional for p in self._points]):
            self._three_dimensional = True
        else:
            self._three_dimensional = False

        # Measure of the angle:
        if self._three_dimensional:
            u = Vector(self.points[1], self.points[0])
            v = Vector(self.points[1], self.points[2])
            self._measure = Number(str(degrees(atan2(u.cross(v).length,
                                                     u.dot(v)))))
        else:  # 2D angles measure
            p0 = Point(self._points[0].x - self._points[1].x,
                       self._points[0].y - self._points[1].y,
                       None)
            p2 = Point(self._points[2].x - self._points[1].x,
                       self._points[2].y - self._points[1].y,
                       None)
            α0 = Number(str(degrees(atan2(p0.y, p0.x))))
            α2 = Number(str(degrees(atan2(p2.y, p2.x))))
            self._measure = α2 - α0

        if self._measure < 0:
            self._measure += 360

        # This is not like the matching Triangle!
        if shoelace_formula(*self.points) > 0:
            self.winding = 'clockwise'
        else:
            self.winding = 'anticlockwise'

        arm0 = Bipoint(self._points[1], self._points[0])
        arm1 = Bipoint(self._points[1], self._points[2])
        self._arms = [arm0, arm1]
        self.armspoints = armspoints

        # Only 2D: labels positioning
        if not self.three_dimensional:
            # Vertex' label positioning
            bisector = Vector(self._points[0], self.vertex)\
                .bisector(Vector(self._points[2], self.vertex),
                          new_endpoint_name=None)
            try:
                self._points[1].label_position = \
                    tikz_approx_position(bisector.slope360)
            except ZeroVector:
                self._points[1].label_position = \
                    tikz_approx_position(
                        Bipoint(self.vertex,
                                self._points[0].rotate(self.vertex, -90,
                                                       rename=None)
                                ).slope360)

            # Endpoints labels positioning
            direction = 1 if self.winding == 'anticlockwise' else -1
            self.endpoints[0].label_position = \
                tikz_approx_position(arm0.slope360 - direction * 55)
            self.endpoints[1].label_position = \
                tikz_approx_position(arm1.slope360 + direction * 55)
示例#16
0
    def __init__(self,
                 *points,
                 start_vertex=None,
                 name=None,
                 width=Number(1),
                 length=Number(2),
                 mark_right_angles=True,
                 draw_vertices=False,
                 label_vertices=True,
                 thickness='thick',
                 color=None,
                 rotation_angle=0,
                 winding=None,
                 sloped_sides_labels=True):
        r"""
        Initialize Rectangle.

        This will be done either using the points (if provided) of the points
        parameter or using the keyword arguments start_vertex, width...

        :param points: a list of 4 points that will be the vertices of the
        Rectangle. It is possible to provide no Point at all.
        :type points: a list of Points
        :param start_vertex: the vertex to start to draw the Rectangle
        (default (0; 0))
        :type start_vertex: Point
        :param name: the name of the Rectangle, like ABCD.
        Can be either None (the names will be automatically created), or a
        string of the letters to use to name the vertices. Only single letters
        are supported as Points' names so far (at Polygon's creation).
        See issue #3.
        :type name: None or str
        :param width: the width that will be used to calculate the coordinates
        of the vertices used to build the Rectangle
        :type width: a number
        :param length: the length that will be used to calculate the
        coordinates of the vertices used to build the Rectangle
        :type length: a number
        :param mark_right_angles: if True (default), all four angles will be
        automatically marked as right angles.
        :type mark_right_angles: bool
        :param draw_vertices: whether to actually draw, or not, the vertices
        :type draw_vertices: bool
        :param label_vertices: whether to label, or not, the vertices
        :type label_vertices: bool
        :param thickness: the thickness of the Polygon's sides
        :type thickness: str
        :param color: the color of the Polygon's sides
        :type color: str
        :param rotate: the angle of rotation around isobarycenter
        :type rotate: int
        """
        if points:
            (v0, v1, v2, v3) = points
            self._length = Bipoint(v0, v1).length
            self._width = Bipoint(v1, v2).length
        else:  # 2D Rectangles only (so far)
            if start_vertex is None:
                start_vertex = Point(0, 0)
            # Accepted type for width and length is number, will be checked at
            # vertices' instanciations.
            self._width = width
            self._length = length
            v0 = start_vertex
            v1 = Point(length + start_vertex.x, start_vertex.y)
            v2 = Point(length + start_vertex.x, width + start_vertex.y)
            v3 = Point(start_vertex.x, width + start_vertex.y)
        Quadrilateral.__init__(self,
                               v0,
                               v1,
                               v2,
                               v3,
                               name=name,
                               draw_vertices=draw_vertices,
                               label_vertices=label_vertices,
                               thickness=thickness,
                               color=color,
                               rotation_angle=rotation_angle,
                               winding=winding,
                               sloped_sides_labels=sloped_sides_labels)
        self._type = 'Rectangle'
        if mark_right_angles:
            for a in self.angles:
                a.decoration = AngleDecoration(thickness=thickness)
                a.mark_right = True