test_emboss.cpp 55 KB

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