예제 #1
0
    def _generate_shapes(self) -> Dict[str, Shape]:
        arrow1 = Arrow(
            self._edge + (self._edge - self._center) * self._offset, self._edge
        )
        text = LineAnnotation(self._text, arrow1, TextPosition.START)
        text.style.font_size = 24
        ret_dict = {"arrow1": arrow1, "text": text}

        if self._diameter:
            inward_vector = self._center - self._edge
            offset_vector = inward_vector.unit_vector * self._offset
            start = self._edge + inward_vector * 2 + offset_vector
            end = self._edge + inward_vector * 2

            ret_dict["arrow2"] = Arrow(start, end)

        if self._center_mark:
            ret_dict["center_mark_h"] = Line(
                self._center - Point(-self._offset * 0.5, 0),
                self._center - Point(self._offset * 0.5, 0),
            )
            ret_dict["center_mark_v"] = Line(
                self._center - Point(0, -self._offset * 0.5),
                self._center - Point(0, self._offset * 0.5),
            )

        if self._center_line:
            ret_dict["center_line"] = Line(self._edge, self._center)

        if self._center_line and self._diameter:
            ret_dict["center_line2"] = Line(
                self._center, self._center + (self._center - self._edge)
            )
        return ret_dict
예제 #2
0
    def interval(self,
                 x_range: Tuple[float, float] = None,
                 y_range: Tuple[float, float] = None):
        """Returns a smaller portion of a line.

        Args:
            x_range: The range of x-coordinates which
                should be used to obtain the segment
            y_range: The range of y-coordinates which
                should be used to obtain the segment

        Returns:
            A line bounded to either ``x_range`` or ``y_range``.

        Raises:
            ValueError: If the line is vertical and ``x_range`` is provided, or if
                the line is horizontal and ``y_range`` is provided.
        """
        if x_range and y_range:
            raise ValueError("Cannot specify both x_range and y_range.")
        if x_range is not None:
            return Line(Point(x_range[0], self(x_range[0])),
                        Point(x_range[1], self(x_range[1])))
        elif y_range is not None:
            return Line(Point(self(y_range[0]), y_range[0]),
                        Point(self(y_range[1]), y_range[1]))
예제 #3
0
    def __init__(self, lower_left_corner: Point, width: float, height: float):

        self._width = width
        self._height = height
        self._lower_left_corner = lower_left_corner

        super().__init__(self._generate_points())

        # Dimensions
        dims = {
            "width":
            DistanceWithText(
                "width",
                self._lower_left_corner + Point(0, -height / 5.0),
                self._lower_left_corner + Point(width, -height / 5.0),
            ),
            "height":
            DistanceWithText(
                "height",
                self._lower_left_corner + Point(width + width / 5.0, 0),
                self._lower_left_corner + Point(width + width / 5.0, height),
            ),
            "lower_left_corner":
            ArrowWithText(
                "lower_left_corner",
                self._lower_left_corner - Point(width / 5.0, height / 5.0),
                self._lower_left_corner,
            ),
        }
        dims["height"]["text"].style.alignment = TextStyle.Alignment.LEFT
        self.dimensions = dims
예제 #4
0
    def __init__(
        self, center: Point, radius: float, inner_radius: float = None, nlines: int = 10
    ):
        self._center = center
        self._radius = radius
        self._inner_radius = inner_radius
        self._nlines = nlines

        if inner_radius is None:
            self._inner_radius = radius / 5.0

        outer = Circle(center, radius)
        inner = Circle(center, inner_radius)
        lines = []
        # Draw nlines+1 since the first and last coincide
        # (then nlines lines will be visible)
        t = np.linspace(0, 2 * np.pi, nlines + 1)

        xinner = self._center.x + self._inner_radius * np.cos(t)
        yinner = self._center.y + self._inner_radius * np.sin(t)
        xouter = self._center.x + self._radius * np.cos(t)
        youter = self._center.y + self._radius * np.sin(t)
        lines = [
            Line(Point(xi, yi), Point(xo, yo))
            for xi, yi, xo, yo in zip(xinner, yinner, xouter, youter)
        ]
        super().__init__(
            {
                "inner": inner,
                "outer": outer,
                "spokes": Composition(
                    {"spoke%d" % i: lines[i] for i in range(len(lines))}
                ),
            }
        )
