예제 #1
0
        public void ISOMLayoutAlgorithm(
            [NotNull] IBidirectionalGraph <string, Edge <string> > graph,
            int maxCrossCount,
            int maxOverlapped)
        {
            IDictionary <string, Size> verticesSizes = GetVerticesSizes(graph.Vertices);

            var parameters = new ISOMLayoutParameters
            {
                Width  = 1000,
                Height = 1000
            };

            int iteration = 0;
            var algorithm = new ISOMLayoutAlgorithm <string, Edge <string>, IBidirectionalGraph <string, Edge <string> > >(
                graph,
                parameters)
            {
                Rand = new Random(123)
            };

            algorithm.IterationEnded += (sender, args) =>
            {
                Assert.LessOrEqual(args.Iteration, parameters.MaxEpochs);
                Assert.AreEqual(iteration++, args.Iteration);
            };
            algorithm.ProgressChanged += (sender, percent) =>
            {
            };

            LayoutResults results = ExecuteLayoutAlgorithm(algorithm, verticesSizes, true);

            results.CheckResult(maxCrossCount, maxOverlapped);
        }
예제 #2
0
        public void SimpleTreeLayoutAlgorithm(
            [NotNull] IBidirectionalGraph <string, Edge <string> > graph,
            int maxCrossCount)
        {
            IDictionary <string, Size> verticesSizes = GetVerticesSizes(graph.Vertices);

            var parameters = new SimpleTreeLayoutParameters
            {
                LayerGap  = 15,
                VertexGap = 20
            };

            foreach (LayoutDirection direction in Enum.GetValues(typeof(LayoutDirection)))
            {
                parameters.Direction = direction;

                foreach (SpanningTreeGeneration treeGen in Enum.GetValues(typeof(SpanningTreeGeneration)))
                {
                    parameters.SpanningTreeGeneration = treeGen;

                    var algorithm = new SimpleTreeLayoutAlgorithm <string, Edge <string>, IBidirectionalGraph <string, Edge <string> > >(
                        graph,
                        verticesSizes,
                        parameters);

                    LayoutResults results = ExecuteLayoutAlgorithm(algorithm, verticesSizes);
                    results.CheckResult(maxCrossCount);
                    CheckTreeLayout(algorithm, verticesSizes);
                }
            }
        }
예제 #3
0
        /// <summary>
        /// Calculates the size of the box layout container.
        /// </summary>
        /// <param name="obj">The container to lay out.</param>
        /// <param name="args">The parameters to use for layout.</param>
        /// <param name="direction">The direction which is being calculated.</param>
        /// <returns>The minimum and preferred box layout size.</returns>
        private static LayoutResults Calc(GameObject obj, BoxLayoutParams args,
                                          PanelDirection direction)
        {
            var transform  = obj.AddOrGet <RectTransform>();
            int n          = transform.childCount;
            var result     = new LayoutResults(direction, n);
            var components = ListPool <ILayoutElement, BoxLayoutGroup> .Allocate();

            for (int i = 0; i < n; i++)
            {
                var child = transform.GetChild(i)?.gameObject;
                if (child != null && child.activeInHierarchy)
                {
                    // Only on active game objects
                    components.Clear();
                    child.GetComponents(components);
                    var hc = PUIUtils.GetSize(child, direction, components);
                    if (args.Direction == direction)
                    {
                        result.Accum(hc, args.Spacing);
                    }
                    else
                    {
                        result.Expand(hc);
                    }
                    result.children.Add(hc);
                }
            }
            components.Recycle();
            return(result);
        }
예제 #4
0
        public void KKLayoutAlgorithm(
            [NotNull] IBidirectionalGraph <string, Edge <string> > graph,
            int maxCrossCount)
        {
            IDictionary <string, Size> verticesSizes = GetVerticesSizes(graph.Vertices);

            var parameters = new KKLayoutParameters
            {
                Width  = 1000,
                Height = 1000
            };

            foreach (bool exchange in new[] { true, false })
            {
                parameters.ExchangeVertices = exchange;

                int iteration = 0;
                var algorithm = new KKLayoutAlgorithm <string, Edge <string>, IBidirectionalGraph <string, Edge <string> > >(
                    graph,
                    parameters)
                {
                    Rand = new Random(12345)
                };
                algorithm.IterationEnded += (sender, args) =>
                {
                    Assert.LessOrEqual(args.Iteration, parameters.MaxIterations);
                    Assert.AreEqual(iteration++, args.Iteration);
                };

                LayoutResults results = ExecuteLayoutAlgorithm(algorithm, verticesSizes, true);
                results.CheckResult(maxCrossCount);
            }
        }
