Beispiel #1
0
 def __init__(
     self, pos_x: Unit, pitch: PitchDef, duration: BeatDef, parent: GraphicObject
 ):
     """
     Args:
         pos_x (Unit): The x-axis position relative to `parent`.
             The y-axis position is calculated automatically based
             on `pitch` and contextual information in `self.staff`.
         pitch (Pitch or str): May be a `str` pitch representation.
             See `Pitch` for valid signatures.
         duration (Beat or init tuple): The logical duration of
             the notehead. This is used to determine the glyph style.
         parent (GraphicObject): Must either be a `Staff` or an object
             with an ancestor `Staff`.
     """
     self._pitch = Pitch.from_def(pitch)
     self._duration = Beat.from_def(duration)
     # Use a temporary y-axis position before calculating it for real
     MusicText.__init__(
         self,
         (pos_x, ZERO),
         [self._glyphnames[self.duration.base_division]],
         parent,
     )
     StaffObject.__init__(self, parent)
     self.y = self.staff.unit(
         self.staff_pos - map_between(self.staff, self.parent).y
     )
Beispiel #2
0
 def test_map_between_where_src_parent_is_dest(self):
     destination = InvisibleObject((Unit(3), Unit(10)),
                                   neoscore.document.pages[0])
     source = InvisibleObject((Unit(1), Unit(2)), destination)
     relative_pos = map_between(source, destination)
     expected = Point(Unit(-1), Unit(-2))
     assert_almost_equal(relative_pos, expected)
Beispiel #3
0
    def _render_occurrence(self, pos: Point, local_start_x: Unit,
                           shift_for_clef: bool):
        """Render one appearance of one key signature accidental.

        Much of the positioning code needs to be performed
        per-occurrence because key signatures can have different
        appearances when clefs change.

        Ideally there should be a way to cache/centralize much of this
        work.

        """
        staff_pos_in_flowable = map_between(self.flowable, self.staff)
        pos_x_in_staff = local_start_x - staff_pos_in_flowable.x
        clef = self.staff.active_clef_at(pos_x_in_staff)
        if clef is None:
            return
        clef_type = clef.clef_type
        pos_tuple = _KeySignatureAccidental.positions[
            self.accidental_type][clef_type][self.pitch_letter]
        visual_pos_x = self.staff.unit(pos_tuple[0]) + pos.x
        visual_pos_y = self.staff.unit(pos_tuple[1]) + pos.y
        if shift_for_clef:
            visual_pos_x += self._padded_clef_width(clef)
        self._render_slice(Point(visual_pos_x, visual_pos_y))
Beispiel #4
0
 def test_map_between_with_common_parent(self):
     parent = InvisibleObject((Unit(5), Unit(6)),
                              neoscore.document.pages[0])
     source = InvisibleObject((Unit(1), Unit(2)), parent)
     destination = InvisibleObject((Unit(3), Unit(10)), parent)
     relative_pos = map_between(source, destination)
     expected = Point(Unit(2), Unit(8))
     assert_almost_equal(relative_pos, expected)
Beispiel #5
0
    def vertical_span(self) -> Unit:
        """StaffUnit: The vertical distance covered by the staves

        The distance from the top of `self.highest_staff` to the bottom
        of `self.lowest_staff`, in `self.highest_staff.unit` StaffUnits.
        """
        return self.highest_staff.unit(
            mapping.map_between(self.highest_staff, self.lowest_staff).y +
            self.lowest_staff.height)
Beispiel #6
0
    def test_map_between(self):
        source = InvisibleObject((Unit(5), Unit(6)),
                                 neoscore.document.pages[1])
        destination = InvisibleObject((Unit(99), Unit(90)),
                                      neoscore.document.pages[4])
        relative_pos = map_between(source, destination)

        page_1_pos = canvas_pos_of(neoscore.document.pages[1])
        page_4_pos = canvas_pos_of(neoscore.document.pages[4])

        expected = (page_4_pos + Point(Unit(99), Unit(90))) - (
            page_1_pos + Point(Unit(5), Unit(6)))
        assert_almost_equal(relative_pos, expected)
Beispiel #7
0
    def spanner_2d_length(self) -> Unit:
        """The 2d length of the spanner.

        Note: This takes into account both the x and y axis. For only
            the horizontal length, use `spanner_x_length`.
        """
        if self.end_parent == self:
            relative_stop = self.end_pos
        else:
            relative_stop = (
                map_between(cast(Positioned, self), self.end_parent) +
                self.end_pos)
        distance = Unit(
            math.sqrt((relative_stop.x.base_value**2) +
                      (relative_stop.y.base_value**2)))
        return type(cast(Positioned, self).pos.x)(distance)
Beispiel #8
0
 def __init__(self, pos_x: Unit, staves: Iterable[Staff]):
     """
     Args:
         pos_x: The barline position relative to
             the top staff.
         staves:
     """
     MultiStaffObject.__init__(self, set(staves))
     Path.__init__(self, Point(pos_x, ZERO), parent=self.highest_staff)
     engraving_defaults = self.highest_staff.music_font.engraving_defaults
     thickness = engraving_defaults["thinBarlineThickness"]
     self.pen = Pen(thickness=thickness)
     # Draw path
     offset = map_between(self.lowest_staff, self.highest_staff)
     bottom_x = pos_x + offset.x
     self.line_to(bottom_x,
                  self.lowest_staff.height,
                  parent=self.lowest_staff)
