Skip to content

Commit

Permalink
feat: improve score height calculation
Browse files Browse the repository at this point in the history
  • Loading branch information
deemaagog committed Mar 25, 2024
1 parent baf7153 commit 50694a2
Show file tree
Hide file tree
Showing 27 changed files with 79 additions and 20 deletions.
6 changes: 3 additions & 3 deletions docs/docs/renderers/overview.mdx
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,7 @@ sidebar_position: 1
Score-storm core aims to be decoupled from environments specific renderer.
Library provides a set of renderers : HTML5 Canvas Renderer, SVG Renderer.

Below is an example of the same music score rendered on Canvas and SVG
Below is an example of the same music score rendered on Canvas and SVG. Rendered music srores are nearly identical, some minor differences are caused by how various browsers handle antialiasing/subpixeling

import Tabs from "@theme/Tabs"
import TabItem from "@theme/TabItem"
Expand All @@ -18,13 +18,13 @@ import FontLoader from "@site/src/components/FontLoader"
<TabItem value="Canvas" default>
This score is rendered on canvas
<FontLoader>
<Demo renderer="canvas" />
<Demo renderer="canvas" scale={80} />
</FontLoader>
</TabItem>
<TabItem value="SVG">
This score is rendered as SVG
<FontLoader>
<Demo renderer="svg" />
<Demo renderer="svg" scale={80} />
</FontLoader>
</TabItem>
</Tabs>
Expand Down
7 changes: 4 additions & 3 deletions docs/src/components/Demo/index.js
Original file line number Diff line number Diff line change
Expand Up @@ -3,10 +3,10 @@ import ScoreStorm, { Score } from "@score-storm/core"
import CanvasRenderer from "@score-storm/canvas-renderer"
import SvgRenderer from "@score-storm/svg-renderer"

