Пример #1
0
 def __init__(
     self,
     start: PointDef,
     start_parent: GraphicObject,
     stop: PointDef,
     stop_parent: GraphicObject,
 ):
     """
     Args:
         start: The starting (left) position of the beam
         start_parent: The parent for the starting position.
             Must be a staff or in one.
         stop: The ending (right) position of the beam
         stop_parent: The parent for the ending position.
             Must be a staff or in one.
     """
     Path.__init__(self, start, parent=start_parent)
     StaffObject.__init__(self, start_parent)
     self.beam_thickness = self.staff.music_font.engraving_defaults[
         "beamThickness"]
     # Draw beam
     stop = Point.from_def(stop)
     self.line_to(stop.x, stop.y, stop_parent)
     self.line_to(stop.x, stop.y + self.beam_thickness, stop_parent)
     self.line_to(ZERO, self.beam_thickness, self)
     self.close_subpath()
Пример #2
0
 def __init__(
     self,
     start: PointDef,
     start_parent: GraphicObject,
     stop: PointDef,
     stop_parent: Optional[GraphicObject],
     direction: int = -1,
 ):
     """
     Args:
         start: The starting point.
         start_parent: The parent for the starting position.
             Must be a staff or in one.
         stop: The stopping point.
         stop_parent: The parent for the ending position.
             If `None`, defaults to `self`.
         direction: The direction of the slur, where
             `-1` indicates curving upward, and `1` vice versa.
     """
     Path.__init__(self,
                   start,
                   parent=start_parent,
                   brush=Brush((0, 0, 0, 255)))
     StaffObject.__init__(self, self.parent)
     stop = Point.from_def(stop)
     Spanner2D.__init__(self, stop, stop_parent or self)
     self.direction = direction
     # Load relevant engraving defaults from music font
     engraving_defaults = self.staff.music_font.engraving_defaults
     self.midpoint_thickness = self.staff.unit(
         engraving_defaults["slurMidpointThickness"])
     self.endpoint_thickness = self.staff.unit(
         engraving_defaults["slurEndpointThickness"])
     self._draw_path()
Пример #3
0
 def __init__(
     self,
     start: PointDef,
     start_parent: GraphicObject,
     stop: PointDef,
     stop_parent: Optional[GraphicObject],
     direction: int,
     width: Optional[Unit] = None,
 ):
     """
     Args:
         start: The starting point.
         start_parent: The parent for the starting position.
             Must be a staff or in one.
         stop: The stopping point.
         stop_parent: The parent for the ending position.
             If `None`, defaults to `self`.
         direction: The direction of the hairpin, where `-1` means diminuendo (>)
             and `1` means crescendo (<).
         width: The width of the wide hairpin. Defaults to 1 staff unit.
     """
     Path.__init__(self, start, parent=start_parent)
     StaffObject.__init__(self, start_parent)
     stop = Point.from_def(stop)
     Spanner2D.__init__(self, stop, stop_parent or self)
     self.direction = direction
     self.width = width if width is not None else self.staff.unit(1)
     self.thickness = self.staff.music_font.engraving_defaults[
         "hairpinThickness"]
     self._draw_path()
Пример #4
0
 def test_move_to_with_parent(self):
     path = Path(ORIGIN)
     parent = InvisibleObject((Unit(100), Unit(50)))
     path.move_to(Unit(10), Unit(11), parent)
     assert len(path.elements) == 1
     assert_path_els_equal(
         path.elements[0], MoveTo(Point(Unit(10), Unit(11)), parent)
     )
Пример #5
0
 def test_line_to_with_parent(self):
     path = Path((Unit(5), Unit(6)))
     parent = InvisibleObject((Unit(100), Unit(50)))
     path.line_to(Unit(1), Unit(3), parent)
     assert path.elements[-1].parent == parent
     resolved_els = path._resolve_path_elements()
     assert resolved_els == [
         ResolvedMoveTo(ZERO, ZERO),
         ResolvedLineTo(Unit(100 + 1 - 5), Unit(50 + 3 - 6)),
     ]
Пример #6
0
 def test_line_to(self):
     path = Path((Unit(5), Unit(6)))
     path.line_to(Unit(10), Unit(12))
     assert len(path.elements) == 2
     assert_path_els_equal(path.elements[0], MoveTo(ORIGIN, path))
     assert_path_els_equal(path.elements[1], LineTo(Point(Unit(10), Unit(12)), path))
     resolved_els = path._resolve_path_elements()
     assert resolved_els == [
         ResolvedMoveTo(ZERO, ZERO),
         ResolvedLineTo(Unit(10), Unit(12)),
     ]
