Exemple #1
0
 def tikz_rightangle_mark(self, winding='anticlockwise'):
     if self.decoration is None or not self.mark_right:
         return ''
     check_winding(winding)
     # Decimal numbers in TikZ must be written with a dot as decimal point.
     # As of now, there is no reliable way to temporarily change the
     # locale to 'C' (or 'en_US'), so here's a little patch that will
     # replace possibly other decimal points by a '.'.
     theta = str(Bipoint(self.vertex, self.points[0])
                 .slope.rounded(Number('0.01')).printed)
     if (locale.localeconv()['decimal_point'] != '.'
         and locale.localeconv()['decimal_point'] in theta):
         theta = theta.replace(locale.localeconv()['decimal_point'], '.')
     rt = 'cm={{cos({θ}), sin({θ}), -sin({θ}), cos({θ}), ({v})}}' \
         .format(θ=theta, v=self.vertex.name)
     draw_options = tikz_options_list([self.decoration.thickness,
                                       self.decoration.color,
                                       rt])
     if winding == 'anticlockwise':
         rightangle_shape = '({R}, 0) -- ({R}, {R}) -- (0, {R})'\
             .format(R=self.decoration.radius.uiprinted)
     elif winding == 'clockwise':
         rightangle_shape = '({R}, 0) -- ({R}, -{R}) -- (0, -{R})'\
             .format(R=self.decoration.radius.uiprinted)
     return '\draw{} {};'.format(draw_options, rightangle_shape)
def test_instanciation():
    """Check Vector's instanciation."""
    u = Vector(Bipoint(Point(0, 0), Point(2, 5)))
    assert not u.three_dimensional
    u = Vector(Bipoint(Point(0, 0, 0), Point(2, 5)))
    assert u.three_dimensional
    v = Vector(Point(0, 0), Point(2, 5))
    assert not v.three_dimensional
    v = Vector(Point(0, 0, 0), Point(2, 5))
    assert v.three_dimensional
    w = Vector(2, 5)
    assert not w.three_dimensional
    assert w.z == 0
    assert w.x == 2
    assert w.y == 5
    assert w.coordinates == (2, 5, 0)
    w = Vector(2, 5, 0)
    assert w.three_dimensional
def test_cross_product():
    """Check cross product of two Vectors."""
    Ω = Point(0, 0, 'Ω')
    A = Point(1, 1, 'A')
    with pytest.raises(TypeError) as excinfo:
        Vector(0, 1).cross(Bipoint(Ω, A))
    assert str(excinfo.value) == 'Can only calculate the cross product of a '\
        'Vector by another Vector. '\
        'Found Bipoint(Point Ω(0, 0), Point A(1, 1)) instead.'
    assert Vector(1, 0).cross(Vector(0, 1)) == Vector(0, 0, 1)
    assert Vector(1, 0, 0).cross(Vector(0, 1, 0)) == Vector(0, 0, 1)
    assert Vector(3, 4, 5).cross(Vector(2, 5, 6)) == Vector(-1, -8, 7)
def test_addition():
    """Check Vectors' additions."""
    Ω = Point(0, 0, 'Ω')
    A = Point(1, 1, 'A')
    with pytest.raises(TypeError) as excinfo:
        Vector(0, 1) + Bipoint(Ω, A)
    assert str(excinfo.value) == 'Can only add a Vector to another Vector. '\
        'Found Bipoint(Point Ω(0, 0), Point A(1, 1)) instead.'
    u = Vector(1, 1)
    v = Vector(3, -7)
    assert u + v == Vector(4, -6)
    v = Vector(3, -7, 2)
    assert u + v == Vector(4, -6, 2)
    u = Vector(2, 5, -7)
    assert u + v == Vector(5, -2, -5)
def test_dot_product():
    """Check dot product of two Vectors."""
    Ω = Point(0, 0, 'Ω')
    A = Point(1, 1, 'A')
    with pytest.raises(TypeError) as excinfo:
        Vector(0, 1).dot(Bipoint(Ω, A))
    assert str(excinfo.value) == 'Can only calculate the dot product of a '\
        'Vector by another Vector. '\
        'Found Bipoint(Point Ω(0, 0), Point A(1, 1)) instead.'
    u = Vector(1, 1)
    v = Vector(3, -7)
    assert u.dot(v) == -4
    u = Vector(2, 5, -7)
    v = Vector(3, -7, 2)
    assert u.dot(v) == -43