예제 #5
0
        public void FRLayoutAlgorithm(
            [NotNull] IBidirectionalGraph <string, Edge <string> > graph,
            int maxCrossCount)
        {
            IDictionary <string, Size> verticesSizes = GetVerticesSizes(graph.Vertices);

            var parameters = new BoundedFRLayoutParameters
            {
                Width  = 1000,
                Height = 1000
            };

            foreach (FRCoolingFunction func in Enum.GetValues(typeof(FRCoolingFunction)))
            {
                parameters.CoolingFunction = func;

                int iteration = 0;
                var algorithm = new FRLayoutAlgorithm <string, Edge <string>, IBidirectionalGraph <string, Edge <string> > >(
                    graph,
                    parameters)
                {
                    Rand = new Random(12345)
                };
                algorithm.IterationEnded += (sender, args) =>
                {
                    Assert.LessOrEqual(args.Iteration, parameters.MaxIterations);
                    Assert.AreEqual(iteration++, args.Iteration);
                };

                LayoutResults results = ExecuteLayoutAlgorithm(algorithm, verticesSizes, true);
                results.CheckResult(maxCrossCount);
            }
        }
예제 #6
0
        public void LinLogLayoutAlgorithm(
            [NotNull] IBidirectionalGraph <string, Edge <string> > graph,
            int maxCrossCount)
        {
            IDictionary <string, Size> verticesSizes = GetVerticesSizes(graph.Vertices);

            var parameters = new LinLogLayoutParameters();

            int iteration = 1;
            var algorithm = new LinLogLayoutAlgorithm <string, Edge <string>, IBidirectionalGraph <string, Edge <string> > >(
                graph,
                parameters)
            {
                Rand = new Random(12345)
            };

            algorithm.IterationEnded += (sender, args) =>
            {
                Assert.LessOrEqual(args.Iteration, parameters.MaxIterations);
                Assert.AreEqual(iteration++, args.Iteration);
            };

            LayoutResults results = ExecuteLayoutAlgorithm(algorithm, verticesSizes, true);

            results.CheckResult(maxCrossCount);
        }
예제 #7
0
 protected override void OnEnable()
 {
     base.OnEnable();
     SetDirty();
     horizontal = null;
     vertical   = null;
 }
예제 #8
0
 internal BoxLayoutGroup()
 {
     horizontal     = null;
     layoutPriority = 1;
     parameters     = new BoxLayoutParams();
     vertical       = null;
 }
예제 #9
0
        public void CalculateLayoutInputVertical()
        {
            var   margin = parameters.Margin;
            float gap    = (margin == null) ? 0.0f : margin.top + margin.bottom;

            vertical = Calc(gameObject, parameters, PanelDirection.Vertical);
            var vTotal = vertical.total;

            minHeight       = vTotal.min + gap;
            preferredHeight = vTotal.preferred + gap;
        }
예제 #10
0
        public void CalculateLayoutInputHorizontal()
        {
            var   margin = parameters.Margin;
            float gap    = (margin == null) ? 0.0f : margin.left + margin.right;

            horizontal = Calc(gameObject, parameters, PanelDirection.Horizontal);
            var hTotal = horizontal.total;

            minWidth       = hTotal.min + gap;
            preferredWidth = hTotal.preferred + gap;
        }
예제 #11
0
        public void CalculateLayoutInputVertical()
        {
#if DEBUG_LAYOUT
            PUIUtils.LogUIDebug("CalculateLayoutInputVertical for " + gameObject.name);
#endif
            var   margin = parameters.Margin;
            float gap    = (margin == null) ? 0.0f : margin.top + margin.bottom;
            vertical = Calc(gameObject, parameters, PanelDirection.Vertical);
            var vTotal = vertical.total;
            minHeight       = vTotal.min + gap;
            preferredHeight = vTotal.preferred + gap;
        }
예제 #12
0
        public void CalculateLayoutInputHorizontal()
        {
#if DEBUG_LAYOUT
            PUIUtils.LogUIDebug("CalculateLayoutInputHorizontal for " + gameObject.name);
#endif
            var   margin = parameters.Margin;
            float gap    = (margin == null) ? 0.0f : margin.left + margin.right;
            horizontal = Calc(gameObject, parameters, PanelDirection.Horizontal);
            var hTotal = horizontal.total;
            minWidth       = hTotal.min + gap;
            preferredWidth = hTotal.preferred + gap;
        }
