#include "opennurbs.h" #if !defined(ON_COMPILING_OPENNURBS) // This check is included in all opennurbs source .c and .cpp files to insure // ON_COMPILING_OPENNURBS is defined when opennurbs source is compiled. // When opennurbs source is being compiled, ON_COMPILING_OPENNURBS is defined // and the opennurbs .h files alter what is declared and how it is declared. #error ON_COMPILING_OPENNURBS must be defined when compiling opennurbs #endif #include "opennurbs_polyedgecurve.h" ON_OBJECT_IMPLEMENT(ON_PolyEdgeSegment,ON_CurveProxy,"42F47A87-5B1B-4e31-AB87-4639D78325D6"); ON_PolyEdgeSegment::ON_PolyEdgeSegment() { Init(); } ON_PolyEdgeSegment::~ON_PolyEdgeSegment() { Init(); } void ON_PolyEdgeSegment::Init() { m_component_index.UnSet(); m_object_id = ON_nil_uuid; m_brep = 0; m_trim = 0; m_edge = 0; m_face = 0; m_surface = 0; m_edge_domain = ON_Interval::EmptyInterval; m_trim_domain = ON_Interval::EmptyInterval; ON_CurveProxy::SetProxyCurve(0); ClearEvalCacheHelper(); } bool ON_PolyEdgeSegment::Create( const ON_BrepTrim* trim, const ON_UUID& object_id ) { Init(); if ( !trim ) return false; if ( trim->m_ei >= 0 ) return false; const ON_Brep* brep = trim->Brep(); if ( !brep ) return false; const ON_BrepEdge* edge = trim->Edge(); if ( !edge ) return false; const ON_Curve* c3 = edge->EdgeCurveOf(); if ( !c3 ) return false; m_brep = brep; m_trim = trim; m_edge = edge; m_face = trim->Face(); if ( m_face ) m_surface = m_face->SurfaceOf(); m_edge_domain = m_edge->Domain(); m_trim_domain = m_trim->Domain(); ON_CurveProxy::SetProxyCurve( c3, edge->ProxyCurveDomain() ); if ( m_edge->ProxyCurveIsReversed() ) ON_CurveProxy::Reverse(); ON_CurveProxy::SetDomain( m_edge_domain[0], m_edge_domain[1] ); m_component_index = trim->ComponentIndex(); m_object_id = object_id; return true; } bool ON_PolyEdgeSegment::ReversedEdgeDir() const { bool rc = false; if ( m_edge ) { rc = m_edge->ProxyCurveIsReversed() != ON_CurveProxy::ProxyCurveIsReversed(); } return rc; } bool ON_PolyEdgeSegment::ReversedTrimDir() const { bool rc = false; if ( m_trim && m_edge ) { rc = ReversedEdgeDir(); if ( m_trim->m_bRev3d ) rc = !rc; } return rc; } bool ON_PolyEdgeSegment::Create( const ON_Curve* curve, const ON_UUID& object_id ) { //bool rc = false; Init(); if ( !curve ) return false; const ON_BrepEdge* edge = ON_BrepEdge::Cast(curve); if ( edge ) { const ON_Brep* brep = edge->Brep(); if ( !brep ) return false; const ON_Curve* c3 = edge->EdgeCurveOf(); if ( !c3 ) return false; m_edge = edge; m_brep = brep; m_component_index = edge->ComponentIndex(); m_edge_domain = m_edge->Domain(); m_trim_domain = m_trim->Domain(); ON_CurveProxy::SetProxyCurve( c3, edge->ProxyCurveDomain() ); if ( m_edge->ProxyCurveIsReversed() ) ON_CurveProxy::Reverse(); ON_CurveProxy::SetDomain( m_edge_domain[0], m_edge_domain[1] ); } else { ON_CurveProxy::SetProxyCurve(const_cast(curve)); } m_object_id = object_id; return true; } const ON_BrepEdge* ON_PolyEdgeSegment::BrepEdge() const { return m_edge; } const ON_BrepTrim* ON_PolyEdgeSegment::BrepTrim() const { return m_trim; } const ON_Brep* ON_PolyEdgeSegment::Brep() const { return m_brep; } const ON_BrepFace* ON_PolyEdgeSegment::BrepFace() const { return m_face; } const ON_Surface* ON_PolyEdgeSegment::Surface() const { return m_surface; } ON_Surface::ISO ON_PolyEdgeSegment::IsoType() const { return m_trim ? m_trim->m_iso : ON_Surface::not_iso; } ON_Interval ON_PolyEdgeSegment::EdgeDomain() const { return m_edge_domain; } ON_Interval ON_PolyEdgeSegment::TrimDomain() const { return m_trim_domain; } void ON_PolyEdgeSegment::ClearEvalCacheHelper() { m_t = ON_UNSET_VALUE; m_edge_t = ON_UNSET_VALUE; m_trim_t = ON_UNSET_VALUE; m_srf_uv[0] = ON_UNSET_VALUE; m_srf_uv[1] = ON_UNSET_VALUE; m_trim_hint = 0; m_edge_hint = 0; m_evsrf_hint[0] = 0; m_evsrf_hint[1] = 0; m_evsrf_uv[0] = ON_UNSET_VALUE; m_evsrf_uv[1] = ON_UNSET_VALUE; m_evsrf_pt = ON_3dPoint::UnsetPoint; } double ON_PolyEdgeSegment::EdgeParameter(double t) const { double edge_t = ON_UNSET_VALUE; if ( m_edge ) { if ( m_t == t && m_edge_t != ON_UNSET_VALUE ) edge_t = m_edge_t; else { ON_PolyEdgeSegment* p = const_cast(this); if ( t != m_t ) { p->m_t = t; p->m_trim_t = ON_UNSET_VALUE; p->m_srf_uv[0] = ON_UNSET_VALUE; p->m_srf_uv[1] = ON_UNSET_VALUE; } ON_Interval d = Domain(); bool bReversedEdgeDir = ReversedEdgeDir(); if ( bReversedEdgeDir || m_edge_domain != d ) { double s = d.NormalizedParameterAt(t); if ( bReversedEdgeDir ) s = 1.0 - s; edge_t = m_edge_domain.ParameterAt(s); } else edge_t = t; p->m_edge_t = edge_t; } } return edge_t; } ON_OBJECT_IMPLEMENT(ON_PolyEdgeCurve,ON_PolyCurve,"39FF3DD3-FE0F-4807-9D59-185F0D73C0E4"); ON_PolyEdgeCurve::ON_PolyEdgeCurve() { } ON_PolyEdgeCurve::~ON_PolyEdgeCurve() { } bool ON_PolyEdgeCurve::SetStartPoint( ON_3dPoint start_point ) { return ON_Curve::SetStartPoint(start_point); // cannot change edges } bool ON_PolyEdgeCurve::SetEndPoint( ON_3dPoint end_point ) { return ON_Curve::SetEndPoint(end_point); // cannot change edges } bool ON_PolyEdgeCurve::ChangeClosedCurveSeam( double t ) { //int saved_is_closed_helper = m_is_closed_helper; if ( SegmentCount() == 1 ) { // A ON_PolyEdgeSegment cannot have its start/end // changed. Split it into two segments and let // ON_PolyCurve::ChangeClosedCurveSeam() do the work. if ( !IsClosed() ) return false; ON_Interval crvd = Domain(); double s = crvd.NormalizedParameterAt(t); if ( s <= ON_SQRT_EPSILON || s >= (1.0 - ON_SQRT_EPSILON) ) { s = fmod(s,1.0); if ( s < 0.0 ) s += 1.0; if ( fabs(s) <= ON_SQRT_EPSILON || fabs(1.0-s) <= ON_SQRT_EPSILON ) { // split parameter is at start/end of this segemnt if ( t != crvd[0] ) { DestroyRuntimeCache(); SetDomain(t,t+crvd.Length() ); //m_is_closed_helper = saved_is_closed_helper; } return true; } return false; } ON_PolyEdgeSegment* left_seg = SegmentCurve(0); if ( 0 == left_seg ) return false; DestroyRuntimeCache(); ON_Curve* left = left_seg; ON_Curve* right = 0; double segt = SegmentCurveParameter(t); if ( !left_seg->Split(segt,left,right) ) return false; SetDomain(crvd[0],t); ON_PolyEdgeSegment* right_seg = ON_PolyEdgeSegment::Cast(right); if ( 0 == right_seg ) return false; Append(right_seg); double st[3]; st[0] = crvd[0]; st[1] = t; st[2] = crvd[1]; SetParameterization( st ); } // ON_PolyCurve::ChangeClosedCurveSeam works fine on // two or more segments. bool rc = ON_PolyCurve::ChangeClosedCurveSeam(t); //if ( saved_is_closed_helper ) // m_is_closed_helper = saved_is_closed_helper; return rc; } bool ON_PolyEdgeCurve::PrependAndMatch(ON_Curve*) { return false; // cannot change edges } bool ON_PolyEdgeCurve::AppendAndMatch(ON_Curve*) { return false; // cannot change edges } ON_Curve* ON_PolyEdgeSegment::DuplicateCurve() const { return ON_CurveProxy::DuplicateCurve(); // 21 December 2004 Dale Lear // This is wrong. I did it some time ago as a quick // fix for one of Lowell's early uses of CRhinoPolyEdges // however, this will cause lots of crashes now that // all commands have to deal with polyedges and the code // in those commands assumes that DuplicateCurve() returns // a valid stand-alone curve. If you end up here and // wish this code still worked the old way, please get // in touch with Dale Lear and we'll find a way to get // your code to work. // NO // ON_PolyEdgeSegment* dup = Duplicate(); // NO // return dup; } ON_Curve* ON_PolyEdgeCurve::DuplicateCurve() const { return ON_PolyCurve::DuplicateCurve(); // 21 December 2004 Dale Lear // The code below is wrong. I wrote it some time ago as a quick // fix for one of Lowell's early uses of CRhinoPolyEdges // however, this will cause lots of crashes now that // all commands have to deal with polyedges and the code // in those commands assumes that DuplicateCurve() returns // a valid stand-alone curve. If you end up here and // wish this code still worked the old way, please get // in touch with Dale Lear and we'll find a way to get // your code to work. // NO // int cnt = Count(); // NO // ON_SimpleArray t(cnt+1); // NO // ON_PolyEdgeCurve* dup_crv = new ON_PolyEdgeCurve(); // NO // // NO // t.Append(Domain()[0]); // NO // // NO // for( int i=0; iON_PolyCurve::Append( seg->DuplicateCurve() ); // NO // } // NO // } // NO // // NO // if( cnt > 0 && cnt+1 == t.Count() ) // NO // { // NO // dup_crv->SetParameterization( t.Array() ); // NO // } // NO // // NO // dup_crv->m_ev_srf_tan_mode = m_ev_srf_tan_mode; // NO // dup_crv->m_is_closed_helper = m_is_closed_helper; // NO // // NO // return dup_crv; } bool ON_PolyEdgeSegment::IsClosed(void) const { bool rc = ON_CurveProxy::IsClosed(); if ( !rc && m_edge && m_edge->m_vi[0] == m_edge->m_vi[1] && m_edge->ProxyCurve() == ProxyCurve() && m_edge->ProxyCurveDomain() == ProxyCurveDomain() && 0 != ProxyCurve() && ProxyCurve()->Domain() == ProxyCurveDomain() ) { rc = m_edge->IsClosed(); } return rc; } // 7-1-03 lw added override to unset cached closed flag // when a segment is removed bool ON_PolyEdgeCurve::Remove( int segment_index ) { bool rc = ON_PolyCurve::Remove( segment_index); //if( rc) // m_is_closed_helper = 0; // Cached closed flag... return rc; } bool ON_PolyEdgeCurve::Remove( ) { return Remove(Count()-1); } bool ON_PolyEdgeCurve::IsClosed(void) const { bool rc = ON_PolyCurve::IsClosed(); if ( !rc && SegmentCount() > 1 ) { // Since the segments that make up a ON_PolyEdgeCurve // cannot have their ends matched (becuase the curves // belong to objects alread in the rhino model), // the IsClosed() test has to tolerate larger gaps // in brep topology. // // If the start and end segments are edges that belong // to the same brep, then they "join" if and only if // they share a vertex. const ON_PolyEdgeSegment* seg0 = SegmentCurve(0); const ON_PolyEdgeSegment* seg1 = SegmentCurve(SegmentCount()-1); const ON_BrepEdge* edge0 = seg0->BrepEdge(); const ON_BrepEdge* edge1 = seg1->BrepEdge(); if ( edge0 && edge1 && edge0->Brep() == edge1->Brep() ) { // check for topological closure // // Do NOT add a test for sloppy geometric closure here. // If the edges are in the same brep and they don't // meet topologicially, then there is something in the // brep that is separating the edges. int evi0 = seg0->ReversedEdgeDir() ? 1 : 0; int evi1 = seg1->ReversedEdgeDir() ? 0 : 1; double et0 = seg0->EdgeParameter(seg0->Domain()[0]); double et1 = seg1->EdgeParameter(seg1->Domain()[1]); if ( et0 != ON_UNSET_VALUE && et1 != ON_UNSET_VALUE ) { ON_Interval edom0 = edge0->Domain(); ON_Interval edom1 = edge1->Domain(); if ( et0 == edom0[evi0] && et1 == edom1[evi1] ) { // The polyedge starts at the (evi0?end:start) of edge0 // and ends at the (ev1?end:start) of edge1. if ( edge0->m_vi[evi0] == edge1->m_vi[evi1] ) { // the polyedge start/ends at a common vertex rc = true; } } else if ( edge0 == edge1 && fabs(et0-et1) <= ON_ZERO_TOLERANCE && edom0.Includes(et0,true) && edom1.Includes(et1,true) ) { // The start/end of the polyedge is an interior point // of a single edge. (This happens when the "seam" gets // adjusted to be inside of an edge.) It is unlikely that // ON_PolyCurve::IsClosed() would return false in this // case, but this check should keep everybody happy. rc = true; } } } } return rc; } bool ON_PolyEdgeCurve::Create( const ON_BrepTrim* trim, const ON_UUID& object_id ) { bool rc = false; Destroy(); //m_is_closed_helper = 0; if ( trim ) { ON_PolyEdgeSegment* segment = new ON_PolyEdgeSegment(); rc = segment->Create(trim,object_id); if (rc ) Append(segment); else delete segment; } return rc; } bool ON_PolyEdgeCurve::Create( const ON_Curve* curve, const ON_UUID& object_id ) { bool rc = false; Destroy(); //m_is_closed_helper = 0; if ( curve ) { ON_PolyEdgeSegment* segment = new ON_PolyEdgeSegment(); rc = segment->Create(curve,object_id); if (rc ) Append(segment); else delete segment; } return rc; } int ON_PolyEdgeCurve::SegmentCount() const { return ON_PolyCurve::Count(); } ON_PolyEdgeSegment* ON_PolyEdgeCurve::SegmentCurve( int segment_index ) const { return ON_PolyEdgeSegment::Cast(ON_PolyCurve::SegmentCurve(segment_index)); } ON_PolyEdgeSegment* ON_PolyEdgeCurve::operator[](int segment_index) const { return SegmentCurve(segment_index); } void ON_PolyEdgeCurve::DestroyRuntimeCache( bool bDelete ) { //m_is_closed_helper = 0; ON_PolyCurve::DestroyRuntimeCache(bDelete); } void ON_PolyEdgeSegment::DestroyRuntimeCache( bool bDelete ) { ClearEvalCacheHelper(); ON_CurveProxy::DestroyRuntimeCache(bDelete); } bool ON_PolyEdgeCurve::Prepend( ON_PolyEdgeSegment* new_segment ) { DestroyRuntimeCache(); bool rc = false; if ( new_segment ) { if ( Count() > 0 ) { // keep segment domains in synch with polycurve domain // so that parameter bookkeeping is easy. ON_Interval cdom = Domain(); ON_Interval sdom = new_segment->Domain(); if ( sdom[1] != cdom[0] ) { sdom[0] = cdom[0] - sdom.Length(); sdom[1] = cdom[0]; new_segment->SetDomain(sdom[0],sdom[1]); } } rc = ON_PolyCurve::Prepend(new_segment); } return rc; } bool ON_PolyEdgeCurve::Append( ON_PolyEdgeSegment* new_segment ) { DestroyRuntimeCache(); bool rc = false; if ( new_segment ) { //m_is_closed_helper = 0; if ( Count() > 0 ) { // keep segment domains in synch with polycurve domain // so that parameter bookkeeping is easy. ON_Interval cdom = Domain(); ON_Interval sdom = new_segment->Domain(); if ( sdom[0] != cdom[1] ) { sdom[1] = cdom[1] + sdom.Length(); sdom[0] = cdom[1]; new_segment->SetDomain(sdom[0],sdom[1]); } } rc = ON_PolyCurve::Append(new_segment); } return rc; } bool ON_PolyEdgeCurve::Insert( int segment_index, ON_PolyEdgeSegment* new_segment ) { DestroyRuntimeCache(); bool rc = false; if ( segment_index > 0 ) { //m_is_closed_helper = 0; rc = ON_PolyCurve::Insert(segment_index,new_segment); if ( rc ) { int i; for ( i = segment_index; i < Count(); i++ ) { ON_PolyEdgeSegment* seg = SegmentCurve(i); ON_Interval d = SegmentDomain(i); seg->SetDomain(d[0],d[1]); } } } else if ( segment_index == 0 ) rc = Prepend(new_segment); return rc; } const ON_BrepEdge* ON_PolyEdgeCurve::EdgeAt(double t) const { ON_PolyEdgeSegment* seg = SegmentCurve( SegmentIndex(t) ); return seg ? seg->BrepEdge() : 0; } const ON_BrepTrim* ON_PolyEdgeCurve::TrimAt(double t) const { ON_PolyEdgeSegment* seg = SegmentCurve( SegmentIndex(t) ); return seg ? seg->BrepTrim() : 0; } const ON_Brep* ON_PolyEdgeCurve::BrepAt(double t) const { ON_PolyEdgeSegment* seg = SegmentCurve( SegmentIndex(t) ); return seg ? seg->Brep() : 0; } const ON_BrepFace* ON_PolyEdgeCurve::FaceAt(double t) const { ON_PolyEdgeSegment* seg = SegmentCurve( SegmentIndex(t) ); return seg ? seg->BrepFace() : 0; } const ON_Surface* ON_PolyEdgeCurve::SurfaceAt(double t) const { ON_PolyEdgeSegment* seg = SegmentCurve( SegmentIndex(t) ); return seg ? seg->Surface() : 0; } ON_Surface::ISO ON_PolyEdgeCurve::IsoType( double t) const { ON_PolyEdgeSegment* seg = SegmentCurve( SegmentIndex(t) ); return seg ? seg->IsoType() : ON_Surface::not_iso; } double ON_PolyEdgeCurve::EdgeParameter(double t) const { double edge_t = ON_UNSET_VALUE; int segment_index = SegmentIndex(t); ON_PolyEdgeSegment* seg = SegmentCurve( segment_index ); if ( seg ) { ON_Interval pdom = SegmentDomain(segment_index); ON_Interval sdom = seg->Domain(); if ( sdom != pdom ) { double s = pdom.NormalizedParameterAt(t); t = sdom.ParameterAt(s); } edge_t = seg->EdgeParameter(t); } return edge_t; } // Test if there are any surface edges in the polyedge bool ON_PolyEdgeCurve::ContainsAnyEdges() const { int i, count = SegmentCount(); for( i = 0; i < count; i++) { ON_PolyEdgeSegment* segment = SegmentCurve(i); if( 0 != segment && nullptr != segment->BrepEdge()) { return true; } } return false; } // Test if all segments of the polyedge are surface edges bool ON_PolyEdgeCurve::ContainsAllEdges() const { int i, count = SegmentCount(); for( i = 0; i < count; i++) { ON_PolyEdgeSegment* segment = SegmentCurve(i); if( nullptr == segment || nullptr == segment->BrepEdge()) { return false; } } return true; } int ON_PolyEdgeCurve::FindEdge( const ON_BrepEdge* edge) const { int rc = -1; if ( 0 != edge ) { int i, count = SegmentCount(); for( i = 0; i < count; i++) { ON_PolyEdgeSegment* segment = SegmentCurve(i); if ( 0 != segment && edge == segment->BrepEdge() ) { rc = i; break; } } } return rc; } int ON_PolyEdgeCurve::FindTrim( const ON_BrepTrim* trim) const { int rc = -1; if ( 0 != trim ) { int i, count = SegmentCount(); for( i = 0; i < count; i++) { ON_PolyEdgeSegment* segment = SegmentCurve(i); if ( 0 != segment && trim == segment->BrepTrim() ) { rc = i; break; } } } return rc; } int ON_PolyEdgeCurve::FindCurve( const ON_Curve* curve) const { int rc = -1; if ( 0 != curve ) { int i, count = SegmentCount(); for( i = 0; i < count; i++) { ON_PolyEdgeSegment* segment = SegmentCurve(i); if ( 0 != segment && (curve == segment || curve == segment->ProxyCurve() || curve == segment->BrepEdge()) ) { rc = i; break; } } } return rc; } bool ON_PolyEdgeSegment::Write( ON_BinaryArchive& archive ) const { bool rc = archive.BeginWrite3dmChunk(TCODE_ANONYMOUS_CHUNK,1,0); if (!rc) return false; for(;;) { rc = archive.WriteUuid(m_object_id); if (!rc) break; rc = archive.WriteComponentIndex(m_component_index); if (!rc) break; rc = archive.WriteInterval(m_edge_domain); if (!rc) break; rc = archive.WriteInterval(m_trim_domain); if (!rc) break; bool b = ON_CurveProxy::ProxyCurveIsReversed(); rc = archive.WriteBool(b); if (!rc) break; rc = archive.WriteInterval(ON_CurveProxy::Domain()); if (!rc) break; rc = archive.WriteInterval(ON_CurveProxy::ProxyCurveDomain()); if (!rc) break; break; } if ( !archive.EndWrite3dmChunk() ) rc = false; return rc; } bool ON_PolyEdgeSegment::Read( ON_BinaryArchive& archive ) { Init(); int major_version = 0; int minor_version = 0; bool rc = archive.BeginRead3dmChunk(TCODE_ANONYMOUS_CHUNK,&major_version,&minor_version); if (!rc) return false; for(;;) { rc = (1 == major_version); if ( !rc ) break; rc = archive.ReadUuid(m_object_id); if (!rc) break; rc = archive.ReadComponentIndex(m_component_index); if (!rc) break; rc = archive.ReadInterval(m_edge_domain); if (!rc) break; rc = archive.ReadInterval(m_trim_domain); if (!rc) break; // Read ON_ProxyCurve values we need bool bReversed = false; rc = archive.ReadBool(&bReversed); if (!rc) break; ON_Interval this_domain; rc = archive.ReadInterval(this_domain); if (!rc) break; ON_Interval real_curve_domain; rc = archive.ReadInterval(real_curve_domain); if (!rc) break; if ( bReversed) ON_CurveProxy::Reverse(); ON_CurveProxy::SetDomain(this_domain); ON_CurveProxy::SetProxyCurveDomain(real_curve_domain); break; } if ( !archive.EndRead3dmChunk() ) rc = false; return rc; }