/// <summary> /// Obtain a starting configuration that is feasible with respect to the structural /// constraints. This is necessary to avoid e.g. cycles in the constraint graph; /// for example, dragging the root of a downward-pointing tree downward below other /// nodes of the tree can result in auto-generation of constraints generating some /// constraints with the root on the right-hand side, and the structural constraints /// have it on the left-hand side. /// /// When AvoidOverlaps==true and we reach ConstraintLevel>=2 then we also need to remove /// overlaps... prior to this we need to force horizontal resolving of overlaps /// between *all* nodes involved in vertical equality constraints (i.e. no skipping), /// and then vertical overlap resolution of all nodes involved in horizontal equality /// constraints /// </summary> static internal void Enforce(FastIncrementalLayoutSettings settings, int currentConstraintLevel, IEnumerable <FiNode> nodes, List <IConstraint> horizontalConstraints, List <IConstraint> verticalConstraints, IEnumerable <Cluster> clusterHierarchies, Func <Cluster, LayoutAlgorithmSettings> clusterSettings) { foreach (LockPosition l in settings.locks) { l.Project(); } ResetPositions(nodes); double dblVpad = settings.NodeSeparation + Pad; double dblHpad = settings.NodeSeparation; double dblCVpad = settings.ClusterMargin + Pad; double dblCHpad = settings.ClusterMargin; for (int level = settings.MinConstraintLevel; level <= currentConstraintLevel; ++level) { // to obtain a feasible solution when equality constraints are present we need to be extra careful // but the solution below is a little bit crummy, is not currently optimized when there are no // equality constraints and we do not really have any scenarios involving equality constraints at // the moment, and also the fact that it turns off DeferToVertical causes it to resolve too // many overlaps horizontally, so let's skip it for now. //if (level >= 2 && settings.AvoidOverlaps) //{ // RemoveOverlapsOnEqualityConstraints(dblVpad, dblHpad, horizontalConstraints, verticalConstraints); //} var hsSolver = new AxisSolver(true, nodes, clusterHierarchies, level >= 2 && settings.AvoidOverlaps, level, clusterSettings); hsSolver.structuralConstraints = horizontalConstraints; hsSolver.OverlapRemovalParameters = new Core.Geometry.OverlapRemovalParameters { AllowDeferToVertical = true, ConsiderProportionalOverlap = settings.IdealEdgeLength.EdgeDirectionConstraints != Core.Geometry.Directions.None }; hsSolver.Initialize(dblHpad, dblVpad, dblCHpad, dblCVpad, v => v.Center); hsSolver.SetDesiredPositions(); hsSolver.Solve(); ResetPositions(nodes); var vsSolver = new AxisSolver(false, nodes, clusterHierarchies, level >= 2 && settings.AvoidOverlaps, level, clusterSettings); vsSolver.structuralConstraints = verticalConstraints; vsSolver.Initialize(dblHpad, dblVpad, dblCHpad, dblCVpad, v => v.Center); vsSolver.SetDesiredPositions(); vsSolver.Solve(); ResetPositions(nodes); } }
/// <summary> /// Obtain a starting configuration that is feasible with respect to the structural /// constraints. This is necessary to avoid e.g. cycles in the constraint graph; /// for example, dragging the root of a downward-pointing tree downward below other /// nodes of the tree can result in auto-generation of constraints generating some /// constraints with the root on the right-hand side, and the structural constraints /// have it on the left-hand side. /// /// When AvoidOverlaps==true and we reach ConstraintLevel>=2 then we also need to remove /// overlaps... prior to this we need to force horizontal resolving of overlaps /// between *all* nodes involved in vertical equality constraints (i.e. no skipping), /// and then vertical overlap resolution of all nodes involved in horizontal equality /// constraints /// </summary> static internal void Enforce(FastIncrementalLayoutSettings settings, int currentConstraintLevel, IEnumerable<FiNode> nodes, List<IConstraint> horizontalConstraints, List<IConstraint> verticalConstraints, IEnumerable<Cluster> clusterHierarchies, Func<Cluster, LayoutAlgorithmSettings> clusterSettings) { foreach (LockPosition l in settings.locks) { l.Project(); } ResetPositions(nodes); double dblVpad = settings.NodeSeparation + Pad; double dblHpad = settings.NodeSeparation; double dblCVpad = settings.ClusterMargin + Pad; double dblCHpad = settings.ClusterMargin; for (int level = settings.MinConstraintLevel; level <= currentConstraintLevel; ++level) { // to obtain a feasible solution when equality constraints are present we need to be extra careful // but the solution below is a little bit crummy, is not currently optimized when there are no // equality constraints and we do not really have any scenarios involving equality constraints at // the moment, and also the fact that it turns off DeferToVertical causes it to resolve too // many overlaps horizontally, so let's skip it for now. //if (level >= 2 && settings.AvoidOverlaps) //{ // RemoveOverlapsOnEqualityConstraints(dblVpad, dblHpad, horizontalConstraints, verticalConstraints); //} var hsSolver = new AxisSolver(true, nodes, clusterHierarchies, level >= 2 && settings.AvoidOverlaps, level, clusterSettings); hsSolver.structuralConstraints = horizontalConstraints; hsSolver.OverlapRemovalParameters = new Core.Geometry.OverlapRemovalParameters { AllowDeferToVertical = true, ConsiderProportionalOverlap = settings.IdealEdgeLength.EdgeDirectionConstraints != Core.Geometry.Directions.None }; hsSolver.Initialize(dblHpad, dblVpad, dblCHpad, dblCVpad, v => v.Center); hsSolver.SetDesiredPositions(); hsSolver.Solve(); ResetPositions(nodes); var vsSolver = new AxisSolver(false, nodes, clusterHierarchies, level >= 2 && settings.AvoidOverlaps, level, clusterSettings); vsSolver.structuralConstraints = verticalConstraints; vsSolver.Initialize(dblHpad, dblVpad, dblCHpad, dblCVpad, v => v.Center); vsSolver.SetDesiredPositions(); vsSolver.Solve(); ResetPositions(nodes); } }
void SolveSeparationConstraints() { if (this.NeedSolve()) { // Increasing the padding effectively increases the size of the rectangle, so it will lead to more overlaps, // and therefore tighter packing once the overlap is removed and therefore more apparent "columnarity". // We don't want to drastically change the shape of the rectangles, just increase them ever so slightly so that // there is a bit more space in the horizontal than vertical direction, thus reducing the likelihood that // the vertical constraint generation will detect spurious overlaps, which should allow the nodes to slide // smoothly around each other. ConGen padding args are: First pad is in direction of the constraints being // generated, second pad is in the perpendicular direction. double dblVpad = settings.NodeSeparation; double dblHpad = dblVpad + Feasibility.Pad; double dblCVpad = settings.ClusterMargin; double dblCHpad = dblCVpad + Feasibility.Pad; // The centers are our desired positions, but we need to find a feasible configuration foreach (FiNode v in nodes) { v.desiredPosition = v.Center; } // Set up horizontal non-overlap constraints based on the (feasible) starting configuration horizontalSolver.Initialize(dblHpad, dblVpad, dblCHpad, dblCVpad, v => v.previousCenter); horizontalSolver.SetDesiredPositions(); horizontalSolver.Solve(); // generate y constraints verticalSolver.Initialize(dblHpad, dblVpad, dblCHpad, dblCVpad, v => v.Center); verticalSolver.SetDesiredPositions(); verticalSolver.Solve(); // If we have multiple locks (hence multiple high-weight nodes), there can still be some // movement of the locked variables - so update all lock positions. foreach (LockPosition l in settings.locks.Where(l => !l.Sticky)) { l.Bounds = l.node.BoundingBox; } } }