Skip to content

Commit 62ddacb

Browse files
Fixed bugs with unpredictable reconstruction of PlayGround
1 parent 358ffdd commit 62ddacb

14 files changed

+128
-113
lines changed
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,5 @@
11
using AngleSharp.Dom;
22
using KristofferStrube.Blazor.SVGEditor;
3-
using KristofferStrube.Blazor.SVGEditor.Extensions;
43
using Microsoft.AspNetCore.Components.Web;
54

65
namespace KristofferStrube.Blazor.WebAudio.WasmExample.AudioEditor;
@@ -20,111 +19,111 @@ public Connector(IElement element, SVGEditor.SVGEditor svg) : base(element, svg)
2019

2120
public override string StateRepresentation => base.StateRepresentation + IsHovered.ToString();
2221

23-
private (Node node, ulong port)? from;
24-
public (Node node, ulong port)? From
22+
private Node? from;
23+
public Node? From
2524
{
2625
get
2726
{
27+
// If not previous loaded, then load from attributes and connect node-node or node-audioparam pairs that it connects.
2828
if (from is null)
2929
{
3030
var fromNode = (Node?)SVG.Elements.FirstOrDefault(e => e is Node && e.Id == Element.GetAttribute("data-from-node"));
31-
ulong fromPort = (ulong)Element.GetAttributeOrZero("data-from-port");
32-
_ = fromNode?.OutgoingConnectors.Add((this, fromPort));
31+
_ = fromNode?.OutgoingConnectors.Add(this);
3332
if (fromNode is null)
3433
{
3534
return null;
3635
}
3736

38-
if (To is { } to)
39-
{
40-
QueuedTasks.Enqueue(async context => await (await fromNode.AudioNode(context)).ConnectAsync(await to.node.AudioNode(context), fromPort, to.port));
41-
}
42-
from = (fromNode, fromPort);
37+
QueueConnect(fromNode, To);
38+
39+
from = fromNode;
4340
}
4441
return from;
4542
}
4643
set
4744
{
48-
if (from is { } previousValue)
49-
{
50-
_ = previousValue.node.OutgoingConnectors.Remove((this, previousValue.port));
51-
QueuedTasks.Enqueue(async context => await (await previousValue.node.AudioNode(context)).DisconnectAsync(previousValue.port));
52-
}
45+
// We don't support updating the from-node to some new value.
46+
47+
// If the new value if null then remove its attribute
5348
if (value is null)
5449
{
5550
_ = Element.RemoveAttribute("data-from-node");
56-
_ = Element.RemoveAttribute("data-from-port");
5751
from = null;
5852
}
59-
else
53+
else // If it is actually a new value
6054
{
61-
Element.SetAttribute("data-from-node", value.Value.node.Id);
62-
Element.SetAttribute("data-from-port", value.Value.port.ToString());
55+
Element.SetAttribute("data-from-node", value.Id);
6356
from = value;
64-
_ = value.Value.node.OutgoingConnectors.Add((this, value.Value.port));
65-
if (To is { } to)
57+
if (value is { } newNode )
6658
{
67-
QueuedTasks.Enqueue(async context => await (await value.Value.node.AudioNode(context)).ConnectAsync(await to.node.AudioNode(context), value.Value.port, to.port));
59+
_ = value.OutgoingConnectors.Add(this);
60+
61+
QueueConnect(newNode, To);
6862
}
6963
}
7064
Changed?.Invoke(this);
7165
}
7266
}
7367

