Line data Source code
1 0 : /*
2 :
3 : OOJSVector.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 "OOJSVector.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 "OOJSQuaternion.h"
37 :
38 :
39 0 : static JSObject *sVectorPrototype;
40 :
41 :
42 : static BOOL GetThisVector(JSContext *context, JSObject *vectorObj, HPVector *outVector, NSString *method) NONNULL_FUNC;
43 :
44 :
45 : static JSBool VectorGetProperty(JSContext *context, JSObject *this, jsid propID, jsval *value);
46 : static JSBool VectorSetProperty(JSContext *context, JSObject *this, jsid propID, JSBool strict, jsval *value);
47 : static void VectorFinalize(JSContext *context, JSObject *this);
48 : static JSBool VectorConstruct(JSContext *context, uintN argc, jsval *vp);
49 :
50 : // Methods
51 : static JSBool VectorToString(JSContext *context, uintN argc, jsval *vp);
52 : static JSBool VectorToSource(JSContext *context, uintN argc, jsval *vp);
53 : static JSBool VectorAdd(JSContext *context, uintN argc, jsval *vp);
54 : static JSBool VectorSubtract(JSContext *context, uintN argc, jsval *vp);
55 : static JSBool VectorDistanceTo(JSContext *context, uintN argc, jsval *vp);
56 : static JSBool VectorSquaredDistanceTo(JSContext *context, uintN argc, jsval *vp);
57 : static JSBool VectorMultiply(JSContext *context, uintN argc, jsval *vp);
58 : static JSBool VectorDot(JSContext *context, uintN argc, jsval *vp);
59 : static JSBool VectorAngleTo(JSContext *context, uintN argc, jsval *vp);
60 : static JSBool VectorFromCoordinateSystem(JSContext *context, uintN argc, jsval *vp);
61 : static JSBool VectorToCoordinateSystem(JSContext *context, uintN argc, jsval *vp);
62 : static JSBool VectorCross(JSContext *context, uintN argc, jsval *vp);
63 : static JSBool VectorTripleProduct(JSContext *context, uintN argc, jsval *vp);
64 : static JSBool VectorDirection(JSContext *context, uintN argc, jsval *vp);
65 : static JSBool VectorMagnitude(JSContext *context, uintN argc, jsval *vp);
66 : static JSBool VectorSquaredMagnitude(JSContext *context, uintN argc, jsval *vp);
67 : static JSBool VectorRotationTo(JSContext *context, uintN argc, jsval *vp);
68 : static JSBool VectorRotateBy(JSContext *context, uintN argc, jsval *vp);
69 : static JSBool VectorToArray(JSContext *context, uintN argc, jsval *vp);
70 :
71 : // Static methods
72 : static JSBool VectorStaticInterpolate(JSContext *context, uintN argc, jsval *vp);
73 : static JSBool VectorStaticRandom(JSContext *context, uintN argc, jsval *vp);
74 : static JSBool VectorStaticRandomDirection(JSContext *context, uintN argc, jsval *vp);
75 : static JSBool VectorStaticRandomDirectionAndLength(JSContext *context, uintN argc, jsval *vp);
76 :
77 :
78 0 : static JSClass sVectorClass =
79 : {
80 : "Vector3D",
81 : JSCLASS_HAS_PRIVATE,
82 :
83 : JS_PropertyStub, // addProperty
84 : JS_PropertyStub, // delProperty
85 : VectorGetProperty, // getProperty
86 : VectorSetProperty, // setProperty
87 : JS_EnumerateStub, // enumerate
88 : JS_ResolveStub, // resolve
89 : JS_ConvertStub, // convert
90 : VectorFinalize, // finalize
91 : JSCLASS_NO_OPTIONAL_MEMBERS
92 : };
93 :
94 :
95 0 : enum
96 : {
97 : // Property IDs
98 : kVector_x,
99 : kVector_y,
100 : kVector_z
101 : };
102 :
103 :
104 0 : static JSPropertySpec sVectorProperties[] =
105 : {
106 : // JS name ID flags
107 : { "x", kVector_x, OOJS_PROP_READWRITE_CB },
108 : { "y", kVector_y, OOJS_PROP_READWRITE_CB },
109 : { "z", kVector_z, OOJS_PROP_READWRITE_CB },
110 : { 0 }
111 : };
112 :
113 :
114 0 : static JSFunctionSpec sVectorMethods[] =
115 : {
116 : // JS name Function min args
117 : { "toSource", VectorToSource, 0, },
118 : { "toString", VectorToString, 0, },
119 : { "add", VectorAdd, 1, },
120 : { "angleTo", VectorAngleTo, 1, },
121 : { "cross", VectorCross, 1, },
122 : { "direction", VectorDirection, 0, },
123 : { "distanceTo", VectorDistanceTo, 1, },
124 : { "dot", VectorDot, 1, },
125 : { "fromCoordinateSystem", VectorFromCoordinateSystem, 1, },
126 : { "magnitude", VectorMagnitude, 0, },
127 : { "multiply", VectorMultiply, 1, },
128 : { "rotateBy", VectorRotateBy, 1, },
129 : { "rotationTo", VectorRotationTo, 1, },
130 : { "squaredDistanceTo", VectorSquaredDistanceTo, 1, },
131 : { "squaredMagnitude", VectorSquaredMagnitude, 0, },
132 : { "subtract", VectorSubtract, 1, },
133 : { "toArray", VectorToArray, 0, },
134 : { "toCoordinateSystem", VectorToCoordinateSystem, 1, },
135 : { "tripleProduct", VectorTripleProduct, 2, },
136 : { 0 }
137 : };
138 :
139 :
140 0 : static JSFunctionSpec sVectorStaticMethods[] =
141 : {
142 : // JS name Function min args
143 : { "interpolate", VectorStaticInterpolate, 3, },
144 : { "random", VectorStaticRandom, 0, },
145 : { "randomDirection", VectorStaticRandomDirection, 0, },
146 : { "randomDirectionAndLength", VectorStaticRandomDirectionAndLength, 0, },
147 : { 0 }
148 : };
149 :
150 :
151 : // *** Public ***
152 :
153 0 : void InitOOJSVector(JSContext *context, JSObject *global)
154 : {
155 : sVectorPrototype = JS_InitClass(context, global, NULL, &sVectorClass, VectorConstruct, 0, sVectorProperties, sVectorMethods, NULL, sVectorStaticMethods);
156 : }
157 :
158 :
159 0 : JSObject *JSVectorWithVector(JSContext *context, Vector vector)
160 : {
161 : OOJS_PROFILE_ENTER
162 :
163 : JSObject *result = NULL;
164 : HPVector *private = NULL;
165 :
166 : private = malloc(sizeof *private);
167 : if (EXPECT_NOT(private == NULL)) return NULL;
168 :
169 : *private = vectorToHPVector(vector);
170 :
171 : result = JS_NewObject(context, &sVectorClass, sVectorPrototype, NULL);
172 : if (result != NULL)
173 : {
174 : if (EXPECT_NOT(!JS_SetPrivate(context, result, private))) result = NULL;
175 : }
176 :
177 : if (EXPECT_NOT(result == NULL)) free(private);
178 :
179 : return result;
180 :
181 : OOJS_PROFILE_EXIT
182 : }
183 :
184 :
185 0 : BOOL VectorToJSValue(JSContext *context, Vector vector, jsval *outValue)
186 : {
187 : OOJS_PROFILE_ENTER
188 :
189 : JSObject *object = NULL;
190 :
191 : assert(outValue != NULL);
192 :
193 : object = JSVectorWithVector(context, vector);
194 : if (EXPECT_NOT(object == NULL)) return NO;
195 :
196 : *outValue = OBJECT_TO_JSVAL(object);
197 : return YES;
198 :
199 : OOJS_PROFILE_EXIT
200 : }
201 :
202 0 : JSObject *JSVectorWithHPVector(JSContext *context, HPVector vector)
203 : {
204 : OOJS_PROFILE_ENTER
205 :
206 : JSObject *result = NULL;
207 : HPVector *private = NULL;
208 :
209 : private = malloc(sizeof *private);
210 : if (EXPECT_NOT(private == NULL)) return NULL;
211 :
212 : *private = vector;
213 :
214 : result = JS_NewObject(context, &sVectorClass, sVectorPrototype, NULL);
215 : if (result != NULL)
216 : {
217 : if (EXPECT_NOT(!JS_SetPrivate(context, result, private))) result = NULL;
218 : }
219 :
220 : if (EXPECT_NOT(result == NULL)) free(private);
221 :
222 : return result;
223 :
224 : OOJS_PROFILE_EXIT
225 : }
226 :
227 :
228 0 : BOOL HPVectorToJSValue(JSContext *context, HPVector vector, jsval *outValue)
229 : {
230 : OOJS_PROFILE_ENTER
231 :
232 : JSObject *object = NULL;
233 :
234 : assert(outValue != NULL);
235 :
236 : object = JSVectorWithHPVector(context, vector);
237 : if (EXPECT_NOT(object == NULL)) return NO;
238 :
239 : *outValue = OBJECT_TO_JSVAL(object);
240 : return YES;
241 :
242 : OOJS_PROFILE_EXIT
243 : }
244 :
245 :
246 0 : BOOL NSPointToVectorJSValue(JSContext *context, NSPoint point, jsval *outValue)
247 : {
248 : return VectorToJSValue(context, make_vector(point.x, point.y, 0), outValue);
249 : }
250 :
251 :
252 0 : BOOL JSValueToHPVector(JSContext *context, jsval value, HPVector *outVector)
253 : {
254 : if (EXPECT_NOT(!JSVAL_IS_OBJECT(value))) return NO;
255 :
256 : return JSObjectGetVector(context, JSVAL_TO_OBJECT(value), outVector);
257 : }
258 :
259 0 : BOOL JSValueToVector(JSContext *context, jsval value, Vector *outVector)
260 : {
261 : if (EXPECT_NOT(!JSVAL_IS_OBJECT(value))) return NO;
262 : HPVector tmp = kZeroHPVector;
263 : BOOL result = JSObjectGetVector(context, JSVAL_TO_OBJECT(value), &tmp);
264 : *outVector = HPVectorToVector(tmp);
265 : return result;
266 : }
267 :
268 :
269 : #if OO_DEBUG
270 :
271 : typedef struct
272 : {
273 : NSUInteger vectorCount;
274 : NSUInteger entityCount;
275 : NSUInteger arrayCount;
276 : NSUInteger protoCount;
277 : NSUInteger nullCount;
278 : NSUInteger failCount;
279 : } VectorStatistics;
280 : static VectorStatistics sVectorConversionStats;
281 :
282 :
283 : @implementation PlayerEntity (JSVectorStatistics)
284 :
285 : // :setM vectorStats PS.callObjC("reportJSVectorStatistics")
286 : // :vectorStats
287 :
288 : - (NSString *) reportJSVectorStatistics
289 : {
290 : VectorStatistics *stats = &sVectorConversionStats;
291 :
292 : NSUInteger sum = stats->vectorCount + stats->entityCount + stats->arrayCount + stats->protoCount;
293 : double convFac = 100.0 / sum;
294 : if (sum == 0) convFac = 0;
295 :
296 : return [NSString stringWithFormat:
297 : @" vector-to-vector conversions: %lu (%g %%)\n"
298 : " entity-to-vector conversions: %lu (%g %%)\n"
299 : " array-to-vector conversions: %lu (%g %%)\n"
300 : "prototype-to-zero conversions: %lu (%g %%)\n"
301 : " null conversions: %lu (%g %%)\n"
302 : " failed conversions: %lu (%g %%)\n"
303 : " total: %lu",
304 : (long)stats->vectorCount, stats->vectorCount * convFac,
305 : (long)stats->entityCount, stats->entityCount * convFac,
306 : (long)stats->arrayCount, stats->arrayCount * convFac,
307 : (long)stats->protoCount, stats->protoCount * convFac,
308 : (long)stats->nullCount, stats->nullCount * convFac,
309 : (long)stats->failCount, stats->failCount * convFac,
310 : (long)sum];
311 : }
312 :
313 :
314 : - (void) clearJSVectorStatistics
315 : {
316 : memset(&sVectorConversionStats, 0, sizeof sVectorConversionStats);
317 : }
318 :
319 : @end
320 :
321 : #define COUNT(FIELD) do { sVectorConversionStats.FIELD++; } while (0)
322 :
323 : #else
324 :
325 0 : #define COUNT(FIELD) do {} while (0)
326 :
327 : #endif
328 :
329 :
330 0 : BOOL JSObjectGetVector(JSContext *context, JSObject *vectorObj, HPVector *outVector)
331 : {
332 : OOJS_PROFILE_ENTER
333 :
334 : assert(outVector != NULL);
335 :
336 : HPVector *private = NULL;
337 : jsuint arrayLength;
338 : jsval arrayX, arrayY, arrayZ;
339 : jsdouble x, y, z;
340 :
341 : // vectorObj can legitimately be NULL, e.g. when JS_NULL is converted to a JSObject *.
342 : if (EXPECT_NOT(vectorObj == NULL))
343 : {
344 : COUNT(nullCount);
345 : return NO;
346 : }
347 :
348 : // If this is a (JS) Vector...
349 : private = JS_GetInstancePrivate(context, vectorObj, &sVectorClass, NULL);
350 : if (EXPECT(private != NULL))
351 : {
352 : COUNT(vectorCount);
353 : *outVector = *private;
354 : return YES;
355 : }
356 :
357 : // If it's an array...
358 : if (EXPECT(JS_IsArrayObject(context, vectorObj)))
359 : {
360 : // ...and it has exactly three elements...
361 : if (JS_GetArrayLength(context, vectorObj, &arrayLength) && arrayLength == 3)
362 : {
363 : if (JS_LookupElement(context, vectorObj, 0, &arrayX) &&
364 : JS_LookupElement(context, vectorObj, 1, &arrayY) &&
365 : JS_LookupElement(context, vectorObj, 2, &arrayZ))
366 : {
367 : // ...use the three numbers as [x, y, z]
368 : if (JS_ValueToNumber(context, arrayX, &x) &&
369 : JS_ValueToNumber(context, arrayY, &y) &&
370 : JS_ValueToNumber(context, arrayZ, &z))
371 : {
372 : COUNT(arrayCount);
373 : *outVector = make_HPvector(x, y, z);
374 : return YES;
375 : }
376 : }
377 : }
378 : }
379 :
380 : // If it's an entity, use its position.
381 : if (OOJSIsMemberOfSubclass(context, vectorObj, JSEntityClass()))
382 : {
383 : COUNT(entityCount);
384 : Entity *entity = [(id)JS_GetPrivate(context, vectorObj) weakRefUnderlyingObject];
385 : *outVector = [entity position];
386 : return YES;
387 : }
388 :
389 : /*
390 : If it's actually a Vector3D but with no private field (this happens for
391 : Vector3D.prototype)...
392 :
393 : NOTE: it would be prettier to do this at the top when we handle normal
394 : Vector3Ds, but it's a rare case which should be kept off the fast path.
395 : */
396 : if (JS_InstanceOf(context, vectorObj, &sVectorClass, NULL))
397 : {
398 : COUNT(protoCount);
399 : *outVector = kZeroHPVector;
400 : return YES;
401 : }
402 :
403 : COUNT(failCount);
404 : return NO;
405 :
406 : OOJS_PROFILE_EXIT
407 : }
408 :
409 :
410 0 : static BOOL GetThisVector(JSContext *context, JSObject *vectorObj, HPVector *outVector, NSString *method)
411 : {
412 : if (EXPECT(JSObjectGetVector(context, vectorObj, outVector))) return YES;
413 :
414 : jsval arg = OBJECT_TO_JSVAL(vectorObj);
415 : OOJSReportBadArguments(context, @"Vector3D", method, 1, &arg, @"Invalid target object", @"Vector3D");
416 : return NO;
417 : }
418 :
419 :
420 0 : BOOL JSVectorSetVector(JSContext *context, JSObject *vectorObj, Vector vector)
421 : {
422 : return JSVectorSetHPVector(context,vectorObj,vectorToHPVector(vector));
423 : }
424 :
425 :
426 0 : BOOL JSVectorSetHPVector(JSContext *context, JSObject *vectorObj, HPVector vector)
427 : {
428 : OOJS_PROFILE_ENTER
429 :
430 : HPVector *private = NULL;
431 :
432 : if (EXPECT_NOT(vectorObj == NULL)) return NO;
433 :
434 : private = JS_GetInstancePrivate(context, vectorObj, &sVectorClass, NULL);
435 : if (private != NULL) // If this is a (JS) Vector...
436 : {
437 : *private = vector;
438 : return YES;
439 : }
440 :
441 : if (JS_InstanceOf(context, vectorObj, &sVectorClass, NULL))
442 : {
443 : // Silently fail for the prototype.
444 : return YES;
445 : }
446 :
447 : return NO;
448 :
449 : OOJS_PROFILE_EXIT
450 : }
451 :
452 :
453 0 : static BOOL VectorFromArgumentListNoErrorInternal(JSContext *context, uintN argc, jsval *argv, HPVector *outVector, uintN *outConsumed, BOOL permitNumberList)
454 : {
455 : OOJS_PROFILE_ENTER
456 :
457 : double x, y, z;
458 :
459 : if (EXPECT_NOT(argc == 0)) return NO;
460 : assert(argv != NULL && outVector != NULL);
461 :
462 : if (outConsumed != NULL) *outConsumed = 0;
463 :
464 : // Is first object a vector, array or entity?
465 : if (JSVAL_IS_OBJECT(argv[0]))
466 : {
467 : if (JSObjectGetVector(context, JSVAL_TO_OBJECT(argv[0]), outVector))
468 : {
469 : if (outConsumed != NULL) *outConsumed = 1;
470 : return YES;
471 : }
472 : }
473 :
474 : if (!permitNumberList) return NO;
475 :
476 : // As a special case for VectorConstruct(), look for three numbers.
477 : if (argc < 3) return NO;
478 :
479 : // Given a string, JS_ValueToNumber() returns YES but provides a NaN number.
480 : if (EXPECT_NOT(!JS_ValueToNumber(context, argv[0], &x) || isnan(x))) return NO;
481 : if (EXPECT_NOT(!JS_ValueToNumber(context, argv[1], &y) || isnan(y))) return NO;
482 : if (EXPECT_NOT(!JS_ValueToNumber(context, argv[2], &z) || isnan(z))) return NO;
483 :
484 : // We got our three numbers.
485 : *outVector = make_HPvector(x, y, z);
486 : if (outConsumed != NULL) *outConsumed = 3;
487 :
488 : return YES;
489 :
490 : OOJS_PROFILE_EXIT
491 : }
492 :
493 :
494 : // EMMSTRAN: remove outConsumed, since it can only be 1 except in failure (constructor is an exception, but it uses VectorFromArgumentListNoErrorInternal() directly).
495 0 : BOOL VectorFromArgumentList(JSContext *context, NSString *scriptClass, NSString *function, uintN argc, jsval *argv, HPVector *outVector, uintN *outConsumed)
496 : {
497 : if (VectorFromArgumentListNoErrorInternal(context, argc, argv, outVector, outConsumed, NO)) return YES;
498 : else
499 : {
500 : OOJSReportBadArguments(context, scriptClass, function, argc, argv,
501 : @"Could not construct vector from parameters",
502 : @"Vector, Entity or array of three numbers");
503 : return NO;
504 : }
505 : }
506 :
507 :
508 0 : BOOL VectorFromArgumentListNoError(JSContext *context, uintN argc, jsval *argv, HPVector *outVector, uintN *outConsumed)
509 : {
510 : return VectorFromArgumentListNoErrorInternal(context, argc, argv, outVector, outConsumed, NO);
511 : }
512 :
513 :
514 : // *** Implementation stuff ***
515 :
516 0 : static JSBool VectorGetProperty(JSContext *context, JSObject *this, jsid propID, jsval *value)
517 : {
518 : if (!JSID_IS_INT(propID)) return YES;
519 :
520 : OOJS_PROFILE_ENTER
521 :
522 : HPVector vector;
523 : OOHPScalar fValue;
524 :
525 : if (EXPECT_NOT(!JSObjectGetVector(context, this, &vector))) return NO;
526 :
527 : switch (JSID_TO_INT(propID))
528 : {
529 : case kVector_x:
530 : fValue = vector.x;
531 : break;
532 :
533 : case kVector_y:
534 : fValue = vector.y;
535 : break;
536 :
537 : case kVector_z:
538 : fValue = vector.z;
539 : break;
540 :
541 : default:
542 : OOJSReportBadPropertySelector(context, this, propID, sVectorProperties);
543 : return NO;
544 : }
545 :
546 : return JS_NewNumberValue(context, fValue, value);
547 :
548 : OOJS_PROFILE_EXIT
549 : }
550 :
551 :
552 0 : static JSBool VectorSetProperty(JSContext *context, JSObject *this, jsid propID, JSBool strict, jsval *value)
553 : {
554 : if (!JSID_IS_INT(propID)) return YES;
555 :
556 : OOJS_PROFILE_ENTER
557 :
558 : HPVector vector;
559 : jsdouble dval;
560 :
561 : if (EXPECT_NOT(!JSObjectGetVector(context, this, &vector))) return NO;
562 : if (EXPECT_NOT(!JS_ValueToNumber(context, *value, &dval)))
563 : {
564 : OOJSReportBadPropertyValue(context, this, propID, sVectorProperties, *value);
565 : return NO;
566 : }
567 :
568 : switch (JSID_TO_INT(propID))
569 : {
570 : case kVector_x:
571 : vector.x = dval;
572 : break;
573 :
574 : case kVector_y:
575 : vector.y = dval;
576 : break;
577 :
578 : case kVector_z:
579 : vector.z = dval;
580 : break;
581 :
582 : default:
583 : OOJSReportBadPropertySelector(context, this, propID, sVectorProperties);
584 : return NO;
585 : }
586 :
587 : return JSVectorSetHPVector(context, this, vector);
588 :
589 : OOJS_PROFILE_EXIT
590 : }
591 :
592 :
593 0 : static void VectorFinalize(JSContext *context, JSObject *this)
594 : {
595 : OOJS_PROFILE_ENTER
596 :
597 : Vector *private = NULL;
598 :
599 : private = JS_GetInstancePrivate(context, this, &sVectorClass, NULL);
600 : if (private != NULL)
601 : {
602 : free(private);
603 : }
604 :
605 : OOJS_PROFILE_EXIT_VOID
606 : }
607 :
608 :
609 0 : static JSBool VectorConstruct(JSContext *context, uintN argc, jsval *vp)
610 : {
611 : OOJS_PROFILE_ENTER
612 :
613 : HPVector vector = kZeroHPVector;
614 : HPVector *private = NULL;
615 : JSObject *this = NULL;
616 :
617 : private = malloc(sizeof *private);
618 : if (EXPECT_NOT(private == NULL)) return NO;
619 :
620 : this = JS_NewObject(context, &sVectorClass, NULL, NULL);
621 : if (EXPECT_NOT(this == NULL)) return NO;
622 :
623 : if (argc != 0)
624 : {
625 : if (EXPECT_NOT(!VectorFromArgumentListNoErrorInternal(context, argc, OOJS_ARGV, &vector, NULL, YES)))
626 : {
627 : free(private);
628 : OOJSReportBadArguments(context, NULL, NULL, argc, OOJS_ARGV,
629 : @"Could not construct vector from parameters",
630 : @"Vector, Entity or array of three numbers");
631 : return NO;
632 : }
633 : }
634 :
635 : *private = vector;
636 :
637 : if (EXPECT_NOT(!JS_SetPrivate(context, this, private)))
638 : {
639 : free(private);
640 : return NO;
641 : }
642 :
643 : OOJS_RETURN_JSOBJECT(this);
644 :
645 : OOJS_PROFILE_EXIT
646 : }
647 :
648 :
649 : // *** Methods ***
650 :
651 : // toString() : String
652 0 : static JSBool VectorToString(JSContext *context, uintN argc, jsval *vp)
653 : {
654 : OOJS_NATIVE_ENTER(context)
655 :
656 : HPVector thisv;
657 :
658 : if (EXPECT_NOT(!GetThisVector(context, OOJS_THIS, &thisv, @"toString"))) return NO;
659 :
660 : OOJS_RETURN_OBJECT(HPVectorDescription(thisv));
661 :
662 : OOJS_NATIVE_EXIT
663 : }
664 :
665 :
666 : // toSource() : String
667 0 : static JSBool VectorToSource(JSContext *context, uintN argc, jsval *vp)
668 : {
669 : OOJS_NATIVE_ENTER(context)
670 :
671 : HPVector thisv;
672 :
673 : if (EXPECT_NOT(!GetThisVector(context, OOJS_THIS, &thisv, @"toSource"))) return NO;
674 :
675 : NSString *str = [NSString stringWithFormat:@"Vector3D(%g, %g, %g)", thisv.x, thisv.y, thisv.z];
676 : OOJS_RETURN_OBJECT(str);
677 :
678 : OOJS_NATIVE_EXIT
679 : }
680 :
681 :
682 : // add(v : vectorExpression) : Vector3D
683 0 : static JSBool VectorAdd(JSContext *context, uintN argc, jsval *vp)
684 : {
685 : OOJS_PROFILE_ENTER
686 :
687 : HPVector thisv, thatv, result;
688 :
689 : if (EXPECT_NOT(!GetThisVector(context, OOJS_THIS, &thisv, @"add"))) return NO;
690 : if (EXPECT_NOT(!VectorFromArgumentList(context, @"Vector3D", @"add", argc, OOJS_ARGV, &thatv, NULL))) return NO;
691 :
692 : result = HPvector_add(thisv, thatv);
693 :
694 : OOJS_RETURN_HPVECTOR(result);
695 :
696 : OOJS_PROFILE_EXIT
697 : }
698 :
699 :
700 : // subtract(v : vectorExpression) : Vector3D
701 0 : static JSBool VectorSubtract(JSContext *context, uintN argc, jsval *vp)
702 : {
703 : OOJS_PROFILE_ENTER
704 :
705 : HPVector thisv, thatv, result;
706 :
707 : if (EXPECT_NOT(!GetThisVector(context, OOJS_THIS, &thisv, @"subtract"))) return NO;
708 : if (EXPECT_NOT(!VectorFromArgumentList(context, @"Vector3D", @"subtract", argc, OOJS_ARGV, &thatv, NULL))) return NO;
709 :
710 : result = HPvector_subtract(thisv, thatv);
711 :
712 : OOJS_RETURN_HPVECTOR(result);
713 :
714 : OOJS_PROFILE_EXIT
715 : }
716 :
717 :
718 : // distanceTo(v : vectorExpression) : Number
719 0 : static JSBool VectorDistanceTo(JSContext *context, uintN argc, jsval *vp)
720 : {
721 : OOJS_PROFILE_ENTER
722 :
723 : HPVector thisv, thatv;
724 : double result;
725 :
726 : if (EXPECT_NOT(!GetThisVector(context, OOJS_THIS, &thisv, @"distanceTo"))) return NO;
727 : if (EXPECT_NOT(!VectorFromArgumentList(context, @"Vector3D", @"distanceTo", argc, OOJS_ARGV, &thatv, NULL))) return NO;
728 :
729 : result = HPdistance(thisv, thatv);
730 :
731 : OOJS_RETURN_DOUBLE(result);
732 :
733 : OOJS_PROFILE_EXIT
734 : }
735 :
736 :
737 : // squaredDistanceTo(v : vectorExpression) : Number
738 0 : static JSBool VectorSquaredDistanceTo(JSContext *context, uintN argc, jsval *vp)
739 : {
740 : OOJS_PROFILE_ENTER
741 :
742 : HPVector thisv, thatv;
743 : double result;
744 :
745 : if (EXPECT_NOT(!GetThisVector(context, OOJS_THIS, &thisv, @"squaredDistanceTo"))) return NO;
746 : if (EXPECT_NOT(!VectorFromArgumentList(context, @"Vector3D", @"squaredDistanceTo", argc, OOJS_ARGV, &thatv, NULL))) return NO;
747 :
748 : result = HPdistance2(thisv, thatv);
749 :
750 : OOJS_RETURN_DOUBLE(result);
751 :
752 : OOJS_PROFILE_EXIT
753 : }
754 :
755 :
756 : // multiply(n : Number) : Vector3D
757 0 : static JSBool VectorMultiply(JSContext *context, uintN argc, jsval *vp)
758 : {
759 : OOJS_PROFILE_ENTER
760 :
761 : HPVector thisv, result;
762 : double scalar;
763 :
764 : if (EXPECT_NOT(!GetThisVector(context, OOJS_THIS, &thisv, @"multiply"))) return NO;
765 : if (EXPECT_NOT(!OOJSArgumentListGetNumber(context, @"Vector3D", @"multiply", argc, OOJS_ARGV, &scalar, NULL))) return NO;
766 :
767 : result = HPvector_multiply_scalar(thisv, scalar);
768 :
769 : OOJS_RETURN_HPVECTOR(result);
770 :
771 : OOJS_PROFILE_EXIT
772 : }
773 :
774 :
775 : // dot(v : vectorExpression) : Number
776 0 : static JSBool VectorDot(JSContext *context, uintN argc, jsval *vp)
777 : {
778 : OOJS_PROFILE_ENTER
779 :
780 : HPVector thisv, thatv;
781 : double result;
782 :
783 : if (EXPECT_NOT(!GetThisVector(context, OOJS_THIS, &thisv, @"dot"))) return NO;
784 : if (EXPECT_NOT(!VectorFromArgumentList(context, @"Vector3D", @"dot", argc, OOJS_ARGV, &thatv, NULL))) return NO;
785 :
786 : result = HPdot_product(thisv, thatv);
787 :
788 : OOJS_RETURN_DOUBLE(result);
789 :
790 : OOJS_PROFILE_EXIT
791 : }
792 :
793 :
794 : // angleTo(v : vectorExpression) : Number
795 0 : static JSBool VectorAngleTo(JSContext *context, uintN argc, jsval *vp)
796 : {
797 : OOJS_PROFILE_ENTER
798 :
799 : HPVector thisv, thatv;
800 : double result;
801 :
802 : if (EXPECT_NOT(!GetThisVector(context, OOJS_THIS, &thisv, @"angleTo"))) return NO;
803 : if (EXPECT_NOT(!VectorFromArgumentList(context, @"Vector3D", @"angleTo", argc, OOJS_ARGV, &thatv, NULL))) return NO;
804 :
805 : result = HPdot_product(HPvector_normal(thisv), HPvector_normal(thatv));
806 : if (result > 1.0) result = 1.0;
807 : if (result < -1.0) result = -1.0;
808 : // for identical vectors the dot_product sometimes returns a value > 1.0 because of rounding errors, resulting
809 : // in an undefined result for the acos.
810 : result = acos(result);
811 :
812 : OOJS_RETURN_DOUBLE(result);
813 :
814 : OOJS_PROFILE_EXIT
815 : }
816 :
817 :
818 : // cross(v : vectorExpression) : Vector3D
819 0 : static JSBool VectorCross(JSContext *context, uintN argc, jsval *vp)
820 : {
821 : OOJS_PROFILE_ENTER
822 :
823 : HPVector thisv, thatv, result;
824 :
825 : if (EXPECT_NOT(!GetThisVector(context, OOJS_THIS, &thisv, @"cross"))) return NO;
826 : if (EXPECT_NOT(!VectorFromArgumentList(context, @"Vector3D", @"cross", argc, OOJS_ARGV, &thatv, NULL))) return NO;
827 :
828 : result = HPtrue_cross_product(thisv, thatv);
829 :
830 : OOJS_RETURN_HPVECTOR(result);
831 :
832 : OOJS_PROFILE_EXIT
833 : }
834 :
835 :
836 : // tripleProduct(v : vectorExpression, u : vectorExpression) : Number
837 0 : static JSBool VectorTripleProduct(JSContext *context, uintN argc, jsval *vp)
838 : {
839 : OOJS_PROFILE_ENTER
840 :
841 : HPVector thisv, thatv, theotherv;
842 : double result;
843 : uintN consumed;
844 : jsval *argv = OOJS_ARGV;
845 :
846 : if (EXPECT_NOT(!GetThisVector(context, OOJS_THIS, &thisv, @"tripleProduct"))) return NO;
847 : if (EXPECT_NOT(!VectorFromArgumentList(context, @"Vector3D", @"tripleProduct", argc, argv, &thatv, &consumed))) return NO;
848 : argc -= consumed;
849 : argv += consumed;
850 : if (EXPECT_NOT(!VectorFromArgumentList(context, @"Vector3D", @"tripleProduct", argc, argv, &theotherv, NULL))) return NO;
851 :
852 : result = HPtriple_product(thisv, thatv, theotherv);
853 :
854 : OOJS_RETURN_DOUBLE(result);
855 :
856 : OOJS_PROFILE_EXIT
857 : }
858 :
859 :
860 : // direction() : Vector3D
861 0 : static JSBool VectorDirection(JSContext *context, uintN argc, jsval *vp)
862 : {
863 : OOJS_PROFILE_ENTER
864 :
865 : HPVector thisv, result;
866 :
867 : if (EXPECT_NOT(!GetThisVector(context, OOJS_THIS, &thisv, @"direction"))) return NO;
868 :
869 : result = HPvector_normal(thisv);
870 :
871 : OOJS_RETURN_HPVECTOR(result);
872 :
873 : OOJS_PROFILE_EXIT
874 : }
875 :
876 :
877 : // magnitude() : Number
878 0 : static JSBool VectorMagnitude(JSContext *context, uintN argc, jsval *vp)
879 : {
880 : OOJS_PROFILE_ENTER
881 :
882 : HPVector thisv;
883 : double result;
884 :
885 : if (EXPECT_NOT(!GetThisVector(context, OOJS_THIS, &thisv, @"magnitude"))) return NO;
886 :
887 : result = HPmagnitude(thisv);
888 :
889 : OOJS_RETURN_DOUBLE(result);
890 :
891 : OOJS_PROFILE_EXIT
892 : }
893 :
894 :
895 : // squaredMagnitude() : Number
896 0 : static JSBool VectorSquaredMagnitude(JSContext *context, uintN argc, jsval *vp)
897 : {
898 : OOJS_PROFILE_ENTER
899 :
900 : HPVector thisv;
901 : double result;
902 :
903 : if (EXPECT_NOT(!GetThisVector(context, OOJS_THIS, &thisv, @"squaredMagnitude"))) return NO;
904 :
905 : result = HPmagnitude2(thisv);
906 :
907 : OOJS_RETURN_DOUBLE(result);
908 :
909 : OOJS_PROFILE_EXIT
910 : }
911 :
912 :
913 : // rotationTo(v : vectorExpression [, limit : Number]) : Quaternion
914 0 : static JSBool VectorRotationTo(JSContext *context, uintN argc, jsval *vp)
915 : {
916 : OOJS_PROFILE_ENTER
917 :
918 : HPVector thisv, thatv;
919 : double limit;
920 : BOOL gotLimit;
921 : Quaternion result;
922 : uintN consumed;
923 : jsval *argv = OOJS_ARGV;
924 :
925 : if (EXPECT_NOT(!GetThisVector(context, OOJS_THIS, &thisv, @"rotationTo"))) return NO;
926 : if (EXPECT_NOT(!VectorFromArgumentList(context, @"Vector3D", @"rotationTo", argc, OOJS_ARGV, &thatv, &consumed))) return NO;
927 :
928 : argc -= consumed;
929 : argv += consumed;
930 : if (argc != 0) // limit parameter is optional.
931 : {
932 : if (EXPECT_NOT(!OOJSArgumentListGetNumber(context, @"Vector3D", @"rotationTo", argc, argv, &limit, NULL))) return NO;
933 : gotLimit = YES;
934 : }
935 : else gotLimit = NO;
936 :
937 : if (gotLimit) result = quaternion_limited_rotation_between(HPVectorToVector(thisv), HPVectorToVector(thatv), limit);
938 : else result = quaternion_rotation_between(HPVectorToVector(thisv), HPVectorToVector(thatv));
939 :
940 : OOJS_RETURN_QUATERNION(result);
941 :
942 : OOJS_PROFILE_EXIT
943 : }
944 :
945 :
946 : // rotateBy(q : quaternionExpression) : Vector3D
947 0 : static JSBool VectorRotateBy(JSContext *context, uintN argc, jsval *vp)
948 : {
949 : OOJS_PROFILE_ENTER
950 :
951 : HPVector thisv, result;
952 : Quaternion q;
953 :
954 : if (EXPECT_NOT(!GetThisVector(context, OOJS_THIS, &thisv, @"rotateBy"))) return NO;
955 : if (EXPECT_NOT(!QuaternionFromArgumentList(context, @"Vector3D", @"rotateBy", argc, OOJS_ARGV, &q, NULL))) return NO;
956 :
957 : result = quaternion_rotate_HPvector(q, thisv);
958 :
959 : OOJS_RETURN_HPVECTOR(result);
960 :
961 : OOJS_PROFILE_EXIT
962 : }
963 :
964 :
965 : // toArray() : Array
966 0 : static JSBool VectorToArray(JSContext *context, uintN argc, jsval *vp)
967 : {
968 : OOJS_PROFILE_ENTER
969 :
970 : HPVector thisv;
971 : JSObject *result = NULL;
972 : jsval nVal;
973 :
974 : if (EXPECT_NOT(!GetThisVector(context, OOJS_THIS, &thisv, @"toArray"))) return NO;
975 :
976 : result = JS_NewArrayObject(context, 0, NULL);
977 : if (result != NULL)
978 : {
979 : // We do this at the top because the return value slot is a GC root.
980 : OOJS_SET_RVAL(OBJECT_TO_JSVAL(result));
981 :
982 : if (JS_NewNumberValue(context, thisv.x, &nVal) && JS_SetElement(context, result, 0, &nVal) &&
983 : JS_NewNumberValue(context, thisv.y, &nVal) && JS_SetElement(context, result, 1, &nVal) &&
984 : JS_NewNumberValue(context, thisv.z, &nVal) && JS_SetElement(context, result, 2, &nVal))
985 : {
986 : return YES;
987 : }
988 : // If we get here, the conversion and stuffing in the previous condition failed.
989 : OOJS_SET_RVAL(JSVAL_VOID);
990 : }
991 :
992 : return YES;
993 :
994 : OOJS_PROFILE_EXIT
995 : }
996 :
997 :
998 : // toCoordinateSystem(coordScheme : String)
999 0 : static JSBool VectorToCoordinateSystem(JSContext *context, uintN argc, jsval *vp)
1000 : {
1001 : OOJS_NATIVE_ENTER(context)
1002 :
1003 : HPVector thisv;
1004 : NSString *coordScheme = nil;
1005 : HPVector result;
1006 :
1007 : if (EXPECT_NOT(!GetThisVector(context, OOJS_THIS, &thisv, @"toCoordinateSystem"))) return NO;
1008 :
1009 : if (EXPECT_NOT(argc < 1 ||
1010 : (coordScheme = OOStringFromJSValue(context, OOJS_ARGV[0])) == nil))
1011 : {
1012 : OOJSReportBadArguments(context, @"Vector3D", @"toCoordinateSystem", MIN(argc, 1U), OOJS_ARGV, nil, @"coordinate system");
1013 : return NO;
1014 : }
1015 :
1016 : OOJS_BEGIN_FULL_NATIVE(context)
1017 : result = [UNIVERSE legacyPositionFrom:thisv asCoordinateSystem:coordScheme];
1018 : OOJS_END_FULL_NATIVE
1019 :
1020 : OOJS_RETURN_HPVECTOR(result);
1021 :
1022 : OOJS_NATIVE_EXIT
1023 : }
1024 :
1025 :
1026 : // fromCoordinateSystem(coordScheme : String)
1027 0 : static JSBool VectorFromCoordinateSystem(JSContext *context, uintN argc, jsval *vp)
1028 : {
1029 : OOJS_NATIVE_ENTER(context)
1030 :
1031 : HPVector thisv;
1032 : NSString *coordScheme = nil;
1033 : HPVector result;
1034 :
1035 : if (EXPECT_NOT(!GetThisVector(context, OOJS_THIS, &thisv, @"fromCoordinateSystem"))) return NO;
1036 :
1037 : if (EXPECT_NOT(argc < 1 ||
1038 : (coordScheme = OOStringFromJSValue(context, OOJS_ARGV[0])) == nil))
1039 : {
1040 : OOJSReportBadArguments(context, @"Vector3D", @"fromCoordinateSystem", MIN(argc, 1U), OOJS_ARGV, nil, @"coordinate system");
1041 : return NO;
1042 : }
1043 :
1044 : OOJS_BEGIN_FULL_NATIVE(context)
1045 : NSString *arg = [NSString stringWithFormat:@"%@ %f %f %f", coordScheme, thisv.x, thisv.y, thisv.z];
1046 : result = [UNIVERSE coordinatesFromCoordinateSystemString:arg];
1047 : OOJS_END_FULL_NATIVE
1048 :
1049 : OOJS_RETURN_HPVECTOR(result);
1050 :
1051 : OOJS_NATIVE_EXIT
1052 : }
1053 :
1054 :
1055 : // *** Static methods ***
1056 :
1057 :
1058 : // interpolate(v : Vector3D, u : Vector3D, alpha : Number) : Vector3D
1059 0 : static JSBool VectorStaticInterpolate(JSContext *context, uintN argc, jsval *vp)
1060 : {
1061 : OOJS_PROFILE_ENTER
1062 :
1063 : HPVector av, bv;
1064 : double interp;
1065 : HPVector result;
1066 : uintN consumed;
1067 : uintN inArgc = argc;
1068 : jsval *argv = OOJS_ARGV;
1069 : jsval *inArgv = argv;
1070 :
1071 : if (EXPECT_NOT(argc < 3)) goto INSUFFICIENT_ARGUMENTS;
1072 : if (EXPECT_NOT(!VectorFromArgumentList(context, @"Vector3D", @"interpolate", argc, argv, &av, &consumed))) return NO;
1073 : argc -= consumed;
1074 : argv += consumed;
1075 : if (EXPECT_NOT(argc < 2)) goto INSUFFICIENT_ARGUMENTS;
1076 : if (EXPECT_NOT(!VectorFromArgumentList(context, @"Vector3D", @"interpolate", argc, argv, &bv, &consumed))) return NO;
1077 : argc -= consumed;
1078 : argv += consumed;
1079 : if (EXPECT_NOT(argc < 1)) goto INSUFFICIENT_ARGUMENTS;
1080 : if (EXPECT_NOT(!OOJSArgumentListGetNumber(context, @"Vector3D", @"interpolate", argc, argv, &interp, NULL))) return NO;
1081 :
1082 : result = OOHPVectorInterpolate(av, bv, interp);
1083 :
1084 : OOJS_RETURN_HPVECTOR(result);
1085 :
1086 : INSUFFICIENT_ARGUMENTS:
1087 : OOJSReportBadArguments(context, @"Vector3D", @"interpolate", inArgc, inArgv,
1088 : @"Insufficient parameters",
1089 : @"vector expression, vector expression and number");
1090 : return NO;
1091 :
1092 : OOJS_PROFILE_EXIT
1093 : }
1094 :
1095 :
1096 : // random([maxLength : Number]) : Vector3D
1097 0 : static JSBool VectorStaticRandom(JSContext *context, uintN argc, jsval *vp)
1098 : {
1099 : OOJS_PROFILE_ENTER
1100 :
1101 : double maxLength;
1102 :
1103 : if (argc == 0 || !OOJSArgumentListGetNumberNoError(context, argc, OOJS_ARGV, &maxLength, NULL)) maxLength = 1.0;
1104 :
1105 : OOJS_RETURN_HPVECTOR(OOHPVectorRandomSpatial(maxLength));
1106 :
1107 : OOJS_PROFILE_EXIT
1108 : }
1109 :
1110 :
1111 : // randomDirection([scale : Number]) : Vector3D
1112 0 : static JSBool VectorStaticRandomDirection(JSContext *context, uintN argc, jsval *vp)
1113 : {
1114 : OOJS_PROFILE_ENTER
1115 :
1116 : double scale;
1117 :
1118 : if (argc == 0 || !OOJSArgumentListGetNumberNoError(context, argc, OOJS_ARGV, &scale, NULL)) scale = 1.0;
1119 :
1120 : OOJS_RETURN_HPVECTOR(HPvector_multiply_scalar(OORandomUnitHPVector(), scale));
1121 :
1122 : OOJS_PROFILE_EXIT
1123 : }
1124 :
1125 :
1126 : // randomDirectionAndLength([maxLength : Number]) : Vector3D
1127 0 : static JSBool VectorStaticRandomDirectionAndLength(JSContext *context, uintN argc, jsval *vp)
1128 : {
1129 : OOJS_PROFILE_ENTER
1130 :
1131 : double maxLength;
1132 :
1133 : if (argc == 0 || !OOJSArgumentListGetNumberNoError(context, argc, OOJS_ARGV, &maxLength, NULL)) maxLength = 1.0;
1134 :
1135 : OOJS_RETURN_HPVECTOR(OOHPVectorRandomRadial(maxLength));
1136 :
1137 : OOJS_PROFILE_EXIT
1138 : }
1139 :
1140 :
|