CutSurface.cpp 158 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839840841842843844845846847848849850851852853854855856857858859860861862863864865866867868869870871872873874875876877878879880881882883884885886887888889890891892893894895896897898899900901902903904905906907908909910911912913914915916917918919920921922923924925926927928929930931932933934935936937938939940941942943944945946947948949950951952953954955956957958959960961962963964965966967968969970971972973974975976977978979980981982983984985986987988989990991992993994995996997998999100010011002100310041005100610071008100910101011101210131014101510161017101810191020102110221023102410251026102710281029103010311032103310341035103610371038103910401041104210431044104510461047104810491050105110521053105410551056105710581059106010611062106310641065106610671068106910701071107210731074107510761077107810791080108110821083108410851086108710881089109010911092109310941095109610971098109911001101110211031104110511061107110811091110111111121113111411151116111711181119112011211122112311241125112611271128112911301131113211331134113511361137113811391140114111421143114411451146114711481149115011511152115311541155115611571158115911601161116211631164116511661167116811691170117111721173117411751176117711781179118011811182118311841185118611871188118911901191119211931194119511961197119811991200120112021203120412051206120712081209121012111212121312141215121612171218121912201221122212231224122512261227122812291230123112321233123412351236123712381239124012411242124312441245124612471248124912501251125212531254125512561257125812591260126112621263126412651266126712681269127012711272127312741275127612771278127912801281128212831284128512861287128812891290129112921293129412951296129712981299130013011302130313041305130613071308130913101311131213131314131513161317131813191320132113221323132413251326132713281329133013311332133313341335133613371338133913401341134213431344134513461347134813491350135113521353135413551356135713581359136013611362136313641365136613671368136913701371137213731374137513761377137813791380138113821383138413851386138713881389139013911392139313941395139613971398139914001401140214031404140514061407140814091410141114121413141414151416141714181419142014211422142314241425142614271428142914301431143214331434143514361437143814391440144114421443144414451446144714481449145014511452145314541455145614571458145914601461146214631464146514661467146814691470147114721473147414751476147714781479148014811482148314841485148614871488148914901491149214931494149514961497149814991500150115021503150415051506150715081509151015111512151315141515151615171518151915201521152215231524152515261527152815291530153115321533153415351536153715381539154015411542154315441545154615471548154915501551155215531554155515561557155815591560156115621563156415651566156715681569157015711572157315741575157615771578157915801581158215831584158515861587158815891590159115921593159415951596159715981599160016011602160316041605160616071608160916101611161216131614161516161617161816191620162116221623162416251626162716281629163016311632163316341635163616371638163916401641164216431644164516461647164816491650165116521653165416551656165716581659166016611662166316641665166616671668166916701671167216731674167516761677167816791680168116821683168416851686168716881689169016911692169316941695169616971698169917001701170217031704170517061707170817091710171117121713171417151716171717181719172017211722172317241725172617271728172917301731173217331734173517361737173817391740174117421743174417451746174717481749175017511752175317541755175617571758175917601761176217631764176517661767176817691770177117721773177417751776177717781779178017811782178317841785178617871788178917901791179217931794179517961797179817991800180118021803180418051806180718081809181018111812181318141815181618171818181918201821182218231824182518261827182818291830183118321833183418351836183718381839184018411842184318441845184618471848184918501851185218531854185518561857185818591860186118621863186418651866186718681869187018711872187318741875187618771878187918801881188218831884188518861887188818891890189118921893189418951896189718981899190019011902190319041905190619071908190919101911191219131914191519161917191819191920192119221923192419251926192719281929193019311932193319341935193619371938193919401941194219431944194519461947194819491950195119521953195419551956195719581959196019611962196319641965196619671968196919701971197219731974197519761977197819791980198119821983198419851986198719881989199019911992199319941995199619971998199920002001200220032004200520062007200820092010201120122013201420152016201720182019202020212022202320242025202620272028202920302031203220332034203520362037203820392040204120422043204420452046204720482049205020512052205320542055205620572058205920602061206220632064206520662067206820692070207120722073207420752076207720782079208020812082208320842085208620872088208920902091209220932094209520962097209820992100210121022103210421052106210721082109211021112112211321142115211621172118211921202121212221232124212521262127212821292130213121322133213421352136213721382139214021412142214321442145214621472148214921502151215221532154215521562157215821592160216121622163216421652166216721682169217021712172217321742175217621772178217921802181218221832184218521862187218821892190219121922193219421952196219721982199220022012202220322042205220622072208220922102211221222132214221522162217221822192220222122222223222422252226222722282229223022312232223322342235223622372238223922402241224222432244224522462247224822492250225122522253225422552256225722582259226022612262226322642265226622672268226922702271227222732274227522762277227822792280228122822283228422852286228722882289229022912292229322942295229622972298229923002301230223032304230523062307230823092310231123122313231423152316231723182319232023212322232323242325232623272328232923302331233223332334233523362337233823392340234123422343234423452346234723482349235023512352235323542355235623572358235923602361236223632364236523662367236823692370237123722373237423752376237723782379238023812382238323842385238623872388238923902391239223932394239523962397239823992400240124022403240424052406240724082409241024112412241324142415241624172418241924202421242224232424242524262427242824292430243124322433243424352436243724382439244024412442244324442445244624472448244924502451245224532454245524562457245824592460246124622463246424652466246724682469247024712472247324742475247624772478247924802481248224832484248524862487248824892490249124922493249424952496249724982499250025012502250325042505250625072508250925102511251225132514251525162517251825192520252125222523252425252526252725282529253025312532253325342535253625372538253925402541254225432544254525462547254825492550255125522553255425552556255725582559256025612562256325642565256625672568256925702571257225732574257525762577257825792580258125822583258425852586258725882589259025912592259325942595259625972598259926002601260226032604260526062607260826092610261126122613261426152616261726182619262026212622262326242625262626272628262926302631263226332634263526362637263826392640264126422643264426452646264726482649265026512652265326542655265626572658265926602661266226632664266526662667266826692670267126722673267426752676267726782679268026812682268326842685268626872688268926902691269226932694269526962697269826992700270127022703270427052706270727082709271027112712271327142715271627172718271927202721272227232724272527262727272827292730273127322733273427352736273727382739274027412742274327442745274627472748274927502751275227532754275527562757275827592760276127622763276427652766276727682769277027712772277327742775277627772778277927802781278227832784278527862787278827892790279127922793279427952796279727982799280028012802280328042805280628072808280928102811281228132814281528162817281828192820282128222823282428252826282728282829283028312832283328342835283628372838283928402841284228432844284528462847284828492850285128522853285428552856285728582859286028612862286328642865286628672868286928702871287228732874287528762877287828792880288128822883288428852886288728882889289028912892289328942895289628972898289929002901290229032904290529062907290829092910291129122913291429152916291729182919292029212922292329242925292629272928292929302931293229332934293529362937293829392940294129422943294429452946294729482949295029512952295329542955295629572958295929602961296229632964296529662967296829692970297129722973297429752976297729782979298029812982298329842985298629872988298929902991299229932994299529962997299829993000300130023003300430053006300730083009301030113012301330143015301630173018301930203021302230233024302530263027302830293030303130323033303430353036303730383039304030413042304330443045304630473048304930503051305230533054305530563057305830593060306130623063306430653066306730683069307030713072307330743075307630773078307930803081308230833084308530863087308830893090309130923093309430953096309730983099310031013102310331043105310631073108310931103111311231133114311531163117311831193120312131223123312431253126312731283129313031313132313331343135313631373138313931403141314231433144314531463147314831493150315131523153315431553156315731583159316031613162316331643165316631673168316931703171317231733174317531763177317831793180318131823183318431853186318731883189319031913192319331943195319631973198319932003201320232033204320532063207320832093210321132123213321432153216321732183219322032213222322332243225322632273228322932303231323232333234323532363237323832393240324132423243324432453246324732483249325032513252325332543255325632573258325932603261326232633264326532663267326832693270327132723273327432753276327732783279328032813282328332843285328632873288328932903291329232933294329532963297329832993300330133023303330433053306330733083309331033113312331333143315331633173318331933203321332233233324332533263327332833293330333133323333333433353336333733383339334033413342334333443345334633473348334933503351335233533354335533563357335833593360336133623363336433653366336733683369337033713372337333743375337633773378337933803381338233833384338533863387338833893390339133923393339433953396339733983399340034013402340334043405340634073408340934103411341234133414341534163417341834193420342134223423342434253426342734283429343034313432343334343435343634373438343934403441344234433444344534463447344834493450345134523453345434553456345734583459346034613462346334643465346634673468346934703471347234733474347534763477347834793480348134823483348434853486348734883489349034913492349334943495349634973498349935003501350235033504350535063507350835093510351135123513351435153516351735183519352035213522352335243525352635273528352935303531353235333534353535363537353835393540354135423543354435453546354735483549355035513552355335543555355635573558355935603561356235633564356535663567356835693570357135723573357435753576357735783579358035813582358335843585358635873588358935903591359235933594359535963597359835993600360136023603360436053606360736083609361036113612361336143615361636173618361936203621362236233624362536263627362836293630363136323633363436353636363736383639364036413642364336443645364636473648364936503651365236533654365536563657365836593660366136623663366436653666366736683669367036713672367336743675367636773678367936803681368236833684368536863687368836893690369136923693369436953696369736983699370037013702370337043705370637073708370937103711371237133714371537163717371837193720372137223723372437253726372737283729373037313732373337343735373637373738373937403741374237433744374537463747374837493750375137523753375437553756375737583759376037613762376337643765376637673768376937703771377237733774377537763777377837793780378137823783378437853786378737883789379037913792379337943795379637973798379938003801380238033804380538063807380838093810381138123813381438153816381738183819382038213822382338243825382638273828382938303831383238333834383538363837383838393840384138423843384438453846384738483849385038513852385338543855385638573858385938603861386238633864386538663867386838693870387138723873387438753876387738783879388038813882388338843885388638873888388938903891389238933894389538963897389838993900390139023903390439053906390739083909391039113912391339143915391639173918391939203921392239233924392539263927392839293930393139323933393439353936393739383939394039413942394339443945394639473948394939503951395239533954395539563957395839593960396139623963396439653966396739683969397039713972397339743975397639773978397939803981398239833984398539863987398839893990399139923993399439953996399739983999400040014002400340044005400640074008400940104011401240134014401540164017401840194020402140224023402440254026402740284029403040314032403340344035403640374038403940404041404240434044404540464047404840494050405140524053405440554056405740584059406040614062406340644065406640674068406940704071407240734074407540764077407840794080408140824083408440854086
  1. ///|/ Copyright (c) Prusa Research 2022 - 2023 Vojtěch Bubník @bubnikv, Filip Sykala @Jony01
  2. ///|/
  3. ///|/ PrusaSlicer is released under the terms of the AGPLv3 or higher
  4. ///|/
  5. #include "CutSurface.hpp"
  6. /// models_input.obj - Check transormation of model to each others
  7. /// projection_center.obj - circle representing center of projection with correct distance
  8. /// {M} .. model index
  9. /// model/model{M}.off - CGAL model created from index_triangle_set
  10. /// model_neg/model{M}.off - CGAL model created for differenciate (multi volume object)
  11. /// shape.off - CGAL model created from shapes
  12. /// constrained/model{M}.off - Visualization of inside and outside triangles
  13. /// Green - not along constrained edge
  14. /// Red - sure that are inside
  15. /// Purple - sure that are outside
  16. /// (only along constrained edge)
  17. /// filled/model{M}.off - flood fill green triangles inside of red area
  18. /// - Same meaning of color as constrained
  19. /// {N} .. Order of cutted Area of Interestmodel from model surface
  20. /// model_AOIs/{M}/cutAOI{N}.obj - Extracted Area of interest from corefined model
  21. /// model_AOIs/{M}/outline{N}.obj - Outline of Cutted Area
  22. /// {O} .. Order number of patch
  23. /// patches/patch{O}.off
  24. /// result.obj - Merged result its
  25. /// result_contours/{O}.obj - visualization of contours for result patches
  26. //#define DEBUG_OUTPUT_DIR std::string("C:/data/temp/cutSurface/")
  27. using namespace Slic3r;
  28. #include "ExPolygonsIndex.hpp"
  29. #include <CGAL/Polygon_mesh_processing/corefinement.h>
  30. #include <CGAL/Exact_integer.h>
  31. #include <CGAL/Surface_mesh.h>
  32. #include <CGAL/Cartesian_converter.h>
  33. #include <tbb/parallel_for.h>
  34. // libslic3r
  35. #include "TriangleMesh.hpp" // its_merge
  36. #include "Utils.hpp" // next_highest_power_of_2
  37. #include "ClipperUtils.hpp" // union_ex + offset_ex
  38. namespace priv {
  39. using Project = Emboss::IProjection;
  40. using Project3d = Emboss::IProject3d;
  41. /// <summary>
  42. /// Set true for indices out of area of interest
  43. /// </summary>
  44. /// <param name="skip_indicies">Flag to convert triangle to cgal</param>
  45. /// <param name="its">model</param>
  46. /// <param name="projection">Convert 2d point to pair of 3d points</param>
  47. /// <param name="shapes_bb">2d bounding box define AOI</param>
  48. void set_skip_for_out_of_aoi(std::vector<bool> &skip_indicies,
  49. const indexed_triangle_set &its,
  50. const Project &projection,
  51. const BoundingBox &shapes_bb);
  52. /// <summary>
  53. /// Set true for indicies outward and almost parallel together.
  54. /// Note: internally calculate normals
  55. /// </summary>
  56. /// <param name="skip_indicies">Flag to convert triangle to cgal</param>
  57. /// <param name="its">model</param>
  58. /// <param name="projection">Direction to measure angle</param>
  59. /// <param name="max_angle">Maximal allowed angle between opposit normal and
  60. /// projection direction [in DEG]</param>
  61. void set_skip_by_angle(std::vector<bool> &skip_indicies,
  62. const indexed_triangle_set &its,
  63. const Project3d &projection,
  64. double max_angle = 89.);
  65. using EpicKernel = CGAL::Exact_predicates_inexact_constructions_kernel;
  66. using CutMesh = CGAL::Surface_mesh<EpicKernel::Point_3>;
  67. using CutMeshes = std::vector<CutMesh>;
  68. using VI = CGAL::SM_Vertex_index;
  69. using HI = CGAL::SM_Halfedge_index;
  70. using EI = CGAL::SM_Edge_index;
  71. using FI = CGAL::SM_Face_index;
  72. using P3 = CGAL::Epick::Point_3;
  73. inline Vec3d to_vec3d(const P3 &p) { return Vec3d(p.x(),p.y(),p.z()); }
  74. /// <summary>
  75. /// Convert triangle mesh model to CGAL Surface_mesh
  76. /// Filtrate out opposite triangles
  77. /// Add property map for source face index
  78. /// </summary>
  79. /// <param name="its">Model</param>
  80. /// <param name="skip_indicies">Flags that triangle should be skiped</param>
  81. /// <param name="flip">When true triangle will flip normal</param>
  82. /// <returns>CGAL mesh - half edge mesh</returns>
  83. CutMesh to_cgal(const indexed_triangle_set &its,
  84. const std::vector<bool> &skip_indicies,
  85. bool flip = false);
  86. /// <summary>
  87. /// Covert 2d shape (e.g. Glyph) to CGAL model
  88. /// NOTE: internaly create
  89. /// edge_shape_map .. Property map to store conversion from edge to contour
  90. /// face_shape_map .. Property map to store conversion from face to contour
  91. /// </summary>
  92. /// <param name="shapes">2d shapes to project</param>
  93. /// <param name="projection">Define transformation 2d point into 3d</param>
  94. /// <returns>CGAL model of extruded shape</returns>
  95. CutMesh to_cgal(const ExPolygons &shapes, const Project &projection);
  96. // function to check result of projection. 2d int32_t -> 3d double
  97. bool exist_duplicit_vertex(const CutMesh& mesh);
  98. /// <summary>
  99. /// IntersectingElement
  100. ///
  101. /// Adress polygon inside of ExPolygon
  102. /// Keep information about source of vertex:
  103. /// - from face (one of 2 possible)
  104. /// - from edge (one of 2 possible)
  105. ///
  106. /// V1~~~~~V2
  107. /// | f1 /:
  108. /// | / :
  109. /// e1| /e2:
  110. /// | / :
  111. /// |/ f2 :
  112. /// V1'~~~~V2'
  113. ///
  114. /// | .. edge
  115. /// / .. edge
  116. /// : .. foreign edge - neighbor
  117. /// ~ .. no care edge - idealy should not cross model
  118. /// V1,V1' .. projected 2d point to 3d
  119. /// V2,V2' .. projected 2d point to 3d
  120. ///
  121. /// Vertex indexing
  122. /// V1 .. i (vertex_base + 2x index of point in polygon)
  123. /// V1' .. i + 1
  124. /// V2 .. j = i + 2 || 0 (for last i in polygon)
  125. /// V2' .. j + 1
  126. ///
  127. /// f1 .. text_face_1 (triangle face made by side of shape contour)
  128. /// f2 .. text_face_2
  129. /// e1 .. text_edge_1 (edge on side of face made by side of shape contour)
  130. /// e2 .. text_edge_2
  131. ///
  132. /// </summary>
  133. struct IntersectingElement
  134. {
  135. // identify source point in shapes
  136. uint32_t shape_point_index{std::numeric_limits<uint32_t>::max()};
  137. // store together type, is_first, is_last
  138. unsigned char attr{std::numeric_limits<unsigned char>::max()};
  139. // vertex or edge ID, where edge ID is the index of the source point.
  140. // There are 4 consecutive indices generated for a single contour edge:
  141. // 0th - 1st text edge (straight)
  142. // 1th - 1st text face
  143. // 2nd - 2nd text edge (diagonal)
  144. // 3th - 2nd text face
  145. // Type of intersecting element from extruded shape( 3d )
  146. // NOTE: type must be storable to 3bit -> max value is 7
  147. enum class Type: unsigned char {
  148. edge_1 = 0,
  149. face_1 = 1,
  150. edge_2 = 2,
  151. face_2 = 3,
  152. undefined = 4
  153. };
  154. IntersectingElement &set_type(Type t)
  155. {
  156. attr = static_cast<unsigned char>(
  157. attr + (int) t - (int) get_type());
  158. return *this;
  159. }
  160. void set_is_first(){ attr += 8; }
  161. void set_is_last(){ attr += 16; }
  162. Type get_type() const { return static_cast<Type>(attr % 8);}
  163. bool is_first() const { return 8 <= attr && attr < 16; }
  164. bool is_last() const { return attr >= 16; }
  165. };
  166. // stored in model made by shape
  167. using EdgeShapeMap = CutMesh::Property_map<EI, IntersectingElement>;
  168. using FaceShapeMap = CutMesh::Property_map<FI, IntersectingElement>;
  169. // stored in surface source - pointer to EdgeShapeMap | FaceShapeMap
  170. using VertexShapeMap = CutMesh::Property_map<VI, const IntersectingElement *>;
  171. // stored in model made by shape
  172. const std::string edge_shape_map_name = "e:IntersectingElement";
  173. const std::string face_shape_map_name = "f:IntersectingElement";
  174. // stored in surface source
  175. const std::string vert_shape_map_name = "v:IntersectingElement";
  176. /// <summary>
  177. /// Flag for faces in CGAL mesh
  178. /// </summary>
  179. enum class FaceType {
  180. // face inside of the cutted shape
  181. inside,
  182. // face outside of the cutted shape
  183. outside,
  184. // face without constrained edge (In or Out)
  185. not_constrained,
  186. // Helper flag that inside was processed
  187. inside_processed
  188. };
  189. using FaceTypeMap = CutMesh::Property_map<FI, FaceType>;
  190. const std::string face_type_map_name = "f:side";
  191. // Conversion one vertex index to another
  192. using CvtVI2VI = CutMesh::Property_map<VI, VI>;
  193. // Each Patch track outline vertex conversion to tource model
  194. const std::string patch_source_name = "v:patch_source";
  195. // For VI that should be reduced, contain VI to use instead of reduced
  196. // Other VI are invalid
  197. using ReductionMap = CvtVI2VI;
  198. const std::string vertex_reduction_map_name = "v:reduction";
  199. // A property map containing the constrained-or-not status of each edge
  200. using EdgeBoolMap = CutMesh::Property_map<EI, bool>;
  201. const std::string is_constrained_edge_name = "e:is_constrained";
  202. /// <summary>
  203. /// Create map to reduce unnecesary triangles,
  204. /// Triangles are made by divided quad to two triangles
  205. /// on side of cutting shape mesh
  206. /// Note: also use from mesh (have to be created)
  207. /// face_type_map .. Type of shape inside / outside
  208. /// vert_shape_map .. Source of outline vertex
  209. /// </summary>
  210. /// <param name="reduction_map">Reduction map from vertex to vertex,
  211. /// when key == value than no reduction</param>
  212. /// <param name="faces">Faces of one </param>
  213. /// <param name="mesh">Input object</param>
  214. void create_reduce_map(ReductionMap &reduction_map, const CutMesh &meshes);
  215. // Patch made by Cut area of interest from model
  216. // connected faces(triangles) and outlines(halfEdges) for one surface cut
  217. using CutAOI = std::pair<std::vector<FI>, std::vector<HI>>;
  218. // vector of Cutted Area of interest cutted from one CGAL model
  219. using CutAOIs = std::vector<CutAOI>;
  220. // vector of CutAOIs for each model
  221. using VCutAOIs = std::vector<CutAOIs>;
  222. /// <summary>
  223. /// Create AOIs(area of interest) on model surface
  224. /// </summary>
  225. /// <param name="cgal_model">Input model converted to CGAL
  226. /// NOTE: will be extended by corefine edge </param>
  227. /// <param name="shapes">2d contours</param>
  228. /// <param name="cgal_shape">[const]Model made by shapes
  229. /// NOTE: Can't be definde as const because of corefine function input definition,
  230. /// but it is.</param>
  231. /// <param name="projection_ratio">Wanted projection distance</param>
  232. /// <param name="s2i">Convert index to shape point from ExPolygons</param>
  233. /// <returns>Patches from model surface</returns>
  234. CutAOIs cut_from_model(CutMesh &cgal_model,
  235. const ExPolygons &shapes,
  236. /*const*/ CutMesh &cgal_shape,
  237. float projection_ratio,
  238. const ExPolygonsIndices &s2i);
  239. using Loop = std::vector<VI>;
  240. using Loops = std::vector<Loop>;
  241. /// <summary>
  242. /// Create closed loops of contour vertices created from open half edges
  243. /// </summary>
  244. /// <param name="outlines">Unsorted half edges</param>
  245. /// <param name="mesh">Source mesh for half edges</param>
  246. /// <returns>Closed loops</returns>
  247. Loops create_loops(const std::vector<HI> &outlines, const CutMesh &mesh);
  248. // To track during diff_models,
  249. // what was cutted off, from CutAOI
  250. struct SurfacePatch
  251. {
  252. // converted cut to CGAL mesh
  253. // Mesh is reduced.
  254. // (do not contain divided triangles on contour - created by side Quad)
  255. CutMesh mesh;
  256. // CvtVI2VI cvt = mesh.property_map<VI, VI>(patch_source_name);
  257. // Conversion VI from this patch to source VI(model) is stored in mesh property
  258. // Outlines - converted CutAOI.second (half edges)
  259. // to loops (vertex indicies) by function create_loops
  260. Loops loops;
  261. // bounding box of mesh
  262. BoundingBoxf3 bb;
  263. //// Data needed to find best projection distances
  264. // index of source model in models
  265. size_t model_id;
  266. // index of source CutAOI
  267. size_t aoi_id;
  268. // index of shape from ExPolygons
  269. size_t shape_id = 0;
  270. // flag that this patch contain whole CutAOI
  271. bool is_whole_aoi = true;
  272. };
  273. using SurfacePatches = std::vector<SurfacePatch>;
  274. struct ModelCutId
  275. {
  276. // index of model
  277. uint32_t model_index;
  278. // index of cut inside model
  279. uint32_t cut_index;
  280. };
  281. /// <summary>
  282. /// Keep conversion from VCutAOIs to Index and vice versa
  283. /// Model_index .. contour(or hole) poin from ExPolygons
  284. /// Index .. continous number
  285. /// </summary>
  286. class ModelCut2index
  287. {
  288. std::vector<uint32_t> m_offsets;
  289. // for check range of index
  290. uint32_t m_count;
  291. public:
  292. ModelCut2index(const VCutAOIs &cuts);
  293. uint32_t calc_index(const ModelCutId &id) const;
  294. ModelCutId calc_id(uint32_t index) const;
  295. uint32_t get_count() const { return m_count; };
  296. const std::vector<uint32_t> &get_offsets() const { return m_offsets; }
  297. };
  298. /// <summary>
  299. /// Differenciate other models
  300. /// </summary>
  301. /// <param name="cuts">Patches from meshes</param>
  302. /// <param name="cut_models">Source points for Cutted AOIs
  303. /// NOTE: Create Reduction map as mesh property - clean on end</param>
  304. /// <param name="models">Original models without cut modifications
  305. /// used for differenciation
  306. /// NOTE: Clip function modify Mesh</param>
  307. /// <param name="projection">Define projection direction</param>
  308. /// <returns>Cuts differenciate by models - Patch</returns>
  309. SurfacePatches diff_models(VCutAOIs &cuts,
  310. /*const*/ CutMeshes &cut_models,
  311. /*const*/ CutMeshes &models,
  312. const Project3d &projection);
  313. /// <summary>
  314. /// Checking whether patch is uninterrupted cover of whole expolygon it belongs.
  315. /// </summary>
  316. /// <param name="cutAOI">Part of surface to check</param>
  317. /// <param name="shape">Source shape</param>
  318. /// <param name="mesh">Source of cut</param>
  319. /// <returns>True when cover whole expolygon otherwise false</returns>
  320. bool is_over_whole_expoly(const CutAOI &cutAOI,
  321. const ExPolygon &shape,
  322. const CutMesh &mesh);
  323. /// <summary>
  324. /// Checking whether patch is uninterrupted cover of whole expolygon it belongs.
  325. /// </summary>
  326. /// <param name="patch">Part of surface to check</param>
  327. /// <param name="shape">Source shape</param>
  328. /// <returns>True when cover whole expolygon otherwise false</returns>
  329. bool is_over_whole_expoly(const SurfacePatch &patch,
  330. const ExPolygons &shapes,
  331. const VCutAOIs &cutAOIs,
  332. const CutMeshes &meshes);
  333. /// <summary>
  334. /// Unptoject points from outline loops of patch
  335. /// </summary>
  336. /// <param name="patch">Contain loops and vertices</param>
  337. /// <param name="projection">Know how to project from 3d to 2d</param>
  338. /// <param name="depth_range">Range of unprojected points x .. min, y .. max value</param>
  339. /// <returns>Unprojected points in loops</returns>
  340. Polygons unproject_loops(const SurfacePatch &patch, const Project &projection, Vec2d &depth_range);
  341. /// <summary>
  342. /// Unproject points from loops and create expolygons
  343. /// </summary>
  344. /// <param name="patch">Patch to convert on expolygon</param>
  345. /// <param name="projection">Convert 3d point to 2d</param>
  346. /// <param name="depth_range">Range of unprojected points x .. min, y .. max value</param>
  347. /// <returns>Expolygon represent patch in 2d</returns>
  348. ExPolygon to_expoly(const SurfacePatch &patch, const Project &projection, Vec2d &depth_range);
  349. /// <summary>
  350. /// To select surface near projection distance
  351. /// </summary>
  352. struct ProjectionDistance
  353. {
  354. // index of source model
  355. uint32_t model_index = std::numeric_limits<uint32_t>::max();
  356. // index of CutAOI
  357. uint32_t aoi_index = std::numeric_limits<uint32_t>::max();
  358. // index of Patch
  359. uint32_t patch_index = std::numeric_limits<uint32_t>::max();
  360. // signed distance to projection
  361. float distance = std::numeric_limits<float>::max();
  362. };
  363. // addresed by ExPolygonsIndices
  364. using ProjectionDistances = std::vector<ProjectionDistance>;
  365. // each point in shapes has its ProjectionDistances
  366. using VDistances = std::vector<ProjectionDistances>;
  367. /// <summary>
  368. /// Calculate distances for SurfacePatches outline points
  369. /// NOTE:
  370. /// each model has to have "vert_shape_map" .. Know source of new vertices
  371. /// </summary>
  372. /// <param name="patches">Part of surface</param>
  373. /// <param name="models">Vertices position</param>
  374. /// <param name="shapes_mesh">Mesh created by shapes</param>
  375. /// <param name="count_shapes_points">Count of contour points in shapes</param>
  376. /// <param name="projection_ratio">Define best distnace</param>
  377. /// <returns>Projection distances of cutted shape points</returns>
  378. VDistances calc_distances(const SurfacePatches &patches,
  379. const CutMeshes &models,
  380. const CutMesh &shapes_mesh,
  381. size_t count_shapes_points,
  382. float projection_ratio);
  383. /// <summary>
  384. /// Select distances in similar depth between expolygons
  385. /// </summary>
  386. /// <param name="distances">All distances - Vector distances for each shape point</param>
  387. /// <param name="shapes">Vector of letters</param>
  388. /// <param name="start">Pivot for start projection in 2d</param>
  389. /// <param name="s2i">Convert index to addresss inside of shape</param>
  390. /// <param name="patches">Cutted parts from surface</param>
  391. /// <returns>Closest distance projection indexed by points in shapes(see s2i)</returns>
  392. ProjectionDistances choose_best_distance(
  393. const VDistances &distances,
  394. const ExPolygons &shapes,
  395. const Point &start,
  396. const ExPolygonsIndices &s2i,
  397. const SurfacePatches &patches);
  398. /// <summary>
  399. /// Create mask for patches
  400. /// </summary>
  401. /// <param name="best_distances">For each point selected closest distance</param>
  402. /// <param name="patches">All patches</param>
  403. /// <param name="shapes">Shape to cut</param>
  404. /// <param name="shapes_bb">Bound of shapes</param>
  405. /// <param name="s2i"></param>
  406. /// <param name="cutAOIs"></param>
  407. /// <param name="meshes"></param>
  408. /// <param name="projection"></param>
  409. /// <returns>Mask of used patch</returns>
  410. std::vector<bool> select_patches(const ProjectionDistances &best_distances,
  411. const SurfacePatches &patches,
  412. const ExPolygons &shapes,
  413. const BoundingBox &shapes_bb,
  414. const ExPolygonsIndices &s2i,
  415. const VCutAOIs &cutAOIs,
  416. const CutMeshes &meshes,
  417. const Project &projection);
  418. /// <summary>
  419. /// Merge two surface cuts together
  420. /// Added surface cut will be consumed
  421. /// </summary>
  422. /// <param name="sc">Surface cut to extend</param>
  423. /// <param name="sc_add">Surface cut to consume</param>
  424. void append(SurfaceCut &sc, SurfaceCut &&sc_add);
  425. /// <summary>
  426. /// Convert patch to indexed_triangle_set
  427. /// </summary>
  428. /// <param name="patch">Part of surface</param>
  429. /// <returns>Converted patch</returns>
  430. SurfaceCut patch2cut(SurfacePatch &patch);
  431. /// <summary>
  432. /// Merge masked patches to one surface cut
  433. /// </summary>
  434. /// <param name="patches">All patches
  435. /// NOTE: Not const because One needs to add property for Convert indices</param>
  436. /// <param name="mask">Mash for using patch</param>
  437. /// <returns>Result surface cut</returns>
  438. SurfaceCut merge_patches(/*const*/ SurfacePatches &patches,
  439. const std::vector<bool> &mask);
  440. #ifdef DEBUG_OUTPUT_DIR
  441. void prepare_dir(const std::string &dir);
  442. void initialize_store(const std::string &dir_to_clear);
  443. /// <summary>
  444. /// Debug purpose store of mesh with colored face by face type
  445. /// </summary>
  446. /// <param name="mesh">Input mesh, could add property color
  447. /// NOTE: Not const because need to [optionaly] append color property map</param>
  448. /// <param name="face_type_map">Color source</param>
  449. /// <param name="file">File to store</param>
  450. void store(const CutMesh &mesh, const FaceTypeMap &face_type_map, const std::string &dir, bool is_filled = false);
  451. void store(const ExPolygons &shapes, const std::string &svg_file);
  452. void store(const CutMesh &mesh, const ReductionMap &reduction_map, const std::string &dir);
  453. void store(const CutAOIs &aois, const CutMesh &mesh, const std::string &dir);
  454. void store(const SurfacePatches &patches, const std::string &dir);
  455. void store(const Vec3f &vertex, const Vec3f &normal, const std::string &file, float size = 2.f);
  456. //void store(const ProjectionDistances &pds, const VCutAOIs &aois, const CutMeshes &meshes, const std::string &file, float width = 0.2f/* [in mm] */);
  457. using Connection = std::pair<size_t, size_t>; using Connections = std::vector<Connection>;
  458. void store(const ExPolygons &shapes, const std::vector<bool> &mask_distances, const Connections &connections, const std::string &file_svg);
  459. void store(const SurfaceCut &cut, const std::string &file, const std::string &contour_dir);
  460. void store(const std::vector<indexed_triangle_set> &models, const std::string &obj_filename);
  461. void store(const std::vector<CutMesh>&models, const std::string &dir);
  462. void store(const Emboss::IProjection &projection, const Point &point_to_project, float projection_ratio, const std::string &obj_filename);
  463. #endif // DEBUG_OUTPUT_DIR
  464. } // namespace privat
  465. #ifdef DEBUG_OUTPUT_DIR
  466. #include "libslic3r/SVG.hpp"
  467. #include <boost/log/trivial.hpp>
  468. #include <filesystem>
  469. #endif // DEBUG_OUTPUT_DIR
  470. SurfaceCut Slic3r::cut_surface(const ExPolygons &shapes,
  471. const std::vector<indexed_triangle_set> &models,
  472. const Emboss::IProjection &projection,
  473. float projection_ratio)
  474. {
  475. assert(!models.empty());
  476. assert(!shapes.empty());
  477. if (models.empty() || shapes.empty() ) return {};
  478. #ifdef DEBUG_OUTPUT_DIR
  479. priv::initialize_store(DEBUG_OUTPUT_DIR);
  480. priv::store(models, DEBUG_OUTPUT_DIR + "models_input.obj");
  481. priv::store(shapes, DEBUG_OUTPUT_DIR + "shapes.svg");
  482. #endif // DEBUG_OUTPUT_DIR
  483. // for filter out triangles out of bounding box
  484. BoundingBox shapes_bb = get_extents(shapes);
  485. #ifdef DEBUG_OUTPUT_DIR
  486. priv::store(projection, shapes_bb.center(), projection_ratio, DEBUG_OUTPUT_DIR + "projection_center.obj");
  487. #endif // DEBUG_OUTPUT_DIR
  488. // for filttrate opposite triangles and a little more
  489. const float max_angle = 89.9f;
  490. priv::CutMeshes cgal_models; // source for patch
  491. priv::CutMeshes cgal_neg_models; // model used for differenciate patches
  492. cgal_models.reserve(models.size());
  493. for (const indexed_triangle_set &its : models) {
  494. std::vector<bool> skip_indicies(its.indices.size(), {false});
  495. priv::set_skip_for_out_of_aoi(skip_indicies, its, projection, shapes_bb);
  496. // create model for differenciate cutted patches
  497. bool flip = true;
  498. cgal_neg_models.push_back(priv::to_cgal(its, skip_indicies, flip));
  499. // cut out more than only opposit triangles
  500. priv::set_skip_by_angle(skip_indicies, its, projection, max_angle);
  501. cgal_models.push_back(priv::to_cgal(its, skip_indicies));
  502. }
  503. #ifdef DEBUG_OUTPUT_DIR
  504. priv::store(cgal_models, DEBUG_OUTPUT_DIR + "model/");// model[0-N].off
  505. priv::store(cgal_neg_models, DEBUG_OUTPUT_DIR + "model_neg/"); // model[0-N].off
  506. #endif // DEBUG_OUTPUT_DIR
  507. priv::CutMesh cgal_shape = priv::to_cgal(shapes, projection);
  508. #ifdef DEBUG_OUTPUT_DIR
  509. CGAL::IO::write_OFF(DEBUG_OUTPUT_DIR + "shape.off", cgal_shape); // only debug
  510. #endif // DEBUG_OUTPUT_DIR
  511. // create tool for convert index to shape Point adress and vice versa
  512. ExPolygonsIndices s2i(shapes);
  513. priv::VCutAOIs model_cuts;
  514. // cut shape from each cgal model
  515. for (priv::CutMesh &cgal_model : cgal_models) {
  516. priv::CutAOIs cutAOIs = priv::cut_from_model(
  517. cgal_model, shapes, cgal_shape, projection_ratio, s2i);
  518. #ifdef DEBUG_OUTPUT_DIR
  519. size_t index = &cgal_model - &cgal_models.front();
  520. priv::store(cutAOIs, cgal_model, DEBUG_OUTPUT_DIR + "model_AOIs/" + std::to_string(index) + "/"); // only debug
  521. #endif // DEBUG_OUTPUT_DIR
  522. model_cuts.push_back(std::move(cutAOIs));
  523. }
  524. priv::SurfacePatches patches = priv::diff_models(model_cuts, cgal_models, cgal_neg_models, projection);
  525. #ifdef DEBUG_OUTPUT_DIR
  526. priv::store(patches, DEBUG_OUTPUT_DIR + "patches/");
  527. #endif // DEBUG_OUTPUT_DIR
  528. if (patches.empty()) return {};
  529. // fix - convert shape_point_id to expolygon index
  530. // save 1 param(s2i) from diff_models call
  531. for (priv::SurfacePatch &patch : patches)
  532. patch.shape_id = s2i.cvt(patch.shape_id).expolygons_index;
  533. // calc distance to projection for all outline points of cutAOI(shape)
  534. // it is used for distiguish the top one
  535. uint32_t shapes_points = s2i.get_count();
  536. // for each point collect all projection distances
  537. priv::VDistances distances = priv::calc_distances(patches, cgal_models, cgal_shape, shapes_points, projection_ratio);
  538. Point start = shapes_bb.center(); // only align center
  539. // Use only outline points
  540. // for each point select best projection
  541. priv::ProjectionDistances best_projection = priv::choose_best_distance(distances, shapes, start, s2i, patches);
  542. std::vector<bool> use_patch = priv::select_patches(best_projection, patches, shapes, shapes_bb, s2i, model_cuts, cgal_models, projection);
  543. SurfaceCut result = merge_patches(patches, use_patch);
  544. //*/
  545. #ifdef DEBUG_OUTPUT_DIR
  546. priv::store(result, DEBUG_OUTPUT_DIR + "result.obj", DEBUG_OUTPUT_DIR + "result_contours/");
  547. #endif // DEBUG_OUTPUT_DIR
  548. return result;
  549. }
  550. indexed_triangle_set Slic3r::cut2model(const SurfaceCut &cut,
  551. const Emboss::IProject3d &projection)
  552. {
  553. assert(!cut.empty());
  554. size_t count_vertices = cut.vertices.size() * 2;
  555. size_t count_indices = cut.indices.size() * 2;
  556. // indices from from zig zag
  557. for (const auto &c : cut.contours) {
  558. assert(!c.empty());
  559. count_indices += c.size() * 2;
  560. }
  561. indexed_triangle_set result;
  562. result.vertices.reserve(count_vertices);
  563. result.indices.reserve(count_indices);
  564. // front
  565. result.vertices.insert(result.vertices.end(),
  566. cut.vertices.begin(), cut.vertices.end());
  567. result.indices.insert(result.indices.end(),
  568. cut.indices.begin(), cut.indices.end());
  569. // back
  570. for (const Vec3f &v : cut.vertices) {
  571. Vec3d vd = v.cast<double>();
  572. Vec3d vd2 = projection.project(vd);
  573. result.vertices.push_back(vd2.cast<float>());
  574. }
  575. size_t back_offset = cut.vertices.size();
  576. for (const auto &i : cut.indices) {
  577. // check range of indices in cut
  578. assert(i.x() + back_offset < result.vertices.size());
  579. assert(i.y() + back_offset < result.vertices.size());
  580. assert(i.z() + back_offset < result.vertices.size());
  581. assert(i.x() >= 0 && i.x() < cut.vertices.size());
  582. assert(i.y() >= 0 && i.y() < cut.vertices.size());
  583. assert(i.z() >= 0 && i.z() < cut.vertices.size());
  584. // Y and Z is swapped CCW triangles for back side
  585. result.indices.emplace_back(i.x() + back_offset,
  586. i.z() + back_offset,
  587. i.y() + back_offset);
  588. }
  589. // zig zag indices
  590. for (const auto &contour : cut.contours) {
  591. size_t prev_front_index = contour.back();
  592. size_t prev_back_index = back_offset + prev_front_index;
  593. for (size_t front_index : contour) {
  594. assert(front_index < cut.vertices.size());
  595. size_t back_index = back_offset + front_index;
  596. result.indices.emplace_back(front_index, prev_front_index, back_index);
  597. result.indices.emplace_back(prev_front_index, prev_back_index, back_index);
  598. prev_front_index = front_index;
  599. prev_back_index = back_index;
  600. }
  601. }
  602. assert(count_vertices == result.vertices.size());
  603. assert(count_indices == result.indices.size());
  604. return result;
  605. }
  606. // set_skip_for_out_of_aoi helping functions
  607. namespace priv {
  608. // define plane
  609. using PointNormal = std::pair<Vec3d, Vec3d>;
  610. using PointNormals = std::array<PointNormal, 4>;
  611. /// <summary>
  612. /// Check
  613. /// </summary>
  614. /// <param name="side"></param>
  615. /// <param name="v"></param>
  616. /// <param name="point_normals"></param>
  617. /// <returns></returns>
  618. bool is_out_of(const Vec3d &v, const PointNormal &point_normal);
  619. using IsOnSides = std::vector<std::array<bool, 4>>;
  620. /// <summary>
  621. /// Check if triangle t has all vertices out of any plane
  622. /// </summary>
  623. /// <param name="t">Triangle</param>
  624. /// <param name="is_on_sides">Flag is vertex index out of plane</param>
  625. /// <returns>True when triangle is out of one of plane</returns>
  626. bool is_all_on_one_side(const Vec3i32 &t, const IsOnSides& is_on_sides);
  627. } // namespace priv
  628. bool priv::is_out_of(const Vec3d &v, const PointNormal &point_normal)
  629. {
  630. const Vec3d& p = point_normal.first;
  631. const Vec3d& n = point_normal.second;
  632. double signed_distance = (v - p).dot(n);
  633. return signed_distance > 1e-5;
  634. };
  635. bool priv::is_all_on_one_side(const Vec3i32 &t, const IsOnSides& is_on_sides) {
  636. for (size_t side = 0; side < 4; side++) {
  637. bool result = true;
  638. for (auto vi : t) {
  639. if (!is_on_sides[vi][side]) {
  640. result = false;
  641. break;
  642. }
  643. }
  644. if (result) return true;
  645. }
  646. return false;
  647. }
  648. void priv::set_skip_for_out_of_aoi(std::vector<bool> &skip_indicies,
  649. const indexed_triangle_set &its,
  650. const Project &projection,
  651. const BoundingBox &shapes_bb)
  652. {
  653. assert(skip_indicies.size() == its.indices.size());
  654. // 1`*----* 2`
  655. // / 2 /|
  656. // 1 *----* |
  657. // | | * 3`
  658. // | |/
  659. // 0 *----* 3
  660. //////////////////
  661. std::array<std::pair<Vec3d, Vec3d>, 4> bb;
  662. int index = 0;
  663. for (Point v :
  664. {shapes_bb.min, Point{shapes_bb.min.x(), shapes_bb.max.y()},
  665. shapes_bb.max, Point{shapes_bb.max.x(), shapes_bb.min.y()}})
  666. bb[index++] = projection.create_front_back(v);
  667. // define planes to test
  668. // 0 .. under
  669. // 1 .. left
  670. // 2 .. above
  671. // 3 .. right
  672. size_t prev_i = 3;
  673. // plane is defined by point and normal
  674. PointNormals point_normals;
  675. for (size_t i = 0; i < 4; i++) {
  676. const Vec3d &p1 = bb[i].first;
  677. const Vec3d &p2 = bb[i].second;
  678. const Vec3d &p3 = bb[prev_i].first;
  679. prev_i = i;
  680. Vec3d v1 = p2 - p1;
  681. v1.normalize();
  682. Vec3d v2 = p3 - p1;
  683. v2.normalize();
  684. Vec3d normal = v2.cross(v1);
  685. normal.normalize();
  686. point_normals[i] = {p1, normal};
  687. }
  688. // check that projection is not left handed
  689. // Fix for reflected projection
  690. if (is_out_of(point_normals[2].first, point_normals[0])) {
  691. // projection is reflected so normals are reflected
  692. for (auto &pn : point_normals)
  693. pn.second *= -1;
  694. }
  695. // same meaning as point normal
  696. IsOnSides is_on_sides(its.vertices.size(), {false,false,false,false});
  697. // inspect all vertices when it is out of bounding box
  698. tbb::parallel_for(tbb::blocked_range<size_t>(0, its.vertices.size()),
  699. [&its, &point_normals, &is_on_sides](const tbb::blocked_range<size_t> &range) {
  700. for (size_t i = range.begin(); i < range.end(); ++i) {
  701. Vec3d v = its.vertices[i].cast<double>();
  702. // under + above
  703. for (int side : {0, 2}) {
  704. if (is_out_of(v, point_normals[side])) {
  705. is_on_sides[i][side] = true;
  706. // when it is under it can't be above
  707. break;
  708. }
  709. }
  710. // left + right
  711. for (int side : {1, 3}) {
  712. if (is_out_of(v, point_normals[side])) {
  713. is_on_sides[i][side] = true;
  714. // when it is on left side it can't be on right
  715. break;
  716. }
  717. }
  718. }
  719. }); // END parallel for
  720. // inspect all triangles, when it is out of bounding box
  721. tbb::parallel_for(tbb::blocked_range<size_t>(0, its.indices.size()),
  722. [&its, &is_on_sides, &skip_indicies](const tbb::blocked_range<size_t> &range) {
  723. for (size_t i = range.begin(); i < range.end(); ++i) {
  724. if (is_all_on_one_side(its.indices[i], is_on_sides))
  725. skip_indicies[i] = true;
  726. }
  727. }); // END parallel for
  728. }
  729. indexed_triangle_set Slic3r::its_mask(const indexed_triangle_set &its,
  730. const std::vector<bool> &mask)
  731. {
  732. if (its.indices.size() != mask.size()) {
  733. assert(false);
  734. return {};
  735. }
  736. std::vector<uint32_t> cvt_vetices(its.vertices.size(), {std::numeric_limits<uint32_t>::max()});
  737. size_t vertices_count = 0;
  738. size_t faces_count = 0;
  739. for (const auto &t : its.indices) {
  740. size_t index = &t - &its.indices.front();
  741. if (!mask[index]) continue;
  742. ++faces_count;
  743. for (const auto vi : t) {
  744. uint32_t &cvt = cvt_vetices[vi];
  745. if (cvt == std::numeric_limits<uint32_t>::max())
  746. cvt = vertices_count++;
  747. }
  748. }
  749. if (faces_count == 0) return {};
  750. indexed_triangle_set result;
  751. result.indices.reserve(faces_count);
  752. result.vertices = std::vector<Vec3f>(vertices_count);
  753. for (size_t i = 0; i < its.vertices.size(); ++i) {
  754. uint32_t index = cvt_vetices[i];
  755. if (index == std::numeric_limits<uint32_t>::max()) continue;
  756. result.vertices[index] = its.vertices[i];
  757. }
  758. for (const stl_triangle_vertex_indices &f : its.indices)
  759. if (mask[&f - &its.indices.front()])
  760. result.indices.push_back(stl_triangle_vertex_indices(
  761. cvt_vetices[f[0]], cvt_vetices[f[1]], cvt_vetices[f[2]]));
  762. return result;
  763. }
  764. indexed_triangle_set Slic3r::its_cut_AoI(const indexed_triangle_set &its,
  765. const BoundingBox &bb,
  766. const Emboss::IProjection &projection)
  767. {
  768. std::vector<bool> skip_indicies(its.indices.size(), false);
  769. priv::set_skip_for_out_of_aoi(skip_indicies, its, projection, bb);
  770. // invert values in vector of bool
  771. skip_indicies.flip();
  772. return its_mask(its, skip_indicies);
  773. }
  774. void priv::set_skip_by_angle(std::vector<bool> &skip_indicies,
  775. const indexed_triangle_set &its,
  776. const Project3d &projection,
  777. double max_angle)
  778. {
  779. assert(max_angle < 90. && max_angle > 89.);
  780. assert(skip_indicies.size() == its.indices.size());
  781. float threshold = static_cast<float>(cos(max_angle / 180. * M_PI));
  782. for (const stl_triangle_vertex_indices& face : its.indices) {
  783. size_t index = &face - &its.indices.front();
  784. if (skip_indicies[index]) continue;
  785. Vec3f n = its_face_normal(its, face);
  786. const Vec3f& v = its.vertices[face[0]];
  787. const Vec3d vd = v.cast<double>();
  788. // Improve: For Orthogonal Projection it is same for each vertex
  789. Vec3d projectedd = projection.project(vd);
  790. Vec3f projected = projectedd.cast<float>();
  791. Vec3f project_dir = projected - v;
  792. project_dir.normalize();
  793. float cos_alpha = project_dir.dot(n);
  794. if (cos_alpha > threshold) continue;
  795. skip_indicies[index] = true;
  796. }
  797. }
  798. priv::CutMesh priv::to_cgal(const indexed_triangle_set &its,
  799. const std::vector<bool> &skip_indicies,
  800. bool flip)
  801. {
  802. const std::vector<stl_vertex> &vertices = its.vertices;
  803. const std::vector<stl_triangle_vertex_indices> &indices = its.indices;
  804. std::vector<bool> use_vetices(vertices.size(), {false});
  805. size_t vertices_count = 0;
  806. size_t faces_count = 0;
  807. size_t edges_count = 0;
  808. for (const auto &t : indices) {
  809. size_t index = &t - &indices.front();
  810. if (skip_indicies[index]) continue;
  811. ++faces_count;
  812. size_t count_used_vertices = 0;
  813. for (const auto vi : t) {
  814. if (!use_vetices[vi]) {
  815. ++vertices_count;
  816. use_vetices[vi] = true;
  817. } else {
  818. ++count_used_vertices;
  819. }
  820. }
  821. switch (count_used_vertices) {
  822. case 3: break; // all edges are already counted
  823. case 2: edges_count += 2; break;
  824. case 1:
  825. case 0: edges_count += 3; break;
  826. default: assert(false);
  827. }
  828. }
  829. assert(vertices_count <= vertices.size());
  830. assert(edges_count <= (indices.size() * 3));
  831. assert(faces_count <= indices.size());
  832. CutMesh result;
  833. result.reserve(vertices_count, edges_count, faces_count);
  834. std::vector<VI> to_filtrated_vertices_index(vertices.size());
  835. size_t filtrated_vertices_index = 0;
  836. for (size_t i = 0; i < vertices.size(); ++i)
  837. if (use_vetices[i]) {
  838. to_filtrated_vertices_index[i] = VI(filtrated_vertices_index);
  839. ++filtrated_vertices_index;
  840. }
  841. for (const stl_vertex& v : vertices) {
  842. if (!use_vetices[&v - &vertices.front()]) continue;
  843. result.add_vertex(CutMesh::Point{v.x(), v.y(), v.z()});
  844. }
  845. if (!flip) {
  846. for (const stl_triangle_vertex_indices &f : indices) {
  847. if (skip_indicies[&f - &indices.front()]) continue;
  848. result.add_face(to_filtrated_vertices_index[f[0]],
  849. to_filtrated_vertices_index[f[1]],
  850. to_filtrated_vertices_index[f[2]]);
  851. }
  852. } else {
  853. for (const stl_triangle_vertex_indices &f : indices) {
  854. if (skip_indicies[&f - &indices.front()]) continue;
  855. result.add_face(to_filtrated_vertices_index[f[2]],
  856. to_filtrated_vertices_index[f[1]],
  857. to_filtrated_vertices_index[f[0]]);
  858. }
  859. }
  860. return result;
  861. }
  862. bool priv::exist_duplicit_vertex(const CutMesh &mesh) {
  863. std::vector<Vec3d> points;
  864. points.reserve(mesh.vertices().size());
  865. // copy points
  866. for (VI vi : mesh.vertices()) {
  867. const P3 &p = mesh.point(vi);
  868. points.emplace_back(p.x(), p.y(), p.z());
  869. }
  870. std::sort(points.begin(), points.end(), [](const Vec3d &v1, const Vec3d &v2) {
  871. return v1.x() < v2.x() ||
  872. (v1.x() == v2.x() &&
  873. (v1.y() < v2.y() ||
  874. (v1.y() == v2.y() &&
  875. v1.z() < v2.z())));
  876. });
  877. // find first duplicit
  878. auto it = std::adjacent_find(points.begin(), points.end());
  879. return it != points.end();
  880. }
  881. priv::CutMesh priv::to_cgal(const ExPolygons &shapes,
  882. const Project &projection)
  883. {
  884. if (shapes.empty()) return {};
  885. CutMesh result;
  886. EdgeShapeMap edge_shape_map = result.add_property_map<EI, IntersectingElement>(edge_shape_map_name).first;
  887. FaceShapeMap face_shape_map = result.add_property_map<FI, IntersectingElement>(face_shape_map_name).first;
  888. std::vector<VI> indices;
  889. auto insert_contour = [&projection, &indices, &result,
  890. &edge_shape_map, &face_shape_map]
  891. (const Polygon &polygon) {
  892. indices.clear();
  893. indices.reserve(polygon.points.size() * 2);
  894. size_t num_vertices_old = result.number_of_vertices();
  895. for (const Point &polygon_point : polygon.points) {
  896. auto [front, back] = projection.create_front_back(polygon_point);
  897. P3 v_front{front.x(), front.y(), front.z()};
  898. VI vi1 = result.add_vertex(v_front);
  899. assert(vi1.idx() == (indices.size() + num_vertices_old));
  900. indices.push_back(vi1);
  901. P3 v_back{back.x(), back.y(), back.z()};
  902. VI vi2 = result.add_vertex(v_back);
  903. assert(vi2.idx() == (indices.size() + num_vertices_old));
  904. indices.push_back(vi2);
  905. }
  906. auto find_edge = [&result](FI fi, VI from, VI to) {
  907. HI hi = result.halfedge(fi);
  908. for (; result.target(hi) != to; hi = result.next(hi));
  909. assert(result.source(hi) == from);
  910. assert(result.target(hi) == to);
  911. return result.edge(hi);
  912. };
  913. uint32_t contour_index = static_cast<uint32_t>(num_vertices_old / 2);
  914. for (int32_t i = 0; i < int32_t(indices.size()); i += 2) {
  915. bool is_first = i == 0;
  916. bool is_last = size_t(i + 2) >= indices.size();
  917. int32_t j = is_last ? 0 : (i + 2);
  918. FI fi1 = result.add_face(indices[i], indices[j], indices[i + 1]);
  919. EI ei1 = find_edge(fi1, indices[i + 1], indices[i]);
  920. EI ei2 = find_edge(fi1, indices[j], indices[i + 1]);
  921. FI fi2 = result.add_face(indices[j], indices[j + 1], indices[i + 1]);
  922. IntersectingElement element {contour_index, (unsigned char)IntersectingElement::Type::undefined};
  923. if (is_first) element.set_is_first();
  924. if (is_last) element.set_is_last();
  925. edge_shape_map[ei1] = element.set_type(IntersectingElement::Type::edge_1);
  926. face_shape_map[fi1] = element.set_type(IntersectingElement::Type::face_1);
  927. edge_shape_map[ei2] = element.set_type(IntersectingElement::Type::edge_2);
  928. face_shape_map[fi2] = element.set_type(IntersectingElement::Type::face_2);
  929. ++contour_index;
  930. }
  931. };
  932. size_t count_point = count_points(shapes);
  933. result.reserve(result.number_of_vertices() + 2 * count_point,
  934. result.number_of_edges() + 4 * count_point,
  935. result.number_of_faces() + 2 * count_point);
  936. // Identify polygon
  937. for (const ExPolygon &shape : shapes) {
  938. insert_contour(shape.contour);
  939. for (const Polygon &hole : shape.holes)
  940. insert_contour(hole);
  941. }
  942. assert(!exist_duplicit_vertex(result));
  943. return result;
  944. }
  945. priv::ModelCut2index::ModelCut2index(const VCutAOIs &cuts)
  946. {
  947. // prepare offsets
  948. m_offsets.reserve(cuts.size());
  949. uint32_t offset = 0;
  950. for (const CutAOIs &model_cuts: cuts) {
  951. m_offsets.push_back(offset);
  952. offset += model_cuts.size();
  953. }
  954. m_count = offset;
  955. }
  956. uint32_t priv::ModelCut2index::calc_index(const ModelCutId &id) const
  957. {
  958. assert(id.model_index < m_offsets.size());
  959. uint32_t offset = m_offsets[id.model_index];
  960. uint32_t res = offset + id.cut_index;
  961. assert(((id.model_index+1) < m_offsets.size() && res < m_offsets[id.model_index+1]) ||
  962. ((id.model_index+1) == m_offsets.size() && res < m_count));
  963. return res;
  964. }
  965. priv::ModelCutId priv::ModelCut2index::calc_id(uint32_t index) const
  966. {
  967. assert(index < m_count);
  968. ModelCutId result{0,0};
  969. // find shape index
  970. for (size_t model_index = 1; model_index < m_offsets.size(); ++model_index) {
  971. if (m_offsets[model_index] > index) break;
  972. result.model_index = model_index;
  973. }
  974. result.cut_index = index - m_offsets[result.model_index];
  975. return result;
  976. }
  977. // cut_from_model help functions
  978. namespace priv {
  979. /// <summary>
  980. /// Track source of intersection
  981. /// Help for anotate inner and outer faces
  982. /// </summary>
  983. struct Visitor : public CGAL::Polygon_mesh_processing::Corefinement::Default_visitor<CutMesh> {
  984. Visitor(const CutMesh &object, const CutMesh &shape, EdgeShapeMap edge_shape_map,
  985. FaceShapeMap face_shape_map, VertexShapeMap vert_shape_map, bool* is_valid) :
  986. object(object), shape(shape), edge_shape_map(edge_shape_map), face_shape_map(face_shape_map),
  987. vert_shape_map(vert_shape_map), is_valid(is_valid)
  988. {}
  989. const CutMesh &object;
  990. const CutMesh &shape;
  991. // Properties of the shape mesh:
  992. EdgeShapeMap edge_shape_map;
  993. FaceShapeMap face_shape_map;
  994. // Properties of the object mesh.
  995. VertexShapeMap vert_shape_map;
  996. // check for anomalities
  997. bool* is_valid;
  998. // keep source of intersection for each intersection
  999. // used to copy data into vert_shape_map
  1000. std::vector<const IntersectingElement*> intersections;
  1001. /// <summary>
  1002. /// Called when a new intersection point is detected.
  1003. /// The intersection is detected using a face of tm_f and an edge of tm_e.
  1004. /// Intersecting an edge hh_edge from tm_f with a face h_e of tm_e.
  1005. /// https://doc.cgal.org/latest/Polygon_mesh_processing/classPMPCorefinementVisitor.html#a00ee0ca85db535a48726a92414acda7f
  1006. /// </summary>
  1007. /// <param name="i_id">The id of the intersection point, starting at 0. Ids are consecutive.</param>
  1008. /// <param name="sdim">Dimension of a simplex part of face(h_e) that is intersected by edge(h_f):
  1009. /// 0 for vertex: target(h_e)
  1010. /// 1 for edge: h_e
  1011. /// 2 for the interior of face: face(h_e) </param>
  1012. /// <param name="h_f">
  1013. /// A halfedge from tm_f indicating the simplex intersected:
  1014. /// if sdim==0 the target of h_f is the intersection point,
  1015. /// if sdim==1 the edge of h_f contains the intersection point in its interior,
  1016. /// if sdim==2 the face of h_f contains the intersection point in its interior.
  1017. /// @Vojta: Edge of tm_f, see is_target_coplanar & is_source_coplanar whether any vertex of h_f is coplanar with face(h_e).
  1018. /// </param>
  1019. /// <param name="h_e">A halfedge from tm_e
  1020. /// @Vojta: Vertex, halfedge or face of tm_e intersected by h_f, see comment at sdim.
  1021. /// </param>
  1022. /// <param name="tm_f">Mesh containing h_f</param>
  1023. /// <param name="tm_e">Mesh containing h_e</param>
  1024. /// <param name="is_target_coplanar">True if the target of h_e is the intersection point
  1025. /// @Vojta: source(h_f) is coplanar with face(made by h_e).</param>
  1026. /// <param name="is_source_coplanar">True if the source of h_e is the intersection point
  1027. /// @Vojta: target(h_f) is coplanar with face(h_e).</param>
  1028. void intersection_point_detected(std::size_t i_id,
  1029. int sdim,
  1030. HI h_f,
  1031. HI h_e,
  1032. const CutMesh &tm_f,
  1033. const CutMesh &tm_e,
  1034. bool is_target_coplanar,
  1035. bool is_source_coplanar);
  1036. /// <summary>
  1037. /// Called when a new vertex is added in tm (either an edge split or a vertex inserted in the interior of a face).
  1038. /// Fill vertex_shape_map by intersections
  1039. /// </summary>
  1040. /// <param name="i_id">Order number of intersection point</param>
  1041. /// <param name="v">New added vertex</param>
  1042. /// <param name="tm">Affected mesh</param>
  1043. void new_vertex_added(std::size_t i_id, VI v, const CutMesh &tm);
  1044. };
  1045. /// <summary>
  1046. /// Distiquish face type for half edge
  1047. /// </summary>
  1048. /// <param name="hi">Define face</param>
  1049. /// <param name="mesh">Mesh to process</param>
  1050. /// <param name="shape_mesh">Vertices of mesh made by shapes</param>
  1051. /// <param name="vertex_shape_map">Keep information about source of created vertex</param>
  1052. /// <param name="shape2index"></param>
  1053. /// <param name="shape2index">Convert index to shape point from ExPolygons</param>
  1054. /// <returns>Face type defined by hi</returns>
  1055. bool is_face_inside(HI hi,
  1056. const CutMesh &mesh,
  1057. const CutMesh &shape_mesh,
  1058. const VertexShapeMap &vertex_shape_map,
  1059. const ExPolygonsIndices &shape2index);
  1060. /// <summary>
  1061. /// Face with constrained edge are inside/outside by type of intersection
  1062. /// Other set to not_constrained(still it could be inside/outside)
  1063. /// </summary>
  1064. /// <param name="face_type_map">[Output] property map with type of faces</param>
  1065. /// <param name="mesh">Mesh to process</param>
  1066. /// <param name="vertex_shape_map">Keep information about source of created vertex</param>
  1067. /// <param name="ecm">Dynamic Edge Constrained Map of bool</param>
  1068. /// <param name="shape_mesh">Vertices of mesh made by shapes</param>
  1069. /// <param name="shape2index">Convert index to shape point from ExPolygons</param>
  1070. void set_face_type(FaceTypeMap &face_type_map,
  1071. const CutMesh &mesh,
  1072. const VertexShapeMap &vertex_shape_map,
  1073. const EdgeBoolMap &ecm,
  1074. const CutMesh &shape_mesh,
  1075. const ExPolygonsIndices &shape2index);
  1076. /// <summary>
  1077. /// Change FaceType from not_constrained to inside
  1078. /// For neighbor(or neighbor of neighbor of ...) of inside triangles.
  1079. /// Process only not_constrained triangles
  1080. /// </summary>
  1081. /// <param name="mesh">Corefined mesh</param>
  1082. /// <param name="face_type_map">In/Out map with faces type</param>
  1083. void flood_fill_inner(const CutMesh &mesh, FaceTypeMap &face_type_map);
  1084. /// <summary>
  1085. /// Collect connected inside faces
  1086. /// Collect outline half edges
  1087. /// </summary>
  1088. /// <param name="process">Queue of face to process - find connected</param>
  1089. /// <param name="faces">[Output] collected Face indices from mesh</param>
  1090. /// <param name="outlines">[Output] collected Halfedge indices from mesh</param>
  1091. /// <param name="face_type_map">Use flag inside / outside
  1092. /// NOTE: Modify in function: inside -> inside_processed</param>
  1093. /// <param name="mesh">mesh to process</param>
  1094. void collect_surface_data(std::queue<FI> &process,
  1095. std::vector<FI> &faces,
  1096. std::vector<HI> &outlines,
  1097. FaceTypeMap &face_type_map,
  1098. const CutMesh &mesh);
  1099. /// <summary>
  1100. /// Create areas from mesh surface
  1101. /// </summary>
  1102. /// <param name="mesh">Model</param>
  1103. /// <param name="shapes">Cutted shapes</param>
  1104. /// <param name="face_type_map">Define Triangles of interest.
  1105. /// Edge between inside / outside.
  1106. /// NOTE: Not const because it need to flag proccessed faces</param>
  1107. /// <returns>Areas of interest from mesh</returns>
  1108. CutAOIs create_cut_area_of_interests(const CutMesh &mesh,
  1109. const ExPolygons &shapes,
  1110. FaceTypeMap &face_type_map);
  1111. } // namespace priv
  1112. void priv::Visitor::intersection_point_detected(std::size_t i_id,
  1113. int sdim,
  1114. HI h_f,
  1115. HI h_e,
  1116. const CutMesh &tm_f,
  1117. const CutMesh &tm_e,
  1118. bool is_target_coplanar,
  1119. bool is_source_coplanar)
  1120. {
  1121. if (i_id >= intersections.size()) {
  1122. size_t capacity = Slic3r::next_highest_power_of_2(i_id + 1);
  1123. intersections.reserve(capacity);
  1124. intersections.resize(capacity);
  1125. }
  1126. const IntersectingElement *intersection_ptr = nullptr;
  1127. if (&tm_e == &shape) {
  1128. assert(&tm_f == &object);
  1129. switch (sdim) {
  1130. case 1:
  1131. // edge x edge intersection
  1132. intersection_ptr = &edge_shape_map[shape.edge(h_e)];
  1133. break;
  1134. case 2:
  1135. // edge x face intersection
  1136. intersection_ptr = &face_shape_map[shape.face(h_e)];
  1137. break;
  1138. default: assert(false);
  1139. }
  1140. if (is_target_coplanar)
  1141. vert_shape_map[object.source(h_f)] = intersection_ptr;
  1142. if (is_source_coplanar)
  1143. vert_shape_map[object.target(h_f)] = intersection_ptr;
  1144. } else {
  1145. assert(&tm_f == &shape && &tm_e == &object);
  1146. assert(!is_target_coplanar);
  1147. assert(!is_source_coplanar);
  1148. if (is_target_coplanar || is_source_coplanar)
  1149. *is_valid = false;
  1150. intersection_ptr = &edge_shape_map[shape.edge(h_f)];
  1151. if (sdim == 0) vert_shape_map[object.target(h_e)] = intersection_ptr;
  1152. }
  1153. if (intersection_ptr->shape_point_index == std::numeric_limits<uint32_t>::max()) {
  1154. // there is unexpected intersection
  1155. // Top (or Bottom) shape contour edge (or vertex) intersection
  1156. // Suggest to change projection min/max limits
  1157. *is_valid = false;
  1158. }
  1159. intersections[i_id] = intersection_ptr;
  1160. }
  1161. void priv::Visitor::new_vertex_added(std::size_t i_id, VI v, const CutMesh &tm)
  1162. {
  1163. assert(&tm == &object);
  1164. assert(i_id < intersections.size());
  1165. const IntersectingElement *intersection_ptr = intersections[i_id];
  1166. assert(intersection_ptr != nullptr);
  1167. // intersection was not filled in function intersection_point_detected
  1168. //assert(intersection_ptr->point_index != std::numeric_limits<uint32_t>::max());
  1169. vert_shape_map[v] = intersection_ptr;
  1170. }
  1171. bool priv::is_face_inside(HI hi,
  1172. const CutMesh &mesh,
  1173. const CutMesh &shape_mesh,
  1174. const VertexShapeMap &vertex_shape_map,
  1175. const ExPolygonsIndices &shape2index)
  1176. {
  1177. VI vi_from = mesh.source(hi);
  1178. VI vi_to = mesh.target(hi);
  1179. // This face has a constrained edge.
  1180. const IntersectingElement &shape_from = *vertex_shape_map[vi_from];
  1181. const IntersectingElement &shape_to = *vertex_shape_map[vi_to];
  1182. assert(shape_from.shape_point_index != std::numeric_limits<uint32_t>::max());
  1183. assert(shape_from.attr != (unsigned char) IntersectingElement::Type::undefined);
  1184. assert(shape_to.shape_point_index != std::numeric_limits<uint32_t>::max());
  1185. assert(shape_to.attr != (unsigned char) IntersectingElement::Type::undefined);
  1186. // index into contour
  1187. uint32_t i_from = shape_from.shape_point_index;
  1188. uint32_t i_to = shape_to.shape_point_index;
  1189. IntersectingElement::Type type_from = shape_from.get_type();
  1190. IntersectingElement::Type type_to = shape_to.get_type();
  1191. if (i_from == i_to && type_from == type_to) {
  1192. // intersecting element must be face
  1193. assert(type_from == IntersectingElement::Type::face_1 ||
  1194. type_from == IntersectingElement::Type::face_2);
  1195. // count of vertices is twice as count of point in the contour
  1196. uint32_t i = i_from * 2;
  1197. // j is next contour point in vertices
  1198. uint32_t j = i + 2;
  1199. if (shape_from.is_last()) {
  1200. ExPolygonsIndex point_id = shape2index.cvt(i_from);
  1201. point_id.point_index = 0;
  1202. j = shape2index.cvt(point_id)*2;
  1203. }
  1204. // opposit point(in triangle face) to edge
  1205. const P3 &p = mesh.point(mesh.target(mesh.next(hi)));
  1206. // abc is source triangle face
  1207. CGAL::Sign abcp = type_from == IntersectingElement::Type::face_1 ?
  1208. CGAL::orientation(shape_mesh.point(VI(i)),
  1209. shape_mesh.point(VI(i + 1)),
  1210. shape_mesh.point(VI(j)), p) :
  1211. // type_from == IntersectingElement::Type::face_2
  1212. CGAL::orientation(shape_mesh.point(VI(j)),
  1213. shape_mesh.point(VI(i + 1)),
  1214. shape_mesh.point(VI(j + 1)), p);
  1215. return abcp == CGAL::POSITIVE;
  1216. } else if (i_from < i_to || (i_from == i_to && type_from < type_to)) {
  1217. bool is_last = shape_to.is_last() && shape_from.is_first();
  1218. // check continuity of indicies
  1219. assert(i_from == i_to || is_last || (i_from + 1) == i_to);
  1220. return !is_last;
  1221. } else {
  1222. assert(i_from > i_to || (i_from == i_to && type_from > type_to));
  1223. bool is_last = shape_to.is_first() && shape_from.is_last();
  1224. // check continuity of indicies
  1225. assert(i_from == i_to || is_last || (i_to + 1) == i_from);
  1226. return is_last;
  1227. }
  1228. assert(false);
  1229. return false;
  1230. }
  1231. void priv::set_face_type(FaceTypeMap &face_type_map,
  1232. const CutMesh &mesh,
  1233. const VertexShapeMap &vertex_shape_map,
  1234. const EdgeBoolMap &ecm,
  1235. const CutMesh &shape_mesh,
  1236. const ExPolygonsIndices &shape2index)
  1237. {
  1238. for (EI ei : mesh.edges()) {
  1239. if (!ecm[ei]) continue;
  1240. HI hi = mesh.halfedge(ei);
  1241. FI fi = mesh.face(hi);
  1242. bool is_inside = is_face_inside(hi, mesh, shape_mesh, vertex_shape_map, shape2index);
  1243. face_type_map[fi] = is_inside ? FaceType::inside : FaceType::outside;
  1244. HI hi_op = mesh.opposite(hi);
  1245. assert(hi_op.is_valid());
  1246. if (!hi_op.is_valid()) continue;
  1247. FI fi_op = mesh.face(hi_op);
  1248. assert(fi_op.is_valid());
  1249. if (!fi_op.is_valid()) continue;
  1250. face_type_map[fi_op] = (!is_inside) ? FaceType::inside : FaceType::outside;
  1251. }
  1252. }
  1253. priv::CutAOIs priv::cut_from_model(CutMesh &cgal_model,
  1254. const ExPolygons &shapes,
  1255. CutMesh &cgal_shape,
  1256. float projection_ratio,
  1257. const ExPolygonsIndices &s2i)
  1258. {
  1259. // pointer to edge or face shape_map
  1260. VertexShapeMap vert_shape_map = cgal_model.add_property_map<VI, const IntersectingElement*>(vert_shape_map_name, nullptr).first;
  1261. // detect anomalities in visitor.
  1262. bool is_valid = true;
  1263. // NOTE: map are created when convert shapes to cgal model
  1264. const EdgeShapeMap& edge_shape_map = cgal_shape.property_map<EI, IntersectingElement>(edge_shape_map_name).first;
  1265. const FaceShapeMap& face_shape_map = cgal_shape.property_map<FI, IntersectingElement>(face_shape_map_name).first;
  1266. Visitor visitor{cgal_model, cgal_shape, edge_shape_map, face_shape_map, vert_shape_map, &is_valid};
  1267. // a property map containing the constrained-or-not status of each edge
  1268. EdgeBoolMap ecm = cgal_model.add_property_map<EI, bool>(is_constrained_edge_name, false).first;
  1269. const auto &p = CGAL::parameters::visitor(visitor)
  1270. .edge_is_constrained_map(ecm)
  1271. .throw_on_self_intersection(false);
  1272. const auto& q = CGAL::parameters::do_not_modify(true);
  1273. CGAL::Polygon_mesh_processing::corefine(cgal_model, cgal_shape, p, q);
  1274. if (!is_valid) return {};
  1275. FaceTypeMap face_type_map = cgal_model.add_property_map<FI, FaceType>(face_type_map_name, FaceType::not_constrained).first;
  1276. // Select inside and outside face in model
  1277. set_face_type(face_type_map, cgal_model, vert_shape_map, ecm, cgal_shape, s2i);
  1278. #ifdef DEBUG_OUTPUT_DIR
  1279. store(cgal_model, face_type_map, DEBUG_OUTPUT_DIR + "constrained/"); // only debug
  1280. #endif // DEBUG_OUTPUT_DIR
  1281. // flood fill the other faces inside the region.
  1282. flood_fill_inner(cgal_model, face_type_map);
  1283. #ifdef DEBUG_OUTPUT_DIR
  1284. store(cgal_model, face_type_map, DEBUG_OUTPUT_DIR + "filled/", true); // only debug
  1285. #endif // DEBUG_OUTPUT_DIR
  1286. // IMPROVE: AOIs area could be created during flood fill
  1287. return create_cut_area_of_interests(cgal_model, shapes, face_type_map);
  1288. }
  1289. void priv::flood_fill_inner(const CutMesh &mesh,
  1290. FaceTypeMap &face_type_map)
  1291. {
  1292. std::vector<FI> process;
  1293. // guess count of connected not constrained triangles
  1294. size_t guess_size = 128;
  1295. process.reserve(guess_size);
  1296. // check if neighbor(one of three in triangle) has type inside
  1297. auto has_inside_neighbor = [&mesh, &face_type_map](FI fi) {
  1298. HI hi = mesh.halfedge(fi);
  1299. HI hi_end = hi;
  1300. auto exist_next = [&hi, &hi_end, &mesh]() -> bool {
  1301. hi = mesh.next(hi);
  1302. return hi != hi_end;
  1303. };
  1304. // loop over 3 half edges of face
  1305. do {
  1306. HI hi_opposite = mesh.opposite(hi);
  1307. // open edge doesn't have opposit half edge
  1308. if (!hi_opposite.is_valid()) continue;
  1309. FI fi_opposite = mesh.face(hi_opposite);
  1310. if (!fi_opposite.is_valid()) continue;
  1311. if (face_type_map[fi_opposite] == FaceType::inside) return true;
  1312. } while (exist_next());
  1313. return false;
  1314. };
  1315. for (FI fi : mesh.faces()) {
  1316. FaceType type = face_type_map[fi];
  1317. if (type != FaceType::not_constrained) continue;
  1318. if (!has_inside_neighbor(fi)) continue;
  1319. assert(process.empty());
  1320. process.push_back(fi);
  1321. //store(mesh, face_type_map, DEBUG_OUTPUT_DIR + "progress.off");
  1322. while (!process.empty()) {
  1323. FI process_fi = process.back();
  1324. process.pop_back();
  1325. // Do not fill twice
  1326. FaceType& process_type = face_type_map[process_fi];
  1327. if (process_type == FaceType::inside) continue;
  1328. process_type = FaceType::inside;
  1329. // check neighbor triangle
  1330. HI hi = mesh.halfedge(process_fi);
  1331. HI hi_end = hi;
  1332. auto exist_next = [&hi, &hi_end, &mesh]() -> bool {
  1333. hi = mesh.next(hi);
  1334. return hi != hi_end;
  1335. };
  1336. do {
  1337. HI hi_opposite = mesh.opposite(hi);
  1338. // open edge doesn't have opposit half edge
  1339. if (!hi_opposite.is_valid()) continue;
  1340. FI fi_opposite = mesh.face(hi_opposite);
  1341. if (!fi_opposite.is_valid()) continue;
  1342. FaceType type_opposite = face_type_map[fi_opposite];
  1343. if (type_opposite == FaceType::not_constrained)
  1344. process.push_back(fi_opposite);
  1345. } while (exist_next());
  1346. }
  1347. }
  1348. }
  1349. void priv::collect_surface_data(std::queue<FI> &process,
  1350. std::vector<FI> &faces,
  1351. std::vector<HI> &outlines,
  1352. FaceTypeMap &face_type_map,
  1353. const CutMesh &mesh)
  1354. {
  1355. assert(!process.empty());
  1356. assert(faces.empty());
  1357. assert(outlines.empty());
  1358. while (!process.empty()) {
  1359. FI fi = process.front();
  1360. process.pop();
  1361. FaceType &fi_type = face_type_map[fi];
  1362. // Do not process twice
  1363. if (fi_type == FaceType::inside_processed) continue;
  1364. assert(fi_type == FaceType::inside);
  1365. // flag face as processed
  1366. fi_type = FaceType::inside_processed;
  1367. faces.push_back(fi);
  1368. // check neighbor triangle
  1369. HI hi = mesh.halfedge(fi);
  1370. HI hi_end = hi;
  1371. do {
  1372. HI hi_opposite = mesh.opposite(hi);
  1373. // open edge doesn't have opposit half edge
  1374. if (!hi_opposite.is_valid()) {
  1375. outlines.push_back(hi);
  1376. hi = mesh.next(hi);
  1377. continue;
  1378. }
  1379. FI fi_opposite = mesh.face(hi_opposite);
  1380. if (!fi_opposite.is_valid()) {
  1381. outlines.push_back(hi);
  1382. hi = mesh.next(hi);
  1383. continue;
  1384. }
  1385. FaceType side = face_type_map[fi_opposite];
  1386. if (side == FaceType::inside) {
  1387. process.emplace(fi_opposite);
  1388. } else if (side == FaceType::outside) {
  1389. // store outlines
  1390. outlines.push_back(hi);
  1391. }
  1392. hi = mesh.next(hi);
  1393. } while (hi != hi_end);
  1394. }
  1395. }
  1396. void priv::create_reduce_map(ReductionMap &reduction_map, const CutMesh &mesh)
  1397. {
  1398. const VertexShapeMap &vert_shape_map = mesh.property_map<VI, const IntersectingElement*>(vert_shape_map_name).first;
  1399. const EdgeBoolMap &ecm = mesh.property_map<EI, bool>(is_constrained_edge_name).first;
  1400. // check if vertex was made by edge_2 which is diagonal of quad
  1401. auto is_reducible_vertex = [&vert_shape_map](VI reduction_from) -> bool {
  1402. const IntersectingElement *ie = vert_shape_map[reduction_from];
  1403. if (ie == nullptr) return false;
  1404. IntersectingElement::Type type = ie->get_type();
  1405. return type == IntersectingElement::Type::edge_2;
  1406. };
  1407. /// <summary>
  1408. /// Append reduction or change existing one.
  1409. /// </summary>
  1410. /// <param name="hi">HalEdge between outside and inside face.
  1411. /// Target vertex will be reduced, source vertex left</param>
  1412. /// [[maybe_unused]] &face_type_map, &is_reducible_vertex are need only in debug
  1413. auto add_reduction = [&] //&reduction_map, &mesh, &face_type_map, &is_reducible_vertex
  1414. (HI hi) {
  1415. VI erase = mesh.target(hi);
  1416. VI left = mesh.source(hi);
  1417. assert(is_reducible_vertex(erase));
  1418. assert(!is_reducible_vertex(left));
  1419. VI &vi = reduction_map[erase];
  1420. // check if it is first add
  1421. if (vi.is_valid())
  1422. return;
  1423. // check that all triangles after reduction has 'erase' and 'left' vertex
  1424. // on same side of opposite line of vertex in triangle
  1425. Vec3d v_erase = to_vec3d(mesh.point(erase));
  1426. Vec3d v_left = to_vec3d(mesh.point(left));
  1427. for (FI fi : mesh.faces_around_target(hi)) {
  1428. if (!fi.is_valid())
  1429. continue;
  1430. // get vertices of rest
  1431. VI vi_a, vi_b;
  1432. for (VI vi : mesh.vertices_around_face(mesh.halfedge(fi))) {
  1433. if (!vi.is_valid())
  1434. continue;
  1435. if (vi == erase)
  1436. continue;
  1437. if (!vi_a.is_valid())
  1438. vi_a = vi;
  1439. else {
  1440. assert(!vi_b.is_valid());
  1441. vi_b = vi;
  1442. }
  1443. }
  1444. assert(vi_b.is_valid());
  1445. // do not check triangle, which will be removed
  1446. if (vi_a == left || vi_b == left)
  1447. continue;
  1448. Vec3d v_a = to_vec3d(mesh.point(vi_a));
  1449. Vec3d v_b = to_vec3d(mesh.point(vi_b));
  1450. // Vectors of triangle edges
  1451. Vec3d v_ab = v_b - v_a;
  1452. Vec3d v_ae = v_erase - v_a;
  1453. Vec3d v_al = v_left - v_a;
  1454. Vec3d n1 = v_ab.cross(v_ae);
  1455. Vec3d n2 = v_ab.cross(v_al);
  1456. // check that normal has same direction
  1457. if (((n1.x() > 0) != (n2.x() > 0)) ||
  1458. ((n1.y() > 0) != (n2.y() > 0)) ||
  1459. ((n1.z() > 0) != (n2.z() > 0)))
  1460. return; // this reduction will create CCW triangle
  1461. }
  1462. reduction_map[erase] = left;
  1463. // I have no better rule than take the first
  1464. // for decide which reduction will be better
  1465. // But it could be use only one of them
  1466. };
  1467. for (EI ei : mesh.edges()) {
  1468. if (!ecm[ei]) continue;
  1469. HI hi = mesh.halfedge(ei);
  1470. VI vi = mesh.target(hi);
  1471. if (is_reducible_vertex(vi)) add_reduction(hi);
  1472. HI hi_op = mesh.opposite(hi);
  1473. VI vi_op = mesh.target(hi_op);
  1474. if (is_reducible_vertex(vi_op)) add_reduction(hi_op);
  1475. }
  1476. #ifdef DEBUG_OUTPUT_DIR
  1477. store(mesh, reduction_map, DEBUG_OUTPUT_DIR + "reduces/");
  1478. #endif // DEBUG_OUTPUT_DIR
  1479. }
  1480. priv::CutAOIs priv::create_cut_area_of_interests(const CutMesh &mesh,
  1481. const ExPolygons &shapes,
  1482. FaceTypeMap &face_type_map)
  1483. {
  1484. // IMPROVE: Create better heuristic for count.
  1485. size_t faces_per_cut = mesh.faces().size() / shapes.size();
  1486. size_t outlines_per_cut = faces_per_cut / 2;
  1487. size_t cuts_per_model = shapes.size() * 2;
  1488. CutAOIs result;
  1489. result.reserve(cuts_per_model);
  1490. // It is faster to use one queue for all cuts
  1491. std::queue<FI> process;
  1492. for (FI fi : mesh.faces()) {
  1493. if (face_type_map[fi] != FaceType::inside) continue;
  1494. CutAOI cut;
  1495. std::vector<FI> &faces = cut.first;
  1496. std::vector<HI> &outlines = cut.second;
  1497. // faces for one surface cut
  1498. faces.reserve(faces_per_cut);
  1499. // outline for one surface cut
  1500. outlines.reserve(outlines_per_cut);
  1501. assert(process.empty());
  1502. // Process queue of faces to separate to surface_cut
  1503. process.push(fi);
  1504. collect_surface_data(process, faces, outlines, face_type_map, mesh);
  1505. assert(!faces.empty());
  1506. assert(!outlines.empty());
  1507. result.emplace_back(std::move(cut));
  1508. }
  1509. return result;
  1510. }
  1511. namespace priv {
  1512. /// <summary>
  1513. /// Calculate projection distance of point [in mm]
  1514. /// </summary>
  1515. /// <param name="p">Point to calc distance</param>
  1516. /// <param name="pi">Index of point on contour</param>
  1517. /// <param name="shapes_mesh">Model of cutting shape</param>
  1518. /// <param name="projection_ratio">Ratio for best projection distance</param>
  1519. /// <returns>Distance of point from best projection</returns>
  1520. float calc_distance(const P3 &p,
  1521. uint32_t pi,
  1522. const CutMesh &shapes_mesh,
  1523. float projection_ratio);
  1524. }
  1525. float priv::calc_distance(const P3 &p,
  1526. uint32_t pi,
  1527. const CutMesh &shapes_mesh,
  1528. float projection_ratio)
  1529. {
  1530. // It is known because shapes_mesh is created inside of private space
  1531. VI vi_start(2 * pi);
  1532. VI vi_end(2 * pi + 1);
  1533. // Get range for intersection
  1534. const P3 &start = shapes_mesh.point(vi_start);
  1535. const P3 &end = shapes_mesh.point(vi_end);
  1536. // find index in vector with biggest difference
  1537. size_t max_i = 0;
  1538. float max_val = 0.f;
  1539. for (size_t i = 0; i < 3; i++) {
  1540. float val = start[i] - end[i];
  1541. // abs value
  1542. if (val < 0.f) val *= -1.f;
  1543. if (max_val < val) {
  1544. max_val = val;
  1545. max_i = i;
  1546. }
  1547. }
  1548. float from_start = p[max_i] - start[max_i];
  1549. float best_distance = projection_ratio * (end[max_i] - start[max_i]);
  1550. return from_start - best_distance;
  1551. }
  1552. priv::VDistances priv::calc_distances(const SurfacePatches &patches,
  1553. const CutMeshes &models,
  1554. const CutMesh &shapes_mesh,
  1555. size_t count_shapes_points,
  1556. float projection_ratio)
  1557. {
  1558. priv::VDistances result(count_shapes_points);
  1559. for (const SurfacePatch &patch : patches) {
  1560. // map is created during intersection by corefine visitor
  1561. const VertexShapeMap &vert_shape_map =
  1562. models[patch.model_id].property_map<VI, const IntersectingElement *>(vert_shape_map_name).first;
  1563. uint32_t patch_index = &patch - &patches.front();
  1564. // map is created during patch creation / dividing
  1565. const CvtVI2VI& cvt = patch.mesh.property_map<VI, VI>(patch_source_name).first;
  1566. // for each point on outline
  1567. for (const Loop &loop : patch.loops)
  1568. for (const VI &vi_patch : loop) {
  1569. VI vi_model = cvt[vi_patch];
  1570. if (!vi_model.is_valid()) continue;
  1571. const IntersectingElement *ie = vert_shape_map[vi_model];
  1572. if (ie == nullptr) continue;
  1573. assert(ie->shape_point_index != std::numeric_limits<uint32_t>::max());
  1574. assert(ie->attr != (unsigned char) IntersectingElement::Type::undefined);
  1575. uint32_t pi = ie->shape_point_index;
  1576. assert(pi <= count_shapes_points);
  1577. std::vector<ProjectionDistance> &pds = result[pi];
  1578. uint32_t model_index = patch.model_id;
  1579. uint32_t aoi_index = patch.aoi_id;
  1580. //uint32_t hi_index = &hi - &patch.outline.front();
  1581. const P3 &p = patch.mesh.point(vi_patch);
  1582. float distance = calc_distance(p, pi, shapes_mesh, projection_ratio);
  1583. pds.push_back({model_index, aoi_index, patch_index, distance});
  1584. }
  1585. }
  1586. return result;
  1587. }
  1588. #include "libslic3r/AABBTreeLines.hpp"
  1589. #include "libslic3r/Line.hpp"
  1590. // functions for choose_best_distance
  1591. namespace priv {
  1592. // euler square size of vector stored in Point
  1593. float calc_size_sq(const Point &p);
  1594. // structure to store index and distance together
  1595. struct ClosePoint
  1596. {
  1597. // index of closest point from another shape
  1598. uint32_t index = std::numeric_limits<uint32_t>::max();
  1599. // squere distance to index
  1600. float dist_sq = std::numeric_limits<float>::max();
  1601. };
  1602. struct SearchData{
  1603. // IMPROVE: float lines are enough
  1604. std::vector<Linef> lines;
  1605. // convert line index into Shape point index
  1606. std::vector<size_t> cvt;
  1607. // contain lines from prev point to Point index
  1608. AABBTreeIndirect::Tree<2, double> tree;
  1609. };
  1610. SearchData create_search_data(const ExPolygons &shapes, const std::vector<bool>& mask);
  1611. uint32_t get_closest_point_index(const SearchData &sd, size_t line_idx, const Vec2d &hit_point, const ExPolygons &shapes, const ExPolygonsIndices &s2i);
  1612. // use AABB Tree Lines to find closest point
  1613. uint32_t find_closest_point_index(const Point &p, const ExPolygons &shapes, const ExPolygonsIndices &s2i, const std::vector<bool> &mask);
  1614. std::pair<uint32_t, uint32_t> find_closest_point_pair(const ExPolygons &shapes, const std::vector<bool> &done_shapes, const ExPolygonsIndices &s2i, const std::vector<bool> &mask);
  1615. // Search for closest projection to wanted distance
  1616. const ProjectionDistance *get_closest_projection(const ProjectionDistances &distance, float wanted_distance);
  1617. // fill result around known index inside one polygon
  1618. void fill_polygon_distances(const ProjectionDistance &pd, uint32_t index, const ExPolygonsIndex &id, ProjectionDistances & result, const ExPolygon &shape, const VDistances &distances);
  1619. // search for closest projection for expolygon
  1620. // choose correct cut by source point
  1621. void fill_shape_distances(uint32_t start_index, const ProjectionDistance *start_pd, ProjectionDistances &result, const ExPolygonsIndices &s2i, const ExPolygon &shape, const VDistances &distances);
  1622. // find close points between finished and unfinished ExPolygons
  1623. ClosePoint find_close_point(const Point &p, ProjectionDistances &result, std::vector<bool>& finished_shapes,const ExPolygonsIndices &s2i, const ExPolygons &shapes);
  1624. }
  1625. float priv::calc_size_sq(const Point &p){
  1626. // NOTE: p.squaredNorm() can't be use due to overflow max int value
  1627. return (float) p.x() * p.x() + (float) p.y() * p.y();
  1628. }
  1629. priv::SearchData priv::create_search_data(const ExPolygons &shapes,
  1630. const std::vector<bool> &mask)
  1631. {
  1632. // IMPROVE: Use float precission (it is enough)
  1633. SearchData sd;
  1634. sd.lines.reserve(mask.size());
  1635. sd.cvt.reserve(mask.size());
  1636. size_t index = 0;
  1637. auto add_lines = [&sd, &index, &mask]
  1638. (const Polygon &poly) {
  1639. Vec2d prev = poly.back().cast<double>();
  1640. bool use_point = mask[index + poly.points.size() - 1];
  1641. for (const Point &p : poly.points) {
  1642. if (!use_point) {
  1643. use_point = mask[index];
  1644. if (use_point) prev = p.cast<double>();
  1645. } else if (!mask[index]) {
  1646. use_point = false;
  1647. } else {
  1648. Vec2d p_d = p.cast<double>();
  1649. sd.lines.emplace_back(prev, p_d);
  1650. sd.cvt.push_back(index);
  1651. prev = p_d;
  1652. }
  1653. ++index;
  1654. }
  1655. };
  1656. for (const ExPolygon &shape : shapes) {
  1657. add_lines(shape.contour);
  1658. for (const Polygon &hole : shape.holes) add_lines(hole);
  1659. }
  1660. sd.tree = AABBTreeLines::build_aabb_tree_over_indexed_lines(sd.lines);
  1661. return sd;
  1662. }
  1663. uint32_t priv::get_closest_point_index(const SearchData &sd,
  1664. size_t line_idx,
  1665. const Vec2d &hit_point,
  1666. const ExPolygons &shapes,
  1667. const ExPolygonsIndices &s2i)
  1668. {
  1669. const Linef &line = sd.lines[line_idx];
  1670. Vec2d dir = line.a - line.b;
  1671. Vec2d dir_abs = dir.cwiseAbs();
  1672. // use x coordinate
  1673. int i = (dir_abs.x() > dir_abs.y())? 0 :1;
  1674. bool use_index = abs(line.a[i] - hit_point[i]) >
  1675. abs(line.b[i] - hit_point[i]);
  1676. size_t point_index = sd.cvt[line_idx];
  1677. // Lambda used only for check result
  1678. [[maybe_unused]] auto is_same = [&s2i, &shapes]
  1679. (const Vec2d &p, size_t i) -> bool {
  1680. auto id = s2i.cvt(i);
  1681. const ExPolygon &shape = shapes[id.expolygons_index];
  1682. const Polygon &poly = (id.polygon_index == 0) ?
  1683. shape.contour :
  1684. shape.holes[id.polygon_index - 1];
  1685. Point p_ = p.cast<coord_t>();
  1686. return p_ == poly[id.point_index];
  1687. };
  1688. if (use_index) {
  1689. assert(is_same(line.b, point_index));
  1690. return point_index;
  1691. }
  1692. auto id = s2i.cvt(point_index);
  1693. if (id.point_index != 0) {
  1694. assert(is_same(line.a, point_index - 1));
  1695. return point_index - 1;
  1696. }
  1697. const ExPolygon &shape = shapes[id.expolygons_index];
  1698. size_t count_polygon_points = (id.polygon_index == 0) ?
  1699. shape.contour.size() :
  1700. shape.holes[id.polygon_index - 1].size();
  1701. size_t prev_point_index = point_index + (count_polygon_points - 1);
  1702. assert(is_same(line.a, prev_point_index));
  1703. // return previous point index
  1704. return prev_point_index;
  1705. }
  1706. // use AABB Tree Lines
  1707. uint32_t priv::find_closest_point_index(const Point &p,
  1708. const ExPolygons &shapes,
  1709. const ExPolygonsIndices &s2i,
  1710. const std::vector<bool> &mask)
  1711. {
  1712. SearchData sd = create_search_data(shapes, mask);
  1713. if (sd.tree.nodes().size() == 0){
  1714. // no lines in expolygon, check whether exist point to start
  1715. double closest_square_distance = INFINITY;
  1716. uint32_t closest_id = -1;
  1717. for (uint32_t i = 0; i < mask.size(); i++)
  1718. if (mask[i]){
  1719. ExPolygonsIndex ei = s2i.cvt(i);
  1720. const Point& s_p = ei.is_contour()?
  1721. shapes[ei.expolygons_index].contour[ei.point_index]:
  1722. shapes[ei.expolygons_index].holes[ei.hole_index()][ei.point_index];
  1723. double square_distance = (p - s_p).cast<double>().squaredNorm();
  1724. if (closest_id >= mask.size() ||
  1725. closest_square_distance > square_distance) {
  1726. closest_id = i;
  1727. closest_square_distance = square_distance;
  1728. }
  1729. }
  1730. assert(closest_id < mask.size());
  1731. return closest_id;
  1732. }
  1733. size_t line_idx = std::numeric_limits<size_t>::max();
  1734. Vec2d hit_point;
  1735. Vec2d p_d = p.cast<double>();
  1736. [[maybe_unused]] double distance_sq =
  1737. AABBTreeLines::squared_distance_to_indexed_lines(
  1738. sd.lines, sd.tree, p_d, line_idx, hit_point);
  1739. assert(distance_sq > 0);
  1740. // IMPROVE: one could use line ratio to find closest point
  1741. return get_closest_point_index(sd, line_idx, hit_point, shapes, s2i);
  1742. }
  1743. std::pair<uint32_t, uint32_t> priv::find_closest_point_pair(
  1744. const ExPolygons &shapes,
  1745. const std::vector<bool> &done_shapes,
  1746. const ExPolygonsIndices &s2i,
  1747. const std::vector<bool> &mask)
  1748. {
  1749. assert(mask.size() == s2i.get_count());
  1750. assert(done_shapes.size() == shapes.size());
  1751. std::vector<bool> unfinished_mask = mask; // copy
  1752. size_t index = 0;
  1753. for (size_t shape_index = 0; shape_index < shapes.size(); shape_index++) {
  1754. size_t count = count_points(shapes[shape_index]);
  1755. if (done_shapes[shape_index]) {
  1756. for (size_t i = 0; i < count; ++i, ++index)
  1757. unfinished_mask[index] = false;
  1758. } else {
  1759. index += count;
  1760. }
  1761. }
  1762. assert(index == s2i.get_count());
  1763. SearchData sd = create_search_data(shapes, unfinished_mask);
  1764. struct ClosestPair
  1765. {
  1766. size_t finish_idx = std::numeric_limits<size_t>::max();
  1767. size_t unfinished_line_idx = std::numeric_limits<size_t>::max();
  1768. Vec2d hit_point = Vec2d();
  1769. double distance_sq = std::numeric_limits<double>::max();
  1770. } cp;
  1771. index = 0;
  1772. for (size_t shape_index = 0; shape_index < shapes.size(); shape_index++) {
  1773. const ExPolygon shape = shapes[shape_index];
  1774. if (!done_shapes[shape_index]) {
  1775. index += count_points(shape);
  1776. continue;
  1777. }
  1778. auto search_in_polygon = [&index, &cp, &sd, &mask](const Polygon& polygon) {
  1779. for (size_t i = 0; i < polygon.size(); ++i, ++index) {
  1780. if (mask[index] == false) continue;
  1781. Vec2d p_d = polygon[i].cast<double>();
  1782. size_t line_idx = std::numeric_limits<size_t>::max();
  1783. Vec2d hit_point;
  1784. double distance_sq = AABBTreeLines::squared_distance_to_indexed_lines(
  1785. sd.lines, sd.tree, p_d, line_idx, hit_point, cp.distance_sq);
  1786. if (distance_sq < 0 ||
  1787. distance_sq >= cp.distance_sq) continue;
  1788. assert(line_idx < sd.lines.size());
  1789. cp.distance_sq = distance_sq;
  1790. cp.unfinished_line_idx = line_idx;
  1791. cp.hit_point = hit_point;
  1792. cp.finish_idx = index;
  1793. }
  1794. };
  1795. search_in_polygon(shape.contour);
  1796. for (const Polygon& hole: shape.holes)
  1797. search_in_polygon(hole);
  1798. }
  1799. assert(index == s2i.get_count());
  1800. // check that exists result
  1801. if (cp.finish_idx == std::numeric_limits<size_t>::max()) {
  1802. return std::make_pair(std::numeric_limits<size_t>::max(),
  1803. std::numeric_limits<size_t>::max());
  1804. }
  1805. size_t unfinished_idx = get_closest_point_index(sd, cp.unfinished_line_idx, cp.hit_point, shapes, s2i);
  1806. return std::make_pair(cp.finish_idx, unfinished_idx);
  1807. }
  1808. const priv::ProjectionDistance *priv::get_closest_projection(
  1809. const ProjectionDistances &distance, float wanted_distance)
  1810. {
  1811. // minimal distance
  1812. float min_d = std::numeric_limits<float>::max();
  1813. const ProjectionDistance *min_pd = nullptr;
  1814. for (const ProjectionDistance &pd : distance) {
  1815. float d = std::fabs(pd.distance - wanted_distance);
  1816. // There should be limit for maximal distance
  1817. if (min_d > d) {
  1818. min_d = d;
  1819. min_pd = &pd;
  1820. }
  1821. }
  1822. return min_pd;
  1823. }
  1824. void priv::fill_polygon_distances(const ProjectionDistance &pd,
  1825. uint32_t index,
  1826. const ExPolygonsIndex &id,
  1827. ProjectionDistances &result,
  1828. const ExPolygon &shape,
  1829. const VDistances &distances)
  1830. {
  1831. const Points& points = (id.polygon_index == 0) ?
  1832. shape.contour.points :
  1833. shape.holes[id.polygon_index - 1].points;
  1834. // border of indexes for Polygon
  1835. uint32_t first_index = index - id.point_index;
  1836. uint32_t last_index = first_index + points.size();
  1837. uint32_t act_index = index;
  1838. const ProjectionDistance* act_pd = &pd;
  1839. // Copy starting pd to result
  1840. result[act_index] = pd;
  1841. auto exist_next = [&distances, &act_index, &act_pd, &result]
  1842. (uint32_t nxt_index) {
  1843. const ProjectionDistance *nxt_pd = get_closest_projection(distances[nxt_index] ,act_pd->distance);
  1844. // exist next projection distance ?
  1845. if (nxt_pd == nullptr) return false;
  1846. // check no rewrite result
  1847. assert(result[nxt_index].aoi_index == std::numeric_limits<uint32_t>::max());
  1848. // copy founded projection to result
  1849. result[nxt_index] = *nxt_pd; // copy
  1850. // next
  1851. act_index = nxt_index;
  1852. act_pd = &result[nxt_index];
  1853. return true;
  1854. };
  1855. // last index in circle
  1856. uint32_t finish_index = (index == first_index) ? (last_index - 1) :
  1857. (index - 1);
  1858. // Positive iteration inside polygon
  1859. do {
  1860. uint32_t nxt_index = act_index + 1;
  1861. // close loop of indexes inside of contour
  1862. if (nxt_index == last_index) nxt_index = first_index;
  1863. // check that exist next
  1864. if (!exist_next(nxt_index)) break;
  1865. } while (act_index != finish_index);
  1866. // when all results for polygon are set no neccessary to iterate negative
  1867. if (act_index == finish_index) return;
  1868. act_index = index;
  1869. act_pd = &pd;
  1870. // Negative iteration inside polygon
  1871. do {
  1872. uint32_t nxt_index = (act_index == first_index) ?
  1873. (last_index-1) : (act_index - 1);
  1874. // When iterate negative it must be split to parts
  1875. // and can't iterate in circle
  1876. assert(nxt_index != index);
  1877. // check that exist next
  1878. if (!exist_next(nxt_index)) break;
  1879. } while (true);
  1880. }
  1881. // IMPROVE: when select distance fill in all distances from Patch
  1882. void priv::fill_shape_distances(uint32_t start_index,
  1883. const ProjectionDistance *start_pd,
  1884. ProjectionDistances &result,
  1885. const ExPolygonsIndices &s2i,
  1886. const ExPolygon &shape,
  1887. const VDistances &distances)
  1888. {
  1889. uint32_t expolygons_index = s2i.cvt(start_index).expolygons_index;
  1890. uint32_t first_shape_index = s2i.cvt({expolygons_index, 0, 0});
  1891. do {
  1892. fill_polygon_distances(*start_pd, start_index, s2i.cvt(start_index),result, shape, distances);
  1893. // seaching only inside shape, return index of closed finished point
  1894. auto find_close_finished_point = [&first_shape_index, &shape, &result]
  1895. (const Point &p) -> ClosePoint {
  1896. uint32_t index = first_shape_index;
  1897. ClosePoint cp;
  1898. auto check_finished_points = [&cp, &result, &index, &p]
  1899. (const Points& pts) {
  1900. for (const Point &p_ : pts) {
  1901. // finished point with some distances
  1902. if (result[index].aoi_index == std::numeric_limits<uint32_t>::max()) {
  1903. ++index;
  1904. continue;
  1905. }
  1906. float distance = calc_size_sq(p_ - p);
  1907. if (cp.dist_sq > distance) {
  1908. cp.dist_sq = distance;
  1909. cp.index = index;
  1910. }
  1911. ++index;
  1912. }
  1913. };
  1914. check_finished_points(shape.contour.points);
  1915. for (const Polygon &h : shape.holes)
  1916. check_finished_points(h.points);
  1917. return cp;
  1918. };
  1919. // find next closest pair of points
  1920. // (finished + unfinished) in ExPolygon
  1921. start_index = std::numeric_limits<uint32_t>::max(); // unfinished_index
  1922. uint32_t finished_index = std::numeric_limits<uint32_t>::max();
  1923. float dist_sq = std::numeric_limits<float>::max();
  1924. // first index in shape
  1925. uint32_t index = first_shape_index;
  1926. auto check_unfinished_points = [&index, &result, &distances, &find_close_finished_point, &dist_sq, &start_index, &finished_index]
  1927. (const Points& pts) {
  1928. for (const Point &p : pts) {
  1929. // try find unfinished
  1930. if (result[index].aoi_index !=
  1931. std::numeric_limits<uint32_t>::max() ||
  1932. distances[index].empty()) {
  1933. ++index;
  1934. continue;
  1935. }
  1936. ClosePoint cp = find_close_finished_point(p);
  1937. if (dist_sq > cp.dist_sq) {
  1938. dist_sq = cp.dist_sq;
  1939. start_index = index;
  1940. finished_index = cp.index;
  1941. }
  1942. ++index;
  1943. }
  1944. };
  1945. // for each unfinished points
  1946. check_unfinished_points(shape.contour.points);
  1947. for (const Polygon &h : shape.holes)
  1948. check_unfinished_points(h.points);
  1949. } while (start_index != std::numeric_limits<uint32_t>::max());
  1950. }
  1951. priv::ClosePoint priv::find_close_point(const Point &p,
  1952. ProjectionDistances &result,
  1953. std::vector<bool> &finished_shapes,
  1954. const ExPolygonsIndices &s2i,
  1955. const ExPolygons &shapes)
  1956. {
  1957. // result
  1958. ClosePoint cp;
  1959. // for all finished points
  1960. for (uint32_t shape_index = 0; shape_index < shapes.size(); ++shape_index) {
  1961. if (!finished_shapes[shape_index]) continue;
  1962. const ExPolygon &shape = shapes[shape_index];
  1963. uint32_t index = s2i.cvt({shape_index, 0, 0});
  1964. auto find_close_point_in_points = [&p, &cp, &index, &result]
  1965. (const Points &pts){
  1966. for (const Point &p_ : pts) {
  1967. // Exist result (is finished) ?
  1968. if (result[index].aoi_index ==
  1969. std::numeric_limits<uint32_t>::max()) {
  1970. ++index;
  1971. continue;
  1972. }
  1973. float distance_sq = calc_size_sq(p - p_);
  1974. if (cp.dist_sq > distance_sq) {
  1975. cp.dist_sq = distance_sq;
  1976. cp.index = index;
  1977. }
  1978. ++index;
  1979. }
  1980. };
  1981. find_close_point_in_points(shape.contour.points);
  1982. // shape could be inside of another shape's hole
  1983. for (const Polygon& h:shape.holes)
  1984. find_close_point_in_points(h.points);
  1985. }
  1986. return cp;
  1987. }
  1988. // IMPROVE: when select distance fill in all distances from Patch
  1989. priv::ProjectionDistances priv::choose_best_distance(
  1990. const VDistances &distances, const ExPolygons &shapes, const Point &start, const ExPolygonsIndices &s2i, const SurfacePatches &patches)
  1991. {
  1992. assert(distances.size() == count_points(shapes));
  1993. // vector of patches for shape
  1994. std::vector<std::vector<uint32_t>> shapes_patches(shapes.size());
  1995. for (const SurfacePatch &patch : patches)
  1996. shapes_patches[patch.shape_id].push_back(&patch-&patches.front());
  1997. // collect one closest projection for each outline point
  1998. ProjectionDistances result(distances.size());
  1999. // store info about finished shapes
  2000. std::vector<bool> finished_shapes(shapes.size(), {false});
  2001. // wanted distance from ideal projection
  2002. // Distances are relative to projection distance
  2003. // so first wanted distance is the closest one (ZERO)
  2004. float wanted_distance = 0.f;
  2005. std::vector<bool> mask_distances(s2i.get_count(), {true});
  2006. for (const auto &d : distances)
  2007. if (d.empty()) mask_distances[&d - &distances.front()] = false;
  2008. // Select point from shapes(text contour) which is closest to center (all in 2d)
  2009. uint32_t unfinished_index = find_closest_point_index(start, shapes, s2i, mask_distances);
  2010. assert(unfinished_index < s2i.get_count());
  2011. if (unfinished_index >= s2i.get_count())
  2012. // no point to select
  2013. return result;
  2014. #ifdef DEBUG_OUTPUT_DIR
  2015. Connections connections;
  2016. connections.reserve(shapes.size());
  2017. connections.emplace_back(unfinished_index, unfinished_index);
  2018. #endif // DEBUG_OUTPUT_DIR
  2019. do {
  2020. const ProjectionDistance* pd = get_closest_projection(distances[unfinished_index], wanted_distance);
  2021. // selection of closest_id should proove that pd has value
  2022. // (functions: get_closest_point_index and find_close_point_in_points)
  2023. assert(pd != nullptr);
  2024. uint32_t expolygons_index = s2i.cvt(unfinished_index).expolygons_index;
  2025. const ExPolygon &shape = shapes[expolygons_index];
  2026. std::vector<uint32_t> &shape_patches = shapes_patches[expolygons_index];
  2027. if (shape_patches.size() == 1){
  2028. // Speed up, only one patch so copy distance from patch
  2029. uint32_t first_shape_index = s2i.cvt({expolygons_index, 0, 0});
  2030. uint32_t laset_shape_index = first_shape_index + count_points(shape);
  2031. for (uint32_t i = first_shape_index; i < laset_shape_index; ++i) {
  2032. const ProjectionDistances &pds = distances[i];
  2033. if (pds.empty()) continue;
  2034. // check that index belongs to patch
  2035. assert(pds.front().patch_index == shape_patches.front());
  2036. result[i] = pds.front();
  2037. if (pds.size() == 1) continue;
  2038. float relative_distance = fabs(result[i].distance - pd->distance);
  2039. // patch could contain multiple value for one outline point
  2040. // so choose closest to start point
  2041. for (uint32_t pds_index = 1; pds_index < pds.size(); ++pds_index) {
  2042. // check that index still belongs to same patch
  2043. assert(pds[pds_index].patch_index == shape_patches.front());
  2044. float relative_distance2 = fabs(pds[pds_index].distance - pd->distance);
  2045. if (relative_distance > relative_distance2) {
  2046. relative_distance = relative_distance2;
  2047. result[i] = pds[pds_index];
  2048. }
  2049. }
  2050. }
  2051. } else {
  2052. // multiple patches for expolygon
  2053. // check that exist patch to fill shape
  2054. assert(!shape_patches.empty());
  2055. fill_shape_distances(unfinished_index, pd, result, s2i, shape, distances);
  2056. }
  2057. finished_shapes[expolygons_index] = true;
  2058. // The most close points between finished and unfinished shapes
  2059. auto [finished, unfinished] = find_closest_point_pair(
  2060. shapes, finished_shapes, s2i, mask_distances);
  2061. // detection of end (best doesn't have value)
  2062. if (finished == std::numeric_limits<uint32_t>::max()) break;
  2063. assert(unfinished != std::numeric_limits<uint32_t>::max());
  2064. const ProjectionDistance &closest_pd = result[finished];
  2065. // check that best_cp is finished and has result
  2066. assert(closest_pd.aoi_index != std::numeric_limits<uint32_t>::max());
  2067. wanted_distance = closest_pd.distance;
  2068. unfinished_index = unfinished;
  2069. #ifdef DEBUG_OUTPUT_DIR
  2070. connections.emplace_back(finished, unfinished);
  2071. #endif // DEBUG_OUTPUT_DIR
  2072. } while (true); //(unfinished_index != std::numeric_limits<uint32_t>::max());
  2073. #ifdef DEBUG_OUTPUT_DIR
  2074. store(shapes, mask_distances, connections, DEBUG_OUTPUT_DIR + "closest_points.svg");
  2075. #endif // DEBUG_OUTPUT_DIR
  2076. return result;
  2077. }
  2078. // functions to help 'diff_model'
  2079. namespace priv {
  2080. const VI default_vi(std::numeric_limits<uint32_t>::max());
  2081. // Keep info about intersection source
  2082. struct Source{ HI hi; int sdim=0;};
  2083. using Sources = std::vector<Source>;
  2084. const std::string vertex_source_map_name = "v:SourceIntersecting";
  2085. using VertexSourceMap = CutMesh::Property_map<VI, Source>;
  2086. /// <summary>
  2087. /// Corefine visitor
  2088. /// Store intersection source for vertices of constrained edge of tm1
  2089. /// Must be used with corefine flag no modification of tm2
  2090. /// </summary>
  2091. struct IntersectionSources
  2092. {
  2093. const CutMesh *patch; // patch
  2094. const CutMesh *model; // const model
  2095. VertexSourceMap vmap;
  2096. // keep sources from call intersection_point_detected
  2097. // until call new_vertex_added
  2098. Sources* sources;
  2099. // count intersections
  2100. void intersection_point_detected(std::size_t i_id,
  2101. int sdim,
  2102. HI h_f,
  2103. HI h_e,
  2104. const CutMesh &tm_f,
  2105. const CutMesh &tm_e,
  2106. bool is_target_coplanar,
  2107. bool is_source_coplanar)
  2108. {
  2109. Source source;
  2110. if (&tm_e == model) {
  2111. source = {h_e, sdim};
  2112. // check other CGAL model that is patch
  2113. assert(&tm_f == patch);
  2114. if (is_target_coplanar) {
  2115. assert(sdim == 0);
  2116. vmap[tm_f.source(h_f)] = source;
  2117. }
  2118. if (is_source_coplanar) {
  2119. assert(sdim == 0);
  2120. vmap[tm_f.target(h_f)] = source;
  2121. }
  2122. // clear source to be able check that this intersection source is
  2123. // not used any more
  2124. if (is_source_coplanar || is_target_coplanar) source = {};
  2125. } else {
  2126. source = {h_f, sdim};
  2127. assert(&tm_f == model && &tm_e == patch);
  2128. assert(!is_target_coplanar);
  2129. assert(!is_source_coplanar);
  2130. // if (is_target_coplanar) vmap[tm_e.source(h_e)] = source;
  2131. // if (is_source_coplanar) vmap[tm_e.target(h_e)] = source;
  2132. // if (sdim == 0)
  2133. // vmap[tm_e.target(h_e)] = source;
  2134. }
  2135. // By documentation i_id is consecutive.
  2136. // check id goes in a row, without skips
  2137. assert(sources->size() == i_id);
  2138. // add source of intersection
  2139. sources->push_back(source);
  2140. }
  2141. /// <summary>
  2142. /// Store VI to intersections by i_id
  2143. /// </summary>
  2144. /// <param name="i_id">Order number of intersection point</param>
  2145. /// <param name="v">New added vertex</param>
  2146. /// <param name="tm">Affected mesh</param>
  2147. void new_vertex_added(std::size_t i_id, VI v, const CutMesh &tm)
  2148. {
  2149. // check that it is first insertation into item of vmap
  2150. assert(!vmap[v].hi.is_valid());
  2151. // check valid addresing into sources
  2152. assert(i_id < sources->size());
  2153. // check that source has value
  2154. assert(sources->at(i_id).hi.is_valid());
  2155. vmap[v] = sources->at(i_id);
  2156. }
  2157. // Not used visitor functions
  2158. void before_subface_creations(FI /* f_old */, CutMesh & /* mesh */) {}
  2159. void after_subface_created(FI /* f_new */, CutMesh & /* mesh */) {}
  2160. void after_subface_creations(CutMesh &) {}
  2161. void before_subface_created(CutMesh &) {}
  2162. void before_edge_split(HI /* h */, CutMesh & /* tm */) {}
  2163. void edge_split(HI /* hnew */, CutMesh & /* tm */) {}
  2164. void after_edge_split() {}
  2165. void add_retriangulation_edge(HI /* h */, CutMesh & /* tm */) {}
  2166. };
  2167. /// <summary>
  2168. /// Create map1 and map2
  2169. /// </summary>
  2170. /// <param name="map">Convert tm1.face to type</param>
  2171. /// <param name="tm1">Corefined mesh</param>
  2172. /// <param name="tm2">Source of intersection</param>
  2173. /// <param name="ecm1">Identify constrainde edge</param>
  2174. /// <param name="sources">Convert tm1.face to type</param>
  2175. void create_face_types(FaceTypeMap &map,
  2176. const CutMesh &tm1,
  2177. const CutMesh &tm2,
  2178. const EdgeBoolMap &ecm,
  2179. const VertexSourceMap &sources);
  2180. /// <summary>
  2181. /// Implement 'cut' Minus 'clipper', where clipper is reverse input Volume
  2182. /// NOTE: clipper will be modified (corefined by cut) !!!
  2183. /// </summary>
  2184. /// <param name="cut">differ from</param>
  2185. /// <param name="clipper">differ what</param>
  2186. /// <returns>True on succes, otherwise FALSE</returns>
  2187. bool clip_cut(SurfacePatch &cut, CutMesh clipper);
  2188. BoundingBoxf3 bounding_box(const CutAOI &cut, const CutMesh &mesh);
  2189. BoundingBoxf3 bounding_box(const CutMesh &mesh);
  2190. BoundingBoxf3 bounding_box(const SurfacePatch &ecut);
  2191. /// <summary>
  2192. /// Create patch
  2193. /// </summary>
  2194. /// <param name="fis">Define patch faces</param>
  2195. /// <param name="mesh">Source of fis
  2196. /// NOTE: Need temporary add property map for convert vertices</param>
  2197. /// <param name="rmap">Options to reduce vertices from fis.
  2198. /// NOTE: Used for skip vertices made by diagonal edge in rectangle of shape side</param>
  2199. /// <returns>Patch</returns>
  2200. SurfacePatch create_surface_patch(const std::vector<FI> &fis,
  2201. /*const*/ CutMesh &mesh,
  2202. const ReductionMap *rmap = nullptr);
  2203. } // namespace priv
  2204. void priv::create_face_types(FaceTypeMap &map,
  2205. const CutMesh &tm1,
  2206. const CutMesh &tm2,
  2207. const EdgeBoolMap &ecm,
  2208. const VertexSourceMap &sources)
  2209. {
  2210. auto get_intersection_source = [&tm2](const Source& s1, const Source& s2)->FI{
  2211. // when one of sources is face than return it
  2212. FI fi1 = tm2.face(s1.hi);
  2213. if (s1.sdim == 2) return fi1;
  2214. FI fi2 = tm2.face(s2.hi);
  2215. if (s2.sdim == 2) return fi2;
  2216. // both vertices are made by same source triangle
  2217. if (fi1 == fi2) return fi1;
  2218. // when one from sources is edge second one decide side of triangle triangle
  2219. HI hi1_opposit = tm2.opposite(s1.hi);
  2220. FI fi1_opposit;
  2221. if (hi1_opposit.is_valid())
  2222. fi1_opposit = tm2.face(hi1_opposit);
  2223. if (fi2 == fi1_opposit) return fi2;
  2224. HI hi2_opposit = tm2.opposite(s2.hi);
  2225. FI fi2_opposit;
  2226. if (hi2_opposit.is_valid())
  2227. fi2_opposit = tm2.face(hi2_opposit);
  2228. if (fi1 == fi2_opposit) return fi1;
  2229. if (fi1_opposit.is_valid() && fi1_opposit == fi2_opposit)
  2230. return fi1_opposit;
  2231. // when intersection is vertex need loop over neighbor
  2232. for (FI fi_around_hi1 : tm2.faces_around_target(s1.hi)) {
  2233. for (FI fi_around_hi2 : tm2.faces_around_target(s2.hi)) {
  2234. if (fi_around_hi1 == fi_around_hi2)
  2235. return fi_around_hi1;
  2236. }
  2237. }
  2238. // should never rich it
  2239. // Exist case when do not know source triangle for decide side of intersection
  2240. assert(false);
  2241. return FI();
  2242. };
  2243. for (FI fi : tm1.faces()) map[fi] = FaceType::not_constrained;
  2244. for (EI ei1 : tm1.edges()) {
  2245. if (!get(ecm, ei1)) continue;
  2246. // get faces from tm1 (f1a + f1b)
  2247. HI hi1 = tm1.halfedge(ei1);
  2248. assert(hi1.is_valid());
  2249. FI f1a = tm1.face(hi1);
  2250. assert(f1a.is_valid());
  2251. HI hi_op = tm1.opposite(hi1);
  2252. assert(hi_op.is_valid());
  2253. FI f1b = tm1.face(hi_op);
  2254. assert(f1b.is_valid());
  2255. // get faces from tm2 (f2a + f2b)
  2256. VI vi1_source = tm1.source(hi1);
  2257. assert(vi1_source.is_valid());
  2258. VI vi1_target = tm1.target(hi1);
  2259. assert(vi1_target.is_valid());
  2260. const Source &s_s = sources[vi1_source];
  2261. const Source &s_t = sources[vi1_target];
  2262. FI fi2 = get_intersection_source(s_s, s_t);
  2263. // in release solve situation that face was NOT deduced
  2264. if (!fi2.is_valid()) continue;
  2265. HI hi2 = tm2.halfedge(fi2);
  2266. std::array<const P3 *, 3> t;
  2267. size_t ti =0;
  2268. for (VI vi2 : tm2.vertices_around_face(hi2))
  2269. t[ti++] = &tm2.point(vi2);
  2270. // triangle tip from face f1a
  2271. VI vi1a_tip = tm1.target(tm1.next(hi1));
  2272. assert(vi1a_tip.is_valid());
  2273. const P3 &p = tm1.point(vi1a_tip);
  2274. // check if f1a is behinde f2a
  2275. // inside mean it will be used
  2276. // outside will be discarded
  2277. if (CGAL::orientation(*t[0], *t[1], *t[2], p) == CGAL::POSITIVE) {
  2278. map[f1a] = FaceType::inside;
  2279. map[f1b] = FaceType::outside;
  2280. } else {
  2281. map[f1a] = FaceType::outside;
  2282. map[f1b] = FaceType::inside;
  2283. }
  2284. }
  2285. }
  2286. #include <CGAL/Polygon_mesh_processing/clip.h>
  2287. #include <CGAL/Polygon_mesh_processing/corefinement.h>
  2288. bool priv::clip_cut(SurfacePatch &cut, CutMesh clipper)
  2289. {
  2290. CutMesh& tm = cut.mesh;
  2291. // create backup for case that there is no intersection
  2292. CutMesh backup_copy = tm;
  2293. class ExistIntersectionClipVisitor: public CGAL::Polygon_mesh_processing::Corefinement::Default_visitor<CutMesh>
  2294. {
  2295. bool* exist_intersection;
  2296. public:
  2297. ExistIntersectionClipVisitor(bool *exist_intersection): exist_intersection(exist_intersection){}
  2298. void intersection_point_detected(std::size_t, int , HI, HI, const CutMesh&, const CutMesh&, bool, bool)
  2299. { *exist_intersection = true;}
  2300. };
  2301. bool exist_intersection = false;
  2302. ExistIntersectionClipVisitor visitor{&exist_intersection};
  2303. // namep parameters for model tm and function clip
  2304. const auto &np_tm = CGAL::parameters::visitor(visitor)
  2305. .throw_on_self_intersection(false);
  2306. // name parameters for model clipper and function clip
  2307. const auto &np_c = CGAL::parameters::throw_on_self_intersection(false);
  2308. // Can't use 'do_not_modify', when Ture than clipper has to be closed !!
  2309. // .do_not_modify(true);
  2310. // .throw_on_self_intersection(false); is set automaticaly by param 'do_not_modify'
  2311. // .clip_volume(false); is set automaticaly by param 'do_not_modify'
  2312. bool suc = CGAL::Polygon_mesh_processing::clip(tm, clipper, np_tm, np_c);
  2313. // true if the output surface mesh is manifold.
  2314. // If false is returned tm and clipper are only corefined.
  2315. assert(suc);
  2316. // decide what TODO when can't clip source object !?!
  2317. if (!exist_intersection || !suc) {
  2318. // TODO: test if cut is fully in or fully out!!
  2319. cut.mesh = backup_copy;
  2320. return false;
  2321. }
  2322. return true;
  2323. }
  2324. BoundingBoxf3 priv::bounding_box(const CutAOI &cut, const CutMesh &mesh) {
  2325. const P3& p_from_cut = mesh.point(mesh.target(mesh.halfedge(cut.first.front())));
  2326. Vec3d min = to_vec3d(p_from_cut);
  2327. Vec3d max = min;
  2328. for (FI fi : cut.first) {
  2329. for(VI vi: mesh.vertices_around_face(mesh.halfedge(fi))){
  2330. const P3& p = mesh.point(vi);
  2331. for (size_t i = 0; i < 3; ++i) {
  2332. if (min[i] > p[i]) min[i] = p[i];
  2333. if (max[i] < p[i]) max[i] = p[i];
  2334. }
  2335. }
  2336. }
  2337. return BoundingBoxf3(min, max);
  2338. }
  2339. BoundingBoxf3 priv::bounding_box(const CutMesh &mesh)
  2340. {
  2341. Vec3d min = to_vec3d(*mesh.points().begin());
  2342. Vec3d max = min;
  2343. for (VI vi : mesh.vertices()) {
  2344. const P3 &p = mesh.point(vi);
  2345. for (size_t i = 0; i < 3; ++i) {
  2346. if (min[i] > p[i]) min[i] = p[i];
  2347. if (max[i] < p[i]) max[i] = p[i];
  2348. }
  2349. }
  2350. return BoundingBoxf3(min, max);
  2351. }
  2352. BoundingBoxf3 priv::bounding_box(const SurfacePatch &ecut) {
  2353. return bounding_box(ecut.mesh);
  2354. }
  2355. priv::SurfacePatch priv::create_surface_patch(const std::vector<FI> &fis,
  2356. /* const */ CutMesh &mesh,
  2357. const ReductionMap *rmap)
  2358. {
  2359. auto is_counted = mesh.add_property_map<VI, bool>("v:is_counted").first;
  2360. uint32_t count_vertices = 0;
  2361. if (rmap == nullptr) {
  2362. for (FI fi : fis)
  2363. for (VI vi : mesh.vertices_around_face(mesh.halfedge(fi)))
  2364. if (!is_counted[vi]) {
  2365. is_counted[vi] = true;
  2366. ++count_vertices;
  2367. }
  2368. } else {
  2369. for (FI fi : fis)
  2370. for (VI vi : mesh.vertices_around_face(mesh.halfedge(fi))) {
  2371. // Will vertex be reduced?
  2372. if ((*rmap)[vi].is_valid()) continue;
  2373. if (!is_counted[vi]) {
  2374. is_counted[vi] = true;
  2375. ++count_vertices;
  2376. }
  2377. }
  2378. }
  2379. mesh.remove_property_map(is_counted);
  2380. uint32_t count_faces = fis.size();
  2381. // IMPROVE: Value is greater than neccessary, count edges used twice
  2382. uint32_t count_edges = count_faces*3;
  2383. CutMesh cm;
  2384. cm.reserve(count_vertices, count_edges, count_faces);
  2385. // vertex conversion function from mesh VI to result VI
  2386. CvtVI2VI mesh2result = mesh.add_property_map<VI,VI>("v:mesh2result").first;
  2387. if (rmap == nullptr) {
  2388. for (FI fi : fis) {
  2389. std::array<VI, 3> t;
  2390. int index = 0;
  2391. for (VI vi : mesh.vertices_around_face(mesh.halfedge(fi))) {
  2392. VI &vi_cvt = mesh2result[vi];
  2393. if (!vi_cvt.is_valid()) {
  2394. vi_cvt = VI(cm.vertices().size());
  2395. cm.add_vertex(mesh.point(vi));
  2396. }
  2397. t[index++] = vi_cvt;
  2398. }
  2399. cm.add_face(t[0], t[1], t[2]);
  2400. }
  2401. } else {
  2402. for (FI fi :fis) {
  2403. std::array<VI, 3> t;
  2404. int index = 0;
  2405. bool exist_reduction = false;
  2406. for (VI vi : mesh.vertices_around_face(mesh.halfedge(fi))) {
  2407. VI vi_r = (*rmap)[vi];
  2408. if (vi_r.is_valid()) {
  2409. exist_reduction = true;
  2410. vi = vi_r;
  2411. }
  2412. VI &vi_cvt = mesh2result[vi];
  2413. if (!vi_cvt.is_valid()) {
  2414. vi_cvt = VI(cm.vertices().size());
  2415. cm.add_vertex(mesh.point(vi));
  2416. }
  2417. t[index++] = vi_cvt;
  2418. }
  2419. // prevent add reduced triangle
  2420. if (exist_reduction &&
  2421. (t[0] == t[1] ||
  2422. t[1] == t[2] ||
  2423. t[2] == t[0]))
  2424. continue;
  2425. cm.add_face(t[0], t[1], t[2]);
  2426. }
  2427. }
  2428. assert(count_vertices == cm.vertices().size());
  2429. assert((rmap == nullptr && count_faces == cm.faces().size()) ||
  2430. (rmap != nullptr && count_faces >= cm.faces().size()));
  2431. assert(count_edges >= cm.edges().size());
  2432. // convert VI from this patch to source VI, when exist
  2433. CvtVI2VI cvt = cm.add_property_map<VI, VI>(patch_source_name).first;
  2434. // vi_s .. VertexIndex into mesh (source)
  2435. // vi_d .. new VertexIndex in cm (destination)
  2436. for (VI vi_s : mesh.vertices()) {
  2437. VI vi_d = mesh2result[vi_s];
  2438. if (!vi_d.is_valid()) continue;
  2439. cvt[vi_d] = vi_s;
  2440. }
  2441. mesh.remove_property_map(mesh2result);
  2442. return {std::move(cm)};
  2443. }
  2444. // diff_models help functions
  2445. namespace priv {
  2446. struct SurfacePatchEx
  2447. {
  2448. SurfacePatch patch;
  2449. // flag that part will be deleted
  2450. bool full_inside = false;
  2451. // flag that Patch could contain more than one part
  2452. bool just_cliped = false;
  2453. };
  2454. using SurfacePatchesEx = std::vector<SurfacePatchEx>;
  2455. using BBS = std::vector<BoundingBoxf3>;
  2456. /// <summary>
  2457. /// Create bounding boxes for AOI
  2458. /// </summary>
  2459. /// <param name="cuts">Cutted AOI from models</param>
  2460. /// <param name="cut_models">Source points of cuts</param>
  2461. /// <returns>Bounding boxes</returns>
  2462. BBS create_bbs(const VCutAOIs &cuts, const CutMeshes &cut_models);
  2463. using Primitive = CGAL::AABB_face_graph_triangle_primitive<CutMesh>;
  2464. using Traits = CGAL::AABB_traits<EpicKernel, Primitive>;
  2465. using Ray = EpicKernel::Ray_3;
  2466. using Tree = CGAL::AABB_tree<Traits>;
  2467. using Trees = std::vector<Tree>;
  2468. /// <summary>
  2469. /// Create AABB trees for check when patch is whole inside of model
  2470. /// </summary>
  2471. /// <param name="models">Source for trees</param>
  2472. /// <returns>trees</returns>
  2473. Trees create_trees(const CutMeshes &models);
  2474. /// <summary>
  2475. /// Check whether bounding box has intersection with model
  2476. /// </summary>
  2477. /// <param name="bb">Bounding box to check</param>
  2478. /// <param name="model_index">Model to check with</param>
  2479. /// <param name="bbs">All bounding boxes from VCutAOIs</param>
  2480. /// <param name="m2i">Help index into VCutAOIs</param>
  2481. /// <returns>True when exist bounding boxes intersection</returns>
  2482. bool has_bb_intersection(const BoundingBoxf3 &bb,
  2483. size_t model_index,
  2484. const BBS &bbs,
  2485. const ModelCut2index &m2i);
  2486. /// <summary>
  2487. /// Only for model without intersection
  2488. /// Use ray (in projection direction) from a point from patch
  2489. /// and count intersections: pair .. outside | odd .. inside
  2490. /// </summary>
  2491. /// <param name="patch">Patch to check</param>
  2492. /// <param name="tree">Model converted to AABB tree</param>
  2493. /// <param name="projection">Define direction of projection</param>
  2494. /// <returns>True when patch point lay inside of model defined by tree,
  2495. /// otherwise FALSE</returns>
  2496. bool is_patch_inside_of_model(const SurfacePatch &patch,
  2497. const Tree &tree,
  2498. const Project3d &projection);
  2499. /// <summary>
  2500. /// Return some shape point index which identify shape
  2501. /// NOTE: Used to find expolygon index
  2502. /// </summary>
  2503. /// <param name="cut">Used to search source shapes poin</param>
  2504. /// <param name="model"></param>
  2505. /// <returns>shape point index</returns>
  2506. uint32_t get_shape_point_index(const CutAOI &cut, const CutMesh &model);
  2507. using PatchNumber = CutMesh::Property_map<FI, size_t>;
  2508. /// <summary>
  2509. /// Separate triangles singned with number n
  2510. /// </summary>
  2511. /// <param name="fis">Face indices owned by separate patch</param>
  2512. /// <param name="patch">Original patch
  2513. /// NOTE: Can't be const. For indexing vetices need temporary add property map</param>
  2514. /// <param name="cvt_from">conversion map</param>
  2515. /// <returns>Just separated patch</returns>
  2516. SurfacePatch separate_patch(const std::vector<FI> &fis,
  2517. /* const*/ SurfacePatch &patch,
  2518. const CvtVI2VI &cvt_from);
  2519. /// <summary>
  2520. /// Separate connected triangles into it's own patches
  2521. /// new patches are added to back of input patches
  2522. /// </summary>
  2523. /// <param name="i">index into patches</param>
  2524. /// <param name="patches">In/Out Patches</param>
  2525. void divide_patch(size_t i, SurfacePatchesEx &patches);
  2526. /// <summary>
  2527. /// Fill outline in patches by open edges
  2528. /// </summary>
  2529. /// <param name="patches">Input/Output meshes with open edges</param>
  2530. void collect_open_edges(SurfacePatches &patches);
  2531. } // namespace priv
  2532. std::vector<BoundingBoxf3> priv::create_bbs(const VCutAOIs &cuts,
  2533. const CutMeshes &cut_models)
  2534. {
  2535. size_t count = 0;
  2536. for (const CutAOIs &cut : cuts) count += cut.size();
  2537. std::vector<BoundingBoxf3> bbs;
  2538. bbs.reserve(count);
  2539. for (size_t model_index = 0; model_index < cut_models.size(); ++model_index) {
  2540. const CutMesh &cut_model = cut_models[model_index];
  2541. const CutAOIs &cutAOIs = cuts[model_index];
  2542. for (size_t cut_index = 0; cut_index < cutAOIs.size(); ++cut_index) {
  2543. const CutAOI &cut = cutAOIs[cut_index];
  2544. bbs.push_back(bounding_box(cut, cut_model));
  2545. }
  2546. }
  2547. return bbs;
  2548. }
  2549. priv::Trees priv::create_trees(const CutMeshes &models) {
  2550. Trees result;
  2551. result.reserve(models.size());
  2552. for (const CutMesh &model : models) {
  2553. Tree tree;
  2554. tree.insert(faces(model).first, faces(model).second, model);
  2555. tree.build();
  2556. result.emplace_back(std::move(tree));
  2557. }
  2558. return result;
  2559. }
  2560. bool priv::has_bb_intersection(const BoundingBoxf3 &bb,
  2561. size_t model_index,
  2562. const BBS &bbs,
  2563. const ModelCut2index &m2i)
  2564. {
  2565. const auto&offsets = m2i.get_offsets();
  2566. // for cut index with model_index2
  2567. size_t start = offsets[model_index];
  2568. size_t next = model_index + 1;
  2569. size_t end = (next < offsets.size()) ? offsets[next] : m2i.get_count();
  2570. for (size_t bb_index = start; bb_index < end; bb_index++)
  2571. if (bb.intersects(bbs[bb_index])) return true;
  2572. return false;
  2573. }
  2574. bool priv::is_patch_inside_of_model(const SurfacePatch &patch,
  2575. const Tree &tree,
  2576. const Project3d &projection)
  2577. {
  2578. // TODO: Solve model with hole in projection direction !!!
  2579. const P3 &a = patch.mesh.point(VI(0));
  2580. Vec3d a_ = to_vec3d(a);
  2581. Vec3d b_ = projection.project(a_);
  2582. P3 b(b_.x(), b_.y(), b_.z());
  2583. Ray ray_query(a, b);
  2584. size_t count = tree.number_of_intersected_primitives(ray_query);
  2585. bool is_in = (count % 2) == 1;
  2586. // try opposit direction result should be same, otherwise open model is used
  2587. //Vec3f c_ = a_ - (b_ - a_); // opposit direction
  2588. //P3 c(c_.x(), c_.y(), c_.z());
  2589. //Ray ray_query2(a, b);
  2590. //size_t count2 = tree.number_of_intersected_primitives(ray_query2);
  2591. //bool is_in2 = (count2 % 2) == 1;
  2592. assert(((tree.number_of_intersected_primitives(
  2593. Ray(a, P3(2 * a.x() - b.x(),
  2594. 2 * a.y() - b.y(),
  2595. 2 * a.z() - b.z()))) %
  2596. 2) == 1) == is_in);
  2597. return is_in;
  2598. }
  2599. uint32_t priv::get_shape_point_index(const CutAOI &cut, const CutMesh &model)
  2600. {
  2601. // map is created during intersection by corefine visitor
  2602. const VertexShapeMap &vert_shape_map = model.property_map<VI, const IntersectingElement *>(vert_shape_map_name).first;
  2603. // for each half edge of outline
  2604. for (HI hi : cut.second) {
  2605. VI vi = model.source(hi);
  2606. const IntersectingElement *ie = vert_shape_map[vi];
  2607. if (ie == nullptr) continue;
  2608. assert(ie->shape_point_index != std::numeric_limits<uint32_t>::max());
  2609. return ie->shape_point_index;
  2610. }
  2611. // can't found any intersecting element in cut
  2612. assert(false);
  2613. return 0;
  2614. }
  2615. priv::SurfacePatch priv::separate_patch(const std::vector<FI>& fis,
  2616. SurfacePatch &patch,
  2617. const CvtVI2VI &cvt_from)
  2618. {
  2619. assert(patch.mesh.is_valid());
  2620. SurfacePatch patch_new = create_surface_patch(fis, patch.mesh);
  2621. patch_new.bb = bounding_box(patch_new.mesh);
  2622. patch_new.aoi_id = patch.aoi_id;
  2623. patch_new.model_id = patch.model_id;
  2624. patch_new.shape_id = patch.shape_id;
  2625. // fix cvt
  2626. CvtVI2VI cvt = patch_new.mesh.property_map<VI, VI>(patch_source_name).first;
  2627. for (VI &vi : cvt) {
  2628. if (!vi.is_valid()) continue;
  2629. vi = cvt_from[vi];
  2630. }
  2631. return patch_new;
  2632. }
  2633. void priv::divide_patch(size_t i, SurfacePatchesEx &patches)
  2634. {
  2635. SurfacePatchEx &patch_ex = patches[i];
  2636. assert(patch_ex.just_cliped);
  2637. patch_ex.just_cliped = false;
  2638. SurfacePatch& patch = patch_ex.patch;
  2639. CutMesh& cm = patch.mesh;
  2640. assert(!cm.faces().empty());
  2641. std::string patch_number_name = "f:patch_number";
  2642. CutMesh::Property_map<FI,bool> is_processed = cm.add_property_map<FI, bool>(patch_number_name, false).first;
  2643. const CvtVI2VI& cvt_from = patch.mesh.property_map<VI, VI>(patch_source_name).first;
  2644. std::vector<FI> fis;
  2645. fis.reserve(cm.faces().size());
  2646. SurfacePatchesEx new_patches;
  2647. std::vector<FI> queue;
  2648. // IMPROVE: create groups around triangles and than connect groups
  2649. for (FI fi_cm : cm.faces()) {
  2650. if (is_processed[fi_cm]) continue;
  2651. assert(queue.empty());
  2652. queue.push_back(fi_cm);
  2653. if (!fis.empty()) {
  2654. // Be carefull after push to patches,
  2655. // all ref on patch contain non valid values
  2656. SurfacePatchEx patch_ex_n;
  2657. patch_ex_n.patch = separate_patch(fis, patch, cvt_from);
  2658. patch_ex_n.patch.is_whole_aoi = false;
  2659. new_patches.push_back(std::move(patch_ex_n));
  2660. fis.clear();
  2661. }
  2662. // flood fill from triangle fi_cm to surrounding
  2663. do {
  2664. FI fi_q = queue.back();
  2665. queue.pop_back();
  2666. if (is_processed[fi_q]) continue;
  2667. is_processed[fi_q] = true;
  2668. fis.push_back(fi_q);
  2669. HI hi = cm.halfedge(fi_q);
  2670. for (FI fi : cm.faces_around_face(hi)) {
  2671. // by documentation The face descriptor may be the null face, and it may be several times the same face descriptor.
  2672. if (!fi.is_valid()) continue;
  2673. if (!is_processed[fi]) queue.push_back(fi);
  2674. }
  2675. } while (!queue.empty());
  2676. }
  2677. cm.remove_property_map(is_processed);
  2678. assert(!fis.empty());
  2679. // speed up for only one patch - no dividing (the most common)
  2680. if (new_patches.empty()) {
  2681. patch.bb = bounding_box(cm);
  2682. patch.is_whole_aoi = false;
  2683. } else {
  2684. patch = separate_patch(fis, patch, cvt_from);
  2685. patches.insert(patches.end(), new_patches.begin(), new_patches.end());
  2686. }
  2687. }
  2688. void priv::collect_open_edges(SurfacePatches &patches) {
  2689. std::vector<HI> open_half_edges;
  2690. for (SurfacePatch &patch : patches) {
  2691. open_half_edges.clear();
  2692. const CutMesh &mesh = patch.mesh;
  2693. for (FI fi : mesh.faces()) {
  2694. HI hi1 = mesh.halfedge(fi);
  2695. assert(hi1.is_valid());
  2696. HI hi2 = mesh.next(hi1);
  2697. assert(hi2.is_valid());
  2698. HI hi3 = mesh.next(hi2);
  2699. assert(hi3.is_valid());
  2700. // Is fi triangle?
  2701. assert(mesh.next(hi3) == hi1);
  2702. for (HI hi : {hi1, hi2, hi3}) {
  2703. HI hi_op = mesh.opposite(hi);
  2704. FI fi_op = mesh.face(hi_op);
  2705. if (!fi_op.is_valid())
  2706. open_half_edges.push_back(hi);
  2707. }
  2708. }
  2709. patch.loops = create_loops(open_half_edges, mesh);
  2710. }
  2711. }
  2712. priv::SurfacePatches priv::diff_models(VCutAOIs &cuts,
  2713. /*const*/ CutMeshes &cut_models,
  2714. /*const*/ CutMeshes &models,
  2715. const Project3d &projection)
  2716. {
  2717. // IMPROVE: when models contain ONE mesh. It is only about convert cuts to patches
  2718. // and reduce unneccessary triangles on contour
  2719. //Convert model_index and cut_index into one index
  2720. priv::ModelCut2index m2i(cuts);
  2721. // create bounding boxes for cuts
  2722. std::vector<BoundingBoxf3> bbs = create_bbs(cuts, cut_models);
  2723. Trees trees(models.size());
  2724. SurfacePatches patches;
  2725. // queue of patches for one AOI (permanent with respect to for loop)
  2726. SurfacePatchesEx aoi_patches;
  2727. //SurfacePatches aoi_patches;
  2728. patches.reserve(m2i.get_count()); // only approximation of count
  2729. size_t index = 0;
  2730. for (size_t model_index = 0; model_index < models.size(); ++model_index) {
  2731. CutAOIs &model_cuts = cuts[model_index];
  2732. CutMesh &cut_model_ = cut_models[model_index];
  2733. const CutMesh &cut_model = cut_model_;
  2734. ReductionMap vertex_reduction_map = cut_model_.add_property_map<VI, VI>(vertex_reduction_map_name).first;
  2735. create_reduce_map(vertex_reduction_map, cut_model);
  2736. for (size_t cut_index = 0; cut_index < model_cuts.size(); ++cut_index, ++index) {
  2737. const CutAOI &cut = model_cuts[cut_index];
  2738. SurfacePatchEx patch_ex;
  2739. SurfacePatch &patch = patch_ex.patch;
  2740. patch = create_surface_patch(cut.first, cut_model_, &vertex_reduction_map);
  2741. patch.bb = bbs[index];
  2742. patch.aoi_id = cut_index;
  2743. patch.model_id = model_index;
  2744. patch.shape_id = get_shape_point_index(cut, cut_model);
  2745. patch.is_whole_aoi = true;
  2746. aoi_patches.clear();
  2747. aoi_patches.push_back(patch_ex);
  2748. for (size_t model_index2 = 0; model_index2 < models.size(); ++model_index2) {
  2749. // do not clip source model itself
  2750. if (model_index == model_index2) continue;
  2751. for (SurfacePatchEx &patch_ex : aoi_patches) {
  2752. SurfacePatch &patch = patch_ex.patch;
  2753. if (has_bb_intersection(patch.bb, model_index2, bbs, m2i) &&
  2754. clip_cut(patch, models[model_index2])){
  2755. patch_ex.just_cliped = true;
  2756. } else {
  2757. // build tree on demand
  2758. // NOTE: it is possible not neccessary: e.g. one model
  2759. Tree &tree = trees[model_index2];
  2760. if (tree.empty()) {
  2761. const CutMesh &model = models[model_index2];
  2762. auto f_range = faces(model);
  2763. tree.insert(f_range.first, f_range.second, model);
  2764. tree.build();
  2765. }
  2766. if (is_patch_inside_of_model(patch, tree, projection))
  2767. patch_ex.full_inside = true;
  2768. }
  2769. }
  2770. // erase full inside
  2771. for (size_t i = aoi_patches.size(); i != 0; --i) {
  2772. auto it = aoi_patches.begin() + (i - 1);
  2773. if (it->full_inside) aoi_patches.erase(it);
  2774. }
  2775. // detection of full AOI inside of model
  2776. if (aoi_patches.empty()) break;
  2777. // divide cliped into parts
  2778. size_t end = aoi_patches.size();
  2779. for (size_t i = 0; i < end; ++i)
  2780. if (aoi_patches[i].just_cliped)
  2781. divide_patch(i, aoi_patches);
  2782. }
  2783. if (!aoi_patches.empty()) {
  2784. patches.reserve(patches.size() + aoi_patches.size());
  2785. for (SurfacePatchEx &patch : aoi_patches)
  2786. patches.push_back(std::move(patch.patch));
  2787. }
  2788. }
  2789. cut_model_.remove_property_map(vertex_reduction_map);
  2790. }
  2791. // Also use outline inside of patches(made by non manifold models)
  2792. // IMPROVE: trace outline from AOIs
  2793. collect_open_edges(patches);
  2794. return patches;
  2795. }
  2796. bool priv::is_over_whole_expoly(const SurfacePatch &patch,
  2797. const ExPolygons &shapes,
  2798. const VCutAOIs &cutAOIs,
  2799. const CutMeshes &meshes)
  2800. {
  2801. if (!patch.is_whole_aoi) return false;
  2802. return is_over_whole_expoly(cutAOIs[patch.model_id][patch.aoi_id],
  2803. shapes[patch.shape_id],
  2804. meshes[patch.model_id]);
  2805. }
  2806. bool priv::is_over_whole_expoly(const CutAOI &cutAOI,
  2807. const ExPolygon &shape,
  2808. const CutMesh &mesh)
  2809. {
  2810. // NonInterupted contour is without other point and contain all from shape
  2811. const VertexShapeMap &vert_shape_map = mesh.property_map<VI, const IntersectingElement*>(vert_shape_map_name).first;
  2812. for (HI hi : cutAOI.second) {
  2813. const IntersectingElement *ie_s = vert_shape_map[mesh.source(hi)];
  2814. const IntersectingElement *ie_t = vert_shape_map[mesh.target(hi)];
  2815. if (ie_s == nullptr || ie_t == nullptr)
  2816. return false;
  2817. assert(ie_s->attr != (unsigned char) IntersectingElement::Type::undefined);
  2818. assert(ie_t->attr != (unsigned char) IntersectingElement::Type::undefined);
  2819. // check if it is neighbor indices
  2820. uint32_t i_s = ie_s->shape_point_index;
  2821. uint32_t i_t = ie_t->shape_point_index;
  2822. assert(i_s != std::numeric_limits<uint32_t>::max());
  2823. assert(i_t != std::numeric_limits<uint32_t>::max());
  2824. if (i_s == std::numeric_limits<uint32_t>::max() ||
  2825. i_t == std::numeric_limits<uint32_t>::max())
  2826. return false;
  2827. // made by same index
  2828. if (i_s == i_t) continue;
  2829. // order from source to target
  2830. if (i_s > i_t) {
  2831. std::swap(i_s, i_t);
  2832. std::swap(ie_s, ie_t);
  2833. }
  2834. // Must be after fix order !!
  2835. bool is_last_polygon_segment = ie_s->is_first() && ie_t->is_last();
  2836. if (is_last_polygon_segment) {
  2837. std::swap(i_s, i_t);
  2838. std::swap(ie_s, ie_t);
  2839. }
  2840. // Is continous indices
  2841. if (!is_last_polygon_segment &&
  2842. (ie_s->is_last() || (i_s + 1) != i_t))
  2843. return false;
  2844. IntersectingElement::Type t_s = ie_s->get_type();
  2845. IntersectingElement::Type t_t = ie_t->get_type();
  2846. if (t_s == IntersectingElement::Type::undefined ||
  2847. t_t == IntersectingElement::Type::undefined)
  2848. return false;
  2849. // next segment must start with edge intersection
  2850. if (t_t != IntersectingElement::Type::edge_1)
  2851. return false;
  2852. // After face1 must be edge2 or face2
  2853. if (t_s == IntersectingElement::Type::face_1)
  2854. return false;
  2855. }
  2856. // When all open edges are on contour than there is NO holes is shape
  2857. auto is_open = [&mesh](HI hi)->bool {
  2858. HI opposite = mesh.opposite(hi);
  2859. return !mesh.face(opposite).is_valid();
  2860. };
  2861. std::vector<HI> opens; // copy
  2862. opens.reserve(cutAOI.second.size());
  2863. for (HI hi : cutAOI.second) // from lower to bigger
  2864. if (is_open(hi)) opens.push_back(hi);
  2865. std::sort(opens.begin(), opens.end());
  2866. for (FI fi: cutAOI.first) {
  2867. HI face_hi = mesh.halfedge(fi);
  2868. for (HI hi : mesh.halfedges_around_face(face_hi)) {
  2869. if (!is_open(hi)) continue;
  2870. // open edge
  2871. auto lb = std::lower_bound(opens.begin(), opens.end(), hi);
  2872. if (lb == opens.end() || *lb != hi)
  2873. return false; // not in contour
  2874. }
  2875. }
  2876. return true;
  2877. }
  2878. std::vector<bool> priv::select_patches(const ProjectionDistances &best_distances,
  2879. const SurfacePatches &patches,
  2880. const ExPolygons &shapes,
  2881. const BoundingBox &shapes_bb,
  2882. const ExPolygonsIndices &s2i,
  2883. const VCutAOIs &cutAOIs,
  2884. const CutMeshes &meshes,
  2885. const Project &projection)
  2886. {
  2887. // extension to cover numerical mistake made by back projection patch from 3d to 2d
  2888. // Calculated as one percent of average size(width and height)
  2889. Point s = shapes_bb.size();
  2890. const float extend_delta = (s.x() + s.y())/ float(2 * 100);
  2891. // vector of patches for shape
  2892. std::vector<std::vector<uint32_t>> used_shapes_patches(shapes.size());
  2893. std::vector<bool> in_distances(patches.size(), {false});
  2894. for (const ProjectionDistance &d : best_distances) {
  2895. // exist valid projection for shape point?
  2896. if (d.patch_index == std::numeric_limits<uint32_t>::max()) continue;
  2897. if (in_distances[d.patch_index]) continue;
  2898. in_distances[d.patch_index] = true;
  2899. ExPolygonsIndex id = s2i.cvt(&d - &best_distances.front());
  2900. used_shapes_patches[id.expolygons_index].push_back(d.patch_index);
  2901. }
  2902. // vector of patches for shape
  2903. std::vector<std::vector<uint32_t>> shapes_patches(shapes.size());
  2904. for (const SurfacePatch &patch : patches)
  2905. shapes_patches[patch.shape_id].push_back(&patch - &patches.front());
  2906. #ifdef DEBUG_OUTPUT_DIR
  2907. std::string store_dir = DEBUG_OUTPUT_DIR + "select_patches/";
  2908. prepare_dir(store_dir);
  2909. #endif // DEBUG_OUTPUT_DIR
  2910. for (size_t shape_index = 0; shape_index < shapes.size(); shape_index++) {
  2911. const ExPolygon &shape = shapes[shape_index];
  2912. std::vector<uint32_t> &used_shape_patches = used_shapes_patches[shape_index];
  2913. if (used_shape_patches.empty()) continue;
  2914. // is used all exist patches?
  2915. if (used_shapes_patches.size() == shapes_patches[shape_index].size()) continue;
  2916. if (used_shape_patches.size() == 1) {
  2917. uint32_t patch_index = used_shape_patches.front();
  2918. const SurfacePatch &patch = patches[patch_index];
  2919. if (is_over_whole_expoly(patch, shapes, cutAOIs, meshes)) continue;
  2920. }
  2921. // only shapes containing multiple patches
  2922. // or not full filled are back projected (hard processed)
  2923. // intersection of converted patches to 2d
  2924. ExPolygons fill;
  2925. fill.reserve(used_shape_patches.size());
  2926. // Heuristics to predict which patch to be used need average patch depth
  2927. Vec2d used_patches_depth(std::numeric_limits<double>::max(), std::numeric_limits<double>::min());
  2928. for (uint32_t patch_index : used_shape_patches) {
  2929. ExPolygon patch_area = to_expoly(patches[patch_index], projection, used_patches_depth);
  2930. //*/
  2931. ExPolygons patch_areas = offset_ex(patch_area, extend_delta);
  2932. fill.insert(fill.end(), patch_areas.begin(), patch_areas.end());
  2933. /*/
  2934. // without save extension
  2935. fill.push_back(patch_area);
  2936. //*/
  2937. }
  2938. fill = union_ex(fill);
  2939. // not cutted area of expolygon
  2940. ExPolygons rest = diff_ex(ExPolygons{shape}, fill, ApplySafetyOffset::Yes);
  2941. #ifdef DEBUG_OUTPUT_DIR
  2942. BoundingBox shape_bb = get_extents(shape);
  2943. SVG svg(store_dir + "shape_" + std::to_string(shape_index) + ".svg", shape_bb);
  2944. svg.draw(fill, "darkgreen");
  2945. svg.draw(rest, "green");
  2946. #endif // DEBUG_OUTPUT_DIR
  2947. // already filled by multiple patches
  2948. if (rest.empty()) continue;
  2949. // find patches overlaped rest area
  2950. struct PatchShape{
  2951. uint32_t patch_index;
  2952. ExPolygon shape;
  2953. ExPolygons intersection;
  2954. double depth_range_center_distance; // always positive
  2955. };
  2956. using PatchShapes = std::vector<PatchShape>;
  2957. PatchShapes patch_shapes;
  2958. double used_patches_depth_center = (used_patches_depth[0] + used_patches_depth[1]) / 2;
  2959. // sort used_patches for faster search
  2960. std::sort(used_shape_patches.begin(), used_shape_patches.end());
  2961. for (uint32_t patch_index : shapes_patches[shape_index]) {
  2962. // check is patch already used
  2963. auto it = std::lower_bound(used_shape_patches.begin(), used_shape_patches.end(), patch_index);
  2964. if (it != used_shape_patches.end() && *it == patch_index) continue;
  2965. // Heuristics to predict which patch to be used need average patch depth
  2966. Vec2d patche_depth_range(std::numeric_limits<double>::max(), std::numeric_limits<double>::min());
  2967. ExPolygon patch_shape = to_expoly(patches[patch_index], projection, patche_depth_range);
  2968. double depth_center = (patche_depth_range[0] + patche_depth_range[1]) / 2;
  2969. double depth_range_center_distance = std::fabs(used_patches_depth_center - depth_center);
  2970. ExPolygons patch_intersection = intersection_ex(ExPolygons{patch_shape}, rest);
  2971. if (patch_intersection.empty()) continue;
  2972. patch_shapes.push_back({patch_index, patch_shape, patch_intersection, depth_range_center_distance});
  2973. }
  2974. // nothing to add
  2975. if (patch_shapes.empty()) continue;
  2976. // only one solution to add
  2977. if (patch_shapes.size() == 1) {
  2978. used_shape_patches.push_back(patch_shapes.front().patch_index);
  2979. continue;
  2980. }
  2981. // Idea: Get depth range of used patches and add patches in order by distance to used depth center
  2982. std::sort(patch_shapes.begin(), patch_shapes.end(), [](const PatchShape &a, const PatchShape &b)
  2983. { return a.depth_range_center_distance < b.depth_range_center_distance; });
  2984. #ifdef DEBUG_OUTPUT_DIR
  2985. for (size_t i = patch_shapes.size(); i > 0; --i) {
  2986. const PatchShape &p = patch_shapes[i - 1];
  2987. int gray_level = (i * 200) / patch_shapes.size();
  2988. std::stringstream color;
  2989. color << "#" << std::hex << std::setfill('0') << std::setw(2) << gray_level << gray_level << gray_level;
  2990. svg.draw(p.shape, color.str());
  2991. Point text_pos = get_extents(p.shape).center().cast<int>();
  2992. svg.draw_text(text_pos, std::to_string(i-1).c_str(), "orange", std::ceil(shape_bb.size().x() / 20 * 0.000001));
  2993. //svg.draw(p.intersection, color.str());
  2994. }
  2995. #endif // DEBUG_OUTPUT_DIR
  2996. for (const PatchShape &patch : patch_shapes) {
  2997. // Check when exist some place to fill
  2998. ExPolygons patch_intersection = intersection_ex(patch.intersection, rest);
  2999. if (patch_intersection.empty()) continue;
  3000. // Extend for sure
  3001. ExPolygons intersection = offset_ex(patch.intersection, extend_delta);
  3002. rest = diff_ex(rest, intersection, ApplySafetyOffset::Yes);
  3003. used_shape_patches.push_back(patch.patch_index);
  3004. if (rest.empty()) break;
  3005. }
  3006. // QUESTION: How to select which patch to use? How to sort them?
  3007. // Now is used back projection distance from used patches
  3008. //
  3009. // Idealy by outline depth: (need ray cast into patches)
  3010. // how to calc wanted depth - idealy by depth of outline help to overlap
  3011. // how to calc patch depth - depth in place of outline position
  3012. // Which outline to use between
  3013. }
  3014. std::vector<bool> result(patches.size(), {false});
  3015. for (const std::vector<uint32_t> &patches: used_shapes_patches)
  3016. for (uint32_t patch_index : patches) {
  3017. assert(patch_index < result.size());
  3018. // check only onece insertation of patch
  3019. assert(!result[patch_index]);
  3020. result[patch_index] = true;
  3021. }
  3022. return result;
  3023. }
  3024. priv::Loops priv::create_loops(const std::vector<HI> &outlines, const CutMesh& mesh)
  3025. {
  3026. Loops loops;
  3027. Loops unclosed;
  3028. for (HI hi : outlines) {
  3029. VI vi_s = mesh.source(hi);
  3030. VI vi_t = mesh.target(hi);
  3031. Loop *loop_move = nullptr;
  3032. Loop *loop_connect = nullptr;
  3033. for (std::vector<VI> &cut : unclosed) {
  3034. if (cut.back() != vi_s) continue;
  3035. if (cut.front() == vi_t) {
  3036. // cut closing
  3037. loop_move = &cut;
  3038. } else {
  3039. loop_connect = &cut;
  3040. }
  3041. break;
  3042. }
  3043. if (loop_move != nullptr) {
  3044. // index of closed cut
  3045. size_t index = loop_move - &unclosed.front();
  3046. // move cut to result
  3047. loops.emplace_back(std::move(*loop_move));
  3048. // remove it from unclosed cut
  3049. unclosed.erase(unclosed.begin() + index);
  3050. } else if (loop_connect != nullptr) {
  3051. // try find tail to connect cut
  3052. Loop *loop_tail = nullptr;
  3053. for (Loop &cut : unclosed) {
  3054. if (cut.front() != vi_t) continue;
  3055. loop_tail = &cut;
  3056. break;
  3057. }
  3058. if (loop_tail != nullptr) {
  3059. // index of tail
  3060. size_t index = loop_tail - &unclosed.front();
  3061. // move to connect vector
  3062. loop_connect->insert(loop_connect->end(),
  3063. make_move_iterator(loop_tail->begin()),
  3064. make_move_iterator(loop_tail->end()));
  3065. // remove tail from unclosed cut
  3066. unclosed.erase(unclosed.begin() + index);
  3067. } else {
  3068. loop_connect->push_back(vi_t);
  3069. }
  3070. } else { // not found
  3071. bool create_cut = true;
  3072. // try to insert to front of cut
  3073. for (Loop &cut : unclosed) {
  3074. if (cut.front() != vi_t) continue;
  3075. cut.insert(cut.begin(), vi_s);
  3076. create_cut = false;
  3077. break;
  3078. }
  3079. if (create_cut)
  3080. unclosed.emplace_back(std::vector{vi_s, vi_t});
  3081. }
  3082. }
  3083. assert(unclosed.empty());
  3084. return loops;
  3085. }
  3086. Polygons priv::unproject_loops(const SurfacePatch &patch, const Project &projection, Vec2d &depth_range)
  3087. {
  3088. assert(!patch.loops.empty());
  3089. if (patch.loops.empty()) return {};
  3090. // NOTE: this method is working only when patch did not contain outward faces
  3091. Polygons polys;
  3092. polys.reserve(patch.loops.size());
  3093. // project conture into 2d space to fillconvert outlines to
  3094. size_t count = 0;
  3095. for (const Loop &l : patch.loops) count += l.size();
  3096. std::vector<float> depths;
  3097. depths.reserve(count);
  3098. Points pts;
  3099. for (const Loop &l : patch.loops) {
  3100. pts.clear();
  3101. pts.reserve(l.size());
  3102. for (VI vi : l) {
  3103. const P3 &p3 = patch.mesh.point(vi);
  3104. Vec3d p = to_vec3d(p3);
  3105. double depth;
  3106. std::optional<Vec2d> p2_opt = projection.unproject(p, &depth);
  3107. if (depth_range[0] > depth) depth_range[0] = depth; // min
  3108. if (depth_range[1] < depth) depth_range[1] = depth; // max
  3109. // Check when appear that skip is enough for poit which can't be unprojected
  3110. // - it could break contour
  3111. assert(p2_opt.has_value());
  3112. if (!p2_opt.has_value()) continue;
  3113. pts.push_back(p2_opt->cast<Point::coord_type>());
  3114. depths.push_back(static_cast<float>(depth));
  3115. }
  3116. // minimal is triangle
  3117. assert(pts.size() >= 3);
  3118. if (pts.size() < 3) continue;
  3119. polys.emplace_back(pts);
  3120. }
  3121. assert(!polys.empty());
  3122. return polys;
  3123. }
  3124. ExPolygon priv::to_expoly(const SurfacePatch &patch, const Project &projection, Vec2d &depth_range)
  3125. {
  3126. Polygons polys = unproject_loops(patch, projection, depth_range);
  3127. // should not be used when no opposit triangle are counted so should not create overlaps
  3128. ClipperLib::PolyFillType fill_type = ClipperLib::PolyFillType::pftEvenOdd;
  3129. ExPolygons expolys = Slic3r::union_ex(polys, fill_type);
  3130. if (expolys.size() == 1)
  3131. return expolys.front();
  3132. // It should be one expolygon
  3133. assert(false);
  3134. if (expolys.empty()) return {};
  3135. // find biggest
  3136. const ExPolygon *biggest = &expolys.front();
  3137. for (size_t index = 1; index < expolys.size(); ++index) {
  3138. const ExPolygon *current = &expolys[index];
  3139. if (biggest->contour.size() < current->contour.size())
  3140. biggest = current;
  3141. }
  3142. return *biggest;
  3143. }
  3144. SurfaceCut priv::patch2cut(SurfacePatch &patch)
  3145. {
  3146. CutMesh &mesh = patch.mesh;
  3147. std::string convert_map_name = "v:convert";
  3148. CutMesh::Property_map<VI, SurfaceCut::Index> convert_map =
  3149. mesh.add_property_map<VI, SurfaceCut::Index>(convert_map_name).first;
  3150. size_t indices_size = mesh.faces().size();
  3151. size_t vertices_size = mesh.vertices().size();
  3152. SurfaceCut sc;
  3153. sc.indices.reserve(indices_size);
  3154. sc.vertices.reserve(vertices_size);
  3155. for (VI vi : mesh.vertices()) {
  3156. // vi order is is not sorted
  3157. // assert(vi.idx() == sc.vertices.size());
  3158. // vi is not continous
  3159. // assert(vi.idx() < vertices_size);
  3160. convert_map[vi] = sc.vertices.size();
  3161. const P3 &p = mesh.point(vi);
  3162. sc.vertices.emplace_back(p.x(), p.y(), p.z());
  3163. }
  3164. for (FI fi : mesh.faces()) {
  3165. HI hi = mesh.halfedge(fi);
  3166. assert(mesh.next(hi).is_valid());
  3167. assert(mesh.next(mesh.next(hi)).is_valid());
  3168. // Is fi triangle?
  3169. assert(mesh.next(mesh.next(mesh.next(hi))) == hi);
  3170. // triangle indicies
  3171. Vec3i32 ti;
  3172. size_t i = 0;
  3173. for (VI vi : { mesh.source(hi),
  3174. mesh.target(hi),
  3175. mesh.target(mesh.next(hi))})
  3176. ti[i++] = convert_map[vi];
  3177. sc.indices.push_back(ti);
  3178. }
  3179. sc.contours.reserve(patch.loops.size());
  3180. for (const Loop &loop : patch.loops) {
  3181. sc.contours.push_back({});
  3182. std::vector<SurfaceCut::Index> &contour = sc.contours.back();
  3183. contour.reserve(loop.size());
  3184. for (VI vi : loop) contour.push_back(convert_map[vi]);
  3185. }
  3186. // Not neccessary, clean and free memory
  3187. mesh.remove_property_map(convert_map);
  3188. return sc;
  3189. }
  3190. void priv::append(SurfaceCut &sc, SurfaceCut &&sc_add)
  3191. {
  3192. if (sc.empty()) {
  3193. sc = std::move(sc_add);
  3194. return;
  3195. }
  3196. if (!sc_add.contours.empty()) {
  3197. SurfaceCut::Index offset = static_cast<SurfaceCut::Index>(
  3198. sc.vertices.size());
  3199. size_t require = sc.contours.size() + sc_add.contours.size();
  3200. if (sc.contours.capacity() < require) sc.contours.reserve(require);
  3201. for (std::vector<SurfaceCut::Index> &cut : sc_add.contours)
  3202. for (SurfaceCut::Index &i : cut) i += offset;
  3203. Slic3r::append(sc.contours, std::move(sc_add.contours));
  3204. }
  3205. its_merge(sc, std::move(sc_add));
  3206. }
  3207. SurfaceCut priv::merge_patches(SurfacePatches &patches, const std::vector<bool>& mask)
  3208. {
  3209. SurfaceCut result;
  3210. for (SurfacePatch &patch : patches) {
  3211. size_t index = &patch - &patches.front();
  3212. if (!mask[index]) continue;
  3213. append(result, patch2cut(patch));
  3214. }
  3215. return result;
  3216. }
  3217. #ifdef DEBUG_OUTPUT_DIR
  3218. void priv::prepare_dir(const std::string &dir){
  3219. namespace fs = std::filesystem;
  3220. if (fs::exists(dir)) {
  3221. for (auto &path : fs::directory_iterator(dir)) fs::remove_all(path);
  3222. } else {
  3223. fs::create_directories(dir);
  3224. }
  3225. }
  3226. namespace priv{
  3227. int reduction_order = 0;
  3228. int filled_order = 0;
  3229. int constrained_order = 0;
  3230. int diff_patch_order = 0;
  3231. } // namespace priv
  3232. void priv::initialize_store(const std::string& dir)
  3233. {
  3234. // clear previous output
  3235. prepare_dir(dir);
  3236. reduction_order = 0;
  3237. filled_order = 0;
  3238. constrained_order = 0;
  3239. diff_patch_order = 0;
  3240. }
  3241. void priv::store(const Vec3f &vertex,
  3242. const Vec3f &normal,
  3243. const std::string &file,
  3244. float size)
  3245. {
  3246. int flatten = 20;
  3247. size_t min_i = 0;
  3248. for (size_t i = 1; i < 3; i++)
  3249. if (normal[min_i] > normal[i]) min_i = i;
  3250. Vec3f up_ = Vec3f::Zero();
  3251. up_[min_i] = 1.f;
  3252. Vec3f side = normal.cross(up_).normalized() * size;
  3253. Vec3f up = side.cross(normal).normalized() * size;
  3254. indexed_triangle_set its;
  3255. its.vertices.reserve(flatten + 1);
  3256. its.indices.reserve(flatten);
  3257. its.vertices.push_back(vertex);
  3258. its.vertices.push_back(vertex + up);
  3259. size_t max_i = static_cast<size_t>(flatten);
  3260. for (size_t i = 1; i < max_i; i++) {
  3261. float angle = i * 2 * M_PI / flatten;
  3262. Vec3f v = vertex + sin(angle) * side + cos(angle) * up;
  3263. its.vertices.push_back(v);
  3264. its.indices.emplace_back(0, i, i + 1);
  3265. }
  3266. its.indices.emplace_back(0, flatten, 1);
  3267. its_write_obj(its, file.c_str());
  3268. }
  3269. void priv::store(const CutMesh &mesh, const FaceTypeMap &face_type_map, const std::string& dir, bool is_filled)
  3270. {
  3271. std::string off_file;
  3272. if (is_filled) {
  3273. if (filled_order == 0) prepare_dir(dir);
  3274. off_file = dir + "model" + std::to_string(filled_order++) + ".off";
  3275. }else{
  3276. if (constrained_order == 0) prepare_dir(dir);
  3277. off_file = dir + "model" + std::to_string(constrained_order++) + ".off";
  3278. }
  3279. CutMesh &mesh_ = const_cast<CutMesh &>(mesh);
  3280. auto face_colors = mesh_.add_property_map<priv::FI, CGAL::Color>("f:color").first;
  3281. for (FI fi : mesh.faces()) {
  3282. auto &color = face_colors[fi];
  3283. switch (face_type_map[fi]) {
  3284. case FaceType::inside: color = CGAL::Color{100, 250, 100}; break; // light green
  3285. case FaceType::inside_processed: color = CGAL::Color{170, 0, 0}; break; // dark red
  3286. case FaceType::outside: color = CGAL::Color{100, 0, 100}; break; // purple
  3287. case FaceType::not_constrained: color = CGAL::Color{127, 127, 127}; break; // gray
  3288. default: color = CGAL::Color{0, 0, 255}; // blue
  3289. }
  3290. }
  3291. CGAL::IO::write_OFF(off_file, mesh, CGAL::parameters::face_color_map(face_colors));
  3292. mesh_.remove_property_map(face_colors);
  3293. }
  3294. void priv::store(const ExPolygons &shapes, const std::string &svg_file) {
  3295. SVG svg(svg_file);
  3296. svg.draw(shapes);
  3297. }
  3298. void priv::store(const CutMesh &mesh, const ReductionMap &reduction_map, const std::string& dir)
  3299. {
  3300. if (reduction_order == 0) prepare_dir(dir);
  3301. std::string off_file = dir + "model" + std::to_string(reduction_order++) + ".off";
  3302. CutMesh &mesh_ = const_cast<CutMesh &>(mesh);
  3303. auto vertex_colors = mesh_.add_property_map<priv::VI, CGAL::Color>("v:color").first;
  3304. // initialize to gray color
  3305. for (VI vi: mesh.vertices())
  3306. vertex_colors[vi] = CGAL::Color{127, 127, 127};
  3307. for (VI reduction_from : mesh.vertices()) {
  3308. VI reduction_to = reduction_map[reduction_from];
  3309. if (!reduction_to.is_valid()) continue;
  3310. vertex_colors[reduction_from] = CGAL::Color{255, 0, 0};
  3311. vertex_colors[reduction_to] = CGAL::Color{0, 0, 255};
  3312. }
  3313. CGAL::IO::write_OFF(off_file, mesh, CGAL::parameters::vertex_color_map(vertex_colors));
  3314. mesh_.remove_property_map(vertex_colors);
  3315. }
  3316. namespace priv {
  3317. indexed_triangle_set create_indexed_triangle_set(const std::vector<FI> &faces,
  3318. const CutMesh &mesh);
  3319. } // namespace priv
  3320. indexed_triangle_set priv::create_indexed_triangle_set(
  3321. const std::vector<FI> &faces, const CutMesh &mesh)
  3322. {
  3323. std::vector<VI> vertices;
  3324. vertices.reserve(faces.size() * 2);
  3325. indexed_triangle_set its;
  3326. its.indices.reserve(faces.size());
  3327. for (FI fi : faces) {
  3328. HI hi = mesh.halfedge(fi);
  3329. HI hi_end = hi;
  3330. int ti = 0;
  3331. Vec3i32 t;
  3332. do {
  3333. VI vi = mesh.source(hi);
  3334. auto res = std::find(vertices.begin(), vertices.end(), vi);
  3335. t[ti++] = res - vertices.begin();
  3336. if (res == vertices.end()) vertices.push_back(vi);
  3337. hi = mesh.next(hi);
  3338. } while (hi != hi_end);
  3339. its.indices.push_back(t);
  3340. }
  3341. its.vertices.reserve(vertices.size());
  3342. for (VI vi : vertices) {
  3343. const auto &p = mesh.point(vi);
  3344. its.vertices.emplace_back(p.x(), p.y(), p.z());
  3345. }
  3346. return its;
  3347. }
  3348. void priv::store(const CutAOIs &aois, const CutMesh &mesh, const std::string &dir) {
  3349. auto create_outline_its =
  3350. [&mesh](const std::vector<HI> &outlines) -> indexed_triangle_set {
  3351. static const float line_width = 0.1f;
  3352. indexed_triangle_set its;
  3353. its.indices.reserve(2*outlines.size());
  3354. its.vertices.reserve(outlines.size()*4);
  3355. for (HI hi : outlines) {
  3356. //FI fi = mesh.face(hi);
  3357. VI vi_a = mesh.source(hi);
  3358. VI vi_b = mesh.target(hi);
  3359. VI vi_c = mesh.target(mesh.next(hi));
  3360. P3 p3_a = mesh.point(vi_a);
  3361. P3 p3_b = mesh.point(vi_b);
  3362. P3 p3_c = mesh.point(vi_c);
  3363. Vec3f a(p3_a.x(), p3_a.y(), p3_a.z());
  3364. Vec3f b(p3_b.x(), p3_b.y(), p3_b.z());
  3365. Vec3f c(p3_c.x(), p3_c.y(), p3_c.z());
  3366. Vec3f v1 = b - a; // from a to b
  3367. v1.normalize();
  3368. Vec3f v2 = c - a; // from a to c
  3369. v2.normalize();
  3370. Vec3f norm = v1.cross(v2);
  3371. norm.normalize();
  3372. Vec3f perp_to_edge = norm.cross(v1);
  3373. perp_to_edge.normalize();
  3374. Vec3f dir = -perp_to_edge * line_width;
  3375. size_t ai = its.vertices.size();
  3376. its.vertices.push_back(a);
  3377. size_t bi = its.vertices.size();
  3378. its.vertices.push_back(b);
  3379. size_t ai2 = its.vertices.size();
  3380. its.vertices.push_back(a + dir);
  3381. size_t bi2 = its.vertices.size();
  3382. its.vertices.push_back(b + dir);
  3383. its.indices.push_back(Vec3i32(ai, ai2, bi));
  3384. its.indices.push_back(Vec3i32(ai2, bi2, bi));
  3385. }
  3386. return its;
  3387. };
  3388. prepare_dir(dir);
  3389. for (const auto &aoi : aois) {
  3390. size_t index = &aoi - &aois.front();
  3391. std::string file = dir + "aoi" + std::to_string(index) + ".obj";
  3392. indexed_triangle_set its = create_indexed_triangle_set(aoi.first, mesh);
  3393. its_write_obj(its, file.c_str());
  3394. // exist some outline?
  3395. if (aoi.second.empty()) continue;
  3396. std::string file_outline = dir + "outline" + std::to_string(index) + ".obj";
  3397. indexed_triangle_set outline = create_outline_its(aoi.second);
  3398. its_write_obj(outline, file_outline.c_str());
  3399. }
  3400. }
  3401. void priv::store(const SurfacePatches &patches, const std::string &dir) {
  3402. prepare_dir(dir);
  3403. for (const priv::SurfacePatch &patch : patches) {
  3404. size_t index = &patch - &patches.front();
  3405. if (patch.mesh.faces().empty()) continue;
  3406. CGAL::IO::write_OFF(dir + "patch" + std::to_string(index) + ".off", patch.mesh);
  3407. }
  3408. }
  3409. //
  3410. //void priv::store(const ProjectionDistances &pds,
  3411. // const VCutAOIs &aois,
  3412. // const CutMeshes &meshes,
  3413. // const std::string &file,
  3414. // float width)
  3415. //{
  3416. // // create rectangle for each half edge from projection distances
  3417. // indexed_triangle_set its;
  3418. // its.vertices.reserve(4 * pds.size());
  3419. // its.indices.reserve(2 * pds.size());
  3420. // for (const ProjectionDistance &pd : pds) {
  3421. // if (pd.aoi_index == std::numeric_limits<uint32_t>::max()) continue;
  3422. // HI hi = aois[pd.model_index][pd.aoi_index].second[pd.hi_index];
  3423. // const CutMesh &mesh = meshes[pd.model_index];
  3424. // VI vi1 = mesh.source(hi);
  3425. // VI vi2 = mesh.target(hi);
  3426. // VI vi3 = mesh.target(mesh.next(hi));
  3427. // const P3 &p1 = mesh.point(vi1);
  3428. // const P3 &p2 = mesh.point(vi2);
  3429. // const P3 &p3 = mesh.point(vi3);
  3430. // Vec3f v1(p1.x(), p1.y(), p1.z());
  3431. // Vec3f v2(p2.x(), p2.y(), p2.z());
  3432. // Vec3f v3(p3.x(), p3.y(), p3.z());
  3433. //
  3434. // Vec3f v12 = v2 - v1;
  3435. // v12.normalize();
  3436. // Vec3f v13 = v3 - v1;
  3437. // v13.normalize();
  3438. // Vec3f n = v12.cross(v13);
  3439. // n.normalize();
  3440. // Vec3f side = n.cross(v12);
  3441. // side.normalize();
  3442. // side *= -width;
  3443. //
  3444. // uint32_t i = its.vertices.size();
  3445. // its.vertices.push_back(v1);
  3446. // its.vertices.push_back(v1+side);
  3447. // its.vertices.push_back(v2);
  3448. // its.vertices.push_back(v2+side);
  3449. //
  3450. // its.indices.emplace_back(i, i + 1, i + 2);
  3451. // its.indices.emplace_back(i + 2, i + 1, i + 3);
  3452. // }
  3453. // its_write_obj(its, file.c_str());
  3454. //}
  3455. void priv::store(const ExPolygons &shapes, const std::vector<bool> &mask, const Connections &connections, const std::string &file_svg)
  3456. {
  3457. auto bb = get_extents(shapes);
  3458. int width = get_extents(shapes.front()).size().x() / 70;
  3459. SVG svg(file_svg, bb);
  3460. svg.draw(shapes);
  3461. ExPolygonsIndices s2i(shapes);
  3462. auto get_point = [&shapes, &s2i](size_t i)->Point {
  3463. auto id = s2i.cvt(i);
  3464. const ExPolygon &s = shapes[id.expolygons_index];
  3465. const Polygon &p = (id.polygon_index == 0) ?
  3466. s.contour :
  3467. s.holes[id.polygon_index - 1];
  3468. return p[id.point_index];
  3469. };
  3470. bool is_first = true;
  3471. for (const Connection &c : connections) {
  3472. if (is_first) {
  3473. is_first = false;
  3474. Point p = get_point(c.first);
  3475. svg.draw(p, "purple", 4 * width);
  3476. continue;
  3477. }
  3478. Point p1 = get_point(c.first);
  3479. Point p2 = get_point(c.second);
  3480. svg.draw(Line(p1, p2), "red", width);
  3481. }
  3482. for (size_t i = 0; i < s2i.get_count(); i++) {
  3483. Point p = get_point(i);
  3484. svg.draw(p, "black", 2*width);
  3485. if (!mask[i])
  3486. svg.draw(p, "white", width);
  3487. }
  3488. svg.Close();
  3489. }
  3490. namespace priv {
  3491. /// <summary>
  3492. /// Create model consist of rectangles for each contour edge
  3493. /// </summary>
  3494. /// <param name="its"></param>
  3495. /// <param name="contour"></param>
  3496. /// <returns></returns>
  3497. indexed_triangle_set create_contour_its(const indexed_triangle_set& its, const std::vector<unsigned int> &contour);
  3498. /// <summary>
  3499. /// Getter on triangle tip (third vertex of face)
  3500. /// </summary>
  3501. /// <param name="vi1">First vertex index</param>
  3502. /// <param name="vi2">Second vertex index</param>
  3503. /// <param name="its">Source model</param>
  3504. /// <returns>Tip Vertex index</returns>
  3505. unsigned int get_triangle_tip(unsigned int vi1,
  3506. unsigned int vi2,
  3507. const indexed_triangle_set &its);
  3508. }
  3509. unsigned int priv::get_triangle_tip(unsigned int vi1,
  3510. unsigned int vi2,
  3511. const indexed_triangle_set &its)
  3512. {
  3513. assert(vi1 < its.vertices.size());
  3514. assert(vi2 < its.vertices.size());
  3515. for (const auto &t : its.indices) {
  3516. unsigned int tvi = std::numeric_limits<unsigned int>::max();
  3517. for (const auto &vi : t) {
  3518. unsigned int vi_ = static_cast<unsigned int>(vi);
  3519. if (vi_ == vi1) continue;
  3520. if (vi_ == vi2) continue;
  3521. if (tvi == std::numeric_limits<unsigned int>::max()) {
  3522. tvi = vi_;
  3523. } else {
  3524. tvi = std::numeric_limits<unsigned int>::max();
  3525. break;
  3526. }
  3527. }
  3528. if (tvi != std::numeric_limits<unsigned int>::max())
  3529. return tvi;
  3530. }
  3531. // triangle with indices vi1 and vi2 doesnt exist
  3532. assert(false);
  3533. return std::numeric_limits<unsigned int>::max();
  3534. }
  3535. indexed_triangle_set priv::create_contour_its(
  3536. const indexed_triangle_set &its, const std::vector<unsigned int> &contour)
  3537. {
  3538. static const float line_width = 0.1f;
  3539. indexed_triangle_set result;
  3540. result.vertices.reserve((contour.size() + 1) * 4);
  3541. result.indices.reserve((contour.size() + 1) * 2);
  3542. unsigned int prev_vi = contour.back();
  3543. for (unsigned int vi : contour) {
  3544. const Vec3f &a = its.vertices[vi];
  3545. const Vec3f &b = its.vertices[prev_vi];
  3546. const Vec3f &c = its.vertices[get_triangle_tip(vi, prev_vi, its)];
  3547. Vec3f v1 = b - a; // from a to b
  3548. v1.normalize();
  3549. Vec3f v2 = c - a; // from a to c
  3550. v2.normalize();
  3551. // triangle normal
  3552. Vec3f norm = v1.cross(v2);
  3553. norm.normalize();
  3554. // perpendiculat to edge lay on triangle
  3555. Vec3f perp_to_edge = norm.cross(v1);
  3556. perp_to_edge.normalize();
  3557. Vec3f dir = -perp_to_edge * line_width;
  3558. size_t ai = result.vertices.size();
  3559. result.vertices.push_back(a);
  3560. size_t bi = result.vertices.size();
  3561. result.vertices.push_back(b);
  3562. size_t ai2 = result.vertices.size();
  3563. result.vertices.push_back(a + dir);
  3564. size_t bi2 = result.vertices.size();
  3565. result.vertices.push_back(b + dir);
  3566. result.indices.push_back(Vec3i32(ai, bi, ai2));
  3567. result.indices.push_back(Vec3i32(ai2, bi, bi2));
  3568. prev_vi = vi;
  3569. }
  3570. return result;
  3571. }
  3572. //void priv::store(const SurfaceCuts &cut, const std::string &dir) {
  3573. // prepare_dir(dir);
  3574. // for (const auto &c : cut) {
  3575. // size_t index = &c - &cut.front();
  3576. // std::string file = dir + "cut" + std::to_string(index) + ".obj";
  3577. // its_write_obj(c, file.c_str());
  3578. // for (const auto& contour : c.contours) {
  3579. // size_t c_index = &contour - &c.contours.front();
  3580. // std::string c_file = dir + "cut" + std::to_string(index) +
  3581. // "contour" + std::to_string(c_index) + ".obj";
  3582. // indexed_triangle_set c_its = create_contour_its(c, contour);
  3583. // its_write_obj(c_its, c_file.c_str());
  3584. // }
  3585. // }
  3586. //}
  3587. void priv::store(const SurfaceCut &cut, const std::string &file, const std::string &contour_dir) {
  3588. prepare_dir(contour_dir);
  3589. its_write_obj(cut, file.c_str());
  3590. for (const auto& contour : cut.contours) {
  3591. size_t c_index = &contour - &cut.contours.front();
  3592. std::string c_file = contour_dir + std::to_string(c_index) + ".obj";
  3593. indexed_triangle_set c_its = create_contour_its(cut, contour);
  3594. its_write_obj(c_its, c_file.c_str());
  3595. }
  3596. }
  3597. void priv::store(const std::vector<indexed_triangle_set> &models,
  3598. const std::string &obj_filename)
  3599. {
  3600. indexed_triangle_set merged_model;
  3601. for (const indexed_triangle_set &model : models)
  3602. its_merge(merged_model, model);
  3603. its_write_obj(merged_model, obj_filename.c_str());
  3604. }
  3605. void priv::store(const std::vector<priv::CutMesh> &models,
  3606. const std::string &dir)
  3607. {
  3608. prepare_dir(dir);
  3609. if (models.empty()) return;
  3610. if (models.size() == 1) {
  3611. CGAL::IO::write_OFF(dir + "model.off", models.front());
  3612. return;
  3613. }
  3614. size_t model_index = 0;
  3615. for (const priv::CutMesh& model : models) {
  3616. std::string filename = dir + "model" + std::to_string(model_index++) + ".off";
  3617. CGAL::IO::write_OFF(filename, model);
  3618. }
  3619. }
  3620. // store projection center
  3621. void priv::store(const Emboss::IProjection &projection,
  3622. const Point &point_to_project,
  3623. float projection_ratio,
  3624. const std::string &obj_filename)
  3625. {
  3626. auto [front, back] = projection.create_front_back(point_to_project);
  3627. Vec3d diff = back - front;
  3628. Vec3d pos = front + diff * projection_ratio;
  3629. priv::store(pos.cast<float>(), diff.normalized().cast<float>(),
  3630. DEBUG_OUTPUT_DIR + "projection_center.obj"); // only debug
  3631. }
  3632. #endif // DEBUG_OUTPUT_DIR
  3633. bool Slic3r::corefine_test(const std::string &model_path, const std::string &shape_path) {
  3634. priv::CutMesh model, shape;
  3635. if (!CGAL::IO::read_OFF(model_path, model)) return false;
  3636. if (!CGAL::IO::read_OFF(shape_path, shape)) return false;
  3637. CGAL::Polygon_mesh_processing::corefine(model, shape);
  3638. return true;
  3639. }