├── LICENSE
├── README.md
├── explanation.js
├── index.html
├── melkman.css
├── melkman.js
└── package.json
/LICENSE:
--------------------------------------------------------------------------------
1 | The MIT License (MIT)
2 |
3 | Copyright (c) 2015 Max Goldstein
4 |
5 | Permission is hereby granted, free of charge, to any person obtaining a copy
6 | of this software and associated documentation files (the "Software"), to deal
7 | in the Software without restriction, including without limitation the rights
8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9 | copies of the Software, and to permit persons to whom the Software is
10 | furnished to do so, subject to the following conditions:
11 |
12 | The above copyright notice and this permission notice shall be included in all
13 | copies or substantial portions of the Software.
14 |
15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21 | SOFTWARE.
22 |
--------------------------------------------------------------------------------
/README.md:
--------------------------------------------------------------------------------
1 | # Melkman's Algorithm Visualized
2 |
3 | A dynamic visualization of the best way to find the convex hull of simple polygon.
4 |
5 | To run locally, do `npm install && browserify melkman.js -o bundle.js && wget http://d3js.org/d3.v3.js` and open your favorite web server.
6 |
7 | ## Contributing
8 | If you find a piece of the explanatory text confusing, change it in `explanation.js` and submit a PR.
9 |
10 | If you have a way to improve the dynamic behaviour or graphics, you should file an issue and let me handle it, rather than witness the Lovecraftian horror contained within `melkman.js`.
11 |
--------------------------------------------------------------------------------
/explanation.js:
--------------------------------------------------------------------------------
1 | function lines(){
2 | return "
" + Array.prototype.slice.call(arguments).join("
") + "
";
3 | }
4 |
5 | exports.intro = lines("Melkman's algorithm finds the convex hull of a polygon. If you wrapped a rubber band around a polygon and let it snap tight, you'd have the convex hull, which is one of the foundations of computational geometry. Melkman's algorithm is interesting because it works in linear time; finding the convex hull of a point set (rather than a polygon) takes O(n log n) time.",
6 | "To start, make a polygon by placing points on the canvas to the right."
7 | );
8 |
9 | exports.okayStop = lines( "Okay, stop.",
10 | "Most convex hull algorithms require the entire polygon up front, but Melkman's asks, what can we find out with only three points?",
11 | "The convex hull of the three points so far is just the triangle they make. But, as we get more points, we may discover that some or all of these edges aren't actually on the hull at all! It's possible these points are deep in a pocket, but we won't know until we see more of the polygon.",
12 | "We need a way to store the current hull that can easily be changed to accomodate new information as it arrives.",
13 | "Hit the spacebar to continue."
14 | );
15 |
16 | exports.dequeIntro = lines("Melkman's algorithm uses a deque, or double-ended queue, to solve this problem. The deque always contains the hull of the points we know about. It is efficient to read or modify either end of the deque. Its contents are now shown above.",
17 | "One property of convex polygons (like the hull we're trying to find) is that if you travel the vertices in one direction, you make only right turns. If you travel in the opposite direction, you make only left turns.",
18 | "The deque uses this property to maintain an invariant. If you read the deque rightward, skipping the gap in the polygon, you will make only right turns. If you read leftward, you will make only left turns.",
19 | "Spacebar to continue..."
20 | );
21 |
22 | exports.pointC = function(leftTurn){
23 | return lines("Point c appears on both ends of the deque because it was the last point added to the hull. This is an invariant we want to maintain. But the next point in the deque, in each direction, is also important.",
24 | "We'll say that point " +
25 | (leftTurn ? "a" : "b" ) +
26 | " will be blue because it appears to point c's left. Notice that this means it appears on the right side of the deque. Similarly, point " +
27 | (leftTurn ? "b" : "a" ) +
28 | " will be red because it appears to point c's right.",
29 | "Right now, the deque is small, so all points matter. Later on, we'll see that we don't need to access points deep in the deque."
30 | );
31 | };
32 |
33 | exports.rbpRegions = lines("Looking at those two outermost points on each side of the deque, we extend the lines they form to create regions, based on where point d could land.",
34 | "A sharp left turn puts us in the blue region. A left turn could violate the invariant on the right side of the deque, where we should be making a right turn.",
35 | "Similarly, a sharp right turn puts us in the red region. And if we go into the purple region, we may need to worry about both ends of the deque.",
36 | "If point d is in any of these regions, it will be on the convex hull (of points seen thusfar) and we must modify the deque."
37 | );
38 |
39 | exports.yellowRegion = lines("But point d could also land within the known hull, requiring no changes to the deque. This is the yellow region. If point d lands here, we simply discard it and wait for the next point.",
40 | "Finally, the remaining white region is only accessible from point c by crossing an existing polygon edge. Melkman's algorithm assumes the polygon is simple, meaning that its edges don't intersect like that. It's this knowledge that allows the algorithm to operate in linear time.",
41 | "Notice how the four permissible regions meet at point c, the last point added to the hull. We can determine which region we land in from the turn direction of lines acd and bcd. Thus, the regions are illustrative only, and do not need to be calculated explicitly.",
42 | "Now place point d in one of the colored regions."
43 | );
44 |
45 | exports.nonsimple = function(n){
46 | var edges = n == 1 ? "edge has" : "edges have";
47 | return lines("Hey, that's not allowed.",
48 | "The algorithm assumes that the polygon is simple, meaning edges don't cross each other. The offending " + edges + " been highlighted in bright red.",
49 | "The white region is off-limits. Try placing the point in one of the colored regions."
50 | );};
51 |
52 | exports.pointInYellow = lines("You've placed a point in the yellow region, which is inside the known hull. Since this new point can't possibly be on the hull, we just ignore it.",
53 | "You can place as many points in the yellow region as you like. Just be sure to leave yourself a way out, because the yellow region does not have a line of sight to all the other regions."
54 | );
55 |
56 | exports.pointInRed = lines("You've placed a point in the red region. We now need to pop from the left side of the deqeue until reading leftwards once again corresponds to making only left turns."
57 | );
58 |
59 | exports.pointInBlue = lines("Since the point was placed in the blue region, we now need to pop from the right side of the deqeue until reading rightwards once again corresponds to making only right turns."
60 | );
61 |
62 | exports.pointInPurple = lines("You've placed a point in the purple region, which is just the composition of the red and blue regions!",
63 | "We pop from both sides of the deque (starting on the left) to restore the order invariant."
64 | );
65 |
66 | exports.redRight = lines("Since the point was placed in the red region, the right side of the deque does not need to be modified.",
67 | "In a real implementation, the algorithm would pop until these three points form a right turn when read rightward. The red region tells us humans that they already form a right turn.");
68 |
69 | exports.blueLeft = lines("You've placed a point in the blue region. This means that the left side of the deque does not need to be modified.",
70 | "In a real implementation, the algorithm would pop until these three points form a left turn when read leftward. The blue region tells us humans that they already form a left turn.");
71 |
72 | exports.donePopping = lines("We have now restored the order invariant of the deque, and can now add the newly added point to both ends.",
73 | "The new deque is once again the convex hull of the known points. Now it's time to add another one!",
74 | "Dashed gray lines show the extension of the polygon from the interior past the red and blue points, which will be popped if the new point is beyond those lines.",
75 | "When you're ready, click the first point you placed to complete the polygon. You'll need an unobstructed line of sight to do so."
76 | );
77 |
78 | exports.finished = lines("Now that we've seen the last point on the polygon, take a moment to read the deque while also tracing the same points on the polygon. You'll notice that it is indeed the convex hull, as it has been all along.",
79 | "Melkman's algorithm is online, meaning that it always has the answer for the data is has seen so far, without requiring any additional processing. Online algorithms have practical value when the data is too big to fit into memory or suffers from network latency. They also lend themselves to visualization, because the audience (that's you) can interact, predict, and respond to the dynamic presentation.",
80 | "Because each point was added and removed from the deque at most twice (once on each end), the algorithm has taken linear time."
81 | );
82 |
83 | exports.finale = "
Visualizations are powerful tools to understand algorithms: how they operate, what are the cases, and where they can falter. Careful design can elucidate the consequences only implicit in mathematical concepts.
" +
84 | "
Thank you to the open source technologies that made this visualization possible: