Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Optimize Schematic.Deserialize, a lot #189

Merged
merged 11 commits into from
May 7, 2023
72 changes: 51 additions & 21 deletions Circuit/Schematic/Schematic.cs
Original file line number Diff line number Diff line change
Expand Up @@ -42,10 +42,35 @@ public class Schematic
/// </summary>
public ILog Log { get { return log; } set { log = value; } }

public Schematic(ILog Log) : this() { log = Log; }
public Schematic()
public Schematic(ILog Log, IEnumerable<Element> Elements = null) : this(Elements)
{
log = Log;
}
public Schematic(IEnumerable<Element> Elements = null)
{
elements = new ElementCollection();
if (Elements != null)
{
// If we rely on the normal events for adding/removing these components,
// it's very slow because the nodes get rebuilt every time a new
// component is added. To avoid this, just manually connect everything
// here, and then rebuild the nodes once.
elements.AddRange(Elements);
foreach (Symbol i in elements.OfType<Symbol>())
circuit.Components.Add(i.Component);
// Rebuild the nodes here so we at least reduce to one node per connected
// group of wires.
RebuildNodes(null, false);
foreach (Element i in elements)
foreach (Terminal j in i.Terminals)
j.ConnectTo(NodeAt(i.MapTerminal(j)));
// Rebuild the nodes again in case a named wire changed the name of the nodes.
RebuildNodes(null, true);
}

// We skipped this usual event setup for the initial elements, add the events now.
foreach (Element i in elements)
i.LayoutChanged += OnLayoutChanged;
elements.ItemAdded += OnElementAdded;
elements.ItemRemoved += OnElementRemoved;
}
Expand Down Expand Up @@ -197,22 +222,26 @@ protected void OnLayoutChanged(object sender, EventArgs e)
UpdateTerminals(of);
}

// Wires was a single node but it is now two. Break it into two sets of nodes
// and return the one containing Target.
private IEnumerable<Wire> ConnectedTo(IEnumerable<Wire> Wires, Wire Target)
private IEnumerable<Wire> ConnectedTo(IEnumerable<Wire> Wires, Wire Target, HashSet<Wire> visited)
{
// Repeatedly search for connections with the target.
IEnumerable<Wire> connected = new Wire[] { Target };
int count = connected.Count();
while (true)
foreach (Wire i in Wires.Where(j => !visited.Contains(j)))
{
connected = Wires.Where(i => connected.Any(j => i.IsConnectedTo(j))).ToArray();
if (connected.Count() == count)
break;
count = connected.Count();
if (i.IsConnectedTo(Target))
{
visited.Add(i);
yield return i;
foreach (Wire j in ConnectedTo(Wires, i, visited))
yield return j;
}
}
}

return connected;
// Wires was a single node but it is now two. Break it into two sets of nodes
// and return the one containing Target.
private IEnumerable<Wire> ConnectedTo(IEnumerable<Wire> Wires, Wire Target)
{
HashSet<Wire> visited = new HashSet<Wire>();
return ConnectedTo(Wires.Buffer(), Target, visited);
}

// Merge all of the nodes contained in wires to one.
Expand Down Expand Up @@ -244,7 +273,7 @@ private Node MergeNode(IEnumerable<Wire> Wires)
circuit.Nodes.Add(n);
}

foreach (Wire i in Wires)
foreach (Wire i in Wires.Where(j => j.Node != n))
{
i.Node = n;
UpdateTerminals(i);
Expand Down Expand Up @@ -289,16 +318,17 @@ private void UpdateTerminals(Element Of)
{
foreach (Terminal i in Of.Terminals)
Connect(i, NodeAt(Of.MapTerminal(i), Of as Wire));

// If Of is a named wire, the nodes might have changed.
if (Of is Symbol symbol && symbol.Component is NamedWire wire)
RebuildNodes(wire.ConnectedTo, false);
}

private void Connect(Terminal T, Node V)
{
if (V != T.ConnectedTo)
{
T.ConnectTo(V);
// If this terminal is a named wire, the nodes need to be rebuilt.
if (T.Owner is NamedWire)
RebuildNodes(V, false);
}
}

private void LogComponents()
Expand Down Expand Up @@ -341,8 +371,8 @@ public XElement Serialize()

public static Schematic Deserialize(XElement X, ILog Log)
{
Schematic s = new Schematic(Log);
s.Elements.AddRange(X.Elements("Element").Select(i => Element.Deserialize(i)));
IEnumerable<Element> elements = X.Elements("Element").Select(i => Element.Deserialize(i));
Schematic s = new Schematic(Log, elements);
s.Circuit.Name = Value(X.Attribute("Name"));
s.Circuit.Description = Value(X.Attribute("Description"));
s.Circuit.PartNumber = Value(X.Attribute("PartNumber"));
Expand Down