/// <summary> /// Gets the lineage of this group pin, starting with the group pin, and ending with /// the top-level group pin</summary> private IEnumerable <GroupPin> GetLineage(bool inputSide) { GroupPin grpPin = this; DomNode domNode = DomNode; while (grpPin != null) { yield return(grpPin); GroupPin nextGrpPin = null; var subGraph = domNode.Parent.Cast <Group>(); if (subGraph.ParentGraph.Is <Group>()) { var parentSubGraph = subGraph.ParentGraph.Cast <Group>(); if (parentSubGraph != null) { var parentPins = inputSide ? parentSubGraph.InputGroupPins : parentSubGraph.OutputGroupPins; foreach (var parentPin in parentPins) { if (parentPin.InternalElement == subGraph && parentPin.InternalPinIndex == grpPin.Index) { nextGrpPin = parentPin; domNode = nextGrpPin.DomNode; break; } } } } grpPin = nextGrpPin; } }
/// <summary> /// Performs custom actions after an attribute in the DOM node subtree changed</summary> /// <param name="sender">Sender (root DOM node)</param> /// <param name="e">Attribute change event args</param> protected override void OnAttributeChanged(object sender, AttributeEventArgs e) { if (Validating && !m_undoingOrRedoing) { if (e.DomNode.Parent.Is <Group>() && e.AttributeInfo == ElementLabelAttribute) { var subGraph = e.DomNode.Parent.Cast <Group>(); if (e.DomNode.Is <Element>()) { // the name of a sub-node has changed, needs to update group pin names that are not manually set SyncGroupPinNamesFromModuleName(subGraph, e.DomNode); } } else if (e.DomNode.Is <GroupPin>() && e.AttributeInfo == PinNameAttributeAttribute) { if (e.DomNode.Parent != null) { var subGraph = e.DomNode.Parent.Cast <Group>(); // ensure group pin names are unique at local level UniqueNamer uniqueName = new UniqueNamer(); GroupPin childGrpPin = e.DomNode.Cast <GroupPin>(); foreach (var grpPin in subGraph.InputGroupPins) { if (grpPin != childGrpPin) { uniqueName.Name((string)grpPin.Name); } } foreach (var grpPin in subGraph.OutputGroupPins) { if (grpPin != childGrpPin) { uniqueName.Name((string)grpPin.Name); } } string unique = uniqueName.Name((string)childGrpPin.Name); if (unique != childGrpPin.Name) { childGrpPin.Name.SetString(unique); } // Reset IsDefaultName. Ignore the suffix because there are typically multiple // circuit elements in a group and the child group pin doesn't know about // our unique namer. var defaultName = childGrpPin.DefaultName(childGrpPin.IsInputSide); string ourRoot; int ourSuffix; uniqueName.Parse(unique, out ourRoot, out ourSuffix); childGrpPin.IsDefaultName = defaultName == ourRoot; UpdateParentGroupPinName(childGrpPin.IsInputSide, childGrpPin); } } } base.OnAttributeChanged(sender, e); }
/// <summary> /// Performs custom actions after an attribute in the DOM node subtree changed</summary> /// <param name="sender">Sender (root DOM node)</param> /// <param name="e">Attribute change event args</param> protected override void OnAttributeChanged(object sender, AttributeEventArgs e) { if (Validating && !m_undoingOrRedoing) { if (e.DomNode.Parent.Is <Group>() && e.AttributeInfo == ElementLabelAttribute) { var subGraph = e.DomNode.Parent.Cast <Group>(); if (e.DomNode.Is <Element>()) { // the name of a sub-node has changed, needs to update group pin names that are not manually set SyncGroupPinNamesFromModuleName(subGraph, e.DomNode); } } else if (e.DomNode.Is <GroupPin>() && e.AttributeInfo == PinNameAttributeAttribute) { if (e.DomNode.Parent != null) { var subGraph = e.DomNode.Parent.Cast <Group>(); // ensure group pin names are unique at local level UniqueNamer uniqueName = new UniqueNamer(); GroupPin childGrpPin = e.DomNode.Cast <GroupPin>(); foreach (var pin in subGraph.Inputs) { var grpPin = pin.Cast <GroupPin>(); if (grpPin != childGrpPin) { uniqueName.Name(grpPin.Name); } } foreach (var pin in subGraph.Outputs) { var grpPin = pin.Cast <GroupPin>(); if (grpPin != childGrpPin) { uniqueName.Name(grpPin.Name); } } string unique = uniqueName.Name(childGrpPin.Name); if (unique != childGrpPin.Name) { childGrpPin.Name = unique; } // try to reset IsDefaultName childGrpPin.IsDefaultName = childGrpPin.Name == childGrpPin.DefaultName(childGrpPin.IsInputSide); UpdateParentGroupPinName(childGrpPin.IsInputSide, childGrpPin); } } } base.OnAttributeChanged(sender, e); }
// whether freePin's desired location overlaps static pin's actual location private static bool PinOverlap(GroupPin freePin, GroupPin againstPin) { // consider a pin's y interval is (pinY - PinNodeMargin, pinY + PinNodeHeight +PinNodeMargin) top down // freePin overlaps againstPin if the two intervals overlap var freePinInterval = new Pair <int, int>(freePin.DesiredLocation.Y - CircuitGroupPinInfo.FloatingPinNodeMargin, freePin.DesiredLocation.Y + CircuitGroupPinInfo.FloatingPinNodeHeight + CircuitGroupPinInfo.FloatingPinNodeMargin); var pinInterval = new Pair <int, int>(againstPin.Bounds.Location.Y - CircuitGroupPinInfo.FloatingPinNodeMargin, againstPin.Bounds.Location.Y + CircuitGroupPinInfo.FloatingPinNodeHeight + CircuitGroupPinInfo.FloatingPinNodeMargin); // two intervals i1 = (s1, e1) and i2 = (s2, e2) overlap if and only if s2 < e1 and s1 < e2 return((pinInterval.First < freePinInterval.Second) && // s2 < e1 (freePinInterval.First < pinInterval.Second)); //s1 < e2 }
/// <summary> /// Propagate up group pin name changes to the parent pin (only need to take care of non-default name, /// the default names are auto-updated in Group.UpdateGroupPins())</summary> private void UpdateParentGroupPinName(bool inputSide, GroupPin childPin) { var parentGrpPin = childPin.GetAncestry(inputSide).FirstOrDefault(); if (parentGrpPin != null) { if (!childPin.IsDefaultName && parentGrpPin.IsDefaultName) { parentGrpPin.Name = childPin.Name; parentGrpPin.IsDefaultName = false; } } }
/// <summary> /// Resolve pin node position to avoid colliding with any node in againstPinNodes</summary> /// <param name="againstPinNodes">List of GroupPins to avoid collision with</param> /// <param name="floatingPin">Floating pin to adjust: use its DesiredLocation to check collisions and its ActualLocation will be set upon return</param> /// <param name="minY">Minimum Y value the floating pin is allowed to take</param> private void PositioningFloatigPin(IList <GroupPin> againstPinNodes, GroupPin floatingPin, int minY) { // resolve pin node position to avoid colliding with any against-pins bool overlapped = false; for (int p = 0; p < againstPinNodes.Count; ++p) // try to locate sufficient empty space between the static nodes { var againstpin = againstPinNodes[p]; if (againstpin.Bounds.Location.Y + CircuitGroupPinInfo.FloatingPinNodeHeight + CircuitGroupPinInfo.FloatingPinNodeMargin < minY) { continue; } if (PinOverlap(floatingPin, againstpin)) { overlapped = true; // find next suitable place from p down (note againstPinNodes y-ascending order, i.e. higher notes appear first) for (int j = p; j < againstPinNodes.Count - 1; ++j) { if (againstPinNodes[j + 1].Bounds.Location.Y - againstPinNodes[j].Bounds.Location.Y >= 2 * (CircuitGroupPinInfo.FloatingPinNodeHeight + CircuitGroupPinInfo.FloatingPinNodeMargin)) { // find empty space >= pin node height + margin // place the floating pin just above static pin j int pinNewY = Constrain(againstPinNodes[j].Bounds.Location.Y + CircuitGroupPinInfo.FloatingPinNodeHeight + CircuitGroupPinInfo.FloatingPinNodeMargin); floatingPin.Bounds = new Rectangle(floatingPin.Bounds.Location.X, pinNewY, floatingPin.Bounds.Width, floatingPin.Bounds.Height); return; } } } } if (overlapped) // no in-between empty space to insert, try at the top region first, then the bottom region { // top region var topStaticPin = againstPinNodes[0]; int pinNewY = Constrain(topStaticPin.Bounds.Location.Y - (CircuitGroupPinInfo.FloatingPinNodeHeight + CircuitGroupPinInfo.FloatingPinNodeMargin)); pinNewY = Math.Max(pinNewY, minY); floatingPin.DesiredLocation = new Point(floatingPin.DesiredLocation.X, pinNewY); if (pinNewY > minY && !PinOverlap(floatingPin, topStaticPin)) { floatingPin.Bounds = new Rectangle(floatingPin.Bounds.Location.X, pinNewY, floatingPin.Bounds.Width, floatingPin.Bounds.Height); } else // bottom region { var bottomStaticPin = againstPinNodes[againstPinNodes.Count - 1]; pinNewY = Constrain(bottomStaticPin.Bounds.Location.Y + (CircuitGroupPinInfo.FloatingPinNodeHeight + CircuitGroupPinInfo.FloatingPinNodeMargin)); pinNewY = Math.Max(pinNewY, minY); floatingPin.DesiredLocation = new Point(floatingPin.DesiredLocation.X, pinNewY); const int increment = 1; while (PinOverlap(floatingPin, bottomStaticPin)) { pinNewY += increment; floatingPin.DesiredLocation = new Point(floatingPin.DesiredLocation.X, pinNewY); } floatingPin.Bounds = new Rectangle(floatingPin.Bounds.Location.X, pinNewY, floatingPin.Bounds.Width, floatingPin.Bounds.Height); } } else // the disired location turns out just fine { floatingPin.Bounds = new Rectangle(floatingPin.Bounds.Location.X, Constrain(floatingPin.DesiredLocation.Y), floatingPin.Bounds.Width, floatingPin.Bounds.Height); } }