74-
private (Node node, ulong port, AudioParam? param)? to;
75-
public (Node node, ulong port, AudioParam? param)? To
68+
private (Node node, string? audioParamIdentifier)? to;
69+
public (Node node, string? audioParamIdentifier)? To
7670
{
7771
get
7872
{
73+
// If not previous loaded, then load from attributes.
7974
if (to is null)
8075
{
8176
var toNode = (Node?)SVG.Elements.FirstOrDefault(e => e is Node && e.Id == Element.GetAttribute("data-to-node"));
82-
ulong toPort = (ulong)Element.GetAttributeOrZero("data-to-port");
83-
AudioParam? toAudioParam = null;
84-
if (Element.GetAttribute("data-to-audioparam") is { } toAttribute)
85-
{
86-
_ = (toNode?.AudioParams.TryGetValue(toAttribute, out toAudioParam));
87-
}
88-
_ = toNode?.OutgoingConnectors.Add((this, toPort));
89-
to = toNode is null ? null : (toNode, toPort, toAudioParam);
77+
78+
string? audioParamIdentifier = Element.GetAttribute("data-to-audioparam");
79+
80+
_ = toNode?.IngoingConnectors.Add(this);
81+
82+
to = toNode is null ? null : (toNode, audioParamIdentifier);
9083
}
9184
return to;
9285
}
9386
set
9487
{
95-
if (to is { } previousValue)
88+
// We don't support updating the to-node to some new value.
89+
90+
if (value is { } newValue) // If it is actually a new value
9691
{
97-
_ = previousValue.node.IngoingConnectors.Remove((this, previousValue.port));
98-
if (from is { } previouvFromValue)
92+
Element.SetAttribute("data-to-node", newValue.node.Id);
93+
94+
if (newValue.audioParamIdentifier is not null)
95+
{
96+
Element.SetAttribute("data-to-audioparam", newValue.audioParamIdentifier);
97+
}
98+
99+
to = value;
100+
_ = newValue.node.IngoingConnectors.Add(this);
101+
if (From is not null)
99102
{
100-
QueuedTasks.Enqueue(async context => await (await previouvFromValue.node.AudioNode(context)).DisconnectAsync(previouvFromValue.port));
103+
QueueConnect(From, newValue);
101104
}
102105
}
103-
if (value is null)
106+
else // If the new value if null then remove its attribute
104107
{
105108
_ = Element.RemoveAttribute("data-to-node");
106-
_ = Element.RemoveAttribute("data-to-port");
107109
to = null;
108110
}
111+
Changed?.Invoke(this);
112+
}
113+
}
114+
115+
private void QueueConnect(Node fromNode, (Node node, string? audioParamIdentifier)? to)
116+
{
117+
if (to is { node: { } toNode })
118+
{
119+
if (to is { audioParamIdentifier: { } paramIdentifier })
120+
{
121+
QueuedTasks.Enqueue(async context => await (await fromNode.AudioNode(context)).ConnectAsync(await toNode.AudioParams[paramIdentifier](context)));
122+
}
109123
else
110124
{
111-
Element.SetAttribute("data-to-node", value.Value.node.Id);
112-
Element.SetAttribute("data-to-port", value.Value.port.ToString());
113-
to = value;
114-
_ = value.Value.node.IngoingConnectors.Add((this, value.Value.port));
115-
if (From is { } from)
116-
{
117-
if (value.Value.param is not null)
118-
{
119-
QueuedTasks.Enqueue(async context => await (await from.node.AudioNode(context)).ConnectAsync(value.Value.param, from.port));
120-
}
121-
else
122-
{
123-
QueuedTasks.Enqueue(async context => await (await from.node.AudioNode(context)).ConnectAsync(await value.Value.node.AudioNode(context), from.port, value.Value.port));
124-
}
125-
}
125+
QueuedTasks.Enqueue(async context => await (await fromNode.AudioNode(context)).ConnectAsync(await toNode.AudioNode(context)));
126126
}
127-
Changed?.Invoke(this);
128127
}
129128
}
130129

@@ -133,27 +132,24 @@ public override void HandlePointerMove(PointerEventArgs eventArgs)
133132
if (SVG.EditMode is EditMode.Add)
134133
{
135134
(X2, Y2) = SVG.LocalDetransform((eventArgs.OffsetX, eventArgs.OffsetY));
136-
double fromX = From!.Value.node.X + From!.Value.node.Width;
137-
double fromY = From!.Value.node.Y + (From!.Value.port * 20) + 20;
135+
double fromX = From!.X + From!.Width;
136+
double fromY = From!.Y + 1 * 20;
138137
SetStart((fromX, fromY), (X2, Y2));
139138
}
140139
}
141140

