/* contour.c: routines for manipulating BREP_CONTOURs */ #include "brep.h" #include "private.h" #include "pools.h" #ifndef NOPOOLS static POOL *contourPool = (POOL *)NULL; #define NEWCONTOUR() (BREP_CONTOUR *)NewPoolCell(sizeof(BREP_CONTOUR), 0, "brep contours", &contourPool) #define DISPOSECONTOUR(ptr) Dispose((unsigned char *)(ptr), &contourPool) #else /* NOPOOLS */ #define NEWCONTOUR() (BREP_CONTOUR *)Alloc(sizeof(BREP_CONTOUR)) #define DISPOSECONTOUR(ptr) Free((char *)ptr, sizeof(BREP_CONTOUR)) #endif /* NOPOOLS */ /* callback functions for manipulating BREP_CONTOURs */ static BREP_CALLBACK_FUNC brep_close_contour_callback = (BREP_CALLBACK_FUNC)NULL, brep_destroy_contour_callback = (BREP_CALLBACK_FUNC)NULL, brep_create_contour_callback = (BREP_CALLBACK_FUNC)NULL, brep_update_contour_callback = (BREP_CALLBACK_FUNC)NULL; /* set the CreateContour callback routine */ BREP_CALLBACK_FUNC BrepSetCreateContourCallback(BREP_CALLBACK_FUNC func) { BREP_CALLBACK_FUNC oldfunc = brep_create_contour_callback; brep_create_contour_callback = func; return oldfunc; } /* set the CloseContour callback routine */ BREP_CALLBACK_FUNC BrepSetCloseContourCallback(BREP_CALLBACK_FUNC func) { BREP_CALLBACK_FUNC oldfunc = brep_close_contour_callback; brep_close_contour_callback = func; return oldfunc; } /* set the UpdateContour callback routine */ BREP_CALLBACK_FUNC BrepSetUpdateContourCallback(BREP_CALLBACK_FUNC func) { BREP_CALLBACK_FUNC oldfunc = brep_update_contour_callback; brep_update_contour_callback = func; return oldfunc; } /* set the DestroyContour callback routine */ BREP_CALLBACK_FUNC BrepSetDestroyContourCallback(BREP_CALLBACK_FUNC func) { BREP_CALLBACK_FUNC oldfunc = brep_destroy_contour_callback; brep_destroy_contour_callback = func; return oldfunc; } /* connects a contour to the specified face. The first contour in a face is * supposed to be the outer contour. */ void BrepConnectContourToFace(BREP_CONTOUR *contour, BREP_FACE *face) { contour->face = face; if (!face->outer_contour) { /* this is the first contour in the face */ face->outer_contour = contour; contour->next = contour->prev = contour; } else { /* not the first contour in the face */ contour->next = face->outer_contour; contour->prev = face->outer_contour->prev; contour->next->prev = contour->prev->next = contour; } } /* creates a new empty contour within the face */ BREP_CONTOUR *BrepCreateContour(BREP_FACE *face, void *client_data) { BREP_CONTOUR *contour; contour = NEWCONTOUR(); contour->wings = (BREP_WING *)NULL; BrepConnectContourToFace(contour, face); /* if a CreateContour callback has been specified, call it */ contour->client_data = client_data; if (brep_create_contour_callback) contour->client_data = brep_create_contour_callback(contour); return contour; } /* various actions to be performed when a contour is specified completely. There * is no iteration over the edges since the edges may occur in two contours. For * edges, there are two constructor callbacks: CreateEdge, which is called when first * inserting the edge in a contour, and CloseEdge callback, which is called when the * edge is inserted a second time in a contour. */ void BrepCloseContour(BREP_CONTOUR *contour) { /* if a CloseContour callback has been specified, call it */ if (brep_close_contour_callback) contour->client_data = brep_close_contour_callback(contour); } /* various actions to be performed when a contour has been updated */ void BrepUpdateContour(BREP_CONTOUR *contour) { /* if a UpdateContour callback has been specified, call it */ if (brep_update_contour_callback) contour->client_data = brep_update_contour_callback(contour); /* BrepUpdateFace(contour->face); */ } /* execute func for every wing in the contour */ void BrepContourIterateWings(BREP_CONTOUR *contour, void (*func)(BREP_WING *)) { BrepIterate((BREP_RING *)contour->wings, (void (*)(BREP_RING *))func); } void BrepContourIterateWings1A(BREP_CONTOUR *contour, void (*func)(BREP_WING *, void *), void *parm) { BrepIterate1A((BREP_RING *)contour->wings, (void (*)(BREP_RING *, void *))func, parm); } void BrepContourIterateWings2A(BREP_CONTOUR *contour, void (*func)(BREP_WING *, void *, void *), void *parm1, void *parm2) { BrepIterate2A((BREP_RING *)contour->wings, (void (*)(BREP_RING *, void *, void *))func, parm1, parm2); } /* execute func for every vertex in the contour */ static void do_vertex_func(BREP_WING *wing, void (*func)(BREP_VERTEX *)) { func(wing->vertex); } void BrepContourIterateVertices(BREP_CONTOUR *contour, void (*func)(BREP_VERTEX *)) { BrepContourIterateWings1A(contour, (void (*)(BREP_WING *, void *))do_vertex_func, (void *)func); } static void do_vertex_func_parm(BREP_WING *wing, void (*func)(BREP_VERTEX *, void *), void *parm) { func(wing->vertex, parm); } void BrepContourIterateVertices1A(BREP_CONTOUR *contour, void (*func)(BREP_VERTEX *, void *), void *parm) { BrepContourIterateWings2A(contour, (void (*)(BREP_WING *, void *, void *))do_vertex_func_parm, (void *)func, parm); } /* Remove a vertex from a (closed) contour, the wings containing the vertex are * removed as well (and their edges if not used in other contours as * well). The vertex itself will also be removed when it is no longer * used. If it is not the only vertex in the contour, a new wing connecting * the neighbooring vertices is created, so the contour remains * a loop. */ void BrepContourRemoveVertex(BREP_CONTOUR *contour, BREP_VERTEX *vertex) { BREP_WING *wing; /* check for empty contour */ if (!contour->wings) { BrepError(contour->client_data, "BrepContourRemoveVertex", "empty contour"); return; } /* if vertex is the only vertex in the contour, remove its self-edge. */ if (contour->wings->next == contour->wings) { if (contour->wings->vertex != vertex) { BrepError(contour->client_data, "BrepContourRemoveVertex", "vertex not belonging to contour or contour is not closed"); return; } else BrepDestroyWing(contour->wings); } /* else, replace the wing arriving at and the wing leaving the last occurence * of the vertex in the contour by a single wing connecting the * previous and next vertex. */ /* look for the last occurence of the vertex in the contour */ wing = contour->wings->prev; while (wing->vertex != vertex && wing != contour->wings) wing = wing->prev; if (wing->vertex != vertex) BrepError(contour->client_data, "BrepContourRemoveVertex", "vertex doesn't occur in contour"); else BrepJoinWings(wing->prev, wing); } /* disconnects a contour from its containing face */ void BrepDisconnectContourFromFace(BREP_CONTOUR *contour) { BREP_FACE *face = contour->face; if (face->outer_contour == contour) { /* the contour is the first contour * in the face */ if (contour->next == contour) /* it is the only contour */ face->outer_contour = (BREP_CONTOUR *)NULL; else { /* the faces looses its outer contour */ /* BrepInfo(face->client_data, "BrepDisconnectContourFromFace", "outer contour disconnected from face"); */ face->outer_contour = contour->next; } } contour->next->prev = contour->prev; contour->prev->next = contour->next; contour->face = (BREP_FACE *)NULL; } /* destroys the wings in a contour */ static void BrepContourDestroyWings(BREP_WING *first) { BREP_WING *wing, *prev; if (first) { for (wing = first->prev; wing != first; wing = prev) { prev = wing->prev; BrepDestroyWing(wing); } BrepDestroyWing(first); } } /* release all storage associated with a contour and its contours, * including edges and vertices if not used in other contours as well */ void BrepDestroyContour(BREP_CONTOUR *contour) { /* inverse actions performed in BrepCreateContour() in reverse order */ /* disconnect the contour from the containing face */ BrepDisconnectContourFromFace(contour); /* notify the user that the client_data can be disposed of */ if (brep_destroy_contour_callback) brep_destroy_contour_callback(contour); /* destroy its wings */ BrepContourDestroyWings(contour->wings); /* dispose of the BREP_CONTOUR structure itself */ DISPOSECONTOUR(contour); } /* wing1 and wing2 should be two wings belonging to the same contour. * This routine creates a new edge connecting the start vertex of wing1 * and wing2, splitting the contour in two. A pointer to the newly created * wing, connecting the start vertex of wing1 to the start vertex of wing2, * is returned. */ BREP_WING *BrepMakeEdgeSplitContour(BREP_WING *wing1, BREP_WING *wing2, void *edge_data, void *contour_data) { BREP_EDGE *edge; BREP_WING *winga, *wingb, *wing; /* test whether the wings belong to the same contour */ if (wing1->contour != wing2->contour) { BrepError(wing1->edge->client_data, "BrepMakeEdgeSplitContour", "wings must belong to the same contour"); return (BREP_WING *)NULL; } /* create an edge connecting the two start vertices */ edge = BrepCreateEdge(wing1->vertex, wing2->vertex, edge_data); winga = &(edge->wing[0]); wingb = &(edge->wing[1]); /* connect the wings of the newly created edge to the contour, * effectively breaking the contour in two loops: one starting * with wing1 and one starting with wing2. */ winga->prev = wing1->prev; winga->next = wing2; wingb->prev = wing2->prev; wingb->next = wing1; winga->prev->next = winga->next->prev = winga; wingb->prev->next = wingb->next->prev = wingb; winga->contour = wingb->contour = wing1->contour; /* let the old contour point to the wing1 loop */ wing1->contour->wings = wing1; /* create a new contour for the wing2 loop */ wing2->contour = BrepCreateContour(wing1->contour->face, contour_data); wing2->contour->wings = wing2; /* let all wings in the wing2 loop point to the new contour */ wing = wing2; do { wing->contour = wing2->contour; wing = wing->next; } while (wing != wing2); /* connect the newly created wings to their start vertices */ BrepConnectWingToVertex(winga); BrepConnectWingToVertex(wingb); /* notify the user that the edge has been connected into two * contours. */ BrepCloseEdge(edge); /* notify the user that two new contours have been completely specified */ /* BrepUpdateContour(wing1->contour); */ BrepCloseContour(wing2->contour); BrepCloseContour(wing1->contour); /* return a pointer to the wing connecting wing1->vertex to wing2->vertex */ return winga; } /* Wing1 and wing2 are two wings belonging to a different contour. This * routine creates a new edge connecting the starting vertices of * wing1 and wing2 and merges to two contours to one. A pointer to * the newly created wing, connecting the start vertex of wing1 to the * start vertex of wing2, is returned. */ BREP_WING *BrepMakeEdgeJoinContours(BREP_WING *wing1, BREP_WING *wing2, void *edge_data) { BREP_EDGE *edge; BREP_WING *winga, *wingb, *wing; /* test whether the wings belong to a different contour */ if (wing1->contour == wing2->contour) { BrepError(wing1->edge->client_data, "BrepMakeEdgeJoinContours", "wings must belong to a different contour"); return (BREP_WING *)NULL; } /* test whether the wings belong to the same face */ if (wing1->contour->face != wing2->contour->face) { BrepWarning(wing1->edge->client_data, "BrepMakeEdgeJoinContours", "joining contours from different faces"); /* it's not an error ... */ } /* create an edge connecting the two start vertices */ edge = BrepCreateEdge(wing1->vertex, wing2->vertex, edge_data); winga = &(edge->wing[0]); wingb = &(edge->wing[1]); /* connect the wings of the newly created edge to the contour to which * wing1 belongs. */ winga->prev = wing1->prev; winga->next = wing2; wingb->prev = wing2->prev; wingb->next = wing1; winga->prev->next = winga->next->prev = winga; wingb->prev->next = wingb->next->prev = wingb; /* unless the contour of wing2 is the outer contour of its face, "move" its * wings to the contour of wing1, and destroy the contour */ if (wing2->contour != wing2->contour->face->outer_contour) { winga->contour = wingb->contour = wing1->contour; /* disconnect wing2->contour from its wings and destroy it */ wing2->contour->wings = (BREP_WING *)NULL; BrepDestroyContour(wing2->contour); /* let all wings in the former contour of wing2 point to the contour of wing1 */ for (wing = wing2; wing->contour != wing1->contour; wing = wing->next) wing->contour = wing1->contour; } else { winga->contour = wingb->contour = wing2->contour; /* disconnect wing1->contour from its wings and destroy it */ wing1->contour->wings = (BREP_WING *)NULL; BrepDestroyContour(wing1->contour); /* let all wings in the former contour of wing2 point to the contour of wing1 */ for (wing = wing1; wing->contour != wing2->contour; wing = wing->next) wing->contour = wing2->contour; } /* connect the newly created wings to their start vertices */ BrepConnectWingToVertex(winga); BrepConnectWingToVertex(wingb); /* notify the user that the edge has been connected into two * contours. */ BrepCloseEdge(edge); /* notify the user that the contour has been updated. */ /* BrepUpdateContour(wing1->contour); */ BrepCloseContour(winga->contour); /* return a pointer to the wing connecting wing1->vertex to wing2->vertex */ return winga; } /* wing is a wing of an edge that is used in two different contours. This routine * deletes the edge to which the wing belongs, inserting the reaminder of * the "other" contour sharing the edge into the contour to which the wing * belongs. The other contour is destroyed and the first is "closed" again * in order to notify that it changed. Returns the wing from the first contour * that comes before the first wing moved from the second to the first, or * NULL if the first contour was a selfloop. In that case, the empty contour * is deleted. In case of errors, wing is returned. Warning: if the face to which * the other contour belongs, contains holes, these holes should probably be moved * to the face to which 'wing' belongs. */ BREP_WING *BrepDeleteEdgeJoinContours(BREP_WING *wing) { BREP_WING *owing = BrepEdgeOtherWing(wing); BREP_WING *prev_wing = wing->prev, *next_wing = wing->next; BREP_CONTOUR *contour = wing->contour, *ocontour = owing->contour; if (!ocontour || ocontour == wing->contour) { BrepWarning(wing->edge->client_data, "BrepDeleteEdgeJoinContours", "Edge not enclosed in two different contours"); return wing; } if (!prev_wing || !next_wing || !contour) { BrepError(wing->edge->client_data, "BrepDeleteEdgeJoinContours", "wing not properly connected in contour"); return wing; } if (wing->prev == wing) { /* remove the selfloop from the other contour */ if (owing->next == owing) BrepDestroyContour(ocontour); else BrepDestroyWing(owing); /* Delete the first contour (that contained only this wing). */ BrepDestroyContour(contour); return (BREP_WING *)NULL; } /* disconnect and delete the wing from its contour */ BrepDestroyWing(wing); if (owing->next != owing) { /* move all the wings of ocontour to contour, insert after prev_wing and before * next_wing */ BREP_WING *wing1 = owing->next, *wing2 = owing->prev; owing->next = owing->prev = owing; ocontour->wings = owing; /* will be destroyed soon ... */ prev_wing->next = wing1; wing1->prev = prev_wing; next_wing->prev = wing2; wing2->next = next_wing; for (wing=wing1; wing!=wing2; wing=wing->next) wing->contour = contour; wing2->contour = contour; } /* destroy the other contour */ BrepDestroyContour(ocontour); /* notify that the first contour has changed. */ BrepCloseContour(contour); /* last wing before the first inserted from the other contour. */ return prev_wing; } /* 'wing' is a wing of an edge that is used two times (in opposite direction) * in the same contour, i.o.w. a seam. This routine deletes this edge. * If the wings happen to be consequtive wings, the wings are just deleted. * If these are the only two wings in the contour, the contour is deleted as * well and a NULL wing pointer is returned. If they are not consequtive, the * contour is split in two contours. The wing before 'wing' will remain in * the original contour and will be returned. */ BREP_WING *BrepDeleteEdgeSplitContour(BREP_WING *wing, void *contour_data) { BREP_WING *owing; if (!wing || !wing->contour || BrepEdgeOtherWing(wing)->contour != wing->contour) { BrepWarning(wing->edge->client_data, "BrepDeleteEdgeSplitContour", "argument should be a seam"); return wing; } owing = BrepEdgeOtherWing(wing); if (owing == wing->next || owing == wing->prev) { /* consequtive wings, the seam is a notch or a slit */ if (wing->next->next == wing) { /* they are the only two wings in the contour */ BrepDestroyContour(wing->contour); return (BREP_WING *)NULL; } else { /* simply destroy the two wings. No new contour is split off. */ BREP_WING *prevwing = (owing->next == wing) ? owing->prev : wing->prev; BrepDestroyWing(wing); BrepDestroyWing(owing); BrepCloseContour(prevwing->contour); return prevwing; } } else { BREP_CONTOUR *contour = wing->contour, *split_contour; BREP_WING *first1 = owing->next, *last1 = wing->prev, *first2 = wing->next, *last2 = owing->prev; /* disconnect wing and owing from the contour + destroy */ BrepDestroyWing(wing); BrepDestroyWing(owing); /* close the loop first1 ... last1 */ first1->prev = last1; last1->next = first1; contour->wings = last1; BrepCloseContour(contour); /* close the second loop 'first2' ... 'last2' and create a separate contour */ split_contour = BrepCreateContour(contour->face, contour_data); first2->prev = last2; last2->next = first2; split_contour->wings = first2; ForAllWingsInContour(w, split_contour) { w->contour = split_contour; } EndForAll; BrepCloseContour(split_contour); return last1; } return wing; /* we never get here .... */ } /* Creates a slit, connecting the two specified vertices, * in the given face. A slit is a new contour containing * two wings: one from v1 to v2 and the second * from v2 back to v1. A pointer to the newly created * wing, connecting v1 to v2, is returned. */ BREP_WING *BrepMakeSlit(BREP_FACE *face, BREP_VERTEX *v1, BREP_VERTEX *v2, void *edge_data, void *contour_data) { BREP_EDGE *edge; BREP_WING *winga, *wingb; BREP_CONTOUR *contour; /* create a new contour in the face */ contour = BrepCreateContour(face, contour_data); /* create an edge connecting the two vertices */ edge = BrepCreateEdge(v1, v2, edge_data); winga = &(edge->wing[0]); wingb = &(edge->wing[1]); /* connect the wings of the newly created edge to the newly created * contour and to each other */ winga->prev = winga->next = wingb; wingb->prev = wingb->next = winga; winga->contour = wingb->contour = contour; contour->wings = winga; /* connect the newly created wings to their start vertices */ BrepConnectWingToVertex(winga); BrepConnectWingToVertex(wingb); /* notify the user that the edge has been connected into two * contours. */ BrepCloseEdge(edge); /* notify the user that the new contour has been completely specified */ BrepCloseContour(contour); /* return the wing connecting wing->vertex to vertex */ return winga; } /* Creates a notch, connecting the start vertex of the wing with * the specified vertex, ans inserts it into the contour to * which the wing belongs. A pointer to the newly created * wing, from the start vertex of the specified wing to the * specified vertex, is returned. */ BREP_WING *BrepMakeNotch(BREP_WING *wing, BREP_VERTEX *vertex, void *edge_data) { BREP_EDGE *edge; BREP_WING *winga, *wingb; /* create an edge connecting the start vertex of the wing to the * specified vertex */ edge = BrepCreateEdge(wing->vertex, vertex, edge_data); winga = &(edge->wing[0]); wingb = &(edge->wing[1]); /* connect the wings of the newly created edge to the contour */ winga->prev = wing->prev; winga->next = wingb; wingb->prev = winga; wingb->next = wing; winga->prev->next = winga->next->prev = winga; wingb->prev->next = wingb->next->prev = wingb; winga->contour = wingb->contour = wing->contour; /* connect the newly created wings to their start vertices */ BrepConnectWingToVertex(winga); BrepConnectWingToVertex(wingb); /* notify the user that the edge has been connected into two * contours. */ BrepCloseEdge(edge); /* notify the user that the contour has been updated. */ /* BrepUpdateContour(wing->contour); */ /* return the wing connecting wing->vertex to vertex */ return winga; }