-
Notifications
You must be signed in to change notification settings - Fork 0
/
Copy pathfunhouse.js
257 lines (232 loc) · 7.73 KB
/
funhouse.js
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
//
// funhouse.js
//
// Author: Jim Fix
// CSCI 385: Computer Graphics, Reed College, Spring 2022
//
// This defines the supporting objects for the ray traced scene
// editor.
//
// It defines these two classes
//
// * Sphere: the placement and sizing of a sphere in the scene.
//
// * Curve: a (Bezier) curve as specified by some control points.
//
// Both can be rendered in a WebGL/opengl context.
//
// ------
//
const MINIMUM_PLACEMENT_SCALE = 0.1; // Smallest sphere we can place.
const MAX_SELECT_DISTANCE = 0.2; // Distance to select a control point.
const SMOOTHNESS = 500.0; // How smooth is our curve approx?
const EPSILON = 0.00000001;
const CURVE_SMOOTHNESS = 1 / SMOOTHNESS;
class Sphere {
//
// Class representing the placement a sphere in the scene.
//
constructor(color, position0) {
//
// `position`, `radius`: a `point` and number,
// representing the location and size of a
// sphere placed in the scene.
//
this.color = color;
this.position = position0;
this.radius = MINIMUM_PLACEMENT_SCALE;
}
resize(scale, bounds) {
//
// Resize the sphere. Some checks prevent growing it beyond
// the scene bounds.
//
scale = Math.max(scale, MINIMUM_PLACEMENT_SCALE);
scale = Math.min(scale, bounds.right - this.position.x);
scale = Math.min(scale, bounds.top - this.position.y);
scale = Math.min(scale, this.position.x - bounds.left);
scale = Math.min(scale, this.position.y - bounds.bottom) ;
this.radius = scale;
}
moveTo(position, bounds) {
//
// Relocate the sphere. Some checks prevent the object from
// being placed outside the scene bounds.
//
position.x = Math.max(position.x ,bounds.left + this.radius);
position.y = Math.max(position.y, bounds.bottom + this.radius);
position.x = Math.min(position.x, bounds.right - this.radius);
position.y = Math.min(position.y, bounds.top - this.radius);
this.position = position;
}
includes(queryPoint) {
//
// Checks whether the `queryPoint` lives within its footprint.
//
const distance = this.position.dist2(queryPoint);
return (distance < this.radius*this.radius);
}
draw(highlightColor, drawBase, drawShaded) {
//
// Draws the sphere within the current WebGL/opengl context.
//
glPushMatrix();
glTranslatef(this.position.x, this.position.y, this.position.z);
glScalef(this.radius, this.radius, this.radius);
//
// draw
if (drawShaded) {
// Turn on lighting.
glEnable(GL_LIGHTING);
glEnable(GL_LIGHT0);
}
glColor3f(this.color.r, this.color.g, this.color.b);
glBeginEnd("sphere");
if (drawShaded) {
// Turn on lighting.
glDisable(GL_LIGHT0);
glDisable(GL_LIGHTING);
}
// draw with highlights
if (highlightColor != null) {
glColor3f(highlightColor.r,
highlightColor.g,
highlightColor.b);
//
// Draw its wireframe.
glBeginEnd("sphere-wireframe");
}
glPopMatrix();
}
}
class Curve {
//
// Class representing a controllable Bezier quadratic curve in a
// scene.
//
// The control points array passed to the constructor can be
// edited externally by a client. The client is required to call
// the `update` method when any control point has been
// edited. This will trigger a "recompiling" of the points of the
// polyline used to render the Bezier curve.
//
constructor(controlPoints) {
this.controlPoints = controlPoints; // Should be an array of 3 Point3d objects.
//
// point3D objs for points
this.points = []; // The samples for the approximation of the curve.
this.compiled = false; // Has `this.points` been computed?
}
compile() {
//
// Recompiles the polyline that is a smooth sampling of the
// points on the Bezier curve. These curve points only need
// to be recompiled if the curve was just created, or if the
// control points have been moved.
//
// The result of this call is a computing of a list of
// sample points, recorded in `this.points`.
//
if (!this.compiled) {
this.points = this.recursivelyCalCurve(...this.controlPoints)
this.compiled = true;
}
}
recursivelyCalCurve(p1, p2, p3) {
const delta = p1.minus(p2).unit().dot(p2.minus(p3).unit())
if (delta > 0 && 1 - delta < CURVE_SMOOTHNESS) {
return [p1, p2, p3]
}
const lpt = p1.combo(1/2, p2)
const rpt = p2.combo(1/2, p3)
const cpt = lpt.combo(1/2, rpt)
let larr = this.recursivelyCalCurve(p1, lpt, cpt)
let rarr = this.recursivelyCalCurve(cpt, rpt, p3)
return larr.concat(rarr.slice(1))
}
update() {
//
// Invalidate `this.points` so that it gets recompiled
// when the curve points need to be used (to draw, e.g.).
//
this.compiled = false;
}
chooseControlPoint(queryPoint) {
//
// Returns the integer index (0, 1, or 2) of the closest
// control point to the given `queryPoint`, or -1 if none
// are close enough.
//
let which = -1;
let distance2 = MAX_SELECT_DISTANCE * MAX_SELECT_DISTANCE;
for (let i=0; i <= 2; i++) {
const d2 = queryPoint.minus(this.controlPoints[i]).norm2();
if (d2 < distance2) {
which = i;
distance2 = d2;
}
}
return which;
}
drawControls() {
//
// Renders the three control points of a quadratic
// Bezier curve.
//
for (let i=0; i <= 2; i++) {
glPushMatrix();
glTranslatef(this.controlPoints[i].x,
this.controlPoints[i].y,
1.9);
glScalef(0.02,0.02,0.02);
const gc = gPOINT_COLOR;
glColor3f(gc.r, gc.g, gc.b);
glBeginEnd("square");
glPopMatrix();
}
}
drawCurve() {
//
// Renders the polyline specified as the array of points
// `this.points`. These should give a smooth approximation
// of the quadratic Bezier, and so as a result this code
// draws the curve.
//
const cc = gCURVE_COLOR;
for (let index = 1; index < this.points.length; index++) {
//
// Compute some info about this segment of the polyline.
const p0 = this.points[index-1];
const p1 = this.points[index];
const dir = p1.minus(p0).unit();
const len = p0.dist(p1);
const ang = Math.atan2(dir.dy, dir.dx) * 180.0 / Math.PI;
glPushMatrix();
//
// Perform the transformations to render this segment.
glTranslatef(p0.x, p0.y, 1.5);
glRotatef(ang, 0.0, 0.0, 1.0);
glRotatef(90,0.0,1.0,0.0);
glScalef(0.01, 0.01, len);
//
// Render this segment of the curve.
glColor3f(cc.r, cc.g, cc.b);
glBeginEnd("path")
//
glPopMatrix();
}
}
draw() {
// Renders the curve control points and the actual
// curve.
//
// If the control points have moved since the last
// time the curve was drawn, then this recompiles
// the curve from the control point info.
//
this.compile(); // Recomputes this.points.
this.drawCurve(); // Uses this.points.
//
this.drawControls();
}
}