예제 #5
0
 def _generate_points(self) -> List[Point]:
     return [
         self._lower_left_corner,
         self._lower_left_corner + Point(self._width, 0),
         self._lower_left_corner + Point(self._width, self._height),
         self._lower_left_corner + Point(0, self._height),
         self._lower_left_corner,
     ]
예제 #6
0
 def _horizontal_arrow(self) -> DoubleArrow:
     if self._start.x < self._end.x:
         return DoubleArrow(
             Point(self._start.x, self._start.y + self._offset),
             Point(self._end.x, self._start.y + self._offset),
         )
     else:
         return DoubleArrow(
             Point(self._start.x, self._end.y + self._offset),
             Point(self._end.x, self._end.y + self._offset),
         )
예제 #7
0
 def _vertical_arrow(self) -> DoubleArrow:
     if self._start.y > self._end.y:
         return DoubleArrow(
             Point(self._end.x + self._offset, self._start.y),
             Point(self._end.x + self._offset, self._end.y),
         )
     else:
         return DoubleArrow(
             Point(self._start.x + self._offset, self._start.y),
             Point(self._start.x + self._offset, self._end.y),
         )
예제 #8
0
 def __init__(self, position: Point, size: float):
     self._position = position
     self._size = size
     self._p0 = Point(position.x - size / 2.0, position.y - size)
     self._p1 = Point(position.x + size / 2.0, position.y - size)
     self._triangle = Triangle(self._p0, self._p1, position)
     gap = size / 5.0
     self._height = size / 4.0  # height of rectangle
     self._p2 = Point(self._p0.x, self._p0.y - gap - self._height)
     self._rectangle = Rectangle(self._p2, self._size, self._height)
     shapes = {"triangle": self._triangle, "rectangle": self._rectangle}
     super().__init__(shapes)
예제 #9
0
 def __init__(self,
              lower_left_corner: Point,
              width: float,
              height: float,
              num_arrows=10):
     box = Rectangle(lower_left_corner, width, height)
     shapes = {"box": box}
     dx = float(width) / (num_arrows - 1)
     for i in range(num_arrows):
         x = lower_left_corner.x + i * dx
         start = Point(x, lower_left_corner.y + height)
         end = Point(x, lower_left_corner.y)
         shapes["arrow%d" % i] = Arrow(start, end)
     super().__init__(shapes)
예제 #10
0
class LineAnnotation(Text):
    """Annotates a line with the provided text."""

    # TODO: Write a LineAnnotation Example
    _DEFAULT_SPACING: Point = Point(0.15, 0.15)

    def __init__(self,
                 text: str,
                 line: Line,
                 text_position: TextPosition = TextPosition.MIDDLE):

        spacing = self._DEFAULT_SPACING

        if text_position == TextPosition.START:
            position = line.start + spacing
            alignment = TextStyle.Alignment.LEFT
        elif text_position == TextPosition.END:
            position = line.end + spacing
            alignment = TextStyle.Alignment.RIGHT
        elif text_position == TextPosition.MIDDLE:
            position = line.start + (line.end - line.start) * 0.5 + spacing
            alignment = TextStyle.Alignment.CENTER
        else:
            raise RuntimeError(
                f"Invalid value of text_position: {text_position}.")

        super().__init__(text, position)
        self.style.alignment = alignment
예제 #11
0
 def __init__(
     self,
     start,
     length,
 ):
     Force.__init__(self, "$g$", start, start + Point(0, -length))
     self.style.line_color = Style.Color.BLACK
