コード例 #1
0
        /// <summary>
        /// Usually we have two solutions for tangent circles. We have to decide which to use.
        /// I assume the polygon goes always in direction m to n.
        /// We chose the solution that is outside the polygon. I want to grow outside.
        /// 1. Calculate segment m-n
        /// 2. Calculate normal vector form
        /// 3. Chose m as vector to the segment
        /// 4. Use vector normal form to check if solution1 or solution2 is outside or inside.
        /// Both scalars may be positive or negative. So I chose the larger on. Not quite sure if this is ok.
        /// </summary>
        private CircularLayoutInfo SelectCircle(CircularLayoutInfo m, CircularLayoutInfo n, CircularLayoutInfo circle1, CircularLayoutInfo circle2)
        {
            Debug.Assert(IsPointValid(circle1.Center) || IsPointValid(circle2.Center));

            // If only one point is valid, take it
            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?

            var segment_m_n = m.Center - n.Center;
            var anyNormal   = new Vector(-segment_m_n.Y, segment_m_n.X)
                              - new Vector(segment_m_n.Y, -segment_m_n.X);
            var value1 = Vector.Multiply(circle1.Center - m.Center, anyNormal);
            var value2 = Vector.Multiply(circle2.Center - m.Center, anyNormal);

            if (value1 > 0 && value2 < 0)
            {
                return(circle2);
            }

            if (value2 > 0 && value1 < 0)
            {
                return(circle1);
            }

            return(value1 > value2 ? circle2 : circle1);
        }
コード例 #2
0
        /// <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);
        }
コード例 #3
0
        private CircularLayoutInfo GetLayout(IHierarchicalData item)
        {
            if (item.Layout == null)
            {
                var layout = new CircularLayoutInfo();
                item.Layout = layout;
            }

            return(item.Layout as CircularLayoutInfo);
        }
コード例 #4
0
        private CircularLayoutInfo SelectCircleWithLargerDistanceFromOrigin(CircularLayoutInfo circle1, CircularLayoutInfo circle2)
        {
            Debug.Assert(IsPointValid(circle1.Center) && IsPointValid(circle2.Center));
            Debug.Assert(circle1.Radius.Equals(circle2.Radius));

            if (((Vector)circle1.Center).Length > ((Vector)circle2.Center).Length)
            {
                return(circle1);
            }

            return(circle2);
        }
コード例 #5
0
        private bool IsOverlapping(CircularLayoutInfo layout1, CircularLayoutInfo layout2)
        {
            if (layout1 == layout2)
            {
                return(true);
            }

            var intersections = Geometry.FindCircleCircleIntersections(layout1.Center, layout1.Radius,
                                                                       layout2.Center, layout2.Radius, out var intersection1, out var intersection2);

            // Two solutions or one circle inside the other.
            return(intersections == 2 || intersections == -1);
        }
コード例 #6
0
        public Node InsertAfter(Node node, CircularLayoutInfo layout)
        {
            var newNode = new Node(layout);

            // Fix new node
            newNode.Next     = node.Next;
            newNode.Previous = node;

            // Fix successor
            var successor = node.Next;

            successor.Previous = newNode;

            // Fix node
            node.Next = newNode;

            return(newNode);
        }
コード例 #7
0
        /// <summary>
        /// Find the circle tangent with the two others.
        /// </summary>
        private Tuple <CircularLayoutInfo, CircularLayoutInfo> FindTangentCircle(CircularLayoutInfo circle1, CircularLayoutInfo circle2, double radiusOfTangentCircle)
        {
            var solutions = Geometry.FindCircleCircleIntersections(circle1.Center, circle1.Radius + radiusOfTangentCircle,
                                                                   circle2.Center, circle2.Radius + radiusOfTangentCircle, out var solution1
                                                                   , out var solution2);

            Debug.Assert(solutions >= 1);

            return(new Tuple <CircularLayoutInfo, CircularLayoutInfo>(
                       new CircularLayoutInfo
            {
                Center = solution1, Radius = radiusOfTangentCircle
            },
                       new CircularLayoutInfo
            {
                Center = solution2,
                Radius = radiusOfTangentCircle
            }));
        }
コード例 #8
0
        public Node Add(CircularLayoutInfo layout)
        {
            var newNode = new Node(layout);

            if (Head == null)
            {
                Head             = newNode;
                newNode.Next     = Head;
                newNode.Previous = Head;
            }
            else
            {
                // Add to the end (before head)
                var tail = Head.Previous;
                tail.Next        = newNode;
                newNode.Previous = tail;

                newNode.Next  = Head;
                Head.Previous = newNode;
            }

            return(newNode);
        }
コード例 #9
0
 private Node FindOverlappingCircleOnFrontChain(FrontChain frontChain, CircularLayoutInfo tmpCircle)
 {
     return(frontChain.Find(node => IsOverlapping(node.Value, tmpCircle)));
 }
コード例 #10
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));
        }
コード例 #11
0
 public Node(CircularLayoutInfo value)
 {
     Value = value;
 }