1 #include <simgear/debug/logstream.hxx>
5 #include "Rotorpart.hpp"
6 #include "Rotorblade.hpp"
12 SG_USING_STD(setprecision);
19 const float pi=3.14159;
21 static inline float sqr(float a) { return a * a; }
37 _base[0] = _base[1] = _base[2] = 0;
44 _force_at_max_pitch=0;
47 _forward[1]=_forward[2]=0;
48 _max_pitch=13./180*pi;
49 _maxcyclicail=10./180*pi;
50 _maxcyclicele=10./180*pi;
52 _mincyclicail=-10./180*pi;
53 _mincyclicele=-10./180*pi;
54 _min_pitch=-.5/180*pi;
56 _normal[0] = _normal[1] = 0;
59 _omega=_omegan=_omegarel=0;
66 _rellenteeterhinge=0.01;
80 for(i=0; i<_rotorparts.size(); i++) {
81 Rotorpart* r = (Rotorpart*)_rotorparts.get(i);
84 for(i=0; i<_rotorblades.size(); i++) {
85 Rotorblade* r = (Rotorblade*)_rotorblades.get(i);
91 void Rotor::inititeration(float dt)
93 if ((_engineon)&&(_omegarel>=1)) return;
94 if ((!_engineon)&&(_omegarel<=0)) return;
95 _omegarel+=dt*1/30.*(_engineon?1:-1);
96 _omegarel=Math::clamp(_omegarel,0,1);
97 _omega=_omegan*_omegarel;
99 for(i=0; i<_rotorparts.size(); i++) {
100 Rotorpart* r = (Rotorpart*)_rotorparts.get(i);
103 for(i=0; i<_rotorblades.size(); i++) {
104 Rotorblade* r = (Rotorblade*)_rotorblades.get(i);
109 int Rotor::getValueforFGSet(int j,char *text,float *f)
111 if (_name[0]==0) return 0;
116 if (!numRotorblades()) return 0;
119 sprintf(text,"/rotors/%s/cone", _name);
121 *f=( ((Rotorblade*)getRotorblade(0))->getFlapatPos(0)
122 +((Rotorblade*)getRotorblade(0))->getFlapatPos(1)
123 +((Rotorblade*)getRotorblade(0))->getFlapatPos(2)
124 +((Rotorblade*)getRotorblade(0))->getFlapatPos(3)
131 sprintf(text,"/rotors/%s/roll", _name);
133 *f=( ((Rotorblade*)getRotorblade(0))->getFlapatPos(1)
134 -((Rotorblade*)getRotorblade(0))->getFlapatPos(3)
140 sprintf(text,"/rotors/%s/yaw", _name);
142 *f=( ((Rotorblade*)getRotorblade(0))->getFlapatPos(2)
143 -((Rotorblade*)getRotorblade(0))->getFlapatPos(0)
149 sprintf(text,"/rotors/%s/rpm", _name);
158 if (b>=numRotorblades()) return 0;
160 sprintf(text,"/rotors/%s/blade%i_%s",
162 w==0?"pos":(w==1?"flap":"incidence"));
163 if (w==0) *f=((Rotorblade*)getRotorblade(b))->getPhi()*180/pi;
164 else if (w==1) *f=((Rotorblade*) getRotorblade(b))->getrealAlpha()*180/pi;
165 else *f=((Rotorblade*)getRotorblade(b))->getIncidence()*180/pi;
171 if (4!=numRotorparts()) return 0; //compile first!
174 sprintf(text,"/rotors/%s/cone", _name);
175 *f=( ((Rotorpart*)getRotorpart(0))->getrealAlpha()
176 +((Rotorpart*)getRotorpart(1))->getrealAlpha()
177 +((Rotorpart*)getRotorpart(2))->getrealAlpha()
178 +((Rotorpart*)getRotorpart(3))->getrealAlpha()
184 sprintf(text,"/rotors/%s/roll", _name);
185 *f=( ((Rotorpart*)getRotorpart(0))->getrealAlpha()
186 -((Rotorpart*)getRotorpart(2))->getrealAlpha()
187 )/2*180/pi*(_ccw?-1:1);
192 sprintf(text,"/rotors/%s/yaw", _name);
193 *f=( ((Rotorpart*)getRotorpart(1))->getrealAlpha()
194 -((Rotorpart*)getRotorpart(3))->getrealAlpha()
200 sprintf(text,"/rotors/%s/rpm", _name);
207 if (b>=_number_of_blades) return 0;
209 sprintf(text,"/rotors/%s/blade%i_%s",
211 w==0?"pos":(w==1?"flap":"incidence"));
212 *f=((Rotorpart*)getRotorpart(0))->getPhi()*180/pi+360*b/_number_of_blades*(_ccw?1:-1);
223 rl=sqr(Math::sin(rl*pi/2));
224 rk=sqr(Math::sin(rk*pi/2));
226 if(w==2) {k+=2;l+=2;}
228 if(w==1) {k+=1;l+=1;}
231 if (w==1) *f=rk*((Rotorpart*) getRotorpart(k))->getrealAlpha()*180/pi
232 +rl*((Rotorpart*) getRotorpart(l))->getrealAlpha()*180/pi;
233 else if(w==2) *f=rk*((Rotorpart*)getRotorpart(k))->getIncidence()*180/pi
234 +rl*((Rotorpart*)getRotorpart(l))->getIncidence()*180/pi;
240 void Rotor::setEngineOn(int value)
245 void Rotor::setAlpha0(float f)
249 void Rotor::setAlphamin(float f)
253 void Rotor::setAlphamax(float f)
257 void Rotor::setAlpha0factor(float f)
263 int Rotor::numRotorparts()
265 return _rotorparts.size();
268 Rotorpart* Rotor::getRotorpart(int n)
270 return ((Rotorpart*)_rotorparts.get(n));
272 int Rotor::numRotorblades()
274 return _rotorblades.size();
277 Rotorblade* Rotor::getRotorblade(int n)
279 return ((Rotorblade*)_rotorblades.get(n));
282 void Rotor::strncpy(char *dest,const char *src,int maxlen)
285 while(src[n]&&n<(maxlen-1))
295 void Rotor::setNormal(float* normal)
298 float invsum,sqrsum=0;
299 for(i=0; i<3; i++) { sqrsum+=normal[i]*normal[i];}
302 invsum=1/Math::sqrt(sqrsum);
305 for(i=0; i<3; i++) { _normal[i] = normal[i]*invsum; }
308 void Rotor::setForward(float* forward)
311 float invsum,sqrsum=0;
312 for(i=0; i<3; i++) { sqrsum+=forward[i]*forward[i];}
315 invsum=1/Math::sqrt(sqrsum);
318 for(i=0; i<3; i++) { _forward[i] = forward[i]*invsum; }
322 void Rotor::setForceAtPitchA(float force)
324 _force_at_pitch_a=force;
326 void Rotor::setPowerAtPitch0(float value)
328 _power_at_pitch_0=value;
330 void Rotor::setPowerAtPitchB(float value)
332 _power_at_pitch_b=value;
334 void Rotor::setPitchA(float value)
336 _pitch_a=value/180*pi;
338 void Rotor::setPitchB(float value)
340 _pitch_b=value/180*pi;
342 void Rotor::setBase(float* base)
345 for(i=0; i<3; i++) _base[i] = base[i];
349 void Rotor::setMaxCyclicail(float value)
351 _maxcyclicail=value/180*pi;
353 void Rotor::setMaxCyclicele(float value)
355 _maxcyclicele=value/180*pi;
357 void Rotor::setMinCyclicail(float value)
359 _mincyclicail=value/180*pi;
361 void Rotor::setMinCyclicele(float value)
363 _mincyclicele=value/180*pi;
365 void Rotor::setMinCollective(float value)
367 _min_pitch=value/180*pi;
369 void Rotor::setMaxCollective(float value)
371 _max_pitch=value/180*pi;
373 void Rotor::setDiameter(float value)
377 void Rotor::setWeightPerBlade(float value)
379 _weight_per_blade=value;
381 void Rotor::setNumberOfBlades(float value)
383 _number_of_blades=int(value+.5);
385 void Rotor::setRelBladeCenter(float value)
387 _rel_blade_center=value;
389 void Rotor::setDynamic(float value)
393 void Rotor::setDelta3(float value)
397 void Rotor::setDelta(float value)
401 void Rotor::setTranslift(float value)
405 void Rotor::setC2(float value)
409 void Rotor::setStepspersecond(float steps)
411 _stepspersecond=steps;
413 void Rotor::setRPM(float value)
417 void Rotor::setRelLenHinge(float value)
419 _rel_len_hinge=value;
422 void Rotor::setAlphaoutput(int i, const char *text)
424 //printf("SetAlphaoutput %i [%s]\n",i,text);
425 strncpy(_alphaoutput[i],text,255);
427 void Rotor::setName(const char *text)
429 strncpy(_name,text,128);//128: some space needed for settings
432 void Rotor::setCcw(int ccw)
436 void Rotor::setNotorque(int value)
440 void Rotor::setSimBlades(int value)
445 void Rotor::setRelLenTeeterHinge(float f)
447 _rellenteeterhinge=f;
449 void Rotor::setTeeterdamp(float f)
453 void Rotor::setMaxteeterdamp(float f)
461 void Rotor::setCollective(float lval)
463 lval = Math::clamp(lval, -1, 1);
465 //printf("col: %5.3f\n",lval);
466 for(i=0; i<_rotorparts.size(); i++) {
467 ((Rotorpart*)_rotorparts.get(i))->setCollective(lval);
470 float col=_min_pitch+(lval+1)/2*(_max_pitch-_min_pitch);
471 for(i=0; i<_rotorblades.size(); i++) {
472 ((Rotorblade*)_rotorblades.get(i))->setCollective(col);
476 void Rotor::setCyclicele(float lval,float rval)
478 rval = Math::clamp(rval, -1, 1);
479 lval = Math::clamp(lval, -1, 1);
480 float col=_mincyclicele+(lval+1)/2*(_maxcyclicele-_mincyclicele);
482 for(i=0; i<_rotorblades.size(); i++) {
483 //((Rotorblade*)_rotorblades.get(i))->setCyclicele(col*Math::sin(((Rotorblade*)_rotorblades.get(i))->getPhi()));
484 ((Rotorblade*)_rotorblades.get(i))->setCyclicele(col);
486 if (_rotorparts.size()!=4) return;
487 //printf(" ele: %5.3f %5.3f\n",lval,rval);
488 ((Rotorpart*)_rotorparts.get(1))->setCyclic(lval);
489 ((Rotorpart*)_rotorparts.get(3))->setCyclic(-lval);
491 void Rotor::setCyclicail(float lval,float rval)
493 lval = Math::clamp(lval, -1, 1);
494 rval = Math::clamp(rval, -1, 1);
495 float col=_mincyclicail+(lval+1)/2*(_maxcyclicail-_mincyclicail);
497 for(i=0; i<_rotorblades.size(); i++) {
498 ((Rotorblade*)_rotorblades.get(i))->setCyclicail(col);
500 if (_rotorparts.size()!=4) return;
501 //printf("ail: %5.3f %5.3f\n",lval,rval);
503 ((Rotorpart*)_rotorparts.get(0))->setCyclic(-lval);
504 ((Rotorpart*)_rotorparts.get(2))->setCyclic( lval);
508 float Rotor::getGroundEffect(float* posOut)
512 for(i=0; i<3; i++) posOut[i] = _base[i];
513 float span = _length * Math::cos(_sweep) * Math::cos(_dihedral);
514 span = 2*(span + Math::abs(_base[2]));
519 void Rotor::compile()
521 // Have we already been compiled?
522 if(_rotorparts.size() != 0) return;
524 //rotor is divided into 4 pointlike parts
526 SG_LOG(SG_FLIGHT, SG_DEBUG, "debug: e "
527 << _mincyclicele << "..." <<_maxcyclicele << ' '
528 << _mincyclicail << "..." << _maxcyclicail << ' '
529 << _min_pitch << "..." << _max_pitch);
533 _dynamic=_dynamic*(1/ //inverse of the time
534 ( (60/_rotor_rpm)/4 //for rotating 90 deg
535 +(60/_rotor_rpm)/(2*_number_of_blades) //+ meantime a rotorblade will pass a given point
537 float directions[5][3];//pointing forward, right, ... the 5th is ony for calculation
538 directions[0][0]=_forward[0];
539 directions[0][1]=_forward[1];
540 directions[0][2]=_forward[2];
542 SG_LOG(SG_FLIGHT, SG_DEBUG, "Rotor rotating ccw? " << _ccw);
547 Math::cross3(directions[i-1],_normal,directions[i]);
549 Math::cross3(_normal,directions[i-1],directions[i]);
550 Math::unit3(directions[i],directions[i]);
552 Math::set3(directions[4],directions[0]);
553 float rotorpartmass = _weight_per_blade*_number_of_blades/4*.453;//was pounds -> now kg
554 float speed=_rotor_rpm/60*_diameter*_rel_blade_center*pi;
555 float lentocenter=_diameter*_rel_blade_center*0.5;
556 float lentoforceattac=_diameter*_rel_len_hinge*0.5;
557 float zentforce=rotorpartmass*speed*speed/lentocenter;
558 _force_at_max_pitch=_force_at_pitch_a/_pitch_a*_max_pitch;
559 float maxpitchforce=_force_at_max_pitch/4*.453*9.81;//was pounds of force, now N
560 float torque0=0,torquemax=0;
561 float omega=_rotor_rpm/60*2*pi;
563 float omega0=omega*Math::sqrt(1/(1-_rel_len_hinge));
564 //float omega0=omega*Math::sqrt((1-_rel_len_hinge));
565 //_delta=omega*_delta;
566 _delta*=maxpitchforce/(_max_pitch*omega*lentocenter*2*rotorpartmass);
568 float phi=Math::atan2(2*omega*_delta,omega0*omega0-omega*omega);
569 //float relamp=omega*omega/(2*_delta*Math::sqrt(omega0*omega0-_delta*_delta));
570 float relamp=omega*omega/(2*_delta*Math::sqrt(sqr(omega0*omega0-omega*omega)+4*_delta*_delta*omega*omega));
573 torque0=_power_at_pitch_0/4*1000/omega;
574 torquemax=_power_at_pitch_b/4*1000/omega/_pitch_b*_max_pitch;
584 SG_LOG(SG_FLIGHT, SG_DEBUG,
585 "spd: " << setprecision(8) << speed
586 << " lentoc: " << lentocenter
587 << " dia: " << _diameter
588 << " rbl: " << _rel_blade_center
589 << " hing: " << _rel_len_hinge
590 << " lfa: " << lentoforceattac);
591 SG_LOG(SG_FLIGHT, SG_DEBUG,
592 "zf: " << setprecision(8) << zentforce
593 << " mpf: " << maxpitchforce);
594 SG_LOG(SG_FLIGHT, SG_DEBUG,
595 "tq: " << setprecision(8) << torque0 << ".." << torquemax
596 << " d3: " << _delta3);
597 SG_LOG(SG_FLIGHT, SG_DEBUG,
598 "o/o0: " << setprecision(8) << omega/omega0
599 << " phi: " << phi*180/pi
600 << " relamp: " << relamp
601 << " delta: " <<_delta);
606 float lpos[3],lforceattac[3],lspeed[3],dirzentforce[3];
608 Math::mul3(lentocenter,directions[i],lpos);
609 Math::add3(lpos,_base,lpos);
610 Math::mul3(lentoforceattac,directions[i+1],lforceattac);//i+1: +90deg (gyro)!!!
611 Math::add3(lforceattac,_base,lforceattac);
612 Math::mul3(speed,directions[i+1],lspeed);
613 Math::mul3(1,directions[i+1],dirzentforce);
615 float maxcyclic=(i&1)?_maxcyclicele:_maxcyclicail;
616 float mincyclic=(i&1)?_mincyclicele:_mincyclicail;
619 Rotorpart* rp=rps[i]=newRotorpart(lpos, lforceattac, _normal,
620 lspeed,dirzentforce,zentforce,maxpitchforce, _max_pitch,_min_pitch,mincyclic,maxcyclic,
621 _delta3,rotorpartmass,_translift,_rel_len_hinge,lentocenter);
622 rp->setAlphaoutput(_alphaoutput[i&1?i:(_ccw?i^2:i)],0);
623 rp->setAlphaoutput(_alphaoutput[4+(i&1?i:(_ccw?i^2:i))],1+(i>1));
625 rp->setTorque(torquemax,torque0);
626 rp->setRelamp(relamp);
633 rps[i]->setlastnextrp(rps[(i+3)%4],rps[(i+1)%4],rps[(i+2)%4]);
638 float directions[5][3];//pointing forward, right, ... the 5th is ony for calculation
639 directions[0][0]=_forward[0];
640 directions[0][1]=_forward[1];
641 directions[0][2]=_forward[2];
643 SG_LOG(SG_FLIGHT, SG_DEBUG, "Rotor rotating ccw? " <<_ccw);
648 Math::cross3(directions[i-1],_normal,directions[i]);
650 // Math::cross3(_normal,directions[i-1],directions[i]);
651 Math::unit3(directions[i],directions[i]);
653 Math::set3(directions[4],directions[0]);
654 float speed=_rotor_rpm/60*_diameter*_rel_blade_center*pi;
655 float lentocenter=_diameter*_rel_blade_center*0.5;
656 float lentoforceattac=_diameter*_rel_len_hinge*0.5;
657 float zentforce=_weight_per_blade*.453*speed*speed/lentocenter;
658 _force_at_max_pitch=_force_at_pitch_a/_pitch_a*_max_pitch;
659 float maxpitchforce=_force_at_max_pitch/_number_of_blades*.453*9.81;//was pounds of force, now N
660 float torque0=0,torquemax=0;
661 float omega=_rotor_rpm/60*2*pi;
663 float omega0=omega*Math::sqrt(1/(1-_rel_len_hinge));
664 //float omega0=omega*Math::sqrt(1-_rel_len_hinge);
665 //_delta=omega*_delta;
666 _delta*=maxpitchforce/(_max_pitch*omega*lentocenter*2*_weight_per_blade*.453);
667 float phi=Math::atan2(2*omega*_delta,omega0*omega0-omega*omega);
668 // float phi2=Math::abs(omega0-omega)<.000000001?pi/2:Math::atan(2*omega*_delta/(omega0*omega0-omega*omega));
669 float relamp=omega*omega/(2*_delta*Math::sqrt(sqr(omega0*omega0-omega*omega)+4*_delta*_delta*omega*omega));
672 torque0=_power_at_pitch_0/_number_of_blades*1000/omega;
673 torquemax=_power_at_pitch_b/_number_of_blades*1000/omega/_pitch_b*_max_pitch;
683 SG_LOG(SG_FLIGHT, SG_DEBUG,
684 "spd: " << setprecision(8) << speed
685 << " lentoc: " << lentocenter
686 << " dia: " << _diameter
687 << " rbl: " << _rel_blade_center
688 << " hing: " << _rel_len_hinge
689 << " lfa: " << lentoforceattac);
690 SG_LOG(SG_FLIGHT, SG_DEBUG,
691 "zf: " << setprecision(8) << zentforce
692 << " mpf: " << maxpitchforce);
693 SG_LOG(SG_FLIGHT, SG_DEBUG,
694 "tq: " << setprecision(8) << torque0 << ".." << torquemax
695 << " d3: " << _delta3);
696 SG_LOG(SG_FLIGHT, SG_DEBUG,
697 "o/o0: " << setprecision(8) << omega/omega0
698 << " phi: " << phi*180/pi
699 << " relamp: " << relamp
700 << " delta: " <<_delta);
703 float dirzentforce[3];
705 float f=(!_ccw)?1:-1;
706 //Math::mul3(f*speed,directions[1],lspeed);
707 Math::mul3(f,directions[1],dirzentforce);
708 for (i=0;i<_number_of_blades;i++)
714 Rotorblade* rb=newRotorblade(_base,_normal,directions[0],directions[1],
715 lentoforceattac,_rel_len_hinge,
716 dirzentforce,zentforce,maxpitchforce, _max_pitch,
717 _delta3,_weight_per_blade*.453,_translift,2*pi/_number_of_blades*i,
718 omega,lentocenter,/*f* */speed);
719 //rp->setAlphaoutput(_alphaoutput[i&1?i:(_ccw?i^2:i)],0);
720 //rp->setAlphaoutput(_alphaoutput[4+(i&1?i:(_ccw?i^2:i))],1+(i>1));
721 _rotorblades.add(rb);
722 rb->setTorque(torquemax,torque0);
723 rb->setDeltaPhi(pi/2.-phi);
724 rb->setDelta(_delta);
726 rb->calcFrontRight();
733 rps[i]->setlastnextrp(rps[(i-1)%4],rps[(i+1)%4],rps[(i+2)%4]);
741 Rotorblade* Rotor::newRotorblade(float* pos, float *normal, float *front, float *right,
742 float lforceattac,float rellenhinge,
743 float *dirzentforce, float zentforce,float maxpitchforce,float maxpitch,
744 float delta3,float mass,float translift,float phi,float omega,float len,float speed)
746 Rotorblade *r = new Rotorblade();
748 r->setNormal(normal);
751 r->setMaxPitchForce(maxpitchforce);
752 r->setZentipetalForce(zentforce);
753 r->setAlpha0(_alpha0);
754 r->setAlphamin(_alphamin);
755 r->setAlphamax(_alphamax);
756 r->setAlpha0factor(_alpha0factor);
761 r->setDirectionofZentipetalforce(dirzentforce);
762 r->setMaxpitch(maxpitch);
763 r->setDelta3(delta3);
764 r->setDynamic(_dynamic);
765 r->setTranslift(_translift);
767 r->setStepspersecond(_stepspersecond);
771 r->setLforceattac(lforceattac);
773 r->setLenHinge(rellenhinge);
774 r->setRelLenTeeterHinge(_rellenteeterhinge);
775 r->setTeeterdamp(_teeterdamp);
776 r->setMaxteeterdamp(_maxteeterdamp);
779 #define a(x) x[0],x[1],x[2]
780 printf("newrp: pos(%5.3f %5.3f %5.3f) pfa (%5.3f %5.3f %5.3f)\n"
781 " nor(%5.3f %5.3f %5.3f) spd (%5.3f %5.3f %5.3f)\n"
782 " dzf(%5.3f %5.3f %5.3f) zf (%5.3f) mpf (%5.3f)\n"
783 " pit (%5.3f..%5.3f) mcy (%5.3f..%5.3f) d3 (%5.3f)\n"
784 ,a(pos),a(posforceattac),a(normal),
785 a(speed),a(dirzentforce),zentforce,maxpitchforce,minpitch,maxpitch,mincyclic,maxcyclic,
792 Rotorpart* Rotor::newRotorpart(float* pos, float *posforceattac, float *normal,
793 float* speed,float *dirzentforce, float zentforce,float maxpitchforce,
794 float maxpitch, float minpitch, float mincyclic,float maxcyclic,
795 float delta3,float mass,float translift,float rellenhinge,float len)
797 Rotorpart *r = new Rotorpart();
799 r->setNormal(normal);
800 r->setMaxPitchForce(maxpitchforce);
801 r->setZentipetalForce(zentforce);
803 r->setPositionForceAttac(posforceattac);
806 r->setDirectionofZentipetalforce(dirzentforce);
807 r->setMaxpitch(maxpitch);
808 r->setMinpitch(minpitch);
809 r->setMaxcyclic(maxcyclic);
810 r->setMincyclic(mincyclic);
811 r->setDelta3(delta3);
812 r->setDynamic(_dynamic);
813 r->setTranslift(_translift);
816 r->setRelLenHinge(rellenhinge);
817 r->setOmegaN(_omegan);
818 r->setAlpha0(_alpha0);
819 r->setAlphamin(_alphamin);
820 r->setAlphamax(_alphamax);
821 r->setAlpha0factor(_alpha0factor);
825 SG_LOG(SG_FLIGHT, SG_DEBUG, setprecision(8)
827 << pos[0] << ' ' << pos[1] << ' ' << pos[2]
829 << posforceattac[0] << ' ' << posforceattac[1] << ' '
830 << posforceattac[2] << ')');
831 SG_LOG(SG_FLIGHT, SG_DEBUG, setprecision(8)
833 << normal[0] << ' ' << normal[1] << ' ' << normal[2]
835 << speed[0] << ' ' << speed[1] << ' ' << speed[2] << ')');
836 SG_LOG(SG_FLIGHT, SG_DEBUG, setprecision(8)
838 << dirzentforce[0] << ' ' << dirzentforce[1] << dirzentforce[2]
839 << ") zf (" << zentforce << ") mpf (" << maxpitchforce << ')');
840 SG_LOG(SG_FLIGHT, SG_DEBUG, setprecision(8)
841 << " pit(" << minpitch << ".." << maxpitch
842 << ") mcy (" << mincyclic << ".." << maxcyclic
843 << ") d3 (" << delta3 << ')');
847 void Rotor::interp(float* v1, float* v2, float frac, float* out)
849 out[0] = v1[0] + frac*(v2[0]-v1[0]);
850 out[1] = v1[1] + frac*(v2[1]-v1[1]);
851 out[2] = v1[2] + frac*(v2[2]-v1[2]);
854 }; // namespace yasim