void PropPositioner::Update() { m_NeedsUpdate = false; m_TransformedCurve = m_Curve; Matrix4d mat; mat.scale( m_Chord ); if ( m_Reverse < 0 ) { mat.translatef( 1.0, 0.0, 0.0 ); mat.flipx(); } m_TransformedCurve.Transform( mat ); if ( !m_ParentProp ) { return; } // Basic transformation orients curve before other transformations. m_ParentProp->GetBasicTransformation( m_Chord, mat ); m_TransformedCurve.Transform( mat ); mat.loadIdentity(); // Propeller rotation first because order is reversed. mat.rotateX( -m_Reverse * m_PropRot ); mat.translatef( m_FoldOrigin.x(), m_FoldOrigin.y(), m_FoldOrigin.z() ); mat.rotate( m_FoldAngle * PI / 180.0, m_FoldDirection ); mat.translatef( -m_FoldOrigin.x(), -m_FoldOrigin.y(), -m_FoldOrigin.z() ); mat.rotateY( m_Reverse * m_Feather ); mat.translatef( 0, m_Radius, 0 ); mat.rotateY( m_Reverse * m_Twist ); mat.rotateX( m_XRotate ); // About rake direction mat.translatef( m_Rake, 0, m_Reverse * m_Skew ); mat.rotateZ( m_ZRotate ); // About chord m_TransformedCurve.Transform( mat ); }
void PropGeom::UpdateDrawObj() { GeomXSec::UpdateDrawObj(); double axlen = 1.0; Vehicle *veh = VehicleMgr.GetVehicle(); if ( veh ) { axlen = veh->m_AxisLength(); } double rev = 1.0; if ( m_ReverseFlag() ) { rev = -1.0; } double data[16]; m_ModelMatrix.getMat( data ); vec3d cen( 0, 0, 0 ); vec3d rotdir( -1, 0, 0 ); vec3d thrustdir( -1, 0, 0 ); rotdir = rotdir * rev; cen = m_ModelMatrix.xform( cen ); rotdir = m_ModelMatrix.xform( rotdir ) - cen; thrustdir = m_ModelMatrix.xform( thrustdir ) - cen; Matrix4d mat; mat.loadIdentity(); mat.rotateX( -rev * m_Rotate() ); mat.postMult( data ); vec3d pmid = mat.xform( m_FoldAxOrigin ); vec3d ptstart = mat.xform( m_FoldAxOrigin + m_FoldAxDirection * axlen / 2.0 ); vec3d ptend = mat.xform( m_FoldAxOrigin - m_FoldAxDirection * axlen / 2.0 ); vec3d dir = ptend - ptstart; dir.normalize(); m_ArrowLinesDO.m_PntVec.clear(); m_ArrowHeadDO.m_PntVec.clear(); m_ArrowLinesDO.m_PntVec.push_back( ptstart ); m_ArrowLinesDO.m_PntVec.push_back( ptend ); m_ArrowLinesDO.m_PntVec.push_back( cen ); m_ArrowLinesDO.m_PntVec.push_back( cen + thrustdir * axlen ); MakeArrowhead( cen + thrustdir * axlen, thrustdir, 0.25 * axlen, m_ArrowHeadDO.m_PntVec ); MakeCircleArrow( pmid, dir, 0.5 * axlen, m_ArrowLinesDO, m_ArrowHeadDO ); MakeCircleArrow( cen, rotdir, 0.5 * axlen, m_ArrowLinesDO, m_ArrowHeadDO ); }
//==== Update Fuselage And Cross Section Placement ====// void PropGeom::UpdateSurf() { int nxsec = m_XSecSurf.NumXSec(); double radius = m_Diameter() / 2.0; double rfirst = 0.0; double rlast = 1.0; vector < double > uvec( nxsec ); vector < double > rvec( nxsec ); double rev = 1.0; if ( m_ReverseFlag() ) { rev = -1.0; } //==== Update XSec Location/Rotation ====// for ( int i = 0 ; i < nxsec ; i++ ) { PropXSec* xs = ( PropXSec* ) m_XSecSurf.FindXSec( i ); if ( xs ) { if ( xs->GetXSecCurve()->GetType() == XS_POINT ) { printf( "Warning: XS_POINT type not valid for propellers\n" ); // Propellers are constructed in two phases. The first phase stacks // all the XSecs with unit chord. Intermediate curves are extracted // from that surface and are scaled to match the required chord. // Since XS_POINT XSecs already have zero chord, they mess up this // process. } //==== Reset Group Names ====// xs->SetGroupDisplaySuffix( i ); //==== Set X Limits ====// EnforceOrder( xs, i ); xs->SetRefLength( radius ); bool first = false; bool last = false; if( i == 0 ) first = true; else if( i == (nxsec-1) ) last = true; if ( first ) { rfirst = xs->m_RadiusFrac(); } if ( last ) { rlast = xs->m_RadiusFrac(); } uvec[i] = i; rvec[i] = xs->m_RadiusFrac(); } } m_rtou = Vsp1DCurve(); m_rtou.InterpolateLinear( uvec, rvec, false ); EnforcePCurveOrder( rfirst, rlast ); // Set lower limit for activity factor integration limit m_AFLimit.SetLowerLimit( rfirst ); // Integrate activity factor. m_AF.Set( m_ChordCurve.IntegrateAF( m_AFLimit() ) ); m_AF.Deactivate(); if ( m_UseBeta34Flag() == 1 ) { if ( rfirst <= 0.75 ) { double theta34 = m_TwistCurve.Comp( 0.75 ); m_Feather = m_Beta34() - theta34; } else { m_Feather = m_Beta34(); } m_Beta34.Activate(); m_Feather.Deactivate(); } else { if ( rfirst <= 0.75 ) { double theta34 = m_TwistCurve.Comp( 0.75 ); m_Beta34 = m_Feather() + theta34; } else { m_Beta34 = m_Feather(); } m_Beta34.Deactivate(); m_Feather.Activate(); } // Set up fold axis & store for visualization. Matrix4d fold, foldrot; fold.loadIdentity(); fold.rotateY( m_AzimuthFoldDir() ); fold.rotateX( m_ElevationFoldDir() ); m_FoldAxDirection = fold.xform( vec3d( 0, 0, 1 ) ); m_FoldAxOrigin = vec3d( m_AxialFoldAxis() * radius, m_RadFoldAxis() * radius, m_OffsetFoldAxis() * radius ); //==== Cross Section Curves & joint info ====// vector< VspCurve > crv_vec; crv_vec.resize( nxsec ); //==== Update XSec Location/Rotation ====// for ( int i = 0 ; i < nxsec ; i++ ) { PropXSec* xs = ( PropXSec* ) m_XSecSurf.FindXSec( i ); if ( xs ) { XSecCurve* xsc = xs->GetXSecCurve(); double r = xs->m_RadiusFrac(); double w = m_ChordCurve.Comp( r ) * radius; if ( xsc ) { //==== Find Width Parm ====// string width_id = xsc->GetWidthParmID(); Parm* width_parm = ParmMgr.FindParm( width_id ); piecewise_curve_type pwc; if ( width_parm ) { width_parm->Deactivate(); Airfoil* af = dynamic_cast < Airfoil* > ( xsc ); if ( af ) { width_parm->Set( 1.0 ); xs->GetXSecCurve()->SetFakeWidth( w ); xs->GetXSecCurve()->SetUseFakeWidth( true ); pwc = xs->GetCurve().GetCurve(); xs->GetXSecCurve()->SetUseFakeWidth( false ); width_parm->Set( w ); } else { CircleXSec* cir = dynamic_cast < CircleXSec* > ( xsc ); if ( cir ) { width_parm->Set( 1.0 ); pwc = xs->GetCurve().GetCurve(); width_parm->Set( w ); } else { double h = xs->GetXSecCurve()->GetHeight(); xsc->SetWidthHeight( 1.0, h/w ); pwc = xs->GetCurve().GetCurve(); xsc->SetWidthHeight( w, h ); } } } else { pwc = xs->GetCurve().GetCurve(); } // Set up prop positioner for highlight curves - not lofting. xs->m_PropPos.m_ParentProp = GetXSecSurf( 0 ); xs->m_PropPos.m_Radius = r * radius; xs->m_PropPos.m_Chord = w; xs->m_PropPos.m_Twist = m_TwistCurve.Comp( r ); xs->m_PropPos.m_XRotate = 0.0; xs->m_PropPos.m_ZRotate = atan( -m_RakeCurve.Compdt( r ) ) * 180.0 / PI; xs->m_PropPos.m_Rake = m_RakeCurve.Comp( r ) * radius; xs->m_PropPos.m_Skew = m_SkewCurve.Comp( r ) * radius; xs->m_PropPos.m_PropRot = m_Rotate(); xs->m_PropPos.m_Feather = m_Feather(); xs->m_PropPos.m_FoldOrigin = m_FoldAxOrigin; xs->m_PropPos.m_FoldDirection = m_FoldAxDirection; xs->m_PropPos.m_FoldAngle = m_FoldAngle(); xs->m_PropPos.m_Reverse = rev; crv_vec[i].SetCurve( pwc ); } } } // This surface linearly interpolates the airfoil sections without // any other transformations. // These sections can be extracted (as u-const curves) and then // transformed to their final position before skinning. m_FoilSurf = VspSurf(); m_FoilSurf.SkinC0( crv_vec, false ); // Find the union of stations required to approximate the blade parameters // with cubic functions. vector < double > tmap = rvec; // Initialize with specified XSecs. vector < double > tdisc; tdisc.push_back( rfirst ); tdisc.push_back( rlast ); for ( int i = 0; i < m_pcurve_vec.size(); i++ ) { vector < double > tm; vector < double > tmout; vector < double > td; m_pcurve_vec[i]->GetTMap( tm, td ); std::set_union( tmap.begin(), tmap.end(), tm.begin(), tm.end(), std::back_inserter(tmout), &aboutcomp ); std::swap( tmout, tmap ); } // Not sure why above set_union leaves duplicate entries, but // sort and force unique just to be sure. std::sort( tmap.begin(), tmap.end() ); auto tmit = std::unique( tmap.begin(), tmap.end(), &abouteq ); tmap.erase( tmit, tmap.end() ); // Treat all control points as possible discontinuities. tdisc = tmap; // Refine by adding two intermediate points to each cubic section // this is needed because the adaptive algorithm above uses derivatives // while our later reconstruction does not. vector < double > tref( ( tmap.size() - 1 ) * 3 + 1 ); for ( int i = 0; i < tmap.size() - 1; i++ ) { int iref = 3*i; double t = tmap[i]; double tnxt = tmap[i+1]; double dt = (tnxt-t)/3.0; tref[iref] = t; tref[iref+1] = t + dt; tref[iref+2] = t + 2 * dt; } tref.back() = tmap.back(); std::swap( tmap, tref ); // Convert tdisc to final parameterization. for ( int i = 0; i < tdisc.size(); i++ ) { tdisc[i] = ( tdisc[i] - rfirst ) / ( rlast - rfirst ); } // Pseudo cross sections // Not directly user-controlled, but an intermediate step in lofting the // surface. int npseudo = tmap.size(); vector < rib_data_type > rib_vec( npseudo ); vector < double > u_pseudo( npseudo ); for ( int i = 0; i < npseudo; i++ ) { // Assume linear interpolation means linear u/r relationship. double r = tmap[i]; double u = m_rtou.CompPnt( r ); VspCurve c; m_FoilSurf.GetUConstCurve( c, u ); vec3d v = c.CompPnt( 0 ); c.OffsetZ( -v.z() ); PropPositioner pp; pp.m_ParentProp = this->GetXSecSurf( 0 ); pp.m_Radius = r * radius; pp.m_Chord = m_ChordCurve.Comp( r ) * radius; pp.m_Twist = m_TwistCurve.Comp( r ); pp.m_XRotate = 0.0; pp.m_ZRotate = atan( -m_RakeCurve.Compdt( r ) ) * 180.0 / PI; pp.m_Rake = m_RakeCurve.Comp( r ) * radius; pp.m_Skew = m_SkewCurve.Comp( r ) * radius; pp.m_PropRot = m_Rotate(); pp.m_Feather = m_Feather(); pp.m_FoldOrigin = m_FoldAxOrigin; pp.m_FoldDirection = m_FoldAxDirection; pp.m_FoldAngle = m_FoldAngle(); pp.m_Reverse = rev; // Set a bunch of other pp variables. pp.SetCurve( c ); pp.Update(); rib_vec[i].set_f( pp.GetCurve().GetCurve() ); u_pseudo[i] = ( r - rfirst ) / ( rlast - rfirst ); } m_MainSurfVec.resize( 1 ); m_MainSurfVec.reserve( m_Nblade() ); m_MainSurfVec[0].SetMagicVParm( false ); m_MainSurfVec[0].SkinCubicSpline( rib_vec, u_pseudo, tdisc, false ); m_MainSurfVec[0].SetMagicVParm( true ); m_MainSurfVec[0].SetSurfType( PROP_SURF ); m_MainSurfVec[0].SetClustering( m_LECluster(), m_TECluster() ); m_MainSurfVec[0].SetFoilSurf( &m_FoilSurf ); if ( m_XSecSurf.GetFlipUD() ) { m_MainSurfVec[0].FlipNormal(); } if ( this->m_ReverseFlag() ) { m_MainSurfVec[0].FlipNormal(); } // UpdateEndCaps here so we only have to cap one blade. UpdateEndCaps(); m_MainSurfVec.resize( m_Nblade(), m_MainSurfVec[0] ); // Duplicate capping variables m_CapUMinSuccess.resize( m_Nblade(), m_CapUMinSuccess[0] ); m_CapUMaxSuccess.resize( m_Nblade(), m_CapUMaxSuccess[0] ); m_CapWMinSuccess.resize( m_Nblade(), m_CapWMinSuccess[0] ); m_CapWMaxSuccess.resize( m_Nblade(), m_CapWMaxSuccess[0] ); Matrix4d rot; for ( int i = 1; i < m_Nblade(); i++ ) { double theta = 360.0 * i / ( double )m_Nblade(); rot.loadIdentity(); rot.rotateX( theta ); m_MainSurfVec[i].Transform( rot ); } CalculateMeshMetrics( u_pseudo ); }