예제 #12
0
class ArrowWithText(ShapeWithText):
    """An ``Arrow`` with a text label at ``text_position``.

    Args:
        text: The text to be displayed.
        start: The start ``Point`` of the arrow.
        end: The end ``Point`` of the arrow.
        text_position: The position of the text on the arrow.
        spacing: The text spacing.

    Examples:
        >>> arrow_with_text = ps.ArrowWithText(
        ...     "$a$",
        ...     ps.Point(1.0, 1.0),
        ...     ps.Point(
        ...         3.0,
        ...         1.0,
        ...     ),
        ... )
        >>> fig = ps.Figure(0.0, 4.0, 0.0, 2.0, backend=MatplotlibBackend)
        >>> fig.add(arrow_with_text)
        >>> fig.save("pysketcher/images/arrow_with_text.png")

    .. figure:: images/arrow_with_text.png
        :alt: An example of ArrowWithText.
        :figclass: align-center

        An example of ``ArrowWithText``.
    """

    _DEFAULT_SPACING: Point = Point(0.15, 0.15)

    @unique
    class TextPosition(Enum):
        """Specifies the position of the text on the ``Arrow``."""

        START = auto()
        END = auto()

    def __init__(
        self,
        text: str,
        start: Point,
        end: Point,
        text_position: TextPosition = TextPosition.START,
        spacing: Union[float, Point] = None,
    ):

        spacing = spacing if spacing else self._DEFAULT_SPACING
        if not issubclass(spacing.__class__, Point):
            spacing = Point(spacing, spacing)

        if text_position == self.TextPosition.START:
            text = Text(text, start + spacing)
        if text_position == self.TextPosition.END:
            text = Text(text, end + spacing)

        arrow = Arrow(start, end)
        super().__init__(arrow, text)
예제 #13
0
 def __init__(
     self, text: str, position: Point, direction: Point = Point(1, 0)  # noqa: B008
 ):
     super().__init__()
     self._text: str = text
     self._position: Point = position
     self._direction: Point = direction
     self._style: TextStyle = TextStyle()
예제 #14
0
    def __init__(
        self,
        start: Point,
        length: float,
        label: str,
        rotation_angle: Angle = Angle(0.0),  # noqa: B008
        label_spacing=1.0 / 4.5,
    ):
        arrow = Arrow(start, start + Point(length, 0)).rotate(rotation_angle, start)
        # should increase spacing for downward pointing axis
        if type(label_spacing) != tuple:
            label_spacing = (label_spacing, label_spacing)
        label_pos = Point(
            start.x + length + label_spacing[0], start.y + label_spacing[1]
        )
        label = Text(label, label_pos).rotate(rotation_angle, start)

        super().__init__(arrow, label)
예제 #15
0
 def __init__(self, points: List[Point], degree: int = 3, resolution: int = 501):
     self._input_points = points
     self._smooth = UnivariateSpline(
         [p.x for p in points], [p.y for p in points], s=0, k=degree
     )
     x_coordinates = np.linspace(points[0].x, points[-1].x, resolution)
     y_coordinates = self._smooth(x_coordinates)
     smooth_points = [Point(p[0], p[1]) for p in zip(x_coordinates, y_coordinates)]
     super().__init__(smooth_points)
예제 #16
0
    def __init__(self, position: Point, size: float):
        p0 = Point(position.x - size / 2.0, position.y - size)
        p1 = Point(position.x + size / 2.0, position.y - size)
        triangle = Triangle(p0, p1, position)
        gap = size / 5.0
        h = size / 4.0  # height of rectangle
        p2 = Point(p0.x, p0.y - gap - h)
        rectangle = Rectangle(p2, size, h)
        shapes = {"triangle": triangle, "rectangle": rectangle}
        super().__init__(shapes)

        self._dimensions = {
            "position":
            Text("position", position),
            "size":
            DistanceWithText("size", Point(p2.x, p2.y - size),
                             Point(p2.x + size, p2.y - size)),
        }