142141
public override void HandlePointerUp(PointerEventArgs eventArgs)
143142
{
144143
if (SVG.EditMode is EditMode.Add
145-
&& SVG.SelectedShapes.FirstOrDefault(s => s is Node node && node != From?.node) is Node { } to)
144+
&& SVG.SelectedShapes.FirstOrDefault(s => s is Node node && node != From) is Node { } to)
146145
{
147-
if (to.IngoingConnectors.Any(c => c.connector.To?.node == From?.node || c.connector.From?.node == From?.node))
146+
if (to.IngoingConnectors.Any(c => c.To?.node == From || c.From == From))
148147
{
149148
Complete();
150149
}
151150
else
152151
{
153-
if (to.CurrentActivePort is { } port)
154-
{
155-
To = (to, port, to.CurrentActiveAudioParam);
156-
}
152+
To = (to, to.CurrentActiveAudioParamIdentifier);
157153
SVG.EditMode = EditMode.None;
158154
UpdateLine();
159155
}
@@ -169,7 +165,7 @@ public override void Complete()
169165
}
170166
}
171167

172-
public static void AddNew(SVGEditor.SVGEditor SVG, Node fromNode, ulong fromPort)
168+
public static void AddNew(SVGEditor.SVGEditor SVG, Node fromNode)
173169
{
174170
IElement element = SVG.Document.CreateElement("LINE");
175171
element.SetAttribute("data-elementtype", "connector");
@@ -179,7 +175,7 @@ public static void AddNew(SVGEditor.SVGEditor SVG, Node fromNode, ulong fromPort
179175
Changed = SVG.UpdateInput,
180176
Stroke = "black",
181177
StrokeWidth = "5",
182-
From = (fromNode, fromPort)
178+
From = fromNode
183179
};
184180
SVG.EditMode = EditMode.Add;
185181

@@ -209,10 +205,10 @@ public void UpdateLine()
209205
return;
210206
}
211207

212-
double fromX = From!.Value.node.X + From!.Value.node.Width;
213-
double fromY = From!.Value.node.Y + (From!.Value.port * 20) + 20;
208+
double fromX = From!.X + From!.Width;
209+
double fromY = From!.Y + 20;
214210
double toX = To!.Value.node.X;
215-
double toY = To!.Value.node.Y + (To!.Value.port * 20) + 20;
211+
double toY = To!.Value.node.Y + (To?.node.Offset(To?.audioParamIdentifier) ?? 20);
216212
double differenceX = toX - fromX;
217213
double differenceY = toY - fromY;
218214
double distance = Math.Sqrt((differenceX * differenceX) + (differenceY * differenceY));
@@ -233,12 +229,12 @@ public override void BeforeBeingRemoved()
233229
{
234230
if (From is { } from)
235231
{
236-
_ = from.node.OutgoingConnectors.Remove((this, from.port));
237-
from.node.QueuedTasks.Enqueue(async context => await (await from.node.AudioNode(context)).DisconnectAsync(from.port));
232+
_ = from.OutgoingConnectors.Remove(this);
233+
from.QueuedTasks.Enqueue(async context => await (await from.AudioNode(context)).DisconnectAsync());
238234
}
239235
if (To is { } to)
240236
{
241-
_ = to.node.IngoingConnectors.Remove((this, to.port));
237+
_ = to.node.IngoingConnectors.Remove(this);
242238
}
243239
}
244240
}

samples/KristofferStrube.Blazor.WebAudio.WasmExample/AudioEditor/MenuItems/AddNewConnectorMenuItem.razor

+1-1
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
@using BlazorContextMenu
22

3-
@if (Data is Port port) {<Item OnClick="_ => Connector.AddNew(SVGEditor, port.Node, port.PortNumber)"><div class="icon">🧵</div> New Connector</Item>}
3+
@if (Data is Port port) {<Item OnClick="_ => Connector.AddNew(SVGEditor, port.Node)"><div class="icon">🧵</div> New Connector</Item>}
44