Пример #7
0
 def test_close_subpath(self):
     path = Path((Unit(5), Unit(6)))
     path.line_to(Unit(10), Unit(10))
     path.line_to(Unit(10), Unit(100))
     path.close_subpath()
     assert len(path.elements) == 4
     assert_path_els_equal(path.elements[3], MoveTo(ORIGIN, path))
Пример #8
0
    def __init__(self, start: PointDef, height: Unit, parent: GraphicObject):
        """
        Args:
            start: Starting point for the stem
            height: The height of the stem,
                where positive extend downward.
            parent:
        """
        Path.__init__(self, start, parent=parent)
        StaffObject.__init__(self, parent=parent)
        thickness = self.staff.music_font.engraving_defaults["stemThickness"]
        self.pen = Pen(thickness=thickness)

        self._height = height
        # Draw stem path
        self.line_to(self.staff.unit(0), self.height)
Пример #9
0
 def test_init(self):
     mock_parent = InvisibleObject(ORIGIN, parent=None)
     test_pen = Pen("#eeeeee")
     test_brush = Brush("#dddddd")
     path = Path((Unit(5), Unit(6)), test_pen, test_brush, mock_parent)
     assert path.pos == Point(Unit(5), Unit(6))
     assert path.pen == test_pen
     assert path.brush == test_brush
Пример #10
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)
Пример #11
0
 def test_cubic_to_with_no_parents(self):
     path = Path((Unit(5), Unit(6)))
     path.cubic_to(Unit(10), Unit(11), ZERO, Unit(1), Unit(5), Unit(6))
     assert len(path.elements) == 2
     assert_path_els_equal(path.elements[0], MoveTo(ORIGIN, path))
     assert_path_els_equal(
         path.elements[1],
         CurveTo(
             ControlPoint(Point(Unit(10), Unit(11)), path),
             ControlPoint(Point(ZERO, Unit(1)), path),
             Point(Unit(5), Unit(6)),
             path,
         ),
     )
     resolved_els = path._resolve_path_elements()
     assert resolved_els == [
         ResolvedMoveTo(ZERO, ZERO),
         ResolvedCurveTo(Unit(10), Unit(11), ZERO, Unit(1), Unit(5), Unit(6)),
     ]
Пример #12
0
 def __init__(self, pos: PointDef, parent: GraphicObject,
              base_length: Unit):
     """
     Args:
         pos: The position of the left edge of the notehead column.
         parent: The parent, which must be a staff or in one.
         base_length: The of the notehead this line is related to.
             The real length will be this plus a small extension defined in the
             `MusicFont`s engraving defaults.
     """
     Path.__init__(self, pos, parent=parent)
     StaffObject.__init__(self, parent=parent)
     thickness = self.staff.music_font.engraving_defaults[
         "legerLineThickness"]
     self.pen = Pen(thickness=thickness)
     extension = self.staff.music_font.engraving_defaults[
         "legerLineExtension"]
     length = base_length + extension
     self.move_to(extension * -1, self.staff.unit(0))
     self.line_to(length, self.staff.unit(0))
Пример #13
0
 def test_cubic_to_with_parents(self):
     path = Path((Unit(Unit(100)), Unit(Unit(200))))
     parent_1 = InvisibleObject((Unit(Unit(10)), Unit(Unit(20))))
     parent_2 = InvisibleObject((Unit(Unit(30)), Unit(Unit(40))))
     parent_3 = InvisibleObject((Unit(Unit(50)), Unit(Unit(60))))
     path.cubic_to(
         Unit(1),
         Unit(2),
         Unit(3),
         Unit(4),
         Unit(5),
         Unit(6),
         parent_1,
         parent_2,
         parent_3,
     )
     assert len(path.elements) == 2
     assert_path_els_equal(path.elements[0], MoveTo(ORIGIN, path))
     assert_path_els_equal(
         path.elements[1],
         CurveTo(
             ControlPoint(Point(Unit(1), Unit(2)), parent_1),
             ControlPoint(Point(Unit(3), Unit(4)), parent_2),
             Point(Unit(5), Unit(6)),
             parent_3,
         ),
     )
     resolved_els = path._resolve_path_elements()
     assert resolved_els == [
         ResolvedMoveTo(ZERO, ZERO),
         ResolvedCurveTo(
             Unit(10 + 1 - 100),
             Unit(20 + 2 - 200),
             Unit(30 + 3 - 100),
             Unit(40 + 4 - 200),
             Unit(50 + 5 - 100),
             Unit(60 + 6 - 200),
         ),
     ]
