05 Note Drawing
In this chapter, we will map each note's time to a panel position and draw the note with undistorted proportions.
Existing Sprite
The shared Skin class in guide/lib/skin.py already maps note to a standard cyan note sprite from Play. Preview reuses this field, so confirm it is present but do not add another declaration:
python
@skin
class Skin:
# ...
note: StandardSprite.NOTE_HEAD_CYANScaled Screen
Unlike play mode, preview uses different scales for the x and y axes. One local x unit spans screen().h / 20, while one local y unit spans screen().h / PANEL_HEIGHT. A local height must therefore be multiplied by the ratio of these scales, PANEL_HEIGHT / 20, to have the same size on screen as a local width.
To draw notes with the correct proportions, add a height scale factor to guide/preview/chart.py:
python
HEIGHT_SCALE = PANEL_HEIGHT / 20Calculating Position
In guide/preview/chart.py, add helpers that map a time to a position. Floor division selects the panel and the remainder gives the vertical position within it:
python
from sonolus.script.vec import Vec2
def x_at_time(time: float):
return (time // PANEL_HEIGHT) * PANEL_WIDTH
def y_at_time(time: float):
return time % PANEL_HEIGHT
def pos_at_time(time: float):
return Vec2(x_at_time(time), y_at_time(time))Drawing
In guide/preview/note.py, add the Rect, Vec2, Options, and Skin imports shown below. Replace the existing guide.preview.chart import with the combined import shown here, then add render. The height scale keeps the note square on screen despite the different local x and y scales:
python
from sonolus.script.quad import Rect
from sonolus.script.vec import Vec2
from guide.lib.options import Options
from guide.lib.skin import Skin
from guide.preview.chart import HEIGHT_SCALE, Chart, pos_at_time
class PreviewNote(PreviewArchetype):
# ...
def render(self):
time = beat_to_time(self.beat)
position = pos_at_time(time)
layout = Rect.from_center(
position,
Vec2(2 * Options.note_size, 2 * Options.note_size * HEIGHT_SCALE),
)
Skin.note.draw(layout, z=(2, -time))Layer 2 keeps Notes above the stage and the timing lines added later. As in Play chapter 10, the second value in the z tuple keeps overlapping Notes in a stable order.
Checkpoint
Refresh Preview. Every sample note should appear in the panel that contains its target time, and each note should keep the same proportions it has in Play.