diff --git a/.gitignore b/.gitignore index 974e6d8e8..c8fc035f9 100644 --- a/.gitignore +++ b/.gitignore @@ -56,5 +56,6 @@ Data* out_foam/ chm000* out.step +*.swp tools/MigrationSVN diff --git a/Cassiopee/Intersector/Intersector/PyTree.py b/Cassiopee/Intersector/Intersector/PyTree.py index 3b52a0663..e07e0c9a7 100644 --- a/Cassiopee/Intersector/Intersector/PyTree.py +++ b/Cassiopee/Intersector/Intersector/PyTree.py @@ -314,6 +314,8 @@ def checkAssemblyForlSolver(t, fullcheck=False, nb_comps=1): print('Boolean WARNING : quality might be not good enough for solver') #import sys; sys.exit() + if fullcheck == False: return + # VERIFICATION 4 : VOL MIN print("Check min cell volume ...") (vmin, cellid, zoneid) = checkCellsVolume(t) @@ -321,9 +323,6 @@ def checkAssemblyForlSolver(t, fullcheck=False, nb_comps=1): if vmin < VOLMIN_SOLVER: print('Boolean ERROR : too small cells detected : under solver threshold') - if fullcheck == False: - return - # print("Check cell volume extrema...") # res = statsSize(t, 1) # dMax = res[0] diff --git a/Cassiopee/XCore/XCore/AdaptMesh/AdaptMesh_AdaptGeom.cpp b/Cassiopee/XCore/XCore/AdaptMesh/AdaptMesh_AdaptGeom.cpp new file mode 100644 index 000000000..08eb133db --- /dev/null +++ b/Cassiopee/XCore/XCore/AdaptMesh/AdaptMesh_AdaptGeom.cpp @@ -0,0 +1,899 @@ +#include "Mesh.h" +#include "common/mem.h" +#include "Array.h" +#include "Point.h" +#include "BVH.h" +#include "Point.h" +#include "Skin.h" +#include "Vec.h" +#include "DynMesh.h" +#include + +// TODO(Imad): sorting routines + +void locate_points_in_skin +( + const Mesh *source, + const ArrayI *points, + const DynMesh *target, + const E_Int *skin, + const E_Int *indices, + const BVH_node *bvh, + PointFaces *ploc +) +{ + for (E_Int i = 0; i < points->count; i++) { + E_Int pid = points->ptr[i]; + const Point p = {source->X[pid], source->Y[pid], source->Z[pid]}; + PointFaces *pfaces = &ploc[i]; + //assert(Point_in_Box3D(&p, &bvh->box)); + BVH_locate_point(bvh, target, skin, indices, &p, pfaces); + /* + if (pfaces->count == 0) { + fprintf(stderr, "bvh_locate: failed at point index %d (%d)!\n", + i, spid); + point_write(&p); + abort(); + } + */ + } +} + +void locate_points_in_skin +( + const DynMesh *source, + const ArrayI *points, + const Mesh *target, + const E_Int *skin, + const E_Int *indices, + const BVH_node *bvh, + PointFaces *ploc +) +{ + for (E_Int i = 0; i < points->count; i++) { + E_Int pid = points->ptr[i]; + const Point p = {source->X[pid], source->Y[pid], source->Z[pid]}; + PointFaces *pfaces = &ploc[i]; + //assert(Point_in_Box3D(&p, &bvh->box)); + BVH_locate_point(bvh, target, skin, indices, &p, pfaces); + /* + if (pfaces->count == 0) { + fprintf(stderr, "bvh_locate: failed at point index %d (%d)!\n", + i, spid); + point_write(&p); + abort(); + } + */ + } +} + +void init_skin_refinement_cells(const SkinGraph *skin_graph, + const E_Int *fdat, Mesh *M, E_Int *rcount) +{ + E_Int nf = skin_graph->nf; + E_Int count = 0; + + for (E_Int i = 0; i < nf; i++) { + if (fdat[i] > 0) { + E_Int fid = skin_graph->skin[i]; + E_Int own = M->owner[fid]; + // If cref[own] != 0, H18 is impossible, refinement has to be H27. + assert(M->cref[own] == 0); + M->cref[own] = 1; + count++; + } + } + + *rcount = count; +} + +void Mesh_set_face_as_cell_bottom(Mesh *M, E_Int fid, E_Int cid) +{ + // Make fid the bottom face + E_Int *cell = Mesh_get_cell(M, cid); + E_Int size = 4*M->cstride[cid]; + E_Int pos = Get_pos(fid, cell, size); + assert(pos != -1); + assert(pos % 4 == 0); + Right_shift(cell, pos, size); + assert(cell[0] == fid); + E_Int *crange = Mesh_get_crange(M, cid); + Right_shift(crange, pos/4, M->cstride[cid]); +} + +void assign_face_refinement_data(Mesh *M) +{ + for (E_Int cid = 0; cid < M->nc; cid++) { + if (M->cref[cid] == 0) continue; + assert(M->cref[cid] == 1); + + E_Int *cell = Mesh_get_cell(M, cid); + E_Int *crange = Mesh_get_crange(M, cid); + E_Int cstride = M->cstride[cid]; + E_Int clvl = M->clevel[cid]; + + for (E_Int i = 0; i < cstride; i++) { + E_Int *pf = cell + 4*i; + + for (E_Int j = 0; j < crange[i]; j++) { + E_Int face = pf[j]; + if (M->fref[face] == 1) continue; + + E_Int flvl = M->flevel[face]; + + assert(flvl >= clvl); + + if (flvl == clvl) M->fref[face] = 1; + } + } + } +} + +// TODO(Imad): isotropic resizing for now + +void Mesh_isolate_refinement_entities(Mesh *M, ArrayI *rcells, ArrayI *rfaces) +{ + rcells->count = 0; + for (E_Int cid = 0; cid < M->nc; cid++) { + rcells->count += (M->cref[cid] == 1); + } + rcells->ptr = (E_Int *)XMALLOC(rcells->count * sizeof(E_Int)); + E_Int *ptr = rcells->ptr; + for (E_Int cid = 0; cid < M->nc; cid++) { + if (M->cref[cid] > 0) { + assert(M->cref[cid] == 1); + *ptr++ = cid; + } + } + + rfaces->count = 0; + for (E_Int fid = 0; fid < M->nf; fid++) { + rfaces->count += (M->fref[fid] == 1); + } + rfaces->ptr = (E_Int *)XMALLOC(rfaces->count * sizeof(E_Int)); + ptr = rfaces->ptr; + for (E_Int fid = 0; fid < M->nf; fid++) { + if (M->fref[fid] > 0) { + assert(M->fref[fid] == 1); + *ptr++ = fid; + } + } +} + +void Mesh_resize(Mesh *M, const ArrayI *rcells, const ArrayI *rfaces) +{ + // 7 new cells per refined cells + E_Int cell_incr = rcells->count * 7; + // 3 new faces per refined face + 12 new faces per refined cell + E_Int face_incr = rfaces->count * 3 + rcells->count * 12; + // Estimate point increase + E_Int point_incr = 2 * face_incr; + + E_Int new_nc = M->nc + cell_incr; + E_Int new_nf = M->nf + face_incr; + E_Int new_np = M->np + point_incr; + + M->X = (E_Float *)XRESIZE(M->X, new_np * sizeof(E_Float)); + M->Y = (E_Float *)XRESIZE(M->Y, new_np * sizeof(E_Float)); + M->Z = (E_Float *)XRESIZE(M->Z, new_np * sizeof(E_Float)); + + Mesh_resize_face_data(M, new_nf); + Mesh_resize_cell_data(M, new_nc); +} + +E_Int refine_quad_X(E_Int quad, Mesh *M) +{ + assert(M->fref[quad] == 1); + E_Int NODES[8]; + + E_Int *fpts = Mesh_get_face(M, quad); + E_Int *frange = Mesh_get_frange(M, quad); + + // BOT + NODES[0] = fpts[0]; + if (frange[0] == 2) { + NODES[4] = fpts[1]; + } else { + E_Int p = fpts[0]; + E_Int q = fpts[2]; + Mesh_refine_or_get_edge_center(M, p, q, NODES[4]); + } + + // RGT + NODES[1] = fpts[2]; + NODES[5] = fpts[3]; + + // TOP + NODES[2] = fpts[4]; + if (frange[2] == 2) { + NODES[6] = fpts[5]; + } else { + E_Int p = fpts[4]; + E_Int q = fpts[6]; + Mesh_refine_or_get_edge_center(M, p, q, NODES[6]); + } + + // LFT + NODES[3] = fpts[6]; + NODES[7] = fpts[7]; + + + // Second child + fpts = Mesh_get_face(M, M->nf); + fpts[0] = NODES[4]; + fpts[1] = -1; + fpts[2] = NODES[1]; + fpts[3] = NODES[5]; + fpts[4] = NODES[2]; + fpts[5] = -1; + fpts[6] = NODES[6]; + fpts[7] = -1; + + // First child replaces quad + fpts = Mesh_get_face(M, quad); + fpts[0] = NODES[0]; + fpts[1] = -1; + fpts[2] = NODES[4]; + fpts[3] = -1; + fpts[4] = NODES[6]; + fpts[5] = -1; + fpts[6] = NODES[3]; + fpts[7] = NODES[7]; + + // Update ranges and strides + Mesh_update_face_range_and_stride(M, quad, M->nf, 1); + + // Conformize parent cells + + E_Int own = M->owner[quad]; + + assert(M->clevel[own] == M->flevel[quad]); + + if (Mesh_conformize_cell_face(M, own, quad, M->nf, 2) != 0) return 1; + + E_Int nei = M->neigh[quad]; + + if (nei != -1) { + assert(M->clevel[nei] == M->flevel[quad]); + + if (Mesh_conformize_cell_face(M, nei, quad, M->nf, 2) != 0) return 1; + } + + for (E_Int i = 0; i < 1; i++) { + M->owner[M->nf+i] = own; + M->neigh[M->nf+i] = nei; + } + + // Update adaptation info + M->flevel[quad]++; + + assert(M->fref[quad] == FACE_REFINED); + + for (E_Int i = 0; i < 1; i++) { + E_Int fid = M->nf + i; + + M->flevel[fid] = M->flevel[quad]; + M->ftype[fid] = M->ftype[quad]; + + M->fref[fid] = FACE_NEW; + } + + M->fchildren[quad] = {quad, M->nf}; + + M->fparent[M->nf] = quad; + + M->ftag[M->nf] = M->ftag[quad]; + + M->fpattern[M->nf] = M->fpattern[quad]; + + // Increment face/edge/point count + M->nf += 1; + + return 0; +} + +E_Int refine_quad_Y(E_Int quad, Mesh *M) +{ + assert(M->fref[quad] == 1); + E_Int NODES[8]; + + E_Int *fpts = Mesh_get_face(M, quad); + E_Int *frange = Mesh_get_frange(M, quad); + + // BOT + NODES[0] = fpts[0]; + NODES[4] = fpts[1]; + + // RGT + NODES[1] = fpts[2]; + if (frange[1] == 2) { + NODES[5] = fpts[3]; + } else { + E_Int p = fpts[2]; + E_Int q = fpts[4]; + Mesh_refine_or_get_edge_center(M, p, q, NODES[5]); + } + + // TOP + NODES[2] = fpts[4]; + NODES[6] = fpts[5]; + + // LFT + NODES[3] = fpts[6]; + if (frange[3] == 2) { + NODES[7] = fpts[7]; + } else { + E_Int p = fpts[6]; + E_Int q = fpts[0]; + Mesh_refine_or_get_edge_center(M, p, q, NODES[7]); + } + + // Second child + fpts = Mesh_get_face(M, M->nf); + fpts[0] = NODES[7]; + fpts[1] = -1; + fpts[2] = NODES[5]; + fpts[3] = -1; + fpts[4] = NODES[2]; + fpts[5] = NODES[6]; + fpts[6] = NODES[3]; + fpts[7] = -1; + + // First child replaces quad + fpts = Mesh_get_face(M, quad); + fpts[0] = NODES[0]; + fpts[1] = NODES[4]; + fpts[2] = NODES[1]; + fpts[3] = -1; + fpts[4] = NODES[5]; + fpts[5] = -1; + fpts[6] = NODES[7]; + fpts[7] = -1; + + // Update ranges and strides + Mesh_update_face_range_and_stride(M, quad, M->nf, 1); + + // Conformize parent cells + + E_Int own = M->owner[quad]; + + assert(M->clevel[own] == M->flevel[quad]); + + if (Mesh_conformize_cell_face(M, own, quad, M->nf, 2) != 0) return 1; + + E_Int nei = M->neigh[quad]; + + if (nei != -1) { + assert(M->clevel[nei] == M->flevel[quad]); + + if (Mesh_conformize_cell_face(M, nei, quad, M->nf, 2) != 0) return 1; + } + + for (E_Int i = 0; i < 1; i++) { + M->owner[M->nf+i] = own; + M->neigh[M->nf+i] = nei; + } + + // Update adaptation info + M->flevel[quad]++; + + assert(M->fref[quad] == FACE_REFINED); + + for (E_Int i = 0; i < 1; i++) { + E_Int fid = M->nf + i; + + M->flevel[fid] = M->flevel[quad]; + M->ftype[fid] = M->ftype[quad]; + + M->fref[fid] = FACE_NEW; + } + + M->fchildren[quad] = {quad, M->nf}; + + M->fparent[M->nf] = quad; + + M->ftag[M->nf] = M->ftag[quad]; + + M->fpattern[M->nf] = M->fpattern[quad]; + + // Increment face/edge/point count + M->nf += 1; + + return 0; +} + +E_Int refine_quad_dir(E_Int fid, Mesh *M) +{ + if (M->fpattern[fid] == DIR_X) return refine_quad_X(fid, M); + if (M->fpattern[fid] == DIR_Y) return refine_quad_Y(fid, M); + return Q9_refine(fid, M); +} + +void Mesh_refine_dir(Mesh *M, ArrayI *ref_cells, ArrayI *ref_faces) +{ + std::set levelset; + + for (E_Int i = 0; i < ref_cells->count; i++) { + levelset.insert(M->clevel[ref_cells->ptr[i]]); + } + + for (E_Int i = 0; i < ref_faces->count; i++) { + levelset.insert(M->flevel[ref_faces->ptr[i]]); + } + + std::vector levels; + for (E_Int level : levelset) levels.push_back(level); + std::sort(levels.begin(), levels.end()); + + //std::reverse(ref_cells->ptr, ref_cells->ptr + ref_cells->count); + //std::reverse(ref_faces->ptr, ref_faces->ptr + ref_faces->count); + + E_Int cells_left = ref_cells->count-1; + E_Int faces_left = ref_faces->count-1; + + for (E_Int level : levels) { + while (faces_left >= 0 && M->flevel[ref_faces->ptr[faces_left]] == level) { + E_Int face = ref_faces->ptr[faces_left]; + faces_left--; + assert(M->fpattern[face] != -1); + refine_quad_dir(face, M); + } + + while (cells_left >= 0 && M->clevel[ref_cells->ptr[cells_left]] == level) { + E_Int cell = ref_cells->ptr[cells_left]; + cells_left--; + refine_cell_dir(cell, M); + } + } +} + +#include "common/Karray.h" + +PyObject *K_XCORE::AdaptMesh_AdaptGeom(PyObject *self, PyObject *args) +{ + PyObject *AMESH, *SLAVE, *TAGGED_FACES; + + if (!PYPARSETUPLE_(args, OOO_, &AMESH, &SLAVE, &TAGGED_FACES)) { + RAISE("Wrong input."); + return NULL; + } + + if (!PyCapsule_IsValid(AMESH, "AdaptMesh")) { + RAISE("Bad first AdaptMesh hook."); + return NULL; + } + + Mesh *M = (Mesh *)PyCapsule_GetPointer(AMESH, "AdaptMesh"); + + if (M->npc > 1) { + RAISE("AdaptGeom is sequential."); + return NULL; + } + + Karray sarray; + + E_Int ret; + + ret = Karray_parse_ngon(SLAVE, sarray); + + if (ret != 0) { + RAISE("Bad slave mesh."); + return NULL; + } + + puts("Preparing meshes for intersection..."); + + // Check slave point tags + E_Int *tagged_faces = NULL; + E_Int tag_size = -1; + ret = K_NUMPY::getFromNumpyArray(TAGGED_FACES, tagged_faces, tag_size, true); + if (ret != 1) { + Karray_free_ngon(sarray); + RAISE("Bad slave points tag."); + return NULL; + } + + // Init slave mesh + DynMesh S(&sarray); + S.orient_skin(OUT); + for (E_Int i = 0; i < tag_size; i++) { + S.ftag[tagged_faces[i]] = 1; + } + + S.triangulate(tagged_faces, tag_size); + + //S = S.extract_conformized(); + //return S.export_karray(); + + // Refine M volumetric wrt to S tagged faces point cloud + // Refine S surfacic wrt to M tagged faces point cloud + + E_Int iter = 0; + E_Int max_iter = 10; + E_Int ref_count_M, ref_count_S; + + do { + iter++; + + /************************** M refinement **************************/ + + printf("\niter: %d\n", iter); + + // Extract spoints from tagged sfaces + ArrayI spoints; + S.extract_points_from_ftag(&spoints); + + // We need the skin connectivity graph + SkinGraph skin_graph = {0}; + Mesh_make_skin_graph(M, &skin_graph); + printf("Skin: %d faces\n", skin_graph.nf); + + // BVH the skin + Vec3f *skin_fc = (Vec3f *)XMALLOC(skin_graph.nf * sizeof(Vec3f)); + Mesh_make_face_centers(M, skin_graph.nf, skin_graph.skin, skin_fc); + E_Int *indices = (E_Int *)XMALLOC(skin_graph.nf * sizeof(E_Int)); + for (E_Int i = 0; i < skin_graph.nf; i++) indices[i] = i; + const Box3 huge = {-FLT_MAX, -FLT_MAX, -FLT_MAX, + FLT_MAX, FLT_MAX, FLT_MAX}; + BVH_node *bvh = BVH_make(M, skin_graph.skin, skin_fc, indices, 0, + skin_graph.nf, &huge); + puts("BVH constructed"); + + + // Locate spoints in skin + PointFaces *sploc = + (PointFaces *)XMALLOC(spoints.count * sizeof(PointFaces)); + memset(sploc, 0, spoints.count * sizeof(PointFaces)); + locate_points_in_skin + ( + &S, &spoints, + M, skin_graph.skin, indices, + bvh, + sploc + ); + puts("Points located"); + + if (iter == 1) { + // We need the skin face normals + Mesh_SkinGraph_compute_normals(M, &skin_graph); + + E_Int *xadj = skin_graph.xadj; + E_Int *fnei = skin_graph.fnei; + Vec3f *fnml = skin_graph.fnml; + + // Traverse the skin in breadth-first fashion + bool *visited = (bool *)XMALLOC(skin_graph.nf * sizeof(bool)); + memset(visited, 0, skin_graph.nf * sizeof(bool)); + + E_Int *fqueue = (E_Int *)XMALLOC(skin_graph.nf * sizeof(E_Int)); + memset(fqueue, -1, skin_graph.nf * sizeof(E_Int)); + + // Start from the first mface + E_Int fseed = indices[sploc[0].ptr[0]]; + + E_Int front = 0, rear = 0; + fqueue[rear++] = fseed; + + visited[fseed] = true; + E_Int nvisited = 1; + + while (front != rear) { + E_Int fid = fqueue[front++]; + + E_Int start = xadj[fid]; + E_Int end = xadj[fid+1]; + E_Int stride = end - start; + E_Int *pn = &fnei[start]; + + Vec3f *fid_nml = &fnml[fid]; + + for (E_Int i = 0; i < stride; i++) { + E_Int nei = pn[i]; + if (visited[nei]) continue; + + Vec3f *nei_nml = &fnml[nei]; + E_Float dp = K_MATH::dot((E_Float *)fid_nml, (E_Float *)nei_nml, 3); + //assert(dp >= 0.0); + dp = std::max(dp, -1.0); + dp = std::min(dp, 1.0); + E_Float theta = acos(dp) * 180.0 / K_MATH::PI; + + if (theta < 60.0 || theta > 120.0) { + visited[nei] = true; + fqueue[rear++] = nei; + nvisited++; + } + } + } + + // Set the base patch + E_Int cvisit = 0; + for (E_Int i = 0; i < skin_graph.nf; i++) { + if (visited[i] == 0) continue; + + // Tag this face for later + assert(M->ftag[skin_graph.skin[i]] == 0); + M->ftag[skin_graph.skin[i]] = 1; + + E_Int fid = skin_graph.skin[i]; + E_Int cid = M->owner[fid]; + + while (cid != -1) { + cvisit++; + Mesh_set_face_as_cell_bottom(M, fid, cid); + H18_reorder(cid, M); + assert(check_canon_hexa(cid, M) == 0); + + E_Int *cell = Mesh_get_cell(M, cid); + + E_Int map[4]; + E_Int *face = Mesh_get_face(M, fid); + for (E_Int j = 0; j < 4; j++) map[j] = face[2*j]; + E_Int reorient = Mesh_get_reorient(M, fid, cid, normalIn_H[0]); + if (reorient) std::swap(map[1], map[3]); + + for (E_Int j = 0; j < 6; j++) { + fid = cell[4*j]; + + if (M->fpattern[fid] != -1) continue; + + if (j == 0 || j == 1) { + M->fpattern[fid] = DIR_ISO; + } else { + + E_Int i0; + reorient = Mesh_get_reorient(M, fid, cid, normalIn_H[j]); + face = Mesh_get_face(M, fid); + + M->fpattern[fid] = DIR_X; + + if (j == 2) { + i0 = Get_pos(map[0], face, 8); + } else if (j == 3) { + i0 = Get_pos(map[1], face, 8); + } else if (j == 4) { + i0 = Get_pos(map[1], face, 8); + } else if (j == 5) { + i0 = Get_pos(map[2], face, 8); + } + + assert(i0 != -1); + i0 /= 2; + + if ((reorient == 0 && (i0 == 1 || i0 == 3)) || + (reorient == 1 && (i0 == 0 || i0 == 2))) { + M->fpattern[fid] = DIR_Y; + } + } + } + + // Step to top adjacent cell + fid = cell[4]; + cid = Mesh_get_cnei(M, cid, fid); + } + } + + assert(cvisit == M->nc); + + XFREE(visited); + XFREE(fqueue); + } + + // Isolate faces that contain more than MAX_POINTS_PER_FACE spoints + ArrayI rfaces; + PointFaces_extract_by_threshold + ( + sploc, spoints.count, + skin_graph.skin, skin_graph.nf, + 1, // threshold + &rfaces + ); + puts("Refinement faces isolated"); + printf("Refinement faces: %d\n", rfaces.count); + ref_count_M = rfaces.count; + + if (rfaces.count > 0) { + + // Smooth face refinement data + E_Int *fdat = (E_Int *)XMALLOC(skin_graph.nf * sizeof(E_Int)); + memset(fdat, 0, skin_graph.nf * sizeof(E_Int)); + for (E_Int i = 0; i < rfaces.count; i++) { + E_Int idx_in_skin = indices[rfaces.ptr[i]]; + fdat[idx_in_skin] = 1; + } + SkinGraph_smooth_ref_data(&skin_graph, fdat, M); + puts("Face refinement smoothed out"); + + E_Int smooth_nfref = 0; + for (E_Int i = 0; i < skin_graph.nf; i++) { + if (fdat[i] > 0) smooth_nfref++; + } + printf("Smooth refinement face count: %d\n", smooth_nfref); + + // Allocate + M->cref = (E_Int *)XRESIZE(M->cref, M->nc * sizeof(E_Int)); + memset(M->cref, 0, M->nc * sizeof(E_Int)); + + // Cells + E_Int ref_cell_count; + init_skin_refinement_cells(&skin_graph, fdat, M, &ref_cell_count); + printf("Refinement cells: %d\n", ref_cell_count); + if (ref_cell_count == 0) break; + + // Smooth cell refinement data + Mesh_smooth_cell_refinement_data(M); + puts("Cell refinement smoothed out"); + + // Assign refinement data + M->fref = (E_Int *)XRESIZE(M->fref, M->nf * sizeof(E_Int)); + memset(M->fref, 0, M->nf * sizeof(E_Int)); + assign_face_refinement_data(M); + + // Isolate cells/faces to be refined + ArrayI ref_cells, ref_faces; + Mesh_isolate_refinement_entities(M, &ref_cells, &ref_faces); + printf("Refinement cells: %d\n", ref_cells.count); + printf("Refinement faces: %d\n", ref_faces.count); + + // Resize for refinement + Mesh_resize(M, &ref_cells, &ref_faces); + puts("Mesh resized for refinement"); + + // Sort entities by refinement level + std::sort(ref_cells.ptr, ref_cells.ptr + ref_cells.count, + [&] (E_Int i, E_Int j) { return M->clevel[i] > M->clevel[j]; }); + puts("Refinement cells sorted"); + std::sort(ref_faces.ptr, ref_faces.ptr + ref_faces.count, + [&] (E_Int i, E_Int j) { return M->flevel[i] > M->flevel[j]; }); + puts("Refinement faces sorted"); + + // Refine + printf("Cells before refinement: %d\n", M->nc); + printf("Faces before refinement: %d\n", M->nf); + Mesh_refine_dir(M, &ref_cells, &ref_faces); + + printf("Cells after refinement: %d\n", M->nc); + printf("Faces after refinement: %d\n", M->nf); + + Mesh_conformize_face_edge(M); + + ArrayI_free(&ref_cells); + ArrayI_free(&ref_faces); + XFREE(fdat); + } + + BVH_free(bvh); + ArrayI_free(&rfaces); + ArrayI_free(&spoints); + + XFREE(sploc); + XFREE(indices); + XFREE(skin_fc); + SkinGraph_free(&skin_graph); + + /************************** S refinement **************************/ + + // Extract mpoints from tagged mfaces + ArrayI mpoints; + Mesh_extract_points_from_ftag(M, &mpoints); + printf("mpoints: %d\n", mpoints.count); + + /*if (iter == 10) + { + std::vector points; + for (E_Int i = 0; i < mpoints.count; i++) { + E_Int p = mpoints.ptr[i]; + Point point = {M->X[p], M->Y[p], M->Z[p]}; + points.push_back(point); + } + points_write("mpoints", points); + } + */ + + // We need the skin connectivity graph + S.make_tri_graph(); + printf("Skin: %lu faces\n", S.tri_graph.nf); + + // BVH the skin + skin_fc = (Vec3f *)XCALLOC(S.tri_graph.nf, sizeof(Vec3f)); + S.make_face_centers(S.tri_graph.nf, S.tri_graph.skin.data(), skin_fc); + indices = (E_Int *)XMALLOC(S.tri_graph.nf * sizeof(E_Int)); + for (size_t i = 0; i < S.tri_graph.nf; i++) indices[i] = i; + bvh = BVH_make(&S, S.tri_graph.skin.data(), skin_fc, indices, 0, + S.tri_graph.nf, &huge); + puts("BVH constructed"); + + // Locate mpoints in skin + PointFaces *mploc = + (PointFaces *)XCALLOC(mpoints.count, sizeof(PointFaces)); + locate_points_in_skin + ( + M, &mpoints, + &S, S.tri_graph.skin.data(), indices, + bvh, + mploc + ); + puts("Points located"); + + // Isolate faces that contain more than MAX_POINTS_PER_FACE mpoints + PointFaces_extract_by_threshold + ( + mploc, mpoints.count, + S.tri_graph.skin.data(), S.tri_graph.nf, + 1, // threshold + &rfaces + ); + puts("Refinement faces isolated"); + printf("Refinement faces: %d\n", rfaces.count); + ref_count_S = rfaces.count; + + /* + if (iter == 2) + { + npy_intp dims[2]; + + // faces array + dims[1] = 1; + dims[0] = (npy_intp)rfaces.count; + + PyArrayObject *FACES = (PyArrayObject *)PyArray_SimpleNew(1, dims, E_NPY_INT); + E_Int *pf = (E_Int *)PyArray_DATA(FACES); + E_Int *ptr = pf; + + for (E_Int j = 0; j < rfaces.count; j++) { + *ptr++ = skin_graph.skin[indices[rfaces.ptr[j]]]+1; + } + + return (PyObject *)FACES; + } + */ + + if (rfaces.count > 0) { + + // Smooth face refinement data + S.tri_graph.fdat.clear(); + S.tri_graph.fdat.resize(S.tri_graph.nf, 0); + for (E_Int i = 0; i < rfaces.count; i++) { + E_Int idx_in_skin = indices[rfaces.ptr[i]]; + S.tri_graph.fdat[idx_in_skin] = 1; + } + + // Isolate refinement faces + ArrayI ref_faces; + S.prepare_for_refinement(&ref_faces); + printf("Refinement faces: %d\n", ref_faces.count); + + // Refine + S.refine_faces(&ref_faces); + + // Make sure to copy ftag data + S = S.extract_conformized(); + + return S.export_karray(); + + ArrayI_free(&ref_faces); + } + + // FREE + S.tri_graph.clear(); + S.fchildren.clear(); + S.ecenter.clear(); + BVH_free(bvh); + ArrayI_free(&rfaces); + ArrayI_free(&mpoints); + XFREE(mploc); + XFREE(indices); + XFREE(skin_fc); + SkinGraph_free(&skin_graph); + + } while (iter < max_iter && (ref_count_M > 0 || ref_count_S > 0)); + + + PyObject *Sout = S.export_karray(); + + Karray_free_ngon(sarray); + + return Sout; + + return Py_None; +} diff --git a/Cassiopee/XCore/XCore/AdaptMesh/AdaptMesh_Exit.cpp b/Cassiopee/XCore/XCore/AdaptMesh/AdaptMesh_Exit.cpp index d6f804cfd..92f826fa4 100644 --- a/Cassiopee/XCore/XCore/AdaptMesh/AdaptMesh_Exit.cpp +++ b/Cassiopee/XCore/XCore/AdaptMesh/AdaptMesh_Exit.cpp @@ -45,7 +45,10 @@ PyObject *K_XCORE::AdaptMesh_Exit(PyObject *self, PyObject *args) Mesh_reset_parallel_data(M); + Mesh_reset_tags(M); + XFREE(M->reqs); + delete [] M->mode_2D; delete M; diff --git a/Cassiopee/XCore/XCore/AdaptMesh/AdaptMesh_GeneratePrisms.cpp b/Cassiopee/XCore/XCore/AdaptMesh/AdaptMesh_GeneratePrisms.cpp new file mode 100644 index 000000000..215c89247 --- /dev/null +++ b/Cassiopee/XCore/XCore/AdaptMesh/AdaptMesh_GeneratePrisms.cpp @@ -0,0 +1,29 @@ +#include "Mesh.h" + +PyObject *K_XCORE::AdaptMesh_GeneratePrisms(PyObject *self, PyObject *args) +{ + PyObject *AMESH, *FACES; + if (!PYPARSETUPLE_(args, OO_, &AMESH, &FACES)) { + RAISE("Bad input."); + return NULL; + } + + if (!PyCapsule_IsValid(AMESH, "AdaptMesh")) { + RAISE("Bad AdaptMesh hook."); + return NULL; + } + + E_Int *faces; + E_Int nf; + E_Int ret = K_NUMPY::getFromNumpyArray(FACES, faces, nf, true); + if (ret != 1) { + RAISE("Bad face list."); + return NULL; + } + + Mesh *M = (Mesh *)PyCapsule_GetPointer(AMESH, "AdaptMesh"); + + Mesh_generate_prisms(M, faces, nf); + + return Py_None; +} diff --git a/Cassiopee/XCore/XCore/AdaptMesh/AdaptMesh_Init.cpp b/Cassiopee/XCore/XCore/AdaptMesh/AdaptMesh_Init.cpp index cfba3d336..18bccda7e 100644 --- a/Cassiopee/XCore/XCore/AdaptMesh/AdaptMesh_Init.cpp +++ b/Cassiopee/XCore/XCore/AdaptMesh/AdaptMesh_Init.cpp @@ -209,9 +209,7 @@ PyObject *K_XCORE::AdaptMesh_Init(PyObject *self, PyObject *args) Mesh_free(M); return NULL; } - } - if (M->mode_2D) { ret = Mesh_set_cells_for_2D(M); if (ret != 0) { RAISE("Failed to set cells for 2D."); @@ -237,8 +235,14 @@ PyObject *K_XCORE::AdaptMesh_Init(PyObject *self, PyObject *args) M->fparent = IntArray(M->nf); for (E_Int i = 0; i < M->nf; i++) M->fparent[i] = i; - M->cref = IntArray(M->nc); - M->fref = IntArray(M->nf); + M->cref = NULL; + M->fref = NULL; + M->fpattern = (E_Int *)XMALLOC(M->nf * sizeof(E_Int)); + memset(M->fpattern, -1, M->nf * sizeof(E_Int)); + + M->ctag = IntArray(M->nc); + M->ftag = IntArray(M->nf); + M->ptag = IntArray(M->np); // Clean-up diff --git a/Cassiopee/XCore/XCore/AdaptMesh/AdaptMesh_TagFaces.cpp b/Cassiopee/XCore/XCore/AdaptMesh/AdaptMesh_TagFaces.cpp new file mode 100644 index 000000000..3e796a8da --- /dev/null +++ b/Cassiopee/XCore/XCore/AdaptMesh/AdaptMesh_TagFaces.cpp @@ -0,0 +1,73 @@ +#include "Mesh.h" + +PyObject *K_XCORE::AdaptMesh_ExtractTaggedFaces(PyObject *self, PyObject *args) +{ + PyObject *AMESH; + if (!PYPARSETUPLE_(args, O_, &AMESH)) { + RAISE("Bad input."); + return NULL; + } + + if (!PyCapsule_IsValid(AMESH, "AdaptMesh")) { + RAISE("Bad AdaptMesh hook."); + return NULL; + } + + Mesh *M = (Mesh *)PyCapsule_GetPointer(AMESH, "AdaptMesh"); + + assert(M->ftag); + + E_Int count = 0; + + for (E_Int i = 0; i < M->nf; i++) { + count += (M->ftag[i] == 1); + } + + npy_intp dims[2]; + dims[1] = 1; + dims[0] = (npy_intp)count; + + PyArrayObject *out = (PyArrayObject *)PyArray_SimpleNew(1, dims, E_NPY_INT); + E_Int *pf = (E_Int *)PyArray_DATA(out); + E_Int *ptr = pf; + + for (E_Int fid = 0; fid < M->nf; fid++) { + if (M->ftag[fid] == 1) + *ptr++ = fid; + } + + return (PyObject *)out; +} + +PyObject *K_XCORE::AdaptMesh_TagFaces(PyObject *self, PyObject *args) +{ + PyObject *AMESH, *FACES; + if (!PYPARSETUPLE_(args, OO_, &AMESH, &FACES)) { + RAISE("Bad input."); + return NULL; + } + + if (!PyCapsule_IsValid(AMESH, "AdaptMesh")) { + RAISE("Bad AdaptMesh hook."); + return NULL; + } + + E_Int *faces; + E_Int NF; + E_Int ret = K_NUMPY::getFromNumpyArray(FACES, faces, NF, true); + if (ret != 1) { + RAISE("Bad face list."); + return NULL; + } + + Mesh *M = (Mesh *)PyCapsule_GetPointer(AMESH, "AdaptMesh"); + + assert(M->ftag); + memset(M->ftag, 0, M->nf * sizeof(E_Int)); + + for (E_Int i = 0; i < NF; i++) { + M->ftag[faces[i]] = 1; + } + + return Py_None; +} diff --git a/Cassiopee/XCore/XCore/AdaptMesh/AdaptMesh_TriangulateFaces.cpp b/Cassiopee/XCore/XCore/AdaptMesh/AdaptMesh_TriangulateFaces.cpp new file mode 100644 index 000000000..6f660ca6a --- /dev/null +++ b/Cassiopee/XCore/XCore/AdaptMesh/AdaptMesh_TriangulateFaces.cpp @@ -0,0 +1,30 @@ +#include "Mesh.h" + +PyObject *K_XCORE::AdaptMesh_TriangulateFaces(PyObject *self, PyObject *args) +{ + PyObject *AMESH, *FACES; + + if (!PYPARSETUPLE_(args, OO_, &AMESH, &FACES)) { + RAISE("Wrong input."); + return NULL; + } + + if (!PyCapsule_IsValid(AMESH, "AdaptMesh")) { + RAISE("Bad mesh hook."); + return NULL; + } + + E_Int *faces; + E_Int NF; + E_Int ret = K_NUMPY::getFromNumpyArray(FACES, faces, NF, true); + if (ret != 1) { + RAISE("Bad face list."); + return NULL; + } + + Mesh *M = (Mesh *)PyCapsule_GetPointer(AMESH, "AdaptMesh"); + + Mesh_triangulate_faces(M, faces, NF); + + return Py_None; +} diff --git a/Cassiopee/XCore/XCore/AdaptMesh/Array.cpp b/Cassiopee/XCore/XCore/AdaptMesh/Array.cpp new file mode 100644 index 000000000..a5e0f0889 --- /dev/null +++ b/Cassiopee/XCore/XCore/AdaptMesh/Array.cpp @@ -0,0 +1,14 @@ +#include "Array.h" +#include "common/mem.h" + +void ArrayI_free(ArrayI *arr) +{ + arr->count = 0; + XFREE(arr->ptr); +} + +void ArrayI_alloc(ArrayI *arr, E_Int nelem) +{ + arr->count = nelem; + arr->ptr = (E_Int *)XMALLOC(nelem * sizeof(E_Int)); +} \ No newline at end of file diff --git a/Cassiopee/XCore/XCore/AdaptMesh/Array.h b/Cassiopee/XCore/XCore/AdaptMesh/Array.h new file mode 100644 index 000000000..cd73faf5b --- /dev/null +++ b/Cassiopee/XCore/XCore/AdaptMesh/Array.h @@ -0,0 +1,12 @@ +#pragma once + +#include "xcore.h" + +struct ArrayI { + E_Int count; + E_Int *ptr; +}; + +void ArrayI_free(ArrayI *arr); + +void ArrayI_alloc(ArrayI *arr, E_Int nelem); \ No newline at end of file diff --git a/Cassiopee/XCore/XCore/AdaptMesh/BVH.cpp b/Cassiopee/XCore/XCore/AdaptMesh/BVH.cpp new file mode 100644 index 000000000..0bbb99021 --- /dev/null +++ b/Cassiopee/XCore/XCore/AdaptMesh/BVH.cpp @@ -0,0 +1,290 @@ +#include "BVH.h" +#include "Mesh.h" +#include "FaceSort.h" +#include "Point.h" +#include "common/mem.h" +#include "Vec.h" +#include "DynMesh.h" + +#define MAX_FACES_PER_BVH_LEAF 10 + +BVH_node *BVH_create_node(const Box3 *box, E_Int start, E_Int end, + BVH_node *left, BVH_node *right) +{ + BVH_node *node = (BVH_node *)XMALLOC(sizeof(BVH_node)); + node->box = *box; + node->start = start; + node->end = end; + node->left = left; + node->right = right; + //printf("%d -> %f %f %f %f %f %f\n", idx, box->xmin, box->ymin, box->zmin, + // box->xmax, box->ymax, box->zmax); + //idx++; + return node; +} + +BVH_node *BVH_make(const Mesh *M, FaceSort *mfaces, E_Int start, E_Int end, + const Box3 *parent_box) +{ + Box3 box = Box3_make(M, mfaces, start, end); + Box3_clamp(parent_box, &box); + assert(Box3_in_Box3(box, *parent_box)); + + E_Int count = end - start; + if (count <= MAX_FACES_PER_BVH_LEAF) { + return BVH_create_node(&box, start, end, NULL, NULL); + } + + E_Float dx = box.xmax - box.xmin; + E_Float dy = box.ymax - box.ymin; + E_Float dz = box.zmax - box.zmin; + + E_Int dim = -1; + + if (dx >= dy && dx >= dz) { + dim = 0; + } else if (dy >= dz) { + dim = 1; + } else { + dim = 2; + } + + std::sort(mfaces + start, mfaces + end, + [&](const FaceSort &fi, const FaceSort &fj) + { + return fi.fc[dim] < fj.fc[dim]; + }); + + E_Int mid = start + count/2; + + BVH_node *left = BVH_make(M, mfaces, start, mid, &box); + BVH_node *right = BVH_make(M, mfaces, mid, end, &box); + + assert(Box3_in_Box3(left->box, box)); + assert(Box3_in_Box3(right->box, box)); + + return BVH_create_node(&box, start, end, left, right); +} + +BVH_node *BVH_make(const Mesh *M, const E_Int *skin, const Vec3f *fc, + E_Int *indices, E_Int start, E_Int end, const Box3 *parent_box) +{ + Box3 box = Box3_make(M, skin, indices, start, end); + Box3_clamp(parent_box, &box); + assert(Box3_in_Box3(box, *parent_box)); + + E_Int count = end - start; + if (count <= MAX_FACES_PER_BVH_LEAF) { + return BVH_create_node(&box, start, end, NULL, NULL); + } + + E_Float dx = box.xmax - box.xmin; + E_Float dy = box.ymax - box.ymin; + E_Float dz = box.zmax - box.zmin; + + E_Int dim = -1; + + if (dx >= dy && dx >= dz) { + dim = 0; + } else if (dy >= dz) { + dim = 1; + } else { + dim = 2; + } + + std::sort(indices + start, indices + end, + [&](const E_Int i, const E_Int j) + { + E_Float *fci = (E_Float *)(&fc[i]); + E_Float *fcj = (E_Float *)(&fc[j]); + return fci[dim] < fcj[dim]; + }); + + E_Int mid = start + count/2; + + BVH_node *left = BVH_make(M, skin, fc, indices, start, mid, &box); + BVH_node *right = BVH_make(M, skin, fc, indices, mid, end, &box); + + assert(Box3_in_Box3(left->box, box)); + assert(Box3_in_Box3(right->box, box)); + + return BVH_create_node(&box, start, end, left, right); +} + +void BVH_locate_point +( + const BVH_node *node, + const Mesh *M, + const E_Int *skin, + const E_Int *indices, + const Point *p, + PointFaces *pfaces +) +{ + if (node->left == NULL && node->right == NULL) { + for (E_Int i = node->start; i < node->end; i++) { + E_Int fid = skin[indices[i]]; + + if (Mesh_point_in_face(M, p, fid)) { + if (pfaces->count >= MAX_FACES_PER_POINT) { + fprintf(stderr, + "bvh_locate: MAX_FACES_PER_POINT exceeded!\n"); + abort(); + } + pfaces->ptr[pfaces->count++] = i; + } + } + return; + } + + assert(node->left && node->right); + assert(Box3_in_Box3(node->left->box, node->box)); + assert(Box3_in_Box3(node->right->box, node->box)); + + bool in_box = Point_in_Box3D(p, &node->box); + + if (!in_box) + return; + + BVH_locate_point(node->left, M, skin, indices, p, pfaces); + BVH_locate_point(node->right, M, skin, indices, p, pfaces); +} + +void BVH_free(BVH_node *node) +{ + if (node == NULL) return; + + BVH_free(node->left); + BVH_free(node->right); + + XFREE(node); +}; + +/* DynMesh */ + +BVH_node *BVH_make(const DynMesh *M, FaceSort *mfaces, E_Int start, E_Int end, + const Box3 *parent_box) +{ + Box3 box = Box3_make(M, mfaces, start, end); + Box3_clamp(parent_box, &box); + assert(Box3_in_Box3(box, *parent_box)); + + E_Int count = end - start; + if (count <= MAX_FACES_PER_BVH_LEAF) { + return BVH_create_node(&box, start, end, NULL, NULL); + } + + E_Float dx = box.xmax - box.xmin; + E_Float dy = box.ymax - box.ymin; + E_Float dz = box.zmax - box.zmin; + + E_Int dim = -1; + + if (dx >= dy && dx >= dz) { + dim = 0; + } else if (dy >= dz) { + dim = 1; + } else { + dim = 2; + } + + std::sort(mfaces + start, mfaces + end, + [&](const FaceSort &fi, const FaceSort &fj) + { + return fi.fc[dim] < fj.fc[dim]; + }); + + E_Int mid = start + count/2; + + BVH_node *left = BVH_make(M, mfaces, start, mid, &box); + BVH_node *right = BVH_make(M, mfaces, mid, end, &box); + + assert(Box3_in_Box3(left->box, box)); + assert(Box3_in_Box3(right->box, box)); + + return BVH_create_node(&box, start, end, left, right); +} + +BVH_node *BVH_make(const DynMesh *M, const E_Int *skin, const Vec3f *fc, + E_Int *indices, E_Int start, E_Int end, const Box3 *parent_box) +{ + Box3 box = Box3_make(M, skin, indices, start, end); + Box3_clamp(parent_box, &box); + assert(Box3_in_Box3(box, *parent_box)); + + E_Int count = end - start; + if (count <= MAX_FACES_PER_BVH_LEAF) { + return BVH_create_node(&box, start, end, NULL, NULL); + } + + E_Float dx = box.xmax - box.xmin; + E_Float dy = box.ymax - box.ymin; + E_Float dz = box.zmax - box.zmin; + + E_Int dim = -1; + + if (dx >= dy && dx >= dz) { + dim = 0; + } else if (dy >= dz) { + dim = 1; + } else { + dim = 2; + } + + std::sort(indices + start, indices + end, + [&](const E_Int i, const E_Int j) + { + E_Float *fci = (E_Float *)(&fc[i]); + E_Float *fcj = (E_Float *)(&fc[j]); + return fci[dim] < fcj[dim]; + }); + + E_Int mid = start + count/2; + + BVH_node *left = BVH_make(M, skin, fc, indices, start, mid, &box); + BVH_node *right = BVH_make(M, skin, fc, indices, mid, end, &box); + + assert(Box3_in_Box3(left->box, box)); + assert(Box3_in_Box3(right->box, box)); + + return BVH_create_node(&box, start, end, left, right); +} + +void BVH_locate_point +( + const BVH_node *node, + const DynMesh *M, + const E_Int *skin, + const E_Int *indices, + const Point *p, + PointFaces *pfaces +) +{ + if (node->left == NULL && node->right == NULL) { + for (E_Int i = node->start; i < node->end; i++) { + E_Int fid = skin[indices[i]]; + + if (M->point_in_face(p, fid)) { + if (pfaces->count >= MAX_FACES_PER_POINT) { + fprintf(stderr, + "bvh_locate: MAX_FACES_PER_POINT exceeded!\n"); + abort(); + } + pfaces->ptr[pfaces->count++] = i; + } + } + return; + } + + assert(node->left && node->right); + assert(Box3_in_Box3(node->left->box, node->box)); + assert(Box3_in_Box3(node->right->box, node->box)); + + bool in_box = Point_in_Box3D(p, &node->box); + + if (!in_box) + return; + + BVH_locate_point(node->left, M, skin, indices, p, pfaces); + BVH_locate_point(node->right, M, skin, indices, p, pfaces); +} \ No newline at end of file diff --git a/Cassiopee/XCore/XCore/AdaptMesh/BVH.h b/Cassiopee/XCore/XCore/AdaptMesh/BVH.h new file mode 100644 index 000000000..5a840f1af --- /dev/null +++ b/Cassiopee/XCore/XCore/AdaptMesh/BVH.h @@ -0,0 +1,56 @@ +#pragma once + +#include "Box.h" + +struct Mesh; +struct FaceSort; +struct Vec3f; +struct Point; +struct PointFaces; +struct DynMesh; + +struct BVH_node { + Box3 box; + E_Int start, end; + BVH_node *left; + BVH_node *right; +}; + +BVH_node *BVH_create_node(const Box3 *box, E_Int start, E_Int end, + BVH_node *left, BVH_node *right); + +BVH_node *BVH_make(const Mesh *M, FaceSort *mfaces, E_Int start, E_Int end, + const Box3 *parent_box); + +BVH_node *BVH_make(const Mesh *M, const E_Int *skin, const Vec3f *fc, + E_Int *indices, E_Int start, E_Int end, const Box3 *parent_box); + +void BVH_locate_point +( + const BVH_node *node, + const Mesh *M, + const E_Int *skin, + const E_Int *indices, + const Point *p, + PointFaces *pfaces +); + +void BVH_free(BVH_node *node); + +/* DynMesh */ + +BVH_node *BVH_make(const DynMesh *M, FaceSort *mfaces, E_Int start, E_Int end, + const Box3 *parent_box); + +BVH_node *BVH_make(const DynMesh *M, const E_Int *skin, const Vec3f *fc, + E_Int *indices, E_Int start, E_Int end, const Box3 *parent_box); + +void BVH_locate_point +( + const BVH_node *node, + const DynMesh *M, + const E_Int *skin, + const E_Int *indices, + const Point *p, + PointFaces *pfaces +); \ No newline at end of file diff --git a/Cassiopee/XCore/XCore/AdaptMesh/Box.cpp b/Cassiopee/XCore/XCore/AdaptMesh/Box.cpp new file mode 100644 index 000000000..4f490b7ec --- /dev/null +++ b/Cassiopee/XCore/XCore/AdaptMesh/Box.cpp @@ -0,0 +1,188 @@ +#include "Box.h" +#include "Mesh.h" +#include "FaceSort.h" +#include "DynMesh.h" + +Box3 Box3_make +( + const Mesh *M, + const FaceSort *mfaces, + E_Int start, E_Int end +) +{ + E_Float xmin, ymin, zmin, xmax, ymax, zmax; + xmin = ymin = zmin = FLT_MAX; + xmax = ymax = zmax = -FLT_MAX; + + for (E_Int i = start; i < end; i++) { + E_Int fid = mfaces[i].fid; + E_Int *face = Mesh_get_face(M, fid); + E_Int *frange = Mesh_get_frange(M, fid); + assert(M->fstride[fid] == 3); + assert(M->ftype[fid] == TRI); + for (E_Int j = 0; j < M->fstride[fid]; j++) { + E_Int *pn = face + 2*j; + assert(frange[j] == 1); + for (E_Int k = 0; k < frange[j]; k++) { + E_Int pid = pn[k]; + if (M->X[pid] < xmin) xmin = M->X[pid]; + if (M->Y[pid] < ymin) ymin = M->Y[pid]; + if (M->Z[pid] < zmin) zmin = M->Z[pid]; + + if (M->X[pid] > xmax) xmax = M->X[pid]; + if (M->Y[pid] > ymax) ymax = M->Y[pid]; + if (M->Z[pid] > zmax) zmax = M->Z[pid]; + } + } + } + + // Safety + E_Float dx = (xmax - xmin) * 0.01; + E_Float dy = (ymax - ymin) * 0.01; + E_Float dz = (zmax - zmin) * 0.01; + xmin -= dx; + ymin -= dy; + zmin -= dz; + xmax += dx; + ymax += dy; + zmax += dz; + + return {xmin, ymin, zmin, xmax, ymax, zmax}; +} + +void Box3_clamp(const Box3 *parent, Box3 *child) +{ + child->xmin = std::max(parent->xmin, child->xmin); + child->ymin = std::max(parent->ymin, child->ymin); + child->zmin = std::max(parent->zmin, child->zmin); + child->xmax = std::min(parent->xmax, child->xmax); + child->ymax = std::min(parent->ymax, child->ymax); + child->zmax = std::min(parent->zmax, child->zmax); +} + +Box3 Box3_make +( + const Mesh *M, + const E_Int *skin, + const E_Int *indices, + E_Int start, E_Int end +) +{ + E_Float xmin, ymin, zmin, xmax, ymax, zmax; + xmin = ymin = zmin = FLT_MAX; + xmax = ymax = zmax = -FLT_MAX; + + for (E_Int i = start; i < end; i++) { + E_Int fid = skin[indices[i]]; + E_Int *face = Mesh_get_face(M, fid); + E_Int *frange = Mesh_get_frange(M, fid); + + for (E_Int j = 0; j < M->fstride[fid]; j++) { + E_Int *pn = face + 2*j; + for (E_Int k = 0; k < frange[j]; k++) { + E_Int pid = pn[k]; + if (M->X[pid] < xmin) xmin = M->X[pid]; + if (M->Y[pid] < ymin) ymin = M->Y[pid]; + if (M->Z[pid] < zmin) zmin = M->Z[pid]; + + if (M->X[pid] > xmax) xmax = M->X[pid]; + if (M->Y[pid] > ymax) ymax = M->Y[pid]; + if (M->Z[pid] > zmax) zmax = M->Z[pid]; + } + } + } + + // Safety + E_Float dx = (xmax - xmin) * 0.01; + E_Float dy = (ymax - ymin) * 0.01; + E_Float dz = (zmax - zmin) * 0.01; + xmin -= dx; + ymin -= dy; + zmin -= dz; + xmax += dx; + ymax += dy; + zmax += dz; + + return {xmin, ymin, zmin, xmax, ymax, zmax}; +} + +/* DynMesh */ + +Box3 Box3_make +( + const DynMesh *M, + const FaceSort *mfaces, + E_Int start, E_Int end +) +{ + E_Float xmin, ymin, zmin, xmax, ymax, zmax; + xmin = ymin = zmin = FLT_MAX; + xmax = ymax = zmax = -FLT_MAX; + + for (E_Int i = start; i < end; i++) { + E_Int fid = mfaces[i].fid; + const auto &pn = M->F[fid]; + + for (E_Int pid : pn) { + if (M->X[pid] < xmin) xmin = M->X[pid]; + if (M->Y[pid] < ymin) ymin = M->Y[pid]; + if (M->Z[pid] < zmin) zmin = M->Z[pid]; + if (M->X[pid] > xmax) xmax = M->X[pid]; + if (M->Y[pid] > ymax) ymax = M->Y[pid]; + if (M->Z[pid] > zmax) zmax = M->Z[pid]; + } + } + + // Safety + E_Float dx = (xmax - xmin) * 0.01; + E_Float dy = (ymax - ymin) * 0.01; + E_Float dz = (zmax - zmin) * 0.01; + xmin -= dx; + ymin -= dy; + zmin -= dz; + xmax += dx; + ymax += dy; + zmax += dz; + + return {xmin, ymin, zmin, xmax, ymax, zmax}; +} + +Box3 Box3_make +( + const DynMesh *M, + const E_Int *skin, + const E_Int *indices, + E_Int start, E_Int end +) +{ + E_Float xmin, ymin, zmin, xmax, ymax, zmax; + xmin = ymin = zmin = FLT_MAX; + xmax = ymax = zmax = -FLT_MAX; + + for (E_Int i = start; i < end; i++) { + E_Int fid = skin[indices[i]]; + const auto &pn = M->F[fid]; + + for (E_Int pid : pn) { + if (M->X[pid] < xmin) xmin = M->X[pid]; + if (M->Y[pid] < ymin) ymin = M->Y[pid]; + if (M->Z[pid] < zmin) zmin = M->Z[pid]; + if (M->X[pid] > xmax) xmax = M->X[pid]; + if (M->Y[pid] > ymax) ymax = M->Y[pid]; + if (M->Z[pid] > zmax) zmax = M->Z[pid]; + } + } + + // Safety + E_Float dx = (xmax - xmin) * 0.01; + E_Float dy = (ymax - ymin) * 0.01; + E_Float dz = (zmax - zmin) * 0.01; + xmin -= dx; + ymin -= dy; + zmin -= dz; + xmax += dx; + ymax += dy; + zmax += dz; + + return {xmin, ymin, zmin, xmax, ymax, zmax}; +} \ No newline at end of file diff --git a/Cassiopee/XCore/XCore/AdaptMesh/Box.h b/Cassiopee/XCore/XCore/AdaptMesh/Box.h new file mode 100644 index 000000000..b7fd38f6a --- /dev/null +++ b/Cassiopee/XCore/XCore/AdaptMesh/Box.h @@ -0,0 +1,54 @@ +#pragma once + +#include "xcore.h" + +struct Mesh; +struct FaceSort; +struct DynMesh; + +struct Box3 { + E_Float xmin, ymin, zmin; + E_Float xmax, ymax, zmax; +}; + +inline +bool Box3_in_Box3(const Box3 small, const Box3 big) +{ + return (big.xmin <= small.xmin) && (big.xmax >= small.xmax) && + (big.ymin <= small.ymin) && (big.ymax >= small.ymax) && + (big.zmin <= small.zmin) && (big.zmax >= small.zmax); +} + +Box3 Box3_make +( + const Mesh *M, + const FaceSort *mfaces, + E_Int start, E_Int end +); + +Box3 Box3_make +( + const Mesh *M, + const E_Int *skin, + const E_Int *indices, + E_Int start, E_Int end +); + +void Box3_clamp(const Box3 *parent, Box3 *child); + +/* DynMesh */ + +Box3 Box3_make +( + const DynMesh *M, + const FaceSort *mfaces, + E_Int start, E_Int end +); + +Box3 Box3_make +( + const DynMesh *M, + const E_Int *skin, + const E_Int *indices, + E_Int start, E_Int end +); \ No newline at end of file diff --git a/Cassiopee/XCore/XCore/AdaptMesh/DynMesh.cpp b/Cassiopee/XCore/XCore/AdaptMesh/DynMesh.cpp new file mode 100644 index 000000000..ef9e9b910 --- /dev/null +++ b/Cassiopee/XCore/XCore/AdaptMesh/DynMesh.cpp @@ -0,0 +1,770 @@ +#include "DynMesh.h" +#include "common/Karray.h" +#include "Array.h" +#include "Skin.h" +#include "Vec.h" +#include "Point.h" +#include "common/mem.h" +#include + +DynMesh::DynMesh() +{} + +DynMesh::DynMesh(Karray *karray) +{ + np = karray->npoints(); + nf = karray->nfaces(); + nc = karray->ncells(); + + X.resize(np); + Y.resize(np); + Z.resize(np); + memcpy(X.data(), karray->x, np * sizeof(E_Float)); + memcpy(Y.data(), karray->y, np * sizeof(E_Float)); + memcpy(Z.data(), karray->z, np * sizeof(E_Float)); + + F.reserve(nf); + + for (E_Int i = 0; i < nf; i++) { + E_Int np = -1; + E_Int *pn = karray->get_face(i, np); + std::vector points(np); + for (E_Int j = 0; j < np; j++) + points[j] = pn[j] - 1; + F.push_back(points); + } + + C.reserve(nc); + for (E_Int i = 0; i < nc; i++) { + E_Int nf = -1; + E_Int *pf = karray->get_cell(i, nf); + std::vector faces(nf); + for (E_Int j = 0; j < nf; j++) + faces[j] = pf[j] - 1; + C.push_back(faces); + } + + ftag.resize(nf, 0); + + owner.resize(nf, -1); + neigh.resize(nf, -1); + for (E_Int cid = 0; cid < nc; cid++) { + const auto &pf = C[cid]; + for (E_Int fid : pf) { + if (owner[fid] == -1) owner[fid] = cid; + else neigh[fid] = cid; + } + } +} + +void DynMesh::extract_points_from_ftag(ArrayI *pids) +{ + E_Int *ptag = (E_Int *)XMALLOC(np * sizeof(E_Int)); + memset(ptag, 0, np * sizeof(E_Int)); + pids->count = 0; + + for (E_Int fid = 0; fid < nf; fid++) { + if (ftag[fid] != 1) continue; + const auto &pn = F[fid]; + + for (E_Int pid : pn) { + pids->count += (ptag[pid] == 0); + ptag[pid] = 1; + } + } + + pids->ptr = (E_Int *)XMALLOC(pids->count * sizeof(E_Int)); + E_Int *ptr = pids->ptr; + + for (E_Int pid = 0; pid < np; pid++) { + if (ptag[pid] == 1) + *ptr++ = pid; + } + + XFREE(ptag); +} + +void DynMesh::make_skin_connectivity(SkinGraph *skin_graph) +{ + // Count + printf("skin count: %d\n", skin_graph->nf); + skin_graph->xadj = (E_Int *)XMALLOC((skin_graph->nf+1) * sizeof(E_Int)); + E_Int *xadj = skin_graph->xadj; + xadj[0] = 0; + + for (E_Int i = 0; i < skin_graph->nf; i++) { + E_Int fid = skin_graph->skin[i]; + xadj[i+1] = F[fid].size(); + xadj[i+1] += xadj[i]; + } + + skin_graph->fpts = (E_Int *)XMALLOC(xadj[skin_graph->nf] * sizeof(E_Int)); + + // Populate + E_Int *ptr = skin_graph->fpts; + + for (E_Int i = 0; i < skin_graph->nf; i++) { + E_Int fid = skin_graph->skin[i]; + const auto &pn = F[fid]; + for (E_Int p : pn) { + *ptr++ = p; + } + } + + assert(ptr - skin_graph->fpts == xadj[skin_graph->nf]); +} + +void DynMesh::make_face_centers(const E_Int NF, const E_Int *skin, + Vec3f *fc) +{ + for (E_Int i = 0; i < NF; i++) { + fc[i].x = fc[i].y = fc[i].z = 0.0; + E_Int fid = skin[i]; + const auto &pn = F[fid]; + assert(ftag[skin[i]] == 1); + for (E_Int p : pn) { + fc[i].x += X[p]; + fc[i].y += Y[p]; + fc[i].z += Z[p]; + } + fc[i].x /= pn.size(); fc[i].y /= pn.size(); fc[i].z /= pn.size(); + } +} + +bool DynMesh::point_in_tri(const Point *p, E_Int tid) const +{ + const auto &pn = F[tid]; + E_Int A = pn[0], B = pn[1], C = pn[2]; + return Point_in_tri(p->x, p->y, p->z, + X[A], Y[A], Z[A], + X[B], Y[B], Z[B], + X[C], Y[C], Z[C]); +} + +bool DynMesh::point_in_quad(const Point *p, E_Int qid) const +{ + // TODO(Imad): maybe compute face centers once in pre-pass + // Star the quad into 4 triangles + E_Float O[3] = {0.0, 0.0, 0.0}; + const auto &pn = F[qid]; + E_Int A = pn[0], B = pn[1], C = pn[2], D = pn[3]; + O[0] = (X[A] + X[B] + X[C] + X[D]) * 0.25; + O[1] = (Y[A] + Y[B] + Y[C] + Y[D]) * 0.25; + O[2] = (Z[A] + Z[B] + Z[C] + Z[D]) * 0.25; + + bool hit = false; + + // First triangle + hit = Point_in_tri(p->x, p->y, p->z, + O[0], O[1], O[2], + X[A], Y[A], Z[A], + X[B], Y[B], Z[B]); + if (hit) return true; + + // Second triangle + hit = Point_in_tri(p->x, p->y, p->z, + O[0], O[1], O[2], + X[B], Y[B], Z[B], + X[C], Y[C], Z[C]); + if (hit) return true; + + + // Third triangle + hit = Point_in_tri(p->x, p->y, p->z, + O[0], O[1], O[2], + X[C], Y[C], Z[C], + X[D], Y[D], Z[D]); + if (hit) return true; + + // Fourth triangle + hit = Point_in_tri(p->x, p->y, p->z, + O[0], O[1], O[2], + X[D], Y[D], Z[D], + X[A], Y[A], Z[A]); + if (hit) return true; + + return false; +} + +bool DynMesh::point_in_face(const Point *p, E_Int fid) const +{ + if (F[fid].size() == 4) return point_in_quad(p, fid); + return point_in_tri(p, fid); +} + +void DynMesh::prepare_for_refinement(ArrayI *ref_faces) +{ + ref_faces->count = 0; + for (size_t i = 0; i < tri_graph.nf; i++) { + ref_faces->count += (tri_graph.fdat[i] == 1); + } + ref_faces->ptr = (E_Int *)XMALLOC(ref_faces->count * sizeof(E_Int)); + E_Int *ptr = ref_faces->ptr; + for (size_t i = 0; i < tri_graph.nf; i++) { + if (tri_graph.fdat[i] == 1) { + *ptr++ = i; + } + } + + // Resize data structures + resize_point_data(ref_faces->count); + resize_face_data(ref_faces->count); +} + +void DynMesh::refine_faces(ArrayI *ref_faces) +{ + tri_graph.T.resize(tri_graph.nf + ref_faces->count*4, {-1, -1, -1}); + tri_graph.E.resize(tri_graph.nf + ref_faces->count*4, {-1, -1, -1}); + tri_graph.skin.resize(tri_graph.nf + ref_faces->count*4, -1); + tri_graph.level.resize(tri_graph.nf + ref_faces->count*4, 0); + + printf("Faces before refinement: %d\n", nf); + + for (E_Int i = 0; i < ref_faces->count; i++) { + E_Int fid = ref_faces->ptr[i]; + assert(tri_graph.level[fid] == 0); + assert(face_is_tri(tri_graph.skin[fid])); + refine_tri(fid); + } +} + +void DynMesh::resize_point_data(size_t nref_faces) +{ + size_t nnew_points = np + nref_faces * 3; + X.resize(nnew_points); + Y.resize(nnew_points); + Z.resize(nnew_points); +} + +void DynMesh::resize_face_data(size_t nref_faces) +{ + size_t nnew_faces = nf + nref_faces * 4; // 3 new triangles + 1 potential neighbor triangle + F.resize(nnew_faces); + owner.resize(nnew_faces); + neigh.resize(nnew_faces); + ftag.resize(nnew_faces, 0); +} + +E_Int DynMesh::get_edge_index(E_Int nei, E_Int tri) +{ + for (E_Int i = 0; i < 3; i++) { + if (tri_graph.E[nei][i] == tri) + return i; + } + return -1; +} + +void DynMesh::refine_tri(E_Int tri_idx) +{ + assert(tri_graph.level[tri_idx] == 0); + puts("refining"); + fflush(stdout); + E_Int tri = tri_graph.skin[tri_idx]; + + // Refine the edges + const auto &pn = F[tri]; + E_Int ec[3]; + + assert(pn.size() == 3); + + for (size_t i = 0; i < pn.size(); i++) { + E_Int p = pn[i]; + E_Int q = pn[(i+1)%pn.size()]; + uEdge edge(p, q); + + auto it = ecenter.find(edge); + + if (it == ecenter.end()) { + X[np] = 0.5 * (X[p] + X[q]); + Y[np] = 0.5 * (Y[p] + Y[q]); + Z[np] = 0.5 * (Z[p] + Z[q]); + ecenter[edge] = np; + ec[i] = np; + np++; + } else { + ec[i] = it->second; + } + } + + E_Int vn[3] = {pn[0], pn[1], pn[2]}; + + // Create new triangles + F[tri] = {vn[0], ec[0], ec[2]}; + F[nf] = {ec[0], vn[1], ec[1]}; + F[nf+1] = {ec[2], ec[1], vn[2]}; + F[nf+2] = {ec[0], ec[1], ec[2]}; + + fchildren[tri] = {nf, nf+1, nf+2}; + ftag[nf] = ftag[nf+1] = ftag[nf+2] = ftag[tri]; + + // Update owners and neighbours + neigh[nf] = neigh[nf+1] = neigh[nf+2] = neigh[tri]; + owner[nf] = owner[nf+1] = owner[nf+2] = owner[tri]; + + // Skin + + // Add the new triangles + size_t NF = tri_graph.nf; + + auto &T = tri_graph.T; + auto &E = tri_graph.E; + + T[tri_idx][0] = vn[0]; T[tri_idx][1] = ec[0]; T[tri_idx][2] = ec[2]; + assert(T[tri_idx][0] != T[tri_idx][1]); + assert(T[tri_idx][0] != T[tri_idx][2]); + assert(T[tri_idx][1] != T[tri_idx][2]); + + T[NF][0] = ec[0]; T[NF][1] = vn[1]; T[NF][2] = ec[1]; + T[NF+1][0] = ec[2]; T[NF+1][1] = ec[1]; T[NF+1][2] = vn[2]; + T[NF+2][0] = ec[0]; T[NF+2][1] = ec[1]; T[NF+2][2] = ec[2]; + + E_Int N0 = NF; + E_Int N1 = NF+1; + E_Int N2 = NF+2; + + E_Int A = tri_graph.E[tri_idx][0]; + E_Int B = tri_graph.E[tri_idx][1]; + E_Int C = tri_graph.E[tri_idx][2]; + + E[tri_idx][0] = A; E[tri_idx][1] = N2; E[tri_idx][2] = C; + E[N0][0] = A; E[N0][1] = B; E[N0][2] = N2; + E[N1][0] = N2; E[N1][1] = B; E[N1][2] = C; + E[N2][0] = N0; E[N2][1] = N1; E[N2][2] = tri_idx; + + tri_graph.skin[NF] = nf; + tri_graph.skin[NF+1] = nf+1; + tri_graph.skin[NF+2] = nf+2; + + tri_graph.level[tri_idx]++; + assert(tri_graph.level[tri_idx] == 1); + tri_graph.level[NF] = tri_graph.level[NF+1] = tri_graph.level[NF+2] = 1; + + // Increment face count + nf += 3; + tri_graph.nf += 3; + + NF = tri_graph.nf; + + bool cut_A = (A != -1) && (tri_graph.level[A] == 0 && tri_graph.fdat[A] == 0); + + if (cut_A) { + E_Int edge_idx = get_edge_index(A, tri_idx); + assert(edge_idx != -1); + E_Int P = T[A][(edge_idx+2)%3]; + + E_Int D = E[A][(edge_idx+1)%3]; + E_Int e = E[A][(edge_idx+2)%3]; + + // A points and neighbours + T[A][0] = ec[0]; T[A][1] = vn[0]; T[A][2] = P; + + // NF points and neighbours + T[NF][0] = ec[0]; T[NF][1] = P; T[NF][2] = vn[1]; + + // e neighbours (if it exists) + if (e != -1) { + E_Int idx = get_edge_index(e, A); + E[e][idx] = NF; + } + + E[A][0] = tri_idx; E[A][1] = D; E[A][2] = NF; + E[NF][0] = A; E[NF][1] = e; E[NF][2] = N0; + + // N0 neighbours + E[N0][0] = NF;// E[N1][0] = B; E[N1][1] = N2; + + tri_graph.skin[NF] = nf; + + assert(tri_graph.level[A] == 0); + assert(tri_graph.level[NF] == 0); + + NF += 1; + + // Global stuff + E_Int gA = tri_graph.skin[A]; + F[gA] = {ec[0], vn[0], P}; + fchildren[gA].push_back(nf); + + F[nf] = {ec[0], P, vn[1]}; + ftag[nf] = ftag[gA]; + owner[nf] = owner[gA]; + neigh[nf] = neigh[gA]; + + nf += 1; + } + + bool cut_B = (B != -1) && (tri_graph.level[B] == 0 && tri_graph.fdat[B] == 0); + + if (cut_B) { + E_Int edge_idx = get_edge_index(B, tri_idx); + assert(edge_idx != -1); + E_Int P = T[B][(edge_idx+2)%3]; + + E_Int f = E[B][(edge_idx+1)%3]; + E_Int G = E[B][(edge_idx+2)%3]; + + // B points and neighbours + T[B][0] = ec[1]; T[B][1] = vn[1]; T[B][2] = P; + + // NF points and neighbours + T[NF][0] = ec[1]; T[NF][1] = P; T[NF][2] = vn[2]; + + // G neighbours (if it exists) + if (G != -1) { + E_Int idx = get_edge_index(G, B); + E[G][idx] = NF; + } + + E[B][0] = N0; E[B][1] = f; E[B][2] = NF; + E[NF][0] = B; E[NF][1] = G; E[NF][2] = N1; + + // N1 neighbours + E[N1][1] = NF; + + assert(tri_graph.level[B] == 0); + assert(tri_graph.level[NF] == 0); + + tri_graph.skin[NF] = nf; + NF += 1; + + // Global stuff + E_Int gB = tri_graph.skin[B]; + F[gB] = {ec[1], vn[1], P}; + fchildren[gB].push_back(nf); + + F[nf] = {ec[1], P, vn[2]}; + ftag[nf] = ftag[gB]; + owner[nf] = owner[gB]; + neigh[nf] = neigh[gB]; + + nf += 1; + } + + bool cut_C = (C != -1) && (tri_graph.level[C] == 0 && tri_graph.fdat[C] == 0); + + if (cut_C) { + E_Int edge_idx = get_edge_index(C, tri_idx); + assert(edge_idx != -1); + E_Int P = T[C][(edge_idx+2)%3]; + + E_Int I = E[C][(edge_idx+1)%3]; + E_Int H = E[C][(edge_idx+2)%3]; + + // C points and neighbours + T[C][0] = ec[2]; T[C][1] = P; T[C][2] = vn[0]; + + // NF points and neighbours + T[NF][0] = ec[2]; T[NF][1] = vn[2]; T[NF][2] = P; + + // I neighbours (if it exists) + if (I != -1) { + E_Int idx = get_edge_index(I, C); + E[I][idx] = NF; + } + + E[C][0] = NF; E[C][1] = H; E[C][2] = tri_idx; + E[NF][0] = N1; E[NF][1] = I; E[NF][2] = C; + + + // N1 adjacency + E[N1][2] = NF; + + assert(tri_graph.level[C] == 0); + assert(tri_graph.level[NF] == 0); + + tri_graph.skin[NF] = nf; + NF += 1; + + // Global stuff + E_Int gC = tri_graph.skin[C]; + F[gC] = {ec[2], vn[2], P}; + fchildren[gC].push_back(nf); + + F[nf] = {ec[2], P, vn[0]}; + ftag[nf] = ftag[gC]; + owner[nf] = owner[gC]; + neigh[nf] = neigh[gC]; + + nf += 1; + } +} + +DynMesh DynMesh::extract_conformized() +{ + // Keep all the points + std::vector new_X(X), new_Y(Y), new_Z(Z); + + // Conformize the faces + + std::vector> new_F(nf); + + for (E_Int fid = 0; fid < nf; fid++) { + + const auto &pn = F[fid]; + + auto &new_face = new_F[fid]; + + for (size_t j = 0; j < pn.size(); j++) { + E_Int p = pn[j]; + E_Int q = pn[(j+1)%pn.size()]; + + std::list epoints; + + extract_edge_points(p, q, epoints); + + epoints.pop_back(); + + for (auto it = epoints.begin(); it != epoints.end(); it++) + new_face.push_back(*it); + } + } + + // Update cell connectivity + + std::vector> new_C(C.size()); + + for (E_Int i = 0; i < nc; i++) { + const auto &pf = C[i]; + + auto &new_cell = new_C[i]; + + for (E_Int fid : pf) { + auto it = fchildren.find(fid); + + if (it == fchildren.end()) { + new_cell.push_back(fid); + } else { + std::vector fleaves; + get_fleaves(fid, fleaves); + for (E_Int leaf : fleaves) + new_cell.push_back(leaf); + } + } + } + + DynMesh new_M; + new_M.np = np; + new_M.X = X; + new_M.Y = Y; + new_M.Z = Z; + new_M.nf = nf; + new_M.F = new_F; + new_M.nc = nc; + new_M.C = new_C; + new_M.ftag = ftag; + + + return new_M; +} + +void DynMesh::get_fleaves(E_Int face, std::vector &fleaves) +{ + fleaves.push_back(face); + + const auto it = fchildren.find(face); + + if (it == fchildren.end()) { + return; + } + + for (E_Int child : it->second) + get_fleaves(child, fleaves); +} + +PyObject *DynMesh::export_karray() +{ + E_Int sizeNGon = 0, sizeNFace = 0; + + for (const auto &pn : F) sizeNGon += (E_Int)pn.size(); + for (const auto &pf : C) sizeNFace += (E_Int)pf.size(); + + const char *varString = "CoordinateX,CoordinateY,CoordinateZ"; + + PyObject *array = K_ARRAY::buildArray3(3, varString, np, nc, nf, "NGON", + sizeNGon, sizeNFace, 3, false, 3); + + K_FLD::FldArrayF *f; + K_FLD::FldArrayI *cn; + K_ARRAY::getFromArray3(array, f, cn); + + E_Float *px = f->begin(1); + for (E_Int i = 0; i < np; i++) px[i] = X[i]; + E_Float *py = f->begin(2); + for (E_Int i = 0; i < np; i++) py[i] = Y[i]; + E_Float *pz = f->begin(3); + for (E_Int i = 0; i < np; i++) pz[i] = Z[i]; + + E_Int *indPG = cn->getIndPG(); + E_Int *ngon = cn->getNGon(); + E_Int *indPH = cn->getIndPH(); + E_Int *nface = cn->getNFace(); + + indPG[0] = indPH[0] = 0; + for (E_Int i = 0; i < nf; i++) indPG[i+1] = indPG[i] + (E_Int)F[i].size(); + for (E_Int i = 0; i < nc; i++) indPH[i+1] = indPH[i] + (E_Int)C[i].size(); + + assert(indPG[nf] == sizeNGon); + assert(indPH[nc] == sizeNFace); + + E_Int *ptr = ngon; + + for (E_Int i = 0; i < nf; i++) { + const auto &pn = F[i]; + for (E_Int p : pn) *ptr++ = p+1; + } + + ptr = nface; + + for (E_Int i = 0; i < nc; i++) { + const auto &pf = C[i]; + for (E_Int f : pf) *ptr++ = f+1; + } + + delete f; + delete cn; + + return array; +} + +void DynMesh::extract_edge_points(E_Int a, E_Int b, std::list &points) +{ + E_Int ref = 0; + + points.clear(); + points.push_back(a); + points.push_back(b); + + do { + ref = 0; + + assert(*std::prev(points.end()) == b); + + for (auto it = points.begin(); it != std::prev(points.end()); it++) { + E_Int a = *it; + E_Int b = *std::next(it); + + uEdge e(a, b); + + auto search = ecenter.find(e); + + if (search != ecenter.end()) { + points.insert(std::next(it), search->second); + ref = 1; + } + } + } while (ref); +} + +struct EdgeNode { + E_Int p, q; + E_Int fi, posi; + mutable E_Int fj, posj; + EdgeNode(E_Int p_, E_Int q_) + { + p = std::min(p_, q_); + q = std::max(p_, q_); + fi = posi = fj = posj = -1; + } + bool operator<(const EdgeNode &e) const + { + return (p < e.p) || (p == e.p && q < e.q); + } +}; + +void DynMesh::make_tri_graph() +{ + tri_graph.nf = 0; + + // From tagged faces + for (E_Int i = 0; i < nf; i++) { + tri_graph.nf += (ftag[i] == 1); + } + tri_graph.skin.clear(); + tri_graph.skin.resize(tri_graph.nf); + E_Int *skin = tri_graph.skin.data(); + E_Int *ptr = skin; + for (E_Int i = 0; i < nf; i++) { + if (ftag[i] == 1) + *ptr++ = i; + } + + tri_graph.T.clear(); + tri_graph.E.clear(); + tri_graph.level.clear(); + + tri_graph.T.resize(tri_graph.nf, {-1, -1, -1}); + tri_graph.E.resize(tri_graph.nf, {-1, -1, -1}); + tri_graph.level.resize(tri_graph.nf, 0); + + std::set edges; + + for (size_t i = 0; i < tri_graph.nf; i++) { + E_Int fid = tri_graph.skin[i]; + const auto &pn = F[fid]; + assert(pn.size() == 3); + for (size_t j = 0; j < pn.size(); j++) { + E_Int p = pn[j]; + + tri_graph.T[i][j] = p; + + E_Int q = pn[(j+1)%3]; + + EdgeNode node(p, q); + auto it = edges.find(node); + if (it == edges.end()) { + node.fi = i; + node.posi = j; + edges.insert(node); + } else { + assert(it->fi != -1); + assert(it->posi != -1); + assert(it->fj == -1); + assert(it->posj == -1); + it->fj = i; + it->posj = j; + } + } + } + + for (const auto &e : edges) { + assert(e.fi != -1); + tri_graph.E[e.fi][e.posi] = e.fj; + if (e.fj != -1) + tri_graph.E[e.fj][e.posj] = e.fi; + else + assert(e.posj == -1); + } +} + + +void DynMesh::triangulate(const E_Int *faces, E_Int fcount) +{ + F.resize(nf + fcount); + owner.resize(nf + fcount, -1); + neigh.resize(nf + fcount, -1); + ftag.resize(nf + fcount, 0); + + for (E_Int i = 0; i < fcount; i++) { + E_Int fid = faces[i]; + + auto &pn = F[fid]; + auto &tri = F[nf]; + + tri = { pn[0], pn[2], pn[3] }; + pn = { pn[0], pn[1], pn[2] }; + + auto &pown = C[owner[fid]]; + pown.push_back(nf); + + if (neigh[fid] != -1) { + auto &pnei = C[neigh[fid]]; + pnei.push_back(nf); + } + + owner[nf] = owner[fid]; + neigh[nf] = neigh[fid]; + ftag[nf] = ftag[fid]; + + nf++; + } +} diff --git a/Cassiopee/XCore/XCore/AdaptMesh/DynMesh.h b/Cassiopee/XCore/XCore/AdaptMesh/DynMesh.h new file mode 100644 index 000000000..d54db8a85 --- /dev/null +++ b/Cassiopee/XCore/XCore/AdaptMesh/DynMesh.h @@ -0,0 +1,131 @@ +#pragma once + +#include +#include +#include +#include + +#include "xcore.h" +#include "common/common.h" +#include "TriGraph.h" + +struct Karray; +struct ArrayI; +struct SkinGraph; +struct Vec3f; +struct Point; + +#define OUT 0 +#define IN 1 + +struct uEdge { + E_Int p, q; + + uEdge(E_Int P, E_Int Q) + { + p = std::min(P, Q); + q = std::max(P, Q); + } + + bool operator<(const uEdge &E) const + { + return (p < E.p) || (p == E.p && q < E.q); + } +}; + +struct DynMesh { + E_Int np, ne, nf, nc; + + std::vector X, Y, Z; + + std::vector> F; + + std::vector> C; + + std::vector owner, neigh; + + std::vector ftag; + + std::map> fchildren; + + std::map ecenter; + + TriGraph tri_graph; + + DynMesh(); + + DynMesh(Karray *karray); + + void triangulate(const E_Int *faces, E_Int fcount); + + E_Int get_edge_index(E_Int nei, E_Int tri); + + void extract_points_from_ftag(ArrayI *points); + + void make_tri_graph(); + + void make_face_centers(const E_Int NF, const E_Int *skin, + Vec3f *fc); + + bool point_in_tri(const Point *p, E_Int tid) const; + + bool point_in_quad(const Point *p, E_Int qid) const; + + bool point_in_face(const Point *p, E_Int fid) const; + + E_Int orient_skin(E_Int normal_direction); + + void extract_skin(E_Int *count, E_Int **skin); + + void make_skin_connectivity(SkinGraph *skin_graph); + + void make_skin_neighbours(SkinGraph *skin_graph); + + void prepare_for_refinement(ArrayI *ref_faces); + + void refine_faces(ArrayI *ref_faces); + + inline bool face_is_tri(E_Int face) const { return F[face].size() == 3; } + + DynMesh extract_conformized(); + + void write_ngon(const char *fname); + + void write_faces(const char *fname, const std::vector &faces) const; + + void write_face(const char *fname, E_Int fid) const; + + void resize_point_data(size_t nref_faces); + + void resize_face_data(size_t nref_faces); + + void refine_tri(E_Int tri); + + void get_fleaves(E_Int face, std::vector &fleaves); + + PyObject *export_karray(); + + void extract_edge_points(E_Int a, E_Int b, std::list &points); + + /* TOPO */ + + void flag_and_get_external_faces(std::vector &fflags, + std::vector &efaces); + + void extract_nface_of_kept_pgs(const std::vector &kept_pgs, + std::vector &NFACE, std::vector &cxadj, + std::vector &cells); + + void flag_marked_external_cells(const std::vector &cells, + const std::vector &fflags, std::vector &cflags); + + void flag_all_external_cells(const std::vector &fflags, + std::vector &cflags); + + E_Int orient_boundary(E_Int ncells, E_Int *efadj, E_Int *exadj, E_Int nefaces, + E_Int *fneis, E_Int *efaces, std::vector &forient, + const std::vector &cflags, const std::vector &fflags, + E_Int *cells, E_Int normal_direction); + + void compute_cell_volume(E_Int cell, E_Float &vol, E_Int refIdx); +}; diff --git a/Cassiopee/XCore/XCore/AdaptMesh/DynMeshTopo.cpp b/Cassiopee/XCore/XCore/AdaptMesh/DynMeshTopo.cpp new file mode 100644 index 000000000..5acbf0582 --- /dev/null +++ b/Cassiopee/XCore/XCore/AdaptMesh/DynMeshTopo.cpp @@ -0,0 +1,328 @@ +#include "DynMesh.h" + +#define OUT 0 +#define IN 1 + +#define INTERNAL 0 +#define EXTERNAL 1 + +#define DSMALL 1e-14 + +E_Int DynMesh::orient_skin(E_Int normal_direction) +{ + // flag external cells and faces + std::vector fflags, efaces; + flag_and_get_external_faces(fflags, efaces); + + // extract external faces connectivity + std::vector fadj; + std::vector xadj(1, 0); + for (E_Int i = 0; i < nf; i++) { + if (fflags[i] == EXTERNAL) { + const auto &pn = F[i]; + xadj.push_back(pn.size()); + for (size_t j = 0; j < pn.size(); j++) fadj.push_back(pn[j]); + } + } + + E_Int nefaces = (E_Int)efaces.size(); + + for (E_Int i = 0; i < nefaces; i++) xadj[i+1] += xadj[i]; + + // build skin neighbourhood + std::vector fneighbours; + K_CONNECT::build_face_neighbourhood(fadj, xadj, fneighbours); + + // color the faces by connex part + std::vector colors(xadj.size()-1); + E_Int nconnex = K_CONNECT::colorConnexParts(&fneighbours[0], &xadj[0], + nefaces, &colors[0]); + + //printf("orient_boundary(): connex parts: " SF_D_ "\n", nconnex); + + assert(efaces.size() == xadj.size()-1); + std::vector forient(nefaces, 0); + std::vector cflags; + E_Int ret = 0; + + if (nconnex > 1) { + // extract nconnex nface-ngon for separate orientation + for (E_Int color = 0; color < nconnex; color++) { + std::vector keep_pgs(nf, false); + for (E_Int i = 0; i < nefaces; i++) { + keep_pgs[efaces[i]] = (colors[i] == color); + } + + // extract nface corresponding to kept faces + std::vector NFACE, cxadj(1, 0), cells; + extract_nface_of_kept_pgs(keep_pgs, NFACE, cxadj, cells); + + std::vector cflags; + flag_marked_external_cells(cells, fflags, cflags); + + ret |= orient_boundary((E_Int)cells.size(), + &fadj[0], &xadj[0], nefaces, &fneighbours[0], &efaces[0], + forient, cflags, fflags, &cells[0], normal_direction); + } + } else { + std::vector cflags; + flag_all_external_cells(fflags, cflags); + ret = orient_boundary(nc, &fadj[0], &xadj[0], nefaces, &fneighbours[0], + &efaces[0], forient, cflags, fflags, NULL, normal_direction); + } + + // Apply orientation + E_Int nrev = 0; + for (E_Int i = 0; i < nefaces; i++) { + if (forient[i] == -1) { + E_Int face = efaces[i]; // 0-based + auto &pn = F[face]; + std::reverse(pn.begin(), pn.end()); + nrev++; + } + } + + //printf("orient_boundary(): reversed " SF_D_ " faces\n", nrev); + + return ret; +} + +void DynMesh::flag_and_get_external_faces(std::vector &fflags, + std::vector &efaces) +{ + std::vector face_count(nf, 0); + + // Loop through the elements and increment face_count + for (E_Int i = 0; i < nc; i++) { + const auto &pf = C[i]; + for (E_Int face : pf) face_count[face]++; + } + + // External faces are those with a count equal to 1 + fflags.resize(nf); + + for (E_Int i = 0; i < nf; i++) { + if (face_count[i] == 1) { + fflags[i] = EXTERNAL; + efaces.push_back(i); + } else { + fflags[i] = INTERNAL; + } + } +} + +void DynMesh::extract_nface_of_kept_pgs(const std::vector &kept_pgs, + std::vector &NFACE, std::vector &xadj, + std::vector &cells) +{ + NFACE.clear(); + xadj.resize(1, 0); + cells.clear(); + + for (E_Int i = 0; i < nc; i++) { + const auto &pf = C[i]; + bool keep = false; + for (size_t j = 0; j < pf.size() && !keep; j++) keep = kept_pgs[pf[j]]; + if (keep) { + cells.push_back(i); + xadj.push_back(pf.size()); + for (size_t j = 0; j < pf.size(); j++) NFACE.push_back(pf[j]); + } + } + + for (size_t i = 0; i < xadj.size(); i++) xadj[i+1] += xadj[i]; +} + +void DynMesh::flag_marked_external_cells(const std::vector &cells, + const std::vector &fflags, std::vector &cflags) +{ + // External cells are those with at least one external face + cflags.resize(cells.size(), INTERNAL); + + for (size_t i = 0; i < cells.size(); i++) { + E_Int cell = cells[i]; + const auto &pf = C[cell]; + for (size_t j = 0; j < pf.size(); j++) { + E_Int face = pf[j]; + if (fflags[face] == EXTERNAL) { + cflags[i] = EXTERNAL; + break; + } + } + } +} + +E_Int DynMesh::orient_boundary(E_Int ncells, E_Int *efadj, E_Int *efxadj, + E_Int nefaces, E_Int *fneis, E_Int *efaces, std::vector &forient, + const std::vector &cflags, const std::vector &fflags, + E_Int *cells, E_Int normal_direction) +{ + // Look for a cell whose volume is unambiguously computed + E_Float cvol = 0.0; + E_Int seed = -1; + E_Int refPG = -1; + E_Int refIdx = -1; + + while (++seed < ncells) { + if (cflags[seed] != EXTERNAL) continue; + + E_Int cid = (cells != NULL) ? cells[seed] : seed; + + const auto &pf = C[cid]; + refPG = -1; + E_Int local_idx = -1; + for (size_t j = 0; j < pf.size(); j++) { + E_Int face = pf[j]; + if (fflags[face] == EXTERNAL) { + refPG = face; + local_idx = j; + break; + } + } + + if (refPG == -1) { + fprintf(stderr, "orient_boundary(): couldn't find an external face " + "within external cell " SF_D_ "\n", cid); + return 1; + } + + // Look for index of refPG in efaces (0-based) + refIdx = -1; + for (E_Int i = 0; i < nefaces; i++) { + if (efaces[i] == refPG) { + refIdx = i; + break; + } + } + + if (refIdx == -1) { + fprintf(stderr, "orient_boundary(): couldn't find reference face " + SF_D_ " in external faces list\n", refPG); + return 1; + } + + // Set orientation of refPG to +1. + // Reorient seed's faces based on orientation of refPG. + // Compute cvol, the volume of seed. + // If cvol > 0, orientation of all faces including refPG, is outwards + // Otherwise, set orientation of refPG to -1. + + compute_cell_volume(cid, cvol, local_idx); + + if (fabs(cvol) < DSMALL) continue; + + // set reference orientation of refPG and exit + forient[refIdx] = (cvol > 0.0) ? 1 : -1; + + if (normal_direction == IN) forient[refIdx] = -forient[refIdx]; + + break; + } + + if (seed >= ncells) { + fprintf(stderr, "orient_boundary_ngon(): couldn't find reference " + "polyhedron\n"); + assert(0); + return 1; + } + + // propagate + K_CONNECT::reversi_connex(efadj, efxadj, nefaces, fneis, refIdx, forient); + + return 0; +} + +void DynMesh::compute_cell_volume(E_Int cell, E_Float &vol, E_Int refIdx) +{ + // Orient the faces coherently + std::vector NGON; + std::vector INDPG(1, 0); + const auto &pf = C[cell]; + E_Int stride = (E_Int)pf.size(); + + for (E_Int i = 0; i < stride; i++) { + E_Int face = pf[i]; + const auto &pn = F[face]; + E_Int np = pn.size(); + INDPG.push_back(np); + for (E_Int j = 0; j < np; j++) NGON.push_back(pn[j]); + } + + for (E_Int i = 0; i < stride; i++) INDPG[i+1] += INDPG[i]; + + // Fix orientation of first face + std::vector orient(stride); + orient[refIdx] = 1; + std::vector neis(NGON.size()); + K_CONNECT::build_face_neighbourhood(NGON, INDPG, neis); + K_CONNECT::reversi_connex(&NGON[0], &INDPG[0], stride, &neis[0], refIdx, + orient); + + // Apply orientation in local NGON + for (E_Int i = 0; i < stride; i++) { + if (orient[i] == -1) { + E_Int start = INDPG[i]; + E_Int np = INDPG[i+1] - start; + E_Int *pn = &NGON[start]; + std::reverse(pn+1, pn+np); + } + } + + // Compute faces area and center + std::vector faceAreas(3*stride, 0.0); + std::vector faceCenters(3*stride, 0.0); + + for (E_Int i = 0; i < stride; i++) { + E_Int face = pf[i]; + E_Float *fa = &faceAreas[3*i]; + E_Float *fc = &faceCenters[3*i]; + E_Int np = INDPG[i+1]-INDPG[i]; + E_Int *pn = &NGON[INDPG[i]]; + for (E_Int j = 0; j < np; j++) pn[j] += 1; + K_METRIC::compute_face_center_and_area(face, np, pn, X.data(), Y.data(), + Z.data(), fc, fa); + } + + // Estimate cell centroid as average of face centers + E_Float cc[3] = {0,0,0}; + for (E_Int i = 0; i < stride; i++) { + E_Float *fc = &faceCenters[3*i]; + for (E_Int j = 0; j < 3; j++) cc[j] += fc[j]; + } + for (E_Int i = 0; i < 3; i++) cc[i] /= stride; + + // Compute cell volume + vol = 0.0; + + for (E_Int i = 0; i < stride; i++) { + E_Float *fa = &faceAreas[3*i]; + E_Float *fc = &faceCenters[3*i]; + + // Compute 3*face-pyramid volume contribution + E_Float d[3] = {fc[0]-cc[0], fc[1]-cc[1], fc[2]-cc[2]}; + //E_Float pyr3Vol = K_MATH::dot(fa, fc, 3); + E_Float pyr3Vol = K_MATH::dot(fa, d, 3); + + vol += pyr3Vol; + } + + vol *= K_MATH::ONE_THIRD; +} + +void DynMesh::flag_all_external_cells(const std::vector &fflags, + std::vector &cflags) +{ + // External cells are those with at least one external face + cflags.resize(nc, INTERNAL); + for (E_Int i = 0; i < nc; i++) { + const auto &pf = C[i]; + E_Int stride = (E_Int)pf.size(); + for (E_Int j = 0; j < stride; j++) { + E_Int face = pf[j]; + if (fflags[face] == EXTERNAL) { + cflags[i] = EXTERNAL; + break; + } + } + } +} diff --git a/Cassiopee/XCore/XCore/AdaptMesh/FaceSort.cpp b/Cassiopee/XCore/XCore/AdaptMesh/FaceSort.cpp new file mode 100644 index 000000000..5157baf13 --- /dev/null +++ b/Cassiopee/XCore/XCore/AdaptMesh/FaceSort.cpp @@ -0,0 +1,42 @@ +#include "FaceSort.h" +#include "Mesh.h" + +void FaceSort_compute_data(const Mesh *M, FaceSort *mfaces, E_Int mcount) +{ + for (E_Int i = 0; i < mcount; i++) { + FaceSort *face = &mfaces[i]; + + E_Int tid = face->fid; + assert(M->ftype[tid] == TRI); + + E_Int *tri = Mesh_get_face(M, tid); + E_Int A = tri[0], B = tri[2], C = tri[4]; + + face->UX = (M->X[B] - M->X[A]); + face->UY = (M->Y[B] - M->Y[A]); + face->UZ = (M->Z[B] - M->Z[A]); + + face->VX = (M->X[C] - M->X[A]); + face->VY = (M->Y[C] - M->Y[A]); + face->VZ = (M->Z[C] - M->Z[A]); + + face->UU = face->UX*face->UX + face->UY*face->UY + face->UZ*face->UZ; + face->VV = face->VX*face->VX + face->VY*face->VY + face->VZ*face->VZ; + face->UV = face->UX*face->VX + face->UY*face->VY + face->UZ*face->VZ; + + face->inv_denom = face->UU*face->VV - face->UV*face->UV; + + assert(face->inv_denom != 0.0); + + face->inv_denom = 1.0 / face->inv_denom; + + face->fc[0] = (M->X[A] + M->X[B] + M->X[C]) / 3.0; + face->fc[1] = (M->Y[A] + M->Y[B] + M->Y[C]) / 3.0; + face->fc[2] = (M->Z[A] + M->Z[B] + M->Z[C]) / 3.0; + + // Store A + face->xa = M->X[A]; + face->ya = M->Y[A]; + face->za = M->Z[A]; + } +} \ No newline at end of file diff --git a/Cassiopee/XCore/XCore/AdaptMesh/FaceSort.h b/Cassiopee/XCore/XCore/AdaptMesh/FaceSort.h new file mode 100644 index 000000000..bacbac758 --- /dev/null +++ b/Cassiopee/XCore/XCore/AdaptMesh/FaceSort.h @@ -0,0 +1,17 @@ +#pragma once + +#include "xcore.h" + +struct Mesh; + +struct FaceSort { + E_Int fid; + E_Float fc[3]; + E_Float UX, UY, UZ; + E_Float VX, VY, VZ; + E_Float UU, VV, UV; + E_Float inv_denom; + E_Float xa, ya, za; +}; + +void FaceSort_compute_data(const Mesh *M, FaceSort *mfaces, E_Int mcount); \ No newline at end of file diff --git a/Cassiopee/XCore/XCore/AdaptMesh/H18.cpp b/Cassiopee/XCore/XCore/AdaptMesh/H18.cpp index d11a57d03..f53836132 100644 --- a/Cassiopee/XCore/XCore/AdaptMesh/H18.cpp +++ b/Cassiopee/XCore/XCore/AdaptMesh/H18.cpp @@ -21,6 +21,7 @@ void H18_refine(E_Int hexa, Mesh *M) { + // This should be replaced by get_ordered_data... H18_reorder(hexa, M); E_Int *cell = Mesh_get_cell(M, hexa); @@ -269,6 +270,12 @@ void H18_refine(E_Int hexa, Mesh *M) M->fref[fid] = FACE_NEW; } + // Update patterns + M->fpattern[M->nf] = DIR_X; + M->fpattern[M->nf+1] = DIR_X; + M->fpattern[M->nf+2] = DIR_X; + M->fpattern[M->nf+3] = DIR_X; + // Assemble children E_Int *child = NULL; @@ -653,7 +660,7 @@ void H18_reorder(E_Int hexa, Mesh *M) Right_shift(local, i0, 8); E_Int reorient = Mesh_get_reorient(M, RGT[0], hexa, normalIn_H[3]); if (reorient) std::reverse(local+1, local+8); - assert(local[0] == NODES[0]); + assert(local[0] == NODES[1]); assert(local[1] == NODES[9]); assert(local[2] == NODES[2]); NODES[6] = local[4]; @@ -932,4 +939,4 @@ void H18_reorder(E_Int hexa, Mesh *M) assert(local[6] == NODES[7]); assert(local[7] == NODES[13]); } -} \ No newline at end of file +} diff --git a/Cassiopee/XCore/XCore/AdaptMesh/H27.cpp b/Cassiopee/XCore/XCore/AdaptMesh/H27.cpp index 1eec0864a..5940975d5 100644 --- a/Cassiopee/XCore/XCore/AdaptMesh/H27.cpp +++ b/Cassiopee/XCore/XCore/AdaptMesh/H27.cpp @@ -1,5 +1,6 @@ #include "Hexa.h" #include "Quad.h" +#include "Mesh.h" void H27_refine(E_Int hexa, Mesh *M) { @@ -1349,7 +1350,6 @@ void H27_reorder(E_Int hexa, Mesh *M) } } -static void reconstruct_quad(Mesh *M, E_Int hexa, E_Int *fids, E_Int crange, E_Int normalIn, E_Int NODE, E_Int pn[4]) { @@ -1515,4 +1515,23 @@ E_Int check_canon_hexa(E_Int hexa, Mesh *M) assert(local[3] == NODES[7]); return 0; -} \ No newline at end of file +} + +void update_range_and_stride(Mesh *M, E_Int hexa, E_Int cpos, E_Int nchildren) +{ + E_Int *crange = Mesh_get_crange(M, hexa); + for (E_Int i = 0; i < M->cstride[hexa]; i++) { + crange[i] = 1; + } + + for (E_Int i = 0; i < nchildren; i++) { + E_Int child = cpos + i; + + M->cstride[child] = M->cstride[hexa]; + + crange = Mesh_get_crange(M, child); + for (E_Int j = 0; j < M->cstride[child]; j++) { + crange[j] = 1; + } + } +} diff --git a/Cassiopee/XCore/XCore/AdaptMesh/Hexa.h b/Cassiopee/XCore/XCore/AdaptMesh/Hexa.h index 93921bffa..3276e05b8 100644 --- a/Cassiopee/XCore/XCore/AdaptMesh/Hexa.h +++ b/Cassiopee/XCore/XCore/AdaptMesh/Hexa.h @@ -19,7 +19,8 @@ #pragma once #include "common/common.h" -#include "Mesh.h" + +struct Mesh; const E_Int normalIn_H[6] = {1, 0, 1, 0, 1, 0}; @@ -31,41 +32,9 @@ void H18_refine(E_Int hexa, Mesh *M); void H18_reorder(E_Int hexa, Mesh *M); -E_Int check_canon_hexa(E_Int hexa, Mesh *M); - -inline -void update_shell_pe(E_Int hexa, Mesh *M) -{ - const auto &children = M->cchildren.at(hexa); - - for (E_Int cid : children) { - E_Int *child = Mesh_get_cell(M, cid); +void reconstruct_quad(Mesh *M, E_Int hexa, E_Int *fids, E_Int crange, E_Int normalIn, + E_Int NODE, E_Int pn[4]); - for (E_Int j = 0; j < 6; j++) { - E_Int face = child[4*j]; - - if (M->owner[face] == hexa) M->owner[face] = cid; - else if (M->neigh[face] == hexa) M->neigh[face] = cid; - } - } -} - -inline -void update_range_and_stride(Mesh *M, E_Int hexa, E_Int cpos, E_Int nchildren) -{ - E_Int *crange = Mesh_get_crange(M, hexa); - for (E_Int i = 0; i < M->cstride[hexa]; i++) { - crange[i] = 1; - } - - for (E_Int i = 0; i < nchildren; i++) { - E_Int child = cpos + i; - - M->cstride[child] = M->cstride[hexa]; +E_Int check_canon_hexa(E_Int hexa, Mesh *M); - crange = Mesh_get_crange(M, child); - for (E_Int j = 0; j < M->cstride[child]; j++) { - crange[j] = 1; - } - } -} \ No newline at end of file +void update_range_and_stride(Mesh *M, E_Int hexa, E_Int cpos, E_Int nchildren); diff --git a/Cassiopee/XCore/XCore/AdaptMesh/Mesh.h b/Cassiopee/XCore/XCore/AdaptMesh/Mesh.h index a66f72f4d..bb4dc3941 100644 --- a/Cassiopee/XCore/XCore/AdaptMesh/Mesh.h +++ b/Cassiopee/XCore/XCore/AdaptMesh/Mesh.h @@ -24,6 +24,8 @@ #include "xcore.h" #include "common/common.h" +#include "Quad.h" +#include "Hexa.h" #define HEXA 0 #define TETRA 1 @@ -37,7 +39,18 @@ #define FACE_REFINED 1 #define FACE_NEW 2 +#define ISO 0 +#define DIR 1 + +#define DIR_ISO 0 +#define DIR_X 1 +#define DIR_Y 2 + struct Karray; +struct SkinGraph; +struct Point; +struct Vec3f; +struct ArrayI; struct BPatch { E_Int gid; @@ -125,6 +138,7 @@ struct Mesh { E_Int *cref; E_Int *fref; + E_Int *fpattern; E_Int *clevel; E_Int *flevel; @@ -165,6 +179,10 @@ struct Mesh { E_Int *xneis; E_Int *cneis; + + E_Int *ctag; + E_Int *ftag; + E_Int *ptag; Mesh(); }; @@ -173,8 +191,6 @@ struct Mesh { E_Int Mesh_set_cells_for_2D(Mesh *M); -void Mesh_get_cneis(Mesh *M, E_Int cid, E_Int &nn, E_Int neis[24]); - E_Int Mesh_set_face_types(Mesh *M); E_Int Mesh_set_cell_types(Mesh *M); @@ -197,6 +213,15 @@ void Mesh_update_global_face_ids(Mesh *M); E_Int Mesh_get_global_face_count(Mesh *M); +void Mesh_get_cneis(Mesh *M, E_Int cid, E_Int &nn, E_Int neis[24]); + +inline +E_Int Mesh_get_cnei(Mesh *M, E_Int cid, E_Int fid) +{ + assert(cid == M->owner[fid] || cid == M->neigh[fid]); + return (M->owner[fid] == cid) ? M->neigh[fid] : M->owner[fid]; +} + inline E_Int *Mesh_get_cell(Mesh *M, E_Int cid) { @@ -224,19 +249,19 @@ E_Int Mesh_get_sizeNFace(Mesh *M) } inline -E_Int *Mesh_get_face(Mesh *M, E_Int fid) +E_Int *Mesh_get_face(const Mesh *M, E_Int fid) { return &M->faces[8*fid]; } inline -E_Int *Mesh_get_frange(Mesh *M, E_Int fid) +E_Int *Mesh_get_frange(const Mesh *M, E_Int fid) { return &M->frange[4*fid]; } inline -E_Int *Mesh_get_fedges(Mesh *M, E_Int fid) +E_Int *Mesh_get_fedges(const Mesh *M, E_Int fid) { return &M->fedg[8*fid]; } @@ -257,7 +282,7 @@ E_Int Mesh_get_sizeNGon(Mesh *M) inline -void Mesh_get_fpoints(Mesh *M, E_Int fid, E_Int &np, E_Int pts[8]) +void Mesh_get_fpoints(const Mesh *M, E_Int fid, E_Int &np, E_Int pts[8]) { np = 0; E_Int *face = Mesh_get_face(M, fid); @@ -321,6 +346,8 @@ void Mesh_reset_adaptation_data(Mesh *M); void Mesh_reset_parallel_data(Mesh *M); +void Mesh_reset_tags(Mesh *M); + void Mesh_free(Mesh *M); @@ -344,6 +371,11 @@ E_Int Mesh_load_balance(Mesh *M); /* Adaptation */ +void Mesh_smooth_skin_ref_data(Mesh *M, const SkinGraph *skin_graph, + E_Int *fdat); + +void Mesh_smooth_cell_refinement_data(Mesh *M); + E_Int Mesh_smooth_cref(Mesh *M); void Mesh_get_ref_entities(Mesh *M, std::vector &ref_cells, @@ -352,6 +384,10 @@ void Mesh_get_ref_entities(Mesh *M, std::vector &ref_cells, void Mesh_resize_for_refinement(Mesh *M, const std::vector &ref_cells, const std::vector &ref_faces, const std::set &ref_edges); +void Mesh_resize_face_data(Mesh *M, E_Int new_nf); + +void Mesh_resize_cell_data(Mesh *M, E_Int new_nc); + void Mesh_sort_ref_entities_by_level(Mesh *M, std::vector &ref_cells, std::vector &ref_faces, std::set &ref_edges); @@ -369,6 +405,23 @@ void Mesh_refine_iso(Mesh *M, std::vector &ref_cells, void Mesh_refine_dir(Mesh *M, std::vector &ref_cells, std::vector &ref_faces, std::set &ref_edges); +inline +void update_shell_pe(E_Int parent, Mesh *M) +{ + const auto &children = M->cchildren.at(parent); + + for (E_Int cid : children) { + E_Int *child = Mesh_get_cell(M, cid); + + for (E_Int j = 0; j < M->cstride[cid]; j++) { + E_Int face = child[4*j]; + + if (M->owner[face] == parent) M->owner[face] = cid; + else if (M->neigh[face] == parent) M->neigh[face] = cid; + } + } +} + inline void Mesh_update_face_range_and_stride(Mesh *M, E_Int quad, E_Int fpos, E_Int nchild) { @@ -404,4 +457,87 @@ void Mesh_refine_or_get_edge_center(Mesh *M, E_Int p, E_Int q, E_Int &node) } else { node = it->second; } -} \ No newline at end of file +} + +inline +void refine_cell_dir(E_Int cell, Mesh *M) +{ + switch (M->ctype[cell]) { + case HEXA: + H18_refine(cell, M); + break; + default: + assert(0); + break; + } +} + +inline +void refine_face_dir(E_Int face, E_Int pattern, Mesh *M) +{ + switch (M->ftype[face]) { + case QUAD: { + if (pattern == ISO) Q9_refine(face, M); + else Q6_refine(face, M); + break; + } + default: + assert(0); + break; + } +} + +inline +E_Int get_face_pattern(E_Int fid, Mesh *M) +{ + E_Int own = M->owner[fid]; + E_Int fpos = -1; + E_Int *cell = Mesh_get_cell(M, own); + E_Int *crange = Mesh_get_crange(M, own); + + for (E_Int j = 0; j < M->cstride[own] && fpos == -1; j++) { + E_Int *pf = cell + 4*j; + for (E_Int k = 0; k < crange[j]; k++) { + E_Int face = pf[k]; + if (face == fid) { + fpos = j; + assert(k == 0); + break; + } + } + } + + assert(fpos != -1); + + if (fpos == 0 || fpos == 1) return ISO; + return DIR; +} + +void Mesh_triangulate_face(Mesh *M, E_Int fid); +void Mesh_triangulate_faces(Mesh *M, E_Int *faces, E_Int nf); + +void Mesh_face_to_prism(Mesh *M, E_Int fid); +void Mesh_generate_prisms(Mesh *M, E_Int *faces, E_Int nf); + +/* Extract */ + +void Mesh_extract_skin(const Mesh *M, E_Int *count, E_Int **skin); + +void Mesh_make_skin_connectivity(const Mesh *M, SkinGraph *skin_graph); + +void Mesh_make_skin_graph(const Mesh *M, SkinGraph *skin_graph); + +void Mesh_make_face_centers(const Mesh *M, const E_Int nf, const E_Int *skin, + Vec3f *fc); + +void Mesh_extract_points_from_ftag(const Mesh *M, ArrayI *pids); + +void Mesh_SkinGraph_compute_normals(const Mesh *M, SkinGraph *skin_graph); + +/* Locate */ + +bool Mesh_point_in_tri(const Mesh *M, const Point *p, E_Int tid); + +bool Mesh_point_in_quad(const Mesh *M, const Point *p, E_Int qid); + +bool Mesh_point_in_face(const Mesh *M, const Point *p, E_Int fid); \ No newline at end of file diff --git a/Cassiopee/XCore/XCore/AdaptMesh/MeshClean.cpp b/Cassiopee/XCore/XCore/AdaptMesh/MeshClean.cpp index 590404f26..336712bb5 100644 --- a/Cassiopee/XCore/XCore/AdaptMesh/MeshClean.cpp +++ b/Cassiopee/XCore/XCore/AdaptMesh/MeshClean.cpp @@ -78,6 +78,7 @@ void Mesh_reset_adaptation_data(Mesh *M) XFREE(M->cref); XFREE(M->fref); + XFREE(M->fpattern); XFREE(M->clevel); XFREE(M->flevel); @@ -113,6 +114,13 @@ void Mesh_reset_parallel_data(Mesh *M) XFREE(M->cneis); } +void Mesh_reset_tags(Mesh *M) +{ + XFREE(M->ctag); + XFREE(M->ftag); + XFREE(M->ptag); +} + void Mesh_free(Mesh *M) { Mesh_reset_base_data(M); @@ -120,5 +128,6 @@ void Mesh_free(Mesh *M) Mesh_reset_adaptation_data(M); Mesh_reset_comm_data(M); Mesh_reset_parallel_data(M); + Mesh_reset_tags(M); delete M; } diff --git a/Cassiopee/XCore/XCore/AdaptMesh/MeshDir.cpp b/Cassiopee/XCore/XCore/AdaptMesh/MeshDir.cpp index 31a6655f9..f271e4398 100644 --- a/Cassiopee/XCore/XCore/AdaptMesh/MeshDir.cpp +++ b/Cassiopee/XCore/XCore/AdaptMesh/MeshDir.cpp @@ -22,63 +22,6 @@ #include "Quad.h" #include "Edge.h" -#define ISO 0 -#define DIR 1 - -static inline -void refine_cell_dir(E_Int cell, Mesh *M) -{ - switch (M->ctype[cell]) { - case HEXA: - H18_refine(cell, M); - break; - default: - assert(0); - break; - } -} - -static inline -void refine_face_dir(E_Int face, E_Int pattern, Mesh *M) -{ - switch (M->ftype[face]) { - case QUAD: { - if (pattern == ISO) Q9_refine(face, M); - else Q6_refine(face, M); - break; - } - default: - assert(0); - break; - } -} - -static inline -E_Int get_face_pattern(E_Int fid, Mesh *M) -{ - E_Int own = M->owner[fid]; - E_Int fpos = -1; - E_Int *cell = Mesh_get_cell(M, own); - E_Int *crange = Mesh_get_crange(M, own); - - for (E_Int j = 0; j < M->cstride[own] && fpos == -1; j++) { - E_Int *pf = cell + 4*j; - for (E_Int k = 0; k < crange[j]; k++) { - E_Int face = pf[k]; - if (face == fid) { - fpos = j; - assert(k == 0); - break; - } - } - } - - assert(fpos != -1); - - if (fpos == 0 || fpos == 1) return ISO; - return DIR; -} - void Mesh_refine_dir(Mesh *M, std::vector &ref_cells, std::vector &ref_faces, std::set &ref_edges) { @@ -113,4 +56,45 @@ void Mesh_refine_dir(Mesh *M, std::vector &ref_cells, refine_cell_dir(cell, M); } } -} \ No newline at end of file +} + +/* +void Mesh_prepare_cell_ordering_and_face_refinement_patterns(Mesh *M, + const ArrayI *rcells, const ArrayI *rfaces, ArrayI *fpatterns) +{ + for (E_Int i = 0; i < rcells->count; i++) { + H18_reorder(rcells->ptr[i], M); + } + + fpatterns->count = rfaces->count; + fpatterns->ptr = (E_Int *)XMALLOC(rfaces->count * sizeof(E_Int)); + + for (E_Int i = 0; i < rfaces->count; i++) { + E_Int fid = rfaces->ptr[i]; + E_Int own = M->owner[fid]; + + E_Int *cell = Mesh_get_cell(M, own); + + E_Int pos = Get_pos(fid, cell, 4*M->cstride[own]); + E_Int side = pos / 4; + E_Int idx = pos % 4; + + if (side == 0 || side == 1) { + fpatterns->ptr[i] = ISO; + } else if (side == 2) { + + } else if (side == 3) { + + + } else if (side == 4) { + + + } else if (side == 5) { + + + } else { + + } + } +} +*/ diff --git a/Cassiopee/XCore/XCore/AdaptMesh/MeshExtract.cpp b/Cassiopee/XCore/XCore/AdaptMesh/MeshExtract.cpp new file mode 100644 index 000000000..1ac055c52 --- /dev/null +++ b/Cassiopee/XCore/XCore/AdaptMesh/MeshExtract.cpp @@ -0,0 +1,161 @@ +#include "Mesh.h" +#include "Skin.h" +#include "common/mem.h" +#include "Array.h" +#include "FaceSort.h" +#include "Vec.h" + +void Mesh_extract_points_from_ftag(const Mesh *M, ArrayI *pids) +{ + E_Int *ptag = (E_Int *)XMALLOC(M->np * sizeof(E_Int)); + memset(ptag, 0, M->np * sizeof(E_Int)); + pids->count = 0; + + for (E_Int fid = 0; fid < M->nf; fid++) { + if (M->ftag[fid] != 1) continue; + E_Int *face = Mesh_get_face(M, fid); + E_Int *frange = Mesh_get_frange(M, fid); + for (E_Int i = 0; i < M->fstride[fid]; i++) { + E_Int *pn = face + 2*i; + for (E_Int j = 0; j < frange[i]; j++) { + E_Int pid = pn[j]; + pids->count += (ptag[pid] == 0); + ptag[pid] = 1; + } + } + } + + pids->ptr = (E_Int *)XMALLOC(pids->count * sizeof(E_Int)); + E_Int *ptr = pids->ptr; + + for (E_Int pid = 0; pid < M->np; pid++) { + if (ptag[pid] == 1) + *ptr++ = pid; + } + + XFREE(ptag); +} + +void Mesh_extract_faces_from_ftag(const Mesh *M, FaceSort **mfaces, + E_Int *mcount) +{ + E_Int count = 0; + + for (E_Int i = 0; i < M->nf; i++) { + count += (M->ftag[i] == 1); + } + + *mfaces = (FaceSort *)XMALLOC(count * sizeof(FaceSort)); + + *mcount = count; + + count = 0; + + for (E_Int i = 0; i < M->nf; i++) { + if (M->ftag[i] == 1) { + assert(M->ftype[i] == TRI); + (*mfaces)[count++].fid = i; + } + } + + assert(*mcount == count); +} + +void Mesh_extract_skin(const Mesh *M, E_Int *count, E_Int **skin) +{ + *count = 0; + + for (E_Int fid = 0; fid < M->nf; fid++) { + *count += (M->neigh[fid] == -1); + } + + *skin = (E_Int *)XMALLOC(*count * sizeof(E_Int)); + E_Int *ptr = *skin; + + for (E_Int fid = 0; fid < M->nf; fid++) { + if (M->neigh[fid] == -1) + *ptr++ = fid; + } +} + +void Mesh_make_skin_connectivity(const Mesh *M, SkinGraph *skin_graph) +{ + // Count + skin_graph->xadj = (E_Int *)XMALLOC((skin_graph->nf+1) * sizeof(E_Int)); + E_Int *xadj = skin_graph->xadj; + xadj[0] = 0; + + for (E_Int i = 0; i < skin_graph->nf; i++) { + E_Int fid = skin_graph->skin[i]; + const E_Int *frange = Mesh_get_frange(M, fid); + xadj[i+1] = 0; + for (E_Int j = 0; j < M->fstride[fid]; j++) + xadj[i+1] += frange[j]; + xadj[i+1] += xadj[i]; + } + + skin_graph->fpts = (E_Int *)XMALLOC(xadj[skin_graph->nf] * sizeof(E_Int)); + + // Populate + E_Int *ptr = skin_graph->fpts; + + for (E_Int i = 0; i < skin_graph->nf; i++) { + E_Int fid = skin_graph->skin[i]; + const E_Int *face = Mesh_get_face(M, fid); + const E_Int *frange = Mesh_get_frange(M, fid); + for (E_Int j = 0; j < M->fstride[fid]; j++) { + const E_Int *pn = face + 2*j; + for (E_Int k = 0; k < frange[j]; k++) + *ptr++ = pn[k]; + } + } +} + +void Mesh_make_skin_graph(const Mesh *M, SkinGraph *skin_graph) +{ + Mesh_extract_skin(M, &skin_graph->nf, &skin_graph->skin); + Mesh_make_skin_connectivity(M, skin_graph); + SkinGraph_make_skin_neighbours(skin_graph); +} + +void Mesh_make_face_centers(const Mesh *M, const E_Int nf, const E_Int *skin, + Vec3f *fc) +{ + for (E_Int i = 0; i < nf; i++) { + E_Int fid = skin[i]; + const E_Int *face = Mesh_get_face(M, fid); + const E_Int *frange = Mesh_get_frange(M, fid); + fc[i].x = fc[i].y = fc[i].z = 0.0; + E_Int np = 0; + for (E_Int j = 0; j < M->fstride[fid]; j++) { + const E_Int *pn = face + 2*j; + for (E_Int k = 0; k < frange[j]; k++) { + fc[i].x += M->X[pn[k]]; + fc[i].y += M->Y[pn[k]]; + fc[i].z += M->Z[pn[k]]; + np++; + } + } + fc[i].x /= np; fc[i].y /= np; fc[i].z /= np; + } +} + +void Mesh_SkinGraph_compute_normals(const Mesh *M, SkinGraph *skin_graph) +{ + assert(!skin_graph->fnml); + skin_graph->fnml = (Vec3f *)XMALLOC(skin_graph->nf * sizeof(Vec3f)); + + for (E_Int i = 0; i < skin_graph->nf; i++) { + E_Int fid = skin_graph->skin[i]; + E_Int *face = Mesh_get_face(M, fid); + E_Int A = face[0], B = face[2], C = face[4]; + E_Float U[3] = {M->X[B]-M->X[A], M->Y[B]-M->Y[A], M->Z[B]-M->Z[A]}; + E_Float V[3] = {M->X[C]-M->X[A], M->Y[C]-M->Y[A], M->Z[C]-M->Z[A]}; + Vec3f *N = &skin_graph->fnml[i]; + K_MATH::cross(U, V, (E_Float *)N); + E_Float NORM = K_MATH::norm((E_Float *)N, 3); + N->x /= NORM; + N->y /= NORM; + N->z /= NORM; + } +} \ No newline at end of file diff --git a/Cassiopee/XCore/XCore/AdaptMesh/MeshIO.cpp b/Cassiopee/XCore/XCore/AdaptMesh/MeshIO.cpp index 8714a4c01..d4f9b3fa8 100644 --- a/Cassiopee/XCore/XCore/AdaptMesh/MeshIO.cpp +++ b/Cassiopee/XCore/XCore/AdaptMesh/MeshIO.cpp @@ -576,7 +576,9 @@ PyObject *export_conformal_mesh(Mesh *M) for (E_Int j = 0; j < M->fstride[i]; j++) { E_Int *pn = face + 2*j; - for (E_Int k = 0; k < frange[j]; k++) *ptr++ = pn[k] + 1; + for (E_Int k = 0; k < frange[j]; k++) { + *ptr++ = pn[k] + 1; + } } } @@ -606,7 +608,9 @@ PyObject *export_conformal_mesh(Mesh *M) for (E_Int j = 0; j < M->cstride[i]; j++) { E_Int *pf = cell + 4*j; - for (E_Int k = 0; k < crange[j]; k++) *ptr++ = pf[k] + 1; + for (E_Int k = 0; k < crange[j]; k++) { + *ptr++ = pf[k] + 1; + } } } @@ -688,4 +692,4 @@ PyObject *Mesh_export_karray(Mesh *M, E_Int conformize) if (conformize) return export_conformal_mesh(M); return export_BE_mesh(M); -} +} \ No newline at end of file diff --git a/Cassiopee/XCore/XCore/AdaptMesh/MeshInit.cpp b/Cassiopee/XCore/XCore/AdaptMesh/MeshInit.cpp index aa11d9067..09025b121 100644 --- a/Cassiopee/XCore/XCore/AdaptMesh/MeshInit.cpp +++ b/Cassiopee/XCore/XCore/AdaptMesh/MeshInit.cpp @@ -54,6 +54,7 @@ Mesh::Mesh() cref = NULL; fref = NULL; + fpattern = NULL; clevel = NULL; flevel = NULL; @@ -82,6 +83,10 @@ Mesh::Mesh() l2gc = l2gf = NULL; xneis = cneis = NULL; + + ctag = NULL; + ftag = NULL; + ptag = NULL; } Mesh *Mesh_from_Karray(Karray *karray) diff --git a/Cassiopee/XCore/XCore/AdaptMesh/MeshIso.cpp b/Cassiopee/XCore/XCore/AdaptMesh/MeshIso.cpp index e9185869e..9b181c6f9 100644 --- a/Cassiopee/XCore/XCore/AdaptMesh/MeshIso.cpp +++ b/Cassiopee/XCore/XCore/AdaptMesh/MeshIso.cpp @@ -69,93 +69,3 @@ void Mesh_refine_iso(Mesh *M, std::vector &ref_cells, } } } - -/* -void Mesh_refine_iso(Mesh *M, std::vector &ref_cells, - std::vector &ref_faces, std::set &ref_edges) -{ - // Adapt the cells that do not need face adaptation - - if (ref_faces.empty()) { - for (E_Int cell : ref_cells) refine_cell_iso(cell, M); - - return; - } - - // Adapt the faces that are not linked to local cells - - if (ref_cells.empty()) { - for (E_Int face : ref_faces) refine_face_iso(face, M); - - return; - } - - // Refine first cells whose level is less than the smallest ref_face level - // Note(Imad): the faces are pre-sorted by increasing level - - E_Int min_flvl = M->flevel[ref_faces[0]]; - - // Refine the "lagging" cells - - E_Int cell_start = 0; - - while (M->clevel[ref_cells[cell_start]] < min_flvl) { - refine_cell_iso(ref_cells[cell_start], M); - cell_start++; - } - - // Now refine lagging faces - - E_Int min_clvl = M->clevel[ref_cells[cell_start]]; - - E_Int face_start = 0; - - while (M->flevel[ref_faces[face_start]] < min_clvl) { - refine_face_iso(ref_faces[face_start], M); - face_start++; - } - - - // At this point the remaining ref faces/cells should be at the same level - - E_Int cells_left = (E_Int)ref_cells.size() - cell_start; - E_Int faces_left = (E_Int)ref_faces.size() - face_start; - - // Refine faces and cells, by increasing level - - E_Int current_lvl = M->clevel[ref_cells[cell_start]]; - - while (cells_left || faces_left) { - - while (faces_left) { - - // Break early if refinement would overstep current level - - if (M->flevel[ref_faces[face_start]] > current_lvl) break; - - E_Int face = ref_faces[face_start]; - - refine_face_iso(face, M); - - face_start++; - faces_left--; - } - - while (cells_left) { - - // Break early if refinement would overstep current level - - if (M->clevel[ref_cells[cell_start]] > current_lvl) break; - - E_Int cell = ref_cells[cell_start]; - - refine_cell_iso(cell, M); - - cell_start++; - cells_left--; - } - - current_lvl++; - } -} -*/ \ No newline at end of file diff --git a/Cassiopee/XCore/XCore/AdaptMesh/MeshLocate.cpp b/Cassiopee/XCore/XCore/AdaptMesh/MeshLocate.cpp new file mode 100644 index 000000000..7e27b2dc4 --- /dev/null +++ b/Cassiopee/XCore/XCore/AdaptMesh/MeshLocate.cpp @@ -0,0 +1,64 @@ +#include "Mesh.h" +#include "Point.h" + +bool Mesh_point_in_tri(const Mesh *M, const Point *p, E_Int tid) +{ + const E_Int *face = Mesh_get_face(M, tid); + E_Int A = face[0], B = face[2], C = face[4]; + return Point_in_tri(p->x, p->y, p->z, + M->X[A], M->Y[A], M->Z[A], + M->X[B], M->Y[B], M->Z[B], + M->X[C], M->Y[C], M->Z[C]); +} + +bool Mesh_point_in_quad(const Mesh *M, const Point *p, E_Int qid) +{ + // TODO(Imad): maybe compute face centers once in pre-pass + // Star the quad into 4 triangles + E_Float O[3] = {0.0, 0.0, 0.0}; + const E_Int *face = Mesh_get_face(M, qid); + E_Int A = face[0], B = face[2], C = face[4], D = face[6]; + O[0] = (M->X[A] + M->X[B] + M->X[C] + M->X[D]) * 0.25; + O[1] = (M->Y[A] + M->Y[B] + M->Y[C] + M->Y[D]) * 0.25; + O[2] = (M->Z[A] + M->Z[B] + M->Z[C] + M->Z[D]) * 0.25; + + bool hit = false; + + // First triangle + hit = Point_in_tri(p->x, p->y, p->z, + O[0], O[1], O[2], + M->X[A], M->Y[A], M->Z[A], + M->X[B], M->Y[B], M->Z[B]); + if (hit) return true; + + // Second triangle + hit = Point_in_tri(p->x, p->y, p->z, + O[0], O[1], O[2], + M->X[B], M->Y[B], M->Z[B], + M->X[C], M->Y[C], M->Z[C]); + if (hit) return true; + + + // Third triangle + hit = Point_in_tri(p->x, p->y, p->z, + O[0], O[1], O[2], + M->X[C], M->Y[C], M->Z[C], + M->X[D], M->Y[D], M->Z[D]); + if (hit) return true; + + // Fourth triangle + hit = Point_in_tri(p->x, p->y, p->z, + O[0], O[1], O[2], + M->X[D], M->Y[D], M->Z[D], + M->X[A], M->Y[A], M->Z[A]); + if (hit) return true; + + return false; +} + +bool Mesh_point_in_face(const Mesh *M, const Point *p, E_Int fid) +{ + if (M->ftype[fid] == QUAD) return Mesh_point_in_quad(M, p, fid); + assert(M->ftype[fid] == TRI); + return Mesh_point_in_tri(M, p, fid); +} \ No newline at end of file diff --git a/Cassiopee/XCore/XCore/AdaptMesh/MeshRefine.cpp b/Cassiopee/XCore/XCore/AdaptMesh/MeshRefine.cpp index 21fcb7101..45be05d12 100644 --- a/Cassiopee/XCore/XCore/AdaptMesh/MeshRefine.cpp +++ b/Cassiopee/XCore/XCore/AdaptMesh/MeshRefine.cpp @@ -67,6 +67,54 @@ void Mesh_get_ref_entities(Mesh *M, std::vector &ref_cells, } } +void Mesh_resize_face_data(Mesh *M, E_Int new_nf) +{ + M->faces = (E_Int *)XRESIZE(M->faces, (8 * new_nf) * sizeof(E_Int)); + M->frange = (E_Int *)XRESIZE(M->frange, (4 * new_nf) * sizeof(E_Int)); + M->fstride = (E_Int *)XRESIZE(M->fstride, new_nf * sizeof(E_Int)); + + for (E_Int i = 8*M->nf; i < 8*new_nf; i++) M->faces[i] = -1; + for (E_Int i = 4*M->nf; i < 4*new_nf; i++) M->frange[i] = -1; + for (E_Int i = M->nf; i < new_nf; i++) M->fstride[i] = -1; + + M->owner = (E_Int *)XRESIZE(M->owner, new_nf * sizeof(E_Int)); + M->neigh = (E_Int *)XRESIZE(M->neigh, new_nf * sizeof(E_Int)); + + for (E_Int i = M->nf; i < new_nf; i++) { + M->owner[i] = -1; + M->neigh[i] = -1; + } + + M->ftype = (E_Int *)XRESIZE(M->ftype, new_nf * sizeof(E_Int)); + M->flevel = (E_Int *)XRESIZE(M->flevel, new_nf * sizeof(E_Int)); + + M->fref = (E_Int *)XRESIZE(M->fref, new_nf * sizeof(E_Int)); + for (E_Int i = M->nf; i < new_nf; i++) M->fref[i] = 0; + + M->fparent = (E_Int *)XRESIZE(M->fparent, new_nf * sizeof(E_Int)); + for (E_Int i = M->nf; i < new_nf; i++) M->fparent[i] = -1; + + M->ftag = (E_Int *)XRESIZE(M->ftag, new_nf * sizeof(E_Int)); + for (E_Int i = M->nf; i < new_nf; i++) M->ftag[i] = -1; + + M->fpattern = (E_Int *)XRESIZE(M->fpattern, new_nf * sizeof(E_Int)); + for (E_Int i = M->nf; i < new_nf; i++) M->fpattern[i] = -1; +} + +void Mesh_resize_cell_data(Mesh *M, E_Int new_nc) +{ + M->cells = (E_Int *)XRESIZE(M->cells, (24 * new_nc) * sizeof(E_Int)); + M->crange = (E_Int *)XRESIZE(M->crange, (6 * new_nc) * sizeof(E_Int)); + M->cstride = (E_Int *)XRESIZE(M->cstride, new_nc * sizeof(E_Int)); + + for (E_Int i = 24*M->nc; i < 24*new_nc; i++) M->cells[i] = -1; + for (E_Int i = 6*M->nc; i < 6*new_nc; i++) M->crange[i] = -1; + for (E_Int i = M->nc; i < new_nc; i++) M->cstride[i] = -1; + + M->ctype = (E_Int *)XRESIZE(M->ctype, new_nc * sizeof(E_Int)); + M->clevel = (E_Int *)XRESIZE(M->clevel, new_nc * sizeof(E_Int)); +} + void Mesh_resize_for_refinement(Mesh *M, const std::vector &ref_cells, const std::vector &ref_faces, const std::set &ref_edges) { @@ -74,7 +122,7 @@ void Mesh_resize_for_refinement(Mesh *M, const std::vector &ref_cells, E_Int cell_incr = ref_cells.size() * 7; - // 3 new faces per refined faces + 13 new faces per refined cell + // 3 new faces per refined face + 12 new faces per refined cell E_Int face_incr = ref_faces.size() * 3 + ref_cells.size() * 12; @@ -94,41 +142,8 @@ void Mesh_resize_for_refinement(Mesh *M, const std::vector &ref_cells, M->Y = (E_Float *)XRESIZE(M->Y, new_np * sizeof(E_Float)); M->Z = (E_Float *)XRESIZE(M->Z, new_np * sizeof(E_Float)); - M->faces = (E_Int *)XRESIZE(M->faces, (8 * new_nf) * sizeof(E_Int)); - M->frange = (E_Int *)XRESIZE(M->frange, (4 * new_nf) * sizeof(E_Int)); - M->fstride = (E_Int *)XRESIZE(M->fstride, new_nf * sizeof(E_Int)); - - for (E_Int i = 8*M->nf; i < 8*new_nf; i++) M->faces[i] = -1; - for (E_Int i = 4*M->nf; i < 4*new_nf; i++) M->frange[i] = -1; - for (E_Int i = M->nf; i < new_nf; i++) M->fstride[i] = -1; - - M->cells = (E_Int *)XRESIZE(M->cells, (24 * new_nc) * sizeof(E_Int)); - M->crange = (E_Int *)XRESIZE(M->crange, (6 * new_nc) * sizeof(E_Int)); - M->cstride = (E_Int *)XRESIZE(M->cstride, new_nc * sizeof(E_Int)); - - for (E_Int i = 24*M->nc; i < 24*new_nc; i++) M->cells[i] = -1; - for (E_Int i = 6*M->nc; i < 6*new_nc; i++) M->crange[i] = -1; - for (E_Int i = M->nc; i < new_nc; i++) M->cstride[i] = -1; - - M->owner = (E_Int *)XRESIZE(M->owner, new_nf * sizeof(E_Int)); - M->neigh = (E_Int *)XRESIZE(M->neigh, new_nf * sizeof(E_Int)); - - for (E_Int i = M->nf; i < new_nf; i++) { - M->owner[i] = -1; - M->neigh[i] = -1; - } - - M->ftype = (E_Int *)XRESIZE(M->ftype, new_nf * sizeof(E_Int)); - M->flevel = (E_Int *)XRESIZE(M->flevel, new_nf * sizeof(E_Int)); - - M->ctype = (E_Int *)XRESIZE(M->ctype, new_nc * sizeof(E_Int)); - M->clevel = (E_Int *)XRESIZE(M->clevel, new_nc * sizeof(E_Int)); - - M->fref = (E_Int *)XRESIZE(M->fref, new_nf * sizeof(E_Int)); - for (E_Int i = M->nf; i < new_nf; i++) M->fref[i] = 0; - - M->fparent = (E_Int *)XRESIZE(M->fparent, new_nf * sizeof(E_Int)); - for (E_Int i = M->nf; i < new_nf; i++) M->fparent[i] = -1; + Mesh_resize_face_data(M, new_nf); + Mesh_resize_cell_data(M, new_nc); } void Mesh_sort_ref_entities_by_level(Mesh *M, @@ -140,4 +155,4 @@ void Mesh_sort_ref_entities_by_level(Mesh *M, std::sort(ref_cells.begin(), ref_cells.end(), [&] (E_Int i, E_Int j) { return M->clevel[i] < M->clevel[j]; }); -} \ No newline at end of file +} diff --git a/Cassiopee/XCore/XCore/AdaptMesh/MeshReorder.cpp b/Cassiopee/XCore/XCore/AdaptMesh/MeshReorder.cpp new file mode 100644 index 000000000..86f14f806 --- /dev/null +++ b/Cassiopee/XCore/XCore/AdaptMesh/MeshReorder.cpp @@ -0,0 +1,7 @@ +#include "Mesh.h" + +void Mesh_reorder_hexa(Mesh *M, E_Int hexa, E_Int base) +{ + + +} diff --git a/Cassiopee/XCore/XCore/AdaptMesh/MeshSmooth.cpp b/Cassiopee/XCore/XCore/AdaptMesh/MeshSmooth.cpp index d48810d6c..865b753ff 100644 --- a/Cassiopee/XCore/XCore/AdaptMesh/MeshSmooth.cpp +++ b/Cassiopee/XCore/XCore/AdaptMesh/MeshSmooth.cpp @@ -21,26 +21,22 @@ #include "Mesh.h" #include "common/mem.h" -inline -E_Int Mesh_get_cnei(Mesh *M, E_Int cid, E_Int fid) -{ - assert(cid == M->owner[fid] || cid == M->neigh[fid]); - return (M->owner[fid] == cid) ? M->neigh[fid] : M->owner[fid]; -} - void Mesh_get_cneis(Mesh *M, E_Int cid, E_Int &nn, E_Int neis[24]) { E_Int *cell = Mesh_get_cell(M, cid); E_Int *crange = Mesh_get_crange(M, cid); E_Int cstride = M->cstride[cid]; + nn = 0; + for (E_Int i = 0; i < cstride; i++) { E_Int *pf = cell + 4*i; for (E_Int j = 0; j < crange[i]; j++) { E_Int face = pf[j]; E_Int nei = Mesh_get_cnei(M, cid, face); - if (nei != -1) neis[nn++] = nei; + if (nei != -1 && nei != neis[nn]) + neis[nn++] = nei; } } } @@ -57,15 +53,15 @@ E_Int Mesh_smooth_cref_local(Mesh *M) E_Int cid = stk.top(); stk.pop(); - E_Int nn = 0; - E_Int neis[24]; + E_Int nn, neis[24]; Mesh_get_cneis(M, cid, nn, neis); - E_Int incr_cell = M->cref[cid] + M->clevel[cid]; + //E_Int incr_cell = M->cref[cid] + M->clevel[cid]; for (E_Int i = 0; i < nn; i++) { E_Int nei = neis[i]; + E_Int incr_cell = M->cref[cid] + M->clevel[cid]; E_Int incr_nei = M->cref[nei] + M->clevel[nei]; E_Int diff = abs(incr_nei - incr_cell); @@ -74,7 +70,8 @@ E_Int Mesh_smooth_cref_local(Mesh *M) E_Int cell_to_mod = incr_cell > incr_nei ? nei : cid; - M->cref[cell_to_mod] += 1; + M->cref[cell_to_mod] += diff-1; + //M->cref[cell_to_mod] += 1; stk.push(cell_to_mod); } @@ -139,3 +136,35 @@ E_Int Mesh_smooth_cref(Mesh *M) return 0; } + +// TODO(Imad): exactly the same as Mesh_smooth_cref... +void Mesh_smooth_cell_refinement_data(Mesh *M) +{ + E_Int nc = M->nc; + + std::stack stk; + + for (E_Int cid = 0; cid < nc; cid++) { + if (M->cref[cid] > 0) + stk.push(cid); + } + + while (!stk.empty()) { + E_Int cid = stk.top(); + stk.pop(); + + E_Int nn, neis[24]; + Mesh_get_cneis(M, cid, nn, neis); + + for (E_Int i = 0; i < nn; i++) { + E_Int nei = neis[i]; + E_Int incr_nei = M->cref[nei] + M->clevel[nei]; + E_Int incr_cid = M->cref[cid] + M->clevel[cid]; + E_Int diff = abs(incr_nei - incr_cid); + if (diff <= 1) continue; + E_Int idx_to_modify = incr_cid > incr_nei ? nei : cid; + M->cref[idx_to_modify] += diff-1; + stk.push(idx_to_modify); + } + } +} diff --git a/Cassiopee/XCore/XCore/AdaptMesh/MeshTriangulate.cpp b/Cassiopee/XCore/XCore/AdaptMesh/MeshTriangulate.cpp new file mode 100644 index 000000000..2218b5f5b --- /dev/null +++ b/Cassiopee/XCore/XCore/AdaptMesh/MeshTriangulate.cpp @@ -0,0 +1,319 @@ +#include "Mesh.h" +#include "Hexa.h" + +void Mesh_face_to_prism(Mesh *M, E_Int fid) +{ + E_Int hexa = M->owner[fid]; + assert(M->ctype[hexa] == HEXA); + + // Make fid the bottom face + E_Int *cell = Mesh_get_cell(M, hexa); + E_Int pos = Get_pos(fid, cell, 24); + assert(pos != -1); + assert(pos % 4 == 0); + + Right_shift(cell, pos, 24); + assert(cell[0] == fid); + E_Int *crange = Mesh_get_crange(M, hexa); + Right_shift(crange, pos/4, 6); + + // Get the top and left face + // Top face shares no point, left face shares p0 and p3 + + E_Int *quad = Mesh_get_face(M, fid); + + E_Int map[4]; + for (E_Int i = 0; i < 4; i++) + map[i] = quad[2*i]; + E_Int reorient = Mesh_get_reorient(M, fid, hexa, normalIn_H[0]); + E_Int BOT[2] = {fid, M->nf}; + if (reorient) { + std::swap(map[1], map[3]); + std::swap(BOT[0], BOT[1]); + } + + E_Int top, lft, rgt, fro, bck; + + for (E_Int i = 1; i < 6; i++) { + E_Int f = cell[4*i]; + E_Int *face = Mesh_get_face(M, f); + E_Int common[4] = {0, 0, 0, 0}; + + for (E_Int j = 0; j < 8; j += 2) { + E_Int p = face[j]; + for (E_Int k = 0; k < 4; k++) { + if (p == map[k]) { + common[k] = 1; + break; + } + } + } + + if (common[0] && common[3]) lft = f; + else if (common[1] && common[2]) rgt = f; + else if (common[1] && common[0]) fro = f; + else if (common[2] && common[3]) bck = f; + else top = f; + + } + + assert(top != -1); + assert(lft != -1); + assert(rgt != -1); + assert(fro != -1); + assert(bck != -1); + + // Setup the canon config + + E_Int NODES[8] = {}; + for (E_Int i = 0; i < 4; i++) + NODES[i] = map[i]; + + E_Int local[4]; + + // Align left with bottom + + E_Int *pn = Mesh_get_face(M, lft); + for (E_Int i = 0; i < 4; i++) local[i] = pn[2*i]; + pos = Get_pos(NODES[0], local, 4); + assert(pos != -1); + assert(pos % 2 == 0); + Right_shift(local, pos, 4); + assert(local[0] == NODES[0]); + reorient = Mesh_get_reorient(M, lft, hexa, normalIn_H[2]); + if (reorient) std::swap(local[1], local[3]); + assert(local[1] == NODES[3]); + NODES[4] = local[3]; + NODES[7] = local[2]; + + // Align top with left + pn = Mesh_get_face(M, top); + for (E_Int i = 0; i < 4; i++) local[i] = pn[2*i]; + pos = Get_pos(NODES[4], local, 4); + assert(pos != -1); + assert(pos % 2 == 0); + Right_shift(local, pos, 4); + assert(local[0] == NODES[4]); + E_Int TOP[2] = {top, M->nf+1}; + reorient = Mesh_get_reorient(M, top, hexa, normalIn_H[1]); + if (reorient) { + std::swap(local[1], local[3]); + if (pos == 0 || pos == 3) std::swap(TOP[0], TOP[1]); + } else { + if (pos == 2 || pos == 3) std::swap(TOP[0], TOP[1]); + } + assert(local[3] == NODES[7]); + NODES[5] = local[1]; + NODES[6] = local[2]; + + // Triangulate bottom + M->fstride[fid] = 3; + quad = Mesh_get_face(M, fid); + E_Int *qrange = Mesh_get_frange(M, fid); + qrange[2] = 1; + + M->fstride[M->nf] = 3; + E_Int *tri = Mesh_get_face(M, M->nf); + tri[0] = quad[6]; tri[2] = quad[0]; tri[4] = quad[4]; + E_Int *trange = Mesh_get_frange(M, M->nf); + trange[0] = qrange[3]; trange[1] = 1; trange[2] = qrange[2]; + + // Triangulate top + M->fstride[top] = 3; + quad = Mesh_get_face(M, top); + qrange = Mesh_get_frange(M, top); + qrange[2] = 1; + + M->fstride[M->nf+1] = 3; + tri = Mesh_get_face(M, M->nf+1); + tri[0] = quad[6]; tri[2] = quad[0]; tri[4] = quad[4]; + trange = Mesh_get_frange(M, M->nf+1); + trange[0] = qrange[3]; trange[1] = 1; trange[2] = qrange[2]; + + // Update face info + M->fref[fid] = M->fref[top] = FACE_REFINED; + M->fref[M->nf] = M->fref[M->nf+1] = FACE_NEW; + M->flevel[M->nf] = M->flevel[fid]; + M->flevel[M->nf+1] = M->flevel[top]; + M->ftag[M->nf] = M->ftag[fid]; + M->ftag[M->nf+1] = M->ftag[top]; + M->fparent[M->nf] = M->nf; + M->fparent[M->nf+1] = M->nf+1; + M->owner[M->nf] = M->owner[fid]; + M->neigh[M->nf] = M->neigh[fid]; + M->owner[M->nf+1] = M->owner[top]; + M->neigh[M->nf+1] = M->neigh[top]; + M->ftype[fid] = M->ftype[M->nf] = TRI; + M->ftype[top] = M->ftype[M->nf+1] = TRI; + + + // Conformize parent cells + E_Int ret, own, nei; + + M->fchildren[fid] = {fid, M->nf}; + M->fchildren[top] = {top, M->nf+1}; + + // fid + ret = Mesh_conformize_cell_face(M, hexa, fid, M->nf, 2); + assert(ret == 0); + nei = M->neigh[fid]; + assert(nei == -1); + /* + if (nei != -1) { + ret = Mesh_conformize_cell_face(M, nei, hexa, M->nf, 2); + assert(ret == 0); + } + */ + + // top + own = M->owner[top]; + ret = Mesh_conformize_cell_face(M, own, top, M->nf+1, 2); + assert(ret == 0); + nei = M->neigh[top]; + if (nei != -1) { + ret = Mesh_conformize_cell_face(M, nei, top, M->nf+1, 2); + assert(ret == 0); + } + + // Set internal face in ngon + + E_Int *iquad = Mesh_get_face(M, M->nf+2); + iquad[0] = NODES[0]; iquad[2] = NODES[2]; + iquad[4] = NODES[6]; iquad[6] = NODES[4]; + + // Set internal face stride, range and state + E_Int *irange = Mesh_get_frange(M, M->nf+2); + for (E_Int i = 0; i < 4; i++) irange[i] = 1; + M->fstride[M->nf+2] = 4; + M->fref[M->nf+2] = FACE_NEW; + + // Make the first prism + E_Int *prism = Mesh_get_cell(M, hexa); + prism[0] = BOT[0]; prism[4] = TOP[0]; + prism[8] = M->nf+2; prism[12] = rgt; + prism[16] = fro; + + // Make the second prism + prism = Mesh_get_cell(M, M->nc); + prism[0] = BOT[1]; prism[4] = TOP[1]; + prism[8] = bck; prism[12] = M->nf+2; + prism[16] = lft; + + // Update ranges and strides + + M->cstride[hexa] = 5; + M->cstride[M->nc] = 5; + + E_Int *hrange = Mesh_get_crange(M, hexa); + + E_Int *prange = Mesh_get_crange(M, M->nc); + prange[0] = 1; prange[1] = 1; prange[2] = hrange[5]; + prange[3] = 1; prange[4] = hrange[2]; + + hrange[0] = 1; hrange[1] = 1; hrange[2] = 1; + + // Update adaptation info + + M->clevel[M->nc] = M->clevel[hexa]; + M->ctype[M->nc] = M->ctype[hexa] = PENTA; + + M->cchildren[hexa] = {hexa, M->nc}; + + // Set shell faces owns and neis + + update_shell_pe(hexa, M); + + // Set PE pf internal face + + M->owner[M->nf+2] = M->nc; + M->neigh[M->nf+2] = hexa; + + M->nc++; + M->nf += 3; +} + +void Mesh_generate_prisms(Mesh *M, E_Int *faces, E_Int nf) +{ + E_Int new_nf = M->nf + 3*nf; + E_Int new_nc = M->nc + nf; + + Mesh_resize_face_data(M, new_nf); + Mesh_resize_cell_data(M, new_nc); + + for (E_Int i = 0; i < nf; i++) { + E_Int fid = faces[i]; + Mesh_face_to_prism(M, fid); + } + + Mesh_update_bpatches(M); + + // TODO(Imad): parallel consequences +} + +void Mesh_triangulate_face(Mesh *M, E_Int fid) +{ + // Get quad data + E_Int *quad = Mesh_get_face(M, fid); + E_Int *qrange = Mesh_get_frange(M, fid); + + // Create the second triangle + E_Int *tri = Mesh_get_face(M, M->nf); + E_Int *trange = Mesh_get_frange(M, M->nf); + + M->fstride[M->nf] = 3; + tri[0] = quad[4]; tri[1] = quad[5]; + tri[2] = quad[6]; tri[3] = quad[7]; + tri[4] = quad[0]; assert(tri[5] == -1); + trange[0] = qrange[2]; + trange[1] = qrange[4]; + trange[2] = 1; + + // Update first quad -> triangle + M->fstride[fid] = 3; + qrange[2] = 1; + + // Conformize parent elements + E_Int own = M->owner[fid]; + E_Int ret = Mesh_conformize_cell_face(M, own, fid, M->nf, 2); + assert(ret == 0); + + E_Int nei = M->neigh[fid]; + assert(nei == -1); + if (nei != -1) { + ret = Mesh_conformize_cell_face(M, nei, fid, M->nf, 2); + assert(ret == 0); + } + + M->owner[M->nf] = own; + M->neigh[M->nf] = nei; + + M->flevel[M->nf] = M->flevel[fid]; + M->ftype[M->nf] = M->ftype[fid] = TRI; + M->fparent[M->nf] = M->nf; + + M->fref[fid] = FACE_REFINED; + M->fref[M->nf] = FACE_NEW; + M->fchildren[fid] = {fid, M->nf}; + + M->ftag[M->nf] = M->ftag[fid]; + + M->nf++; +} + +void Mesh_triangulate_faces(Mesh *M, E_Int *faces, E_Int nf) +{ + E_Int new_nf = M->nf + nf; + + Mesh_resize_face_data(M, new_nf); + + for (E_Int i = 0; i < nf; i++) { + E_Int fid = faces[i]; + assert(M->ftag[fid] == 1); + assert(M->fstride[fid] == 4); + Mesh_triangulate_face(M, fid); + } + + Mesh_update_bpatches(M); + + // TODO(Imad): parallel consequences +} diff --git a/Cassiopee/XCore/XCore/AdaptMesh/Mpi.h b/Cassiopee/XCore/XCore/AdaptMesh/Mpi.h index 9dfc0bb72..c3c76a7c6 100644 --- a/Cassiopee/XCore/XCore/AdaptMesh/Mpi.h +++ b/Cassiopee/XCore/XCore/AdaptMesh/Mpi.h @@ -13,12 +13,12 @@ #define MPI_Isend(a, b, c, d, e, f, g) #define MPI_Irecv(a, b, c, d, e, f, g) -#define MPI_Waitall(a) +#define MPI_Waitall(a) (void) #define MPI_Barrier(a) #define MPI_Allreduce(a, b, c, d, e, f) -#define MPI_Waitall(a, b, c) +#define MPI_Waitall(a, b, c) (void) #define MPI_Scan(a, b, c, d, e, f) -#endif \ No newline at end of file +#endif diff --git a/Cassiopee/XCore/XCore/AdaptMesh/Point.cpp b/Cassiopee/XCore/XCore/AdaptMesh/Point.cpp new file mode 100644 index 000000000..9e2c014af --- /dev/null +++ b/Cassiopee/XCore/XCore/AdaptMesh/Point.cpp @@ -0,0 +1,131 @@ +#include "Point.h" +#include "constants.h" +#include "FaceSort.h" +#include "Box.h" +#include "Array.h" +#include "common/mem.h" + +bool Point_in_tri(E_Float px, E_Float py, E_Float pz, + E_Float ax, E_Float ay, E_Float az, + E_Float bx, E_Float by, E_Float bz, + E_Float cx, E_Float cy, E_Float cz) +{ + // Normal vector to the plane + E_Float Y[3] = {bx-ax, by-ay, bz-az}; + E_Float Z[3] = {cx-ax, cy-ay, cz-az}; + E_Float N[3]; + K_MATH::cross(Y, Z, N); + + E_Float X[3] = {px-ax, py-ay, pz-az}; + + E_Float dp = K_MATH::dot(N, X, 3); + + // Is the point on the plane? + if (dp < -TOL || dp > TOL) return 0; + + E_Float x1 = K_MATH::dot(X, Y, 3); + E_Float y1 = K_MATH::dot(Y, Y, 3); + E_Float z1 = K_MATH::dot(Z, Y, 3); + E_Float x2 = K_MATH::dot(X, Z, 3); + E_Float y2 = K_MATH::dot(Y, Z, 3); + E_Float z2 = K_MATH::dot(Z, Z, 3); + + E_Float u = (x1*z2 - x2*z1) / (y1*z2 - y2*z1); + if (u < -TOL || u > 1 + TOL) return false; + + E_Float v = (-x1*y2 + x2*y1) / (y1*z2 - y2*z1); + if (v < -TOL || v > 1 + TOL) return false; + + E_Float w = 1 - u - v; + if (w < -TOL || w > 1 + TOL) return false; + + return true; +} + +bool Point_in_FaceSort(const Point *p, const FaceSort *f) +{ + E_Float DX = p->x - f->xa; + E_Float DY = p->y - f->ya; + E_Float DZ = p->z - f->za; + + E_Float d20 = DX*f->UX + DY*f->UY + DZ*f->UZ; + E_Float d21 = DX*f->VX + DY*f->VY + DZ*f->VZ; + + E_Float u = (f->VV*d20 - f->UV*d21) * f->inv_denom; + if (u < -TOL || u > 1.0 + TOL) return false; + + E_Float v = (f->UU*d21 - f->UV*d20) * f->inv_denom; + if (v < -TOL || v > 1.0 + TOL) return false; + + E_Float w = 1.0 - (u + v); + if (w < -TOL || w > 1.0 + TOL) return false; + + return true; +} + +bool Point_in_Box3D(const Point *p, const Box3 *box) +{ + return (p->x >= box->xmin) && (p->x <= box->xmax) && + (p->y >= box->ymin) && (p->y <= box->ymax) && + (p->z >= box->zmin) && (p->z <= box->zmax); +} + +void PointFaces_extract_by_threshold +( + const PointFaces *sploc, E_Int spcount, + const E_Int *skin, E_Int mcount, + const E_Int threshold, + ArrayI *faces +) +{ + E_Int *ftag = (E_Int *)XMALLOC(mcount * sizeof(E_Int)); + memset(ftag, 0, mcount * sizeof(E_Int)); + + for (E_Int i = 0; i < spcount; i++) { + const PointFaces *pfaces = &sploc[i]; + for (E_Int j = 0; j < pfaces->count; j++) + ftag[pfaces->ptr[j]]++; + } + + faces->count = 0; + + for (E_Int i = 0; i < mcount; i++) { + if (ftag[i] > threshold) + faces->count++; + } + + faces->ptr = (E_Int *)XMALLOC(faces->count * sizeof(E_Int)); + E_Int *ptr = faces->ptr; + + for (E_Int i = 0; i < mcount; i++) { + if (ftag[i] > threshold) + *ptr++ = i;//skin[i]; + } + + XFREE(ftag); +} + +void points_write(const char *fname, const std::vector &P) +{ + FILE *fh = fopen(fname, "w"); + assert(fh); + fprintf(fh, "POINTS\n"); + fprintf(fh, "%zu\n", P.size()); + for (auto p : P) fprintf(fh, "%f %f %f\n", p.x, p.y, p.z); + fclose(fh); +} + +void point_write(E_Float px, E_Float py, E_Float pz) +{ + FILE *fh = fopen("point", "w"); + assert(fh); + fprintf(fh, "POINTS\n"); + fprintf(fh, "1\n"); + fprintf(fh, "%f %f %f\n", px, py, pz); + fclose(fh); +} + +void point_write(const Point *p) +{ + return point_write(p->x, p->y, p->z); +} \ No newline at end of file diff --git a/Cassiopee/XCore/XCore/AdaptMesh/Point.h b/Cassiopee/XCore/XCore/AdaptMesh/Point.h new file mode 100644 index 000000000..a738a3166 --- /dev/null +++ b/Cassiopee/XCore/XCore/AdaptMesh/Point.h @@ -0,0 +1,41 @@ +#pragma once + +#include "xcore.h" + +struct Box3; +struct FaceSort; +struct ArrayI; + +#define MAX_FACES_PER_POINT 8 + +struct Point { + E_Float x, y, z; +}; + +struct PointFaces { + E_Int count; + E_Int ptr[MAX_FACES_PER_POINT]; +}; + +bool Point_in_tri(E_Float px, E_Float py, E_Float pz, + E_Float ax, E_Float ay, E_Float az, + E_Float bx, E_Float by, E_Float bz, + E_Float cx, E_Float cy, E_Float cz); + +bool Point_in_Box3D(const Point *p, const Box3 *box); + +bool Point_in_FaceSort(const Point *p, const FaceSort *f); + +void PointFaces_extract_by_threshold +( + const PointFaces *sploc, E_Int spcount, + const E_Int *skin, E_Int mcount, + const E_Int threshold, + ArrayI *faces +); + +void points_write(const char *fname, const std::vector &P); + +void point_write(E_Float px, E_Float py, E_Float pz); + +void point_write(const Point *p); \ No newline at end of file diff --git a/Cassiopee/XCore/XCore/AdaptMesh/Q6.cpp b/Cassiopee/XCore/XCore/AdaptMesh/Q6.cpp index 039aa2705..1030c4c9e 100644 --- a/Cassiopee/XCore/XCore/AdaptMesh/Q6.cpp +++ b/Cassiopee/XCore/XCore/AdaptMesh/Q6.cpp @@ -17,6 +17,7 @@ along with Cassiopee. If not, see . */ #include "Quad.h" +#include "Mesh.h" E_Int Q6_refine(E_Int quad, Mesh *M) { @@ -147,6 +148,8 @@ E_Int Q6_refine(E_Int quad, Mesh *M) M->fparent[M->nf] = quad; + M->ftag[M->nf] = M->ftag[quad]; + // Increment face/edge/point count M->nf += 1; diff --git a/Cassiopee/XCore/XCore/AdaptMesh/Q9.cpp b/Cassiopee/XCore/XCore/AdaptMesh/Q9.cpp index a51327403..5098a74da 100644 --- a/Cassiopee/XCore/XCore/AdaptMesh/Q9.cpp +++ b/Cassiopee/XCore/XCore/AdaptMesh/Q9.cpp @@ -17,6 +17,7 @@ along with Cassiopee. If not, see . */ #include "Quad.h" +#include "Mesh.h" E_Int Q9_refine(E_Int quad, Mesh *M) { @@ -145,10 +146,19 @@ E_Int Q9_refine(E_Int quad, Mesh *M) M->fchildren[quad] = {quad, M->nf, M->nf+1, M->nf+2}; - M->fparent[M->nf] = quad; + M->fparent[M->nf] = quad; M->fparent[M->nf+1] = quad; M->fparent[M->nf+2] = quad; + M->ftag[M->nf] = M->ftag[quad]; + M->ftag[M->nf+1] = M->ftag[quad]; + M->ftag[M->nf+2] = M->ftag[quad]; + + //assert(M->fpattern[quad] == DIR_ISO); + M->fpattern[M->nf] = M->fpattern[quad]; + M->fpattern[M->nf+1] = M->fpattern[quad]; + M->fpattern[M->nf+2] = M->fpattern[quad]; + // Increment face/edge/point count M->nf += 3; M->np += 1; @@ -161,4 +171,16 @@ void Q4_reorder(E_Int *pn, E_Int reorient, E_Int i0, E_Int local[4]) for (E_Int i = 0; i < 4; i++) local[i] = pn[i]; Right_shift(local, i0, 4); if (reorient) std::swap(local[1], local[3]); +} + +void refine_face_iso(E_Int face, Mesh *M) +{ + switch (M->ftype[face]) { + case QUAD: + Q9_refine(face, M); + break; + default: + assert(0); + break; + } } \ No newline at end of file diff --git a/Cassiopee/XCore/XCore/AdaptMesh/Quad.h b/Cassiopee/XCore/XCore/AdaptMesh/Quad.h index b5f99ad07..1035ddca7 100644 --- a/Cassiopee/XCore/XCore/AdaptMesh/Quad.h +++ b/Cassiopee/XCore/XCore/AdaptMesh/Quad.h @@ -19,7 +19,8 @@ #pragma once #include "common/common.h" -#include "Mesh.h" + +struct Mesh; E_Int Q9_refine(E_Int quad, Mesh *M); @@ -27,15 +28,4 @@ E_Int Q6_refine(E_Int quad, Mesh *M); void Q4_reorder(E_Int *pn, E_Int reorient, E_Int i0, E_Int local[4]); -inline -void refine_face_iso(E_Int face, Mesh *M) -{ - switch (M->ftype[face]) { - case QUAD: - Q9_refine(face, M); - break; - default: - assert(0); - break; - } -} \ No newline at end of file +void refine_face_iso(E_Int face, Mesh *M); diff --git a/Cassiopee/XCore/XCore/AdaptMesh/Skin.cpp b/Cassiopee/XCore/XCore/AdaptMesh/Skin.cpp new file mode 100644 index 000000000..673f3b239 --- /dev/null +++ b/Cassiopee/XCore/XCore/AdaptMesh/Skin.cpp @@ -0,0 +1,121 @@ +#include "Skin.h" +#include "common/mem.h" +#include "Mesh.h" +#include "DynMesh.h" + +#include +#include + +void SkinGraph_free(SkinGraph *skin_graph) +{ + skin_graph->nf = 0; + XFREE(skin_graph->skin); + XFREE(skin_graph->xadj); + XFREE(skin_graph->fpts); + XFREE(skin_graph->fnei); +} + +struct EdgeNode { + E_Int m_p, m_q; + E_Int m_i, m_posi; + mutable E_Int m_j, m_posj; + + EdgeNode(E_Int p, E_Int q) + { + m_p = std::min(p, q); + m_q = std::max(p, q); + m_i = -1; + m_posi = -1; + m_j = -1; + m_posj = -1; + } + + bool operator<(const EdgeNode &e) const + { + return (m_p < e.m_p) || (m_p == e.m_p && m_q < e.m_q); + } +}; + +void SkinGraph_make_skin_neighbours(SkinGraph *skin_graph) +{ + E_Int nf = skin_graph->nf; + const E_Int *xadj = skin_graph->xadj; + const E_Int *fpts = skin_graph->fpts; + + skin_graph->fnei = (E_Int *)XMALLOC(xadj[nf] * sizeof(E_Int)); + memset(skin_graph->fnei, -1, xadj[nf] * sizeof(E_Int)); + E_Int *fnei = skin_graph->fnei; + + std::set edge_set; + + for (E_Int i = 0; i < nf; i++) { + E_Int start = xadj[i]; + E_Int np = xadj[i+1] - start; + const E_Int *pn = &fpts[start]; + for (E_Int j = 0; j < np; j++) { + E_Int p = pn[j]; + E_Int q = pn[(j+1)%np]; + EdgeNode e(p, q); + auto it = edge_set.find(e); + if (it != edge_set.end()) { + assert(it->m_i != -1); + assert(it->m_posi != -1); + assert(it->m_j == -1); + assert(it->m_posj == -1); + it->m_j = i; + it->m_posj = j; + } else { + e.m_i = i; + e.m_posi = j; + edge_set.insert(e); + } + } + } + + for (const auto &e : edge_set) { + E_Int pi = xadj[e.m_i] + e.m_posi; + assert(fnei[pi] == -1); + fnei[pi] = e.m_j; + + E_Int pj = xadj[e.m_j] + e.m_posj; + assert(fnei[pj] == -1); + fnei[pj] = e.m_i; + } +} + +void SkinGraph_smooth_ref_data(const SkinGraph *skin_graph, E_Int *fdat, + const Mesh *M) +{ + E_Int nf = skin_graph->nf; + + std::stack stk; + + for (E_Int i = 0; i < nf; i++) { + if (fdat[i] > 0) + stk.push(i); + } + + const E_Int *xadj = skin_graph->xadj; + const E_Int *fnei = skin_graph->fnei; + + while (!stk.empty()) { + + E_Int fid = stk.top(); + stk.pop(); + + E_Int start = xadj[fid]; + E_Int nneis = xadj[fid+1] - start; + const E_Int *neis = &fnei[start]; + + for (E_Int i = 0; i < nneis; i++) { + E_Int nei = neis[i]; + E_Int incr_nei = fdat[nei] + M->flevel[skin_graph->skin[nei]]; + E_Int incr_fid = fdat[fid] + M->flevel[skin_graph->skin[fid]]; + E_Int diff = abs(incr_nei - incr_fid); + if (diff <= 1) continue; + E_Int idx_to_modify = incr_fid > incr_nei ? nei : fid; + fdat[idx_to_modify] += diff-1; + stk.push(idx_to_modify); + } + } +} diff --git a/Cassiopee/XCore/XCore/AdaptMesh/Skin.h b/Cassiopee/XCore/XCore/AdaptMesh/Skin.h new file mode 100644 index 000000000..fddeee803 --- /dev/null +++ b/Cassiopee/XCore/XCore/AdaptMesh/Skin.h @@ -0,0 +1,23 @@ +#pragma once + +#include "xcore.h" +#include "Vec.h" + +struct Mesh; +struct DynMesh; + +struct SkinGraph { + E_Int nf; + E_Int *skin; + E_Int *xadj; + E_Int *fpts; + E_Int *fnei; + Vec3f *fnml; +}; + +void SkinGraph_free(SkinGraph *skin_graph); + +void SkinGraph_make_skin_neighbours(SkinGraph *skin_graph); + +void SkinGraph_smooth_ref_data(const SkinGraph *skin_graph, + E_Int *fdat, const Mesh *M); diff --git a/Cassiopee/XCore/XCore/AdaptMesh/TriGraph.cpp b/Cassiopee/XCore/XCore/AdaptMesh/TriGraph.cpp new file mode 100644 index 000000000..40dc4d730 --- /dev/null +++ b/Cassiopee/XCore/XCore/AdaptMesh/TriGraph.cpp @@ -0,0 +1,10 @@ +#include "TriGraph.h" + +void TriGraph::clear() +{ + skin.clear(); + T.clear(); + E.clear(); + fdat.clear(); + level.clear(); +} diff --git a/Cassiopee/XCore/XCore/AdaptMesh/TriGraph.h b/Cassiopee/XCore/XCore/AdaptMesh/TriGraph.h new file mode 100644 index 000000000..5add888ea --- /dev/null +++ b/Cassiopee/XCore/XCore/AdaptMesh/TriGraph.h @@ -0,0 +1,18 @@ +#pragma once + +#include "common/mem.h" +#include +#include + +struct TriGraph { + size_t nf; + std::vector skin; + std::vector> T; + std::vector> E; + std::vector fdat; + std::vector level; + + void clear(); +}; + + diff --git a/Cassiopee/XCore/XCore/AdaptMesh/Vec.h b/Cassiopee/XCore/XCore/AdaptMesh/Vec.h new file mode 100644 index 000000000..0298d729d --- /dev/null +++ b/Cassiopee/XCore/XCore/AdaptMesh/Vec.h @@ -0,0 +1,5 @@ +#pragma once + +struct Vec3f { + E_Float x, y, z; +}; \ No newline at end of file diff --git a/Cassiopee/XCore/XCore/AdaptMesh/constants.cpp b/Cassiopee/XCore/XCore/AdaptMesh/constants.cpp new file mode 100644 index 000000000..94b4257db --- /dev/null +++ b/Cassiopee/XCore/XCore/AdaptMesh/constants.cpp @@ -0,0 +1,3 @@ +#include "constants.h" + +const E_Float TOL = 1e-12; \ No newline at end of file diff --git a/Cassiopee/XCore/XCore/AdaptMesh/constants.h b/Cassiopee/XCore/XCore/AdaptMesh/constants.h new file mode 100644 index 000000000..5dc00742e --- /dev/null +++ b/Cassiopee/XCore/XCore/AdaptMesh/constants.h @@ -0,0 +1,5 @@ +#pragma once + +#include "xcore.h" + +extern const E_Float TOL; \ No newline at end of file diff --git a/Cassiopee/XCore/XCore/AdaptMesh/test.py b/Cassiopee/XCore/XCore/AdaptMesh/test.py new file mode 100644 index 000000000..bd6ba7dcc --- /dev/null +++ b/Cassiopee/XCore/XCore/AdaptMesh/test.py @@ -0,0 +1,93 @@ +import Converter.PyTree as C +import Generator.PyTree as G +import Transform.PyTree as T +import XCore.PyTree as X +import Converter.Internal as I +import Converter.elsAProfile as elsAProfile +import numpy as np +import Connector.PyTree as Conn +import Intersector.PyTree as XOR +import Post.PyTree as P + +TOL_match = 1e-7 +TOL_recover = 1e-11 +rot_angle = 2.*np.pi/36. + +# MASTER + +m = C.convertFile2PyTree("data/maillage_veine.cgns") + +(BCs_m, BCNames_m, BCTypes_m) = C.getBCs(m) +families_m = I.getNodesFromType2(m, 'Family_t') + +m = C.convertArray2NGon(m) +m = T.merge(m) + +# rotate master + +rot_center = (0.,0.,0.) +rot_axis = (1.,0.,0.) + +r1 = T.rotate(m, rot_center, rot_axis, rot_angle * 180. / np.pi) +r2 = T.rotate(m, rot_center, rot_axis, -rot_angle * 180. / np.pi) + +# define helper tags + +C._initVars(m, 'centers:keep', 1.0) +C._initVars(r1, 'centers:keep', 0.0) +C._initVars(r2, 'centers:keep', 0.0) + +# merge + +zm = I.getZones(m)[0] +zr1 = I.getZones(r1)[0] +zr2 = I.getZones(r2)[0] +mm = T.merge([zm, zr1, zr2]) + +# SLAVE + +s = C.convertFile2PyTree("data/maillage_fente_axi.cgns") + +(BCs_s, BCNames_s, BCTypes_s) = C.getBCs(s) +families_s = I.getNodesFromType2(s, 'Family_t') + +# rotate slave + +s1 = T.rotate(s, rot_center, rot_axis, -rot_angle * 180. / np.pi) + +# REMOVE INTERSECTING PLANES + +IM = X.IntersectMesh_Init(mm) +sp = X.removeIntersectingKPlanes(IM, s) +sp = T.merge(sp) +C._initVars(sp, 'centers:keep', 1.0) + +sp1 = X.removeIntersectingKPlanes(IM, s1) +sp1 = T.merge(sp1) +C._initVars(sp1, 'centers:keep', 0.0) + +zp = I.getZones(sp)[0] +zp1 = I.getZones(sp1)[0] +sp = T.merge([zp, zp1]) +sfaces = X.extractFacesFromPointTag(sp, "tag") +print(len(sfaces)) +cont = I.createUniqueChild(I.getZones(sp)[0], 'ZoneBC', 'ZoneBC_t') +I.newBC(name='CONTACT', pointList=sfaces[:]+1, btype='BCWall', parent=cont) +C.convertPyTree2File(sp, "sp.cgns") + + +# AdaptGeom + +AM = X.AdaptMesh_Init(mm) + +#sfaces = X.AdaptMesh_AdaptGeom(AM, sp, sfaces) +#cont = I.createUniqueChild(I.getZones(sp)[0], 'ZoneBC', 'ZoneBC_t') +#I.newBC(name='CONTACT', pointList=sfaces, btype='BCWall', parent=cont) +sa = X.AdaptMesh_AdaptGeom(AM, sp, sfaces) +C.convertPyTree2File(sa, "sa.cgns") + +ma = X.AdaptMesh_ExtractMesh(AM) +C.convertPyTree2File(ma, "ma.cgns") + +X.IntersectMesh_Exit(IM) +X.AdaptMesh_Exit(AM) diff --git a/Cassiopee/XCore/XCore/PyTree.py b/Cassiopee/XCore/XCore/PyTree.py index 28a101566..3ac2ee7f1 100644 --- a/Cassiopee/XCore/XCore/PyTree.py +++ b/Cassiopee/XCore/XCore/PyTree.py @@ -91,6 +91,49 @@ def AdaptMesh_ExtractCellRanges(AM): def AdaptMesh_ExtractHaloCellLevels(AM): return xcore.AdaptMesh_ExtractHaloCellLevels(AM) +def AdaptMesh_TagFaces(AM, faces): + return xcore.AdaptMesh_TagFaces(AM, faces) + +def AdaptMesh_TriangulateFaces(AM, faces): + return xcore.AdaptMesh_TriangulateFaces(AM, faces) + +def AdaptMesh_GeneratePrisms(AM, faces): + return xcore.AdaptMesh_GeneratePrisms(AM, faces) + +def AdaptMesh_AdaptGeom(AM, slave, tagged_faces): + zs = I.getZones(slave)[0] + s = C.getFields(I.__GridCoordinates__, zs, api=3)[0] + + keep = I.getNodeFromName(zs, 'keep') + + s = xcore.AdaptMesh_AdaptGeom(AM, s, tagged_faces) + + zso = I.createZoneNode("S_adapted", s) + + ts = C.newPyTree(["S_adapted", zso]) + + return ts + + ''' + s, spatch = xcore.AdaptMesh_AdaptGeom(AM, s, tagged_faces) + + zso = I.createZoneNode("S_adapted", s) + + zbcs = I.createUniqueChild(zso, 'ZoneBC', 'ZoneBC_t') + + I.newBC(name="intersection_patch", pointList=spatch, family='UserDefined', parent=zbcs) + + ts = C.newPyTree(["S_adapted", zso]) + + if keep is not None: + C._cpVars(slave, 'centers:keep', ts, 'centers:keep') + + return ts + ''' + +def AdaptMesh_ExtractTaggedFaces(AM): + return xcore.AdaptMesh_ExtractTaggedFaces(AM) + ################################################################################ @@ -298,16 +341,18 @@ def IntersectMesh_ExtractMesh(IM, removePeriodic=0): t = XOR.closeCells(t) return t -def removeIntersectingKPlanes(IM, slave): - - slave_bases = I.getBases(slave) +def removeIntersectingKPlanes(IM, slave_struct): + slave_bases = I.getBases(slave_struct) iter = -1 + import Generator.PyTree as G + import Transform.PyTree as T + ts = I.newCGNSTree() for slave_base in slave_bases: - + iter = iter + 1 zs = I.getZones(slave_base) @@ -324,13 +369,21 @@ def removeIntersectingKPlanes(IM, slave): new_base = I.newCGNSBase('slave'+str(iter), 3, 3, parent=ts) + zones = [] for i in range(len(new_slaves_and_tags)): new_slave, tag = new_slaves_and_tags[i] zname = zs[i][0] zo = I.createZoneNode(zname, new_slave) cont = I.createUniqueChild(zo, I.__FlowSolutionNodes__, 'FlowSolution_t') I.newDataArray("tag", value=tag, parent=cont) - I.addChild(new_base, zo) + C._convertArray2NGon(zo) + G._close(zo) + zones.append(zo) + + merged = T.merge(zones) + merged = G.close(merged) + I.addChild(new_base, merged) + return ts @@ -349,45 +402,69 @@ def prepareMeshesForIntersection(IM, slave): keep = I.getNodeFromName(zs, 'keep') - s, spatch = xcore.prepareMeshesForIntersection(IM, s, tag) + m, mpatch, s, spatch = xcore.prepareMeshesForIntersection(IM, s, tag) - zso = I.createZoneNode("S_adapted", s) + zmo = I.createZoneNode("M_adapted", m) + zbcs = I.createUniqueChild(zmo, 'ZoneBC', 'ZoneBC_t') + I.newBC(name="intersection_patch", pointList=mpatch, family='UserDefined', parent=zbcs) + ma = C.newPyTree(["M_adapted", zmo]) + zso = I.createZoneNode("S_adapted", s) zbcs = I.createUniqueChild(zso, 'ZoneBC', 'ZoneBC_t') - I.newBC(name="intersection_patch", pointList=spatch, family='UserDefined', parent=zbcs) + sa = C.newPyTree(["S_adapted", zso]) - ts = C.newPyTree(["S_adapted", zso]) + try: import Intersector.PyTree as XOR + except: raise ImportError("XCore.PyTree: requires Intersector.PyTree module.") + + ma = XOR.closeCells(ma) + sa = XOR.closeCells(sa) if keep is not None: - C._cpVars(slave, 'centers:keep', ts, 'centers:keep') + C._cpVars(slave, 'centers:keep', sa, 'centers:keep') - return ts + return ma, sa -def intersectMesh(IM, slave): - zs = I.getZones(slave)[0] +def intersectMesh(master, slave): + zm = I.getZones(master)[0] + marr = C.getFields(I.__GridCoordinates__, zm, api=3)[0] + mpatch = I.getNodeFromName(zm, "intersection_patch")[2][0][1] + zs = I.getZones(slave)[0] keep = I.getNodeFromName(zs, 'keep') - - s = C.getFields(I.__GridCoordinates__, zs, api=3)[0] - + sarr = C.getFields(I.__GridCoordinates__, zs, api=3)[0] + #spatch = I.getNodeFromName(zs, "intersection_patch")[2][1][1][0] spatch = I.getNodeFromName(zs, "intersection_patch")[2][0][1] - - sinter = xcore.intersectMesh(IM, s, spatch) - zso = I.createZoneNode("S_inter", sinter) + marr, sarr = xcore.intersectMesh(marr, mpatch, sarr, spatch) - ts = C.newPyTree(["S_inter", zso]) + zmo = I.createZoneNode("mi", marr) + zso = I.createZoneNode("si", sarr) + + mi = C.newPyTree(["mi", zmo]) + si = C.newPyTree(["si", zso]) try: import Intersector.PyTree as XOR except: raise ImportError("XCore.PyTree: requires Intersector.PyTree module.") - ts = XOR.closeCells(ts) + mi = XOR.closeCells(mi) + si = XOR.closeCells(si) if keep is not None: - C._cpVars(slave, 'centers:keep', ts, 'centers:keep') + C._cpVars(slave, 'centers:keep', si, 'centers:keep') - return ts + return mi, si + +def IntersectMesh_ExtractFaceSet(AM): + return xcore.IntersectMesh_ExtractFaceSet(AM) + +def IntersectMesh_Merge(M, S): + zm = I.getZones(M)[0] + m = C.getFields(I.__GridCoordinates__, zm, api=3)[0] + zs = I.getZones(S)[0] + s = C.getFields(I.__GridCoordinates__, zs, api=3)[0] + + return xcore.IntersectMesh_Merge(m, s) def extractCell(a, cid): z = I.getZones(a)[0] @@ -398,3 +475,116 @@ def extractCell(a, cid): return zmo +def extractFacesFromPointTag(t, tag_name): + z = I.getZones(t)[0] + tag = I.getNodeFromName(z, tag_name) + if tag is None: raise ValueError(tag_name + 'not found') + arr = C.getFields(I.__GridCoordinates__, z, api=3)[0] + return xcore.extractFacesFromPointTag(arr, tag[1]) + +def icapsule_init(mp, sp): + zm = I.getZones(mp)[0] + marr = C.getFields(I.__GridCoordinates__, zm, api=3)[0] + + sarrs = [] + tags = [] + bases = I.getBases(sp) + + for base in bases: + zones = I.getZones(base) + for zone in zones: + sarr = C.getFields(I.__GridCoordinates__, zone, api=3)[0] + sarrs.append(sarr) + tag = I.getNodeFromName(zone, 'tag')[1] + tags.append(tag) + + return xcore.icapsule_init(marr, sarrs, tags) + +def icapsule_adapt(IC): + marr, sarrs, stags = xcore.icapsule_adapt(IC) + zm = I.createZoneNode("ma", marr) + assert(len(sarrs) == len(stags)) + slave_zones = [] + for i in range(len(sarrs)): + zs = I.createZoneNode("sa"+str(i), sarrs[i]) + cont = I.createUniqueChild(zs, 'ZoneBC', 'ZoneBC_t') + bc = I.newBC(name="IntersectionFaces", pointList=stags[i], + family="IntersectionFaces", parent=cont) + slave_zones.append(zs) + tm = C.newPyTree(['Base', zm]) + ts = C.newPyTree(['Base', slave_zones]) + return tm, ts + +def icapsule_intersect(ma, sa): + zm = I.getZones(ma)[0] + marr = C.getFields(I.__GridCoordinates__, zm, api=3)[0] + + zs = I.getZones(sa) + sarrs = [] + stags = [] + for zone in zs: + sarr = C.getFields(I.__GridCoordinates__, zone, api=3)[0] + sarrs.append(sarr) + zonebc = I.getNodeFromType(zone, 'ZoneBC_t') + zbc = I.getNodesFromType(zonebc, 'BC_t') + stag = I.getNodeFromName(zbc, 'PointList')[1] + stags.append(stag) + + marr_i, slist_i, stags_i = xcore.icapsule_intersect(marr, sarrs, stags) + + zm = I.createZoneNode("mi", marr_i) + assert(len(slist_i) == len(stags_i)) + slave_zones = [] + for i in range(len(slist_i)): + zs = I.createZoneNode("si"+str(i), slist_i[i]) + cont = I.createUniqueChild(zs, 'ZoneBC', 'ZoneBC_t') + bc = I.newBC(name="IntersectionFaces", pointList=stags_i[i], + family="IntersectionFaces", parent=cont) + slave_zones.append(zs) + tm = C.newPyTree(['Base', zm]) + ts = C.newPyTree(['Base', slave_zones]) + return tm, ts + + +def icapsule_extract_master(IC): + marr = xcore.icapsule_extract_master(IC) + zm = I.createZoneNode("master", marr) + return zm + +def icapsule_extract_slave(IC, index=0): + sarr = xcore.icapsule_extract_slave(IC, index) + zs = I.createZoneNode("slave", sarr) + return zs + +def icapsule_extract_slaves(IC): + sarrs = xcore.icapsule_extract_slaves(IC) + zs = [] + for i in range(len(sarrs)): + z = I.createZoneNode("slave"+str(i), sarrs[i]) + zs.append(z) + return zs + +def triangulateSkin(m): + m_copy = I.copyRef(m) + _triangulateSkin(m_copy) + return m_copy + +def _triangulateSkin(m): + zones = I.getNodesFromType(m, 'Zone_t') + for i, zone in enumerate(zones): + marr = C.getFields(I.__GridCoordinates__, zone, api=3)[0] + zbc = I.getNodeFromType(zone, 'ZoneBC_t') + ptlists = [] + if zbc is not None: + bcs = I.getNodesFromType(zbc, 'BC_t') + for bc in bcs: + ptlists.append(I.getNodeFromName(bc, 'PointList')[1][0]) + m_out, ptlists_out = xcore.triangulate_skin(marr, ptlists) + C.setFields([m_out], zone, 'nodes') + if zbc is not None: + bcs = I.getNodesFromType(zbc, 'BC_t') + for j, bc in enumerate(bcs): + ptlist = I.getNodeFromName(bc, 'PointList') + ptlist[1] = ptlists_out[j] + + return None \ No newline at end of file diff --git a/Cassiopee/XCore/XCore/common/common.h b/Cassiopee/XCore/XCore/common/common.h index 9d8e2a712..af290d1af 100644 --- a/Cassiopee/XCore/XCore/common/common.h +++ b/Cassiopee/XCore/XCore/common/common.h @@ -54,8 +54,6 @@ E_Int Get_pos(E_Int e, E_Int *pn, E_Int size) if (pn[i] == e) return i; } - assert(0); - return -1; } diff --git a/Cassiopee/XCore/XCore/extractFacesFromPointTag.cpp b/Cassiopee/XCore/XCore/extractFacesFromPointTag.cpp new file mode 100644 index 000000000..b2ef47bd0 --- /dev/null +++ b/Cassiopee/XCore/XCore/extractFacesFromPointTag.cpp @@ -0,0 +1,63 @@ +#include "common/Karray.h" + +PyObject *K_XCORE::extractFacesFromPointTag(PyObject *self, PyObject *args) +{ + PyObject *ARR, *TAG; + if (!PYPARSETUPLE_(args, OO_, &ARR, &TAG)) { + RAISE("Bad input."); + return NULL; + } + + Karray karray; + E_Int ret = Karray_parse_ngon(ARR, karray); + if (ret != 0) return NULL; + + E_Float *tag = NULL; + E_Int nfld, size; + ret = K_NUMPY::getFromNumpyArray(TAG, tag, size, nfld, true); + if (ret != 1 || nfld != 1 || size != karray.npoints()) { + RAISE("Bad input tag."); + Karray_free_ngon(karray); + return NULL; + } + + // Count the faces to extract + E_Int nf = 0; + + for (E_Int fid = 0; fid < karray.nfaces(); fid++) { + E_Int np; + E_Int *pn = karray.get_face(fid, np); + E_Int i; + for (i = 0; i < np; i++) { + if (tag[pn[i]-1] != 1.0) { + break; + } + } + if (i == np) + nf++; + } + + // Allocate + npy_intp dims[2]; + dims[1] = 1; + dims[0] = (npy_intp)nf; + + PyArrayObject *out = (PyArrayObject *)PyArray_SimpleNew(1, dims, E_NPY_INT); + E_Int *pf = (E_Int *)PyArray_DATA(out); + E_Int *ptr = pf; + + for (E_Int fid = 0; fid < karray.nfaces(); fid++) { + E_Int np; + E_Int *pn = karray.get_face(fid, np); + E_Int i; + for (i = 0; i < np; i++) { + if (tag[pn[i]-1] != 1.0) { + break; + } + } + if (i == np) + *ptr++ = fid; + } + + return (PyObject *)out; +} diff --git a/Cassiopee/XCore/XCore/intersectMesh/AABB.cpp b/Cassiopee/XCore/XCore/intersectMesh/AABB.cpp index 89bc83981..c6398e50a 100644 --- a/Cassiopee/XCore/XCore/intersectMesh/AABB.cpp +++ b/Cassiopee/XCore/XCore/intersectMesh/AABB.cpp @@ -1,31 +1,11 @@ #include "AABB.h" -#include "mesh.h" -AABB::AABB() -: xmin(EFLOATMAX), ymin(EFLOATMAX), zmin(EFLOATMAX), - xmax(EFLOATMIN), ymax(EFLOATMIN), zmax(EFLOATMIN) -{} - -AABB::AABB(const IMesh &M, E_Int *ids, E_Int count) +void AABB_clamp(AABB &box, const AABB &parent) { - xmin = ymin = zmin = EFLOATMAX; - xmax = ymax = zmax = EFLOATMIN; - - for (E_Int i = 0; i < count; i++) { - E_Int fid = ids[i]; - - const auto &pn = M.F[fid]; - - for (E_Int p : pn) { - E_Float x = M.X[p]; - E_Float y = M.Y[p]; - E_Float z = M.Z[p]; - if (x < xmin) xmin = x; - if (y < ymin) ymin = y; - if (z < zmin) zmin = z; - if (x > xmax) xmax = x; - if (y > ymax) ymax = y; - if (z > zmax) zmax = z; - } - } + box.xmin = std::max(parent.xmin, box.xmin); + box.ymin = std::max(parent.ymin, box.ymin); + box.zmin = std::max(parent.zmin, box.zmin); + box.xmax = std::min(parent.xmax, box.xmax); + box.ymax = std::min(parent.ymax, box.ymax); + box.zmax = std::min(parent.zmax, box.zmax); } \ No newline at end of file diff --git a/Cassiopee/XCore/XCore/intersectMesh/AABB.h b/Cassiopee/XCore/XCore/intersectMesh/AABB.h index a07c133d0..ebafdafa2 100644 --- a/Cassiopee/XCore/XCore/intersectMesh/AABB.h +++ b/Cassiopee/XCore/XCore/intersectMesh/AABB.h @@ -2,8 +2,6 @@ #include "xcore.h" -struct IMesh; - struct AABB { E_Float xmin; E_Float ymin; @@ -11,8 +9,12 @@ struct AABB { E_Float xmax; E_Float ymax; E_Float zmax; + E_Float dx; + E_Float dy; + E_Float dz; +}; - AABB(); +const AABB AABB_HUGE = {EFLOATMIN, EFLOATMIN, EFLOATMIN, + EFLOATMAX, EFLOATMAX, EFLOATMAX}; - AABB(const IMesh &M, E_Int *ids, E_Int count); -}; \ No newline at end of file +void AABB_clamp(AABB &box, const AABB &parent); diff --git a/Cassiopee/XCore/XCore/intersectMesh/BVH.cpp b/Cassiopee/XCore/XCore/intersectMesh/BVH.cpp deleted file mode 100644 index 56dd3b545..000000000 --- a/Cassiopee/XCore/XCore/intersectMesh/BVH.cpp +++ /dev/null @@ -1,107 +0,0 @@ -#include "BVH.h" -#include "mesh.h" - -// Creates the tree - -static size_t leaf_count = 0; - -BVH_node *BVH_create_node(AABB aabb, BVH_node *left_child, BVH_node *right_child, - E_Int *ids, E_Int count, Mem_arena &arena) -{ - BVH_node *node = (BVH_node *)ARENA_ALLOC(sizeof(BVH_node)); - - node->bbox = aabb; - node->left = left_child; - node->right = right_child; - node->elements = (E_Int *)ARENA_ALLOC(count * sizeof(E_Int)); - assert(count > 0); - memcpy(node->elements, ids, count * sizeof(E_Int)); - - leaf_count++; - - return node; -} - - -BVH_node *BVH_create_node(AABB aabb, BVH_node *left_child, - BVH_node *right_child, Mem_arena &arena) -{ - BVH_node *node = (BVH_node *)ARENA_ALLOC(sizeof(BVH_node)); - - node->bbox = aabb; - node->left = left_child; - node->right = right_child; - node->elements = NULL; - - return node; -} - -BVH_node *BVH_create_node(E_Int *ids, E_Int count, const IMesh &M, - Mem_arena &arena) -{ - AABB aabb(M, ids, count); - - if (count <= MAX_FACES_PER_LEAF) { - return BVH_create_node(aabb, NULL, NULL, ids, count, arena); - } - - E_Float dx = aabb.xmax - aabb.xmin; - E_Float dy = aabb.ymax - aabb.ymin; - E_Float dz = aabb.zmax - aabb.zmin; - - E_Float *pCoor; - - if (dx >= dy && dx >= dz) { - pCoor = (E_Float *)M.X.data(); - } else if (dy >= dz) { - assert(dy >= dx); - pCoor = (E_Float *)M.Y.data(); - } else { - assert(dz >= dx && dz >= dy); - pCoor = (E_Float *)M.Z.data(); - } - - std::sort(ids, ids + count, [&] (E_Int i, E_Int j) - { - E_Float cci = 0.0, ccj = 0.0; - - const auto &pni = M.F[i]; - for (const E_Int p : pni) cci += pCoor[p]; - cci /= pni.size(); - - const auto &pnj = M.F[j]; - for (const E_Int p : pnj) ccj += pCoor[p]; - ccj /= pnj.size(); - - return cci < ccj; - }); - - BVH_node *left = BVH_create_node(ids, count/2, M, arena); - BVH_node *right = BVH_create_node(ids + count/2, count - count/2, M, arena); - - return BVH_create_node(aabb, left, right, arena); -} - -BVH::BVH(const IMesh &M) -{ - if (M.skin.empty()) { - puts("empty skin!"); - exit(1); - } - - size_t NF = M.skin.size(); - - printf("Number of faces: %zu\n", NF); - - arena.reserve(10 * NF * sizeof(E_Int)); - - E_Int *ids = (E_Int *)ARENA_ALLOC(NF * sizeof(E_Int)); - - for (size_t i = 0; i < NF; i++) ids[i] = M.skin[i]; - - root = BVH_create_node(ids, NF, M, arena); - - printf("Leaves: %zu\n", leaf_count); - - arena.print_stats(); -} \ No newline at end of file diff --git a/Cassiopee/XCore/XCore/intersectMesh/BVH.h b/Cassiopee/XCore/XCore/intersectMesh/BVH.h index ddcd60023..026e1c6b0 100644 --- a/Cassiopee/XCore/XCore/intersectMesh/BVH.h +++ b/Cassiopee/XCore/XCore/intersectMesh/BVH.h @@ -1,32 +1,9 @@ #pragma once #include "AABB.h" -#include "common/mem.h" - -#define MAX_FACES_PER_LEAF 10 - -struct IMesh; struct BVH_node { - AABB bbox; - BVH_node *left; - BVH_node *right; - E_Int *elements; + AABB box = AABB_HUGE; + E_Int left_node, first_tri_idx, tri_count; + bool is_leaf() const { return tri_count > 0; } }; - -BVH_node *BVH_create_node(AABB aabb, BVH_node *left_child, - BVH_node *right_child, Mem_arena &arena); - -BVH_node *BVH_create_node(AABB aabb, BVH_node *left_child, BVH_node *right_child, - E_Int *ids, E_Int count, Mem_arena &arena); - -BVH_node *BVH_create_node(E_Int *ids, E_Int count, const IMesh &M, - Mem_arena &arena); - -struct BVH { - BVH_node *root; - - Mem_arena arena; - - BVH(const IMesh &M); -}; \ No newline at end of file diff --git a/Cassiopee/XCore/XCore/intersectMesh/DDA.cpp b/Cassiopee/XCore/XCore/intersectMesh/DDA.cpp index ff76c2258..a347ef7ea 100644 --- a/Cassiopee/XCore/XCore/intersectMesh/DDA.cpp +++ b/Cassiopee/XCore/XCore/intersectMesh/DDA.cpp @@ -2,7 +2,7 @@ #include "primitives.h" void IMesh::hash_skin() -{ +{ bin_faces.clear(); bin_faces.resize(NXYZ); @@ -25,14 +25,6 @@ void IMesh::hash_skin() E_Int J = floor((y - ymin) / HY); E_Int K = floor((z - zmin) / HZ); - assert(I >= 0); - assert(J >= 0); - assert(K >= 0); - - assert(I < NX); - assert(J < NY); - assert(K < NZ); - if (I < Imin) Imin = I; if (J < Jmin) Jmin = J; if (K < Kmin) Kmin = K; @@ -56,27 +48,43 @@ void IMesh::hash_skin() void IMesh::hash_patch() { - fmap.clear(); + bin_faces.clear(); for (E_Int fid : patch) { assert(face_is_active(fid)); const auto &pn = F[fid]; - AABB bbox = AABB_face(pn); + E_Int Imin, Jmin, Kmin; + E_Int Imax, Jmax, Kmax; + + Imin = Jmin = Kmin = NXYZ; + Imax = Jmax = Kmax = -1; - E_Int imin = floor((bbox.xmin - xmin) / HX); - E_Int imax = floor((bbox.xmax - xmin) / HX); - E_Int jmin = floor((bbox.ymin - ymin) / HY); - E_Int jmax = floor((bbox.ymax - ymin) / HY); - E_Int kmin = floor((bbox.zmin - zmin) / HZ); - E_Int kmax = floor((bbox.zmax - zmin) / HZ); + for (E_Int p : pn) { + E_Float x = X[p]; + E_Float y = Y[p]; + E_Float z = Z[p]; + + E_Int I = floor((x - xmin) / HX); + E_Int J = floor((y - ymin) / HY); + E_Int K = floor((z - zmin) / HZ); - for (E_Int k = kmin; k < kmax+1; k++) { - for (E_Int j = jmin; j < jmax+1; j++) { - for (E_Int i = imin; i < imax+1; i++) { - E_Int voxel = i + NX*j + NXY*k; - fmap[voxel].push_back(fid); + if (I < Imin) Imin = I; + if (J < Jmin) Jmin = J; + if (K < Kmin) Kmin = K; + if (I > Imax) Imax = I; + if (J > Jmax) Jmax = J; + if (K > Kmax) Kmax = K; + } + + for (E_Int I = Imin; I <= Imax; I++) { + for (E_Int J = Jmin; J <= Jmax; J++) { + for (E_Int K = Kmin; K <= Kmax; K++) { + E_Int voxel = get_voxel(I, J, K); + assert(voxel >= 0); + assert(voxel < NXYZ); + bin_faces[voxel].push_back(fid); } } } @@ -95,4 +103,4 @@ AABB IMesh::AABB_face(const std::vector &pn) const if (Z[p] < ret.zmin) ret.zmin = Z[p]; } return ret; -} \ No newline at end of file +} diff --git a/Cassiopee/XCore/XCore/intersectMesh/IntersectMesh_ExtractFaceSet.cpp b/Cassiopee/XCore/XCore/intersectMesh/IntersectMesh_ExtractFaceSet.cpp new file mode 100644 index 000000000..e9b31a3c9 --- /dev/null +++ b/Cassiopee/XCore/XCore/intersectMesh/IntersectMesh_ExtractFaceSet.cpp @@ -0,0 +1,31 @@ +#include "mesh.h" + +PyObject *K_XCORE::IntersectMesh_ExtractFaceSet(PyObject *self, PyObject *args) +{ + PyObject *IMESH; + if (!PYPARSETUPLE_(args, O_, &IMESH)) { + RAISE("Bad input."); + return NULL; + } + + if (!PyCapsule_IsValid(IMESH, "IntersectMesh")) { + RAISE("Bad IntersectMesh hook."); + return NULL; + } + + IMesh *M = (IMesh *)PyCapsule_GetPointer(IMESH, "IntersectMesh"); + + // Allocate + npy_intp dims[2]; + dims[1] = 1; + dims[0] = (npy_intp)M->faces_to_tri.size(); + + PyArrayObject *out = (PyArrayObject *)PyArray_SimpleNew(1, dims, E_NPY_INT); + E_Int *pf = (E_Int *)PyArray_DATA(out); + E_Int *ptr = pf; + + for (const E_Int fid : M->faces_to_tri) + *ptr++ = fid; + + return (PyObject *)out; +} diff --git a/Cassiopee/XCore/XCore/intersectMesh/IntersectMesh_Init.cpp b/Cassiopee/XCore/XCore/intersectMesh/IntersectMesh_Init.cpp index 294eee1e1..baf1395aa 100644 --- a/Cassiopee/XCore/XCore/intersectMesh/IntersectMesh_Init.cpp +++ b/Cassiopee/XCore/XCore/intersectMesh/IntersectMesh_Init.cpp @@ -39,7 +39,7 @@ PyObject *K_XCORE::IntersectMesh_Init(PyObject *self, PyObject *args) // Init mesh - IMesh *M = new IMesh(*karray.cn, karray.x, karray.y, karray.z, karray.npts); + IMesh *M = new IMesh(karray); /* M->make_skin(); puts("Making BVH"); diff --git a/Cassiopee/XCore/XCore/intersectMesh/IntersectMesh_Merge.cpp b/Cassiopee/XCore/XCore/intersectMesh/IntersectMesh_Merge.cpp new file mode 100644 index 000000000..49d2d0b1a --- /dev/null +++ b/Cassiopee/XCore/XCore/intersectMesh/IntersectMesh_Merge.cpp @@ -0,0 +1,419 @@ +#include "xcore.h" +#include "common/Karray.h" +#include +#include + +#define TOL 1e-12 + +struct Vtx { + E_Float x, y, z; + E_Int i0; + mutable E_Int i1; + E_Int id; + + Vtx(E_Float x_, E_Float y_, E_Float z_) + : x(x_), y(y_), z(z_), i0(-1), i1(-1), id(-1) + {} + + bool operator<(const Vtx &v) const + { + if (x < v.x) return true; + if (x == v.x && y < v.y) return true; + if (x == v.x && y == v.y && z < v.z) return true; + return false; + } +}; + +struct FACE { + std::vector F; + E_Int i0; + mutable E_Int i1; + E_Int id; + + FACE(E_Int *pn, E_Int np) + { + F.reserve(np); + for (E_Int i = 0; i < np; i++) { + F.push_back(pn[i]); + } + std::sort(F.begin(), F.end()); + i0 = i1 = id = -1; + } + + bool operator==(const FACE &face) const + { + if (F.size() == face.F.size()) { + for (size_t i = 0; i < F.size(); i++) { + if (F[i] != face.F[i]) { + return false; + } + } + + return true; + } + + return false; + } +}; + +struct FACEHASH { + uint64_t hash(uint64_t val, uint64_t seed) const + { + uint64_t HASH = seed; + HASH += val; + HASH += HASH << 10; + HASH ^= HASH >> 6; + return HASH; + } + + uint32_t operator()(const FACE &face) const + { + uint64_t res = 0; + for (size_t i = 0; i < face.F.size(); i++) + res = hash(face.F[i], res); + + res += res << 3; + res ^= res >> 11; + res += res << 15; + + return res; + } +}; + +PyObject *K_XCORE::IntersectMesh_Merge(PyObject *self, PyObject *args) +{ + PyObject *MASTER, *SLAVE; + if (!PYPARSETUPLE_(args, OO_, &MASTER, &SLAVE)) { + RAISE("Bad input."); + return NULL; + } + + Karray marray, sarray; + E_Int ret = Karray_parse_ngon(MASTER, marray); + if (ret != 0) { + RAISE("First input mesh is not an NGon."); + return NULL; + } + + ret = Karray_parse_ngon(SLAVE, sarray); + if (ret != 0) { + RAISE("Second input mesh is not an NGon."); + Karray_free_ngon(marray); + return NULL; + } + + // Points + std::set points; + + // Master points + E_Float *X = marray.x; + E_Float *Y = marray.y; + E_Float *Z = marray.z; + + printf("M points: %d\n", marray.npts); + printf("S points: %d\n", sarray.npts); + + E_Int NP = 0; + + for (E_Int i = 0; i < marray.npts; i++) { + Vtx xyz(X[i], Y[i], Z[i]); + auto it = points.find(xyz); + if (it != points.end()) abort(); + xyz.i0 = i; + xyz.id = NP; + points.insert(xyz); + NP++; + } + + assert((size_t)NP == points.size()); + + // Slave points + X = sarray.x; + Y = sarray.y; + Z = sarray.z; + + for (E_Int i = 0; i < sarray.npts; i++) { + Vtx xyz(X[i], Y[i], Z[i]); + auto it = points.find(xyz); + if (it == points.end()) { + xyz.i1 = i; + xyz.id = NP; + points.insert(xyz); + NP++; + } else { + assert(it->i0 != -1); + assert(it->id != -1); + it->i1 = i; + } + } + + printf("Duplicate points: %lu\n", (size_t)(marray.npts + sarray.npts) - points.size()); + + // Change the points ids within marray + + X = marray.x; + Y = marray.y; + Z = marray.z; + + for (E_Int fid = 0; fid < marray.nfaces(); fid++) { + E_Int np = -1; + E_Int *pn = marray.get_face(fid, np); + for (E_Int j = 0; j < np; j++) { + E_Int pid = pn[j] - 1; + Vtx xyz(X[pid], Y[pid], Z[pid]); + auto it = points.find(xyz); + if (it == points.end()) abort(); + pn[j] = it->id; + } + } + + // Change the points within sarray + + X = sarray.x; + Y = sarray.y; + Z = sarray.z; + + for (E_Int fid = 0; fid < sarray.nfaces(); fid++) { + E_Int np = -1; + E_Int *pn = sarray.get_face(fid, np); + for (E_Int j = 0; j < np; j++) { + E_Int pid = pn[j] - 1; + Vtx xyz(X[pid], Y[pid], Z[pid]); + auto it = points.find(xyz); + if (it == points.end()) abort(); + pn[j] = it->id; + } + } + + // Hash the faces + std::unordered_set faces; + + // Hash the M faces + + E_Int NF = 0; + + for (E_Int fid = 0; fid < marray.nfaces(); fid++) { + E_Int np = -1; + E_Int *pn = marray.get_face(fid, np); + FACE face(pn, np); + auto it = faces.find(face); + if (it != faces.end()) abort(); + face.i0 = fid; + face.id = NF; + faces.insert(face); + NF++; + } + + // Hash the S faces + + for (E_Int fid = 0; fid < sarray.nfaces(); fid++) { + E_Int np = -1; + E_Int *pn = sarray.get_face(fid, np); + FACE face(pn, np); + auto it = faces.find(face); + if (it == faces.end()) { + face.i1 = fid; + face.id = NF; + faces.insert(face); + NF++; + } else { + assert(it->i0 != -1); + assert(it->id != -1); + it->i1 = fid; + } + } + + printf("Duplicate faces: %lu\n", marray.nfaces() + sarray.nfaces() - faces.size()); + + // Replace face ids in marray + + for (E_Int cid = 0; cid < marray.ncells(); cid++) { + E_Int nf = -1; + E_Int *pf = marray.get_cell(cid, nf); + for (E_Int i = 0; i < nf; i++) { + E_Int fid = pf[i]-1; + E_Int np = -1; + E_Int *pn = marray.get_face(fid, np); + FACE face(pn, np); + auto it = faces.find(face); + if (it == faces.end()) abort(); + pf[i] = it->id; + } + } + + + // Replace face ids in sarray + + for (E_Int cid = 0; cid < sarray.ncells(); cid++) { + E_Int nf = -1; + E_Int *pf = sarray.get_cell(cid, nf); + for (E_Int i = 0; i < nf; i++) { + E_Int fid = pf[i]-1; + E_Int np = -1; + E_Int *pn = sarray.get_face(fid, np); + FACE face(pn, np); + auto it = faces.find(face); + if (it == faces.end()) abort(); + pf[i] = it->id; + } + } + + + E_Int sizeNGon = 0, sizeNFace = 0; + + for (E_Int cid = 0; cid < marray.ncells(); cid++) { + E_Int nf = -1; + marray.get_cell(cid, nf); + sizeNFace += nf; + } + + for (E_Int cid = 0; cid < sarray.ncells(); cid++) { + E_Int nf = -1; + sarray.get_cell(cid, nf); + sizeNFace += nf; + } + + printf("sizeNFace: %d\n", sizeNFace); + + for (E_Int fid = 0; fid < marray.nfaces(); fid++) { + E_Int np = -1; + marray.get_face(fid, np); + sizeNGon += np; + } + + for (E_Int fid = 0; fid < sarray.nfaces(); fid++) { + E_Int np = -1; + E_Int *pn = sarray.get_face(fid, np); + // Don't add it if it's a dup + FACE face(pn, np); + auto it = faces.find(face); + if (it->i0 == -1) + sizeNGon += np; + } + + printf("sizeNGon: %d\n", sizeNGon); + + const char *var_string = "CoordinateX,CoordinateY,CoordinateZ"; + + E_Int NC = marray.ncells() + sarray.ncells(); + + printf("NC: %d\n", NC); + printf("NF: %d\n", NF); + printf("NP: %d\n", NP); + + PyObject *array = K_ARRAY::buildArray3(3, var_string, NP, NC, NF, + "NGON", sizeNGon, sizeNFace, 3, false, 3); + + K_FLD::FldArrayF *f; + K_FLD::FldArrayI *cn; + K_ARRAY::getFromArray3(array, f, cn); + + E_Float *px = f->begin(1); + E_Float *py = f->begin(2); + E_Float *pz = f->begin(3); + + for (const auto &point : points) { + if (point.id >= NP) abort(); + if (point.id < 0) abort(); + px[point.id] = point.x; + py[point.id] = point.y; + pz[point.id] = point.z; + } + + E_Int *indPG = cn->getIndPG(); + E_Int *ngon = cn->getNGon(); + E_Int *indPH = cn->getIndPH(); + E_Int *nface = cn->getNFace(); + + + indPG[0] = indPH[0] = 0; + + for (E_Int fid = 0; fid < marray.nfaces(); fid++) { + E_Int np = -1; + E_Int *pn = marray.get_face(fid, np); + FACE face(pn, np); + auto it = faces.find(face); + indPG[it->id+1] = np; + } + + for (E_Int fid = 0; fid < sarray.nfaces(); fid++) { + E_Int np = -1; + E_Int *pn = sarray.get_face(fid, np); + FACE face(pn, np); + auto it = faces.find(face); + indPG[it->id+1] = np; + } + + for (E_Int i = 0; i < NF; i++) indPG[i+1] += indPG[i]; + + printf("indPG[NF]: %d\n", indPG[NF]); + fflush(stdout); + + if (indPG[NF] != sizeNGon) abort(); + + puts("ok indPG"); + fflush(stdout); + + for (E_Int cid = 0; cid < marray.ncells(); cid++) { + E_Int nf = -1; + marray.get_cell(cid, nf); + indPH[cid+1] = nf; + } + + for (E_Int cid = 0; cid < sarray.ncells(); cid++) { + E_Int nf = -1; + sarray.get_cell(cid, nf); + indPH[cid + marray.ncells() + 1] = nf; + } + + for (E_Int i = 0; i < NC; i++) indPH[i+1] += indPH[i]; + + printf("indPH[NC]: %d\n", indPH[NC]); + fflush(stdout); + + if (indPH[NC] != sizeNFace) abort(); + + puts("ok indPH"); + fflush(stdout); + + for (E_Int fid = 0; fid < marray.nfaces(); fid++) { + E_Int np = -1; + E_Int *pn = marray.get_face(fid, np); + FACE face(pn, np); + auto it = faces.find(face); + E_Int *ptr = &ngon[indPG[it->id]]; + for (E_Int i = 0; i < np; i++) + *ptr++ = pn[i] + 1; + } + + for (E_Int fid = 0; fid < sarray.nfaces(); fid++) { + E_Int np = -1; + E_Int *pn = sarray.get_face(fid, np); + FACE face(pn, np); + auto it = faces.find(face); + E_Int *ptr = &ngon[indPG[it->id]]; + for (E_Int i = 0; i < np; i++) + *ptr++ = pn[i] + 1; + } + + for (E_Int cid = 0; cid < marray.ncells(); cid++) { + E_Int nf = -1; + E_Int *pf = marray.get_cell(cid, nf); + E_Int *ptr = &nface[indPH[cid]]; + for (E_Int i = 0; i < nf; i++) + *ptr++ = pf[i] + 1; + } + + for (E_Int cid = 0; cid < sarray.ncells(); cid++) { + E_Int nf = -1; + E_Int *pf = sarray.get_cell(cid, nf); + E_Int *ptr = &nface[indPH[cid + marray.ncells()]]; + for (E_Int i = 0; i < nf; i++) + *ptr++ = pf[i] + 1; + } + + delete f; + delete cn; + + return array; +} diff --git a/Cassiopee/XCore/XCore/intersectMesh/cycle.cpp b/Cassiopee/XCore/XCore/intersectMesh/cycle.cpp deleted file mode 100644 index cb42b71da..000000000 --- a/Cassiopee/XCore/XCore/intersectMesh/cycle.cpp +++ /dev/null @@ -1,64 +0,0 @@ -/* - Copyright 2013-2024 Onera. - - This file is part of Cassiopee. - - Cassiopee is free software: you can redistribute it and/or modify - it under the terms of the GNU General Public License as published by - the Free Software Foundation, either version 3 of the License, or - (at your option) any later version. - - Cassiopee is distributed in the hope that it will be useful, - but WITHOUT ANY WARRANTY; without even the implied warranty of - MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - GNU General Public License for more details. - - You should have received a copy of the GNU General Public License - along with Cassiopee. If not, see . -*/ -#include -#include -#include -#include - -#include "cycle.h" -#include "dcel.h" -#include "hedge.h" -#include "primitives.h" - -E_Int Cycle::INNER = 0; -E_Int Cycle::OUTER = 1; -E_Int Cycle::DEGEN = 2; - -Cycle::Cycle(Hedge *Rep) -: rep(Rep), inout(Dcel::NO_IDEA), left(NULL), prev(NULL), next(NULL) -{} - -void Cycle::write_vertices(const char *fname, const std::vector &C) -{ - FILE *fh = fopen(fname, "w"); - assert(fh); - - std::set V; - for (Cycle *c : C) { - Hedge *h = c->rep; - Vertex *v = h->orig; - if (V.find(v) == V.end()) V.insert(v); - - Hedge *w = h->next; - while (w != h) { - v = w->orig; - if (V.find(v) == V.end()) V.insert(v); - w = w->next; - } - } - - fprintf(fh, "POINTS\n"); - fprintf(fh, "%zu\n", V.size()); - for (const auto &v : V) { - fprintf(fh, "%f %f %f\n", v->x, v->y, v->z); - } - - - fclose(fh); -} diff --git a/Cassiopee/XCore/XCore/intersectMesh/cycle.h b/Cassiopee/XCore/XCore/intersectMesh/cycle.h deleted file mode 100644 index 3db2493d2..000000000 --- a/Cassiopee/XCore/XCore/intersectMesh/cycle.h +++ /dev/null @@ -1,46 +0,0 @@ -/* - Copyright 2013-2024 Onera. - - This file is part of Cassiopee. - - Cassiopee is free software: you can redistribute it and/or modify - it under the terms of the GNU General Public License as published by - the Free Software Foundation, either version 3 of the License, or - (at your option) any later version. - - Cassiopee is distributed in the hope that it will be useful, - but WITHOUT ANY WARRANTY; without even the implied warranty of - MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - GNU General Public License for more details. - - You should have received a copy of the GNU General Public License - along with Cassiopee. If not, see . -*/ -#pragma once - -#include - -#include "xcore.h" -#include "common/common.h" - -struct Hedge; -struct Vertex; - -struct Cycle { - Hedge *rep; - E_Int inout; - Vertex *left; - Cycle *prev; - Cycle *next; - - static E_Int INNER; - static E_Int OUTER; - static E_Int DEGEN; - - Cycle(Hedge *Rep); - void print() const; - - static void set_inout(std::vector &C); - - static void write_vertices(const char *fname, const std::vector &C); -}; \ No newline at end of file diff --git a/Cassiopee/XCore/XCore/intersectMesh/dcel.cpp b/Cassiopee/XCore/XCore/intersectMesh/dcel.cpp index 01d94bcd0..f3388a360 100644 --- a/Cassiopee/XCore/XCore/intersectMesh/dcel.cpp +++ b/Cassiopee/XCore/XCore/intersectMesh/dcel.cpp @@ -22,1140 +22,1198 @@ #include #include "dcel.h" -#include "status.h" -#include "segment.h" #include "primitives.h" #include "io.h" -#include "hedge.h" #include "smesh.h" -#include "event.h" -#include "face.h" -#include "cycle.h" #include "triangle.h" -E_Int Dcel::RED = 0; -E_Int Dcel::BLACK = 1; -E_Int Dcel::NO_IDEA = 2; +Dcel::Dcel() +{} -void Dcel::write_degen_faces(const char *fname) +void Dcel::init_vertices(const Smesh &Mf, const Smesh &Sf, + const std::vector &plocs) { - auto degen_indices = extract_indices_of_type(Cycle::DEGEN); - auto degen_faces = extract_faces_of_indices(degen_indices); - write_ngon(fname, degen_faces); -} - -void Dcel::write_outer_faces(const char *fname) -{ - auto outer_indices = extract_indices_of_type(Cycle::OUTER); - auto outer_faces = extract_faces_of_indices(outer_indices); - write_ngon(fname, outer_faces); -} - -void Dcel::write_inner_faces(const char *fname) -{ - auto inner_indices = extract_indices_of_type(Cycle::INNER); - auto inner_faces = extract_faces_of_indices(inner_indices); - write_ngon(fname, inner_faces); -} - -void Dcel::write_ngon(const char *fname, const std::vector &faces) const -{ - FILE *fh = fopen(fname, "w"); - assert(fh); - - E_Int np = 0; - E_Int ne = 0; - E_Int nf = (E_Int)faces.size(); - - std::map vmap; - std::vector new_pids; - - for (Face *f : faces) { - Hedge *h = f->rep; - ne++; - Vertex *p = h->orig; - if (vmap.find(p) == vmap.end()) { - vmap[p] = np++; - new_pids.push_back(p); - } - Hedge *w = h->next; - while (w != h) { - p = w->orig; - if (vmap.find(p) == vmap.end()) { - vmap[p] = np++; - new_pids.push_back(p); - } - ne++; - w = w->next; - } - } + E_Int duplicate_vertices = 0; - fprintf(fh, "POINTS\n"); - fprintf(fh, SF_D_ "\n", np); - for (const auto &v : new_pids) { - fprintf(fh, "%f %f %f\n", v->x, v->y, v->z); - } - - fprintf(fh, "INDPG\n"); - fprintf(fh, SF_D_ "\n", ne+1); - E_Int sizeNGon = 0; - fprintf(fh, SF_D_ " ", sizeNGon); - for (E_Int i = 0; i < ne; i++) { - sizeNGon += 2; - fprintf(fh, SF_D_ " ", sizeNGon); + for (E_Int i = 0; i < Mf.np; i++) { + Vertex *v = new Vertex(Mf.X[i], Mf.Y[i], Mf.Z[i]); + v->oids[Dcel::RED] = i; + vertex_set.insert(v); } - assert(sizeNGon == 2*ne); - fprintf(fh, "\n"); - fprintf(fh, "NGON\n"); - fprintf(fh, SF_D_ "\n", sizeNGon); - for (Face *f : faces) { - Hedge *h = f->rep; - Vertex *p = h->orig; - Vertex *q = h->twin->orig; - fprintf(fh, SF_D_ " " SF_D_ " ", vmap[p], vmap[q]); - Hedge *w = h->next; - while (w != h) { - p = w->orig; - q = w->twin->orig; - fprintf(fh, SF_D_ " " SF_D_ " ", vmap[p], vmap[q]); - w = w->next; + for (E_Int i = 0; i < Sf.np; i++) { + Vertex tmp(Sf.X[i], Sf.Y[i], Sf.Z[i]); + auto it = vertex_set.find(&tmp); + Vertex *v = NULL; + if (it == vertex_set.end()) { + v = new Vertex(Sf.X[i], Sf.Y[i], Sf.Z[i]); + v->oids[Dcel::BLACK] = i; + vertex_set.insert(v); + } else { + v = *it; + v->oids[Dcel::BLACK] = i; + duplicate_vertices++; } + v->ploc = plocs[i]; } - fprintf(fh, "\n"); - fprintf(fh, "INDPH\n"); - fprintf(fh, SF_D_ "\n", nf+1); - E_Int sizeNFace = 0; - fprintf(fh, SF_D_ " ", sizeNFace); - for (Face *f : faces) { - Hedge *h = f->rep; - sizeNFace += 1; - Hedge *w = h->next; - while (w != h) { - assert(w->left == f); - sizeNFace += 1; - w = w->next; - } - fprintf(fh, SF_D_ " ", sizeNFace); + V.reserve(vertex_set.size()); + for (Vertex *v : vertex_set) { + v->id = V.size(); + V.push_back(v); } - fprintf(fh, "\n"); - fprintf(fh, "NFACE\n"); - fprintf(fh, SF_D_ "\n", sizeNFace); - for (E_Int i = 0; i < sizeNFace; i++) - fprintf(fh, SF_D_ " ", i); - - fclose(fh); + printf("Duplicate vertices: %d\n", duplicate_vertices); } -std::vector Dcel::extract_faces_of_indices( - const std::vector &indices) +void Dcel::get_face_vertices(const Face *f, std::vector &ret) { - std::vector ret; - ret.reserve(indices.size()); - - for (E_Int index : indices) ret.push_back(F[index]); - - return ret; + assert(f); + ret.clear(); + Hedge *h = f->rep; + ret.push_back(h->orig); + Hedge *w = h->next; + while (w != h) { + ret.push_back(w->orig); + w = w->next; + } } -std::vector Dcel::extract_indices_of_type(E_Int type) +struct HitData { + E_Float t, s; +}; + +Dcel Dcel::intersect(const Smesh &Mf, const Smesh &Sf, + const std::vector &plocs) { - std::vector ret; + Dcel D; + D.init_vertices(Mf, Sf, plocs); + + D.init_hedges_and_faces(Mf, RED); + D.init_hedges_and_faces(Sf, BLACK); - for (size_t i = 0; i < C.size(); i++) { - if (C[i]->inout == type) - ret.push_back(i); + if (D.check_hedges(D.H) != 0) { + fprintf(stderr, "Dcel: Inconsistent half-edge records!\n"); + abort(); } + + D.make_cycles(); + D.set_cycles_inout(); - return ret; -} + if (check_faces(D.H, D.F) != 0) { + fprintf(stderr, "Dcel: Inconsistent face records!\n"); + abort(); + } -void Dcel::update_hedge_faces(const std::vector &F) -{ - for (Face *f : F) { - Hedge *h = f->rep; - h->left = f; - Hedge *w = h->next; - while (w != h) { - w->left = f; - w = w->next; + //std::vector xpoints; + //std::vector dpoints; + + // Register the intersections between Sf points and Mf edges + E_Int v_on_e = 0; + for (Vertex *v : D.V) { + // Strictly the Sf points + if (v->oids[0] == -1 && v->oids[1] != -1) { + const auto &ploc = v->ploc; + if (ploc.e_idx == -1) continue; + + // Set the reference edge for normal computation + E_Int fid = ploc.fid; + const auto &pe = Mf.F2E[fid]; + E_Int e = pe[ploc.e_idx]; + + // Add the vertex to hedge intersections + Hedge *h = D.H[2*e]; + assert(h->color == RED); + Vertex *O = h->orig; + Vertex *T = h->twin->orig; + if (D.cmp_vtx(O, T) > 0) h = h->twin; + D.hedge_intersections[h].push_back(v); + + //dpoints.push_back({v->x,v->y,v->z}); + + v_on_e++; } } -} + printf("Sf points on Mf edges: %d\n", v_on_e); + //point_write("dpoints.im", dpoints); -std::vector Dcel::make_cycle_faces(const std::vector &C) -{ - std::vector new_F; + // Trace + for (E_Int eid_s = 0; eid_s < Sf.ne; eid_s++) { + Hedge *hs = D.H[2*(Mf.ne + eid_s)]; + assert(hs->color == BLACK); + Vertex *O = hs->orig; + Vertex *T = hs->twin->orig; + if (D.cmp_vtx(O, T) > 0) hs = hs->twin; - for (Cycle *c : C) { - - // Create a face record - Face *f = new Face; + E_Int p = hs->orig->oids[1]; + E_Int q = hs->twin->orig->oids[1]; - // Set its rep hedge to some edge of the cycle - Hedge *h = c->rep; - f->rep = h; + E_Float spx = Sf.X[p], spy = Sf.Y[p], spz = Sf.Z[p]; + E_Float sqx = Sf.X[q], sqy = Sf.Y[q], sqz = Sf.Z[q]; - new_F.push_back(f); - } + E_Float DIR[3] = {sqx-spx, sqy-spy, sqz-spz}; + E_Float NORM = K_MATH::norm(DIR, 3); + DIR[0] /= NORM, DIR[1] /= NORM, DIR[2] /= NORM; - return new_F; -} + std::vector orig_faces; + std::vector tail_faces; -void Dcel::set_face_labels(std::vector &F) -{ - // Label each face with the ids of the original faces containing it + E_Int last_vertex = -1, last_edge = -1, dummy; - for (size_t i = 0; i < F.size(); i++) { - Face *f = F[i]; + Mf.get_shared_faces(plocs[p], orig_faces, last_vertex, last_edge); + Mf.get_shared_faces(plocs[q], tail_faces, dummy, dummy); - // Get the first RED and BLACK half-edges in the face cycle. - Hedge *h = f->rep; + E_Int starting_face = Mf.deduce_face(orig_faces, spx, spy, spz, + DIR, last_vertex, last_edge, eid_s); + assert(starting_face != -1); - Hedge *R = NULL; - Hedge *B = NULL; - E_Int RB = 0; + bool found_tail = false; + E_Int cur_fid = starting_face; + E_Float cur_pos[3] = {spx, spy, spz}; - if (h->color == Dcel::RED) { - R = h; - B = get_hedge_of_color(f, Dcel::BLACK); - if (B) RB = 1; - } else if (h->color == Dcel::BLACK) { - B = h; - R = get_hedge_of_color(f, Dcel::RED); - if (R) RB = 1; - } else { - assert(0); - } + E_Int walk = 0; + E_Int max_walks = 20; - if (RB) { - // First case: R and B both exist - assert(R->left); - assert(B->left); - assert(R->color == Dcel::RED); - assert(B->color == Dcel::BLACK); - f->oid[Dcel::RED] = R->left->oid[Dcel::RED]; - f->oid[Dcel::BLACK] = B->left->oid[Dcel::BLACK]; - } else { - // Second case: the face is single color - Hedge *REP = (R != NULL) ? R : B; - assert(REP->left); - f->oid[REP->color] = REP->left->oid[REP->color]; - } - } -} + std::vector path; -Hedge *Dcel::get_hedge_of_color(Face *f, E_Int color) -{ - Hedge *h = f->rep; - if (h->color == color) return h; - Hedge *w = h->next; - while (w != h) { - if (w->color == color) return w; - w = w->next; - } - return NULL; -} + while (!found_tail && walk <= max_walks) { -void Dcel::make_cycles() -{ - C.clear(); + path.push_back(cur_fid); - for (size_t i = 0; i < H.size(); i++) { - Hedge *h = H[i]; + for (auto fid : tail_faces) { + if (fid == cur_fid) { + found_tail = true; + break; + } + } - if (h->cycle) continue; + if (found_tail) break; - Cycle *c = new Cycle(h); - C.push_back(c); + // Update the direction + DIR[0] = sqx-cur_pos[0]; + DIR[1] = sqy-cur_pos[1]; + DIR[2] = sqz-cur_pos[2]; + E_Float NORM = K_MATH::norm(DIR, 3); + DIR[0] /= NORM, DIR[1] /= NORM, DIR[2] /= NORM; - h->cycle = c; + // Project + E_Float proj[3]; + Mf.get_unit_projected_direction(cur_fid, DIR, proj); - Hedge *w = h->next; - while (w != h) { - w->cycle = c; - w = w->next; - } - } -} + const auto &pn = Mf.Fc[cur_fid]; + const auto &pe = Mf.F2E[cur_fid]; + assert(pe.size() == pn.size()); + //const E_Float *fN = &Mf.fnormals[3*cur_fid]; -void Dcel::init_vertices(const Smesh &M0, const Smesh &M1) -{ - assert(Q.empty()); + E_Int next_fid = -1; + E_Float next_pos[3] = {EFLOATMAX, EFLOATMAX, EFLOATMAX}; - for (E_Int i = 0; i < M0.np; i++) { - //Q.insert(M0.X[i], M0.Y[i], M0.Z[i], M0.l2gp.at(i), Dcel::RED); - Q.insert(M0.X[i], M0.Y[i], M0.Z[i], i, Dcel::RED); - } + bool hit = false; - for (E_Int i = 0; i < M1.np; i++) { - //Q.insert(M1.X[i], M1.Y[i], M1.Z[i], M1.l2gp.at(i), Dcel::BLACK); - Q.insert(M1.X[i], M1.Y[i], M1.Z[i], i, Dcel::BLACK); - } -} + std::vector hitData; -Dcel::Dcel(Smesh &M0, Smesh &M1) -{ - init_vertices(M0, M1); - Q.inorder(V); - for (size_t i = 0; i < V.size(); i++) { - V[i]->id = i; - } + for (size_t i = 0; i < pn.size(); i++) { + E_Int p = pn[i]; + E_Int q = pn[(i+1)%pn.size()]; + E_Int e = pe[i]; - init_hedges_and_faces(M0, RED); - - init_hedges_and_faces(M1, BLACK); + if (p == last_vertex || q == last_vertex || e == last_edge) + continue; + + E_Float px = Mf.X[p], py = Mf.Y[p], pz = Mf.Z[p]; + E_Float qx = Mf.X[q], qy = Mf.Y[q], qz = Mf.Z[q]; + + E_Float t = -1.0, s = -1.0; + hit = ray_edge_intersect( + cur_pos[0], cur_pos[1], cur_pos[2], + proj[0], proj[1], proj[2], + px, py, pz, qx, qy, qz, + t, s + ); + + hitData.push_back({t, s}); + + if (hit) { + if (s > RAY_EDGE_TOL && s < 1 - RAY_EDGE_TOL) { + // Hit edge middle + const auto &pe = Mf.F2E[cur_fid]; + E_Int eid_m = pe[i]; + last_edge = eid_m; + last_vertex = -1; + assert(Mf.E2F[eid_m][0] == cur_fid || Mf.E2F[eid_m][1] == cur_fid); + if (Mf.E2F[eid_m][0] == cur_fid) next_fid = Mf.E2F[eid_m][1]; + else next_fid = Mf.E2F[eid_m][0]; + + next_pos[0] = cur_pos[0] + t * proj[0]; + next_pos[1] = cur_pos[1] + t * proj[1]; + next_pos[2] = cur_pos[2] + t * proj[2]; + + if (eid_s == 80) { + point_write("x.im", next_pos[0], next_pos[1], next_pos[2]); + } + + // Create a new intersection vertex + Vertex tmp(next_pos[0], next_pos[1], next_pos[2]); + assert(D.vertex_set.find(&tmp) == D.vertex_set.end()); + Vertex *x = new Vertex(next_pos[0], next_pos[1], + next_pos[2]); + + x->ploc.fid = cur_fid; + x->ploc.e_idx = i; - assert(check_hedges(H)); + x->id = D.V.size(); + D.V.push_back(x); + D.vertex_set.insert(x); - assert(check_faces(H, F)); -} + // Register the intersection + Hedge *hm = D.H[2*eid_m]; + assert(hm->color == RED); + Vertex *O = hm->orig; + Vertex *T = hm->twin->orig; + if (D.cmp_vtx(O, T) > 0) hm = hm->twin; -void mat3_mult(E_Float A[3][3], E_Float B[3][3], E_Float C[3][3]) -{ - for (E_Int i = 0; i < 3; i++) { - for (E_Int j = 0; j < 3; j++) { - C[i][j] = 0; + D.hedge_intersections[hm].push_back(x); + D.hedge_intersections[hs].push_back(x); + + } else { + // Hit an edge endpoint + bool hit_p = (s <= RAY_EDGE_TOL); + bool hit_q = (s >= 1 - RAY_EDGE_TOL); + assert(!(hit_p && hit_q)); + last_edge = -1; + if (hit_p) last_vertex = p; + else last_vertex = q; + next_pos[0] = Mf.X[last_vertex]; + next_pos[1] = Mf.Y[last_vertex]; + next_pos[2] = Mf.Z[last_vertex]; + const auto &pf = Mf.P2F[last_vertex]; + + if (eid_s == 80) { + Mf.write_ngon("pf.im", pf); + point_write("x.im", next_pos[0], next_pos[1], next_pos[2]); + } + + next_fid = Mf.deduce_face(pf, + next_pos[0], next_pos[1], next_pos[2], + DIR, last_vertex, last_edge, eid_s + ); + assert(next_fid != -1); + + // Find Vertex corresponding to hit vertex + Vertex tmp(Mf.X[last_vertex], Mf.Y[last_vertex], Mf.Z[last_vertex]); + auto it = D.vertex_set.find(&tmp); + assert(it != D.vertex_set.end()); + Vertex *x = *it; - for (E_Int k = 0; k < 3; k++) { - C[i][j] += A[i][k] * B[k][j]; + // Register the intersection + D.hedge_intersections[hs].push_back(x); + + //xpoints.push_back({x->x, x->y, x->z}); + } + break; + } + } + if (!hit) { + for (const auto &hd : hitData) { + printf("t = %.12e | s = %.12e\n", hd.t, hd.s); + } + fflush(stdout); + std::vector fids; + fids.push_back(cur_fid); + Mf.write_ngon("cur_fid.im", fids); + edge_write("cur_sedge.im", cur_pos[0], cur_pos[1], cur_pos[2], + proj[0], proj[1], proj[2]); } + assert(hit); + assert(next_fid != cur_fid); + cur_fid = next_fid; + cur_pos[0] = next_pos[0]; + cur_pos[1] = next_pos[1]; + cur_pos[2] = next_pos[2]; + walk++; } - } -} -void mat3_vec(E_Float A[3][3], E_Float x[3], E_Float b[3]) -{ - for (E_Int i = 0; i < 3; i++) { - b[i] = 0; - for (E_Int j = 0; j < 3; j++) { - b[i] += A[i][j] * x[j]; + if (!found_tail) { + edge_write("lost_edge.im", spx, spy, spz, sqx, sqy, sqz); + Mf.write_ngon("orig_faces.im", orig_faces); + Mf.write_ngon("tail_faces.im", tail_faces); + Mf.write_ngon("path.im", path); } - } -} - -void Dcel::init_hedges_and_faces(Smesh &M, E_Int color) -{ - printf("Doing color %d\n", color); - size_t nh = H.size(); - size_t nhh = nh + 2 * M.E.size(); - H.reserve(nhh); + assert(found_tail); + assert(walk <= max_walks); + } - std::vector> list(M.np); + //point_write("xpoints.im", xpoints); - for (E_Int i = 0; i < M.ne; i++) { - const auto &e = M.E[i]; + // Cut + for (auto &h2x : D.hedge_intersections) { + Hedge *h = h2x.first; + auto it = D.hedge_intersections.find(h->twin); + assert(it == D.hedge_intersections.end()); - E_Int p = e.p; - E_Int q = e.q; + auto &xs = h2x.second; - Event *xit = Q.lookup(M.X[p], M.Y[p], M.Z[p]); - assert(xit); + Vertex *o = h->orig; + Vertex *tail = h->twin->orig; + assert(D.cmp_vtx(o, tail) < 0); - Vertex *P = xit->key; + // TODO(Imad): check that the intersections are 'sufficiently' spaced out + for (Vertex *x : xs) { + E_Float d[3] = {x->x-o->x, x->y-o->y, x->z-o->z}; + x->d2 = K_MATH::dot(d, d, 3); + } - Hedge *h = new Hedge(P); - h->eid = i; + std::sort(xs.begin(), xs.end(), [&] (const Vertex *a, const Vertex *b) + { + assert(Sign(a->d2-b->d2) != 0); + return a->d2 < b->d2; + }); + + // Before cutting, cache the intersections for volume mesh reconstruction + { + Vertex *start = o; + Vertex *end = tail; + for (size_t i = 0; i < xs.size(); i++) { + D.vcenter[h->color][{start, end}] = xs[i]; + start = xs[i]; + } + } - list[p].push_back(h); + // Cut + Hedge *current_h = h; + Hedge *t = h->twin; - xit = Q.lookup(M.X[q], M.Y[q], M.Z[q]); - assert(xit); + for (Vertex *x : xs) { + Hedge *e1 = new Hedge(x, current_h->color); + Hedge *e2 = new Hedge(x, t->color); - Vertex *V = xit->key; - - Hedge *t = new Hedge(V); - t->eid = i; + D.H.push_back(e1); + D.H.push_back(e2); - list[q].push_back(t); + e1->left = current_h->left; + e2->left = t->left; - h->twin = t; - t->twin = h; + current_h->twin = e2; + e2->twin = current_h; + t->twin = e1; + e1->twin = t; - h->color = color; - t->color = color; + current_h->next = e1; + e1->prev = current_h; + t->next = e2; + e2->prev = t; - H.push_back(h); - H.push_back(t); + current_h = e1; + } } - - // Pair-up hedges - const auto &pnormals = M.pnormals; + // Resolve + std::vector> list(D.V.size()); + for (Hedge *h : D.H) { + Vertex *o = h->orig; + list[o->id].push_back(h); + } - for (E_Int pid = 0; pid < M.np; pid++) { - auto &hedges = list[pid]; + for (size_t vid = 0; vid < D.V.size(); vid++) { + Vertex *v = D.V[vid]; - assert(!hedges.empty()); + E_Float N[3] = {0, 0, 0}; + + if (v->oids[0] != -1) { + const E_Float *pN = &Mf.pnormals[3*v->oids[0]]; + for (E_Int i = 0; i < 3; i++) N[i] = pN[i]; + } else if (v->oids[1] != -1) { + const E_Float *fN = &Mf.fnormals[3*v->ploc.fid]; + for (E_Int i = 0; i < 3; i++) N[i] = fN[i]; + } else { + E_Int fid_m = v->ploc.fid; + E_Int eid_m = Mf.F2E[fid_m][v->ploc.e_idx]; + const auto &pf = Mf.E2F[eid_m]; + assert(pf[0] == fid_m || pf[1] == fid_m); + const E_Float *fN1 = &Mf.fnormals[3*pf[0]]; + const E_Float *fN2 = &Mf.fnormals[3*pf[1]]; + for (int i = 0; i < 3; i++) N[i] += fN1[i] + fN2[i]; + E_Float NORM = K_MATH::norm(N, 3); + for (int i = 0; i < 3; i++) N[i] /= NORM; + } - const E_Float *N = &pnormals[3*pid]; - assert(Sign(K_MATH::norm(N, 3)-1) == 0); + auto &leaving = list[vid]; - sort_leaving_hedges(hedges, N, M); + D.sort_leaving_hedges(leaving, N); - for (size_t i = 0; i < hedges.size(); i++) { - Hedge *h = hedges[i]; - Hedge *w = hedges[(i+1)%hedges.size()]; + for (size_t i = 0; i < leaving.size(); i++) { + Hedge *h = leaving[i]; + Hedge *w = leaving[(i+1)%leaving.size()]; h->twin->next = w; w->prev = h->twin; } - - Event *xit = Q.lookup(hedges[0]->orig); - - xit->key->rep = hedges[0]; } + if (D.check_hedges(D.H) != 0) { + fprintf(stderr, "Dcel: Inconsistent half-edge records!\n"); + assert(0); + abort(); + } + D.make_cycles(); + D.set_cycles_inout(); - for (E_Int i = 0; i < M.nf; i++) { - const auto &edges = M.F2E[i]; - E_Int first_edge = edges[0]; - E_Int where = nh + 2 * first_edge; - Hedge *h = H[where]; - Hedge *t = H[where + 1]; - assert(h->twin == t); - assert(t->twin == h); - - Face *f = new Face; - f->oid[color] = i; + //write_hole_cycles("hole.im"); + //write_degen_cycles("degen.im"); + //write_inner_cycles("inner.im"); - assert(M.E2F[first_edge][0] == (E_Int)i || M.E2F[first_edge][1] == E_Int(i)); - Hedge *REP = (M.E2F[first_edge][0] == (E_Int)i) ? h : t; + auto new_F = D.make_cycle_faces(D.C); + D.set_face_labels(new_F); + D.update_hedge_faces(new_F); + D.check_faces(D.H, new_F); - assert(REP->left == NULL); + for (Face *f : D.F) delete f; + D.F = new_F; - f->rep = REP; - REP->left = f; - Hedge *w = REP->next; - while (w != REP) { w->left = f; w = w->next; } - - F.push_back(f); + D.Fv.resize(D.F.size()); + for (size_t fid = 0; fid < D.F.size(); fid++) { + D.get_face_vertices(D.F[fid], D.Fv[fid]); } - for (Face *f : F) { - Hedge *h = f->rep; - assert(h->left == f); - Hedge *w = h->next; - while (w != h) { - assert(w->left == f); - w = w->next; - } - } - - // Create the unbounded faces - f_unbounded[color] = new Face; - f_unbounded[color]->oid[color] = -1; - - // Set it as the left face for hedges without a left face - for (size_t i = nh; i < nhh; i++) { - if (H[i]->left == NULL) - H[i]->left = f_unbounded[color]; - } + //D.triangulate(Mf, Sf); + + return D; } -E_Int Dcel::check_hedges(const std::vector &H) +static +void get_vertex_normal(const Dcel::Vertex *q, const Smesh &Mf, E_Float N[3]) { - for (size_t i = 0; i < H.size(); i++) { - Hedge *h = H[i]; - if (h->prev->next != h) { assert(0); return 0; } - if (h->next->prev != h) { assert(0); return 0; } - if (h->twin->twin != h) { assert(0); return 0; } - if (h->twin->next->orig != h->orig) { assert(0); return 0; } - if (h->prev->twin->orig != h->orig) { assert(0); return 0; } + N[0] = N[1] = N[2] = 0; + if (q->oids[0] != -1) { + const E_Float *pN = &Mf.pnormals[3*q->oids[0]]; + for (E_Int i = 0; i < 3; i++) N[i] = pN[i]; + } else if (q->oids[1] != -1) { + const E_Float *fN = &Mf.fnormals[3*q->ploc.fid]; + for (E_Int i = 0; i < 3; i++) N[i] = fN[i]; + } else { + E_Int fid_m = q->ploc.fid; + E_Int eid_m = Mf.F2E[fid_m][q->ploc.e_idx]; + const auto &pf = Mf.E2F[eid_m]; + assert(pf[0] == fid_m || pf[1] == fid_m); + const E_Float *fN1 = &Mf.fnormals[3*pf[0]]; + const E_Float *fN2 = &Mf.fnormals[3*pf[1]]; + for (int i = 0; i < 3; i++) N[i] += fN1[i] + fN2[i]; + E_Float NORM = K_MATH::norm(N, 3); + for (int i = 0; i < 3; i++) N[i] /= NORM; } - - puts("CHECK: EDGES OK."); - - return 1; } +// Circular doubly linked list -E_Int Dcel::check_faces(const std::vector &H, - const std::vector &F) -{ - for (size_t i = 0; i < H.size(); i++) { - Hedge *h = H[i]; - if (h->prev->left != h->left) { assert(0); return 0; } - if (h->next->left != h->left) { assert(0); return 0; } - } +struct VNode { + Dcel::Vertex *v; + VNode *next; + VNode *prev; - for (size_t i = 0; i < F.size(); i++) { - Face *f = F[i]; - if (f->rep->left != f) { assert(0); return 0; } + VNode(Dcel::Vertex *V) + { + v = V; + next = this; + prev = this; } +}; - puts("CHECK: FACES OK."); - - return 1; +void VNode_push_front(VNode **head, Dcel::Vertex *v) +{ + VNode *node = new VNode(v); + if (*head == NULL) { + *head = node; + } else { + VNode *last = (*head)->prev; + node->next = *head; + node->prev = last; + last->next = node; + (*head)->prev = node; + *head = node; + } } -Dcel::~Dcel() +void VNode_push_back(VNode **head, Dcel::Vertex *v) { - delete f_unbounded[0]; - delete f_unbounded[1]; - - for (size_t i = 0; i < V.size(); i++) delete V[i]; - for (size_t i = 0; i < H.size(); i++) delete H[i]; - for (size_t i = 0; i < F.size(); i++) delete F[i]; - for (size_t i = 0; i < C.size(); i++) delete C[i]; - - Q.drop(); + VNode *node = new VNode(v); + if (*head == NULL) { + *head = node; + } else { + VNode *last = (*head)->prev; + node->next = *head; + node->prev = last; + last->next = node; + (*head)->prev = node; + } } -void Dcel::set_cycles_inout(const Smesh &M, const Smesh &S) +bool VNode_erase(VNode **head, Dcel::Vertex *v) { - E_Int inner = 0; - E_Int outer = 0; - E_Int degen = 0; - - for (Cycle *c : C) { - // Get the leftmost vertex in the cycle - Hedge *h = c->rep; - Vertex *v = h->orig; - - Hedge *e2 = h; // Half-edge starting at v - Hedge *e1 = h->prev; // Half-edge ending at v - - Hedge *w = h->next; - while (w != h) { - Vertex *p = w->orig; - E_Int cmp = cmp_vtx(p, v); - if (cmp < 0) { - v = p; - e2 = w; - e1 = w->prev; + if (*head == NULL) return false; + + VNode *current = *head; + do { + if (current->v == v) { + if (current->next == current) { + // Single node in the list + *head = NULL; + } else { + VNode *prev = current->prev; + VNode *next = current->next; + prev->next = next; + next->prev = prev; + if (current == *head) { + // If head to be deleted, set it to the next element + *head = next; + } } - - w = w->next; + delete current; + return true; } + current = current->next; + } while (current != *head); - assert(e2->orig == v); - assert(e1->twin->orig == v); + assert(0); + return false; +} - c->left = v; +VNode *VNode_find(const VNode *head, const Dcel::Vertex *v) +{ + if (!head) return NULL; - Vertex *a = e1->orig; - Vertex *b = e2->twin->orig; + VNode *current = (VNode *)head; + do { + if (current->v == v) return current; + current = current->next; + } while (current != head); + return NULL; +} - // If the angle from e1 to e2 is less than 180°, c is an outer cycle. - // Else, c is an inner cycle. - - E_Float px = v->x - a->x; - E_Float py = v->y - a->y; - E_Float pz = v->z - a->z; - E_Float nx = b->x - v->x; - E_Float ny = b->y - v->y; - E_Float nz = b->z - v->z; +static +bool vertex_is_in_triangle(const Dcel::Vertex *v, const Dcel::Vertex *a, + const Dcel::Vertex *b, const Dcel::Vertex *c) +{ + return Triangle::is_point_inside(v->x, v->y, v->z, + a->x, a->y, a->z, b->x, b->y, b->z, c->x, c->y, c->z); +} - E_Float cp[3] = {py*nz - pz*ny, pz*nx - px*nz, px*ny - py*nx}; +static +bool vertex_is_ear(const Dcel::Vertex *b, const VNode *polygon, + const VNode *convex, const VNode *reflex) +{ + // Polygon empty + if (!polygon) return false; + // Vertex not in polygon + VNode *node = VNode_find(polygon, b); + if (!node) return false; - E_Float N[3]= { }; + // Vertex not convex + if (!VNode_find(convex, b)) return false; - // M point - if (v->oid[0] != -1) { + // No reflex vertices + if (!reflex) return true; + + const Dcel::Vertex *a = node->prev->v; + const Dcel::Vertex *c = node->next->v; - E_Int mpid = v->oid[0]; + // Test the inclusion of all reflex vertices within triangle {a, b, c} + VNode *current = (VNode *)reflex; + bool is_ear = true; - const E_Float *pN = &M.pnormals[3*mpid]; - for (E_Int i = 0; i < 3; i++) N[i] = pN[i]; + do { + Dcel::Vertex *v = current->v; + if (v != a && v != b && v != c && + vertex_is_in_triangle(v, a, b, c)) { + is_ear = false; + break; } - - // S point - else if (v->oid[1] != -1) { - const auto &loc = v->loc; - - E_Int mfid = loc.fid; - - if (loc.e_idx != -1) { + current = current->next; + + } while (current != reflex); - const auto &pe = M.F2E[mfid]; - E_Int eid = pe[loc.e_idx]; - const auto &pf = M.E2F[eid]; - assert(mfid == pf[0] || mfid == pf[1]); + return is_ear; +} - E_Int mf1 = pf[0]; - E_Int mf2 = pf[1]; +void VNode_free_list(VNode *head) +{ + if (!head) return; + VNode *current = head; + do { + VNode *next = current->next; + delete current; + current = next; + } while (current != head); +} - const E_Float *fN1 = &M.fnormals[3*mf1]; - const E_Float *fN2 = &M.fnormals[3*mf2]; +void VNode_print_list(const VNode *head) +{ + if (!head) return; - for (E_Int i = 0; i < 3; i++) { - N[i] += fN1[i]; - N[i] += fN2[i]; - } + VNode *current = (VNode *)head; - E_Float NORM = K_MATH::norm(N, 3); - for (E_Int i = 0; i < 3; i++) N[i] /= NORM; + do { + printf("%d ", current->v->id); + current = current->next; + } while (current != head); - } else if (loc.v_idx != -1) { - - const auto &pn = M.F[mfid]; - E_Int mpid = pn[loc.v_idx]; - const E_Float *pN = &M.pnormals[3*mpid]; - for (E_Int i = 0; i < 3; i++) N[i] = pN[i]; + printf("\n"); + fflush(stdout); +} - } else { +struct Vertex_triple +{ + Dcel::Vertex *a, *b, *c; +}; - const E_Float *fN = &M.fnormals[3*mfid]; +static +bool vertex_list_is_convex(const Dcel::Vertex *p, const Dcel::Vertex *q, + const Dcel::Vertex *r, const Smesh &Mf) +{ + E_Float A[3] = {q->x-p->x, q->y-p->y, q->z-p->z}; + E_Float B[3] = {r->x-q->x, r->y-q->y, r->z-q->z}; + E_Float C[3]; + K_MATH::cross(A, B, C); - for (E_Int i = 0; i < 3; i++) N[i] = fN[i]; + E_Float N[3]; + get_vertex_normal(q, Mf, N); - } + E_Float dp = K_MATH::dot(C, N, 3); + if (dp > TOL) return true; + return false; +} - } +static +bool vertex_is_convex(const Dcel::Vertex *b, const VNode *polygon, + const Smesh &Mf) +{ + VNode *node = VNode_find(polygon, b); + if (!node) return false; - // Intersection - else { + const Dcel::Vertex *a = node->prev->v; + const Dcel::Vertex *c = node->next->v; - Hedge *h = v->xhedge; - assert(h); + return vertex_list_is_convex(a, b, c, Mf); +} - Face *f1 = h->left; - Face *f2 = h->twin->left; +void Dcel::triangulate(const Smesh &Mf, const Smesh &Sf) +{ + E_Int non_convex_count = 0; + std::vector non_convex_faces; - E_Int mf1 = f1->oid[0]; - E_Int mf2 = f2->oid[0]; - - const E_Float *fN1 = &M.fnormals[3*mf1]; - const E_Float *fN2 = &M.fnormals[3*mf2]; + for (size_t fid = 0; fid < F.size(); fid++) { + // TODO(Imad): skip single color faces + + const auto &vertices = Fv[fid]; + if (vertices.size() == 3) continue; - for (E_Int i = 0; i < 3; i++) { - N[i] += fN1[i]; - N[i] += fN2[i]; + for (size_t i = 0; i < vertices.size(); i++) { + Vertex *p = vertices[i]; + Vertex *q = vertices[(i+1)%vertices.size()]; + Vertex *r = vertices[(i+2)%vertices.size()]; + + E_Float A[3] = {q->x-p->x, q->y-p->y, q->z-p->z}; + E_Float B[3] = {r->x-q->x, r->y-q->y, r->z-q->z}; + E_Float C[3]; + K_MATH::cross(A, B, C); + + E_Float N[3]; + get_vertex_normal(q, Mf, N); + + E_Float dp = K_MATH::dot(C, N, 3); + if (dp < 0) { + /* + write_vertex("p.im", p); + write_vertex("q.im", q); + write_vertex("r.im", r); + */ + + std::vector face; + face.push_back(fid); + char fname[16] = {0}; + sprintf(fname, "fid%d.im", non_convex_count); + write_ngon(fname, face); + + non_convex_faces.push_back(fid); + non_convex_count++; + break; } - - E_Float NORM = K_MATH::norm(N, 3); - for (E_Int i = 0; i < 3; i++) N[i] /= NORM; } + } - E_Float NORM = K_MATH::norm(N, 3); - assert(Sign(NORM -1) == 0); + printf("Total faces: %lu\n", F.size()); + printf("Non-convex count: %d\n", non_convex_count); + write_ngon("non_convex.im", non_convex_faces); - E_Float cmp = K_MATH::dot(N, cp, 3); + std::vector new_faces; - if (cmp < 0) { - c->inout = Cycle::INNER; - inner++; - } else if (cmp == 0) { - c->inout = Cycle::DEGEN; - degen++; - } else { - c->inout = Cycle::OUTER; - outer++; - } - } + for (size_t i = 0; i < non_convex_faces.size(); i++) { + E_Int fid = non_convex_faces[i]; - printf("Inner cycles: " SF_D_ "\n", inner); - printf("Outer cycles: " SF_D_ "\n", outer); - printf("Degen cycles: " SF_D_ "\n", degen); - - printf("Total faces: " SF_D_ "\n", outer); -} + const auto &vertices = Fv[fid]; + assert(vertices.size() > 3); -std::vector Dcel::get_face_vertices(Face *f) -{ - std::vector ret; - Hedge *h = f->rep; - ret.push_back(h->orig); - Hedge *w = h->next; - while (w != h) { - ret.push_back(w->orig); - w = w->next; - } - return ret; -} + // Store the polygon -void Dcel::locate_spoints(const Smesh &M, const Smesh &S) -{ - for (E_Int sp = 0; sp < S.np; sp++) { + VNode *polygon = NULL; + for (Vertex *v : vertices) VNode_push_back(&polygon, v); - Event *xit = Q.lookup(S.X[sp], S.Y[sp], S.Z[sp]); - assert(xit); + { + VNode *current = polygon; + E_Int vid = 0; + do { + Vertex *v = current->v; + char fname[128] = {0}; + sprintf(fname, "vertex%d.im", vid); + point_write(fname, v->x, v->y, v->z); + current = current->next; + vid++; + } while (current != polygon); + } - Vertex *V = xit->key; - auto &ploc = V->loc; + // Find the convex/reflex vertices - E_Int found = 0; + VNode *convex = NULL, *reflex = NULL; + VNode *current = polygon; + do { + if (vertex_is_convex(current->v, polygon, Mf)) { + VNode_push_back(&convex, current->v); + } else { + VNode_push_back(&reflex, current->v); + } - E_Int voxel_x = floor((S.X[sp] - M.xmin) / M.HX); - E_Int voxel_y = floor((S.Y[sp] - M.ymin) / M.HY); - E_Int voxel_z = floor((S.Z[sp] - M.zmin) / M.HZ); - E_Int sp_bin = voxel_x + M.NX * voxel_y + M.NXY * voxel_z; + current = current->next; + } while (current != polygon); - auto it = M.fmap.find(sp_bin); + // Store the ears - assert(it != M.fmap.end()); + VNode *ears = NULL; + assert(current == polygon); + do { + if (vertex_is_ear(current->v, polygon, convex, reflex)) + VNode_push_back(&ears, current->v); + current = current->next; + } while (current != polygon); - const auto &pf = it->second; + // Ear-clipping algorithm - for (size_t mf = 0; mf < pf.size() && !found; mf++) { + size_t polygon_size = vertices.size(); - const auto &pn = M.F[pf[mf]]; + std::vector tris; - E_Float o[3] = {0, 0, 0}; + if (i == 0) { + point_write("polygon_head.im", polygon->v->x, polygon->v->y, polygon->v->z); + printf("polygon before: "); + VNode_print_list(polygon); + printf("ears before: "); + VNode_print_list(ears); + printf("convex before: "); + VNode_print_list(convex); + printf("reflex before: "); + VNode_print_list(reflex); + } - for (E_Int p : pn) { - o[0] += M.X[p]; - o[1] += M.Y[p]; - o[2] += M.Z[p]; + while (polygon_size != 3) { + // Current ear is one of the resulting triangles + Vertex *b = ears->v; + VNode *node = VNode_find(polygon, b); + Vertex *a = node->prev->v; + Vertex *c = node->next->v; + tris.push_back({a, b, c}); + + if (i == 0) { + point_write("a.im", a->x, a->y, a->z); + point_write("b.im", b->x, b->y, b->z); + point_write("c.im", c->x, c->y, c->z); } - for (E_Int i = 0; i < 3; i++) o[i] /= pn.size(); - - for (size_t i = 0; i < pn.size(); i++) { - E_Int p = pn[i]; - E_Int q = pn[(i+1)%pn.size()]; - E_Float u, v, w; + // Delete current ear tip from ear tip list + VNode_erase(&ears, b); - if (Triangle::is_point_inside( - S.X[sp], S.Y[sp], S.Z[sp], - M.X[p], M.Y[p], M.Z[p], - M.X[q], M.Y[q], M.Z[q], - o[0], o[1], o[2], - u, v, w)) { + // Delete current ear tip from polygon + VNode_erase(&polygon, b); + polygon_size--; - found = 1; + // Delete current ear tip from convex list + VNode_erase(&convex, b); - ploc.fid = pf[mf]; + // Rules after ear tip deletion: + // - if an adjacent vertex was convex, it remains convex, and may become an ear. + // - if an adjacent vertex was an ear, it does not necessarily remains an ear. + // - if an adjacent vertex was reflex, it may become convex and possibly and ear. - if (Sign(v) == 0) ploc.e_idx = i; - else if (Sign(1-u) == 0) ploc.v_idx = (i+1)%pn.size(); - else if (Sign(1-w) == 0) ploc.v_idx = i; + // Update prev - break; + bool was_convex = (VNode_find(convex, a) != NULL); + if (was_convex) { + if (!VNode_find(ears, a)) { + if (vertex_is_ear(a, polygon, convex, reflex)) { + VNode_push_back(&ears, a); + } + } + } else { + assert(VNode_find(reflex, a)); + if (vertex_is_convex(a, polygon, Mf)) { + VNode_erase(&reflex, a); + VNode_push_back(&convex, a); + + assert(!VNode_find(ears, a)); + if (vertex_is_ear(a, polygon, convex, reflex)) { + VNode_push_back(&ears, a); + } } } - } - - assert(found); - } -} - -void Dcel::cut_hedge_at_vertex(Hedge *e, Vertex *x) -{ - // Create two new half-edge records with x as their origin - Hedge *e1 = new Hedge(x); - Hedge *e2 = new Hedge(x); - - e1->color = e->color; - e2->color = e->color; - - Hedge *t = e->twin; - - // Copy the face record - e1->left = e->left; - e2->left = t->left; - // Pair-up the new half-edges - e->twin = e2; - e1->twin = t; - t->twin = e1; - e2->twin = e; + // Update next - // Set prev and next pointers at the endpoints - e1->next = e->next; - e2->next = t->next; - e->next->prev = e1; - t->next->prev = e2; - - H.push_back(e1); - H.push_back(e2); - - e->next = e1; - t->next = e2; - - e1->prev = e; - e2->prev = t; - - Cp[x].push_back(e1); - Cp[x].push_back(e2); -} - -void Dcel::handle_intersecting_endpoint(Vertex *v, const Smesh &M) -{ - if (!Cp[v].empty()) return; - - const auto &vloc = v->loc; - - if (vloc.e_idx == -1) return; - - E_Int fid = vloc.fid; - - const auto &pe = M.F2E[fid]; - - E_Int me = pe[vloc.e_idx]; - - Hedge *start = H[2*me]; + was_convex = (VNode_find(convex, c) != NULL); + if (was_convex) { + if (!VNode_find(ears, c)) { + if (vertex_is_ear(c, polygon, convex, reflex)) { + VNode_push_back(&ears, c); + } + } + } else { + assert(VNode_find(reflex, c)); + if (vertex_is_convex(c, polygon, Mf)) { + VNode_erase(&reflex, c); + VNode_push_back(&convex, c); + + assert(!VNode_find(ears, c)); + if (vertex_is_ear(c, polygon, convex, reflex)) { + VNode_push_back(&ears, c); + } + } + } + } - Face *face = F[fid]; + if (i == 0) { + printf("polygon after: "); + VNode_print_list(polygon); + printf("ears after: "); + VNode_print_list(ears); + printf("convex after: "); + VNode_print_list(convex); + printf("reflex after: "); + VNode_print_list(reflex); + puts(""); + } - if (start->left != face) start = start->twin; + tris.push_back({polygon->prev->v, polygon->v, polygon->next->v}); - Hedge *h = start; + assert(tris.size() == vertices.size()-2); - E_Int done = 0; + // From the triangles, create new face records. + // These face records inherit the color of the parent face. + if (i == 0) { + for (size_t j = 0; j < tris.size(); j++) { + write_vertex("a.im", tris[j].a); + write_vertex("b.im", tris[j].b); + write_vertex("c.im", tris[j].c); + printf("bleu"); + } + } - while (1) { + // Replace fid by the first - if (hedge_contains_vertex(h, v)) { - done = 1; - v->xhedge = h; - cut_hedge_at_vertex(h, v); - break; + for (size_t j = 0; j < tris.size(); j++) { + const auto &tri = tris[j]; + if (j == 0) { + Fv[fid] = {tri.a, tri.b, tri.c}; + } else { + Face *new_f = new Face; + new_f->oids[0] = F[fid]->oids[0]; + new_f->oids[1] = F[fid]->oids[1]; + Fv.push_back({tri.a, tri.b, tri.c}); + F.push_back(new_f); + } } - h = h->next; - if (h == start) break; + VNode_free_list(polygon); + VNode_free_list(reflex); + VNode_free_list(ears); + VNode_free_list(convex); } - assert(done == 1); + //printf("Total faces: %lu\n", F.size()); } -E_Int Dcel::get_next_face(const Smesh &M, E_Float px, E_Float py, E_Float pz, - const std::vector &pf, E_Float dir[3]) +void Dcel::update_hedge_faces(std::vector &new_F) { - E_Int next_face = -1; - E_Float t_min = EFLOATMAX; - - for (size_t i = 0; i < pf.size(); i++) { - - E_Int fid = pf[i]; - - Face *face = F[fid]; - - const E_Float *fN = &M.fnormals[3*fid]; - - E_Float proj[3] = { }; - E_Float dp = K_MATH::dot(fN, dir, 3); - for (E_Int j = 0; j < 3; j++) proj[j] = dir[j] - dp * fN[j]; + for (Hedge *h : H) h->left = NULL; - Hedge *h = face->rep; + for (Face *f : new_F) { + assert(f); - E_Int hit = 0; - - while (1) { - - Vertex *a = h->orig; - Vertex *b = h->twin->orig; - - E_Float dx = px + 10000 * proj[0]; - E_Float dy = py + 10000 * proj[1]; - E_Float dz = pz + 10000 * proj[2]; - - E_Float t; - - hit = EdgeEdgeIntersect( - px, py, pz, - dx, dy, dz, - a->x, a->y, a->z, - b->x, b->y, b->z, - t); + Hedge *h = f->rep; + assert(h); + Cycle *c = h->cycle; + assert(c->inout == Cycle::INNER); - if (hit) { - if (t < t_min) { - next_face = fid; - t_min = t; - } + h->left = f; + assert(f->rep->left == f); - break; - } - - h = h->next; - if (h == face->rep) break; + Hedge *w = h->next; + while (w != h) { + assert(w->left == NULL); + w->left = f; + w = w->next; } } - /* - if (next_face == -1) { - - point_write("test_point", px, py, pz); + for (Face *f : new_F) { + assert(f->rep->left == f); + } - for (E_Int fid : pf) { - char fname[128] = {}; - sprintf(fname, "test_face_%d", fid); - face_write(fname, F[fid]); + for (Hedge *h : H) { + Face *f = h->left; + Hedge *w = h->next; + while (w != h) { + assert(w->left == f); + w = w->next; } } - */ - - return next_face; } -void Dcel::trace_hedge(Hedge *sh, const Smesh &M, const Smesh &S, E_Int hid) +E_Int Dcel::check_faces(const std::vector &H, + const std::vector &F) { - Vertex *p = sh->orig; - Vertex *q = sh->twin->orig; - - E_Float dir[3] = {q->x-p->x, q->y-p->y, q->z-p->z}; - - const auto &ploc = p->loc; + for (size_t i = 0; i < H.size(); i++) { + Hedge *h = H[i]; + if (h->prev->left != h->left) { assert(0); return 1; } + if (h->next->left != h->left) { assert(0); return 1; } + } - std::vector test_faces; + for (size_t i = 0; i < F.size(); i++) { + Face *f = F[i]; + //Hedge *h = f->rep; + //Cycle *c = h->cycle; + //assert(c->inout == Cycle::DEGEN || c->inout == Cycle::INNER); + if (f->rep->left != f) { assert(0); return 1; } + } - E_Int mfid = ploc.fid; + puts("CHECK: FACES OK."); - // Get the potential starting faces + return 0; +} - if (ploc.e_idx != -1) { - assert(ploc.v_idx == -1); - const auto &pe = M.F2E[mfid]; - E_Int eid = pe[ploc.e_idx]; - const auto &pf = M.E2F[eid]; - for (E_Int fid : pf) test_faces.push_back(fid); - } else if (ploc.v_idx != -1) { - assert(ploc.e_idx == -1); - const auto &pn = M.F[mfid]; - E_Int pid = pn[ploc.v_idx]; - const auto &pf = M.P2F[pid]; - for (E_Int fid : pf) test_faces.push_back(fid); - } else { - test_faces.push_back(mfid); - } - // Handle potential intersection of starting point - handle_intersecting_endpoint(p, M); - handle_intersecting_endpoint(q, M); +void Dcel::set_face_labels(std::vector &new_F) +{ + // Label each face with the ids of the original faces containing it - // Determine the starting face - E_Int start_face = get_next_face(M, p->x, p->y, p->z, test_faces, dir); + for (size_t i = 0; i < new_F.size(); i++) { + Face *f = new_F[i]; - assert(start_face != -1); + // Get the first RED and BLACK half-edges in the face cycle + Hedge *h = f->rep; - // Trace - - E_Int found = 0; - E_Int walk = 0; - E_Int max_walk = 10; - - Face *current_face = F[start_face]; - - E_Float px = p->x, py = p->y, pz = p->z; - - Hedge *start_hedge = current_face->rep; - - Hedge *current_hedge = sh; - - // Pinpoint the endpoint - std::vector end_faces; - const auto &qloc = q->loc; - E_Int qfid = qloc.fid; - - if (qloc.e_idx != -1) { - assert(qloc.v_idx == -1); - const auto &pe = M.F2E[qfid]; - E_Int eid = pe[qloc.e_idx]; - const auto &pf = M.E2F[eid]; - for (E_Int fid : pf) end_faces.push_back(fid); - } else if (qloc.v_idx != -1) { - assert(qloc.e_idx == -1); - const auto &pn = M.F[qfid]; - E_Int pid = pn[qloc.v_idx]; - const auto &pf = M.P2F[pid]; - for (E_Int fid : pf) end_faces.push_back(fid); - } else { - end_faces.push_back(qfid); - } + Cycle *c = h->cycle; + assert(c->inout == Cycle::INNER); + Hedge *R = NULL; + Hedge *B = NULL; + E_Int RB = 0; - while (!found && walk < max_walk) { + if (h->color == Dcel::RED) { + R = h; + B = get_hedge_of_color(f, Dcel::BLACK); + if (B) RB = 1; + } else if (h->color == Dcel::BLACK) { + B = h; + R = get_hedge_of_color(f, Dcel::RED); + if (R) RB = 1; + } else { + assert(0); + } - // Check if we reached q + if (RB) { + // RED face should always exist + assert(R->left); + f->oids[Dcel::RED] = R->left->oids[Dcel::RED]; + assert(f->oids[Dcel::RED] != -1); - for (E_Int fid : end_faces) { - if (F[fid] == current_face) { - found = 1; - break; + // BLACK face might not exist + f->oids[Dcel::BLACK] = (B->left) ? B->left->oids[Dcel::BLACK] : -1; + } else { + // Only single color possible is RED + if (!R && B) { + write_hedge("black.im", B); + point_write("orig.im", B->orig->x, B->orig->y, B->orig->z); } + assert(R && !B); + //Hedge *REP = (R != NULL) ? R : B; + //if (REP != R) { + // hedge_write("black", REP); + //} + f->oids[Dcel::RED] = R->left->oids[Dcel::RED]; } + } +} - if (found) break; - - E_Int current_fid = current_face->oid[0]; - - const E_Float *fN = &M.fnormals[3*current_fid]; - - E_Float proj[3] = { }; - E_Float dp = K_MATH::dot(fN, dir, 3); - for (E_Int i = 0; i < 3; i++) proj[i] = dir[i] - dp * fN[i]; - - E_Float dx = px + 2*proj[0]; - E_Float dy = py + 2*proj[1]; - E_Float dz = pz + 2*proj[2]; - - Hedge *h = current_face->rep; - E_Int reached = 0; - E_Int hit = 0; - - E_Float ix, iy, iz; - ix = iy = iz = -10000; - - while (!reached && !found) { - - Vertex *a = h->orig; - Vertex *b = h->twin->orig; - - hit = EdgeEdgeIntersect( - px, py, pz, - dx, dy, dz, - a->x, a->y, a->z, - b->x, b->y, b->z, - ix, iy, iz); +Dcel::Hedge *Dcel::get_hedge_of_color(Face *f, int color) +{ + Hedge *h = f->rep; + if (h->color == color) return h; + Hedge *w = h->next; + while (w != h) { + if (w->color == color) return w; + w = w->next; + } + return NULL; +} - if (hit) { +std::vector Dcel::make_cycle_faces(const std::vector &C) +{ + std::vector new_F; - Vertex *x = NULL; + for (Cycle *c : C) { + //if (c->inout == Cycle::HOLE) continue; + if (c->inout != Cycle::INNER) continue; + + // Create a face record + Face *f = new Face; - E_Int hit_a = cmp_points(ix, iy, iz, a->x, a->y, a->z) == 0; - E_Int hit_b = cmp_points(ix, iy, iz, b->x, b->y, b->z) == 0; + // Set its rep hedge to some edge of the cycle + Hedge *h = c->rep; + f->rep = h; - // Hit a vertex: original m vertex, or intersection + new_F.push_back(f); + } - if (hit_a) x = a; - else if (hit_b) x = b; + return new_F; +} - if (x != NULL) { +void Dcel::init_hedges_and_faces(const Smesh &Mf, int color) +{ + printf("Doing color %d\n", color); - // Stop if reached destination - if (x->oid[1] != -1) { - assert(x == q); - found = 1; - } + size_t current_nh = H.size(); - // M point, get the next face - else if (x->oid[0] != -1) { + H.reserve(current_nh + Mf.ne*2); - E_Int mpid = x->oid[0]; - const auto &pf = M.P2F[mpid]; - E_Int next_fid = get_next_face(M, x->x, x->y, x->z, pf, dir); - assert(next_fid != -1); - assert(next_fid != current_fid); - current_face = F[next_fid]; + std::vector> list(Mf.np); - } else { - - // Intersection, move + // Create hedge records - current_face = h->twin->left; - - } - } else { + const auto &E = Mf.E; - // Hit the inside of an edge + for (E_Int i = 0; i < Mf.ne; i++) { + const auto &e = E[i]; - // Must be a new intersection - Event *xit = Q.lookup(ix, iy, iz); + E_Int p = e.p; + E_Int q = e.q; - assert(xit == NULL); + Hedge *h = NULL; + Hedge *t = NULL; - x = new Vertex(ix, iy, iz); - x->id = V.size(); - V.push_back(x); - x->xhedge = h; + { + Vertex tmp(Mf.X[p], Mf.Y[p], Mf.Z[p]); + auto it = vertex_set.find(&tmp); + assert(it != vertex_set.end()); + Vertex *P = *it; + h = new Hedge(P, color); + list[p].push_back(h); + } - cut_hedge_at_vertex(h, x); + { + Vertex tmp(Mf.X[q], Mf.Y[q], Mf.Z[q]); + auto it = vertex_set.find(&tmp); + assert(it != vertex_set.end()); + Vertex *Q = *it; + t = new Hedge(Q, color); + list[q].push_back(t); + } - current_face = h->twin->left; + h->twin = t; + t->twin = h; - } + //h->oid = i; + //t->oid = i; - if (found) break; - - assert(x); + H.push_back(h); + H.push_back(t); + } + + // Pair-up hedges - cut_hedge_at_vertex(current_hedge, x); - current_hedge = current_hedge->next; + const auto &pnormals = Mf.pnormals; - px = ix; - py = iy; - pz = iz; + for (E_Int pid = 0; pid < Mf.np; pid++) { + auto &hedges = list[pid]; - break; - } + const E_Float *N = &pnormals[3*pid]; + sort_leaving_hedges(hedges, N); - h = h->next; - if (h == start_hedge) { - reached = 1; - } + for (size_t i = 0; i < hedges.size(); i++) { + Hedge *h = hedges[i]; + Hedge *w = hedges[(i+1)%hedges.size()]; + h->twin->next = w; + w->prev = h->twin; } - - assert(reached == 0); - walk++; } - assert(walk < max_walk); -} - -void Dcel::find_intersections_3D(const Smesh &M, const Smesh &S) -{ - puts("Isolating s_hedges..."); + // Create face records - std::vector s_hedges; + const auto &F2E = Mf.F2E; + const auto &E2F = Mf.E2F; - for (E_Int i = 2*M.ne; i < 2*(M.ne + S.ne); i += 2) { - Hedge *h = H[i]; - assert(h->twin == H[i+1]); - assert(h->color == Dcel::BLACK); - Hedge *t = h->twin; - Vertex *p = h->orig; - Vertex *q = t->orig; - if (cmp_vtx(p, q) <= 0) { - s_hedges.push_back(h); - } else { - s_hedges.push_back(t); - } - } + size_t current_nf = F.size(); - puts("Sorting s_hedges..."); + F.reserve(current_nf + Mf.nf); - std::sort(s_hedges.begin(), s_hedges.end(), [&] (Hedge *h, Hedge *w) - { - return cmp_vtx(h->orig, w->orig) <= 0; - }); + for (E_Int fid = 0; fid < Mf.nf; fid++) { + const auto &pe = F2E[fid]; + E_Int first_edge = pe[0]; + E_Int where = 2*first_edge; + Hedge *h = H[current_nh + where]; + Hedge *t = H[current_nh + where + 1]; + assert(h->twin == t); + assert(t->twin == h); - puts("Tracing edges..."); + Face *f = new Face; + f->oids[color] = fid; - for (size_t hid = 0; hid < s_hedges.size(); hid++) { - Hedge *sh = s_hedges[hid]; + // Face must lie to the left of hedge + assert(E2F[first_edge][0] == fid || E2F[first_edge][1] == fid); + Hedge *REP = (E2F[first_edge][0] == fid) ? h : t; - //printf("Tracing hedge %d / %zu\n", hid+1, s_hedges.size()); + assert(REP->left == NULL); - trace_hedge(sh, M, S, hid); + f->rep = REP; + REP->left = f; + Hedge *w = REP->next; + while (w != REP) { w->left = f; w = w->next; } + + F.push_back(f); } } void Dcel::sort_leaving_hedges(std::vector &leaving, - const E_Float N[3], - const Smesh &M) const + const E_Float N[3]) const { // Choose a vector that is not parallel to N E_Float ref_vec[3] = {0, N[2], -N[1]}; - - if (Sign(K_MATH::norm(ref_vec, 3)) == 0) { + E_Float NORM = K_MATH::norm(ref_vec, 3); + if (Sign(NORM) == 0) { ref_vec[0] = -N[2]; ref_vec[1] = 0; ref_vec[2] = N[0]; - assert(Sign(K_MATH::norm(ref_vec, 3)) != 0); + NORM = K_MATH::norm(ref_vec, 3); + assert(Sign(NORM) != 0); } E_Float dp = K_MATH::dot(ref_vec, N, 3); - for (E_Int i = 0; i < 3; i++) ref_vec[i] = ref_vec[i] - dp * N[i]; - - E_Float NORM = K_MATH::norm(ref_vec, 3); - + NORM = K_MATH::norm(ref_vec, 3); for (E_Int i = 0; i < 3; i++) ref_vec[i] /= NORM; - assert(Sign(K_MATH::norm(ref_vec, 3) - 1) == 0); - - std::vector angles; + std::vector angles(leaving.size()); for (size_t i = 0; i < leaving.size(); i++) { Hedge *h = leaving[i]; @@ -1167,232 +1225,218 @@ void Dcel::sort_leaving_hedges(std::vector &leaving, // Project the hedge onto the plane (pid, N) E_Float PQ[3] = {Q->x-P->x, Q->y-P->y, Q->z-P->z}; - E_Float dp = K_MATH::dot(PQ, N, 3); - E_Float PQ_proj[3]; - - for (E_Int j = 0; j < 3; j++) { - PQ_proj[j] = PQ[j] - dp * N[j]; - } + for (E_Int j = 0; j < 3; j++) PQ_proj[j] = PQ[j] - dp * N[j]; E_Float costheta = K_MATH::dot(ref_vec, PQ_proj, 3) / K_MATH::norm(PQ_proj, 3); - costheta = std::min(costheta, 1.0); - costheta = std::max(costheta, -1.0); - - assert(costheta >= -1 && costheta <= 1); - E_Float angle = acos(costheta); // Determine the direction of the angle - E_Float C[3] = {}; - + E_Float C[3]; K_MATH::cross(ref_vec, PQ_proj, C); - if (K_MATH::dot(N, C, 3) > 0) - angle = 2*K_MATH::PI - angle; - - //angle = angle * 180 / K_MATH::PI; + if (K_MATH::dot(N, C, 3) > 0) angle = 2*K_MATH::PI - angle; - angles.push_back(angle); + angles[i] = angle; } std::vector indices(leaving.size()); - for (size_t i = 0; i < leaving.size(); i++) - indices[i] = i; + for (size_t i = 0; i < leaving.size(); i++) indices[i] = i; std::sort(indices.begin(), indices.end(), [&](E_Int i, E_Int j) { if (angles[i] < angles[j]) return true; - else if (angles[i] > angles[j]) return false; + if (angles[i] > angles[j]) return false; - else { - Hedge *h = leaving[i]; - Hedge *w = leaving[j]; - - assert(h->color != w->color); + Hedge *h = leaving[i]; + Hedge *w = leaving[j]; - Vertex *P = h->orig; - Vertex *Q = h->twin->orig; - if (cmp_vtx(P, Q) < 0) return true; - + if (h->color == w->color) { + write_hedge("h.im", h); + write_hedge("w.im", w); + } + + assert(h->color != w->color); + + Vertex *O_h = h->orig; + Vertex *T_h = h->twin->orig; + + Vertex *O_w = w->orig; + Vertex *T_w = w->twin->orig; + + assert(O_w == O_h); + assert(T_w == T_h); + + // If this is the origin: red comes first + if (cmp_vtx(O_w, T_w) < 0) { + if (h->color == RED) return true; return false; + } else { + if (h->color == RED) return false; + return true; } }); std::vector tmp(leaving); - - for (size_t i = 0; i < leaving.size(); i++) - leaving[i] = tmp[indices[i]]; + for (size_t i = 0; i < leaving.size(); i++) leaving[i] = tmp[indices[i]]; } -void Dcel::resolve_hedges(const Smesh &M, const Smesh &S) +E_Int Dcel::check_hedges(const std::vector &H) { - assert(Up.empty()); - assert(Lp.empty()); - - Up.clear(); - Lp.clear(); - - for (Hedge *h : H) { - - Hedge *t = h->twin; - - Vertex *p = h->orig; - Vertex *q = t->orig; - - if (cmp_vtx(p, q) <= 0) { - - Up[p].push_back(h); - Up[q].push_back(t); - - } + for (size_t i = 0; i < H.size(); i++) { + Hedge *h = H[i]; + if (h->prev->next != h) { assert(0); return 1; } + if (h->next->prev != h) { assert(0); return 1; } + if (h->twin->twin != h) { assert(0); return 1; } + if (h->twin->next->orig != h->orig) { assert(0); return 1; } + if (h->prev->twin->orig != h->orig) { assert(0); return 1; } } - puts("Resolving vertices..."); - - for (size_t i = 0; i < V.size(); i++) { - - //printf("Resolving vertex %d / %zu\n", i+1, V.size()); - - Vertex *v = V[i]; - - std::vector leaving; - - for (Hedge *h : Up[v]) { - assert(h->orig == v); - leaving.push_back(h); - } - - E_Int do_sort = 0; + puts("CHECK: EDGES OK."); - for (size_t i = 1; i < leaving.size(); i++) { - if (leaving[i]->color != leaving[0]->color) { - do_sort = 1; - break; - } - } + return 0; +} - if (!do_sort) continue; - +void Dcel::make_cycles() +{ + C.clear(); + for (Hedge *h : H) h->cycle = NULL; - E_Float N[3]= { }; + for (size_t i = 0; i < H.size(); i++) { + Hedge *h = H[i]; - // M point - if (v->oid[0] != -1) { + if (h->cycle) continue; - E_Int mpid = v->oid[0]; + Cycle *c = new Cycle(h); + C.push_back(c); - const E_Float *pN = &M.pnormals[3*mpid]; - for (E_Int i = 0; i < 3; i++) N[i] = pN[i]; + h->cycle = c; + Hedge *w = h->next; + while (w != h) { + w->cycle = c; + w = w->next; } - - // S point - else if (v->oid[1] != -1) { - - const auto &loc = v->loc; - - E_Int mfid = loc.fid; - - if (loc.e_idx != -1) { + } +} - const auto &pe = M.F2E[mfid]; - E_Int eid = pe[loc.e_idx]; - const auto &pf = M.E2F[eid]; - assert(mfid == pf[0] || mfid == pf[1]); +void Dcel::set_cycles_inout() +{ + inner = 0; + degen = 0; + hole = 0; - E_Int mf1 = pf[0]; - E_Int mf2 = pf[1]; + for (Cycle *cycle : C) { - const E_Float *fN1 = &M.fnormals[3*mf1]; - const E_Float *fN2 = &M.fnormals[3*mf2]; + // Hole cycle is a cycle where all hedges have a null left face + bool is_hole = true; - for (E_Int i = 0; i < 3; i++) { - N[i] += fN1[i]; - N[i] += fN2[i]; + Hedge *h = cycle->rep; + if (!h->left) { + Hedge *w = h->next; + while (w != h) { + if (w->left) { + is_hole = false; + break; } + w = w->next; + } + } else { + is_hole = false; + } - E_Float NORM = K_MATH::norm(N, 3); - for (E_Int i = 0; i < 3; i++) N[i] /= NORM; - - } else if (loc.v_idx != -1) { - - const auto &pn = M.F[mfid]; - E_Int mpid = pn[loc.v_idx]; - const E_Float *pN = &M.pnormals[3*mpid]; - for (E_Int i = 0; i < 3; i++) N[i] = pN[i]; - - } else { + if (is_hole) { + cycle->inout = Cycle::HOLE; + hole++; + continue; + } - const E_Float *fN = &M.fnormals[3*mfid]; + // Get the leftmost vertex in the cycle + Vertex *b = h->orig; - for (E_Int i = 0; i < 3; i++) N[i] = fN[i]; + Hedge *e2 = h; // Half-edge starting at leftmost vertex + Hedge *w = h->next; + while (w != h) { + Vertex *p = w->orig; + E_Int cmp = cmp_vtx(p, b); + if (cmp < 0) { + b = p; + e2 = w; } + w = w->next; } - // Intersection - else { - - Hedge *h = v->xhedge; - assert(h); + Hedge *e1 = e2->prev; - Face *f1 = h->left; - Face *f2 = h->twin->left; + assert(e2->orig == b); + assert(e1->twin->orig == b); - E_Int mf1 = f1->oid[0]; - E_Int mf2 = f2->oid[0]; - - const E_Float *fN1 = &M.fnormals[3*mf1]; - const E_Float *fN2 = &M.fnormals[3*mf2]; - - for (E_Int i = 0; i < 3; i++) { - N[i] += fN1[i]; - N[i] += fN2[i]; - } - - E_Float NORM = K_MATH::norm(N, 3); - for (E_Int i = 0; i < 3; i++) N[i] /= NORM; - } + Vertex *a = e1->orig; + Vertex *c = e2->twin->orig; - E_Float NORM = K_MATH::norm(N, 3); - assert(Sign(NORM -1) == 0); + // Vectors ab and bc + E_Float ab[3] = {b->x-a->x, b->y-a->y, b->z-a->z}; + E_Float bc[3] = {c->x-b->x, c->y-b->y, c->z-b->z}; - sort_leaving_hedges(leaving, N, M); + E_Float N[3]; + K_MATH::cross(ab, bc, N); - for (size_t i = 0; i < leaving.size(); i++) { - Hedge *h = leaving[i]; - Hedge *w = leaving[(i+1)%leaving.size()]; - h->twin->next = w; - w->prev = h->twin; + if (Sign(K_MATH::norm(N, 3) == 0)) { + cycle->inout = Cycle::DEGEN; + degen++; + } else { + cycle->inout = Cycle::INNER; + inner++; } } + + printf("Inner cycles: " SF_D_ "\n", inner); + printf("Degen cycles: " SF_D_ "\n", degen); + printf("Hole cycles: " SF_D_ "\n", hole); } -void Dcel::reconstruct(const Smesh &M, const Smesh &S) +Dcel::~Dcel() { - check_hedges(H); + for (Vertex *v : V) delete v; + for (Hedge *h : H) delete h; + for (Face *f : F) delete f; + for (Cycle *c : C) delete c; +} - make_cycles(); +Dcel::Dcel(const Smesh &Mf, int color) +{ + const auto &X = Mf.X; + const auto &Y = Mf.Y; + const auto &Z = Mf.Z; + + V.reserve(Mf.np); + for (E_Int pid = 0; pid < Mf.np; pid++) { + Vertex *v = new Vertex(X[pid], Y[pid], Z[pid]); + v->oids[color] = pid; + vertex_set.insert(v); + V.push_back(v); + } - set_cycles_inout(M, S); + init_hedges_and_faces(Mf, color); - auto new_F = make_cycle_faces(C); - - set_face_labels(new_F); + if (check_hedges(H) != 0) { + fprintf(stderr, "Dcel: Inconsistent half-edge records!\n"); + abort(); + } - update_hedge_faces(new_F); + if (check_faces(H, F) != 0) { + fprintf(stderr, "Dcel: Inconsistent face records!\n"); + abort(); + } - for (Face *f : F) delete f; - F = new_F; + make_cycles(); + set_cycles_inout(); +} - check_faces(H, F); - //write_degen_faces("degen"); - //write_inner_faces("inner"); -} diff --git a/Cassiopee/XCore/XCore/intersectMesh/dcel.h b/Cassiopee/XCore/XCore/intersectMesh/dcel.h index 74a3d066a..b2bc357ff 100644 --- a/Cassiopee/XCore/XCore/intersectMesh/dcel.h +++ b/Cassiopee/XCore/XCore/intersectMesh/dcel.h @@ -23,91 +23,170 @@ #include #include "common/common.h" -#include "queue.h" #include "point.h" +#include "primitives.h" +#include "u_edge.h" -struct Vertex; -struct Hedge; -struct Face; -struct Segment; struct Smesh; -struct Cycle; struct Dcel { + + static const int RED = 0; + static const int BLACK = 1; + static const int NO_IDEA = -1; + + struct Vertex { + E_Float x, y, z; + E_Int oids[2] = {-1, -1}; + E_Int id = -1; + + PointLoc ploc; + E_Float d2; + + Vertex(E_Float x_, E_Float y_, E_Float z_) + { + x = x_; + y = y_; + z = z_; + oids[0] = -1; + oids[1] = -1; + } + }; + + int cmp_vtx(const Vertex *p, const Vertex *q) const + { + return cmp_points(p->x, p->y, p->z, q->x, q->y, q->z); + } + + struct cmp_vertex { + bool operator()(const Vertex *p, const Vertex *q) const + { + return cmp_points(p->x, p->y, p->z, q->x, q->y, q->z) < 0; + } + }; + + struct Face; + struct Cycle; + + struct Hedge { + Vertex *orig = NULL; + Hedge *twin = NULL; + Hedge *next = NULL; + Hedge *prev = NULL; + Face *left = NULL; + int color = NO_IDEA; + Cycle *cycle = NULL; + + Hedge(Vertex *V, int color_) + { + orig = V; + color = color_; + } + }; + + struct Face { + Hedge *rep = NULL; + E_Int oids[2] = {-1, -1}; + }; + + struct Cycle { + Hedge *rep = NULL; + int inout = 0; + + static const int HOLE = -1; + static const int DEGEN = 0; + static const int INNER = 1; + static const int OUTER = 2; + + Cycle(Hedge *h) + : rep(h) + {} + }; + std::vector V; std::vector H; std::vector F; std::vector C; - Queue Q; // Filters out duplicate vertices + std::vector> Fv; - Face *f_unbounded[2]; + std::set vertex_set; - static E_Int RED; - static E_Int BLACK; - static E_Int NO_IDEA; + E_Int inner = 0; + E_Int outer = 0; + E_Int degen = 0; + E_Int hole = 0; - std::map> Up; - std::map> Cp; - std::map> Lp; + std::map> hedge_intersections; + std::map, Vertex *> vcenter[2]; - Dcel(Smesh &M0, Smesh &M1); + std::map l2gp; + std::map g2lp; + std::map l2gf; + std::map g2lf; + Dcel(); + Dcel(const Smesh &Mf, const Smesh &Sf, const std::vector &plocs); + Dcel(const Smesh &Mf, int color); ~Dcel(); - void init_vertices(const Smesh &M0, const Smesh &M1); - - void init_hedges_and_faces(Smesh &M, E_Int color); + // Intersection - static E_Int check_hedges(const std::vector &H); - - static E_Int check_faces(const std::vector &H, - const std::vector &F); - + void init_vertices(const Smesh &Mf, const Smesh &Sf, + const std::vector &plocs); + void init_hedges_and_faces(const Smesh &Mf, int color); + void sort_leaving_hedges(std::vector &leaving, + const E_Float N[3]) const; void make_cycles(); - + void set_cycles_inout(); void set_face_labels(std::vector &F); - - Hedge *get_hedge_of_color(Face *f, E_Int color); - std::vector make_cycle_faces(const std::vector &C); + void reconstruct(const Smesh &Mf, const Smesh &Sf); + static Dcel intersect(const Smesh &Mf, const Smesh &Sf, + const std::vector &plocs); + void triangulate(const Smesh &Mf, const Smesh &Sf); - void update_hedge_faces(const std::vector &F); - - void set_cycles_inout(const Smesh &M, const Smesh &S); + // Checks - std::vector extract_indices_of_type(E_Int inout); - - std::vector extract_faces_of_indices( - const std::vector &indices); - - void write_ngon(const char *fname, const std::vector &faces) const; - - void write_degen_faces(const char *fname); - - void write_outer_faces(const char *fname); + static E_Int check_hedges(const std::vector &H); + static E_Int check_faces(const std::vector &H, + const std::vector &F); - void write_inner_faces(const char *fname); - - static std::vector get_face_vertices(Face *f); + // Helpers - void locate_spoints(const Smesh &M, const Smesh &S); + Hedge *get_hedge_of_color(Face *f, int color); + void update_hedge_faces(std::vector &F); + void get_face_vertices(const Face *f, std::vector &vertices); + //void get_vertex_normal(Vertex *q, const Smesh &Mf, E_Float N[3]); + //bool is_vertex_in_triangle(Vertex *v, Vertex *a, Vertex *b, Vertex *c); + //bool vertex_list_is_convex(const Vertex *a, const Vertex *b, + // const Vertex *c, const Smesh &Mf); - void find_intersections_3D(const Smesh &M, const Smesh &S); + // Export - void cut_hedge_at_vertex(Hedge *h, Vertex *v); + Smesh export_smesh(bool check_Euler=true) const; + void reconstruct(Smesh &Mf, int color) const; - void resolve_hedges(const Smesh &M, const Smesh &S); + // Extract - void reconstruct(const Smesh &M, const Smesh &S); + std::vector extract_indices_of_type(int inout) const; + std::vector extract_cycles_of_indices( + const std::vector &indices) const; - E_Int get_next_face(const Smesh &M, E_Float px, E_Float py, E_Float pz, - const std::vector &pf, E_Float dir[3]); + // IO - void handle_intersecting_endpoint(Vertex *v, const Smesh &M); - - void trace_hedge(Hedge *sh, const Smesh &M, const Smesh &S, E_Int hid); - - void sort_leaving_hedges(std::vector &leaving, const E_Float N[3], - const Smesh &M) const; + void write_face(const char *fname, const Face *face) const; + void write_faces(const char *fname, const std::vector &faces, + E_Float scale = 1.0) const; + void write_hedge(const char *fname, const Hedge *h) const; + void write_vertex(const char *fname, const Vertex *v) const; + void write_point(const char *fname, const std::vector &I) const; + void write_ngon(const char *fname, const std::vector &cycles) const; + void write_ngon(const char *fname, const std::vector &faces) const; + void write_ngon(const char *fname, const std::vector &fids) const; + void write_degen_cycles(const char *fname) const; + void write_inner_cycles(const char *fname) const; + void write_hole_cycles(const char *fname) const; + void write_outer_cycles(const char *fname) const; + void write_cycles_of_type(const char *fname, int type) const; }; diff --git a/Cassiopee/XCore/XCore/intersectMesh/dcel_extract.cpp b/Cassiopee/XCore/XCore/intersectMesh/dcel_extract.cpp new file mode 100644 index 000000000..12634c788 --- /dev/null +++ b/Cassiopee/XCore/XCore/intersectMesh/dcel_extract.cpp @@ -0,0 +1,136 @@ +#include "dcel.h" +#include "smesh.h" + +/* +void Dcel::reconstruct(Smesh &Mf, int color, bool check_Euler) const +{ + Smesh ret; + ret.check_Euler = check_Euler; + auto &new_F = ret.F; + std::map new_pids; + ret.np = ret.nf = 0; + + std::vector fids; + + for (size_t i = 0; i < F.size(); i++) { + Face *f = F[i]; + Hedge *h = f->rep; + if (h->color != color) continue; + Hedge *w = h->next; + while (w != h) { + if (w->color != h->color) break; + w = w->next; + } + if (w == h) { + fids.push_back(f); + } + } + + char fname[128] = {0}; + sprintf(fname, "single_color_%d.im", color); + write_ngon(fname, fids); + + for (Face *f : F) { + if (f->oids[color] == -1) continue; + + std::vector pn; + + std::vector vertices = get_face_vertices(f); + for (Vertex *v : vertices) { + auto it = new_pids.find(v); + if (it == new_pids.end()) { + new_pids[v] = ret.np; + pn.push_back(ret.np); + ret.np++; + } else { + pn.push_back(it->second); + } + } + + new_F.push_back(pn); + ret.nf++; + } + + auto &new_X = ret.X; + auto &new_Y = ret.Y; + auto &new_Z = ret.Z; + + new_X.resize(ret.np), new_Y.resize(ret.np), new_Z.resize(ret.np); + for (const auto &vdat : new_pids) { + new_X[vdat.second] = vdat.first->x; + new_Y[vdat.second] = vdat.first->y; + new_Z[vdat.second] = vdat.first->z; + } + + ret.Fc = ret.F; + + ret.make_edges(); +} +*/ + +Smesh Dcel::export_smesh(bool check_Euler) const +{ + Smesh smesh; + smesh.check_Euler = check_Euler; + + smesh.np = V.size(); + smesh.X.resize(smesh.np); + smesh.Y.resize(smesh.np); + smesh.Z.resize(smesh.np); + + for (Vertex *v : V) { + smesh.X[v->id] = v->x; + smesh.Y[v->id] = v->y; + smesh.Z[v->id] = v->z; + } + + for (size_t i = 0; i < F.size(); i++) { + Face *f = F[i]; + Hedge *REP = f->rep; + Cycle *c = REP->cycle; + if (c->inout != Cycle::OUTER) continue; + Hedge *h = REP; + + std::vector pn; + + do { + Vertex *v = h->orig; + pn.push_back(v->id); + h = h->next; + } while (h != REP); + + smesh.F.push_back(pn); + } + + smesh.nf = (E_Int)smesh.F.size(); + + smesh.Fc = smesh.F; + + smesh.make_edges(); + + return smesh; +} + +std::vector Dcel::extract_cycles_of_indices( + const std::vector &indices) const +{ + std::vector ret; + ret.reserve(indices.size()); + + for (E_Int index : indices) { + ret.push_back(C[index]); + } + + return ret; +} + +std::vector Dcel::extract_indices_of_type(int type) const +{ + std::vector ret; + + for (size_t i = 0; i < C.size(); i++) { + if (C[i]->inout == type) ret.push_back(i); + } + + return ret; +} diff --git a/Cassiopee/XCore/XCore/intersectMesh/dcel_io.cpp b/Cassiopee/XCore/XCore/intersectMesh/dcel_io.cpp new file mode 100644 index 000000000..d8ee0f44a --- /dev/null +++ b/Cassiopee/XCore/XCore/intersectMesh/dcel_io.cpp @@ -0,0 +1,318 @@ +#include "dcel.h" + +void Dcel::write_face(const char *fname, const Face *f) const +{ + FILE *fh = fopen(fname, "w"); + assert(fh); + + E_Int np = 1; + Hedge *h = f->rep; + Hedge *w = h->next; + while (w != h) { + np++; + w = w->next; + } + + fprintf(fh, "POINTS\n"); + fprintf(fh, "%d\n", np); + + h = f->rep; + Vertex *v = h->orig; + fprintf(fh, "%f %f %f\n", v->x, v->y, v->z); + w = h->next; + while (w != h) { + v = w->orig; + fprintf(fh, "%f %f %f\n", v->x, v->y, v->z); + w = w->next; + } + fclose(fh); +} + +void Dcel::write_faces(const char *fname, const std::vector &faces, + E_Float scale) const +{ + FILE *fh = fopen(fname, "w"); + assert(fh); + + E_Int NP = 0; + for (const Face *f : faces) { + E_Int np = 1; + Hedge *h = f->rep; + Hedge *w = h->next; + while (w != h) { + np++; + w = w->next; + } + NP += np; + } + + fprintf(fh, "POINTS\n"); + fprintf(fh, "%d\n", NP); + + for (const Face *f : faces) { + Hedge *h = f->rep; + Vertex *v = h->orig; + fprintf(fh, "%f %f %f\n", scale*v->x, scale*v->y, scale*v->z); + Hedge *w = h->next; + while (w != h) { + v = w->orig; + fprintf(fh, "%f %f %f\n", scale*v->x, scale*v->y, scale*v->z); + w = w->next; + } + } + + fclose(fh); +} + +void Dcel::write_hedge(const char *fname, const Hedge *h) const +{ + FILE *fh = fopen(fname, "w"); + assert(fh); + fprintf(fh, "POINTS\n"); + fprintf(fh, "2\n"); + Vertex *p = h->orig; + Vertex *q = h->twin->orig; + fprintf(fh, "%f %f %f\n", p->x, p->y, p->z); + fprintf(fh, "%f %f %f\n", q->x, q->y, q->z); + fprintf(fh, "EDGES\n"); + fprintf(fh, "1\n"); + fprintf(fh, "0 1\n"); + fclose(fh); +} + +void Dcel::write_vertex(const char *fname, const Vertex *v) const +{ + FILE *fh = fopen(fname, "w"); + assert(fh); + fprintf(fh, "POINTS\n"); + fprintf(fh, "1\n"); + fprintf(fh, "%f %f %f\n", v->x, v->y, v->z); + fclose(fh); +} + +void Dcel::write_point(const char *fname, + const std::vector &I) const +{ + FILE *fh = fopen(fname, "w"); + assert(fh); + fprintf(fh, "POINTS\n"); + fprintf(fh, "%zu\n", I.size()); + for (auto &v : I) fprintf(fh, "%f %f %f\n", v->x, v->y, v->z); + fclose(fh); +} + +void Dcel::write_cycles_of_type(const char *fname, int type) const +{ + auto indices = extract_indices_of_type(type); + auto cids = extract_cycles_of_indices(indices); + write_ngon(fname, cids); +} + +void Dcel::write_degen_cycles(const char *fname) const +{ + write_cycles_of_type(fname, Cycle::DEGEN); +} + +void Dcel::write_inner_cycles(const char *fname) const +{ + write_cycles_of_type(fname, Cycle::INNER); +} + +void Dcel::write_hole_cycles(const char *fname) const +{ + write_cycles_of_type(fname, Cycle::HOLE); +} + +void Dcel::write_outer_cycles(const char *fname) const +{ + write_cycles_of_type(fname, Cycle::OUTER); +} + +void Dcel::write_ngon(const char *fname, const std::vector &cycles) const +{ + FILE *fh = fopen(fname, "w"); + assert(fh); + + E_Int np = 0; + E_Int ne = 0; + E_Int nf = (E_Int)cycles.size(); + + std::map vmap; + std::vector new_pids; + + for (size_t i = 0; i < cycles.size(); i++) { + Cycle *c = cycles[i]; + Hedge *h = c->rep; + ne++; + Vertex *p = h->orig; + if (vmap.find(p) == vmap.end()) { + vmap[p] = np++; + new_pids.push_back(p); + } + Hedge *w = h->next; + while (w != h) { + p = w->orig; + if (vmap.find(p) == vmap.end()) { + vmap[p] = np++; + new_pids.push_back(p); + } + ne++; + w = w->next; + } + } + + fprintf(fh, "POINTS\n"); + fprintf(fh, SF_D_ "\n", np); + for (const auto &v : new_pids) { + fprintf(fh, "%f %f %f\n", v->x, v->y, v->z); + } + + fprintf(fh, "INDPG\n"); + fprintf(fh, SF_D_ "\n", ne+1); + E_Int sizeNGon = 0; + fprintf(fh, SF_D_ " ", sizeNGon); + for (E_Int i = 0; i < ne; i++) { + sizeNGon += 2; + fprintf(fh, SF_D_ " ", sizeNGon); + } + assert(sizeNGon == 2*ne); + fprintf(fh, "\n"); + + fprintf(fh, "NGON\n"); + fprintf(fh, SF_D_ "\n", sizeNGon); + for (Cycle *c : cycles) { + Hedge *h = c->rep; + Vertex *p = h->orig; + Vertex *q = h->twin->orig; + fprintf(fh, SF_D_ " " SF_D_ " ", vmap[p], vmap[q]); + Hedge *w = h->next; + while (w != h) { + p = w->orig; + q = w->twin->orig; + fprintf(fh, SF_D_ " " SF_D_ " ", vmap[p], vmap[q]); + w = w->next; + } + } + fprintf(fh, "\n"); + + fprintf(fh, "INDPH\n"); + fprintf(fh, SF_D_ "\n", nf+1); + E_Int sizeNFace = 0; + fprintf(fh, SF_D_ " ", sizeNFace); + for (Cycle *c : cycles) { + Hedge *h = c->rep; + sizeNFace += 1; + Hedge *w = h->next; + while (w != h) { + sizeNFace += 1; + w = w->next; + } + fprintf(fh, SF_D_ " ", sizeNFace); + } + fprintf(fh, "\n"); + + fprintf(fh, "NFACE\n"); + fprintf(fh, SF_D_ "\n", sizeNFace); + for (E_Int i = 0; i < sizeNFace; i++) + fprintf(fh, SF_D_ " ", i); + + fclose(fh); +} + +void Dcel::write_ngon(const char *fname, const std::vector &fids) const +{ + std::vector faces; + for (E_Int fid : fids) faces.push_back(F[fid]); + write_ngon(fname, faces); +} + +void Dcel::write_ngon(const char *fname, const std::vector &faces) const +{ + FILE *fh = fopen(fname, "w"); + assert(fh); + + E_Int np = 0; + E_Int ne = 0; + E_Int nf = (E_Int)faces.size(); + + std::map vmap; + std::vector new_pids; + + for (size_t i = 0; i < faces.size(); i++) { + Face *c = faces[i]; + Hedge *h = c->rep; + ne++; + Vertex *p = h->orig; + if (vmap.find(p) == vmap.end()) { + vmap[p] = np++; + new_pids.push_back(p); + } + Hedge *w = h->next; + while (w != h) { + p = w->orig; + if (vmap.find(p) == vmap.end()) { + vmap[p] = np++; + new_pids.push_back(p); + } + ne++; + w = w->next; + } + } + + fprintf(fh, "POINTS\n"); + fprintf(fh, SF_D_ "\n", np); + for (const auto &v : new_pids) { + fprintf(fh, "%f %f %f\n", v->x, v->y, v->z); + } + + fprintf(fh, "INDPG\n"); + fprintf(fh, SF_D_ "\n", ne+1); + E_Int sizeNGon = 0; + fprintf(fh, SF_D_ " ", sizeNGon); + for (E_Int i = 0; i < ne; i++) { + sizeNGon += 2; + fprintf(fh, SF_D_ " ", sizeNGon); + } + assert(sizeNGon == 2*ne); + fprintf(fh, "\n"); + + fprintf(fh, "NGON\n"); + fprintf(fh, SF_D_ "\n", sizeNGon); + for (Face *c : faces) { + Hedge *h = c->rep; + Vertex *p = h->orig; + Vertex *q = h->twin->orig; + fprintf(fh, SF_D_ " " SF_D_ " ", vmap[p], vmap[q]); + Hedge *w = h->next; + while (w != h) { + p = w->orig; + q = w->twin->orig; + fprintf(fh, SF_D_ " " SF_D_ " ", vmap[p], vmap[q]); + w = w->next; + } + } + fprintf(fh, "\n"); + + fprintf(fh, "INDPH\n"); + fprintf(fh, SF_D_ "\n", nf+1); + E_Int sizeNFace = 0; + fprintf(fh, SF_D_ " ", sizeNFace); + for (Face *c : faces) { + Hedge *h = c->rep; + sizeNFace += 1; + Hedge *w = h->next; + while (w != h) { + sizeNFace += 1; + w = w->next; + } + fprintf(fh, SF_D_ " ", sizeNFace); + } + fprintf(fh, "\n"); + + fprintf(fh, "NFACE\n"); + fprintf(fh, SF_D_ "\n", sizeNFace); + for (E_Int i = 0; i < sizeNFace; i++) + fprintf(fh, SF_D_ " ", i); + + fclose(fh); +} \ No newline at end of file diff --git a/Cassiopee/XCore/XCore/intersectMesh/dcel_reconstruct.cpp b/Cassiopee/XCore/XCore/intersectMesh/dcel_reconstruct.cpp new file mode 100644 index 000000000..129f73b3f --- /dev/null +++ b/Cassiopee/XCore/XCore/intersectMesh/dcel_reconstruct.cpp @@ -0,0 +1,135 @@ +#include "dcel.h" +#include "smesh.h" + +void Dcel::reconstruct(Smesh &Mf, int color) const +{ + std::map> ofid_to_ifids; + + std::vector faces_to_keep; + + E_Int nif = 0; + + for (size_t fid = 0; fid < F.size(); fid++) { + Face *f = F[fid]; + E_Int ofid = f->oids[color]; + if (ofid == -1) continue; + + ofid_to_ifids[ofid].push_back(fid); + + faces_to_keep.push_back(fid); + + nif++; + } + + //write_ngon("keep.im", faces_to_keep); + + Mf.F.resize(faces_to_keep.size()); + + // POINTS + + std::map new_pids; + E_Int NP = Mf.np; + + for (size_t i = 0; i < faces_to_keep.size(); i++) { + E_Int fid = faces_to_keep[i]; + //Face *f = F[fid]; + const auto &vertices = Fv[fid]; + for (size_t j = 0; j < vertices.size(); j++) { + Vertex *v = vertices[j]; + if (v->oids[color] != -1) continue; + E_Int vid = v->id; + auto it = new_pids.find(vid); + if (it == new_pids.end()) { + new_pids[vid] = NP; + NP++; + } + } + } + + E_Int NF = Mf.nf; + + for (const auto &fdat : ofid_to_ifids) { + E_Int parent = fdat.first; + const auto &children = fdat.second; + + // In Mf, replace parent with first child + const auto &vertices = Fv[children[0]]; + std::vector PN(vertices.size()); + for (size_t i = 0; i < vertices.size(); i++) { + Vertex *v = vertices[i]; + E_Int oid = v->oids[color]; + if (oid != -1) { + PN[i] = oid; + } else { + PN[i] = new_pids.at(v->id); + } + } + Mf.F[parent] = PN; + + // Construct fchildren for volume mesh reconstruction + std::vector fchildren(children.size()-1); + + // Add the rest of the children + for (size_t i = 1; i < children.size(); i++) { + //Face *f = F[children[i]]; + const auto &vertices = Fv[children[i]]; + std::vector PN(vertices.size()); + for (size_t j = 0; j < vertices.size(); j++) { + Vertex *v = vertices[j]; + E_Int oid = v->oids[color]; + if (oid != -1) { + PN[j] = oid; + } else { + PN[j] = new_pids.at(v->id); + } + } + Mf.F[NF] = PN; + + fchildren[i-1] = NF; + + NF++; + } + + Mf.fchildren[parent].push_back(fchildren); + } + + Mf.Fc = Mf.F; + + Mf.X.resize(NP); + Mf.Y.resize(NP); + Mf.Z.resize(NP); + + for (const auto &pdat : new_pids) { + E_Int vid = pdat.first; + E_Int pid = pdat.second; + Mf.X[pid] = V[vid]->x; + Mf.Y[pid] = V[vid]->y; + Mf.Z[pid] = V[vid]->z; + } + + Mf.nf = NF; + Mf.np = NP; + + Mf.clear_conformal_data(); + Mf.make_edges(); + Mf.make_point_faces(); + Mf.make_point_edges(); + + // Construct edge centers for volume mesh reconstruction + + const auto &vc = vcenter[color]; + + for (const auto &vdat : vc) { + const std::pair &e = vdat.first; + const Vertex *c = vdat.second; + const Vertex *p = e.first; + const Vertex *q = e.second; + + E_Int pid = p->oids[color] != -1 ? p->oids[color] : new_pids.at(p->id); + E_Int qid = q->oids[color] != -1 ? q->oids[color] : new_pids.at(q->id); + + Mf.ecenter[{pid, qid}] = new_pids.at(c->id); + } + + puts("constructed ecenters"); +} \ No newline at end of file diff --git a/Cassiopee/XCore/XCore/intersectMesh/event.cpp b/Cassiopee/XCore/XCore/intersectMesh/event.cpp deleted file mode 100644 index 30e818aad..000000000 --- a/Cassiopee/XCore/XCore/intersectMesh/event.cpp +++ /dev/null @@ -1,52 +0,0 @@ -/* - Copyright 2013-2024 Onera. - - This file is part of Cassiopee. - - Cassiopee is free software: you can redistribute it and/or modify - it under the terms of the GNU General Public License as published by - the Free Software Foundation, either version 3 of the License, or - (at your option) any later version. - - Cassiopee is distributed in the hope that it will be useful, - but WITHOUT ANY WARRANTY; without even the implied warranty of - MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - GNU General Public License for more details. - - You should have received a copy of the GNU General Public License - along with Cassiopee. If not, see . -*/ -#include "event.h" -#include "vertex.h" - -Event::Event(E_Float x, E_Float y, E_Float z) -{ - key = new Vertex(x, y, z); - inf = NULL; - left = right = NULL; -} - -Event::Event(E_Float x, E_Float y, E_Float z, E_Int oid, E_Int color) -{ - key = new Vertex(x, y, z, oid, color); - inf = NULL; - left = right = NULL; -} - -void Event::inorder(std::vector &V) const -{ - if (left) left->inorder(V); - V.push_back(key); - if (right) right->inorder(V); -} - -void Event_drop(Event *event) -{ - if (event == NULL) return; - - Event_drop(event->left); - Event_drop(event->right); - - assert(event->inf == NULL); - delete event; -} \ No newline at end of file diff --git a/Cassiopee/XCore/XCore/intersectMesh/event.h b/Cassiopee/XCore/XCore/intersectMesh/event.h deleted file mode 100644 index e9bc7712c..000000000 --- a/Cassiopee/XCore/XCore/intersectMesh/event.h +++ /dev/null @@ -1,41 +0,0 @@ -/* - Copyright 2013-2024 Onera. - - This file is part of Cassiopee. - - Cassiopee is free software: you can redistribute it and/or modify - it under the terms of the GNU General Public License as published by - the Free Software Foundation, either version 3 of the License, or - (at your option) any later version. - - Cassiopee is distributed in the hope that it will be useful, - but WITHOUT ANY WARRANTY; without even the implied warranty of - MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - GNU General Public License for more details. - - You should have received a copy of the GNU General Public License - along with Cassiopee. If not, see . -*/ -#pragma once - -#include - -#include "xcore.h" -#include "common/common.h" - -struct Vertex; -struct Segment; - -struct Event { - Vertex *key; - Segment *inf; - Event *left; - Event *right; - - Event(E_Float x, E_Float y, E_Float z, E_Int oid, E_Int color); - Event(E_Float x, E_Float y, E_Float z); - - void inorder(std::vector &V) const; -}; - -void Event_drop(Event *event); \ No newline at end of file diff --git a/Cassiopee/XCore/XCore/intersectMesh/extract.cpp b/Cassiopee/XCore/XCore/intersectMesh/extract.cpp index 406de4999..bf84cc1a7 100644 --- a/Cassiopee/XCore/XCore/intersectMesh/extract.cpp +++ b/Cassiopee/XCore/XCore/intersectMesh/extract.cpp @@ -19,7 +19,7 @@ PyObject *K_XCORE::extractCell(PyObject *self, PyObject *args) if (ret != 0) return NULL; - IMesh M(*marray.cn, marray.x, marray.y, marray.z, marray.npts); + IMesh M(marray); IMesh M_out; diff --git a/Cassiopee/XCore/XCore/intersectMesh/face.cpp b/Cassiopee/XCore/XCore/intersectMesh/face.cpp deleted file mode 100644 index 8f1a27549..000000000 --- a/Cassiopee/XCore/XCore/intersectMesh/face.cpp +++ /dev/null @@ -1,27 +0,0 @@ -/* - Copyright 2013-2024 Onera. - - This file is part of Cassiopee. - - Cassiopee is free software: you can redistribute it and/or modify - it under the terms of the GNU General Public License as published by - the Free Software Foundation, either version 3 of the License, or - (at your option) any later version. - - Cassiopee is distributed in the hope that it will be useful, - but WITHOUT ANY WARRANTY; without even the implied warranty of - MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - GNU General Public License for more details. - - You should have received a copy of the GNU General Public License - along with Cassiopee. If not, see . -*/ -#include - -#include "face.h" - -Face::Face() -: rep(NULL) -{ - oid[0] = oid[1] = -1; -} diff --git a/Cassiopee/XCore/XCore/intersectMesh/face.h b/Cassiopee/XCore/XCore/intersectMesh/face.h deleted file mode 100644 index 45779396f..000000000 --- a/Cassiopee/XCore/XCore/intersectMesh/face.h +++ /dev/null @@ -1,31 +0,0 @@ -/* - Copyright 2013-2024 Onera. - - This file is part of Cassiopee. - - Cassiopee is free software: you can redistribute it and/or modify - it under the terms of the GNU General Public License as published by - the Free Software Foundation, either version 3 of the License, or - (at your option) any later version. - - Cassiopee is distributed in the hope that it will be useful, - but WITHOUT ANY WARRANTY; without even the implied warranty of - MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - GNU General Public License for more details. - - You should have received a copy of the GNU General Public License - along with Cassiopee. If not, see . -*/ -#pragma once - -#include "xcore.h" -#include "common/common.h" - -struct Hedge; - -struct Face { - Hedge *rep; - E_Int oid[2]; - - Face(); -}; diff --git a/Cassiopee/XCore/XCore/intersectMesh/hedge.cpp b/Cassiopee/XCore/XCore/intersectMesh/hedge.cpp deleted file mode 100644 index 1b2b03e53..000000000 --- a/Cassiopee/XCore/XCore/intersectMesh/hedge.cpp +++ /dev/null @@ -1,148 +0,0 @@ -/* - Copyright 2013-2024 Onera. - - This file is part of Cassiopee. - - Cassiopee is free software: you can redistribute it and/or modify - it under the terms of the GNU General Public License as published by - the Free Software Foundation, either version 3 of the License, or - (at your option) any later version. - - Cassiopee is distributed in the hope that it will be useful, - but WITHOUT ANY WARRANTY; without even the implied warranty of - MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - GNU General Public License for more details. - - You should have received a copy of the GNU General Public License - along with Cassiopee. If not, see . -*/ -#include -#include - -#include "hedge.h" -#include "vertex.h" -#include "primitives.h" -#include "dcel.h" -#include "io.h" - -Hedge::Hedge(Vertex *v) -: orig(v), twin(NULL), prev(NULL), next(NULL), left(NULL), color(Dcel::NO_IDEA), - cycle(NULL) -{} - -static -E_Int _partition(std::vector &H, E_Int low, E_Int high) -{ - Hedge *pivot = H[high]; - E_Int i = low-1; - - for (E_Int j = low; j < high; j++) { - if (Hedge::cmp_cwise(H[j], pivot) <= 0) { - i++; - std::swap(H[i], H[j]); - } - } - - i++; - std::swap(H[i], H[high]); - return i; -} - -void Hedge::sort_cwise(std::vector &H, E_Int low, E_Int high) -{ - if (low >= high) - return; - - E_Int p = _partition(H, low, high); - - sort_cwise(H, low, p - 1); - sort_cwise(H, p + 1, high); -} - -void Hedge::sort_ccwise(std::vector &H, E_Int low, E_Int high) -{ - sort_cwise(H, low, high); - std::reverse(H.begin(), H.end()); -} - -E_Int Hedge::cmp_cwise(const Hedge *h, const Hedge *w) -{ - assert(h != w); - assert(h->orig == w->orig); - - E_Float ax = h->proj_tx; - E_Float ay = h->proj_ty; - E_Float bx = w->proj_tx; - E_Float by = w->proj_ty; - E_Float cx = h->proj_ox; - E_Float cy = h->proj_oy; - - assert(Sign(cx-w->proj_ox) == 0); - assert(Sign(cy-w->proj_oy) == 0); - - long double acx = (long double)ax - (long double)cx; - long double acy = (long double)ay - (long double)cy; - long double bcx = (long double)bx - (long double)cx; - long double bcy = (long double)by - (long double)cy; - - E_Int sign_acx = Sign(acx); - E_Int sign_acy = Sign(acy); - E_Int sign_bcx = Sign(bcx); - E_Int sign_bcy = Sign(bcy); - - if (sign_acx >= 0 && sign_bcx < 0) - return -1; - if (sign_acx < 0 && sign_bcx >= 0) - return 1; - if (sign_acx == 0 && sign_bcx == 0) { - if (sign_acy >= 0 || sign_bcy >= 0) { - long double diff = (long double)ay - (long double)by; - if (Sign(diff) > 0) return -1; - else return 1; - } - - long double diff = (long double)by - (long double)ay; - if (Sign(diff) > 0) return -1; - else return 1; - } - - E_Float det = acx * bcy - bcx * acy; - //E_Float det = DifferenceOfProducts(acx, bcy, bcx, acy); - E_Int cmp = Sign(det); - - if (cmp < 0) - return -1; - else if (cmp > 0) - return 1; - - // Overlapping segments - - if (h->color == w->color) { - hedge_write("h", h); - hedge_write("w", w); - } - - assert(h->color != w->color); - - // If right half, red before black - // Otherwise, black before red - - cmp = Sign(h->color - w->color); - - if (sign_acx >= 0) { - assert(sign_bcx >= 0); - return cmp; - } else { - return -cmp; - } -} - -E_Int hedge_contains_vertex(Hedge *h, Vertex *v) -{ - Vertex *a = h->orig; - Vertex *b = h->twin->orig; - return is_point_on_segment( - v->x, v->y, v->z, - a->x, a->y, a->z, - b->x, b->y, b->z); -} diff --git a/Cassiopee/XCore/XCore/intersectMesh/hedge.h b/Cassiopee/XCore/XCore/intersectMesh/hedge.h deleted file mode 100644 index 760827f24..000000000 --- a/Cassiopee/XCore/XCore/intersectMesh/hedge.h +++ /dev/null @@ -1,58 +0,0 @@ -/* - Copyright 2013-2024 Onera. - - This file is part of Cassiopee. - - Cassiopee is free software: you can redistribute it and/or modify - it under the terms of the GNU General Public License as published by - the Free Software Foundation, either version 3 of the License, or - (at your option) any later version. - - Cassiopee is distributed in the hope that it will be useful, - but WITHOUT ANY WARRANTY; without even the implied warranty of - MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - GNU General Public License for more details. - - You should have received a copy of the GNU General Public License - along with Cassiopee. If not, see . -*/ -#pragma once - -#include - -#include "xcore.h" -#include "common/common.h" - -struct Vertex; -struct Face; -struct Cycle; - -struct Hedge { - Vertex *orig; - Hedge *twin; - Hedge *prev; - Hedge *next; - Face *left; - E_Int color; - Cycle *cycle; - - E_Int eid; - - // Projection of orig - E_Float proj_ox = -10000; - E_Float proj_oy = -10000; - E_Float proj_oz = -10000; - - // Projection of tail - E_Float proj_tx = -10000; - E_Float proj_ty = -10000; - E_Float proj_tz = -10000; - - Hedge(Vertex *Orig); - - static E_Int cmp_cwise(const Hedge *h, const Hedge *w); - static void sort_cwise(std::vector &H, E_Int start, E_Int end); - static void sort_ccwise(std::vector &H, E_Int start, E_Int end); -}; - -E_Int hedge_contains_vertex(Hedge *h, Vertex *v); diff --git a/Cassiopee/XCore/XCore/intersectMesh/icapsule.cpp b/Cassiopee/XCore/XCore/intersectMesh/icapsule.cpp new file mode 100644 index 000000000..54bed37e4 --- /dev/null +++ b/Cassiopee/XCore/XCore/intersectMesh/icapsule.cpp @@ -0,0 +1,499 @@ +#include + +#include "icapsule.h" +#include "common/Karray.h" +#include "point.h" +#include "io.h" +#include "primitives.h" +#include "dcel.h" + +/* +ICapsule ICapsule::do_it(const Karray &marray, + const std::vector &sarrays, const std::vector &ptags) +{ + ICapsule icap; + auto &M = icap.M; + auto &Ss = icap.Ss; + + E_Float NEAR_VERTEX_TOL = 1e-3; + E_Float NEAR_EDGE_TOL = 1e-3; + + puts("Creating mesh from karray"); + M = IMesh(marray); + M.set_tolerances(NEAR_VERTEX_TOL, NEAR_EDGE_TOL); + M.make_skin(); + M.orient_skin(OUT); + M.triangulate_skin(); + puts("Creating surface mesh"); + Smesh Mf(M, M.skin, false); + puts("Making surface mesh data"); + Mf.make_bbox(); + Mf.hash_faces(); + Mf.make_fcenters(); + Mf.make_fnormals(); + Mf.make_pnormals(); + + //Mf.write_ngon("Mf_raw.im"); + + Ss.reserve(sarrays.size()); + + // Adapt + + //for (size_t i = 0; i < 4; i++) { + for (size_t i = 0; i < sarrays.size(); i++) { + + printf("S%lu\n", i); + + // Create IMesh S + IMesh S(sarrays[i]); + S.set_tolerances(NEAR_VERTEX_TOL, NEAR_EDGE_TOL); + S.make_skin(); + S.orient_skin(IN); + S.triangulate_skin(); + S.make_edges(); + + // Create SMesh Sf + Smesh Sf = Smesh::Smesh_from_point_tags(S, ptags[i], true); + + Sf.make_fcenters(); + Sf.make_fnormals(); + Sf.make_pnormals(); + Sf.make_bbox(); + Sf.hash_faces(); + Sf.compute_min_distance_between_points(); + printf("Min dist: %f\n", Sf.min_pdist); + + //Sf.write_ngon("Sf_raw.im"); + + // Locate Sf points on Mf faces + auto plocs = Mf.locate(Sf); + std::vector spids(Sf.np); + for (int i = 0; i < Sf.np; i++) spids[i] = i; + Sf.replace_by_projections(spids, plocs); + + // Extract the initial Mf faces that cover Sf + auto bfaces = Mf.extract_covering_faces(Sf, plocs); + + // Refinement loop + refine(Mf, bfaces, Sf); + + // Reconstruct S + Sf.reconstruct(S); + + // TODO(Imad): Tag Sf faces + Sf.tag_faces(S); + + // Append + Ss.push_back(S); + + puts(""); + } + + Mf.reconstruct(M); + + return icap; +} +*/ + + +PyObject *K_XCORE::icapsule_extract_master(PyObject *self, PyObject *args) +{ + PyObject *ICAPSULE; + if (!PYPARSETUPLE_(args, O_, &ICAPSULE)) { + RAISE("Bad input."); + return NULL; + } + + if (!PyCapsule_IsValid(ICAPSULE, "ICAPSULE")) { + RAISE("Bad ICapsule hook."); + return NULL; + } + + ICapsule *icap = (ICapsule *)PyCapsule_GetPointer(ICAPSULE, "ICAPSULE"); + + auto Mout = icap->M.export_karray(); + + return Mout; +} + +PyObject *K_XCORE::icapsule_extract_slave(PyObject *self, PyObject *args) +{ + PyObject *ICAPSULE; + E_Int INDEX; + if (!PYPARSETUPLE_(args, O_ I_, &ICAPSULE, &INDEX)) { + RAISE("Bad input."); + return NULL; + } + + if (!PyCapsule_IsValid(ICAPSULE, "ICAPSULE")) { + RAISE("Bad ICapsule hook."); + return NULL; + } + + ICapsule *icap = (ICapsule *)PyCapsule_GetPointer(ICAPSULE, "ICAPSULE"); + + if (INDEX >= (E_Int)icap->Ss.size()) { + RAISE("Bad slave index."); + return NULL; + } + + auto Sout = icap->Ss[INDEX].export_karray(); + + return Sout; +} + +PyObject *K_XCORE::icapsule_extract_slaves(PyObject *self, PyObject *args) +{ + PyObject *ICAPSULE; + + if (!PYPARSETUPLE_(args, O_, &ICAPSULE)) { + RAISE("Bad input."); + return NULL; + } + + if (!PyCapsule_IsValid(ICAPSULE, "ICAPSULE")) { + RAISE("Bad ICapsule hook."); + return NULL; + } + + ICapsule *icap = (ICapsule *)PyCapsule_GetPointer(ICAPSULE, "ICAPSULE"); + + PyObject *out = PyList_New(0); + + for (size_t i = 0; i < icap->Ss.size(); i++) { + PyObject *sarray = icap->Ss[i].export_karray(); + PyList_Append(out, sarray); + Py_DECREF(sarray); + } + + return out; +} + +ICapsule::ICapsule(const Karray &marray, const std::vector &sarrays, + const std::vector &ptags) +{ + E_Float NEAR_VERTEX_TOL = 1e-3; + E_Float NEAR_EDGE_TOL = 1e-3; + + M = IMesh(marray); + M.set_tolerances(NEAR_VERTEX_TOL, NEAR_EDGE_TOL); + M.make_skin(); + M.orient_skin(OUT); + M.triangulate_skin(); + + Ss.reserve(sarrays.size()); + for (size_t i = 0; i < sarrays.size(); i++) { + Ss.push_back(sarrays[i]); + Ss[i].set_tolerances(NEAR_VERTEX_TOL, NEAR_EDGE_TOL); + Ss[i].make_skin(); + Ss[i].orient_skin(IN); + Ss[i].triangulate_skin(); + Ss[i].ptag.resize(Ss[i].np); + memcpy(Ss[i].ptag.data(), ptags[i], Ss[i].np*sizeof(E_Float)); + } +} + +PyObject *K_XCORE::icapsule_init(PyObject *self, PyObject *args) +{ + PyObject *MASTER, *SLAVES, *PTAGS; + if (!PYPARSETUPLE_(args, OOO_, &MASTER, &SLAVES, &PTAGS)) { + RAISE("Bad input."); + return NULL; + } + + Karray marray; + if (Karray_parse_ngon(MASTER, marray) != 0) { + RAISE("Master should be an NGon."); + return NULL; + } + + E_Int nslaves = PyList_Size(SLAVES); + std::vector sarrays(nslaves); + E_Int ok = 0; + + for (E_Int i = 0; i < nslaves; i++) { + PyObject *SLAVE = PyList_GetItem(SLAVES, i); + E_Int ret = Karray_parse_ngon(SLAVE, sarrays[i]); + Py_DECREF(SLAVE); + if (ret != 0) { + RAISE("Slaves should be NGons."); + break; + } + ok++; + } + + if (ok != nslaves) { + Karray_free_ngon(marray); + for (E_Int i = 0; i < ok; i++) + Karray_free_ngon(sarrays[i]); + return NULL; + } + + std::vector ptags(nslaves, NULL); + + if (PyList_Size(PTAGS) != nslaves) { + RAISE("Ptags should be the same size as slaves."); + Karray_free_ngon(marray); + for (E_Int i = 0; i < nslaves; i++) + Karray_free_ngon(sarrays[i]); + return NULL; + } + + for (E_Int i = 0; i < nslaves; i++) { + PyObject *PTAG = PyList_GetItem(PTAGS, i); + E_Int size = -1; + E_Int ret = K_NUMPY::getFromNumpyArray(PTAG, ptags[i], size, true); + //Py_DECREF(PTAG); + if (ret != 1 || size != sarrays[i].npoints()) { + RAISE("Ptag[i] should have size sarrays[i].npoints."); + Karray_free_ngon(marray); + for (E_Int j = 0; j < nslaves; j++) + Karray_free_ngon(sarrays[j]); + return NULL; + } + } + + ICapsule *icap = new ICapsule(marray, sarrays, ptags); + + PyObject *hook = PyCapsule_New((void *)icap, "ICAPSULE", NULL); + + return hook; +} + +PyObject *K_XCORE::icapsule_adapt(PyObject *self, PyObject *args) +{ + PyObject *ICAPSULE; + if (!PYPARSETUPLE_(args, O_, &ICAPSULE)) { + RAISE("Bad input."); + return NULL; + } + + if (!PyCapsule_IsValid(ICAPSULE, "ICAPSULE")) { + RAISE("Bad capsule hook."); + return NULL; + } + + ICapsule *icap = (ICapsule *)PyCapsule_GetPointer(ICAPSULE, "ICAPSULE"); + + auto &M = icap->M; + auto &Ss = icap->Ss; + + Smesh Mf(M, M.skin, false); + puts("Making surface mesh data"); + + Mf.write_ngon("Mf_before_inter.im"); + + //for (E_Int i = 25; i < 26; i++) { + for (size_t i = 0; i < Ss.size(); i++) { + printf("S%lu\n", i); + + Mf.make_bbox(); + Mf.hash_faces(); + Mf.make_fcenters(); + Mf.make_fnormals(); + Mf.make_pnormals(); + Mf.make_point_faces(); + + auto &S = Ss[i]; + + Smesh Sf = Smesh::Smesh_from_point_tags(S, S.ptag.data(), true); + + Sf.make_fcenters(); + Sf.make_fnormals(); + Sf.make_pnormals(); + Sf.make_bbox(); + Sf.hash_faces(); + Sf.compute_min_distance_between_points(); + printf("Min dist: %f\n", Sf.min_pdist); + + //Sf.write_ngon("Sf_before_inter.im"); + + // Locate Sf points on Mf faces + auto plocs = Mf.locate2(Sf); + std::vector spids(Sf.np); + for (int i = 0; i < Sf.np; i++) spids[i] = i; + Sf.replace_by_projections(spids, plocs); + + // Extract the initial Mf faces that cover Sf + auto bfaces = Mf.extract_covering_faces(Sf, plocs); + + // Refinement loop + ICapsule::refine(Mf, bfaces, Sf); + + // Reconstruct S + Sf.reconstruct(S); + + // Reconstruct S skin + S.make_skin(); + + // Tag Sf faces + Sf.tag_faces(S); + } + + Mf.reconstruct(M); + + PyObject *out = PyList_New(0); + PyList_Append(out, M.export_karray()); + + PyObject *slist = PyList_New(0); + for (const auto &S : Ss) { + PyList_Append(slist, S.export_karray()); + } + PyList_Append(out, slist); + Py_DECREF(slist); + + PyObject *tlist = PyList_New(0); + for (const auto &S : Ss) { + npy_intp dims[2]; + dims[0] = (npy_intp)S.ftag.size(); + dims[1] = 1; + PyArrayObject *arr = (PyArrayObject *)PyArray_SimpleNew(1, dims, E_NPY_INT); + E_Int *ptr = (E_Int *)PyArray_DATA(arr); + for (size_t i = 0; i < S.ftag.size(); i++) ptr[i] = S.ftag[i]+1; + PyList_Append(tlist, (PyObject *)arr); + Py_DECREF(arr); + } + PyList_Append(out, tlist); + Py_DECREF(tlist); + + return out; +} + +PyObject *K_XCORE::icapsule_intersect(PyObject *self, PyObject *args) +{ + PyObject *MASTER, *SLAVES, *STAGS; + if (!PYPARSETUPLE_(args, OOO_, &MASTER, &SLAVES, &STAGS)) { + RAISE("Bad input."); + return NULL; + } + + Karray marray; + int ret = Karray_parse_ngon(MASTER, marray); + if (ret != 0) { + RAISE("Bad master mesh."); + return NULL; + } + IMesh M(marray); + + int slave_count = PyList_Size(SLAVES); + std::vector Ss; + Ss.reserve(slave_count); + + assert(PyList_Size(SLAVES) == PyList_Size(STAGS)); + + for (int i = 0; i < slave_count; i++) { + Karray sarray; + ret = Karray_parse_ngon(PyList_GetItem(SLAVES, i), sarray); + assert(ret == 0); + IMesh S(sarray); + S.make_skin(); + S.orient_skin(IN); + + PyObject *STAG = PyList_GetItem(STAGS, i); + E_Int *tag = NULL; + E_Int tag_size = -1; + ret = K_NUMPY::getFromNumpyArray(STAG, tag, tag_size, true); + assert(ret == 1); + S.ftag.reserve(tag_size); + for (int j = 0; j < tag_size; j++) { + E_Int fid = tag[j]-1; + assert(fid < S.nf); + S.ftag.push_back(fid); + } + + Ss.push_back(S); + } + + M.make_skin(); + M.orient_skin(OUT); + Smesh Mf(M, M.skin, false); + + for (size_t i = 0; i < Ss.size(); i++) { + + printf("Intersecting slave %lu\n", i); + + Mf.make_bbox(); + Mf.hash_faces(); + Mf.make_fcenters(); + Mf.make_fnormals(); + Mf.make_pnormals(); + Mf.make_point_faces(); + + //Mf.write_ngon("Mf_before_intersect.im"); + + auto &S = Ss[i]; + + Smesh Sf = Smesh::Smesh_from_tagged_faces(S, true); + Sf.make_fcenters(); + Sf.make_fnormals(); + Sf.make_pnormals(); + + //{ + // char fname[32] = {0}; + // sprintf(fname, "Sf_before_intersect_%d.im", i); + // Sf.write_ngon(fname); + //} + + auto plocs = Mf.locate2(Sf); + + Dcel D = Dcel::intersect(Mf, Sf, plocs); + + //E_Int nf_before_intersect = Sf.nf; + + D.reconstruct(Mf, Dcel::RED); + //Mf.write_ngon("Mf_after_intersect.im"); + + D.reconstruct(Sf, Dcel::BLACK); + + //{ + // char fname[32] = {0}; + // sprintf(fname, "Sf_after_intersect_%d.im", i); + // Sf.write_ngon(fname); + //} + + //{ + // char fname[32] = {0}; + // sprintf(fname, "intersected_%d.im", i); + // D.write_inner_cycles(fname); + //} + + Sf.reconstruct(S); + + // Tag Sf faces + Sf.tag_faces(S); + + puts("Intersection OK\n"); + fflush(stdout); + } + + Mf.reconstruct(M); + + PyObject *out = PyList_New(0); + + PyList_Append(out, M.export_karray()); + + PyObject *slist = PyList_New(0); + + for (const auto &S : Ss) { + PyList_Append(slist, S.export_karray()); + } + + PyList_Append(out, slist); + Py_DECREF(slist); + + PyObject *tlist = PyList_New(0); + for (const auto &S : Ss) { + npy_intp dims[2]; + dims[0] = (npy_intp)S.ftag.size(); + dims[1] = 1; + PyArrayObject *arr = (PyArrayObject *)PyArray_SimpleNew(1, dims, E_NPY_INT); + E_Int *ptr = (E_Int *)PyArray_DATA(arr); + for (size_t i = 0; i < S.ftag.size(); i++) ptr[i] = S.ftag[i]+1; + PyList_Append(tlist, (PyObject *)arr); + Py_DECREF(arr); + } + PyList_Append(out, tlist); + Py_DECREF(tlist); + + return out; +} diff --git a/Cassiopee/XCore/XCore/intersectMesh/icapsule.h b/Cassiopee/XCore/XCore/intersectMesh/icapsule.h new file mode 100644 index 000000000..d155035e9 --- /dev/null +++ b/Cassiopee/XCore/XCore/intersectMesh/icapsule.h @@ -0,0 +1,14 @@ +#pragma once + +#include "mesh.h" + +struct ICapsule { + IMesh M; + std::vector Ss; + + ICapsule(const Karray &marray, const std::vector &sarrays, + const std::vector &ptags); + + static std::vector refine(Smesh &Mf, + std::set &mfids, Smesh &Sf); +}; diff --git a/Cassiopee/XCore/XCore/intersectMesh/icapsule_refine.cpp b/Cassiopee/XCore/XCore/intersectMesh/icapsule_refine.cpp new file mode 100644 index 000000000..70d4e0261 --- /dev/null +++ b/Cassiopee/XCore/XCore/intersectMesh/icapsule_refine.cpp @@ -0,0 +1,166 @@ +#include "icapsule.h" +#include "ray.h" +#include "BVH.h" +#include "io.h" +#include "primitives.h" + +std::vector Smesh::project(const Smesh &Mf, + const std::vector &mpids) const +{ + std::vector plocs; + plocs.reserve(mpids.size()); + + for (size_t i = 0; i < mpids.size(); i++) { + E_Int mpid = mpids[i]; + const E_Float *N = &Mf.pnormals[3*mpid]; + E_Float mx = Mf.X[mpid]; + E_Float my = Mf.Y[mpid]; + E_Float mz = Mf.Z[mpid]; + std::vector mlocs; // to parse + ray_intersect_BVH(mx, my, mz, N[0], N[1], N[2], root_node_idx, mlocs); + PointLoc ploc; + E_Float min_abs_t = EFLOATMAX; + for (const auto &mloc : mlocs) { + if (fabs(mloc.t) < min_abs_t) { + min_abs_t = fabs(mloc.t); + ploc = mloc; + } + } + plocs.push_back(ploc); + } + + return plocs; +} + +std::vector Smesh::deduce_ref_faces(const std::vector &mpids, + const std::vector &plocs_m, const Smesh &Mf, + std::vector &ref_faces) +{ + // Sfids to enclosed mpids + std::map> smap; + for (size_t i = 0; i < mpids.size(); i++) { + const auto &ploc_m = plocs_m[i]; + if (ploc_m.fid == -1) continue; + + std::vector sfids; + E_Int dummy; + get_shared_faces(ploc_m, sfids, dummy, dummy); + + for (E_Int sfid : sfids) + smap[sfid].push_back(mpids[i]); + } + + std::map> sfid_to_mfids; + + for (const auto &spdata : smap) { + E_Int sfid = spdata.first; + + // Partly enclosed mfids to point count + std::map mfid_pcount; + + const auto &mpids = spdata.second; + for (E_Int mpid : mpids) { + const auto &pf = Mf.P2F[mpid]; + for (E_Int mfid : pf) mfid_pcount[mfid] += 1; + } + + // Deduce the mfids that are completely enclosed + for (const auto &mpdata : mfid_pcount) { + E_Int mfid = mpdata.first; + size_t count = mpdata.second; + assert(count <= Mf.Fc[mfid].size()); + if (count == Mf.Fc[mfid].size()) { + sfid_to_mfids[sfid].push_back(mfid); + } + } + } + + ref_faces.clear(); + ref_faces.reserve(sfid_to_mfids.size()); + for (auto it = sfid_to_mfids.begin(); it != sfid_to_mfids.end(); it++) + ref_faces.push_back(it->first); + + return ref_faces; +} + +std::vector ICapsule::refine(Smesh &Mf, std::set &mfids, + Smesh &Sf) +{ + size_t ref_M, ref_S; + + E_Int iter = 0; + + std::vector plocs_s; + + do { + iter++; + + std::vector fat_sfids; + std::vector fat_mfids; + + Sf.make_BVH(); + + Mf.make_bbox(); + Mf.hash_faces(); + + /*********************** Mf refinement ***********************/ + + // Test all the Sf points + std::vector spids; + spids.reserve(Sf.np); + for (E_Int i = 0; i < Sf.np; i++) spids.push_back(i); + + // Reproject spids on mfaces + Mf.make_fcenters(); + plocs_s = Mf.locate2(Sf); + Sf.replace_by_projections(spids, plocs_s); + + // Deduce mfids to refine + std::vector mref_faces; + Mf.deduce_ref_faces(spids, plocs_s, Sf, mref_faces); + printf("Fat mfids: %lu\n", mref_faces.size()); + + ref_M = mref_faces.size(); + if (ref_M > 0) { + Mf.refine(mref_faces); + Mf.conformize(); + Mf.make_pnormals(); + // update mfids + for (E_Int fparent : mref_faces) { + const auto &children = Mf.fchildren[fparent].back(); + for (E_Int child : children) mfids.insert(child); + } + } + + /*********************** Sf refinement ***********************/ + + // Isolate the points to project + std::set mpids_set; + for (E_Int fid : mfids) { + const auto &pn = Mf.Fc[fid]; + for (E_Int p : pn) mpids_set.insert(p); + } + std::vector mpids; + for (E_Int p : mpids_set) mpids.push_back(p); + + // Project mpids on Sf + Sf.make_fcenters(); + auto plocs_m = Sf.project(Mf, mpids); + + // Deduce sfids to refine + std::vector sref_faces; + Sf.deduce_ref_faces(mpids, plocs_m, Mf, sref_faces); + printf("Fat sfids: %lu\n", sref_faces.size()); + + // Refine + ref_S = sref_faces.size(); + if (ref_S > 0) { + Sf.refine(sref_faces); + Sf.conformize(); + Sf.make_pnormals(); + } + + } while (ref_M > 0 || ref_S > 0); + + return plocs_s; +} diff --git a/Cassiopee/XCore/XCore/intersectMesh/intersectMesh.cpp b/Cassiopee/XCore/XCore/intersectMesh/intersectMesh.cpp index b70089049..fa76a1073 100644 --- a/Cassiopee/XCore/XCore/intersectMesh/intersectMesh.cpp +++ b/Cassiopee/XCore/XCore/intersectMesh/intersectMesh.cpp @@ -22,17 +22,14 @@ #include "mesh.h" #include "smesh.h" #include "dcel.h" -#include "vertex.h" -#include "face.h" -#include "hedge.h" #include "io.h" -#include "cycle.h" #include "triangle.h" #include "primitives.h" -static IMesh reconstruct_mesh(IMesh &M, Smesh &Mf, const Dcel &D, E_Int color) { + assert(0); + /* // Isolate patch faces std::set pfset(M.patch); @@ -283,28 +280,32 @@ IMesh reconstruct_mesh(IMesh &M, Smesh &Mf, const Dcel &D, E_Int color) new_M.ctag = M.ctag; return new_M; + */ + return IMesh(); } PyObject *K_XCORE::intersectMesh(PyObject *self, PyObject *args) { - PyObject *MASTER, *SLAVE, *SPATCH; + /* + PyObject *MASTER, *SLAVE, *MPATCH, *SPATCH; - if (!PYPARSETUPLE_(args, OOO_, &MASTER, &SLAVE, &SPATCH)) { + if (!PYPARSETUPLE_(args, OOOO_, &MASTER, &MPATCH, &SLAVE, &SPATCH)) { RAISE("Bad input."); return NULL; } - if (!PyCapsule_IsValid(MASTER, "IntersectMesh")) { - RAISE("Bad mesh hook."); + Karray marray; + + E_Int ret; + + ret = Karray_parse_ngon(MASTER, marray); + + if (ret != 0) { return NULL; } - IMesh &M = *(IMesh *)PyCapsule_GetPointer(MASTER, "IntersectMesh"); - Karray sarray; - E_Int ret; - ret = Karray_parse_ngon(SLAVE, sarray); if (ret != 0) { @@ -312,7 +313,8 @@ PyObject *K_XCORE::intersectMesh(PyObject *self, PyObject *args) } // Init and orient master/slave meshes - IMesh S(*sarray.cn, sarray.x, sarray.y, sarray.z, sarray.npts); + IMesh M(*marray.cn, marray.X(), marray.Y(), marray.Z(), marray.npts); + IMesh S(*sarray.cn, sarray.X(), sarray.Y(), sarray.Z(), sarray.npts); M.make_skin(); S.make_skin(); @@ -320,39 +322,62 @@ PyObject *K_XCORE::intersectMesh(PyObject *self, PyObject *args) M.orient_skin(OUT); S.orient_skin(IN); - M.patch.clear(); - for (E_Int fid : M.skin) M.patch.insert(fid); - - printf("Master patch: %zu faces\n", M.patch.size()); + // Check intersection patches (one-based) + E_Int *mpatch = NULL; + E_Int mpatch_size = -1; + ret = K_NUMPY::getFromNumpyArray(MPATCH, mpatch, mpatch_size, true); + if (ret != 1) { + Karray_free_ngon(marray); + RAISE("Bad master patch."); + return NULL; + } + printf("Master patch: " SF_D_ " faces\n", mpatch_size); - // Check slave intersection patch (zero-based) E_Int *spatch = NULL; E_Int spatch_size = -1; ret = K_NUMPY::getFromNumpyArray(SPATCH, spatch, spatch_size, true); if (ret != 1) { + Karray_free_ngon(marray); Karray_free_ngon(sarray); RAISE("Bad slave patch."); return NULL; } - printf("Slave patch: " SF_D_ " faces\n", spatch_size); + assert(M.patch.empty()); + assert(S.patch.empty()); + for (E_Int i = 0; i < mpatch_size; i++) M.patch.insert(mpatch[i]-1); for (E_Int i = 0; i < spatch_size; i++) S.patch.insert(spatch[i]-1); + + { + //E_Int weird[2]; + //E_Int count = 0; + //const auto &pf = S.C[7726]; + //for (auto fid : pf) { + // if (S.patch.find(fid) != S.patch.end()) { + // weird[count++] = fid; + // assert(count == 1 || count == 2); + // } + //} + //printf("weirds: %d - %d\n", weird[0], weird[1]); + } + + // Extract surface meshes - Smesh Mf(M); - Smesh Sf(S); + Smesh Mf = Smesh::Smesh_from_mesh_patch(M); + Smesh Sf = Smesh::Smesh_from_mesh_patch(S); - //Mf.write_ngon("Mf"); - //Sf.write_ngon("Sf"); + Mf.write_ngon("Mf"); + Sf.write_ngon("Sf"); puts("Making point edges..."); Mf.make_point_edges(); Sf.make_point_edges(); puts("Making point faces..."); - Mf.make_point_faces_all(); - Sf.make_point_faces_all(); + Mf.make_point_faces(); + Sf.make_point_faces(); puts("Making point/face normals..."); Mf.make_pnormals(); @@ -382,6 +407,11 @@ PyObject *K_XCORE::intersectMesh(PyObject *self, PyObject *args) D.reconstruct(Mf, Sf); + puts("Constructing surface mesh..."); + + //Smesh smesh = D.export_smesh(); + //smesh.write_ngon("smesh"); + for (Vertex *v : D.V) { E_Int oid = v->oid[0]; if (oid != -1) v->oid[0] = Mf.l2gp[oid]; @@ -389,21 +419,31 @@ PyObject *K_XCORE::intersectMesh(PyObject *self, PyObject *args) oid = v->oid[1]; if (oid != -1) v->oid[1] = Sf.l2gp[oid]; } - - M = reconstruct_mesh(M, Mf, D, Dcel::RED); + IMesh M_inter = reconstruct_mesh(M, Mf, D, Dcel::RED); IMesh S_inter = reconstruct_mesh(S, Sf, D, Dcel::BLACK); // Export printf("Exporting... "); + PyObject *Mout = M_inter.export_karray(); PyObject *Sout = S_inter.export_karray(); printf("Done.\n"); + Karray_free_ngon(marray); Karray_free_ngon(sarray); - + + Py_DECREF(MPATCH); Py_DECREF(SPATCH); - return Sout; + PyObject *out = PyList_New(0); + PyList_Append(out, Mout); + PyList_Append(out, Sout); + Py_DECREF(Mout); + Py_DECREF(Sout); + + return out; + */ + return Py_None; } diff --git a/Cassiopee/XCore/XCore/intersectMesh/io.cpp b/Cassiopee/XCore/XCore/intersectMesh/io.cpp index 05626e6e6..7257a5996 100644 --- a/Cassiopee/XCore/XCore/intersectMesh/io.cpp +++ b/Cassiopee/XCore/XCore/intersectMesh/io.cpp @@ -20,50 +20,6 @@ #include #include "io.h" -#include "face.h" - -void face_write(const char *fname, Face *f) -{ - FILE *fh = fopen(fname, "w"); - assert(fh); - - E_Int np = 1; - Hedge *h = f->rep; - Hedge *w = h->next; - while (w != h) { - np++; - w = w->next; - } - - fprintf(fh, "POINTS\n"); - fprintf(fh, "%d\n", np); - h = f->rep; - Vertex *v = h->orig; - fprintf(fh, "%f %f %f\n", v->x, v->y, v->z); - w = h->next; - while (w != h) { - v = w->orig; - fprintf(fh, "%f %f %f\n", v->x, v->y, v->z); - w = w->next; - } - fclose(fh); -} - -void hedge_write(const char *fname, const Hedge *h) -{ - FILE *fh = fopen(fname, "w"); - assert(fh); - fprintf(fh, "POINTS\n"); - fprintf(fh, "2\n"); - Vertex *p = h->orig; - Vertex *q = h->twin->orig; - fprintf(fh, "%f %f %f\n", p->x, p->y, p->z); - fprintf(fh, "%f %f %f\n", q->x, q->y, q->z); - fprintf(fh, "EDGES\n"); - fprintf(fh, "1\n"); - fprintf(fh, "0 1\n"); - fclose(fh); -} void point_write(const char *fname, E_Float x, E_Float y, E_Float z) { @@ -75,26 +31,6 @@ void point_write(const char *fname, E_Float x, E_Float y, E_Float z) fclose(fh); } -void point_write(const char *fname, Vertex *v) -{ - FILE *fh = fopen(fname, "w"); - assert(fh); - fprintf(fh, "POINTS\n"); - fprintf(fh, "1\n"); - fprintf(fh, "%f %f %f\n", v->x, v->y, v->z); - fclose(fh); -} - -void point_write(const char *fname, const std::vector &I) -{ - FILE *fh = fopen(fname, "w"); - assert(fh); - fprintf(fh, "POINTS\n"); - fprintf(fh, "%zu\n", I.size()); - for (auto &v : I) fprintf(fh, "%f %f %f\n", v->x, v->y, v->z); - fclose(fh); -} - void point_write(const char *fname, E_Float *Xs, E_Float *Ys, E_Float *Zs, const std::vector &P) { @@ -112,7 +48,7 @@ void point_write(const char *fname, const std::vector &P) assert(fh); fprintf(fh, "POINTS\n"); fprintf(fh, "%zu\n", P.size()); - for (auto p : P) fprintf(fh, "%f %f %f\n", p[0], p[1], p[2]); + for (auto p : P) fprintf(fh, "%f %f %f\n", p.x, p.y, p.z); fclose(fh); } @@ -152,22 +88,3 @@ void edge_write(const char *fname, E_Float px, E_Float py, E_Float pz, fprintf(fh, "0 1\n"); fclose(fh); } - -void edges_write(const char *fname, const std::vector &edges) -{ - FILE *fh = fopen(fname, "w"); - assert(fh); - fprintf(fh, "POINTS\n"); - fprintf(fh, "%zu\n", edges.size() * 2); - for (auto e : edges) { - fprintf(fh, "%f %f %f\n", e.px, e.py, e.pz); - fprintf(fh, "%f %f %f\n", e.qx, e.qy, e.qz); - } - fprintf(fh, "EDGES\n"); - fprintf(fh, "%zu\n", edges.size()); - for (size_t i = 0; i < 2*edges.size(); i++) { - fprintf(fh, "%zu ", i); - } - fprintf(fh, "\n"); - fclose(fh); -} diff --git a/Cassiopee/XCore/XCore/intersectMesh/io.h b/Cassiopee/XCore/XCore/intersectMesh/io.h index df4092807..511a434cd 100644 --- a/Cassiopee/XCore/XCore/intersectMesh/io.h +++ b/Cassiopee/XCore/XCore/intersectMesh/io.h @@ -21,34 +21,14 @@ #include #include -#include "xcore.h" -#include "vertex.h" -#include "triangleIntersection.h" +#include "triangle.h" #include "common/common.h" #include "point.h" -#include "hedge.h" - -struct IO_Edge { - E_Float px, py, pz; - E_Float qx, qy, qz; - - IO_Edge(E_Float PX, E_Float PY, E_Float PZ, E_Float QX, E_Float QY, E_Float QZ) - : px(PX), py(PY), pz(PZ), qx(QX), qy(QY), qz(QZ) - {} -}; - -void face_write(const char *fname, Face *face); void point_write(const char *fname, E_Float x, E_Float y, E_Float z); -void point_write(const char *fname, Vertex *v); - -void hedge_write(const char *fname, const Hedge *h); - void point_write(const char *fname, const std::vector &P); -void point_write(const char *fname, const std::vector &I); - void point_write(const char *fname, E_Float *Xs, E_Float *Ys, E_Float *Zs, const std::vector &proj_points); @@ -57,5 +37,3 @@ void edge_write(const char *fname, E_Float *X, E_Float *Y, E_Float *Z, void edge_write(const char *fname, E_Float px, E_Float py, E_Float pz, E_Float qx, E_Float qy, E_Float qz); - -void edges_write(const char *fname, const std::vector &edges); diff --git a/Cassiopee/XCore/XCore/intersectMesh/mesh.cpp b/Cassiopee/XCore/XCore/intersectMesh/mesh.cpp index b12ae569d..1c73cf88c 100644 --- a/Cassiopee/XCore/XCore/intersectMesh/mesh.cpp +++ b/Cassiopee/XCore/XCore/intersectMesh/mesh.cpp @@ -32,31 +32,61 @@ #include "primitives.h" #include "ray.h" #include "io.h" +#include "common/Karray.h" -void IMesh::triangulate_face_set() +void IMesh::triangulate_face_set(bool propagate) { - E_Int NF = nf; - - E_Int face_incr = (E_Int)faces_to_tri.size(); + make_skin(); + + if (propagate) { + E_Float xmin, ymin, zmin, xmax, ymax, zmax; + xmin = ymin = zmin = EFLOATMAX; + xmax = ymax = zmax = EFLOATMIN; + + for (auto fid : faces_to_tri) { + const auto &pn = F[fid]; + for (auto p : pn) { + if (X[p] < xmin) xmin = X[p]; + if (Y[p] < ymin) ymin = Y[p]; + if (Z[p] < zmin) zmin = Z[p]; + if (X[p] > xmax) xmax = X[p]; + if (Y[p] > ymax) ymax = Y[p]; + if (Z[p] > zmax) zmax = Z[p]; + } + } - F.resize(NF + face_incr); - std::vector owner(NF, -1), neigh(NF, -1); + faces_to_tri.clear(); - for (E_Int i = 0; i < nc; i++) { - const auto &pf = C[i]; - for (E_Int fid : pf) { - if (owner[fid] == -1) owner[fid] = i; - else neigh[fid] = i; + for (auto fid : skin) { + const auto &pn = F[fid]; + for (auto p : pn) { + if (X[p] >= xmin && X[p] <= xmax && + Y[p] >= ymin && Y[p] <= ymax && + Z[p] >= zmin && Z[p] <= zmax) { + faces_to_tri.insert(fid); + break; + } + } } } - assert(skin.size() > 0); + E_Int NF = nf; + + E_Int face_incr = (E_Int)faces_to_tri.size(); + + F.resize(NF + face_incr); + + patch.clear(); for (E_Int fid : faces_to_tri) { auto &pn = F[fid]; + patch.insert(fid); + + if (pn.size() == 3) continue; + assert(pn.size() == 4); std::vector tri0(3), tri1(3); @@ -83,138 +113,95 @@ void IMesh::triangulate_face_set() skin.push_back(NF); + // Update patch + + patch.insert(NF); + NF++; } - assert(NF == nf + face_incr); - nf = NF; - - faces_to_tri.clear(); - - for (E_Int cid = 0; cid < nc; cid++) { - assert(C[cid].size() == 6 || C[cid].size() == 7); - } + F.resize(NF); } -struct DEdge { - E_Int p, q; - - DEdge(E_Int P, E_Int Q) : p(P), q(Q) {} - - bool operator<(const DEdge &f) const +struct o_edge_cmp { + bool operator()(const o_edge &e, const o_edge &f) const { - E_Int ep = std::min(p, q); - E_Int eq = std::max(p, q); - E_Int fp = std::min(f.p, f.q); - E_Int fq = std::max(f.p, f.q); - return (ep < fp) || (ep == fp && eq < fq); + E_Int e_p = std::min(e.p, e.q); + E_Int e_q = std::max(e.p, e.q); + E_Int f_p = std::min(f.p, f.q); + E_Int f_q = std::max(f.p, f.q); + return (e_p < f_p) || + (e_p == f_p && e_q < f_q); } }; void IMesh::make_edges() { - std::map edges; + F2E.clear(); + F2E.resize(F.size()); + std::map edges; + E.clear(); + assert(E.empty()); ne = 0; - F2E.resize(nf); - - for (E_Int i = 0; i < nf; i++) { - const auto &pn = F[i]; - - F2E[i].resize(pn.size()); - + for (E_Int fid = 0; fid < nf; fid++) { + auto &pn = F[fid]; for (size_t j = 0; j < pn.size(); j++) { E_Int p = pn[j]; E_Int q = pn[(j+1)%pn.size()]; - DEdge e(p, q); - auto it = edges.find(e); + o_edge EDGE(p, q); + auto it = edges.find(EDGE); if (it == edges.end()) { - F2E[i][j] = ne; - edges[e] = ne; + F2E[fid].push_back(ne); + edges[EDGE] = ne; + E.push_back(EDGE); ne++; } else { - F2E[i][j] = it->second; + F2E[fid].push_back(it->second); } } } - E.resize(ne); - - for (const auto &edata : edges) { - E[edata.second][0] = edata.first.p; - E[edata.second][1] = edata.first.q; - } -} - -void IMesh::init_adaptation_data() -{ - flevel.resize(nf, 0); - - for (E_Int i = 0; i < nf; i++) factive.insert(i); -} - -bool IMesh::faces_are_dups(E_Int mface, E_Int sface, const IMesh &S) -{ - const auto &pnm = F[mface]; - const auto &pns = S.F[sface]; + assert((size_t)ne == E.size()); - assert(face_is_quad(mface) || face_is_tri(mface)); - assert(S.face_is_quad(sface) || S.face_is_tri(sface)); + E2F.clear(); + E2F.resize(ne); - if (pnm.size() != pns.size()) return false; + for (E_Int fid = 0; fid < nf; fid++) { + const auto &pe = F2E[fid]; - E_Int mfound[4] = { 0, 0, 0, 0 }; - - for (size_t i = 0; i < pnm.size(); i++) { - E_Int pm = pnm[i]; - for (size_t j = 0; j < pns.size(); j++) { - E_Int ps = pns[j]; - if (cmp_points(X[pm], Y[pm], Z[pm], S.X[ps], S.Y[ps], S.Z[ps]) == 0) { - assert(mfound[i] == 0); - mfound[i] = 1; - break; - } + for (E_Int eid : pe) { + E2F[eid].push_back(fid); } - - if (mfound[i] == 0) return false; } - - return true; } + + IMesh::IMesh() {} -IMesh::IMesh(K_FLD::FldArrayI &cn, E_Float *x, E_Float *y, E_Float *z, E_Int npts) +IMesh::IMesh(const Karray &karray) { - NX = 100; - NY = 100; - NZ = 100; - NXY = NX * NY; - NXYZ = NXY * NZ; - - np = npts; - ne = 0; - nf = cn.getNFaces(); - nc = cn.getNElts(); + np = karray.npoints(); + nf = karray.nfaces(); + nc = karray.ncells(); X.resize(np); Y.resize(np); Z.resize(np); for (E_Int i = 0; i < np; i++) { - X[i] = x[i]; - Y[i] = y[i]; - Z[i] = z[i]; + X[i] = karray.x[i]; + Y[i] = karray.y[i]; + Z[i] = karray.z[i]; } F.reserve(nf); - E_Int *indPG = cn.getIndPG(); - E_Int *ngon = cn.getNGon(); - for (E_Int i = 0; i < nf; i++) { + for (E_Int fid = 0; fid < nf; fid++) { E_Int np = -1; - E_Int *pn = cn.getFace(i, np, ngon, indPG); + E_Int *pn = karray.get_face(fid, np); std::vector points(np); for (E_Int j = 0; j < np; j++) points[j] = pn[j] - 1; @@ -222,26 +209,14 @@ IMesh::IMesh(K_FLD::FldArrayI &cn, E_Float *x, E_Float *y, E_Float *z, E_Int npt } C.reserve(nc); - E_Int *indPH = cn.getIndPH(); - E_Int *nface = cn.getNFace(); - for (E_Int i = 0; i < nc; i++) { + for (E_Int cid = 0; cid < nc; cid++) { E_Int nf = -1; - E_Int *pf = cn.getElt(i, nf, nface, indPH); + E_Int *pf = karray.get_cell(cid, nf); std::vector faces(nf); for (E_Int j = 0; j < nf; j++) faces[j] = pf[j] - 1; C.push_back(faces); } - - //make_skin(); - - //make_bbox(); - - //hash_skin(); - - //make_point_faces(); - - //init_adaptation_data(); } void IMesh::make_point_faces() @@ -257,6 +232,13 @@ void IMesh::make_point_faces() void IMesh::make_bbox() { + // Grid + NX = 100; + NY = 100; + NZ = 100; + NXY = NX * NY; + NXYZ = NXY * NZ; + xmin = ymin = zmin = EFLOATMAX; xmax = ymax = zmax = EFLOATMIN; @@ -269,6 +251,9 @@ void IMesh::make_bbox() if (Z[i] > zmax) zmax = Z[i]; } + xmin = xmin - (xmax - xmin) * 0.01; + ymin = ymin - (ymax - ymin) * 0.01; + zmin = zmin - (zmax - zmin) * 0.01; xmax = xmax + (xmax - xmin) * 0.01; ymax = ymax + (ymax - ymin) * 0.01; zmax = zmax + (zmax - zmin) * 0.01; @@ -282,17 +267,25 @@ void IMesh::make_skin() { skin.clear(); + owner.clear(); + neigh.clear(); + + owner.resize(nf, -1); + neigh.resize(nf, -1); + std::vector count(nf, 0); - - for (const auto &cn : C) { - for (E_Int face : cn) - count[face]++; + + for (E_Int cid = 0; cid < nc; cid++) { + const auto &pf = C[cid]; + for (auto fid : pf) { + if (owner[fid] == -1) owner[fid] = cid; + else neigh[fid] = cid; + } } - for (E_Int i = 0; i < nf; i++) { - E_Int c = count[i]; - assert(c == 1 || c == 2); - if (c == 1) skin.push_back(i); + for (E_Int fid = 0; fid < nf; fid++) { + if (neigh[fid] == -1) + skin.push_back(fid); } } @@ -301,8 +294,8 @@ E_Int IMesh::RayFaceIntersect(E_Float px, E_Float py, E_Float pz, E_Float dx, { const auto &pn = F[fid]; - // TODO(Imad): hexa mesh for now - assert(pn.size() == 4); + // TODO(Imad): quads or tris for now + assert(pn.size() == 4 || pn.size() == 3); E_Int a = pn[0], b = pn[1], c = pn[2]; @@ -325,6 +318,8 @@ E_Int IMesh::RayFaceIntersect(E_Float px, E_Float py, E_Float pz, E_Float dx, return 1; } + if (pn.size() == 3) return 0; + E_Int d = pn[3]; TI.tri = 1; @@ -729,89 +724,6 @@ void IMesh::write_face(const char *fname, E_Int fid) const fclose(fh); } -void IMesh::write_faces(const char *fname, const std::vector &faces) const -{ - FILE *fh = fopen(fname, "w"); - assert(fh); - - std::map new_pids; - std::map old_pids; - - E_Int npts = 0; - - for (E_Int face : faces) { - const auto &pn = F[face]; - - for (E_Int p : pn) { - if (new_pids.find(p) == new_pids.end()) { - new_pids[p] = npts; - old_pids[npts] = p; - npts++; - } - } - } - - fprintf(fh, "POINTS\n"); - fprintf(fh, "%d\n", npts); - for (E_Int i = 0; i < npts; i++) { - E_Int opid = old_pids[i]; - fprintf(fh, "%f %f %f\n", X[opid], Y[opid], Z[opid]); - } - - fclose(fh); -} - -void IMesh::write_ngon(const char *fname) -{ - FILE *fh = fopen(fname, "w"); - assert(fh); - - fprintf(fh, "POINTS\n"); - fprintf(fh, SF_D_ "\n", np); - for (E_Int i = 0; i < np; i++) { - fprintf(fh, "%f %f %f\n", X[i], Y[i], Z[i]); - } - - fprintf(fh, "INDPG\n"); - fprintf(fh, SF_D_ "\n", nf+1); - E_Int sizeNGon = 0; - fprintf(fh, SF_D_ " ", sizeNGon); - for (E_Int i = 0; i < nf; i++) { - sizeNGon += F[i].size(); - fprintf(fh, SF_D_ " ", sizeNGon); - } - fprintf(fh, "\n"); - - fprintf(fh, "NGON\n"); - fprintf(fh, SF_D_ "\n", sizeNGon); - for (E_Int i = 0; i < nf; i++) { - for (E_Int p : F[i]) - fprintf(fh, SF_D_ " ", p); - } - fprintf(fh, "\n"); - - fprintf(fh, "INDPH\n"); - fprintf(fh, SF_D_ "\n", nc+1); - E_Int sizeNFace = 0; - fprintf(fh, SF_D_ " ", sizeNFace); - for (E_Int i = 0; i < nc; i++) { - sizeNFace += C[i].size(); - fprintf(fh, SF_D_ " ", sizeNFace); - } - fprintf(fh, "\n"); - - fprintf(fh, "NFace\n"); - fprintf(fh, SF_D_ "\n", sizeNFace); - for (E_Int i = 0; i < nc; i++) { - for (E_Int p : C[i]) - fprintf(fh, SF_D_ " ", p); - } - fprintf(fh, "\n"); - - fclose(fh); -} - - bool IMesh::face_contains_sface(E_Int face, E_Int sface, const IMesh &S) const { // face containes mface iff it contains all its points @@ -891,4 +803,4 @@ void IMesh::get_fleaves(E_Int face, std::vector &fleaves) } for (E_Int child : fchildren.at(face)) get_fleaves(child, fleaves); -} \ No newline at end of file +} diff --git a/Cassiopee/XCore/XCore/intersectMesh/mesh.h b/Cassiopee/XCore/XCore/intersectMesh/mesh.h index 30a0bef25..55d172610 100644 --- a/Cassiopee/XCore/XCore/intersectMesh/mesh.h +++ b/Cassiopee/XCore/XCore/intersectMesh/mesh.h @@ -23,18 +23,20 @@ #include #include #include +#include #include "point.h" #include "xcore.h" #include "common/common.h" -#include "triangleIntersection.h" +#include "triangle.h" #include "AABB.h" +#include "smesh.h" #define OUT 0 #define IN 1 struct Ray; -struct Smesh; +struct Karray; struct UEdge { E_Int p, q; @@ -51,20 +53,38 @@ struct UEdge { } }; +struct Sgraph { + std::vector xadj; + std::vector fpts; + std::vector fadj; +}; + +struct Py_BC { + E_Int size; + E_Int *ptr; +}; + struct IMesh { E_Int np, ne, nf, nc; std::vector X, Y, Z; + std::vector> F; + std::vector> P2F; + std::vector> P2E; - std::vector> E; - - std::vector> F; + std::vector E; + std::vector> E2F; std::vector> F2E; std::vector> C; std::vector skin; + std::vector owner; + std::vector neigh; + + E_Float NEAR_VERTEX_TOL = 1e-3; + E_Float NEAR_EDGE_TOL = 1e-3; E_Float xmin, ymin, zmin; E_Float xmax, ymax, zmax; @@ -74,13 +94,16 @@ struct IMesh { std::vector> bin_faces; - std::map> fmap; - std::set patch; - - std::vector entries; - - std::vector ctag; + std::vector ftag; + std::vector ptag; + std::vector ctag; + + void set_tolerances(E_Float near_vertex_tol, E_Float near_edge_tol) + { + NEAR_VERTEX_TOL = near_vertex_tol; + NEAR_EDGE_TOL = near_edge_tol; + } inline E_Int get_voxel(E_Int I, E_Int J, E_Int K) const { @@ -95,13 +118,22 @@ struct IMesh { E_Int project_point(E_Float px, E_Float py, E_Float pz, E_Float dx, E_Float dy, E_Float dz, TriangleIntersection &TI, E_Int II); - void triangulate_face_set(); + void triangulate_face_set(bool propagate = true); + + std::vector triangulate_skin(const std::vector &bcs_in, + const std::unordered_map &fid_to_bc); + + void triangulate_skin(); + + size_t refine_slave(const IMesh &master); void hash_patch(); IMesh(); - IMesh(K_FLD::FldArrayI &cn, E_Float *X, E_Float *Y, E_Float *Z, E_Int npts); + IMesh(const Karray &karray); + + //IMesh(K_FLD::FldArrayI &cn, E_Float *X, E_Float *Y, E_Float *Z, E_Int npts); void make_patch(E_Int *faces, E_Int nfaces); @@ -123,7 +155,9 @@ struct IMesh { void write_ngon(const char *fname); - void write_faces(const char *fname, const std::vector &faces) const; + void write_ngon(const char *fname, const std::vector &faces) const; + + void write_ngon(const char *fname, const std::set &fset) const; void write_face(const char *fname, E_Int fid) const; @@ -150,9 +184,6 @@ struct IMesh { size_t refine(const IMesh &S); - //std::vector locate(E_Int p, E_Float x, E_Float y, E_Float z, - // const std::set &patch) const; - inline bool face_is_active(E_Int face) const { return factive.find(face) != factive.end(); } @@ -178,11 +209,11 @@ struct IMesh { void get_fleaves(E_Int face, std::vector &fleaves); - PyObject *export_karray(E_Int remove_periodic = 0); + PyObject *export_karray(E_Int remove_periodic = 0) const; - PyObject *export_karray_orig(); + PyObject *export_karray_orig() const; - PyObject *export_karray_periodic(); + PyObject *export_karray_periodic() const; /* TOPO */ diff --git a/Cassiopee/XCore/XCore/intersectMesh/meshExport.cpp b/Cassiopee/XCore/XCore/intersectMesh/meshExport.cpp index 050f83cdf..50a7f310d 100644 --- a/Cassiopee/XCore/XCore/intersectMesh/meshExport.cpp +++ b/Cassiopee/XCore/XCore/intersectMesh/meshExport.cpp @@ -1,13 +1,13 @@ #include "mesh.h" -PyObject *IMesh::export_karray(E_Int remove_periodic) +PyObject *IMesh::export_karray(E_Int remove_periodic) const { if (remove_periodic) return export_karray_periodic(); return export_karray_orig(); } -PyObject *IMesh::export_karray_periodic() +PyObject *IMesh::export_karray_periodic() const { // Keep the cells whose tag is 1 @@ -47,7 +47,6 @@ PyObject *IMesh::export_karray_periodic() for (const auto &fdat : new_fids) { E_Int ofid = fdat.first; - //E_Int nfid = fdat.second; const auto &pn = F[ofid]; @@ -142,7 +141,7 @@ PyObject *IMesh::export_karray_periodic() return array; } -PyObject *IMesh::export_karray_orig() +PyObject *IMesh::export_karray_orig() const { E_Int sizeNGon = 0, sizeNFace = 0; @@ -194,7 +193,6 @@ PyObject *IMesh::export_karray_orig() delete f; delete cn; - return array; } @@ -288,4 +286,4 @@ IMesh IMesh::extract_conformized() } return new_M; -} \ No newline at end of file +} diff --git a/Cassiopee/XCore/XCore/intersectMesh/meshRefine.cpp b/Cassiopee/XCore/XCore/intersectMesh/meshRefine.cpp index e398b0708..6c60de429 100644 --- a/Cassiopee/XCore/XCore/intersectMesh/meshRefine.cpp +++ b/Cassiopee/XCore/XCore/intersectMesh/meshRefine.cpp @@ -23,6 +23,7 @@ #include "triangle.h" #include "io.h" +#include "primitives.h" E_Int meshes_mutual_refinement(IMesh &M, IMesh &S) { @@ -32,29 +33,273 @@ E_Int meshes_mutual_refinement(IMesh &M, IMesh &S) S.init_adaptation_data(); M.init_adaptation_data(); + E_Int S_np_before = S.np; + do { iter++; + // Refine M wrt S + S.make_point_faces(); M.make_bbox(); - M.hash_skin(); + M.hash_skin(); // TODO(Imad): hash_patch! refM = M.refine(S); printf("Refined mf: %zu\n", refM); - if (refM > 0) { + // Refine S wrt M + /*if (iter == 1 || (iter > 1 && refM > 0)) { M.make_point_faces(); S.make_bbox(); - S.hash_skin(); + S.hash_skin(); // TODO(Imad): hash_patch! - refS = S.refine(M); + refS = S.refine_slave(M); printf("Refined sf: %zu\n", refS); - } + }*/ + } while (refS > 0); + S.make_point_faces(); + + // Project all the new points from S onto M faces + // TODO(Imad): shouldn't this step be done after conformizing? + + for (E_Int spid = S_np_before; spid < S.np; spid++) { + const auto &pf = S.P2F[spid]; + std::vector stids; + for (auto stid : pf) { + if (S.F[stid].size() == 3) stids.push_back(stid); + } + + // Compute the normal at spid := sum of the normals of stids + E_Float N[3] = {0}; + for (auto stid : stids) { + const auto &pn = S.F[stid]; + E_Int a = pn[0], b = pn[1], c = pn[2]; + E_Float v0[3] = {S.X[b]-S.X[a], S.Y[b]-S.Y[a], S.Z[b]-S.Z[a]}; + E_Float v1[3] = {S.X[c]-S.X[a], S.Y[c]-S.Y[a], S.Z[c]-S.Z[a]}; + E_Float fN[3]; + K_MATH::cross(v0, v1, fN); + N[0] += fN[0]; + N[1] += fN[1]; + N[2] += fN[2]; + } + + E_Float NORM = K_MATH::norm(N, 3); + assert(Sign(NORM) != 0); + N[0] /= NORM; + N[1] /= NORM; + N[2] /= NORM; + + TriangleIntersection TI; + E_Int hit = 0; + + if (M.is_point_inside(S.X[spid], S.Y[spid], S.Z[spid])) { + hit = M.project_point(S.X[spid], S.Y[spid], S.Z[spid], + -N[0], -N[1], -N[2], TI, spid - S_np_before); + } else { + hit = M.project_point(S.X[spid], S.Y[spid], S.Z[spid], + N[0], N[1], N[2], TI, spid - S_np_before); + } + + assert(hit); + assert(M.patch.find(TI.face) != M.patch.end()); + + printf("Spid: %f %f %f -> Proj: %f %f %f (t = %f)\n", + S.X[spid], S.Y[spid], S.Z[spid], TI.x, TI.y, TI.z, TI.t); + + // Replace the point by its projection + S.X[spid] = TI.x; + S.Y[spid] = TI.y; + S.Z[spid] = TI.z; + } + return 0; } +struct Fidn { + E_Int fid; + E_Float N[3]; +}; + +size_t IMesh::refine_slave(const IMesh &master) +{ + const auto &mpatch = master.patch; + std::set mpids; + for (const auto mfid : mpatch) { + assert(master.face_is_active(mfid)); + const auto &pn = master.F[mfid]; + for (const auto p : pn) mpids.insert(p); + } + + std::vector spatch; + spatch.reserve(patch.size()); + for (const auto fid : patch) { + const auto &pn = F[fid]; + assert(face_is_tri(fid)); + assert(face_is_active(fid)); + E_Int a = pn[0], b = pn[1], c = pn[2]; + E_Float v0[3] = {X[b]-X[a], Y[b]-Y[a], Z[b]-Z[a]}; + E_Float v1[3] = {X[c]-X[a], Y[c]-Y[a], Z[c]-Z[a]}; + Fidn fidn; + fidn.fid = fid; + K_MATH::cross(v0, v1, fidn.N); + E_Float NORM = K_MATH::norm(fidn.N, 3); + assert(Sign(NORM) != 0); + fidn.N[0] /= NORM; + fidn.N[1] /= NORM; + fidn.N[2] /= NORM; + spatch.push_back(fidn); + } + + const auto &mX = master.X; + const auto &mY = master.Y; + const auto &mZ = master.Z; + + // Master points to Slave triangles + std::map> mpids_to_stids; + + // TODO(Imad): how to accelerate this? + for (const auto &fidn : spatch) { + const E_Int fid = fidn.fid; + const E_Float *N = fidn.N; + const auto &pn = F[fid]; + E_Int a = pn[0]; + E_Int b = pn[1]; + E_Int c = pn[2]; + + for (auto mpid : mpids) { + // Does the projection of mpid along N live in the triangle fid? + + E_Float V[3] = {mX[mpid]-X[a], mY[mpid]-Y[a], mZ[mpid]-Z[a]}; + E_Float dp = K_MATH::dot(V, N, 3); + + E_Float Proj[3]; + Proj[0] = mX[mpid] - dp * N[0]; + Proj[1] = mY[mpid] - dp * N[1]; + Proj[2] = mZ[mpid] - dp * N[2]; + + bool inside = Triangle::is_point_inside(Proj[0], Proj[1], Proj[2], + X[a], Y[a], Z[a], + X[b], Y[b], Z[b], + X[c], Y[c], Z[c]); + + if (inside) { + mpids_to_stids[mpid].push_back(fid); + } + } + } + + // Invert the map: S triangles to all the M points (projection) within them + std::map> stids_to_mpids; + + for (const auto &m2s : mpids_to_stids) { + E_Int mpid = m2s.first; + const auto &stids = m2s.second; + for (const auto tid : stids) { + stids_to_mpids[tid].push_back(mpid); + } + } + + // Keep the S triangles containing three of more M points + std::map> filtered_stids_map; + + for (const auto &s2m : stids_to_mpids) { + if (s2m.second.size() >= 3) { + filtered_stids_map.insert({s2m.first, s2m.second}); + } + } + + // Sensor + std::map> ref_stids_to_mtids; + + for (const auto &s2m : filtered_stids_map) { + auto stid = s2m.first; + const auto &mpids = s2m.second; + + // For every sface, how many points are contained within mface? + std::map mcontained; + + for (auto mpid : mpids) { + // M tris sharing mpid + const auto &mtris = master.P2F[mpid]; + + for (auto mtri : mtris) { + assert(master.face_is_active(mtri)); + + // M tri should belong to patch + if (master.patch.find(mtri) == master.patch.end()) continue; + + // S tri contains another point of M tri + mcontained[mtri]++; + } + } + + for (const auto &mfdat : mcontained) { + E_Int mtid = mfdat.first; + + // The number of points of mtid contained within stid... + size_t npts = mfdat.second; + + // ... should be at most equal to the stride of mtid! + assert(npts <= master.F[mtid].size()); + + // Discard mtid if it is not completely enclosed by stid + if (npts < master.F[mtid].size()) continue; + + // Discard sface if it is a duplicate of mface + if (faces_are_dups(stid, mtid, master)) continue; + + // Add mtid to the set of faces enclosed by stid + ref_stids_to_mtids[stid].push_back(mtid); + } + } + + //printf("Faces to refine: %zu\n", ref_mfaces_to_sfaces.size()); + + E_Int iter = 0; + size_t ret = 0; + + while (!ref_stids_to_mtids.empty()) { + iter++; + + auto ref_data = smooth_ref_data(ref_stids_to_mtids); + + auto ref_faces = prepare_for_refinement(ref_data); + + ret += ref_faces.size(); + + refine_faces(ref_faces); + + std::map> new_sensor; + + for (const auto &fdata : ref_stids_to_mtids) { + E_Int parent = fdata.first; + const auto &mtids = fdata.second; + + const auto &children = fchildren[parent]; + + for (E_Int child : children) { + for (E_Int mtid : mtids) { + if (face_contains_sface(child, mtid, master)) { + // TODO(Imad): I think we can safely discard this check + assert(!faces_are_dups(child, mtid, master)); + new_sensor[child].push_back(mtid); + } + } + } + + // Update mpatch + patch.erase(parent); + for (E_Int child : children) patch.insert(child); + } + + ref_stids_to_mtids = new_sensor; + } + + return ret; +} + size_t IMesh::refine(const IMesh &S) { // Hash mpatch @@ -81,11 +326,9 @@ size_t IMesh::refine(const IMesh &S) E_Int voxel_z = floor((S.Z[spt] - zmin) / HZ); E_Int spt_bin = voxel_x + NX * voxel_y + NXY * voxel_z; - auto it = fmap.find(spt_bin); - - if (it == fmap.end()) continue; + const auto &pf = bin_faces[spt_bin]; - const auto &pf = it->second; + assert(pf.size() > 0); for (E_Int fid : pf) { diff --git a/Cassiopee/XCore/XCore/intersectMesh/mesh_io.cpp b/Cassiopee/XCore/XCore/intersectMesh/mesh_io.cpp new file mode 100644 index 000000000..22c686f62 --- /dev/null +++ b/Cassiopee/XCore/XCore/intersectMesh/mesh_io.cpp @@ -0,0 +1,155 @@ +#include "mesh.h" + +void IMesh::write_ngon(const char *fname, const std::set &fset) const +{ + std::vector flist; + flist.reserve(fset.size()); + for (E_Int fid : fset) flist.push_back(fid); + write_ngon(fname, flist); +} + +void IMesh::write_ngon(const char *fname, const std::vector &faces) const +{ + std::vector INDPH(faces.size() + 1); + INDPH[0] = 0; + + std::map new_pids; + std::map new_eids; + E_Int idx = 0; + E_Int NP = 0; + E_Int NE = 0; + E_Int NF = (E_Int)faces.size(); + + for (E_Int fid : faces) { + const auto &pn = F[fid]; + const auto &pe = F2E[fid]; + + INDPH[idx+1] = INDPH[idx] + (E_Int)pn.size(); + idx++; + + for (E_Int pid : pn) { + if (new_pids.find(pid) == new_pids.end()) { + new_pids[pid] = NP; + NP++; + } + } + + for (E_Int eid : pe) { + if (new_eids.find(eid) == new_eids.end()) { + new_eids[eid] = NE; + NE++; + } + } + } + + FILE *fh = fopen(fname, "w"); + assert(fh); + fprintf(fh, "POINTS\n"); + fprintf(fh, "%lu\n", new_pids.size()); + + std::vector nX(NP), nY(NP), nZ(NP); + for (const auto &pids : new_pids) { + E_Int opid = pids.first; + E_Int npid = pids.second; + nX[npid] = X[opid]; + nY[npid] = Y[opid]; + nZ[npid] = Z[opid]; + } + + for (E_Int pid = 0; pid < NP; pid++) { + fprintf(fh, "%f %f %f\n", nX[pid], nY[pid], nZ[pid]); + } + + fprintf(fh, "INDPG\n"); + fprintf(fh, "%lu\n", new_eids.size()+1); + E_Int sizeNGon = -2; + for (size_t i = 0; i < new_eids.size() + 1; i++) { + sizeNGon += 2; + fprintf(fh, "%d ", sizeNGon); + } + fprintf(fh, "\n"); + assert(sizeNGon == 2*NE); + + std::vector nE(new_eids.size(), {-1, -1}); + for (const auto &eids : new_eids) { + E_Int oeid = eids.first; + E_Int neid = eids.second; + nE[neid].p = new_pids[E[oeid].p]; + nE[neid].q = new_pids[E[oeid].q]; + } + + fprintf(fh, "NGON\n"); + fprintf(fh, "%d\n", 2*NE); + for (const auto &e : nE) { + fprintf(fh, "%d %d ", e.p, e.q); + } + fprintf(fh, "\n"); + + fprintf(fh, "INDPH\n"); + fprintf(fh, "%d\n", NF+1); + for (E_Int i = 0; i < NF+1; i++) + fprintf(fh, "%d ", INDPH[i]); + fprintf(fh, "\n"); + + fprintf(fh, "NFACE\n"); + fprintf(fh, "%d\n", INDPH[NF]); + for (size_t i = 0; i < faces.size(); i++) { + const auto &pe = F2E[faces[i]]; + for (E_Int eid : pe) { + fprintf(fh, "%d ", new_eids[eid]); + } + } + fprintf(fh, "\n"); + + fclose(fh); +} + +void IMesh::write_ngon(const char *fname) +{ + FILE *fh = fopen(fname, "w"); + assert(fh); + + fprintf(fh, "POINTS\n"); + fprintf(fh, SF_D_ "\n", np); + for (E_Int i = 0; i < np; i++) { + fprintf(fh, "%f %f %f\n", X[i], Y[i], Z[i]); + } + + fprintf(fh, "INDPG\n"); + fprintf(fh, SF_D_ "\n", nf+1); + E_Int sizeNGon = 0; + fprintf(fh, SF_D_ " ", sizeNGon); + for (E_Int i = 0; i < nf; i++) { + sizeNGon += F[i].size(); + fprintf(fh, SF_D_ " ", sizeNGon); + } + fprintf(fh, "\n"); + + fprintf(fh, "NGON\n"); + fprintf(fh, SF_D_ "\n", sizeNGon); + for (E_Int i = 0; i < nf; i++) { + for (E_Int p : F[i]) + fprintf(fh, SF_D_ " ", p); + } + fprintf(fh, "\n"); + + fprintf(fh, "INDPH\n"); + fprintf(fh, SF_D_ "\n", nc+1); + E_Int sizeNFace = 0; + fprintf(fh, SF_D_ " ", sizeNFace); + for (E_Int i = 0; i < nc; i++) { + sizeNFace += C[i].size(); + fprintf(fh, SF_D_ " ", sizeNFace); + } + fprintf(fh, "\n"); + + fprintf(fh, "NFace\n"); + fprintf(fh, SF_D_ "\n", sizeNFace); + for (E_Int i = 0; i < nc; i++) { + for (E_Int p : C[i]) + fprintf(fh, SF_D_ " ", p); + } + fprintf(fh, "\n"); + + fclose(fh); +} diff --git a/Cassiopee/XCore/XCore/intersectMesh/point.h b/Cassiopee/XCore/XCore/intersectMesh/point.h index 3cde67d91..a734d73c9 100644 --- a/Cassiopee/XCore/XCore/intersectMesh/point.h +++ b/Cassiopee/XCore/XCore/intersectMesh/point.h @@ -18,9 +18,30 @@ */ #pragma once -#include "vec3.h" +#include "xcore.h" -typedef Vec3 Point; +struct Point { + E_Float x, y, z; +}; + +struct PointLoc { + E_Int fid = -1; + E_Int v_idx = -1; + E_Int e_idx = -1; + E_Float bcrd[3] = {EFLOATMAX, EFLOATMAX, EFLOATMAX}; + E_Float t = EFLOATMAX; + E_Float x = EFLOATMAX; + E_Float y = EFLOATMAX; + E_Float z = EFLOATMAX; + E_Int sub = -1; +}; + +struct PointData { + E_Int pid; // my id in spatch + E_Int fid; // id of the mface I am on + E_Int sid; // if of the smesh I belong to + E_Float x, y, z; +}; struct pointFace { E_Int F; diff --git a/Cassiopee/XCore/XCore/intersectMesh/prepareMeshesForIntersection.cpp b/Cassiopee/XCore/XCore/intersectMesh/prepareMeshesForIntersection.cpp index 7f5618355..aaeed0102 100644 --- a/Cassiopee/XCore/XCore/intersectMesh/prepareMeshesForIntersection.cpp +++ b/Cassiopee/XCore/XCore/intersectMesh/prepareMeshesForIntersection.cpp @@ -22,15 +22,12 @@ #include "mesh.h" #include "smesh.h" #include "dcel.h" -#include "vertex.h" -#include "face.h" -#include "hedge.h" #include "io.h" -#include "cycle.h" #include "triangle.h" PyObject *K_XCORE::prepareMeshesForIntersection(PyObject *self, PyObject *args) { + /* PyObject *MASTER, *SLAVE, *TAG; if (!PYPARSETUPLE_(args, OOO_, &MASTER, &SLAVE, &TAG)) { @@ -45,13 +42,6 @@ PyObject *K_XCORE::prepareMeshesForIntersection(PyObject *self, PyObject *args) IMesh &M = *(IMesh *)PyCapsule_GetPointer(MASTER, "IntersectMesh"); - M.make_skin(); - assert(M.patch.empty()); - for (E_Int fid : M.skin) { - assert(fid < M.nf); - M.patch.insert(fid); - } - Karray sarray; E_Int ret; @@ -79,13 +69,15 @@ PyObject *K_XCORE::prepareMeshesForIntersection(PyObject *self, PyObject *args) // TODO(Imad): quasi-planar surfaces // Init master/slave meshes - IMesh S(*sarray.cn, sarray.x, sarray.y, sarray.z, sarray.npts); + IMesh S(*sarray.cn, sarray.X(), sarray.Y(), sarray.Z(), sarray.npts); S.make_skin(); + S.patch.clear(); + + assert(tag_size == S.np); for (E_Int fid : S.skin) { const auto &pn = S.F[fid]; size_t stride = pn.size(); - assert(stride == 3 || stride == 4); E_Int keep = 1; @@ -100,8 +92,20 @@ PyObject *K_XCORE::prepareMeshesForIntersection(PyObject *self, PyObject *args) if (keep) S.patch.insert(fid); } + printf("S.patch size before triangulation: %lu\n", S.patch.size()); + + S.faces_to_tri.clear(); + for (auto fid : S.patch) S.faces_to_tri.insert(fid); + S.triangulate_face_set(false); + + printf("S.patch size after triangulation: %lu\n", S.patch.size()); + + //M.project_patch(S); + puts("Adapting intersection zones..."); + printf("M.patch size before refinement: %lu\n", M.patch.size()); + ret = meshes_mutual_refinement(M, S); if (ret != 0) { Karray_free_ngon(sarray); @@ -109,39 +113,54 @@ PyObject *K_XCORE::prepareMeshesForIntersection(PyObject *self, PyObject *args) return NULL; } + printf("M.patch size after refinement: %lu\n", M.patch.size()); + printf("S.patch size after refinement: %lu\n", S.patch.size()); + puts("Extracting conformized meshes..."); S = S.extract_conformized(); M = M.extract_conformized(); - + // Export puts("Exporting to CGNS format..."); + PyObject *Mout = M.export_karray(); PyObject *Sout = S.export_karray(); - PyObject *Out = PyList_New(0); - - PyList_Append(Out, Sout); - - Py_DECREF(Sout); - Karray_free_ngon(sarray); // Extract master and slave patches - puts("Saving slave intersection patch..."); + puts("Saving intersection patches..."); npy_intp dims[2]; dims[1] = 1; + dims[0] = (npy_intp)M.patch.size(); + PyArrayObject *MP = (PyArrayObject *)PyArray_SimpleNew(1, dims, E_NPY_INT); + E_Int *mptr = (E_Int *)PyArray_DATA(MP); + E_Int *ptr = mptr; + for (E_Int fid : M.patch) *ptr++ = fid + 1; + dims[0] = (npy_intp)S.patch.size(); PyArrayObject *SP = (PyArrayObject *)PyArray_SimpleNew(1, dims, E_NPY_INT); E_Int *sptr = (E_Int *)PyArray_DATA(SP); - E_Int *ptr = sptr; - for (E_Int face : S.patch) *ptr++ = face + 1; + ptr = sptr; + for (E_Int fid : S.patch) *ptr++ = fid + 1; + + PyObject *Out = PyList_New(0); + PyList_Append(Out, Mout); + PyList_Append(Out, (PyObject *)MP); + PyList_Append(Out, Sout); PyList_Append(Out, (PyObject *)SP); + Py_DECREF(Mout); + Py_DECREF(Sout); + Py_DECREF(MP); Py_DECREF(SP); + Py_DECREF(TAG); return Out; + */ + return Py_None; } diff --git a/Cassiopee/XCore/XCore/intersectMesh/primitives.cpp b/Cassiopee/XCore/XCore/intersectMesh/primitives.cpp index b1be4c1cc..c788780ab 100644 --- a/Cassiopee/XCore/XCore/intersectMesh/primitives.cpp +++ b/Cassiopee/XCore/XCore/intersectMesh/primitives.cpp @@ -21,20 +21,25 @@ #include #include "primitives.h" -#include "event.h" -E_Float TOL = 1e-12; - -E_Int Sign(E_Float x) +E_Int Sign(E_Float x, E_Float tol) { - if (x > TOL) return 1; - if (x < -TOL) return -1; - //assert(x == 0.0); + if (x > tol) return 1; + if (x < -tol) return -1; return 0; } -E_Int cmp_points(E_Float x1, E_Float y1, E_Float z1, E_Float x2, E_Float y2, E_Float z2) +E_Int cmp_points(E_Float x1, E_Float y1, E_Float z1, + E_Float x2, E_Float y2, E_Float z2) { + if (x1 < x2) return -1; + if (x1 > x2) return 1; + if (y1 < y2) return -1; + if (y1 > y2) return 1; + if (z1 < z2) return -1; + if (z1 > z2) return 1; + return 0; + /* E_Float t = x1 - x2; E_Int s = Sign(t); if (s) return s; @@ -45,313 +50,49 @@ E_Int cmp_points(E_Float x1, E_Float y1, E_Float z1, E_Float x2, E_Float y2, E_F t = z1 - z2; return Sign(t); + */ } -/* -E_Int cmp_points(E_Float x1, E_Float y1, E_Float x2, E_Float y2) -{ - E_Float t = x1 - x2; - E_Int s = Sign(t); - if (s) return s; - - t = y1 - y2; - return Sign(t); -} -*/ - -E_Int cmp_segments -( - E_Float px0, E_Float py0, - E_Float px1, E_Float py1, - E_Float qx1, E_Float qy1, - E_Float rx, E_Float ry, - E_Float dx0, E_Float dy0, - E_Float dx1, E_Float dy1 -) +bool ray_edge_intersect(E_Float ox, E_Float oy, E_Float oz, + E_Float dx, E_Float dy, E_Float dz, + E_Float px, E_Float py, E_Float pz, + E_Float qx, E_Float qy, E_Float qz, + E_Float &t, E_Float &u, + bool coplanar) { - E_Float T1 = dy0 * dx1 - dy1 * dx0; + E_Float v[3]= {px-ox, py-oy, pz-oz}; - E_Int sign1 = Sign(T1); + E_Float dl[3] = {qx-px, qy-py, qz-pz}; - if (sign1 == 0) { + E_Float dr[3] = {dx, dy, dz}; - E_Float mdx = qx1 - px0; - E_Float mdy = qy1 - py0; + if (!coplanar) { + E_Float w[3]; + K_MATH::cross(v, dl, w); + E_Float det = K_MATH::dot(w, dr, 3); - E_Int sign2 = Sign(dy0 * mdx - mdy * dx0); - - if (sign2 == 0) { - E_Int sign3 = Sign(dy1 * mdx - mdy * dx1); - - assert(sign3 == 0); - - if (sign3 == 0) { - return 0; - } - } - } - - if (Sign(dx0) == 0) { - E_Float T2 = (py1 * dx1 - px1 * dy1) + (dy1 * rx - ry * dx1); - E_Int sign2 = Sign(T2); - return (sign2 <= 0) ? 1 : -1; - } - - if (Sign(dx1) == 0) { - E_Float T2 = (py0 * dx0 - px0 * dy0) + (dy0 * rx - ry * dx0); - E_Int sign2 = Sign(T2); - return (sign2 <= 0) ? -1 : 1; + // Ray and edge must be coplanar + if (Sign(det) != 0) return false; } - E_Float T2 = dx1 * (py0 * dx0 + dy0 * (rx - px0)) - dx0 - * (py1 * dx1 + dy1 * (rx - px1)); - - E_Int sign2 = Sign(T2); - if (sign2 != 0) return sign2; - - E_Float T3 = (py0 * dx0 - px0 * dy0) + (dy0 * rx - ry * dx0); - E_Int sign3 = Sign(T3); - return (sign3 <= 0) ? sign1 : -sign1; -} - -// We define the compare functions for Vertexs and Segments by first calling -// cmp_points and cmp_segments on the floating point filter coordinates of the -// corresponding points and segments. In the case that these calls do not -// return a reliable result (i.e. return NO_IDEA) we call them again with the -// exact routines. -E_Int compare(const Vertex &a, const Vertex &b) -{ - return cmp_points(a.x, a.y, a.z, b.x, b.y, b.z); -} - -E_Int compare(const Segment &s1, const Segment &s2, E_Float rx, E_Float ry) -{ - return cmp_segments(s1.p->x, s1.p->y, - s2.p->x, s2.p->y, - s2.q->x, s2.q->y, - rx, ry, - s1.dx, s1.dy, s2.dx, s2.dy); -} - -E_Int cmp_mySeg(const Segment &s1, const Segment &s2) -{ - E_Int cmp = cmp_points(s1.p->x, s1.p->y, s1.p->z, s2.p->x, s2.p->y, - s2.p->z); - if (cmp) return cmp; - - cmp = Sign(s1.color - s2.color); - if (cmp) return cmp; - - cmp = Sign(s1.id - s2.id); - - assert(cmp); - - return cmp; -} - -void compute_intersection(Queue &Q, Snode *sit0, Snode *sit1, - std::vector &I) -{ - Segment s0 = *sit0->key; - Segment s1 = *sit1->key; - - E_Float w = s0.dy * s1.dx - s1.dy * s0.dx; - E_Int i = Sign(w); - if (i == -1 || i == 0) return; - - E_Float c1 = s0.X2() * s0.Y1() - s0.X1() * s0.Y2(); - E_Float c2 = s1.X2() * s1.Y1() - s1.X1() * s1.Y2(); - - E_Float x = c2 * s0.dx - c1 * s1.dx; - E_Float d0 = x - s0.X2() * w; - if (Sign(d0) > 0) return; - if (Sign(x - s1.X2() * w) > 0) return; - - E_Float y = c2 * s0.dy - c1 * s1.dy; - if (Sign(d0) == 0 && - Sign(y - s0.Y2() * w) > 0) return; - - - x /= w; - y /= w; - - Event *xit = Q.lookup(x, y, 0); - - if (xit == NULL) { - xit = Q.insert(x, y, 0); - xit->key->id = I.size(); - I.push_back(xit->key); - } - - xit->inf = sit0->key; - sit0->inf = xit->key; -} - -E_Float DifferenceOfProducts(E_Float a, E_Float b, E_Float c, E_Float d) -{ - E_Float cd = c * d; - E_Float differenceOfProducts = std::fma(a, b, -cd); - E_Float err = std::fma(-c, d, cd); - return differenceOfProducts + err; -} - -E_Float dRand(E_Float dMin, E_Float dMax) -{ - E_Float d = (E_Float) rand() / RAND_MAX; - return dMin + d * (dMax - dMin); -} - -E_Int is_point_on_segment(E_Float px, E_Float py, E_Float pz, E_Float ax, E_Float ay, - E_Float az, E_Float bx, E_Float by, E_Float bz) -{ - E_Float Vab[3] = {bx-ax, by-ay, bz-az}; - E_Float Vap[3] = {px-ax, py-ay, pz-az}; - E_Float N[3]; - K_MATH::cross(Vab, Vap, N); - if (Sign(K_MATH::norm(N, 3)) != 0) return 0; - - E_Float Vbp[3] = {px-bx, py-by, pz-bz}; - - E_Float dp = K_MATH::dot(Vap, Vab, 3); - if (dp < -TOL) return 0; - - dp = K_MATH::dot(Vbp, Vab, 3); - if (dp > TOL) return 0; - - return 1; -} - -E_Int EdgeEdgeIntersect(E_Float ax, E_Float ay, E_Float az, E_Float bx, E_Float by, - E_Float bz, E_Float px, E_Float py, E_Float pz, E_Float qx, E_Float qy, E_Float qz, - E_Float &t) -{ - E_Float d1[3] = {bx-ax, by-ay, bz-az}; - E_Float d2[3] = {qx-px, qy-py, qz-pz}; - E_Float r[3] = {px-ax, py-ay, pz-az}; - - E_Float d1d2[3]; - K_MATH::cross(d1, d2, d1d2); - E_Float denom = K_MATH::dot(d1d2, d1d2, 3); - - if (Sign(denom) == 0) { - - return 0; - - /* - E_Float colli[3]; - K_MATH::cross(d1, r, colli); - E_Float NORM = K_MATH::norm(colli, 3); - if (Sign(NORM) == 0) { - assert("collinear!" && 0); - } else { - return 0; - } - */ - } + // ray and edge must not be parallel + E_Float n[3]; + K_MATH::cross(dr, dl, n); + E_Float denom = K_MATH::dot(n, n, 3); + if (Sign(denom) == 0) return false; E_Float tmp[3]; - K_MATH::cross(r, d2, tmp); - t = K_MATH::dot(tmp, d1d2, 3); - t /= denom; - if (t <= TOL) return 0; + K_MATH::cross(v, dl, tmp); - K_MATH::cross(r, d1, tmp); - E_Float u = K_MATH::dot(tmp, d1d2, 3); - u /= denom; - if (u < -TOL || u > 1 + TOL) return 0; + t = K_MATH::dot(tmp, n, 3) / denom; - //ix = px + u*(qx - px); - //iy = py + u*(qy - py); - //iz = pz + u*(qz - pz); + if (t < TOL) return false; - return 1; -} - -E_Int EdgeEdgeIntersect(E_Float ax, E_Float ay, E_Float az, E_Float bx, E_Float by, - E_Float bz, E_Float px, E_Float py, E_Float pz, E_Float qx, E_Float qy, E_Float qz, - E_Float &ix, E_Float &iy, E_Float &iz) -{ - E_Float d1[3] = {bx-ax, by-ay, bz-az}; - E_Float d2[3] = {qx-px, qy-py, qz-pz}; - E_Float r[3] = {px-ax, py-ay, pz-az}; - - E_Float d1d2[3]; - K_MATH::cross(d1, d2, d1d2); - E_Float denom = K_MATH::dot(d1d2, d1d2, 3); - - if (Sign(denom) == 0) { - - return 0; - - /* - E_Float colli[3]; - K_MATH::cross(d1, r, colli); - E_Float NORM = K_MATH::norm(colli, 3); - if (Sign(NORM) == 0) { - assert("collinear!" && 0); - } else { - return 0; - } - */ - } - - E_Float tmp[3]; - K_MATH::cross(r, d2, tmp); - E_Float t = K_MATH::dot(tmp, d1d2, 3); - t /= denom; - //if (t < -TOL) return 0; - if (t <= TOL) return 0; - - K_MATH::cross(r, d1, tmp); - E_Float u = K_MATH::dot(tmp, d1d2, 3); - u /= denom; - if (u < -TOL || u > 1 + TOL) return 0; + K_MATH::cross(v, dr, tmp); - ix = px + u*(qx - px); - iy = py + u*(qy - py); - iz = pz + u*(qz - pz); - - return 1; -} - -E_Int EdgeEdgeIntersect(E_Float ax, E_Float ay, E_Float az, E_Float bx, E_Float by, - E_Float bz, E_Float px, E_Float py, E_Float pz, E_Float qx, E_Float qy, E_Float qz) -{ - E_Float d1[3] = {bx-ax, by-ay, bz-az}; - E_Float d2[3] = {qx-px, qy-py, qz-pz}; - E_Float r[3] = {px-ax, py-ay, pz-az}; - - E_Float d1d2[3]; - K_MATH::cross(d1, d2, d1d2); - E_Float denom = K_MATH::dot(d1d2, d1d2, 3); - - if (Sign(denom) == 0) { - - return 0; - - /* - E_Float colli[3]; - K_MATH::cross(d1, r, colli); - E_Float NORM = K_MATH::norm(colli, 3); - if (Sign(NORM) == 0) { - assert("collinear!" && 0); - } else { - return 0; - } - */ - } - - E_Float tmp[3]; - K_MATH::cross(r, d2, tmp); - E_Float t = K_MATH::dot(tmp, d1d2, 3); - t /= denom; - //if (t < -TOL) return 0; - if (t <= TOL) return 0; + u = K_MATH::dot(tmp, n, 3) / denom; - K_MATH::cross(r, d1, tmp); - E_Float u = K_MATH::dot(tmp, d1d2, 3); - u /= denom; - if (u < -TOL || u > 1 + TOL) return 0; + if (u < -RAY_EDGE_TOL || u > 1 + RAY_EDGE_TOL) return false; - return 1; + return true; } diff --git a/Cassiopee/XCore/XCore/intersectMesh/primitives.h b/Cassiopee/XCore/XCore/intersectMesh/primitives.h index bc359f707..977dcfc39 100644 --- a/Cassiopee/XCore/XCore/intersectMesh/primitives.h +++ b/Cassiopee/XCore/XCore/intersectMesh/primitives.h @@ -20,44 +20,22 @@ #include -#include "queue.h" -#include "status.h" #include "common/common.h" -extern E_Float TOL; +constexpr E_Float TOL = 1e-11; +constexpr E_Float RAY_EDGE_TOL = 1e-5; -void compute_intersection(Queue &Q, Snode *sit0, Snode *sit1, - std::vector &I); +E_Int Sign(E_Float x, E_Float tol=TOL); -E_Int compare(const Vertex &a, const Vertex &b); +E_Int cmp_points(E_Float x1, E_Float y1, E_Float z1, + E_Float x2, E_Float y2, E_Float z2); -E_Int compare(const Segment &s0, const Segment &s1, E_Float rx, E_Float ry); +bool ray_edge_intersect(E_Float ox, E_Float oy, E_Float oz, + E_Float dx, E_Float dy, E_Float dz, + E_Float px, E_Float py, E_Float pz, + E_Float qx, E_Float qy, E_Float qz, + E_Float &t, E_Float &u, + bool coplanar = true); -E_Int cmp_mySeg(const Segment &s1, const Segment &s2); - -E_Int cmp_points(E_Float x1, E_Float y1, E_Float z1, E_Float x2, E_Float y2, E_Float z2); -//E_Int cmp_points(E_Float x1, E_Float y1, E_Float x2, E_Float y2); - -E_Float DifferenceOfProducts(E_Float a, E_Float b, E_Float c, E_Float d); - -E_Float TwoDiff(E_Float a, E_Float b); - -E_Int Sign(E_Float x); - -E_Int orient3D(E_Float *A, E_Float *B, E_Float *C, E_Float *D); - -E_Float dRand(E_Float dMin, E_Float dMax); - -E_Int is_point_on_segment(E_Float px, E_Float py, E_Float pz, E_Float ax, E_Float ay, - E_Float az, E_Float bx, E_Float by, E_Float bz); - -E_Int EdgeEdgeIntersect(E_Float ax, E_Float ay, E_Float az, E_Float bx, E_Float by, - E_Float bz, E_Float px, E_Float py, E_Float pz, E_Float qx, E_Float qy, E_Float qz, - E_Float &ix, E_Float &iy, E_Float &iz); - -E_Int EdgeEdgeIntersect(E_Float ax, E_Float ay, E_Float az, E_Float bx, E_Float by, - E_Float bz, E_Float px, E_Float py, E_Float pz, E_Float qx, E_Float qy, E_Float qz); - -E_Int EdgeEdgeIntersect(E_Float ax, E_Float ay, E_Float az, E_Float bx, E_Float by, - E_Float bz, E_Float px, E_Float py, E_Float pz, E_Float qx, E_Float qy, E_Float qz, - E_Float &t); +E_Int ray_point_orient(const E_Float o[3], const E_Float d[3], + const E_Float fN[3], E_Float px, E_Float py, E_Float pz); diff --git a/Cassiopee/XCore/XCore/intersectMesh/queue.cpp b/Cassiopee/XCore/XCore/intersectMesh/queue.cpp deleted file mode 100644 index e52fc7bf7..000000000 --- a/Cassiopee/XCore/XCore/intersectMesh/queue.cpp +++ /dev/null @@ -1,181 +0,0 @@ -/* - Copyright 2013-2024 Onera. - - This file is part of Cassiopee. - - Cassiopee is free software: you can redistribute it and/or modify - it under the terms of the GNU General Public License as published by - the Free Software Foundation, either version 3 of the License, or - (at your option) any later version. - - Cassiopee is distributed in the hope that it will be useful, - but WITHOUT ANY WARRANTY; without even the implied warranty of - MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - GNU General Public License for more details. - - You should have received a copy of the GNU General Public License - along with Cassiopee. If not, see . -*/ -#include -#include - -#include "queue.h" -#include "primitives.h" -#include "event.h" - -Queue::Queue() -: root(NULL), nelem(0) -{} - -// Insert an intersection -Event *Queue::insert(E_Float x, E_Float y, E_Float z) -{ - return insert_(root, x, y, z); -} - -Event *Queue::insert_(Event *&root, E_Float x, E_Float y, E_Float z) -{ - if (root == NULL) { - root = new Event(x, y, z); - return root; - } - - Vertex *key = root->key; - - E_Int cmp = cmp_points(key->x, key->y, key->z, x, y, z); - - if (cmp == 0) { - return root; - } else if (cmp < 0) { - return insert_(root->right, x, y, z); - } else { - return insert_(root->left, x, y, z); - } -} - -// Insert an input vertex -Event *Queue::insert(E_Float x, E_Float y, E_Float z, E_Int oid, E_Int color) -{ - return insert_(root, x, y, z, oid, color); -} - -Event *Queue::insert_(Event *&root, E_Float x, E_Float y, E_Float z, E_Int oid, E_Int color) -{ - if (root == NULL) { - root = new Event(x, y, z, oid, color); - return root; - } - - Vertex *key = root->key; - - E_Int cmp = cmp_points(key->x, key->y, key->z, x, y, z); - - if (cmp == 0) { - assert(key->oid[color] == -1); - assert(key->oid[(color+1)%2] != -1); - key->oid[color] = oid; - return root; - } else if (cmp < 0) { - return insert_(root->right, x, y, z, oid, color); - } else { - return insert_(root->left, x, y, z, oid, color); - } -} - -Event *Queue::lookup(E_Float x, E_Float y, E_Float z) -{ - return lookup_(root, x, y, z); -} - -Event *Queue::lookup(Vertex *key) -{ - return lookup_(root, key->x, key->y, key->z); -} - -Event *Queue::lookup_(Event *root, E_Float x, E_Float y, E_Float z) -{ - if (root == NULL) return NULL; - - Vertex *key = root->key; - E_Int cmp = cmp_points(key->x, key->y, key->z, x, y, z); - - if (cmp == 0) return root; - else if (cmp < 0) return lookup_(root->right, x, y, z); - else return lookup_(root->left, x, y, z); -} - -Event *Queue::min() -{ - if (root == NULL) return NULL; - - Event *curr = root; - - while (curr->left != NULL) curr = curr->left; - - return curr; -} - -void Queue::erase(Event *event) -{ - root = erase_(root, event->key); -} - -void Queue::erase(Vertex *p) -{ - root = erase_(root, p); -} - -Event *Queue::erase_(Event *root, Vertex *p) -{ - if (root == NULL) return NULL; - - E_Int cmp = compare(*root->key, *p); - - if (cmp < 0) { - root->right = erase_(root->right, p); - return root; - } else if (cmp > 0) { - root->left = erase_(root->left, p); - return root; - } - - assert(root->key == p); - - if (root->left == NULL) { - Event *tmp = root->right; - delete root; - return tmp; - } else if (root->right == NULL) { - Event *tmp = root->left; - delete root; - return tmp; - } else { - Event *succ_parent = root; - - Event *succ = root->right; - while (succ->left) { - succ_parent = succ; - succ = succ->left; - } - - if (succ_parent != root) succ_parent->left = succ->right; - else succ_parent->right = succ->left; - - root->key = succ->key; - root->inf = succ->inf; - - delete succ; - return root; - } -} - -void Queue::inorder(std::vector &V) const -{ - if (root == NULL) return; - root->inorder(V); -} - -void Queue::drop() -{ - Event_drop(root); -} \ No newline at end of file diff --git a/Cassiopee/XCore/XCore/intersectMesh/queue.h b/Cassiopee/XCore/XCore/intersectMesh/queue.h deleted file mode 100644 index c60c89c1b..000000000 --- a/Cassiopee/XCore/XCore/intersectMesh/queue.h +++ /dev/null @@ -1,65 +0,0 @@ -/* - Copyright 2013-2024 Onera. - - This file is part of Cassiopee. - - Cassiopee is free software: you can redistribute it and/or modify - it under the terms of the GNU General Public License as published by - the Free Software Foundation, either version 3 of the License, or - (at your option) any later version. - - Cassiopee is distributed in the hope that it will be useful, - but WITHOUT ANY WARRANTY; without even the implied warranty of - MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - GNU General Public License for more details. - - You should have received a copy of the GNU General Public License - along with Cassiopee. If not, see . -*/ -#pragma once - -#include -#include - -#include "xcore.h" -#include "common/common.h" - -struct Vertex; -struct Segment; -struct Event; - -struct Queue { - Event *root; - - E_Int nelem; - - Queue(); - - Event *insert(E_Float X, E_Float Y, E_Float Z, E_Int oid, E_Int color); - - Event *insert(E_Float X, E_Float Y, E_Float Z); - - Event *lookup(Vertex *key); - - Event *lookup(E_Float x, E_Float y, E_Float z); - - inline bool empty() { return root == NULL; } - - Event *min(); - - void erase(Event *event); - - void erase(Vertex *p); - - void inorder(std::vector &V) const; - - Event *insert_(Event *&root, E_Float x, E_Float y, E_Float z, E_Int oid, E_Int color); - - Event *insert_(Event *&root, E_Float x, E_Float y, E_Float z); - - Event *lookup_(Event *root, E_Float x, E_Float y, E_Float z); - - Event *erase_(Event *root, Vertex *p); - - void drop(); -}; diff --git a/Cassiopee/XCore/XCore/intersectMesh/ray.cpp b/Cassiopee/XCore/XCore/intersectMesh/ray.cpp index 29966d557..b907e38fb 100644 --- a/Cassiopee/XCore/XCore/intersectMesh/ray.cpp +++ b/Cassiopee/XCore/XCore/intersectMesh/ray.cpp @@ -18,6 +18,129 @@ */ #include "ray.h" #include "primitives.h" +#include "AABB.h" + +E_Int ray_point_orient(const E_Float o[3], const E_Float d[3], + const E_Float fN[3], E_Float px, E_Float py, E_Float pz) +{ + E_Float w[3] = {px-o[0], py-o[1], pz-o[2]}; + E_Float c[3]; + K_MATH::cross(d, w, c); + E_Float dp = K_MATH::dot(c, fN, 3); + // TODO(Imad): needs FEA + float128 + E_Int cmp = Sign(dp); + if (cmp > 0) return 1; + if (cmp < 0) return -1; + return 0; +} + +bool ray_AABB_intersect(E_Float ox, E_Float oy, E_Float oz, + E_Float dx, E_Float dy, E_Float dz, + const AABB &box) +{ + E_Float inv_dx = (dx != 0.0) ? 1.0/dx : EFLOATMAX; + E_Float inv_dy = (dy != 0.0) ? 1.0/dy : EFLOATMAX; + E_Float inv_dz = (dz != 0.0) ? 1.0/dz : EFLOATMAX; + + E_Float t_min = EFLOATMIN; + E_Float t_max = EFLOATMAX; + + // X + if (dx == 0.0) { + if (ox < box.xmin || ox > box.xmax) { + return false; + } + } else { + E_Float t1 = (box.xmin - ox) * inv_dx; + E_Float t2 = (box.xmax - ox) * inv_dx; + + if (t1 > t2) std::swap(t1, t2); + + t_min = std::max(t_min, t1); + t_max = std::max(t_max, t2); + + if (t_min > t_max) + return false; + } + + // Y + if (dy == 0.0) { + if (oy < box.ymin || oy > box.ymax) { + return false; + } + } else { + E_Float t1 = (box.ymin - oy) * inv_dy; + E_Float t2 = (box.ymax - oy) * inv_dy; + + if (t1 > t2) std::swap(t1, t2); + + t_min = std::max(t_min, t1); + t_max = std::max(t_max, t2); + + if (t_min > t_max) + return false; + } + + // Z + if (dz == 0.0) { + if (oz < box.zmin || oz > box.zmax) { + return false; + } + } else { + E_Float t1 = (box.zmin - oz) * inv_dz; + E_Float t2 = (box.zmax - oz) * inv_dz; + + if (t1 > t2) std::swap(t1, t2); + + t_min = std::max(t_min, t1); + t_max = std::max(t_max, t2); + + if (t_min > t_max) + return false; + } + + return true; +} + +bool MollerTrumboreAnyDir( + E_Float ox, E_Float oy, E_Float oz, + E_Float dx, E_Float dy, E_Float dz, + E_Float ax, E_Float ay, E_Float az, + E_Float bx, E_Float by, E_Float bz, + E_Float cx, E_Float cy, E_Float cz, + E_Float &u, E_Float &v, E_Float &w, E_Float &t, + E_Float &x, E_Float &y, E_Float &z) +{ + E_Float v1[3] = {bx-ax, by-ay, bz-az}; + E_Float v2[3] = {cx-ax, cy-ay, cz-az}; + + E_Float d[3] = {dx, dy, dz}; + E_Float h[3]; + K_MATH::cross(d, v2, h); + E_Float det = K_MATH::dot(v1, h, 3); + if (Sign(det) == 0) return false; + + E_Float inv_det = 1.0 / det; + + E_Float s[3] = {ox-ax, oy-ay, oz-az}; + v = K_MATH::dot(s, h, 3) * inv_det; + if (v < -TOL || v > 1+TOL) return false; + + E_Float q[3]; + K_MATH::cross(s, v1, q); + w = K_MATH::dot(d, q, 3) * inv_det; + if (w < -TOL || w > 1+TOL) return false; + + u = 1 - v - w; + if (u < -TOL || u > 1+TOL) return false; + + t = K_MATH::dot(v2, q, 3) * inv_det; + x = ox + t * dx; + y = oy + t * dy; + z = oz + t * dz; + + return true; +} E_Int MollerTrumbore(E_Float px, E_Float py, E_Float pz, E_Float dx, E_Float dy, E_Float dz, E_Float ax, E_Float ay, E_Float az, E_Float bx, E_Float by, E_Float bz, @@ -74,12 +197,18 @@ E_Int MollerTrumbore(E_Float px, E_Float py, E_Float pz, E_Float dx, E_Float dy, return 0; } -Ray::Ray(Point O, Vec3 D) +/* +Ray::Ray(Point O, E_Float D[3]) : org(O), dir(D) { - E_Float adx = fabs(dir[0]); - E_Float ady = fabs(dir[1]); - E_Float adz = fabs(dir[2]); + org.x = O.x; + org.y = O.y; + org.z = O.z; + + + E_Float adx = fabs(dir.x); + E_Float ady = fabs(dir.y); + E_Float adz = fabs(dir.z); if (adx > ady && adx > adz) kz = 0; else if (ady > adz) kz = 1; @@ -162,3 +291,4 @@ E_Int Ray::intersect_triangle(const Point &a, const Point &b, const Point &c, return 1; } +*/ diff --git a/Cassiopee/XCore/XCore/intersectMesh/ray.h b/Cassiopee/XCore/XCore/intersectMesh/ray.h index 291c3d30a..a80170a31 100644 --- a/Cassiopee/XCore/XCore/intersectMesh/ray.h +++ b/Cassiopee/XCore/XCore/intersectMesh/ray.h @@ -18,9 +18,15 @@ */ #pragma once -#include "triangleIntersection.h" +#include "triangle.h" #include "point.h" +struct AABB; + +struct Vec3 { + E_Float x, y, z; +}; + struct Ray { Point org; Vec3 dir; @@ -33,6 +39,22 @@ struct Ray { TriangleIntersection &TI); }; +E_Int ray_point_orient(const E_Float o[3], const E_Float d[3], + const E_Float fN[3], E_Float px, E_Float py, E_Float pz); + +bool ray_AABB_intersect(E_Float ox, E_Float oy, E_Float oz, + E_Float dx, E_Float dy, E_Float dz, + const AABB &box); + E_Int MollerTrumbore(E_Float px, E_Float py, E_Float pz, E_Float dx, E_Float dy, E_Float dz, E_Float ax, E_Float ay, E_Float az, E_Float bx, E_Float by, E_Float bz, E_Float cx, E_Float cy, E_Float cz, TriangleIntersection &TI); + +bool MollerTrumboreAnyDir( + E_Float px, E_Float py, E_Float pz, + E_Float dx, E_Float dy, E_Float dz, + E_Float ax, E_Float ay, E_Float az, + E_Float bx, E_Float by, E_Float bz, + E_Float cx, E_Float cy, E_Float cz, + E_Float &u, E_Float &v, E_Float &w, E_Float &t, + E_Float &x, E_Float &y, E_Float &z); diff --git a/Cassiopee/XCore/XCore/intersectMesh/removeIntersectingKPlanes.cpp b/Cassiopee/XCore/XCore/intersectMesh/removeIntersectingKPlanes.cpp index 7a8316cf2..5e8c66231 100644 --- a/Cassiopee/XCore/XCore/intersectMesh/removeIntersectingKPlanes.cpp +++ b/Cassiopee/XCore/XCore/intersectMesh/removeIntersectingKPlanes.cpp @@ -55,8 +55,7 @@ void construct_faces(std::vector> &faces, NF++; } -static -PyObject *handle_slave(IMesh *M, Karray& sarray) +PyObject *handle_slave(const IMesh *M, const Smesh &Mf, Karray& sarray) { E_Int ni = sarray.ni; E_Int nj = sarray.nj; @@ -77,6 +76,7 @@ PyObject *handle_slave(IMesh *M, Karray& sarray) for (E_Int i = 0; i < ni; i++) { E_Int base = i + ni*j; + bool was_inside = false; for (E_Int k = 0; k < nk; k++) { @@ -92,53 +92,78 @@ PyObject *handle_slave(IMesh *M, Karray& sarray) // Cache the point to be projected E_Int proj_id = base + nij*kmax[base]; proj_points.push_back(proj_id); + + was_inside = true; break; } } + + if (!was_inside) { + // i-j line completely outside of M + // Projection points is the last point + kmax[base] = nk-1; + E_Int proj_id = base + nij*kmax[base]; + proj_points.push_back(proj_id); + } } } - //point_write("proj_points", Xs, Ys, Zs, proj_points); + assert(proj_points.size() == (size_t)nij); + + //point_write("proj_points.im", Xs, Ys, Zs, proj_points); + //printf("points written!\n"); + //fflush(stdout); E_Int np = ni*nj*nk; // Project points onto marray surface std::unordered_map point_hit_table; + //std::vector projections; + for (E_Int i = 0; i < nij; i++) { E_Int p = proj_points[i]; - E_Int q = p + nij; + E_Int q = p - nij; E_Float px = Xs[p]; E_Float py = Ys[p]; E_Float pz = Zs[p]; - E_Float dx = Xs[q] - Xs[p]; - E_Float dy = Ys[q] - Ys[p]; - E_Float dz = Zs[q] - Zs[p]; + E_Float dx = Xs[p] - Xs[q]; + E_Float dy = Ys[p] - Ys[q]; + E_Float dz = Zs[p] - Zs[q]; E_Float NORM = sqrt(dx*dx + dy*dy + dz*dz); dx /= NORM, dy /= NORM, dz /= NORM; - TriangleIntersection TI; + std::vector mlocs; + Mf.ray_intersect_BVH(px, py, pz, dx, dy, dz, Mf.root_node_idx, mlocs); - E_Int hit = M->project_point(px, py, pz, dx, dy, dz, TI, i); + //if (mlocs.empty()) { + // point_write("lost.im", px, py, pz); + //} - assert(hit); + assert(mlocs.size() > 0); + PointLoc ploc; + E_Float min_abs_t = EFLOATMAX; + for (const auto &mloc : mlocs) { + if (fabs(mloc.t) < min_abs_t) { + min_abs_t = fabs(mloc.t); + ploc = mloc; + } + } + TriangleIntersection TI; TI.pid = p; - + TI.x = ploc.x, TI.y = ploc.y, TI.z = ploc.z; point_hit_table[p] = TI; - } - for (const auto &ploc : point_hit_table) { - E_Int fid = ploc.second.face; - const auto &pn = M->F[fid]; - assert(pn.size() == 4); - M->faces_to_tri.insert(fid); + //projections.push_back({ploc.x, ploc.y, ploc.z}); } + //point_write("projections.im", projections); + // Construct the new faces and cells std::vector> faces; @@ -368,6 +393,158 @@ PyObject *handle_slave(IMesh *M, Karray& sarray) return out; } +E_Int get_kmax(IMesh *M, Karray& sarray) +{ + E_Int ni = sarray.ni; + E_Int nj = sarray.nj; + E_Int nk = sarray.nk; + E_Int nij = ni * nj; + + E_Float *Xs = sarray.x; + E_Float *Ys = sarray.y; + E_Float *Zs = sarray.z; + + E_Int kmax = -1; + + for (E_Int k = 0; k < nk && kmax == -1; k++) { + for (E_Int j = 0; j < nj && kmax == -1; j++) { + for (E_Int i = 0; i < ni; i++) { + E_Int idx = i + ni*j + nij*k; + + E_Float px = Xs[idx]; + E_Float py = Ys[idx]; + E_Float pz = Zs[idx]; + + if (M->is_point_inside(px, py, pz)) { + kmax = k-1; + return kmax; + } + } + } + } + + assert(0); + return kmax; +} + +PyObject *handle_slave2(IMesh *M, Karray& sarray, E_Int kmax) +{ + E_Int ni = sarray.ni; + E_Int nj = sarray.nj; + E_Int nk = sarray.nk; + E_Int nij = ni * nj; + + E_Float *Xs = sarray.x; + E_Float *Ys = sarray.y; + E_Float *Zs = sarray.z; + + // Indices of points to be projected + std::vector proj_points; + + for (E_Int j = 0; j < nj; j++) { + for (E_Int i = 0; i < ni; i++) { + + E_Int idx = i + ni*j + nij*kmax; + + proj_points.push_back(idx); + } + } + + //point_write("proj_points", Xs, Ys, Zs, proj_points); + + //E_Int np = ni*nj*nk; + + // Project points onto marray surface + std::unordered_map point_hit_table; + + for (E_Int i = 0; i < nij; i++) { + E_Int p = proj_points[i]; + E_Int q = p + nij; + + E_Float px = Xs[p]; + E_Float py = Ys[p]; + E_Float pz = Zs[p]; + + E_Float dx = Xs[q] - Xs[p]; + E_Float dy = Ys[q] - Ys[p]; + E_Float dz = Zs[q] - Zs[p]; + + E_Float NORM = sqrt(dx*dx + dy*dy + dz*dz); + + dx /= NORM, dy /= NORM, dz /= NORM; + + TriangleIntersection TI; + + E_Int hit = M->project_point(px, py, pz, dx, dy, dz, TI, i); + + assert(hit); + + TI.pid = p; + + point_hit_table[p] = TI; + } + + // Make out cartesian mesh + PyObject *tpl; + nk = kmax + 2; + tpl = K_ARRAY::buildArray3(3, "x,y,z", ni, nj, nk, 3); + + K_FLD::FldArrayF *f; + K_FLD::FldArrayI *c; + char *varString, *eltType; + K_ARRAY::getFromArray3(tpl, varString, f, ni, nj, nk, c, eltType); + + E_Float* xt = f->begin(1); + E_Float* yt = f->begin(2); + E_Float* zt = f->begin(3); + + // Copy all the points up to kmax + for (E_Int k = 0; k < kmax+1; k++) { + for (E_Int j = 0; j < nj; j++) { + for (E_Int i = 0; i < ni; i++) { + E_Int ind = i + j*ni + k*nij; + xt[ind] = Xs[ind]; + yt[ind] = Ys[ind]; + zt[ind] = Zs[ind]; + } + } + } + + // Copy the projected points + E_Int ind = nij*(kmax+1); + for (E_Int i = 0; i < nij; i++) { + E_Int p = proj_points[i]; + auto EH = point_hit_table[p]; + E_Float x = EH.x; + E_Float y = EH.y; + E_Float z = EH.z; + xt[ind] = x; + yt[ind] = y; + zt[ind] = z; + ind++; + } + + // Tag the projected points + npy_intp dims[2]; + dims[1] = 1; + dims[0] = (npy_intp)ni*nj*nk; + PyArrayObject *tag = (PyArrayObject *)PyArray_SimpleNew(1, dims, NPY_DOUBLE); + E_Float *ptag = (E_Float *)PyArray_DATA(tag); + for (E_Int i = 0; i < nij*(kmax+1); i++) ptag[i] = 0.0; + for (E_Int i = nij*(kmax+1); i < nij*nk; i++) ptag[i] = 1.0; + + PyObject *out = PyList_New(0); + PyList_Append(out, tpl); + PyList_Append(out, (PyObject *)tag); + RELEASESHAREDS(tpl, f); + Py_DECREF(tpl); + Py_DECREF(tag); + + return out; +} + +#include "smesh.h" + PyObject *K_XCORE::removeIntersectingKPlanes(PyObject *self, PyObject *args) { PyObject *MASTER, *SLAVES; @@ -388,6 +565,11 @@ PyObject *K_XCORE::removeIntersectingKPlanes(PyObject *self, PyObject *args) M->make_bbox(); M->hash_skin(); + Smesh Mf = Smesh::Smesh_from_mesh_skin(*M, M->skin, false); + printf("Mf: %d tris\n", Mf.nf); + Mf.make_fcenters(); + Mf.make_BVH(); + E_Int nslaves = PyList_Size(SLAVES); E_Int i, ret; @@ -411,11 +593,24 @@ PyObject *K_XCORE::removeIntersectingKPlanes(PyObject *self, PyObject *args) return NULL; } + /* + E_Int kmax = 10000000; + + for (E_Int i = 0; i < nslaves; i++) { + E_Int k = get_kmax(M, sarrays[i]); + printf("k: %d\n", k); + if (k < kmax) kmax = k; + } + + printf("kmax: %d\n", kmax); + */ + PyObject *slaves_out = PyList_New(0); for (E_Int i = 0; i < nslaves; i++) { printf("Projecting %d / %d\n", i+1, nslaves); - PyObject *st = handle_slave(M, sarrays[i]); + //PyObject *st = handle_slave2(M, sarrays[i], kmax); + PyObject *st = handle_slave(M, Mf, sarrays[i]); PyList_Append(slaves_out, st); Py_DECREF(st); Karray_free_structured(sarrays[i]); diff --git a/Cassiopee/XCore/XCore/intersectMesh/segment.cpp b/Cassiopee/XCore/XCore/intersectMesh/segment.cpp deleted file mode 100644 index 72b4c4dee..000000000 --- a/Cassiopee/XCore/XCore/intersectMesh/segment.cpp +++ /dev/null @@ -1,120 +0,0 @@ -/* - Copyright 2013-2024 Onera. - - This file is part of Cassiopee. - - Cassiopee is free software: you can redistribute it and/or modify - it under the terms of the GNU General Public License as published by - the Free Software Foundation, either version 3 of the License, or - (at your option) any later version. - - Cassiopee is distributed in the hope that it will be useful, - but WITHOUT ANY WARRANTY; without even the implied warranty of - MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - GNU General Public License for more details. - - You should have received a copy of the GNU General Public License - along with Cassiopee. If not, see . -*/ -#include -#include - -#include "segment.h" -#include "primitives.h" -#include "hedge.h" -#include "dcel.h" - -Segment::Segment(Vertex *P, Vertex *Q, E_Int Id) -{ - p = P; - q = Q; - dx = q->x - p->x; - dy = q->y - p->y; - rep = NULL; - id = Id; - color = Dcel::NO_IDEA; - assert(Sign(dx) >= 0); -} - -Segment::Segment(Vertex *P) -: p(P), q(P), dx(0.0), dy(0.0), rep(NULL), id(-1), color(Dcel::NO_IDEA) -{} - -Segment::Segment(Hedge *h, E_Int Id) -{ - p = h->orig; - Hedge *t = h->twin; - q = t->orig; - rep = h; - if (compare(*p, *q) > 0) { - std::swap(p, q); - rep = t; - } - dx = q->x - p->x; - dy = q->y - p->y; - id = Id; - color = rep->color; - assert(Sign(dx) >= 0); -} - -bool Segment::overlaps(const Segment &s) -{ - E_Float sdx = s.dx; - E_Float sdy = s.dy; - E_Float sqx = s.q->x; - E_Float sqy = s.q->y; - E_Float px = p->x; - E_Float py = p->y; - - E_Float T1 = dy * sdx - sdy * dx; - E_Int sign1 = Sign(T1); - - if (sign1 == 0) { - E_Float mdx = sqx - px; - E_Float mdy = sqy - py; - - E_Int sign2 = Sign(dy * mdx - mdy * dx); - - if (sign2 == 0) { - E_Int sign3 = Sign(sdy * mdx - mdy * sdx); - - assert(sign3 == 0); - - if (sign3 == 0) { - return true; - } - } - } - - return false; -} - -static -E_Int _partition(std::vector &S, E_Int low, E_Int high, - E_Int (*cmp)(const Segment &, const Segment &)) -{ - Segment *pivot = S[high]; - E_Int i = low-1; - - for (E_Int j = low; j < high; j++) { - if (cmp(*S[j], *pivot) <= 0) { - i++; - std::swap(S[i], S[j]); - } - } - - i++; - std::swap(S[i], S[high]); - return i; -} - -void Segment::sort(std::vector &S, E_Int start, E_Int end, - E_Int (*cmp)(const Segment &, const Segment &)) -{ - if (start >= end) return; - - E_Int p = _partition(S, start, end, cmp); - - sort(S, start, p - 1, cmp); - sort(S, p + 1, end, cmp); -} diff --git a/Cassiopee/XCore/XCore/intersectMesh/segment.h b/Cassiopee/XCore/XCore/intersectMesh/segment.h deleted file mode 100644 index 98275b5a8..000000000 --- a/Cassiopee/XCore/XCore/intersectMesh/segment.h +++ /dev/null @@ -1,49 +0,0 @@ -/* - Copyright 2013-2024 Onera. - - This file is part of Cassiopee. - - Cassiopee is free software: you can redistribute it and/or modify - it under the terms of the GNU General Public License as published by - the Free Software Foundation, either version 3 of the License, or - (at your option) any later version. - - Cassiopee is distributed in the hope that it will be useful, - but WITHOUT ANY WARRANTY; without even the implied warranty of - MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - GNU General Public License for more details. - - You should have received a copy of the GNU General Public License - along with Cassiopee. If not, see . -*/ -#pragma once - -#include - -#include "vertex.h" - -struct Hedge; - -struct Segment { - Vertex *p; - Vertex *q; - E_Float dx; - E_Float dy; - Hedge *rep; - E_Int id; - E_Int color; - - inline E_Float X1() const { return p->x; } - inline E_Float Y1() const { return p->y; } - inline E_Float X2() const { return q->x; } - inline E_Float Y2() const { return q->y; } - - Segment(Vertex *p, Vertex *q, E_Int Id); - Segment(Hedge *h, E_Int Id); - Segment(Vertex *P); - - bool overlaps(const Segment &seg); - - static void sort(std::vector &S, E_Int start, E_Int end, - E_Int (*cmp)(const Segment &, const Segment &)); -}; diff --git a/Cassiopee/XCore/XCore/intersectMesh/smesh.cpp b/Cassiopee/XCore/XCore/intersectMesh/smesh.cpp index 85d7ea10d..287d5e8cb 100644 --- a/Cassiopee/XCore/XCore/intersectMesh/smesh.cpp +++ b/Cassiopee/XCore/XCore/intersectMesh/smesh.cpp @@ -21,9 +21,9 @@ #include "triangle.h" #include "mesh.h" #include "io.h" +#include "point.h" #include -#include #include #include #include @@ -31,38 +31,128 @@ #include #include -bool Smesh::ccw_oriented(E_Int face) +void Smesh::get_edge_centers(E_Int p, E_Int q, std::vector &edge_centers) { - E_Float sum = 0; + u_edge e(p, q); + auto it = ecenter.find(e); + if (it == ecenter.end()) { + edge_centers.push_back(p); + return; + } + + E_Int ec = it->second; + get_edge_centers(p, ec, edge_centers); + get_edge_centers(ec, q, edge_centers); +} + +void Smesh::clear_conformal_data() +{ + //Fc.clear(); + F2E.clear(); + F2F.clear(); + + P2F.clear(); + P2E.clear(); + + ne = 0; + E.clear(); + E2F.clear(); +} + +void Smesh::conformize() +{ + clear_conformal_data(); + + std::vector> new_Fc(nf); + + for (E_Int fid = 0; fid < nf; fid++) { + const auto &pn = Fc[fid]; + + std::vector new_pn; + + for (size_t i = 0; i < pn.size(); i++) { + E_Int p = pn[i]; + E_Int q = pn[(i+1)%pn.size()]; + + std::vector local; + get_edge_centers(p, q, local); + for (E_Int p : local) new_pn.push_back(p); + assert(local[0] == p); + } + + new_Fc[fid] = new_pn; + } + + Fc = new_Fc; + + make_edges(); + make_point_faces(); + make_point_edges(); +} + +Smesh::Smesh() +{} + +Smesh Smesh::Smesh_from_mesh_skin(const IMesh &M, + const std::vector &skin, bool check_Euler) +{ + return Smesh(M, skin, check_Euler); +} + +Smesh Smesh::Smesh_from_point_tags(const IMesh &M, const E_Float *ptag, + bool check_Euler) +{ + std::vector fids; + + for (E_Int fid : M.skin) { + const auto &pn = M.F[fid]; + bool keep = true; + for (E_Int pid : pn) { + if (ptag[pid] != 1) { + keep = false; + break; + } + } + if (keep) fids.push_back(fid); + } + + return Smesh(M, fids, check_Euler); +} - const auto &pn = F[face]; +Smesh Smesh::Smesh_from_mesh_patch(const IMesh &M, bool check_Euler) +{ + std::vector fids; - for (size_t i = 0; i < pn.size(); i++) { - E_Int p = pn[i]; - E_Int q = pn[(i+1)%pn.size()]; - sum += (X[q]-X[p])*(Y[q]+Y[p]); + for (E_Int fid : M.patch) { + fids.push_back(fid); } - E_Int sign = Sign(sum); - assert(sign != 0); + return Smesh(M, fids, check_Euler); +} - return sign < 0; +Smesh Smesh::Smesh_from_tagged_faces(const IMesh &M, bool check_Euler) +{ + return Smesh(M, M.ftag, check_Euler); } -Smesh::Smesh(const IMesh &M) +Smesh::Smesh(const IMesh &M, const std::vector &fids, bool check_Euler_) { - F.resize(M.patch.size()); - //assert(M.patch.size() == M.skin.size()); + NEAR_VERTEX_TOL = M.NEAR_VERTEX_TOL; + NEAR_EDGE_TOL = M.NEAR_EDGE_TOL; + + check_Euler = check_Euler_; + + F.resize(fids.size()); nf = 0; np = 0; // Get the faces - for (E_Int gf : M.patch) { + for (E_Int gf : fids) { g2lf[gf] = nf; l2gf[nf] = gf; - auto &face = F[nf]; + auto &pn = F[nf]; for (E_Int gp : M.F[gf]) { auto it = g2lp.find(gp); @@ -70,21 +160,24 @@ Smesh::Smesh(const IMesh &M) if (it == g2lp.end()) { g2lp[gp] = np; l2gp[np] = gp; - face.push_back(np); + pn.push_back(np); np++; } else { - face.push_back(it->second); + pn.push_back(it->second); } } nf++; } - assert(np == g2lp.size()); - assert(np == l2gp.size()); + assert((size_t)np == g2lp.size()); + assert((size_t)np == l2gp.size()); + + // Cache the original number of points and faces + np_before_adapt = np; + nf_before_adapt = nf; // Get the points - //np = g2lp.size(); X.resize(np); Y.resize(np); Z.resize(np); @@ -95,7 +188,69 @@ Smesh::Smesh(const IMesh &M) Z[pids.second] = M.Z[pids.first]; } + Fc = F; + make_edges(); + make_point_faces(); + make_point_edges(); +} + +Smesh::Smesh(const Smesh &Mf, const std::set &fids, bool check_Euler_) +{ + NEAR_VERTEX_TOL = Mf.NEAR_VERTEX_TOL; + NEAR_EDGE_TOL = Mf.NEAR_EDGE_TOL; + + check_Euler = check_Euler_; + + F.resize(fids.size()); + + nf = 0; + np = 0; + + // Get the faces + for (E_Int gf : fids) { + g2lf[gf] = nf; + l2gf[nf] = gf; + + auto &pn = F[nf]; + + for (E_Int gp : Mf.Fc[gf]) { + auto it = g2lp.find(gp); + + if (it == g2lp.end()) { + g2lp[gp] = np; + l2gp[np] = gp; + pn.push_back(np); + np++; + } else { + pn.push_back(it->second); + } + } + + nf++; + } + + assert((size_t)np == g2lp.size()); + assert((size_t)np == l2gp.size()); + + // Cache the original number of points and faces + np_before_adapt = np; + nf_before_adapt = nf; + + // Get the points + X.resize(np); + Y.resize(np); + Z.resize(np); + + for (const auto &pids : g2lp) { + X[pids.second] = Mf.X[pids.first]; + Y[pids.second] = Mf.Y[pids.first]; + Z[pids.second] = Mf.Z[pids.first]; + } + + Fc = F; make_edges(); + make_point_faces(); + make_point_edges(); } o_edge::o_edge(E_Int P, E_Int Q) @@ -117,16 +272,17 @@ struct o_edge_cmp { void Smesh::make_edges() { // Make the edges - F2E.resize(F.size()); + F2E.resize(Fc.size()); std::map edges; + assert(E.empty()); ne = 0; for (E_Int i = 0; i < nf; i++) { - auto &face = F[i]; - for (size_t j = 0; j < face.size(); j++) { - E_Int p = face[j]; - E_Int q = face[(j+1)%face.size()]; + auto &pn = Fc[i]; + for (size_t j = 0; j < pn.size(); j++) { + E_Int p = pn[j]; + E_Int q = pn[(j+1)%pn.size()]; o_edge EDGE(p, q); auto it = edges.find(EDGE); if (it == edges.end()) { @@ -140,20 +296,21 @@ void Smesh::make_edges() } } - assert(ne == E.size()); + assert((size_t)ne == E.size()); // Make edge_to_face + assert(E2F.empty()); E2F.resize(ne, {-1,-1}); std::vector count(ne, 0); for (E_Int i = 0; i < nf; i++) { - const auto &face = F2E[i]; - const auto &pn = F[i]; - assert(face.size() == pn.size()); + const auto &pe = F2E[i]; + const auto &pn = Fc[i]; + assert(pe.size() == pn.size()); - for (size_t j = 0; j < face.size(); j++) { - E_Int e = face[j]; + for (size_t j = 0; j < pe.size(); j++) { + E_Int e = pe[j]; count[e]++; if (E2F[e][0] == -1) E2F[e][0] = i; @@ -165,15 +322,24 @@ void Smesh::make_edges() } - // Check + // Check Euler formula for planar graphs + if (check_Euler) + assert(np - ne + nf + 1 == 2); + + // Check that each edge's count is 1 or 2 + for (E_Int i = 0; i < ne; i++) { + assert(count[i] == 1 || count[i] == 2); + } + + // Check consistency between E2F and F2E for (E_Int i = 0; i < ne; i++) { E_Int fi = E2F[i][0]; E_Int fj = E2F[i][1]; - const auto &face_i = F2E[fi]; + const auto &pe_i = F2E[fi]; E_Int found = false; - for (size_t j = 0; j < face_i.size(); j++) { - if (face_i[j] == (E_Int)i) { + for (size_t j = 0; j < pe_i.size(); j++) { + if (pe_i[j] == i) { found = true; break; } @@ -183,10 +349,10 @@ void Smesh::make_edges() if (fj == -1) continue; - const auto &face_j = F2E[fj]; + const auto &pe_j = F2E[fj]; found = false; - for (size_t j = 0; j < face_j.size(); j++) { - if (face_j[j] == (E_Int)i) { + for (size_t j = 0; j < pe_j.size(); j++) { + if (pe_j[j] == i) { found = true; break; } @@ -196,418 +362,33 @@ void Smesh::make_edges() } // Make faces neighbourhood + assert(F2F.empty()); F2F.resize(nf); - for (size_t i = 0; i < F2E.size(); i++) { - auto &face = F2E[i]; - auto &neis = F2F[i]; - for (size_t j = 0; j < face.size(); j++) { - E_Int e = face[j]; - if (E2F[e][0] == (E_Int)i) neis.push_back(E2F[e][1]); - else if (E2F[e][1] == (E_Int)i) neis.push_back(E2F[e][0]); - else assert(0); - } - } - - // Traverse the face list breadth-first and adjust edges accordingly - std::vector visited(nf, 0); - std::queue Q; - Q.push(0); - visited[0] = 1; - - while (!Q.empty()) { - E_Int f = Q.front(); - Q.pop(); - - assert(f != -1); - - visited[f] = 1; - - auto &neis = F2F[f]; - auto &edgs = F2E[f]; - auto &face = F[f]; - - for (size_t j = 0; j < face.size(); j++) { - E_Int nei = neis[j]; - - E_Int p = face[j]; - E_Int q = face[(j+1)%face.size()]; - - E_Int e = edgs[j]; - - if (nei == -1) { - assert(E[e].p == p); - assert(E[e].q == q); - continue; - } - - if (visited[nei]) { - assert(E2F[e][0] == nei); - assert(E2F[e][1] == f); - assert(E[e].p == q); - assert(E[e].q == p); - continue; - } - - if (E[e].p != p) { - assert(visited[nei] == 0); - assert(E[e].q == p); - assert(E[e].p == q); - std::swap(E[e].p, E[e].q); - E2F[e][0] = f; - E2F[e][1] = nei; - Q.push(nei); - } - } - } - - // Check for (E_Int i = 0; i < nf; i++) { - auto &face = F[i]; - for (size_t j = 0; j < face.size(); j++) { - E_Int e = F2E[i][j]; - E_Int p = face[j]; - E_Int q = face[(j+1)%face.size()]; - - if (E[e].p == p) { - assert(E[e].q == q); - assert(E2F[e][0] == (E_Int)i); - } else if (E[e].q == p) { - assert(E[e].p == q); - assert(E2F[e][1] == (E_Int)i); - } else { - assert(0); - } - } - } -} - -#define TRI 5 -#define QUAD 9 - -Smesh::Smesh(const char *fname) -{ - FILE *fh = fopen(fname, "r"); - assert(fh); - - char buf[256]; - - fgets(buf, 256, fh); - fgets(buf, 256, fh); - char *next = strtok(buf, " "); - next = strtok(NULL, "\n"); - - char *bad_ptr = NULL; - nf = strtod(next, &bad_ptr); - assert(*bad_ptr == '\0'); - printf("Faces: " SF_D_ "\n", nf); - - F.resize(nf); - - E_Int ret, type, dummy; - - for (E_Int i = 0; i < nf; i++) { - auto &cn = F[i]; - ret = fscanf(fh, SF_D_ " ", &type); - if (ret != 1) abort(); - if (type == TRI) { - cn.resize(3); - ret = fscanf(fh, SF_D_ SF_D_ SF_D_ SF_D_ "\n", - &cn[0], &cn[1], &cn[2], &dummy); - if (ret != 4) abort(); - } else if (type == QUAD) { - cn.resize(4); - ret = fscanf(fh, SF_D_ SF_D_ SF_D_ SF_D_ SF_D_ "\n", - &cn[0], &cn[1], &cn[2], &cn[3], &dummy); - if (ret != 5) abort(); - } else { - assert(0); - } - } - - fgets(buf, 256, fh); - next = strtok(buf, " "); - next = strtok(NULL, "\n"); - - np = strtod(next, &bad_ptr); - assert(*bad_ptr == '\0'); - printf("points: " SF_D_ "\n", np); - - X.resize(np); - Y.resize(np); - - for (E_Int i = 0; i < np; i++) { - ret = fscanf(fh, "%lf %lf " SF_D_ "\n", &X[i], &Y[i], &dummy); - if (ret != 3) abort(); - } - - fclose(fh); - - make_edges(); - - init_adaptation_data(); -} - -// Refine M0/M1 as long as one of its faces contains another face from M1/M0 - -#define NON_DEGEN 0 -#define ON_VERTEX 1 -#define ON_EDGE 2 - -bool Smesh::faces_are_dups(E_Int face, E_Int mface, const Smesh &M) -{ - const auto &pn = F[face]; - const auto &pnm = M.F[mface]; - - assert(face_is_quad(face) || face_is_tri(face)); - assert(M.face_is_quad(mface) || M.face_is_tri(mface)); - - if (pn.size() != pnm.size()) return false; - - E_Int mfound[4] = { 0, 0, 0, 0 }; - - for (size_t i = 0; i < pn.size(); i++) { - E_Int p = pn[i]; - for (size_t j = 0; j < pnm.size(); j++) { - E_Int pm = pnm[j]; - if (cmp_points(X[p], Y[p], Z[p], M.X[pm], M.Y[pm], M.Z[pm]) == 0) { - assert(mfound[i] == 0); - mfound[i] = 1; - break; - } - } - - if (mfound[i] == 0) return false; - } - - return true; -} - -std::vector Smesh::prepare_for_refinement(const std::vector &ref_data) -{ - std::vector ref_faces; - - for (size_t i = 0; i < ref_data.size(); i++) { - if (ref_data[i] > 0) ref_faces.push_back(i); - } - - // Refine the lower-level faces first - std::sort(ref_faces.begin(), ref_faces.end(), - [&] (E_Int i, E_Int j) { return flevel[i] < flevel[j]; }); - - // Resize data structures - resize_point_data(ref_faces.size()); - resize_edge_data(ref_faces.size()); - resize_face_data(ref_faces.size()); - - return ref_faces; -} - -void Smesh::resize_point_data(size_t nref_faces) -{ - size_t nnew_points = np + nref_faces * 5; - X.resize(nnew_points); - Y.resize(nnew_points); -} - -void Smesh::resize_edge_data(size_t nref_faces) -{ - size_t nnew_edges = ne + nref_faces * 12; - E.resize(nnew_edges, {-1, -1}); - E2F.resize(nnew_edges, {-1, -1}); - elevel.resize(nnew_edges, -1); -} - -void Smesh::resize_face_data(size_t nref_faces) -{ - size_t nnew_faces = nf + nref_faces * 4; - F.resize(nnew_faces); - F2E.resize(nnew_faces); - flevel.resize(nnew_faces, -1); -} - -void Smesh::refine_faces(const std::vector &ref_faces) -{ - for (E_Int ref_face : ref_faces) { - - if (face_is_tri(ref_face)) refine_tri(ref_face); - else refine_quad(ref_face); - } -} - -std::vector Smesh::smooth_ref_data(std::map> &sensor) -{ - std::vector ref_data(nf, 0); - std::stack stk; - - for (const auto &fdata : sensor) { - assert(face_is_active(fdata.first)); - ref_data[fdata.first] = 1; - stk.push(fdata.first); - } - - while (!stk.empty()) { - E_Int face = stk.top(); - stk.pop(); - - E_Int face_incr = ref_data[face] + flevel[face]; - - auto neis = get_active_neighbours(face); - - for (auto nei : neis) { - assert(nei != -1); - - E_Int nei_incr = ref_data[nei] + flevel[nei]; - - E_Int diff = abs(face_incr - nei_incr); - - if (diff <= 1) continue; - - E_Int fid = face_incr > nei_incr ? nei : face; - - ref_data[fid] += 1; - - stk.push(fid); - } - } - - return ref_data; -} - -std::vector Smesh::get_active_neighbours(E_Int face) -{ - std::vector neis; - - for (E_Int edge : F2E[face]) { - assert(face == E2F[edge][0] || face == E2F[edge][1]); - - // For now, an edge is enabled if it has no children. - // This is not necessarily true if unrefinement is considered. - auto it = echildren.find(edge); - - if (it == echildren.end()) { - assert(edge_is_active(edge)); - - E_Int nei = get_neighbour(face, edge); - - if (nei != -1) neis.push_back(nei); - } else { - // Edge was refined, get the neighbours of its children - assert(!edge_is_active(edge)); - - const auto &children = it->second; - - for (E_Int child : children) { - E_Int nei = get_neighbour(face, child); - - if (nei != -1) neis.push_back(nei); - } + const auto &pe = F2E[i]; + auto &neis = F2F[i]; + for (size_t j = 0; j < pe.size(); j++) { + E_Int e = pe[j]; + assert(E2F[e][0] == i || E2F[e][1] == i); + if (E2F[e][0] == i) neis.push_back(E2F[e][1]); + else neis.push_back(E2F[e][0]); } } - - return neis; -} - -bool Smesh::face_contains_Mface(E_Int face, E_Int mface, const Smesh &M) const -{ - // face containes mface iff it contains all its points - assert(M.face_is_active(mface)); - const auto &cn = M.F[mface]; - for (E_Int p : cn) { - if (face_contains_point(face, M.X[p], M.Y[p], M.Z[p]) == -1) - return false; - } - return true; -} - -E_Int Smesh::face_contains_point(E_Int face, E_Float x, E_Float y, E_Float z) const -{ - const auto &cn = F[face]; - - E_Int hit, a, b, c; - - if (face_is_quad(face)) { - - // First triangle - - a = cn[0], b = cn[1], c = cn[2]; - - hit = Triangle::is_point_inside(x, y, z, X[a], Y[a], Z[a], - X[b], Y[b], Z[b], X[c], Y[c], Z[c]); - - if (hit) return 0; - - // Second triangle - a = cn[0], b = cn[2], c = cn[3]; - - hit = Triangle::is_point_inside(x, y, z, X[a], Y[a], Z[a], - X[b], Y[b], Z[b], X[c], Y[c], Z[c]); - - if (hit) return 1; - } else { - - assert(face_is_tri(face)); - - a = cn[0], b = cn[1], c = cn[2]; - - hit = Triangle::is_point_inside(x, y, z, X[a], Y[a], Z[a], - X[b], Y[b], Z[b], X[c], Y[c], Z[c]); - - if (hit) return 0; - } - - return -1; } -/* -std::vector Smesh::locate(E_Float x, E_Float y, E_Float z) const +void Smesh::make_fcenters() { - E_Int a, b, c; - E_Int hit = 0; - std::vector HITS; - - for (E_Int face : factive) { - const auto &cn = F[face]; - if (cn.size() == 4) { - // First triangle - a = cn[0], b = cn[1], c = cn[2]; - hit = Triangle::is_point_inside(x, y, z, X[a], Y[a], Z[a], - X[b], Y[b], Z[b], X[c], Y[c], Z[c]); - if (hit) { - HITS.push_back(pointFace(face, 0)); - } else { - // Second triangle - a = cn[0], b = cn[2], c = cn[3]; - hit = Triangle::is_point_inside(x, y, z, X[a], Y[a], Z[a], - X[b], Y[b], Z[b], X[c], Y[c], Z[c]); - if (hit) { - HITS.push_back(pointFace(face, 1)); - } - } - } else { - a = cn[0], b = cn[1], c = cn[2]; - hit = Triangle::is_point_inside(x, y, z, X[a], Y[a], Z[a], - X[b], Y[b], Z[b], X[c], Y[c], Z[c]); - if (hit) { - HITS.push_back(pointFace(face, 0)); - } + fcenters.clear(); + fcenters.resize(3*nf, 0); + for (E_Int fid = 0; fid < nf; fid++) { + E_Float *fc = &fcenters[3*fid]; + const auto &pn = Fc[fid]; + for (E_Int p : pn) { + fc[0] += X[p]; + fc[1] += Y[p]; + fc[2] += Z[p]; } - } - - return HITS; -} -*/ - -void Smesh::init_adaptation_data() -{ - flevel.resize(nf, 0); - elevel.resize(ne, 0); - - for (E_Int i = 0; i < nf; i++) { - factive.insert(i); - } - - for (E_Int i = 0; i < ne; i++) { - eactive.insert(i); + for (E_Int i = 0; i < 3; i++) fc[i] /= pn.size(); } } @@ -616,23 +397,9 @@ void Smesh::make_point_faces() P2F.clear(); P2F.resize(np); - for (E_Int face : factive) { - const auto &cn = F[face]; - for (auto p : cn) - P2F[p].push_back(face); - } -} - -void Smesh::make_point_faces_all() -{ - assert(P2F.empty()); - P2F.clear(); - P2F.resize(np); - - for (E_Int face = 0; face < nf; face++) { - const auto &cn = F[face]; - for (auto p : cn) - P2F[p].push_back(face); + for (E_Int fid = 0; fid < nf; fid++) { + const auto &pn = Fc[fid]; + for (auto p : pn) P2F[p].push_back(fid); } } @@ -645,194 +412,15 @@ void Smesh::make_point_edges() P2E[E[eid].p].push_back(eid); P2E[E[eid].q].push_back(eid); } -} - -Smesh Smesh::extract_conformized() -{ - // Keep all the points - std::vector new_X(np), new_Y(np); - for (E_Int i = 0; i < np; i++) { - new_X[i] = X[i]; - new_Y[i] = Y[i]; - } - - // Keep the active edges and faces - std::map new_eids; - std::map new_fids; - - E_Int new_ne = 0, new_nf = 0; - - for (E_Int edge : eactive) new_eids[edge] = new_ne++; - for (E_Int face : factive) new_fids[face] = new_nf++; - - // E - std::vector new_E(new_ne, {-1, -1}); - - for (E_Int edge : eactive) { - E_Int new_eid = new_eids[edge]; - - // TODO: (p,q) -> new_pids(p,q) - new_E[new_eid].p = E[edge].p; - new_E[new_eid].q = E[edge].q; - } - - // E2F - std::vector> new_E2F(new_ne, {-1, -1}); - - for (E_Int edge : eactive) { - E_Int new_eid = new_eids[edge]; - - E_Int left = E2F[edge][0]; - E_Int right = E2F[edge][1]; - - new_E2F[new_eid][0] = new_fids[left]; - if (right != -1) new_E2F[new_eid][1] = new_fids[right]; - } - - // F2E - std::vector> new_F2E(new_nf); - - for (E_Int face : factive) { - E_Int new_fid = new_fids[face]; - - const auto &old_pe = F2E[face]; - - auto &new_pe = new_F2E[new_fid]; - - for (E_Int e : old_pe) { - if (edge_is_active(e)) { - new_pe.push_back(new_eids[e]); - } else { - const auto &children = echildren[e]; - assert(children.size() == 2); - - for (E_Int child : children) new_pe.push_back(new_eids[child]); - } - } - } - - // Check consistent E2F and F2E - /* - for (E_Int i = 0; i < new_nf; i++) { - const auto &pe = new_F2E[i]; - for (E_Int e : pe) { - assert(i == new_E2F[e][0] || i == new_E2F[e][1]); - } - } - */ - - // F - std::vector> new_F(new_nf); - - for (E_Int face = 0; face < new_nf; face++) { - const auto &pe = new_F2E[face]; - - auto &pn = new_F[face]; - - for (E_Int e : pe) { - assert(face == new_E2F[e][0] || face == new_E2F[e][1]); - if (new_E2F[e][0] == face) pn.push_back(new_E[e].p); - else pn.push_back(new_E[e].q); - } - } - - // Build and return - Smesh ret; - ret.X = new_X; - ret.Y = new_Y; - ret.E = new_E; - ret.E2F = new_E2F; - ret.F = new_F; - ret.F2E = new_F2E; - ret.np = np; - ret.ne = new_ne; - ret.nf = new_nf; - //ret.l2gp = l2gp; - //ret.factive = factive; - - return ret; -} - -void Smesh::write_faces(const char *fname, const std::vector &faces) const -{ - std::map pmap; - - E_Int np = 0; - - for (size_t i = 0; i < faces.size(); i++) { - E_Int f = faces[i]; - const auto &cn = F[f]; - for (auto p : cn) { - if (pmap.find(p) == pmap.end()) { - pmap[p] = np++; - } + for (E_Int pid = 0; pid < np; pid++) { + const auto &pe = P2E[pid]; + for (auto eid : pe) { + const auto &e = E[eid]; + assert(e.p == pid || e.q == pid); } } - std::vector ipmap(pmap.size()); - for (auto &pdata : pmap) ipmap[pdata.second] = pdata.first; - - FILE *fh = fopen(fname, "w"); - assert(fh); - - fprintf(fh, "POINTS\n"); - fprintf(fh, "%d\n", np); - for (size_t i = 0; i < ipmap.size(); i++) { - E_Int op = ipmap[i]; - fprintf(fh, "%f %f %f\n", X[op], Y[op], Z[op]); - } - - fclose(fh); -} - - -void Smesh::write_ngon(const char *fname) -{ - FILE *fh = fopen(fname, "w"); - assert(fh); - - fprintf(fh, "POINTS\n"); - fprintf(fh, SF_D_ "\n", np); - for (E_Int i = 0; i < np; i++) { - fprintf(fh, "%f %f %f\n", X[i], Y[i], Z[i]); - } - - fprintf(fh, "INDPG\n"); - fprintf(fh, SF_D_ "\n", ne+1); - size_t sizeNGon = 0; - fprintf(fh, "0 "); - for (E_Int i = 0; i < ne; i++) { - sizeNGon += 2; - fprintf(fh, "%zu ", sizeNGon); - } - fprintf(fh, "\n"); - - fprintf(fh, "NGON\n"); - fprintf(fh, "%zu\n", sizeNGon); - for (E_Int i = 0; i < ne; i++) { - fprintf(fh, SF_D_ " " SF_D_ " ", E[i].p, E[i].q); - } - fprintf(fh, "\n"); - - fprintf(fh, "INDPH\n"); - fprintf(fh, SF_D_ "\n", nf+1); - size_t sizeNFace = 0; - fprintf(fh, "0 "); - for (E_Int i = 0; i < nf; i++) { - sizeNFace += F2E[i].size(); - fprintf(fh, "%zu ", sizeNFace); - } - fprintf(fh, "\n"); - - fprintf(fh, "NFACE\n"); - fprintf(fh, "%zu\n", sizeNFace); - for (E_Int i = 0; i < nf; i++) { - for (E_Int e : F2E[i]) fprintf(fh, SF_D_ " ", e); - } - fprintf(fh, "\n"); - - fclose(fh); } void Smesh::make_fnormals() @@ -841,45 +429,33 @@ void Smesh::make_fnormals() fnormals.resize(3*nf, 0); for (E_Int fid = 0; fid < nf; fid++) { - const auto &pn = F[fid]; - - E_Float o[3] = {0, 0, 0}; - for (E_Int p : pn) { - o[0] += X[p]; - o[1] += Y[p]; - o[2] += Z[p]; - } - for (int i = 0; i < 3; i++) o[i] /= pn.size(); - - E_Int a = pn[0]; - E_Int b = pn[1]; - - E_Float oa[3] = {X[a]-o[0], Y[a]-o[1], Z[a]-o[2]}; - E_Float ob[3] = {X[b]-o[0], Y[b]-o[1], Z[b]-o[2]}; - + const auto &pn = Fc[fid]; + const E_Float *fc = &fcenters[3*fid]; + E_Int a = pn[0], b = pn[1]; + E_Float v0[3] = {X[a]-fc[0], Y[a]-fc[1], Z[a]-fc[2]}; + E_Float v1[3] = {X[b]-fc[0], Y[b]-fc[1], Z[b]-fc[2]}; E_Float *N = &fnormals[3*fid]; - K_MATH::cross(oa, ob, N); + K_MATH::cross(v0, v1, N); E_Float NORM = K_MATH::norm(N, 3); - assert(Sign(NORM) != 0); for (E_Int i = 0; i < 3; i++) N[i] /= NORM; } } void Smesh::make_pnormals() { - make_fnormals(); - pnormals.clear(); pnormals.resize(3*np, 0); // Point normals: aggregate of shared faces normals + assert(fnormals.size() == (size_t)(3*nf)); + for (E_Int pid = 0; pid < np; pid++) { - const auto &faces = P2F[pid]; + const auto &pf = P2F[pid]; E_Float *N = &pnormals[3*pid]; - for (E_Int fid : faces) { + for (E_Int fid : pf) { E_Float *fN = &fnormals[3*fid]; for (E_Int i = 0; i < 3; i++) N[i] += fN[i]; } @@ -890,68 +466,33 @@ void Smesh::make_pnormals() } } -AABB Smesh::AABB_face(const std::vector &pn) const -{ - AABB ret; - for (E_Int p : pn) { - if (X[p] > ret.xmax) ret.xmax = X[p]; - if (X[p] < ret.xmin) ret.xmin = X[p]; - if (Y[p] > ret.ymax) ret.ymax = Y[p]; - if (Y[p] < ret.ymin) ret.ymin = Y[p]; - if (Z[p] > ret.zmax) ret.zmax = Z[p]; - if (Z[p] < ret.zmin) ret.zmin = Z[p]; - } - return ret; -} - -void Smesh::make_bbox() +void Smesh::clear() { - xmin = ymin = zmin = std::numeric_limits::max(); - xmax = ymax = zmax = std::numeric_limits::min(); - - for (E_Int i = 0; i < np; i++) { - if (X[i] < xmin) xmin = X[i]; - if (Y[i] < ymin) ymin = Y[i]; - if (Z[i] < zmin) zmin = Z[i]; - if (X[i] > xmax) xmax = X[i]; - if (Y[i] > ymax) ymax = Y[i]; - if (Z[i] > zmax) zmax = Z[i]; - } + np = ne = nf = 0; + X.clear(); + Y.clear(); + Z.clear(); + P2F.clear(); + P2E.clear(); + E.clear(); + E2F.clear(); + F.clear(); + F2E.clear(); + F2F.clear(); + g2lp.clear(); + l2gp.clear(); + g2lf.clear(); + l2gf.clear(); } -void Smesh::hash_faces() +void Smesh::tag_faces(IMesh &M) const { - NX = 100; - NY = 100; - NZ = 10; - - HX = (xmax - xmin) / NX; - HY = (ymax - ymin) / NY; - HZ = (zmax - zmin) / NZ; - - NXY = NX * NY; - - fmap.clear(); + M.ftag.clear(); + M.ftag.reserve(nf); for (E_Int fid = 0; fid < nf; fid++) { - const auto &pn = F[fid]; - - AABB bbox = AABB_face(pn); - - E_Int imin = floor((bbox.xmin - xmin) / HX); - E_Int imax = floor((bbox.xmax - xmin) / HX); - E_Int jmin = floor((bbox.ymin - ymin) / HY); - E_Int jmax = floor((bbox.ymax - ymin) / HY); - E_Int kmin = floor((bbox.zmin - zmin) / HZ); - E_Int kmax = floor((bbox.zmax - zmin) / HZ); - - for (E_Int k = kmin; k < kmax+1; k++) { - for (E_Int j = jmin; j < jmax+1; j++) { - for (E_Int i = imin; i < imax+1; i++) { - E_Int voxel = i + NX*j + NXY*k; - fmap[voxel].push_back(fid); - } - } - } + E_Int gfid = l2gf.at(fid); + assert(gfid < M.nf); + M.ftag.push_back(gfid); } -} \ No newline at end of file +} diff --git a/Cassiopee/XCore/XCore/intersectMesh/smesh.h b/Cassiopee/XCore/XCore/intersectMesh/smesh.h index 8d4a7dbc2..55c2832e3 100644 --- a/Cassiopee/XCore/XCore/intersectMesh/smesh.h +++ b/Cassiopee/XCore/XCore/intersectMesh/smesh.h @@ -22,13 +22,27 @@ #include #include #include -#include -#include "point.h" #include "xcore.h" -#include "AABB.h" +#include "u_edge.h" +#include "point.h" +#include "BVH.h" + +#define LEFT 0 +#define RIGHT 1 + +struct Point3D +{ + E_Float x, y, z; +}; + +struct Point2D +{ + E_Float x, y; +}; struct IMesh; +struct AABB; struct o_edge { E_Int p, q; @@ -37,136 +51,156 @@ struct o_edge { }; struct Smesh { - E_Int np, ne, nf; + // Universal data + + E_Int np, nf; std::vector X, Y, Z; + std::vector> F; + + // Conformal data + E_Int ne; + std::vector> Fc; std::vector> P2F; std::vector> P2E; - std::vector E; std::vector> E2F; - - std::vector> F; std::vector> F2E; std::vector> F2F; - E_Int M_np; - E_Int M_ne; - E_Int M_nf; + void clear_conformal_data(); + + // Link to parent mesh std::map g2lp; std::map l2gp; - std::map g2lf; std::map l2gf; - std::map g2le; - std::map l2ge; + void reconstruct(IMesh &M); + + // Constructors + + Smesh(); + Smesh(const Smesh &Mf, const std::set &fids, bool check_Euler=true); + Smesh(const IMesh &M, const std::vector &fids, bool check_Euler=true); + static Smesh Smesh_from_point_tags(const IMesh &M, const E_Float *ptag, + bool check_Euler=true); + static Smesh Smesh_from_mesh_skin(const IMesh &M, + const std::vector &skin, bool check_Euler=true); + static Smesh Smesh_from_mesh_patch(const IMesh &M, + bool check_Euler=true); + static Smesh Smesh_from_tagged_faces(const IMesh &M, bool check_Euler); + static Smesh make_sub(const Smesh &Mf, const std::set &bfaces, + bool check_Euler); + void make_edges(); + void clear(); + void tag_faces(IMesh &M) const; + + // Geometry + std::vector fcenters; std::vector fnormals; std::vector pnormals; - - E_Int NX, NY, NZ, NXY; - E_Float xmin, xmax, ymin, ymax, zmin, zmax; - E_Float HX, HY, HZ; - - std::map> fmap; - + E_Float min_pdist = EFLOATMIN; + E_Float NEAR_VERTEX_TOL = 1e-3; + E_Float NEAR_EDGE_TOL = 1e-3; + + bool is_point_in_3D_polygon(E_Float x, E_Float y, E_Float z, E_Int fid) const; + bool is_point_on_a_polygon_edge(E_Float x, E_Float y, E_Float z, E_Int fid, + PointLoc &ploc, E_Float min_pdist) const; + bool is_point_a_polygon_vertex(E_Float x, E_Float y, E_Float z, E_Int fid, + PointLoc &ploc, E_Float min_pdist) const; + + void make_fcenters(); void make_fnormals(); void make_pnormals(); - - void make_bbox(); - void hash_faces(); - - AABB AABB_face(const std::vector &pn) const; - - // Adaptation - void get_leaves(E_Int face, std::vector &leaves) const; - - - std::map> fchildren; - std::set factive; - std::vector flevel; - - std::map> echildren; - std::set eactive; - std::vector elevel; - - Smesh() {}; - - Smesh(const char *fname); - - Smesh(const IMesh &M); - - Smesh(const IMesh &M, const std::vector &faces); - - bool ccw_oriented(E_Int face); - - void make_edges(); - void make_point_faces(); - - void make_point_faces_all(); - void make_point_edges(); - - inline bool edge_is_active(E_Int edge) const - { return eactive.find(edge) != eactive.end(); } - - inline bool face_is_active(E_Int face) const - { return factive.find(face) != factive.end(); } - - //size_t refine(Smesh &M); - - //std::vector locate(E_Float x, E_Float y, E_Float z) const; - - void write_faces(const char *fname, const std::vector &faces) const; - - void write_ngon(const char *fname); - - inline bool face_is_tri(E_Int fid) const { return F[fid].size() == 3; } - - inline bool face_is_quad(E_Int fid) const { return F[fid].size() == 4; } - - bool face_contains_Mface(E_Int face, E_Int mface, const Smesh &M) const; - - E_Int face_contains_point(E_Int face, E_Float x, E_Float y, E_Float z) const; - - std::vector smooth_ref_data(std::map> &sensor); - - std::vector prepare_for_refinement(const std::vector &ref_data); - - void refine_faces(const std::vector &ref_faces); - - std::vector get_active_neighbours(E_Int face); - - inline E_Int get_neighbour(E_Int face, E_Int edge) const + std::vector locate(const Smesh &Sf) const; + std::vector locate2(const Smesh &Sf) const; + void correct_near_points_and_edges(Smesh &Sf, std::vector &plocs); + void get_unit_projected_direction(E_Int fid, const E_Float D[3], + E_Float proj[3]) const; + void compute_min_distance_between_points(); + std::vector project(const Smesh &Mf, + const std::vector &mpids) const; + void replace_by_projections(const std::vector &pids, + const std::vector &plocs); + inline void compute_face_center(E_Int fid); + + // Hash + + E_Int NX, NY, NZ, NXY, NXYZ; + E_Float xmin, xmax, ymin, ymax, zmin, zmax; + E_Float HX, HY, HZ; + std::map> bin_faces; + + void make_bbox(); + inline void bin_face(E_Int fid); + void hash_faces(); + inline E_Int get_voxel(E_Int i, E_Int j, E_Int k) const { - assert(E2F[edge][0] == face || E2F[edge][1] == face); - return (E2F[edge][0] == face) ? E2F[edge][1] : E2F[edge][0]; + return i + NX*j + NXY*k; } - void resize_point_data(size_t nref_faces); - - void resize_edge_data(size_t nref_faces); - void resize_face_data(size_t nref_faces); + // BVH + + E_Int root_node_idx, nodes_used; + std::vector tri_idx; + std::vector bvh_nodes; + static const E_Int MAX_TRIS_PER_BVH_LEAF = 2; + void make_BVH(); + void make_BVH(const std::set &fids); + void update_node_bounds(E_Int node_idx); + void BVH_subdivide(E_Int node_idx); + void ray_intersect_BVH(E_Float ox, E_Float oy, E_Float oz, + E_Float dx, E_Float dy, E_Float dz, E_Int node_idx, + std::vector &plocs) const; - void refine_tri(E_Int tri); - - void refine_quad(E_Int quad); + // Adaptation - void refine_edge(E_Int edge); + std::map ecenter; + std::map>> fchildren; + E_Int np_before_adapt; + E_Int nf_before_adapt; - inline E_Int get_edge_center(E_Int edge) - { - assert(echildren.find(edge) != echildren.end()); - return E[echildren[edge][0]].q; - } - - void init_adaptation_data(); - + void resize_for_refinement(size_t nref_faces); + void refine(const std::vector &ref_faces); + void get_leaves(E_Int face, std::vector &leaves) const; + void refine_tri(E_Int fid); + void refine_edge(const u_edge &e); + void update_plocs(const std::vector &parents, + std::vector &plocs); + void conformize(); + void get_edge_centers(E_Int p, E_Int q, std::vector &edge_centers); + std::vector deduce_ref_faces(const std::vector &mpids, + const std::vector &plocs_m, const Smesh &Mf, + std::vector &ref_faces); + + // Topology + + bool check_Euler = true; + + std::set extract_covering_faces(const Smesh &Sf, + const std::vector &plocs) const; + E_Int deduce_face(const std::vector &pf, + E_Float ox, E_Float oy, E_Float oz, E_Float D[3], + E_Int last_vertex, E_Int last_edge, E_Int eid) const; + void get_shared_faces(const PointLoc &loc, std::vector &ret, + E_Int &pid, E_Int &eid) const; + Smesh extract_smesh(const std::set &fids, bool check_Euler=true); Smesh extract_conformized(); - bool faces_are_dups(E_Int face, E_Int mface, const Smesh &M); + // IO + + void write_points(const char *fname, const std::set &pset) const; + void write_points(const char *fname, const std::vector &pids) const; + void write_edges(const char *fname, const std::set &eids) const; + void write_ngon(const char *fname, const std::set &fset) const; + void write_ngon(const char *fname, const std::vector &faces) const; + void write_ngon(const char *fname) const; + void write_face(const char *fname, E_Int fid) const; }; + diff --git a/Cassiopee/XCore/XCore/intersectMesh/smeshRefine.cpp b/Cassiopee/XCore/XCore/intersectMesh/smeshRefine.cpp deleted file mode 100644 index dc1041482..000000000 --- a/Cassiopee/XCore/XCore/intersectMesh/smeshRefine.cpp +++ /dev/null @@ -1,308 +0,0 @@ -/* - Copyright 2013-2024 Onera. - - This file is part of Cassiopee. - - Cassiopee is free software: you can redistribute it and/or modify - it under the terms of the GNU General Public License as published by - the Free Software Foundation, either version 3 of the License, or - (at your option) any later version. - - Cassiopee is distributed in the hope that it will be useful, - but WITHOUT ANY WARRANTY; without even the implied warranty of - MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - GNU General Public License for more details. - - You should have received a copy of the GNU General Public License - along with Cassiopee. If not, see . -*/ -#include - -#include "smesh.h" - -// Refine wrt M's point cloud -/* -size_t Smesh::refine(Smesh &M) -{ - // Locate M points within my faces - std::vector> pfs; - pfs.reserve(M.np); - - for (E_Int i = 0; i < M.np; i++) { - auto pf = locate(M.X[i], M.Y[i], M.Z[i]); - pfs.push_back(pf); - } - - // Face to mpoints located within it - std::map> fpoints; - - for (size_t i = 0; i < pfs.size(); i++) { - for (const auto &faces : pfs[i]) { - E_Int F = faces.F; - fpoints[F].push_back(i); - } - } - - // Keep the faces which contain 3 points or more - std::map> filtered_faces_map; - std::vector filtered_faces; - - for (auto &fpts : fpoints) { - if (fpts.second.size() >= 3) { - filtered_faces_map.insert({fpts.first, fpts.second}); - filtered_faces.push_back(fpts.first); - } - } - - // Make point-to-face connectivity for M - M.make_point_faces(); - - std::map> ref_faces_to_Mfaces; - - for (const auto &fdata : filtered_faces_map) { - E_Int face = fdata.first; - const auto &points = fdata.second; - - // For every mface, how many points are contained within face? - std::map mcontained; - - for (E_Int p : points) { - for (E_Int mface : M.P2F[p]) { - assert(M.face_is_active(mface)); - mcontained[mface]++; - } - } - - for (const auto &mfdat : mcontained) { - E_Int mface = mfdat.first; - size_t npts = mfdat.second; - - assert(npts <= M.F[mface].size()); - - // Do not keep mface if it is not completely enclosed by face - if (npts < M.F[mface].size()) continue; - - // Do not keep mface if it is a duplicate of face - if (faces_are_dups(face, mface, M)) continue; - - ref_faces_to_Mfaces[face].push_back(mface); - } - } - - printf("Faces to refine: %zu\n", ref_faces_to_Mfaces.size()); - - filtered_faces.clear(); - - for (auto &fdata : ref_faces_to_Mfaces) { - filtered_faces.push_back(fdata.first); - } - - E_Int iter = 0; - size_t ret = 0; - - while (!ref_faces_to_Mfaces.empty()) { - iter++; - - auto ref_data = smooth_ref_data(ref_faces_to_Mfaces); - - auto ref_faces = prepare_for_refinement(ref_data); - - ret += ref_faces.size(); - - refine_faces(ref_faces); - - std::map> new_ref_faces_to_Mfaces; - - for (const auto &fdata : ref_faces_to_Mfaces) { - E_Int parent = fdata.first; - const auto &Mfaces = fdata.second; - - const auto &children = fchildren[parent]; - - for (auto child : children) { - for (E_Int mface : Mfaces) { - if (face_contains_Mface(child, mface, M)) { - assert(!faces_are_dups(child, mface, M)); - new_ref_faces_to_Mfaces[child].push_back(mface); - } - } - } - } - - ref_faces_to_Mfaces = new_ref_faces_to_Mfaces; - } - - return ret; -} -*/ - -void Smesh::refine_edge(E_Int edge) -{ - E_Int p = E[edge].p; - E_Int q = E[edge].q; - - // Rounding errors! - X[np] = 0.5 * (X[p] + X[q]); - Y[np] = 0.5 * (Y[p] + Y[q]); - - E_Int ne1 = ne + 1; - - E[ne].p = p; - E[ne].q = np; - E[ne1].p = np; - E[ne1].q = q; - - E2F[ne][0] = E2F[ne1][0] = E2F[edge][0]; - E2F[ne][1] = E2F[ne1][1] = E2F[edge][1]; - - eactive.erase(edge); - eactive.insert(ne); - eactive.insert(ne1); - - echildren[edge] = {ne, ne1}; - - elevel[ne] = elevel[ne+1] = elevel[edge] + 1; - - l2gp[np] = M_np++; - - l2ge[ne] = M_ne++; - l2ge[ne+1] = M_ne++; - - np += 1; - ne += 2; -} - -void Smesh::refine_quad(E_Int quad) -{ - // Refine the edges - const auto &pe = F2E[quad]; - assert(pe.size() == 4); - for (E_Int edge : pe) { - if (echildren.find(edge) == echildren.end()) { - refine_edge(edge); - } - } - - // Edge centers - E_Int ec[4]; - for (E_Int i = 0; i < 4; i++) { - ec[i] = get_edge_center(pe[i]); - } - - // Face centroid - const auto &pn = F[quad]; - - X[np] = Y[np] = 0.0; - for (E_Int i = 0; i < 4; i++) { - E_Int p = pn[i]; - X[np] += X[p]; - Y[np] += Y[p]; - } - X[np] *= 0.25; - Y[np] *= 0.25; - - // New face points - E_Int nf0 = nf, nf1 = nf+1, nf2 = nf+2, nf3 = nf+3; - - F[nf0] = { pn[0], ec[0], np, ec[3] }; - F[nf1] = { ec[0], pn[1], ec[1], np }; - F[nf2] = { np , ec[1], pn[2], ec[2] }; - F[nf3] = { ec[3], np , ec[2], pn[3] }; - - // Internal edge points - E_Int ne0 = ne, ne1 = ne+1, ne2 = ne+2, ne3 = ne+3; - - E[ne0].p = ec[0]; E[ne0].q = np; - E[ne1].p = ec[1]; E[ne1].q = np; - E[ne2].p = ec[2]; E[ne2].q = np; - E[ne3].p = ec[3]; E[ne3].q = np; - - // New face edges - const auto &pe0 = echildren[pe[0]]; - const auto &pe1 = echildren[pe[1]]; - const auto &pe2 = echildren[pe[2]]; - const auto &pe3 = echildren[pe[3]]; - - E_Int eid[8]; - eid[0] = pe0[0]; eid[1] = pe0[1]; - eid[2] = pe1[0]; eid[3] = pe1[1]; - eid[4] = pe2[0]; eid[5] = pe2[1]; - eid[6] = pe3[0]; eid[7] = pe3[1]; - - if (E2F[pe[0]][0] != quad) std::swap(eid[0], eid[1]); - if (E2F[pe[1]][0] != quad) std::swap(eid[2], eid[3]); - if (E2F[pe[2]][0] != quad) std::swap(eid[4], eid[5]); - if (E2F[pe[3]][0] != quad) std::swap(eid[6], eid[7]); - - F2E[nf0] = { eid[0], ne0 , ne3 , eid[7] }; - F2E[nf1] = { eid[1], eid[2], ne1 , ne0 }; - F2E[nf2] = { ne1 , eid[3], eid[4], ne2 }; - F2E[nf3] = { ne3 , ne2 , eid[5], eid[6] }; - - // External E2F - for (E_Int i = 0; i < 4; i++) { - E_Int fchild = nf + i; - const auto &pe = F2E[fchild]; - for (E_Int j = 0; j < 4; j++) { - E_Int edge = pe[j]; - if (E2F[edge][0] == quad) E2F[edge][0] = fchild; - else if (E2F[edge][1] == quad) E2F[edge][1] = fchild; - } - } - - // E_Internal E2F - E2F[ne0][0] = nf0; E2F[ne0][1] = nf1; - E2F[ne1][0] = nf1; E2F[ne1][1] = nf2; - E2F[ne2][0] = nf2; E2F[ne2][1] = nf3; - E2F[ne3][0] = nf3; E2F[ne3][1] = nf0; - - // Disable quad and enable its children - factive.erase(quad); - factive.insert(nf0); - factive.insert(nf1); - factive.insert(nf2); - factive.insert(nf3); - - // Set quad children pointers - fchildren[quad] = { nf0, nf1, nf2, nf3 }; - flevel[nf0] = flevel[nf1] = flevel[nf2] = flevel[nf3] = flevel[quad] + 1; - - // Enable internal edges - eactive.insert(ne0); - eactive.insert(ne1); - eactive.insert(ne2); - eactive.insert(ne3); - elevel[ne0] = elevel[ne1] = elevel[ne2] = elevel[ne3] = flevel[quad] + 1; - - l2gp[np] = M_np++; - - l2ge[ne0] = M_ne++; - l2ge[ne1] = M_ne++; - l2ge[ne2] = M_ne++; - l2ge[ne3] = M_ne++; - - l2gf[nf0] = M_nf++; - l2gf[nf1] = M_nf++; - l2gf[nf2] = M_nf++; - l2gf[nf3] = M_nf++; - - np += 1; - ne += 4; - nf += 4; -} - -void Smesh::refine_tri(E_Int tri) -{ - printf(SF_D_ "\n", tri); - assert(0); -} - -void Smesh::get_leaves(E_Int face, std::vector &leaves) const -{ - if (face_is_active(face)) { - leaves.push_back(face); - return; - } - - for (E_Int child : fchildren.at(face)) get_leaves(child, leaves); -} diff --git a/Cassiopee/XCore/XCore/intersectMesh/smesh_bvh.cpp b/Cassiopee/XCore/XCore/intersectMesh/smesh_bvh.cpp new file mode 100644 index 000000000..b70d867e1 --- /dev/null +++ b/Cassiopee/XCore/XCore/intersectMesh/smesh_bvh.cpp @@ -0,0 +1,279 @@ +#include "smesh.h" +#include "BVH.h" +#include "ray.h" +#include "primitives.h" + +/* +AABB Smesh::make_AABB(E_Int start, E_Int end) +{ + E_Float xmin, ymin, zmin, xmax, ymax, zmax; + xmin = ymin = zmin = EFLOATMAX; + xmax = ymax = zmax = EFLOATMIN; + + for (E_Int i = start; i < end; i++) { + E_Int fid = bvh_fids[i]; + const auto &pn = F[fid]; + for (E_Int p : pn) { + if (X[p] < xmin) xmin = X[p]; + if (Y[p] < ymin) ymin = Y[p]; + if (Z[p] < zmin) zmin = Z[p]; + if (X[p] > xmax) xmax = X[p]; + if (Y[p] > ymax) ymax = Y[p]; + if (Z[p] > zmax) zmax = Z[p]; + } + } + + E_Float dx = xmax - xmin; + E_Float dy = ymax - ymin; + E_Float dz = zmax - zmin; + + xmin -= dx * 0.01; + ymin -= dy * 0.01; + zmin -= dz * 0.01; + xmax += dx * 0.01; + ymax += dy * 0.01; + zmax += dz * 0.01; + + return {xmin, ymin, zmin, xmax, ymax, zmax}; +} +*/ + +void Smesh::make_BVH() +{ + bvh_nodes.clear(); + bvh_nodes.resize(2*nf - 1); + root_node_idx = 0, nodes_used = 1; + + tri_idx.clear(); + tri_idx.resize(nf); + for (E_Int i = 0; i < nf; i++) tri_idx[i] = i; + assert(fcenters.size() == (size_t)nf*3); + + // Assign all triangles to root node + BVH_node &root = bvh_nodes[root_node_idx]; + root.left_node = 1; + root.first_tri_idx = 0; + root.tri_count = nf; + update_node_bounds(root_node_idx); + BVH_subdivide(root_node_idx); +} + +void Smesh::update_node_bounds(E_Int node_idx) +{ + BVH_node &node = bvh_nodes[node_idx]; + node.box.xmin = node.box.ymin = node.box.zmin = EFLOATMAX; + node.box.xmax = node.box.ymax = node.box.zmax = EFLOATMIN; + for (E_Int first = node.first_tri_idx, i = 0; i < node.tri_count; i++) { + E_Int leaf_tri_idx = tri_idx[first + i]; + const auto &pn = F[leaf_tri_idx]; + for (E_Int p : pn) { + node.box.xmin = std::min(node.box.xmin, X[p]); + node.box.ymin = std::min(node.box.ymin, Y[p]); + node.box.zmin = std::min(node.box.zmin, Z[p]); + node.box.xmax = std::max(node.box.xmax, X[p]); + node.box.ymax = std::max(node.box.ymax, Y[p]); + node.box.zmax = std::max(node.box.zmax, Z[p]); + } + node.box.dx = node.box.xmax - node.box.xmin; + node.box.dy = node.box.ymax - node.box.ymin; + node.box.dz = node.box.zmax - node.box.zmin; + } +} + +void Smesh::BVH_subdivide(E_Int node_idx) +{ + // Terminate recursion + BVH_node &node = bvh_nodes[node_idx]; + if (node.tri_count <= MAX_TRIS_PER_BVH_LEAF) return; + + // Determine split axis and position + int axis; + E_Float split_pos; + if (node.box.dx > node.box.dy && node.box.dx > node.box.dz) { + axis = 0; + split_pos = node.box.xmin + node.box.dx*0.5; + } + else if (node.box.dy > node.box.dz) { + axis = 1; + split_pos = node.box.ymin + node.box.dy*0.5; + } + else { + axis = 2; + split_pos = node.box.zmin + node.box.dz*0.5; + } + + // In-place partition + E_Int i = node.first_tri_idx; + E_Int j = i + node.tri_count - 1; + while (i <= j) { + const E_Float *fc = &fcenters[3*tri_idx[i]]; + if (fc[axis] < split_pos) + i++; + else + std::swap(tri_idx[i], tri_idx[j--]); + } + + // Abort split if one of the sides is empty + E_Int left_count = i - node.first_tri_idx; + if (left_count == 0 || left_count == node.tri_count) return; + + // Create child nodes + E_Int left_child_idx = nodes_used++; + E_Int right_child_idx = nodes_used++; + bvh_nodes[left_child_idx].first_tri_idx = node.first_tri_idx; + bvh_nodes[left_child_idx].tri_count = left_count; + bvh_nodes[right_child_idx].first_tri_idx = i; + bvh_nodes[right_child_idx].tri_count = node.tri_count - left_count; + node.left_node = left_child_idx; + node.tri_count = 0; + update_node_bounds(left_child_idx); + update_node_bounds(right_child_idx); + + // Recurse + BVH_subdivide(left_child_idx); + BVH_subdivide(right_child_idx); +} + +void Smesh::make_BVH(const std::set &fids) +{ + bvh_nodes.clear(); + size_t NF = fids.size(); + bvh_nodes.resize(2*NF - 1); + root_node_idx = 0, nodes_used = 1; + + tri_idx.clear(); + tri_idx.reserve(NF); + for (E_Int fid : fids) tri_idx.push_back(fid); + + // Assign all triangles to root node + BVH_node &root = bvh_nodes[root_node_idx]; + root.left_node = 1; + root.first_tri_idx = 0; + root.tri_count = NF; + update_node_bounds(root_node_idx); + BVH_subdivide(root_node_idx); +} + +static +bool ray_intersect_AABB(E_Float ox, E_Float oy, E_Float oz, + E_Float dx, E_Float dy, E_Float dz, const AABB &box) +{ + E_Float tmin = 0; + E_Float tmax = EFLOATMAX; + + E_Float origin[3] = { ox, oy, oz }; + E_Float direction[3] = { dx, dy, dz }; + E_Float boxMin[3] = { box.xmin, box.ymin, box.zmin }; + E_Float boxMax[3] = { box.xmax, box.ymax, box.zmax }; + + for (int i = 0; i < 3; i++) { + E_Float o = origin[i]; + E_Float d = direction[i]; + E_Float bmin = boxMin[i]; + E_Float bmax = boxMax[i]; + + if (d != 0) { + E_Float t1 = (bmin - o) / d; + E_Float t2 = (bmax - o) / d; + + if (t1 > t2) { E_Float temp = t1; t1 = t2; t2 = temp; } + + tmin = (t1 > tmin) ? t1 : tmin; + tmax = (t2 < tmax) ? t2 : tmax; + + if (tmin > tmax) return false; // No intersection + } else { + if (o < bmin || o > bmax) return false; // Parallel and outside slab + } + } + + return true; + + /* + E_Float tx1 = (box.xmin - ox) / dx, tx2 = (box.xmax - ox) / dx; + E_Float tmin = std::min(tx1, tx2), tmax = std::max(tx1, tx2); + E_Float ty1 = (box.ymin - oy) / dy, ty2 = (box.ymax - oy) / dy; + tmin = std::max(tmin, std::min(ty1, ty2)); + tmax = std::min(tmax, std::max(ty1, ty2)); + E_Float tz1 = (box.zmin - oz) / dz, tz2 = (box.zmax - oz) / dz; + tmin = std::max(tmin, std::min(tz1, tz2)); + tmax = std::min(tmax, std::max(tz1, tz2)); + return tmax >= tmin; + */ +} + +void Smesh::ray_intersect_BVH(E_Float ox, E_Float oy, E_Float oz, + E_Float dx, E_Float dy, E_Float dz, E_Int node_idx, + std::vector &plocs) const +{ + const BVH_node &node = bvh_nodes[node_idx]; + if (!ray_intersect_AABB(ox, oy, oz, dx, dy, dz, node.box)) return; + if (node.is_leaf()) { + for (E_Int i = 0; i < node.tri_count; i++) { + E_Int tri = tri_idx[node.first_tri_idx+i]; + const auto &pn = Fc[tri]; + const E_Float *fc = &fcenters[3*tri]; + + for (size_t j = 0; j < pn.size(); j++) { + E_Int p = pn[j]; + E_Int q = pn[(j+1)%pn.size()]; + E_Float u, v, w, t, x, y, z; + + bool hit = MollerTrumboreAnyDir( + ox, oy, oz, dx, dy, dz, + X[p], Y[p], Z[p], + X[q], Y[q], Z[q], + fc[0], fc[1], fc[2], + u, v, w, t, x, y, z + ); + + if (hit) { + PointLoc ploc; + ploc.fid = tri; + ploc.sub = j; + ploc.bcrd[0] = u; + ploc.bcrd[1] = v; + ploc.bcrd[2] = w; + ploc.t = t; + ploc.x = x; + ploc.y = y; + ploc.z = z; + + // on p + if (Sign(1-u, NEAR_VERTEX_TOL) == 0) { + ploc.v_idx = j; + ploc.bcrd[0] = 1, ploc.bcrd[1] = 0, ploc.bcrd[2] = 0; + ploc.x = X[p]; + ploc.y = Y[p]; + ploc.z = Z[p]; + } + // on q + else if (Sign(1-v, NEAR_VERTEX_TOL) == 0) { + ploc.v_idx = (j+1)%pn.size(); + ploc.bcrd[0] = 0, ploc.bcrd[1] = 1, ploc.bcrd[2] = 0; + ploc.x = X[q]; + ploc.y = Y[q]; + ploc.z = Z[q]; + } + // on edge {p, q} + else if (Sign(w, NEAR_EDGE_TOL) == 0) { + ploc.e_idx = j; + ploc.bcrd[0] = u, ploc.bcrd[1] = 1-u, ploc.bcrd[2] = 0; + ploc.x = u*X[p] + (1-u)*X[q]; + ploc.y = u*Y[p] + (1-u)*Y[q]; + ploc.z = u*Z[p] + (1-u)*Z[q]; + } + + plocs.push_back(ploc); + + break; + } + } + } + return ; + } + + ray_intersect_BVH(ox, oy, oz, dx, dy, dz, node.left_node, plocs); + ray_intersect_BVH(ox, oy, oz, dx, dy, dz, node.left_node+1, plocs); +} + diff --git a/Cassiopee/XCore/XCore/intersectMesh/smesh_extract.cpp b/Cassiopee/XCore/XCore/intersectMesh/smesh_extract.cpp new file mode 100644 index 000000000..d02a0a19d --- /dev/null +++ b/Cassiopee/XCore/XCore/intersectMesh/smesh_extract.cpp @@ -0,0 +1,316 @@ +#include + +#include "smesh.h" +#include "primitives.h" +#include "ray.h" +#include "io.h" + +void Smesh::get_shared_faces(const PointLoc &loc, std::vector &ret, + E_Int &pid, E_Int &eid) const +{ + ret.clear(); + + E_Int fid = loc.fid; + assert(fid != -1); + + // TODO(Imad): e_idx is no good!!!! + + if (loc.e_idx != -1) { + assert(loc.v_idx == -1); + const auto &pe = F2E[fid]; + eid = pe[loc.e_idx]; + const auto &pf = E2F[eid]; + assert(pf[0] == fid || pf[1] == fid); + ret.push_back(pf[0]); + // O could be on a boundary edge + if (pf[1] != -1) ret.push_back(pf[1]); + } + else if (loc.v_idx != -1) { + assert(loc.e_idx == -1); + const auto &pn = Fc[fid]; + pid = pn[loc.v_idx]; + const auto &pf = P2F[pid]; + // For consistency + bool found_fid = false; + for (auto face : pf) { + ret.push_back(face); + if (face == fid) { + found_fid = true; + } + } + assert(found_fid == true); + } + else { + ret.push_back(fid); + } +} + +std::set ewalls; +std::set fwalls; +std::vector pchains; + +std::set Smesh::extract_covering_faces(const Smesh &Sf, + const std::vector &plocs) const +{ + // Get boundary edges from spatch + std::set bedges; + for (size_t i = 0; i < Sf.E2F.size(); i++) { + const auto &pf = Sf.E2F[i]; + assert(pf[0] != -1); + if (pf[1] == -1) bedges.insert(i); + } + size_t nbedges = bedges.size(); + + // Make the boundary point chain + std::vector pchain; + + E_Int first_edge = *bedges.begin(); + + pchain.push_back(Sf.E[first_edge].p); + pchain.push_back(Sf.E[first_edge].q); + + bedges.erase(first_edge); + + E_Int current_point = pchain[1]; + + while (pchain.size() < nbedges) { + E_Int to_delete = -1; + for (auto e : bedges) { + if (Sf.E[e].p == current_point) { + pchain.push_back(Sf.E[e].q); + current_point = pchain.back(); + to_delete = e; + break; + } else if (Sf.E[e].q == current_point) { + pchain.push_back(Sf.E[e].p); + current_point = pchain.back(); + to_delete = e; + break; + } + } + assert(to_delete != -1); + bedges.erase(to_delete); + } + + assert(pchain.size() == nbedges); + + /* + // Sort the pchain counterclockwise + E_Int a = pchain[0], b = pchain[1], c = pchain[2]; + E_Float ux = Sf.X[b] - Sf.X[a]; + E_Float uy = Sf.Y[b] - Sf.Y[a]; + E_Float uz = Sf.Z[b] - Sf.Z[a]; + E_Float vx = Sf.X[c] - Sf.X[b]; + E_Float vy = Sf.Y[c] - Sf.Y[b]; + E_Float vz = Sf.Z[c] - Sf.Z[b]; + E_Float cp[3] = {uy*vz - uz*vy, uz*vx - ux*vz, ux*vy - uy*vx}; + // TODO(Imad): inherit fnormals + const E_Float *N_b = &fnormals[3*plocs[b].fid]; + E_Float dp = K_MATH::dot(cp, N_b, 3); + E_Int cmp = Sign(dp); + assert(cmp != 0); + if (cmp < 0) std::reverse(pchain.begin(), pchain.end()); + */ + + //Sf.write_points("pchain.im", pchain); + + for (E_Int p : pchain) pchains.push_back({Sf.X[p], Sf.Y[p], Sf.Z[p]}); + + std::set wfids; + std::set weids; + + for (size_t i = 0; i < pchain.size(); i++) { + E_Int p = pchain[i]; + E_Int q = pchain[(i+1)%pchain.size()]; + + E_Float px = Sf.X[p], py = Sf.Y[p], pz = Sf.Z[p]; + E_Float qx = Sf.X[q], qy = Sf.Y[q], qz = Sf.Z[q]; + + //point_write("p", px, py, pz); + //point_write("q", qx, qy, qz); + + E_Float D[3] = {qx-px, qy-py, qz-pz}; + E_Float NORM = K_MATH::norm(D, 3); + D[0] /= NORM, D[1] /= NORM, D[2] /= NORM; + + std::vector orig_faces; + std::vector tail_faces; + + E_Int last_vertex = -1, last_edge = -1, dummy; + + get_shared_faces(plocs[p], orig_faces, last_vertex, last_edge); + get_shared_faces(plocs[q], tail_faces, dummy, dummy); + + //write_ngon("shared", orig_faces); + + E_Int starting_face = deduce_face(orig_faces, px, py, pz, + D, last_vertex, last_edge, dummy); + assert(starting_face != -1); + + bool found_tail = false; + E_Int cur_fid = starting_face; + E_Float cur_pos[3] = {px, py, pz}; + + E_Int walk = 0; + E_Int max_walks = 20; + + while (!found_tail && walk <= max_walks) { + + wfids.insert(cur_fid); + + //write_face("cur_fid", cur_fid); + + E_Float proj[3]; + get_unit_projected_direction(cur_fid, D, proj); + + const auto &pn = Fc[cur_fid]; + const auto &pe = F2E[cur_fid]; + assert(pe.size() == pn.size()); + const E_Float *fN = &fnormals[3*cur_fid]; + + // First pass: define the wall data + for (size_t i = 0; i < pn.size(); i++) { + E_Int p = pn[i]; + E_Int q = pn[(i+1)%pn.size()]; + E_Int e = pe[i]; + E_Float px = X[p], py = Y[p], pz = Z[p]; + E_Float qx = X[q], qy = Y[q], qz = Z[q]; + if (ray_point_orient(cur_pos, proj, fN, px, py, pz) <= 0 || + ray_point_orient(cur_pos, proj, fN, qx, qy, qz) <= 0) { + weids.insert(e); + } + } + + //write_edges("wall", weids); + + for (auto fid : tail_faces) { + if (fid == cur_fid) { + found_tail = true; + break; + } + } + + if (found_tail) break; + + E_Int next_fid = -1; + E_Float next_pos[3] = {EFLOATMAX, EFLOATMAX, EFLOATMAX}; + + bool hit = false; + + for (size_t i = 0; i < pn.size(); i++) { + E_Int p = pn[i]; + E_Int q = pn[(i+1)%pn.size()]; + E_Int e = pe[i]; + + + if (p == last_vertex || q == last_vertex || e == last_edge) + continue; + + E_Float px = X[p], py = Y[p], pz = Z[p]; + E_Float qx = X[q], qy = Y[q], qz = Z[q]; + + E_Float t, s; + hit = ray_edge_intersect( + cur_pos[0], cur_pos[1], cur_pos[2], + proj[0], proj[1], proj[2], + px, py, pz, qx, qy, qz, + t, s + ); + + if (hit) { + if (s > TOL && s < 1 - TOL) { + const auto &pe = F2E[cur_fid]; + E_Int eid = pe[i]; + last_edge = eid; + last_vertex = -1; + assert(E2F[eid][0] == cur_fid || E2F[eid][1] == cur_fid); + if (E2F[eid][0] == cur_fid) next_fid = E2F[eid][1]; + else next_fid = E2F[eid][0]; + + next_pos[0] = cur_pos[0] + t * proj[0]; + next_pos[1] = cur_pos[1] + t * proj[1]; + next_pos[2] = cur_pos[2] + t * proj[2]; + } else { + bool hit_p = (s <= TOL); + bool hit_q = (s >= 1 - TOL); + assert(!(hit_p && hit_q)); + last_edge = -1; + if (hit_p) last_vertex = p; + else last_vertex = q; + next_pos[0] = X[last_vertex]; + next_pos[1] = Y[last_vertex]; + next_pos[2] = Z[last_vertex]; + const auto &pf = P2F[last_vertex]; + next_fid = deduce_face(pf, + next_pos[0], next_pos[1], next_pos[2], + D, last_vertex, last_edge, dummy); + assert(next_fid != -1); + } + break; + } + } + + assert(hit); + assert(next_fid != cur_fid); + cur_fid = next_fid; + cur_pos[0] = next_pos[0]; + cur_pos[1] = next_pos[1]; + cur_pos[2] = next_pos[2]; + walk++; + } + + assert(found_tail); + assert(walk <= max_walks); + } + + //write_edges("wall", weids); + + // TODO(Imad): project wpids on best-fit plane and jarvis march + + // BFS to get the smesh mpids + std::queue Q; + for (E_Int fid : wfids) Q.push(fid); + + //write_ngon("fwall_before_BFS", wfids); + + while (!Q.empty()) { + E_Int fid = Q.front(); + Q.pop(); + + const auto &neis = F2F[fid]; + const auto &pe = F2E[fid]; + + for (size_t i = 0; i < pe.size(); i++) { + E_Int eid = pe[i]; + if (weids.find(eid) != weids.end()) continue; + E_Int nei = neis[i]; + if (wfids.find(nei) == wfids.end()) { + wfids.insert(nei); + Q.push(nei); + } + } + } + + //write_ngon("fwall_after_BFS", wfids); + + //write_ngon("fwall", wfids); + //write_edges("ewall", weids); + + for (E_Int eid : weids) ewalls.insert(eid); + for (E_Int fid : wfids) fwalls.insert(fid); + + return wfids; +} + +Smesh Smesh::extract_smesh(const std::set &fids, bool check_Euler) +{ + assert(0); + return Smesh(); +} + +Smesh Smesh::extract_conformized() +{ + assert(0); + return Smesh(); +} + diff --git a/Cassiopee/XCore/XCore/intersectMesh/smesh_geom.cpp b/Cassiopee/XCore/XCore/intersectMesh/smesh_geom.cpp new file mode 100644 index 000000000..da684c9df --- /dev/null +++ b/Cassiopee/XCore/XCore/intersectMesh/smesh_geom.cpp @@ -0,0 +1,147 @@ +#include "smesh.h" +#include "primitives.h" +#include "io.h" + +void Smesh::replace_by_projections(const std::vector &pids, + const std::vector &plocs) +{ + for (size_t i = 0; i < pids.size(); i++) { + E_Int pid = pids[i]; + const auto &ploc = plocs[pid]; + if (ploc.v_idx != -1 || ploc.e_idx != -1) { + E_Float dx = X[pid]-ploc.x; + E_Float dy = Y[pid]-ploc.y; + E_Float dz = Z[pid]-ploc.z; + E_Float d = sqrt(dx*dx + dy*dy + dz*dz); + if (d >= min_pdist/10) { + fprintf(stderr, "Tight near vertex/edge situation!\n"); + point_write("orig.im", X[pid], Y[pid], Z[pid]); + point_write("dest.im", ploc.x, ploc.y, ploc.z); + assert(0); + } + X[pid] = ploc.x; + Y[pid] = ploc.y; + Z[pid] = ploc.z; + } + } +} + +void Smesh::compute_min_distance_between_points() +{ + min_pdist = EFLOATMAX; + size_t ndists = 0; + + for (E_Int i = 0; i < np; i++) { + E_Float xi = X[i]; + E_Float yi = Y[i]; + E_Float zi = Z[i]; + for (E_Int j = i+1; j < np; j++) { + E_Float xj = X[j]; + E_Float yj = Y[j]; + E_Float zj = Z[j]; + + E_Float dx = xj-xi; + E_Float dy = yj-yi; + E_Float dz = zj-zi; + + E_Float dist = sqrt(dx*dx + dy*dy + dz*dz); + + if (dist < min_pdist) min_pdist = dist; + + ndists++; + } + } + + assert(ndists == (size_t)np*((size_t)np-1)/2); +} + +void Smesh::get_unit_projected_direction(E_Int fid, const E_Float D[3], + E_Float proj[3]) const +{ + assert(fid >= 0); + assert(fid < nf); + + // Unit normal + const E_Float *fN = &fnormals[3*fid]; + assert(Sign(K_MATH::norm(fN, 3)) != 0); + + E_Float dp = K_MATH::dot(D, fN, 3); + + proj[0] = D[0] - dp * fN[0]; + proj[1] = D[1] - dp * fN[1]; + proj[2] = D[2] - dp * fN[2]; + E_Float NORM = K_MATH::norm(proj, 3); + proj[0] /= NORM, proj[1] /= NORM, proj[2] /= NORM; +} + +E_Int Smesh::deduce_face(const std::vector &pf, + E_Float ox, E_Float oy, E_Float oz, E_Float D[3], + E_Int last_vertex, E_Int last_edge, E_Int eid) const +{ + // Intersect the projection of D with all the faces in pf + // At least one intersection must exist + // Return the face with the earliest intersection + + // For debugging + E_Int faces_hit = 0; + + E_Float t_min = EFLOATMAX; + E_Int ret_face = -1; + + for (auto fid : pf) { + + // Compute the unit projection of D on this face + + E_Float proj[3]; + get_unit_projected_direction(fid, D, proj); + + const auto &pn = Fc[fid]; + const auto &pe = F2E[fid]; + assert(pn.size() == pe.size()); + + for (size_t i = 0; i < pn.size(); i++) { + + E_Int p = pn[i]; + E_Int q = pn[(i+1)%pn.size()]; + E_Int e = pe[i]; + + if (p == last_vertex || q == last_vertex || e == last_edge) + continue; + + E_Float t, s; + + //if (eid == 133) { + // point_write("p.im", X[p], Y[p], Z[p]); + // point_write("q.im", X[q], Y[q], Z[q]); + //} + + bool hit = ray_edge_intersect(ox, oy, oz, + proj[0], proj[1], proj[2], + X[p], Y[p], Z[p], + X[q], Y[q], Z[q], + t, s + ); + + if (hit) { + faces_hit += 1; + + if (t < t_min) { + t_min = t; + ret_face = fid; + } + + // Hit an edge of the face, stop + break; + } + } + } + + // We must have hit a face + if (faces_hit == 0) { + write_ngon("pf.im", pf); + edge_write("ray.im", ox, oy, oz, ox+D[0], oy+D[1], oz+D[2]); + } + assert(faces_hit > 0); + + return ret_face; +} diff --git a/Cassiopee/XCore/XCore/intersectMesh/smesh_io.cpp b/Cassiopee/XCore/XCore/intersectMesh/smesh_io.cpp new file mode 100644 index 000000000..737b35e24 --- /dev/null +++ b/Cassiopee/XCore/XCore/intersectMesh/smesh_io.cpp @@ -0,0 +1,249 @@ +#include "smesh.h" + +void Smesh::write_edges(const char *fname, const std::set &eids) const +{ + FILE *fh = fopen(fname, "w"); + assert(fh); + + fprintf(fh, "POINTS\n"); + fprintf(fh, "%lu\n", eids.size()*2); + for (E_Int eid : eids) { + const auto &e = E[eid]; + E_Int p = e.p; + E_Int q = e.q; + fprintf(fh, "%f %f %f\n", X[p], Y[p], Z[p]); + fprintf(fh, "%f %f %f\n", X[q], Y[q], Z[q]); + } + fprintf(fh, "EDGES\n"); + fprintf(fh, "%lu\n", eids.size()); + for (size_t i = 0; i < 2*eids.size(); i++) { + fprintf(fh, "%lu ", i); + } + fprintf(fh, "\n"); + + fclose(fh); +} + +void Smesh::write_points(const char *fname, const std::vector &pids) const +{ + FILE *fh= fopen(fname, "w"); + assert(fh); + + fprintf(fh, "POINTS\n"); + fprintf(fh, "%lu\n", pids.size()); + for (E_Int pid : pids){ + fprintf(fh, "%f %f %f\n", X[pid], Y[pid], Z[pid]); + } + + fclose(fh); +} + +void Smesh::write_points(const char *fname, const std::set &pids) const +{ + FILE *fh= fopen(fname, "w"); + assert(fh); + + fprintf(fh, "POINTS\n"); + fprintf(fh, "%lu\n", pids.size()); + for (E_Int pid : pids){ + fprintf(fh, "%f %f %f\n", X[pid], Y[pid], Z[pid]); + } + + fclose(fh); +} + +void Smesh::write_ngon(const char *fname, const std::set &fset) const +{ + std::vector flist; + flist.reserve(fset.size()); + for (E_Int fid : fset) flist.push_back(fid); + write_ngon(fname, flist); +} + +void Smesh::write_ngon(const char *fname, const std::vector &faces) const +{ + std::vector INDPH(faces.size() + 1); + INDPH[0] = 0; + + std::map new_pids; + std::map new_eids; + E_Int idx = 0; + E_Int NP = 0; + E_Int NE = 0; + E_Int NF = (E_Int)faces.size(); + + for (E_Int fid : faces) { + const auto &pn = Fc[fid]; + const auto &pe = F2E[fid]; + + INDPH[idx+1] = INDPH[idx] + (E_Int)pn.size(); + idx++; + + for (E_Int pid : pn) { + if (new_pids.find(pid) == new_pids.end()) { + new_pids[pid] = NP; + NP++; + } + } + + for (E_Int eid : pe) { + if (new_eids.find(eid) == new_eids.end()) { + new_eids[eid] = NE; + NE++; + } + } + } + + FILE *fh = fopen(fname, "w"); + assert(fh); + fprintf(fh, "POINTS\n"); + fprintf(fh, "%lu\n", new_pids.size()); + + std::vector nX(NP), nY(NP), nZ(NP); + for (const auto &pids : new_pids) { + E_Int opid = pids.first; + E_Int npid = pids.second; + nX[npid] = X[opid]; + nY[npid] = Y[opid]; + nZ[npid] = Z[opid]; + } + + for (E_Int pid = 0; pid < NP; pid++) { + fprintf(fh, "%f %f %f\n", nX[pid], nY[pid], nZ[pid]); + } + + fprintf(fh, "INDPG\n"); + fprintf(fh, "%lu\n", new_eids.size()+1); + E_Int sizeNGon = -2; + for (size_t i = 0; i < new_eids.size() + 1; i++) { + sizeNGon += 2; + fprintf(fh, "%d ", sizeNGon); + } + fprintf(fh, "\n"); + assert(sizeNGon == 2*NE); + + std::vector nE(new_eids.size(), {-1, -1}); + for (const auto &eids : new_eids) { + E_Int oeid = eids.first; + E_Int neid = eids.second; + nE[neid].p = new_pids[E[oeid].p]; + nE[neid].q = new_pids[E[oeid].q]; + } + + fprintf(fh, "NGON\n"); + fprintf(fh, "%d\n", 2*NE); + for (const auto &e : nE) { + fprintf(fh, "%d %d ", e.p, e.q); + } + fprintf(fh, "\n"); + + fprintf(fh, "INDPH\n"); + fprintf(fh, "%d\n", NF+1); + for (E_Int i = 0; i < NF+1; i++) + fprintf(fh, "%d ", INDPH[i]); + fprintf(fh, "\n"); + + fprintf(fh, "NFACE\n"); + fprintf(fh, "%d\n", INDPH[NF]); + for (size_t i = 0; i < faces.size(); i++) { + const auto &pe = F2E[faces[i]]; + for (E_Int eid : pe) { + fprintf(fh, "%d ", new_eids[eid]); + } + } + fprintf(fh, "\n"); + + fclose(fh); +} + +void Smesh::write_face(const char *fname, E_Int fid) const +{ + FILE *fh = fopen(fname, "w"); + assert(fh); + + const auto &pn = Fc[fid]; + E_Int np = (size_t)pn.size(); + + fprintf(fh, "POINTS\n"); + fprintf(fh, "%d\n", np); + for (E_Int p : pn) { + fprintf(fh, "%f %f %f\n", X[p], Y[p], Z[p]); + } + + fprintf(fh, "INDPG\n"); + fprintf(fh, "%d\n", np + 1); + E_Int sizeNGon = -2; + for (E_Int i = 0; i < np+1; i++) { + sizeNGon += 2; + fprintf(fh, "%d ", sizeNGon); + } + fprintf(fh, "\n"); + + fprintf(fh, "NGON\n"); + fprintf(fh, "%d\n", 2*np); + for (E_Int i = 0; i < np; i++) { + fprintf(fh, "%d %d ", i, (i+1)%np); + } + fprintf(fh, "\n"); + + fprintf(fh, "INDPH\n"); + fprintf(fh, "%d\n", 2); + fprintf(fh, "0 %d\n", np); + + fprintf(fh, "NFACE\n"); + fprintf(fh, "%d\n", np); + for (E_Int i = 0; i < np; i++) fprintf(fh, "%d ", i); + + fclose(fh); +} + +void Smesh::write_ngon(const char *fname) const +{ + FILE *fh = fopen(fname, "w"); + assert(fh); + + fprintf(fh, "POINTS\n"); + fprintf(fh, SF_D_ "\n", np); + for (E_Int i = 0; i < np; i++) { + fprintf(fh, "%f %f %f\n", X[i], Y[i], Z[i]); + } + + fprintf(fh, "INDPG\n"); + fprintf(fh, SF_D_ "\n", ne+1); + size_t sizeNGon = 0; + fprintf(fh, "0 "); + for (E_Int i = 0; i < ne; i++) { + sizeNGon += 2; + fprintf(fh, "%zu ", sizeNGon); + } + fprintf(fh, "\n"); + + fprintf(fh, "NGON\n"); + fprintf(fh, "%zu\n", sizeNGon); + for (E_Int i = 0; i < ne; i++) { + fprintf(fh, SF_D_ " " SF_D_ " ", E[i].p, E[i].q); + } + fprintf(fh, "\n"); + + fprintf(fh, "INDPH\n"); + fprintf(fh, SF_D_ "\n", nf+1); + size_t sizeNFace = 0; + fprintf(fh, "0 "); + for (E_Int i = 0; i < nf; i++) { + sizeNFace += F2E[i].size(); + fprintf(fh, "%zu ", sizeNFace); + } + fprintf(fh, "\n"); + + fprintf(fh, "NFACE\n"); + fprintf(fh, "%zu\n", sizeNFace); + for (E_Int i = 0; i < nf; i++) { + for (E_Int e : F2E[i]) fprintf(fh, SF_D_ " ", e); + } + fprintf(fh, "\n"); + + fclose(fh); +} + + + diff --git a/Cassiopee/XCore/XCore/intersectMesh/smesh_locate.cpp b/Cassiopee/XCore/XCore/intersectMesh/smesh_locate.cpp new file mode 100644 index 000000000..d673bbe68 --- /dev/null +++ b/Cassiopee/XCore/XCore/intersectMesh/smesh_locate.cpp @@ -0,0 +1,433 @@ +#include "smesh.h" +#include "triangle.h" +#include "primitives.h" +#include "io.h" + +bool is_point_in_2D_polygon(Point2D point, Point2D *polygon, int num_points) +{ + bool inside = false; + + for (int i = 0, j = num_points-1; i < num_points; j = i++) { + if (((polygon[i].y > point.y) != (polygon[j].y > point.y)) && + (point.x < (polygon[j].x - polygon[i].x) * (point.y - polygon[i].y) / + (polygon[j].y - polygon[i].y) + polygon[i].x)) { + inside = !inside; + } + } + return inside; +} + +Point2D project_to_2D(Point3D point, const E_Float *N) +{ + Point2D projected; + + if (fabs(N[2]) > fabs(N[0]) && fabs(N[2]) > fabs(N[1])) { + projected.x = point.x; + projected.y = point.y; + } else if (fabs(N[1]) > fabs(N[0])) { + projected.x = point.x; + projected.y = point.z; + } else { + projected.x = point.y; + projected.y = point.z; + } + + return projected; +} + +#define MAX_PTS 100 + +bool Smesh::is_point_in_3D_polygon(E_Float x, E_Float y, E_Float z, E_Int fid) const +{ + const auto &pn = Fc[fid]; + const auto *fN = &fnormals[3*fid]; + int a = pn[0]; + E_Float ap[3] = {x-X[a], y-Y[a], z-Z[a]}; + E_Float dp = fabs(K_MATH::dot(ap, fN, 3)); + if (dp > TOL) return false; + + assert(pn.size() <= MAX_PTS); + Point2D projected_polygon[MAX_PTS]; + for (size_t i = 0; i < pn.size(); i++) { + Point3D p = {X[pn[i]], Y[pn[i]], Z[pn[i]]}; + projected_polygon[i] = project_to_2D(p, fN); + } + Point2D projected_point = project_to_2D({x, y, z}, fN); + + + return is_point_in_2D_polygon(projected_point, projected_polygon, pn.size()); +} + +bool Smesh::is_point_a_polygon_vertex(E_Float x, E_Float y, E_Float z, + E_Int fid, PointLoc &ploc, E_Float min_pdist) const +{ + const auto &pn = Fc[fid]; + + for (size_t i = 0; i < pn.size(); i++) { + E_Int p = pn[i]; + E_Float dist = (x-X[p])*(x-X[p]) + (y-Y[p])*(y-Y[p]) + (z-Z[p])*(z-Z[p]); + dist = sqrt(dist); + if (dist < min_pdist/10) { + ploc.v_idx = i; + ploc.x = X[p]; + ploc.y = Y[p]; + ploc.z = Z[p]; + return true; + } + } + + return false; +} + +bool Smesh::is_point_on_a_polygon_edge(E_Float x, E_Float y, E_Float z, + E_Int fid, PointLoc &ploc, E_Float min_pdist) const +{ + const auto &pn = Fc[fid]; + + for (size_t i = 0; i < pn.size(); i++) { + E_Int a = pn[i]; + E_Int b = pn[(i+1)%pn.size()]; + + // Check collinearity of ap with ab + E_Float ap[3] = {x-X[a], y-Y[a], z-Z[a]}; + E_Float ab[3] = {X[b]-X[a], Y[b]-Y[a], Z[b]-Z[a]}; + E_Float C[3]; + K_MATH::cross(ap, ab, C); + if (fabs(K_MATH::norm(C, 3)) > TOL) continue; + E_Float dp1 = K_MATH::dot(ap, ab, 3); + E_Float dp2 = K_MATH::dot(ab, ab, 3); + if (dp1 >= 0 && dp1 <= dp2) { + ploc.e_idx = i; + E_Float t; + E_Float dx = fabs(X[a]-X[b]); + E_Float dy = fabs(Y[a]-Y[b]); + E_Float dz = fabs(Z[a]-Z[b]); + if (dx > dy && dx > dz) t = (x-X[a])/(X[b]-X[a]); + else if (dy > dz) t = (y-Y[a])/(Y[b]-Y[a]); + else t = (z-Z[a])/(Z[b]-Z[a]); + ploc.x = X[a] + t * (X[b]-X[a]); + ploc.y = Y[a] + t * (Y[b]-Y[a]); + ploc.z = Z[a] + t * (Z[b]-Z[a]); + return true; + } + } + + return false; +} + +std::vector Smesh::locate2(const Smesh &Sf) const +{ + std::vector plocs(Sf.np); + + size_t on_vertex = 0, on_edge = 0; + + //std::vector oedge, dedge, vpoints; + + for (E_Int pid = 0; pid < Sf.np; pid++) { + E_Float x = Sf.X[pid]; + E_Float y = Sf.Y[pid]; + E_Float z = Sf.Z[pid]; + + E_Int I = floor((x - xmin) / HX); + E_Int J = floor((y - ymin) / HY); + E_Int K = floor((z - zmin) / HZ); + E_Int voxel = get_voxel(I, J, K); + + const auto &pf = bin_faces.at(voxel); + + //if (pid == 332) write_ngon("pf.im", pf); + + bool found = false; + + auto &ploc = plocs[pid]; + + for (auto fid : pf) { + found = is_point_in_3D_polygon(x, y, z, fid); + + if (found) { + + //if (pid == 332) write_face("fid.im", fid); + + ploc.fid = fid; + + if (is_point_a_polygon_vertex(x, y, z, fid, ploc, Sf.min_pdist)) { + on_vertex++; + //vpoints.push_back({x, y, z}); + } else if (is_point_on_a_polygon_edge(x, y, z, fid, ploc, Sf.min_pdist)) { + //oedge.push_back({x, y, z}); + //dedge.push_back({ploc.x, ploc.y, ploc.z}); + on_edge++; + } + + break; + } + } + + if (!found) { + fprintf(stderr, "Couldn't locate point %d\n", pid); + point_write("lost.im", x, y, z); + write_ngon("bin.im", pf); + } + + assert(found); + } + + printf("on vertex: %lu\n", on_vertex); + printf("on edge: %lu\n", on_edge); + + //point_write("oedge.im", oedge); + //point_write("dedge.im", dedge); + //point_write("vpoints.im", vpoints); + + return plocs; +} + +std::vector Smesh::locate(const Smesh &Sf) const +{ + std::vector ploc(Sf.np); + + for (E_Int pid = 0; pid < Sf.np; pid++) { + + E_Float x = Sf.X[pid]; + E_Float y = Sf.Y[pid]; + E_Float z = Sf.Z[pid]; + + E_Int I = floor((x - xmin) / HX); + E_Int J = floor((y - ymin) / HY); + E_Int K = floor((z - zmin) / HZ); + E_Int voxel = get_voxel(I, J, K); + + const auto &pf = bin_faces.at(voxel); + + bool found = false; + + auto &loc = ploc[pid]; + + for (size_t i = 0; i < pf.size() && !found; i++) { + E_Int fid = pf[i]; + const auto &pn = Fc[fid]; + const E_Float *fc = &fcenters[3*fid]; + + if (pid == 276) { + point_write("fc.im", fc[0], fc[1], fc[2]); + } + + for (size_t j = 0; j < pn.size(); j++) { + E_Int p = pn[j]; + E_Int q = pn[(j+1)%pn.size()]; + + if (pid == 276) { + point_write("p.im", X[p], Y[p], Z[p]); + point_write("q.im", X[q], Y[q], Z[q]); + } + + E_Float u, v, w; + + found = Triangle::is_point_inside(x, y, z, + X[p], Y[p], Z[p], + X[q], Y[q], Z[q], + fc[0], fc[1], fc[2], + u, v, w + ); + + if (found) { + loc.fid = fid; + loc.sub = j; + loc.bcrd[0] = u; + loc.bcrd[1] = v; + loc.bcrd[2] = w; + + /* + if (pid == 107) { + printf("fid: %d - size: %d\n", fid, pn.size()); + printf("u: %f - v: %f - w: %f\n", u, v, w); + printf("%f %f %f\n", fc[0], fc[1], fc[2]); + } + */ + + // on p + if (Sign(1-u, NEAR_VERTEX_TOL) == 0) { + loc.v_idx = j; + loc.x = X[p]; loc.y = Y[p]; loc.z = Z[p]; + loc.bcrd[0] = 1; loc.bcrd[1] = 0; loc.bcrd[2] = 0; + } + // on q + else if (Sign(1-v, NEAR_VERTEX_TOL) == 0) { + loc.v_idx = (j+1)%pn.size(); + loc.x = X[q]; loc.y = Y[q]; loc.z = Z[q]; + loc.bcrd[0] = 0; loc.bcrd[1] = 1; loc.bcrd[2] = 0; + } + // on edge {p, q} + else if (Sign(w, NEAR_EDGE_TOL) == 0) { + loc.e_idx = j; + loc.x = u*X[p] + (1-u)*X[q]; + loc.y = u*Y[p] + (1-u)*Y[q]; + loc.z = u*Z[p] + (1-u)*Z[q]; + loc.bcrd[1] = 1-u; loc.bcrd[2] = 0; + } + + break; + } + } + } + + if (!found) { + point_write("lost", x, y, z); + write_ngon("bin", pf); + } + + assert(found); + } + + return ploc; +} + +/* +void Smesh::correct_near_points_and_edges(Smesh &Sf, + std::vector &plocs) +{ + E_Int on_vertex = 0, on_edge = 0; + for (size_t i = 0; i < plocs.size(); i++) { + auto &ploc = plocs[i]; + + E_Int fid = ploc.fid; + assert(fid != -1); + const auto &pn = Fc[fid]; + + if (ploc.v_idx != -1) { + on_vertex++; + E_Int p = pn[ploc.v_idx]; + E_Float dx = X[p]-Sf.X[i]; + E_Float dy = Y[p]-Sf.Y[i]; + E_Float dz = Z[p]-Sf.Z[i]; + E_Float dist = dx*dx + dy*dy + dz*dz; + if (dist >= Sf.min_pdist_squared) { + fprintf(stderr, "Tight near-vertex situation!\n"); + point_write("mpoint", X[p], Y[p], Z[p]); + point_write("spoint", Sf.X[i], Sf.Y[i], Sf.Z[i]); + assert(0); + } else { + Sf.X[i] = X[p]; + Sf.Y[i] = Y[p]; + Sf.Z[i] = Z[p]; + } + } else if (ploc.e_idx != -1) { + on_edge++; + E_Float u = ploc.bcrd[0]; + E_Float v = ploc.bcrd[1]; + E_Float w = ploc.bcrd[2]; + assert(Sign(w, NEAR_EDGE_TOL) == 0); + v = 1 - u, w = 0; + + E_Int p = pn[ploc.e_idx]; + E_Int q = pn[(ploc.e_idx+1)%pn.size()]; + E_Float new_x = u*X[p] + v*X[q]; + E_Float new_y = u*Y[p] + v*Y[q]; + E_Float new_z = u*Z[p] + v*Z[q]; + + E_Float dx = new_x-Sf.X[i]; + E_Float dy = new_y-Sf.Y[i]; + E_Float dz = new_z-Sf.Z[i]; + E_Float dist = dx*dx + dy*dy + dz*dz; + if (dist >= Sf.min_pdist_squared) { + fprintf(stderr, "Tight near-edge situation!\n"); + point_write("mpoint", X[p], Y[p], Z[p]); + point_write("spoint", Sf.X[i], Sf.Y[i], Sf.Z[i]); + assert(0); + } else { + ploc.bcrd[1] = v; ploc.bcrd[2] = 0; + + Sf.X[i] = new_x; + Sf.Y[i] = new_y; + Sf.Z[i] = new_z; + } + } + } + printf("on vertex: %d - on edge: %d\n", on_vertex, on_edge); +} +*/ + +void Smesh::make_bbox() +{ + NX = 100; + NY = 100; + NZ = 100; + NXY = NX * NY; + NXYZ = NXY * NZ; + + xmin = ymin = zmin = EFLOATMAX; + xmax = ymax = zmax = EFLOATMIN; + + for (E_Int i = 0; i < np; i++) { + if (X[i] < xmin) xmin = X[i]; + if (Y[i] < ymin) ymin = Y[i]; + if (Z[i] < zmin) zmin = Z[i]; + if (X[i] > xmax) xmax = X[i]; + if (Y[i] > ymax) ymax = Y[i]; + if (Z[i] > zmax) zmax = Z[i]; + } + + E_Float dx = xmax - xmin; + E_Float dy = ymax - ymin; + E_Float dz = zmax - zmin; + + xmin = xmin - dx*0.01; + ymin = ymin - dy*0.01; + zmin = zmin - dz*0.01; + xmax = xmax + dx*0.01; + ymax = ymax + dy*0.01; + zmax = zmax + dz*0.01; + + HX = (dx != 0) ? (xmax - xmin) / NX : 1; + HY = (dy != 0) ? (ymax - ymin) / NY : 1; + HZ = (dz != 0) ? (zmax - zmin) / NZ : 1; +} + +inline +void Smesh::bin_face(E_Int fid) +{ + const auto &pn = Fc[fid]; + + E_Int Imin, Jmin, Kmin; + E_Int Imax, Jmax, Kmax; + + Imin = Jmin = Kmin = NXYZ; + Imax = Jmax = Kmax = -1; + + for (E_Int p : pn) { + E_Float x = X[p]; + E_Float y = Y[p]; + E_Float z = Z[p]; + + E_Int I = floor((x - xmin) / HX); + E_Int J = floor((y - ymin) / HY); + E_Int K = floor((z - zmin) / HZ); + + if (I < Imin) Imin = I; + if (J < Jmin) Jmin = J; + if (K < Kmin) Kmin = K; + if (I > Imax) Imax = I; + if (J > Jmax) Jmax = J; + if (K > Kmax) Kmax = K; + } + + for (E_Int I = Imin; I <= Imax; I++) { + for (E_Int J = Jmin; J <= Jmax; J++) { + for (E_Int K = Kmin; K <= Kmax; K++) { + E_Int voxel = get_voxel(I, J, K); + assert(voxel >= 0); + assert(voxel < NXYZ); + bin_faces[voxel].push_back(fid); + } + } + } +} + +void Smesh::hash_faces() +{ + bin_faces.clear(); + + for (E_Int fid = 0; fid < nf; fid++) { + bin_face(fid); + } +} diff --git a/Cassiopee/XCore/XCore/intersectMesh/smesh_reconstruct.cpp b/Cassiopee/XCore/XCore/intersectMesh/smesh_reconstruct.cpp new file mode 100644 index 000000000..256168170 --- /dev/null +++ b/Cassiopee/XCore/XCore/intersectMesh/smesh_reconstruct.cpp @@ -0,0 +1,214 @@ +#include "smesh.h" +#include "mesh.h" +#include "io.h" + +void Smesh::get_leaves(E_Int fid, std::vector &leaves) const +{ + leaves.push_back(fid); + auto it = fchildren.find(fid); + if (it == fchildren.end()) return; + + const auto &children_lists = it->second; + for (const auto &children_list : children_lists) { + for (E_Int child : children_list) + get_leaves(child, leaves); + } +} + +void Smesh::reconstruct(IMesh &M) +{ + // POINTS + + E_Int NP = M.np + (np - np_before_adapt); + + M.X.resize(NP); + M.Y.resize(NP); + M.Z.resize(NP); + + NP = M.np; + + std::vector points; + + for (E_Int pid = np_before_adapt; pid < np; pid++) { + assert(l2gp.find(pid) == l2gp.end()); + l2gp[pid] = NP; + g2lp[NP] = pid; + + M.X[NP] = X[pid]; + M.Y[NP] = Y[pid]; + M.Z[NP] = Z[pid]; + + points.push_back({M.X[NP], M.Y[NP], M.Z[NP]}); + + NP++; + } + + // Isolate the smesh cells + + std::set conf_cells; + + for (E_Int fid = 0; fid < nf_before_adapt; fid++) { + E_Int own = M.owner[l2gf.at(fid)]; + assert(M.neigh[l2gf.at(fid)] == -1); + conf_cells.insert(own); + } + + // Isolate the faces to conformize + std::set conf_faces; + + for (E_Int cid : conf_cells) { + const auto &pf = M.C[cid]; + for (E_Int fid : pf) { + if (g2lf.find(fid) == g2lf.end()) conf_faces.insert(fid); + } + } + + // Delete from owners the smesh faces + + for (E_Int cid : conf_cells) { + const auto &pf = M.C[cid]; + std::vector PF; + for (E_Int fid : pf) { + if (g2lf.find(fid) == g2lf.end()) PF.push_back(fid); + } + M.C[cid] = PF; + } + + // FACES + + E_Int NF = M.nf + (nf - nf_before_adapt); + + M.F.resize(NF); + M.owner.resize(NF); + M.neigh.resize(NF, -1); + + NF = M.nf; + + for (E_Int fid = nf_before_adapt; fid < nf; fid++) { + assert(l2gf.find(fid) == l2gf.end()); + l2gf[fid] = NF; + assert(g2lf.find(NF) == g2lf.end()); + g2lf[NF] = fid; + NF++; + } + + // Update old faces and add new faces + + for (E_Int fid = 0; fid < nf; fid++) { + std::vector PN(Fc[fid]); + for (auto &pid : PN) pid = l2gp.at(pid); + + E_Int gfid = l2gf.at(fid); + assert(M.neigh[gfid] == -1); + + M.F[gfid] = PN; + } + + + for (E_Int fid : conf_faces) { + auto it = g2lf.find(fid); + assert (it == g2lf.end()); + + const auto &pn = M.F[fid]; + + std::vector new_pn; + + for (size_t i = 0; i < pn.size(); i++) { + E_Int gp = pn[i]; + E_Int gq = pn[(i+1)%pn.size()]; + + auto itp = g2lp.find(gp); + auto itq = g2lp.find(gq); + + if (itp == g2lp.end() || itq == g2lp.end()) { + new_pn.push_back(gp); + continue; + } + + E_Int lp = itp->second; + E_Int lq = itq->second; + + std::vector local; + get_edge_centers(lp, lq, local); + for (auto p : local) new_pn.push_back(l2gp.at(p)); + } + + M.F[fid] = new_pn; + } + + // Add skin faces leaves to owners + + for (E_Int fid = 0; fid < nf_before_adapt; fid++) { + std::vector leaves; + get_leaves(fid, leaves); + + // Parent element to update + E_Int gfid = l2gf.at(fid); + assert(M.owner[gfid] != -1); + assert(M.neigh[gfid] == -1); + E_Int own = M.owner[gfid]; + assert(conf_cells.find(own) != conf_cells.end()); + auto &pf = M.C[own]; + + // Add the children + for (E_Int leaf : leaves) { + E_Int gleaf = l2gf.at(leaf); + pf.push_back(gleaf); + + M.owner[gleaf] = own; + M.neigh[gleaf] = -1; + } + } + + /* + { + if (M.nc > 24) { + auto pf = M.C[24]; + std::map local_map; + int npts = 0; + int i = 0; + for (E_Int fid : pf) { + const auto &pn = M.F[fid]; + printf("face%d: ", i); + for (E_Int p : pn) { + auto it = local_map.find(p); + if (it == local_map.end()) { + local_map[p] = npts; + printf("%d ", npts); + + if (npts == 10) { + point_write("10.im", M.X[p], M.Y[p], M.Z[p]); + } + + if (npts == 11) { + point_write("11.im", M.X[p], M.Y[p], M.Z[p]); + } + + if (npts == 12) { + point_write("12.im", M.X[p], M.Y[p], M.Z[p]); + } + + if (npts == 13) { + point_write("13.im", M.X[p], M.Y[p], M.Z[p]); + } + + if (npts == 14) { + point_write("14.im", M.X[p], M.Y[p], M.Z[p]); + } + + npts++; + } else { + printf("%d ", it->second); + } + } + puts(""); + i++; + } + + } + } + */ + + M.np = NP; + M.nf = NF; +} \ No newline at end of file diff --git a/Cassiopee/XCore/XCore/intersectMesh/smesh_refine.cpp b/Cassiopee/XCore/XCore/intersectMesh/smesh_refine.cpp new file mode 100644 index 000000000..815c22cba --- /dev/null +++ b/Cassiopee/XCore/XCore/intersectMesh/smesh_refine.cpp @@ -0,0 +1,116 @@ +/* + Copyright 2013-2024 Onera. + + This file is part of Cassiopee. + + Cassiopee is free software: you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation, either version 3 of the License, or + (at your option) any later version. + + Cassiopee is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with Cassiopee. If not, see . +*/ +#include "smesh.h" + +void Smesh::update_plocs(const std::vector &fparents, + std::vector &plocs) +{ + // Children to update: fchildren[fparent].back(); + assert(0); +} + +void Smesh::refine_edge(const u_edge &e) +{ + E_Int p = e.p; + E_Int q = e.q; + + X[np] = 0.5 * (X[p] + X[q]); + Y[np] = 0.5 * (Y[p] + Y[q]); + Z[np] = 0.5 * (Z[p] + Z[q]); + + ecenter[e] = np; + np++; +} + +inline +void Smesh::compute_face_center(E_Int fid) +{ + E_Float *fc = &fcenters[3*fid]; + fc[0] = fc[1] = fc[2] = 0; + const auto &pn = Fc[fid]; + for (E_Int p : pn) { + fc[0] += X[p]; + fc[1] += Y[p]; + fc[2] += Z[p]; + } + for (E_Int i = 0; i < 3; i++) fc[i] /= pn.size(); +} + +void Smesh::refine_tri(E_Int fid) +{ + std::vector nodes(F[fid]); + + E_Int ec[3]; + + for (size_t i = 0; i < nodes.size(); i++) { + E_Int p = nodes[i]; + E_Int q = nodes[(i+1)%nodes.size()]; + u_edge e(p, q); + auto it = ecenter.find(e); + if (it == ecenter.end()) { + refine_edge(e); + ec[i] = ecenter[e]; + } else { + ec[i] = it->second; + } + } + + fchildren[fid].push_back({nf, nf+1, nf+2}); + + F[fid] = {nodes[0], ec[0], ec[2]}; + F[nf] = {ec[0], nodes[1], ec[1]}; + F[nf+1] = {ec[2], ec[1], nodes[2]}; + F[nf+2] = {ec[0], ec[1], ec[2]}; + + Fc[fid] = F[fid]; + Fc[nf] = F[nf]; + Fc[nf+1] = F[nf+1]; + Fc[nf+2] = F[nf+2]; + + const E_Float *N = &fnormals[3*fid]; + for (E_Int i = 0; i < 3; i++) { + E_Float *fN = &fnormals[3*(nf+i)]; + for (E_Int j = 0; j < 3; j++) fN[j] = N[j]; + } + + //compute_face_center(fid); + //for (E_Int i = 0; i < 3; i++) compute_face_center(nf+i); + + nf += 3; +} + +void Smesh::resize_for_refinement(size_t nref_faces) +{ + E_Int fincr = nref_faces * 3; + E_Int pincr = nref_faces * 3; + F.resize(nf + fincr); + Fc.resize(nf + fincr); + X.resize(np + pincr, EFLOATMAX); + Y.resize(np + pincr, EFLOATMAX); + Z.resize(np + pincr, EFLOATMAX); + fnormals.resize(3*(nf + fincr), EFLOATMAX); + fcenters.resize(3*(nf + fincr), EFLOATMAX); +} + +void Smesh::refine(const std::vector &ref_faces) +{ + resize_for_refinement(ref_faces.size()); + for (E_Int fid : ref_faces) + refine_tri(fid); +} diff --git a/Cassiopee/XCore/XCore/intersectMesh/snode.cpp b/Cassiopee/XCore/XCore/intersectMesh/snode.cpp deleted file mode 100644 index 145d71925..000000000 --- a/Cassiopee/XCore/XCore/intersectMesh/snode.cpp +++ /dev/null @@ -1,32 +0,0 @@ -/* - Copyright 2013-2024 Onera. - - This file is part of Cassiopee. - - Cassiopee is free software: you can redistribute it and/or modify - it under the terms of the GNU General Public License as published by - the Free Software Foundation, either version 3 of the License, or - (at your option) any later version. - - Cassiopee is distributed in the hope that it will be useful, - but WITHOUT ANY WARRANTY; without even the implied warranty of - MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - GNU General Public License for more details. - - You should have received a copy of the GNU General Public License - along with Cassiopee. If not, see . -*/ -#include "snode.h" - -Snode::Snode(Segment *Key, void *Inf) -: key(Key), inf(Inf), left(NULL), right(NULL) -{} - -void Snode::print_tree(Snode *root) -{ - if (root == NULL) return; - - print_tree(root->left); - root->print(); - print_tree(root->right); -} diff --git a/Cassiopee/XCore/XCore/intersectMesh/snode.h b/Cassiopee/XCore/XCore/intersectMesh/snode.h deleted file mode 100644 index 722caeaf5..000000000 --- a/Cassiopee/XCore/XCore/intersectMesh/snode.h +++ /dev/null @@ -1,40 +0,0 @@ -/* - Copyright 2013-2024 Onera. - - This file is part of Cassiopee. - - Cassiopee is free software: you can redistribute it and/or modify - it under the terms of the GNU General Public License as published by - the Free Software Foundation, either version 3 of the License, or - (at your option) any later version. - - Cassiopee is distributed in the hope that it will be useful, - but WITHOUT ANY WARRANTY; without even the implied warranty of - MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - GNU General Public License for more details. - - You should have received a copy of the GNU General Public License - along with Cassiopee. If not, see . -*/ -#pragma once - -#include - -#include "segment.h" - -struct Snode { - Segment *key; - void *inf; - Snode *left; - Snode *right; - - Snode(Segment *Key, void *Inf); - - inline void print() - { - printf("S" SF_D_ "(P" SF_D_ ", P" SF_D_ ")\n", key->id, key->p->id, - key->q->id); - } - - static void print_tree(Snode *root); -}; diff --git a/Cassiopee/XCore/XCore/intersectMesh/status.cpp b/Cassiopee/XCore/XCore/intersectMesh/status.cpp deleted file mode 100644 index 7033a1faa..000000000 --- a/Cassiopee/XCore/XCore/intersectMesh/status.cpp +++ /dev/null @@ -1,230 +0,0 @@ -/* - Copyright 2013-2024 Onera. - - This file is part of Cassiopee. - - Cassiopee is free software: you can redistribute it and/or modify - it under the terms of the GNU General Public License as published by - the Free Software Foundation, either version 3 of the License, or - (at your option) any later version. - - Cassiopee is distributed in the hope that it will be useful, - but WITHOUT ANY WARRANTY; without even the implied warranty of - MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - GNU General Public License for more details. - - You should have received a copy of the GNU General Public License - along with Cassiopee. If not, see . -*/ -#include -#include - -#include "status.h" -#include "primitives.h" - -Status::Status() -: root(NULL), rx(0.0), ry(0.0) -{} - -static -E_Int Segment_cmp(Segment *s0, Segment *s1, E_Float rx, E_Float ry) -{ - if (s0 == s1) return 0; - - E_Int cmp = compare(*s0, *s1, rx, ry); - - if (cmp == 0) { - cmp = cmp_mySeg(*s0, *s1); - } - - return cmp; -} - -Snode *Status::insert(Segment *key, void *inf) -{ - if (key == NULL) return NULL; - return insert_(root, key, inf); -} - -Snode *Status::insert_(Snode *&root, Segment *key, void *inf) -{ - if (root == NULL) { - root = new Snode(key, inf); - return root; - } - - E_Int cmp = Segment_cmp(root->key, key, rx, ry); - - if (cmp == 0) { - root->inf = inf; - return root; - } else if (cmp < 0) { - return insert_(root->right, key, inf); - } else { - return insert_(root->left, key, inf); - } -} - -Snode *Status::locate(Segment *seg) -{ - if (seg == NULL) return NULL; - return locate_(root, seg); -} - -Snode *Status::locate_(Snode *root, Segment *seg) -{ - if (root == NULL) return NULL; - - E_Int cmp = compare(*root->key, *seg, rx, ry); - - if (cmp == 0) return root; - else if (cmp < 0) return locate_(root->right, seg); - else return locate_(root->left, seg); -} - -Snode *Status::lookup(Segment *seg) -{ - if (seg == NULL) return NULL; - return lookup_(root, seg); -} - -Snode *Status::lookup_(Snode *root, Segment *seg) -{ - if (root == NULL) return NULL; - - E_Int cmp = Segment_cmp(root->key, seg, rx, ry); - - if (cmp == 0) return root; - else if (cmp < 0) return lookup_(root->right, seg); - else return lookup_(root->left, seg); -} - -Snode *Status::pred(Segment *seg) -{ - Snode *pre = NULL; - pred_(root, seg, pre); - return pre; -} - -Snode *Status::succ(Segment *seg) -{ - Snode *suc = NULL; - succ_(root, seg, suc); - return suc; -} - -Snode *Status::pred(Snode *sit) -{ - return pred(sit->key); -} - -Snode *Status::succ(Snode *sit) -{ - return succ(sit->key); -} - -void Status::pred_(Snode *root, Segment *seg, Snode *&pre) -{ - if (root == NULL) return; - - E_Int cmp = Segment_cmp(root->key, seg, rx, ry); - - if (cmp == 0) { - assert(root->key == seg); - - if (root->left != NULL) { - Snode *tmp = root->left; - while (tmp->right) tmp = tmp->right; - pre = tmp; - } - - return; - } - - if (cmp > 0) { - pred_(root->left, seg, pre); - } else { - pre = root; - pred_(root->right, seg, pre); - } -} - -void Status::succ_(Snode *root, Segment *seg, Snode *&suc) -{ - if (root == NULL) return; - - E_Int cmp = Segment_cmp(root->key, seg, rx, ry); - - if (cmp == 0) { - assert(root->key == seg); - - if (root->right != NULL) { - Snode *tmp = root->right; - while (tmp->left) tmp = tmp->left; - suc = tmp; - } - - return; - } - - if (cmp > 0) { - suc = root; - succ_(root->left, seg, suc); - } else { - succ_(root->right, seg, suc); - } -} - -void Status::erase(Segment *seg) -{ - root = erase_(root, seg); -} - -Snode *Status::erase_(Snode *root, Segment *seg) -{ - if (root == NULL) return NULL; - - E_Int cmp = Segment_cmp(root->key, seg, rx, ry); - - if (cmp < 0) { - root->right = erase_(root->right, seg); - return root; - } else if (cmp > 0) { - root->left = erase_(root->left, seg); - return root; - } - - assert(root->key == seg); - - if (root->left == NULL) { - Snode *tmp = root->right; - delete root; - return tmp; - } else if (root->right == NULL) { - Snode *tmp = root->left; - delete root; - return tmp; - } else { - Snode *succ_parent = root; - - Snode *succ = root->right; - while (succ->left) { - succ_parent = succ; - succ = succ->left; - } - - if (succ_parent != root) succ_parent->left = succ->right; - else succ_parent->right = succ->right; - - root->key = succ->key; - root->inf = succ->inf; - - delete succ; - return root; - } -} - -void Status::print() -{ - return Snode::print_tree(root); -} diff --git a/Cassiopee/XCore/XCore/intersectMesh/status.h b/Cassiopee/XCore/XCore/intersectMesh/status.h deleted file mode 100644 index 0cad6c59a..000000000 --- a/Cassiopee/XCore/XCore/intersectMesh/status.h +++ /dev/null @@ -1,60 +0,0 @@ -/* - Copyright 2013-2024 Onera. - - This file is part of Cassiopee. - - Cassiopee is free software: you can redistribute it and/or modify - it under the terms of the GNU General Public License as published by - the Free Software Foundation, either version 3 of the License, or - (at your option) any later version. - - Cassiopee is distributed in the hope that it will be useful, - but WITHOUT ANY WARRANTY; without even the implied warranty of - MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - GNU General Public License for more details. - - You should have received a copy of the GNU General Public License - along with Cassiopee. If not, see . -*/ -#pragma once - -#include - -#include "snode.h" - -struct Status { - Snode *root; - E_Float rx, ry; - - Status(); - - Snode *insert(Segment *key, void *inf = NULL); - - Snode *lookup(Segment *seg); - - Snode *locate(Segment *seg); - - Snode *pred(Snode *sit); - - Snode *pred(Segment *seg); - - Snode *succ(Snode *sit); - - Snode *succ(Segment *seg); - - void erase(Segment *seg); - - void print(); - - Snode *insert_(Snode *& root, Segment *key, void *inf); - - Snode *lookup_(Snode *root, Segment *seg); - - Snode *locate_(Snode *root, Segment *seg); - - void pred_(Snode *root, Segment *seg, Snode *&pre); - - void succ_(Snode *root, Segment *seg, Snode *&suc); - - Snode *erase_(Snode *root, Segment *seg); -}; diff --git a/Cassiopee/XCore/XCore/intersectMesh/triangle.cpp b/Cassiopee/XCore/XCore/intersectMesh/triangle.cpp index 0929cfc3a..43cf77336 100644 --- a/Cassiopee/XCore/XCore/intersectMesh/triangle.cpp +++ b/Cassiopee/XCore/XCore/intersectMesh/triangle.cpp @@ -29,34 +29,37 @@ E_Int Triangle::is_point_inside(E_Float px, E_Float py, E_Float pz, E_Float &u, E_Float &v, E_Float &w) { // Normal vector to the plane - E_Float Y[3] = {bx-ax, by-ay, bz-az}; - E_Float Z[3] = {cx-ax, cy-ay, cz-az}; + E_Float v0[3] = {bx-ax, by-ay, bz-az}; + E_Float v1[3] = {cx-ax, cy-ay, cz-az}; + E_Float v2[3] = {px-ax, py-ay, pz-az}; + E_Float N[3]; - K_MATH::cross(Y, Z, N); + K_MATH::cross(v0, v1, N); - E_Float X[3] = {px-ax, py-ay, pz-az}; + // TODO(Imad): check degenerate triangle with |N| = 0 - E_Float dp = K_MATH::dot(N, X, 3); + E_Float dp = K_MATH::dot(N, v2, 3); // Is the point on the plane? if (dp < -TOL || dp > TOL) return 0; - E_Float x1 = K_MATH::dot(X, Y, 3); - E_Float y1 = K_MATH::dot(Y, Y, 3); - E_Float z1 = K_MATH::dot(Z, Y, 3); - E_Float x2 = K_MATH::dot(X, Z, 3); - E_Float y2 = K_MATH::dot(Y, Z, 3); - E_Float z2 = K_MATH::dot(Z, Z, 3); + E_Float d00 = K_MATH::dot(v0, v0, 3); + E_Float d01 = K_MATH::dot(v0, v1, 3); + E_Float d11 = K_MATH::dot(v1, v1, 3); + E_Float d20 = K_MATH::dot(v2, v0, 3); + E_Float d21 = K_MATH::dot(v2, v1, 3); - u = (x1*z2 - x2*z1) / (y1*z2 - y2*z1); - if (u < -TOL || u > 1 + TOL) return 0; + E_Float denom = d00 * d11 - d01 * d01; - v = (-x1*y2 + x2*y1) / (y1*z2 - y2*z1); + v = (d11*d20 - d01*d21) / denom; if (v < -TOL || v > 1 + TOL) return 0; - - w = 1 - u - v; + + w = (d00*d21 - d01*d20) / denom; if (w < -TOL || w > 1 + TOL) return 0; + u = 1 - v - w; + if (u < -TOL || u > 1 + TOL) return 0; + return 1; } @@ -66,33 +69,36 @@ E_Int Triangle::is_point_inside(E_Float px, E_Float py, E_Float pz, E_Float cx, E_Float cy, E_Float cz) { // Normal vector to the plane - E_Float Y[3] = {bx-ax, by-ay, bz-az}; - E_Float Z[3] = {cx-ax, cy-ay, cz-az}; + E_Float v0[3] = {bx-ax, by-ay, bz-az}; + E_Float v1[3] = {cx-ax, cy-ay, cz-az}; + E_Float v2[3] = {px-ax, py-ay, pz-az}; + E_Float N[3]; - K_MATH::cross(Y, Z, N); + K_MATH::cross(v0, v1, N); - E_Float X[3] = {px-ax, py-ay, pz-az}; + // TODO(Imad): check degenerate triangle with |N| = 0 - E_Float dp = K_MATH::dot(N, X, 3); + E_Float dp = K_MATH::dot(N, v2, 3); // Is the point on the plane? if (dp < -TOL || dp > TOL) return 0; - E_Float x1 = K_MATH::dot(X, Y, 3); - E_Float y1 = K_MATH::dot(Y, Y, 3); - E_Float z1 = K_MATH::dot(Z, Y, 3); - E_Float x2 = K_MATH::dot(X, Z, 3); - E_Float y2 = K_MATH::dot(Y, Z, 3); - E_Float z2 = K_MATH::dot(Z, Z, 3); + E_Float d00 = K_MATH::dot(v0, v0, 3); + E_Float d01 = K_MATH::dot(v0, v1, 3); + E_Float d11 = K_MATH::dot(v1, v1, 3); + E_Float d20 = K_MATH::dot(v2, v0, 3); + E_Float d21 = K_MATH::dot(v2, v1, 3); - E_Float u = (x1*z2 - x2*z1) / (y1*z2 - y2*z1); - if (u < -TOL || u > 1 + TOL) return 0; + E_Float denom = d00 * d11 - d01 * d01; - E_Float v = (-x1*y2 + x2*y1) / (y1*z2 - y2*z1); + E_Float v = (d11*d20 - d01*d21) / denom; if (v < -TOL || v > 1 + TOL) return 0; - - E_Float w = 1 - u - v; + + E_Float w = (d00*d21 - d01*d20) / denom; if (w < -TOL || w > 1 + TOL) return 0; + E_Float u = 1 - v - w; + if (u < -TOL || u > 1 + TOL) return 0; + return 1; } diff --git a/Cassiopee/XCore/XCore/intersectMesh/triangle.h b/Cassiopee/XCore/XCore/intersectMesh/triangle.h index fbcdc4988..13f2846b0 100644 --- a/Cassiopee/XCore/XCore/intersectMesh/triangle.h +++ b/Cassiopee/XCore/XCore/intersectMesh/triangle.h @@ -18,10 +18,7 @@ */ #pragma once -#include - -#include "point.h" -#include "smesh.h" +#include "xcore.h" struct Triangle { E_Int a, b, c; @@ -43,3 +40,14 @@ struct Triangle { E_Float by, E_Float bz, E_Float cx, E_Float cy, E_Float cz, E_Float &u, E_Float &v, E_Float &w, E_Float &t, E_Float &x, E_Float &y, E_Float &z); }; + +struct TriangleIntersection { + E_Float x, y, z; + E_Float u, v, w; + E_Float t = -1; + E_Int face = -1; + E_Int tri = -1; + E_Int eid = -1; + E_Int vid = -1; + E_Int pid = -1; +}; diff --git a/Cassiopee/XCore/XCore/intersectMesh/triangleIntersection.h b/Cassiopee/XCore/XCore/intersectMesh/triangleIntersection.h deleted file mode 100644 index a69e5900d..000000000 --- a/Cassiopee/XCore/XCore/intersectMesh/triangleIntersection.h +++ /dev/null @@ -1,33 +0,0 @@ -/* - Copyright 2013-2024 Onera. - - This file is part of Cassiopee. - - Cassiopee is free software: you can redistribute it and/or modify - it under the terms of the GNU General Public License as published by - the Free Software Foundation, either version 3 of the License, or - (at your option) any later version. - - Cassiopee is distributed in the hope that it will be useful, - but WITHOUT ANY WARRANTY; without even the implied warranty of - MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - GNU General Public License for more details. - - You should have received a copy of the GNU General Public License - along with Cassiopee. If not, see . -*/ -#pragma once - -#include "xcore.h" -#include "common/common.h" - -struct TriangleIntersection { - E_Float x, y, z; - E_Float u, v, w; - E_Float t = -1; - E_Int face = -1; - E_Int tri = -1; - E_Int eid = -1; - E_Int vid = -1; - E_Int pid = -1; -}; diff --git a/Cassiopee/XCore/XCore/intersectMesh/triangulate.cpp b/Cassiopee/XCore/XCore/intersectMesh/triangulate.cpp new file mode 100644 index 000000000..d09ace588 --- /dev/null +++ b/Cassiopee/XCore/XCore/intersectMesh/triangulate.cpp @@ -0,0 +1,160 @@ +#include "mesh.h" +#include "common/Karray.h" + +PyObject *K_XCORE::triangulate_skin(PyObject *self, PyObject *args) +{ + PyObject *MESH, *PTLISTS; + if (!PYPARSETUPLE_(args, OO_, &MESH, &PTLISTS)) { + RAISE("Bad input."); + return NULL; + } + + Karray array; + if (Karray_parse_ngon(MESH, array) != 0) { + RAISE("Mesh should be an NGon."); + return NULL; + } + + IMesh M(array); + M.make_skin(); + + std::vector bcs_in; + + if (PTLISTS != Py_None) { + E_Int nbcs = PyList_Size(PTLISTS); + for (E_Int i = 0; i < nbcs; i++) { + PyObject *PTLIST = PyList_GetItem(PTLISTS, i); + Py_BC bc; + E_Int ret = K_NUMPY::getFromNumpyArray(PTLIST, bc.ptr, bc.size, true); + Py_DECREF(PTLIST); + if (ret != 1) { + RAISE("Couldn't extract pointlists."); + Karray_free_ngon(array); + return NULL; + } + bcs_in.push_back(bc); + } + + std::unordered_map fid_to_bc; + + for (size_t i = 0; i < bcs_in.size(); i++) { + const auto &bc = bcs_in[i]; + for (E_Int j = 0; j < bc.size; j++) { + E_Int fid = bc.ptr[j]; + fid_to_bc[fid] = i; + } + } + + std::vector new_bcs = M.triangulate_skin(bcs_in, fid_to_bc); + + PyObject *out = PyList_New(0); + PyList_Append(out, M.export_karray()); + PyObject *bcs_out = PyList_New(0); + for (const auto &new_bc : new_bcs) { + PyList_Append(bcs_out, (PyObject *)new_bc); + Py_DECREF(new_bc); + } + PyList_Append(out, bcs_out); + Py_DECREF(bcs_out); + return out; + } else { + M.triangulate_skin(); + return M.export_karray(); + } +} + +std::vector IMesh::triangulate_skin(const std::vector &bcs_in, + const std::unordered_map &fid_to_bc) +{ + E_Int NF = nf; + + owner.resize(nf + skin.size(), -1); + neigh.resize(nf + skin.size(), -1); + + std::vector new_bcs(bcs_in.size()); + std::vector ptrs(bcs_in.size()); + std::vector counts(bcs_in.size(), 0); + + for (size_t i = 0; i < bcs_in.size(); i++) { + E_Int new_size = bcs_in[i].size * 2; + npy_intp dims[2]; + dims[0] = (npy_intp)new_size; + dims[1] = 1; + new_bcs[i] = (PyArrayObject *)PyArray_SimpleNew(1, dims, E_NPY_INT); + ptrs[i] = (E_Int *)PyArray_DATA(new_bcs[i]); + } + + for (auto fid : skin) { + assert(neigh[fid] == -1); + + const auto &pn = F[fid]; + if (pn.size() == 3) continue; + + assert(pn.size() == 4); + + E_Int nodes[4] = {pn[0], pn[1], pn[2], pn[3]}; + + F.push_back({nodes[2], nodes[3], nodes[0]}); + F[fid] = {nodes[0], nodes[1], nodes[2]}; + assert(F[fid].size() == 3); + + E_Int own = owner[fid]; + auto &pf = C[own]; + pf.push_back(nf); + + owner[nf] = own; + + // Which bc (if any) does fid belong to? + auto it = fid_to_bc.find(fid+1); + if (it != fid_to_bc.end()) { + E_Int bc_id = it->second; + ptrs[bc_id][counts[bc_id]++] = fid+1; + ptrs[bc_id][counts[bc_id]++] = nf+1; + } + + nf++; + } + + for (E_Int i = NF; i < nf; i++) + skin.push_back(i); + + for (size_t i = 0; i < counts.size(); i++) { + assert(counts[i] == 2*bcs_in[i].size); + } + + return new_bcs; +} + +void IMesh::triangulate_skin() +{ + E_Int NF = nf; + + owner.resize(nf + skin.size(), -1); + neigh.resize(nf + skin.size(), -1); + + for (auto fid : skin) { + assert(neigh[fid] == -1); + + const auto &pn = F[fid]; + if (pn.size() == 3) continue; + + assert(pn.size() == 4); + + E_Int nodes[4] = {pn[0], pn[1], pn[2], pn[3]}; + + F.push_back({nodes[2], nodes[3], nodes[0]}); + F[fid] = {nodes[0], nodes[1], nodes[2]}; + assert(F[fid].size() == 3); + + E_Int own = owner[fid]; + auto &pf = C[own]; + pf.push_back(nf); + + owner[nf] = own; + + nf++; + } + + for (E_Int i = NF; i < nf; i++) + skin.push_back(i); +} \ No newline at end of file diff --git a/Cassiopee/XCore/XCore/intersectMesh/u_edge.h b/Cassiopee/XCore/XCore/intersectMesh/u_edge.h new file mode 100644 index 000000000..050cf627b --- /dev/null +++ b/Cassiopee/XCore/XCore/intersectMesh/u_edge.h @@ -0,0 +1,16 @@ +#pragma once + +#include "xcore.h" + +struct u_edge { + E_Int p, q; + + u_edge(E_Int P, E_Int Q) + : p(std::min(P, Q)), q(std::max(P, Q)) + {} + + bool operator<(const u_edge& e) const + { + return (p < e.p) || (p == e.p && q < e.q); + } +}; \ No newline at end of file diff --git a/Cassiopee/XCore/XCore/intersectMesh/vec3.cpp b/Cassiopee/XCore/XCore/intersectMesh/vec3.cpp deleted file mode 100644 index 9fe3b0253..000000000 --- a/Cassiopee/XCore/XCore/intersectMesh/vec3.cpp +++ /dev/null @@ -1,67 +0,0 @@ -/* - Copyright 2013-2024 Onera. - - This file is part of Cassiopee. - - Cassiopee is free software: you can redistribute it and/or modify - it under the terms of the GNU General Public License as published by - the Free Software Foundation, either version 3 of the License, or - (at your option) any later version. - - Cassiopee is distributed in the hope that it will be useful, - but WITHOUT ANY WARRANTY; without even the implied warranty of - MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - GNU General Public License for more details. - - You should have received a copy of the GNU General Public License - along with Cassiopee. If not, see . -*/ -#include "vec3.h" - -Vec3::Vec3() -{} - -Vec3::Vec3(E_Float X, E_Float Y, E_Float Z) -{ - ptr[0] = X; - ptr[1] = Y; - ptr[2] = Z; -} - -Vec3 Vec3::operator-(const Vec3 &p) const -{ - Vec3 ret; - for (E_Int i = 0; i < 3; i++) ret[i] = ptr[i] - p[i]; - return ret; -} - -Vec3 Vec3::operator+(const Vec3 &p) const -{ - Vec3 ret; - for (E_Int i = 0; i < 3; i++) ret[i] = ptr[i] + p[i]; - return ret; -} - -Vec3 Vec3::operator*(E_Float a) const -{ - Vec3 ret; - for (E_Int i = 0; i < 3; i++) ret[i] = a * ptr[i]; - return ret; -} - -Vec3 operator*(E_Float a, const Vec3 & v) -{ - Vec3 ret; - for (E_Int i = 0; i < 3; i++) ret[i] = a * v[i]; - return ret; -} - -E_Float &Vec3::operator[](E_Int idx) -{ - return ptr[idx]; -} - -E_Float Vec3::operator[](E_Int idx) const -{ - return ptr[idx]; -} diff --git a/Cassiopee/XCore/XCore/intersectMesh/vec3.h b/Cassiopee/XCore/XCore/intersectMesh/vec3.h deleted file mode 100644 index b0db340c4..000000000 --- a/Cassiopee/XCore/XCore/intersectMesh/vec3.h +++ /dev/null @@ -1,42 +0,0 @@ -/* - Copyright 2013-2024 Onera. - - This file is part of Cassiopee. - - Cassiopee is free software: you can redistribute it and/or modify - it under the terms of the GNU General Public License as published by - the Free Software Foundation, either version 3 of the License, or - (at your option) any later version. - - Cassiopee is distributed in the hope that it will be useful, - but WITHOUT ANY WARRANTY; without even the implied warranty of - MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - GNU General Public License for more details. - - You should have received a copy of the GNU General Public License - along with Cassiopee. If not, see . -*/ -#pragma once - -#include "xcore.h" -#include "common/common.h" - -struct Vec3 { - E_Float ptr[3]; - - Vec3(); - - Vec3(E_Float X, E_Float Y, E_Float Z); - - Vec3 operator-(const Vec3 &v) const; - - Vec3 operator+(const Vec3 &v) const; - - Vec3 operator*(E_Float a) const; - - E_Float &operator[](E_Int idx); - - E_Float operator[](E_Int idx) const; -}; - -Vec3 operator*(E_Float a, const Vec3 &v); diff --git a/Cassiopee/XCore/XCore/intersectMesh/vertex.cpp b/Cassiopee/XCore/XCore/intersectMesh/vertex.cpp deleted file mode 100644 index 8f9b4db45..000000000 --- a/Cassiopee/XCore/XCore/intersectMesh/vertex.cpp +++ /dev/null @@ -1,39 +0,0 @@ -/* - Copyright 2013-2024 Onera. - - This file is part of Cassiopee. - - Cassiopee is free software: you can redistribute it and/or modify - it under the terms of the GNU General Public License as published by - the Free Software Foundation, either version 3 of the License, or - (at your option) any later version. - - Cassiopee is distributed in the hope that it will be useful, - but WITHOUT ANY WARRANTY; without even the implied warranty of - MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - GNU General Public License for more details. - - You should have received a copy of the GNU General Public License - along with Cassiopee. If not, see . -*/ -#include "vertex.h" -#include "primitives.h" - -Vertex::Vertex(E_Float X, E_Float Y, E_Float Z, E_Int Oid, E_Int color) -: x(X), y(Y), z(Z), rep(NULL), id(-1), left(NULL) -{ - oid[color] = Oid; - oid[(color+1)%2] = -1; -} - -Vertex::Vertex(E_Float X, E_Float Y, E_Float Z) -: x(X), y(Y), z(Z), rep(NULL), id(-1), left(NULL) -{ - oid[0] = -1; - oid[1] = -1; -} - -E_Int cmp_vtx(Vertex *a, Vertex *b) -{ - return cmp_points(a->x, a->y, a->z, b->x, b->y, b->z); -} diff --git a/Cassiopee/XCore/XCore/intersectMesh/vertex.h b/Cassiopee/XCore/XCore/intersectMesh/vertex.h deleted file mode 100644 index 8718a7439..000000000 --- a/Cassiopee/XCore/XCore/intersectMesh/vertex.h +++ /dev/null @@ -1,52 +0,0 @@ -/* - Copyright 2013-2024 Onera. - - This file is part of Cassiopee. - - Cassiopee is free software: you can redistribute it and/or modify - it under the terms of the GNU General Public License as published by - the Free Software Foundation, either version 3 of the License, or - (at your option) any later version. - - Cassiopee is distributed in the hope that it will be useful, - but WITHOUT ANY WARRANTY; without even the implied warranty of - MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - GNU General Public License for more details. - - You should have received a copy of the GNU General Public License - along with Cassiopee. If not, see . -*/ -#pragma once - -#include - -#include "xcore.h" -#include "common/common.h" - -struct PointLoc { - E_Int fid = -1; - E_Int v_idx = -1; - E_Int e_idx = -1; -}; - -struct Hedge; - -struct Vertex { - E_Float x, y, z; - Hedge *rep; - E_Int id; - Hedge *left; - E_Int oid[2]; - - PointLoc loc; - - Hedge *xhedge = NULL; - - Vertex(E_Float X, E_Float Y, E_Float Z, E_Int Oid, E_Int color); - - Vertex(E_Float X, E_Float Y, E_Float Z); - - inline void print() { printf("P" SF_D_ ": %f %f %f\n", id, x, y, z); } -}; - -E_Int cmp_vtx(Vertex *a, Vertex *b); diff --git a/Cassiopee/XCore/XCore/xcore.cpp b/Cassiopee/XCore/XCore/xcore.cpp index 17a36e8da..9bd3931ad 100644 --- a/Cassiopee/XCore/XCore/xcore.cpp +++ b/Cassiopee/XCore/XCore/xcore.cpp @@ -38,12 +38,16 @@ static PyMethodDef Pyxcore [] = {"AdaptMesh_Adapt", K_XCORE::AdaptMesh_Adapt, METH_VARARGS}, {"AdaptMesh_ExtractMesh", K_XCORE::AdaptMesh_ExtractMesh, METH_VARARGS}, {"AdaptMesh_Exit", K_XCORE::AdaptMesh_Exit, METH_VARARGS}, - {"AdaptMesh_ExtractOwners", K_XCORE::AdaptMesh_ExtractOwners, METH_VARARGS}, {"AdaptMesh_ExtractNeighbours", K_XCORE::AdaptMesh_ExtractNeighbours, METH_VARARGS}, {"AdaptMesh_ExtractCellLevels", K_XCORE::AdaptMesh_ExtractCellLevels, METH_VARARGS}, {"AdaptMesh_ExtractCellRanges", K_XCORE::AdaptMesh_ExtractCellRanges, METH_VARARGS}, {"AdaptMesh_ExtractHaloCellLevels", K_XCORE::AdaptMesh_ExtractHaloCellLevels, METH_VARARGS}, + {"AdaptMesh_TagFaces", K_XCORE::AdaptMesh_TagFaces, METH_VARARGS}, + {"AdaptMesh_TriangulateFaces", K_XCORE::AdaptMesh_TriangulateFaces, METH_VARARGS}, + {"AdaptMesh_GeneratePrisms", K_XCORE::AdaptMesh_GeneratePrisms, METH_VARARGS}, + {"AdaptMesh_AdaptGeom", K_XCORE::AdaptMesh_AdaptGeom, METH_VARARGS}, + {"AdaptMesh_ExtractTaggedFaces", K_XCORE::AdaptMesh_ExtractTaggedFaces, METH_VARARGS}, {"intersectMesh", K_XCORE::intersectMesh, METH_VARARGS}, {"removeIntersectingKPlanes", K_XCORE::removeIntersectingKPlanes, METH_VARARGS}, @@ -53,8 +57,22 @@ static PyMethodDef Pyxcore [] = {"IntersectMesh_TriangulateFaceSet", K_XCORE::IntersectMesh_TriangulateFaceSet, METH_VARARGS}, {"IntersectMesh_ExtractMesh", K_XCORE::IntersectMesh_ExtractMesh, METH_VARARGS}, {"IntersectMesh_Exit", K_XCORE::IntersectMesh_Exit, METH_VARARGS}, + {"IntersectMesh_ExtractFaceSet", K_XCORE::IntersectMesh_ExtractFaceSet, METH_VARARGS}, + {"IntersectMesh_Merge", K_XCORE::IntersectMesh_Merge, METH_VARARGS}, + + {"icapsule_init", K_XCORE::icapsule_init, METH_VARARGS}, + {"icapsule_adapt", K_XCORE::icapsule_adapt, METH_VARARGS}, + {"icapsule_intersect", K_XCORE::icapsule_intersect, METH_VARARGS}, + + {"icapsule_extract_master", K_XCORE::icapsule_extract_master, METH_VARARGS}, + {"icapsule_extract_slave", K_XCORE::icapsule_extract_slave, METH_VARARGS}, + {"icapsule_extract_slaves", K_XCORE::icapsule_extract_slaves, METH_VARARGS}, + + {"triangulate_skin", K_XCORE::triangulate_skin, METH_VARARGS}, {"extractCell", K_XCORE::extractCell, METH_VARARGS}, + + {"extractFacesFromPointTag", K_XCORE::extractFacesFromPointTag, METH_VARARGS}, {NULL, NULL} }; diff --git a/Cassiopee/XCore/XCore/xcore.h b/Cassiopee/XCore/XCore/xcore.h index 03a1c2634..96ef59b50 100644 --- a/Cassiopee/XCore/XCore/xcore.h +++ b/Cassiopee/XCore/XCore/xcore.h @@ -45,22 +45,21 @@ namespace K_XCORE PyObject *exchangeFields(PyObject *self, PyObject *args); PyObject *AdaptMesh_Init(PyObject *self, PyObject *args); - PyObject *AdaptMesh_AssignRefData(PyObject *self, PyObject *args); - PyObject *AdaptMesh_LoadBalance(PyObject *self, PyObject *args); - PyObject *AdaptMesh_Adapt(PyObject *self, PyObject *args); - PyObject *AdaptMesh_ExtractMesh(PyObject *self, PyObject *args); - PyObject *AdaptMesh_Exit(PyObject *self, PyObject *args); - PyObject *AdaptMesh_ExtractOwners(PyObject *self, PyObject *args); PyObject *AdaptMesh_ExtractNeighbours(PyObject *self, PyObject *args); PyObject *AdaptMesh_ExtractCellLevels(PyObject *self, PyObject *args); PyObject *AdaptMesh_ExtractCellRanges(PyObject *self, PyObject *args); PyObject *AdaptMesh_ExtractHaloCellLevels(PyObject *self, PyObject *args); + PyObject *AdaptMesh_TagFaces(PyObject *self, PyObject *args); + PyObject *AdaptMesh_TriangulateFaces(PyObject *self, PyObject *args); + PyObject *AdaptMesh_GeneratePrisms(PyObject *self, PyObject *args); + PyObject *AdaptMesh_AdaptGeom(PyObject *self, PyObject *args); + PyObject *AdaptMesh_ExtractTaggedFaces(PyObject *self, PyObject *args); PyObject *intersectMesh(PyObject *self, PyObject *args); PyObject *removeIntersectingKPlanes(PyObject *self, PyObject *args); @@ -70,8 +69,22 @@ namespace K_XCORE PyObject *IntersectMesh_ExtractMesh(PyObject *self, PyObject *args); PyObject *IntersectMesh_TriangulateFaceSet(PyObject *self, PyObject *args); PyObject *IntersectMesh_Exit(PyObject *self, PyObject *args); + PyObject *IntersectMesh_ExtractFaceSet(PyObject *self, PyObject *args); + PyObject *IntersectMesh_Merge(PyObject *self, PyObject *args); + + PyObject *icapsule_init(PyObject *self, PyObject *args); + PyObject *icapsule_adapt(PyObject *self, PyObject *args); + PyObject *icapsule_intersect(PyObject *self, PyObject *args); + PyObject *icapsule_extract_master(PyObject *self, PyObject *args); + PyObject *icapsule_extract_slave(PyObject *self, PyObject *args); + PyObject *icapsule_extract_slaves(PyObject *self, PyObject *args); + + + PyObject *triangulate_skin(PyObject *self, PyObject *args); PyObject *extractCell(PyObject *self, PyObject *args); + + PyObject *extractFacesFromPointTag(PyObject *self, PyObject *args); } #endif diff --git a/Cassiopee/XCore/srcs.py b/Cassiopee/XCore/srcs.py index f3737a047..6db1086b5 100644 --- a/Cassiopee/XCore/srcs.py +++ b/Cassiopee/XCore/srcs.py @@ -21,7 +21,27 @@ 'XCore/common/common.cpp', 'XCore/common/Karray.cpp', - 'XCore/intersectMesh/BVH.cpp', + 'XCore/intersectMesh/icapsule.cpp', + 'XCore/intersectMesh/icapsule_refine.cpp', + + 'XCore/intersectMesh/triangulate.cpp', + + 'XCore/intersectMesh/mesh_io.cpp', + + 'XCore/intersectMesh/smesh.cpp', + 'XCore/intersectMesh/smesh_locate.cpp', + 'XCore/intersectMesh/smesh_io.cpp', + 'XCore/intersectMesh/smesh_refine.cpp', + 'XCore/intersectMesh/smesh_extract.cpp', + 'XCore/intersectMesh/smesh_bvh.cpp', + 'XCore/intersectMesh/smesh_geom.cpp', + 'XCore/intersectMesh/smesh_reconstruct.cpp', + + 'XCore/intersectMesh/dcel.cpp', + 'XCore/intersectMesh/dcel_extract.cpp', + 'XCore/intersectMesh/dcel_io.cpp', + 'XCore/intersectMesh/dcel_reconstruct.cpp', + 'XCore/intersectMesh/AABB.cpp', 'XCore/intersectMesh/extract.cpp', @@ -30,68 +50,72 @@ 'XCore/intersectMesh/IntersectMesh_TriangulateFaceSet.cpp', 'XCore/intersectMesh/IntersectMesh_ExtractMesh.cpp', 'XCore/intersectMesh/IntersectMesh_Exit.cpp', - 'XCore/intersectMesh/DDA.cpp', + 'XCore/intersectMesh/IntersectMesh_ExtractFaceSet.cpp', + 'XCore/intersectMesh/IntersectMesh_Merge.cpp', 'XCore/intersectMesh/intersectMesh.cpp', 'XCore/intersectMesh/removeIntersectingKPlanes.cpp', 'XCore/intersectMesh/prepareMeshesForIntersection.cpp', - 'XCore/intersectMesh/cycle.cpp', - 'XCore/intersectMesh/dcel.cpp', - 'XCore/intersectMesh/event.cpp', - 'XCore/intersectMesh/hedge.cpp', - 'XCore/intersectMesh/face.cpp', - 'XCore/intersectMesh/vertex.cpp', - 'XCore/intersectMesh/status.cpp', - 'XCore/intersectMesh/snode.cpp', - 'XCore/intersectMesh/queue.cpp', - 'XCore/intersectMesh/segment.cpp', 'XCore/intersectMesh/mesh.cpp', 'XCore/intersectMesh/meshRefine.cpp', 'XCore/intersectMesh/meshTopo.cpp', - 'XCore/intersectMesh/smesh.cpp', - 'XCore/intersectMesh/smeshRefine.cpp', 'XCore/intersectMesh/io.cpp', 'XCore/intersectMesh/primitives.cpp', - 'XCore/intersectMesh/vec3.cpp', 'XCore/intersectMesh/triangle.cpp', 'XCore/intersectMesh/point.cpp', 'XCore/intersectMesh/ray.cpp', 'XCore/intersectMesh/meshExport.cpp', - + 'XCore/intersectMesh/DDA.cpp', 'XCore/AdaptMesh/AdaptMesh_Init.cpp', 'XCore/AdaptMesh/AdaptMesh_ExtractMesh.cpp', 'XCore/AdaptMesh/AdaptMesh_AssignRefData.cpp', 'XCore/AdaptMesh/AdaptMesh_Adapt.cpp', 'XCore/AdaptMesh/AdaptMesh_Exit.cpp', - 'XCore/AdaptMesh/AdaptMesh_ExtractData.cpp', - + 'XCore/AdaptMesh/AdaptMesh_TagFaces.cpp', + 'XCore/AdaptMesh/AdaptMesh_TriangulateFaces.cpp', + 'XCore/AdaptMesh/AdaptMesh_GeneratePrisms.cpp', + 'XCore/AdaptMesh/AdaptMesh_AdaptGeom.cpp', + 'XCore/AdaptMesh/MeshInit.cpp', 'XCore/AdaptMesh/MeshOrient.cpp', 'XCore/AdaptMesh/MeshClean.cpp', 'XCore/AdaptMesh/MeshTopo.cpp', 'XCore/AdaptMesh/MeshIO.cpp', 'XCore/AdaptMesh/MeshSmooth.cpp', - 'XCore/AdaptMesh/MeshConnectivity.cpp', 'XCore/AdaptMesh/MeshConformize.cpp', 'XCore/AdaptMesh/MeshRefine.cpp', 'XCore/AdaptMesh/MeshIso.cpp', 'XCore/AdaptMesh/MeshDir.cpp', + 'XCore/AdaptMesh/MeshTriangulate.cpp', 'XCore/AdaptMesh/H27.cpp', 'XCore/AdaptMesh/H18.cpp', 'XCore/AdaptMesh/Tetra.cpp', 'XCore/AdaptMesh/Penta.cpp', 'XCore/AdaptMesh/Pyra.cpp', - 'XCore/AdaptMesh/Q9.cpp', 'XCore/AdaptMesh/Q6.cpp', - 'XCore/AdaptMesh/Tri.cpp', - 'XCore/AdaptMesh/Edge.cpp', + + 'XCore/AdaptMesh/BVH.cpp', + 'XCore/AdaptMesh/Box.cpp', + 'XCore/AdaptMesh/FaceSort.cpp', + 'XCore/AdaptMesh/MeshExtract.cpp', + 'XCore/AdaptMesh/MeshLocate.cpp', + 'XCore/AdaptMesh/Point.cpp', + 'XCore/AdaptMesh/constants.cpp', + 'XCore/AdaptMesh/Skin.cpp', + 'XCore/AdaptMesh/Array.cpp', + + 'XCore/AdaptMesh/DynMesh.cpp', + 'XCore/AdaptMesh/DynMeshTopo.cpp', + 'XCore/AdaptMesh/TriGraph.cpp', + + 'XCore/extractFacesFromPointTag.cpp', ] if mpi: # source that requires mpi cpp_srcs += [