Пример #14
0
 def __init__(
     self,
     start: PointDef,
     start_parent: GraphicObject,
     end_x: Unit,
     end_parent: Optional[GraphicObject] = None,
     half_lift_positions: Unit = None,
 ):
     """
     Args:
         start (Point or init tuple): The starting position of the
             pedal line.
         start_parent (GraphicObject): An object either in a Staff
             or an actual Staff.
         end_x (Unit): The
     """
     StaffObject.__init__(self, start_parent)
     pen = Pen(thickness=self.staff.music_font.
               engraving_defaults["pedalLineThickness"])
     Path.__init__(self, start, pen, parent=start_parent)
     Spanner.__init__(self, end_x, end_parent or self)
     self.half_lift_positions = half_lift_positions
     self._draw_path()
Пример #15
0
 def draw_bar_lines(self):
     for measure_num in range(self.measure_count + 1):
         current_path = Path(
             (Measure(measure_num), GridUnit(0)),
             pen=Score._bar_line_pen,
             parent=self,
         )
         drawing = False
         for divider_num in range(len(self.instruments) + 1):
             if self._bar_line_extends_below(measure_num, divider_num):
                 if not drawing:
                     current_path.move_to(
                         GridUnit(0), Score._instrument_pos_y(divider_num))
                     drawing = True
             else:
                 if drawing:
                     current_path.line_to(
                         GridUnit(0), Score._instrument_pos_y(divider_num))
                     drawing = False
Пример #16
0
 def draw_instrument_dividers(self):
     for divider in range(len(self.instruments) + 1):
         current_path = Path(
             (Measure(0), Score._divider_pos_y(divider)),
             pen=Score._instrument_divider_pen,
             parent=self,
         )
         instrument_above = self.instruments[divider -
                                             1] if divider > 0 else None
         instrument_below = (self.instruments[divider]
                             if divider < len(self.instruments) else None)
         drawing = False
         for measure_num in range(self.measure_count + 1):
             if Score._divider_visible(instrument_above, instrument_below,
                                       measure_num):
                 if not drawing:
                     current_path.move_to(Measure(measure_num), GridUnit(0))
                     drawing = True
             else:
                 if drawing:
                     current_path.line_to(Measure(measure_num), GridUnit(0))
                     drawing = False
Пример #17
0
 def test_move_to_with_no_parent(self):
     path = Path((Unit(5), Unit(6)))
     path.move_to(Unit(10), Unit(11))
     assert len(path.elements) == 1
     assert_path_els_equal(path.elements[0], MoveTo(Point(Unit(10), Unit(11)), path))
Пример #18
0
 def test_straight_line(self):
     path = Path.straight_line((Unit(5), Unit(6)), (Unit(10), Unit(11)))
     assert path.pos == Point(Unit(5), Unit(6))
     assert len(path.elements) == 2
     assert_path_els_equal(path.elements[0], MoveTo(ORIGIN, path))
     assert_path_els_equal(path.elements[1], LineTo(Point(Unit(10), Unit(11)), path))
Пример #19
0
 def __init__(self, parent, length):
     Path.__init__(
         self, (GridUnit(0), GridUnit(0)), pen=_EventBox.box_pen, parent=parent
     )
     Spanner.__init__(self, length, self)
     self._construct_path()
Пример #20
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,
        )
Пример #21
0
class OctaveLine(ObjectGroup, Spanner, StaffObject):

    """An octave indication with a dashed line.

    When placed in the context of a Staff, pitched content under the spanner
    is automatically transposed accordingly. Care should be taken to ensure
    OctaveLines do not overlap with one another. If this occurs,
    the transposition reflected in the staff will be an undefined choice
    among those active.

    Supported octave indications are:
        - '15ma' (two octaves higher)
        - '8va' (one octave higher)
        - '8vb' (one octave lower)
        - '15mb' (two octaves lower)

    At the starting position the octave is written in text, followed by
    a dashed line ending in a small vertical hook pointing toward the staff.
    If the spanner goes across line breaks, the octave text is repeated
    in parenthesis at the line beginning.

    TODO LOW: The dashed line portion of this spanner overlaps with
    the '8va' text. This is an involved fix that may require
    implementing text background masking or a way to easily inject
    line continuation offsets for paths.
    """

    intervals = {
        "15ma": Interval("aP15"),
        "8va": Interval("aP8"),
        "8vb": Interval("dP8"),
        "15mb": Interval("dP15"),
    }

    glyphs = {
        "15ma": "quindicesimaAlta",
        "8va": "ottavaAlta",
        "8vb": "ottavaBassaVb",
        "15mb": "quindicesimaBassaMb",
        "(": "octaveParensLeft",
        ")": "octaveParensRight",
    }

    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,
        )

    @property
    def length(self) -> Unit:
        return self.spanner_x_length