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