// FIXME: Switch to union find for this... static void FloodFill(List <Wire> wires, List <Wire> toCheck, Wire currentWire) { if (wires.Contains(currentWire)) { return; } wires.Add(currentWire); for (int i = toCheck.Count - 1; i >= 0; i--) { Wire wire = toCheck[i]; if (wire.IsPointOnWire(currentWire.Pos)) { FloodFill(wires, toCheck, wire); } else if (wire.IsPointOnWire(currentWire.EndPos)) { FloodFill(wires, toCheck, wire); } // Do don't need to check anymore if (wires.Count == toCheck.Count) { return; } } }
public WireTransaction?CreateAddWireTransaction(Wire wire) { var wireEnd = wire.GetEndPosition(); List <Wire> Deleted = new List <Wire>(); List <Wire> Added = new List <Wire>(); // This dictionary maps wires to the point where they intersect this wire // If there exists a mapping but the value is null, that means both ends // of the wire are contained inside of the addeed wire. Dictionary <Wire, Vector2i?> SplitPoints = new Dictionary <Wire, Vector2i?>(); // First we get all of the points where we need to split the current wire foreach (var bWire in WiresList) { bool start = wire.IsPointOnWire(bWire.Pos); bool end = wire.IsPointOnWire(bWire.EndPos); if (start && end) { // Null indicates that both ends are contained in this wire SplitPoints[bWire] = null; } else if (start) { SplitPoints[bWire] = bWire.Pos; } else if (end) { SplitPoints[bWire] = bWire.EndPos; } } // FIXME: Add other split points like component connection points! // After we've found all of the intersection points we can check // if the ends of the added wire will split some other wire. // At the same time we check to see if there is a wire that // contains both ends of the wire we added. // In this case we shouldn't create or delete any wires because // then there will be two wires on the same space. Wire?containingWire = null; foreach (var bWire in WiresList) { // We check to see if the point is *inside* // the wire because if the new wire touches // the other wire at it's end point we don't // need to do anything to it. It's only if the // new point is on the inside of the wire that // we need to do anything. bool containedStart = false; if (bWire.IsPointInsideWire(wire.Pos)) { // We only care if the wires are going in different direction // as that means we should split the wire. // The case where they are going the same direction // will be handled in the next step. if (wire.Direction != bWire.Direction) { // The diff will be in non-zero in only one direction so this will work fine. var diff = wire.Pos - bWire.Pos; Deleted.Add(bWire); Added.Add(new Wire(bWire.Pos, diff.ManhattanDistance, bWire.Direction)); Added.Add(new Wire(bWire.Pos + diff, bWire.Length - diff.ManhattanDistance, bWire.Direction)); } containedStart = true; } if (bWire.IsPointInsideWire(wire.EndPos)) { if (wire.Direction != bWire.Direction) { // The diff will be in non-zero in only one direction so this will work fine. var diff = wire.EndPos - bWire.Pos; Deleted.Add(bWire); Added.Add(new Wire(bWire.Pos, diff.ManhattanDistance, bWire.Direction)); Added.Add(new Wire(bWire.Pos + diff, bWire.Length - diff.ManhattanDistance, bWire.Direction)); } // This means that both the start and the end point of this wire was contained // inside of bWire. So we basically shouldn't create any transaction.s if (containedStart) { containingWire = bWire; // We don't care about any more wires so it's fine to break. break; } } } // If the wire we are adding is entrierly contained within another wire // the addition won't change any state so we don't create a transaction for that. if (containingWire.HasValue) { // This addition wouldn't change any state. return(null); } List <Vector2i> Points = new List <Vector2i>(); // Now we should merge or remove wires that are going in the same direction as us. Wire mergedWire = wire; foreach (var(splitWire, location) in SplitPoints) { if (location == null) { // Both ends are contained. // So to merge we can just delete the contained wire. Console.WriteLine($"Wire ({splitWire}) was completely contained in ({wire}), so it was removed."); Deleted.Add(splitWire); continue; } else if (wire.Direction == splitWire.Direction) { // This means that we should merge the two wires. // We can do that by deleting the existing wire and // extending the wire we are adding to include that wire. var minPos = Vector2i.ComponentWiseMin(mergedWire.Pos, splitWire.Pos); var maxPos = Vector2i.ComponentWiseMax(mergedWire.EndPos, splitWire.EndPos); var diff = maxPos - minPos; Wire newMerged; newMerged.Direction = wire.Direction; newMerged.Pos = minPos; newMerged.Length = diff.ManhattanDistance; Console.WriteLine($"Merged ({splitWire}) with ({mergedWire}). Result: ({newMerged})"); mergedWire = newMerged; Deleted.Add(splitWire); } else { // This will not fail due to the null check above. Console.WriteLine($"The wire ({splitWire}) should split the current wire at ({location})."); if (Points.Contains(location.Value) == false) { Points.Add(location.Value); } } } // Lastly we split the merged wire on all of the remaining points. // We do this by first sorting the points in the wires direction // and then go through linearly and add the parts of the wire. // We do the comparison outside of the lambda to avoid a capture. // (this is a premature optimization) Points.Sort( wire.Direction == Direction.Vertical ? (Comparison <Vector2i>)((v1, v2) => v1.Y - v2.Y) : ((v1, v2) => v1.X - v2.X)); Vector2i pos = mergedWire.Pos; foreach (var split in Points) { Wire newWire; newWire.Direction = mergedWire.Direction; newWire.Pos = pos; newWire.Length = (split - pos).ManhattanDistance; if (newWire.Length < 0) { Debugger.Break(); } pos = newWire.EndPos; Added.Add(newWire); Console.WriteLine($"Split wire at {split}. Result: ({newWire})"); } // Here we need to add the last part of the wire Wire w = new Wire(pos, (mergedWire.EndPos - pos).ManhattanDistance, mergedWire.Direction); Added.Add(w); Console.WriteLine($"End part of split: {w}"); // FIXME: We want to deduplicate the values in Added and Deleted // If both lists contain exactly the same wires we want to return // null instead as the transaction doesn't change anything. // Remove all wires with zero length Added.RemoveAll(w => { if (w.Length == 0) { Console.WriteLine($"Warn: Trying to add a wire with zero length! {w}"); return(true); } else { return(false); } }); // Now we just return the transaction. // To apply this transaction ApplyTransaction(...) must be called. return(new WireTransaction(wire, Deleted, Added)); }