function Demo({ renderer = "canvas", musicXml = undefined, bordered = false }) {
function Demo({ renderer = "canvas", musicXml = undefined, bordered = false, scale = 100 }) {
const rootElementRef = useRef(null)

const scoreStorm = useRef(new ScoreStorm())
const scoreStorm = useRef(new ScoreStorm({scale}))

useEffect(() => {
if (!rootElementRef.current) {
Expand All @@ -16,6 +16,7 @@ function Demo({ renderer = "canvas", musicXml = undefined, bordered = false }) {
scoreStorm.current.setRenderer(
renderer === "canvas" ? new CanvasRenderer(rootElementRef.current) : new SvgRenderer(rootElementRef.current),
)

scoreStorm.current.render()
}, [rootElementRef, renderer])

Expand All @@ -29,7 +30,7 @@ function Demo({ renderer = "canvas", musicXml = undefined, bordered = false }) {
}, [rootElementRef, musicXml])

return (
<div style={{ width: "100%", border: bordered ? "1px solid #efefef" : "none", padding: "40px" }}>
<div style={{ width: "100%", border: bordered ? "1px solid #efefef" : "none", padding: "40px 0" }}>
<div style={{ width: "100%", lineHeight: 0 }} ref={rootElementRef} />
</div>
)
Expand Down
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
11 changes: 10 additions & 1 deletion packages/canvas-renderer/src/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -58,7 +58,16 @@ class CanvasRenderer implements Renderer {
}

drawRect(x: number, y: number, width: number, height: number) {
this.context.fillRect(x, y, width, height)
// this.context.fillRect(x, y, width, height)

this.context.beginPath()
// this.context.rect(x, y, width, height)
this.context.moveTo(x, y)
this.context.lineTo(x + width, y)
this.context.lineTo(x + width, y + height)
this.context.lineTo(x, y + height)
this.context.lineTo(x, y)
this.context.fill()
}

drawGlyph(glyph: string, x: number, y: number) {
Expand Down
8 changes: 4 additions & 4 deletions packages/core/src/BaseRenderer.ts
Original file line number Diff line number Diff line change
Expand Up @@ -51,7 +51,7 @@ class BaseRenderer {
timeSignatureMargin: 1,
barLineThickness: staffLineThickness * 1.2,
mainColor: "black",
staveLineColor: "red",
staveLineColor: "#666666",
barlineHeight,
midStave: barlineHeight / 2,
...rest,
Expand Down Expand Up @@ -80,7 +80,7 @@ class BaseRenderer {
this.renderer.init()
}

this.graphicalScore = new GraphicalScore(score, this.renderer.containerWidth, this.settings.unit)
this.graphicalScore = new GraphicalScore(score, this.renderer.containerWidth, this.settings)

// clear
this.renderer.clear()
Expand All @@ -99,14 +99,14 @@ class BaseRenderer {
for (let ri = 0; ri < this.graphicalScore.rows.length; ri++) {
const latestRow = ri === this.graphicalScore.rows.length - 1
const row = this.graphicalScore.rows[ri]
this.y = row.yPosition

for (let mi = 0; mi < row.measures.length; mi++) {
const latestMeasureInRow = mi === row.measures.length - 1
const graphicalMeasure = row.measures[mi]
this.renderMeasure(graphicalMeasure, latestRow, latestMeasureInRow)
}
this.x = 0
this.y += row.height
}
}

Expand Down Expand Up @@ -149,7 +149,7 @@ class BaseRenderer {
}

renderStaveLines(graphicalMeasure: GraphicalMeasure) {
this.renderer.setColor("#666666")
this.renderer.setColor(this.settings.staveLineColor)
const half = Math.floor(this.settings.numberOfStaffLines / 2)
for (let index = -half; index <= half; index++) {
this.renderer.drawRect(
Expand Down
16 changes: 15 additions & 1 deletion packages/core/src/graphical/GraphicalClef.ts
Original file line number Diff line number Diff line change
Expand Up @@ -42,7 +42,7 @@ export class GraphicalClef extends BaseGraphical implements IGraphical {
renderer.drawGlyph(
this.getTextFromUnicode(this.clefGlyph.symbol),
x - this.clefGlyph.bBoxes.bBoxSW[0] * settings.unit,
y + settings.unit * this.verticalShift,
y + this.verticalShift * settings.unit,
)

if (settings.debug?.bBoxes) {
Expand All @@ -59,4 +59,18 @@ export class GraphicalClef extends BaseGraphical implements IGraphical {
renderer.setColor("black")
}
}

getMaxY(settings: Settings) {
return Math.max(
this.clefGlyph.bBoxes.bBoxNE[1] * settings.unit - this.verticalShift * settings.unit - settings.midStave,
0,
)
}

getMinY(settings: Settings) {
return Math.max(
-this.clefGlyph.bBoxes.bBoxSW[1] * settings.unit + this.verticalShift * settings.unit - settings.midStave,
0,
)
}
}
2 changes: 1 addition & 1 deletion packages/core/src/graphical/GraphicalRow.ts
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
import { GraphicalMeasure } from "./GraphicalMeasure"

export class GraphicalRow {
height!: number
yPosition!: number
measures!: GraphicalMeasure[]
}
49 changes: 42 additions & 7 deletions packages/core/src/graphical/GraphicalScore.ts
Original file line number Diff line number Diff line change
@@ -1,9 +1,10 @@
import { Settings } from "../BaseRenderer"
import { Clef } from "../model/Measure"
import { Score } from "../model/Score"
import { GraphicalMeasure } from "./GraphicalMeasure"
import { GraphicalRow } from "./GraphicalRow"

const SPACE_BETWEEN_STAVE_ROWS_COEF = 10 // space unit
const SPACE_BETWEEN_STAVE_ROWS_COEF = 6 // space unit

/**
* The main class for graphical representation of music score model
Expand All @@ -12,7 +13,7 @@ export class GraphicalScore {
rows: GraphicalRow[]
height: number

constructor(score: Score, containerWidth: number, unit: number) {
constructor(score: Score, containerWidth: number, settings: Settings) {
const measureWidth = containerWidth / 2 // temp, just for demo

const rows: GraphicalRow[] = []
Expand All @@ -21,20 +22,41 @@ export class GraphicalScore {
let isFirstMeasureInRow = true
let isFirstRow = true
let currentClef: Clef
let currentYTopShift = 0
let currentYBottomShift = 0
let currentYPosition = 0
this.height = 0

for (let i = 0; i < score.measures.length; i++) {
if (currentRowWidth >= containerWidth) {
rows.push({ height: unit * SPACE_BETWEEN_STAVE_ROWS_COEF, measures: currentRowGraphicalMeasures })
currentYPosition =
currentYPosition +
(isFirstRow ? currentYTopShift : settings.unit * SPACE_BETWEEN_STAVE_ROWS_COEF + settings.barlineHeight)
rows.push({
measures: currentRowGraphicalMeasures,
yPosition: currentYPosition,
})

this.height =
this.height +
(isFirstRow ? currentYTopShift : settings.unit * SPACE_BETWEEN_STAVE_ROWS_COEF) +
settings.barlineHeight

currentRowWidth = 0
currentRowGraphicalMeasures = []
isFirstMeasureInRow = true

currentYTopShift = 0
currentYBottomShift = 0

isFirstRow = false
}

const measure = score.measures[i]
const globalMeasure = score.globalMeasures[i]
currentRowWidth += measureWidth

if (isFirstRow) {
if (isFirstRow && isFirstMeasureInRow) {
if (!measure.clef) {
throw new Error("Clef is not set in first measure")
}
Expand All @@ -47,17 +69,30 @@ export class GraphicalScore {
time: isFirstRow ? globalMeasure.time : undefined,
})

if (graphicalMeasure.clef) {
currentYTopShift = graphicalMeasure.clef.getMaxY(settings)
currentYBottomShift = graphicalMeasure.clef.getMinY(settings)
}

currentRowGraphicalMeasures.push(graphicalMeasure)

isFirstMeasureInRow = false
isFirstRow = false
}

if (currentRowGraphicalMeasures.length) {
rows.push({ height: unit * SPACE_BETWEEN_STAVE_ROWS_COEF, measures: currentRowGraphicalMeasures })
currentYPosition =
currentYPosition +
(isFirstRow ? currentYTopShift : settings.unit * SPACE_BETWEEN_STAVE_ROWS_COEF + settings.barlineHeight)
this.height +=
(isFirstRow ? currentYTopShift : settings.unit * SPACE_BETWEEN_STAVE_ROWS_COEF) + settings.barlineHeight
rows.push({
measures: currentRowGraphicalMeasures,
yPosition: currentYPosition,
})
}

this.height += currentYBottomShift
this.height = Math.ceil(this.height) // that needs to be done at row level
this.rows = rows
this.height = unit * SPACE_BETWEEN_STAVE_ROWS_COEF * rows.length // very initial attempt to calculate total height
}
}

0 comments on commit 50694a2

Please sign in to comment.