예제 #13
0
        public void CircularLayoutAlgorithm(
            [NotNull] IBidirectionalGraph <string, Edge <string> > graph,
            int maxCrossCount)
        {
            IDictionary <string, Size> verticesSizes = GetVerticesSizes(graph.Vertices);
            var algorithm = new CircularLayoutAlgorithm <string, Edge <string>, IBidirectionalGraph <string, Edge <string> > >(
                graph,
                verticesSizes,
                new CircularLayoutParameters());

            LayoutResults results = ExecuteLayoutAlgorithm(algorithm, verticesSizes);

            results.CheckResult(maxCrossCount);
            CheckCircularLayout(algorithm);
        }
예제 #14
0
        public void RandomLayoutAlgorithm([NotNull] IVertexAndEdgeListGraph <string, Edge <string> > graph)
        {
            IDictionary <string, Size> verticesSizes = GetVerticesSizes(graph.Vertices);
            var algorithm = new RandomLayoutAlgorithm <string, Edge <string>, IVertexAndEdgeListGraph <string, Edge <string> > >(
                graph,
                verticesSizes,
                null,
                new RandomLayoutParameters())
            {
                Rand = new Random(12345)
            };

            LayoutResults results = ExecuteLayoutAlgorithm(algorithm, verticesSizes, true);

            results.CheckPositions();
        }
예제 #15
0
        /// <summary>
        /// Lays out components in the box layout container against the layout axis.
        /// </summary>
        /// <param name="required">The calculated minimum and preferred sizes.</param>
        /// <param name="args">The parameters to use for layout.</param>
        /// <param name="status">The current status of layout.</param>
        private static void DoLayoutPerp(LayoutResults required, BoxLayoutParams args,
                                         LayoutStatus status)
        {
            var components = ListPool <ILayoutController, BoxLayoutGroup> .Allocate();

            var   direction = args.Direction;
            float size      = status.size;

            foreach (var child in required.children)
            {
                var obj = child.source;
                // Active objects only
                if (obj != null && obj.activeInHierarchy)
                {
                    float compSize = size;
                    if (child.flexible <= 0.0f)
                    {
                        // Does not expand to all
                        compSize = Math.Min(compSize, child.preferred);
                    }
                    float offset = (size > compSize) ? GetOffset(args, status.direction,
                                                                 size - compSize) : 0.0f;
                    // Place and size component
                    obj.AddOrGet <RectTransform>().SetInsetAndSizeFromParentEdge(status.edge,
                                                                                 offset + status.offset, compSize);
                    // Invoke SetLayout on dependents
                    components.Clear();
                    obj.GetComponents(components);
                    foreach (var component in components)
                    {
                        if (!PUIUtils.IgnoreLayout(component))
                        {
                            if (direction == PanelDirection.Horizontal)
                            {
                                component.SetLayoutVertical();
                            }
                            else                             // if (direction == PanelDirection.Vertical)
                            {
                                component.SetLayoutHorizontal();
                            }
                        }
                    }
                }
            }
            components.Recycle();
        }
예제 #16
0
        public void RandomLayoutAlgorithm_WithFixedVertices()
        {
            IVertexAndEdgeListGraph <string, Edge <string> > graph = GraphFactory.CreateGeneralGraph(
                30,
                15,
                10,
                false,
                i => i.ToString(),
                (s, t) => new Edge <string>(s, t),
                new Random(123));

            string[] vertices = graph.Vertices.ToArray();

            string fixedVertexNoPos    = vertices[2];
            string fixedVertexWithPos  = vertices[16];
            var    fixedVertexPosition = new Point(50.0, 50.0);
            IDictionary <string, Point> verticesPositions = new Dictionary <string, Point>
            {
                [fixedVertexWithPos] = fixedVertexPosition
            };
            IDictionary <string, Size> verticesSizes = GetVerticesSizes(vertices);
            var verticesTypes = new Dictionary <string, RandomVertexType>
            {
                [fixedVertexNoPos]   = RandomVertexType.Fixed,
                [vertices[8]]        = RandomVertexType.Free,
                [fixedVertexWithPos] = RandomVertexType.Fixed
            };

            var algorithm = new RandomLayoutAlgorithm <string, Edge <string>, IVertexAndEdgeListGraph <string, Edge <string> > >(
                graph,
                verticesPositions,
                verticesSizes,
                verticesTypes,
                new RandomLayoutParameters())
            {
                Rand = new Random(12345)
            };

            // Run without overlap removal otherwise fixed vertex may change their positions after algorithm run
            LayoutResults results = ExecuteLayoutAlgorithm(algorithm, verticesSizes);

            results.CheckPositions();

            Assert.AreEqual(fixedVertexPosition, algorithm.VerticesPositions[fixedVertexWithPos]);
        }
