Skip to content

Commit 0c9ebc9

Browse files
committed
Merge branch 'render-slurs-ties'
2 parents 849d9c6 + 2d7f562 commit 0c9ebc9

File tree

18 files changed

+552
-37
lines changed

18 files changed

+552
-37
lines changed

Runtime/.gitignore

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
Tests/Baselines~/.mscbackup

Runtime/Lib/ABC.dll

3.5 KB
Binary file not shown.
Lines changed: 83 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,83 @@
1+
%YAML 1.1
2+
%TAG !u! tag:unity3d.com,2011:
3+
--- !u!21 &2100000
4+
Material:
5+
serializedVersion: 8
6+
m_ObjectHideFlags: 0
7+
m_CorrespondingSourceObject: {fileID: 0}
8+
m_PrefabInstance: {fileID: 0}
9+
m_PrefabAsset: {fileID: 0}
10+
m_Name: AbcLineMaterial
11+
m_Shader: {fileID: 10755, guid: 0000000000000000f000000000000000, type: 0}
12+
m_Parent: {fileID: 0}
13+
m_ModifiedSerializedProperties: 0
14+
m_ValidKeywords: []
15+
m_InvalidKeywords: []
16+
m_LightmapFlags: 4
17+
m_EnableInstancingVariants: 0
18+
m_DoubleSidedGI: 0
19+
m_CustomRenderQueue: -1
20+
stringTagMap: {}
21+
disabledShaderPasses: []
22+
m_LockedProperties:
23+
m_SavedProperties:
24+
serializedVersion: 3
25+
m_TexEnvs:
26+
- _BumpMap:
27+
m_Texture: {fileID: 0}
28+
m_Scale: {x: 1, y: 1}
29+
m_Offset: {x: 0, y: 0}
30+
- _DetailAlbedoMap:
31+
m_Texture: {fileID: 0}
32+
m_Scale: {x: 1, y: 1}
33+
m_Offset: {x: 0, y: 0}
34+
- _DetailMask:
35+
m_Texture: {fileID: 0}
36+
m_Scale: {x: 1, y: 1}
37+
m_Offset: {x: 0, y: 0}
38+
- _DetailNormalMap:
39+
m_Texture: {fileID: 0}
40+
m_Scale: {x: 1, y: 1}
41+
m_Offset: {x: 0, y: 0}
42+
- _EmissionMap:
43+
m_Texture: {fileID: 0}
44+
m_Scale: {x: 1, y: 1}
45+
m_Offset: {x: 0, y: 0}
46+
- _MainTex:
47+
m_Texture: {fileID: 0}
48+
m_Scale: {x: 1, y: 1}
49+
m_Offset: {x: 0, y: 0}
50+
- _MetallicGlossMap:
51+
m_Texture: {fileID: 0}
52+
m_Scale: {x: 1, y: 1}
53+
m_Offset: {x: 0, y: 0}
54+
- _OcclusionMap:
55+
m_Texture: {fileID: 0}
56+
m_Scale: {x: 1, y: 1}
57+
m_Offset: {x: 0, y: 0}
58+
- _ParallaxMap:
59+
m_Texture: {fileID: 0}
60+
m_Scale: {x: 1, y: 1}
61+
m_Offset: {x: 0, y: 0}
62+
m_Ints: []
63+
m_Floats:
64+
- _BumpScale: 1
65+
- _Cutoff: 0.5
66+
- _DetailNormalMapScale: 1
67+
- _DstBlend: 0
68+
- _GlossMapScale: 1
69+
- _Glossiness: 0.5
70+
- _GlossyReflections: 1
71+
- _Metallic: 0
72+
- _Mode: 0
73+
- _OcclusionStrength: 1
74+
- _Parallax: 0.02
75+
- _SmoothnessTextureChannel: 0
76+
- _SpecularHighlights: 1
77+
- _SrcBlend: 1
78+
- _UVSec: 0
79+
- _ZWrite: 1
80+
m_Colors:
81+
- _Color: {r: 0, g: 0, b: 0, a: 1}
82+
- _EmissionColor: {r: 0, g: 0, b: 0, a: 1}
83+
m_BuildTextureStacks: []

Runtime/Materials/AbcLineMaterial.mat.meta

Lines changed: 8 additions & 0 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

Runtime/Prefabs/ABCLayout.prefab