55
@code {
66
[CascadingParameter]

samples/KristofferStrube.Blazor.WebAudio.WasmExample/AudioEditor/NodeEditors/AnalyserEditor.razor

+3-3
Original file line numberDiff line numberDiff line change
@@ -34,12 +34,12 @@
3434
<br />
3535
<Plot Data="measurements" Height="50" />
3636
</foreignObject>
37-
<ContextMenuTrigger MenuId="SVGMenu" WrapperTag="g" Data=@(new Port(SVGElement, 0, true) { Node = SVGElement, PortNumber = 0 }) MouseButtonTrigger="SVGElement.ShouldTriggerContextMenu ? MouseButtonTrigger.Right : (MouseButtonTrigger)4">
38-
<circle @onpointerdown="() => SelectPort(0)" cx=@((SVGElement.X).AsString()) cy="@((SVGElement.Y+20).AsString())" r="10" fill="grey">
37+
<ContextMenuTrigger MenuId="SVGMenu" WrapperTag="g" Data=@(new Port(SVGElement, true)) MouseButtonTrigger="SVGElement.ShouldTriggerContextMenu ? MouseButtonTrigger.Right : (MouseButtonTrigger)4">
38+
<circle @onpointerdown="() => SelectPort()" cx=@((SVGElement.X).AsString()) cy="@((SVGElement.Y+20).AsString())" r="10" fill="grey">
3939

4040
</circle>
4141
</ContextMenuTrigger>
42-
<ContextMenuTrigger MenuId="SVGMenu" WrapperTag="g" Data=@(new Port(SVGElement, 0, false)) MouseButtonTrigger="SVGElement.ShouldTriggerContextMenu ? MouseButtonTrigger.Right : (MouseButtonTrigger)4">
42+
<ContextMenuTrigger MenuId="SVGMenu" WrapperTag="g" Data=@(new Port(SVGElement, false)) MouseButtonTrigger="SVGElement.ShouldTriggerContextMenu ? MouseButtonTrigger.Right : (MouseButtonTrigger)4">
4343
<circle cx=@((SVGElement.X+SVGElement.Width).AsString()) cy="@((SVGElement.Y+20).AsString())" r="10" fill="grey">
4444

4545
</circle>

samples/KristofferStrube.Blazor.WebAudio.WasmExample/AudioEditor/NodeEditors/AudioDestinationEditor.razor

+2-2
Original file line numberDiff line numberDiff line change
@@ -23,8 +23,8 @@
2323
fill="@SVGElement.Fill"
2424
style="filter:brightness(@(SVGElement.Selected ? "0.9" : "1"))">
2525
</rect>
26-
<ContextMenuTrigger MenuId="SVGMenu" WrapperTag="g" Data=@(new Port(SVGElement, 0, true) { Node = SVGElement, PortNumber = 0 }) MouseButtonTrigger="SVGElement.ShouldTriggerContextMenu ? MouseButtonTrigger.Right : (MouseButtonTrigger)4">
27-
<circle @onpointerdown="() => SelectPort(0)" cx=@((SVGElement.X).AsString()) cy="@((SVGElement.Y+20).AsString())" r="10" fill="grey">
26+
<ContextMenuTrigger MenuId="SVGMenu" WrapperTag="g" Data=@(new Port(SVGElement, true)) MouseButtonTrigger="SVGElement.ShouldTriggerContextMenu ? MouseButtonTrigger.Right : (MouseButtonTrigger)4">
27+
<circle @onpointerdown="() => SelectPort()" cx=@((SVGElement.X).AsString()) cy="@((SVGElement.Y+20).AsString())" r="10" fill="grey">
2828

2929
</circle>
3030
</ContextMenuTrigger>

samples/KristofferStrube.Blazor.WebAudio.WasmExample/AudioEditor/NodeEditors/BiquadFilterEditor.razor

+3-3
Original file line numberDiff line numberDiff line change
@@ -63,12 +63,12 @@
6363
<AudioParamSlider AudioParam="GainAudioParam" Label="Gain" Min="0" Max="200" StepSize="1" UpdateCallback="f => SVGElement.Gain = f" />
6464
}
6565
</foreignObject>
66-
<ContextMenuTrigger MenuId="SVGMenu" WrapperTag="g" Data=@(new Port(SVGElement, 0, true) { Node = SVGElement, PortNumber = 0 }) MouseButtonTrigger="SVGElement.ShouldTriggerContextMenu ? MouseButtonTrigger.Right : (MouseButtonTrigger)4">
67-
<circle @onpointerdown="() => SelectPort(0)" cx=@((SVGElement.X).AsString()) cy="@((SVGElement.Y+20).AsString())" r="10" fill="grey">
66+
<ContextMenuTrigger MenuId="SVGMenu" WrapperTag="g" Data=@(new Port(SVGElement, true)) MouseButtonTrigger="SVGElement.ShouldTriggerContextMenu ? MouseButtonTrigger.Right : (MouseButtonTrigger)4">
67+
<circle @onpointerdown="() => SelectPort()" cx=@((SVGElement.X).AsString()) cy="@((SVGElement.Y+20).AsString())" r="10" fill="grey">
6868

