1#X3D V3.0 utf8
2PROFILE Interchange
3
4# works at least with InstantPlayer, freewrl
5#
6# A VRML Proto to account the data for a PositionInterpolator and a
7# OrientationInterpolator from NurbsCurve data by scripting
8# (slow)
9# Copyright (C) 2006, 2019 J. "MUFTI" Scheurich
10#
11# Developed from NodeNurbsSurface.cpp of the vrml97 editor dune
12# Copyright (C) 1999 Stephen F. White
13#
14# exampe use
15#
16# EXTERNPROTO CurveAnimation
17#    [
18#    input      SFFloat    set_fraction
19#    outputOnly     SFVec3f    position_changed
20#    outputOnly     SFRotation orientation_changed
21#    inputoutputOnly MFVec3f    controlPoint
22#    inputoutputOnly SFInt32    tessellation
23#    inputoutputOnly MFFloat    weight
24#    field        MFFloat    knot
25#    field        SFInt32    order
26#    inputoutputOnly SFVec3f    rotationAxis
27#    inputoutputOnly SFBool     enableRotation
28#    inputoutputOnly SFBool     hover
29#    ]
30# [
31# "CurveAnimationPROTO.wrl"
32# ]
33#
34#
35# This program is free software; you can redistribute it and/or modify
36# it under the terms of the GNU General Public License as published by
37# the Free Software Foundation; either version 2 of the License, or
38# (at your option) any later version.
39#
40# This program is distributed in the hope that it will be useful,
41# but WITHOUT ANY WARRANTY; without even the implied warranty of
42# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
43# GNU General Public License for more details.
44#
45# You should have received a copy of the GNU General Public License
46# along with this program (see the file "COPYING" for details); if
47# not, write to the Free Software Foundation, Inc., 675 Mass Ave,
48# Cambridge, MA 02139, USA.
49#
50PROTO CurveAnimation
51   [
52   inputOnly      SFFloat    set_fraction
53   outputOnly     SFVec3f    position_changed
54   outputOnly     SFRotation orientation_changed
55   inputOutput MFVec3f    controlPoint          []
56   inputOutput SFInt32    tessellation          0      # (-∞,∞)
57   inputOutput MFFloat    weight                []     # (0,∞)
58   field        MFFloat    knot                 []     # (-∞,∞)
59   field        SFInt32    order                3      # [2,∞)
60   inputOutput SFVec3f    rotationAxis          0 1 0
61   inputOutput SFBool     enableRotation        TRUE
62   inputOutput SFBool     hover                 TRUE
63   ]
64{
65Group
66  {
67  children
68    [
69    DEF POSITION_INTERPOLATOR PositionInterpolator
70      {
71      set_fraction IS set_fraction
72      value_changed IS position_changed
73      }
74    DEF ORIENTATION_INTERPOLATOR OrientationInterpolator
75      {
76      set_fraction IS set_fraction
77      value_changed IS orientation_changed
78      }
79    ]
80  }
81
82DEF NURBS_SCRIPT Script
83  {
84  directOutput TRUE
85
86  inputOnly SFFloat set_fraction IS set_fraction
87  outputOnly SFFloat set_fraction_out
88
89  inputOutput MFVec3f controlPoint IS controlPoint
90  inputOutput SFInt32 tessellation IS tessellation
91  inputOutput MFFloat weight IS weight
92
93  field MFFloat knot IS knot
94  field SFInt32 order IS order
95
96  inputOutput SFVec3f rotationAxis IS rotationAxis
97
98  field SFBool enableRotation TRUE
99
100  outputOnly MFFloat positionKey
101  outputOnly MFVec3f positionKeyValue
102
103  outputOnly MFFloat orientationKey
104  outputOnly MFRotation orientationKeyValue
105
106  field SFNode positionInterpolator USE POSITION_INTERPOLATOR
107  field SFNode orientationInterpolator USE ORIENTATION_INTERPOLATOR
108
109  field MFFloat weights []
110  field MFVec3f tess []
111  field MFFloat w []
112  field MFVec2f tc []
113  field MFInt32 ci []
114
115  field MFFloat basis []
116  field MFFloat deriv []
117
118  field SFVec3f s 0 0 0
119  field SFVec3f u 0 0 0
120
121  field SFVec3f n 0 0 0
122
123  field MFFloat left []
124  field MFFloat right []
125
126  field SFInt32 dimension 0
127  url
128    [
129    "javascript:
130
131    function findSpan(dimension, order, u, knots) {
132       var low;
133       var mid;
134       var high;
135       var n;
136
137       n = dimension + order - 1;
138
139       if (u >= knots[n]) {
140           return n - order;
141       }
142       low = order - 1;
143       high = n - order + 1;
144
145       mid = Math.floor((low + high) / 2);
146
147       while ((u < knots[mid]) || (u >= knots[mid+1])) {
148           if (u < knots[mid])
149               high = mid;
150           else
151               low = mid;
152           mid = Math.floor((low + high) / 2);
153       }
154
155       return Math.floor(mid);
156    }
157
158    function basisFuns(span, u, order, knots, basis, deriv) {
159       var j;
160       var saved;
161       var dsaved;
162       var r;
163       var temp;
164
165       basis[0] = 1.0;
166       for (j = 1; j < order; j++) {
167           left[j] = u - knots[span+1-j];
168           right[j] = knots[span+j]-u;
169           saved = 0.0;
170           dsaved = 0.0;
171           for (r = 0; r < j; r++) {
172               temp = basis[r] / (right[r+1] + left[j-r]);
173               basis[r] = saved + right[r+1] * temp;
174               deriv[r] = dsaved - j * temp;
175               saved = left[j-r] * temp;
176               dsaved = j * temp;
177           }
178	   basis[j] = saved;
179	   deriv[j] = dsaved;
180       }
181    }
182
183
184    function linePoint(weight,u,ind) {
185       var i;
186       var j;
187
188       var span;
189
190       var base;
191
192       var index;
193
194       var w;
195       var dw;
196
197       var gain;
198       var dgain;
199
200       span = findSpan(dimension, order, u, knot);
201
202       basisFuns(span, u, order, knot, basis, deriv);
203
204       base = span-order+1;
205
206       index = base;
207
208       s=new SFVec3f(0.0, 0.0, 0.0);
209       w = 0.0;
210       dw = 0.0;
211       for (i = 0; i < order; i++) {
212           gain = basis[i];
213           dgain = deriv[i];
214           s.x += controlPoint[index].x * gain;
215           s.y += controlPoint[index].y * gain;
216           s.z += controlPoint[index].z * gain;
217           w += weight[index] * gain;
218           index++;
219       }
220       s.x = s.x / w;
221       s.y = s.y / w;
222       s.z = s.z / w;
223
224       return s;
225    }
226
227
228    function newQuat(x, y, z, a) {
229       var r;
230       r = new MFFloat();
231       r[0] = x;
232       r[1] = y;
233       r[2] = z;
234       r[3] = a;
235       return r;
236    }
237
238    function quatNorm(quat) {
239       var rlen;
240       rlen = Math.sqrt(quat[0] * quat[0] + quat[1] * quat[1] +
241                        quat[2] * quat[2] + quat[3] * quat[3]);
242       if (rlen > 0.000001)
243          rlen = 1.0 / rlen;
244       else
245          rlen = 1.0;
246       quat[0] *= rlen;
247       quat[1] *= rlen;
248       quat[2] *= rlen;
249       quat[3] *= rlen;
250       return quat;
251    }
252
253    function quatMult(q1, q2) {
254       var r;
255       r = newQuat(0, 0, 1, 0);
256       r[0] = q2[3] * q1[0] + q2[0] * q1[3] + q2[1] * q1[2] - q2[2] * q1[1];
257       r[1] = q2[3] * q1[1] + q2[1] * q1[3] + q2[2] * q1[0] - q2[0] * q1[2];
258       r[2] = q2[3] * q1[2] + q2[2] * q1[3] + q2[0] * q1[1] - q2[1] * q1[0];
259       r[3] = q2[3] * q1[3] - q2[0] * q1[0] - q2[1] * q1[1] - q2[2] * q1[2];
260       r = quatNorm(r);
261       return r;
262    }
263
264    function accountAngle(vec1, vec2) {
265       var vec1len;
266       var vec2len;
267       var sinangle;
268       var cosangle;
269       var angle;
270
271       vec1len = vec1.length();
272       vec2len = vec2.length();
273       if ((vec1len == 0) || (vec2len == 0))
274          return 0;
275       sinangle = vec1.cross(vec2).length() / vec1len / vec2len;
276       cosangle = vec1.dot(vec2) / vec1len / vec2len;
277       if (sinangle < 1e-9)
278          return 0;
279       angle = Math.atan2(sinangle, cosangle);
280
281       return angle;
282    }
283
284
285    function accountProjection(vector, normal) {
286       var angle;
287       var mult;
288
289       angle = accountAngle(vector, normal);
290       mult = vector.length() * Math.cos(angle);
291       return new SFVec3f(vector.x - normal.x * mult,
292                          vector.y - normal.y * mult,
293                          vector.z - normal.z * mult);
294    }
295
296    function accountQuat(vec1, vec2) {
297       var rotAxis;
298       var axis;
299       var axisLength;
300       var rot;
301       var angle;
302       var sinAngle;
303
304       rotAxis = vec1.cross(vec2);
305       axisLength = rotAxis.length();
306       axis = new MFFloat();
307       if (axisLength < 0.0000001) {
308          axis[0] = 0;
309          axis[1] = 0;
310          axis[2] = 0;
311       } else {
312          axis[0] = rotAxis.x / axisLength;
313          axis[1] = rotAxis.y / axisLength;
314          axis[2] = rotAxis.z / axisLength;
315       }
316       angle = accountAngle(vec1, vec2);
317       sinAngle = Math.sin(0.5 * angle);
318       rot = newQuat(axis[0] * sinAngle,
319                     axis[1] * sinAngle,
320                     axis[2] * sinAngle,
321                     Math.cos(0.5 * angle));
322       rot = quatNorm(rot);
323       return rot;
324    }
325
326    function sendToOrientationInterpolator() {
327       var i;
328       var j;
329       var chainLength;
330       var chainRot;
331       var oldQuat;
332       var correctionX;
333       var correctionY;
334       var normal;
335       var vector1;
336       var point3;
337       var point2;
338       var vector2;
339       var vec1;
340       var vec2
341       var quat;
342       var rlen;
343       var axis;
344       var rot;
345       var flipAxis;
346       var flip;
347
348       if (tess.length == 0)
349          makeChain();
350
351       chainLength = tess.length;
352       if (chainLength < 1)
353          return;
354       chainRot = new MFFloat(chainLength * 4);
355       correctionX = newQuat(1, 0, 0, Math.cos(0.5 * Math.PI));
356       correctionY = newQuat(0, 1, 0, Math.cos(0.5 * Math.PI));
357       oldQuat = newQuat(0, 0, 1, 0);
358       quat = newQuat(0, 0, 1, 0);
359       for (j = 0; j < (chainLength - 1); j++) {
360          normal = rotationAxis;
361          vector1 = new SFVec3f(0, 0, 1);
362          point3 = new SFVec3f (tess[j + 1].x,  tess[j + 1].y, tess[j + 1].z);
363          point2 = new SFVec3f (tess[j    ].x,  tess[j    ].y, tess[j    ].z);
364          if (j > 0) {
365             point1 = new SFVec3f(tess[j - 1].x,  tess[j - 1].y, tess[j - 1].z);
366             vector1 = new SFVec3f(point2.x - point1.x,
367                                   point2.y - point1.y,
368                                   point2.z - point1.z);
369          }
370          vector2 = new SFVec3f(point3.x - point2.x,
371                                point3.y - point2.y,
372                                point3.z - point2.z);
373          vec1 = accountProjection(vector1, normal);
374          vec2 = accountProjection(vector2, normal);
375          quat = accountQuat(vec1, vec2);
376          quat = quatMult(quat, oldQuat);
377          if (j == 0) {
378             quat = quatMult(quat, correctionY);
379             quat = quatMult(quat, correctionX);
380          }
381          rlen = Math.sqrt(quat[0] * quat[0] + quat[1] * quat[1] +
382                           quat[2] * quat[2]);
383          axis = new SFVec3f(quat[0], quat[1], quat[2]);
384          axis.normalize();
385          rot = new SFRotation(axis, 2.0 * Math.acos(quat[3]));
386          flipAxis = new SFVec3f(0, 1, 0);
387          flip = new SFRotation(flipAxis, 3.141592);
388          rot = rot.multiply(flip);
389          for (i = 0; i < 4; i++)
390             chainRot[j * 4 + i] = rot[i];
391          oldQuat = quat;
392       }
393
394       if (chainLength > 0) {
395          oldQuat[1] *= -1;
396          for (i = 0; i < 4; i++)
397             chainRot[(chainLength - 1) * 4 + i] =  oldQuat[i];
398       }
399       orientationInterpolator.key = new MFFloat();
400       orientationInterpolator.keyValue = new MFRotation();
401       for (i = 0; i < tess.length; i++) {
402           if (tess.length == 1)
403               orientationKey[i] = 0;
404           else
405               orientationKey[i] = (1.0 / (tess.length - 1)) * i;
406           orientationKeyValue[i].x = chainRot[i * 4];
407           orientationKeyValue[i].y = chainRot[i * 4 + 1];
408           orientationKeyValue[i].z = chainRot[i * 4 + 2];
409           orientationKeyValue[i].angle = chainRot[i * 4 + 3];
410
411       }
412    }
413
414    function sendToPositionInterpolator() {
415       positionKey = new MFFloat();
416       positionKeyValue = new MFVec3f();
417       for (i = 0; i < tess.length; i++) {
418           if (tess.length == 1)
419               positionKey[i] = 0;
420           else
421               positionKey[i] = (1.0 / (tess.length - 1)) * i;
422           positionKeyValue[i].x = tess[i].x;
423           positionKeyValue[i].y = tess[i].y;
424           positionKeyValue[i].z = tess[i].z;
425       }
426    }
427
428    function makeChain() {
429       var size;
430       var i;
431       var j;
432       var index;
433       var u;
434       var inv;
435       var inc;
436       var uTess;
437
438       index=0;
439
440       weights = new MFFloat();
441
442       dimension = controlPoint.length;
443
444       if (dimension == 0) return;
445
446       if (knot.length != order + dimension) {
447           print('no NurbsCurve: knot.length!=order+dimension');
448           return;
449       }
450
451       if (weight.length == 0) {
452          weights = new MFFloat();
453          for (i = 0; i < controlPoint.length; i++) {
454               weights[i] = 1.0;
455          }
456       } else if (weight.length != controlPoint.length) {
457           print('no NurbsCurve: weight.length!=controlPoint.length');
458           return;
459       }
460
461       uTess=tessellation;
462
463       if (uTess <= 0) uTess = 32;
464
465       tess = new MFVec3f();
466
467       size = (uTess + 1);
468
469       if (knot.length != 0)
470           inc = (knot[knot.length-1] - knot[0]) / uTess;
471       else
472           print('knot.length = 0 ');
473
474       w = (weight.length == 0) ? weights : weight;
475       u = knot[0];
476
477       for (i = 0; i <= uTess; i++) {
478           tess[i] = linePoint(w,u,i);
479           index++;
480           u += inc;
481       }
482    }
483
484    function initialize() {
485       makeChain();
486       sendToPositionInterpolator();
487       sendToOrientationInterpolator();
488    }
489
490    function set_fraction(value, time) {
491    }
492
493    function controlPoint(value, time) {
494       initialize();
495    }
496
497    function tessellation(value, time) {
498       initialize();
499    }
500
501    function weight(value, time) {
502       initialize();
503    }
504
505    function rotationAxis(value, time) {
506       initialize();
507    }
508    "
509    ]
510  }
511  ROUTE NURBS_SCRIPT.positionKey TO POSITION_INTERPOLATOR.key
512  ROUTE NURBS_SCRIPT.positionKeyValue TO POSITION_INTERPOLATOR.keyValue
513
514  ROUTE NURBS_SCRIPT.orientationKey TO ORIENTATION_INTERPOLATOR.key
515  ROUTE NURBS_SCRIPT.orientationKeyValue TO ORIENTATION_INTERPOLATOR.keyValue
516}
517
518