*/ static REBFLG Set_Struct_Var(REBSTU *stu, REBVAL *word, REBVAL *elem, REBVAL *val) /* ***********************************************************************/ { struct Struct_Field *field = NULL; REBCNT i = 0; field = (struct Struct_Field *)SERIES_DATA(stu->fields); for (i = 0; i < SERIES_TAIL(stu->fields); i ++, field ++) { if (VAL_WORD_CANON(word) == VAL_SYM_CANON(BLK_SKIP(PG_Word_Table.series, field->sym))) { if (field->array) { if (elem == NULL) { //set the whole array REBCNT n = 0; if ((!IS_BLOCK(val) || field->dimension != VAL_LEN(val))) { return FALSE; } for(n = 0; n < field->dimension; n ++) { if (!assign_scalar(stu, field, n, VAL_BLK_SKIP(val, n))) { return FALSE; } } } else {// set only one element if (!IS_INTEGER(elem) || VAL_INT32(elem) <= 0 || VAL_INT32(elem) > cast(REBINT, field->dimension)) { return FALSE; } return assign_scalar(stu, field, VAL_INT32(elem) - 1, val); } return TRUE; } else { return assign_scalar(stu, field, 0, val); } return TRUE; } } return FALSE; }
IGL_INLINE void igl::copyleft::cgal::mesh_to_cgal_triangle_list( const Eigen::PlainObjectBase<DerivedV> & V, const Eigen::PlainObjectBase<DerivedF> & F, std::vector<CGAL::Triangle_3<Kernel> > & T) { typedef CGAL::Point_3<Kernel> Point_3; typedef CGAL::Triangle_3<Kernel> Triangle_3; // Must be 3D assert(V.cols() == 3); // **Copy** to convert to output type (this is especially/only needed if the // input type DerivedV::Scalar is CGAL::Epeck Eigen::Matrix< typename Kernel::FT, DerivedV::RowsAtCompileTime, DerivedV::ColsAtCompileTime> KV(V.rows(),V.cols()); // Just use f'ing for loops. What if V and KV don't use same ordering? for(int i = 0;i<V.rows();i++) { for(int j = 0;j<V.cols();j++) { assign_scalar(V(i,j),KV(i,j)); } } // Must be triangles assert(F.cols() == 3); T.reserve(F.rows()); // Loop over faces for(int f = 0;f<(int)F.rows();f++) { T.push_back( Triangle_3( Point_3( KV(F(f,0),0), KV(F(f,0),1), KV(F(f,0),2)), Point_3( KV(F(f,1),0), KV(F(f,1),1), KV(F(f,1),2)), Point_3( KV(F(f,2),0), KV(F(f,2),1), KV(F(f,2),2)))); } }
*/ REBFLG MT_Struct(REBVAL *out, REBVAL *data, enum Reb_Kind type) /* * Format: * make struct! [ * field1 [type1] * field2: [type2] field2-init-value * field3: [struct [field1 [type1]]] * field4: [type1[3]] * ... * ] ***********************************************************************/ { //RL_Print("%s\n", __func__); REBINT max_fields = 16; VAL_STRUCT_FIELDS(out) = Make_Series( max_fields, sizeof(struct Struct_Field), MKS_NONE ); MANAGE_SERIES(VAL_STRUCT_FIELDS(out)); if (IS_BLOCK(data)) { //if (Reduce_Block_No_Set_Throws(VAL_SERIES(data), 0, NULL))... //data = DS_POP; REBVAL *blk = VAL_BLK_DATA(data); REBINT field_idx = 0; /* for field index */ u64 offset = 0; /* offset in data */ REBCNT eval_idx = 0; /* for spec block evaluation */ REBVAL *init = NULL; /* for result to save in data */ REBOOL expect_init = FALSE; REBINT raw_size = -1; REBUPT raw_addr = 0; REBCNT alignment = 0; VAL_STRUCT_SPEC(out) = Copy_Array_Shallow(VAL_SERIES(data)); VAL_STRUCT_DATA(out) = Make_Series( 1, sizeof(struct Struct_Data), MKS_NONE ); EXPAND_SERIES_TAIL(VAL_STRUCT_DATA(out), 1); VAL_STRUCT_DATA_BIN(out) = Make_Series(max_fields << 2, 1, MKS_NONE); VAL_STRUCT_OFFSET(out) = 0; // We tell the GC to manage this series, but it will not cause a // synchronous garbage collect. Still, when's the right time? ENSURE_SERIES_MANAGED(VAL_STRUCT_SPEC(out)); MANAGE_SERIES(VAL_STRUCT_DATA(out)); MANAGE_SERIES(VAL_STRUCT_DATA_BIN(out)); /* set type early such that GC will handle it correctly, i.e, not collect series in the struct */ SET_TYPE(out, REB_STRUCT); if (IS_BLOCK(blk)) { parse_attr(blk, &raw_size, &raw_addr); ++ blk; } while (NOT_END(blk)) { REBVAL *inner; struct Struct_Field *field = NULL; u64 step = 0; EXPAND_SERIES_TAIL(VAL_STRUCT_FIELDS(out), 1); DS_PUSH_NONE; inner = DS_TOP; /* save in stack so that it won't be GC'ed when MT_Struct is recursively called */ field = (struct Struct_Field *)SERIES_SKIP(VAL_STRUCT_FIELDS(out), field_idx); field->offset = (REBCNT)offset; if (IS_SET_WORD(blk)) { field->sym = VAL_WORD_SYM(blk); expect_init = TRUE; if (raw_addr) { /* initialization is not allowed for raw memory struct */ raise Error_Invalid_Arg(blk); } } else if (IS_WORD(blk)) { field->sym = VAL_WORD_SYM(blk); expect_init = FALSE; } else raise Error_Has_Bad_Type(blk); ++ blk; if (!IS_BLOCK(blk)) raise Error_Invalid_Arg(blk); if (!parse_field_type(field, blk, inner, &init)) { return FALSE; } ++ blk; STATIC_assert(sizeof(field->size) <= 4); STATIC_assert(sizeof(field->dimension) <= 4); step = (u64)field->size * (u64)field->dimension; if (step > VAL_STRUCT_LIMIT) raise Error_1(RE_SIZE_LIMIT, out); EXPAND_SERIES_TAIL(VAL_STRUCT_DATA_BIN(out), step); if (expect_init) { REBVAL safe; // result of reduce or do (GC saved during eval) init = &safe; if (IS_BLOCK(blk)) { if (Reduce_Block_Throws(init, VAL_SERIES(blk), 0, FALSE)) raise Error_No_Catch_For_Throw(init); ++ blk; } else { DO_NEXT_MAY_THROW( eval_idx, init, VAL_SERIES(data), blk - VAL_BLK_DATA(data) ); if (eval_idx == THROWN_FLAG) raise Error_No_Catch_For_Throw(init); blk = VAL_BLK_SKIP(data, eval_idx); } if (field->array) { if (IS_INTEGER(init)) { /* interpreted as a C pointer */ void *ptr = cast(void *, cast(REBUPT, VAL_INT64(init))); /* assuming it's an valid pointer and holding enough space */ memcpy(SERIES_SKIP(VAL_STRUCT_DATA_BIN(out), (REBCNT)offset), ptr, field->size * field->dimension); } else if (IS_BLOCK(init)) { REBCNT n = 0; if (VAL_LEN(init) != field->dimension) raise Error_Invalid_Arg(init); /* assign */ for (n = 0; n < field->dimension; n ++) { if (!assign_scalar(&VAL_STRUCT(out), field, n, VAL_BLK_SKIP(init, n))) { //RL_Print("Failed to assign element value\n"); goto failed; } } } else raise Error_Unexpected_Type(REB_BLOCK, VAL_TYPE(blk)); } else { /* scalar */ if (!assign_scalar(&VAL_STRUCT(out), field, 0, init)) { //RL_Print("Failed to assign scalar value\n"); goto failed; } } } else if (raw_addr == 0) {
IGL_INLINE void igl::copyleft::cgal::propagate_winding_numbers( const Eigen::PlainObjectBase<DerivedV>& V, const Eigen::PlainObjectBase<DerivedF>& F, const Eigen::PlainObjectBase<DerivedL>& labels, Eigen::PlainObjectBase<DerivedW>& W) { const size_t num_faces = F.rows(); //typedef typename DerivedF::Scalar Index; Eigen::MatrixXi E, uE; Eigen::VectorXi EMAP; std::vector<std::vector<size_t> > uE2E; igl::unique_edge_map(F, E, uE, EMAP, uE2E); if (!propagate_winding_numbers_helper::is_orientable(F, uE, uE2E)) { std::cerr << "Input mesh is not orientable!" << std::endl; } Eigen::VectorXi P; const size_t num_patches = igl::extract_manifold_patches(F, EMAP, uE2E, P); DerivedW per_patch_cells; const size_t num_cells = igl::copyleft::cgal::extract_cells( V, F, P, E, uE, uE2E, EMAP, per_patch_cells); typedef std::tuple<size_t, bool, size_t> CellConnection; std::vector<std::set<CellConnection> > cell_adjacency(num_cells); for (size_t i=0; i<num_patches; i++) { const int positive_cell = per_patch_cells(i,0); const int negative_cell = per_patch_cells(i,1); cell_adjacency[positive_cell].emplace(negative_cell, false, i); cell_adjacency[negative_cell].emplace(positive_cell, true, i); } auto save_cell = [&](const std::string& filename, size_t cell_id) { std::vector<size_t> faces; for (size_t i=0; i<num_patches; i++) { if ((per_patch_cells.row(i).array() == cell_id).any()) { for (size_t j=0; j<num_faces; j++) { if ((size_t)P[j] == i) { faces.push_back(j); } } } } Eigen::MatrixXi cell_faces(faces.size(), 3); for (size_t i=0; i<faces.size(); i++) { cell_faces.row(i) = F.row(faces[i]); } Eigen::MatrixXd vertices(V.rows(), 3); for (size_t i=0; i<(size_t)V.rows(); i++) { assign_scalar(V(i,0), vertices(i,0)); assign_scalar(V(i,1), vertices(i,1)); assign_scalar(V(i,2), vertices(i,2)); } writePLY(filename, vertices, cell_faces); }; #ifndef NDEBUG { // Check for odd cycle. Eigen::VectorXi cell_labels(num_cells); cell_labels.setZero(); Eigen::VectorXi parents(num_cells); parents.setConstant(-1); auto trace_parents = [&](size_t idx) { std::list<size_t> path; path.push_back(idx); while ((size_t)parents[path.back()] != path.back()) { path.push_back(parents[path.back()]); } return path; }; for (size_t i=0; i<num_cells; i++) { if (cell_labels[i] == 0) { cell_labels[i] = 1; std::queue<size_t> Q; Q.push(i); parents[i] = i; while (!Q.empty()) { size_t curr_idx = Q.front(); Q.pop(); int curr_label = cell_labels[curr_idx]; for (const auto& neighbor : cell_adjacency[curr_idx]) { if (cell_labels[std::get<0>(neighbor)] == 0) { cell_labels[std::get<0>(neighbor)] = curr_label * -1; Q.push(std::get<0>(neighbor)); parents[std::get<0>(neighbor)] = curr_idx; } else { if (cell_labels[std::get<0>(neighbor)] != curr_label * -1) { std::cerr << "Odd cell cycle detected!" << std::endl; auto path = trace_parents(curr_idx); path.reverse(); auto path2 = trace_parents(std::get<0>(neighbor)); path.insert(path.end(), path2.begin(), path2.end()); for (auto cell_id : path) { std::cout << cell_id << " "; std::stringstream filename; filename << "cell_" << cell_id << ".ply"; save_cell(filename.str(), cell_id); } std::cout << std::endl; } assert(cell_labels[std::get<0>(neighbor)] == curr_label * -1); } } } } } } #endif size_t outer_facet; bool flipped; Eigen::VectorXi I; I.setLinSpaced(num_faces, 0, num_faces-1); igl::copyleft::cgal::outer_facet(V, F, I, outer_facet, flipped); const size_t outer_patch = P[outer_facet]; const size_t infinity_cell = per_patch_cells(outer_patch, flipped?1:0); Eigen::VectorXi patch_labels(num_patches); const int INVALID = std::numeric_limits<int>::max(); patch_labels.setConstant(INVALID); for (size_t i=0; i<num_faces; i++) { if (patch_labels[P[i]] == INVALID) { patch_labels[P[i]] = labels[i]; } else { assert(patch_labels[P[i]] == labels[i]); } } assert((patch_labels.array() != INVALID).all()); const size_t num_labels = patch_labels.maxCoeff()+1; Eigen::MatrixXi per_cell_W(num_cells, num_labels); per_cell_W.setConstant(INVALID); per_cell_W.row(infinity_cell).setZero(); std::queue<size_t> Q; Q.push(infinity_cell); while (!Q.empty()) { size_t curr_cell = Q.front(); Q.pop(); for (const auto& neighbor : cell_adjacency[curr_cell]) { size_t neighbor_cell, patch_idx; bool direction; std::tie(neighbor_cell, direction, patch_idx) = neighbor; if ((per_cell_W.row(neighbor_cell).array() == INVALID).any()) { per_cell_W.row(neighbor_cell) = per_cell_W.row(curr_cell); for (size_t i=0; i<num_labels; i++) { int inc = (patch_labels[patch_idx] == (int)i) ? (direction ? -1:1) :0; per_cell_W(neighbor_cell, i) = per_cell_W(curr_cell, i) + inc; } Q.push(neighbor_cell); } else { #ifndef NDEBUG for (size_t i=0; i<num_labels; i++) { if ((int)i == patch_labels[patch_idx]) { int inc = direction ? -1:1; assert(per_cell_W(neighbor_cell, i) == per_cell_W(curr_cell, i) + inc); } else { assert(per_cell_W(neighbor_cell, i) == per_cell_W(curr_cell, i)); } } #endif } } } W.resize(num_faces, num_labels*2); for (size_t i=0; i<num_faces; i++) { const size_t patch = P[i]; const size_t positive_cell = per_patch_cells(patch, 0); const size_t negative_cell = per_patch_cells(patch, 1); for (size_t j=0; j<num_labels; j++) { W(i,j*2 ) = per_cell_W(positive_cell, j); W(i,j*2+1) = per_cell_W(negative_cell, j); } } }
IGL_INLINE void igl::copyleft::cgal::propagate_winding_numbers( const Eigen::PlainObjectBase<DerivedV>& V, const Eigen::PlainObjectBase<DerivedF>& F, const Eigen::PlainObjectBase<DerivedL>& labels, Eigen::PlainObjectBase<DerivedW>& W) { #ifdef PROPAGATE_WINDING_NUMBER_TIMING const auto & tictoc = []() { static double t_start = igl::get_seconds(); double diff = igl::get_seconds()-t_start; t_start += diff; return diff; }; tictoc(); #endif const size_t num_faces = F.rows(); //typedef typename DerivedF::Scalar Index; Eigen::MatrixXi E, uE; Eigen::VectorXi EMAP; std::vector<std::vector<size_t> > uE2E; igl::unique_edge_map(F, E, uE, EMAP, uE2E); if (!propagate_winding_numbers_helper::is_orientable(F, uE, uE2E)) { std::cerr << "Input mesh is not orientable!" << std::endl; } Eigen::VectorXi P; const size_t num_patches = igl::extract_manifold_patches(F, EMAP, uE2E, P); #ifdef PROPAGATE_WINDING_NUMBER_TIMING std::cout << "extract manifold patches: " << tictoc() << std::endl; #endif DerivedW per_patch_cells; const size_t num_cells = igl::copyleft::cgal::extract_cells( V, F, P, E, uE, uE2E, EMAP, per_patch_cells); #ifdef PROPAGATE_WINDING_NUMBER_TIMING std::cout << "extract cells: " << tictoc() << std::endl;; #endif typedef std::tuple<size_t, bool, size_t> CellConnection; std::vector<std::set<CellConnection> > cell_adjacency(num_cells); for (size_t i=0; i<num_patches; i++) { const int positive_cell = per_patch_cells(i,0); const int negative_cell = per_patch_cells(i,1); cell_adjacency[positive_cell].emplace(negative_cell, false, i); cell_adjacency[negative_cell].emplace(positive_cell, true, i); } #ifdef PROPAGATE_WINDING_NUMBER_TIMING std::cout << "cell connection: " << tictoc() << std::endl; #endif auto save_cell = [&](const std::string& filename, size_t cell_id) { std::vector<size_t> faces; for (size_t i=0; i<num_patches; i++) { if ((per_patch_cells.row(i).array() == cell_id).any()) { for (size_t j=0; j<num_faces; j++) { if ((size_t)P[j] == i) { faces.push_back(j); } } } } Eigen::MatrixXi cell_faces(faces.size(), 3); for (size_t i=0; i<faces.size(); i++) { cell_faces.row(i) = F.row(faces[i]); } Eigen::MatrixXd vertices(V.rows(), 3); for (size_t i=0; i<(size_t)V.rows(); i++) { assign_scalar(V(i,0), vertices(i,0)); assign_scalar(V(i,1), vertices(i,1)); assign_scalar(V(i,2), vertices(i,2)); } writePLY(filename, vertices, cell_faces); }; #ifndef NDEBUG { // Check for odd cycle. Eigen::VectorXi cell_labels(num_cells); cell_labels.setZero(); Eigen::VectorXi parents(num_cells); parents.setConstant(-1); auto trace_parents = [&](size_t idx) { std::list<size_t> path; path.push_back(idx); while ((size_t)parents[path.back()] != path.back()) { path.push_back(parents[path.back()]); } return path; }; for (size_t i=0; i<num_cells; i++) { if (cell_labels[i] == 0) { cell_labels[i] = 1; std::queue<size_t> Q; Q.push(i); parents[i] = i; while (!Q.empty()) { size_t curr_idx = Q.front(); Q.pop(); int curr_label = cell_labels[curr_idx]; for (const auto& neighbor : cell_adjacency[curr_idx]) { if (cell_labels[std::get<0>(neighbor)] == 0) { cell_labels[std::get<0>(neighbor)] = curr_label * -1; Q.push(std::get<0>(neighbor)); parents[std::get<0>(neighbor)] = curr_idx; } else { if (cell_labels[std::get<0>(neighbor)] != curr_label * -1) { std::cerr << "Odd cell cycle detected!" << std::endl; auto path = trace_parents(curr_idx); path.reverse(); auto path2 = trace_parents(std::get<0>(neighbor)); path.insert(path.end(), path2.begin(), path2.end()); for (auto cell_id : path) { std::cout << cell_id << " "; std::stringstream filename; filename << "cell_" << cell_id << ".ply"; save_cell(filename.str(), cell_id); } std::cout << std::endl; } // Do not fail when odd cycle is detected because the resulting // integer winding number field, although inconsistent, may still // be used if the problem region is local and embedded within a // valid volume. //assert(cell_labels[std::get<0>(neighbor)] == curr_label * -1); } } } } } #ifdef PROPAGATE_WINDING_NUMBER_TIMING std::cout << "check for odd cycle: " << tictoc() << std::endl; #endif } #endif size_t outer_facet; bool flipped; Eigen::VectorXi I; I.setLinSpaced(num_faces, 0, num_faces-1); igl::copyleft::cgal::outer_facet(V, F, I, outer_facet, flipped); #ifdef PROPAGATE_WINDING_NUMBER_TIMING std::cout << "outer facet: " << tictoc() << std::endl; #endif const size_t outer_patch = P[outer_facet]; const size_t infinity_cell = per_patch_cells(outer_patch, flipped?1:0); Eigen::VectorXi patch_labels(num_patches); const int INVALID = std::numeric_limits<int>::max(); patch_labels.setConstant(INVALID); for (size_t i=0; i<num_faces; i++) { if (patch_labels[P[i]] == INVALID) { patch_labels[P[i]] = labels[i]; } else { assert(patch_labels[P[i]] == labels[i]); } } assert((patch_labels.array() != INVALID).all()); const size_t num_labels = patch_labels.maxCoeff()+1; Eigen::MatrixXi per_cell_W(num_cells, num_labels); per_cell_W.setConstant(INVALID); per_cell_W.row(infinity_cell).setZero(); std::queue<size_t> Q; Q.push(infinity_cell); while (!Q.empty()) { size_t curr_cell = Q.front(); Q.pop(); for (const auto& neighbor : cell_adjacency[curr_cell]) { size_t neighbor_cell, patch_idx; bool direction; std::tie(neighbor_cell, direction, patch_idx) = neighbor; if ((per_cell_W.row(neighbor_cell).array() == INVALID).any()) { per_cell_W.row(neighbor_cell) = per_cell_W.row(curr_cell); for (size_t i=0; i<num_labels; i++) { int inc = (patch_labels[patch_idx] == (int)i) ? (direction ? -1:1) :0; per_cell_W(neighbor_cell, i) = per_cell_W(curr_cell, i) + inc; } Q.push(neighbor_cell); } else { #ifndef NDEBUG // Checking for winding number consistency. // This check would inevitably fail for meshes that contain open // boundary or non-orientable. However, the inconsistent winding number // field would still be useful in some cases such as when problem region // is local and embedded within the volume. This, unfortunately, is the // best we can do because the problem of computing integer winding // number is ill-defined for open and non-orientable surfaces. for (size_t i=0; i<num_labels; i++) { if ((int)i == patch_labels[patch_idx]) { int inc = direction ? -1:1; //assert(per_cell_W(neighbor_cell, i) == // per_cell_W(curr_cell, i) + inc); } else { //assert(per_cell_W(neighbor_cell, i) == // per_cell_W(curr_cell, i)); } } #endif } } } #ifdef PROPAGATE_WINDING_NUMBER_TIMING std::cout << "propagate winding number: " << tictoc() << std::endl; #endif W.resize(num_faces, num_labels*2); for (size_t i=0; i<num_faces; i++) { const size_t patch = P[i]; const size_t positive_cell = per_patch_cells(patch, 0); const size_t negative_cell = per_patch_cells(patch, 1); for (size_t j=0; j<num_labels; j++) { W(i,j*2 ) = per_cell_W(positive_cell, j); W(i,j*2+1) = per_cell_W(negative_cell, j); } } #ifdef PROPAGATE_WINDING_NUMBER_TIMING std::cout << "save result: " << tictoc() << std::endl; #endif }