1 |
|
---|
2 | /////////////////////////////////////////////////////////////////////////////////////////
|
---|
3 | // PathPlacer
|
---|
4 | //
|
---|
5 | // Class for creating a winding path between two points
|
---|
6 | //
|
---|
7 | // x1,z1: Starting point of path
|
---|
8 | // x2,z2: Ending point of path
|
---|
9 | // width: Width of the path
|
---|
10 | // a: Waviness - how wavy the path will be (higher is wavier, 0.0 is straight line)
|
---|
11 | // b: Smoothness - how smooth the path will be (higher is smoother)
|
---|
12 | // c: Offset - max amplitude of waves along the path (0.0 is straight line)
|
---|
13 | // taper: Tapering - how much the width of the path changes from start to end
|
---|
14 | // if positive, the width will decrease by that factor, if negative the width
|
---|
15 | // will increase by that factor
|
---|
16 | //
|
---|
17 | /////////////////////////////////////////////////////////////////////////////////////////
|
---|
18 |
|
---|
19 | function PathPlacer(x1, z1, x2, z2, width, a, b, c, taper)
|
---|
20 | {
|
---|
21 | this.x1 = x1;
|
---|
22 | this.z1 = z1;
|
---|
23 | this.x2 = x2;
|
---|
24 | this.z2 = z2;
|
---|
25 | this.width = width;
|
---|
26 | this.a = a;
|
---|
27 | this.b = b;
|
---|
28 | this.c = c;
|
---|
29 | this.taper = taper;
|
---|
30 | }
|
---|
31 |
|
---|
32 | PathPlacer.prototype.place = function(constraint)
|
---|
33 | {
|
---|
34 | // Preliminary bounds check
|
---|
35 | if (!g_Map.validT(this.x1, this.z1) || !constraint.allows(this.x1, this.z1) ||
|
---|
36 | !g_Map.validT(this.x2, this.z2) || !constraint.allows(this.x2, this.z2))
|
---|
37 | {
|
---|
38 | return undefined;
|
---|
39 | }
|
---|
40 |
|
---|
41 | var dx = (this.x2 - this.x1);
|
---|
42 | var dz = (this.z2 - this.z1);
|
---|
43 | var dist = Math.sqrt(dx*dx + dz*dz);
|
---|
44 | dx /= dist;
|
---|
45 | dz /= dist;
|
---|
46 |
|
---|
47 | var numSteps = 1 + Math.floor(dist/4 * this.a);
|
---|
48 | var numISteps = 1 + Math.floor(dist/4 * this.b);
|
---|
49 | var totalSteps = numSteps*numISteps;
|
---|
50 | var offset = 1 + Math.floor(dist/4 * this.c);
|
---|
51 |
|
---|
52 | // Generate random offsets
|
---|
53 | var ctrlVals = new Float32Array(numSteps); //float32
|
---|
54 | for (var j = 1; j < (numSteps-1); ++j)
|
---|
55 | {
|
---|
56 | ctrlVals[j] = randFloat(-offset, offset);
|
---|
57 | }
|
---|
58 |
|
---|
59 | // Interpolate for smoothed 1D noise
|
---|
60 | var noise = new Float32Array(totalSteps+1); //float32
|
---|
61 | for (var j = 0; j < numSteps; ++j)
|
---|
62 | {
|
---|
63 | // Cubic interpolation
|
---|
64 | var v0 = ctrlVals[(j+numSteps-1)%numSteps];
|
---|
65 | var v1 = ctrlVals[j];
|
---|
66 | var v2 = ctrlVals[(j+1)%numSteps];
|
---|
67 | var v3 = ctrlVals[(j+2)%numSteps];
|
---|
68 | var P = (v3 - v2) - (v0 - v1);
|
---|
69 | var Q = (v0 - v1) - P;
|
---|
70 | var R = v2 - v0;
|
---|
71 | var S = v1;
|
---|
72 |
|
---|
73 | for (var k = 0; k < numISteps; ++k)
|
---|
74 | {
|
---|
75 | var t = k/numISteps;
|
---|
76 | noise[j*numISteps + k] = P*t*t*t + Q*t*t + R*t + S;
|
---|
77 | }
|
---|
78 | }
|
---|
79 |
|
---|
80 | var halfWidth = 0.5 * this.width;
|
---|
81 |
|
---|
82 | // Add smoothed noise to straight path
|
---|
83 | var segments1 = [];
|
---|
84 | var segments2 = [];
|
---|
85 | for (var j = 0; j < totalSteps; ++j)
|
---|
86 | {
|
---|
87 | // Interpolated points along straight path
|
---|
88 | var t = j/totalSteps;
|
---|
89 | var tx = this.x1 * (1.0 - t) + this.x2 * t;
|
---|
90 | var tz = this.z1 * (1.0 - t) + this.z2 * t;
|
---|
91 | var t2 = (j+1)/totalSteps;
|
---|
92 | var tx2 = this.x1 * (1.0 - t2) + this.x2 * t2;
|
---|
93 | var tz2 = this.z1 * (1.0 - t2) + this.z2 * t2;
|
---|
94 |
|
---|
95 | // Find noise offset points
|
---|
96 | var nx = (tx - dz * noise[j]);
|
---|
97 | var nz = (tz + dx * noise[j]);
|
---|
98 | var nx2 = (tx2 - dz * noise[j+1]);
|
---|
99 | var nz2 = (tz2 + dx * noise[j+1]);
|
---|
100 |
|
---|
101 | // Find slope of offset points
|
---|
102 | var ndx = (nx2 - nx);
|
---|
103 | var ndz = (nz2 - nz);
|
---|
104 | var dist = Math.sqrt(ndx*ndx + ndz*ndz);
|
---|
105 | ndx /= dist;
|
---|
106 | ndz /= dist;
|
---|
107 |
|
---|
108 | var taperedWidth = (1.0 - t*this.taper) * halfWidth;
|
---|
109 |
|
---|
110 | // Find slope of offset path
|
---|
111 | var px = (nx - ndz * -taperedWidth);
|
---|
112 | var pz = (nz + ndx * -taperedWidth);
|
---|
113 | segments1.push(new PointXZ(px, pz));
|
---|
114 | var px2 = (nx2 - ndz * taperedWidth);
|
---|
115 | var pz2 = (nz2 + ndx * taperedWidth);
|
---|
116 | segments2.push(new PointXZ(px2, pz2));
|
---|
117 | }
|
---|
118 |
|
---|
119 | var retVec = [];
|
---|
120 |
|
---|
121 | // Draw path segments
|
---|
122 | var num = segments1.length - 1;
|
---|
123 | for (var j = 0; j < num; ++j)
|
---|
124 | {
|
---|
125 | // Fill quad formed by these 4 points
|
---|
126 | var pt11 = segments1[j];
|
---|
127 | var pt12 = segments1[j+1];
|
---|
128 | var pt21 = segments2[j];
|
---|
129 | var pt22 = segments2[j+1];
|
---|
130 |
|
---|
131 | var tris = [[pt12, pt11, pt21], [pt12, pt21, pt22]];
|
---|
132 |
|
---|
133 | for (var t = 0; t < 2; ++t)
|
---|
134 | {
|
---|
135 | // Sort vertices by min z
|
---|
136 | var tri = tris[t].sort(
|
---|
137 | function(a, b)
|
---|
138 | {
|
---|
139 | return a.z - b.z;
|
---|
140 | }
|
---|
141 | );
|
---|
142 | var A = tri[0];
|
---|
143 | var B = tri[1];
|
---|
144 | var C = tri[2];
|
---|
145 | var dx1 = (B.z != A.z) ? ((B.x - A.x) / (B.z - A.z)) : 0;
|
---|
146 | var dx2 = (C.z != A.z) ? ((C.x - A.x) / (C.z - A.z)) : 0;
|
---|
147 | var dx3 = (C.z != B.z) ? ((C.x - B.x) / (C.z - B.z)) : 0;
|
---|
148 |
|
---|
149 | var z = Math.round(A.z);
|
---|
150 | var minX = A.x
|
---|
151 | var maxX = A.x
|
---|
152 |
|
---|
153 | if (dx1 > dx2)
|
---|
154 | {
|
---|
155 | while (z <= B.z)
|
---|
156 | {
|
---|
157 | minX = Math.round(minX);
|
---|
158 | maxX = Math.round(maxX);
|
---|
159 | for (var x = minX; x < maxX; ++x)
|
---|
160 | {
|
---|
161 | if (g_Map.validT(x, z) && constraint.allows(x, z))
|
---|
162 | {
|
---|
163 | retVec.push(new PointXZ(x, z));
|
---|
164 | }
|
---|
165 | }
|
---|
166 |
|
---|
167 | ++z;
|
---|
168 | minX += dx2;
|
---|
169 | maxX += dx1;
|
---|
170 | }
|
---|
171 |
|
---|
172 | maxX = B.x;
|
---|
173 |
|
---|
174 | while (z <= C.z)
|
---|
175 | {
|
---|
176 | minX = Math.round(minX);
|
---|
177 | maxX = Math.round(maxX);
|
---|
178 | for (var x = minX; x < maxX; ++x)
|
---|
179 | {
|
---|
180 | if (g_Map.validT(x, z) && constraint.allows(x, z))
|
---|
181 | {
|
---|
182 | retVec.push(new PointXZ(x, z));
|
---|
183 | }
|
---|
184 | }
|
---|
185 |
|
---|
186 | ++z;
|
---|
187 | minX += dx2;
|
---|
188 | maxX += dx3;
|
---|
189 | }
|
---|
190 | }
|
---|
191 | else
|
---|
192 | {
|
---|
193 | while (z <= B.z)
|
---|
194 | {
|
---|
195 | minX = Math.round(minX);
|
---|
196 | maxX = Math.round(maxX);
|
---|
197 | for (var x = minX; x < maxX; ++x)
|
---|
198 | {
|
---|
199 | if (g_Map.validT(x, z) && constraint.allows(x, z))
|
---|
200 | {
|
---|
201 | retVec.push(new PointXZ(x, z));
|
---|
202 | }
|
---|
203 | }
|
---|
204 |
|
---|
205 | ++z;
|
---|
206 | minX += dx1;
|
---|
207 | maxX += dx2;
|
---|
208 | }
|
---|
209 |
|
---|
210 | minX = B.x;
|
---|
211 |
|
---|
212 | while (z <= C.z)
|
---|
213 | {
|
---|
214 | minX = Math.round(minX);
|
---|
215 | maxX = Math.round(maxX);
|
---|
216 | for (var x = minX; x < maxX; ++x)
|
---|
217 | {
|
---|
218 | if (g_Map.validT(x, z) && constraint.allows(x, z))
|
---|
219 | {
|
---|
220 | retVec.push(new PointXZ(x, z));
|
---|
221 | }
|
---|
222 | }
|
---|
223 |
|
---|
224 | ++z;
|
---|
225 | minX += dx3;
|
---|
226 | maxX += dx2;
|
---|
227 | }
|
---|
228 | }
|
---|
229 | }
|
---|
230 | }
|
---|
231 |
|
---|
232 | return retVec;
|
---|
233 | }
|
---|