Exemple #6
0
    def __init__(self,
                 *points,
                 thickness='thick',
                 dashpattern='solid',
                 label=None,
                 label_mask=None,
                 label_winding='anticlockwise',
                 label_position=None,
                 label_scale=None,
                 mark=None,
                 mark_scale=Number('0.5'),
                 color=None,
                 draw_endpoints=True,
                 label_endpoints=True,
                 locked_label=False,
                 allow_zero_length=True,
                 sloped_label=True):
        """
        Initialize LineSegment

        :param points: either one LineSegment to copy, or two Points
        :type points: another LineSegment or a list or tuple of two Points
        :param thickness: the LineSegment's thickness. Available values are
        TikZ's ones.
        :type thickness: str
        :param label: what will be written along the LineSegment, about its
        middle. A None value will disable the LineSegment's labeling.
        :type label: None or str
        :param label_mask: if not None (default), hide the label with ' '
        or '?'
        :type label_mask: None or str (' ' or '?')
        :param label_position: tells where to put the LineSegment's label.
        Can be a value used by TikZ or 'clockwise' or 'anticlockwise'.
        'anticlockwise' (default) will automatically set the label_position to
        'above' if Δx < 0 and to 'below' if Δx > 0. This is useful to
        put all LineSegments' labels outside a Polygon that is drawn in an
        anticlockwise manner. Same for 'clockwise', in the reversed direction.
        :type label_position: str
        :param label_scale: the label's scale
        :type label_scale: None or anything that is accepted to create a Number
        :param draw_endpoints: whether or not actually draw the endpoints.
        Defaults to True.
        :type draw_endpoints: bool
        :param label_endpoints: whether or not label the endpoints.
        Defaults to True.
        :type label_endpoints: bool
        :param mark: the mark to print on the line segment
        :type mark: str
        :param mark_scale: the scale (size) of the mark. Defaults to 0.5
        :type mark_scale: any number
        :param locked_label: to allow or prevent, by default, modifications of
        the LineSegment's label.
        :type locked_label: bool
        """
        if len(points) != 2:
            raise TypeError('Two Points are required to create a '
                            'LineSegment. Got {} object(s) instead.'.format(
                                len(points)))
        Bipoint.__init__(self, *points, allow_zero_length=allow_zero_length)
        self._label = None
        self._thickness = None
        self._label_mask = None
        self._label_scale = None
        self._draw_endpoints = None
        self._label_endpoints = None
        self._locked_label = False  # Only temporary, in order to be able to
        self.label = label  # use the label setter.
        self.label_mask = label_mask
        self.label_scale = label_scale
        self.thickness = thickness
        self.dashpattern = dashpattern
        self.draw_endpoints = draw_endpoints
        self.sloped_label = sloped_label
        self.label_winding = label_winding
        self.label_endpoints = label_endpoints
        self.mark = mark
        self.mark_scale = mark_scale
        try:
            self.endpoints[1].label_position = \
                tikz_approx_position(self.slope360)
        except ZeroLengthLineSegment:
            self.endpoints[1].label_position = 'below left'
        self.endpoints[0].label_position = \
            OPPOSITE_LABEL_POSITIONS[self.endpoints[1].label_position]
        if label_position is None:
            label_position = 'automatic'
        self.label_position = label_position
        if color is not None:
            self.color = color
        self._comment_designation = 'Line Segment'
        if not isinstance(locked_label, bool):
            raise TypeError('Expected bool type for \'locked_label\' keyword '
                            'argument. Found {}.'.format(type(locked_label)))
        self._locked_label = locked_label
def test_equality():
    """Check __eq__() is correct."""
    assert Vector(1, 1) != Bipoint(Point(0, 0), Point(1, 1))
    assert Vector(1, 1) == Vector(Point(1, 1), Point(2, 2))
def test_repr():
    """Check Vector.__repr__()"""
    u = Vector(Bipoint(Point(0, 0), Point(2, 5)))
    assert repr(u) == 'Vector(2, 5)'
    u = Vector(Bipoint(Point(0, 0, 0), Point(2, 5)))
    assert repr(u) == 'Vector(2, 5, 0)'
Exemple #9
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)
Exemple #10
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