void subbrep_planar_init(struct subbrep_object_data *data) { if (!data) return; if (data->planar_obj) return; BU_GET(data->planar_obj, struct subbrep_object_data); subbrep_object_init(data->planar_obj, data->brep); bu_vls_sprintf(data->planar_obj->key, "%s", bu_vls_addr(data->key)); data->planar_obj->obj_cnt = data->obj_cnt; (*data->obj_cnt)++; bu_vls_sprintf(data->planar_obj->name_root, "%s_%d", bu_vls_addr(data->name_root), *(data->obj_cnt)); data->planar_obj->type = PLANAR_VOLUME; data->planar_obj->local_brep = ON_Brep::New(); std::map<int, int> face_map; std::map<int, int> surface_map; std::map<int, int> edge_map; std::map<int, int> vertex_map; std::map<int, int> loop_map; std::map<int, int> c3_map; std::map<int, int> c2_map; std::map<int, int> trim_map; std::set<int> faces; std::set<int> fil; std::set<int> loops; std::set<int> skip_verts; std::set<int> skip_edges; std::set<int> keep_verts; std::set<int> partial_edges; std::set<int> isolated_trims; // collect 2D trims whose parent loops aren't fully included here array_to_set(&faces, data->faces, data->faces_cnt); array_to_set(&fil, data->fil, data->fil_cnt); array_to_set(&loops, data->loops, data->loops_cnt); std::map<int, std::set<int> > face_loops; std::map<int, std::set<int> >::iterator fl_it; std::set<int>::iterator l_it; for (int i = 0; i < data->edges_cnt; i++) { int c3i; int new_edge_curve = 0; const ON_BrepEdge *old_edge = &(data->brep->m_E[data->edges[i]]); //std::cout << "old edge: " << old_edge->Vertex(0)->m_vertex_index << "," << old_edge->Vertex(1)->m_vertex_index << "\n"; // See if the vertices from this edge play a role in the planar volume int use_edge = 2; for (int vi = 0; vi < 2; vi++) { int vert_test = -1; int vert_ind = old_edge->Vertex(vi)->m_vertex_index; if (skip_verts.find(vert_ind) != skip_verts.end()) { vert_test = 1; } if (vert_test == -1 && keep_verts.find(vert_ind) != keep_verts.end()) { vert_test = 0; } if (vert_test == -1) { vert_test = characterize_vert(data, old_edge->Vertex(vi)); if (vert_test) { skip_verts.insert(vert_ind); ON_3dPoint vp = old_edge->Vertex(vi)->Point(); bu_log("vert %d (%f %f %f): %d\n", vert_ind, vp.x, vp.y, vp.z, vert_test); } else { keep_verts.insert(vert_ind); } } if (vert_test == 1) { use_edge--; } } if (use_edge == 0) { bu_log("skipping edge %d - both verts are skips\n", old_edge->m_edge_index); skip_edges.insert(old_edge->m_edge_index); continue; } if (use_edge == 1) { bu_log("One of the verts for edge %d is a skip.\n", old_edge->m_edge_index); partial_edges.insert(old_edge->m_edge_index); continue; } // Get the 3D curves from the edges if (c3_map.find(old_edge->EdgeCurveIndexOf()) == c3_map.end()) { ON_Curve *nc = old_edge->EdgeCurveOf()->Duplicate(); ON_Curve *tc = old_edge->EdgeCurveOf()->Duplicate(); if (tc->IsLinear()) { c3i = data->planar_obj->local_brep->AddEdgeCurve(nc); c3_map[old_edge->EdgeCurveIndexOf()] = c3i; } else { ON_Curve *c3 = new ON_LineCurve(old_edge->Vertex(0)->Point(), old_edge->Vertex(1)->Point()); c3i = data->planar_obj->local_brep->AddEdgeCurve(c3); c3_map[old_edge->EdgeCurveIndexOf()] = c3i; new_edge_curve = 1; } } else { c3i = c3_map[old_edge->EdgeCurveIndexOf()]; } // Get the vertices from the edges int v[2]; for (int vi = 0; vi < 2; vi++) { if (vertex_map.find(old_edge->Vertex(vi)->m_vertex_index) == vertex_map.end()) { ON_BrepVertex& newvvi = data->planar_obj->local_brep->NewVertex(old_edge->Vertex(vi)->Point(), old_edge->Vertex(vi)->m_tolerance); v[vi] = newvvi.m_vertex_index; vertex_map[old_edge->Vertex(vi)->m_vertex_index] = v[vi]; } else { v[vi] = vertex_map[old_edge->Vertex(vi)->m_vertex_index]; } } ON_BrepEdge& new_edge = data->planar_obj->local_brep->NewEdge(data->planar_obj->local_brep->m_V[v[0]], data->planar_obj->local_brep->m_V[v[1]], c3i, NULL ,0); edge_map[old_edge->m_edge_index] = new_edge.m_edge_index; // Get the 2D curves from the trims for (int j = 0; j < old_edge->TrimCount(); j++) { ON_BrepTrim *old_trim = old_edge->Trim(j); if (faces.find(old_trim->Face()->m_face_index) != faces.end()) { if (c2_map.find(old_trim->TrimCurveIndexOf()) == c2_map.end()) { ON_Curve *nc = old_trim->TrimCurveOf()->Duplicate(); int c2i = data->planar_obj->local_brep->AddTrimCurve(nc); c2_map[old_trim->TrimCurveIndexOf()] = c2i; //std::cout << "c2i: " << c2i << "\n"; } } } // Get the faces and surfaces from the trims for (int j = 0; j < old_edge->TrimCount(); j++) { ON_BrepTrim *old_trim = old_edge->Trim(j); if (face_map.find(old_trim->Face()->m_face_index) == face_map.end()) { if (faces.find(old_trim->Face()->m_face_index) != faces.end()) { ON_Surface *ns = old_trim->Face()->SurfaceOf()->Duplicate(); ON_Surface *ts = old_trim->Face()->SurfaceOf()->Duplicate(); if (ts->IsPlanar(NULL, BREP_PLANAR_TOL)) { int nsid = data->planar_obj->local_brep->AddSurface(ns); surface_map[old_trim->Face()->SurfaceIndexOf()] = nsid; ON_BrepFace &new_face = data->planar_obj->local_brep->NewFace(nsid); face_map[old_trim->Face()->m_face_index] = new_face.m_face_index; //std::cout << "old face " << old_trim->Face()->m_face_index << " is now " << new_face.m_face_index << "\n"; if (fil.find(old_trim->Face()->m_face_index) != fil.end()) { data->planar_obj->local_brep->FlipFace(new_face); } } } } } // Get the loops from the trims for (int j = 0; j < old_edge->TrimCount(); j++) { ON_BrepTrim *old_trim = old_edge->Trim(j); ON_BrepLoop *old_loop = old_trim->Loop(); if (face_map.find(old_trim->Face()->m_face_index) != face_map.end()) { if (loops.find(old_loop->m_loop_index) != loops.end()) { if (loop_map.find(old_loop->m_loop_index) == loop_map.end()) { face_loops[old_trim->Face()->m_face_index].insert(old_loop->m_loop_index); } } } } } for (fl_it = face_loops.begin(); fl_it != face_loops.end(); fl_it++) { int loop_cnt = fl_it->second.size(); if (loop_cnt == 1) { // If we have only one loop on a face it's an outer loop, // whatever it was in the original brep. const ON_BrepLoop *old_loop = &(data->brep->m_L[*(fl_it->second.begin())]); ON_BrepLoop &nl = data->planar_obj->local_brep->NewLoop(ON_BrepLoop::outer, data->planar_obj->local_brep->m_F[face_map[fl_it->first]]); loop_map[old_loop->m_loop_index] = nl.m_loop_index; } else { bu_log("loop_cnt: %d\n", loop_cnt); // If we ended up with multiple loops, one of them should be an outer loop // and the rest inner loops // Get the outer loop first for (l_it = fl_it->second.begin(); l_it != fl_it->second.end(); l_it++) { const ON_BrepLoop *old_loop = &(data->brep->m_L[*l_it]); if (data->brep->LoopDirection(data->brep->m_L[*l_it]) == 1) { ON_BrepLoop &nl = data->planar_obj->local_brep->NewLoop(ON_BrepLoop::outer, data->planar_obj->local_brep->m_F[face_map[fl_it->first]]); loop_map[old_loop->m_loop_index] = nl.m_loop_index; } } // Now get the inner loops; for (l_it = fl_it->second.begin(); l_it != fl_it->second.end(); l_it++) { const ON_BrepLoop *old_loop = &(data->brep->m_L[*l_it]); if (data->brep->LoopDirection(data->brep->m_L[*l_it]) != 1) { ON_BrepLoop &nl = data->planar_obj->local_brep->NewLoop(ON_BrepLoop::inner, data->planar_obj->local_brep->m_F[face_map[fl_it->first]]); loop_map[old_loop->m_loop_index] = nl.m_loop_index; } } } } // Now, create new trims using the old loop definitions and the maps std::map<int, int>::iterator loop_it; std::set<int> evaluated; for (loop_it = loop_map.begin(); loop_it != loop_map.end(); loop_it++) { const ON_BrepLoop *old_loop = &(data->brep->m_L[(*loop_it).first]); ON_BrepLoop &new_loop = data->planar_obj->local_brep->m_L[(*loop_it).second]; for (int j = 0; j < old_loop->TrimCount(); j++) { const ON_BrepTrim *old_trim = old_loop->Trim(j); ON_BrepEdge *o_edge = old_trim->Edge(); if (!o_edge) { /* If we didn't have an edge originally, we need to add the 2d curve here */ if (c2_map.find(old_trim->TrimCurveIndexOf()) == c2_map.end()) { ON_Curve *nc = old_trim->TrimCurveOf()->Duplicate(); int c2i = data->planar_obj->local_brep->AddTrimCurve(nc); c2_map[old_trim->TrimCurveIndexOf()] = c2i; } if (vertex_map.find(old_trim->Vertex(0)->m_vertex_index) == vertex_map.end()) { ON_BrepVertex& newvs = data->planar_obj->local_brep->NewVertex(old_trim->Vertex(0)->Point(), old_trim->Vertex(0)->m_tolerance); vertex_map[old_trim->Vertex(0)->m_vertex_index] = newvs.m_vertex_index; ON_BrepTrim &nt = data->planar_obj->local_brep->NewSingularTrim(newvs, new_loop, old_trim->m_iso, c2_map[old_trim->TrimCurveIndexOf()]); nt.m_tolerance[0] = old_trim->m_tolerance[0]; nt.m_tolerance[1] = old_trim->m_tolerance[1]; } else { ON_BrepTrim &nt = data->planar_obj->local_brep->NewSingularTrim(data->planar_obj->local_brep->m_V[vertex_map[old_trim->Vertex(0)->m_vertex_index]], new_loop, old_trim->m_iso, c2_map[old_trim->TrimCurveIndexOf()]); nt.m_tolerance[0] = old_trim->m_tolerance[0]; nt.m_tolerance[1] = old_trim->m_tolerance[1]; } continue; } if (evaluated.find(o_edge->m_edge_index) != evaluated.end()) { bu_log("edge %d already handled, continuing...\n", o_edge->m_edge_index); continue; } // Don't use a trim connected to an edge we are skipping if (skip_edges.find(o_edge->m_edge_index) != skip_edges.end()) { bu_log("edge %d is skipped, continuing...\n", o_edge->m_edge_index); evaluated.insert(o_edge->m_edge_index); continue; } int is_partial = 0; if (partial_edges.find(o_edge->m_edge_index) != partial_edges.end()) is_partial = 1; if (!is_partial) { ON_BrepEdge &n_edge = data->planar_obj->local_brep->m_E[edge_map[o_edge->m_edge_index]]; ON_Curve *ec = o_edge->EdgeCurveOf()->Duplicate(); if (ec->IsLinear()) { ON_BrepTrim &nt = data->planar_obj->local_brep->NewTrim(n_edge, old_trim->m_bRev3d, new_loop, c2_map[old_trim->TrimCurveIndexOf()]); nt.m_tolerance[0] = old_trim->m_tolerance[0]; nt.m_tolerance[1] = old_trim->m_tolerance[1]; nt.m_iso = old_trim->m_iso; } else { // Wasn't linear, but wasn't partial either - replace with a line ON_Curve *c2_orig = old_trim->TrimCurveOf()->Duplicate(); ON_3dPoint p1 = c2_orig->PointAt(c2_orig->Domain().Min()); ON_3dPoint p2 = c2_orig->PointAt(c2_orig->Domain().Max()); ON_Curve *c2 = new ON_LineCurve(p1, p2); c2->ChangeDimension(2); int c2i = data->planar_obj->local_brep->AddTrimCurve(c2); ON_BrepTrim &nt = data->planar_obj->local_brep->NewTrim(n_edge, old_trim->m_bRev3d, new_loop, c2i); nt.m_tolerance[0] = old_trim->m_tolerance[0]; nt.m_tolerance[1] = old_trim->m_tolerance[1]; nt.m_iso = old_trim->m_iso; delete c2_orig; } delete ec; } else { // Partial edge - let the fun begin ON_3dPoint p1, p2; ON_BrepEdge *next_edge; bu_log("working a partial edge: %d\n", o_edge->m_edge_index); int v[2]; v[0] = o_edge->Vertex(0)->m_vertex_index; v[1] = o_edge->Vertex(1)->m_vertex_index; // figure out which trim point we can use, the min or max int pos1 = 0; if (skip_verts.find(v[0]) != skip_verts.end()) { pos1 = 1; } int j_next = j; ON_Curve *c2_orig = old_trim->TrimCurveOf()->Duplicate(); ON_Curve *c2_next = NULL; int walk_dir = 1; // bump the loop iterator to get passed any skipped edges to // the next partial while (!c2_next) { (walk_dir == 1) ? j_next++ : j_next--; if (j_next == old_loop->TrimCount()) { j_next = 0; } if (j_next == -1) { j_next = old_loop->TrimCount() - 1; } const ON_BrepTrim *next_trim = old_loop->Trim(j_next); next_edge = next_trim->Edge(); if (!next_edge) continue; if (skip_edges.find(next_edge->m_edge_index) == skip_edges.end()) { if (partial_edges.find(next_edge->m_edge_index) != partial_edges.end()) { bu_log("found next partial edge %d\n", next_edge->m_edge_index); evaluated.insert(next_edge->m_edge_index); c2_next = next_trim->TrimCurveOf()->Duplicate(); } else { bu_log("partial edge %d followed by non-partial %d, need to go the other way\n", o_edge->m_edge_index, next_edge->m_edge_index); j_next--; walk_dir = -1; } } else { bu_log("skipping fully ignored edge %d\n", next_edge->m_edge_index); evaluated.insert(next_edge->m_edge_index); } } int v2[2]; v2[0] = next_edge->Vertex(0)->m_vertex_index; v2[1] = next_edge->Vertex(1)->m_vertex_index; // figure out which trim point we can use, the min or max int pos2 = 0; if (skip_verts.find(v2[0]) != skip_verts.end()) { pos2 = 1; } int vmapped[2]; if (vertex_map.find(o_edge->Vertex(pos1)->m_vertex_index) == vertex_map.end()) { ON_BrepVertex& newvvi = data->planar_obj->local_brep->NewVertex(o_edge->Vertex(pos1)->Point(), o_edge->Vertex(pos1)->m_tolerance); vertex_map[o_edge->Vertex(pos1)->m_vertex_index] = newvvi.m_vertex_index; } if (vertex_map.find(next_edge->Vertex(pos2)->m_vertex_index) == vertex_map.end()) { ON_BrepVertex& newvvi = data->planar_obj->local_brep->NewVertex(next_edge->Vertex(pos2)->Point(), next_edge->Vertex(pos2)->m_tolerance); vertex_map[next_edge->Vertex(pos2)->m_vertex_index] = newvvi.m_vertex_index; } // If walk_dir is -1, need to flip things around (I think...) the verts and trim points // will be swapped compared to a forward walk if (walk_dir == -1) { vmapped[1] = vertex_map[o_edge->Vertex(pos1)->m_vertex_index]; vmapped[0] = vertex_map[next_edge->Vertex(pos2)->m_vertex_index]; } else { vmapped[0] = vertex_map[o_edge->Vertex(pos1)->m_vertex_index]; vmapped[1] = vertex_map[next_edge->Vertex(pos2)->m_vertex_index]; } // New Edge curve ON_Curve *c3 = new ON_LineCurve(o_edge->Vertex(pos1)->Point(), next_edge->Vertex(pos2)->Point()); int c3i = data->planar_obj->local_brep->AddEdgeCurve(c3); ON_BrepEdge& new_edge = data->planar_obj->local_brep->NewEdge(data->planar_obj->local_brep->m_V[vmapped[0]], data->planar_obj->local_brep->m_V[vmapped[1]], c3i, NULL ,0); // Again, flip if walk_dir is -1 if (walk_dir == -1) { p2 = c2_orig->PointAt(c2_orig->Domain().Min()); p1 = c2_next->PointAt(c2_orig->Domain().Max()); } else { p1 = c2_orig->PointAt(c2_orig->Domain().Min()); p2 = c2_next->PointAt(c2_orig->Domain().Max()); } std::cout << "p1: " << pout(p1) << "\n"; std::cout << "p2: " << pout(p2) << "\n"; ON_Curve *c2 = new ON_LineCurve(p1, p2); c2->ChangeDimension(2); int c2i = data->planar_obj->local_brep->AddTrimCurve(c2); ON_BrepTrim &nt = data->planar_obj->local_brep->NewTrim(new_edge, false, new_loop, c2i); nt.m_tolerance[0] = old_trim->m_tolerance[0]; nt.m_tolerance[1] = old_trim->m_tolerance[1]; nt.m_iso = old_trim->m_iso; delete c2_orig; delete c2_next; } } } // If there is a possibility of a negative volume for the planar solid, do a test. // The only way to get a negative planar solid in this context is if that solid is // "inside" a non-planar shape (it would be "part of" the parent shape if it were // planar and it would be a separate shape altogether if it were not topologically // connected. So we take one partial edge, find its associated non-planar faces, // and collect all the partial and skipped edges from that face and any non-planar // faces associated with the other partial/skipped edges. // // TODO - We still have an unhandled possibility here - the self-intersecting // planar_obj. For example: // // * * // * * * * // * * * * * * // * * * * * * * * // * * * * * * // * * * * * * * * * * * * // if (partial_edges.size() > 0) { std::queue<int> connected_faces; std::set<int> relevant_edges; std::set<int>::iterator re_it; std::set<int> efaces; std::set<int>::iterator f_it; std::set<int> found_faces; const ON_BrepEdge *seed_edge = &(data->brep->m_E[*partial_edges.begin()]); for (int j = 0; j < seed_edge->TrimCount(); j++) { ON_BrepTrim *trim = seed_edge->Trim(j); efaces.insert(trim->Face()->m_face_index); } for(f_it = efaces.begin(); f_it != efaces.end(); f_it++) { surface_t stype = GetSurfaceType(data->brep->m_F[*f_it].SurfaceOf(), NULL); if (stype != SURFACE_PLANE) { connected_faces.push(data->brep->m_F[*f_it].m_face_index); } } while (!connected_faces.empty()) { int face_index = connected_faces.front(); connected_faces.pop(); std::set<int> local_edges; std::set<int>::iterator le_it; found_faces.insert(face_index); const ON_BrepFace *face = &(data->brep->m_F[face_index]); const ON_BrepLoop *loop = NULL; // Find the loop in this face that is associated with this subbrep for (int i = 0; i < face->LoopCount(); i++) { int loop_ind = face->Loop(i)->m_loop_index; if (loops.find(loop_ind) != loops.end()) { loop = &(data->brep->m_L[loop_ind]); break; } } // Collect the edges that are partial or skipped for (int i = 0; i < loop->TrimCount(); i++) { const ON_BrepTrim *trim = loop->Trim(i); ON_BrepEdge *edge = trim->Edge(); if (edge) { if (partial_edges.find(edge->m_edge_index) != partial_edges.end()) { relevant_edges.insert(edge->m_edge_index); local_edges.insert(edge->m_edge_index); } if (skip_edges.find(edge->m_edge_index) != skip_edges.end()) { relevant_edges.insert(edge->m_edge_index); local_edges.insert(edge->m_edge_index); } } } // For each collected partial/skipped edge, add any faces not already // found to the queue. for (le_it = local_edges.begin(); le_it != local_edges.end(); le_it++) { const ON_BrepEdge *edge = &(data->brep->m_E[*le_it]); for (int j = 0; j < edge->TrimCount(); j++) { ON_BrepTrim *trim = edge->Trim(j); if (found_faces.find(trim->Face()->m_face_index) == found_faces.end()) { found_faces.insert(trim->Face()->m_face_index); connected_faces.push(trim->Face()->m_face_index); } } } } // Build two bounding boxes - one with the new verts in planar_obj, and the other with // the edges found above. ON_BoundingBox pbb, ebb; ON_MinMaxInit(&pbb.m_min, &pbb.m_max); ON_MinMaxInit(&ebb.m_min, &ebb.m_max); for (int i = 0; i < data->planar_obj->local_brep->m_V.Count(); i++) { const ON_BrepVertex *v = &(data->planar_obj->local_brep->m_V[i]); pbb.Set(v->Point(), true); } for (re_it = relevant_edges.begin(); re_it != relevant_edges.end(); re_it++) { const ON_BrepEdge *e = &(data->brep->m_E[*re_it]); ON_BoundingBox cbb = e->EdgeCurveOf()->BoundingBox(); ebb.Set(cbb.m_min, true); ebb.Set(cbb.m_max, true); } //std::cout << "in pbb.s rpp " << pout(pbb.m_min) << " " << pout(pbb.m_max) << "\n"; //std::cout << "in ebb.s rpp " << pout(ebb.m_min) << " " << pout(ebb.m_max) << "\n"; if (ebb.Includes(pbb)) { bu_log("negative volume\n"); data->planar_obj->negative_shape = -1; } else { bu_log("positive volume\n"); data->planar_obj->negative_shape = 1; } data->planar_obj->params->bool_op = (data->planar_obj->negative_shape == -1) ? '-' : 'u'; } // Need to preserve the vertex map for this, since we're not done building up the brep map_to_array(&(data->planar_obj->planar_obj_vert_map), &(data->planar_obj->planar_obj_vert_cnt), &vertex_map); data->planar_obj->local_brep->SetTrimTypeFlags(true); }
// Criteria: // // 1. A linear edge associated with a planar face == 0 // 2. All linear edges associated with non-planar faces are part // of the same CSG shape AND all non-linear edges' non-planar faces // are also part of that same CSG shape = 1 // 3. If not 2, 0 int characterize_vert(struct subbrep_object_data *data, const ON_BrepVertex *v) { std::set<int> non_planar_faces; std::set<int>::iterator f_it; std::set<struct filter_obj *> fobjs; std::set<struct filter_obj *>::iterator fo_it; struct filter_obj *control = NULL; for (int i = 0; i < v->m_ei.Count(); i++) { std::set<int> efaces; const ON_BrepEdge *edge = &(data->brep->m_E[v->m_ei[i]]); ON_Curve *tc = edge->EdgeCurveOf()->Duplicate(); int is_linear = tc->IsLinear(); delete tc; // Get the faces associated with the edge for (int j = 0; j < edge->TrimCount(); j++) { ON_BrepTrim *trim = edge->Trim(j); efaces.insert(trim->Face()->m_face_index); } for(f_it = efaces.begin(); f_it != efaces.end(); f_it++) { struct filter_obj *new_obj; BU_GET(new_obj, struct filter_obj); surface_t stype = GetSurfaceType(data->brep->m_F[*f_it].SurfaceOf(), new_obj); if (stype == SURFACE_PLANE) { filter_obj_free(new_obj); BU_PUT(new_obj, struct filter_obj); if (is_linear) { for (fo_it = fobjs.begin(); fo_it != fobjs.end(); fo_it++) { struct filter_obj *obj = (struct filter_obj *)(*fo_it); filter_obj_free(obj); BU_PUT(obj, struct filter_obj); } return 0; } } else { if (!control) { control = new_obj; } else { fobjs.insert(new_obj); } } } } // Can this happen? if (fobjs.size() == 0) { if (control) { filter_obj_free(control); BU_PUT(control, struct filter_obj); } return 0; } int equal = 1; for(fo_it = fobjs.begin(); fo_it != fobjs.end(); fo_it++) { if (equal && !filter_objs_equal(*fo_it, control)) equal = 0; struct filter_obj *obj = (struct filter_obj *)(*fo_it); filter_obj_free(obj); BU_PUT(obj, struct filter_obj); } filter_obj_free(control); BU_PUT(control, struct filter_obj); return equal; }