6969
</circle>
7070
</ContextMenuTrigger>
71-
<ContextMenuTrigger MenuId="SVGMenu" WrapperTag="g" Data=@(new Port(SVGElement, 0, false)) MouseButtonTrigger="SVGElement.ShouldTriggerContextMenu ? MouseButtonTrigger.Right : (MouseButtonTrigger)4">
71+
<ContextMenuTrigger MenuId="SVGMenu" WrapperTag="g" Data=@(new Port(SVGElement, false)) MouseButtonTrigger="SVGElement.ShouldTriggerContextMenu ? MouseButtonTrigger.Right : (MouseButtonTrigger)4">
7272
<circle cx=@((SVGElement.X+SVGElement.Width).AsString()) cy="@((SVGElement.Y+20).AsString())" r="10" fill="grey">
7373

7474
</circle>

samples/KristofferStrube.Blazor.WebAudio.WasmExample/AudioEditor/NodeEditors/GainEditor.razor

+7-8
Original file line numberDiff line numberDiff line change
@@ -31,23 +31,23 @@
3131
<AudioParamSlider AudioParam="GainAudioParam" Label="Volume" Min="0" Max="5" StepSize="0.01f" UpdateCallback="f => SVGElement.GainValue = f" />
3232
}
3333
</foreignObject>
34-
<ContextMenuTrigger MenuId="SVGMenu" WrapperTag="g" Data=@(new Port(SVGElement, 0, true) { Node = SVGElement, PortNumber = 0 }) MouseButtonTrigger="SVGElement.ShouldTriggerContextMenu ? MouseButtonTrigger.Right : (MouseButtonTrigger)4">
35-
<circle @onpointerdown="() => SelectPort(0)" cx=@((SVGElement.X).AsString()) cy="@((SVGElement.Y+20).AsString())" r="10" fill="grey">
34+
<ContextMenuTrigger MenuId="SVGMenu" WrapperTag="g" Data=@(new Port(SVGElement, true)) MouseButtonTrigger="SVGElement.ShouldTriggerContextMenu ? MouseButtonTrigger.Right : (MouseButtonTrigger)4">
35+
<circle @onpointerdown="() => SelectPort()" cx=@((SVGElement.X).AsString()) cy="@((SVGElement.Y + 20).AsString())" r="10" fill="grey">
3636

3737
</circle>
3838
</ContextMenuTrigger>
3939

4040
@if (GainAudioParam is not null)
4141
{
42-
<ContextMenuTrigger MenuId="SVGMenu" WrapperTag="g" Data=@(new Port(SVGElement, 0, true) { Node = SVGElement, PortNumber = 0 }) MouseButtonTrigger="SVGElement.ShouldTriggerContextMenu ? MouseButtonTrigger.Right : (MouseButtonTrigger)4">
43-
<circle @onpointerdown="() => SelectAudioParam(2, GainAudioParam)" cx=@((SVGElement.X).AsString()) cy="@((SVGElement.Y+60).AsString())" r="10" fill="#1976D3">
42+
<ContextMenuTrigger MenuId="SVGMenu" WrapperTag="g" Data=@(new Port(SVGElement, true)) MouseButtonTrigger="SVGElement.ShouldTriggerContextMenu ? MouseButtonTrigger.Right : (MouseButtonTrigger)4">
43+
<circle @onpointerdown=@(() => SelectAudioParam("gain")) cx=@((SVGElement.X).AsString()) cy="@((SVGElement.Y+SVGElement.Offset("gain")).AsString())" r="10" fill="#1976D3">
4444

4545
</circle>
4646
</ContextMenuTrigger>
4747
}
4848

