/* edge.c: routines for manipulating BREP_EDGEs and BREP_WINGs */ #include "brep.h" #include "private.h" #include "pools.h" #ifndef NOPOOLS static POOL *edgePool = (POOL *)NULL; #define NEWEDGE() (BREP_EDGE *)NewPoolCell(sizeof(BREP_EDGE), 0, "brep edges", &edgePool) #define DISPOSEEDGE(ptr) Dispose((unsigned char *)(ptr), &edgePool) #else /* NOPOOLS */ #define NEWEDGE() (BREP_EDGE *)Alloc(sizeof(BREP_EDGE)) #define DISPOSEEDGE(ptr) Free((char *)ptr, sizeof(BREP_EDGE)) #endif /* NOPOOLS */ /* callback functions for manipulating BREP_EDGEs */ static BREP_CALLBACK_FUNC brep_create_edge_callback = (BREP_CALLBACK_FUNC)NULL, brep_close_edge_callback = (BREP_CALLBACK_FUNC)NULL, brep_destroy_edge_callback = (BREP_CALLBACK_FUNC)NULL; /* set the CreateEdge callback routine: called when the edge is created, normally * when it is inserted into a first contour. */ BREP_CALLBACK_FUNC BrepSetCreateEdgeCallback(BREP_CALLBACK_FUNC func) { BREP_CALLBACK_FUNC oldfunc = brep_create_edge_callback; brep_create_edge_callback = func; return oldfunc; } /* set the CloseEdge callback routine: called when an edge is inserted into a second * contour. */ BREP_CALLBACK_FUNC BrepSetCloseEdgeCallback(BREP_CALLBACK_FUNC func) { BREP_CALLBACK_FUNC oldfunc = brep_close_edge_callback; brep_close_edge_callback = func; return oldfunc; } /* set the DestroyEdge callback routine */ BREP_CALLBACK_FUNC BrepSetDestroyEdgeCallback(BREP_CALLBACK_FUNC func) { BREP_CALLBACK_FUNC oldfunc = brep_destroy_edge_callback; brep_destroy_edge_callback = func; return oldfunc; } /* Creates an edge connecting vertex1 to vertex2. It is not * connected to a contour. It calls the CreateEdge callback * if set. */ BREP_EDGE *BrepCreateEdge(BREP_VERTEX *vertex1, BREP_VERTEX *vertex2, void *client_data) { BREP_EDGE *edge; edge = NEWEDGE(); edge->wing[0].edge = edge->wing[1].edge = edge; edge->wing[0].vertex = vertex1; edge->wing[1].vertex = vertex2; /* initialize the wings: they still need to be connected with a contour */ edge->wing[0].contour = edge->wing[1].contour = (BREP_CONTOUR *)NULL; edge->wing[0].prev = edge->wing[0].next = edge->wing[1].prev = edge->wing[1].next = (BREP_WING *)NULL; /* call the CreateEdge callback if set */ edge->client_data = client_data; if (brep_create_edge_callback) edge->client_data = brep_create_edge_callback(edge); return edge; } /* Creates a wing from vertex1 to vertex2. First looks for an * existing edge connecting the vertices. Creates such an edge if * if there is not yet one. Returns a pointer to an unused wing in * the edge. The wing still needs to be connected in a contour. */ BREP_WING *BrepCreateWingWithoutContour(BREP_VERTEX *vertex1, BREP_VERTEX *vertex2, void *client_data) { BREP_EDGE *edge; BREP_WING *wing; /* first look if there is already an edge connecting the two * vertices */ edge = BrepFindEdge(vertex1, vertex2); if (!edge) { /* there was not yet such an edge */ /* create such an edge */ edge = BrepCreateEdge(vertex1, vertex2, client_data); /* the wing to be used is the first wing --- vertex1 is the * startvertex for the wing to be created */ wing = &edge->wing[0]; } else { /* there was already such an edge */ if (edge->wing[0].vertex == vertex1) /* vertex1 is startvertex in the first wing? */ wing = &(edge->wing[0]); else if (edge->wing[1].vertex == vertex1) /* vertex1 is startvertex in the second wing */ wing = &(edge->wing[1]); else { /* shouldn't happen */ BrepFatal(edge->client_data, "BrepCreateWingWithoutContour", "something impossible wrong here"); return (BREP_WING *)NULL; /* actually, it shouldn't matter what is returned here */ } /* is the wing found already used in a contour? */ if (wing->contour) { BrepInfo(edge->client_data, "BrepCreateWing", "attempt to use an edge two times in the same sense - creating a duplicate edge"); edge = BrepCreateEdge(vertex1, vertex2, client_data); wing = &edge->wing[0]; } } return wing; } /* connect the wing as last wing in the contour */ void BrepConnectWingToContour(BREP_WING *wing, BREP_CONTOUR *contour) { wing->contour = contour; if (!contour->wings) { /* first wing in contour */ wing->next = wing->prev = wing; contour->wings = wing; } else { /* not first wing */ wing->next = contour->wings; wing->prev = contour->wings->prev; wing->prev->next = wing->next->prev = wing; } } /* various actions to be performed when an edge has been specified * completely, i.e. it is enclosed in two contours. */ void BrepCloseEdge(BREP_EDGE *edge) { if (brep_close_edge_callback) edge->client_data = brep_close_edge_callback(edge); } /* creates a wing into the contour from vertex1 to vertex2. The * inside of the face bounded by the contour is supposed to be on the * left side. The wing being created is supposed to be the new last * one in a contour, i.e. its starting vertex 'vertex1' must be the * endvertex of the most recently created wing in the contour (if * non-empty). It is the responsibility of the user to close the * contour, i.e., the last edge in a contour should connect to the * first one. */ BREP_WING *BrepCreateWing(BREP_VERTEX *vertex1, BREP_VERTEX *vertex2, BREP_CONTOUR *contour, void *client_data) { BREP_WING *wing; if (contour->wings && BrepEdgeOtherWing(contour->wings->prev)->vertex != vertex1) { BrepError(contour->client_data, "BrepCreateWing", "the starting point of a new wing should be the endpoint of the previous wing"); return (BREP_WING *)NULL; } /* first create a wing that still needs to be connected with a contour */ wing = BrepCreateWingWithoutContour(vertex1, vertex2, client_data); /* complain if the edge is not used in opposite sense in both directions */ if (wing->vertex != vertex1) BrepError(wing->edge->client_data, "BrepCreateWing", "edge should be used in opposite sense by the contours sharing it"); /* connect the wing to the contour */ BrepConnectWingToContour(wing, contour); /* connect the wing to its startvertex after it has been connected in a * contour */ BrepConnectWingToVertex(wing); /* close the edge */ BrepCloseEdge(wing->edge); return wing; } /* Splits only one wing. If a full edge is to be split, also its * wing in the other contour (if any) needs to be split at the same vertex. * (BrepSplitEdge does this). * Returns the wing leaving at the inserted vertex. */ BREP_WING *BrepSplitWing(BREP_WING *wing, BREP_VERTEX *vertex) { BREP_WING *wing1, *wing2; /* create two new wings, not connected to the contour */ wing1 = BrepCreateWingWithoutContour(wing->vertex, vertex, NULL); wing2 = BrepCreateWingWithoutContour(vertex, wing->next->vertex, NULL); /* now connect the pair of consecutive wings to the contour */ wing1->contour = wing2->contour = wing->contour; wing1->next = wing2; wing2->prev = wing1; if (wing->prev != wing) { /* wing is not the only wing in the contour */ wing1->prev = wing->prev; wing2->next = wing->next; } else { /* wing is the only wing in the contour */ wing1->prev = wing2; wing2->next = wing1; } /* destroy the original wing, leaves the contour with a hole in it or * even a contour without wings ... */ BrepDestroyWing(wing); /* ... fill the hole */ wing1->prev->next = wing1; wing2->next->prev = wing2; /* and make sure the contour knows about the news wings. */ if (!wing1->contour->wings) wing1->contour->wings = wing1; /* finally, connect the new wings to their startvertex */ BrepConnectWingToVertex(wing1); BrepConnectWingToVertex(wing2); /* close the edges */ BrepCloseEdge(wing1->edge); BrepCloseEdge(wing2->edge); return wing2; } /* Splits an edge in two at the specified vertex (at least: * topologically; the new vertex doesn't have to be collinear * with the endpoints of the edge to be split, but you should make * sure that no contours are created that intersect in other * places than at vertices and along edges. The contours * to which the edge belongs are assumed to be closed loops. */ void BrepSplitEdge(BREP_EDGE *edge, BREP_VERTEX *vertex) { int do_wing0, do_wing1; do_wing0 = (edge->wing[0].contour != (BREP_CONTOUR *)NULL); do_wing1 = (edge->wing[1].contour != (BREP_CONTOUR *)NULL); if (do_wing0) BrepSplitWing(&(edge->wing[0]), vertex); if (do_wing1) BrepSplitWing(&(edge->wing[1]), vertex); } /* Joins two wings at there common endpoint. The wings are supposed to * be consecutive wings, i.e. wing1->next = wing2 (and wing2->prev = wing1) * in a closed contour. The common vertex is wing2->vertex, * the start vertex of the second wing (which is supposed to be * the endpoint of the first wing as well). */ BREP_WING *BrepJoinWings(BREP_WING *wing1, BREP_WING *wing2) { BREP_WING *wing; /* create a new wing connecting the startpoint of wing1 with the endpoint of * wing2 */ wing = BrepCreateWingWithoutContour(wing1->vertex, wing2->next->vertex, NULL); /* connect it to the contour */ wing->contour = wing1->contour; if (wing1->prev != wing2) { /* the two wings were not the only two in the contour */ wing->prev = wing1->prev; wing->next = wing2->next; } else { /* the two wings were the only two in the contour */ wing->prev = wing->next = wing; } /* destroy the two wings, this leaves a hole in the contour, or if the wings * were the only two, a contour without wings ... */ BrepDestroyWing(wing1); BrepDestroyWing(wing2); /* ... fill the hole */ wing->prev->next = wing; wing->next->prev = wing; /* make sure the contour knows about the new wing */ if (!wing->contour->wings) wing->contour->wings = wing; /* connect the new wing to its starvertex */ BrepConnectWingToVertex(wing); /* close the edge */ BrepCloseEdge(wing->edge); return wing; } /* Join two edges at their common endpoint. The edges must be used * in the same contours and must share an endpoint. */ void BrepJoinEdges(BREP_EDGE *edge1, BREP_EDGE *edge2) { int do_wing0, do_wing1; do_wing0 = (edge1->wing[0].contour != (BREP_CONTOUR *)NULL); do_wing1 = (edge1->wing[1].contour != (BREP_CONTOUR *)NULL); /* first join the wings on the edge1->wing[0] side */ if (do_wing0) { if (edge1->wing[0].next == &(edge2->wing[0])) BrepJoinWings(&(edge1->wing[0]), &(edge2->wing[0])); else if (edge1->wing[0].next == &(edge2->wing[1])) BrepJoinWings(&(edge1->wing[0]), &(edge2->wing[1])); else if (edge1->wing[0].prev == &(edge2->wing[0])) BrepJoinWings(&(edge2->wing[0]), &(edge1->wing[0])); else if (edge1->wing[0].prev == &(edge2->wing[1])) BrepJoinWings(&(edge2->wing[1]), &(edge1->wing[0])); else BrepError(edge1->client_data, "BrepJoinEdges", "the two edges don't fit on the first side"); } /* then join the wings on the edge1->wing[1] side */ if (do_wing1) { if (edge1->wing[1].next == &(edge2->wing[0])) BrepJoinWings(&(edge1->wing[1]), &(edge2->wing[0])); else if (edge1->wing[1].next == &(edge2->wing[1])) BrepJoinWings(&(edge1->wing[1]), &(edge2->wing[1])); else if (edge1->wing[1].prev == &(edge2->wing[0])) BrepJoinWings(&(edge2->wing[0]), &(edge1->wing[1])); else if (edge1->wing[1].prev == &(edge2->wing[1])) BrepJoinWings(&(edge2->wing[1]), &(edge1->wing[1])); else BrepError(edge1->client_data, "BrepJoinEdges", "the two edges don't fit on the second side"); } } /* Replace the starting vertex of 'wing' by 'newvertex'. Assumes * that 'wing' is correctly connected in a contour and it is not * the only wing (that means: the contour is not a selfloop). * Returns a pointer to the new wing, which is leaving at 'newvertex'. */ BREP_WING *BrepWingReplaceVertex(BREP_WING *wing, BREP_VERTEX *newvertex) { BREP_WING *wing1, *wing2; if (!wing->prev) { BrepError(wing->edge->client_data, "BrepWingReplaceVertex", "wing should be properly connected to a contour"); return (BREP_WING *)NULL; } if (wing->prev == wing) { BrepError(wing->edge->client_data, "BrepWingReplaceVertex", "wing should not be the only wing in the contour"); return (BREP_WING *)NULL; } /* create two new wings that will replace wing->prev and wing */ wing1 = BrepCreateWingWithoutContour(wing->prev->vertex, newvertex, NULL); wing2 = BrepCreateWingWithoutContour(newvertex, wing->next->vertex, NULL); /* connect the pair of new wings to the contour */ wing1->contour = wing2->contour = wing->contour; wing1->next = wing2; wing2->prev = wing1; if (wing->prev->prev != wing) { /* there are more than two wings in the contour */ wing1->prev = wing->prev->prev; wing2->next = wing->next; } else { /* there are only two wings in the contour: wing and its predecessor, * and they will be destroyed. */ wing1->prev = wing2; wing2->next = wing1; } /* destroy the original wing and its predecessor in the contour, leaves * the contour with a hole in it or even a contour without wings ... */ BrepDestroyWing(wing->prev); BrepDestroyWing(wing); /* ... fill the hole */ wing1->prev->next = wing1; wing2->next->prev = wing2; /* and make sure the contour knows about the news wings. */ if (!wing1->contour->wings) wing1->contour->wings = wing1; /* finally, connect the new wings to their startvertex */ BrepConnectWingToVertex(wing1); BrepConnectWingToVertex(wing2); /* close the edges */ BrepCloseEdge(wing1->edge); BrepCloseEdge(wing2->edge); return wing2; } /* returns the other wing in the edge */ BREP_WING *BrepEdgeOtherWing(BREP_WING *wing) { return (wing == &(wing->edge->wing[0]) ? &(wing->edge->wing[1]) : &(wing->edge->wing[0]) ); } /* iterate over a ring of wings */ void BrepIterateWings(BREP_WING *wings, void (*func)(BREP_WING *)) { BREP_WING *first_wing, *wing, *next; if (wings) { next = first_wing = wings; do { wing = next; next = wing->next; func(wing); } while (next != first_wing); } } /* release all storage associated with an edge and its vertices * if not used in other edges as well. The given edge must already * be disconnected from its contours. */ void BrepDestroyEdge(BREP_EDGE *edge) { /* check if the edge is still being used in a contour. If * so, don't destroy the edge. */ if (edge->wing[0].contour || edge->wing[1].contour) return; /* notify the user if a DestroyEdge callback is set */ if (brep_destroy_edge_callback) brep_destroy_edge_callback(edge); /* dispose of the BREP_EDGE structure itself (inverse of * BrepCreateEdgeWithoutVertices() */ DISPOSEEDGE(edge); } /* Disconnect the wing from the contour. Use with care: the contour * might contain holes after disconnecting the wing! wing->next * and wing->prev are left untouched as these fields are needed * when disconnecting an edge from its vertices. */ void BrepDisconnectWingFromContour(BREP_WING *wing) { BREP_CONTOUR *contour = wing->contour; if (!contour || !wing->next || !wing->prev) BrepError(wing->edge->client_data, "BrepDisconnectWingFromContour", "wing improperly connected to contour"); if (contour && contour->wings == wing) { /* the wing is the first wing * in the contour */ if (wing->next == wing) /* it is the only wing */ contour->wings = (BREP_WING *)NULL; else /* make the next wing the first wing of the contour */ contour->wings = wing->next; } /* endpoint of wing->prev will in general not be the same as * startpoint of wing->next! A hole might be created in the contour. */ if (wing->next) wing->next->prev = wing->prev; if (wing->prev) wing->prev->next = wing->next; wing->contour = (BREP_CONTOUR *)NULL; wing->next = wing->prev = (BREP_WING *)NULL; } /* remove a wing from a contour, release the storage associated with the * edge and its vertices if it is not used in other contours as well. * Inverse of BrepCreateWing(), but also destroys unused vertices. */ void BrepDestroyWing(BREP_WING *wing) { /* first disconnect the wing from its starting vertex */ BrepDisconnectWingFromVertex(wing); /* then disconnect the wing from its contour */ BrepDisconnectWingFromContour(wing); /* destroy the edge if not being used in any contour anymore * (BrepDestroyEdge already checks for this). */ BrepDestroyEdge(wing->edge); }