1 | <!DOCTYPE html>
|
---|
2 | <html>
|
---|
3 | <head>
|
---|
4 | <meta charset="UTF-8">
|
---|
5 | <title>Meta Balls</title>
|
---|
6 | <link rel="stylesheet" href="../css/style.css">
|
---|
7 | <script type="text/javascript" src="../../dist/paper.js"></script>
|
---|
8 | <script type="text/paperscript" canvas="canvas">
|
---|
9 | // Ported from original Metaball script by SATO Hiroyuki
|
---|
10 | // http://park12.wakwak.com/~shp/lc/et/en_aics_script.html
|
---|
11 | project.currentStyle = {
|
---|
12 | fillColor: 'black'
|
---|
13 | };
|
---|
14 |
|
---|
15 | var ballPositions = [[255, 129], [610, 73], [486, 363],
|
---|
16 | [117, 459], [484, 726], [843, 306], [789, 615], [1049, 82],
|
---|
17 | [1292, 428], [1117, 733], [1352, 86], [92, 798]];
|
---|
18 |
|
---|
19 | var handle_len_rate = 2.4;
|
---|
20 | var circlePaths = [];
|
---|
21 | var radius = 50;
|
---|
22 | for (var i = 0, l = ballPositions.length; i < l; i++) {
|
---|
23 | var circlePath = new Path.Circle({
|
---|
24 | center: ballPositions[i],
|
---|
25 | radius: 50
|
---|
26 | });
|
---|
27 | circlePaths.push(circlePath);
|
---|
28 | }
|
---|
29 |
|
---|
30 | var largeCircle = new Path.Circle({
|
---|
31 | center: [676, 433],
|
---|
32 | radius: 100
|
---|
33 | });
|
---|
34 | circlePaths.push(largeCircle);
|
---|
35 |
|
---|
36 | function onMouseMove(event) {
|
---|
37 | largeCircle.position = event.point;
|
---|
38 | generateConnections(circlePaths);
|
---|
39 | }
|
---|
40 |
|
---|
41 | var connections = new Group();
|
---|
42 | function generateConnections(paths) {
|
---|
43 | // Remove the last connection paths:
|
---|
44 | connections.children = [];
|
---|
45 |
|
---|
46 | for (var i = 0, l = paths.length; i < l; i++) {
|
---|
47 | for (var j = i - 1; j >= 0; j--) {
|
---|
48 | var path = metaball(paths[i], paths[j], 0.5, handle_len_rate, 300);
|
---|
49 | if (path) {
|
---|
50 | connections.appendTop(path);
|
---|
51 | path.removeOnMove();
|
---|
52 | }
|
---|
53 | }
|
---|
54 | }
|
---|
55 | }
|
---|
56 |
|
---|
57 | generateConnections(circlePaths);
|
---|
58 |
|
---|
59 | // ---------------------------------------------
|
---|
60 | function metaball(ball1, ball2, v, handle_len_rate, maxDistance) {
|
---|
61 | var center1 = ball1.position;
|
---|
62 | var center2 = ball2.position;
|
---|
63 | var radius1 = ball1.bounds.width / 2;
|
---|
64 | var radius2 = ball2.bounds.width / 2;
|
---|
65 | var pi2 = Math.PI / 2;
|
---|
66 | var d = center1.getDistance(center2);
|
---|
67 | var u1, u2;
|
---|
68 |
|
---|
69 | if (radius1 == 0 || radius2 == 0)
|
---|
70 | return;
|
---|
71 |
|
---|
72 | if (d > maxDistance || d <= Math.abs(radius1 - radius2)) {
|
---|
73 | return;
|
---|
74 | } else if (d < radius1 + radius2) { // case circles are overlapping
|
---|
75 | u1 = Math.acos((radius1 * radius1 + d * d - radius2 * radius2) /
|
---|
76 | (2 * radius1 * d));
|
---|
77 | u2 = Math.acos((radius2 * radius2 + d * d - radius1 * radius1) /
|
---|
78 | (2 * radius2 * d));
|
---|
79 | } else {
|
---|
80 | u1 = 0;
|
---|
81 | u2 = 0;
|
---|
82 | }
|
---|
83 |
|
---|
84 | var angle1 = (center2 - center1).getAngleInRadians();
|
---|
85 | var angle2 = Math.acos((radius1 - radius2) / d);
|
---|
86 | var angle1a = angle1 + u1 + (angle2 - u1) * v;
|
---|
87 | var angle1b = angle1 - u1 - (angle2 - u1) * v;
|
---|
88 | var angle2a = angle1 + Math.PI - u2 - (Math.PI - u2 - angle2) * v;
|
---|
89 | var angle2b = angle1 - Math.PI + u2 + (Math.PI - u2 - angle2) * v;
|
---|
90 | var p1a = center1 + getVector(angle1a, radius1);
|
---|
91 | var p1b = center1 + getVector(angle1b, radius1);
|
---|
92 | var p2a = center2 + getVector(angle2a, radius2);
|
---|
93 | var p2b = center2 + getVector(angle2b, radius2);
|
---|
94 |
|
---|
95 | // define handle length by the distance between
|
---|
96 | // both ends of the curve to draw
|
---|
97 | var totalRadius = (radius1 + radius2);
|
---|
98 | var d2 = Math.min(v * handle_len_rate, (p1a - p2a).length / totalRadius);
|
---|
99 |
|
---|
100 | // case circles are overlapping:
|
---|
101 | d2 *= Math.min(1, d * 2 / (radius1 + radius2));
|
---|
102 |
|
---|
103 | radius1 *= d2;
|
---|
104 | radius2 *= d2;
|
---|
105 |
|
---|
106 | var path = new Path({
|
---|
107 | segments: [p1a, p2a, p2b, p1b],
|
---|
108 | style: ball1.style,
|
---|
109 | closed: true
|
---|
110 | });
|
---|
111 | var segments = path.segments;
|
---|
112 | segments[0].handleOut = getVector(angle1a - pi2, radius1);
|
---|
113 | segments[1].handleIn = getVector(angle2a + pi2, radius2);
|
---|
114 | segments[2].handleOut = getVector(angle2b - pi2, radius2);
|
---|
115 | segments[3].handleIn = getVector(angle1b + pi2, radius1);
|
---|
116 | return path;
|
---|
117 | }
|
---|
118 |
|
---|
119 | // ------------------------------------------------
|
---|
120 | function getVector(radians, length) {
|
---|
121 | return new Point({
|
---|
122 | // Convert radians to degrees:
|
---|
123 | angle: radians * 180 / Math.PI,
|
---|
124 | length: length
|
---|
125 | });
|
---|
126 | }
|
---|
127 | </script>
|
---|
128 | </head>
|
---|
129 | <body>
|
---|
130 | <canvas id="canvas" resize></canvas>
|
---|
131 | </body>
|
---|
132 | </html> |
---|