[{"data":1,"prerenderedAt":1987},["ShallowReactive",2],{"tutorial-constrained-delaunay-triangulation-in-cpp":3},{"id":4,"title":5,"author":6,"body":7,"date":1961,"description":1962,"extension":1963,"keywords":1964,"meta":1976,"navigation":151,"ogImage":1977,"path":1978,"published":151,"seo":1979,"stem":1980,"tags":1981,"__hash__":1986},"tutorials\u002Ftutorials\u002Fconstrained-delaunay-triangulation-in-cpp.md","Fast and Exact Constrained Delaunay Triangulation in C++","Žiga Sajovic",{"type":8,"value":9,"toc":1949},"minimark",[10,28,36,40,47,52,55,114,124,128,131,314,328,332,343,407,413,420,424,427,884,890,893,897,908,1151,1157,1163,1267,1271,1284,1384,1390,1400,1410,1443,1453,1457,1468,1597,1600,1604,1607,1693,1711,1714,1840,1852,1856,1885,1889,1892,1896,1912,1917,1923,1929,1941,1945],[11,12,13],"note",{},[14,15,16,17,22,23,27],"p",{},"Also available in ",[18,19,21],"a",{"href":20},"\u002Ftutorials\u002Fconstrained-delaunay-triangulation-in-python","Python"," and ",[18,24,26],{"href":25},"\u002Ftutorials\u002Fconstrained-delaunay-triangulation-in-javascript","JavaScript",".",[14,29,30,31,35],{},"A constrained Delaunay triangulation (CDT) triangulates a set of 2D points and keeps chosen edges in the result. It turns outlines into meshes, cuts holes, and holds feature lines. ",[32,33,34],"code",{},"trueform"," builds it on exact predicates. Constraints may cross; the crossings are resolved. It is header-only C++17. This page covers plain Delaunay, outlines with holes, crossing constraints and feature lines, region labels, quality refinement, and index maps.",[37,38],"article-cta",{":buttons":39},"[{\"label\":\"trueform\",\"to\":\"\u002Ftrueform\",\"icon\":\"i-lucide-cpu\",\"variant\":\"soft\",\"color\":\"neutral\"},{\"label\":\"GitHub\",\"to\":\"https:\u002F\u002Fgithub.com\u002Fpolydera\u002Ftrueform\",\"icon\":\"i-simple-icons-github\",\"variant\":\"soft\",\"color\":\"neutral\"},{\"label\":\"Documentation\",\"to\":\"https:\u002F\u002Ftrueform.polydera.com\u002Fcpp\u002Fmodules\u002Ftopology#triangulation\",\"variant\":\"soft\",\"color\":\"neutral\"}]",[14,41,42],{},[43,44],"img",{"alt":45,"src":46},"A constrained Delaunay triangulation of a wavy outline with three holes and a constraint curve","\u002Fimg\u002Ftutorials\u002Fconstrained-delaunay.png",[48,49,51],"h2",{"id":50},"install","Install",[14,53,54],{},"Header-only. It needs C++17 and oneTBB. Add it to your project via CMake FetchContent:",[56,57,62],"pre",{"className":58,"code":59,"language":60,"meta":61,"style":61},"language-cmake shiki shiki-themes github-dark","FetchContent_Declare(\n  trueform\n  GIT_REPOSITORY https:\u002F\u002Fgithub.com\u002Fpolydera\u002Ftrueform\n)\nFetchContent_MakeAvailable(trueform)\ntarget_link_libraries(my_app PRIVATE tf::trueform)\n","cmake","",[32,63,64,73,79,85,91,97],{"__ignoreMap":61},[65,66,69],"span",{"class":67,"line":68},"line",1,[65,70,72],{"class":71},"s95oV","FetchContent_Declare(\n",[65,74,76],{"class":67,"line":75},2,[65,77,78],{"class":71},"  trueform\n",[65,80,82],{"class":67,"line":81},3,[65,83,84],{"class":71},"  GIT_REPOSITORY https:\u002F\u002Fgithub.com\u002Fpolydera\u002Ftrueform\n",[65,86,88],{"class":67,"line":87},4,[65,89,90],{"class":71},")\n",[65,92,94],{"class":67,"line":93},5,[65,95,96],{"class":71},"FetchContent_MakeAvailable(trueform)\n",[65,98,100,104,107,111],{"class":67,"line":99},6,[65,101,103],{"class":102},"snl16","target_link_libraries",[65,105,106],{"class":71},"(my_app ",[65,108,110],{"class":109},"svObZ","PRIVATE",[65,112,113],{"class":71}," tf::trueform)\n",[14,115,116,117,123],{},"Also available via conan, nuget, and pip. See the ",[18,118,122],{"href":119,"rel":120},"https:\u002F\u002Ftrueform.polydera.com\u002Fcpp\u002Fgetting-started\u002Finstallation",[121],"nofollow","installation guide"," for all options.",[48,125,127],{"id":126},"loading-the-points","Loading the points",[14,129,130],{},"The input is a flat array of 2D coordinates and, optionally, a flat array of constraint edges that index into it. Wrap them as views. No copy is made:",[56,132,136],{"className":133,"code":134,"language":135,"meta":61,"style":61},"language-cpp shiki shiki-themes github-dark","#include \u003Ctrueform\u002Ftrueform.hpp>\n\nstd::vector\u003Cfloat> xy;  \u002F\u002F x0, y0, x1, y1, ...\nfor (int i = 0; i \u003C 1000; ++i) {\n  xy.push_back(tf::random(-1.f, 1.f));\n  xy.push_back(tf::random(-1.f, 1.f));\n}\nauto points = tf::make_points\u003C2>(xy);\n","cpp",[32,137,138,147,153,171,210,251,281,287],{"__ignoreMap":61},[65,139,140,143],{"class":67,"line":68},[65,141,142],{"class":102},"#include",[65,144,146],{"class":145},"sU2Wk"," \u003Ctrueform\u002Ftrueform.hpp>\n",[65,148,149],{"class":67,"line":75},[65,150,152],{"emptyLinePlaceholder":151},true,"\n",[65,154,155,158,161,164,167],{"class":67,"line":81},[65,156,157],{"class":109},"std",[65,159,160],{"class":71},"::vector",[65,162,163],{"class":102},"\u003Cfloat>",[65,165,166],{"class":71}," xy;",[65,168,170],{"class":169},"sAwPA","  \u002F\u002F x0, y0, x1, y1, ...\n",[65,172,173,176,179,182,185,188,192,195,198,201,204,207],{"class":67,"line":87},[65,174,175],{"class":102},"for",[65,177,178],{"class":71}," (",[65,180,181],{"class":102},"int",[65,183,184],{"class":71}," i ",[65,186,187],{"class":102},"=",[65,189,191],{"class":190},"sDLfK"," 0",[65,193,194],{"class":71},"; i ",[65,196,197],{"class":102},"\u003C",[65,199,200],{"class":190}," 1000",[65,202,203],{"class":71},"; ",[65,205,206],{"class":102},"++",[65,208,209],{"class":71},"i) {\n",[65,211,212,215,218,221,224,227,230,232,235,238,241,244,246,248],{"class":67,"line":93},[65,213,214],{"class":71},"  xy.",[65,216,217],{"class":109},"push_back",[65,219,220],{"class":71},"(",[65,222,223],{"class":109},"tf",[65,225,226],{"class":71},"::",[65,228,229],{"class":109},"random",[65,231,220],{"class":71},[65,233,234],{"class":102},"-",[65,236,237],{"class":190},"1.",[65,239,240],{"class":102},"f",[65,242,243],{"class":71},", ",[65,245,237],{"class":190},[65,247,240],{"class":102},[65,249,250],{"class":71},"));\n",[65,252,253,255,257,259,261,263,265,267,269,271,273,275,277,279],{"class":67,"line":99},[65,254,214],{"class":71},[65,256,217],{"class":109},[65,258,220],{"class":71},[65,260,223],{"class":109},[65,262,226],{"class":71},[65,264,229],{"class":109},[65,266,220],{"class":71},[65,268,234],{"class":102},[65,270,237],{"class":190},[65,272,240],{"class":102},[65,274,243],{"class":71},[65,276,237],{"class":190},[65,278,240],{"class":102},[65,280,250],{"class":71},[65,282,284],{"class":67,"line":283},7,[65,285,286],{"class":71},"}\n",[65,288,290,293,296,298,301,303,306,308,311],{"class":67,"line":289},8,[65,291,292],{"class":102},"auto",[65,294,295],{"class":71}," points ",[65,297,187],{"class":102},[65,299,300],{"class":109}," tf",[65,302,226],{"class":71},[65,304,305],{"class":109},"make_points",[65,307,197],{"class":71},[65,309,310],{"class":190},"2",[65,312,313],{"class":71},">(xy);\n",[14,315,316,317,327],{},"If your points come from a 3D file, read it with ",[18,318,320,323,324],{"href":319},"\u002Ftutorials\u002Ffast-stl-and-obj-reading-in-cpp",[32,321,322],{},"tf::read_stl"," or ",[32,325,326],{},"tf::read_obj"," and project to two coordinates first.",[48,329,331],{"id":330},"delaunay-triangulation","Delaunay triangulation",[14,333,334,335,342],{},"Without edges, ",[18,336,339],{"href":337,"rel":338},"https:\u002F\u002Ftrueform.polydera.com\u002Fcpp\u002Fmodules\u002Ftopology#constrained-delaunay-make_cdt",[121],[32,340,341],{},"tf::make_cdt"," returns the Delaunay triangulation of the convex hull:",[56,344,346],{"className":133,"code":345,"language":135,"meta":61,"style":61},"auto tri = tf::make_cdt(points);\nauto faces = tri.faces();          \u002F\u002F triangles, int indices\nauto out_points = tri.points();    \u002F\u002F 2D points, float\n",[32,347,348,367,388],{"__ignoreMap":61},[65,349,350,352,355,357,359,361,364],{"class":67,"line":68},[65,351,292],{"class":102},[65,353,354],{"class":71}," tri ",[65,356,187],{"class":102},[65,358,300],{"class":109},[65,360,226],{"class":71},[65,362,363],{"class":109},"make_cdt",[65,365,366],{"class":71},"(points);\n",[65,368,369,371,374,376,379,382,385],{"class":67,"line":75},[65,370,292],{"class":102},[65,372,373],{"class":71}," faces ",[65,375,187],{"class":102},[65,377,378],{"class":71}," tri.",[65,380,381],{"class":109},"faces",[65,383,384],{"class":71},"();",[65,386,387],{"class":169},"          \u002F\u002F triangles, int indices\n",[65,389,390,392,395,397,399,402,404],{"class":67,"line":81},[65,391,292],{"class":102},[65,393,394],{"class":71}," out_points ",[65,396,187],{"class":102},[65,398,378],{"class":71},[65,400,401],{"class":109},"points",[65,403,384],{"class":71},[65,405,406],{"class":169},"    \u002F\u002F 2D points, float\n",[14,408,409],{},[43,410],{"alt":411,"src":412},"Delaunay triangulation: 1,000 random points triangulated over their convex hull","\u002Fimg\u002Ftutorials\u002Fsteps\u002Fconstrained-delaunay-triangulation-1-delaunay.png",[14,414,415,416,419],{},"The result is a ",[32,417,418],{},"tf::polygons_buffer",". The faces index its own points, not your input array. The order can differ. To map between them, ask for the index map (below).",[48,421,423],{"id":422},"an-outline-with-a-hole","An outline with a hole",[14,425,426],{},"Pass the outline and the hole as constraint edges. By default every edge is a wall. Triangles inside an odd number of walls are kept, so a ring of walls inside another ring is a hole:",[56,428,430],{"className":133,"code":429,"language":135,"meta":61,"style":61},"std::vector\u003Cfloat> xy = {\n    -1, -1,  1, -1,  1,  1,  -1,  1,                        \u002F\u002F outer square\n    -0.4f, -0.4f, 0.4f, -0.4f, 0.4f, 0.4f, -0.4f, 0.4f};    \u002F\u002F inner square\nfor (int i = 0; i \u003C 500; ++i) {                             \u002F\u002F interior points\n  xy.push_back(tf::random(-1.f, 1.f));\n  xy.push_back(tf::random(-1.f, 1.f));\n}\nstd::vector\u003Cint> ring = {0, 1, 1, 2, 2, 3, 3, 0,   \u002F\u002F outer square\n                         4, 5, 5, 6, 6, 7, 7, 4};  \u002F\u002F inner square: the hole\n\nauto points = tf::make_points\u003C2>(xy);\nauto edges = tf::make_edges(tf::make_range(ring));\nauto polys = tf::make_cdt(points, edges);\n\nfloat area = 0;\nfor (auto t : polys.polygons()) area += tf::area(t);\n\u002F\u002F area == 3.36 = 4 - 0.64\n",[32,431,432,448,497,560,591,621,651,655,709,752,757,778,807,826,831,847,878],{"__ignoreMap":61},[65,433,434,436,438,440,443,445],{"class":67,"line":68},[65,435,157],{"class":109},[65,437,160],{"class":71},[65,439,163],{"class":102},[65,441,442],{"class":71}," xy ",[65,444,187],{"class":102},[65,446,447],{"class":71}," {\n",[65,449,450,453,456,458,460,462,465,467,469,471,473,475,477,479,481,483,485,487,489,491,494],{"class":67,"line":75},[65,451,452],{"class":102},"    -",[65,454,455],{"class":190},"1",[65,457,243],{"class":71},[65,459,234],{"class":102},[65,461,455],{"class":190},[65,463,464],{"class":71},",  ",[65,466,455],{"class":190},[65,468,243],{"class":71},[65,470,234],{"class":102},[65,472,455],{"class":190},[65,474,464],{"class":71},[65,476,455],{"class":190},[65,478,464],{"class":71},[65,480,455],{"class":190},[65,482,464],{"class":71},[65,484,234],{"class":102},[65,486,455],{"class":190},[65,488,464],{"class":71},[65,490,455],{"class":190},[65,492,493],{"class":71},",",[65,495,496],{"class":169},"                        \u002F\u002F outer square\n",[65,498,499,501,504,506,508,510,512,514,516,518,520,522,524,526,528,530,532,534,536,538,540,542,544,546,548,550,552,554,557],{"class":67,"line":81},[65,500,452],{"class":102},[65,502,503],{"class":190},"0.4",[65,505,240],{"class":102},[65,507,243],{"class":71},[65,509,234],{"class":102},[65,511,503],{"class":190},[65,513,240],{"class":102},[65,515,243],{"class":71},[65,517,503],{"class":190},[65,519,240],{"class":102},[65,521,243],{"class":71},[65,523,234],{"class":102},[65,525,503],{"class":190},[65,527,240],{"class":102},[65,529,243],{"class":71},[65,531,503],{"class":190},[65,533,240],{"class":102},[65,535,243],{"class":71},[65,537,503],{"class":190},[65,539,240],{"class":102},[65,541,243],{"class":71},[65,543,234],{"class":102},[65,545,503],{"class":190},[65,547,240],{"class":102},[65,549,243],{"class":71},[65,551,503],{"class":190},[65,553,240],{"class":102},[65,555,556],{"class":71},"};",[65,558,559],{"class":169},"    \u002F\u002F inner square\n",[65,561,562,564,566,568,570,572,574,576,578,581,583,585,588],{"class":67,"line":87},[65,563,175],{"class":102},[65,565,178],{"class":71},[65,567,181],{"class":102},[65,569,184],{"class":71},[65,571,187],{"class":102},[65,573,191],{"class":190},[65,575,194],{"class":71},[65,577,197],{"class":102},[65,579,580],{"class":190}," 500",[65,582,203],{"class":71},[65,584,206],{"class":102},[65,586,587],{"class":71},"i) {",[65,589,590],{"class":169},"                             \u002F\u002F interior points\n",[65,592,593,595,597,599,601,603,605,607,609,611,613,615,617,619],{"class":67,"line":93},[65,594,214],{"class":71},[65,596,217],{"class":109},[65,598,220],{"class":71},[65,600,223],{"class":109},[65,602,226],{"class":71},[65,604,229],{"class":109},[65,606,220],{"class":71},[65,608,234],{"class":102},[65,610,237],{"class":190},[65,612,240],{"class":102},[65,614,243],{"class":71},[65,616,237],{"class":190},[65,618,240],{"class":102},[65,620,250],{"class":71},[65,622,623,625,627,629,631,633,635,637,639,641,643,645,647,649],{"class":67,"line":99},[65,624,214],{"class":71},[65,626,217],{"class":109},[65,628,220],{"class":71},[65,630,223],{"class":109},[65,632,226],{"class":71},[65,634,229],{"class":109},[65,636,220],{"class":71},[65,638,234],{"class":102},[65,640,237],{"class":190},[65,642,240],{"class":102},[65,644,243],{"class":71},[65,646,237],{"class":190},[65,648,240],{"class":102},[65,650,250],{"class":71},[65,652,653],{"class":67,"line":283},[65,654,286],{"class":71},[65,656,657,659,661,664,667,669,672,675,677,679,681,683,685,687,689,691,693,696,698,700,702,704,706],{"class":67,"line":289},[65,658,157],{"class":109},[65,660,160],{"class":71},[65,662,663],{"class":102},"\u003Cint>",[65,665,666],{"class":71}," ring ",[65,668,187],{"class":102},[65,670,671],{"class":71}," {",[65,673,674],{"class":190},"0",[65,676,243],{"class":71},[65,678,455],{"class":190},[65,680,243],{"class":71},[65,682,455],{"class":190},[65,684,243],{"class":71},[65,686,310],{"class":190},[65,688,243],{"class":71},[65,690,310],{"class":190},[65,692,243],{"class":71},[65,694,695],{"class":190},"3",[65,697,243],{"class":71},[65,699,695],{"class":190},[65,701,243],{"class":71},[65,703,674],{"class":190},[65,705,493],{"class":71},[65,707,708],{"class":169},"   \u002F\u002F outer square\n",[65,710,712,715,717,720,722,724,726,729,731,733,735,738,740,742,744,747,749],{"class":67,"line":711},9,[65,713,714],{"class":190},"                         4",[65,716,243],{"class":71},[65,718,719],{"class":190},"5",[65,721,243],{"class":71},[65,723,719],{"class":190},[65,725,243],{"class":71},[65,727,728],{"class":190},"6",[65,730,243],{"class":71},[65,732,728],{"class":190},[65,734,243],{"class":71},[65,736,737],{"class":190},"7",[65,739,243],{"class":71},[65,741,737],{"class":190},[65,743,243],{"class":71},[65,745,746],{"class":190},"4",[65,748,556],{"class":71},[65,750,751],{"class":169},"  \u002F\u002F inner square: the hole\n",[65,753,755],{"class":67,"line":754},10,[65,756,152],{"emptyLinePlaceholder":151},[65,758,760,762,764,766,768,770,772,774,776],{"class":67,"line":759},11,[65,761,292],{"class":102},[65,763,295],{"class":71},[65,765,187],{"class":102},[65,767,300],{"class":109},[65,769,226],{"class":71},[65,771,305],{"class":109},[65,773,197],{"class":71},[65,775,310],{"class":190},[65,777,313],{"class":71},[65,779,781,783,786,788,790,792,795,797,799,801,804],{"class":67,"line":780},12,[65,782,292],{"class":102},[65,784,785],{"class":71}," edges ",[65,787,187],{"class":102},[65,789,300],{"class":109},[65,791,226],{"class":71},[65,793,794],{"class":109},"make_edges",[65,796,220],{"class":71},[65,798,223],{"class":109},[65,800,226],{"class":71},[65,802,803],{"class":109},"make_range",[65,805,806],{"class":71},"(ring));\n",[65,808,810,812,815,817,819,821,823],{"class":67,"line":809},13,[65,811,292],{"class":102},[65,813,814],{"class":71}," polys ",[65,816,187],{"class":102},[65,818,300],{"class":109},[65,820,226],{"class":71},[65,822,363],{"class":109},[65,824,825],{"class":71},"(points, edges);\n",[65,827,829],{"class":67,"line":828},14,[65,830,152],{"emptyLinePlaceholder":151},[65,832,834,837,840,842,844],{"class":67,"line":833},15,[65,835,836],{"class":102},"float",[65,838,839],{"class":71}," area ",[65,841,187],{"class":102},[65,843,191],{"class":190},[65,845,846],{"class":71},";\n",[65,848,850,852,854,856,859,862,865,868,870,872,875],{"class":67,"line":849},16,[65,851,175],{"class":102},[65,853,178],{"class":71},[65,855,292],{"class":102},[65,857,858],{"class":71}," t : polys.",[65,860,861],{"class":109},"polygons",[65,863,864],{"class":71},"()) area ",[65,866,867],{"class":102},"+=",[65,869,300],{"class":109},[65,871,226],{"class":71},[65,873,874],{"class":109},"area",[65,876,877],{"class":71},"(t);\n",[65,879,881],{"class":67,"line":880},17,[65,882,883],{"class":169},"\u002F\u002F area == 3.36 = 4 - 0.64\n",[14,885,886],{},[43,887],{"alt":888,"src":889},"An outline with a hole: the outer square and the inner square as constraint walls; the triangles inside the hole are dropped","\u002Fimg\u002Ftutorials\u002Fsteps\u002Fconstrained-delaunay-triangulation-2-hole.png",[14,891,892],{},"The points inside the hole are dropped. The area is the square minus the hole.",[48,894,896],{"id":895},"crossing-constraints-and-feature-lines","Crossing constraints and feature lines",[14,898,899,900,903,904,907],{},"To keep a constraint as an edge without making it a wall, pass a per-edge mask. ",[32,901,902],{},"true"," marks a wall, ",[32,905,906],{},"false"," a feature line that splits nothing. Constraints may cross. The crossing point is added to the output:",[56,909,911],{"className":133,"code":910,"language":135,"meta":61,"style":61},"std::vector\u003Cfloat> square = {-1, -1, 1, -1, 1, 1, -1, 1};\nstd::vector\u003Cint> lines = {0, 1, 1, 2, 2, 3, 3, 0,  \u002F\u002F outline\n                          0, 2, 1, 3};             \u002F\u002F diagonals\nstd::vector\u003Cbool> is_boundary = {true, true, true, true, false, false};\n\nauto sq = tf::make_cdt(tf::make_points\u003C2>(square),\n                       tf::make_edges(tf::make_range(lines)),\n                       tf::make_range(is_boundary));\n\u002F\u002F 4 triangles, 5 points: the corners and the crossing at (0, 0)\n",[32,912,913,969,1019,1041,1081,1085,1115,1135,1146],{"__ignoreMap":61},[65,914,915,917,919,921,924,926,928,930,932,934,936,938,940,942,944,946,948,950,952,954,956,958,960,962,964,966],{"class":67,"line":68},[65,916,157],{"class":109},[65,918,160],{"class":71},[65,920,163],{"class":102},[65,922,923],{"class":71}," square ",[65,925,187],{"class":102},[65,927,671],{"class":71},[65,929,234],{"class":102},[65,931,455],{"class":190},[65,933,243],{"class":71},[65,935,234],{"class":102},[65,937,455],{"class":190},[65,939,243],{"class":71},[65,941,455],{"class":190},[65,943,243],{"class":71},[65,945,234],{"class":102},[65,947,455],{"class":190},[65,949,243],{"class":71},[65,951,455],{"class":190},[65,953,243],{"class":71},[65,955,455],{"class":190},[65,957,243],{"class":71},[65,959,234],{"class":102},[65,961,455],{"class":190},[65,963,243],{"class":71},[65,965,455],{"class":190},[65,967,968],{"class":71},"};\n",[65,970,971,973,975,977,980,982,984,986,988,990,992,994,996,998,1000,1002,1004,1006,1008,1010,1012,1014,1016],{"class":67,"line":75},[65,972,157],{"class":109},[65,974,160],{"class":71},[65,976,663],{"class":102},[65,978,979],{"class":71}," lines ",[65,981,187],{"class":102},[65,983,671],{"class":71},[65,985,674],{"class":190},[65,987,243],{"class":71},[65,989,455],{"class":190},[65,991,243],{"class":71},[65,993,455],{"class":190},[65,995,243],{"class":71},[65,997,310],{"class":190},[65,999,243],{"class":71},[65,1001,310],{"class":190},[65,1003,243],{"class":71},[65,1005,695],{"class":190},[65,1007,243],{"class":71},[65,1009,695],{"class":190},[65,1011,243],{"class":71},[65,1013,674],{"class":190},[65,1015,493],{"class":71},[65,1017,1018],{"class":169},"  \u002F\u002F outline\n",[65,1020,1021,1024,1026,1028,1030,1032,1034,1036,1038],{"class":67,"line":81},[65,1022,1023],{"class":190},"                          0",[65,1025,243],{"class":71},[65,1027,310],{"class":190},[65,1029,243],{"class":71},[65,1031,455],{"class":190},[65,1033,243],{"class":71},[65,1035,695],{"class":190},[65,1037,556],{"class":71},[65,1039,1040],{"class":169},"             \u002F\u002F diagonals\n",[65,1042,1043,1045,1047,1050,1053,1055,1057,1059,1061,1063,1065,1067,1069,1071,1073,1075,1077,1079],{"class":67,"line":87},[65,1044,157],{"class":109},[65,1046,160],{"class":71},[65,1048,1049],{"class":102},"\u003Cbool>",[65,1051,1052],{"class":71}," is_boundary ",[65,1054,187],{"class":102},[65,1056,671],{"class":71},[65,1058,902],{"class":190},[65,1060,243],{"class":71},[65,1062,902],{"class":190},[65,1064,243],{"class":71},[65,1066,902],{"class":190},[65,1068,243],{"class":71},[65,1070,902],{"class":190},[65,1072,243],{"class":71},[65,1074,906],{"class":190},[65,1076,243],{"class":71},[65,1078,906],{"class":190},[65,1080,968],{"class":71},[65,1082,1083],{"class":67,"line":93},[65,1084,152],{"emptyLinePlaceholder":151},[65,1086,1087,1089,1092,1094,1096,1098,1100,1102,1104,1106,1108,1110,1112],{"class":67,"line":99},[65,1088,292],{"class":102},[65,1090,1091],{"class":71}," sq ",[65,1093,187],{"class":102},[65,1095,300],{"class":109},[65,1097,226],{"class":71},[65,1099,363],{"class":109},[65,1101,220],{"class":71},[65,1103,223],{"class":109},[65,1105,226],{"class":71},[65,1107,305],{"class":109},[65,1109,197],{"class":71},[65,1111,310],{"class":190},[65,1113,1114],{"class":71},">(square),\n",[65,1116,1117,1120,1122,1124,1126,1128,1130,1132],{"class":67,"line":283},[65,1118,1119],{"class":109},"                       tf",[65,1121,226],{"class":71},[65,1123,794],{"class":109},[65,1125,220],{"class":71},[65,1127,223],{"class":109},[65,1129,226],{"class":71},[65,1131,803],{"class":109},[65,1133,1134],{"class":71},"(lines)),\n",[65,1136,1137,1139,1141,1143],{"class":67,"line":289},[65,1138,1119],{"class":109},[65,1140,226],{"class":71},[65,1142,803],{"class":109},[65,1144,1145],{"class":71},"(is_boundary));\n",[65,1147,1148],{"class":67,"line":711},[65,1149,1150],{"class":169},"\u002F\u002F 4 triangles, 5 points: the corners and the crossing at (0, 0)\n",[14,1152,1153],{},[43,1154],{"alt":1155,"src":1156},"Crossing constraints and feature lines: the outline as walls and the two diagonals as feature lines; the square stays solid in four quarter triangles and the crossing point is added at the centre","\u002Fimg\u002Ftutorials\u002Fsteps\u002Fconstrained-delaunay-triangulation-3-feature-lines.png",[14,1158,1159,1160,1162],{},"If crossings are not allowed in your data, pass ",[32,1161,906],{}," as the last argument. The call then refuses on a crossing and returns an empty buffer. The empty result is the answer to \"do these constraints cross\", not an error:",[56,1164,1166],{"className":133,"code":1165,"language":135,"meta":61,"style":61},"auto strict = tf::make_cdt(tf::make_points\u003C2>(square),\n                           tf::make_edges(tf::make_range(lines)),\n                           tf::make_range(is_boundary), false);\nbool crossed = strict.polygons().size() == 0;  \u002F\u002F true: the diagonals cross\n",[32,1167,1168,1197,1216,1232],{"__ignoreMap":61},[65,1169,1170,1172,1175,1177,1179,1181,1183,1185,1187,1189,1191,1193,1195],{"class":67,"line":68},[65,1171,292],{"class":102},[65,1173,1174],{"class":71}," strict ",[65,1176,187],{"class":102},[65,1178,300],{"class":109},[65,1180,226],{"class":71},[65,1182,363],{"class":109},[65,1184,220],{"class":71},[65,1186,223],{"class":109},[65,1188,226],{"class":71},[65,1190,305],{"class":109},[65,1192,197],{"class":71},[65,1194,310],{"class":190},[65,1196,1114],{"class":71},[65,1198,1199,1202,1204,1206,1208,1210,1212,1214],{"class":67,"line":75},[65,1200,1201],{"class":109},"                           tf",[65,1203,226],{"class":71},[65,1205,794],{"class":109},[65,1207,220],{"class":71},[65,1209,223],{"class":109},[65,1211,226],{"class":71},[65,1213,803],{"class":109},[65,1215,1134],{"class":71},[65,1217,1218,1220,1222,1224,1227,1229],{"class":67,"line":81},[65,1219,1201],{"class":109},[65,1221,226],{"class":71},[65,1223,803],{"class":109},[65,1225,1226],{"class":71},"(is_boundary), ",[65,1228,906],{"class":190},[65,1230,1231],{"class":71},");\n",[65,1233,1234,1237,1240,1242,1245,1247,1250,1253,1256,1259,1261,1264],{"class":67,"line":87},[65,1235,1236],{"class":102},"bool",[65,1238,1239],{"class":71}," crossed ",[65,1241,187],{"class":102},[65,1243,1244],{"class":71}," strict.",[65,1246,861],{"class":109},[65,1248,1249],{"class":71},"().",[65,1251,1252],{"class":109},"size",[65,1254,1255],{"class":71},"() ",[65,1257,1258],{"class":102},"==",[65,1260,191],{"class":190},[65,1262,1263],{"class":71},";",[65,1265,1266],{"class":169},"  \u002F\u002F true: the diagonals cross\n",[48,1268,1270],{"id":1269},"region-labels","Region labels",[14,1272,1273,1275,1276,1283],{},[32,1274,341],{}," keeps only the interior. For every triangle with a label, use the ",[18,1277,1280],{"href":1278,"rel":1279},"https:\u002F\u002Ftrueform.polydera.com\u002Fcpp\u002Fmodules\u002Ftopology#constrained-delaunay-triangulator",[121],[32,1281,1282],{},"tf::constrained_delaunay_triangulator"," class:",[56,1285,1287],{"className":133,"code":1286,"language":135,"meta":61,"style":61},"tf::constrained_delaunay_triangulator\u003Cint, float> cdt;\ncdt.build(points, edges);\n\nauto all_faces = cdt.make_faces();     \u002F\u002F every triangle of the hull\nauto labels = cdt.region_labels();     \u002F\u002F per triangle, parity of walls crossed\nauto all_points = cdt.converted_points();  \u002F\u002F float, indexed by make_faces()\n\u002F\u002F labels[t] % 2 == 1: interior. Otherwise outside or in the hole.\n",[32,1288,1289,1307,1317,1321,1341,1360,1379],{"__ignoreMap":61},[65,1290,1291,1293,1296,1299,1301,1304],{"class":67,"line":68},[65,1292,223],{"class":109},[65,1294,1295],{"class":71},"::constrained_delaunay_triangulator",[65,1297,1298],{"class":102},"\u003Cint",[65,1300,243],{"class":71},[65,1302,1303],{"class":102},"float>",[65,1305,1306],{"class":71}," cdt;\n",[65,1308,1309,1312,1315],{"class":67,"line":75},[65,1310,1311],{"class":71},"cdt.",[65,1313,1314],{"class":109},"build",[65,1316,825],{"class":71},[65,1318,1319],{"class":67,"line":81},[65,1320,152],{"emptyLinePlaceholder":151},[65,1322,1323,1325,1328,1330,1333,1336,1338],{"class":67,"line":87},[65,1324,292],{"class":102},[65,1326,1327],{"class":71}," all_faces ",[65,1329,187],{"class":102},[65,1331,1332],{"class":71}," cdt.",[65,1334,1335],{"class":109},"make_faces",[65,1337,384],{"class":71},[65,1339,1340],{"class":169},"     \u002F\u002F every triangle of the hull\n",[65,1342,1343,1345,1348,1350,1352,1355,1357],{"class":67,"line":93},[65,1344,292],{"class":102},[65,1346,1347],{"class":71}," labels ",[65,1349,187],{"class":102},[65,1351,1332],{"class":71},[65,1353,1354],{"class":109},"region_labels",[65,1356,384],{"class":71},[65,1358,1359],{"class":169},"     \u002F\u002F per triangle, parity of walls crossed\n",[65,1361,1362,1364,1367,1369,1371,1374,1376],{"class":67,"line":99},[65,1363,292],{"class":102},[65,1365,1366],{"class":71}," all_points ",[65,1368,187],{"class":102},[65,1370,1332],{"class":71},[65,1372,1373],{"class":109},"converted_points",[65,1375,384],{"class":71},[65,1377,1378],{"class":169},"  \u002F\u002F float, indexed by make_faces()\n",[65,1380,1381],{"class":67,"line":283},[65,1382,1383],{"class":169},"\u002F\u002F labels[t] % 2 == 1: interior. Otherwise outside or in the hole.\n",[14,1385,1386],{},[43,1387],{"alt":1388,"src":1389},"Region labels: every triangle of the outline-with-hole triangulation, coloured by its nesting label, interior 1 and hole 0","\u002Fimg\u002Ftutorials\u002Fsteps\u002Fconstrained-delaunay-triangulation-4-labels.png",[14,1391,1392,1395,1396,1399],{},[32,1393,1394],{},"cdt.points()"," gives the same points on the exact integer lattice the predicates run on. Use ",[32,1397,1398],{},"converted_points()"," for coordinates in your own type.",[14,1401,1402,1403,1405,1406,1409],{},"For an id per region cut out by the walls, with ",[32,1404,674],{}," for the hull exterior, build in ",[32,1407,1408],{},"components"," mode. An island inside a hole then gets its own id:",[56,1411,1413],{"className":133,"code":1412,"language":135,"meta":61,"style":61},"cdt.build(points, edges, true, tf::cdt_region_mode::components);\n\u002F\u002F labels: 1 for the ring, 2 for the hole\n",[32,1414,1415,1438],{"__ignoreMap":61},[65,1416,1417,1419,1421,1424,1426,1428,1430,1432,1435],{"class":67,"line":68},[65,1418,1311],{"class":71},[65,1420,1314],{"class":109},[65,1422,1423],{"class":71},"(points, edges, ",[65,1425,902],{"class":190},[65,1427,243],{"class":71},[65,1429,223],{"class":109},[65,1431,226],{"class":71},[65,1433,1434],{"class":109},"cdt_region_mode",[65,1436,1437],{"class":71},"::components);\n",[65,1439,1440],{"class":67,"line":75},[65,1441,1442],{"class":169},"\u002F\u002F labels: 1 for the ring, 2 for the hole\n",[14,1444,1445,1446,1448,1449,1452],{},"The ",[32,1447,902],{}," is the ",[32,1450,1451],{},"split_constraints"," flag. It must be spelled out before the mode.",[48,1454,1456],{"id":1455},"quality-refinement","Quality refinement",[14,1458,1459,1460,1467],{},"A CDT keeps the triangles the points give it, slivers included. ",[18,1461,1464],{"href":1462,"rel":1463},"https:\u002F\u002Ftrueform.polydera.com\u002Fcpp\u002Fmodules\u002Ftopology#delaunay-refinement-cdt_refiner",[121],[32,1465,1466],{},"tf::cdt_refiner"," inserts points until every triangle meets a quality floor (Ruppert refinement):",[56,1469,1471],{"className":133,"code":1470,"language":135,"meta":61,"style":61},"tf::cdt_refine_config quality;\nquality.min_quality = 0.3f;        \u002F\u002F 1 is equilateral; values above 0.45 are clamped\nquality.split_encroached = true;   \u002F\u002F allow midpoint splits of constraints\n\ntf::cdt_refiner\u003Cint, float> refiner;\nif (refiner.build(points, edges, quality)) {\n  auto rfaces = refiner.make_faces();\n  auto rlabels = refiner.region_labels();\n  auto rpoints = refiner.converted_points();\n}\n",[32,1472,1473,1480,1497,1512,1516,1532,1545,1563,1578,1593],{"__ignoreMap":61},[65,1474,1475,1477],{"class":67,"line":68},[65,1476,223],{"class":109},[65,1478,1479],{"class":71},"::cdt_refine_config quality;\n",[65,1481,1482,1485,1487,1490,1492,1494],{"class":67,"line":75},[65,1483,1484],{"class":71},"quality.min_quality ",[65,1486,187],{"class":102},[65,1488,1489],{"class":190}," 0.3",[65,1491,240],{"class":102},[65,1493,1263],{"class":71},[65,1495,1496],{"class":169},"        \u002F\u002F 1 is equilateral; values above 0.45 are clamped\n",[65,1498,1499,1502,1504,1507,1509],{"class":67,"line":81},[65,1500,1501],{"class":71},"quality.split_encroached ",[65,1503,187],{"class":102},[65,1505,1506],{"class":190}," true",[65,1508,1263],{"class":71},[65,1510,1511],{"class":169},"   \u002F\u002F allow midpoint splits of constraints\n",[65,1513,1514],{"class":67,"line":87},[65,1515,152],{"emptyLinePlaceholder":151},[65,1517,1518,1520,1523,1525,1527,1529],{"class":67,"line":93},[65,1519,223],{"class":109},[65,1521,1522],{"class":71},"::cdt_refiner",[65,1524,1298],{"class":102},[65,1526,243],{"class":71},[65,1528,1303],{"class":102},[65,1530,1531],{"class":71}," refiner;\n",[65,1533,1534,1537,1540,1542],{"class":67,"line":99},[65,1535,1536],{"class":102},"if",[65,1538,1539],{"class":71}," (refiner.",[65,1541,1314],{"class":109},[65,1543,1544],{"class":71},"(points, edges, quality)) {\n",[65,1546,1547,1550,1553,1555,1558,1560],{"class":67,"line":283},[65,1548,1549],{"class":102},"  auto",[65,1551,1552],{"class":71}," rfaces ",[65,1554,187],{"class":102},[65,1556,1557],{"class":71}," refiner.",[65,1559,1335],{"class":109},[65,1561,1562],{"class":71},"();\n",[65,1564,1565,1567,1570,1572,1574,1576],{"class":67,"line":289},[65,1566,1549],{"class":102},[65,1568,1569],{"class":71}," rlabels ",[65,1571,187],{"class":102},[65,1573,1557],{"class":71},[65,1575,1354],{"class":109},[65,1577,1562],{"class":71},[65,1579,1580,1582,1585,1587,1589,1591],{"class":67,"line":711},[65,1581,1549],{"class":102},[65,1583,1584],{"class":71}," rpoints ",[65,1586,187],{"class":102},[65,1588,1557],{"class":71},[65,1590,1373],{"class":109},[65,1592,1562],{"class":71},[65,1594,1595],{"class":67,"line":754},[65,1596,286],{"class":71},[14,1598,1599],{},"Every split of a constraint is recorded as a parameter along the original input edge, so several triangulations that share a polyline can agree on the same split points.",[48,1601,1603],{"id":1602},"working-with-results","Working with results",[14,1605,1606],{},"To trace input points through the triangulation, ask for the index map:",[56,1608,1610],{"className":133,"code":1609,"language":135,"meta":61,"style":61},"auto [mapped, im] = tf::make_cdt(points, edges, tf::return_index_map);\nauto sentinel = im.f().size();\nint dropped = 0;\nfor (auto out_id : im.f()) dropped += out_id == sentinel;\n",[32,1611,1612,1634,1654,1667],{"__ignoreMap":61},[65,1613,1614,1616,1619,1621,1623,1625,1627,1629,1631],{"class":67,"line":68},[65,1615,292],{"class":102},[65,1617,1618],{"class":71}," [mapped, im] ",[65,1620,187],{"class":102},[65,1622,300],{"class":109},[65,1624,226],{"class":71},[65,1626,363],{"class":109},[65,1628,1423],{"class":71},[65,1630,223],{"class":109},[65,1632,1633],{"class":71},"::return_index_map);\n",[65,1635,1636,1638,1641,1643,1646,1648,1650,1652],{"class":67,"line":75},[65,1637,292],{"class":102},[65,1639,1640],{"class":71}," sentinel ",[65,1642,187],{"class":102},[65,1644,1645],{"class":71}," im.",[65,1647,240],{"class":109},[65,1649,1249],{"class":71},[65,1651,1252],{"class":109},[65,1653,1562],{"class":71},[65,1655,1656,1658,1661,1663,1665],{"class":67,"line":81},[65,1657,181],{"class":102},[65,1659,1660],{"class":71}," dropped ",[65,1662,187],{"class":102},[65,1664,191],{"class":190},[65,1666,846],{"class":71},[65,1668,1669,1671,1673,1675,1678,1680,1683,1685,1688,1690],{"class":67,"line":87},[65,1670,175],{"class":102},[65,1672,178],{"class":71},[65,1674,292],{"class":102},[65,1676,1677],{"class":71}," out_id : im.",[65,1679,240],{"class":109},[65,1681,1682],{"class":71},"()) dropped ",[65,1684,867],{"class":102},[65,1686,1687],{"class":71}," out_id ",[65,1689,1258],{"class":102},[65,1691,1692],{"class":71}," sentinel;\n",[14,1694,1695,1698,1699,1702,1703,1706,1707,1710],{},[32,1696,1697],{},"im.f()[i]"," is the output index of input point ",[32,1700,1701],{},"i",", or the sentinel if the point fell outside the kept region. ",[32,1704,1705],{},"im.kept_ids()[k]"," is the input index of output point ",[32,1708,1709],{},"k",", or the sentinel for a point made at a constraint crossing. Coincident input points are merged.",[14,1712,1713],{},"To lift a 2D triangulation into 3D, add a third coordinate and write it out:",[56,1715,1717],{"className":133,"code":1716,"language":135,"meta":61,"style":61},"auto p2 = polys.polygons();\ntf::points_buffer\u003Cfloat, 3> lifted;\nfor (auto p : p2.points()) lifted.emplace_back(p[0], p[1], 0.f);\nauto mesh3 = tf::make_polygons(p2.faces(), lifted.points());\ntf::write_obj(mesh3, \"cdt.obj\");\n",[32,1718,1719,1735,1755,1794,1823],{"__ignoreMap":61},[65,1720,1721,1723,1726,1728,1731,1733],{"class":67,"line":68},[65,1722,292],{"class":102},[65,1724,1725],{"class":71}," p2 ",[65,1727,187],{"class":102},[65,1729,1730],{"class":71}," polys.",[65,1732,861],{"class":109},[65,1734,1562],{"class":71},[65,1736,1737,1739,1742,1745,1747,1749,1752],{"class":67,"line":75},[65,1738,223],{"class":109},[65,1740,1741],{"class":71},"::points_buffer",[65,1743,1744],{"class":102},"\u003Cfloat",[65,1746,243],{"class":71},[65,1748,695],{"class":190},[65,1750,1751],{"class":102},">",[65,1753,1754],{"class":71}," lifted;\n",[65,1756,1757,1759,1761,1763,1766,1768,1771,1774,1777,1779,1782,1784,1787,1790,1792],{"class":67,"line":81},[65,1758,175],{"class":102},[65,1760,178],{"class":71},[65,1762,292],{"class":102},[65,1764,1765],{"class":71}," p : p2.",[65,1767,401],{"class":109},[65,1769,1770],{"class":71},"()) lifted.",[65,1772,1773],{"class":109},"emplace_back",[65,1775,1776],{"class":71},"(p[",[65,1778,674],{"class":190},[65,1780,1781],{"class":71},"], p[",[65,1783,455],{"class":190},[65,1785,1786],{"class":71},"], ",[65,1788,1789],{"class":190},"0.",[65,1791,240],{"class":102},[65,1793,1231],{"class":71},[65,1795,1796,1798,1801,1803,1805,1807,1810,1813,1815,1818,1820],{"class":67,"line":87},[65,1797,292],{"class":102},[65,1799,1800],{"class":71}," mesh3 ",[65,1802,187],{"class":102},[65,1804,300],{"class":109},[65,1806,226],{"class":71},[65,1808,1809],{"class":109},"make_polygons",[65,1811,1812],{"class":71},"(p2.",[65,1814,381],{"class":109},[65,1816,1817],{"class":71},"(), lifted.",[65,1819,401],{"class":109},[65,1821,1822],{"class":71},"());\n",[65,1824,1825,1827,1829,1832,1835,1838],{"class":67,"line":93},[65,1826,223],{"class":109},[65,1828,226],{"class":71},[65,1830,1831],{"class":109},"write_obj",[65,1833,1834],{"class":71},"(mesh3, ",[65,1836,1837],{"class":145},"\"cdt.obj\"",[65,1839,1231],{"class":71},[14,1841,1842,1843,1847,1848,27],{},"The same CDT triangulates the cut surfaces in ",[18,1844,1846],{"href":1845},"\u002Ftutorials\u002Ffast-mesh-booleans-in-cpp","mesh booleans",". For slicing a 3D mesh into 2D outlines, see ",[18,1849,1851],{"href":1850},"\u002Ftutorials\u002Fmesh-slicing-and-cross-sections-in-cpp","mesh slicing and cross-sections",[48,1853,1855],{"id":1854},"related-tutorials","Related tutorials",[1857,1858,1859,1866,1872,1879],"ul",{},[1860,1861,1862,1865],"li",{},[18,1863,1864],{"href":1850},"Fast and Robust Mesh Slicing and Cross-Sections in C++"," — plane sections, layers and slabs of a mesh.",[1860,1867,1868,1871],{},[18,1869,1870],{"href":1845},"Fast Mesh Booleans in C++"," — exact union, intersection and difference of two meshes.",[1860,1873,1874,1878],{},[18,1875,1877],{"href":1876},"\u002Ftutorials\u002Fisocontours-on-a-triangle-mesh-in-cpp","Fast and Robust Isocontours on a Triangle Mesh in C++"," — curves and bands of equal value on a scalar field.",[1860,1880,1881,1884],{},[18,1882,1883],{"href":319},"Fast STL and OBJ Reading in C++"," — load and write meshes, the first step of every pipeline.",[48,1886,1888],{"id":1887},"performance","Performance",[14,1890,1891],{},"Arrays in, triangle arrays out, at 1M random points, in C++. Region labels are excluded for every library. Every row is validated.",[1893,1894],"headline-numbers",{":items":1895},"[{\"value\":\"26.0ms\",\"label\":\"Delaunay\",\"detail\":\"1M points\"},{\"value\":\"62.3ms\",\"label\":\"Constrained Delaunay\",\"detail\":\"1M points, one constraint per ~10 points\"},{\"value\":\"7.6×\",\"label\":\"Faster CDT than Triangle\",\"detail\":\"471.9 ms\"}]",[14,1897,1898,1903,1904,1903,1908],{},[65,1899,1902],{"className":1900},[1901],"tag-pill","Apple M4 Max"," ",[65,1905,1907],{"className":1906},[1901],"16 threads",[65,1909,1911],{"className":1910},[1901],"Clang -O3",[1913,1914],"data-table",{":headers":1915,":highlight":674,":rows":1916},"[\"Library\",\"Delaunay, 1M points\",\"Constrained, 1M points\"]","[[\"trueform\",\"26.0 ms\",\"62.3 ms\"],[\"Delaunay32\",\"35.7 ms\",\"no constraints\"],[\"Triangle\",\"445.1 ms\",\"471.9 ms\"],[\"CGAL\",\"453.1 ms\",\"526.5 ms\"],[\"Geogram\",\"723.9 ms\",\"733.9 ms\"],[\"artem-ogre\u002FCDT\",\"726.7 ms\",\"762.7 ms\"],[\"detria\",\"742.9 ms\",\"791.3 ms\"]]",[14,1918,1919,1920,1922],{},"Of the seven libraries measured, ",[32,1921,34],{}," is the fastest on both tasks at 1M points. Delaunay32 is 1.4× slower on plain Delaunay and has no constrained mode. The constrained case uses non-crossing constraints, one per about ten points. trueform runs on float32 coordinates with exact int32 predicates. The other libraries use their own native kernels.",[14,1924,1925],{},[43,1926],{"alt":1927,"src":1928},"Benchmark chart: trueform against other C++ libraries, cdt","\u002Fimg\u002Falgorithms\u002Fbenchmark-cdt-cpp-2026.png",[14,1930,1931,1935,1936],{},[18,1932,1934],{"href":1933},"\u002Ftrueform\u002Fbenchmarks","Full benchmarks"," · ",[18,1937,1940],{"href":1938,"rel":1939},"https:\u002F\u002Ftrueform.polydera.com\u002Fcpp\u002Fmodules\u002Ftopology#triangulation",[121],"Documentation",[1942,1943],"cite-as",{"author":1944,"title":5},"Sajovic, {\\v{Z}}iga",[1946,1947,1948],"style",{},"html pre.shiki code .s95oV, html code.shiki .s95oV{--shiki-default:#E1E4E8}html pre.shiki code .snl16, html code.shiki .snl16{--shiki-default:#F97583}html pre.shiki code .svObZ, html code.shiki .svObZ{--shiki-default:#B392F0}html .default .shiki span {color: var(--shiki-default);background: var(--shiki-default-bg);font-style: var(--shiki-default-font-style);font-weight: var(--shiki-default-font-weight);text-decoration: var(--shiki-default-text-decoration);}html .shiki span {color: var(--shiki-default);background: var(--shiki-default-bg);font-style: var(--shiki-default-font-style);font-weight: var(--shiki-default-font-weight);text-decoration: var(--shiki-default-text-decoration);}html pre.shiki code .sU2Wk, html code.shiki .sU2Wk{--shiki-default:#9ECBFF}html pre.shiki code .sAwPA, html code.shiki .sAwPA{--shiki-default:#6A737D}html pre.shiki code .sDLfK, html code.shiki .sDLfK{--shiki-default:#79B8FF}",{"title":61,"searchDepth":75,"depth":75,"links":1950},[1951,1952,1953,1954,1955,1956,1957,1958,1959,1960],{"id":50,"depth":75,"text":51},{"id":126,"depth":75,"text":127},{"id":330,"depth":75,"text":331},{"id":422,"depth":75,"text":423},{"id":895,"depth":75,"text":896},{"id":1269,"depth":75,"text":1270},{"id":1455,"depth":75,"text":1456},{"id":1602,"depth":75,"text":1603},{"id":1854,"depth":75,"text":1855},{"id":1887,"depth":75,"text":1888},"2026-10-31","Triangulate 2D points with constraint edges in C++. Keep outlines and holes, resolve crossing constraints, label regions, refine to a quality floor. Delaunay of 1M points in 26.0 ms, measured against Triangle, CGAL, Geogram and others.","md",[1965,1966,1967,1968,1969,1970,1971,1972,1973,1974,1975],"constrained Delaunay triangulation C++","CDT C++","Delaunay triangulation C++","triangulate polygon with holes C++","2D triangulation library C++","Triangle library alternative","CGAL Constrained_Delaunay_triangulation_2 alternative","Geogram Delaunay alternative","artem-ogre CDT alternative","detria alternative","Ruppert refinement C++",{},"\u002Fimg\u002Fog\u002Ftutorials\u002Fconstrained-delaunay-triangulation-in-cpp.png","\u002Ftutorials\u002Fconstrained-delaunay-triangulation-in-cpp",{"title":5,"description":1962},"tutorials\u002Fconstrained-delaunay-triangulation-in-cpp",[1982,1983,1984,1985],"c++","delaunay","triangulation","tutorial","QL5q4rxqZuQBawlXq8NWW8rY7wS4cVIbfKKD7OtOqAs",1793517811389]