예제 #17
0
    def __init__(self, x_min=0, x_max=6, y_min=0.5, y_max=3.8):
        xs = np.array([0.0, 2.0, 3.0, 4.0, 5.0, 6.0])
        ys = np.array([0.5, 3.5, 3.8, 2, 2.5, 3.5])

        # Scale x and y
        xs = _scale_array(x_min, x_max, xs)
        ys = _scale_array(y_min, y_max, ys)
        points = Point.from_coordinate_lists(xs, ys)

        super().__init__(points)
        self.style.line_color = Style.Color.BLACK
예제 #18
0
    def __init__(
            self,
            start: Point,
            length: float,
            label: str,
            rotation_angle: Angle = Angle(0.0),  # noqa: B008
    ):
        arrow = Arrow(start,
                      start + Point(length, 0)).rotate(rotation_angle, start)
        # should increase spacing for downward pointing axis
        label = LineAnnotation(label, arrow)

        super().__init__({"arrow": arrow, "label": label})
예제 #19
0
    def __init__(self,
                 name_pos="start",
                 x_min=0,
                 x_max=6,
                 y_min=0.5,
                 y_max=1.8):
        xs = np.array([0, 2, 3, 4, 5, 6])
        ys = np.array([1.5, 1.3, 0.7, 0.5, 0.6, 0.8])
        # Scale x and y
        xs = _scale_array(x_min, x_max, xs)
        ys = _scale_array(y_min, y_max, ys)
        points = Point.from_coordinate_lists(xs, ys)

        super().__init__(points)
예제 #20
0
    def __init__(
        self,
        start: Point,
        height: float,
        profile: Callable[[float], Point],
        num_arrows: int,
        scaling: float = 1,
    ):

        self._start = start
        self._height = height
        self._profile = profile
        self._num_arrows = num_arrows
        self._scaling = scaling

        shapes = dict()

        # Draw left line
        shapes["start line"] = Line(self._start,
                                    (self._start + Point(0, self._height)))

        # Draw velocity arrows
        dy = float(self._height) / (self._num_arrows - 1)

        end_points = []

        for i in range(self._num_arrows):
            start_position = Point(start.x, start.y + i * dy)
            end_position = start_position + profile(
                start_position.y) * self._scaling
            end_points += [end_position]
            if start_position == end_position:
                continue
            shapes["arrow%d" % i] = Arrow(start_position, end_position)

        shapes["smooth curve"] = Spline(end_points)
        super().__init__(shapes)
예제 #21
0
    def __init__(self,
                 name=None,
                 name_pos="start",
                 x_min=0,
                 x_max=6,
                 y_min=0,
                 y_max=2):
        xs = np.array([0.0, 2.0, 3.0, 4.0, 5.0, 6.0])
        ys = np.array([1, 1.8, 1.2, 0.7, 0.8, 0.85])

        xs = _scale_array(x_min, x_max, xs)
        ys = _scale_array(y_min, y_max, ys)
        points = Point.from_coordinate_lists(xs, ys)

        super().__init__(points)
예제 #22
0
    def __init__(self,
                 x_min=0,
                 x_max=2.25,
                 y_min=0.046679703125,
                 y_max=1.259375):
        a = 0
        b = 2.25
        resolution = 100
        xs = np.linspace(a, b, resolution + 1)
        ys = self.__call__(xs)
        # Scale x and y
        xs = _scale_array(x_min, x_max, xs)
        ys = _scale_array(y_min, y_max, ys)
        points = Point.from_coordinate_lists(xs, ys)

        super().__init__(points)
예제 #23
0
    def __init__(
        self,
        text: str,
        start: Point,
        end: Point,
        text_position: TextPosition = TextPosition.START,
        spacing: Union[float, Point] = None,
    ):

        spacing = spacing if spacing else self._DEFAULT_SPACING
        if not issubclass(spacing.__class__, Point):
            spacing = Point(spacing, spacing)

        if text_position == self.TextPosition.START:
            text = Text(text, start + spacing)
        if text_position == self.TextPosition.END:
            text = Text(text, end + spacing)

        arrow = Arrow(start, end)
        super().__init__(arrow, text)