Beispiel #9
0
 def end_y(self) -> Unit:
     """The y position of the endpoint"""
     return map_between(self.end_parent, cast(Positioned, self)).y
Beispiel #10
0
 def _relative_element_pos(self, element: Parent) -> Point:
     return map_between(self, element)
Beispiel #11
0
    def __init__(
        self,
        start: PointDef,
        start_parent: GraphicObject,
        end_x: Unit,
        end_parent: Optional[GraphicObject] = None,
        indication: str = "8va",
    ):
        """
        Args:
            start (Point or tuple init args):
            start_parent (GraphicObject): An object either in a Staff or
                a staff itself. This object will become the line's parent.
            end_x (Unit): The spanner end x position. The y position will be
                automatically calculated to be horizontal.
            end_parent (GraphicObject): An object either in a Staff or
                a staff itself. The root staff of this *must* be the same
                as the root staff of `start_parent`. If omitted, the
                stop point is relative to the start point.
            indication (str): A valid octave indication.
                currently supported indications are:
                    - '15ma' (two octaves higher)
                    - '8va' (one octave higher)
                    - '8vb' (one octave lower)
                    - '15mb' (two octaves lower)
                The default value is '8va'.
        """
        ObjectGroup.__init__(self, start, start_parent)
        Spanner.__init__(self, end_x, end_parent or self)
        StaffObject.__init__(self, self.parent)
        self.transposition = Transposition(OctaveLine.intervals[indication])
        self.line_text = _OctaveLineText(
            # No offset relative to ObjectGroup
            pos=ORIGIN,
            parent=self,
            length=self.length,
            indication=indication,
        )

        # Vertically center the path relative to the text
        text_rect = self.line_text.bounding_rect
        # TODO LOW line needs some padding
        path_x = text_rect.width
        path_y = text_rect.height / -2
        self.line_path = Path(
            pos=Point(path_x, path_y),
            pen=Pen(
                thickness=self.staff.music_font.engraving_defaults[
                    "octaveLineThickness"
                ],
                pattern=PenPattern.DASH,
            ),
            parent=self,
        )
        # Drawn main line part
        self.line_path.line_to(self.end_pos.x, path_y, self.end_parent)
        pos_relative_to_staff = map_between(self.staff, self)
        # Draw end hook pointing toward the staff
        hook_direction = 1 if pos_relative_to_staff.y <= ZERO else -1
        self.line_path.line_to(
            self.end_pos.x,
            (path_y + self.staff.unit(0.75 * hook_direction)),
            self.end_parent,
        )
Beispiel #12
0
 def test_map_between_with_same_source_and_dest(self):
     obj = InvisibleObject((Unit(5), Unit(6)), neoscore.document.pages[0])
     assert_almost_equal(map_between(obj, obj), Point(Unit(0), Unit(0)))
Beispiel #13
0
    def _find_hairpin_points(
        self,
    ) -> tuple[Point, GraphicObject, Point, GraphicObject, Point,
               GraphicObject]:
        """Find the hairpin path points for a set of parameters.

        The returned tuple is 3 pairs of Points and parents, where the
        outer 2 represent the wide ends of the hairpin and the middle
        represents the small end joint.
        """
        if self.direction == -1:
            joint_pos = self.end_pos
            joint_parent = self.end_parent
            end_center_pos = self.pos
            end_center_parent = self.parent
        else:
            joint_pos = self.pos
            joint_parent = self.parent
            end_center_pos = self.end_pos
            end_center_parent = self.end_parent
        dist = self.width / 2
        # Find relative distance from joint to end_center_pos
        parent_distance = map_between(joint_parent, end_center_parent)
        relative_stop = parent_distance + end_center_pos - joint_pos
        if relative_stop.y == ZERO:
            return (
                Point(end_center_pos.x, end_center_pos.y + dist),
                end_center_parent,
                joint_pos,
                joint_parent,
                Point(end_center_pos.x, end_center_pos.y - dist),
                end_center_parent,
            )
        elif relative_stop.x == ZERO:
            return (
                Point(end_center_pos.x + dist, end_center_pos.y),
                end_center_parent,
                joint_pos,
                joint_parent,
                Point(end_center_pos.x - dist, end_center_pos.y),
                end_center_parent,
            )
        # else ...

        # Find the two points (self.width / 2) away from the end_center_pos
        # which lie on the line perpendicular to the spanner line.

        #   Note that there is no risk of division by zero because
        #   previous if / elif statements catch those possibilities
        center_slope = relative_stop.y / relative_stop.x
        opening_slope = (center_slope * -1)**-1
        opening_y_intercept = (end_center_pos.x *
                               opening_slope) - end_center_pos.y
        # Find needed x coordinates of outer points
        #     x = dist / sqrt(1 + slope^2)
        first_x = end_center_pos.x + (dist / math.sqrt(1 + (opening_slope**2)))
        last_x = end_center_pos.x - (dist / math.sqrt(1 + (opening_slope**2)))
        # Calculate matching y coordinates from opening line function
        first_y = (first_x * opening_slope) - opening_y_intercept
        last_y = (last_x * opening_slope) - opening_y_intercept
        return (
            Point(first_x, first_y),
            end_center_parent,
            joint_pos,
            joint_parent,
            Point(last_x, last_y),
            end_center_parent,
        )
Beispiel #14
0
 def pos_in_staff(self) -> Point:
     """The logical position of this object relative to the staff."""
     return mapping.map_between(self.staff, cast(mapping.Positioned, self))