test_emboss.cpp 55 KB

1234567891011121314151617181920212223242526272829303132333435363738394041424344454647484950515253545556575859606162636465666768697071727374757677787980818283848586878889909192939495969798991001011021031041051061071081091101111121131141151161171181191201211221231241251261271281291301311321331341351361371381391401411421431441451461471481491501511521531541551561571581591601611621631641651661671681691701711721731741751761771781791801811821831841851861871881891901911921931941951961971981992002012022032042052062072082092102112122132142152162172182192202212222232242252262272282292302312322332342352362372382392402412422432442452462472482492502512522532542552562572582592602612622632642652662672682692702712722732742752762772782792802812822832842852862872882892902912922932942952962972982993003013023033043053063073083093103113123133143153163173183193203213223233243253263273283293303313323333343353363373383393403413423433443453463473483493503513523533543553563573583593603613623633643653663673683693703713723733743753763773783793803813823833843853863873883893903913923933943953963973983994004014024034044054064074084094104114124134144154164174184194204214224234244254264274284294304314324334344354364374384394404414424434444454464474484494504514524534544554564574584594604614624634644654664674684694704714724734744754764774784794804814824834844854864874884894904914924934944954964974984995005015025035045055065075085095105115125135145155165175185195205215225235245255265275285295305315325335345355365375385395405415425435445455465475485495505515525535545555565575585595605615625635645655665675685695705715725735745755765775785795805815825835845855865875885895905915925935945955965975985996006016026036046056066076086096106116126136146156166176186196206216226236246256266276286296306316326336346356366376386396406416426436446456466476486496506516526536546556566576586596606616626636646656666676686696706716726736746756766776786796806816826836846856866876886896906916926936946956966976986997007017027037047057067077087097107117127137147157167177187197207217227237247257267277287297307317327337347357367377387397407417427437447457467477487497507517527537547557567577587597607617627637647657667677687697707717727737747757767777787797807817827837847857867877887897907917927937947957967977987998008018028038048058068078088098108118128138148158168178188198208218228238248258268278288298308318328338348358368378388398408418428438448458468478488498508518528538548558568578588598608618628638648658668678688698708718728738748758768778788798808818828838848858868878888898908918928938948958968978988999009019029039049059069079089099109119129139149159169179189199209219229239249259269279289299309319329339349359369379389399409419429439449459469479489499509519529539549559569579589599609619629639649659669679689699709719729739749759769779789799809819829839849859869879889899909919929939949959969979989991000100110021003100410051006100710081009101010111012101310141015101610171018101910201021102210231024102510261027102810291030103110321033103410351036103710381039104010411042104310441045104610471048104910501051105210531054105510561057105810591060106110621063106410651066106710681069107010711072107310741075107610771078107910801081108210831084108510861087108810891090109110921093109410951096109710981099110011011102110311041105110611071108110911101111111211131114111511161117111811191120112111221123112411251126112711281129113011311132113311341135113611371138113911401141114211431144114511461147114811491150115111521153115411551156115711581159116011611162116311641165116611671168116911701171117211731174117511761177117811791180118111821183118411851186118711881189119011911192119311941195119611971198119912001201120212031204120512061207120812091210121112121213121412151216121712181219122012211222122312241225122612271228122912301231123212331234123512361237123812391240124112421243124412451246124712481249125012511252125312541255125612571258125912601261126212631264126512661267126812691270127112721273127412751276127712781279128012811282128312841285128612871288128912901291129212931294129512961297129812991300130113021303130413051306130713081309131013111312131313141315131613171318131913201321132213231324132513261327132813291330133113321333133413351336133713381339134013411342134313441345134613471348134913501351135213531354
  1. #include <catch2/catch.hpp>
  2. #include <libslic3r/Emboss.hpp>
  3. #include <libslic3r/SVG.hpp> // only debug visualization
  4. #include <optional>
  5. #include <libslic3r/AABBTreeIndirect.hpp>
  6. #include <libslic3r/Utils.hpp> // for next_highest_power_of_2()
  7. #include <libslic3r/IntersectionPoints.hpp>
  8. using namespace Slic3r;
  9. namespace Private{
  10. // calculate multiplication of ray dir to intersect - inspired by
  11. // segment_segment_intersection when ray dir is normalized retur distance from
  12. // ray point to intersection No value mean no intersection
  13. std::optional<double> ray_segment_intersection(const Vec2d &r_point,
  14. const Vec2d &r_dir,
  15. const Vec2d &s0,
  16. const Vec2d &s1)
  17. {
  18. auto denominate = [](const Vec2d &v0, const Vec2d &v1) -> double {
  19. return v0.x() * v1.y() - v1.x() * v0.y();
  20. };
  21. Vec2d segment_dir = s1 - s0;
  22. double d = denominate(segment_dir, r_dir);
  23. if (std::abs(d) < std::numeric_limits<double>::epsilon())
  24. // Line and ray are collinear.
  25. return {};
  26. Vec2d s12 = s0 - r_point;
  27. double s_number = denominate(r_dir, s12);
  28. bool change_sign = false;
  29. if (d < 0.) {
  30. change_sign = true;
  31. d = -d;
  32. s_number = -s_number;
  33. }
  34. if (s_number < 0. || s_number > d)
  35. // Intersection outside of segment.
  36. return {};
  37. double r_number = denominate(segment_dir, s12);
  38. if (change_sign) r_number = -r_number;
  39. if (r_number < 0.)
  40. // Intersection before ray start.
  41. return {};
  42. return r_number / d;
  43. }
  44. Vec2d get_intersection(const Vec2d & point,
  45. const Vec2d & dir,
  46. const std::array<Vec2d, 3> &triangle)
  47. {
  48. std::optional<double> t;
  49. for (size_t i = 0; i < 3; ++i) {
  50. size_t i2 = i + 1;
  51. if (i2 == 3) i2 = 0;
  52. if (!t.has_value()) {
  53. t = ray_segment_intersection(point, dir, triangle[i],
  54. triangle[i2]);
  55. continue;
  56. }
  57. // small distance could be preccission inconsistance
  58. std::optional<double> t2 = ray_segment_intersection(point, dir,
  59. triangle[i],
  60. triangle[i2]);
  61. if (t2.has_value() && *t2 > *t) t = t2;
  62. }
  63. assert(t.has_value()); // Not found intersection.
  64. return point + dir * (*t);
  65. }
  66. Vec3d calc_hit_point(const igl::Hit & h,
  67. const Vec3i & triangle,
  68. const std::vector<Vec3f> &vertices)
  69. {
  70. double c1 = h.u;
  71. double c2 = h.v;
  72. double c0 = 1.0 - c1 - c2;
  73. Vec3d v0 = vertices[triangle[0]].cast<double>();
  74. Vec3d v1 = vertices[triangle[1]].cast<double>();
  75. Vec3d v2 = vertices[triangle[2]].cast<double>();
  76. return v0 * c0 + v1 * c1 + v2 * c2;
  77. }
  78. Vec3d calc_hit_point(const igl::Hit &h, indexed_triangle_set &its)
  79. {
  80. return calc_hit_point(h, its.indices[h.id], its.vertices);
  81. }
  82. } // namespace Private
  83. std::string get_font_filepath() {
  84. std::string resource_dir =
  85. std::string(TEST_DATA_DIR) + "/../../resources/";
  86. return resource_dir + "fonts/NotoSans-Regular.ttf";
  87. }
  88. // Explicit horror include (used to be implicit) - libslic3r "officialy" does not depend on imgui.
  89. #include "../../bundled_deps/imgui/imgui/imstb_truetype.h" // stbtt_fontinfo
  90. TEST_CASE("Read glyph C shape from font, stb library calls ONLY", "[Emboss]") {
  91. std::string font_path = get_font_filepath();
  92. char letter = 'C';
  93. // Read font file
  94. FILE *file = fopen(font_path.c_str(), "rb");
  95. REQUIRE(file != nullptr);
  96. // find size of file
  97. REQUIRE(fseek(file, 0L, SEEK_END) == 0);
  98. size_t size = ftell(file);
  99. REQUIRE(size != 0);
  100. rewind(file);
  101. std::vector<unsigned char> buffer(size);
  102. size_t count_loaded_bytes = fread((void *) &buffer.front(), 1, size, file);
  103. REQUIRE(count_loaded_bytes == size);
  104. // Use stb true type library
  105. int font_offset = stbtt_GetFontOffsetForIndex(buffer.data(), 0);
  106. REQUIRE(font_offset >= 0);
  107. stbtt_fontinfo font_info;
  108. REQUIRE(stbtt_InitFont(&font_info, buffer.data(), font_offset) != 0);
  109. int unicode_letter = (int) letter;
  110. int glyph_index = stbtt_FindGlyphIndex(&font_info, unicode_letter);
  111. REQUIRE(glyph_index != 0);
  112. stbtt_vertex *vertices;
  113. int num_verts = stbtt_GetGlyphShape(&font_info, glyph_index, &vertices);
  114. CHECK(num_verts > 0);
  115. free(vertices);
  116. }
  117. #include <libslic3r/Utils.hpp>
  118. TEST_CASE("Convert glyph % to model", "[Emboss]")
  119. {
  120. std::string font_path = get_font_filepath();
  121. unsigned int font_index = 0; // collection
  122. char letter = '%';
  123. float flatness = 2.;
  124. auto font = Emboss::create_font_file(font_path.c_str());
  125. REQUIRE(font != nullptr);
  126. std::optional<Emboss::Glyph> glyph =
  127. Emboss::letter2glyph(*font, font_index, letter, flatness);
  128. REQUIRE(glyph.has_value());
  129. ExPolygons shape = glyph->shape;
  130. REQUIRE(!shape.empty());
  131. float z_depth = 1.f;
  132. Emboss::ProjectZ projection(z_depth);
  133. indexed_triangle_set its = Emboss::polygons2model(shape, projection);
  134. CHECK(!its.indices.empty());
  135. }
  136. //#define VISUALIZE
  137. #ifdef VISUALIZE
  138. TEST_CASE("Visualize glyph from font", "[Emboss]")
  139. {
  140. std::string font_path = "C:/data/ALIENATO.TTF";
  141. std::string text = "i";
  142. Emboss::FontFileWithCache font(
  143. Emboss::create_font_file(font_path.c_str()));
  144. REQUIRE(font.has_value());
  145. FontProp fp;
  146. fp.size_in_mm = 8;
  147. fp.emboss = 4;
  148. ExPolygons shapes = Emboss::text2shapes(font, text.c_str(), fp);
  149. // char letter = 'i';
  150. // unsigned int font_index = 0; // collection
  151. // float flatness = 5;
  152. // auto glyph = Emboss::letter2glyph(*font.font_file, font_index, letter,
  153. // flatness); ExPolygons shapes2 = glyph->shape; { SVG
  154. //svg("C:/data/temp/stored_letter.svg", get_extents(shapes2));
  155. //svg.draw(shapes2); } // debug shape
  156. REQUIRE(!shapes.empty());
  157. //{ SVG svg("C:/data/temp/shapes.svg"); svg.draw(shapes); } // debug shape
  158. float z_depth = 100.f;
  159. Emboss::ProjectZ projection(z_depth);
  160. indexed_triangle_set its = Emboss::polygons2model(shapes, projection);
  161. its_write_obj(its, "C:/data/temp/bad_glyph.obj");
  162. CHECK(!its.indices.empty());
  163. TriangleMesh tm(its);
  164. auto s = tm.stats();
  165. }
  166. #endif // VISUALIZE
  167. #include "test_utils.hpp"
  168. #include <nanosvg/nanosvg.h> // load SVG file
  169. #include <libslic3r/NSVGUtils.hpp>
  170. #include <libslic3r/IntersectionPoints.hpp>
  171. ExPolygons heal_and_check(const Polygons &polygons)
  172. {
  173. IntersectionsLines intersections_prev = get_intersections(polygons);
  174. Points polygons_points = to_points(polygons);
  175. Points duplicits_prev = collect_duplicates(polygons_points);
  176. auto [shape, success] = Emboss::heal_polygons(polygons);
  177. CHECK(success);
  178. // Is default shape for unhealabled shape?
  179. bool is_default_shape =
  180. shape.size() == 1 &&
  181. shape.front().contour.points.size() == 4 &&
  182. shape.front().holes.size() == 1 &&
  183. shape.front().holes.front().points.size() == 4 ;
  184. CHECK(!is_default_shape);
  185. IntersectionsLines intersections = get_intersections(shape);
  186. Points shape_points = to_points(shape);
  187. Points duplicits = collect_duplicates(shape_points);
  188. //{
  189. // BoundingBox bb(polygons_points);
  190. // // bb.scale(svg_scale);
  191. // SVG svg("C:/data/temp/test_visualization.svg", bb);
  192. // svg.draw(polygons, "gray"); // input
  193. // svg.draw(shape, "green"); // output
  194. // Points pts;
  195. // pts.reserve(intersections.size());
  196. // for (const Vec2d &intersection : intersections)
  197. // pts.push_back(intersection.cast<int>());
  198. // svg.draw(pts, "red", 10);
  199. // svg.draw(duplicits, "orenge", 10);
  200. //}
  201. CHECK(intersections.empty());
  202. CHECK(duplicits.empty());
  203. return shape;
  204. }
  205. void scale(Polygons &polygons, double multiplicator) {
  206. for (Polygon &polygon : polygons)
  207. for (Point &p : polygon) p *= multiplicator;
  208. }
  209. Polygons load_polygons(const std::string &svg_file) {
  210. std::string file_path = TEST_DATA_DIR PATH_SEPARATOR + svg_file;
  211. NSVGimage *image = nsvgParseFromFile(file_path.c_str(), "px", 96.0f);
  212. NSVGLineParams param{1000};
  213. param.scale = 10.;
  214. Polygons polygons = to_polygons(*image, param);
  215. nsvgDelete(image);
  216. return polygons;
  217. }
  218. TEST_CASE("Heal of 'i' in ALIENATO.TTF", "[Emboss]")
  219. {
  220. // Shape loaded from svg is letter 'i' from font 'ALIENATE.TTF'
  221. std::string file_name = "contour_ALIENATO.TTF_glyph_i.svg";
  222. Polygons polygons = load_polygons(file_name);
  223. auto a = heal_and_check(polygons);
  224. Polygons scaled_shape = polygons; // copy
  225. double text_shape_scale = 0.001; // Emboss.cpp --> SHAPE_SCALE
  226. scale(scaled_shape, 1 / text_shape_scale);
  227. auto b = heal_and_check(scaled_shape);
  228. // different scale
  229. scale(scaled_shape, 10.);
  230. auto c = heal_and_check(scaled_shape);
  231. // check reverse order of points
  232. Polygons reverse_shape = polygons;
  233. for (Polygon &p : reverse_shape)
  234. std::reverse(p.points.begin(), p.points.end());
  235. auto d = heal_and_check(scaled_shape);
  236. #ifdef VISUALIZE
  237. CHECK(false);
  238. #endif // VISUALIZE
  239. }
  240. TEST_CASE("Heal of 'm' in Allura_Script.ttf", "[Emboss]")
  241. {
  242. Polygons polygons = load_polygons("contour_Allura_Script.ttf_glyph_m.svg");
  243. auto a = heal_and_check(polygons);
  244. }
  245. #include "libslic3r/NSVGUtils.hpp"
  246. TEST_CASE("Heal of svg contour overlap", "[Emboss]") {
  247. std::string svg_file = "contour_neighbor.svg";
  248. auto image = nsvgParseFromFile(TEST_DATA_DIR PATH_SEPARATOR + svg_file, "mm");
  249. NSVGLineParams param(1e10);
  250. ExPolygonsWithIds shapes = create_shape_with_ids(*image, param);
  251. Polygons polygons;
  252. for (ExPolygonsWithId &shape : shapes)
  253. polygons.push_back(shape.expoly.front().contour);
  254. auto a = heal_and_check(polygons);
  255. }
  256. // Input contour is extracted from case above "contour_neighbor.svg" with trouble shooted scale
  257. TEST_CASE("Heal of overlaping contour", "[Emboss]"){
  258. // Extracted from svg:
  259. Points contour{{2228926, 1543620}, {745002, 2065101}, {745002, 2065094}, {744990, 2065094}, {684487, 1466338},
  260. {510999, 908378}, {236555, 403250}, {-126813, -37014}, {-567074, -400382}, {-1072201, -674822},
  261. {-567074, -400378}, {-126813, -37010}, {236555, 403250}, {510999, 908382}, {684487, 1466346},
  262. {744990, 2065105}, {-2219648, 2073234}, {-2228926, -908814}, {-1646879, -2073235}};
  263. ExPolygons shapes = {ExPolygon{contour}};
  264. CHECK(Emboss::heal_expolygons(shapes));
  265. }
  266. TEST_CASE("Heal of points close to line", "[Emboss]")
  267. {
  268. std::string file_name = "points_close_to_line.svg";
  269. std::string file_path = TEST_DATA_DIR PATH_SEPARATOR + file_name;
  270. NSVGimage *image = nsvgParseFromFile(file_path.c_str(), "px", 96.0f);
  271. NSVGLineParams param{1000};
  272. param.scale = 1.;
  273. Polygons polygons = to_polygons(*image, param);
  274. nsvgDelete(image);
  275. REQUIRE(polygons.size() == 1);
  276. Polygon polygon = polygons.front();
  277. polygon.points.pop_back();// NSVG put first point as last one when polygon is closed
  278. ExPolygons expoly({ExPolygon(polygon)});
  279. CHECK(Emboss::divide_segments_for_close_point(expoly, .6));
  280. //{ SVG svg("C:/data/temp/healed.svg"); svg.draw(expoly);}
  281. CHECK(to_points(expoly).size() >= (to_points(polygon).size() + 2));
  282. }
  283. TEST_CASE("Convert text with glyph cache to model", "[Emboss]")
  284. {
  285. std::string font_path = get_font_filepath();
  286. std::string text =
  287. "Because Ford never learned to say his original name, \n\
  288. his father eventually died of shame, which is still \r\n\
  289. a terminal disease in some parts of the Galaxy.\n\r\
  290. The other kids at school nicknamed him Ix,\n\
  291. which in the language of Betelgeuse Five translates as\t\n\
  292. \"boy who is not able satisfactorily to explain what a Hrung is,\n\
  293. nor why it should choose to collapse on Betelgeuse Seven\".";
  294. float line_height = 10.f;
  295. auto font = Emboss::create_font_file(font_path.c_str());
  296. REQUIRE(font != nullptr);
  297. Emboss::FontFileWithCache ffwc(std::move(font));
  298. FontProp fp{line_height};
  299. auto was_canceled = []() { return false; };
  300. ExPolygons shapes = Emboss::text2shapes(ffwc, text.c_str(), fp, was_canceled);
  301. REQUIRE(!shapes.empty());
  302. float depth = 2.f;
  303. Emboss::ProjectZ projection(depth);
  304. indexed_triangle_set its = Emboss::polygons2model(shapes, projection);
  305. CHECK(!its.indices.empty());
  306. //its_write_obj(its, "C:/data/temp/text.obj");
  307. }
  308. TEST_CASE("Test hit point", "[AABBTreeIndirect]")
  309. {
  310. indexed_triangle_set its;
  311. its.vertices = {
  312. Vec3f(1, 1, 1),
  313. Vec3f(2, 10, 2),
  314. Vec3f(10, 0, 2),
  315. };
  316. its.indices = {Vec3i(0, 2, 1)};
  317. auto tree = AABBTreeIndirect::build_aabb_tree_over_indexed_triangle_set(
  318. its.vertices, its.indices);
  319. Vec3d ray_point(8, 1, 0);
  320. Vec3d ray_dir(0, 0, 1);
  321. igl::Hit hit;
  322. AABBTreeIndirect::intersect_ray_first_hit(its.vertices, its.indices, tree,
  323. ray_point, ray_dir, hit);
  324. Vec3d hp = Private::calc_hit_point(hit, its);
  325. CHECK(abs(hp.x() - ray_point.x()) < .1);
  326. CHECK(abs(hp.y() - ray_point.y()) < .1);
  327. }
  328. TEST_CASE("ray segment intersection", "[MeshBoolean]")
  329. {
  330. Vec2d r_point(1, 1);
  331. Vec2d r_dir(1, 0);
  332. // colinear
  333. CHECK(!Private::ray_segment_intersection(r_point, r_dir, Vec2d(0, 0), Vec2d(2, 0)).has_value());
  334. CHECK(!Private::ray_segment_intersection(r_point, r_dir, Vec2d(2, 0), Vec2d(0, 0)).has_value());
  335. // before ray
  336. CHECK(!Private::ray_segment_intersection(r_point, r_dir, Vec2d(0, 0), Vec2d(0, 2)).has_value());
  337. CHECK(!Private::ray_segment_intersection(r_point, r_dir, Vec2d(0, 2), Vec2d(0, 0)).has_value());
  338. // above ray
  339. CHECK(!Private::ray_segment_intersection(r_point, r_dir, Vec2d(2, 2), Vec2d(2, 3)).has_value());
  340. CHECK(!Private::ray_segment_intersection(r_point, r_dir, Vec2d(2, 3), Vec2d(2, 2)).has_value());
  341. // belove ray
  342. CHECK(!Private::ray_segment_intersection(r_point, r_dir, Vec2d(2, 0), Vec2d(2, -1)).has_value());
  343. CHECK(!Private::ray_segment_intersection(r_point, r_dir, Vec2d(2, -1), Vec2d(2, 0)).has_value());
  344. // intersection at [2,1] distance 1
  345. auto t1 = Private::ray_segment_intersection(r_point, r_dir, Vec2d(2, 0), Vec2d(2, 2));
  346. REQUIRE(t1.has_value());
  347. auto t2 = Private::ray_segment_intersection(r_point, r_dir, Vec2d(2, 2), Vec2d(2, 0));
  348. REQUIRE(t2.has_value());
  349. CHECK(abs(*t1 - *t2) < std::numeric_limits<double>::epsilon());
  350. }
  351. TEST_CASE("triangle intersection", "[]")
  352. {
  353. Vec2d point(1, 1);
  354. Vec2d dir(-1, 0);
  355. std::array<Vec2d, 3> triangle = {Vec2d(0, 0), Vec2d(5, 0), Vec2d(0, 5)};
  356. Vec2d i = Private::get_intersection(point, dir, triangle);
  357. CHECK(abs(i.x()) < std::numeric_limits<double>::epsilon());
  358. CHECK(abs(i.y() - 1.) < std::numeric_limits<double>::epsilon());
  359. }
  360. #if defined _WIN32
  361. #define FONT_DIR_PATH "C:/Windows/Fonts";
  362. #endif
  363. //#elif defined __linux__
  364. //#define FONT_DIR_PATH "/usr/share/fonts";
  365. //#elif defined __APPLE__
  366. //#define FONT_DIR_PATH "//System/Library/Fonts";
  367. //#endif
  368. #ifdef FONT_DIR_PATH
  369. #include <string>
  370. #include <iostream>
  371. #include <boost/filesystem.hpp>
  372. namespace fs = boost::filesystem;
  373. //#include <filesystem>
  374. //namespace fs = std::filesystem;
  375. // Check function Emboss::is_italic that exist some italic and some non-italic font.
  376. TEST_CASE("Italic check", "[Emboss]")
  377. {
  378. std::string dir_path = FONT_DIR_PATH;
  379. std::queue<std::string> dir_paths;
  380. dir_paths.push(dir_path);
  381. bool exist_italic = false;
  382. bool exist_non_italic = false;
  383. while (!dir_paths.empty()) {
  384. std::string dir_path = dir_paths.front();
  385. dir_paths.pop();
  386. for (const auto &entry : fs::directory_iterator(dir_path)) {
  387. const fs::path &act_path = entry.path();
  388. if (fs::is_directory(entry)) {
  389. dir_paths.push(act_path.string());
  390. continue;
  391. }
  392. std::string ext = act_path.extension().string();
  393. std::transform(ext.begin(), ext.end(), ext.begin(),
  394. [](unsigned char c) { return std::tolower(c); });
  395. if (ext != ".ttf") continue;
  396. std::string path_str = act_path.string();
  397. auto font_opt = Emboss::create_font_file(path_str.c_str());
  398. if (font_opt == nullptr) continue;
  399. unsigned int collection_number = 0;
  400. if (Emboss::is_italic(*font_opt, collection_number))
  401. exist_italic = true;
  402. else
  403. exist_non_italic = true;
  404. if (exist_italic && exist_non_italic) break;
  405. //std::cout << ((Emboss::is_italic(*font_opt)) ? "[yes] " : "[no ] ") << entry.path() << std::endl;
  406. }
  407. }
  408. CHECK(exist_italic);
  409. CHECK(exist_non_italic);
  410. }
  411. #endif // FONT_DIR_PATH
  412. #include "libslic3r/CutSurface.hpp"
  413. TEST_CASE("Cut surface", "[]")
  414. {
  415. std::string font_path = get_font_filepath();
  416. char letter = '%';
  417. float flatness = 2.;
  418. unsigned int font_index = 0; // collection
  419. double z_depth = 50.; // projection size
  420. auto font = Emboss::create_font_file(font_path.c_str());
  421. REQUIRE(font != nullptr);
  422. std::optional<Emboss::Glyph> glyph = Emboss::letter2glyph(*font,
  423. font_index,
  424. letter,
  425. flatness);
  426. REQUIRE(glyph.has_value());
  427. ExPolygons shape = glyph->shape;
  428. REQUIRE(!shape.empty());
  429. Transform3d tr = Transform3d::Identity();
  430. tr.translate(Vec3d(0., 0., -z_depth));
  431. double text_shape_scale = 0.001; // Emboss.cpp --> SHAPE_SCALE
  432. tr.scale(text_shape_scale);
  433. Emboss::OrthoProject cut_projection(tr, Vec3d(0., 0., z_depth));
  434. auto object = its_make_cube(782 - 49 + 50, 724 + 10 + 50, 5);
  435. its_translate(object, Vec3f(49 - 25, -10 - 25, -40));
  436. auto cube2 = object; // copy
  437. its_translate(cube2, Vec3f(100, -40, 7.5));
  438. its_merge(object, std::move(cube2));
  439. auto surfaces = cut_surface(shape, {object}, cut_projection, 0);
  440. CHECK(!surfaces.empty());
  441. Emboss::OrthoProject projection(Transform3d::Identity(), Vec3d(0., 0., 10.));
  442. its_translate(surfaces, Vec3f(0., 0., 10.));
  443. indexed_triangle_set its = cut2model(surfaces, projection);
  444. CHECK(!its.empty());
  445. //its_write_obj(its, "C:/data/temp/projected.obj");
  446. }
  447. #include <sstream>
  448. #include <cereal/cereal.hpp>
  449. #include <cereal/archives/binary.hpp>
  450. TEST_CASE("UndoRedo TextConfiguration serialization", "[Emboss]")
  451. {
  452. TextConfiguration tc;
  453. tc.text = "Dovede-li se člověk zasmát sám sobě, nevyjde ze smíchu po celý život.";
  454. EmbossStyle& es = tc.style;
  455. es.name = "Seneca";
  456. es.path = "Simply the best";
  457. es.type = EmbossStyle::Type::file_path;
  458. FontProp &fp = es.prop;
  459. fp.char_gap = 3;
  460. fp.line_gap = 7;
  461. fp.boldness = 2.3f;
  462. fp.skew = 4.5f;
  463. fp.collection_number = 13;
  464. fp.size_in_mm= 6.7f;
  465. std::stringstream ss; // any stream can be used
  466. {
  467. cereal::BinaryOutputArchive oarchive(ss); // Create an output archive
  468. oarchive(tc);
  469. } // archive goes out of scope, ensuring all contents are flushed
  470. TextConfiguration tc_loaded;
  471. {
  472. cereal::BinaryInputArchive iarchive(ss); // Create an input archive
  473. iarchive(tc_loaded);
  474. }
  475. CHECK(tc.style == tc_loaded.style);
  476. CHECK(tc.text == tc_loaded.text);
  477. }
  478. #include "libslic3r/EmbossShape.hpp"
  479. TEST_CASE("UndoRedo EmbossShape serialization", "[Emboss]")
  480. {
  481. EmbossShape emboss;
  482. emboss.shapes_with_ids = {{0, {{{0, 0}, {10, 0}, {10, 10}, {0, 10}}, {{5, 5}, {6, 5}, {6, 6}, {5, 6}}}}};
  483. emboss.scale = 2.;
  484. emboss.projection.depth = 5.;
  485. emboss.projection.use_surface = true;
  486. emboss.fix_3mf_tr = Transform3d::Identity();
  487. emboss.svg_file = EmbossShape::SvgFile{};
  488. emboss.svg_file->path = "Everything starts somewhere, though many physicists disagree.\
  489. But people have always been dimly aware of the problem with the start of things.\
  490. They wonder how the snowplough driver gets to work,\
  491. or how the makers of dictionaries look up the spelling of words.";
  492. emboss.svg_file->path_in_3mf = "Všechno někde začíná, i když mnoho fyziků nesouhlasí.\
  493. Ale lidé si vždy jen matně uvědomovali problém se začátkem věcí.\
  494. Zajímalo je, jak se řidič sněžného pluhu dostane do práce\
  495. nebo jak tvůrci slovníků vyhledávají pravopis slov.";
  496. emboss.svg_file->file_data = std::make_unique<std::string>("cite: Terry Pratchett");
  497. std::stringstream ss; // any stream can be used
  498. {
  499. cereal::BinaryOutputArchive oarchive(ss); // Create an output archive
  500. oarchive(emboss);
  501. } // archive goes out of scope, ensuring all contents are flushed
  502. EmbossShape emboss_loaded;
  503. {
  504. cereal::BinaryInputArchive iarchive(ss); // Create an input archive
  505. iarchive(emboss_loaded);
  506. }
  507. CHECK(emboss.shapes_with_ids.front().expoly == emboss_loaded.shapes_with_ids.front().expoly);
  508. CHECK(emboss.scale == emboss_loaded.scale);
  509. CHECK(emboss.projection.depth == emboss_loaded.projection.depth);
  510. CHECK(emboss.projection.use_surface == emboss_loaded.projection.use_surface);
  511. CHECK(emboss.svg_file->path == emboss_loaded.svg_file->path);
  512. CHECK(emboss.svg_file->path_in_3mf == emboss_loaded.svg_file->path_in_3mf);
  513. }
  514. #include <CGAL/Polygon_mesh_processing/corefinement.h>
  515. #include <CGAL/Exact_integer.h>
  516. #include <CGAL/Surface_mesh.h>
  517. #include <CGAL/Cartesian_converter.h>
  518. /// <summary>
  519. /// Distiguish point made by shape(Expolygon)
  520. /// Referencing an ExPolygon contour plus a vertex base of the contour.
  521. /// Used for adressing Vertex of mesh created by extrude ExPolygons
  522. /// </summary>
  523. struct ShapesVertexId {
  524. // Index of an ExPolygon in ExPolygons.
  525. int32_t expoly{ -1 };
  526. // Index of a contour in ExPolygon.
  527. // 0 - outer contour, >0 - hole
  528. int32_t contour{ -1 };
  529. // Base of the zero'th point of a contour in text mesh.
  530. // There are two vertices (front and rear) created for each contour,
  531. // thus there are 2x more vertices in text mesh than the number of contour points.
  532. int32_t vertex_base{ -1 };
  533. };
  534. /// <summary>
  535. /// IntersectingElemnt
  536. ///
  537. /// Adress polygon inside of ExPolygon
  538. /// Keep information about source of vertex:
  539. /// - from face (one of 2 possible)
  540. /// - from edge (one of 2 possible)
  541. ///
  542. /// V1~~~~V2
  543. /// : f1 /|
  544. /// : / |
  545. /// : /e1|
  546. /// : / |e2
  547. /// :/ f2 |
  548. /// V1'~~~V2'
  549. ///
  550. /// | .. edge
  551. /// / .. edge
  552. /// : .. foreign edge - neighbor
  553. /// ~ .. no care edge - idealy should not cross model
  554. /// V1,V1' .. projected 2d point to 3d
  555. /// V2,V2' .. projected 2d point to 3d
  556. ///
  557. /// f1 .. text_face_1 (triangle face made by side of shape contour)
  558. /// f2 .. text_face_2
  559. /// e1 .. text_edge_1 (edge on side of face made by side of shape contour)
  560. /// e2 .. text_edge_2
  561. ///
  562. /// </summary>
  563. struct IntersectingElemnt
  564. {
  565. // Index into vector of ShapeVertexId
  566. // describe point on shape contour
  567. int32_t vertex_index{-1};
  568. // index of point in Polygon contour
  569. int32_t point_index{-1};
  570. // vertex or edge ID, where edge ID is the index of the source point.
  571. // There are 4 consecutive indices generated for a single glyph edge:
  572. // 0th - 1st text edge (straight)
  573. // 1th - 1st text face
  574. // 2nd - 2nd text edge (diagonal)
  575. // 3th - 2nd text face
  576. // Type of intersecting element from extruded shape( 3d )
  577. enum class Type {
  578. edge_1 = 0,
  579. face_1 = 1,
  580. edge_2 = 2,
  581. face_2 = 3,
  582. undefined = 4
  583. } type = Type::undefined;
  584. };
  585. namespace Slic3r::MeshBoolean::cgal2 {
  586. namespace CGALProc = CGAL::Polygon_mesh_processing;
  587. namespace CGALParams = CGAL::Polygon_mesh_processing::parameters;
  588. using EpicKernel = CGAL::Exact_predicates_inexact_constructions_kernel;
  589. using _EpicMesh = CGAL::Surface_mesh<EpicKernel::Point_3>;
  590. // using EpecKernel = CGAL::Exact_predicates_exact_constructions_kernel;
  591. // using _EpecMesh = CGAL::Surface_mesh<EpecKernel::Point_3>;
  592. using CGALMesh = _EpicMesh;
  593. /// <summary>
  594. /// Convert triangle mesh model to CGAL Surface_mesh
  595. /// Add property map for source face index
  596. /// </summary>
  597. /// <param name="its">Model</param>
  598. /// <param name="face_map_name">Property map name for store conversion from CGAL face to index to its</param>
  599. /// <returns>CGAL mesh - half edge mesh</returns>
  600. CGALMesh to_cgal(const indexed_triangle_set &its,
  601. const std::string &face_map_name)
  602. {
  603. CGALMesh result;
  604. if (its.empty()) return result;
  605. const std::vector<stl_vertex> &V = its.vertices;
  606. const std::vector<stl_triangle_vertex_indices> &F = its.indices;
  607. // convert from CGAL face to its face
  608. auto face_map = result.add_property_map<CGALMesh::Face_index, int32_t>(face_map_name).first;
  609. size_t vertices_count = V.size();
  610. size_t edges_count = (F.size() * 3) / 2;
  611. size_t faces_count = F.size();
  612. result.reserve(vertices_count, edges_count, faces_count);
  613. for (auto &v : V)
  614. result.add_vertex(typename CGALMesh::Point{v.x(), v.y(), v.z()});
  615. using VI = typename CGALMesh::Vertex_index;
  616. for (auto &f : F)
  617. {
  618. auto fid = result.add_face(VI(f(0)), VI(f(1)), VI(f(2)));
  619. // index of face in source triangle mesh
  620. int32_t index = static_cast<int32_t>(&f - &F.front());
  621. face_map[fid] = index;
  622. }
  623. return result;
  624. }
  625. /// <summary>
  626. /// Covert 2d shape (e.g. Glyph) to CGAL model
  627. /// </summary>
  628. /// <param name="shape">2d shape to project</param>
  629. /// <param name="projection">Define transformation 2d point into 3d</param>
  630. /// <param name="shape_id">Identify shape</param>
  631. /// <param name="edge_shape_map_name">Name of property map to store conversion from edge to contour</param>
  632. /// <param name="face_shape_map_name">Name of property map to store conversion from face to contour</param>
  633. /// <param name="contour_indices">Identify point on shape contour</param>
  634. /// <returns>CGAL model of extruded shape</returns>
  635. CGALMesh to_cgal(const ExPolygons &shape,
  636. const Slic3r::Emboss::IProjection &projection,
  637. int32_t shape_id,
  638. const std::string &edge_shape_map_name,
  639. const std::string &face_shape_map_name,
  640. std::vector<ShapesVertexId> &contour_indices)
  641. {
  642. CGALMesh result;
  643. if (shape.empty()) return result;
  644. auto edge_shape_map = result.add_property_map<CGALMesh::Edge_index, IntersectingElemnt>(edge_shape_map_name).first;
  645. auto face_shape_map = result.add_property_map<CGALMesh::Face_index, IntersectingElemnt>(face_shape_map_name).first;
  646. std::vector<CGALMesh::Vertex_index> indices;
  647. auto insert_contour = [&projection, &indices , &result, &contour_indices, &edge_shape_map, &face_shape_map](const Polygon& polygon, int32_t iexpoly, int32_t id) {
  648. indices.clear();
  649. indices.reserve(polygon.points.size() * 2);
  650. size_t num_vertices_old = result.number_of_vertices();
  651. int32_t vertex_index = static_cast<int32_t>(contour_indices.size());
  652. contour_indices.push_back({iexpoly, id, int32_t(num_vertices_old) });
  653. for (const Point& p2 : polygon.points) {
  654. auto p = projection.create_front_back(p2);
  655. auto vi = result.add_vertex(typename CGALMesh::Point{ p.first.x(), p.first.y(), p.first.z() });
  656. assert((size_t)vi == indices.size() + num_vertices_old);
  657. indices.emplace_back(vi);
  658. vi = result.add_vertex(typename CGALMesh::Point{ p.second.x(), p.second.y(), p.second.z() });
  659. assert((size_t)vi == indices.size() + num_vertices_old);
  660. indices.emplace_back(vi);
  661. }
  662. int32_t contour_index = 0;
  663. for (int32_t i = 0; i < int32_t(indices.size()); i += 2) {
  664. int32_t j = (i + 2) % int32_t(indices.size());
  665. auto find_edge = [&result](CGALMesh::Face_index fi, CGALMesh::Vertex_index from, CGALMesh::Vertex_index to) {
  666. CGALMesh::Halfedge_index hi = result.halfedge(fi);
  667. for (; result.target(hi) != to; hi = result.next(hi));
  668. assert(result.source(hi) == from);
  669. assert(result.target(hi) == to);
  670. return hi;
  671. };
  672. auto fi = result.add_face(indices[i], indices[i + 1], indices[j]);
  673. edge_shape_map[result.edge(find_edge(fi, indices[i], indices[i + 1]))] =
  674. IntersectingElemnt{vertex_index, contour_index, IntersectingElemnt::Type::edge_1};
  675. face_shape_map[fi] =
  676. IntersectingElemnt{vertex_index, contour_index, IntersectingElemnt::Type::face_1};
  677. edge_shape_map[result.edge(find_edge(fi, indices[i + 1], indices[j]))] =
  678. IntersectingElemnt{vertex_index, contour_index, IntersectingElemnt::Type::edge_2};
  679. face_shape_map[result.add_face(indices[j], indices[i + 1], indices[j + 1])] =
  680. IntersectingElemnt{vertex_index, contour_index, IntersectingElemnt::Type::face_2};
  681. ++contour_index;
  682. }
  683. };
  684. size_t count_point = count_points(shape);
  685. result.reserve(result.number_of_vertices() + 2 * count_point, result.number_of_edges() + 4 * count_point, result.number_of_faces() + 2 * count_point);
  686. // Identify polygon
  687. // (contour_id > 0) are holes
  688. for (const auto &s : shape) {
  689. size_t contour_id = 0;
  690. insert_contour(s.contour, shape_id, contour_id++);
  691. for (const Polygon &hole : s.holes)
  692. insert_contour(hole, shape_id, contour_id++);
  693. ++shape_id;
  694. }
  695. return result;
  696. }
  697. }
  698. #include "libslic3r/TriangleMesh.hpp"
  699. //// 1 ////
  700. // Question store(1) Or calculate on demand(2) ??
  701. // (1) type: vector <vector<vertex indices>>
  702. // (1) Needs recalculation when merge and propagation togewther with its
  703. // (2) Could appear surface mistakes(need calc - all half edges)
  704. // (2) NO need of trace cut outline and connect it with letter conture points
  705. /// <summary>
  706. /// Cut surface shape from source model
  707. /// </summary>
  708. /// <param name="source">Input source mesh</param>
  709. /// <param name="shape">Input 2d shape to cut from surface</param>
  710. /// <param name="projection">Define transformation from 2d to 3d</param>
  711. /// <returns>Cutted surface, Its do not represent Volume</returns>
  712. indexed_triangle_set cut_shape(const indexed_triangle_set &source,
  713. const ExPolygon &shape,
  714. const Emboss::IProjection &projection)
  715. {
  716. // NOT implemented yet
  717. return {};
  718. }
  719. /// <summary>
  720. /// Cut surface shape from source model
  721. /// </summary>
  722. /// <param name="source">Input source mesh</param>
  723. /// <param name="shapes">Input 2d shape to cut from surface</param>
  724. /// <param name="projection">Define transformation from 2d to 3d</param>
  725. /// <returns>Cutted surface, Its do not represent Volume</returns>
  726. indexed_triangle_set cut_shape(const indexed_triangle_set &source,
  727. const ExPolygons &shapes,
  728. const Emboss::IProjection &projection)
  729. {
  730. indexed_triangle_set result;
  731. for (const ExPolygon &shape : shapes)
  732. its_merge(result, cut_shape(source, shape, projection));
  733. return result;
  734. }
  735. using MyMesh = Slic3r::MeshBoolean::cgal2::CGALMesh;
  736. // First Idea //// 1 ////
  737. // Use source model to modify ONLY surface of text ModelVolume
  738. // Second Idea
  739. // Store original its inside of text configuration[optional]
  740. // Cause problem with next editation of object -> cut, simplify, repair by WinSDK, Hollow, ...(transform original vertices)
  741. TEST_CASE("Emboss extrude cut", "[Emboss-Cut]")
  742. {
  743. std::string font_path = get_font_filepath();
  744. unsigned int font_index = 0; // collection
  745. char letter = '%';
  746. float flatness = 2.;
  747. auto font = Emboss::create_font_file(font_path.c_str());
  748. REQUIRE(font != nullptr);
  749. std::optional<Emboss::Glyph> glyph =
  750. Emboss::letter2glyph(*font, font_index, letter, flatness);
  751. REQUIRE(glyph.has_value());
  752. ExPolygons shape = glyph->shape;
  753. REQUIRE(!shape.empty());
  754. float z_depth = 50.f;
  755. Emboss::ProjectZ projection(z_depth);
  756. #if 0
  757. indexed_triangle_set text = Emboss::polygons2model(shape, projection);
  758. BoundingBoxf3 bbox = bounding_box(text);
  759. CHECK(!text.indices.empty());
  760. #endif
  761. auto cube = its_make_cube(782 - 49 + 50, 724 + 10 + 50, 5);
  762. its_translate(cube, Vec3f(49 - 25, -10 - 25, 2.5));
  763. auto cube2 = cube;
  764. // its_translate(cube2, Vec3f(0, 0, 40));
  765. its_translate(cube2, Vec3f(100, -40, 40));
  766. its_merge(cube, std::move(cube2));
  767. //cube = its_make_sphere(350., 1.);
  768. //for (auto &face : cube2.indices)
  769. // for (int i = 0; i < 3; ++ i)
  770. // face(i) += int(cube.vertices.size());
  771. //append(cube.vertices, cube2.vertices);
  772. //append(cube.indices, cube2.indices);
  773. using MyMesh = Slic3r::MeshBoolean::cgal2::CGALMesh;
  774. // name of CGAL property map for store source object face id - index into its.indices
  775. std::string face_map_name = "f:face_map";
  776. std::string face_type_map_name = "f:type";
  777. // identify glyph for intersected vertex
  778. std::string vert_shape_map_name = "v:glyph_id";
  779. MyMesh cgal_object = MeshBoolean::cgal2::to_cgal(cube, face_map_name);
  780. auto face_map = cgal_object.property_map<MyMesh::Face_index, int32_t>(face_map_name).first;
  781. auto vert_shape_map = cgal_object.add_property_map<MyMesh::Vertex_index, IntersectingElemnt>(vert_shape_map_name).first;
  782. std::string edge_shape_map_name = "e:glyph_id";
  783. std::string face_shape_map_name = "f:glyph_id";
  784. std::vector<ShapesVertexId> glyph_contours;
  785. MyMesh cgal_shape = MeshBoolean::cgal2::to_cgal(shape, projection, 0, edge_shape_map_name, face_shape_map_name, glyph_contours);
  786. auto edge_shape_map = cgal_shape.property_map<MyMesh::Edge_index, IntersectingElemnt>(edge_shape_map_name).first;
  787. auto face_shape_map = cgal_shape.property_map<MyMesh::Face_index, IntersectingElemnt>(face_shape_map_name).first;
  788. // bool map for affected edge
  789. using d_prop_bool = CGAL::dynamic_edge_property_t<bool>;
  790. using ecm_it = boost::property_map<MyMesh, d_prop_bool>::SMPM;
  791. using EcmType = CGAL::internal::Dynamic<MyMesh, ecm_it>;
  792. EcmType ecm = get(d_prop_bool(), cgal_object);
  793. struct Visitor : public CGAL::Polygon_mesh_processing::Corefinement::Default_visitor<MyMesh> {
  794. Visitor(const MyMesh &object, const MyMesh &shape,
  795. MyMesh::Property_map<CGAL::SM_Edge_index, IntersectingElemnt> edge_shape_map,
  796. MyMesh::Property_map<CGAL::SM_Face_index, IntersectingElemnt> face_shape_map,
  797. MyMesh::Property_map<CGAL::SM_Face_index, int32_t> face_map,
  798. MyMesh::Property_map<CGAL::SM_Vertex_index, IntersectingElemnt> vert_shape_map) :
  799. object(object), shape(shape), edge_shape_map(edge_shape_map), face_shape_map(face_shape_map),
  800. face_map(face_map), vert_shape_map(vert_shape_map)
  801. {}
  802. const MyMesh &object;
  803. const MyMesh &shape;
  804. // Properties of the shape mesh:
  805. MyMesh::Property_map<CGAL::SM_Edge_index, IntersectingElemnt> edge_shape_map;
  806. MyMesh::Property_map<CGAL::SM_Face_index, IntersectingElemnt> face_shape_map;
  807. // Properties of the object mesh.
  808. MyMesh::Property_map<CGAL::SM_Face_index, int32_t> face_map;
  809. MyMesh::Property_map<CGAL::SM_Vertex_index, IntersectingElemnt> vert_shape_map;
  810. typedef boost::graph_traits<MyMesh> GT;
  811. typedef typename GT::face_descriptor face_descriptor;
  812. typedef typename GT::halfedge_descriptor halfedge_descriptor;
  813. typedef typename GT::vertex_descriptor vertex_descriptor;
  814. int32_t source_face_id = -1;
  815. void before_subface_creations(face_descriptor f_old, MyMesh& mesh)
  816. {
  817. assert(&mesh == &object);
  818. source_face_id = face_map[f_old];
  819. }
  820. // it is called multiple times for one source_face_id
  821. void after_subface_created(face_descriptor f_new, MyMesh &mesh)
  822. {
  823. assert(&mesh == &object);
  824. assert(source_face_id != -1);
  825. face_map[f_new] = source_face_id;
  826. }
  827. std::vector<const IntersectingElemnt*> intersection_point_glyph;
  828. // Intersecting an edge hh_edge from tm_edge with a face hh_face of tm_face.
  829. void intersection_point_detected(
  830. // ID of the intersection point, starting at 0. Ids are consecutive.
  831. std::size_t i_id,
  832. // Dimension of a simplex part of face(hh_face) that is intersected by hh_edge:
  833. // 0 for vertex: target(hh_face)
  834. // 1 for edge: hh_face
  835. // 2 for the interior of face: face(hh_face)
  836. int simplex_dimension,
  837. // Edge of tm_edge, see edge_source_coplanar_with_face & edge_target_coplanar_with_face whether any vertex of hh_edge is coplanar with face(hh_face).
  838. halfedge_descriptor hh_edge,
  839. // Vertex, halfedge or face of tm_face intersected by hh_edge, see comment at simplex_dimension.
  840. halfedge_descriptor hh_face,
  841. // Mesh containing hh_edge
  842. const MyMesh& tm_edge,
  843. // Mesh containing hh_face
  844. const MyMesh& tm_face,
  845. // source(hh_edge) is coplanar with face(hh_face).
  846. bool edge_source_coplanar_with_face,
  847. // target(hh_edge) is coplanar with face(hh_face).
  848. bool edge_target_coplanar_with_face)
  849. {
  850. if (i_id <= intersection_point_glyph.size()) {
  851. intersection_point_glyph.reserve(Slic3r::next_highest_power_of_2(i_id + 1));
  852. intersection_point_glyph.resize(i_id + 1);
  853. }
  854. const IntersectingElemnt* glyph = nullptr;
  855. if (&tm_face == &shape) {
  856. assert(&tm_edge == &object);
  857. switch (simplex_dimension) {
  858. case 1:
  859. // edge x edge intersection
  860. glyph = &edge_shape_map[shape.edge(hh_face)];
  861. break;
  862. case 2:
  863. // edge x face intersection
  864. glyph = &face_shape_map[shape.face(hh_face)];
  865. break;
  866. default:
  867. assert(false);
  868. }
  869. if (edge_source_coplanar_with_face)
  870. vert_shape_map[object.source(hh_edge)] = *glyph;
  871. if (edge_target_coplanar_with_face)
  872. vert_shape_map[object.target(hh_edge)] = *glyph;
  873. } else {
  874. assert(&tm_edge == &shape && &tm_face == &object);
  875. assert(!edge_source_coplanar_with_face);
  876. assert(!edge_target_coplanar_with_face);
  877. glyph = &edge_shape_map[shape.edge(hh_edge)];
  878. if (simplex_dimension == 0)
  879. vert_shape_map[object.target(hh_face)] = *glyph;
  880. }
  881. intersection_point_glyph[i_id] = glyph;
  882. }
  883. void new_vertex_added(std::size_t node_id, vertex_descriptor vh, const MyMesh &tm)
  884. {
  885. assert(&tm == &object);
  886. assert(node_id < intersection_point_glyph.size());
  887. const IntersectingElemnt * glyph = intersection_point_glyph[node_id];
  888. assert(glyph != nullptr);
  889. assert(glyph->vertex_index != -1);
  890. assert(glyph->point_index != -1);
  891. vert_shape_map[vh] = glyph ? *glyph : IntersectingElemnt{};
  892. }
  893. } visitor{cgal_object, cgal_shape, edge_shape_map, face_shape_map,
  894. face_map, vert_shape_map};
  895. const auto& p = CGAL::Polygon_mesh_processing::parameters::throw_on_self_intersection(false).visitor(visitor).edge_is_constrained_map(ecm);
  896. const auto& q = CGAL::Polygon_mesh_processing::parameters::do_not_modify(true);
  897. // CGAL::Polygon_mesh_processing::corefine(cgal_object, cgalcube2, p, p);
  898. CGAL::Polygon_mesh_processing::corefine(cgal_object, cgal_shape, p, q);
  899. enum class SideType {
  900. // face inside of the cutted shape
  901. inside,
  902. // face outside of the cutted shape
  903. outside,
  904. // face without constrained edge (In or Out)
  905. not_constrained
  906. };
  907. auto side_type_map = cgal_object.add_property_map<MyMesh::Face_index, SideType>("f:side").first;
  908. for (auto fi : cgal_object.faces()) {
  909. SideType side_type = SideType::not_constrained;
  910. auto hi_end = cgal_object.halfedge(fi);
  911. auto hi = hi_end;
  912. do {
  913. CGAL::SM_Edge_index edge_index = cgal_object.edge(hi);
  914. // is edge new created - constrained?
  915. if (get(ecm, edge_index)) {
  916. // This face has a constrained edge.
  917. IntersectingElemnt shape_from = vert_shape_map[cgal_object.source(hi)];
  918. IntersectingElemnt shape_to = vert_shape_map[cgal_object.target(hi)];
  919. assert(shape_from.vertex_index != -1);
  920. assert(shape_from.vertex_index == shape_to.vertex_index);
  921. assert(shape_from.point_index != -1);
  922. assert(shape_to.point_index != -1);
  923. const ShapesVertexId &vertex_index = glyph_contours[shape_from.vertex_index];
  924. const ExPolygon &expoly = shape[vertex_index.expoly];
  925. const Polygon &contour = vertex_index.contour == 0 ? expoly.contour : expoly.holes[vertex_index.contour - 1];
  926. bool is_inside = false;
  927. // 4 type
  928. // index into contour
  929. int32_t i_from = shape_from.point_index;
  930. int32_t i_to = shape_to.point_index;
  931. if (i_from == i_to && shape_from.type == shape_to.type) {
  932. const auto &p = cgal_object.point(cgal_object.target(cgal_object.next(hi)));
  933. int i = i_from * 2;
  934. int j = (i_from + 1 == int(contour.size())) ? 0 : i + 2;
  935. i += vertex_index.vertex_base;
  936. j += vertex_index.vertex_base;
  937. auto abcp =
  938. shape_from.type == IntersectingElemnt::Type::face_1 ?
  939. CGAL::orientation(
  940. cgal_shape.point(CGAL::SM_Vertex_index(i)),
  941. cgal_shape.point(CGAL::SM_Vertex_index(i + 1)),
  942. cgal_shape.point(CGAL::SM_Vertex_index(j)), p) :
  943. // shape_from.type == IntersectingElemnt::Type::face_2
  944. CGAL::orientation(
  945. cgal_shape.point(CGAL::SM_Vertex_index(j)),
  946. cgal_shape.point(CGAL::SM_Vertex_index(i + 1)),
  947. cgal_shape.point(CGAL::SM_Vertex_index(j + 1)),
  948. p);
  949. is_inside = abcp == CGAL::POSITIVE;
  950. } else if (i_from < i_to || (i_from == i_to && shape_from.type < shape_to.type)) {
  951. bool is_last = i_from == 0 && static_cast<size_t>(i_to + 1) == contour.size();
  952. if (!is_last) is_inside = true;
  953. } else { // i_from > i_to || (i_from == i_to && shape_from.type > shape_to.type)
  954. bool is_last = i_to == 0 && static_cast<size_t>(i_from + 1) == contour.size();
  955. if (is_last) is_inside = true;
  956. }
  957. if (is_inside) {
  958. // Is this face oriented towards p or away from p?
  959. const auto &a = cgal_object.point(cgal_object.source(hi));
  960. const auto &b = cgal_object.point(cgal_object.target(hi));
  961. const auto &c = cgal_object.point(cgal_object.target(cgal_object.next(hi)));
  962. //FIXME prosim nahrad skutecnou projekci.
  963. //projection.project()
  964. const auto p = a + MeshBoolean::cgal2::EpicKernel::Vector_3(0, 0, 10);
  965. auto abcp = CGAL::orientation(a, b, c, p);
  966. if (abcp == CGAL::POSITIVE)
  967. side_type = SideType::inside;
  968. else
  969. is_inside = false;
  970. }
  971. if (!is_inside) side_type = SideType::outside;
  972. break;
  973. }
  974. // next half edge index inside of face
  975. hi = cgal_object.next(hi);
  976. } while (hi != hi_end);
  977. side_type_map[fi] = side_type;
  978. }
  979. // debug output
  980. auto face_colors = cgal_object.add_property_map<MyMesh::Face_index, CGAL::Color>("f:color").first;
  981. for (auto fi : cgal_object.faces()) {
  982. auto &color = face_colors[fi];
  983. switch (side_type_map[fi]) {
  984. case SideType::inside: color = CGAL::Color{255, 0, 0}; break;
  985. case SideType::outside: color = CGAL::Color{255, 0, 255}; break;
  986. case SideType::not_constrained: color = CGAL::Color{0, 255, 0}; break;
  987. }
  988. }
  989. CGAL::IO::write_OFF("c:\\data\\temp\\constrained.off", cgal_object);
  990. // Seed fill the other faces inside the region.
  991. for (Visitor::face_descriptor fi : cgal_object.faces()) {
  992. if (side_type_map[fi] != SideType::not_constrained) continue;
  993. // check if neighbor face is inside
  994. Visitor::halfedge_descriptor hi = cgal_object.halfedge(fi);
  995. Visitor::halfedge_descriptor hi_end = hi;
  996. bool has_inside_neighbor = false;
  997. std::vector<MyMesh::Face_index> queue;
  998. do {
  999. Visitor::face_descriptor fi_opposite = cgal_object.face(cgal_object.opposite(hi));
  1000. SideType side = side_type_map[fi_opposite];
  1001. if (side == SideType::inside) {
  1002. has_inside_neighbor = true;
  1003. } else if (side == SideType::not_constrained) {
  1004. queue.emplace_back(fi_opposite);
  1005. }
  1006. hi = cgal_object.next(hi);
  1007. } while (hi != hi_end);
  1008. if (!has_inside_neighbor) continue;
  1009. side_type_map[fi] = SideType::inside;
  1010. while (!queue.empty()) {
  1011. Visitor::face_descriptor fi = queue.back();
  1012. queue.pop_back();
  1013. // Do not fill twice
  1014. if (side_type_map[fi] == SideType::inside) continue;
  1015. side_type_map[fi] = SideType::inside;
  1016. // check neighbor triangle
  1017. Visitor::halfedge_descriptor hi = cgal_object.halfedge(fi);
  1018. Visitor::halfedge_descriptor hi_end = hi;
  1019. do {
  1020. Visitor::face_descriptor fi_opposite = cgal_object.face(cgal_object.opposite(hi));
  1021. SideType side = side_type_map[fi_opposite];
  1022. if (side == SideType::not_constrained)
  1023. queue.emplace_back(fi_opposite);
  1024. hi = cgal_object.next(hi);
  1025. } while (hi != hi_end);
  1026. }
  1027. }
  1028. // debug output
  1029. for (auto fi : cgal_object.faces()) {
  1030. auto &color = face_colors[fi];
  1031. switch (side_type_map[fi]) {
  1032. case SideType::inside: color = CGAL::Color{255, 0, 0}; break;
  1033. case SideType::outside: color = CGAL::Color{255, 0, 255}; break;
  1034. case SideType::not_constrained: color = CGAL::Color{0, 255, 0}; break;
  1035. }
  1036. }
  1037. CGAL::IO::write_OFF("c:\\data\\temp\\filled.off", cgal_object);
  1038. // Mapping of its_extruded faces to source faces.
  1039. enum class FaceState : int8_t {
  1040. Unknown = -1,
  1041. Unmarked = -2,
  1042. UnmarkedSplit = -3,
  1043. Marked = -4,
  1044. MarkedSplit = -5,
  1045. UnmarkedEmitted = -6,
  1046. };
  1047. std::vector<FaceState> face_states(cube.indices.size(), FaceState::Unknown);
  1048. for (auto fi_seed : cgal_object.faces()) {
  1049. FaceState &state = face_states[face_map[fi_seed]];
  1050. bool is_face_inside = side_type_map[fi_seed] == SideType::inside;
  1051. switch (state) {
  1052. case FaceState::Unknown:
  1053. state = is_face_inside ? FaceState::Marked : FaceState::Unmarked;
  1054. break;
  1055. case FaceState::Unmarked:
  1056. case FaceState::UnmarkedSplit:
  1057. state = is_face_inside ? FaceState::MarkedSplit : FaceState::UnmarkedSplit;
  1058. break;
  1059. case FaceState::Marked:
  1060. case FaceState::MarkedSplit:
  1061. state = FaceState::MarkedSplit;
  1062. break;
  1063. default:
  1064. assert(false);
  1065. }
  1066. }
  1067. indexed_triangle_set its_extruded;
  1068. its_extruded.indices.reserve(cgal_object.number_of_faces());
  1069. its_extruded.vertices.reserve(cgal_object.number_of_vertices());
  1070. // Mapping of its_extruded vertices (original and offsetted) to cgalcuble's vertices.
  1071. std::vector<std::pair<int32_t, int32_t>> map_vertices(cgal_object.number_of_vertices(), std::pair<int32_t, int32_t>{-1, -1});
  1072. Vec3f extrude_dir { 0, 0, 5.f };
  1073. for (auto fi : cgal_object.faces()) {
  1074. const int32_t source_face_id = face_map[fi];
  1075. const FaceState state = face_states[source_face_id];
  1076. assert(state == FaceState::Unmarked || state == FaceState::UnmarkedSplit || state == FaceState::UnmarkedEmitted ||
  1077. state == FaceState::Marked || state == FaceState::MarkedSplit);
  1078. if (state == FaceState::UnmarkedEmitted) continue; // Already emitted.
  1079. if (state == FaceState::Unmarked ||
  1080. state == FaceState::UnmarkedSplit) {
  1081. // Just copy the unsplit source face.
  1082. const Vec3i source_vertices = cube.indices[source_face_id];
  1083. Vec3i target_vertices;
  1084. for (int i = 0; i < 3; ++i) {
  1085. target_vertices(i) = map_vertices[source_vertices(i)].first;
  1086. if (target_vertices(i) == -1) {
  1087. map_vertices[source_vertices(i)].first = target_vertices(i) = int(its_extruded.vertices.size());
  1088. its_extruded.vertices.emplace_back(cube.vertices[source_vertices(i)]);
  1089. }
  1090. }
  1091. its_extruded.indices.emplace_back(target_vertices);
  1092. face_states[source_face_id] = FaceState::UnmarkedEmitted;
  1093. continue; // revert modification
  1094. }
  1095. auto hi = cgal_object.halfedge(fi);
  1096. auto hi_prev = cgal_object.prev(hi);
  1097. auto hi_next = cgal_object.next(hi);
  1098. const Vec3i source_vertices{
  1099. int((std::size_t)cgal_object.target(hi)),
  1100. int((std::size_t)cgal_object.target(hi_next)),
  1101. int((std::size_t)cgal_object.target(hi_prev)) };
  1102. Vec3i target_vertices;
  1103. if (side_type_map[fi] != SideType::inside) {
  1104. // Copy the face.
  1105. Vec3i target_vertices;
  1106. for (int i = 0; i < 3; ++ i) {
  1107. target_vertices(i) = map_vertices[source_vertices(i)].first;
  1108. if (target_vertices(i) == -1) {
  1109. map_vertices[source_vertices(i)].first = target_vertices(i) = int(its_extruded.vertices.size());
  1110. const auto &p = cgal_object.point(cgal_object.target(hi));
  1111. its_extruded.vertices.emplace_back(p.x(), p.y(), p.z());
  1112. }
  1113. hi = cgal_object.next(hi);
  1114. }
  1115. its_extruded.indices.emplace_back(target_vertices);
  1116. continue; // copy splitted triangle
  1117. }
  1118. // Extrude the face. Neighbor edges separating extruded face from
  1119. // non-extruded face will be extruded.
  1120. bool boundary_vertex[3] = {false, false, false};
  1121. Vec3i target_vertices_extruded{-1, -1, -1};
  1122. for (int i = 0; i < 3; ++i) {
  1123. if (side_type_map[cgal_object.face(cgal_object.opposite(hi))] != SideType::inside)
  1124. // Edge separating extruded / non-extruded region.
  1125. boundary_vertex[i] = true;
  1126. hi = cgal_object.next(hi);
  1127. }
  1128. for (int i = 0; i < 3; ++i) {
  1129. target_vertices_extruded(i) = map_vertices[source_vertices(i)].second;
  1130. if (target_vertices_extruded(i) == -1) {
  1131. map_vertices[source_vertices(i)].second =
  1132. target_vertices_extruded(i) = int(
  1133. its_extruded.vertices.size());
  1134. const auto &p = cgal_object.point(cgal_object.target(hi));
  1135. its_extruded.vertices.emplace_back(
  1136. Vec3f{float(p.x()), float(p.y()), float(p.z())} +
  1137. extrude_dir);
  1138. }
  1139. if (boundary_vertex[i]) {
  1140. target_vertices(i) = map_vertices[source_vertices(i)].first;
  1141. if (target_vertices(i) == -1) {
  1142. map_vertices[source_vertices(i)].first = target_vertices(
  1143. i) = int(its_extruded.vertices.size());
  1144. const auto &p = cgal_object.point(cgal_object.target(hi));
  1145. its_extruded.vertices.emplace_back(p.x(), p.y(), p.z());
  1146. }
  1147. }
  1148. hi = cgal_object.next(hi);
  1149. }
  1150. its_extruded.indices.emplace_back(target_vertices_extruded);
  1151. // Add the sides.
  1152. for (int i = 0; i < 3; ++i) {
  1153. int j = (i + 1) % 3;
  1154. assert(target_vertices_extruded[i] != -1 &&
  1155. target_vertices_extruded[j] != -1);
  1156. if (boundary_vertex[i] && boundary_vertex[j]) {
  1157. assert(target_vertices[i] != -1 && target_vertices[j] != -1);
  1158. its_extruded.indices.emplace_back(
  1159. Vec3i{target_vertices[i], target_vertices[j],
  1160. target_vertices_extruded[i]});
  1161. its_extruded.indices.emplace_back(
  1162. Vec3i{target_vertices_extruded[i], target_vertices[j],
  1163. target_vertices_extruded[j]});
  1164. }
  1165. }
  1166. }
  1167. its_write_obj(its_extruded, "c:\\data\\temp\\text-extruded.obj");
  1168. indexed_triangle_set edges_its;
  1169. std::vector<Vec3f> edges_its_colors;
  1170. for (auto ei : cgal_object.edges())
  1171. if (cgal_object.is_valid(ei)) {
  1172. const auto &p1 = cgal_object.point(cgal_object.vertex(ei, 0));
  1173. const auto &p2 = cgal_object.point(cgal_object.vertex(ei, 1));
  1174. bool constrained = get(ecm, ei);
  1175. Vec3f color = constrained ? Vec3f{ 1.f, 0, 0 } : Vec3f{ 0, 1., 0 };
  1176. edges_its.indices.emplace_back(Vec3i(edges_its.vertices.size(), edges_its.vertices.size() + 1, edges_its.vertices.size() + 2));
  1177. edges_its.vertices.emplace_back(Vec3f(p1.x(), p1.y(), p1.z()));
  1178. edges_its.vertices.emplace_back(Vec3f(p2.x(), p2.y(), p2.z()));
  1179. edges_its.vertices.emplace_back(Vec3f(p2.x(), p2.y(), p2.z() + 0.001));
  1180. edges_its_colors.emplace_back(color);
  1181. edges_its_colors.emplace_back(color);
  1182. edges_its_colors.emplace_back(color);
  1183. }
  1184. its_write_obj(edges_its, edges_its_colors, "c:\\data\\temp\\corefined-edges.obj");
  1185. // MeshBoolean::cgal::minus(cube, cube2);
  1186. // REQUIRE(!MeshBoolean::cgal::does_self_intersect(cube));
  1187. }