49-
<ContextMenuTrigger MenuId="SVGMenu" WrapperTag="g" Data=@(new Port(SVGElement, 0, false)) MouseButtonTrigger="SVGElement.ShouldTriggerContextMenu ? MouseButtonTrigger.Right : (MouseButtonTrigger)4">
50-
<circle cx=@((SVGElement.X+SVGElement.Width).AsString()) cy="@((SVGElement.Y+20).AsString())" r="10" fill="grey">
49+
<ContextMenuTrigger MenuId="SVGMenu" WrapperTag="g" Data=@(new Port(SVGElement, false)) MouseButtonTrigger="SVGElement.ShouldTriggerContextMenu ? MouseButtonTrigger.Right : (MouseButtonTrigger)4">
50+
<circle cx=@((SVGElement.X+SVGElement.Width).AsString()) cy="@((SVGElement.Y + 20).AsString())" r="10" fill="grey">
5151

5252
</circle>
5353
</ContextMenuTrigger>
@@ -66,8 +66,7 @@
6666

6767
if (GainAudioParam is null && AudioContext is not null)
6868
{
69-
GainAudioParam = await ((GainNode)(await SVGElement.AudioNode(AudioContext))).GetGainAsync();
70-
SVGElement.AudioParams["gain"] = GainAudioParam;
69+
GainAudioParam = await SVGElement.AudioParams["gain"](AudioContext);
7170
StateHasChanged();
7271
}
7372
}

samples/KristofferStrube.Blazor.WebAudio.WasmExample/AudioEditor/NodeEditors/MediaStreamAudioSourceEditor.razor

+1-1
Original file line numberDiff line numberDiff line change
@@ -35,7 +35,7 @@
3535
</select>
3636
}
3737
</foreignObject>
38-
<ContextMenuTrigger MenuId="SVGMenu" WrapperTag="g" Data=@(new Port(SVGElement, 0, false)) MouseButtonTrigger="SVGElement.ShouldTriggerContextMenu ? MouseButtonTrigger.Right : (MouseButtonTrigger)4">
38+
<ContextMenuTrigger MenuId="SVGMenu" WrapperTag="g" Data=@(new Port(SVGElement, false)) MouseButtonTrigger="SVGElement.ShouldTriggerContextMenu ? MouseButtonTrigger.Right : (MouseButtonTrigger)4">
3939
<circle cx=@((SVGElement.X+SVGElement.Width).AsString()) cy="@((SVGElement.Y+20).AsString())" r="10" fill="grey">
4040

4141
</circle>

samples/KristofferStrube.Blazor.WebAudio.WasmExample/AudioEditor/NodeEditors/NodeEditor.razor

+3-5
Original file line numberDiff line numberDiff line change
@@ -9,21 +9,19 @@
99
@code {
1010
public bool ChildContentIsNoninteractive => SVGElement.SVG.EditMode is EditMode.Move && SVGElement.Selected || SVGElement.SVG.EditMode is EditMode.Add or EditMode.Move;
1111

12-
public void SelectPort(ulong port)
12+
public void SelectPort()
1313
{
1414
if (SVGElement.SVG.EditMode is EditMode.Add && SVGElement.SVG.SelectedShapes.Any(s => s is Connector))
1515
{
16-
SVGElement.CurrentActivePort = port;
1716
SVGElement.SVG.SelectedShapes.Add(SVGElement);
1817
}
1918
}
2019

21-
public void SelectAudioParam(ulong port, AudioParam param)
20+
public void SelectAudioParam(string paramName)
2221
{
2322
if (SVGElement.SVG.EditMode is EditMode.Add && SVGElement.SVG.SelectedShapes.Any(s => s is Connector))
2423
{
25-
SVGElement.CurrentActivePort = port;
26-
SVGElement.CurrentActiveAudioParam = param;
24+
SVGElement.CurrentActiveAudioParamIdentifier = paramName;
2725
SVGElement.SVG.SelectedShapes.Add(SVGElement);
2826
}
2927
}

0 commit comments

Comments
 (0)