Example #1
0
        public override void Run()
        {
            // Algorithm Setup: save input points
            var layer = History.CreateAndAddNewLayer("Setup (sorting input)");

            layer.AddCommand(new UpdatePointSetCommand
            {
                Label  = "Input",
                Points = AlgorithmUtil.CopyVectorList(InputPoints)
            });
            layer.AddCommand(new HighlightLiveCodeSectionsCommand(AlgorithmId, SECTION_SORT_START, SECTION_SORT));

            SortedInput = new List <Vector>();

            // algorithm step 1: sort, saved sorted input points

            foreach (var v in InputPoints)
            {
                SortedInput.Add(new Vector {
                    X = v.X, Y = v.Y, Alternates = v.Alternates
                });
            }

            SortedInput.Sort(GrahamSort);

            layer.AddCommand(new UpdatePointSetCommand
            {
                Label  = "Sorted Input",
                Points = AlgorithmUtil.CopyVectorList(SortedInput)
            });

            // check degenerate case #1: 0,1,2 points
            layer = History.CreateAndAddNewLayer("Degenerate Case Checks");

            if (SortedInput.Count <= 2)
            {
                layer.AddCommand(new AddTextStatusCommand
                {
                    AssociatedAlgorithm = this,
                    Comments            = "Algorithm cannot execute, it requires at least 3 points"
                });
                layer.AddCommand(new HighlightLiveCodeSectionsCommand(AlgorithmId, SECTION_DEGENERATE));
                return;
            }

            // check degenerate case #2: all points on same line
            if (AllPointsCollinear())
            {
                layer.AddCommand(new AddTextStatusCommand
                {
                    AssociatedAlgorithm = this,
                    Comments            = "Algorithm cannot execute if all points are collinear"
                });
                layer.AddCommand(new HighlightLiveCodeSectionsCommand(AlgorithmId, SECTION_DEGENERATE));
                return;
            }

            // points cannot be collinear, points must be at least 3

            Stack <Vector> grahamStack = new Stack <Vector>();

            grahamStack.Push(SortedInput[0]);
            grahamStack.Push(SortedInput[1]);

            // starting points: first two sorted points, pushed onto stack

            // STATIC LAYER: "Starting Points", has only input points
            // layer commentary: if 0, 1 or 2 points, cannot perform graham scan
            //                   if all points on same line, cannot perform graham scan

            layer = History.CreateAndAddNewLayer("Initiailzation");
            layer.AddCommand(new AlgorithmStartingCommand {
                AssociatedAlgorithm = this
            });
            layer.AddCommand(new HighlightLiveCodeSectionsCommand(AlgorithmId, SECTION_PRE));
            var hip = new HighlightPointsCommand
            {
                AssociatedAlgorithm = this,
                HighlightLevel      = 1
            };

            hip.Points.Add(new Vector {
                X = SortedInput[0].X, Y = SortedInput[0].Y
            });
            hip.Points.Add(new Vector {
                X = SortedInput[1].X, Y = SortedInput[1].Y
            });

            layer.AddCommand(hip);

            layer.AddCommand(new VectorProcessingStackUpdatedCommand
            {
                AssociatedAlgorithm = this,
                ProcessingStack     = AlgorithmUtil.CopyVectorStack(grahamStack),
                Comments            = "Initial stack"
            });
            AddStackLinesToLayer(layer, grahamStack);

            for (int i = 2; i < SortedInput.Count; i++)
            {
                layer = History.CreateAndAddNewLayer("Processing Point, index=" + i);

                layer.AddCommand(new AlgorithmStepStartingCommand
                {
                    AssociatedAlgorithm = this,
                    Comments            = "Step Starting"
                });

                layer.AddCommand(new ClearPointHighlightsCommand
                {
                    AssociatedAlgorithm = this,
                    Comments            = ""
                });

                layer.AddCommand(new ClearTextStatusCommand
                {
                    AssociatedAlgorithm = this,
                    Comments            = ""
                });

                // NORMAL LAYER: "Algorithm Layer (i-2)"
                // Commentary: "We examine the next point in sorted order with the top two
                //              elements from the stack status structure, popping the first one"
                // stack visualization: highlight top of stack, label it "tail"
                // standalone visualization: show SortedInput[i] as "head"
                //                           show popped element as "middle"
                // highlight the "head" point in yellow, and the "middle" / "tail" points in green
                // layer commentary:

                // loop iteration "i"
                Vector head   = SortedInput[i];
                Vector middle = grahamStack.Pop();
                Vector tail   = grahamStack.Peek();

                layer.AddCommand(new HighlightInputPointCommand
                {
                    AssociatedAlgorithm = this,
                    Comments            = "Next input point",
                    X = head.X,
                    Y = head.Y,
                    HightlightLevel = 1
                });

                hip = new HighlightPointsCommand
                {
                    AssociatedAlgorithm = this,
                    HighlightLevel      = 1
                };

                hip.Points.Add(new Vector {
                    X = head.X, Y = head.Y
                });
                hip.Points.Add(new Vector {
                    X = middle.X, Y = middle.Y
                });
                hip.Points.Add(new Vector {
                    X = tail.X, Y = tail.Y
                });
                layer.AddCommand(hip);

                layer.AddCommand(new HighlightInputPointCommand
                {
                    AssociatedAlgorithm = this,
                    Comments            = "Freshly popped from top of stack",
                    X = middle.X,
                    Y = middle.Y,
                    HightlightLevel = 2
                });

                layer.AddCommand(new HighlightInputPointCommand
                {
                    AssociatedAlgorithm = this,
                    Comments            = "Top of stack, which is left on stack and peeked",
                    X = tail.X,
                    Y = tail.Y,
                    HightlightLevel = 2
                });

                // we examine next point in sorted list, with top element of stack (which we pop)
                // and 2nd element of stack (which we leave as new top of stack)

                // Commentary: "The turn direction of these three points is calculated using
                //              the cross product of these three points"
                int turn = GeomMath.GetTurnDirection(tail, middle, head);

                // determine "turn" of these 3 points, in sequence tail / middle / head
                string turnString = "Counter-clockwise";
                if (turn == GeomMath.DIRECTION_CLOCKWISE)
                {
                    turnString = "Clockwise";
                }
                else if (turn == GeomMath.DIRECTION_NONE)
                {
                    turnString = "None";
                }

                layer.AddCommand(new AddTextStatusCommand
                {
                    AssociatedAlgorithm = this,
                    Comments            = "Computed turn: " + turnString
                });

                // Standalone visualization: "The turn direction for these three points is: "
                switch (turn)
                {
                case GeomMath.DIRECTION_COUNTERCLOCKWISE:
                    layer.AddCommand(new AddTextStatusCommand
                    {
                        AssociatedAlgorithm = this,
                        Comments            = "The point popped is pushed back onto stack since it is part of the hull"
                    });
                    layer.AddCommand(new AddTextStatusCommand
                    {
                        AssociatedAlgorithm = this,
                        Comments            = "The input point is also pushed since it is potentially part of the hull"
                    });
                    layer.AddCommand(new HighlightLiveCodeSectionsCommand(AlgorithmId, SECTION_COUNTERCLOCKWISE));

                    grahamStack.Push(middle);
                    grahamStack.Push(head);

                    layer.AddCommand(new VectorProcessingStackUpdatedCommand
                    {
                        AssociatedAlgorithm = this,
                        ProcessingStack     = AlgorithmUtil.CopyVectorStack(grahamStack),
                        Comments            = "Updated processing stack"
                    });
                    break;

                case GeomMath.DIRECTION_CLOCKWISE:
                    layer.AddCommand(new AddTextStatusCommand
                    {
                        AssociatedAlgorithm = this,
                        Comments            = "The point on the top of the stack is discarded, but the input point is preserved for re-consideration"
                    });
                    layer.AddCommand(new HighlightLiveCodeSectionsCommand(AlgorithmId, SECTION_CLOCKWISE));
                    i--;
                    break;

                case GeomMath.DIRECTION_NONE:
                    layer.AddCommand(new AddTextStatusCommand
                    {
                        AssociatedAlgorithm = this,
                        Comments            = "Input point is co-linear with other points, so it is part of the hull"
                    });
                    layer.AddCommand(new HighlightLiveCodeSectionsCommand(AlgorithmId, SECTION_NONE));

                    grahamStack.Push(head);

                    layer.AddCommand(new VectorProcessingStackUpdatedCommand
                    {
                        AssociatedAlgorithm = this,
                        ProcessingStack     = AlgorithmUtil.CopyVectorStack(grahamStack),
                        Comments            = "Updated processing stack"
                    });
                    break;
                }

                AddStackLinesToLayer(layer, grahamStack);
            }

            layer = History.CreateAndAddNewLayer("Final Results");
            layer.AddCommand(new AlgorithmCompleteCommand {
                AssociatedAlgorithm = this
            });
            layer.AddCommand(new ClearPointHighlightsCommand
            {
                AssociatedAlgorithm = this,
                Comments            = ""
            });

            layer.AddCommand(new ClearTextStatusCommand
            {
                AssociatedAlgorithm = this,
                Comments            = ""
            });
            layer.AddCommand(new HighlightLiveCodeSectionsCommand(AlgorithmId, SECTION_POST));

            grahamStack.Push(SortedInput[0]);

            layer.AddCommand(new VectorProcessingStackUpdatedCommand
            {
                AssociatedAlgorithm = this,
                ProcessingStack     = AlgorithmUtil.CopyVectorStack(grahamStack),
                Comments            = "Final stack, first input point is pushed to complete the hull"
            });
            AddStackLinesToLayer(layer, grahamStack);

            Hull = new PolygonModel();

            var a = grahamStack.ToArray <Vector>();

            for (int i = 0; i < a.Length - 1; i++)
            {
                var ap  = a[i].Alternates;
                var ap2 = a[i + 1].Alternates;

                if (ap != null && ap2 != null)
                {
                    // Main operation
                    Hull.Lines.Add(new LineModel
                    {
                        StartPoint = new Vector {
                            X          = a[i].X,
                            Y          = a[i].Y,
                            Alternates = new CanvasPoint {
                                DotIndexLeft = ap.DotIndexLeft, DotIndexTop = ap.DotIndexTop
                            }
                        },
                        EndPoint = new Vector
                        {
                            X          = a[i + 1].X,
                            Y          = a[i + 1].Y,
                            Alternates = new CanvasPoint {
                                DotIndexLeft = ap2.DotIndexLeft, DotIndexTop = ap2.DotIndexTop
                            }
                        }
                    });
                }
                else
                {
                    // Side operation that doesn't involve points from grid
                    Hull.Lines.Add(new LineModel
                    {
                        StartPoint = new Vector {
                            X = a[i].X, Y = a[i].Y
                        },
                        EndPoint = new Vector
                        {
                            X = a[i + 1].X, Y = a[i + 1].Y
                        }
                    });
                }
            }
        }
