Source code for plotpy.items.shape.segment

# -*- coding: utf-8 -*-

from __future__ import annotations

from typing import TYPE_CHECKING

import numpy as np
from guidata.utils.misc import assert_interfaces_valid

from plotpy.items.shape.polygon import PolygonShape

if TYPE_CHECKING:
    from plotpy.styles.shape import ShapeParam


[docs] class SegmentShape(PolygonShape): """Segment shape Args: x1: X coordinate of the first point y1: Y coordinate of the first point x2: X coordinate of the second point y2: Y coordinate of the second point shapeparam: Shape parameters """ CLOSED = False ADDITIONNAL_POINTS = 1 # Number of points which are not part of the shape _icon_name = "segment.png" def __init__( self, x1: float = 0.0, y1: float = 0.0, x2: float = 0.0, y2: float = 0.0, shapeparam: ShapeParam = None, ): super().__init__(shapeparam=shapeparam) self.set_rect(x1, y1, x2, y2)
[docs] def set_rect(self, x1: float, y1: float, x2: float, y2: float) -> None: """Set the segment coordinates Args: x1: X coordinate of the first point y1: Y coordinate of the first point x2: X coordinate of the second point y2: Y coordinate of the second point """ self.set_points([(x1, y1), (x2, y2), (0.5 * (x1 + x2), 0.5 * (y1 + y2))])
[docs] def get_rect(self) -> tuple[float, float, float, float]: """Return the segment coordinates Returns: Segment coordinates as a tuple (x1, y1, x2, y2) """ return tuple(self.points[0]) + tuple(self.points[1])
[docs] def move_point_to( self, handle: int, pos: tuple[float, float], ctrl: bool = False ) -> None: """Move a handle as returned by hit_test to the new position Args: handle: Handle pos: Position ctrl: True if <Ctrl> button is being pressed, False otherwise """ nx, ny = pos x1, y1, x2, y2 = self.get_rect() if ctrl: # compute linear coefficient y = a * x + b # When ctrl is pressed, the point is moved on the line # defined by the segment, and the line is conserved # (the segment is not rotated) if x2 == x1: yA = ny xA = x1 elif y2 == y1: yA = y1 xA = nx else: a = (y2 - y1) / (x2 - x1) b = y1 - a * x1 xA = a / (a * a + 1) * (nx / a + ny - b) yA = a * xA + b if handle == 0: self.set_rect(xA, yA, x2, y2) elif handle == 1: self.set_rect(x1, y1, xA, yA) elif handle in (2, -1): # selection du point milieu, on déplace le segment # c'est identique au comportmeent sans la touche Ctrl delta = (nx, ny) - self.points.mean(axis=0) self.points += delta else: # Ctrl is not pressed, the segment may be rotated freely if handle == 0: self.set_rect(nx, ny, x2, y2) elif handle == 1: self.set_rect(x1, y1, nx, ny) elif handle in (2, -1): delta = (nx, ny) - self.points.mean(axis=0) self.points += delta
def __reduce__(self) -> tuple: """Reduce object to picklable state""" state = (self.shapeparam, self.points, self.z()) return (self.__class__, (), state) def __setstate__(self, state: tuple) -> None: """Set object state from pickled state""" param, points, z = state # ---------------------------------------------------------------------- # compatibility with previous version of SegmentShape: x1, y1, x2, y2, x3, y3 = points.ravel() v12 = np.array((x2 - x1, y2 - y1)) v13 = np.array((x3 - x1, y3 - y1)) if np.linalg.norm(v12) < np.linalg.norm(v13): # old pickle format points = np.flipud(np.roll(points, -1, axis=0)) # ---------------------------------------------------------------------- self.points = points self.setZ(z) self.shapeparam = param self.shapeparam.update_item(self)
assert_interfaces_valid(SegmentShape)