예제 #17
0
        /// <summary>
        /// Lays out components in the box layout container.
        /// </summary>
        /// <param name="args">The parameters to use for layout.</param>
        /// <param name="required">The calculated minimum and preferred sizes.</param>
        /// <param name="size">The total available size in this dimension.</param>
        private static void DoLayout(BoxLayoutParams args, LayoutResults required, float size)
        {
            if (required == null)
            {
                throw new ArgumentNullException("required");
            }
            var direction = required.direction;
            var status    = new LayoutStatus(direction, args.Margin ?? new RectOffset(), size);

            if (args.Direction == direction)
            {
                DoLayoutLinear(required, args, status);
            }
            else
            {
                DoLayoutPerp(required, args, status);
            }
        }
예제 #18
0
        public void SugiyamaLayoutAlgorithm(
            [NotNull] IBidirectionalGraph <string, Edge <string> > graph,
            int maxCrossCount,
            int maxOverlapped)
        {
            IDictionary <string, Size> verticesSizes = GetVerticesSizes(graph.Vertices);

            var parameters = new SugiyamaLayoutParameters();

            foreach (LayoutDirection direction in Enum.GetValues(typeof(LayoutDirection)))
            {
                parameters.Direction = direction;

                foreach (int mode in new[] { -1, 0, 1, 2, 3 })
                {
                    parameters.PositionMode = mode;

                    foreach (bool optimizeWidth in new[] { false, true })
                    {
                        parameters.OptimizeWidth = optimizeWidth;

                        foreach (bool minimizeEdgeLength in new[] { false, true })
                        {
                            parameters.MinimizeEdgeLength = minimizeEdgeLength;

                            var algorithm = new SugiyamaLayoutAlgorithm <string, Edge <string>, IBidirectionalGraph <string, Edge <string> > >(
                                graph,
                                verticesSizes,
                                parameters)
                            {
                                Rand = new Random(12345)
                            };

                            LayoutResults results = ExecuteLayoutAlgorithm(algorithm, verticesSizes);
                            results.CheckResult(maxCrossCount, maxOverlapped);
                        }
                    }
                }
            }
        }
