void COrientOnCrvXform::MakeNormalPlane( const ON_3dPoint origin, const ON_3dVector& normal, ON_Plane& plane ) { plane.origin = origin; ON_3dVector up( normal.x, normal.y, normal.z + 1.0 ); plane.xaxis = ON_CrossProduct( up, normal ); if( plane.xaxis.IsTiny() ) { if( normal.z < 0.0 ) { plane.xaxis = ON_3dVector( 1.0, 0.0, 0.0 ); plane.yaxis = ON_3dVector( 0.0, -1.0, 0.0 ); } else { plane.xaxis = ON_3dVector( 1.0, 0.0, 0.0 ); plane.yaxis = ON_3dVector( 0.0, 1.0, 0.0 ); } } else { plane.xaxis.Unitize(); plane.yaxis = ON_CrossProduct( normal, plane.xaxis ); plane.yaxis.Unitize(); } plane.UpdateEquation(); }
// Try to make up a 1st derivative if one is zero length static void CookDerivativesHelper( ON_3dVector& du, ON_3dVector& dv, ON_3dVector& duu, ON_3dVector& duv, ON_3dVector& dvv) { bool du_ok = du.LengthSquared() > ON_SQRT_EPSILON; bool dv_ok = dv.LengthSquared() > ON_SQRT_EPSILON; if( !du_ok || !dv_ok) { ON_3dVector normal; bool normal_ok = ON_EvNormal( 0, du, dv, duu, duv, dvv, normal ) ? true : false; if( normal_ok) normal_ok = normal.LengthSquared() > ON_SQRT_EPSILON; if( normal_ok) { if(( !du_ok) && ( dv_ok && normal_ok)) { du = ON_CrossProduct( dv, normal); du_ok = du.Unitize(); du *= (0.00390625*dv.Length()); } if( du_ok && ( !dv_ok) && normal_ok) { dv = ON_CrossProduct( normal, du); dv_ok = dv.Unitize(); dv *= (0.00390625*du.Length()); } } } }
void arbaxis(const ON_3dVector& givenaxis, ON_3dVector& newaxis) { if(fabs(givenaxis[0]) < ARBBOUND && fabs(givenaxis[1]) < ARBBOUND) // near world z newaxis = ON_CrossProduct(ON_yaxis, givenaxis); else newaxis = ON_CrossProduct(ON_zaxis, givenaxis); newaxis.Unitize(); }
ON_3dVector ON_Light::PerpindicularDirection() const { // returns a consistent vector perpendicular to the // light's direction. This vector is useful for // user interface display. ON_3dVector dir = m_direction; if ( !dir.IsValid() || !dir.Unitize() ) return ON_UNSET_VECTOR; ON_3dVector xdir; if ( IsLinearLight() || IsRectangularLight() ) { xdir = m_length; if ( xdir.IsValid() && xdir.Unitize() && fabs(xdir*dir) <= ON_SQRT_EPSILON ) return xdir; } if( dir.IsParallelTo( ON_zaxis, ON_DEGREES_TO_RADIANS * 3.0)) xdir = ON_CrossProduct( dir, ON_xaxis); else xdir = ON_CrossProduct( dir, ON_zaxis); xdir.Unitize(); ON_3dVector ydir = ON_CrossProduct(dir,xdir); ydir.Unitize(); ON_3dVector right; switch(dir.MaximumCoordinateIndex()) { case 0: right = (fabs(xdir.y) > fabs(ydir.y)) ? xdir : ydir; if ( right.y < 0.0 ) right.Reverse(); break; case 1: case 2: right = (fabs(xdir.x) > fabs(ydir.x)) ? xdir : ydir; if ( right.x < 0.0 ) right.Reverse(); break; default: right = xdir; break; } if ( right[right.MaximumCoordinateIndex()] < 0.0 ) right.Reverse(); return right; }
ON_BOOL32 ON_ArcCurve::SetEndPoint(ON_3dPoint end_point) { if (IsCircle()) return false; ON_BOOL32 rc = false; if ( m_dim == 3 || end_point.z == 0.0 ) { ON_3dPoint P; ON_3dVector T; double t = Domain()[0]; Ev1Der( t, P, T ); ON_Arc a; rc = a.Create( P, T, end_point ); if ( rc ) { m_arc = a; } else { ON_3dPoint start_point = PointAt(Domain()[0]); if (end_point.DistanceTo(start_point) < ON_ZERO_TOLERANCE*m_arc.Radius()){ //make arc into circle m_arc.plane.xaxis = start_point - m_arc.Center(); m_arc.plane.xaxis.Unitize(); m_arc.plane.yaxis = ON_CrossProduct(m_arc.Normal(), m_arc.plane.xaxis); m_arc.plane.yaxis.Unitize(); m_arc.SetAngleRadians(2.0*ON_PI); rc = true; } } } return rc; }
ON_BOOL32 ON_Surface::FrameAt( double u, double v, ON_Plane& frame) const { ON_BOOL32 rc = false; ON_3dPoint origin; ON_3dVector udir, vdir, normal; if( EvNormal( u, v, origin, udir, vdir, normal)) { if ( udir.Unitize() ) vdir = ON_CrossProduct( normal, udir); else if ( vdir.Unitize() ) udir = ON_CrossProduct( vdir, normal); frame.CreateFromFrame( origin, udir, vdir); rc = frame.IsValid(); } return rc; }
RH_C_FUNCTION int ON_RayShooter_OneSurface(ON_3DPOINT_STRUCT _point, ON_3DVECTOR_STRUCT _direction, const ON_Surface* pConstSurface, ON_SimpleArray<ON_3dPoint>* pPoints, int maxReflections) { int rc = 0; ON_3dPoint point(_point.val[0], _point.val[1], _point.val[2]); ON_3dVector direction(_direction.val[0], _direction.val[1], _direction.val[2]); if( pConstSurface && pPoints && maxReflections>0 && point.IsValid() && direction.Unitize() ) { ON_RayShooter shooter; ON_X_EVENT hit; ON_3dPoint Q = point; ON_3dVector R = direction; ON_3dVector V[3]; for( int i=0; i<maxReflections; i++ ) { memset(&hit,0,sizeof(hit)); ON_3dVector T = R; if( !T.Unitize() ) break; if( !shooter.Shoot(Q,T,pConstSurface,hit) ) break; Q = hit.m_A[0]; pPoints->Append(Q); if( !hit.m_snodeB[0] ) break; hit.m_snodeB[0]->Evaluate(hit.m_b[0], hit.m_b[1], 1, 3, &V[0].x); ON_3dVector N = ON_CrossProduct(V[1],V[2]); if ( !N.Unitize() ) break; double d = N*T; R = T + (-2.0*d)*N; // R = reflection direction } rc = pPoints->Count(); } return rc; }
bool ON_Plane::CreateFromFrame( const ON_3dPoint& P, // point on the plane const ON_3dVector& X, // non-zero vector in plane const ON_3dVector& Y // another non-zero vector in the plane ) { origin = P; xaxis = X; xaxis.Unitize(); yaxis = Y - ON_DotProduct( Y, xaxis)*xaxis; yaxis.Unitize(); zaxis = ON_CrossProduct( xaxis, yaxis ); bool b = zaxis.Unitize(); UpdateEquation(); if ( b ) { // 11 February 2004 Dale Lear // Add more validation checks. b = IsValid(); if ( b ) { // make sure zaxis is perp to Y if ( fabs(Y*zaxis) > ON_SQRT_EPSILON*Y.Length() ) b = false; } } return b; }
bool ON_Plane::CreateFromEquation( const double e[4] // equation of plane ) { bool b = false; plane_equation.x = e[0]; plane_equation.y = e[1]; plane_equation.z = e[2]; plane_equation.d = e[3]; zaxis.x = e[0]; zaxis.y = e[1]; zaxis.z = e[2]; double d = zaxis.Length(); if ( d > 0.0 ) { d = 1.0/d; origin = (-d*plane_equation.d)*zaxis; b = true; } xaxis.PerpendicularTo( zaxis ); xaxis.Unitize(); yaxis = ON_CrossProduct( zaxis, xaxis ); yaxis.Unitize(); return b; }
BOOL COrientOnCrvXform::CalculateTransform( CRhinoViewport& vp, const ON_3dPoint& pt, ON_Xform& xform ) { BOOL bResult = FALSE; if( m_path_curve ) { double t = 0.0; if( m_path_curve->GetClosestPoint(pt, &t) ) { ON_3dPoint origin = m_path_curve->PointAt( t ); ON_Plane dest_plane; if( m_perp_mode ) { ON_3dVector tangent = m_path_curve->TangentAt( t ); MakeNormalPlane( origin, tangent, dest_plane ); } else { dest_plane.origin = origin; dest_plane.xaxis = m_path_curve->TangentAt( t ); dest_plane.zaxis = m_base_plane.zaxis; dest_plane.yaxis = ON_CrossProduct( dest_plane.zaxis, dest_plane.xaxis ); dest_plane.UpdateEquation(); } xform.Rotation( m_base_plane, dest_plane ); bResult = xform.IsValid() ? TRUE : FALSE; } } return bResult; }
bool ON_PolyEdgeCurve::EvSrfTangent( double t, bool bIsoDir, ON_3dPoint& srfpoint, ON_3dVector& srftangent, ON_3dVector& srfnormal ) const { ON_3dPoint srfpt; ON_3dVector du, dv, duu, duv, dvv; bool rc = EvSrfDerivatives(t,srfpoint,du,dv,duu,duv,dvv); if (rc ) rc = ON_EvNormal( 0, du, dv, duu, duv, dvv, srfnormal ) ? true : false; if (rc) { int segment_index = SegmentIndex(t); ON_PolyEdgeSegment* seg = SegmentCurve(segment_index); if ( seg ) { if ( bIsoDir && seg->IsoType() == ON_Surface::not_iso ) bIsoDir = false; ON_3dVector crvtangent = TangentAt(t); ON_3dVector binormal = ON_CrossProduct(crvtangent,srfnormal); binormal.Unitize(); if ( seg->ReversedTrimDir() ) binormal.Reverse(); // at this point, binormal points "out" and is tangent // to the surface. if ( bIsoDir ) { du.Unitize(); dv.Unitize(); double B_dot_du = binormal*du; double B_dot_dv = binormal*dv; if ( fabs(B_dot_dv) > fabs(B_dot_du) ) { if (B_dot_dv < 0.0) dv.Reverse(); srftangent = dv; } else { if (B_dot_du < 0.0) du.Reverse(); srftangent = du; } } else srftangent = binormal; if ( seg && seg->m_face && seg->m_face->m_bRev ) srfnormal.Reverse(); } else rc = false; } return rc; }
////////// // Create an circle from two 3d points and a tangent at the first point. bool ON_Circle::Create( const ON_3dPoint& P, // [IN] point P const ON_3dVector& Pdir, // [IN] tangent at P const ON_3dPoint& Q // [IN] point Q ) { bool rc = false; double a, b; ON_3dVector QP, RM, RP, X, Y, Z; ON_3dPoint M, C; ON_Line A, B; // n = normal to circle QP = Q-P; Z = ON_CrossProduct( QP, Pdir ); if ( Z.Unitize() ) { M = 0.5*(P+Q); RM = ON_CrossProduct( QP, Z ); // vector parallel to center-M A.Create(M,M+RM); RP = ON_CrossProduct( Pdir, Z ); // vector parallel to center-P B.Create(P,P+RP); if ( ON_Intersect( A, B, &a, &b ) ) { C = A.PointAt( a ); // center = intersection of lines A and B X = P-C; radius = C.DistanceTo(P); if ( X.Unitize() ) { Y = ON_CrossProduct( Z, X ); if ( Y*Pdir < 0.0 ) { Z.Reverse(); Y.Reverse(); RM.Reverse(); } plane.origin = C; plane.xaxis = X; plane.yaxis = Y; plane.zaxis = Z; plane.UpdateEquation(); //m_point[0] = P; //m_point[1] = C + radius*RM/RM.Length(); //m_point[2] = Q; rc = IsValid(); } } } return rc; }
int ON_PlaneSurface::GetNurbForm( // returns 0: unable to create NURBS representation // with desired accuracy. // 1: success - returned NURBS parameterization // matches the surface's to wthe desired accuracy // 2: success - returned NURBS point locus matches // the surfaces's to the desired accuracy but, on // the interior of the surface's domain, the // surface's parameterization and the NURBS // parameterization may not match to the // desired accuracy. ON_NurbsSurface& nurbs, double tolerance ) const { ON_BOOL32 rc = IsValid(); if( !rc ) { if ( m_plane.origin.x != ON_UNSET_VALUE && m_plane.xaxis.x != ON_UNSET_VALUE && m_plane.yaxis.x != ON_UNSET_VALUE && m_domain[0].IsIncreasing() && m_domain[1].IsIncreasing() && m_extents[0].Length() > 0.0 && m_extents[1].Length() > 0.0 ) { ON_3dVector N = ON_CrossProduct(m_plane.xaxis,m_plane.yaxis); if ( N.Length() <= 1.0e-4 ) { ON_WARNING("ON_PlaneSurface::GetNurbForm - using invalid surface."); rc = true; } } } if ( rc ) { nurbs.m_dim = 3; nurbs.m_is_rat = 0; nurbs.m_order[0] = nurbs.m_order[1] = 2; nurbs.m_cv_count[0] = nurbs.m_cv_count[1] = 2; nurbs.m_cv_stride[1] = nurbs.m_dim; nurbs.m_cv_stride[0] = nurbs.m_cv_stride[1]*nurbs.m_cv_count[1]; nurbs.ReserveCVCapacity(12); nurbs.ReserveKnotCapacity(0,2); nurbs.ReserveKnotCapacity(1,2); nurbs.m_knot[0][0] = m_domain[0][0]; nurbs.m_knot[0][1] = m_domain[0][1]; nurbs.m_knot[1][0] = m_domain[1][0]; nurbs.m_knot[1][1] = m_domain[1][1]; nurbs.SetCV( 0, 0, PointAt( m_domain[0][0], m_domain[1][0] )); nurbs.SetCV( 0, 1, PointAt( m_domain[0][0], m_domain[1][1] )); nurbs.SetCV( 1, 0, PointAt( m_domain[0][1], m_domain[1][0] )); nurbs.SetCV( 1, 1, PointAt( m_domain[0][1], m_domain[1][1] )); } return rc; }
bool ON_Intersect( const ON_Plane& R, const ON_Plane& S, ON_Line& L ) { ON_3dVector d = ON_CrossProduct( S.zaxis, R.zaxis ); ON_3dPoint p = 0.5*(R.origin + S.origin); ON_Plane T( p, d ); bool rc = ON_Intersect( R, S, T, L.from ); L.to = L.from + d; return rc; }
ON_Circle ON_Torus::MinorCircleRadians(double major_angle_radians ) const { EVAL_SETUP_MAJOR; ON_Circle c; c.plane.xaxis = raxis; c.plane.yaxis = plane.zaxis; c.plane.zaxis = ON_CrossProduct( c.plane.xaxis, c.plane.yaxis ); c.plane.origin = plane.origin + major_radius*raxis; c.plane.UpdateEquation(); c.radius = minor_radius; return c; }
bool ON_Circle::Create( // circle through three 3d points const ON_3dPoint& P, const ON_3dPoint& Q, const ON_3dPoint& R ) { ON_3dPoint C; ON_3dVector X, Y, Z; // return ( radius > 0.0 && plane.IsValid() ); //m_point[0] = P; //m_point[1] = Q; //m_point[2] = R; // get normal for(;;) { if ( !Z.PerpendicularTo( P, Q, R ) ) break; // get center as the intersection of 3 planes ON_Plane plane0( P, Z ); ON_Plane plane1( 0.5*(P+Q), P-Q ); ON_Plane plane2( 0.5*(R+Q), R-Q ); if ( !ON_Intersect( plane0, plane1, plane2, C ) ) break; X = P - C; radius = X.Length(); if ( !(radius > 0.0) ) break; if ( !X.Unitize() ) break; Y = ON_CrossProduct( Z, X ); if ( !Y.Unitize() ) break; plane.origin = C; plane.xaxis = X; plane.yaxis = Y; plane.zaxis = Z; plane.UpdateEquation(); return true; } plane = ON_Plane::World_xy; radius = 0.0; return false; }
ON_3dVector ON_Cone::NormalAt( double radial_parameter, double height_parameter ) const { double s = sin(radial_parameter); double c = cos(radial_parameter); if ( radius<0.) { c = -c; s = -s; } ON_3dVector ds = c*plane.yaxis - s*plane.xaxis; ON_3dVector N = ON_CrossProduct( ((radius<0.0)?-ds:ds), plane.PointAt(radius*c,radius*s,height) - plane.origin ); N.Unitize(); return N; }
double ON_Localizer::Value(ON_3dPoint P) const { double v,s,t = m_d.m_t[1]; switch ( m_type ) { case cylinder_type: // t = distance from P to axis t = ON_CrossProduct( P-m_P, m_V ).Length(); break; case plane_type: // t = distance above plane t = m_V.x*P.x + m_V.y*P.y + m_V.z*P.z + m_P.x; break; case sphere_type: // t = distance to P t = (P-m_P).Length(); break; case curve_type: if ( !m_nurbs_curve ) return 1.0; if ( !m_nurbs_curve->GetClosestPoint(P,&v) ) return 1.0; t = P.DistanceTo(m_nurbs_curve->PointAt(v)); break; case surface_type: if ( !m_nurbs_surface ) return 1.0; if ( !m_nurbs_surface->GetClosestPoint(P,&s,&v) ) return 1.0; t = P.DistanceTo(m_nurbs_surface->PointAt(s,v)); break; case distance_type: // confused user should be calling Value(double) return 1.0; // default must be one break; default: return 1.0; // default must be one } return Value(t); }
bool ON_Plane::CreateFromNormal( const ON_3dPoint& P, // point on the plane const ON_3dVector& N // non-zero normal to the plane ) { origin = P; zaxis = N; bool b = zaxis.Unitize(); xaxis.PerpendicularTo( zaxis ); xaxis.Unitize(); yaxis = ON_CrossProduct( zaxis, xaxis ); yaxis.Unitize(); UpdateEquation(); return b; }
bool ON_Plane::CreateFromPoints( const ON_3dPoint& P, // point on the plane const ON_3dPoint& Q, // point on the plane const ON_3dPoint& R // point on the plane ) { origin = P; bool rc = zaxis.PerpendicularTo(P,Q,R); xaxis = Q - P; xaxis.Unitize(); yaxis = ON_CrossProduct( zaxis, xaxis ); yaxis.Unitize(); if ( !plane_equation.Create(origin,zaxis) ) rc = false; return rc; }
static double Angle3d(const ON_3dVector& axis, ON_3dVector& from, const ON_3dVector& to) { ON_3dVector x = from, a = to; x.Unitize(); a.Unitize(); ON_3dVector y = ON_CrossProduct(axis, from); y.Unitize(); double cosa = x * a; if(cosa > 1.0 - ON_SQRT_EPSILON) return 0.0; if(cosa < ON_SQRT_EPSILON - 1.0) return ON_PI; double sina = a * y; return atan2(sina, cosa); }
double ON_Localizer::Value(ON_3dPoint P) const { double t = m_d.m_t[1]; switch ( m_type ) { case cylinder_type: // t = distance from P to axis t = ON_CrossProduct( P-m_P, m_V ).Length(); break; case plane_type: // t = distance above plane t = m_V.x*P.x + m_V.y*P.y + m_V.z*P.z + m_P.x; break; case sphere_type: // t = distance to P t = (P-m_P).Length(); break; case curve_type: return 1.0; break; case surface_type: return 1.0; break; case distance_type: // confused user should be calling Value(double) return 1.0; // default must be one break; default: return 1.0; // default must be one } return Value(t); }
// Converts from WCS 3d points to 2d points in annotation bool ON_Annotation::GeWCStoECSXform( ON_Xform& xform ) const { ON_3dVector z = ON_CrossProduct( m_plane.xaxis, m_plane.yaxis ); return xform.ChangeBasis( ON_origin, ON_xaxis, ON_yaxis, ON_zaxis, m_plane.origin, m_plane.xaxis, m_plane.yaxis, z ); }
bool ON_Plane::Morph( const ON_SpaceMorph& morph ) { ON_Plane mp; double s = sqrt( fabs(origin.MaximumCoordinate())*ON_SQRT_EPSILON + ON_ZERO_TOLERANCE ); mp.xaxis = morph.MorphVector(origin,s*xaxis); mp.yaxis = morph.MorphVector(origin,s*yaxis); mp.zaxis = morph.MorphVector(origin,s*zaxis); origin = morph.MorphPoint(origin); UpdateEquation(); bool bx = mp.xaxis.Unitize(); bool by = mp.yaxis.Unitize(); bool bz = mp.zaxis.Unitize(); if (!bx) { mp.xaxis = ON_CrossProduct(mp.yaxis,mp.zaxis); bx = mp.xaxis.Unitize(); } if (!by) { mp.yaxis = ON_CrossProduct(mp.zaxis,mp.xaxis); by = mp.yaxis.Unitize(); } if (!bz) { mp.zaxis = ON_CrossProduct(mp.xaxis,mp.yaxis); bz = mp.zaxis.Unitize(); } mp.origin.Set(0.0,0.0,0.0); mp.UpdateEquation(); bool rc = mp.IsValid(); ON_3dVector x, y, z; if ( rc ) { x = mp.xaxis; y = mp.yaxis; z = mp.zaxis; } else { x = ON_CrossProduct(mp.yaxis,mp.zaxis); y = ON_CrossProduct(mp.zaxis,mp.xaxis); z = ON_CrossProduct(mp.xaxis,mp.yaxis); x.Unitize(); y.Unitize(); z.Unitize(); x = mp.xaxis + x; y = mp.yaxis + y; z = mp.zaxis + z; x.Unitize(); y.Unitize(); z.Unitize(); rc = mp.CreateFromFrame(ON_origin,x,y); if (rc) { x = mp.xaxis; y = mp.yaxis; z = mp.zaxis; } else { rc = mp.CreateFromFrame(ON_origin,y,z); if ( rc ) { y = mp.xaxis; z = mp.yaxis; x = mp.zaxis; } else { rc = mp.CreateFromFrame(ON_origin,z,x); if (rc) { z = mp.xaxis; x = mp.yaxis; y = mp.zaxis; } else { rc = mp.CreateFromNormal(ON_origin,z); if (rc) { x = mp.xaxis; y = mp.yaxis; z = mp.zaxis; } } } } } if (rc) { xaxis = x; yaxis = y; zaxis = z; UpdateEquation(); } return rc; }
bool ON_Mesh::CollapseEdge( int topei ) { ON_Mesh& mesh = *this; ON__MESHEDGE me; memset(&me,0,sizeof(me)); const ON_MeshTopology& top = mesh.Topology(); const int F_count = mesh.m_F.Count(); const int V_count = mesh.m_V.Count(); const int topv_count = top.m_topv.Count(); //const int tope_count = top.m_tope.Count(); if ( topei < 0 || topei >= top.m_tope.Count() ) { return false; } const ON_MeshTopologyEdge& tope = top.m_tope[topei]; if ( tope.m_topf_count < 1 || tope.m_topvi[0] == tope.m_topvi[1] || tope.m_topvi[0] < 0 || tope.m_topvi[1] < 0 || tope.m_topvi[0] >= topv_count || tope.m_topvi[1] >= topv_count ) { return false; } const ON_MeshTopologyVertex& topv0 = top.m_topv[tope.m_topvi[0]]; const ON_MeshTopologyVertex& topv1 = top.m_topv[tope.m_topvi[1]]; if ( topv0.m_v_count < 1 || topv1.m_v_count < 1 ) { return false; } if ( topv0.m_vi[0] < 0 || topv0.m_vi[0] >= V_count ) { return false; } if ( topv1.m_vi[0] < 0 || topv1.m_vi[0] >= V_count ) { return false; } // create a ON__MESHEDGE for each face (usually one or two) that uses the edge ON__MESHEDGE* me_list = (ON__MESHEDGE*)alloca(tope.m_topf_count*sizeof(me_list[0])); int me_list_count = 0; int efi; for ( efi = 0; efi < tope.m_topf_count; efi++ ) { int fi = tope.m_topfi[efi]; if ( fi < 0 || fi >= F_count ) continue; const ON_MeshFace& f = mesh.m_F[fi]; if ( !f.IsValid(V_count) ) continue; me.vi1 = f.vi[3]; me.topvi1 = top.m_topv_map[me.vi1]; int fvi; for ( fvi = 0; fvi < 4; fvi++ ) { me.vi0 = me.vi1; me.topvi0 = me.topvi1; me.vi1 = f.vi[fvi]; me.topvi1 = top.m_topv_map[me.vi1]; if ( me.vi0 != me.vi1 ) { if ( (me.topvi0 == tope.m_topvi[0] && me.topvi1 == tope.m_topvi[1]) || (me.topvi0 == tope.m_topvi[1] && me.topvi1 == tope.m_topvi[0]) ) { if ( me.vi0 > me.vi1 ) { int i = me.vi0; me.vi0 = me.vi1; me.vi1 = i; i = me.topvi0; me.topvi0 = me.topvi1; me.topvi1 = i; } me_list[me_list_count++] = me; break; } } } } if (me_list_count<1) { return false; } // Sort me_list[] so edges using same vertices are adjacent // to each other in the list. This is needed so that non-manifold // crease edges will be properly collapsed. ON_qsort(me_list,me_list_count,sizeof(me_list[0]),(QSORTCMPFUNC)CompareMESHEDGE); // create new vertex or vertices that edge will be // collapsed to. mesh.m_C.Destroy(); mesh.m_K.Destroy(); int mei; bool bHasVertexNormals = mesh.HasVertexNormals(); bool bHasTextureCoordinates = mesh.HasTextureCoordinates(); bool bHasFaceNormals = mesh.HasFaceNormals(); if ( topv0.m_v_count == 1 || topv1.m_v_count == 1 ) { // a single new vertex ON_Line Vline(ON_origin,ON_origin); ON_Line Tline(ON_origin,ON_origin); ON_3dVector N0(0,0,0); ON_3dVector N1(0,0,0); ON_3dPoint P; int vi, tvi, cnt; int newvi = topv0.m_vi[0]; cnt = 0; for ( tvi = 0; tvi < topv0.m_v_count; tvi++ ) { vi = topv0.m_vi[tvi]; if ( vi < 0 || vi > V_count ) continue; if ( vi < newvi ) newvi = vi; cnt++; P = mesh.m_V[vi]; Vline.from += P; if ( bHasVertexNormals ) { N0 += ON_3dVector(mesh.m_N[vi]); } if ( bHasTextureCoordinates ) { P = mesh.m_T[vi]; Tline.from += P; } } if (cnt > 1) { double s = 1.0/((double)cnt); Vline.from.x *= s; Vline.from.y *= s; Vline.from.z *= s; Tline.from.x *= s; Tline.from.y *= s; Tline.from.z *= s; N0 = s*N0; } cnt = 0; for ( tvi = 0; tvi < topv1.m_v_count; tvi++ ) { vi = topv1.m_vi[tvi]; if ( vi < 0 || vi > V_count ) continue; if ( vi < newvi ) newvi = vi; cnt++; P = mesh.m_V[vi]; Vline.to += P; if ( bHasVertexNormals ) { N1 += ON_3dVector(mesh.m_N[vi]); } if ( bHasTextureCoordinates ) { P = mesh.m_T[vi]; Tline.to += P; } } if (cnt > 1) { double s = 1.0/((double)cnt); Vline.to.x *= s; Vline.to.y *= s; Vline.to.z *= s; Tline.to.x *= s; Tline.to.y *= s; Tline.to.z *= s; N1 = s*N1; } ON_3fPoint newV(Vline.PointAt(0.5)); ON_3fVector newN; ON_2fPoint newT; if ( bHasVertexNormals ) { N0.Unitize(); N1.Unitize(); ON_3dVector N = N0+N1; if ( !N.Unitize() ) { N = (topv0.m_v_count == 1) ? mesh.m_N[topv0.m_vi[0]] :mesh.m_N[topv1.m_vi[0]]; } newN = N; } if ( bHasTextureCoordinates ) { newT = Tline.PointAt(0.5); } for ( mei = 0; mei < me_list_count; mei++ ) { me_list[mei].newvi = newvi; me_list[mei].newV = newV; me_list[mei].newN = newN; me_list[mei].newT = newT; } } else { // collapsing a "crease" edge - attempt to preserve // the crease. memset(&me,0,sizeof(me)); me.vi0 = -1; me.vi1 = -1; for ( mei = 0; mei < me_list_count; mei++ ) { if ( 0 == mei && CompareMESHEDGE(&me,me_list+mei) ) { // cook up new vertex me_list[mei].newvi = mesh.m_V.Count(); me = me_list[mei]; ON_Line line; line.from = mesh.m_V[me.vi0]; line.to = mesh.m_V[me.vi1]; me.newV = line.PointAt(0.5); if ( bHasVertexNormals ) { ON_3dVector N0(mesh.m_N[me.vi0]); ON_3dVector N1(mesh.m_N[me.vi1]); ON_3dVector N = N0 + N1; if ( !N.Unitize() ) N = N0; me.newN = N; } if ( bHasTextureCoordinates ) { line.from = mesh.m_T[me.vi0]; line.to = mesh.m_T[me.vi1]; me.newT = line.PointAt(0.5); } me.newvi = (me.vi0 < me.vi1) ? me.vi0 : me.vi1; } else { me_list[mei].newvi = me.newvi; me_list[mei].newV = me.newV; me_list[mei].newN = me.newN; me_list[mei].newT = me.newT; } } } // We are done averaging old mesh values. // Change values in mesh m_V[], m_N[] and m_T[] arrays. for ( mei = 0; mei < me_list_count; mei++ ) { mesh.m_V[me_list[mei].vi0] = me_list[mei].newV; mesh.m_V[me_list[mei].vi1] = me_list[mei].newV; if ( bHasVertexNormals ) { mesh.m_N[me_list[mei].vi0] = me_list[mei].newN; mesh.m_N[me_list[mei].vi1] = me_list[mei].newN; } if ( bHasTextureCoordinates ) { mesh.m_T[me_list[mei].vi0] = me_list[mei].newT; mesh.m_T[me_list[mei].vi1] = me_list[mei].newT; } } // make a map of old to new int old2new_map_count = 0; ON__NEWVI* old2new_map = (ON__NEWVI*)alloca(2*me_list_count*sizeof(old2new_map[0])); for ( mei = 0; mei < me_list_count; mei++ ) { old2new_map[old2new_map_count].oldvi = me_list[mei].vi0; old2new_map[old2new_map_count].newvi = me_list[mei].newvi; old2new_map_count++; old2new_map[old2new_map_count].oldvi = me_list[mei].vi1; old2new_map[old2new_map_count].newvi = me_list[mei].newvi; old2new_map_count++; } // sort old2new_map[] so we can use a fast bsearch() call // to update faces. ON_qsort(old2new_map,old2new_map_count,sizeof(old2new_map[0]),(QSORTCMPFUNC)CompareNEWVI); // count faces that use the vertices that are being changed int bad_fi_count = 0; int topv_end, vei, fi, fvi23, fvi; ON__NEWVI nvi; for ( topv_end = 0; topv_end < 2; topv_end++ ) { const ON_MeshTopologyVertex& topv = (topv_end) ? topv1 : topv0; for ( vei = 0; vei < topv.m_tope_count; vei++ ) { topei = topv.m_topei[vei]; if ( topei < 0 && topei >= top.m_tope.Count() ) continue; bad_fi_count += top.m_tope[topei].m_topf_count; } } int* bad_fi = (int*)alloca(bad_fi_count*sizeof(*bad_fi)); bad_fi_count = 0; // Go through all the faces that use the vertices at the // ends of the edge and update the vi[] values to use the // new vertices. for ( topv_end = 0; topv_end < 2; topv_end++ ) { const ON_MeshTopologyVertex& topv = (topv_end) ? topv1 : topv0; for ( vei = 0; vei < topv.m_tope_count; vei++ ) { topei = topv.m_topei[vei]; if ( topei < 0 && topei >= top.m_tope.Count() ) continue; const ON_MeshTopologyEdge& e = top.m_tope[topei]; for ( efi = 0; efi < e.m_topf_count; efi++ ) { fi = e.m_topfi[efi]; if ( fi < 0 || fi >= F_count ) continue; bool bChangedFace = false; ON_MeshFace& f = mesh.m_F[fi]; for ( fvi = 0; fvi < 4; fvi++ ) { nvi.oldvi = f.vi[fvi]; ON__NEWVI* p = (ON__NEWVI*)bsearch(&nvi,old2new_map,old2new_map_count,sizeof(old2new_map[0]),(QSORTCMPFUNC)CompareNEWVI); if ( 0 != p && p->oldvi != p->newvi) { f.vi[fvi] = p->newvi; bChangedFace = true; } } if ( bChangedFace ) { if ( !f.IsValid(V_count) ) { if ( f.vi[3] == f.vi[0] ) { f.vi[0] = f.vi[1]; f.vi[1] = f.vi[2]; f.vi[2] = f.vi[3]; } else if ( f.vi[0] == f.vi[1] ) { fvi23 = f.vi[0]; f.vi[0] = f.vi[2]; f.vi[1] = f.vi[3]; f.vi[2] = fvi23; f.vi[3] = fvi23; } else if ( f.vi[1] == f.vi[2] ) { fvi23 = f.vi[1]; f.vi[1] = f.vi[0]; f.vi[0] = f.vi[3]; f.vi[2] = fvi23; f.vi[3] = fvi23; } if ( f.vi[0] == f.vi[1] || f.vi[1] == f.vi[2] || f.vi[2] == f.vi[0] || f.vi[2] != f.vi[3] ) { bad_fi[bad_fi_count++] = fi; } } if ( bHasFaceNormals ) { // invalid faces are removed below ON_3fVector a, b, n; a = mesh.m_V[f.vi[2]] - mesh.m_V[f.vi[0]]; b = mesh.m_V[f.vi[3]] - mesh.m_V[f.vi[1]]; n = ON_CrossProduct( a, b ); n.Unitize(); mesh.m_FN[fi] = n; } } } } } if ( bad_fi_count > 0 ) { // remove collapsed faces ON_qsort(bad_fi,bad_fi_count,sizeof(bad_fi[0]),CompareInt); int bfi = 1; int dest_fi = bad_fi[0]; for ( fi = dest_fi+1; fi < F_count && bfi < bad_fi_count; fi++ ) { if ( fi == bad_fi[bfi] ) { bfi++; } else { mesh.m_F[dest_fi++] = mesh.m_F[fi]; } } while (fi<F_count) { mesh.m_F[dest_fi++] = mesh.m_F[fi++]; } mesh.m_F.SetCount(dest_fi); if ( bHasFaceNormals ) { bfi = 1; dest_fi = bad_fi[0]; for ( fi = dest_fi+1; fi < F_count && bfi < bad_fi_count; fi++ ) { if ( fi == bad_fi[bfi] ) { bfi++; } else { mesh.m_FN[dest_fi++] = mesh.m_FN[fi]; } } while (fi<F_count) { mesh.m_FN[dest_fi++] = mesh.m_FN[fi++]; } mesh.m_FN.SetCount(dest_fi); } } mesh.Compact(); mesh.DestroyTopology(); mesh.DestroyPartition(); return true; }
ON_BOOL32 ON_Surface::EvNormal( // returns false if unable to evaluate double s, double t, // evaluation parameters (s,t) ON_3dPoint& point, // returns value of surface ON_3dVector& ds, // first partial derivatives (Ds) ON_3dVector& dt, // (Dt) ON_3dVector& normal, // unit normal int side, // optional - determines which side to evaluate from // 0 = default // 1 from NE quadrant // 2 from NW quadrant // 3 from SW quadrant // 4 from SE quadrant int* hint // optional - evaluation hint (int[2]) used to speed // repeated evaluations ) const { // simple cross product normal - override to support singular surfaces ON_BOOL32 rc = Ev1Der( s, t, point, ds, dt, side, hint ); if ( rc ) { const double len_ds = ds.Length(); const double len_dt = dt.Length(); // do not reduce the tolerance used here - there is a retry in the code // below. if ( len_ds > ON_SQRT_EPSILON*len_dt && len_dt > ON_SQRT_EPSILON*len_ds ) { ON_3dVector a = ds/len_ds; ON_3dVector b = dt/len_dt; normal = ON_CrossProduct( a, b ); rc = normal.Unitize(); } else { // see if we have a singular point double v[6][3]; int normal_side = side; ON_BOOL32 bOnSide = false; ON_Interval sdom = Domain(0); ON_Interval tdom = Domain(1); if (s == sdom.Min()) { normal_side = (normal_side >= 3) ? 4 : 1; bOnSide = true; } else if (s == sdom.Max()) { normal_side = (normal_side >= 3) ? 3 : 2; bOnSide = true; } if (t == tdom.Min()) { normal_side = (normal_side == 2 || normal_side == 3) ? 2 : 1; bOnSide = true; } else if (t == tdom.Max()) { normal_side = (normal_side == 2 || normal_side == 3) ? 3 : 4; bOnSide = true; } if ( !bOnSide ) { // 2004 November 11 Dale Lear // Added a retry again with a more generous tolerance if ( len_ds > ON_EPSILON*len_dt && len_dt > ON_EPSILON*len_ds ) { ON_3dVector a = ds/len_ds; ON_3dVector b = dt/len_dt; normal = ON_CrossProduct( a, b ); rc = normal.Unitize(); } else { rc = false; } } else { rc = Evaluate( s, t, 2, 3, &v[0][0], normal_side, hint ); if ( rc ) { rc = ON_EvNormal( normal_side, v[1], v[2], v[3], v[4], v[5], normal); } } } } if ( !rc ) { normal.Zero(); } return rc; }
ON_3dPoint ON_Cylinder::NormalAt( double s, double t ) const { ON_3dVector N = ON_CrossProduct( circle.TangentAt(s), circle.plane.zaxis ); N.Unitize(); return N; }