Lines changed: 7 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -27,9 +27,9 @@ RectTransform:
2727
m_LocalRotation: {x: 0, y: 0, z: 0, w: 1}
2828
m_LocalPosition: {x: 0, y: 0, z: 0}
2929
m_LocalScale: {x: 1, y: 1, z: 1}
30+
m_ConstrainProportionsScale: 0
3031
m_Children: []
3132
m_Father: {fileID: 0}
32-
m_RootOrder: 0
3333
m_LocalEulerAnglesHint: {x: 0, y: 0, z: 0}
3434
m_AnchorMin: {x: 0.5, y: 0.5}
3535
m_AnchorMax: {x: 0.5, y: 0.5}
@@ -48,10 +48,11 @@ MonoBehaviour:
4848
m_Script: {fileID: 11500000, guid: f3b46fe4dd2878e4284e77e0c7f6b689, type: 3}
4949
m_Name:
5050
m_EditorClassIdentifier:
51-
spriteAtlas: {fileID: 4343727234628468602, guid: c135832626606554697bc5e6d39f5867,
52-
type: 2}
53-
layoutScale: 0.35
51+
spriteAtlas: {fileID: 4343727234628468602, guid: c135832626606554697bc5e6d39f5867, type: 2}
5452
color: {r: 0, g: 0, b: 0, a: 1}
5553
NoteMaterial: {fileID: 2100000, guid: 45ad52670051e204a831e584dcff4feb, type: 2}
56-
textPrefab: {fileID: 6477534108993699429, guid: 5a04610f96d954d629c68779a84e25cf,
57-
type: 3}
54+
LineMaterial: {fileID: 2100000, guid: 077cc8a1072d7934c9dac7bcaefce314, type: 2}
55+
textPrefab: {fileID: 6477534108993699429, guid: 5a04610f96d954d629c68779a84e25cf, type: 3}
56+
staffLinePadding: 0.4
57+
staffLineMargin: 1
58+
overrideLineBreaks: 0
Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,11 @@
1+
X:1
2+
T:Test Ties and Slurs
3+
M:C
4+
L:1/4
5+
K:C
6+
7+
(CDEF) | (fedc) | (DDdd) | (ddDD) |
8+
9+
(DFAc | cAFD) |
10+
11+
d-d dd- | d EEE- | EEE-E|

Runtime/Resources/Tests/Slurs.abc.txt.meta

Lines changed: 7 additions & 0 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

Runtime/Scripts/Alignment.cs

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,6 @@
11
using System;
22
using System.Collections.Generic;
3+
using ABC;
34
using UnityEngine;
45

56
namespace ABCUnity
@@ -49,8 +50,11 @@ private bool IsMeasureRest()
4950
public List<Measure> measures { get; private set; }
5051
TimeSignature timeSignature;
5152