예제 #19
0
        protected static LayoutResults ExecuteLayoutAlgorithm <TVertex, TEdge>(
            [NotNull] ILayoutAlgorithm <TVertex, TEdge, IVertexAndEdgeListGraph <TVertex, TEdge> > algorithm,
            [NotNull] IDictionary <TVertex, Size> verticesSizes,
            bool requireOverlapRemoval = false)
            where TVertex : class
            where TEdge : IEdge <TVertex>
        {
            var results = new LayoutResults();

            Assert.DoesNotThrow(algorithm.Compute);
            IDictionary <TVertex, Point> verticesPositions = algorithm.VerticesPositions;

            if (requireOverlapRemoval)
            {
                var rectangles = new Dictionary <TVertex, Rect>();
                foreach (TVertex vertex in algorithm.VisitedGraph.Vertices)
                {
                    Point position = algorithm.VerticesPositions[vertex];
                    Size  size     = verticesSizes[vertex];
                    rectangles[vertex] = new Rect(
                        position.X - size.Width * (float)0.5,
                        position.Y - size.Height * (float)0.5,
                        size.Width,
                        size.Height);
                }

                var overlapRemoval = new FSAAlgorithm <TVertex>(
                    rectangles,
                    new OverlapRemovalParameters());
                Assert.DoesNotThrow(overlapRemoval.Compute);

                foreach (KeyValuePair <TVertex, Rect> pair in overlapRemoval.Rectangles)
                {
                    verticesPositions[pair.Key] = new Point(
                        pair.Value.Left + pair.Value.Size.Width * 0.5,
                        pair.Value.Top + pair.Value.Size.Height * 0.5);
                }
            }

            IDictionary <TEdge, Point[]> edgeRoutes =
                algorithm is IEdgeRoutingAlgorithm <TVertex, TEdge, IVertexAndEdgeListGraph <TVertex, TEdge> > routingAlgorithm
                    ? routingAlgorithm.EdgeRoutes
                    : new Dictionary <TEdge, Point[]>();

            // Compute metrics
            var positionsMetric = new PositionsMetricCalculator <TVertex, TEdge, IVertexAndEdgeListGraph <TVertex, TEdge> >(
                algorithm.VisitedGraph,
                verticesPositions,
                verticesSizes,
                edgeRoutes);

            positionsMetric.Calculate();
            results.PositionsSet = positionsMetric.PositionsSet;


            var overlapMetric = new OverlapMetricCalculator <TVertex, TEdge, IVertexAndEdgeListGraph <TVertex, TEdge> >(
                algorithm.VisitedGraph,
                verticesPositions,
                verticesSizes,
                edgeRoutes);

            overlapMetric.Calculate();

            results.OverlapCount   = overlapMetric.OverlapCount;
            results.OverlappedArea = overlapMetric.OverlappedArea;


            var areaMetric = new LayoutAreaMetricCalculator <TVertex, TEdge, IVertexAndEdgeListGraph <TVertex, TEdge> >(
                algorithm.VisitedGraph,
                verticesPositions,
                verticesSizes,
                edgeRoutes);

            areaMetric.Calculate();
            results.TopLeft     = areaMetric.TopLeft;
            results.BottomRight = areaMetric.BottomRight;
            results.Area        = areaMetric.Area;
            results.Ratio       = areaMetric.Ratio;

            var edgeMetric = new EdgeCrossingCalculator <TVertex, TEdge, IVertexAndEdgeListGraph <TVertex, TEdge> >(
                algorithm.VisitedGraph,
                verticesPositions,
                verticesSizes,
                edgeRoutes);

            edgeMetric.Calculate();
            results.CrossCount        = edgeMetric.CrossCount;
            results.MaximumEdgeLength = edgeMetric.MaximumEdgeLength;
            results.MinimumEdgeLength = edgeMetric.MinimumEdgeLength;
            results.AverageEdgeLength = edgeMetric.AverageEdgeLength;

            return(results);
        }
예제 #20
0
        /// <summary>
        /// Lays out components in the box layout container parallel to the layout axis.
        /// </summary>
        /// <param name="required">The calculated minimum and preferred sizes.</param>
        /// <param name="args">The parameters to use for layout.</param>
        /// <param name="status">The current status of layout.</param>
        private static void DoLayoutLinear(LayoutResults required, BoxLayoutParams args,
                                           LayoutStatus status)
        {
            var total      = required.total;
            var components = ListPool <ILayoutController, BoxLayoutGroup> .Allocate();

            var direction = args.Direction;
            // Determine flex size ratio
            float size = status.size, prefRatio = 0.0f, minSize = total.min, prefSize =
                total.preferred, excess = Math.Max(0.0f, size - prefSize), flexTotal = total.
                                                                                       flexible, offset = status.offset, spacing = args.Spacing;

            if (size > minSize && prefSize > minSize)
            {
                // Do not divide by 0
                prefRatio = Math.Min(1.0f, (size - minSize) / (prefSize - minSize));
            }
            if (excess > 0.0f && flexTotal == 0.0f)
            {
                // If no components can be expanded, offset all
                offset += GetOffset(args, status.direction, excess);
            }
            foreach (var child in required.children)
            {
                var obj = child.source;
                // Active objects only
                if (obj != null && obj.activeInHierarchy)
                {
                    float compSize = child.min;
                    if (prefRatio > 0.0f)
                    {
                        compSize += (child.preferred - child.min) * prefRatio;
                    }
                    if (excess > 0.0f && flexTotal > 0.0f)
                    {
                        compSize += excess * child.flexible / flexTotal;
                    }
                    // Place and size component
                    obj.AddOrGet <RectTransform>().SetInsetAndSizeFromParentEdge(status.edge,
                                                                                 offset, compSize);
                    offset += compSize + ((compSize > 0.0f) ? spacing : 0.0f);
                    // Invoke SetLayout on dependents
                    components.Clear();
                    obj.GetComponents(components);
                    foreach (var component in components)
                    {
                        if (!PUIUtils.IgnoreLayout(component))
                        {
                            if (direction == PanelDirection.Horizontal)
                            {
                                component.SetLayoutHorizontal();
                            }
                            else                             // if (direction == PanelDirection.Vertical)
                            {
                                component.SetLayoutVertical();
                            }
                        }
                    }
                }
            }
            components.Recycle();
        }