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