예제 #24
0
파일: _arc.py 프로젝트: rvodden/pysketcher
    def __call__(self, theta: Angle) -> Point:
        """Provides a point on the arc ``theta`` of the way around.

        Args:
            theta: The angle from the ``start_angle`` from which the point should
                be taken.

        Returns:
            the point ``theta`` of the way around the arc.

        Raises:
            ValueError: if ``theta`` is beyond the bounds of the arc.
        """
        if self._arc_angle != 0.0 and theta > self._arc_angle:
            raise ValueError("Theta is outside the bounds of the arc")
        iota = Angle(self.start_angle + theta)
        ret_point = Point(
            self.center.x + self.radius * np.cos(iota),
            self.center.y + self.radius * np.sin(iota),
        )
        return ret_point
예제 #25
0
 def scale(self, factor: float) -> "Curve":
     """Scale all coordinates by `factor`: ``x = factor*x``, etc."""
     ret_curve = Curve(
         Point.from_coordinate_lists(factor * self.xs, factor * self.ys))
     ret_curve.style = self.style
     return ret_curve
예제 #26
0
 def upper_right(self) -> Point:
     """The upper right point of the rectangle."""
     return self._lower_left_corner + Point(self._width, self._height)
예제 #27
0
 def mid_top(self) -> Point:
     """The middle of the top of the load."""
     return self._lower_left_corner + Point(self._width / 2, self._height)
예제 #28
0
    def __init__(
        self,
        start: Point,
        total_length: float,
        bar_length: float = None,
        width: float = None,
        dashpot_length: float = None,
        piston_pos: float = None,
    ):
        B = start
        L = total_length
        if width is None:
            w = L / 10.0  # total width 1/5 of length
        else:
            w = width / 2.0
        s = bar_length

        shapes = {}
        # dashpot is p0-p1 in y and width 2*w
        if dashpot_length is None:
            if s is None:
                f = Dashpot._dashpot_fraction
                s = L * (1 - f) / 2.0  # default
            p1 = Point(B.x, B.y + L - s)
            dashpot_length = f * L
        else:
            if s is None:
                f = 1.0 / 2  # the bar lengths are taken as f*dashpot_length
                s = f * dashpot_length  # default
            p1 = Point(B.x, B.y + s + dashpot_length)
        p0 = Point(B.x, B.y + s)
        p2 = Point(B.x, B.y + L)

        if not (p2.y > p1.y > p0.y):
            raise ValueError(
                ("Dashpot has inconsistent dimensions! start: %g, "
                 "dashpot begin: %g, dashpot end: %g, very end: %g" %
                 (B.y, p0.y, p1.y, p2.y)))

        shapes["line start"] = Line(B, p0)

        shapes["pot"] = Curve([
            Point(p1.x - w, p1.y),
            Point(p0.x - w, p0.y),
            Point(p0.x + w, p0.y),
            Point(p1.x + w, p1.y),
        ])
        piston_thickness = dashpot_length * Dashpot._piston_thickness_fraction
        if piston_pos is None:
            piston_pos = 1 / 3.0 * dashpot_length
        if piston_pos < 0:
            piston_pos = 0
        elif piston_pos > dashpot_length:
            piston_pos = dashpot_length - piston_thickness

        abs_piston_pos = p0.y + piston_pos

        gap = w * Dashpot._piston_gap_fraction
        shapes["piston"] = Composition({
            "line":
            Line(p2, Point(B.x, abs_piston_pos + piston_thickness)),
            "rectangle":
            Rectangle(
                Point(B.x - w + gap, abs_piston_pos),
                2 * w - 2 * gap,
                piston_thickness,
            ),
        })
        shapes["piston"]["rectangle"].set_fill_pattern(Style.FillPattern.CROSS)

        super().__init__(shapes)
예제 #29
0
 def _segment_function(t: float) -> Point:
     x = _bernstein_cubic([p0.x, p1.x, p2.x, p3.x], t)
     y = _bernstein_cubic([p0.y, p1.y, p2.y, p3.y], t)
     return Point(x, y)