Exemple #1
0
        public static Desmos ToDesmos(this FrontChain frontChain)
        {
            // roughly estimate the size of the graph
            var top    = 0;
            var bottom = 0;
            var right  = 0;
            var left   = 0;

            var desmos = new Desmos();

            var id      = 0;
            var current = frontChain.Head;

            do
            {
                var layout     = current.Value;
                var expression = layout.ToString();

                id++;
                desmos.Add(id.ToString(), expression);
                desmos.AddXY(layout.Center);

                left   = (int)Math.Min(left, layout.Center.X - layout.Radius);
                right  = (int)Math.Max(right, layout.Center.X + layout.Radius);
                top    = (int)Math.Max(top, layout.Center.Y + layout.Radius);
                bottom = (int)Math.Min(bottom, layout.Center.Y - layout.Radius);

                current = current.Next;
            } while (current != frontChain.Head);

            desmos.SetBounds(top, left, bottom, right);

            return(desmos);
        }
        private CircularLayoutInfo CalculateEnclosingCircle(IHierarchicalData root, FrontChain frontChain)
        {
            // Get extreme points by extending the vector from origin to center by the radius
            var layouts = frontChain.ToList();
            var points  = layouts.Select(x => Geometry.MovePointAlongLine(new Point(), x.Center, x.Radius)).ToList();

            var layout = GetLayout(root);

            Debug.Assert(layout.Center == new Point()); // Since we process bottom up this node was not considered yet.

            double radius;
            Point  center;

            if (frontChain.Head.Next == frontChain.Head) // Single element
            {
                center = frontChain.Head.Value.Center;
                radius = frontChain.Head.Value.Radius;
            }
            else
            {
                // 2 and 3 points seem to be handled correctly.
                Geometry.FindMinimalBoundingCircle(points, out center, out radius);
            }

            layout.Center = center;
            Debug.Assert(Math.Abs(radius) > 0.00001);
            layout.Radius = radius * 1.03; // Just a little larger 3%.
            return(layout);

            // Note that this is not exact. It is a difficult problem.
            // We may have a little overlap. The larger the increment in radius
            // the smaller this problem gets.
        }
        /// <summary>
        /// Layouting of items starts with arranging the first three items around the center 0,0
        /// Note that we do not set the radius. The radius was determined for the leaf nodes before.
        /// The non leaf nodes don't reflect the sum of the children (area metric!).
        /// </summary>
        private FrontChain InitializeFrontChain(List <IHierarchicalData> children)
        {
            var frontChain = new FrontChain();

            var left  = new CircularLayoutInfo();
            var right = new CircularLayoutInfo();
            var top   = new CircularLayoutInfo();

            IHierarchicalData child;

            if (children.Count >= 1)
            {
                // Left
                child = children[0];
                left  = GetLayout(child);

                // If child has children its origin is not (0,0) any more. So move this node to origin first.
                var displacement = -(Vector)left.Center + new Vector(-left.Radius, 0);
                left.Move(displacement);
            }

            if (children.Count >= 2)
            {
                // Right
                child = children[1];
                right = GetLayout(child);

                // If child has children its origin is not (0,0) any more. So move this node to origin first.
                var displacement = -(Vector)right.Center + new Vector(right.Radius, 0);
                right.Move(displacement);
            }

            if (children.Count >= 3)
            {
                // Top
                child = children[2];
                top   = GetLayout(child);

                var solutions = Geometry.FindCircleCircleIntersections(
                    left.Center, left.Radius + top.Radius,
                    right.Center, right.Radius + top.Radius,
                    out var solution1, out var solution2);

                // If not maybe you did not remove zero weights?
                Debug.Assert(solutions == 2);
                var solution = solution1.Y > solution2.Y ? solution1 : solution2;

                var displacement = -(Vector)top.Center + (Vector)solution;
                top.Move(displacement);
            }

            // This order makes a huge difference.
            // left -> right -> top did not work and leads to overlapping circles!
            frontChain.Add(left);
            frontChain.Add(top);
            frontChain.Add(right);
            return(frontChain);
        }
Exemple #4
0
        public static void WriteDebugOutput(FrontChain frontChain, string extra, params CircularLayoutInfo[] proposedSolutions)
        {
            var id = 1;
            var d  = frontChain.ToDesmos();

            foreach (var circle in proposedSolutions)
            {
                d.Add("solution" + id++, circle.ToString());
            }

            if (extra == null)
            {
                d.Write($"d:\\circles_{_dbg:D3}.html");
            }
            else
            {
                d.Write($"d:\\circles_{_dbg:D3}_{extra}.html");
            }
        }
 private Node FindOverlappingCircleOnFrontChain(FrontChain frontChain, CircularLayoutInfo tmpCircle)
 {
     return(frontChain.Find(node => IsOverlapping(node.Value, tmpCircle)));
 }
