internal void Insert(OverlapRemovalNode node) { Debug.Assert(null == this.nodeTree.Find(node), "node already exists in the rbtree"); // RBTree's internal operations on insert/remove etc. mean the node can't cache the // RBNode returned by insert(); instead we must do find() on each call. this.nodeTree.Insert(node); }
internal void SetOlapNode(bool horizontal, OverlapRemovalNode olapNode) { if (horizontal) { mOlapNodeX = olapNode; } else { mOlapNodeY = olapNode; } }
internal void SetOlapNode(bool horizontal, OverlapRemovalNode olapNode) { if (horizontal) mOlapNodeX = olapNode; else mOlapNodeY = olapNode; }
internal OlapTestNode(OverlapRemovalNode node) { this.Node = node; }
} // end GetRightNeighbours bool AddNeighbour(OverlapRemovalParameters parameters, OverlapRemovalNode currentNode, OverlapRemovalNode nextNode, List<OverlapRemovalNode> neighbors, bool isLeftNeighbor, bool isHorizontal) { // Sanity check to be sure that the borders are past all other nodes. Debug.Assert(currentNode != (isLeftNeighbor ? this.LeftBorderNode : this.RightBorderNode), "currentNode must != BorderNode"); double overlap = Overlap(currentNode, nextNode, NodePadding); if (overlap <= 0) { // This is the first node encountered on this neighbour-traversal that did not // overlap within the required padding. Add it to the list and we're done with // this traversal, unless this is a vertical pass and it is not an overlap on // the horizontal axis; in that case, pretend we never saw it and return true // so the next non-overlapping node will be found. (See below for more information // on why this is necessary). if (!isHorizontal && (OverlapP(currentNode, nextNode, NodePaddingP) <= parameters.SolverParameters.GapTolerance)) { #if VERBOSE Console.WriteLine(" V {0}nbourHTolSkipNO: {1}", isLeftNeighbor ? "L" : "R", nextNode); #endif // VERBOSE return true; } neighbors.Add(nextNode); #if VERBOSE Console.WriteLine("{0}nbourAddNO: {1}", isLeftNeighbor ? "L" : "R", nextNode); #endif // VERBOSE return false; } if (isHorizontal) { if (parameters.AllowDeferToVertical) { // We are doing horizontal constraints so examine the vertical overlap and see which // is the smallest (normalized by total node size in that orientation) such that the // least amount of movement required. this.padding is currently the same in both // directions; if this changes, we'll have to add different padding values here for // each direction. @@DCR: consider adding weights to the defer-to-vertical calculation; // this would allow two nodes to pop up/down if they're being squeezed, rather than // force apart the borders (which happens regardless of their weight). double overlapP = OverlapP(currentNode, nextNode, NodePaddingP); bool isOverlapping = parameters.ConsiderProportionalOverlap ? overlap / (currentNode.Size + nextNode.Size) > overlapP / (currentNode.SizeP + nextNode.SizeP) : overlap > overlapP; if (isOverlapping) { // Don't skip if either of these is a border node. if ((currentNode != this.LeftBorderNode) && (currentNode != this.RightBorderNode) && (nextNode != this.LeftBorderNode) && (nextNode != this.RightBorderNode)) { // Moving in the horizontal direction requires more movement than in the vertical // direction to remove the overlap, so skip this node on horizontal constraint // generation and we'll pick it up on vertical constraint generation. Return true // to keep looking for more overlapping nodes. // Note: it is still possible that we'll pick up a constraint in both directions, // due to either or both of this.padding and the "create a constraint to the first // non-overlapping node" logic. This is expected and the latter helps retain stability. #if VERBOSE Console.WriteLine("{0}nbourDeferToV: {1}", isLeftNeighbor ? "L" : "R", nextNode); #endif // VERBOSE // We need to track whether we skipped these so that we don't have a broken transition chain. // See Test_OverlapRemoval.cs, Test_DeferToV_Causing_Missing_Cst() for more information. if (isLeftNeighbor) { currentNode.DeferredLeftNeighborToV = true; nextNode.DeferredRightNeighborToV = true; } else { currentNode.DeferredRightNeighborToV = true; nextNode.DeferredLeftNeighborToV = true; } return true; } } // endif Overlap is greater than OverlapP } // endif AllowDeferToVertical } else { // We're on the vertical pass so make sure we match up with the Solver's tolerance in the // scanline direction, because it is possible that there was a horizontal constraint between // these nodes that was within the Solver's tolerance and thus was not enforced. In that // case, we could spuriously add a vertical constraint here that would result in undesired // and possibly huge vertical movement. There is a corresponding Assert during constraint // generation when the node is Closed. We have to do this here rather than at runtime because // doing it then may skip a Neighbour that replaced other Neighbors by transitivity. if (OverlapP(currentNode, nextNode, NodePaddingP) <= parameters.SolverParameters.GapTolerance) { #if VERBOSE Console.WriteLine(" V {0}nbourHTolSkipO: {1}", isLeftNeighbor ? "L" : "R", nextNode); #endif // VERBOSE return true; } } // Add this overlapping neighbour and return true to keep looking for more overlapping neighbours. neighbors.Add(nextNode); #if VERBOSE Console.WriteLine("{0}nbourAddO: {1}", isLeftNeighbor ? "L" : "R", nextNode); #endif // VERBOSE return true; }
} // end GetLeftNeighbours List<OverlapRemovalNode> GetRightNeighbours(OverlapRemovalParameters parameters, ScanLine scanLine, OverlapRemovalNode currentNode, bool isHorizontal) { var lstNeighbours = new List<OverlapRemovalNode>(); OverlapRemovalNode nextNode = scanLine.NextRight(currentNode); for (; null != nextNode; nextNode = scanLine.NextRight(nextNode)) { // AddNeighbor returns false if we are done adding them. if (!AddNeighbour(parameters, currentNode, nextNode, lstNeighbours, false /* isLeftNeighbor */ , isHorizontal)) { if (!nextNode.DeferredRightNeighborToV) { break; } } } // endfor NextLeft return lstNeighbours; } // end GetRightNeighbours
internal static OverlapRemovalNode GetRightConstraintNode(OverlapRemovalNode node) { var cluster = node as OverlapRemovalCluster; return (null != cluster) ? cluster.LeftBorderNode : node; }
// Adds an open/close event pair for the node. paddingP is either cluster or node padding. void AddEvents(OverlapRemovalNode node, List<Event> events) { // Add/subtract only half the padding so they meet in the middle of the padding. events.Add(new Event(true /* fForOpen */, node, node.OpenP - (NodePaddingP / 2))); events.Add(new Event(false /* fForOpen */, node, node.CloseP + (NodePaddingP / 2))); }
// newNode may be a cluster in which case we add it to the cluster list. We never call this to // add the fake border nodes to nodeList; the caller never sees them. internal void AddNode(OverlapRemovalNode newNode) { this.nodeList.Add(newNode); var newCluster = newNode as OverlapRemovalCluster; if (null != newCluster) { this.clusterList.Add(newCluster); } }
internal OverlapRemovalNode NextRight(OverlapRemovalNode node) { var succ = this.nodeTree.Next(this.nodeTree.Find(node)); return (null != succ) ? succ.Item : null; }
internal OverlapRemovalNode NextLeft(OverlapRemovalNode node) { var pred = this.nodeTree.Previous(this.nodeTree.Find(node)); return (null != pred) ? pred.Item : null; }
internal void Remove(OverlapRemovalNode node) { this.nodeTree.Remove(node); }
public void AddNodeToCluster(OverlapRemovalCluster cluster, OverlapRemovalNode node) { // Node derives from Cluster so make sure we don't have this - the only way to create // cluster hierarchies is by AddCluster. ValidateArg.IsNotNull(cluster, "cluster"); if (node is OverlapRemovalCluster) { throw new InvalidOperationException( #if DEBUG "Argument 'node' must not be a Cluster" #endif // DEBUG ); } cluster.AddNode(node); }
/// <summary> /// Add a new variable to the ConstraintGenerator. /// </summary> /// <param name="initialCluster">The cluster this node is to be a member of. It may not be null; pass /// DefaultClusterHierarchy to create a node at the lowest level. Subsequently a node /// may be added to additional clusters, but only to one cluster per hierarchy.</param> /// <param name="userData">An object that is passed through.</param> /// <param name="position">Position of the node in the primary axis; if isHorizontal, it contains horizontal /// position and size, else it contains vertical position and size.</param> /// <param name="size">Size of the node in the primary axis.</param> /// <param name="positionP">Position of the node in the secondary (Perpendicular) axis.</param> /// <param name="sizeP">Size of the node in the secondary (Perpendicular) axis.</param> /// <param name="weight">Weight of the node (indicates how freely it should move).</param> /// <returns>The created node.</returns> public OverlapRemovalNode AddNode(OverlapRemovalCluster initialCluster, object userData, double position, double positionP, double size, double sizeP, double weight) { ValidateArg.IsNotNull(initialCluster, "initialCluster"); // @@PERF: Currently every node will have at least one constraint generated if there are any // other nodes along its line, regardless of whether the perpendicular coordinates result in overlap. // It might be worthwhile to add a check to avoid constraint generation in the case that there cannot // be such an overlap on a line, or if the nodes are separated by some amount of distance. Debug.Assert(null != initialCluster, "initialCluster must not be null"); var nodNew = new OverlapRemovalNode(this.nextNodeId++, userData, position, positionP, size, sizeP, weight); initialCluster.AddNode(nodNew); return nodNew; }