Line data Source code
1 0 : /*
2 :
3 : OOJSQuaternion.m
4 :
5 : Oolite
6 : Copyright (C) 2004-2013 Giles C Williams and contributors
7 :
8 : This program is free software; you can redistribute it and/or
9 : modify it under the terms of the GNU General Public License
10 : as published by the Free Software Foundation; either version 2
11 : of the License, or (at your option) any later version.
12 :
13 : This program is distributed in the hope that it will be useful,
14 : but WITHOUT ANY WARRANTY; without even the implied warranty of
15 : MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
16 : GNU General Public License for more details.
17 :
18 : You should have received a copy of the GNU General Public License
19 : along with this program; if not, write to the Free Software
20 : Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston,
21 : MA 02110-1301, USA.
22 :
23 : */
24 :
25 : #import "OOJSQuaternion.h"
26 : #import "OOJavaScriptEngine.h"
27 :
28 : #if OOLITE_GNUSTEP
29 : #import <GNUstepBase/GSObjCRuntime.h>
30 : #else
31 : #import <objc/objc-runtime.h>
32 : #endif
33 :
34 : #import "OOConstToString.h"
35 : #import "OOJSEntity.h"
36 : #import "OOJSVector.h"
37 :
38 :
39 0 : static JSObject *sQuaternionPrototype;
40 :
41 :
42 : static BOOL GetThisQuaternion(JSContext *context, JSObject *quaternionObj, Quaternion *outQuaternion, NSString *method) NONNULL_FUNC;
43 :
44 :
45 : static JSBool QuaternionGetProperty(JSContext *context, JSObject *this, jsid propID, jsval *value);
46 : static JSBool QuaternionSetProperty(JSContext *context, JSObject *this, jsid propID, JSBool strict, jsval *value);
47 : static void QuaternionFinalize(JSContext *context, JSObject *this);
48 : static JSBool QuaternionConstruct(JSContext *context, uintN argc, jsval *vp);
49 :
50 : // Methods
51 : static JSBool QuaternionToString(JSContext *context, uintN argc, jsval *vp);
52 : static JSBool QuaternionToSource(JSContext *context, uintN argc, jsval *vp);
53 : static JSBool QuaternionMultiply(JSContext *context, uintN argc, jsval *vp);
54 : static JSBool QuaternionDot(JSContext *context, uintN argc, jsval *vp);
55 : static JSBool QuaternionRotate(JSContext *context, uintN argc, jsval *vp);
56 : static JSBool QuaternionRotateX(JSContext *context, uintN argc, jsval *vp);
57 : static JSBool QuaternionRotateY(JSContext *context, uintN argc, jsval *vp);
58 : static JSBool QuaternionRotateZ(JSContext *context, uintN argc, jsval *vp);
59 : static JSBool QuaternionNormalize(JSContext *context, uintN argc, jsval *vp);
60 : static JSBool QuaternionConjugate(JSContext *context, uintN argc, jsval *vp);
61 : static JSBool QuaternionVectorForward(JSContext *context, uintN argc, jsval *vp);
62 : static JSBool QuaternionVectorUp(JSContext *context, uintN argc, jsval *vp);
63 : static JSBool QuaternionVectorRight(JSContext *context, uintN argc, jsval *vp);
64 : static JSBool QuaternionToArray(JSContext *context, uintN argc, jsval *vp);
65 :
66 : // Static methods
67 : static JSBool QuaternionStaticRandom(JSContext *context, uintN argc, jsval *vp);
68 :
69 :
70 0 : static JSClass sQuaternionClass =
71 : {
72 : "Quaternion",
73 : JSCLASS_HAS_PRIVATE,
74 :
75 : JS_PropertyStub, // addProperty
76 : JS_PropertyStub, // delProperty
77 : QuaternionGetProperty, // getProperty
78 : QuaternionSetProperty, // setProperty
79 : JS_EnumerateStub, // enumerate
80 : JS_ResolveStub, // resolve
81 : JS_ConvertStub, // convert
82 : QuaternionFinalize, // finalize
83 : JSCLASS_NO_OPTIONAL_MEMBERS
84 : };
85 :
86 :
87 0 : enum
88 : {
89 : // Property IDs
90 : kQuaternion_w,
91 : kQuaternion_x,
92 : kQuaternion_y,
93 : kQuaternion_z
94 : };
95 :
96 :
97 0 : static JSPropertySpec sQuaternionProperties[] =
98 : {
99 : // JS name ID flags
100 : { "w", kQuaternion_w, OOJS_PROP_READWRITE_CB },
101 : { "x", kQuaternion_x, OOJS_PROP_READWRITE_CB },
102 : { "y", kQuaternion_y, OOJS_PROP_READWRITE_CB },
103 : { "z", kQuaternion_z, OOJS_PROP_READWRITE_CB },
104 : { 0 }
105 : };
106 :
107 :
108 0 : static JSFunctionSpec sQuaternionMethods[] =
109 : {
110 : // JS name Function min args
111 : { "toString", QuaternionToString, 0, },
112 : { "toSource", QuaternionToSource, 0, },
113 : { "dot", QuaternionDot, 1, },
114 : { "multiply", QuaternionMultiply, 1, },
115 : { "normalize", QuaternionNormalize, 0, },
116 : { "conjugate", QuaternionConjugate, 0, },
117 : { "rotate", QuaternionRotate, 2, },
118 : { "rotateX", QuaternionRotateX, 1, },
119 : { "rotateY", QuaternionRotateY, 1, },
120 : { "rotateZ", QuaternionRotateZ, 1, },
121 : { "toArray", QuaternionToArray, 0, },
122 : { "vectorForward", QuaternionVectorForward, 0, },
123 : { "vectorRight", QuaternionVectorRight, 0, },
124 : { "vectorUp", QuaternionVectorUp, 0, },
125 : { 0 }
126 : };
127 :
128 :
129 0 : static JSFunctionSpec sQuaternionStaticMethods[] =
130 : {
131 : // JS name Function min args
132 : { "random", QuaternionStaticRandom, 0, },
133 : { 0 }
134 : };
135 :
136 :
137 : // *** Public ***
138 :
139 0 : void InitOOJSQuaternion(JSContext *context, JSObject *global)
140 : {
141 : sQuaternionPrototype = JS_InitClass(context, global, NULL, &sQuaternionClass, QuaternionConstruct, 4, sQuaternionProperties, sQuaternionMethods, NULL, sQuaternionStaticMethods);
142 : }
143 :
144 :
145 0 : JSObject *JSQuaternionWithQuaternion(JSContext *context, Quaternion quaternion)
146 : {
147 : OOJS_PROFILE_ENTER
148 :
149 : JSObject *result = NULL;
150 : Quaternion *private = NULL;
151 :
152 : private = malloc(sizeof *private);
153 : if (EXPECT_NOT(private == NULL)) return NULL;
154 :
155 : *private = quaternion;
156 :
157 : result = JS_NewObject(context, &sQuaternionClass, sQuaternionPrototype, NULL);
158 : if (result != NULL)
159 : {
160 : if (!JS_SetPrivate(context, result, private)) result = NULL;
161 : }
162 :
163 : if (EXPECT_NOT(result == NULL)) free(private);
164 :
165 : return result;
166 :
167 : OOJS_PROFILE_EXIT
168 : }
169 :
170 :
171 0 : BOOL QuaternionToJSValue(JSContext *context, Quaternion quaternion, jsval *outValue)
172 : {
173 : OOJS_PROFILE_ENTER
174 :
175 : JSObject *object = NULL;
176 :
177 : assert(outValue != NULL);
178 :
179 : object = JSQuaternionWithQuaternion(context, quaternion);
180 : if (EXPECT_NOT(object == NULL)) return NO;
181 :
182 : *outValue = OBJECT_TO_JSVAL(object);
183 : return YES;
184 :
185 : OOJS_PROFILE_EXIT
186 : }
187 :
188 :
189 0 : BOOL JSValueToQuaternion(JSContext *context, jsval value, Quaternion *outQuaternion)
190 : {
191 : if (EXPECT_NOT(!JSVAL_IS_OBJECT(value))) return NO;
192 :
193 : return JSObjectGetQuaternion(context, JSVAL_TO_OBJECT(value), outQuaternion);
194 : }
195 :
196 :
197 : #if OO_DEBUG
198 :
199 : typedef struct
200 : {
201 : NSUInteger quatCount;
202 : NSUInteger entityCount;
203 : NSUInteger arrayCount;
204 : NSUInteger protoCount;
205 : NSUInteger nullCount;
206 : NSUInteger failCount;
207 : } QuaternionStatistics;
208 : static QuaternionStatistics sQuaternionConversionStats;
209 :
210 :
211 : @implementation PlayerEntity (JSQuaternionStatistics)
212 :
213 : // :setM quatStats PS.callObjC("reportJSQuaternionStatistics")
214 : // :quatStats
215 :
216 : - (NSString *) reportJSQuaternionStatistics
217 : {
218 : QuaternionStatistics *stats = &sQuaternionConversionStats;
219 :
220 : NSUInteger sum = stats->quatCount + stats->entityCount + stats->arrayCount + stats->protoCount;
221 : double convFac = 100.0 / sum;
222 :
223 : return [NSString stringWithFormat:
224 : @"quaternion-to-quaternion conversions: %lu (%g %%)\n"
225 : " entity-to-quaternion conversions: %lu (%g %%)\n"
226 : " array-to-quaternion conversions: %lu (%g %%)\n"
227 : " prototype-to-zero conversions: %lu (%g %%)\n"
228 : " null conversions: %lu (%g %%)\n"
229 : " failed conversions: %lu (%g %%)\n"
230 : " total: %lu",
231 : (long)stats->quatCount, stats->quatCount * convFac,
232 : (long)stats->entityCount, stats->entityCount * convFac,
233 : (long)stats->arrayCount, stats->arrayCount * convFac,
234 : (long)stats->protoCount, stats->protoCount * convFac,
235 : (long)stats->nullCount, stats->nullCount * convFac,
236 : (long)stats->failCount, stats->failCount * convFac,
237 : (long)sum];
238 : }
239 :
240 :
241 : - (void) clearJSQuaternionStatistics
242 : {
243 : memset(&sQuaternionConversionStats, 0, sizeof sQuaternionConversionStats);
244 : }
245 :
246 : @end
247 :
248 : #define COUNT(FIELD) do { sQuaternionConversionStats.FIELD++; } while (0)
249 :
250 : #else
251 :
252 0 : #define COUNT(FIELD) do {} while (0)
253 :
254 : #endif
255 :
256 :
257 0 : BOOL JSObjectGetQuaternion(JSContext *context, JSObject *quaternionObj, Quaternion *outQuaternion)
258 : {
259 : OOJS_PROFILE_ENTER
260 :
261 : assert(outQuaternion != NULL);
262 :
263 : Quaternion *private = NULL;
264 : jsuint arrayLength;
265 : jsval arrayW, arrayX, arrayY, arrayZ;
266 : jsdouble dVal;
267 :
268 : // quaternionObj can legitimately be NULL, e.g. when JS_NULL is converted to a JSObject *.
269 : if (EXPECT_NOT(quaternionObj == NULL))
270 : {
271 : COUNT(nullCount);
272 : return NO;
273 : }
274 :
275 : // If this is a (JS) Quaternion...
276 : private = JS_GetInstancePrivate(context, quaternionObj, &sQuaternionClass, NULL);
277 : if (EXPECT(private != NULL))
278 : {
279 : COUNT(quatCount);
280 : *outQuaternion = *private;
281 : return YES;
282 : }
283 :
284 : // If it's an array...
285 : if (EXPECT(JS_IsArrayObject(context, quaternionObj)))
286 : {
287 : // ...and it has exactly four elements...
288 : if (JS_GetArrayLength(context, quaternionObj, &arrayLength) && arrayLength == 4)
289 : {
290 : if (JS_LookupElement(context, quaternionObj, 0, &arrayW) &&
291 : JS_LookupElement(context, quaternionObj, 1, &arrayX) &&
292 : JS_LookupElement(context, quaternionObj, 2, &arrayY) &&
293 : JS_LookupElement(context, quaternionObj, 3, &arrayZ))
294 : {
295 : // ...se the four numbers as [w, x, y, z]
296 : if (!JS_ValueToNumber(context, arrayW, &dVal)) return NO;
297 : outQuaternion->w = dVal;
298 : if (!JS_ValueToNumber(context, arrayX, &dVal)) return NO;
299 : outQuaternion->x = dVal;
300 : if (!JS_ValueToNumber(context, arrayY, &dVal)) return NO;
301 : outQuaternion->y = dVal;
302 : if (!JS_ValueToNumber(context, arrayZ, &dVal)) return NO;
303 : outQuaternion->z = dVal;
304 :
305 : COUNT(arrayCount);
306 : return YES;
307 : }
308 : }
309 : }
310 :
311 : // If it's an entity, use its orientation.
312 : if (OOJSIsMemberOfSubclass(context, quaternionObj, JSEntityClass()))
313 : {
314 : COUNT(entityCount);
315 : Entity *entity = JS_GetPrivate(context, quaternionObj);
316 : *outQuaternion = [entity orientation];
317 : return YES;
318 : }
319 :
320 : /*
321 : If it's actually a Quaternion but with no private field (this happens for
322 : Quaternion.prototype)...
323 :
324 : NOTE: it would be prettier to do this at the top when we handle normal
325 : Quaternions, but it's a rare case which should be kept off the fast path.
326 : */
327 : if (JS_InstanceOf(context, quaternionObj, &sQuaternionClass, NULL))
328 : {
329 : COUNT(protoCount);
330 : *outQuaternion = kZeroQuaternion;
331 : return YES;
332 : }
333 :
334 : COUNT(failCount);
335 : return NO;
336 :
337 : OOJS_PROFILE_EXIT
338 : }
339 :
340 :
341 0 : static BOOL GetThisQuaternion(JSContext *context, JSObject *quaternionObj, Quaternion *outQuaternion, NSString *method)
342 : {
343 : if (EXPECT(JSObjectGetQuaternion(context, quaternionObj, outQuaternion))) return YES;
344 :
345 : jsval arg = OBJECT_TO_JSVAL(quaternionObj);
346 : OOJSReportBadArguments(context, @"Quaternion", method, 1, &arg, @"Invalid target object", @"Quaternion");
347 : return NO;
348 : }
349 :
350 :
351 0 : BOOL JSQuaternionSetQuaternion(JSContext *context, JSObject *quaternionObj, Quaternion quaternion)
352 : {
353 : OOJS_PROFILE_ENTER
354 :
355 : Quaternion *private = NULL;
356 :
357 : assert(quaternionObj != NULL);
358 :
359 : private = JS_GetInstancePrivate(context, quaternionObj, &sQuaternionClass, NULL);
360 : if (private != NULL) // If this is a (JS) Quaternion...
361 : {
362 : *private = quaternion;
363 : return YES;
364 : }
365 :
366 : if (JS_InstanceOf(context, quaternionObj, &sQuaternionClass, NULL))
367 : {
368 : // Silently fail for the prototype.
369 : return YES;
370 : }
371 :
372 : return NO;
373 :
374 : OOJS_PROFILE_EXIT
375 : }
376 :
377 :
378 0 : static BOOL QuaternionFromArgumentListNoErrorInternal(JSContext *context, uintN argc, jsval *argv, Quaternion *outQuaternion, uintN *outConsumed, BOOL permitNumberList)
379 : {
380 : OOJS_PROFILE_ENTER
381 :
382 : double w, x, y, z;
383 :
384 : if (EXPECT_NOT(argc == 0)) return NO;
385 : assert(argv != NULL && outQuaternion != NULL);
386 :
387 : if (outConsumed != NULL) *outConsumed = 0;
388 :
389 : // Is first object a quaternion or entity?
390 : if (JSVAL_IS_OBJECT(argv[0]))
391 : {
392 : if (JSObjectGetQuaternion(context, JSVAL_TO_OBJECT(argv[0]), outQuaternion))
393 : {
394 : if (outConsumed != NULL) *outConsumed = 1;
395 : return YES;
396 : }
397 : }
398 :
399 : if (!permitNumberList) return NO;
400 :
401 : // As a special case for QuaternionConstruct(), look for four numbers.
402 : if (EXPECT_NOT(argc < 4)) return NO;
403 :
404 : // Given a string, JS_ValueToNumber() returns YES but provides a NaN number.
405 : if (EXPECT_NOT(!JS_ValueToNumber(context, argv[0], &w) || isnan(w))) return NO;
406 : if (EXPECT_NOT(!JS_ValueToNumber(context, argv[1], &x) || isnan(x))) return NO;
407 : if (EXPECT_NOT(!JS_ValueToNumber(context, argv[2], &y) || isnan(y))) return NO;
408 : if (EXPECT_NOT(!JS_ValueToNumber(context, argv[3], &z) || isnan(z))) return NO;
409 :
410 : // We got our four numbers.
411 : *outQuaternion = make_quaternion(w, x, y, z);
412 : if (outConsumed != NULL) *outConsumed = 4;
413 :
414 : return YES;
415 :
416 : OOJS_PROFILE_EXIT
417 : }
418 :
419 :
420 : // EMMSTRAN: remove outConsumed, since it can only be 1 except in failure (constructor is an exception, but it uses QuaternionFromArgumentListNoErrorInternal() directly).
421 0 : BOOL QuaternionFromArgumentList(JSContext *context, NSString *scriptClass, NSString *function, uintN argc, jsval *argv, Quaternion *outQuaternion, uintN *outConsumed)
422 : {
423 : if (QuaternionFromArgumentListNoErrorInternal(context, argc, argv, outQuaternion, outConsumed, NO)) return YES;
424 : else
425 : {
426 : OOJSReportBadArguments(context, scriptClass, function, argc, argv,
427 : @"Could not construct quaternion from parameters",
428 : @"Quaternion, Entity or four numbers");
429 : return NO;
430 : }
431 : }
432 :
433 :
434 0 : BOOL QuaternionFromArgumentListNoError(JSContext *context, uintN argc, jsval *argv, Quaternion *outQuaternion, uintN *outConsumed)
435 : {
436 : return QuaternionFromArgumentListNoErrorInternal(context, argc, argv, outQuaternion, outConsumed, NO);
437 : }
438 :
439 :
440 : // *** Implementation stuff ***
441 :
442 0 : static JSBool QuaternionGetProperty(JSContext *context, JSObject *this, jsid propID, jsval *value)
443 : {
444 : if (!JSID_IS_INT(propID)) return YES;
445 :
446 : OOJS_PROFILE_ENTER
447 :
448 : Quaternion quaternion;
449 : GLfloat fValue;
450 :
451 : if (EXPECT_NOT(!JSObjectGetQuaternion(context, this, &quaternion))) return NO;
452 :
453 : switch (JSID_TO_INT(propID))
454 : {
455 : case kQuaternion_w:
456 : fValue = quaternion.w;
457 : break;
458 :
459 : case kQuaternion_x:
460 : fValue = quaternion.x;
461 : break;
462 :
463 : case kQuaternion_y:
464 : fValue = quaternion.y;
465 : break;
466 :
467 : case kQuaternion_z:
468 : fValue = quaternion.z;
469 : break;
470 :
471 : default:
472 : OOJSReportBadPropertySelector(context, this, propID, sQuaternionProperties);
473 : return NO;
474 : }
475 :
476 : return JS_NewNumberValue(context, fValue, value);
477 :
478 : OOJS_PROFILE_EXIT
479 : }
480 :
481 :
482 0 : static JSBool QuaternionSetProperty(JSContext *context, JSObject *this, jsid propID, JSBool strict, jsval *value)
483 : {
484 : if (!JSID_IS_INT(propID)) return YES;
485 :
486 : OOJS_PROFILE_ENTER
487 :
488 : Quaternion quaternion;
489 : jsdouble dval;
490 :
491 : if (EXPECT_NOT(!JSObjectGetQuaternion(context, this, &quaternion))) return NO;
492 : if (EXPECT_NOT(!JS_ValueToNumber(context, *value, &dval)))
493 : {
494 : OOJSReportBadPropertyValue(context, this, propID, sQuaternionProperties, *value);
495 : return NO;
496 : }
497 :
498 : switch (JSID_TO_INT(propID))
499 : {
500 : case kQuaternion_w:
501 : quaternion.w = dval;
502 : break;
503 :
504 : case kQuaternion_x:
505 : quaternion.x = dval;
506 : break;
507 :
508 : case kQuaternion_y:
509 : quaternion.y = dval;
510 : break;
511 :
512 : case kQuaternion_z:
513 : quaternion.z = dval;
514 : break;
515 :
516 : default:
517 : OOJSReportBadPropertySelector(context, this, propID, sQuaternionProperties);
518 : return NO;
519 : }
520 :
521 : return JSQuaternionSetQuaternion(context, this, quaternion);
522 :
523 : OOJS_PROFILE_EXIT
524 : }
525 :
526 :
527 0 : static void QuaternionFinalize(JSContext *context, JSObject *this)
528 : {
529 : Quaternion *private = NULL;
530 :
531 : private = JS_GetInstancePrivate(context, this, &sQuaternionClass, NULL);
532 : if (private != NULL)
533 : {
534 : free(private);
535 : }
536 : }
537 :
538 :
539 0 : static JSBool QuaternionConstruct(JSContext *context, uintN argc, jsval *vp)
540 : {
541 : OOJS_PROFILE_ENTER
542 :
543 : Quaternion quaternion = kIdentityQuaternion;
544 : Quaternion *private = NULL;
545 : JSObject *this = NULL;
546 :
547 : private = malloc(sizeof *private);
548 : if (EXPECT_NOT(private == NULL)) return NO;
549 :
550 : this = JS_NewObject(context, &sQuaternionClass, NULL, NULL);
551 : if (EXPECT_NOT(this == NULL)) return NO;
552 :
553 : if (argc != 0)
554 : {
555 : if (EXPECT_NOT(!QuaternionFromArgumentListNoErrorInternal(context, argc, OOJS_ARGV, &quaternion, NULL, YES)))
556 : {
557 : free(private);
558 : OOJSReportBadArguments(context, NULL, NULL, argc, OOJS_ARGV,
559 : @"Could not construct quaternion from parameters",
560 : @"Quaternion, Entity or array of four numbers");
561 : return NO;
562 : }
563 : }
564 :
565 : *private = quaternion;
566 :
567 : if (!JS_SetPrivate(context, this, private))
568 : {
569 : free(private);
570 : return NO;
571 : }
572 :
573 : OOJS_RETURN_JSOBJECT(this);
574 :
575 : OOJS_PROFILE_EXIT
576 : }
577 :
578 :
579 : // *** Methods ***
580 :
581 : // toString() : String
582 0 : static JSBool QuaternionToString(JSContext *context, uintN argc, jsval *vp)
583 : {
584 : OOJS_NATIVE_ENTER(context)
585 :
586 : Quaternion thisq;
587 :
588 : if (EXPECT_NOT(!GetThisQuaternion(context, OOJS_THIS, &thisq, @"toString"))) return NO;
589 :
590 : OOJS_RETURN_OBJECT(QuaternionDescription(thisq));
591 :
592 : OOJS_NATIVE_EXIT
593 : }
594 :
595 :
596 : // toSource() : String
597 0 : static JSBool QuaternionToSource(JSContext *context, uintN argc, jsval *vp)
598 : {
599 : OOJS_NATIVE_ENTER(context)
600 :
601 : Quaternion thisq;
602 :
603 : if (EXPECT_NOT(!GetThisQuaternion(context, OOJS_THIS, &thisq, @"toSource"))) return NO;
604 :
605 : NSString *str = [NSString stringWithFormat:@"Quaternion(%g, %g, %g, %g)", thisq.w, thisq.x, thisq.y, thisq.z];
606 : OOJS_RETURN_OBJECT(str);
607 :
608 : OOJS_NATIVE_EXIT
609 : }
610 :
611 :
612 : // multiply(q : quaternionExpression) : Quaternion
613 0 : static JSBool QuaternionMultiply(JSContext *context, uintN argc, jsval *vp)
614 : {
615 : OOJS_PROFILE_ENTER
616 :
617 : Quaternion thisq, thatq, result;
618 :
619 : if (EXPECT_NOT(!GetThisQuaternion(context, OOJS_THIS, &thisq, @"multiply"))) return NO;
620 : if (EXPECT_NOT(!QuaternionFromArgumentList(context, @"Quaternion", @"multiply", argc, OOJS_ARGV, &thatq, NULL))) return NO;
621 :
622 : result = quaternion_multiply(thisq, thatq);
623 :
624 : OOJS_RETURN_QUATERNION(result);
625 :
626 : OOJS_PROFILE_EXIT
627 : }
628 :
629 :
630 : // dot(q : quaternionExpression) : Number
631 0 : static JSBool QuaternionDot(JSContext *context, uintN argc, jsval *vp)
632 : {
633 : OOJS_PROFILE_ENTER
634 :
635 : Quaternion thisq, thatq;
636 : OOScalar result;
637 :
638 : if (EXPECT_NOT(!GetThisQuaternion(context, OOJS_THIS, &thisq, @"dot"))) return NO;
639 : if (EXPECT_NOT(!QuaternionFromArgumentList(context, @"Quaternion", @"dot", argc, OOJS_ARGV, &thatq, NULL))) return NO;
640 :
641 : result = quaternion_dot_product(thisq, thatq);
642 :
643 : OOJS_RETURN_DOUBLE(result);
644 :
645 : OOJS_PROFILE_EXIT
646 : }
647 :
648 :
649 : // rotate(axis : vectorExpression, angle : Number) : Quaternion
650 0 : static JSBool QuaternionRotate(JSContext *context, uintN argc, jsval *vp)
651 : {
652 : OOJS_PROFILE_ENTER
653 :
654 : Quaternion thisq;
655 : HPVector axis;
656 : double angle;
657 : uintN consumed;
658 : jsval *argv = OOJS_ARGV;
659 :
660 : if (EXPECT_NOT(!GetThisQuaternion(context, OOJS_THIS, &thisq, @"rotate"))) return NO;
661 : if (EXPECT_NOT(!VectorFromArgumentList(context, @"Quaternion", @"rotate", argc, argv, &axis, &consumed))) return NO;
662 : argv += consumed;
663 : argc -= consumed;
664 : if (argc > 0)
665 : {
666 : if (EXPECT_NOT(!OOJSArgumentListGetNumber(context, @"Quaternion", @"rotate", argc, argv, &angle, NULL))) return NO;
667 : quaternion_rotate_about_axis(&thisq, HPVectorToVector(axis), angle);
668 : }
669 : // Else no angle specified, so don't rotate and pass value through unchanged.
670 :
671 : OOJS_RETURN_QUATERNION(thisq);
672 :
673 : OOJS_PROFILE_EXIT
674 : }
675 :
676 :
677 : // rotateX(angle : Number) : Quaternion
678 0 : static JSBool QuaternionRotateX(JSContext *context, uintN argc, jsval *vp)
679 : {
680 : OOJS_PROFILE_ENTER
681 :
682 : Quaternion quat;
683 : double angle;
684 :
685 : if (EXPECT_NOT(!GetThisQuaternion(context, OOJS_THIS, &quat, @"rotateX"))) return NO;
686 : if (EXPECT_NOT(!OOJSArgumentListGetNumber(context, @"Quaternion", @"rotateX", argc, OOJS_ARGV, &angle, NULL))) return NO;
687 :
688 : quaternion_rotate_about_x(&quat, angle);
689 :
690 : OOJS_RETURN_QUATERNION(quat);
691 :
692 : OOJS_PROFILE_EXIT
693 : }
694 :
695 :
696 : // rotateY(angle : Number) : Quaternion
697 0 : static JSBool QuaternionRotateY(JSContext *context, uintN argc, jsval *vp)
698 : {
699 : OOJS_PROFILE_ENTER
700 :
701 : Quaternion quat;
702 : double angle;
703 :
704 : if (EXPECT_NOT(!GetThisQuaternion(context, OOJS_THIS, &quat, @"rotateY"))) return NO;
705 : if (EXPECT_NOT(!OOJSArgumentListGetNumber(context, @"Quaternion", @"rotateY", argc, OOJS_ARGV, &angle, NULL))) return NO;
706 :
707 : quaternion_rotate_about_y(&quat, angle);
708 :
709 : OOJS_RETURN_QUATERNION(quat);
710 :
711 : OOJS_PROFILE_EXIT
712 : }
713 :
714 :
715 : // rotateZ(angle : Number) : Quaternion
716 0 : static JSBool QuaternionRotateZ(JSContext *context, uintN argc, jsval *vp)
717 : {
718 : OOJS_PROFILE_ENTER
719 :
720 : Quaternion quat;
721 : double angle;
722 :
723 : if (EXPECT_NOT(!GetThisQuaternion(context, OOJS_THIS, &quat, @"rotateZ"))) return NO;
724 : if (EXPECT_NOT(!OOJSArgumentListGetNumber(context, @"Quaternion", @"rotateZ", argc, OOJS_ARGV, &angle, NULL))) return NO;
725 :
726 : quaternion_rotate_about_z(&quat, angle);
727 :
728 : OOJS_RETURN_QUATERNION(quat);
729 :
730 : OOJS_PROFILE_EXIT
731 : }
732 :
733 :
734 : // normalize() : Quaternion
735 0 : static JSBool QuaternionNormalize(JSContext *context, uintN argc, jsval *vp)
736 : {
737 : OOJS_PROFILE_ENTER
738 :
739 : Quaternion quat;
740 :
741 : if (EXPECT_NOT(!GetThisQuaternion(context, OOJS_THIS, &quat, @"normalize"))) return NO;
742 :
743 : quaternion_normalize(&quat);
744 :
745 : OOJS_RETURN_QUATERNION(quat);
746 :
747 : OOJS_PROFILE_EXIT
748 : }
749 :
750 :
751 : // conjugate() : Quaternion
752 0 : static JSBool QuaternionConjugate(JSContext *context, uintN argc, jsval *vp)
753 : {
754 : OOJS_PROFILE_ENTER
755 :
756 : Quaternion quat, result;
757 :
758 : if (EXPECT_NOT(!GetThisQuaternion(context, OOJS_THIS, &quat, @"conjugate"))) return NO;
759 :
760 : result = quaternion_conjugate(quat);
761 :
762 : OOJS_RETURN_QUATERNION(result);
763 :
764 : OOJS_PROFILE_EXIT
765 : }
766 :
767 :
768 : // vectorForward() : Vector
769 0 : static JSBool QuaternionVectorForward(JSContext *context, uintN argc, jsval *vp)
770 : {
771 : OOJS_PROFILE_ENTER
772 :
773 : Quaternion thisq;
774 : Vector result;
775 :
776 : if (EXPECT_NOT(!GetThisQuaternion(context, OOJS_THIS, &thisq, @"vectorForward"))) return NO;
777 :
778 : result = vector_forward_from_quaternion(thisq);
779 :
780 : OOJS_RETURN_VECTOR(result);
781 :
782 : OOJS_PROFILE_EXIT
783 : }
784 :
785 :
786 : // vectorUp() : Vector
787 0 : static JSBool QuaternionVectorUp(JSContext *context, uintN argc, jsval *vp)
788 : {
789 : OOJS_PROFILE_ENTER
790 :
791 : Quaternion thisq;
792 : Vector result;
793 :
794 : if (EXPECT_NOT(!GetThisQuaternion(context, OOJS_THIS, &thisq, @"vectorUp"))) return NO;
795 :
796 : result = vector_up_from_quaternion(thisq);
797 :
798 : OOJS_RETURN_VECTOR(result);
799 :
800 : OOJS_PROFILE_EXIT
801 : }
802 :
803 :
804 : // vectorRight() : Vector
805 0 : static JSBool QuaternionVectorRight(JSContext *context, uintN argc, jsval *vp)
806 : {
807 : OOJS_PROFILE_ENTER
808 :
809 : Quaternion thisq;
810 : Vector result;
811 :
812 : if (EXPECT_NOT(!GetThisQuaternion(context, OOJS_THIS, &thisq, @"vectorRight"))) return NO;
813 :
814 : result = vector_right_from_quaternion(thisq);
815 :
816 : OOJS_RETURN_VECTOR(result);
817 :
818 : OOJS_PROFILE_EXIT
819 : }
820 :
821 :
822 : // toArray() : Array
823 0 : static JSBool QuaternionToArray(JSContext *context, uintN argc, jsval *vp)
824 : {
825 : OOJS_PROFILE_ENTER
826 :
827 : Quaternion thisq;
828 : JSObject *result = NULL;
829 : BOOL OK = YES;
830 : jsval nVal;
831 :
832 : if (EXPECT_NOT(!GetThisQuaternion(context, OOJS_THIS, &thisq, @"toArray"))) return NO;
833 :
834 : result = JS_NewArrayObject(context, 0, NULL);
835 : if (result != NULL)
836 : {
837 : // We do this at the top because *outResult is a GC root.
838 : OOJS_SET_RVAL(OBJECT_TO_JSVAL(result));
839 :
840 : if (JS_NewNumberValue(context, thisq.w, &nVal)) JS_SetElement(context, result, 0, &nVal);
841 : else OK = NO;
842 : if (JS_NewNumberValue(context, thisq.x, &nVal)) JS_SetElement(context, result, 1, &nVal);
843 : else OK = NO;
844 : if (JS_NewNumberValue(context, thisq.y, &nVal)) JS_SetElement(context, result, 2, &nVal);
845 : else OK = NO;
846 : if (JS_NewNumberValue(context, thisq.z, &nVal)) JS_SetElement(context, result, 3, &nVal);
847 : else OK = NO;
848 : }
849 :
850 : if (!OK) OOJS_SET_RVAL(JSVAL_VOID);
851 : return YES;
852 :
853 : OOJS_PROFILE_EXIT
854 : }
855 :
856 :
857 : // *** Static methods ***
858 :
859 : // random() : Quaternion
860 0 : static JSBool QuaternionStaticRandom(JSContext *context, uintN argc, jsval *vp)
861 : {
862 : OOJS_PROFILE_ENTER
863 :
864 : OOJS_RETURN_QUATERNION(OORandomQuaternion());
865 :
866 : OOJS_PROFILE_EXIT
867 : }
|