Exemple #6
0
        /// <summary>
        /// Usually we have two solutions for tangent circles. We have to decide which to use.
        /// Theoretically that is the one that is outside the front chain polygon.
        /// However there are many edge cases. So I do not have an exact mathematical solution to this problem.
        /// Therefore I use different heuristics.
        /// </summary>
        private CircularLayoutInfo SelectCircle(FrontChain frontChain, CircularLayoutInfo circle1, CircularLayoutInfo circle2)
        {
            var poly = frontChain.ToList().Select(x => x.Center).ToList();

            Debug.Assert(IsPointValid(circle1.Center) || IsPointValid(circle2.Center));

            // ----------------------------------------------------------------------------
            // If exactly one of the two points is valid that is the solution.
            // ----------------------------------------------------------------------------

            if (IsPointValid(circle1.Center) && !IsPointValid(circle2.Center))
            {
                return(circle1);
            }

            if (!IsPointValid(circle1.Center) && IsPointValid(circle2.Center))
            {
                return(circle2);
            }

            // We have two solutions. Which point to chose?

            // ----------------------------------------------------------------------------
            // If one center is inside and one outside the polygon take the outside.
            // ----------------------------------------------------------------------------

            var center1Inside = MathHelper.PointInPolygon(poly, circle1.Center.X, circle1.Center.Y);
            var center2Inside = MathHelper.PointInPolygon(poly, circle2.Center.X, circle2.Center.Y);

            if (center1Inside && !center2Inside)
            {
                return(circle2);
            }

            if (!center1Inside && center2Inside)
            {
                return(circle1);
            }

            // Both centers outside: Examples\Both_centers_outside_polygon.html.
            // Note that the purple circle (center) may also be inside the polygon if it was a little bit smaller.
            // Debug.Assert(center2Inside && center2Inside);

            // Both centers inside: Examples\Both_centers_inside_polygon.html
            // Happens when centers are on the polygon edges.
            // Debug.Assert(!center2Inside && !center2Inside);

            // ----------------------------------------------------------------------------
            // If one circle is crossed by an polygon edge and the other is not,
            // take the other.
            // ----------------------------------------------------------------------------
            var circle1HitByEdge = false;
            var circle2HitByEdge = false;
            var iter             = frontChain.Head;

            while (iter != null)
            {
                var first  = iter.Value;
                var second = iter.Next.Value;

                Point intersect1;
                Point intersect2;

                // Note we consider a line here, not a line segment
                var solutions = MathHelper.FindLineCircleIntersections(circle1.Center.X,
                                                                       circle1.Center.Y, circle1.Radius, first.Center, second.Center, out intersect1, out intersect2);

                if (solutions > 0)
                {
                    circle1HitByEdge |= MathHelper.PointOnLineSegment(first.Center, second.Center, intersect1);
                }

                if (solutions > 1)
                {
                    circle1HitByEdge |= MathHelper.PointOnLineSegment(first.Center, second.Center, intersect2);
                }

                solutions = MathHelper.FindLineCircleIntersections(circle2.Center.X,
                                                                   circle2.Center.Y, circle2.Radius, first.Center, second.Center, out intersect1, out intersect2);

                if (solutions > 0)
                {
                    circle2HitByEdge |= MathHelper.PointOnLineSegment(first.Center, second.Center, intersect1);
                }

                if (solutions > 1)
                {
                    circle2HitByEdge |= MathHelper.PointOnLineSegment(first.Center, second.Center, intersect2);
                }

                iter = iter.Next;
                if (iter == frontChain.Head)
                {
                    // Ensure that the segment from tail to head is also processed.
                    iter = null;
                }
            }

            if (circle1HitByEdge && !circle2HitByEdge)
            {
                return(circle2);
            }

            if (!circle1HitByEdge && circle2HitByEdge)
            {
                return(circle1);
            }

            if (DebugEnabled)
            {
                DebugHelper.WriteDebugOutput(frontChain, "not_sure_which_circle", circle1, circle2);
            }

            // Still inconclusive which solution to take.
            // I choose the one with the largst distance from the origin.
            // Background is following example: Inconclusive_solution.html
            // I need to get rid of the inner (green) circle If I want to grow outwards.

            // In my understanding this inner node is always m. We chose it because it had the smallest distance to the origin.
            // My hope is that the selected solution leads to removal of m. Cannot prove it, but I think so.
            return(SelectCircleWithLargerDistanceFromOrigin(circle1, circle2));
        }