53+
public Voice voice {get; private set;}
54+
5255
public void Create(ABC.Voice voice)
5356
{
57+
this.voice = voice;
5458
measures = new List<Measure>();
5559

5660
if (voice.items.Count == 0) return;

Runtime/Scripts/Grouping.cs

Lines changed: 233 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,233 @@
1+
using UnityEngine;
2+
using System.Collections.Generic;
3+
using UnityEditor.Experimental.GraphView;
4+
using System.Runtime.InteropServices.WindowsRuntime;
5+
using System;
6+
using UnityEditor;
7+
8+
namespace ABCUnity
9+
{
10+
static class Grouping
11+
{
12+
const float ParaoblaMidpointScale = 0.3f;
13+
14+
private enum SlurPosition {Above, Below};
15+
16+
public static LineRenderer Create(VoiceLayout.ScoreLine.Element startElement, VoiceLayout.ScoreLine.Element endElement, Material material)
17+
{
18+
var elements = CollectElements(startElement, endElement);
19+
return CreateSingleScorelineSlur(elements, material);
20+
}
21+
22+
private static LineRenderer CreateSingleScorelineSlur(List<VoiceLayout.ScoreLine.Element> elements, Material material)
23+
{
24+
var startElement = elements[0];
25+
var endElement = elements[elements.Count - 1];
26+
var scoreLine = startElement.measure.scoreLine;
27+
28+
var slurPosition = DetermineSlurPosition(elements);
29+
var startPos = startElement.container.transform.localPosition + startElement.measure.container.transform.localPosition;
30+
Vector3 startAnchor, endAnchor;
31+
32+
var endPos = endElement.container.transform.localPosition + endElement.measure.container.transform.localPosition;
33+
34+
if (slurPosition == SlurPosition.Below)
35+
{
36+
startPos += new Vector3(startElement.info.rootBounding.max.x, startElement.info.rootBounding.min.y, 0.0f);
37+
startAnchor = startPos;
38+
39+
startPos += new Vector3(0.1f, -0.1f, 0.0f);
40+
startAnchor.x -= startElement.info.rootBounding.extents.x;
41+
42+
endPos += endElement.info.rootBounding.min;
43+
endAnchor = endPos;
44+
45+
endPos += new Vector3(-0.1f, -0.1f, 0.0f);
46+
endAnchor.x += endElement.info.rootBounding.extents.x;
47+
}
48+
else
49+
{
50+
startPos += new Vector3(startElement.info.rootBounding.max.x, startElement.info.rootBounding.max.y, 0.0f);
51+
startAnchor = startPos;
52+
53+
startPos += new Vector3(0.1f, 0.1f, 0.0f);
54+
startAnchor.x -= startElement.info.rootBounding.extents.x;
55+
56+
endPos += new Vector3(endElement.info.rootBounding.min.x, endElement.info.rootBounding.max.y, 0.0f);
57+
endAnchor = endPos;
58+
59+
endPos += new Vector3(-0.1f, 0.1f, 0.0f);
60+
endAnchor.x += endElement.info.rootBounding.extents.x;
61+
}
62+
63+
var boundingY = GetSlurBoundingY(elements, slurPosition);
64+
Vector3 boundingPt1 = new Vector3(0.0f, boundingY, 0.0f);
65+
Vector3 boundingPt2 = new Vector3(1.0f, boundingY, 0.0f);
66+
67+
var lineMidpoint = (startPos + endPos) / 2.0f;
68+
var anchorMidpoint = (startAnchor + endAnchor) / 2.0f;
69+
70+
71+
var slurMidpoint = MathUtil.LineIntersect(lineMidpoint, anchorMidpoint, boundingPt1, boundingPt2);
72+
var direction = (lineMidpoint - anchorMidpoint).normalized * ParaoblaMidpointScale;
73+
74+
var slurLinePoints = CreatePoints(startPos, slurMidpoint + direction, endPos);
75+
76+
return CreateLineRenderer(scoreLine, slurLinePoints, material);
77+
}
78+
79+
static List<Vector3> CreatePoints(Vector3 startPos, Vector3 midpoint, Vector3 endPos)
80+
{
81+
var slurPositions = new List<Vector3>();
82+
83+
slurPositions.Add(startPos);
84+
85+
float[,] matrix = new float[3, 4]{
86+
{ startPos.x * startPos.x, startPos.x, 1, startPos.y },
87+
{ midpoint.x * midpoint.x, midpoint.x, 1, midpoint.y },
88+
{ endPos.x * endPos.x, endPos.x, 1, endPos.y }
89+
};
90+
91+
matrix = MathUtil.ReducedRowEchelonForm(matrix);
92+
float a = matrix[0, 3];
93+
float b = matrix[1, 3];
94+
float c = matrix[2, 3];
95+
96+
const int segmentCount = 20;
97+
float step = (endPos.x - startPos.x) / segmentCount;
98+
float x = startPos.x;
99+
for (int i = 0; i < segmentCount; i++) {
100+
x += step;
101+
102+
float y = a * (x*x) + b * x + c;
103+
104+
slurPositions.Add(new Vector3(x, y, 0));
105+
}
106+
107+
return slurPositions;
108+
}
109+
110+
private static LineRenderer CreateLineRenderer(VoiceLayout.ScoreLine scoreLine, List<Vector3> slurPositions, Material material)
111+
{
112+
if (scoreLine.slurs == null) {
113+
scoreLine.slurs = new GameObject("Slurs");
114+
scoreLine.slurs.transform.SetParent(scoreLine.container.transform, false);
115+
}
116+
117+
var slur = new GameObject("Slur");
118+
slur.transform.SetParent(scoreLine.slurs.transform, false);
119+
120+
var lineRenderer = slur.AddComponent<LineRenderer>();
121+
lineRenderer.positionCount = slurPositions.Count;
122+
lineRenderer.SetPositions(slurPositions.ToArray());
123+
124+
lineRenderer.useWorldSpace = false;
125+
lineRenderer.startWidth = 0.1f;
126+
lineRenderer.endWidth = 0.1f;
127+
128+
lineRenderer.material = material;
129+
130+
return lineRenderer;
131+
}
132+
133+
/// <summary>
134+
/// Returns a list containing all the elements between start and end elements inclusive.
135+
/// </summary>
136+
137+
private static List<VoiceLayout.ScoreLine.Element> CollectElements(VoiceLayout.ScoreLine.Element startElement, VoiceLayout.ScoreLine.Element endElement)
138+
{
139+
List<VoiceLayout.ScoreLine.Element> elements = new List<VoiceLayout.ScoreLine.Element>();
140+
var currentElement = startElement;
141+
var elementIndex = currentElement.measure.elements.FindIndex(e => e == startElement);
142+
143+
var currentMeasure = currentElement.measure;
144+
var measureIndex = currentMeasure.scoreLine.measures.FindIndex(m => m == currentMeasure);
145+
146+
var currentScoreLine = currentMeasure.scoreLine;
147+
var scoreLineIndex = currentScoreLine.voiceLayout.scoreLines.FindIndex(sl => sl == currentScoreLine);
148+
149+
var voiceLayout = currentScoreLine.voiceLayout;
150+
151+
while (currentMeasure.elements[elementIndex] != endElement) {
152+
elements.Add(currentMeasure.elements[elementIndex++]);
153+
154+
if (elementIndex >= currentMeasure.elements.Count)
155+
{
156+
elementIndex = 0;
157+
measureIndex += 1;
158+
}
159+
160+
if (measureIndex >= currentScoreLine.measures.Count) {
161+
measureIndex = 0;
162+
scoreLineIndex += 1;
163+
}
164+
165+
currentScoreLine = voiceLayout.scoreLines[scoreLineIndex];
166+
currentMeasure = currentScoreLine.measures[measureIndex];
167+
currentElement = currentMeasure.elements[elementIndex];
168+
}
169+
170+
elements.Add(endElement);
171+
172+
return elements;
173+
}
174+
175+
/// Determines the Position of the slur by looking at note directions.
176+
/// If the stems point up then the slur will be placed below.
177+
/// If the stems point down then the slur will be placed above.
178+
/// If the stems are mixed then the slur will be placed above
179+
private static SlurPosition DetermineSlurPosition(List<VoiceLayout.ScoreLine.Element> elements)
180+
{
181+
182+
var clef = elements[0].measure.scoreLine.voiceLayout.voice.clef;
183+
184+
// get the initial direction
185+
int i = 0;
186+
var initialDirection = NoteCreator.NoteDirection.Unknown;
187+
188+
for (; i < elements.Count; i++)
189+
{
190+
if (initialDirection != NoteCreator.NoteDirection.Unknown)
191+
break;
192+
193+
initialDirection = NoteCreator.DetermineNoteDirection(elements[i].item, clef);
194+
}
195+
196+
for (; i < elements.Count; i++)
197+
{
198+
var direction = NoteCreator.DetermineNoteDirection(elements[i].item, clef);
199+
if (direction == NoteCreator.NoteDirection.Unknown)
200+
continue;
201+
202+
if (direction != initialDirection)
203+
return SlurPosition.Above;
204+
}
205+
206+
return initialDirection == NoteCreator.NoteDirection.Up ? SlurPosition.Below : SlurPosition.Above;
207+
}
208+
209+
private static float GetSlurBoundingY(List<VoiceLayout.ScoreLine.Element> elements, SlurPosition slurPosition)
210+
{
211+
if (slurPosition == SlurPosition.Above)
212+
{
213+
float max = float.MinValue;
214+
foreach(var element in elements)
215+
{
216+
max = Math.Max(max, element.info.rootBounding.max.y);
217+
}
218+
219+
return max;
220+
}
221+
else
222+
{
223+
float min = float.MaxValue;
224+
foreach(var element in elements)
225+
{
226+
min = Math.Min(min, element.info.rootBounding.min.y);
227+
}
228+
229+
return min;
230+
}
231+
}
232+
}
233+
}

0 commit comments

Comments
 (0)