Example #2
0
        public override void Run()
        {
            if (InputPoints == null || InputPoints.Count < 3)
            {
                return;
            }

            double minx = InputPoints[0].X;
            double miny = InputPoints[0].Y;
            double maxx = minx;
            double maxy = miny;

            for (int i = 0; i < InputPoints.Count; i++)
            {
                var v = InputPoints[i];

                if (v.X < minx)
                {
                    minx = v.X;
                }
                if (v.Y < miny)
                {
                    miny = v.Y;
                }
                if (v.X > maxx)
                {
                    maxx = v.X;
                }
                if (v.Y > maxy)
                {
                    maxy = v.Y;
                }
            }

            double dx       = maxx - minx;
            double dy       = maxy - miny;
            double deltaMax = Math.Max(dx, dy);
            double midx     = 0.5 * (minx + maxx);
            double midy     = 0.5 * (miny + maxy);

            Vector p1 = new Vector {
                X = midx - 20 * deltaMax, Y = midy - deltaMax
            };
            Vector p2 = new Vector {
                X = midx, Y = midy + 20 * deltaMax
            };
            Vector p3 = new Vector {
                X = midx + 20 * deltaMax, Y = midy - deltaMax
            };

            Triangles.Clear();
            Triangles.Add(new DelaunayTriangle {
                V1 = p1, V2 = p2, V3 = p3
            });

            for (int i = 0; i < InputPoints.Count; i++)
            {
                Vector p = InputPoints[i];
                List <DelaunayEdge> polygon = new List <DelaunayEdge>();

                var badTrianglesLayer = History.CreateAndAddNewLayer("Identifying bad triangles");
                AddTriangleLinesToLayer(badTrianglesLayer);

                foreach (var t in Triangles)
                {
                    if (t.CircumcircleContainsVertex(p))
                    {
                        t.IsBad = true;
                        polygon.Add(new DelaunayEdge(t.V1, t.V2));
                        polygon.Add(new DelaunayEdge(t.V2, t.V3));
                        polygon.Add(new DelaunayEdge(t.V3, t.V1));

                        var hip = new HighlightPointsCommand
                        {
                            AssociatedAlgorithm = this,
                            HighlightLevel      = 1
                        };

                        hip.Points.Add(new Vector {
                            X = t.V1.X, Y = t.V1.Y
                        });
                        hip.Points.Add(new Vector {
                            X = t.V2.X, Y = t.V2.Y
                        });
                        hip.Points.Add(new Vector {
                            X = t.V3.X, Y = t.V3.Y
                        });
                        badTrianglesLayer.AddCommand(hip);
                    }
                }

                RemoveBadTriangles();

                for (int c = 0; c < polygon.Count; c++)
                {
                    for (int d = c + 1; d < polygon.Count; d++)
                    {
                        if (polygon[c].AlmostEquals(polygon[d]))
                        {
                            polygon[c].BadEdge = true;
                            polygon[d].BadEdge = true;
                        }
                    }
                }

                RemoveBadEdges(polygon);

                foreach (var poly in polygon)
                {
                    Triangles.Add(new DelaunayTriangle {
                        V1 = poly.VStart, V2 = poly.VEnd, V3 = p
                    });
                }

                var triLayer = History.CreateAndAddNewLayer("for loop iteration i=" + i);
                AddTriangleLinesToLayer(triLayer);
            }

            RemoveTrianglesWithVertex(p1, p2, p3);

            foreach (var t in Triangles)
            {
                Edges.Add(new DelaunayEdge {
                    VStart = t.V1, VEnd = t.V2
                });
                Edges.Add(new DelaunayEdge {
                    VStart = t.V2, VEnd = t.V3
                });
                Edges.Add(new DelaunayEdge {
                    VStart = t.V3, VEnd = t.V1
                });
            }

            var layer = History.CreateAndAddNewLayer("Final Result");

            AddEdgesToLayer(layer);
        }