static tb_void_t gb_demo_utils_mesh_tetrahedron() { // trace tb_trace_i("=========================================================================="); // init mesh gb_mesh_ref_t mesh = gb_mesh_init(tb_element_str(tb_true), tb_element_str(tb_true), tb_element_str(tb_true)); if (mesh) { // init listener gb_mesh_listener_set(mesh, gb_demo_utils_mesh_listener, mesh); gb_mesh_listener_event_add(mesh, GB_MESH_EVENT_FACE_MERGE | GB_MESH_EVENT_FACE_SPLIT | GB_MESH_EVENT_EDGE_MERGE | GB_MESH_EVENT_EDGE_SPLIT); // make a clockwise self-loop edge gb_mesh_edge_ref_t edge0 = gb_mesh_edge_make_loop(mesh, tb_false); if (edge0) { /* make a tetrahedron * * e1 * v0 --------------> v1----------------- * | . rface | | * e0 | . | e2 lface | * | face1 e5 . | | e4 * v3 <-------------- v2 | * | e3 | * | | face0 * <----------------------------------- * */ gb_mesh_edge_ref_t edge1 = gb_mesh_edge_insert(mesh, edge0, edge0); gb_mesh_edge_ref_t edge2 = gb_mesh_edge_insert(mesh, edge1, edge0); gb_mesh_edge_ref_t edge3 = gb_mesh_edge_insert(mesh, edge2, edge0); // save face name gb_mesh_face_data_set(mesh, gb_mesh_edge_lface(edge0), "lface"); gb_mesh_face_data_set(mesh, gb_mesh_edge_rface(edge0), "rface"); gb_mesh_edge_ref_t edge4 = gb_mesh_edge_connect(mesh, edge1, edge0); gb_mesh_edge_ref_t edge5 = gb_mesh_edge_connect(mesh, gb_mesh_edge_sym(edge3), gb_mesh_edge_sym(edge0)); // save face name gb_mesh_face_data_set(mesh, gb_mesh_edge_lface(edge4), "face0"); gb_mesh_face_data_set(mesh, gb_mesh_edge_lface(edge5), "face1"); // save edge name gb_mesh_edge_data_set(mesh, edge0, "e0"); gb_mesh_edge_data_set(mesh, edge1, "e1"); gb_mesh_edge_data_set(mesh, edge2, "e2"); gb_mesh_edge_data_set(mesh, edge3, "e3"); gb_mesh_edge_data_set(mesh, edge4, "e4"); gb_mesh_edge_data_set(mesh, edge5, "e5"); // save vertex name gb_mesh_vertex_data_set(mesh, gb_mesh_edge_dst(edge0), "v0"); gb_mesh_vertex_data_set(mesh, gb_mesh_edge_dst(edge1), "v1"); gb_mesh_vertex_data_set(mesh, gb_mesh_edge_dst(edge2), "v2"); gb_mesh_vertex_data_set(mesh, gb_mesh_edge_dst(edge3), "v3"); #ifdef __gb_debug__ // trace tb_trace_i(""); tb_trace_i("tetrahedron: make"); // check mesh gb_mesh_check(mesh); // dump mesh gb_mesh_dump(mesh); #endif // delete two gb_mesh_edge_delete(mesh, edge4); gb_mesh_edge_delete(mesh, edge5); #ifdef __gb_debug__ // trace tb_trace_i(""); tb_trace_i("tetrahedron: kill"); // check mesh gb_mesh_check(mesh); // dump mesh gb_mesh_dump(mesh); #endif // remove all gb_mesh_edge_remove(mesh, edge2); gb_mesh_edge_remove(mesh, edge3); gb_mesh_edge_remove(mesh, edge0); gb_mesh_edge_remove(mesh, edge1); // check tb_assert_abort(gb_mesh_is_empty(mesh)); } // exit mesh gb_mesh_exit(mesh); } }
/* ////////////////////////////////////////////////////////////////////////////////////// * implementation */ tb_bool_t gb_tessellator_mesh_make(gb_tessellator_impl_t* impl, gb_polygon_ref_t polygon) { // check tb_assert(impl && polygon); // the points gb_point_ref_t points = polygon->points; tb_uint16_t const* counts = polygon->counts; tb_assert_and_check_return_val(points && counts, tb_false); // not exists mesh? if (!impl->mesh) { // init func tb_element_t edge_element = tb_element_mem(sizeof(gb_tessellator_edge_t), tb_null, tb_null); tb_element_t face_element = tb_element_mem(sizeof(gb_tessellator_face_t), tb_null, tb_null); tb_element_t vertex_element = tb_element_mem(sizeof(gb_tessellator_vertex_t), tb_null, tb_null); #ifdef __gb_debug__ // init func cstr for gb_mesh_dump edge_element.cstr = gb_tessellator_edge_cstr; face_element.cstr = gb_tessellator_face_cstr; vertex_element.cstr = gb_tessellator_vertex_cstr; #endif // init mesh impl->mesh = gb_mesh_init(edge_element, face_element, vertex_element); /* init the order * * the new edges/faces/vertice will be inserted to the head of list */ gb_mesh_edge_order_set(impl->mesh, GB_MESH_ORDER_INSERT_HEAD); gb_mesh_face_order_set(impl->mesh, GB_MESH_ORDER_INSERT_HEAD); gb_mesh_vertex_order_set(impl->mesh, GB_MESH_ORDER_INSERT_HEAD); // init listener gb_mesh_listener_set(impl->mesh, gb_tessellator_listener, impl->mesh); gb_mesh_listener_event_add(impl->mesh, GB_MESH_EVENT_FACE_SPLIT | GB_MESH_EVENT_EDGE_SPLIT); } // check gb_mesh_ref_t mesh = impl->mesh; tb_assert_and_check_return_val(mesh, tb_false); // clear mesh first gb_mesh_clear(mesh); // done gb_point_ref_t point = tb_null; tb_uint16_t count = *counts++; tb_size_t index = 0; gb_mesh_edge_ref_t edge = tb_null; gb_mesh_edge_ref_t edge_first = tb_null; while (index < count) { // the point point = points++; // first point? if (!index) { // must be closed contour tb_assertf(gb_point_eq(point, point + count - 1), "this contour(%lu: %{point} => %{point}) is not closed!", count, point, point + count - 1); // clear the edge edge = tb_null; // clear the first edge edge_first = tb_null; // trace tb_trace_d("move_to: %{point}", point); } // closed? else if (index + 1 == count) { // trace tb_trace_d("closed: %{point}", point); // connect an edge to the first edge edge = gb_mesh_edge_connect(mesh, edge, edge_first); // init edge.faces.inside gb_tessellator_face_inside_set(gb_mesh_edge_lface(edge), 0); gb_tessellator_face_inside_set(gb_mesh_edge_rface(edge), 0); } else { // trace tb_trace_d("line_to: %{point}", point); // exists the first edge? if (edge_first) { // append an edge edge = gb_mesh_edge_append(mesh, edge); } else { // make a new non-loop edge edge = gb_mesh_edge_make(mesh); // save the first edge edge_first = edge; } } // has new edge? if (edge) { // init edge.winding gb_tessellator_edge_winding_set(edge, 1); gb_tessellator_edge_winding_set(gb_mesh_edge_sym(edge), -1); // init edge.region gb_tessellator_edge_region_set(edge, tb_null); gb_tessellator_edge_region_set(gb_mesh_edge_sym(edge), tb_null); // init edge.dst gb_tessellator_vertex_point_set(gb_mesh_edge_dst(edge), point); } // next point index++; // next polygon if (index == count) { // next count = *counts++; index = 0; } } #ifdef __gb_debug__ // check mesh gb_mesh_check(mesh); #endif // ok? return !gb_mesh_is_empty(mesh); }
/* make triangulation region * * TODO need optimization, maybe generate some degenerated triangles * * the region of this face must be horizontal monotone and counter-clockwise loop * * before: * * ccw * <------------ * | | * * 1 * . . right * left . 2 * . . * . . * 3 4 * . . * . . * 5 . * . . * . . * . . * 6 . * . . * . 7 * . . * 8 . * . 9 * . . * 10 11 * . . * . . * 12 * * | | * | | * \ / \ / * * * after: * 1 * . . R2 * L1 . . 2 * . .L4 . * . . . R3 * 3 . . R6 4 * . . . * L5 . .L8 . * 5 . * . . . R7 * L9 . R11 . * . . . * 6 . R12 . * . . . . * L10 . . 7 * . L14 . . * 8 . . R13 * . R16 . 9 * L15 . . * 10 L18 . 11 * . . * L19 . . R17 * 12 * */ static tb_void_t gb_tessellator_triangulation_make_face(gb_tessellator_impl_t* impl, gb_mesh_face_ref_t face) { // check tb_assert_abort(impl && face); // the mesh gb_mesh_ref_t mesh = impl->mesh; tb_assert_abort(mesh); // the face edge gb_mesh_edge_ref_t edge = gb_mesh_face_edge(face); // must be triangle region at least tb_assert_abort(edge && gb_mesh_edge_lnext(edge) != edge && gb_mesh_edge_lnext(gb_mesh_edge_lnext(edge)) != edge); /* get the uppermost left edge * * @note the face edge has been optimizated when we made monotone polygon. * * . * left . . right * . . * . . * . . * . . */ gb_mesh_edge_ref_t left = edge; while (gb_tessellator_edge_go_down_(left)) left = gb_mesh_edge_lprev(left); while (gb_tessellator_edge_go_up_(left)) left = gb_mesh_edge_lnext(left); // get the uppermost right edge gb_mesh_edge_ref_t right = gb_mesh_edge_lprev(left); // done while (gb_mesh_edge_lnext(left) != right) { /* the right edge is too lower? done some left edges * * . * left . . right * dst . . * . . org * . . * . . */ if (gb_tessellator_vertex_in_top_or_horizontal_(gb_mesh_edge_dst(left), gb_mesh_edge_org(right))) { /* done some left edges * * go up? connect it * * . * . . * . . * . . * L . L1 . . * . . . * . . . * . . . . . . . * L2 . * . R * . * * * on left? connect it * * TODO: will generate some degenerated triangles * * . * . . * on left?. . . * . . * . . . * . . * L . . . R * .. . * . . * . . * . . * . * . */ while ( gb_mesh_edge_lnext(right) != left && ( gb_tessellator_edge_go_up_(gb_mesh_edge_lprev(left)) || gb_tessellator_vertex_on_edge_or_left_( gb_mesh_edge_org(left) , gb_mesh_edge_org(gb_mesh_edge_lprev(left)) , gb_mesh_edge_dst(left)))) { // connect it edge = gb_mesh_edge_connect(mesh, left, gb_mesh_edge_lprev(left)); tb_assert_abort_and_check_return(edge); // update the left edge left = gb_mesh_edge_sym(edge); } // the next left edge left = gb_mesh_edge_lnext(left); } /* the left edge is too lower? done some right edges * * . * left . . right * . . org * dst . . * . . * . . */ else { /* done some right edges * * go down? connect it * * . * . . * . . * . . R * . . R1 . * . . . * . R2 . . * . . . . . . . . . * . * L . * . * * on right? connect it * * TODO: will generate some degenerated triangles * * . * . . * . . . * . . . * . . . on right? * . . . * L . . . R * . . . * . . * . . * . . * . * . */ while ( gb_mesh_edge_lnext(right) != left && ( gb_tessellator_edge_go_down_(gb_mesh_edge_lnext(right)) || gb_tessellator_vertex_on_edge_or_right_(gb_mesh_edge_dst(right) , gb_mesh_edge_dst(gb_mesh_edge_lnext(right)) , gb_mesh_edge_org(right)))) { // connect it edge = gb_mesh_edge_connect(mesh, gb_mesh_edge_lnext(right), right); tb_assert_abort_and_check_return(edge); // update the right edge right = gb_mesh_edge_sym(edge); } // the next right edge right = gb_mesh_edge_lprev(right); } } // the last region must be triangle at least tb_assert_abort(gb_mesh_edge_lnext(right) != left); /* tessellate the remaining region * * . . . . * . . * . . * . . * left . . right * . * */ while (gb_mesh_edge_lnext(gb_mesh_edge_lnext(right)) != left) { // connect it edge = gb_mesh_edge_connect(mesh, gb_mesh_edge_lnext(right), right); tb_assert_abort_and_check_return(edge); // the next edge right = gb_mesh_edge_sym(edge); } }