Source code for neoscore.western.abstract_slur

from __future__ import annotations

from typing import Optional

from neoscore.core.brush import BrushDef
from neoscore.core.directions import DirectionY
from neoscore.core.math_helpers import interpolate
from neoscore.core.music_font import MusicFont
from neoscore.core.music_path import MusicPath
from neoscore.core.pen import PenDef
from neoscore.core.point import ORIGIN, Point, PointDef
from neoscore.core.positioned_object import PositionedObject
from neoscore.core.units import ZERO, Unit


[docs]class AbstractSlur(MusicPath): """An abstract superclass for slur and tie. This is not meant to be used directly."""
[docs] def __init__( self, pos: PointDef, parent: PositionedObject, direction: DirectionY = DirectionY.UP, height: Optional[Unit] = None, arch_length: Optional[Unit] = None, font: Optional[MusicFont] = None, brush: Optional[BrushDef] = None, pen: Optional[PenDef] = None, ): MusicPath.__init__(self, pos, parent, font, brush, pen) self.direction = direction self.height = height self.arch_length = arch_length # Load relevant engraving defaults from music font self.midpoint_thickness = self.music_font.engraving_defaults[ "slurMidpointThickness" ] self.endpoint_thickness = self.music_font.engraving_defaults[ "slurEndpointThickness" ]
[docs] def draw_slur(self, end_pos, end_parent): """Draw the slur shape. This should generally not be called directly. """ # Work out parameters length = self.distance_to(end_parent, end_pos) abs_height = self.height if self.height else self._derive_height(length) arch_length = ( self.arch_length if self.arch_length else self._derive_arch_length(length) ) mid_height = abs_height * self.direction.value mid_upper_height = mid_height + (self.midpoint_thickness * self.direction.value) end_height = self.endpoint_thickness * self.direction.value # Draw upper curve part self.move_to(ZERO, end_height, self) control_1 = Point(arch_length, mid_upper_height) control_1_parent = self control_2 = Point( self.end_pos.x - arch_length, self.end_pos.y + mid_upper_height ) control_2_parent = self.end_parent end = Point(self.end_pos.x, self.end_pos.y + end_height) end_parent = self.end_parent self.cubic_to( control_1.x, control_1.y, control_2.x, control_2.y, end.x, end.y, control_1_parent, control_2_parent, end_parent, ) # Draw right-side end self.line_to(self.end_pos.x, self.end_pos.y, self.end_parent) # Draw lower curve part control_1 = Point(self.end_pos.x - arch_length, self.end_pos.y + mid_height) control_1_parent = self.end_parent control_2 = Point(arch_length, mid_height) control_2_parent = self end = ORIGIN end_parent = self self.cubic_to( control_1.x, control_1.y, control_2.x, control_2.y, end.x, end.y, control_1_parent, control_2_parent, end_parent, )
def _derive_height(self, length): # length = self.spanner_2d_length unit = self.music_font.unit max_length_considered = unit(10) min_length_considered = unit(4) max_height = unit(2) min_height = unit(0.75) if length > max_length_considered: return max_height if length < min_length_considered: return min_height return interpolate( Point(min_length_considered, min_height), Point(max_length_considered, max_height), length, ) def _derive_arch_length(self, length): # spanner_length = self.spanner_2d_length unit = self.music_font.unit max_spanner_length_considered = unit(10) min_spanner_length_considered = unit(4) max_arch_length = unit(1) min_arch_length = unit(0) if length > max_spanner_length_considered: return max_arch_length if length < min_spanner_length_considered: return min_arch_length return interpolate( Point(min_spanner_length_considered, min_arch_length), Point(max_spanner_length_considered, max_arch_length), length, )