// (Steve) #include using namespace std; #include "mathlib.h" #include "datalib.h" #include "slices.h" #include "sifpart.h" // BUG!!! I need to check for coincident contours and match them up and // ignore matches and invalidate nesting of any unmatched one on the next // slice. #ifndef NDEBUG #define NOBBTEST #endif #ifndef NDISPLAY #include #endif #ifndef NDEBUG static UINT_32 uiColinear = 0; static UINT_32 uiIncremental = 0, uiNonIncremental = 0; #endif //Timer g_timerIsect; Point *CContourElt::GetPrevPt() { CContourElt *pContourEltPrev; CSliceInfo *pInfoPrev; pContourEltPrev = this->GetPrevPtLink(); pInfoPrev = (CSliceInfo *)(pContourEltPrev->GetEdge()->GetExtra()); return (pInfoPrev->GetLastPt()); } Point *CContourElt::GetNextPt() { CContourElt *pContourEltNext; CSliceInfo *pInfoNext; pContourEltNext = this->GetNextPtLink(); pInfoNext = (CSliceInfo *)(pContourEltNext->GetEdge()->GetExtra()); return (pInfoNext->GetLastPt()); } CContourElt *CContourElt::GetPrevPtLink() { CContourElt *pContourEltPrev; CSliceInfo *pInfoPrev; pContourEltPrev = this->GetPrev(); pInfoPrev = (CSliceInfo *)(pContourEltPrev->GetEdge()->GetExtra()); while ((pInfoPrev->GetColinear() == TRUE)){ pContourEltPrev = pContourEltPrev->GetPrev(); pInfoPrev = (CSliceInfo *)(pContourEltPrev->GetEdge()->GetExtra()); if (pContourEltPrev == this) { return NULL; } } return (pContourEltPrev); } CContourElt *CContourElt::GetNextPtLink() { CContourElt *pContourEltNext; CSliceInfo *pInfoNext; pContourEltNext = this->GetNext(); pInfoNext = (CSliceInfo *)(pContourEltNext->GetEdge()->GetExtra()); while ((pInfoNext->GetColinear() == TRUE)){ pContourEltNext = pContourEltNext->GetNext(); pInfoNext = (CSliceInfo *)(pContourEltNext->GetEdge()->GetExtra()); if (pContourEltNext == this) { return NULL; } } return (pContourEltNext); } ////////////////////////////////////////////////////////////////////////////// // CSliceInfo Class Point *CSliceInfo::UpdatePt(FLOAT fZ, CLEDSliteEdgeUse *pledsEU, FLOAT fThickness) { // g_timerIsect.Start(); FLOAT fX, fY; #ifdef REMOVE_COLINEAR if (m_bPtColinear) { #ifndef NDEBUG // g_timerIsect.Stop(); uiColinear++; #endif return NULL; } #endif if (m_bPtSet) { // reduce self-intersections due to error stackup #ifndef NDEBUG uiIncremental++; // bookkeeping for incremental calculations #endif FLOAT fFraction; fFraction = ( (m_ptTop(Z) - fZ) / m_fDelZ); fX = m_ptTop(X) - (fFraction * m_fDelX); fY = m_ptTop(Y) - (fFraction * m_fDelY); } else { //initial calculation for first intersection with edge #ifndef NDEBUG uiNonIncremental++; #endif FLOAT fFraction; Point PtBottom; // This lookup time is considerable, so we store. Would be nice // to store it with the edge to begin with if we know we'll slice. PtBottom = pledsEU->GetLEDSRootVtx()->GetPoint(); m_ptTop = pledsEU->GetLEDSSiblingEdgeUse()->GetLEDSRootVtx()->GetPoint(); ASSERT ( (m_ptTop(Z)) >= (PtBottom(Z)) ); m_fDelZ = (m_ptTop(Z) - PtBottom(Z)); fFraction = ( (m_ptTop(Z) - fZ) / m_fDelZ); m_fDelX = (m_ptTop(X) - PtBottom(X)); m_fDelY = (m_ptTop(Y) - PtBottom(Y)); fX = m_ptTop(X) - (fFraction * m_fDelX); fY = m_ptTop(Y) - (fFraction * m_fDelY); m_bPtSet = TRUE; } m_ptLast.Set(fX, fY, 0.0); // g_timerIsect.Stop(); return &m_ptLast; } ////////////////////////////////////////////////////////////////////////////// // CSlices Class // CSlices::CSlices() { Init(); } CSlices::~CSlices() { Uninit(); } VOID CSlices::Init() { m_pVertexArray = NULL; m_uiNumVerts = 0; m_fOffset = 0; m_fThickness = 0.5; m_fScale = 1.0; m_fMinZ = 0; m_fSliceZ = 0; m_iPrecisionExp = 6; m_fEps = .0001f; // This is just a guess; not derived based on actual code. m_vUp.Set(0,0,1.0); } // CSG: This should take as input a CSG tree of LEDSGeometries. //VOID CSlices::Init(CLEDSGeometry *pCLEDSGeometry) VOID CSlices::Init(CSIFPart *pCSIFPart) { CListIter ListIter; CLEDSliteVertex *item; CList *pListVertices; Point pt; Quater q; Vector vZ, vCross; FLOAT fAngle; BOOL bRotate; CList *pListComponents; m_pCSIFPart = pCSIFPart; vZ.Set(0,0,1.0); vCross = m_vUp.Cross(vZ); fAngle = (FLOAT) AngleBetween(vZ, m_vUp); bRotate = (q.SetRotation(vCross, fAngle)); m_pVertexArray = new CDynamicArray; m_pVertexArray->Init(128); m_uiNumVerts = 0; CLEDSGeometry *pCLEDSGeometry; CSIFPartGeomIter PartGeomIter; PartGeomIter.Init(pCSIFPart); while (pCLEDSGeometry = PartGeomIter.PeekNext()) { pListComponents = new CList; pListComponents->Init(); pCLEDSGeometry->SetListComponents(pListComponents); pListVertices = pCLEDSGeometry->GetCListVertices(); m_uiNumVerts += pListVertices->GetLength(); // put list of vertices into an array for sorting ListIter.Init(pListVertices); item = (CLEDSliteVertex *) ListIter.PeekFirst(); while (item) { if (bRotate) { pt = item->GetPoint(); pt = q.RotatePoint(pt); item->SetPoint(pt); } if (m_fScale != 1.0) { pt = item->GetPoint(); pt *= m_fScale; item->SetPoint(pt); } m_pVertexArray->AddItemToEnd(item); item =(CLEDSliteVertex *) ListIter.PeekNext(); } } // sort them by z coordinate (OPTIMIZE: is there a faster sort?) // Timer timer; // timer.Start(); m_pVertexArray->Sort(ZVertexCompare); // timer.Stop(); // DOUBLE foo = timer.GetSeconds(); // printf("Sorting vertices took %f seconds\n",foo); // set minimum z coordinate item = *(m_pVertexArray->GetItem(0)); m_fMinZ = item->GetPoint()(Z); } VOID CSlices::Uninit() { if (m_pVertexArray) delete m_pVertexArray; // if (m_pListComponents) // delete m_pListComponents; } VOID CSlices::SliceIt() { UINT_32 i, uiNumSlices = 0; CLEDSliteVertex *pVtx; CList *pListComponents; OutputHeader(m_pCSIFPart); OutputThickness(m_fThickness); m_fSliceZ = m_fMinZ + m_fOffset; // CSG: Will need to do this for each leaf of csg tree // pListComponents = new CList; // pListComponents->Init(); for (i = 0; i < m_uiNumVerts; i++) { pVtx = *(m_pVertexArray->GetItem(i)); while( (pVtx->GetPoint()(Z)) > m_fSliceZ ) { // This function takes a components list and outputs its // intersection with the given Z-plane. // CSG: Call this on list of all of the pListComponents? // pListComponents = pVtx->GetLEDSGeometry()->GetListComponents(); // DoIntersect(m_fSliceZ, pListComponents); DoIntersect(m_fSliceZ); m_pCSIFPart->OutputLayer(m_fSliceZ, m_iPrecisionExp); uiNumSlices++; m_fSliceZ += m_fThickness; } // CSG: Will need to find correct pListComponents from pVtx; // Perhaps store the pListComponents ptr with the LEDSGeometry? pListComponents = pVtx->GetLEDSGeometry()->GetListComponents(); ModifyComponents(pVtx, pListComponents); } OutputTrailer(); #ifdef CONCAVE_FACES CleanFaces(); #endif // CSG: Will need to do this for each leaf of csg tree // delete pListComponents; #ifndef NDEBUG fprintf(stdout,"%ld Incremental; %ld not incremental.\n", uiIncremental, uiNonIncremental); fprintf(stdout,"%ld Colinear skipped.\n", uiColinear); #endif fprintf(stdout,"%ld Slices.\n", uiNumSlices); // DOUBLE foo = g_timerIsect.GetSeconds(); // fprintf(stdout, "Intersection calculations took %f seconds\n",foo); } // Modify the old list of components according to the information about edges // incident to this vertex VOID CSlices::ModifyComponents(CLEDSliteVertex *pVtx, CList *pListComponents) { #ifdef SLICEDEBUG pVtx->Display(); #endif //SLICEDEBUG // Build a list of the different face cycles around this vertex. // (For manifold vertices, there will be just one.) // Question: should this be done here, after sorting the vertices, // Or should the lists be computed in vertex creation order? CList *, UINT_8> *pListContours; pListContours = MakeContours(pVtx); CCircObjectList *pCircList; pCircList = new CCircObjectList; pCircList->Init(); CCircObjectList *VtxEUCycle; CListIter *, UINT_8> *CyclesListIter; CyclesListIter = new CListIter *, UINT_8>; CyclesListIter->Init(pListContours); VtxEUCycle = CyclesListIter->PeekFirst(); // process each separate contour in new edge lists while (VtxEUCycle) { #ifdef SLICEDEBUG // print out the new contour list for the vertex CContourElt *pcontourCur, *pcontourFirst; CCircObjectListIter *VtxEUCycleIter; VtxEUCycleIter = new CCircObjectListIter; VtxEUCycleIter->Init(VtxEUCycle); pcontourCur = pcontourFirst = VtxEUCycleIter->PeekFirst(); do { pcontourCur->Display(); pcontourCur = VtxEUCycleIter->PeekNext(); } while (pcontourCur != pcontourFirst); delete VtxEUCycleIter; #endif //SLICEDEBUG ProcessNewContour(VtxEUCycle, pListComponents); VtxEUCycle = CyclesListIter->PeekNext(); } delete CyclesListIter; } CCircularListInt *CSlices::MakeContour(CListInt *pListVtxEUCycle) { CCircularListInt *pcirclistContour; CContourElt *pContourElt; CListIterInt *pCycleIter; CLEDSliteEdgeUse *pEUcur; CSliceInfo *pSliceInfo; pcirclistContour = new CCircularListInt; pcirclistContour->Init(); pCycleIter = new CListIterInt; pCycleIter->Init(pListVtxEUCycle); pEUcur = ((CLEDSliteEdgeUse *)pCycleIter->PeekFirst()); do { pContourElt = new CContourElt; pContourElt->Init(pEUcur); pSliceInfo = new CSliceInfo; pEUcur->SetExtra(pSliceInfo); #ifdef CONCAVE_FACES { CLEDSliteFace *pledsFace; // We only want to do this if the face doesn't have the extra's // inContour bit set already. pledsFace = pEUcur->GetLEDSFace(); if (!(pledsFace->GetExtra())) { pSliceInfo = new CSliceInfo; pledsFace->SetExtra(pSliceInfo); } else if (!((CSliceInfo *)pledsFace->GetExtra())->InContour()) ((CSliceInfo *)pledsFace->GetExtra())->SetInContour(TRUE); } #endif pcirclistContour->InsertLast(pContourElt, 0); pEUcur = ((CLEDSliteEdgeUse *)pCycleIter->PeekNext()); } while (pEUcur); delete pCycleIter; delete pListVtxEUCycle; return pcirclistContour; } CList *, UINT_8> * CSlices::MakeContours(CLEDSliteVertex *pVtx) { CList *, UINT_8> *pListContours; CCircObjectList *pListContour; CLEDSliteEdgeUse *pEUcur, *pEUfirstEdge; CLEDSliteVtxEdgeIter *VtxEdgeIter = new CLEDSliteVtxEdgeIter; VtxEdgeIter->Init(pVtx); pEUfirstEdge = pEUcur = VtxEdgeIter->PeekFirstVtxEdgeEU(); pListContours = new CList *, UINT_8>; pListContours->Init(); do { if ((pEUcur != NULL) && (pEUcur->GetExtra() == NULL)) { pListContour = new CCircObjectList; pListContour->Init(); pListContours->InsertLast(pListContour, 0); MakeContour(pEUcur, pListContour); } pEUcur = VtxEdgeIter->PeekNextVtxEdgeEU(); } while (pEUcur && (pEUcur != pEUfirstEdge)); delete VtxEdgeIter; return (pListContours); } VOID CSlices::MakeContour(CLEDSliteEdgeUse *pEUedge, CCircObjectList *pListNewContour) { CLEDSliteEdgeUse *pEUfirstEdge, *pEUcurEdge; CLEDSliteEdgeUse *pEUsiblingEdge; CContourElt *pContourElt; CSliceInfo *pSliceInfo; pEUcurEdge = pEUfirstEdge = pEUedge; do { pContourElt = new CContourElt; pContourElt->Init(pEUcurEdge); pSliceInfo = new CSliceInfo; pEUcurEdge->SetExtra(pSliceInfo); pListNewContour->InsertLast(pContourElt); pEUsiblingEdge = pEUcurEdge->GetLEDSSiblingEdgeUse(); // Check that object has had edges grouped into pairs for // pseudo-2-manifold representation ASSERT(pEUsiblingEdge->GetLEDSSiblingEdgeUse() == pEUcurEdge); pEUcurEdge = pEUsiblingEdge->GetLEDSNCEdgeUse(); } while (pEUcurEdge != pEUfirstEdge); } // The slicer assumes that non-manifold edges have been divided up into // pseudo-manifold edges, so that every edge looks 2-manifold. // This function takes a contour elt and // finds the match for its edge use (sibling use in opposite direction), // and then returns the contour elt containing that match if there is one. // It also functions as a test of whether the edge use in the input contour // elt corresponds to an "ending" edge that has been seen before. CContourElt * CSlices::GetMatch(CContourElt *pContourElt) { CSliceInfo *pSliceInfo; if (!pContourElt) return NULL; pSliceInfo = (CSliceInfo *)(pContourElt->GetEdge()-> GetLEDSSiblingEdgeUse()->GetExtra()); if (!pSliceInfo) return NULL; return pSliceInfo->GetContourEltLink(); } // pLinkCurr has an ending edge. We want to find the first link in the // run of matching ending edges containing it, so we search backwards looking // for a link with a beginning edge (ie with no matching link existing), or // one that wasn't contiguous in the matching contour. // We return this link and its match via the ** args. VOID CSlices::SearchBackwardsFirstEnd(CContourElt *pLinkCurr, CContourElt *pLinkCurrMatch, CContourElt **ppLinkFirstEnd, CContourElt **ppLinkFirstEndMatch) { CContourElt *pLinkFirstEndCandidate, *pLinkFirstEndCandidateMatch; CContourElt *pLinkPrev, *pLinkPrevMatch; pLinkFirstEndCandidate = pLinkCurr; pLinkFirstEndCandidateMatch = pLinkCurrMatch; *ppLinkFirstEnd = NULL; do { pLinkPrev = pLinkFirstEndCandidate->GetPrev(); pLinkPrevMatch = GetMatch(pLinkPrev); if (!pLinkPrevMatch // If no match, it was a begin edge || (pLinkPrevMatch != // check if contiguous in existing contour too pLinkFirstEndCandidateMatch->GetNext())) { *ppLinkFirstEnd = pLinkFirstEndCandidate; *ppLinkFirstEndMatch = pLinkFirstEndCandidateMatch; break; // we've found the first end; break out of this do loop } else { pLinkFirstEndCandidate = pLinkPrev; pLinkFirstEndCandidateMatch = GetMatch(pLinkFirstEndCandidate); } } while (pLinkFirstEndCandidate != pLinkCurr); } // Input is a new contour formed from a face cycle around a vertex, // a so-called Vertex Ring. We use it to modify the component list. VOID CSlices::ProcessNewContour(CCircObjectList *pCircListVtxRing, CList *pListComponents) { CContourElt *pLinkLast; CContourElt *pLinkCurr, *pLinkCurrMatch; CContourElt *pLinkNext, *pLinkNextMatch; CContourElt *pLinkLastEnd, *pLinkLastEndMatch; CContourElt *pLinkFirstEnd = NULL, *pLinkFirstEndMatch; CComponent *pCComponentExisting; BOOL bDone = FALSE; BOOL bFirstLink = TRUE; BOOL bSeenEndRun = FALSE; pLinkCurr = pCircListVtxRing->PeekFirst(); #ifdef CONCAVE_FACES CContourElt *pLinkExistingPrev, *pLinkExistingNext; // Check if we have special case of a pair of begin edges bordering a // face we've seen before (requires an extra splice at end). // Must check BEFORE doing other splicing // (since splicing modifies face extra ptrs) CContourElt *pLinkSpecialCase = NULL; pLinkSpecialCase = SpecialCase(pLinkCurr, &pLinkExistingPrev, &pLinkExistingNext); #endif // CONCAVE_FACES // This is the last link we should look at because the ones after it in // the original vertex ring have already been processed. pLinkLast = pLinkCurr->GetPrev(); // look for matching end runs -- consecutive old "end" edges in the new // VtxRing that appear in the opposite order in an existing contour do { // Check if pLinkCurr is an end edge, // ie there's a matching link in an existing contour. if ((pLinkCurrMatch = GetMatch(pLinkCurr))) { // Find the first end edge in the matching edge run containing it. if (!bFirstLink) { pLinkFirstEnd = pLinkCurr; pLinkFirstEndMatch = pLinkCurrMatch; } else { // if it's the first link we've checked out, need to // search backwards to find first elt in the matching end run SearchBackwardsFirstEnd(pLinkCurr, pLinkCurrMatch, &pLinkFirstEnd, &pLinkFirstEndMatch); // If we didn't find a first one, then it was all end edges. // (Since we're inside here, we know that we found at least one end.) if (!pLinkFirstEnd) { ProcessAllEnds(pLinkCurr, pListComponents); delete pCircListVtxRing; return; } // If it wasn't all end edges, it's safe to reset the last pointer // (set so that we don't look at these edges again). pLinkLast = (CContourElt *)pLinkFirstEnd->GetPrev(); } // Find the last end edge in this matching end run, // advancing pLinkCurr as we go. pLinkCurrMatch = GetMatch(pLinkCurr); while (pLinkCurr != pLinkLast) { pLinkNext = pLinkCurr->GetNext(); pLinkNextMatch = GetMatch(pLinkNext); if (!pLinkNextMatch // If there's no match, it's not an end edge || (pLinkNextMatch !=// check if contig. in existing contour too pLinkCurrMatch->GetPrev())) { break; // We've found the last end; break out of while loop } pLinkCurr = pLinkNext; pLinkCurrMatch = GetMatch(pLinkCurr); } pLinkLastEnd = pLinkCurr; pLinkLastEndMatch = pLinkCurrMatch; // must check and advance pLinkCurr before splicing and deleting it bDone = (pLinkCurr == pLinkLast); pLinkCurr = pLinkCurr->GetNext(); // Now splice between the new contour and the existing contour // that contains this matching end run, then clean up deleted elts Splice(pLinkFirstEnd, pLinkLastEnd, pLinkFirstEndMatch, pLinkLastEndMatch); pCComponentExisting = SplitOrMerge(pLinkFirstEnd->GetPrev(), pLinkLastEnd->GetNext(), pLinkFirstEndMatch->GetNext(), pLinkLastEndMatch->GetPrev(), pListComponents); DeleteMatchingEndRun(pLinkFirstEnd, pLinkLastEnd); // if we have multiple ending runs in the disk cycle, then // the existing components may have split or merged. // In some cases, splitting or merging can invalidate contained/container // info (for example, info may have contour pointing to itself when an // inner contour merges with its container; many more cases). if (bSeenEndRun) { pCComponentExisting->SetType(ctUnknown); pCComponentExisting->InvalidateNesting(); } bSeenEndRun = TRUE; } else { bDone = (pLinkCurr == pLinkLast); pLinkCurr = pLinkCurr->GetNext(); } bFirstLink = FALSE; } while (!bDone); #ifdef CONCAVE_FACES if (pLinkSpecialCase) { #ifdef SLICEDEBUG fprintf(stdout,"*****Special Case Splicing called******\n"); #endif // SLICEDEBUG pLinkNext = pLinkSpecialCase->GetNext(); SpliceSpecial(pLinkSpecialCase, pLinkNext, pLinkExistingNext, pLinkExistingPrev); SplitOrMerge(pLinkSpecialCase, pLinkNext, pLinkExistingNext, pLinkExistingPrev, pListComponents); // Need to do the invalidating of nesting info for this case! } else #endif // CONCAVE_FACES if (!pLinkFirstEnd) { ProcessAllBegins(pCircListVtxRing, pListComponents); // ProcessAllBegins turns the pCircListVtxRing into a component, so // don't delete it afterwards! return; } // Implemented so that freeing a circular list won't free its links // (since some of the links are in current contours) delete pCircListVtxRing; #ifdef SLICEDEBUG PrintComponents(pListComponents); #endif } VOID CSlices::PrintComponents(CList *pListComponents) { /* CComponent *pComponent; CListIter *pListIterComponents = new CListIter; CCircularListIterInt *CycleIter; CContourElt *pcontourCur, *pcontourFirst; // See DoIntersect (code copied from there) pListIterComponents->Init(pListComponents); pComponent = pListIterComponents->PeekFirst(); while (pComponent) { fprintf(stdout,"*****Contour*****\n"); CycleIter = new CCircularListIterInt; CycleIter->Init(pComponent->GetContour()); pcontourCur = pcontourFirst = ((CContourElt *)CycleIter->PeekFirst()); do { pcontourCur->Display(); pcontourCur = ((CContourElt *)CycleIter->PeekNext()); } while (pcontourCur != pcontourFirst); delete CycleIter; pComponent = pListIterComponents->PeekNext(); } delete pListIterComponents; */ } // This will only get called in case where these edges really form a // brand new contour. VOID CSlices::ProcessAllBegins(CCircObjectList *pCircListVtxRing, CList *pListComponents) { #ifdef SLICEDEBUG fprintf(stdout,"*****ProcessAllBegins*****\n"); #endif CContourElt *pLinkFirst; CComponent *pComponentNew; // make a new component and add it to the list pComponentNew = MakeNewComponent(pCircListVtxRing, pListComponents); // update the edges and contour elts for each elt in the vtx ring pLinkFirst = pCircListVtxRing->PeekFirst(); UpdateLinksContourInfo(pLinkFirst, pLinkFirst, pComponentNew); } #ifdef CONCAVE_FACES // Check if edge and its successor are a pair of begin edges // that border a face we've seen before and that require an extra splice. // If they do, set Prev and Next to the edges of the face in an existing // contour that surround the new vertex. // WARNING: Untested, and written for a different list structure. CContourElt *CSlices::SpecialCase(CContourElt *pLinkCurr, CContourElt **ppLinkExistingPrev, CContourElt **ppLinkExistingNext) { CLEDSliteFace *pledsFace1, *pledsFace2; CContourElt *pLinkFirst, *pLinkFirstOfPair, *pLinkNext; CContourElt *pLinkOldContour1 = NULL; // look for a pair of begin edges. pLinkFirstOfPair = NULL; pLinkFirst = pLinkCurr; do { pLinkNext = pLinkCurr->GetNext(); if (!(GetMatch(pLinkCurr)) && !(GetMatch(pLinkNext))) { pLinkFirstOfPair = pLinkCurr; break; // there can only be one such pair } pLinkCurr = pLinkNext; } while (pLinkCurr != pLinkFirst); if (!pLinkFirstOfPair) return NULL; // Get the face between them // (treating them as undirectional edges, not edge uses). pledsFace1 = ((CContourElt *)pLinkNext->GetData())->GetEdge()-> GetLEDSFace(); pledsFace2 = ((CContourElt *)pLinkCurr->GetData())->GetEdge()-> GetLEDSSiblingEdgeUse()->GetLEDSFace(); ASSERT (pledsFace1 == pledsFace2); // Test if we've seen the face before, in which case it will have a // contour set. if (pledsFace1 && pledsFace1->GetExtra() && ((CSliceInfo *)(pledsFace1->GetExtra()))->InContour() && ((CSliceInfo *)(pledsFace1->GetExtra()))->GetContourEltLink()) { pLinkOldContour1 = ((CSliceInfo *)(pledsFace1->GetExtra()))->GetContourEltLink(); } if (pLinkOldContour1) { #ifdef SLICEDEBUG fprintf(stderr,"*****This isn't implemented yet!!!*****\n"); #endif // PLACEHOLDERS ONLY! Need to search for correct assignments. // Search through all the active edges for the face we've identified // and find the two that pLinkCurr and pLinkNext's edges are between. // If there aren't two such edges then we should return FALSE. CLEDSliteVertex *pledsVtx; pledsVtx = ((CContourElt *)pLinkCurr->GetData())->GetEdge()->GetLEDSRootVtx(); // Look at the vertex coordinates, intersect active edges for the // face with this z-coordinate, and then find the ones around the vtx. /* *ppLinkExistingPrev = ; *ppLinkExistingNext = ; */ return pLinkFirstOfPair; } else { return NULL; } } #endif // CONCAVE_FACES VOID CSlices::DeletePair(CContourElt *pContEltDelete) { CContourElt *pContEltDeleteMatch; pContEltDeleteMatch = GetMatch(pContEltDelete); delete (CSliceInfo *)pContEltDelete->GetEdge()->GetExtra(); pContEltDelete->GetEdge()->SetExtra(NULL); delete (CSliceInfo *)pContEltDeleteMatch->GetEdge()->GetExtra(); pContEltDeleteMatch->GetEdge()->SetExtra(NULL); delete pContEltDelete; delete pContEltDeleteMatch; } // This routine should only get called when the pCircListVtxRing // matches a whole contour that is ending. VOID CSlices::ProcessAllEnds(CContourElt *pLinkFirst, CList *pListComponents) { #ifdef SLICEDEBUG fprintf(stdout,"*****ProcessAllEnds*****\n"); #endif CContourElt *pLinkCurr, *pLinkCurrMatch, *pLinkNext, *pLinkLast; CContourElt *pContEltExisting; CComponent *pCComponentExisting; CComponent *pCComponent; #ifndef NDEBUG CList *pClistContained; #endif //record the component info first, but delay freeing it till later just // in case pLinkCurrMatch = GetMatch(pLinkFirst); pContEltExisting = pLinkCurrMatch; pCComponentExisting = pContEltExisting->GetComponent(); // Free up the memory from the deleted elts pLinkCurr = pLinkFirst; pLinkLast = pLinkCurr->GetPrev(); do { if (pLinkCurr != pLinkLast) pLinkNext = pLinkCurr->GetNext(); else pLinkNext = NULL; DeletePair(pLinkCurr); pLinkCurr = pLinkNext; } while (pLinkNext); // delete the old component and contour from the component list. #ifndef NDEBUG // By the time we delete a component, there should be no others // contained in it pClistContained = pCComponentExisting->GetContained(); ASSERT ((pClistContained == NULL) || (pClistContained->PeekFirst() == NULL)); #endif // But it could be contained in another contour -- if so, delete it. pCComponentExisting->RemoveFromContainer(); // Have to delete the contour explicitly -- maybe should add to destructor? delete pCComponentExisting->GetContour(); pCComponent = pListComponents->Remove(pCComponentExisting); #ifndef NDEBUG ASSERT (pCComponent == pCComponentExisting); #endif delete pCComponentExisting; } #ifdef CONCAVE_FACES // Never used for input that's all convex faces. Untested, possibly obsolete. VOID CSlices::SpliceSpecial(CContourElt *pLinkCurrPrev, CContourElt *pLinkCurrNext, CContourElt *pLinkExistingNext, CContourElt *pLinkExistingPrev) { pLinkCurrPrev->SetNext(pLinkExistingNext); pLinkExistingNext->SetPrev(pLinkCurrPrev); pLinkExistingPrev->SetNext(pLinkCurrNext); pLinkCurrNext->SetPrev(pLinkExistingPrev); } #endif // CONCAVE_FACES VOID CSlices::Splice(CContourElt *pLinkFirstEnd, CContourElt *pLinkLastEnd, CContourElt *pLinkFirstEndMatch, CContourElt *pLinkLastEndMatch) { pLinkFirstEnd->GetPrev()->SetNext(pLinkFirstEndMatch->GetNext()); pLinkFirstEndMatch->GetNext()->SetPrev(pLinkFirstEnd->GetPrev()); pLinkLastEndMatch->GetPrev()->SetNext(pLinkLastEnd->GetNext()); pLinkLastEnd->GetNext()->SetPrev(pLinkLastEndMatch->GetPrev()); } // pLinkCurrPrev and CurrNext are elements from the vertex ring. // pLinkExistingNext and ExistingPrev are elements from the matching // existing contour that we are splicing it with (which may in turn // be the result of a previous splice with the same vertex ring). CComponent *CSlices::SplitOrMerge(CContourElt *pLinkCurrPrev, CContourElt *pLinkCurrNext, CContourElt *pLinkExistingNext, CContourElt *pLinkExistingPrev, CList *pListComponents) { #ifdef SLICEDEBUG fprintf(stdout,"****Splice*****\n"); #endif // CComponent *pCComponentExisting, *pCComponent, *pCComponentDeleting; CComponent *pCComponentExisting; pCComponentExisting = //we could also get this from pLinkExistingPrev pLinkExistingNext->GetComponent(); // We are splitting if the components on both sides of the splice // were already the same. If they were different, it's a merge. if (pLinkCurrPrev->GetComponent() == pCComponentExisting) { #ifdef SLICEDEBUG fprintf(stdout,"Splitting\n"); #endif ASSERT(pLinkCurrNext->GetComponent() == pLinkExistingPrev->GetComponent()); // I think this is redundant because the caller takes care of // invalidating container ptrs in its contained contours // pCComponentExisting->InvalidateNesting(); // We arbitrarily choose the pLinkExistingNext side to be new component. // (The splice should already made it circularly connected back to itself.) // Make sure that it's the other side from the one we update the existing // component to point to (pLinkExistingPrev) at the end of this routine! // allocate a new contour (a circular list) and initialize with these elts CCircObjectList *pCCircularListNewContour; pCCircularListNewContour = new CCircObjectList; pCCircularListNewContour->Init(pLinkExistingNext); // Make a new component to hold this contour CComponent *pComponentNew; pComponentNew = MakeNewComponent(pCCircularListNewContour, pListComponents); // update all the elts in the list to reflect their new membership UpdateLinksContourInfo(pLinkExistingNext, pLinkExistingNext, pComponentNew); } else { #ifdef SLICEDEBUG fprintf(stdout,"Merging\n"); ASSERT(pLinkCurrNext->GetComponent() != pLinkExistingPrev->GetComponent()); #endif CComponent *pCComponentDeleting = pLinkCurrNext->GetComponent(); // Usually we'll just be merging in a vertex ring which won't have a // component to delete. if (pCComponentDeleting) { // Should I delete the contour here, or is it being used still? CComponent *pCComponent = pListComponents-> Remove(pCComponentDeleting); ASSERT (pCComponent == pCComponentDeleting); delete pCComponentDeleting; } // We've merged part of the contour into an existing contour. Update // all the contour elts we've added to point to the existing contour's // handle and component, and set the EU of each to point to the // contour elt. UpdateLinksContourInfo(pLinkCurrNext, // first link to update pLinkExistingNext, // one past last pCComponentExisting);// links' (new) component ptr } // The existing component's contour may point to a contour elt that was // deleted (or moved into another contour if splitting). Update it. /* pCComponentExisting->GetContour()->SetHeadNext(pLinkExistingPrev); pCComponentExisting->GetContour()->SetHeadPrev(pLinkExistingPrev->GetPrev()); */ pCComponentExisting->GetContour()->Init(pLinkExistingPrev); // invalidate its type -- moved to caller // pCComponentExisting->SetType(ctUnknown); return (pCComponentExisting); } // Frees up the memory, including the contour elts and the links themselves, // that were taken out by the splice. VOID CSlices::DeleteMatchingEndRun(CContourElt *pLinkFirst, CContourElt *pLinkLast) { CContourElt *pLinkCurr, *pLinkNext; pLinkCurr = pLinkFirst; do { if (pLinkCurr != pLinkLast) pLinkNext = pLinkCurr->GetNext(); else pLinkNext = NULL; DeletePair(pLinkCurr); pLinkCurr = pLinkNext; } while (pLinkCurr); } // Update the ContourElts in links from first to stopper so they pt to the // given component and circular linked list that contain them, and // update corresponding EdgeUse's extra field to point to the link itself. VOID CSlices::UpdateLinksContourInfo(CContourElt *pLinkFirst, CContourElt *pLinkStopper, CComponent *pCComponent) { CContourElt *pLinkCurr = pLinkFirst; do { UpdateContourElt(pLinkCurr, pCComponent); pLinkCurr = pLinkCurr->GetNext(); } while (pLinkCurr != pLinkStopper); } // Update the ContourElt in the given link so that it pts to the // given component and circular linked list that contain it, and // update its EdgeUse's extra field to point to the link itself. VOID CSlices::UpdateContourElt(CContourElt *pContElt, CComponent *pCComponent) { /* #ifdef SLICEDEBUG pContElt->GetEdge()->Display(); #endif */ // Set the contour elt ptr in the EU's extra field ((CSliceInfo *)(pContElt->GetEdge()->GetExtra()))->Init(pContElt); #ifdef CONCAVE_FACES // Set the contour elt ptr in the Face's extra field // (Really just want the face to know the component, but this should be ok) ((CSliceInfo *)(pContElt->GetEdge()->GetLEDSFace()->GetExtra()))-> Init(pContElt); #endif // Set the pointers in the contour elt pContElt->SetComponent(pCComponent); pContElt->SetContourHandle(pCComponent->GetContourHandle()); } // make a new component and add it to the list CComponent * CSlices::MakeNewComponent(CCircObjectList *pCCircularListContour, CList *pListComponents) { CComponent *pComponentNew; CCircObjectList **ppCCircularListContourHandle; pComponentNew = new CComponent; ppCCircularListContourHandle = new (CCircObjectList *); *ppCCircularListContourHandle = pCCircularListContour; pComponentNew->SetContourHandle(ppCCircularListContourHandle); pListComponents->InsertLast(pComponentNew, 0); return (pComponentNew); } // We don't use this non-incremental intersection routine in the real code; // See CSliceInfo::UpdatePt instead. VOID CSlices::IntersectEdge(FLOAT fZ, CLEDSliteEdgeUse *pledsEU, FLOAT *pfX, FLOAT *pfY) { Point PtBottom, PtTop; FLOAT fFraction, fDelX, fDelY; PtBottom = pledsEU->GetLEDSRootVtx()->GetPoint(); PtTop = pledsEU->GetLEDSSiblingEdgeUse()->GetLEDSRootVtx()->GetPoint(); ASSERT ( (PtTop(Z)) >= (PtBottom(Z)) ); fFraction = ( (PtTop(Z) - fZ) / (PtTop(Z) - PtBottom(Z)) ); fDelX = (PtTop(X) - PtBottom(X)); fDelY = (PtTop(Y) - PtBottom(Y)); *pfX = PtTop(X) - (fFraction * fDelX); *pfY = PtTop(Y) - (fFraction * fDelY); } /* VOID CSlices:: FreeIntersectionList(CList *pCListIntersections) { CListIter *pListIter; DOUBLE dCurIntersect; pListIter = new CListIter; pListIter->Init(pCListIntersections); pListIter->PeekFirst(&dCurIntersect); do { delete dCurIntersect; } while (pListIterInner->PeekNext(&dCurIntersect)); delete pCListIntersections; } */ // Find intersection between horizontal ray shooting right from // (dRayBaseX, dRayBaseY) and line segment between (dX1, dY1) and (dX2, dY2). // If the intersection is very close to the ray base, set pbEpsilonSuspicions // to TRUE. Otherwise, DON'T modify it. // Allocates memory for return value, the X coordinate of the intersection. // Must be freed later. DOUBLE *CSlices::IntersectTest(DOUBLE dX1, DOUBLE dY1, DOUBLE dX2, DOUBLE dY2, DOUBLE dRayBaseX, DOUBLE dRayBaseY, BOOL *pbEpsilonSuspicions) { // FLOAT fRayBaseX, fRayBaseY, fX1, fY1, fX2, fY2; DOUBLE *pdIntersect, dDenom, dR, dDiff; // Check for trivial reject cases if ( ((dY1 < dRayBaseY) && (dY2 < dRayBaseY)) || ((dY1 > dRayBaseY) && (dY2 > dRayBaseY)) || ((dY1 == dRayBaseY) && (dY2 == dRayBaseY)) || ((dX1 < dRayBaseX) && (dX2 < dRayBaseX))) { return NULL; } pdIntersect = new DOUBLE; dDenom = dY2 - dRayBaseY; if (dDenom == 0.0) { *pdIntersect = (dX2); #ifdef SLICEDEBUG fprintf(stdout,"zero denominator branch exercised.\n"); #endif } else { dR = ( dRayBaseY - dY1)/dDenom; *pdIntersect = ((dR * dX2) + dX1)/(dR + 1.0); } #ifdef SLICEDEBUG fprintf(stdout,"Ray cast from %lf,%lf has intersection at X = %lf\n", dRayBaseX, dRayBaseY, (*pdIntersect)); #endif // check if intersection is to left of raybase dDiff = dRayBaseX - *pdIntersect; if ((dDiff > 0.0) && (dDiff < (DOUBLE)m_fEps)) { // it's left and very close #ifndef NDEBUG fprintf (stdout,"May be floating point rndoff error in nesting calcualations.\n"); #endif *pbEpsilonSuspicions = TRUE; return pdIntersect; } else if (dDiff > 0.0) { // to left but not too close delete pdIntersect; return NULL; } else if ((dDiff <= 0.0) && (dDiff > -(DOUBLE)m_fEps)) { // it's on or to right and very close #ifndef NDEBUG fprintf (stdout,"May be floating point rndoff error in nesting calcualations.\n"); #endif *pbEpsilonSuspicions = TRUE; return pdIntersect; } else { // to right of ray base but not too close return pdIntersect; } } // Check if we should count an intersection when the ray base (the rightmost // vertex of the input contour) is coincident with part of the candidate // contour. // // Here is a diagram to explain some of the important variables. // If there are no horizontal segments at the intersection, then // CandidateNextBase == CandidatePrevEnd == RayBase. // (the alpha and beta angles are too hard to draw in ascii; see thesis) // // // // RayBase CandidateNextBase // CandidatePrevEnd *-------------*------->*----------------------> (Ray) // /|\ /| \ // | / | \ // | * * \ // | InputNext InputPrev _\| // CandidatePrevBase * * CandidateNextEnd // VOID CSlices:: CoincidentPointTest(CList *pCListIntersections, CContourElt *pCandidateContourElt, CContourElt *pRayBaseContourElt, CComponent *pComponentCandidate, CComponent *pComponentInput, DOUBLE dRayBaseY, DOUBLE dRayBaseX, DOUBLE dCandidatePrevEndX, DOUBLE dCandidateNextBaseX, Point *pPtCandidatePrevBase, Point *pPtCandidateNextEnd) { CContourElt *pInputContourEltCur; Point *pPtRayBase; // should be point for RayBaseContourElt Point *pPtInputNext, *pPtInputPrev; // points on either side in input contour DOUBLE dInputNextY, dInputPrevY; DOUBLE dCandidatePrevBaseY, dCandidateNextEndY; DOUBLE dCandidatePrevBaseX, dCandidateNextEndX; DOUBLE dAlphaPrev, dAlphaPost; DOUBLE dBetaPrev, dBetaPost, dBetaLesser, dBetaGreater; // BOOL bLocalMax; Vector vPrevInput, vNextInput, vRay; Vector vPrevCandidate, vNextCandidate; dCandidateNextEndX = (DOUBLE)((*pPtCandidateNextEnd)(X)); dCandidateNextEndY = (DOUBLE)((*pPtCandidateNextEnd)(Y)); dCandidatePrevBaseX = (DOUBLE)((*pPtCandidatePrevBase)(X)); dCandidatePrevBaseY = (DOUBLE)((*pPtCandidatePrevBase)(Y)); fprintf(stdout,"******Called Coincident Point Test************.\n"); // Find neighboring segments of input contour around the ray base pPtRayBase = ((CSliceInfo *)(pRayBaseContourElt->GetEdge()-> GetExtra()))->GetLastPt(); ASSERT (((DOUBLE)((*pPtRayBase)(Y))) == dRayBaseY); ASSERT (((DOUBLE)((*pPtRayBase)(X))) == dRayBaseX); // Now find next segment pInputContourEltCur = pRayBaseContourElt->GetNextPtLink(); pPtInputNext = ((CSliceInfo *)(pInputContourEltCur->GetEdge()->GetExtra()))-> GetLastPt(); pInputContourEltCur = pRayBaseContourElt->GetPrevPtLink(); pPtInputPrev = ((CSliceInfo *)(pInputContourEltCur->GetEdge()->GetExtra()))-> GetLastPt(); dInputPrevY = (DOUBLE)((*pPtInputPrev)(Y)); dInputNextY = (DOUBLE)((*pPtInputNext)(Y)); // Find Betas: angles between ray and two segments of input at ray base vPrevInput = *pPtInputPrev - *pPtRayBase; vNextInput = *pPtInputNext - *pPtRayBase; vRay.Set(1, 0, 0); dBetaPrev = AngleBetween(vPrevInput, vRay); dBetaPost = AngleBetween(vNextInput, vRay); // AngleBetween just gives [0,Pi] answer; want full [0,2Pi] // (well, actually, [Pi/2, 3Pi/2] since ray base is rightmost point) if (dInputPrevY < dRayBaseY) dBetaPrev = (2*PI) - dBetaPrev; if (dInputNextY < dRayBaseY) dBetaPost = (2*PI) - dBetaPost; // find smaller beta if (dBetaPrev < dBetaPost) { dBetaLesser = dBetaPrev; dBetaGreater = dBetaPost; } else { dBetaLesser = dBetaPost; dBetaGreater = dBetaPrev; } UINT_32 uiNumLesser = 0; // counter for number of alphas less than beta #ifndef NDEBUG UINT_32 uiNumBetween = 0; // counter for number of alphas between betas #endif // NDEBUG // Calculate alphas: angle from ray to non-horizontal candidate segments // if segment touches ray base. if (dCandidatePrevEndX == dRayBaseX) { vPrevCandidate.Set((FLOAT)(dCandidatePrevBaseX - dCandidatePrevEndX), (FLOAT)(dCandidatePrevBaseY - dRayBaseY), 0.0f); dAlphaPrev = AngleBetween(vPrevCandidate, vRay); // AngleBetween just gives [0,Pi] answer; want [0,2Pi] if (dCandidatePrevBaseY < dRayBaseY) dAlphaPrev = (2*PI) - dAlphaPrev; } else if (dCandidatePrevEndX > dRayBaseX) { if (dCandidatePrevBaseY > dRayBaseY) { dAlphaPrev = 0; } else { dAlphaPrev = 2*PI; } } else { // (dCandidateNextBaseX > dRayBaseX) dAlphaPrev = PI; } if (dAlphaPrev <= dBetaLesser) uiNumLesser++; #ifndef NDEBUG else if (dAlphaPrev < dBetaGreater) uiNumBetween++; #endif if (dCandidateNextBaseX == dRayBaseX) { vNextCandidate.Set((FLOAT)(dCandidateNextEndX - dCandidateNextBaseX), (FLOAT)(dCandidateNextEndY - dRayBaseY), 0.0f); dAlphaPost = AngleBetween(vNextCandidate, vRay); // AngleBetween just gives [0,Pi] answer; want [0,2Pi] if (dCandidateNextEndY < dRayBaseY) dAlphaPost = (2*PI) - dAlphaPost; } else if (dCandidateNextBaseX > dRayBaseX) { dAlphaPrev = 0; } else { dAlphaPrev = PI; } if (dAlphaPost <= dBetaLesser) uiNumLesser++; #ifndef NDEBUG else if (dAlphaPost < dBetaGreater) uiNumBetween++; #endif /* if (dCandidatePrevEndX == dRayBaseX) { vPrevCandidate.Set((dCandidatePrevBaseX - dCandidatePrevEndX), (dCandidatePrevBaseY - dRayBaseY), 0); dAlphaPrev = AngleBetween(vPrevCandidate, vRay); // AngleBetween just gives [0,Pi] answer; want [0,2Pi] if (dCandidatePrevBaseY < dRayBaseY) dAlphaPrev = (2*PI) - dAlphaPrev; if (dAlphaPrev <= dBetaLesser) uiNumLesser++; #ifndef NDEBUG else if (dAlphaPrev < dBetaGreater) uiNumBetween++; #endif } if (dCandidateNextBaseX == dRayBaseX) { vNextCandidate.Set((dCandidateNextEndX - dCandidateNextBaseX), (dCandidateNextEndY - dRayBaseY), 0); dAlphaPost = AngleBetween(vNextCandidate, vRay); // AngleBetween just gives [0,Pi] answer; want [0,2Pi] if (dCandidateNextEndY < dRayBaseY) dAlphaPost = (2*PI) - dAlphaPost; if (dAlphaPost <= dBetaLesser) uiNumLesser++; #ifndef NDEBUG else if (dAlphaPost < dBetaGreater) uiNumBetween++; #endif } // if segment is to right of ray base, check if below or above ray to // determine whether to count it as if its alpha < beta. if (dCandidateNextBaseX > dRayBaseX) { if (dCandidateNextEndY > dRayBaseY) { uiNumLesser++; } } if (dCandidatePrevEndX > dRayBaseX) { if (dCandidatePrevBaseY > dRayBaseY) { uiNumLesser++; } } if ((dCandidateNextBaseX < dRayBaseX) || (dCandidatePrevEndX < dRayBaseX)){ // In this case one of the non-horizontal adjacent segments is // to the left. Generally, this means the ray base is a local max or min: if (((dInputPrevY <= dRayBaseY) && (dInputNextY <= dRayBaseY)) || ((dInputPrevY >= dRayBaseY) && (dInputNextY >= dRayBaseY))) { // we need to determine if the ray base was a local max or local min // for the query contour. if (dInputPrevY < dRayBaseY) { ASSERT(dInputNextY <= dRayBaseY); bLocalMax = TRUE; } else if (dInputNextY < dRayBaseY) { ASSERT(dInputPrevY == dRayBaseY); bLocalMax = TRUE; } else { bLocalMax = FALSE; } // Only if ray base is a local max on input contour do we treat // the alpha as being less than beta if (dCandidateNextBaseX < dRayBaseX) { if (bLocalMax) { uiNumLesser++; } } if (dCandidatePrevEndX < dRayBaseX) { if (bLocalMax) { uiNumLesser++; } } } // it's not a local max or min so horizontal segment is inside input // contour. #ifndef NDEBUG else { uiNumBetween++; } #endif } */ if (uiNumLesser == 1) { // yes, count an intersection with the contour if (dCandidateNextBaseX >= dCandidatePrevEndX) { pCListIntersections->InsertForward(pCandidateContourElt, dCandidateNextBaseX); } else { pCListIntersections->InsertForward(pCandidateContourElt, dCandidatePrevEndX); } } #ifndef NDEBUG if (uiNumBetween > 0) { // (if it's one, that should mean that the other was coincident with one // of the beta's) // candidate Component area should be smaller than input component area // in this case - should be completely inside. // Otherwise, contours intersected or FP error FLOAT fAreaInput, fAreaCandidate; fAreaInput = pComponentInput->CalculateArea(); fAreaCandidate = pComponentCandidate->CalculateArea(); ASSERT(fAreaInput > fAreaCandidate); } #endif } // Test if contour is outer contour or hole by finding right-most point // and taking cross product of adjacent edges. // (If contour is pseudo-manifold at right-most point, must use instance // whose edges are right-most) SliceContourType CComponent::DeriveType() { CContourElt *pContourEltRightmost, *pContourEltPrev, *pContourEltNext; Vector v1, v2, vCross; Point *pPtRightmost, *pPtNext, *pPtPrev; CSliceInfo *pInfoNext, *pInfoPrev; // Find rightmost point // BUG!!! in case of rightmost point being non-manifold, // need further checking. Or is it okay? pContourEltRightmost = this->GetRightmostElt(); pPtRightmost = ((CSliceInfo *)(pContourEltRightmost->GetEdge()-> GetExtra()))->GetLastPt(); // Find distinct previous point in contour pContourEltPrev = pContourEltRightmost; do { pContourEltPrev = pContourEltPrev->GetPrevPtLink(); pInfoPrev = (CSliceInfo *)(pContourEltPrev->GetEdge()->GetExtra()); pPtPrev = pInfoPrev->GetLastPt(); } while (*pPtPrev == *pPtRightmost); // Find distinct next point in contour pContourEltNext = pContourEltRightmost; do { pContourEltNext = pContourEltNext->GetNextPtLink(); pInfoNext = (CSliceInfo *)(pContourEltNext->GetEdge()->GetExtra()); pPtNext = pInfoNext->GetLastPt(); } while ((*pPtNext == *pPtRightmost) || (*pPtNext == *pPtPrev)); // make adjacent edges into vectors and take cross-product v1 = *pPtRightmost - *pPtPrev; v2 = *pPtNext - *pPtRightmost; vCross = (v1.Cross(v2)); ASSERT (vCross(X) == 0.0); ASSERT (vCross(Y) == 0.0); if (vCross(Z) == 0.0) { ASSERT (FALSE); return ctUnknown; } if (vCross(Z) > 0.0) return ctOuter; else return ctInner; } CContourElt *CComponent::GetRightmostElt() { CCircObjectListIter *pRayBaseIter; CContourElt *pRayBaseContourEltCur, *pRayBaseContourEltFirst; CContourElt *pRayBaseContourElt; DOUBLE dRayBaseX; Point *pPtRayBaseCur; CSliceInfo *pInfoCur; pRayBaseIter = new CCircObjectListIter; pRayBaseIter->Init(this->GetContour()); pRayBaseContourEltFirst = pRayBaseContourEltCur = ((CContourElt *)pRayBaseIter->PeekFirst()); dRayBaseX = -DBL_MAX; do { DOUBLE dRayBaseCurX; pInfoCur = (CSliceInfo *)(pRayBaseContourEltCur->GetEdge()->GetExtra()); if (pInfoCur->GetColinear() != TRUE) { pPtRayBaseCur = pInfoCur->GetLastPt(); // the intersection of the edge with the current slice plane dRayBaseCurX = (DOUBLE)((*pPtRayBaseCur)(X)); if (dRayBaseCurX > dRayBaseX){ dRayBaseX = dRayBaseCurX; pRayBaseContourElt = pRayBaseContourEltCur; } } pRayBaseContourEltCur = ((CContourElt *)pRayBaseIter->PeekNext()); } while (pRayBaseContourEltFirst != pRayBaseContourEltCur); delete pRayBaseIter; return pRayBaseContourElt; } // Called when bounding box test finds multiple components whose BBox's // contained the input component's BBox (or if any found if input was outer). // They're in the list of possible containers. Find the actual container // by shooting a ray and set nesting pointers appropriately. VOID CSlices::RayCastNesting(CComponent *pComponentInput, CList *pListPossibleContainers) { CComponent *pComponentCandidate, *pContainer, *pContainerLast = NULL; CListIter *pListIterComponents; CCircObjectListIter *pCycleIter; CContourElt *pRayBaseContourElt; CContourElt *pContourEltCur, *pContourEltFirst, *pContourEltNext; Point *pPtPrev, *pPtCur, *pPtNextEnd, *pPtNextBase; Point *pPtRayBase; DOUBLE *pdXIntersect; DOUBLE dRayBaseX, dRayBaseY, dXPrev, dYPrev, dXCur, dYCur; DOUBLE dYNextEnd, dXNextBase; BOOL bEpsilonSuspicions = FALSE; CList *pCListIntersections; // Find rightmost vertex in input component to cast ray from // (For regular intersection test could use any point on boundary, // but the CoincidentPointTest for intersection with a candidate contour // that coincides with the input contour at the ray base is much // simpler to code if we use an extreme point, such as rightmost.) // pRayBaseContourElt = pComponentInput->GetRightmostElt(); // Changed code so it should work w/ random point on query contour pRayBaseContourElt = pComponentInput->GetContour()-> PeekFirst()->GetNextPtLink();; pPtRayBase = ((CSliceInfo *)(pRayBaseContourElt->GetEdge()-> GetExtra()))->GetLastPt(); dRayBaseX = (DOUBLE)((*pPtRayBase)(X)); dRayBaseY = (DOUBLE)((*pPtRayBase)(Y)); // Now, find closest intersection with each possible containing component pCListIntersections = new CList; // list of i-sections pCListIntersections->Init(); // iterating through poss. containers pListIterComponents = new CListIter; pListIterComponents->Init(pListPossibleContainers); pComponentCandidate = pListIterComponents->PeekFirst(); while (pComponentCandidate) { #ifdef NOBBTEST if ((pComponentCandidate->GetType()) != (pComponentInput->GetType())) { #endif // Check all of its edge segments for intersection with the ray. // Kill use of iterator here that just gets first contour elt pCycleIter = new CCircObjectListIter; pCycleIter->Init(pComponentCandidate->GetContour()); pContourEltCur = ((CContourElt *)pCycleIter->PeekFirst()); pContourEltCur = pContourEltFirst = pContourEltCur->GetNextPtLink(); pPtCur = ((CSliceInfo *)(pContourEltCur->GetEdge()->GetExtra()))-> GetLastPt(); do { pPtPrev = pPtCur; pContourEltCur = pContourEltCur->GetNextPtLink(); pPtCur = ((CSliceInfo *)(pContourEltCur->GetEdge()->GetExtra()))-> GetLastPt(); dXPrev = (DOUBLE)((*pPtPrev)(X)); dYPrev = (DOUBLE)((*pPtPrev)(Y)); dXCur = (DOUBLE)((*pPtCur)(X)); dYCur = (DOUBLE)((*pPtCur)(Y)); // We want one entry in list for each time contour actually crosses // the ray, not one for each segment that intersects/touches the ray. if (dYPrev == dRayBaseY) { // don't put intersections with segments rooted at ray height in list #ifdef SLICEDEBUG fprintf(stdout,"Ray cast from %lf, %lf; ", dRayBaseX, dRayBaseY); fprintf(stdout,"skipping intersection at %lf, %lf\n", dRayBaseX, dXPrev); #endif continue; } else if (dYCur == dRayBaseY) { // Find endpts of next non-horizontal segment. pContourEltNext = pContourEltCur; do { pPtNextBase = pPtCur; pContourEltNext = pContourEltNext->GetNextPtLink(); pPtNextEnd = ((CSliceInfo *) (pContourEltNext->GetEdge()->GetExtra()))-> GetLastPt(); dYNextEnd = (DOUBLE)((*pPtNextEnd)(Y)); if (dYNextEnd == dRayBaseY) { #ifdef SLICEDEBUG fprintf(stdout,"Skipping horizontal; ray cast from %lf,%lf; ", dRayBaseX, dRayBaseY); fprintf(stdout,"endpoint at X = %lf\n", (DOUBLE)((*pPtNextEnd)(X))); #endif } } while (dYNextEnd == dRayBaseY); dXNextBase = (DOUBLE)((*pPtNextBase)(X)); if ((dXCur < dRayBaseX) && (dXNextBase < dRayBaseX)) { // No intersection } else if ((dXCur > dRayBaseX) && (dXNextBase > dRayBaseX)) { // only put intersections with segments ending at ray height in list // if the contour continues through the ray--a glancing touch doesn't // count. E.g., in the figure the lower segment on the left would go // in the list, but neither of the two segments on the lower right // would go in the list (the rightmost would get rejected by the // previous "if" statement, actually, being "rooted" at ray height). // _ // |\ // \ // \ // (Ray)------------------------------------> // / /\ // / / \ // / / \ // _\| // if ( ((dYPrev < dRayBaseY) && (dYNextEnd > dRayBaseY)) || ((dYPrev > dRayBaseY) && (dYNextEnd < dRayBaseY))) { pCListIntersections->InsertForward(pContourEltCur, dXCur); #ifdef SLICEDEBUG fprintf(stdout,"Ray cast from %lf,%lf has intersection at X = %lf\n", dRayBaseX, dRayBaseY, dXCur); #endif } } else if ((dXCur <= dRayBaseX) || (dXNextBase <= dRayBaseX)) { CoincidentPointTest(pCListIntersections, pContourEltCur, pRayBaseContourElt, pComponentCandidate, pComponentInput, dRayBaseY, dRayBaseX, dXCur, dXNextBase, pPtPrev, pPtNextEnd); } // Note we can't advance pContourEltCur to be pContourEltNext // because we might have skipped over the first contour elt // during the above while loop and never terminate the outer while. } else if (pdXIntersect = IntersectTest(dXPrev, dYPrev, dXCur, dYCur, dRayBaseX, dRayBaseY, &bEpsilonSuspicions)) { if (*pdXIntersect == dRayBaseX) { CoincidentPointTest(pCListIntersections, pContourEltCur, pRayBaseContourElt, pComponentCandidate, pComponentInput, dRayBaseY, dRayBaseX, dRayBaseX, dRayBaseX, pPtPrev, pPtCur); } else { pCListIntersections->InsertForward(pContourEltCur, (*pdXIntersect)); // IntersectTest returns a ptr to a double instead of a double // so it can return NULL if no intersection, but that means it // had to allocate space for the double it returned. Free it now. } delete pdXIntersect; } } while (pContourEltCur != pContourEltFirst); delete pCycleIter; #ifdef NOBBTEST } #endif pComponentCandidate = pListIterComponents->PeekNext(); } delete pListIterComponents; if (pCListIntersections->GetLength() > 0) { pContainer = ProcessIntersectionList(pComponentInput, pCListIntersections); // Intersection test was done with a ray to the right so that first item // in intersections list, with lowest X coord, and which has an odd // number of distinct intersections with the ray, is the right one. } else { pContainer = NULL; if (pComponentInput->GetType() == ctInner) { fprintf(stderr,"No surrounding contour found for inner hole contour.\n"); fprintf(stderr,"Perhaps input file had one or more shells inside out?\n"); ASSERT (FALSE); } #ifdef SLICEDEBUG fprintf(stdout,"Failed to find any i-sections while searching for container.\n"); #endif } if ((pContainerLast) && (pContainerLast != pContainer)) { fprintf(stderr,"*******Inconsistent results while searching for container.****\n"); } pContainerLast = pContainer; if (bEpsilonSuspicions) { // BUG!! We used to try another vertex for ray base if current vertex // caused difficulties. Of course, we might try them all and // still fail... // pRayBaseContourEltCur = ((CContourElt *)pRayBaseIter->PeekNext()); bEpsilonSuspicions = FALSE; } pComponentInput->SetContainerMutual(pContainer); delete pCListIntersections; } // Here, the input list of containers is all contours that actually contain // the input component (or are contained in it, in rare cases). // The container with the smallest area (greater than the area of the input // container) will be its immediate container. CComponent *CSlices::AreaTest(CComponent *pComponentInput, CList *pListContainers) { CComponent *pComponent, *pContainer; CListIter *pListIterComponents; FLOAT fInputArea, fCandidateArea, fMinCandidateArea; if (pListContainers->GetLength() == 0) return NULL; fprintf(stdout,"******Called Area Test************.\n"); fMinCandidateArea = FLT_MAX; pListIterComponents = new CListIter; pListIterComponents->Init(pListContainers); pComponent = pListIterComponents->PeekFirst(); // if our query contour is an inner contour and it has only one potential // container, then that one is the real thing. if ((pComponentInput->GetType() == ctInner) && (pListContainers->GetLength() == 1)) { pContainer = pComponent; } else { fInputArea = pComponentInput->CalculateArea(); while (pComponent) { fCandidateArea = pComponent->CalculateArea(); if ((fCandidateArea > fInputArea) && (fCandidateArea < fMinCandidateArea)) { fMinCandidateArea = fCandidateArea; pContainer = pComponent; } pComponent = pListIterComponents->PeekNext(); } } delete pListIterComponents; return pContainer; } CComponent *CSlices:: ProcessIntersectionList(CComponent *pComponentInput, CList *pCListIntersections) { // CComponent *pContainer; CList *pListContainers = 0; CListIter *pListIterOuter, *pListIterInner; CContourElt *pContourEltCur;// *pContourEltLast; CComponent *pComponentCur, *pComponentNext, *pComponentCheck; DOUBLE dCurIntersect; UINT_32 uiWindingNum; // Intersection test was done with a ray to the right so that first item // in intersections list, with lowest X coord, and which has an odd // number of distinct intersections with the ray, and isn't inside, // will be the immediate container. pListContainers = new CList; pListContainers->Init(); pListIterOuter = new CListIter; pListIterOuter->Init(pCListIntersections); pListIterInner = new CListIter; pListIterInner->Init(pCListIntersections); // pContourEltLast = pContourEltCur = pListIterOuter->PeekFirst(&dCurIntersect); pComponentCheck = pComponentCur = pContourEltCur->GetComponent(); // dLastIntersect = dCurIntersect; do { // At start of loop, we haven't found another unchecked component pComponentNext = NULL; // initialize inner iterator with peekfirst pContourEltCur = pListIterInner->PeekFirst(&dCurIntersect); pComponentCur = pContourEltCur->GetComponent(); // Iterate forward to first elt that's in component we are checking while (pComponentCur != pComponentCheck) { pContourEltCur = pListIterInner->PeekNext(&dCurIntersect); pComponentCur = pContourEltCur->GetComponent(); } uiWindingNum = 1; // We've found one distinct itersection point so far // dLastIntersect = dCurIntersect; // pContourEltLast = pContourEltCur; // continue iterating from after first elt that's in this component while (pContourEltCur = pListIterInner->PeekNext(&dCurIntersect)) { pComponentCur = pContourEltCur->GetComponent(); if (pComponentCur == pComponentCheck) { /* if ((dLastIntersect == dCurIntersect) && (pContourEltLast != pContourEltCur)) { fprintf(stderr, "Trouble.\n"); } pContourEltLast = pContourEltCur; dLastIntersect = dCurIntersect; */ uiWindingNum++; } else if (!pComponentNext) { pComponentNext = pComponentCur; } } if ((uiWindingNum%2) == 1) { pListContainers->InsertLast(pComponentCheck, 0); } pComponentCheck = pComponentNext; } while (pComponentNext); return AreaTest(pComponentInput, pListContainers); } // Should I rename this to FindContainer? It first finds the containing // contour for the input component, then updates the nesting information // accordingly. VOID CSlices::SetNesting(CComponent *pComponentInput, CList *pListComponents) { CComponent *pComponent; CListIter *pListIterComponents = new CListIter; SliceContourType ctSearchType; CList *pListPossibleContainers; if (pComponentInput->GetType() == ctOuter) ctSearchType = ctInner; else if (pComponentInput->GetType() == ctInner) ctSearchType = ctOuter; else ASSERT(FALSE); ASSERT(!pComponentInput->GetContainer()); pListPossibleContainers = new CList; pListPossibleContainers->Init(); pListIterComponents->Init(pListComponents); pComponent = pListIterComponents->PeekFirst(); while (pComponent) { if ((pComponent->GetType() == ctSearchType) && (pComponent->GetContour()) && (pComponent->GetFirstID())) { if ((pComponent->GetMinX() <= pComponentInput->GetMinX()) && (pComponent->GetMinY() <= pComponentInput->GetMinY()) && (pComponent->GetMaxX() >= pComponentInput->GetMaxX()) && (pComponent->GetMaxY() >= pComponentInput->GetMaxY())) { pListPossibleContainers->InsertLast(pComponent, 0); pComponentInput->SetContainer(pComponent); } } pComponent = pListIterComponents->PeekNext(); } if ((pComponentInput->GetType() == ctInner) && (pListPossibleContainers->GetLength() == 1)) { // There was only one component whose bounding box contained it // if it's an inner contour, it has to have a container so the one // whose bounding box contained it is that one. pComponent = (pComponentInput->GetContainer()); #ifndef NDEBUG if (pComponent) { FLOAT fQueryArea, fCandidateArea; fQueryArea = pComponentInput->CalculateArea(); fCandidateArea = pComponent->CalculateArea(); ASSERT (fQueryAreaSetContainerMutual(pComponent); } else { RayCastNesting(pComponentInput, pListPossibleContainers); } #ifdef SLICEDEBUG if ((ctSearchType == ctInner) && (pComponentInput->GetContainer())){ fprintf(stdout,"Outer contour surrounding bounding box found\n"); } if ((ctSearchType == ctInner) && (!(pComponentInput->GetContainer()))){ fprintf(stdout,"No outer contour surrounding bounding box found\n"); } if ((ctSearchType == ctOuter) && (pComponentInput->GetContainer())){ fprintf(stdout,"Inner contour surrounding bounding box found\n"); } #endif if ((ctSearchType == ctOuter) && (!(pComponentInput->GetContainer()))){ fprintf(stderr,"No surrounding bounding box found for inner contour\n"); fprintf(stderr,"Perhaps input file had one or more shells inside out?\n"); ASSERT (FALSE); } } VOID CSlices::DoIntersect(FLOAT fSliceZ) { CLEDSGeometry *pCLEDSGeometry; CSIFPartGeomIter PartGeomIter; CList *pListComponents; CListIter *pListIterComponents; CComponent *pComponent; BOOL bNewTypeUnknown, bInnersExist; BOOL bCurLayerEmpty; pListIterComponents = new CListIter; PartGeomIter.Init(m_pCSIFPart); while (pCLEDSGeometry = PartGeomIter.PeekNext()) { pListComponents = pCLEDSGeometry->GetListComponents(); bCurLayerEmpty = TRUE; bNewTypeUnknown = FALSE; bInnersExist = FALSE; // do the intersection calculation for each contour pListIterComponents->Init(pListComponents); pComponent = pListIterComponents->PeekFirst(); while (pComponent) { if (pComponent->GetType() == ctUnknown) bNewTypeUnknown = TRUE; pComponent->IntersectContour(fSliceZ, m_fThickness, m_fEps); if (pComponent->GetType() == ctInner) { bCurLayerEmpty = FALSE; bInnersExist = TRUE; } else if (pComponent->GetType() == ctOuter){ bCurLayerEmpty = FALSE; } pComponent = pListIterComponents->PeekNext(); } pCLEDSGeometry->SetCurLayerEmpty(bCurLayerEmpty); if (bInnersExist && bNewTypeUnknown) { // find containing contour of inner contours with container unmarked pListIterComponents->Init(pListComponents); pComponent = pListIterComponents->PeekFirst(); while (pComponent) { if ((pComponent->GetType() == ctInner) && (pComponent->GetContour()) && (pComponent->GetFirstID()) // If the inner contour's nesting changed, its container split or // merged and called InvalidateNesting, so it's container is null. && (!(pComponent->GetContainer())) ) { #ifdef NOBBTEST RayCastNesting(pComponent, pListComponents); #else SetNesting(pComponent, pListComponents); #endif } // containment for outer contours (islands) may have changed too. else if ((pComponent->GetType() == ctOuter) && (pComponent->GetContour()) && (pComponent->GetFirstID()) ) { // An outer contour could suddenly be nested into a newly created // inner contour that used to be part of a sibling outer contour // that had no relation to the contour now inside it. So we have // to check all outer contours regardless of whether their // containers are set. #ifdef NOBBTEST RayCastNesting(pComponent, pListComponents); #else SetNesting(pComponent, pListComponents); #endif } pComponent = pListIterComponents->PeekNext(); } } } } /* // CSG: Call this on list of all of the pListComponents (?) VOID CSlices::oldDoIntersect(FLOAT fSliceZ, CList *pListComponents) { CComponent *pComponent; CListIter *pListIterComponents = new CListIter; BOOL bNewTypeUnknown; BOOL bInnersExist; // static BOOL bLayerOutput = TRUE; BOOL bLayerOutput = FALSE; // if (bLayerOutput) #ifndef NDISPLAY glColor3f(1.0, 1.0, 0.0); #endif // StartLayerOutput(); //If the last layer had output, start a new one // This whole layer thing seems wrong; either we still want empty // layers, or we need to change syntax to include height with each // layer. // bLayerOutput = FALSE; // assume false for this layer till we see output // CSG: Can we alternate vertices, contours, vertices, contours? // If so, want these three "paragraphs" inside of a loop that // deals with outputing their boolean wrappers. // For a diff, if first arg is empty, no output for the whole expression. // For empty args elsewhere, just omit the empty args; if all args are // empty, then no ouput for the whole expression. { bNewTypeUnknown = FALSE; bInnersExist = FALSE; // do the intersection calculation for each contour & classify it, // printing out vertices as we go. pListIterComponents->Init(pListComponents); pComponent = pListIterComponents->PeekFirst(); while (pComponent) { if (pComponent->GetType() == ctUnknown) bNewTypeUnknown = TRUE; // IntersectContour(fSliceZ, pComponent); pComponent->IntersectContour(fSliceZ, m_fThickness, m_fEps); if (pComponent->GetType() != ctUnknown) {//type unknown if < 3 pts if (!bLayerOutput) { StartLayerOutput(); bLayerOutput = TRUE; pComponent->OutputVertices(m_iPrecisionExp); } } // bLayerOutput = TRUE; // It's a real component with >= 3 pts if (pComponent->GetType() == ctInner) bInnersExist = TRUE; pComponent = pListIterComponents->PeekNext(); } if (bInnersExist && bNewTypeUnknown) { // find containing contour of inner contours with container unmarked pListIterComponents->Init(pListComponents); pComponent = pListIterComponents->PeekFirst(); while (pComponent) { if ((pComponent->GetType() == ctInner) && (pComponent->GetContour()) && (pComponent->GetFirstID()) // If the inner contour's nesting changed, its container split or // merged and called InvalidateNesting, so it's container is null. && (!(pComponent->GetContainer())) ) { #ifdef NOBBTEST RayCastNesting(pComponent, pListComponents); #else SetNesting(pComponent, pListComponents); #endif } // containment for outer contours (islands) may have changed too. else if ((pComponent->GetType() == ctOuter) && (pComponent->GetContour()) && (pComponent->GetFirstID()) ) { // An outer contour could suddenly be nested into a newly created // inner contour that used to be part of a sibling outer contour // that had no relation to the contour now inside it. So we have // to check all outer contours regardless of whether their // containers are set. #ifdef NOBBTEST RayCastNesting(pComponent, pListComponents); #else SetNesting(pComponent, pListComponents); #endif } pComponent = pListIterComponents->PeekNext(); } } // print out the faces pListIterComponents->Init(pListComponents); pComponent = pListIterComponents->PeekFirst(); // if (!pComponent) // bLayerOutput = FALSE; while (pComponent) { if ((pComponent->GetType() == ctOuter) && (pComponent->GetContour()) && (pComponent->GetFirstID()) && !(pComponent->GetContainer())) { // pComponent->Output(); } pComponent = pListIterComponents->PeekNext(); } delete pListIterComponents; } // if (bLayerOutput) { EndConstructOutput(); // end the union in the layer EndConstructOutput(); // end layer #ifndef NDISPLAY glFlush(); #endif // } } */ VOID CComponent::OutputVertices(INT_32 iPrecisionExp) { CListIter VtxIter; CList *pListVertices; Point *pPt; static UINT_32 uiCounter = 1; SetFirstID(uiCounter); pListVertices = m_pCListVerts; if (pListVertices->GetLength() > 0) { VtxIter.Init(pListVertices); pPt = VtxIter.PeekFirst(); if (pPt) { do { OutputVertex(iPrecisionExp, uiCounter++, pPt); } while (pPt = VtxIter.PeekNext()); } } SetLastID(uiCounter - 1); } VOID CComponent::Output(INT_32 iPrecisionExp) { CComponent *pComponentContained; CList *pListContained; CListIter *pListIterContained; if ((pListContained = GetContained())) StartNested1dOutput(); StartLoop(); OutputVertices(iPrecisionExp); PrintLoopVertices(); EndConstructOutput(); // end loop if (pListContained) { pListIterContained = new CListIter; pListIterContained->Init(pListContained); pComponentContained = pListIterContained->PeekFirst(); while (pComponentContained) { pComponentContained->Output(iPrecisionExp); pComponentContained = pListIterContained->PeekNext(); } delete pListIterContained; EndConstructOutput(); // end nested1d } } #ifdef CONCAVE_FACES // This would need to be called for all the LEDSGeometries. VOID CSlices::CleanFaces() { CListIter CListIterIntFace; CLEDSliteFace *pledsFace; CListIterIntFace.Init(m_pCLEDSGeometry->GetCListFaces()); pledsFace = (CLEDSFace *)CListIterIntFace.PeekFirst(); while (pledsFace) { if (pledsFace->GetExtra()) delete (CSliceInfo *)pledsFace->GetExtra(); pledsFace->SetExtra(NULL); pledsFace = (CLEDSFace *)CListIterIntFace.PeekNext(); } } #endif