Files
solvecraft/harness/cpp/shim/intersect_curve_class_p1.cpp
T
Claude 68d43bcfbb intersect.curve_class_p1: port sm_MakeConsistentClassifications over the B-rep
The Rust stub returned Unported for any edge interval with a vertex end.
It now follows upstream: when the end vertex is not one of the interior
edge's, substitute the first edge of that vertex that the interval
midpoint drops to, else snap the end to the nearer edge vertex. Topology
goes through ClassGeometry::topology_entity / topology_handle, so the
function now takes the kernel and geometry (FixupClassifications passes
them). The p1 difftest gains face scripts over the one-face B-rep
(add_face moved from the p7 runner to p1 and shared).

intersect.curve_class_p1: 2054 passed, 71 deferred, 0 failed
(was 2048 / 77).

Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01RDs9gsVaDzsfRDvh47p2Rq
2026-10-20 22:41:03 +00:00

2938 lines
130 KiB
C++

// SPDX-License-Identifier: Apache-2.0
// difftest shim for unit `intersect.curve_class_p1`: SmCurveInterval, the part-1 members of
// SmCurveClassification, SmTouchData::GetConnectedTouches / GetWindingCount, the
// SmCurveClassification / SmCurveInterval overloads of SmTol, and the static helpers of
// source/SMLib/src/SmCurveClass.cpp (sm_SetXSectCurveThroughPoint, ...).
//
// Static functions of SmCurveClass.cpp cannot be linked against, so they are VERBATIM copies of
// upstream SmCurveClass.cpp (commit c9c5979) inside the namespace sc_cc_p1 (line ranges noted at
// each block; generated, do not edit by hand). sm_GetXSectTol2dOr3d_ForClassifyPair is global
// and called in the library. Private members (SmCurveInterval::SetIntervalMin / SetIntervalMax,
// SmCurveClassification::m_bIsCurveOwned) are reached through explicit instantiation (Rob).
//
// One entry point, sc_intersect_curve_class_p1_run(in, n_in, out, cap, out_len): a script over a
// pool of objects and two default-constructed classifications cc[0], cc[1]. The layout is
// documented in crates/solidcore-ffi/src/units/intersect_curve_class_p1.rs, which validates the
// static shape; the run-time checks below reject (SC_ERR_ARG) the ops that are undefined
// behaviour upstream (indices past the intervals, NULL curves or surfaces that are dereferenced,
// objects of the wrong kind, topology walks over the bare stand-in edges). Objects live for the
// whole process (never deleted: topology destructors notify owners).
//
// With has_face, the pool also holds a real one-face B-rep built with SmBrepConstructor
// (sc_one_face_brep.h, as the intersect.curve_class_p4 shim): its face, loops, edges, vertices and
// edge uses follow the script's objects, and sm_MakeConsistentClassifications may walk its real
// topology. The B-rep is deleted when the run returns.
#include "sc_shim.h"
#include "sc_one_face_brep.h"
#include "StdAfx.h"
#include <SmBSplineCurve.h>
#include <SmBSplineSurface.h>
#include <SmCacheMgr.h>
#include <SmCircle.h>
#include <SmContext.h>
#include <SmCrvOnSurf.h>
#include <SmCurve.h>
#include <SmCurveCache.h>
#include <SmCurveClass.h>
#include <SmEdge.h>
#include <SmEllipse.h>
#include <SmExtent1d.h>
#include <SmFace.h>
#include <SmGeomUtility.h>
#include <SmGraphicsExtern.h>
#include <SmGraphicsOutput.h>
#include <SmHermiteCurve.h>
#include <SmLine.h>
#include <SmLoop.h>
#include <SmNurbsCrv.h>
#include <SmNurbsSrf.h>
#include <SmPlane.h>
#include <SmPointClass.h>
#include <SmSolutionArray.h>
#include <SmSurface.h>
#include <SmSurfaceIntersector.h>
#include <SmTArray.h>
#include <SmTol.h>
#include <SmTopologyIntersector.h>
#include <SmTrimSrfCache.h>
#include <SmVector3d.h>
#include <SmVertex.h>
#include <nurbs.h>
#include <map>
#include <vector>
#include "sc_sm.h"
using sc::put_vec;
// global in the library (SmCurveClass.cpp), not declared in a header
SmXSectTol3d sm_GetXSectTol2dOr3d_ForClassifyPair(const SmCurve* cpSrcCurve, const SmSurface* cpSurface1,
double dSrcCurveOrObj1_Param,
SmZoneTol3d sSrcCurveOrObj1_ZoneTol3d,
double dObj2_SrcCurveParam, SmZoneTol3d sObj2_ZoneTol3d);
namespace sc_cc_p1
{
// clang-format off
// ---- verbatim: SmCurveClass.cpp lines 512-698: sm_SetXSectCurveThroughPoint
static SmStatus sm_SetXSectCurveThroughPoint
(SmCurve * pSrcCurve3d, // i/o: SrcCurve, expected to be CurveClassification->m_cpCurve
SmZoneTol3d s3DCurveZoneTol3d, // in : 3D Curve's ZoneTol3d - use SmCurveClassification::SrcZoneTol3d when 3D Curve is the m_cpCurve
SmTArray<SmBSplineCurve*> & rUVCurve, // i/o: UVTrimCurves sharing pSrcCurve3d parameterization, possibly modified
SmTArray<const SmSurface*> & rSurface, // in : Surfaces intersecting 3DCurve corresponding with rUVCurve array,
// the intersection of 1st 2 rSurface surfaces define pSrcCurve3d intersection curve,
// if rUVCurve.GetSize() == 1, rSurface.GetSize is still expected to equal 2.
SmTArray<SmVector2d*> & rUVPoint, // in : Intersection through UV-Points on corresponding surfaces of rSurface array
SmBoolean & bModifiedCurve, // out: TRUE=Curves updated, FALSE=No need to modify curves
double & dParam, // i/o: in = guessed Curve parameter assigned to this xsect throughPoint
// out = actual Curve parameter assigned to this xsect throughPoint by projecting sssPoint onto pSrcCurve3d
double & dDeviation, // out: deviation from xsect point to pSrcCurve3d after modifications
SmBoolean & bSet3DPoint, // out: TRUE = p3DPoint was set, FALSE=wasn't
SmVector3d * p3DPoint) // out: xsect point 3D value
{
// init outputs - curve not modified
bModifiedCurve = FALSE ;
bSet3DPoint = FALSE ;
// check state
SmBoolean bOKInputs = ( pSrcCurve3d != NULL
// && rUVCurve.GetSize() >= 0 - GetSize() returns ULONG which is always >= 0
&& rUVCurve.GetSize() <= rSurface.GetSize()
&& rSurface.GetSize() >= 2
&& rSurface.GetSize() == rUVPoint.GetSize()
&& p3DPoint != NULL) ;
SM_ASSERT(bOKInputs) ;
// locals
//double dOrigParam = dParam ;
SmStatus sRtn = SM_ERR ;
// 1st the surface evaluations and setting p3DPoint
if(rSurface.GetSize() < 2)
{ return(SM_ERR) ; }
// temporarily allow out of bound evaluations for surface[0], surface[1]
SmBoolean bTmp0 = FALSE, bTmp1 = FALSE ;
SmBSplineSurface * pBSplineSurface0 = SM_CAST_PTR(SmBSplineSurface, rSurface[0]) ;
SmBSplineSurface * pBSplineSurface1 = SM_CAST_PTR(SmBSplineSurface, rSurface[1]) ;
SmTemporaryChangeValue<SmBoolean> sClean0(pBSplineSurface0 ? pBSplineSurface0->GetOutOfBoundsEnabled() : bTmp0, TRUE) ;
SmTemporaryChangeValue<SmBoolean> sClean1(pBSplineSurface1 ? pBSplineSurface1->GetOutOfBoundsEnabled() : bTmp1, TRUE) ;
// evaluate each surface at the given intersection parameter points
SmVector3d aSurfVal0[2][2], aSurfVal1[2][2] ;
sRtn = rSurface[0]->Evaluate(*rUVPoint[0], 1, 1, TRUE, TRUE, TRUE, aSurfVal0[0]) ;
rSurface[1]->Evaluate(*rUVPoint[1], 1, 1, TRUE, TRUE, TRUE, aSurfVal1[0]) ;
// tolerances
SM_ASSERT_BREAK_MSG(pSrcCurve3d->GetDim() == 3, _T("Bad CurveClass 3d Assumption switch call to sm_GetXSectTol2dOr3d_ForClassifyPair()")) ;
SmZoneTol3d sXSectObjZoneTol3d = SmTol::GetZoneTol3d_ForXSectResult(rSurface[0], rSurface[1]) ;
SmXSectTol3d sXSectTol3d = SmTol::GetXSectTol3d(s3DCurveZoneTol3d, // was 1.0E-5 ;
sXSectObjZoneTol3d) ;
SmXSectTol3d sXSectTol3dSq = sXSectTol3d * sXSectTol3d ; // was 1.0E-10 ;
double dAngBoundRad = 6 * SmTol::GetAngTolRad() ; // match previous behavior SM_DEG2RAD(3.0) ; now 0.5
SmScaledZero sScaledZero = SmTol::GetEffZero() ;
// let surf/surf/surf xsect point = pSurface[0] evaluation
if(sRtn == SM_SUCCESS)
{ bSet3DPoint = TRUE ;
*p3DPoint = aSurfVal0[0][0] ;
}
// Abort when inputs are confused (after setting output variables).
if(bOKInputs == FALSE)
{ return(SM_ERR) ;
}
// check state - surface xsect projections are within XSectTol3d(Curve,Surface) of one another
double dMaxDeviationSq = aSurfVal0[0][0].DistanceBetweenSquared(aSurfVal1[0][0]) ;
if(dMaxDeviationSq > sXSectTol3dSq)
{
SM_DBG_WARN(_T("Tighten Intersection Curve heuristic not applicable - \n ThroughPoint not added: Given Surface xSectPt pair too far apart to add ThroughPoint to xSect curve to tighten tolerances\n")) ;
// SM_DBG_WARN(_T("Distance between two surface intersection points check failed - points too far apart\n")) ;
// return(SM_ERR);
}
// project p3DPoint onto pSrcCurve3d to get its param value
SmBoolean bCurveProjectAnswer = FALSE ;
SmSolution sCurveProjection ;
dParam = pSrcCurve3d->GetNaturalInterval().ClampValue(dParam);
pSrcCurve3d->LocalPointSolve(pSrcCurve3d->GetNaturalInterval(),
SM_SO_MINIMIZE,
*p3DPoint,
NULL, NULL, NULL,
dParam, // in : guess param
bCurveProjectAnswer, // out: TRUE=found answer
sCurveProjection) ; // out: SmSolution container
// when the point fails to project to the curve
SM_ASSERT(bCurveProjectAnswer == TRUE) ;
if(bCurveProjectAnswer == FALSE)
{
SM_DBG_WARN(_T("SSSPoint projection to XSect Curve failed\n")) ;
return(SM_ERR) ;
}
// grab the projected point's curve param and curve/point deviation values
dParam = sCurveProjection.m_vStart[0] ;
dDeviation = sCurveProjection.m_vStart.m_dSolutionValue ;
// Note: sXSectTol3d = XSectTol3d(CurveSrcZoneTol3d,Surface)
if(dDeviation < sXSectTol3d)
{
return(SM_SUCCESS) ;
}
// evaluate the curve at the param value for pos and tangent information
SmVector3d sSrcCrvPt3d[2] ;
pSrcCurve3d->Evaluate(dParam, 1, TRUE, sSrcCrvPt3d) ;
// skip curves close enough to xsectPoint
SmVector3d sGap = sSrcCrvPt3d[0] - *p3DPoint ;
#if SM_DEBUG_CODE
// set through point tangent = cross(surf1Normal,surf2Normal) ;
SM_ASSERT(aSurfVal0[1][0].LengthSquared() > SmTol::GetEffZeroSq());
SM_ASSERT(aSurfVal0[0][1].LengthSquared() > SmTol::GetEffZeroSq());
SM_ASSERT(aSurfVal1[1][0].LengthSquared() > SmTol::GetEffZeroSq());
SM_ASSERT(aSurfVal1[0][1].LengthSquared() > SmTol::GetEffZeroSq());
#endif // SM_DEBUG_CODE
aSurfVal0[1][0].Unitize() ;
aSurfVal0[0][1].Unitize() ;
aSurfVal1[1][0].Unitize() ;
aSurfVal1[0][1].Unitize() ;
SmVector3d sNorm1 = aSurfVal0[1][0]*aSurfVal0[0][1] ;
SmVector3d sNorm2 = aSurfVal1[1][0]*aSurfVal1[0][1] ;
SmVector3d sXSectTangent3d = sNorm1 * sNorm2 ;
// skip curve modification when the throughPoint is close
// enough to a tangent surf/surf intersection point that
// the tangent vector can't easily be computed. Experience
// has shown inserting throughpoints in these cases
// easily corrupt intersection-curve shapes by
// adding doglegs and hooks to the geometry shape.
// sXSectTangent3d == 0 for truly tangent surfaces
// 0 for points at poles
// 0 points at poorly constructed surface (1st Dir U and V vecs are parallel)
if(sXSectTangent3d.LengthSquared() <= 100 * sScaledZero) // this could be broken down in to an angle test with AngTol and a SpeedTest with ScaledZero tol
{ return(SM_SUCCESS) ; } // but I can't predict how that will change things - this is good enough for now
// and to change would take a study of using this over a large set of examples - (Examples not available)
// ensure Tangent direction is same as curve's direction
if(sXSectTangent3d.Dot(sSrcCrvPt3d[1]) < 0.0)
{
sXSectTangent3d = -sXSectTangent3d ;
}
// Surf/Surf XSectLine Tang direction is not nearly parallel to pSrcCurve3d's tangent
double dTangAngle = 0.0 ;
sXSectTangent3d.AngleBetween(sSrcCrvPt3d[1], dTangAngle) ;
if(dTangAngle > dAngBoundRad) // works well at 3 degrees - could try bigger limits to see how it goes
{ return(SM_SUCCESS) ; }
// unitize the tangent vector
sXSectTangent3d.Unitize() ;
// insert throughPoint=p3DPoint (tangent=sXSectTangent3d) at dParam into pSrcCurve3d
if(pSrcCurve3d->IsKindOf(SmBSplineCurve_TYPE))
{
// force 3DCurve to interpolate v3DPoint point with given tangent
sRtn = ((SmBSplineCurve *)pSrcCurve3d)->SetIntersectionCurveThroughPoint
(dParam, // i/o: suggested param value updated to actual param value
*p3DPoint, // in : through point position
&sXSectTangent3d, // in : optional tangent value, NULL to ignore
rUVCurve, // in : UVTrimCurves associated with this curve, expected (not required) to equal number of surfaces
rSurface, // in : corresponding surfaces for each pUVCurve
rUVPoint, // in : UVPoint on pSurface corresponding to Through3dPoint,
// A NULL entry=project crThrough3dPoint onto pSurface
bModifiedCurve) ; // out: TRUE=modified curve, FALSE=didn't
// in : smallest allowed size for new spans.
// A curve span is either split and updated or
// just updated. Splitting is skipped when the split
// point would create a span less than dMinimunSpanPercent
// of the current span size.
// When Successful - reset dDeviation
if(sRtn == SM_SUCCESS) { dDeviation = 0.0 ; }
else { bModifiedCurve = FALSE; }
} // end need to insert throughPoint check
// all done - succesfully
return(SM_SUCCESS) ;
} // end sm_SetXSectCurveThroughPoint
// ---- verbatim: SmCurveClass.cpp lines 726-960: sm_SetXSectEdgeThroughPoint
static SmStatus sm_SetXSectEdgeThroughPoint
(SmEdge * pEdge, // i/o: 3D Edge to modify
SmPoint3d * pThroughPoint, // in : point to interpolate
SmTArray<const SmSurface*> & rSurface, // in : two surfaces whose xsect define shape of pEdge
SmTArray<SmVector2d*> & rUVPoint, // in : 3DPoint associated UVPoints for surfaces in rSurface
double & dParam, // i/o: in = guessed pEdge Curve parameter assigned to this xsect throughPoint
// out = pEdge Curve parameter assigned to this xsect throughPoint by projecting sssPoint onto pSrcCurve3d
double & dDeviation, // out: pThroughPoint to edge distance
SmBoolean & bModifiedCurve) // out: TRUE=Curves updated and outputs are set
{
// init outputs - curve not modified
bModifiedCurve = FALSE ;
// locals
SmTArray<SmFace*> aFace ;
SmCurve * pSrcCurve3d = pEdge->GetCurve() ;
SmZoneTol3d s3DCurveZoneTol3d = SmTol::GetZoneTol3d(pEdge) ;
pEdge->GetFaces(aFace) ;
SmSolution sCurveProjection, sSurfaceProjection ;
SmPoint3d sTempPoint ;
SmVector3d sSrcCrvPt3d[2] ;
SmBoolean bCurveProjectAnswer = FALSE ;
SmExtent1d sEdgeInterval = pEdge->GetInterval() ;
SmExtent1d sCurveInterval = pSrcCurve3d->GetNaturalInterval() ;
SmBoolean bCommonCurveEdgeInterval = ( sEdgeInterval.GetMin() == sCurveInterval.GetMin()
&& sEdgeInterval.GetMax() == sCurveInterval.GetMax()) ? TRUE : FALSE ;
// check state - given or found valid pointers to edge, curve, points, and surfaces
if( pEdge == NULL
|| pThroughPoint == NULL
|| pSrcCurve3d == NULL
|| rSurface.GetSize() < 2
|| rUVPoint.GetSize() != rSurface.GetSize())
{ return(SM_ERR) ; }
// project pThroughPoint onto pCurve
pSrcCurve3d->LocalPointSolve(pEdge->GetInterval(),
SM_SO_MINIMIZE,
*pThroughPoint,
NULL, NULL, NULL,
dParam, // in : guess param
bCurveProjectAnswer, // out: TRUE=found answer
sCurveProjection) ; // out: SmSolution container
// error - when no projected point is found
if(bCurveProjectAnswer == FALSE)
{ return(SM_ERR) ; }
// grab the projected point's param and deviaton values
dParam = sCurveProjection.m_vStart[0] ;
dDeviation = sCurveProjection.m_vStart.m_dSolutionValue ;
// evaluate the curve at the param value for pos and tangent information
pSrcCurve3d->Evaluate(dParam, 1, TRUE, sSrcCrvPt3d) ;
// compute a tangent value for the throughPoint
// temporarily allow out of bound evaluations for surface[0], surface[1]
SmBoolean bTmp0 = FALSE, bTmp1 = FALSE ;
SmBSplineSurface * pBSplineSurface0 = SM_CAST_PTR(SmBSplineSurface, rSurface[0]) ;
SmBSplineSurface * pBSplineSurface1 = SM_CAST_PTR(SmBSplineSurface, rSurface[1]) ;
SmTemporaryChangeValue<SmBoolean> sClean0(pBSplineSurface0 ? pBSplineSurface0->GetOutOfBoundsEnabled() : bTmp0, TRUE) ;
SmTemporaryChangeValue<SmBoolean> sClean1(pBSplineSurface1 ? pBSplineSurface1->GetOutOfBoundsEnabled() : bTmp1, TRUE) ;
SmVector3d aSurfVal0[2][2], aSurfVal1[2][2] ;
rSurface[0]->Evaluate(*rUVPoint[0], 1, 1, TRUE, TRUE, TRUE, aSurfVal0[0]) ;
rSurface[1]->Evaluate(*rUVPoint[1], 1, 1, TRUE, TRUE, TRUE, aSurfVal1[0]) ;
// tolerances s3DCurveZoneTol3d
SM_ASSERT_BREAK_MSG(pSrcCurve3d->GetDim() == 3, _T("Bad CurveClass 3d Assumption switch call to sm_GetXSectTol2dOr3d_ForClassifyPair()")) ;
SmXSectTol3d sXSectTol3d = SmTol::GetXSectTol3d(s3DCurveZoneTol3d, // was 1.0E-5 ;
smos_Max(SmTol::GetZoneTol3d(rSurface[0]),
SmTol::GetZoneTol3d(rSurface[1]))) ;
SmXSectTol3d sXSectTol3dSq = sXSectTol3d * sXSectTol3d ; // was 1.0E-10 ;
SmScaledZero sScaledZero = SmTol::GetEffZero() ;
double dAngBoundRad = 6 * SmTol::GetAngTolRad() ; // was SM_DEG2RAD(3.0) ; now 0.5
// check state - surface xsect projections are near one another
if( pThroughPoint->DistanceBetweenSquared(aSurfVal0[0][0]) > sXSectTol3dSq
|| pThroughPoint->DistanceBetweenSquared(aSurfVal1[0][0]) > sXSectTol3dSq
|| aSurfVal0[0][0].DistanceBetweenSquared(aSurfVal1[0][0]) > sXSectTol3dSq)
{ return(SM_ERR) ; }
// set through point tangent = cross(surf1Normal,surf2Normal) ;
SM_ASSERT(aSurfVal0[1][0].LengthSquared() > sScaledZero) ;
SM_ASSERT(aSurfVal0[0][1].LengthSquared() > sScaledZero) ;
SM_ASSERT(aSurfVal1[1][0].LengthSquared() > sScaledZero) ;
SM_ASSERT(aSurfVal1[0][1].LengthSquared() > sScaledZero) ;
aSurfVal0[1][0].Unitize() ;
aSurfVal0[0][1].Unitize() ;
aSurfVal1[1][0].Unitize() ;
aSurfVal1[0][1].Unitize() ;
SmVector3d sNorm1 = aSurfVal0[1][0]*aSurfVal0[0][1] ;
SmVector3d sNorm2 = aSurfVal1[1][0]*aSurfVal1[0][1] ;
SmVector3d sXSectTangent3d = sNorm1 * sNorm2 ;
if(sXSectTangent3d.LengthSquared() <= sScaledZero)
{
// the surfaces are tangent at this point
// Let tangent = CurrentCurveTangent
// gwc: We really should optimize here to find the tangent value
sXSectTangent3d = sSrcCrvPt3d[1] ;
}
// ensure Tangent direction is same as curve's direction
if(sXSectTangent3d.Dot(sSrcCrvPt3d[1]) < 0.0)
{
sXSectTangent3d = -sXSectTangent3d ;
}
// skip curve modification when the throughPoint is close
// enough to a tangent surf/surf intersection point that
// the tangent vector can't easily be computed. Experience
// has shown inserting throughpoints in these cases
// easily corrupt intersection-curve shapes by
// adding doglegs and hooks to the geometry shape.
// sXSectTangent3d == 0 for truly tangent surfaces, or
// sXSectTangent3d not nearly parallel to sSrcCrvPt3d[1] for nearly
// tangent surfaces.
double dTangAngle = 0.0 ;
sXSectTangent3d.AngleBetween(sSrcCrvPt3d[1], dTangAngle) ;
if( sXSectTangent3d.LengthSquared() <= sScaledZero
|| dTangAngle > dAngBoundRad) // works well at 3 degrees - could try bigger limits to see how it goes
{
return(SM_SUCCESS) ;
}
// unitize the tangent vector
sXSectTangent3d.Unitize() ;
// force the 3DCurve to interpolate the point
// insert throughPoint=pThroughPoint (tangent=sXSectTangent3d) at dParam into pSrcCurve3d
if( pSrcCurve3d->IsKindOf(SmBSplineCurve_TYPE)
&& sCurveProjection.m_vStart.m_dSolutionValue > s3DCurveZoneTol3d/2000) // usually about 1e-8 = ApproxTol/1000 (2 gets ApproxTol)
{
SmTArray<SmBSplineCurve*> aUVCurve ;
SmTArray<const SmSurface*> aSurface ;
SmTArray<SmVector2d*> aOptUVPoint ;
// for every face
for(ULONG ii=0;ii<aFace.GetSize();ii++)
{
// get edgeuse connecting pEdge to this face
SmEdgeuse *pEdgeuse = pEdge->GetEdgeuseOfFace(aFace[ii]) ;
// Build the SetIntersectionCurveThroughPoint UVTrimCurve arrays
if( pEdgeuse ) aUVCurve.Add(pEdgeuse->GetUVTrimCurve()) ;
aSurface.Add(aFace[ii]->GetSurface()) ;
aOptUVPoint.Add(NULL) ;
} // end iter every face
// GWC:EXPERIMENT - Leave the end-points of existing edge-curves alone.
// They may be attached to vertices and part of CEdges.
// Just add-through points.
// Leave the associated UVTrimCurves alone. The parameterization
// of an Edge->Curve that has been trimmed by a previous Curve Merge will
// be different than its Edge->UVTrimcurves.
SM_TOL_LINE double dMinimumSpanPercent = 1.0E-6 ; // stop using 'move existing through point cases'
aUVCurve.ReSet() ; // stop editing UVTrimCurves
aSurface.ReSet() ;
aOptUVPoint.ReSet() ;
// force 3DCurve to interpolate pThroughPoint with given tangent
// Note: Curve's natural domain may be modified
SmStatus eStat = ((SmBSplineCurve *)pSrcCurve3d)->SetIntersectionCurveThroughPoint
(dParam, // i/o: suggested param value updated to actual param value
*pThroughPoint, // in : through point position
&sXSectTangent3d, // in : optional tangent value, NULL to ignore
aUVCurve, // in : UVTrimCurves associated with this curve, expected (not required) to equal number of surfaces
aSurface, // in : corresponding surfaces for each pUVCurve
aOptUVPoint, // in : UVPoint on pSurface corresponding to Through3dPoint,
// A NULL entry=project crThrough3dPoint onto pSurface
bModifiedCurve, // out: TRUE=modified curve, FALSE=didn't
dMinimumSpanPercent) ; // in : smallest allowed size for new spans.
// A curve span is either split and updated or
// just updated. Splitting is skipped when the split
// point would create a span less than dMinimunSpanPercent
// of the current span size.
// If the curve was not changed, then we're done.
if ( eStat != SM_SUCCESS || ! bModifiedCurve )
{ return SM_SUCCESS; }
// restore common curve/edge interval if its stopped being common
// by scaling the curve's parameterization
sEdgeInterval = pEdge->GetInterval() ;
sCurveInterval = pSrcCurve3d->GetNaturalInterval() ;
double dEMin = sEdgeInterval.GetMin() ;
double dEMax = sEdgeInterval.GetMax() ;
double dCMin = sCurveInterval.GetMin() ;
double dCMax = sCurveInterval.GetMax() ;
if( bCommonCurveEdgeInterval
&& ( fabs(dEMin - dCMin) > sScaledZero
|| fabs(dEMax - dCMax) > sScaledZero))
{
// we should be fixing the case where one end of the curve's
// natural interval was modified.
SM_ASSERT( (fabs(dEMin - dCMin) <= sScaledZero && dEMax == dParam)
|| (fabs(dEMax - dCMax) <= sScaledZero && dEMin == dParam)) ;
// change the parameter values of every pSrcCurve3d knot
((SmBSplineCurve *)pSrcCurve3d)->ScaleKnotVector(dEMin, dEMax) ;
// change the parameter values of every UVTrimCurve knot
for(ULONG ii=0;ii<aUVCurve.GetSize();ii++)
{
((SmBSplineCurve *)aUVCurve[ii])->ScaleKnotVector(dEMin, dEMax) ;
} // end iter every UVTrimCurve
} // end need to restore common curve/edge interval check
// reset the deviation value
dDeviation = 0.0 ;
// GWC:CHANGE_SSSUVTRIMCURVE
// remove obsolete UVTrimCurves from edge->Edgeuses
SmTArray<SmEdgeuse*> aEdgeuses ;
pEdge->GetEdgeuses(aEdgeuses) ;
for(ULONG ii=0;ii<aEdgeuses.GetSize();ii++)
{
aEdgeuses[ii]->SetUVTrimCurve(NULL, 0.0, TRUE) ; // TRUE = delete existing UVTrimCurve
}
// inform the public that the curves were updated
bModifiedCurve = TRUE ;
} // end need to adjust throughPoint Check
// all done - successfully
return(SM_SUCCESS) ;
} // end sm_SetXSectEdgeThroughPoint
// ---- verbatim: SmCurveClass.cpp lines 968-1080: sm_GetCCISurfaceGaps
static SmStatus sm_GetCCISurfaceGaps
(SmPoint3d & rClassCurvePoint3d, // in : Current ClassificationCurve Solution Point3d
SmPoint3d & rEdgeCurvePoint, // in : Current Edge Solution Point
SmSolution & rSolution, // in : ClassificationCurve/EdgeCurve intersection solution
SmTArray <const SmSurface *> & rSurfaces, // in : List of all Surfaces connected to this intersection point
// ordered:[CurveSurfaces, EdgeSurfaces, commonEdgeCurveSurface, EdgeBooleanMateSurfaces]
SmTArray <const SmCurve * > & rUVCurves, // in : edgeuse UVTrimCurve (may be NULL)
ULONG lEdgeSurfaceCount, // NotUsed: in : ignored - number of EdgeSurfaces in rSurfaces (number of Curve Surfaces == 2, the rest are pEMate->Surfaces)
double & dCurveEdgeGap, // out: CurvePoint/EdgePoint gap
double & dCurveSurfaceGap, // out: CurvePoint/SurfacesConnectedToCurve max gap
double & dEdgeSurfaceGap, // out: EdgePoint/SurfacesConnectedToEdge max gap
SmTArray <SmPoint2d> & rUVPoints) // out: Surface UVPoint for each GapVector
// plus one for the EdgeCurvePoint/CommonSurf gap
{
SM_REF1(lEdgeSurfaceCount) ;
// init output
dCurveSurfaceGap = 0.0 ;
dEdgeSurfaceGap = 0.0 ;
rUVPoints.ReSet() ;
// Get CurvePoint/EdgePoint gap size
dCurveEdgeGap = rClassCurvePoint3d.DistanceBetween(rEdgeCurvePoint) ;
// for every surface
SmSolutionArray sSolutions;
double dMaxDropDist = 1.0 ;
ULONG ii ;
for(ii=0;ii<rSurfaces.GetSize();ii++)
{
// get target Point/Surface combination
const SmSurface *pSurface = rSurfaces[ii] ;
const SmCurve *pUVCurve = rUVCurves[ii] ;
SmPoint3d *pPoint = ii < 2
? &rClassCurvePoint3d
: &rEdgeCurvePoint ;
// When posible use cheap localSolve else use expensive Global Solve
SmSolution sSol ;
SmSolution *pSol = NULL ;
if(pUVCurve)
{
// get UVGuessPoint
SmPoint3d sGuessPoint ;
double dTParam = ii < 2
? rSolution.m_vStart.m_adParameters[0]
: rSolution.m_vStart.m_adParameters[1] ;
pUVCurve->EvaluatePoint(dTParam, sGuessPoint) ;
SmPoint2d sUVPoint(sGuessPoint.x, sGuessPoint.y) ;
// get classCurvePoint/Surf dist from cheap local solve
SmBoolean bFoundAnswer ;
pSurface->LocalPointSolve(pSurface->GetNaturalUVDomain(),
SM_SO_MINIMIZE,
*pPoint,
sUVPoint,
bFoundAnswer,
sSol) ;
if(bFoundAnswer) { pSol = &sSol ; }
else { pSol = NULL ;
SM_DBG_WARN(_T("XSectCurvePoint failed LocalDrop to Surface")) ;
}
} // end LocalSolve from UVTrimCurve Point branch
else // need a GlobalSolve branch
{
// get classCurvePoint/surf dist without checking trim boundaries
sSolutions.ReSet() ;
SmSurfaceCache *pSC = smsurf_GetSurfaceCache(pSurface);
// Turn off point testing so GlobalPointSolve() will keep all point solutions
// without classifying the solution point against the trim boundaries.
SmTemporaryChangeValue<SmBoolean> sChange(pSC->m_bPointTestEnabled,FALSE);
// Get a tolerance to use.
SmVector2d sSize;
pSurface->ApproximateSize( sSize );
double dTol = 0.001 * sSize.Length();
pSurface->GlobalPointSolve(pSurface->GetNaturalUVDomain(),
SM_SO_MINIMIZE,
*pPoint,
dTol, // not: dMaxDropDist,
&dMaxDropDist,
SM_SR_SINGLE,
sSolutions) ;
if(sSolutions.GetSize() > 0)
{ pSol = &(sSolutions[0]) ; }
else { return(SM_ERR) ;
}
} // end GlobalSolve ClassCurvePoint/Surf distance branch
// accumulate UVPoints and distances
if(pSol)
{
double dGap = pSol->m_vStart.m_dSolutionValue ;
SmPoint2d sUVPoint(pSol->m_vStart.m_adParameters[0],
pSol->m_vStart.m_adParameters[1]) ;
rUVPoints.Add(sUVPoint) ;
// save max CurvePoint/Surface and EdgePoint/Surface gaps
if(ii < 2) { if(dGap > dCurveSurfaceGap) dCurveSurfaceGap = dGap ; }
else { if(dGap > dEdgeSurfaceGap ) dEdgeSurfaceGap = dGap ; }
}
else
{
// Point failed to drop to connected surface
return(SM_ERR) ;
}
} // end iter every surface looking for ClassCurvePoint/Surf distance
// all done
return(SM_SUCCESS) ;
} // end sm_GetCCISurfaceGaps
// ---- verbatim: SmCurveClass.cpp lines 1106-1235: sm_GetCCIGeometry
static SmStatus sm_GetCCIGeometry
(SmCurveClassification * pCurveClassification, // in : classification containing curve to check
SmSolution & rSolution, // NotUsed: in : ClassificationCurve/EdgeCurve intersection solution
SmEdge * pEdge, // in : Edge found to intersect the curve being classified
SmEdge *& pEMate, // out: edge->BooleanMateEdge
SmTArray <const SmSurface *> & rSurfaces, // out: List of all Surfaces connected to this intersection point
// ordered:[CurveSurfaces(2), EdgeSurfaces, CommonEdgeCurveSurface, EdgeBooleanMateSurfaces]
SmTArray <SmEdgeuse *> & rEdgeuses, // out: associated edgeuse connecting intersectionPoint to Surface (may be NULL)
SmTArray <const SmCurve * > & rUVCurves, // out: edgeuse UVTrimCurve (may be NULL)
ULONG & lEdgeSurfaceCount) // out: number of EdgeSurfaces in rSurfaces including the duplicate CommonEdgeCurveSurface
{
SM_REF1(rSolution) ;
// init output
pEMate = NULL ;
rSurfaces.ReSet() ;
rEdgeuses.ReSet() ;
rUVCurves.ReSet() ;
lEdgeSurfaceCount = 0 ;
// locals
ULONG ii ;
const SmSurface *pSurface1 = pCurveClassification->GetSurface1() ;
const SmSurface *pSurface2 = pCurveClassification->GetSurface2() ;
ULONG lCommonSurf = SM_BIG_ULONG ;
// Part 1 - gather Surfaces, edgeuses, and UVTrimCurves connected to intersection Point
// gather the ClassCurve Surfaces and UVTrimCurves
if(pSurface1) { rSurfaces.Add(pSurface1) ;
rEdgeuses.Add(NULL) ;
rUVCurves.Add(pCurveClassification->GetUVCurve1()) ;
}
if(pSurface2) { rSurfaces.Add(pSurface2) ;
rEdgeuses.Add(NULL) ;
rUVCurves.Add(pCurveClassification->GetUVCurve2()) ;
}
// get Faces connected to Edge and Edge->BooleanMate
SmTArray<SmFace *> sFaces ;
pEdge->GetFaces(sFaces) ;
ULONG lEdgeFaceCount = sFaces.GetSize() ;
// When curve classification is being done as part of a boolean operation
const SmTopologyIntersector *cpTI = pCurveClassification->GetTopologyIntersector() ;
if(cpTI)
{
// look for edge->BooleanMate
pEMate = cpTI->GetOtherMate(pEdge)
? (SmEdge *)cpTI->GetOtherMate(pEdge)
: (SmEdge *)cpTI->GetBrepMate(pEdge) ;
// when pEdge has a boolean operation temporary mate
if(pEMate)
{
// add in Faces and Edgeuses connected to Edge->BooleanMate
SmTArray<SmFace*> tFaces ;
SM_ASSERT(pEMate->IsKindOf(SmEdge_TYPE)) ;
pEMate->GetFaces(tFaces) ;
sFaces.Append(tFaces) ;
}
} // end possible temporary mating edge check
// gather unique surfaces and edgeuses connected to this intersection point
ULONG lEdgeCurveCommonSurf = 0 ;
for(ii=0;ii<sFaces.GetSize();ii++)
{
SmFace *pFace = sFaces[ii] ;
SmSurface *pSurf = pFace->GetSurface() ;
// when pSurf is not in sSurfaces list - add it
ULONG lIndex ;
if(FALSE == rSurfaces.FindElement(pSurf, lIndex))
{
const SmEdgeuse *pEdgeuse = ii<lEdgeFaceCount
? pEdge->GetEdgeuseOfFace(pFace)
: pEMate->GetEdgeuseOfFace(pFace) ;
if(pEdgeuse != NULL)
{
rSurfaces.Add( pSurf ); // Should this be outside of if?
rEdgeuses.Add( (SmEdgeuse *)pEdgeuse );
// don't use pEMate UVTrimCurves (they have a different parameterization)
rUVCurves.Add( (pEdgeuse && ii < lEdgeFaceCount)
? pEdgeuse->GetUVTrimCurve()
: NULL );
}
if(ii <lEdgeFaceCount) { lEdgeSurfaceCount ++ ; }
}
// remember an edge->Surface which is shared with the curve (nonManifold may have 2)
SM_ASSERT(pSurface1 != NULL) ;
if (pSurf == pSurface1) { lEdgeCurveCommonSurf = 1 ;
lCommonSurf = ii ;
}
else if(pSurf == pSurface2) { lEdgeCurveCommonSurf = 2 ;
lCommonSurf = ii ;
}
// when we are done with edgeSurfaces
if( ii == lEdgeFaceCount - 1 && lCommonSurf < lEdgeFaceCount )
{
// add the commonSurf a 2nd time
pFace = sFaces[lCommonSurf] ;
pSurf = pFace->GetSurface() ;
const SmEdgeuse *pEdgeuse = pEdge->GetEdgeuseOfFace(pFace) ;
if(pEdgeuse)
{
rSurfaces.Add( pSurf );
rEdgeuses.Add( (SmEdgeuse *)pEdgeuse );
// don't use pEMate UVTrimCurves (they have a different parameterization)
rUVCurves.Add( pEdgeuse->GetUVTrimCurve() );
lEdgeSurfaceCount++;
}
}
} // end iter every face attached to this edge
// at least 1 edge or edge->BooleanMate face's surface is one of the intersection surfaces
SM_ASSERT( lEdgeCurveCommonSurf == 1
|| lEdgeCurveCommonSurf == 2) ;
SM_ASSERT(lCommonSurf != SM_BIG_ULONG) ;
// all done
return(SM_SUCCESS) ;
} // end sm_GetCCIGeometry
// ---- verbatim: SmCurveClass.cpp lines 1247-1625: sm_ApplyPointSolution
static SmStatus sm_ApplyPointSolution
(SmCurveClassification * pCurveClassification, // in : CurveClassification containing ClassCurve, NULL = leave ClassCurve alone
SmEdge * pEdge, // in : Edge, NULL = leave EdgeCurve alone
SmEdge * pEMate, // in : Edge->BooleanMateEdge, NULL = leave EdgeMateCurve alone
const SmSurface * pClassCurveSurface1, // in : ClassCurve Surf1
SmPoint2d * pClassCurveUVPoint1, // in : Desired Surf1 Solution point
const SmSurface * pClassCurveSurface2, // in : ClassCurve Surf2
SmPoint2d * pClassCurveUVPoint2, // in : Desired Surf2 Solution point
const SmSurface * pEdgeCurveSurface1, // in : pEdge/pEMate Surf1
SmPoint2d * pEdgeCurveUVPoint1, // in : Desired pEdge/pEMate1 Solution point
const SmSurface * pEdgeCurveSurface2, // in : pEdge/pEMate Surf2
SmPoint2d * pEdgeCurveUVPoint2, // in : Desired pEdge/pEMate2 Solution point
SmSolution & rSolution, // i/o: ClassCurve/Edge Solution being refined.
// Sol.m_vStart.Value, Param[0], and Param[1] are changed
SmPoint3d & rSSSPoint, // out: surf/surf/surf xsect point for this solution
SmBoolean & bSSSPointFlag, // out: TRUE=use Surf/surf/surf intersection point
SmBoolean & bModifiedCurve, // out: TRUE=Classification Curve modified, Classification should be redone
SmBoolean & bModifiedEdgeCurve, // out: TRUE=EdgeCurve modified, UVTrimCurves should be cleared
SmBoolean & bModifiedMateCurve, // out: TRUE=MateCurve modified, UVTrimCurves should be cleared
double & dDeviation) // out: deviation of curves from sssPoint
{
// check input - must have a pEdge if given a pEMate
SM_ASSERT(pEMate == NULL || pEdge != NULL) ;
// init output
bSSSPointFlag = FALSE ;
bModifiedCurve = FALSE ;
bModifiedEdgeCurve = FALSE ;
bModifiedMateCurve = FALSE ;
dDeviation = 0.0 ;
// locals
const SmCurve * pSrcCurve3d = pCurveClassification ? pCurveClassification->GetCurve() : NULL ;
const SmCurve * pEdgeCurve = pEdge ? pEdge->GetCurve() : NULL ;
SmZoneTol3d sSrcZoneTol3d = SmTol::GetSrcZoneTol3d(pCurveClassification) ;
SmStatus eStat;
#ifdef SM_DEBUG_CODE
static constexpr ULONG bDebugMe = FALSE ;
SmExtent2d sInitDomain ;
SmExtent2d sUVDomain1 = pClassCurveSurface1 ? pClassCurveSurface1->GetNaturalUVDomain() : sInitDomain ;
SmExtent2d sUVDomain2 = pClassCurveSurface2 ? pClassCurveSurface2->GetNaturalUVDomain() : sInitDomain ;
SmExtent2d sUVDomain3 = pEdgeCurveSurface1 ? pEdgeCurveSurface1->GetNaturalUVDomain() : sInitDomain ;
// output a message if any of the UVPoints are outside their surface boundary
if( (pCurveClassification != NULL && FALSE == sUVDomain1.ContainsPoint2d(*pClassCurveUVPoint1, SM_EFF_ZERO))
|| (pCurveClassification != NULL && FALSE == sUVDomain2.ContainsPoint2d(*pClassCurveUVPoint2, SM_EFF_ZERO))
|| ((pEdge != NULL || pEMate != NULL) && FALSE == sUVDomain3.ContainsPoint2d(*pEdgeCurveUVPoint1, SM_EFF_ZERO)))
{
SmVector2d sZero(0.0,0.0) ;
SmVector2d sGap1 = pCurveClassification ? *pClassCurveUVPoint1 - sUVDomain1.ClampPoint2d(*pClassCurveUVPoint1) : sZero;
SmVector2d sGap2 = pCurveClassification ? *pClassCurveUVPoint2 - sUVDomain2.ClampPoint2d(*pClassCurveUVPoint2) : sZero;
SmVector2d sGap3 = (pEdge != NULL || pEMate != NULL) ? *pEdgeCurveUVPoint1 - sUVDomain3.ClampPoint2d(*pEdgeCurveUVPoint1) : sZero ;
if(bDebugMe)
{
smgfx_Erase() ;
smgfx_SetLook(1,2, 0,0,1) ; if(pClassCurveSurface1) pClassCurveSurface1->DrawUV(2,2) ; sm_GraphicsLoop() ;
smgfx_SetLook(1,2, 0,1,0) ; if(pClassCurveSurface2) pClassCurveSurface2->DrawUV(2,2) ; sm_GraphicsLoop() ;
smgfx_SetLook(1,2, 0,1,1) ; if(pEdgeCurveSurface1 ) pEdgeCurveSurface1->DrawUV(2,2) ; sm_GraphicsLoop() ;
sm_GraphicsLoop() ;
} // end bDebug check
if(sGap1.LengthSquared() > SM_EFF_ZERO) { SM_DBG_WARN(_T("FYI: SSSPoint found outside surface1 NaturalUVDomain")) ;
if(!SM_IS_ZERO(sGap1.x)) ((SmBSplineSurface *)pClassCurveSurface1)->ExpandBoundarySpan(pClassCurveUVPoint1->x, SM_SP_U) ;
if(!SM_IS_ZERO(sGap1.y)) ((SmBSplineSurface *)pClassCurveSurface1)->ExpandBoundarySpan(pClassCurveUVPoint1->y, SM_SP_V) ;
}
if(sGap2.LengthSquared() > SM_EFF_ZERO) { SM_DBG_WARN(_T("FYI: SSSPoint found outside surface2 NaturalUVDomain")) ;
if(!SM_IS_ZERO(sGap2.x)) ((SmBSplineSurface *)pClassCurveSurface2)->ExpandBoundarySpan(pClassCurveUVPoint2->x, SM_SP_U) ;
if(!SM_IS_ZERO(sGap2.y)) ((SmBSplineSurface *)pClassCurveSurface2)->ExpandBoundarySpan(pClassCurveUVPoint2->y, SM_SP_V) ;
}
if(sGap3.LengthSquared() > SM_EFF_ZERO) { SM_DBG_WARN(_T("FYI: SSSPoint found outside surface3 NaturalUVDomain")) ;
if(!SM_IS_ZERO(sGap3.x)) ((SmBSplineSurface *)pEdgeCurveSurface1)->ExpandBoundarySpan(pEdgeCurveUVPoint1->x, SM_SP_U) ;
if(!SM_IS_ZERO(sGap3.y)) ((SmBSplineSurface *)pEdgeCurveSurface1)->ExpandBoundarySpan(pEdgeCurveUVPoint1->y, SM_SP_V) ;
}
} // end SSSPoint outside surface boundary check
const SmCurve* pEMateCurve = pEMate ? pEMate->GetCurve() : NULL;
SmFace* pFace1 = pClassCurveSurface1 ? (SmFace*)pClassCurveSurface1->GetFace() : NULL;
SmFace *pFace2 = pClassCurveSurface2 ? (SmFace *)pClassCurveSurface2->GetFace() : NULL ;
SmFace *pFace3 = pEdgeCurveSurface1 ? (SmFace *)pEdgeCurveSurface1->GetFace() : NULL ;
SmBrep *pBrep1 = pFace1 ? pFace1->GetBrep() : NULL ;
SmBrep *pBrep2 = pFace2 ? pFace2->GetBrep() : NULL ;
SmBrep *pBrep3 = pFace3 ? pFace3->GetBrep() : NULL ;
if(bDebugMe)
{
SmPoint3d sSurface1Point ;
SmBoolean bTmp0 = FALSE ;
SmBSplineSurface * pBSplineSurface0 = SM_CAST_PTR(SmBSplineSurface, pClassCurveSurface1) ;
SmTemporaryChangeValue<SmBoolean> sClean0(pBSplineSurface0 ? pBSplineSurface0->GetOutOfBoundsEnabled() : bTmp0, TRUE) ;
pClassCurveSurface1->EvaluatePoint(*pClassCurveUVPoint1,sSurface1Point) ;
smgfx_Erase() ;
smgfx_SetLook(1,2, 0,0,1) ; if(pBrep1) pBrep1->Draw() ; sm_GraphicsLoop() ;
smgfx_SetLook(1,2, 0,1,0) ; if(pBrep2 && pBrep2 != pBrep1) pBrep2->Draw() ; sm_GraphicsLoop() ;
smgfx_SetLook(1,2, 0,0,0) ; if(pBrep3 && pBrep3 != pBrep1 && pBrep3 != pBrep2) pBrep3->Draw() ; sm_GraphicsLoop() ;
smgfx_SetLook(1,2, 0,0,1) ; if(pClassCurveSurface1) pClassCurveSurface1->DrawUV() ; sm_GraphicsLoop() ;
smgfx_SetLook(1,2, 0,0,0) ; if(pFace1) { pFace1->Draw(SM_DM_CROSSHATCH) ; } sm_GraphicsLoop() ;
smgfx_SetLook(1,2, 0,1,0) ; if(pClassCurveSurface2) pClassCurveSurface2->DrawUV() ; sm_GraphicsLoop() ;
smgfx_SetLook(1,2, 0,0,0) ; if(pFace2) { pFace2->Draw(SM_DM_CROSSHATCH) ; } sm_GraphicsLoop() ;
smgfx_SetLook(1,2, 0,1,1) ; if(pEdgeCurveSurface1) pEdgeCurveSurface1->DrawUV() ; sm_GraphicsLoop() ;
smgfx_SetLook(1,2, 0,0,0) ; if(pFace3) { pFace3->Draw(SM_DM_CROSSHATCH) ; } sm_GraphicsLoop() ;
smgfx_SetLook(5,7, 1,0,1) ; if(pSrcCurve3d) pSrcCurve3d->Draw() ; sm_GraphicsLoop() ;
smgfx_SetLook(3,6, 0,1,0) ; if(pEdgeCurve) pEdgeCurve->Draw() ; sm_GraphicsLoop() ;
smgfx_SetLook(3,6, 0,1,0) ; if(pEMateCurve) pEMateCurve->Draw() ; sm_GraphicsLoop() ;
smgfx_SetLook(7,9, 1,0,0) ; sSurface1Point.Draw() ; sm_GraphicsLoop() ;
sm_GraphicsLoop() ;
} // end bDebug check
#endif // SM_DEBUG_CODE
// add switches to turn curve/edge through point interpolation on and off
SmBoolean gwcUpdateCurves = TRUE ; // system found to be more robust when gwcUpdateCurves is run
SmBoolean gwcUpdateEdges = TRUE ; // system found to be more robust when gwcUpdateEdges is run
// locals
SmTArray<SmBSplineCurve*> aUVCurve ;
SmTArray<const SmSurface*> aSurface ;
SmTArray<SmVector2d*> aUVPoint ;
SmBoolean bCurveFoundAnswer = FALSE ;
SmSolution sCurveProjection ;
double dParam, dTDeviation = 0.0 ;
// let rSSSPoint = pClassCurveSurface1 evaluation at the intersection point
SmStatus sRtn = (pSrcCurve3d && pClassCurveSurface1) ? pClassCurveSurface1->EvaluatePoint(*pClassCurveUVPoint1, rSSSPoint)
: (pSrcCurve3d && pClassCurveSurface2) ? pClassCurveSurface2->EvaluatePoint(*pClassCurveUVPoint2, rSSSPoint)
: ((pEdge || pEMate) && pEdgeCurveSurface1) ? pEdgeCurveSurface1->EvaluatePoint (*pEdgeCurveUVPoint1, rSSSPoint)
: SM_ERR ;
if( sRtn == SM_ERR )
return( SM_ERR );
// set closest point on curve to surf/surf/surf intersection point
if(pSrcCurve3d)
{
if(gwcUpdateCurves == TRUE) // system found to be more robust when gwcUpdateCurves TRUE
{
// set up sm_SetXSectCurveThroughPoint arguments
dParam = rSolution.m_vStart[0] ; // init pSrcCurve3d param guess = curve/curve xsect solution
aUVCurve.ReSet() ;
aSurface.ReSet() ;
aUVPoint.ReSet() ;
// load surfaces and UVPoints marking the intersection
aSurface.Add(pClassCurveSurface1) ;
aUVPoint.Add(pClassCurveUVPoint1) ;
aSurface.Add(pClassCurveSurface2) ;
aUVPoint.Add(pClassCurveUVPoint2) ;
// ensure surf/surf/surf solution is through point on classCurve and its UVTrimCurves
// side effects: curves with large tolerance are modified to interpolate xsect point
// rSSSPoint is set with 3DPoint value for intersection
// dParam is moved to best through point value
// dTDeviation is set with surf/surf/surf xsect Point to curve distance
SmBoolean bSetSSSPoint = FALSE ;
sm_SetXSectCurveThroughPoint
((SmCurve *)pSrcCurve3d, // i/o: 3D Curve, possibly modified
sSrcZoneTol3d, // in : pSrcCurve3d's ZoneTol3d value
aUVCurve, // i/o: UVTrimCurves sharing pSrcCurve3d parameterization, possibly modified
aSurface, // in : Surfaces intersecting 3DCurve corresponding with rUVCurve array,
// the intersection of 1st 2 rSurface surfaces define pSrcCurve3d intersection curve,
// if rUVCurve.GetSize() == 1, rSurface.GetSize is still expected to equal 2.
aUVPoint, // in : Intersection through UV-Points on corresponding surfaces of rSurface array
bModifiedCurve, // out: TRUE=Curves updated, FALSE=No need to modify curves
dParam, // i/o: in = guessed Curve parameter assigned to this xsect throughPoint
// out = actual Curve parameter assigned to this xsect throughPoint by projecting sssPoint onto pSrcCurve3d
dTDeviation, // out: deviation from xsect point to pSrcCurve3d after modifications
bSetSSSPoint, // out: TRUE = p3DPoint was set, FALSE=wasn't
&rSSSPoint) ; // out: xsect point 3D value
// inform the public - remove obsolete UVTrimCurves
if(bModifiedCurve)
{ bSSSPointFlag = TRUE; }
// move pSrcCurve3d parameter in rSolution to surf/surf/surf xsect point
rSolution.m_vStart[0] = dParam ;
// increase deviation to include distance to this curve from rSSSPoint
dDeviation = smos_Max(dDeviation, dTDeviation) ;
} // end gwcUpdateCurves == TRUE branch
else // don't add through points to xsect curves branch
{
// temporarily allow out of bounds evaluations for pClassCurveSurface1
SmBoolean bTmp0 = FALSE ;
SmBSplineSurface * pBSplineSurface0 = SM_CAST_PTR(SmBSplineSurface, pClassCurveSurface1) ;
SmTemporaryChangeValue<SmBoolean> sClean0(pBSplineSurface0 ? pBSplineSurface0->GetOutOfBoundsEnabled() : bTmp0, TRUE) ;
// let rSSSPoint = pClassCurveSurface1 evaluation at the intersection point
sRtn = pClassCurveSurface1->EvaluatePoint(*pClassCurveUVPoint1, rSSSPoint) ;
// inform the public
if(sRtn == SM_SUCCESS)
{ bSSSPointFlag = TRUE ; }
// move pSrcCurve3d parameter in rSolution to surf/surf/surf xsect point
eStat = pSrcCurve3d->LocalPointSolve(pCurveClassification->GetInterval(),
SM_SO_MINIMIZE,
rSSSPoint,
NULL, NULL, NULL,
rSolution.m_vStart[0],
bCurveFoundAnswer,
sCurveProjection) ;
// when we found a project point - save the parameter and deviation values
if ( eStat == SM_SUCCESS && bCurveFoundAnswer )
{
rSolution.m_vStart[0] = sCurveProjection.m_vStart[0] ;
// increase deviation to include distance to this curve from rSSSPoint
dDeviation = smos_Max(dDeviation, sCurveProjection.m_vStart.m_dSolutionValue) ;
} // end Point projected to curve check
} // end gwcUpdateCurves == FALSE branch
} // end pSrcCurve3d existence check
// set closest point on pEdge to surf/surf/surf intersection point
if(pEdge)
{
if(gwcUpdateEdges == TRUE)
{
// set arguments for sm_SetXSectEdgeThroughPoint() call.
dParam = rSolution.m_vStart[1] ;
aSurface.ReSet() ;
aUVPoint.ReSet() ;
if(pEdgeCurveSurface1) { aSurface.Add(pEdgeCurveSurface1) ;
aUVPoint.Add(pEdgeCurveUVPoint1) ;
}
if(pEdgeCurveSurface2) { aSurface.Add(pEdgeCurveSurface2) ;
aUVPoint.Add(pEdgeCurveUVPoint2) ;
}
// fix the pEdge and its UVTrimCurves - edge is only modified if it is piecewise C0
sm_SetXSectEdgeThroughPoint
(pEdge, // i/o: 3D Edge to modify
&rSSSPoint, // in : point to interpolate
aSurface, // in : two surfaces whose xsect define shape of pEdge
aUVPoint, // in : 3DPoint associated UVPoints for surfaces in rSurface
dParam, // i/o: in = guessed pEdge Curve parameter assigned to this xsect throughPoint
// out = pEdge Curve parameter assigned to this xsect throughPoint by projecting sssPoint onto pSrcCurve3d
dTDeviation, // out: pThroughPoint to edge distance
bModifiedEdgeCurve) ; // out: TRUE=Curves updated and outputs are set
// move pEdge parameter in rSolution to surf/surf/surf xsect point
rSolution.m_vStart[1] = dParam ;
// increase deviation to include distance to this edge from rSSSPoint
dDeviation = smos_Max(dDeviation, dTDeviation) ;
// inform the public - remove obsolete UVTrimCurves
if(bModifiedEdgeCurve)
{ bSSSPointFlag = TRUE;}
// when there is an associated pEMate
if(pEMate)
{
// use edge's solution to get a good guess on the mate
double dParam1 ;
pEdge->GetInterval().Inversion(dParam,dParam1) ;
dParam1 = pEMate->GetInterval().Evaluate(dParam1) ;
// fix the pEMate and its UVTrimCurves
sm_SetXSectEdgeThroughPoint
(pEMate, // i/o: 3D Edge to modify
&rSSSPoint, // in : point to interpolate
aSurface, // in : two surfaces whose xsect define shape of pEdge
aUVPoint, // in : 3DPoint associated UVPoints for surfaces in rSurface
dParam1, // i/o: in = guessed pEdge Curve parameter assigned to this xsect throughPoint
// out = pEdge Curve parameter assigned to this xsect throughPoint by projecting sssPoint onto pSrcCurve3d
dTDeviation, // out: pThroughPoint to edge distance
bModifiedMateCurve) ; // out: TRUE=Curves updated and outputs are set
// increase deviation to include distance to this edge from rSSSPoint
dDeviation = smos_Max(dDeviation, dTDeviation) ;
} // end pEMate existence check
} // end gwcUpdateEdges == TRUE branch
else
{
// move pEdgeCurve parameter in rSolution to surf/surf/surf xsect point
pEdgeCurve->LocalPointSolve(pEdge->GetInterval(),
SM_SO_MINIMIZE,
rSSSPoint,
NULL, NULL, NULL,
rSolution.m_vStart[1],
bCurveFoundAnswer,
sCurveProjection) ;
if(bCurveFoundAnswer) { rSolution.m_vStart[1] = sCurveProjection.m_vStart[0] ; }
// increase deviation to include distance from edge to rSSSPoint
dDeviation = smos_Max(dDeviation, sCurveProjection.m_vStart.m_dSolutionValue) ;
// to be complete we could also increase the dDeviation to include
// the distance from the pEMate edge to the rSSSPoint. But that
// should be the same as the pEdge to rSSSPoint distance.
} // end gwcUpdateEdges == FALSE branch
} // end pEdge or pEMate existence check
#ifdef SM_DEBUG_CODE
// check gaps
// get the xyz point for each surface solution domain point
SmBoolean bTmp0 = FALSE, bTmp1 = FALSE, bTmp2 = FALSE ;
SmBSplineSurface * pBSplineSurface0 = SM_CAST_PTR(SmBSplineSurface, pClassCurveSurface1) ;
SmBSplineSurface * pBSplineSurface1 = SM_CAST_PTR(SmBSplineSurface, pClassCurveSurface2) ;
SmBSplineSurface * pBSplineSurface2 = SM_CAST_PTR(SmBSplineSurface, pEdgeCurveSurface1 ) ;
SmTemporaryChangeValue<SmBoolean> sClean0(pBSplineSurface0 ? pBSplineSurface0->GetOutOfBoundsEnabled() : bTmp0, TRUE) ;
SmTemporaryChangeValue<SmBoolean> sClean1(pBSplineSurface1 ? pBSplineSurface1->GetOutOfBoundsEnabled() : bTmp1, TRUE) ;
SmTemporaryChangeValue<SmBoolean> sClean2(pBSplineSurface2 ? pBSplineSurface2->GetOutOfBoundsEnabled() : bTmp2, TRUE) ;
SmPoint3d sSurface1Point(0,0,0), sSurface2Point(0,0,0), sSurface3Point(0,0,0) ;
if(pCurveClassification) pClassCurveSurface1->EvaluatePoint(*pClassCurveUVPoint1,sSurface1Point) ;
if(pCurveClassification) pClassCurveSurface2->EvaluatePoint(*pClassCurveUVPoint2,sSurface2Point) ;
if(pEdge || pEMate) pEdgeCurveSurface1->EvaluatePoint (*pEdgeCurveUVPoint1,sSurface3Point) ;
// get the xyz point for each curve solution domain point
SmPoint3d sClassCurvePoint, sEdgeCurvePoint ;
if(pSrcCurve3d) pSrcCurve3d->EvaluatePoint(rSolution.m_vStart[0], sClassCurvePoint) ;
if(pEdgeCurve ) pEdgeCurve->EvaluatePoint (rSolution.m_vStart[1], sEdgeCurvePoint) ;
// get the distances between
// 2 curve/curve xsect points and 3 surf/surf/surf xsect points
// double dDistCC = (pSrcCurve3d && pEdgeCurve) ? sClassCurvePoint.DistanceBetween(sEdgeCurvePoint) : 0 ;
//double dDist1a = (pSrcCurve3d && pCurveClassification) ? sSurface1Point.DistanceBetween(sClassCurvePoint) : 0.0 ;
//double dDist2a = (pSrcCurve3d && pCurveClassification) ? sSurface2Point.DistanceBetween(sClassCurvePoint) : 0.0 ;
//double dDist3a = (pSrcCurve3d && (pEdge || pEMate) ) ? sSurface3Point.DistanceBetween(sClassCurvePoint) : 0.0 ;
//double dDist1b = (pEdgeCurve && pCurveClassification) ? sSurface1Point.DistanceBetween(sEdgeCurvePoint) : 0.0 ;
//double dDist2b = (pEdgeCurve && pCurveClassification) ? sSurface2Point.DistanceBetween(sEdgeCurvePoint) : 0.0 ;
//double dDist3b = (pEdgeCurve && (pEdge || pEMate) ) ? sSurface3Point.DistanceBetween(sEdgeCurvePoint) : 0.0 ;
//double dDist12 = (pCurveClassification) ? sSurface1Point.DistanceBetween(sSurface2Point) : 0.0 ;
//double dDist23 = (pCurveClassification && (pEdge || pEMate)) ? sSurface2Point.DistanceBetween(sSurface3Point) : 0.0 ;
//double dDist31 = (pCurveClassification && (pEdge || pEMate)) ? sSurface3Point.DistanceBetween(sSurface1Point) : 0.0 ;
// double dMaxDistSS = smos_3Max(dDist12, dDist23, dDist31) ;
// double dmaxDistCS = smos_Max(smos_3Max(dDist1a, dDist2a, dDist3a),
// smos_3Max(dDist1b, dDist2b, dDist3b)) ;
// draw Breps, Surfaces, Curves, Points
if(bDebugMe)
{
smgfx_Erase() ;
smgfx_SetLook(1,2, 0,0,1) ; if(pBrep1) pBrep1->Draw() ; sm_GraphicsLoop() ;
smgfx_SetLook(1,2, 0,1,0) ; if(pBrep2 && pBrep2 != pBrep1) pBrep2->Draw() ; sm_GraphicsLoop() ;
smgfx_SetLook(1,2, 0,0,0) ; if(pBrep3 && pBrep3 != pBrep1 && pBrep3 != pBrep2) pBrep3->Draw() ; sm_GraphicsLoop() ;
smgfx_SetLook(1,2, 0,0,1) ; if(pClassCurveSurface1) pClassCurveSurface1->DrawUV() ; sm_GraphicsLoop() ;
smgfx_SetLook(1,2, 0,0,0) ; if(pFace1) { pFace1->Draw(SM_DM_CROSSHATCH) ; } sm_GraphicsLoop() ;
smgfx_SetLook(1,2, 0,1,0) ; if(pClassCurveSurface2) pClassCurveSurface2->DrawUV() ; sm_GraphicsLoop() ;
smgfx_SetLook(1,2, 0,0,0) ; if(pFace2) { pFace2->Draw(SM_DM_CROSSHATCH) ; } sm_GraphicsLoop() ;
smgfx_SetLook(1,2, 0,1,1) ; if(pEdgeCurveSurface1) pEdgeCurveSurface1->DrawUV() ; sm_GraphicsLoop() ;
smgfx_SetLook(1,2, 0,0,0) ; if(pFace3) { pFace3->Draw(SM_DM_CROSSHATCH) ; } sm_GraphicsLoop() ;
smgfx_SetLook(5,7, 1,0,1) ; if(pSrcCurve3d) pSrcCurve3d->Draw() ; sm_GraphicsLoop() ;
smgfx_SetLook(3,6, 0,1,0) ; if(pEdgeCurve) pEdgeCurve->Draw() ; sm_GraphicsLoop() ;
smgfx_SetLook(3,6, 0,1,0) ; if(pEMateCurve) pEMateCurve->Draw() ; sm_GraphicsLoop() ;
smgfx_SetLook(7,9, 1,0,0) ; sSurface1Point.Draw() ; sm_GraphicsLoop() ;
smgfx_SetLook(7,9, 1,0,0) ; sSurface2Point.Draw() ; sm_GraphicsLoop() ;
smgfx_SetLook(7,9, 1,0,0) ; sSurface3Point.Draw() ; sm_GraphicsLoop() ;
sm_GraphicsLoop() ;
} // end draw section
#endif // SM_DEBUG_CODE
// all done
return(SM_SUCCESS) ;
} // end sm_ApplyPointSolution
// ---- verbatim: SmCurveClass.cpp lines 3048-3275: sm_MakeConsistentClassifications
static SmStatus sm_MakeConsistentClassifications(SmCurveClassification & rCurveClass)
{
// locals
SmZoneTol3d sSrcZoneTol3d = SmTol::GetSrcZoneTol3d(&rCurveClass) ;
// for every interval
for (ULONG i=0; i<rCurveClass.GetSize(); i++)
{
SmCurveInterval & rCIvl = rCurveClass[i];
// If interval classifies to an Edge
if (rCIvl.m_vMid.GetPointClass() == SM_PC_EDGE)
{
// if startPoint classifies to a Vertex
if (rCIvl.m_vStart.GetPointClass() == SM_PC_VERTEX)
{
SmEdge *pClassifyEdge = (SmEdge*)rCIvl.m_vMid.GetObject();
SmVertex *pClassifyVertex = (SmVertex*)rCIvl.m_vStart.GetObject();
SmVertex *pEdgeVertex1 = pClassifyEdge->GetVertex();
SmVertex *pEdgeVertex2 = pClassifyEdge->GetOtherVertex(pEdgeVertex1);
// check that classified vertex is one of the Edge's vertices
if ( pClassifyVertex != pEdgeVertex1
&& pClassifyVertex != pEdgeVertex2)
{
// Something is wrong: what looks like a coincident edge with:[Mid=Edge,Start=Vertex]
// does not have the ClassifyStartV == Edge->[StartV or EndV] as expected
// Since every Edge always has just one StartV and one EndV, it's not that the ClassifyEdge
// is missing a connection to the ClassifyVertex. It's more likely that one of the classifications
// of Ivl.Mid=EDGE or the Ivl.Start=VERTEX was made to the wrong Object. Perhaps one in the neighborhood
// of the Classify EDGE and VERTEX objects themselves.
WARN(_T("MakeConsistentClassification: bad CoinEdge classify:[ClassifyMid=Edge,ClassifyStart=Vertex] but Edge:[StartV,EndV] != ClassifyStart")) ;
#ifdef SM_DEBUG_CODE
SmBoolean bDebugMe=FALSE;
if (bDebugMe)
{
SM_DUMP_AND_ASSERT_VALID(&rCurveClass);
SmBrep *pBrep = pClassifyEdge->GetBrep() ;
smgfx_Erase();
smgfx_SetLook(1,2, 0,0,1) ; if(pBrep) { pBrep->Draw(TRUE) ; } sm_GraphicsLoop() ;
smgfx_SetLook(3,5, 0,1,1) ; rCurveClass.GetCurve()->DrawWDeriv(rCIvl.m_vInterval); sm_GraphicsLoop() ;
smgfx_SetLook(4,5, 1,0,1) ; pClassifyEdge->Draw(); sm_GraphicsLoop() ;
smgfx_SetLook(3,8, 1,0,0) ; pClassifyVertex->GetPoint().Draw(); sm_GraphicsLoop() ;
smgfx_SetLook(3,9, 0,1,0) ; pEdgeVertex1->GetPoint().Draw(); sm_GraphicsLoop() ;
smgfx_SetLook(3,9, 0,0,1) ; pEdgeVertex2->GetPoint().Draw(); sm_GraphicsLoop() ;
sm_GraphicsLoop();
}
#endif // SM_DEBUG_CODE
// Try 1 - Simple ClassifyEdge substitution: look for better ClassifyEdge assuming ClassifyVertex is correct
// Check every Edge attached to ClassifyVertex - pick the first Edge within XSectTol3d at the Ivl MidPoint
SmBoolean bFixedProblem = FALSE;
// for every edge attached to the vertex
SmTArray<SmEdge*> pVertexEdges;
pClassifyVertex->GetEdges(pVertexEdges);
for (ULONG kk=0; kk<pVertexEdges.GetSize(); kk++)
{
SmEdge * pVertexEdge = pVertexEdges[kk];
SmBoolean bSuccess;
SmPoint3d sMidPnt;
double dDropParam, dDistToCurve;
double dTMid = rCIvl.m_vInterval.Evaluate(0.5);
// Ivl MidPoint, XSectTol3d(SrcCrv,VertexEdge)
SER(rCurveClass.GetCurve()->EvaluatePoint(dTMid,sMidPnt));
SM_ASSERT_BREAK_MSG(rCurveClass.GetCurve()->GetDim() == 3, _T("Bad CurveClass 3d Assumption switch call to sm_GetXSectTol2dOr3d_ForClassifyPair()")) ;
SmXSectTol3d sXSectTol3d = SmTol::GetXSectTol3d(sSrcZoneTol3d, SmTol::GetZoneTol3d(pVertexEdge)) ;
// drop a midInterval Point to this VertexEdge
SER(pVertexEdge->GetCurve()->DropPoint(pVertexEdge->GetInterval(), // in : target curve allowed domain
sMidPnt, // in : Point to drop to curve
NULL, // in : For Snapping, NULL=NoSnap, Vec pointing to CrvInside from desired EndPt for snapping.
// when DropPt is within dDistTol in cpVecToCrvInside dir of EndPt, Snap to Ivl EndPt.
// Vec handles ambiguities and makes sure that curves are not just touching at the ends.
sXSectTol3d, // in : Used for SnapDist, and SM_SO_INTERSECT sXSectTol3d distance.
// sXSectTol3d = dDistTol ; SM_SO_INTERSECT
// sXSectTol3d = SM_BIG_DOUBLE ; SM_SO_MINIMIZE, SM_SO_NORMALIZE
// sSnapTol3d = dDistTol ; SM_SO_MINIMIZE, SM_SO_NORMALIZE, SM_SO_INTERSECT
NULL, // in : If given, call cheap LocalPointSolve() else call expensive GlobalPointSolve()
bSuccess, // out: TRUE = found a drop point
dDropParam, // out: found drop curve param
dDistToCurve)) ; // out: found drop distance
// in : SM_SO_MINIMIZE = allow nonNormal drops near endPoints
// SM_SO_INTERSECT= like Minimize but point must be within dDistTol
// SM_SO_NORMALIZE= exclude nonNormal drops near endPoints
// default:[SM_SO_MINIMIZE] to preserve original behavior
// if point was close enough to edge - substitute the ClassifyMid Edge target with the found VertexEdge
if (bSuccess)
{
// set the interval's Classification object to this edge
MSG(_T("MakeConsistentClassification: fixing bad CoinEdge classify, switching ClassifyEdge = a StartVertexEdge within XSectTol3d at mid interval")) ;
rCIvl.m_vMid.SetClassObject(SM_PC_EDGE, pVertexEdge);
bFixedProblem = TRUE;
break;
} // end point was close enough check
} // end iter every edge attached to the vertex
// arrive here when none of the Edges attached to ClassifyVertex are likely coincident with the ClassifyVertex
// Try 2 - simple ClassifyVertex substitution: Substitute ClassifyVertex with the EdgeVertex
// closest to ClassifyVertex
// gwc: I'm surprised that this helps
if (!bFixedProblem)
{
MSG(_T("MakeConsistentClassification: fixing bad CoinEdge classify, switching Start ClassifyVertex = Closest EdgeVertex")) ;
if (pClassifyVertex->GetPoint().DistanceBetween(pEdgeVertex1->GetPoint()) <
pClassifyVertex->GetPoint().DistanceBetween(pEdgeVertex2->GetPoint()) )
{ rCIvl.m_vStart.SetClassObject(SM_PC_VERTEX, pEdgeVertex1); }
else { rCIvl.m_vStart.SetClassObject(SM_PC_VERTEX, pEdgeVertex2); }
} // end bFixedProblem == FALSE check
} // end classified vertex is one of the Edge's vertices check
} // end Classification IVl with Mid=EDGE and startPoint classified to a Vertex check
// if endPoint classifies to a Vertex
if (rCIvl.m_vEnd.GetPointClass() == SM_PC_VERTEX)
{
SmEdge *pClassifyEdge = (SmEdge*)rCIvl.m_vMid.GetObject();
SmVertex *pClassifyVertex = (SmVertex*)rCIvl.m_vEnd.GetObject();
SmVertex *pEdgeVertex1 = pClassifyEdge->GetVertex();
SmVertex *pEdgeVertex2 = pClassifyEdge->GetOtherVertex(pEdgeVertex1);
// check that classified vertex is one of the Edge's vertices
if ( pClassifyVertex != pEdgeVertex1
&& pClassifyVertex != pEdgeVertex2)
{
// Something is wrong: what looks like a coincident edge with:[Mid=Edge,End=Vertex]
// does not have the ClassifyEndV == Edge->[StartV or EndV] as expected
// Since every Edge always has just one StartV and one EndV, it's not that the ClassifyEdge
// is missing a connection to the ClassifyVertex. It's more likely that one of the classifications
// of Ivl.Mid=EDGE or the Ivl.Start=VERTEX was made to the wrong Object. Perhaps one in the neighborhood
// of the Classify EDGE and VERTEX objects themselves.
WARN(_T("MakeConsistentClassification: bad CoinEdge classify:[ClassifyMid=Edge,ClassifyEnd=Vertex] but Edge:[StartV,EndV] != ClassifyStart")) ;
#ifdef SM_DEBUG_CODE
SmBoolean bDebugMe=FALSE;
if (bDebugMe)
{
SM_DUMP_AND_ASSERT_VALID(&rCurveClass);
SmBrep *pBrep = pClassifyEdge->GetBrep() ;
smgfx_Erase();
smgfx_SetLook(1,2, 0,0,1) ; if(pBrep) { pBrep->Draw(TRUE) ; } sm_GraphicsLoop() ;
smgfx_SetLook(3,5, 0,1,1) ; rCurveClass.GetCurve()->DrawWDeriv(rCIvl.m_vInterval); sm_GraphicsLoop() ;
smgfx_SetLook(4,5, 1,0,1) ; pClassifyEdge->Draw(); sm_GraphicsLoop() ;
smgfx_SetLook(3,8, 1,0,0) ; pClassifyVertex->GetPoint().Draw(); sm_GraphicsLoop() ;
smgfx_SetLook(3,9, 0,1,0) ; pEdgeVertex1->GetPoint().Draw(); sm_GraphicsLoop() ;
smgfx_SetLook(3,9, 0,0,1) ; pEdgeVertex2->GetPoint().Draw(); sm_GraphicsLoop() ;
sm_GraphicsLoop();
}
#endif // SM_DEBUG_CODE
// Try 1 - Simple ClassifyEdge substitution: look for better ClassifyEdge assuming ClassifyVertex is correct
// Check every Edge attached to ClassifyVertex - pick the first Edge within XSectTol3d at the Ivl MidPoint
SmBoolean bFixedProblem = FALSE;
// for every edge attached to the vertex
SmTArray<SmEdge*> sEdges;
pClassifyVertex->GetEdges(sEdges);
for (ULONG kk=0; kk<sEdges.GetSize(); kk++)
{
SmEdge * pVertexEdge = sEdges[kk];
SmBoolean bSuccess;
SmPoint3d sMidPnt;
double dDropParam, dDistToCurve;
double dTMid = rCIvl.m_vInterval.Evaluate(0.5);
// Ivl MidPoint, XSectTol3d(SrcCrv,VertexEdge)
SER(rCurveClass.GetCurve()->EvaluatePoint(dTMid,sMidPnt));
SM_ASSERT_BREAK_MSG(rCurveClass.GetCurve()->GetDim() == 3, _T("Bad CurveClass 3d Assumption switch call to sm_GetXSectTol2dOr3d_ForClassifyPair()")) ;
SmXSectTol3d sXSectTol3d = SmTol::GetXSectTol3d(sSrcZoneTol3d, SmTol::GetZoneTol3d(pVertexEdge)) ;
SER(pVertexEdge->GetCurve()->DropPoint(pVertexEdge->GetInterval(), // in : target curve allowed domain
sMidPnt, // in : Point to drop to curve
NULL, // in : For Snapping, NULL=NoSnap, Vec pointing to CrvInside from desired EndPt for snapping.
// when DropPt is within dDistTol in cpVecToCrvInside dir of EndPt, Snap to Ivl EndPt.
// Vec handles ambiguities and makes sure that curves are not just touching at the ends.
sXSectTol3d, // in : Used for SnapDist, and SM_SO_INTERSECT sXSectTol3d distance.
// sXSectTol3d = dDistTol ; SM_SO_INTERSECT
// sXSectTol3d = SM_BIG_DOUBLE ; SM_SO_MINIMIZE, SM_SO_NORMALIZE
// sSnapTol3d = dDistTol ; SM_SO_MINIMIZE, SM_SO_NORMALIZE, SM_SO_INTERSECT
NULL, // in : If given, call cheap LocalPointSolve() else call expensive GlobalPointSolve()
bSuccess, // out: TRUE = found a drop point
dDropParam, // out: found drop curve param
dDistToCurve)) ; // out: found drop distance
// in : SM_SO_MINIMIZE = allow nonNormal drops near endPoints
// SM_SO_INTERSECT= like Minimize but point must be within dDistTol
// SM_SO_NORMALIZE= exclude nonNormal drops near endPoints
// default:[SM_SO_MINIMIZE] to preserve original behavior
// if point was close enough to edge - substitute the ClassifyMid Edge target with the found VertexEdge
if (bSuccess)
{
// set the interval's Classification object to this edge
MSG(_T("MakeConsistentClassification: fixing bad CoinEdge classify, switching ClassifyEdge = a EndVertexEdge within XSectTol3d at mid interval")) ;
rCIvl.m_vMid.SetClassObject(SM_PC_EDGE, pVertexEdge);
bFixedProblem = TRUE;
break;
} // end point was close enough check
} // end iter every edge attached to the vertex
// arrive here when none of the Edges attached to ClassifyVertex are likely coincident with the ClassifyVertex
// Try 2 - simple ClassifyVertex substitution: Substitute ClassifyVertex with the EdgeVertex
// closest to ClassifyVertex
// gwc: I'm surprised that this helps
if (!bFixedProblem)
{
MSG(_T("MakeConsistentClassification: fixing bad CoinEdge classify, switching End ClassifyVertex = Closest EdgeVertex")) ;
if (pClassifyVertex->GetPoint().DistanceBetween(pEdgeVertex1->GetPoint()) <
pClassifyVertex->GetPoint().DistanceBetween(pEdgeVertex2->GetPoint()) )
{ rCIvl.m_vEnd.SetClassObject(SM_PC_VERTEX, pEdgeVertex1); }
else { rCIvl.m_vEnd.SetClassObject(SM_PC_VERTEX, pEdgeVertex2); }
}
} // end classified vertex is one of the Edge's vertices check
} // end Classification IVl with Mid=EDGE and EndPoint classified to a Vertex check
} // end interval classified to Edge check
} // end iter every interval
return SM_SUCCESS;
} // end sm_MakeConsistentClassifications
// ---- verbatim: SmCurveClass.cpp lines 3752-3806: sm_GetCurveCurveAngle
static double sm_GetCurveCurveAngle
(const SmCurve *pThisCurve, // in : 1st tgt curve
double dThisParam, // in : param for ThisCurve evaluation
const SmCurve *pOtherCurve, // in : 2nd tgt Curve
double dOtherParam, // in : param for OtherCurve evaluation
double dDeviation) // in : expected deviation between curve points - only used for debug code check
{
SmVector3d sThisPV[2], sOtherPV[2] ;
// no work - no curves
if( pThisCurve == NULL
|| pOtherCurve == NULL)
{ return 0.0 ; }
// check input - curves are 3d and params are valid
SM_ASSERT(pThisCurve-> GetDim() == 3) ;
SM_ASSERT(pOtherCurve->GetDim() == 3) ;
SM_ASSERT(pThisCurve-> GetNaturalInterval().ContainsValue(dThisParam) == TRUE) ;
SM_ASSERT(pOtherCurve->GetNaturalInterval().ContainsValue(dOtherParam) == TRUE) ;
// Evaluate the Curve locations
pThisCurve->Evaluate (dThisParam, 1, TRUE, sThisPV) ;
pOtherCurve->Evaluate(dOtherParam, 1, TRUE, sOtherPV) ;
// check for zero-length tangents.
SmScaledZero dScaledZero = SmTol::GetScaledZero( sThisPV[0] );
if ( sThisPV[1].Length() < dScaledZero )
{ return 0.0; }
if ( sOtherPV[1].Length() < dScaledZero )
{ return 0.0; }
#ifdef SM_DEBUG_CODE
// check that points are the same
double dChkDist = sThisPV[0].DistanceBetween(sOtherPV[0]) ;
double dChkTol = 5.0 * smos_Max(dDeviation, dScaledZero.val) ;
if(dChkDist >= dChkTol)
{ SM_ASSERT(dChkDist < dChkTol) ; }
#else
SM_REF1(dDeviation);
#endif // SM_DEBUG_CODE
// get the angle between the tangents
double dAngleRadians = 0.0 ;
sThisPV[1].AngleBetween(sOtherPV[1], dAngleRadians) ;
// return angle from 0 to PI/2
if(dAngleRadians < 0.0) dAngleRadians = -dAngleRadians ;
if(dAngleRadians > SM_PI/2.0) dAngleRadians = smos_Fabs(SM_PI - dAngleRadians) ;
// all done
return(dAngleRadians) ;
} // end sm_GetCurveCurveAngle
// ---- verbatim: SmCurveClass.cpp lines 3821-3920: sm_CullOrRefineCCIIn3d
static SmStatus sm_CullOrRefineCCIIn3d // eff: tigthen 2d curve/curve intersections by checking 3dcurve/curve intersection tolerances
(const SmSurface & crSurface, // in : surface of face being classified against
const SmCurve & crUVCurve1, // in : UVCurve being classified
const SmExtent1d & crInterval1, // in : UVCurve's interval
const SmCurve & cr3DCurve2, // in : 3dCurve for face's trimCurve used to generate rSolutions
const SmExtent1d & crInterval2, // in : 3DCurve's interval
double dXSectTol3d, // in : XSectTol3d(SrcCurve,Edge) to use in 3dCurve/3dCurve intersection
SmSolutionArray & rSolutions) // in : solutions from 2dCurve/2dCurve intersections
{
// project UVCurve into 3d space
SmCrvOnSurf s3DCurve1(SM_CONST_CAST(SmCurve&, crUVCurve1),
SM_CONST_CAST(SmSurface&,crSurface),
NULL,
0,
crSurface.GetContext());
// s3DCurve1.SetContext(crSurface.GetContext());
// for every solution
for (ULONG i=0; i<rSolutions.GetSize(); i++)
{
SmSolution & rSol = rSolutions[i];
// when the solution is a point (not an interval)
if (rSol.m_eSolutionType == SM_ST_SINGLE_VALUE)
{
double dNewParam1, dNewParam2, dDeviation;
SmBoolean bFoundIntersection;
// get the 3dCurve/3Dcurve intersection
SER(s3DCurve1.LocalCurveIntersect(crInterval1, // in : ThisCurve intersection limits
cr3DCurve2, // in : other curve
crInterval2, // in : other curve intersection limits
dXSectTol3d, // in : Passed to SmLocalSolveNd::SolveIt() and
// LocalCurveSolve()
rSol.m_vStart[0], // in : ThisCurve GuessPoint
rSol.m_vStart[1], // in : OtherCurve GuessPoint
bFoundIntersection, // out: TRUE = curves intersect
dNewParam1, // out: ThisCurve intersection point
dNewParam2, // out: OtherCurve intersection point
dDeviation)); // out: Intersection Gap Vector size
// when 3dCurve/3dCurve intersection is tighter than 2dCurve/2dCurve intersection
if (bFoundIntersection && dDeviation < rSol.m_vStart.m_dSolutionValue)
{
// use it
rSol.m_vStart.m_dSolutionValue = dDeviation;
rSol.m_vStart[0] = dNewParam1;
rSol.m_vStart[1] = dNewParam2;
} // end better 3d xsect solution check
// when 3dCurve/3dCurve intersection is not good enough
if(!bFoundIntersection || dDeviation > dXSectTol3d)
{
// remove the solution
rSolutions.RemoveAt(i) ;
}
} // end SM_ST_SINGLE_VALUE solution check
// when the solution is a coincident curve pair
else if(rSol.m_eSolutionType == SM_ST_RANGE_OF_VALUES)
{
// get quick max edge/UVTrimCurve 3d distance estimate at 8 sample points
double dMaxCurveCurveDist3d = 0.0 ;
SmExtent1d sSolInterval1(rSol.m_vStart[0], rSol.m_vEnd[0]) ;
SER(s3DCurve1.CurveMaxDistanceBetween(sSolInterval1,
cr3DCurve2,
rSol.m_vStart[1],
rSol.m_vEnd[1],
8,
NULL,
dMaxCurveCurveDist3d));
// when the quick estimate is good but not very tight
if ( dMaxCurveCurveDist3d < dXSectTol3d
&& dMaxCurveCurveDist3d > dXSectTol3d / 100.00)
{
// get max dist value possible between curves by setting samplePointCount = 0
SER(s3DCurve1.CurveMaxDistanceBetween(sSolInterval1,
cr3DCurve2,
rSol.m_vStart[1],
rSol.m_vEnd[1],
0,
NULL,
dMaxCurveCurveDist3d));
}
// when max edge/UVTrimCurve 3d distance is not good enough
if(dMaxCurveCurveDist3d > dXSectTol3d)
{
// remove the solution
rSolutions.RemoveAt(i) ;
}
} // end SM_ST_RANGE_OF_VALUES solution check
} // end iter every solution
// all done
return SM_SUCCESS;
} // end sm_CullOrRefineCCIIn3d
// ---- verbatim: SmCurveClass.cpp lines 3937-4044: sm_ComputeFacePointClass
static SmStatus sm_ComputeFacePointClass
(const SmPoint3d & cr3DPoint, // in : Point expected to be on face
SmZoneTol3d sSrcZoneTol3d, // in : ZoneTol3d(3Dpoint Assoc Obj, e.g. SrcCurve for a CurveClassification with this Face)
SmFace * pFace, // in : target face
SmPointClassification & rPointClass) // out: classification to remember point is on face
{
// locals
SmSurface *pSurface = pFace->GetSurface(); NER(pSurface);
// Method:
// 1: project to surface without checking trimmed (face) boundaries:
// 'point testing turned off'.
// 2: face->PointClassify().
// Project Point to Face->Surface
SmSolution sData[16];
SmSolutionArray sSolutions(16,sData);
// Turn off point testing in the face (temporarily).
SmSurfaceCache * pSC = smsurf_GetSurfaceCache(pSurface) ;
SmCacheCheckOutIn sCheckIO(pSC);
// Local scope for temporary assignment to pSC->PointTestEnabled block:
{
// Turn off point testing so GlobalPointSolve() will keep all point solutions
// without classifying the solution point against the trim boundaries.
SmTemporaryChangeValue<SmBoolean> sCV(pSC->m_bPointTestEnabled,FALSE);
SmXSectTol3d sXSectTol3d = SmTol::GetXSectTol3d(sSrcZoneTol3d, SmTol::GetZoneTol3d(pFace)) ;
SER(pSurface->GlobalPointSolve(pFace->GetUVDomain(),
SM_SO_INTERSECT,
cr3DPoint,
sSrcZoneTol3d,
NULL,
SM_SR_ALL,
sSolutions));
// If global point solve fails, try again with a bigger tolerance.
if (sSolutions.GetSize() == 0)
{
SER(pSurface->GlobalPointSolve(pFace->GetUVDomain(),
SM_SO_INTERSECT,
cr3DPoint,
10 * sXSectTol3d, // increased tol in hopes of a solution
NULL,
SM_SR_ALL,
sSolutions));
// If point still fails to project, give it one more try.
if (sSolutions.GetSize() == 0)
{
SER(pSurface->GlobalPointSolve(pFace->GetUVDomain(),
SM_SO_INTERSECT,
cr3DPoint,
100 * sXSectTol3d, // additional increased tol in hopes of a solution
NULL,
SM_SR_ALL,
sSolutions));
// signal error and give up if point still fails to project
if (sSolutions.GetSize() == 0)
{
return SM_ERR;
} // end 3rd project failed check
} // end 2nd project failed check
} // end 1st project point to surface failed check
} // end local scope for temporary assignment to pSC->PointTestEnabled block
// Find the best solution, and classify it to the face.
double dBestDist = SM_BIG_DOUBLE;
SmPoint2d sUV;
for (ULONG i=0; i<sSolutions.GetSize(); i++)
{
SmSolution & rSol = sSolutions[i];
if (rSol.m_vStart.m_dSolutionValue < dBestDist)
{
dBestDist = rSol.m_vStart.m_dSolutionValue;
sUV.Set( rSol.m_vStart[0], rSol.m_vStart[1] );
}
}
pFace->PointClassify(sUV, // in : UVPoint to Classify
sSrcZoneTol3d, // in : ZoneTol3d of obj assoc with UVPoint, not this face
// (when none, use: SmTol::GetZoneTol3d(Brep_Context_Or_NULL)
TRUE, // in : TRUE = return point face->Vertex/Edge intersections when found
// FALSE= return loop-containment classifications.
TRUE, // in : TRUE = force classification to be done only in UV space.
// else when LoopClassification fails try again in 3d.
rPointClass ) ; // out: Classification of the point.
// when in : m_ePointClass = SM_PC_FACE
// out: m_ePointClass = SM_PC_UNKNOWN
// on : m_ePointClass = SM_PC_EDGE or SM_PC_VERTEX
#ifdef SM_DEBUG_CODE
SmBoolean bDebugMe = FALSE;
if ( bDebugMe )
{
if ( rPointClass.GetPointClass() != SM_PC_FACE )
{
SM_DBG_MSG( TRUE,
_T("MSG: sm_ComputeFacePointClass() did NOT classify to Face.\n")) ;
}
}
#endif // SM_DEBUG_CODE
// all done
return SM_SUCCESS;
} // end sm_ComputeFacePointClass
// ---- verbatim: SmCurveClass.cpp lines 4802-4810: sm_ObjectDimension
static int sm_ObjectDimension( SmObject *pObj )
{
return( pObj == NULL ? 4
: pObj->IsKindOf( SmFace_TYPE ) ? 2
: pObj->IsKindOf( SmEdge_TYPE ) ? 1
: pObj->IsKindOf( SmVertex_TYPE ) ? 0
: 4 ) ; // UNKNOWN, etc.
} // end sm_ObjectDimension
// clang-format on
} // namespace sc_cc_p1
namespace
{
enum : int64_t
{
K_CURVE = 0,
K_PLANE = 1,
K_BSURF = 2,
K_POINT = 3,
K_VERTEX = 4,
K_EDGE = 5,
K_FACE = 6,
K_LOOP = 7,
K_OTHER = 8,
K_EDGEUSE = 9,
};
// Ops (keep in sync with crates/solidcore-ffi/src/units/intersect_curve_class_p1.rs).
enum : int64_t
{
OP_SET_SIZE = 0,
OP_REMOVE_AT = 1,
OP_IVL_SET = 2,
OP_IVL_SET_MIN = 3,
OP_IVL_SET_MAX = 4,
OP_IVL_RESET = 5,
OP_IVL_ASSIGN = 6,
OP_IVL_NEW = 7,
OP_PC_SET_CLASS = 8,
OP_PC_SET_T = 9,
OP_PC_SET_UV = 10,
OP_PC_SET_GAP = 11,
OP_PC_SET_SSS = 12,
OP_PC_SET_PRESNAP = 13,
OP_PC_SET_SRC_TOL = 14,
OP_SET_CURVE = 15,
OP_SET_SURFACE1 = 16,
OP_SET_SURFACE2 = 17,
OP_SET_FACE = 18,
OP_SET_INTERSECT_DATA = 19,
OP_SET_IS_CURVE_CURRENT = 20,
OP_SET_IGNORE_TANGENCY = 21,
OP_SET_IS_CURVE_OWNED = 22,
OP_SET_OK_TO_SET_TOL = 23,
OP_SET_FILLET_DATA = 24,
OP_SET_TGT_LOOPS_ONLY = 25,
OP_ADD_TGT_LOOP = 26,
OP_SET_SRC_TOL = 27,
OP_SET_UV_CURVE1 = 28,
OP_SET_UV_CURVE2 = 29,
OP_SUB_INTERVAL = 30,
OP_CLASSIFY_CRV_LOC = 31,
OP_SET_PC_PARAMS = 32,
OP_SQUEEZE_END = 33,
OP_IS_REFERENCED = 34,
OP_MAKE_CONSISTENT = 35,
OP_TYPE = 36,
OP_SRC_TOL = 37,
OP_IVL_TOLS = 38,
OP_OBJ_DIM = 39,
OP_XSECT_CURVE = 40,
OP_APPLY = 41,
OP_ANGLE = 42,
OP_CULL = 43,
OP_TOL_PAIR = 44,
OP_GAPS = 45,
OP_TOUCH_CONNECTED = 46,
OP_TOUCH_WINDING = 47,
OP_EDGE_THROUGH = 48,
OP_CCI_GEOMETRY = 49,
OP_FACE_POINT_CLASS = 50,
};
// Access to private / protected members through explicit instantiation.
template <typename Tag, typename Tag::type M>
struct Rob
{
friend typename Tag::type get(Tag) { return M; }
};
struct IvlSetMin
{
typedef SmStatus (SmCurveInterval::*type)(double);
friend type get(IvlSetMin);
};
template struct Rob<IvlSetMin, &SmCurveInterval::SetIntervalMin>;
struct IvlSetMax
{
typedef SmStatus (SmCurveInterval::*type)(double);
friend type get(IvlSetMax);
};
template struct Rob<IvlSetMax, &SmCurveInterval::SetIntervalMax>;
struct CcOwned
{
typedef SmBoolean SmCurveClassification::*type;
friend type get(CcOwned);
};
template struct Rob<CcOwned, &SmCurveClassification::m_bIsCurveOwned>;
struct Rd
{
const double* in;
int64_t n;
int64_t at;
bool bad = false;
double num()
{
if (at >= n)
{
bad = true;
return 0.0;
}
return in[at++];
}
int64_t idx() { return (int64_t)num(); }
bool flag() { return num() != 0.0; }
SmVector3d vec()
{
double x = num();
double y = num();
double z = num();
return SmVector3d(x, y, z);
}
};
double st(SmStatus s) { return s == SM_SUCCESS ? 0.0 : (double)s; }
double flg(SmBoolean b) { return b ? 1.0 : 0.0; }
struct ScVertex : public SmVertex
{
ScVertex() : SmVertex() {}
};
struct ScEdge : public SmEdge
{
explicit ScEdge(SmCurve* c) : SmEdge() { m_pCurve = c; }
};
struct ScFace : public SmFace
{
explicit ScFace(SmSurface* s) : SmFace() { m_pSurface = s; }
};
// SmObject stand-in of one class; answers GetType and IsKindOf like that class.
class ScStandIn : public SmObject
{
public:
explicit ScStandIn(SM_TYPE t) : m_type(t) {}
SM_TYPE GetType() const override { return m_type; }
SmBoolean IsKindOf(SM_TYPE t) const override { return t == m_type || SmObject::IsKindOf(t); }
private:
SM_TYPE m_type;
};
const SmContext& the_context()
{
static SmContext* p = new SmContext();
return *p;
}
// An object carrying the context, for the topology `operator new(size_t, SmObject*)`.
SmObject* anchor()
{
static SmLine* p = new (&the_context())
SmLine(SmPoint3d(0.0, 0.0, 0.0), SmPoint3d(1.0, 0.0, 0.0), 3, &the_context());
return p;
}
gw_CURVE* read_nurb(Rd& rd)
{
gw_INDEX n = (gw_INDEX)rd.idx();
gw_DEGREE p = (gw_DEGREE)rd.idx();
gw_INDEX m = (gw_INDEX)rd.idx();
gw_CURVE* cur = sm_AllocateNurbCurve(n, p, m);
for (gw_INDEX i = 0; i <= n; i++)
{
cur->pol->Pw[i].x = rd.num();
cur->pol->Pw[i].y = rd.num();
cur->pol->Pw[i].z = rd.num();
cur->pol->Pw[i].w = rd.num();
}
for (gw_INDEX i = 0; i <= m; i++)
cur->knt->U[i] = rd.num();
return cur;
}
// `n m p q r s cpts(x y z w, v fastest) u v`.
gw_SURFACE* read_nurb_surface(Rd& rd)
{
gw_INDEX n = (gw_INDEX)rd.idx();
gw_INDEX m = (gw_INDEX)rd.idx();
gw_DEGREE p = (gw_DEGREE)rd.idx();
gw_DEGREE q = (gw_DEGREE)rd.idx();
gw_INDEX r = (gw_INDEX)rd.idx();
gw_INDEX s = (gw_INDEX)rd.idx();
gw_SURFACE* sur = sm_AllocateNurbSurface(n, m, p, q, r, s);
for (gw_INDEX i = 0; i <= n; i++)
for (gw_INDEX j = 0; j <= m; j++)
{
sur->net->Pw[i][j].x = rd.num();
sur->net->Pw[i][j].y = rd.num();
sur->net->Pw[i][j].z = rd.num();
sur->net->Pw[i][j].w = rd.num();
}
for (gw_INDEX i = 0; i <= r; i++)
sur->knu->U[i] = rd.num();
for (gw_INDEX i = 0; i <= s; i++)
sur->knv->U[i] = rd.num();
return sur;
}
// A curve block `kind dim ...` (0: SmBSplineCurve `oob n p m cpts(x y z w) knots`;
// 1: SmHermiteCurve `P1 D1 P2 D2`; 2: bounded SmLine `P V d0 d1 scale io`; 3 / 4: SmEllipse /
// SmCircle `C X Y d0 d1 rx [ry] io`).
SmCurve* read_curve(Rd& rd, const SmContext& ctx, bool& is_bspline, ULONG& dim)
{
int64_t kind = rd.idx();
dim = (ULONG)rd.idx();
is_bspline = kind == 0;
if (kind == 3 || kind == 4)
{
SmPoint3d c = rd.vec();
SmVector3d x = rd.vec();
SmVector3d y = rd.vec();
double d0 = rd.num();
double d1 = rd.num();
double rx = rd.num();
double ry = kind == 3 ? rd.num() : rx;
bool io = rd.flag();
if (kind == 3)
return new (ctx) SmEllipse(c, x, y, SmExtent1d(d0, d1), rx, ry, dim, &ctx, NULL, io ? TRUE : FALSE);
return new (ctx) SmCircle(c, x, y, SmExtent1d(d0, d1), rx, dim, &ctx, NULL, io ? TRUE : FALSE);
}
if (kind == 0)
{
bool oob = rd.flag();
SmBSplineCurve* c = new (ctx) SmBSplineCurve(read_nurb(rd), dim, FALSE);
c->SetOutOfBoundsEnabled(oob ? TRUE : FALSE);
return c;
}
if (kind == 2)
{
SmPoint3d p = rd.vec();
SmVector3d v = rd.vec();
double d0 = rd.num();
double d1 = rd.num();
double scale = rd.num();
bool io = rd.flag();
return new (ctx) SmLine(p, v, SmExtent1d(d0, d1), scale, 3, &ctx, NULL, io ? TRUE : FALSE);
}
SmVector3d p1 = rd.vec();
SmVector3d d1 = rd.vec();
SmVector3d p2 = rd.vec();
SmVector3d d2 = rd.vec();
return new (ctx) SmHermiteCurve(p1, d1, p2, d2, dim);
}
struct Pool
{
std::vector<int64_t> kinds;
std::vector<void*> ptrs; // SmObject* or SmPoint3d*
std::vector<bool> bspline;
std::vector<ULONG> dims;
// entities of the one-face B-rep (their topology is real, not a stand-in's)
std::vector<bool> real;
std::map<const void*, int64_t> ids;
bool is_real(const void* p) const
{
auto it = ids.find(p);
return it != ids.end() && real[(size_t)it->second];
}
double id(const void* p) const
{
if (p == NULL)
return -1.0;
auto it = ids.find(p);
return it == ids.end() ? -2.0 : (double)it->second;
}
bool valid(int64_t i) const { return i >= 0 && i < (int64_t)ptrs.size(); }
// pool object i (NULL for -1); `ok` false for an index out of range or of the wrong kind
void* get(int64_t i, std::initializer_list<int64_t> want, bool& ok) const
{
if (i == -1)
return NULL;
if (!valid(i))
{
ok = false;
return NULL;
}
bool match = want.size() == 0;
for (int64_t w : want)
match = match || kinds[(size_t)i] == w;
if (!match)
ok = false;
return ptrs[(size_t)i];
}
int64_t kind_of(const void* p) const
{
auto it = ids.find(p);
return it == ids.end() ? -1 : kinds[(size_t)it->second];
}
};
void put_pc(std::vector<double>& o, const SmPointClassification& pc, const Pool& pool)
{
o.push_back((double)pc.GetPointClass());
o.push_back(pool.id(pc.GetObject()));
o.push_back(pool.id(pc.GetFace()));
o.push_back(pc.GetDeviation());
o.push_back(pc.GetTParam());
SmPoint2d uv = pc.GetUVParam();
o.push_back(uv.x);
o.push_back(uv.y);
put_vec(o, pc.GetUVWParam());
o.push_back(flg(pc.AreParametersSet()));
o.push_back(pc.GetPreSnapParam());
o.push_back(flg(pc.GetSSSPointFlag()));
put_vec(o, pc.GetSSSPoint());
o.push_back(SmTol::GetSrcZoneTol3d(&pc));
}
void put_ivl(std::vector<double>& o, const SmCurveInterval& ivl, const Pool& pool)
{
o.push_back(ivl.GetMin());
o.push_back(ivl.GetMax());
put_pc(o, ivl.m_vStart, pool);
put_pc(o, ivl.m_vMid, pool);
put_pc(o, ivl.m_vEnd, pool);
}
void put_cc(std::vector<double>& o, const SmCurveClassification& cc, const Pool& pool)
{
o.push_back((double)cc.GetSize());
SmExtent1d ivl = cc.GetInterval();
o.push_back(ivl.GetMin());
o.push_back(ivl.GetMax());
o.push_back(SmTol::GetSrcZoneTol3d(&cc));
o.push_back(flg(cc.*get(CcOwned())));
o.push_back(flg(cc.GetIsCurveCurrent()));
o.push_back(flg(cc.GetIgnoreTangency()));
o.push_back(flg(cc.GetDoingCrossHatch()));
o.push_back(flg(cc.GetDoingFillets()));
o.push_back(flg(cc.GetOKToSetTolerance()));
o.push_back(flg(cc.GetTgtLoopsOnly()));
o.push_back(pool.id(cc.GetCurve()));
o.push_back(pool.id(cc.GetUVCurve1()));
o.push_back(pool.id(cc.GetUVCurve2()));
o.push_back(pool.id(cc.GetAnyUVCurve1()));
o.push_back(pool.id(cc.GetAnyUVCurve2()));
o.push_back(pool.id(cc.GetSurface1()));
o.push_back(pool.id(cc.GetSurface2()));
o.push_back(pool.id(cc.GetFace()));
o.push_back(pool.id(cc.GetTopologyIntersector()));
o.push_back(pool.id(cc.GetOtherClassification()));
const SmTArray<SmLoop*>& loops = cc.GetTgtLoops();
o.push_back((double)loops.GetSize());
for (ULONG i = 0; i < loops.GetSize(); i++)
o.push_back(pool.id(loops[i]));
for (ULONG i = 0; i < cc.GetSize(); i++)
put_ivl(o, cc.GetAt(i), pool);
}
// natural interval, control point and knot counts of a B-spline (else -1 -1), 7 points
void put_curve(std::vector<double>& o, const SmCurve* c)
{
SmExtent1d ivl = c->GetNaturalInterval();
o.push_back(ivl.GetMin());
o.push_back(ivl.GetMax());
const SmBSplineCurve* b = c->IsKindOf(SmBSplineCurve_TYPE) ? (const SmBSplineCurve*)c : NULL;
const gw_CURVE* cur = b ? b->GetGwNurbPointer() : NULL;
o.push_back(cur ? (double)cur->pol->n : -1.0);
o.push_back(cur ? (double)cur->knt->m : -1.0);
for (int i = 0; i <= 6; i++)
{
SmPoint3d p;
SmStatus s = c->EvaluatePoint(ivl.Evaluate(i / 6.0), p);
o.push_back(st(s));
if (s == SM_SUCCESS)
put_vec(o, p);
}
}
bool is_surface(int64_t k) { return k == K_PLANE || k == K_BSURF; }
// the object kind a classification type needs; -1 for none (UNKNOWN, NOTHING), -2 invalid
int64_t kind_of_class(int64_t c)
{
switch (c)
{
case 100:
case 110: return -1;
case 102: return K_FACE;
case 103: return K_EDGE;
case 104: return K_VERTEX;
case 105: return K_POINT;
case 106: return K_CURVE;
case 107: return K_BSURF; // or K_PLANE
default: return -2;
}
}
SmPointClassification* pick(SmCurveInterval& ivl, int64_t w)
{
return w == 0 ? &ivl.m_vStart : w == 1 ? &ivl.m_vMid : w == 2 ? &ivl.m_vEnd : NULL;
}
// A point classification ComputePointParameters may be called on: not a point or stand-in.
bool computable(const SmPointClassification& pc, const Pool& pool)
{
int64_t k = pc.GetObject() == NULL ? -1 : pool.kind_of(pc.GetObject());
return k != K_POINT && k != K_LOOP && k != K_OTHER;
}
bool read_touches(Rd& rd, const Pool& pool, SmTArray<SmTouchData>& out)
{
int64_t n = rd.idx();
if (n < 0 || n > 64)
return false;
for (int64_t i = 0; i < n; i++)
{
SmTouchData t;
int64_t c = rd.idx();
t.m_eLoopDir = (SmCrvDirOnFaceType)rd.idx();
t.m_lLoopIndx = (ULONG)rd.idx();
t.m_dParam = rd.num();
t.m_lPtClassifyIndx = (ULONG)rd.idx();
int64_t pc = rd.idx();
if ((c != -1 && !pool.valid(c)) || (pc != -1 && !pool.valid(pc)))
return false;
t.m_pConnectedTo = c == -1 ? NULL : (SmTopology*)pool.ptrs[(size_t)c];
t.m_pPtClassify = pc == -1 ? NULL : (SmPointClassification*)pool.ptrs[(size_t)pc];
out.Add(t);
}
return !rd.bad;
}
void put_touch(std::vector<double>& o, const SmTouchData& t, const Pool& pool)
{
o.push_back(pool.id(t.m_pConnectedTo));
o.push_back((double)t.m_eLoopDir);
o.push_back((double)t.m_lLoopIndx);
o.push_back(t.m_dParam);
o.push_back((double)t.m_lPtClassifyIndx);
o.push_back(pool.id(t.m_pPtClassify));
}
int32_t run(Rd& rd, std::vector<double>& o)
{
const SmContext& ctx = the_context();
Pool pool;
// the one-face B-rep (deleted on every return; its objects join the pool after the
// script's objects)
SmBrep* brep = NULL;
sc_one_face::OneFace of;
bool has_face = rd.flag();
bool built = !has_face || sc_one_face::build(rd, ctx, brep, of);
SmObjDelete clean(brep);
if (rd.bad || !built)
return SC_ERR_ARG;
int64_t n_obj = rd.idx();
for (int64_t i = 0; i < n_obj && !rd.bad; i++)
{
int64_t kind = rd.idx();
void* p = NULL;
bool bs = false;
ULONG dim = 0;
switch (kind)
{
case K_CURVE: p = read_curve(rd, ctx, bs, dim); break;
case K_PLANE:
{
SmPoint3d org = rd.vec();
SmVector3d nrm = rd.vec();
p = new (ctx) SmPlane(org, nrm, &ctx);
break;
}
case K_BSURF:
{
bool oob = rd.flag();
SmBSplineSurface* s = new (ctx) SmBSplineSurface(read_nurb_surface(rd), FALSE, NULL);
s->GetOutOfBoundsEnabled() = oob ? TRUE : FALSE;
p = s;
break;
}
case K_POINT: p = new SmPoint3d(rd.vec()); break;
case K_VERTEX: p = new (anchor()) ScVertex(); break;
case K_EDGE:
{
int64_t c = rd.idx();
bool ok = true;
SmCurve* co = (SmCurve*)pool.get(c, {K_CURVE}, ok);
if (!ok)
return SC_ERR_ARG;
p = new (anchor()) ScEdge(co);
break;
}
case K_FACE:
{
int64_t s = rd.idx();
bool ok = true;
SmSurface* so = (SmSurface*)pool.get(s, {K_PLANE, K_BSURF}, ok);
if (!ok)
return SC_ERR_ARG;
p = new (anchor()) ScFace(so);
break;
}
case K_LOOP: p = new (&ctx) ScStandIn(SmLoop_TYPE); break;
case K_OTHER: p = new (&ctx) ScStandIn(SmObject_TYPE + 7); break;
default: return SC_ERR_ARG;
}
pool.ids[p] = (int64_t)pool.ptrs.size();
pool.kinds.push_back(kind);
pool.ptrs.push_back(p);
pool.bspline.push_back(bs);
pool.dims.push_back(dim);
pool.real.push_back(false);
}
if (rd.bad)
return SC_ERR_ARG;
// the B-rep's topology: the face, then per loop the loop and its vertex (a vertex loop) or
// per edge use its edge, its vertex use's vertex and the edge use (an edge loop)
auto add = [&pool](void* q, int64_t kind) {
pool.ids[q] = (int64_t)pool.ptrs.size();
pool.kinds.push_back(kind);
pool.ptrs.push_back(q);
pool.bspline.push_back(false);
pool.dims.push_back(0);
pool.real.push_back(true);
};
if (has_face)
{
add(of.face, K_FACE);
for (size_t li = 0; li < of.loops.size(); li++)
{
add(of.loops[li], K_LOOP);
if (of.loop_vertex[li] != NULL)
{
add(of.loop_vertex[li], K_VERTEX);
continue;
}
for (SmEdgeuse* eu : of.loop_edgeuses[li])
{
SmEdge* e = eu->GetEdge();
SmVertexuse* vu = eu->GetVertexuse();
SmPoint3d start;
// the edge use's vertex is its edge's start vertex (the Rust B-rep is built so)
if (e == NULL || vu == NULL || vu->GetVertex() == NULL ||
e->GetCurve()->EvaluatePoint(e->GetInterval().GetMin(), start) != SM_SUCCESS ||
vu->GetVertex()->GetPoint().DistanceBetweenSquared(start) > 1e-18)
return SC_ERR_ARG;
// the edge line with its NURB made, as the Rust side makes it
SmBSplineCurve* line = dynamic_cast<SmBSplineCurve*>(e->GetCurve());
if (line == NULL)
return SC_ERR_ARG;
line->GetOrCreateGwNurbPointer();
add(e, K_EDGE);
add(vu->GetVertex(), K_VERTEX);
add(eu, K_EDGEUSE);
}
}
}
SmCurveClassification ccs[2];
// the pool's curves are never deleted: clear the ownership flags before the
// classifications are destroyed (on every return)
struct Disown
{
SmCurveClassification* c;
~Disown()
{
c[0].*get(CcOwned()) = FALSE;
c[1].*get(CcOwned()) = FALSE;
}
} disown{ccs};
int64_t n_ops = rd.idx();
for (int64_t i = 0; i < n_ops && !rd.bad; i++)
{
int64_t op = rd.idx();
int64_t t = rd.idx();
if (t < 0 || t > 1)
return SC_ERR_ARG;
SmCurveClassification& cc = ccs[t];
SmCurveClassification& other = ccs[1 - t];
int64_t size = (int64_t)cc.GetSize();
bool ok = true;
o.push_back((double)op);
switch (op)
{
case OP_SET_SIZE:
{
int64_t n = rd.idx();
if (n < 0 || n > 64)
return SC_ERR_ARG;
cc.SetSize((ULONG)n);
o.push_back(0.0);
break;
}
case OP_REMOVE_AT:
{
int64_t j = rd.idx();
if (size == 0 || j < 0 || j > 64)
return SC_ERR_ARG;
cc.RemoveAt((ULONG)j);
o.push_back(0.0);
break;
}
case OP_IVL_SET:
case OP_IVL_SET_MIN:
case OP_IVL_SET_MAX:
case OP_IVL_RESET:
case OP_IVL_ASSIGN:
case OP_IVL_NEW:
{
int64_t j = rd.idx();
if (j < 0 || j >= size)
return SC_ERR_ARG;
SmCurveInterval& ivl = cc.GetAt((ULONG)j);
if (op == OP_IVL_SET)
{
double a = rd.num();
double b = rd.num();
ivl.SetInterval(SmExtent1d(a, b));
o.push_back(0.0);
}
else if (op == OP_IVL_SET_MIN)
o.push_back(st((ivl.*get(IvlSetMin()))(rd.num())));
else if (op == OP_IVL_SET_MAX)
o.push_back(st((ivl.*get(IvlSetMax()))(rd.num())));
else if (op == OP_IVL_RESET)
{
ivl.ReSet();
o.push_back(0.0);
}
else if (op == OP_IVL_ASSIGN)
{
int64_t k = rd.idx();
if (k < 0 || k >= (int64_t)other.GetSize())
return SC_ERR_ARG;
ivl = other.GetAt((ULONG)k);
o.push_back(0.0);
}
else
{
bool with = rd.flag();
bool owner = rd.flag();
double a = rd.num();
double b = rd.num();
const SmCurveClassification* po = owner ? &cc : NULL;
if (with)
ivl = SmCurveInterval(SmExtent1d(a, b), &ctx, po);
else
ivl = SmCurveInterval(&ctx, po);
o.push_back(0.0);
}
break;
}
case OP_PC_SET_CLASS:
case OP_PC_SET_T:
case OP_PC_SET_UV:
case OP_PC_SET_GAP:
case OP_PC_SET_SSS:
case OP_PC_SET_PRESNAP:
case OP_PC_SET_SRC_TOL:
{
int64_t j = rd.idx();
int64_t w = rd.idx();
if (j < 0 || j >= size || w < 0 || w > 2)
return SC_ERR_ARG;
SmPointClassification& pc = *pick(cc.GetAt((ULONG)j), w);
if (op == OP_PC_SET_CLASS)
{
int64_t c = rd.idx();
int64_t obj = rd.idx();
int64_t want = kind_of_class(c);
if (want == -2)
return SC_ERR_ARG;
void* po = pool.get(obj, {}, ok);
if (!ok)
return SC_ERR_ARG;
if (obj != -1)
{
int64_t k = pool.kinds[(size_t)obj];
if (!(k == want || (want == K_BSURF && k == K_PLANE)))
return SC_ERR_ARG;
}
pc.SetClassObject((SmPointClassificationType)c, (SmObject*)po);
}
else if (op == OP_PC_SET_T)
pc.SetTParam(rd.num());
else if (op == OP_PC_SET_UV)
{
double u = rd.num();
double v = rd.num();
pc.SetUVParam(SmPoint2d(u, v));
}
else if (op == OP_PC_SET_GAP)
pc.SetGap3d(rd.num());
else if (op == OP_PC_SET_SSS)
pc.SetSSSPoint(rd.vec());
else if (op == OP_PC_SET_PRESNAP)
pc.SetPreSnapParam(rd.num());
else
pc.SetSrcZoneTol3d(SmZoneTol3d(rd.num()));
o.push_back(0.0);
break;
}
case OP_SET_CURVE:
{
int64_t obj = rd.idx();
bool owned = rd.flag();
SmCurve* c = (SmCurve*)pool.get(obj, {K_CURVE}, ok);
if (!ok)
return SC_ERR_ARG;
// SetCurve deletes an owned previous curve: the pool's curves are never deleted,
// so the previous one is disowned first (the flag is data here)
cc.*get(CcOwned()) = FALSE;
cc.SetCurve(c, owned ? TRUE : FALSE);
o.push_back(0.0);
break;
}
case OP_SET_SURFACE1:
case OP_SET_SURFACE2:
{
SmSurface* s = (SmSurface*)pool.get(rd.idx(), {K_PLANE, K_BSURF}, ok);
if (!ok)
return SC_ERR_ARG;
if (op == OP_SET_SURFACE1)
cc.SetSurface1(s);
else
cc.SetSurface2(s);
o.push_back(0.0);
break;
}
case OP_SET_FACE:
{
SmFace* f = (SmFace*)pool.get(rd.idx(), {K_FACE}, ok);
if (!ok)
return SC_ERR_ARG;
cc.SetFace(f);
o.push_back(0.0);
break;
}
case OP_SET_INTERSECT_DATA:
{
SmSurface* s1 = (SmSurface*)pool.get(rd.idx(), {K_PLANE, K_BSURF}, ok);
SmSurface* s2 = (SmSurface*)pool.get(rd.idx(), {K_PLANE, K_BSURF}, ok);
SmCurve* uv2 = (SmCurve*)pool.get(rd.idx(), {K_CURVE}, ok);
bool cur = rd.flag();
void* ti = pool.get(rd.idx(), {K_OTHER}, ok);
if (!ok)
return SC_ERR_ARG;
cc.SetIntersectData(s1, s2, uv2, cur ? TRUE : FALSE, (const SmTopologyIntersector*)ti);
o.push_back(0.0);
break;
}
case OP_SET_IS_CURVE_CURRENT:
cc.SetIsCurveCurrent(rd.flag() ? TRUE : FALSE);
o.push_back(0.0);
break;
case OP_SET_IGNORE_TANGENCY:
cc.SetIgnoreTangency(rd.flag() ? TRUE : FALSE);
o.push_back(0.0);
break;
case OP_SET_IS_CURVE_OWNED:
// the flag only: the pool's curves are never deleted (see OP_SET_CURVE)
cc.SetIsCurveOwned(rd.flag() ? TRUE : FALSE);
o.push_back(0.0);
break;
case OP_SET_OK_TO_SET_TOL:
cc.SetOKToSetTolerance(rd.flag() ? TRUE : FALSE);
o.push_back(0.0);
break;
case OP_SET_FILLET_DATA:
{
void* oc = pool.get(rd.idx(), {K_OTHER}, ok);
bool b = rd.flag();
if (!ok)
return SC_ERR_ARG;
cc.SetFilletData((SmCurveClassification*)oc, b ? TRUE : FALSE);
o.push_back(0.0);
break;
}
case OP_SET_TGT_LOOPS_ONLY:
cc.SetTgtLoopsOnly(rd.flag() ? TRUE : FALSE);
o.push_back(0.0);
break;
case OP_ADD_TGT_LOOP:
{
int64_t j = rd.idx();
void* l = pool.get(j, {K_LOOP}, ok);
if (!ok || j == -1)
return SC_ERR_ARG;
cc.AddTgtLoop((SmLoop*)l);
o.push_back(0.0);
break;
}
case OP_SET_SRC_TOL:
SmTol::SetZoneTol3d(&cc, SmZoneTol3d(rd.num()));
o.push_back(0.0);
break;
case OP_SET_UV_CURVE1:
case OP_SET_UV_CURVE2:
{
SmCurve* c = (SmCurve*)pool.get(rd.idx(), {K_CURVE}, ok);
if (!ok)
return SC_ERR_ARG;
if (op == OP_SET_UV_CURVE1)
cc.SetUVCurve1(c);
else
cc.SetUVCurve2(c);
o.push_back(0.0);
break;
}
case OP_SUB_INTERVAL:
{
int64_t j = rd.idx();
if (j < 0 || j >= size)
return SC_ERR_ARG;
SmExtent1d e = cc.GetSubInterval((ULONG)j);
o.push_back(0.0);
o.push_back(e.GetMin());
o.push_back(e.GetMax());
break;
}
case OP_CLASSIFY_CRV_LOC:
{
int64_t j = rd.idx();
int64_t w = rd.idx();
double prm = rd.num();
if (j < 0 || j >= size || w < 0 || w > 2)
return SC_ERR_ARG;
SmPointClassification* pc = pick(cc.GetAt((ULONG)j), w);
if (!pc->GetSSSPointFlag() && cc.GetCurve() == NULL)
return SC_ERR_ARG;
SmPoint3d p = cc.GetClassifyCrvLoc(pc, prm);
o.push_back(0.0);
put_vec(o, p);
break;
}
case OP_SET_PC_PARAMS:
{
bool mids = rd.flag();
if (size > 0 && cc.GetCurve() == NULL)
return SC_ERR_ARG;
for (int64_t j = 0; j < size; j++)
{
SmCurveInterval& ivl = cc.GetAt((ULONG)j);
if (!computable(ivl.m_vStart, pool) || !computable(ivl.m_vEnd, pool) ||
(mids && !computable(ivl.m_vMid, pool)))
return SC_ERR_ARG;
}
o.push_back(st(cc.SetPointClassParameters(mids ? TRUE : FALSE)));
break;
}
case OP_SQUEEZE_END:
{
int64_t j = rd.idx();
if (j < 0 || j >= size)
return SC_ERR_ARG;
SmIntervalPosition pos = cc.FindBestSqueezeEnd((ULONG)j);
o.push_back(0.0);
o.push_back((double)pos);
break;
}
case OP_IS_REFERENCED:
{
void* po = pool.get(rd.idx(), {}, ok);
if (!ok)
return SC_ERR_ARG;
o.push_back(0.0);
o.push_back(flg(cc.IsReferenced((const SmObject*)po)));
break;
}
case OP_MAKE_CONSISTENT:
{
// the stand-in edges have no vertices (SmEdge::GetVertex would crash) and the
// stand-in vertices no edges: only the one-face B-rep's topology is walked
for (int64_t j = 0; j < size; j++)
{
SmCurveInterval& ivl = cc.GetAt((ULONG)j);
if (ivl.m_vMid.GetPointClass() != SM_PC_EDGE)
continue;
for (const SmPointClassification* e : {&ivl.m_vStart, &ivl.m_vEnd})
if (e->GetPointClass() == SM_PC_VERTEX &&
!(pool.is_real(ivl.m_vMid.GetObject()) && pool.is_real(e->GetObject())))
return SC_ERR_ARG;
// the drop to each edge of the vertex evaluates the classification curve
if (cc.GetCurve() == NULL || cc.GetCurve()->GetDim() != 3)
return SC_ERR_ARG;
}
o.push_back(st(sc_cc_p1::sm_MakeConsistentClassifications(cc)));
break;
}
case OP_TYPE:
{
int64_t n = rd.idx();
if (n < 0 || n > 8)
return SC_ERR_ARG;
std::vector<SM_TYPE> types;
for (int64_t k = 0; k < n; k++)
types.push_back((SM_TYPE)rd.idx());
o.push_back(0.0);
o.push_back((double)cc.GetType());
for (SM_TYPE ty : types)
o.push_back(flg(cc.IsKindOf(ty)));
const TCHAR* str = cc.GetTypeString();
std::vector<double> cs;
for (const TCHAR* c = str; *c; c++)
cs.push_back((double)*c);
o.push_back((double)cs.size());
o.insert(o.end(), cs.begin(), cs.end());
break;
}
case OP_SRC_TOL:
o.push_back(0.0);
o.push_back(SmTol::GetSrcZoneTol3d(&cc));
o.push_back(SmTol::GetSrcZoneTol3d((const SmCurveClassification*)NULL));
break;
case OP_IVL_TOLS:
{
int64_t j = rd.idx();
if (j < 0 || j >= size)
return SC_ERR_ARG;
const SmCurveInterval& ivl = cc.GetAt((ULONG)j);
int64_t k = ivl.m_vMid.GetObject() == NULL ? -1 : pool.kind_of(ivl.m_vMid.GetObject());
if (k == K_POINT)
return SC_ERR_ARG;
// SmTol::GetSrcZoneTol3d(ivl) reads the interval's back pointer (NULL: crash). The
// port has no back pointer and takes the owner explicitly (this classification):
// compare only intervals owned by this classification.
if (ivl.GetCurveClassification() != &cc)
return SC_ERR_ARG;
o.push_back(0.0);
o.push_back(SmTol::GetSrcZoneTol3d(&ivl));
o.push_back(SmTol::GetObjZoneTol3d(&ivl));
o.push_back(SmTol::GetObjZoneTol3d((const SmCurveInterval*)NULL));
o.push_back(SmTol::GetScaledZero(ivl));
break;
}
case OP_OBJ_DIM:
{
void* po = pool.get(rd.idx(), {}, ok);
if (!ok || (po != NULL && pool.kind_of(po) == K_POINT))
return SC_ERR_ARG;
o.push_back(0.0);
o.push_back((double)sc_cc_p1::sm_ObjectDimension((SmObject*)po));
break;
}
case OP_XSECT_CURVE:
{
int64_t cobj = rd.idx();
SmCurve* c = (SmCurve*)pool.get(cobj, {K_CURVE}, ok);
double tol = rd.num();
SmTArray<SmBSplineCurve*> uvc;
int64_t n_uvc = rd.idx();
for (int64_t k = 0; k < n_uvc && k < 8; k++)
{
int64_t j = rd.idx();
SmCurve* u = (SmCurve*)pool.get(j, {K_CURVE}, ok);
if (j == -1 || !ok || !pool.bspline[(size_t)j] || pool.dims[(size_t)j] != 2)
return SC_ERR_ARG;
uvc.Add((SmBSplineCurve*)u);
}
SmTArray<const SmSurface*> srf;
int64_t n_srf = rd.idx();
for (int64_t k = 0; k < n_srf && k < 8; k++)
{
int64_t j = rd.idx();
const SmSurface* s = (const SmSurface*)pool.get(j, {K_PLANE, K_BSURF}, ok);
if (j == -1)
return SC_ERR_ARG;
srf.Add(s);
}
int64_t n_uv = rd.idx();
std::vector<SmVector2d> uvs;
for (int64_t k = 0; k < n_uv && k < 8; k++)
{
double u = rd.num();
double v = rd.num();
uvs.push_back(SmVector2d(u, v));
}
double prm = rd.num();
double dev = rd.num();
SmPoint3d p3 = rd.vec();
if (!ok || c == NULL || n_uvc > 8 || n_srf > 8 || n_uv > 8 || rd.bad)
return SC_ERR_ARG;
// the first two UV points are read whenever there are two surfaces
if (n_srf >= 2 && n_uv < 2)
return SC_ERR_ARG;
SmTArray<SmVector2d*> uvp;
for (auto& u : uvs)
uvp.Add(&u);
SmBoolean mod = FALSE;
SmBoolean set3 = FALSE;
SmStatus s = sc_cc_p1::sm_SetXSectCurveThroughPoint(c, SmZoneTol3d(tol), uvc, srf, uvp, mod, prm, dev, set3, &p3);
o.push_back(st(s));
o.push_back(flg(mod));
o.push_back(prm);
o.push_back(dev);
o.push_back(flg(set3));
put_vec(o, p3);
put_curve(o, c);
for (ULONG k = 0; k < uvc.GetSize(); k++)
put_curve(o, uvc[k]);
break;
}
case OP_APPLY:
{
bool use_cc = rd.flag();
void* edge = pool.get(rd.idx(), {K_EDGE}, ok);
void* emate = pool.get(rd.idx(), {K_EDGE}, ok);
const SmSurface* srf[4];
SmPoint2d uvs[4];
for (int k = 0; k < 4; k++)
{
srf[k] = (const SmSurface*)pool.get(rd.idx(), {K_PLANE, K_BSURF}, ok);
double u = rd.num();
double v = rd.num();
uvs[k].Set(u, v);
}
SmSolution sol;
sol.m_eSolutionType = SM_ST_SINGLE_VALUE;
sol.m_vStart[0] = rd.num();
sol.m_vStart[1] = rd.num();
sol.m_vStart.m_dSolutionValue = rd.num();
SmPoint3d sss = rd.vec();
if (!ok || rd.bad)
return SC_ERR_ARG;
SmCurveClassification* pcc = use_cc ? &cc : NULL;
bool src = pcc != NULL && pcc->GetCurve() != NULL;
bool edge_eval = (edge != NULL || emate != NULL) && srf[2] != NULL;
// the classification curve's two surfaces are dereferenced together
if (src && ((srf[0] == NULL) != (srf[1] == NULL)))
return SC_ERR_ARG;
if (src && srf[0] == NULL && edge_eval)
return SC_ERR_ARG;
// the edge path walks topology the stand-in edges do not have
if (edge != NULL)
return SC_ERR_ARG;
SmBoolean flag = FALSE, mc = FALSE, me = FALSE, mm = FALSE;
double dev = 0.0;
SmStatus s = sc_cc_p1::sm_ApplyPointSolution(pcc, (SmEdge*)edge, (SmEdge*)emate, srf[0], &uvs[0], srf[1],
&uvs[1], srf[2], &uvs[2], srf[3], &uvs[3], sol, sss, flag, mc,
me, mm, dev);
o.push_back(st(s));
put_vec(o, sss);
o.push_back(flg(flag));
o.push_back(flg(mc));
o.push_back(flg(me));
o.push_back(flg(mm));
o.push_back(dev);
o.push_back(sol.m_vStart[0]);
o.push_back(sol.m_vStart[1]);
if (src)
put_curve(o, pcc->GetCurve());
break;
}
case OP_ANGLE:
{
const SmCurve* c1 = (const SmCurve*)pool.get(rd.idx(), {K_CURVE}, ok);
double t1 = rd.num();
const SmCurve* c2 = (const SmCurve*)pool.get(rd.idx(), {K_CURVE}, ok);
double t2 = rd.num();
double dev = rd.num();
if (!ok)
return SC_ERR_ARG;
o.push_back(0.0);
o.push_back(sc_cc_p1::sm_GetCurveCurveAngle(c1, t1, c2, t2, dev));
break;
}
case OP_CULL:
{
int64_t sj = rd.idx();
const SmSurface* s = (const SmSurface*)pool.get(sj, {K_PLANE, K_BSURF}, ok);
int64_t uj = rd.idx();
const SmCurve* uvc = (const SmCurve*)pool.get(uj, {K_CURVE}, ok);
double a0 = rd.num();
double a1 = rd.num();
const SmCurve* c2 = (const SmCurve*)pool.get(rd.idx(), {K_CURVE}, ok);
double b0 = rd.num();
double b1 = rd.num();
double tol = rd.num();
int64_t n = rd.idx();
if (!ok || s == NULL || uvc == NULL || c2 == NULL || n < 0 || n > 16 || pool.dims[(size_t)uj] != 2)
return SC_ERR_ARG;
SmSolutionArray sols;
for (int64_t k = 0; k < n; k++)
{
SmSolution sol;
sol.m_eSolutionType = (SmSolutionType)rd.idx();
sol.m_vStart[0] = rd.num();
sol.m_vStart[1] = rd.num();
sol.m_vEnd[0] = rd.num();
sol.m_vEnd[1] = rd.num();
sol.m_vStart.m_dSolutionValue = rd.num();
sols.Add(sol);
}
if (rd.bad)
return SC_ERR_ARG;
SmStatus r = sc_cc_p1::sm_CullOrRefineCCIIn3d(*s, *uvc, SmExtent1d(a0, a1), *c2, SmExtent1d(b0, b1), tol, sols);
o.push_back(st(r));
o.push_back((double)sols.GetSize());
for (ULONG k = 0; k < sols.GetSize(); k++)
{
const SmSolution& x = sols[k];
o.push_back((double)x.m_eSolutionType);
o.push_back(x.m_vStart[0]);
o.push_back(x.m_vStart[1]);
o.push_back(x.m_vEnd[0]);
o.push_back(x.m_vEnd[1]);
o.push_back(x.m_vStart.m_dSolutionValue);
}
break;
}
case OP_TOL_PAIR:
{
const SmCurve* c = (const SmCurve*)pool.get(rd.idx(), {K_CURVE}, ok);
const SmSurface* s = (const SmSurface*)pool.get(rd.idx(), {K_PLANE, K_BSURF}, ok);
double p1 = rd.num();
double t1 = rd.num();
double p2 = rd.num();
double t2 = rd.num();
if (!ok || c == NULL)
return SC_ERR_ARG;
o.push_back(0.0);
o.push_back(sm_GetXSectTol2dOr3d_ForClassifyPair(c, s, p1, SmZoneTol3d(t1), p2, SmZoneTol3d(t2)));
break;
}
case OP_GAPS:
{
SmPoint3d cp = rd.vec();
SmPoint3d ep = rd.vec();
SmSolution sol;
sol.m_vStart[0] = rd.num();
sol.m_vStart[1] = rd.num();
int64_t n = rd.idx();
if (n < 0 || n > 8)
return SC_ERR_ARG;
SmTArray<const SmSurface*> srf;
SmTArray<const SmCurve*> uvc;
for (int64_t k = 0; k < n; k++)
{
const SmSurface* s = (const SmSurface*)pool.get(rd.idx(), {K_PLANE, K_BSURF}, ok);
const SmCurve* u = (const SmCurve*)pool.get(rd.idx(), {K_CURVE}, ok);
if (s == NULL)
return SC_ERR_ARG;
srf.Add(s);
uvc.Add(u);
}
ULONG cnt = (ULONG)rd.idx();
if (!ok || rd.bad)
return SC_ERR_ARG;
double g1 = -7.0, g2 = -7.0, g3 = -7.0;
SmTArray<SmPoint2d> uvs;
SmStatus r = sc_cc_p1::sm_GetCCISurfaceGaps(cp, ep, sol, srf, uvc, cnt, g1, g2, g3, uvs);
o.push_back(st(r));
o.push_back(g1);
o.push_back(g2);
o.push_back(g3);
o.push_back((double)uvs.GetSize());
for (ULONG k = 0; k < uvs.GetSize(); k++)
{
o.push_back(uvs[k].x);
o.push_back(uvs[k].y);
}
break;
}
case OP_TOUCH_CONNECTED:
case OP_TOUCH_WINDING:
{
int64_t c = op == OP_TOUCH_CONNECTED ? rd.idx() : -1;
if (c != -1 && !pool.valid(c))
return SC_ERR_ARG;
SmTArray<SmTouchData> in;
if (!read_touches(rd, pool, in))
return SC_ERR_ARG;
o.push_back(0.0);
if (op == OP_TOUCH_CONNECTED)
{
SmTArray<SmTouchData> out;
ULONG cnt = SmTouchData::GetConnectedTouches(c == -1 ? NULL : (SmTopology*)pool.ptrs[(size_t)c], in, &out);
ULONG cnt2 = SmTouchData::GetConnectedTouches(c == -1 ? NULL : (SmTopology*)pool.ptrs[(size_t)c], in);
o.push_back((double)cnt);
o.push_back((double)cnt2);
for (ULONG k = 0; k < out.GetSize(); k++)
put_touch(o, out[k], pool);
}
else
{
if (in.GetSize() % 2 != 0)
return SC_ERR_ARG;
SmOrientType ot = SM_OT_UNKNOWN;
ULONG w = SmTouchData::GetWindingCount(in, ot);
o.push_back((double)w);
o.push_back((double)ot);
}
break;
}
case OP_EDGE_THROUGH:
{
// deferred in the port (the edge's topology); run for the record only
SmEdge* e = (SmEdge*)pool.get(rd.idx(), {K_EDGE}, ok);
SmPoint3d p = rd.vec();
SmTArray<const SmSurface*> srf;
SmTArray<SmVector2d*> uvp;
std::vector<SmVector2d> uvs(2);
for (int k = 0; k < 2; k++)
{
srf.Add((const SmSurface*)pool.get(rd.idx(), {K_PLANE, K_BSURF}, ok));
double u = rd.num();
double v = rd.num();
uvs[(size_t)k] = SmVector2d(u, v);
}
for (auto& u : uvs)
uvp.Add(&u);
double prm = rd.num();
if (!ok || e == NULL || srf[0] == NULL || srf[1] == NULL || e->GetCurve() == NULL)
return SC_ERR_ARG;
double dev = -7.0;
SmBoolean mod = FALSE;
SmStatus s = sc_cc_p1::sm_SetXSectEdgeThroughPoint(e, &p, srf, uvp, prm, dev, mod);
o.push_back(st(s));
o.push_back(prm);
o.push_back(dev);
o.push_back(flg(mod));
break;
}
case OP_CCI_GEOMETRY:
{
SmEdge* e = (SmEdge*)pool.get(rd.idx(), {K_EDGE}, ok);
if (!ok || e == NULL)
return SC_ERR_ARG;
SmSolution sol;
SmEdge* mate = NULL;
SmTArray<const SmSurface*> srf;
SmTArray<SmEdgeuse*> eus;
SmTArray<const SmCurve*> uvc;
ULONG cnt = 0;
SmStatus s = sc_cc_p1::sm_GetCCIGeometry(&cc, sol, e, mate, srf, eus, uvc, cnt);
o.push_back(st(s));
o.push_back((double)srf.GetSize());
o.push_back((double)cnt);
break;
}
case OP_FACE_POINT_CLASS:
{
SmFace* f = (SmFace*)pool.get(rd.idx(), {K_FACE}, ok);
SmPoint3d p = rd.vec();
double tol = rd.num();
if (!ok || f == NULL || f->GetSurface() == NULL)
return SC_ERR_ARG;
SmPointClassification pc;
SmStatus s = sc_cc_p1::sm_ComputeFacePointClass(p, SmZoneTol3d(tol), f, pc);
o.push_back(st(s));
o.push_back((double)pc.GetPointClass());
break;
}
default: return SC_ERR_ARG;
}
if (rd.bad)
return SC_ERR_ARG;
put_cc(o, cc, pool);
}
if (rd.bad || rd.at != rd.n)
return SC_ERR_ARG;
return SC_OK;
}
} // namespace
SC_EXPORT int32_t sc_intersect_curve_class_p1_run(const double* in, int64_t n_in, double* out, int64_t cap,
int64_t* out_len)
{
if (in == NULL || n_in < 0 || out_len == NULL)
return SC_ERR_ARG;
SC_TRY
Rd rd{in, n_in, 0};
std::vector<double> o;
int32_t s = run(rd, o);
if (s != SC_OK)
{
*out_len = 0;
return s;
}
return sc_copy_out(o.data(), (int64_t)o.size(), out, cap, out_len);
SC_CATCH
}