Merge branch 'master' of https://github.com/Prusa-Development/PrusaSlicerPrivate into et_picking_parts_fix

This commit is contained in:
enricoturri1966 2023-03-30 13:08:13 +02:00
commit 2e0d21d2e2
41 changed files with 3251 additions and 458 deletions

View File

@ -1,4 +1,5 @@
min_slic3r_version = 2.6.0-alpha5 min_slic3r_version = 2.6.0-alpha5
1.9.0-alpha1 Added profiles for Original Prusa MK4.
1.9.0-alpha0 Updated output filename format. 1.9.0-alpha0 Updated output filename format.
1.7.0-alpha2 Updated compatibility condition in some filament profiles (Prusa XL). 1.7.0-alpha2 Updated compatibility condition in some filament profiles (Prusa XL).
1.7.0-alpha1 Added profiles for Original Prusa XL. Added filament profile for Prusament PETG Tungsten 75%. 1.7.0-alpha1 Added profiles for Original Prusa XL. Added filament profile for Prusament PETG Tungsten 75%.
@ -8,10 +9,12 @@ min_slic3r_version = 2.6.0-alpha1
1.6.0-alpha1 Updated FW version notification. Decreased min layer time for PLA. 1.6.0-alpha1 Updated FW version notification. Decreased min layer time for PLA.
1.6.0-alpha0 Default top fill set to monotonic lines. Updated infill/perimeter overlap values. Updated output filename format. Enabled dynamic overhang speeds. 1.6.0-alpha0 Default top fill set to monotonic lines. Updated infill/perimeter overlap values. Updated output filename format. Enabled dynamic overhang speeds.
min_slic3r_version = 2.5.1-rc0 min_slic3r_version = 2.5.1-rc0
1.6.3 Added SLA materials.
1.6.2 Updated compatibility condition in some filament profiles (Prusa XL). 1.6.2 Updated compatibility condition in some filament profiles (Prusa XL).
1.6.1 Added filament profile for Prusament PETG Tungsten 75%. Updated Prusa XL profiles. 1.6.1 Added filament profile for Prusament PETG Tungsten 75%. Updated Prusa XL profiles.
1.6.0 Added Original Prusa XL profiles. Updated acceleration settings for Prusa MINI. Updated infill/perimeter overlap values. 1.6.0 Added Original Prusa XL profiles. Updated acceleration settings for Prusa MINI. Updated infill/perimeter overlap values.
min_slic3r_version = 2.5.0-alpha0 min_slic3r_version = 2.5.0-alpha0
1.5.9 Added SLA materials.
1.5.8 Added filament profile for Prusament PETG Tungsten 75%. Updated FW version notification. 1.5.8 Added filament profile for Prusament PETG Tungsten 75%. Updated FW version notification.
1.5.7 Added filament profile for Prusament PETG Carbon Fiber and Fiberthree F3 PA-GF30 Pro. 1.5.7 Added filament profile for Prusament PETG Carbon Fiber and Fiberthree F3 PA-GF30 Pro.
1.5.6 Updated FW version notification (MK2.5/MK3 family). Added filament profile for Kimya PEBA-S. 1.5.6 Updated FW version notification (MK2.5/MK3 family). Added filament profile for Kimya PEBA-S.

File diff suppressed because it is too large Load Diff

Binary file not shown.

After

Width:  |  Height:  |  Size: 68 KiB

View File

@ -0,0 +1,101 @@
<?xml version="1.0" encoding="UTF-8"?>
<svg id="svg3128" xmlns="http://www.w3.org/2000/svg" width="710.1" height="596.7" viewBox="0 0 710.1 596.7">
<line id="line2794" x1=".7" y1=".7" x2=".7" y2="596" style="fill: none; stroke: #fff; stroke-linecap: round; stroke-linejoin: round; stroke-width: 1.4px;"/>
<line id="line2796" x1="142.4" y1=".7" x2="142.4" y2="595.6" style="fill: none; stroke: #fff; stroke-linecap: round; stroke-linejoin: round; stroke-width: 1.4px;"/>
<line id="line2798" x1="284.2" y1=".7" x2="284.2" y2="595.6" style="fill: none; stroke: #fff; stroke-linecap: round; stroke-linejoin: round; stroke-width: 1.4px;"/>
<line id="line2800" x1="709.4" y1="596" x2="709.4" y2=".7" style="fill: none; stroke: #fff; stroke-linecap: round; stroke-linejoin: round; stroke-width: 1.4px;"/>
<line id="line2802" x1="1.2" y1="581.8" x2="709.4" y2="581.8" style="fill: none; stroke: #fff; stroke-linecap: round; stroke-linejoin: round; stroke-width: 1.4px;"/>
<line id="line2804" x1=".7" y1="596" x2="709.4" y2="596" style="fill: none; stroke: #fff; stroke-linecap: round; stroke-linejoin: round; stroke-width: 1.4px;"/>
<line id="line2806" x1="1.2" y1="440.1" x2="709.4" y2="440.1" style="fill: none; stroke: #fff; stroke-linecap: round; stroke-linejoin: round; stroke-width: 1.4px;"/>
<line id="line2808" x1="1.2" y1="298.4" x2="709.4" y2="298.4" style="fill: none; stroke: #fff; stroke-linecap: round; stroke-linejoin: round; stroke-width: 1.4px;"/>
<line id="line2810" x1="1.2" y1="156.6" x2="709.4" y2="156.6" style="fill: none; stroke: #fff; stroke-linecap: round; stroke-linejoin: round; stroke-width: 1.4px;"/>
<line id="line2812" x1="1.2" y1="14.9" x2="709.4" y2="14.9" style="fill: none; stroke: #fff; stroke-linecap: round; stroke-linejoin: round; stroke-width: 1.4px;"/>
<line id="line2814" x1="709.4" y1=".7" x2=".7" y2=".7" style="fill: none; stroke: #fff; stroke-linecap: round; stroke-linejoin: round; stroke-width: 1.4px;"/>
<line id="line2816" x1="425.9" y1=".7" x2="425.9" y2="499.4" style="fill: none; stroke: #fff; stroke-linecap: round; stroke-linejoin: round; stroke-width: 1.4px;"/>
<line id="line2818" x1="425.9" y1="522.5" x2="425.9" y2="595.7" style="fill: none; stroke: #fff; stroke-linecap: round; stroke-linejoin: round; stroke-width: 1.4px;"/>
<line id="line2820" x1="567.6" y1=".7" x2="567.6" y2="499.4" style="fill: none; stroke: #fff; stroke-linecap: round; stroke-linejoin: round; stroke-width: 1.4px;"/>
<line id="line2822" x1="567.6" y1="522.5" x2="567.6" y2="527.7" style="fill: none; stroke: #fff; stroke-linecap: round; stroke-linejoin: round; stroke-width: 1.4px;"/>
<line id="line2824" x1="567.6" y1="548.6" x2="567.6" y2="595.4" style="fill: none; stroke: #fff; stroke-linecap: round; stroke-linejoin: round; stroke-width: 1.4px;"/>
<line id="line2826" x1="85.8" y1=".7" x2="85.8" y2="595.9" style="fill: none; stroke: #fff; stroke-linecap: round; stroke-linejoin: round; stroke-width: .4px;"/>
<line id="line2828" x1="114.1" y1=".7" x2="114.1" y2="595.9" style="fill: none; stroke: #fff; stroke-linecap: round; stroke-linejoin: round; stroke-width: .4px;"/>
<line id="line2830" x1="170.8" y1=".7" x2="170.8" y2="595.9" style="fill: none; stroke: #fff; stroke-linecap: round; stroke-linejoin: round; stroke-width: .4px;"/>
<line id="line2832" x1="199.1" y1=".7" x2="199.1" y2="595.9" style="fill: none; stroke: #fff; stroke-linecap: round; stroke-linejoin: round; stroke-width: .4px;"/>
<line id="line2834" x1="227.5" y1=".7" x2="227.5" y2="595.9" style="fill: none; stroke: #fff; stroke-linecap: round; stroke-linejoin: round; stroke-width: .4px;"/>
<line id="line2836" x1="255.8" y1=".7" x2="255.8" y2="595.9" style="fill: none; stroke: #fff; stroke-linecap: round; stroke-linejoin: round; stroke-width: .4px;"/>
<line id="line2838" x1="312.5" y1=".7" x2="312.5" y2="595.9" style="fill: none; stroke: #fff; stroke-linecap: round; stroke-linejoin: round; stroke-width: .4px;"/>
<line id="line2840" x1="340.9" y1=".7" x2="340.9" y2="595.9" style="fill: none; stroke: #fff; stroke-linecap: round; stroke-linejoin: round; stroke-width: .4px;"/>
<line id="line2842" x1="681" y1=".7" x2="681" y2="595.9" style="fill: none; stroke: #fff; stroke-linecap: round; stroke-linejoin: round; stroke-width: .4px;"/>
<line id="line2844" x1="29.1" y1=".7" x2="29.1" y2="595.9" style="fill: none; stroke: #fff; stroke-linecap: round; stroke-linejoin: round; stroke-width: .4px;"/>
<line id="line2846" x1="57.4" y1=".7" x2="57.4" y2="595.9" style="fill: none; stroke: #fff; stroke-linecap: round; stroke-linejoin: round; stroke-width: .4px;"/>
<line id="line2848" x1="1.5" y1="468.4" x2="709.4" y2="468.4" style="fill: none; stroke: #fff; stroke-linecap: round; stroke-linejoin: round; stroke-width: .4px;"/>
<line id="line2850" x1="1.5" y1="496.8" x2="709.4" y2="496.8" style="fill: none; stroke: #fff; stroke-linecap: round; stroke-linejoin: round; stroke-width: .4px;"/>
<line id="line2852" x1="1.5" y1="525.1" x2="709.4" y2="525.1" style="fill: none; stroke: #fff; stroke-linecap: round; stroke-linejoin: round; stroke-width: .4px;"/>
<line id="line2854" x1="1.5" y1="553.5" x2="709.4" y2="553.5" style="fill: none; stroke: #fff; stroke-linecap: round; stroke-linejoin: round; stroke-width: .4px;"/>
<line id="line2856" x1="1.5" y1="411.7" x2="709.4" y2="411.7" style="fill: none; stroke: #fff; stroke-linecap: round; stroke-linejoin: round; stroke-width: .4px;"/>
<line id="line2858" x1="1.5" y1="383.4" x2="709.4" y2="383.4" style="fill: none; stroke: #fff; stroke-linecap: round; stroke-linejoin: round; stroke-width: .4px;"/>
<line id="line2860" x1="1.5" y1="355" x2="709.4" y2="355" style="fill: none; stroke: #fff; stroke-linecap: round; stroke-linejoin: round; stroke-width: .4px;"/>
<line id="line2862" x1="1.5" y1="326.7" x2="709.4" y2="326.7" style="fill: none; stroke: #fff; stroke-linecap: round; stroke-linejoin: round; stroke-width: .4px;"/>
<line id="line2864" x1="1.5" y1="270" x2="709.4" y2="270" style="fill: none; stroke: #fff; stroke-linecap: round; stroke-linejoin: round; stroke-width: .4px;"/>
<line id="line2866" x1="1.5" y1="241.6" x2="709.4" y2="241.6" style="fill: none; stroke: #fff; stroke-linecap: round; stroke-linejoin: round; stroke-width: .4px;"/>
<line id="line2868" x1="1.5" y1="213.3" x2="709.4" y2="213.3" style="fill: none; stroke: #fff; stroke-linecap: round; stroke-linejoin: round; stroke-width: .4px;"/>
<line id="line2870" x1="1.5" y1="185" x2="709.4" y2="185" style="fill: none; stroke: #fff; stroke-linecap: round; stroke-linejoin: round; stroke-width: .4px;"/>
<line id="line2872" x1="1.5" y1="128.3" x2="709.4" y2="128.3" style="fill: none; stroke: #fff; stroke-linecap: round; stroke-linejoin: round; stroke-width: .4px;"/>
<line id="line2874" x1="1.5" y1="99.9" x2="709.4" y2="99.9" style="fill: none; stroke: #fff; stroke-linecap: round; stroke-linejoin: round; stroke-width: .4px;"/>
<line id="line2876" x1="1.5" y1="71.6" x2="709.4" y2="71.6" style="fill: none; stroke: #fff; stroke-linecap: round; stroke-linejoin: round; stroke-width: .4px;"/>
<line id="line2878" x1="1.5" y1="43.2" x2="709.4" y2="43.2" style="fill: none; stroke: #fff; stroke-linecap: round; stroke-linejoin: round; stroke-width: .4px;"/>
<line id="line2880" x1="369.2" y1=".7" x2="369.2" y2="522.6" style="fill: none; stroke: #fff; stroke-linecap: round; stroke-linejoin: round; stroke-width: .4px;"/>
<line id="line2882" x1="369.2" y1="522.6" x2="369.2" y2="595.7" style="fill: none; stroke: #fff; stroke-linecap: round; stroke-linejoin: round; stroke-width: .4px;"/>
<line id="line2884" x1="397.6" y1=".7" x2="397.6" y2="499.4" style="fill: none; stroke: #fff; stroke-linecap: round; stroke-linejoin: round; stroke-width: .4px;"/>
<line id="line2886" x1="397.5" y1="522.6" x2="397.5" y2="595.7" style="fill: none; stroke: #fff; stroke-linecap: round; stroke-linejoin: round; stroke-width: .4px;"/>
<line id="line2888" x1="454.2" y1=".7" x2="454.2" y2="499.4" style="fill: none; stroke: #fff; stroke-linecap: round; stroke-linejoin: round; stroke-width: .4px;"/>
<line id="line2890" x1="454.3" y1="522.6" x2="454.3" y2="595.6" style="fill: none; stroke: #fff; stroke-linecap: round; stroke-linejoin: round; stroke-width: .4px;"/>
<line id="line2892" x1="482.6" y1=".7" x2="482.6" y2="499.4" style="fill: none; stroke: #fff; stroke-linecap: round; stroke-linejoin: round; stroke-width: .4px;"/>
<line id="line2894" x1="482.6" y1="522.6" x2="482.6" y2="595.6" style="fill: none; stroke: #fff; stroke-linecap: round; stroke-linejoin: round; stroke-width: .4px;"/>
<line id="line2896" x1="510.9" y1=".7" x2="510.9" y2="499.4" style="fill: none; stroke: #fff; stroke-linecap: round; stroke-linejoin: round; stroke-width: .4px;"/>
<line id="line2898" x1="510.9" y1="522.6" x2="510.9" y2="595.6" style="fill: none; stroke: #fff; stroke-linecap: round; stroke-linejoin: round; stroke-width: .4px;"/>
<line id="line2900" x1="539.3" y1=".7" x2="539.3" y2="499.4" style="fill: none; stroke: #fff; stroke-linecap: round; stroke-linejoin: round; stroke-width: .4px;"/>
<line id="line2902" x1="539.3" y1="522.6" x2="539.3" y2="595.6" style="fill: none; stroke: #fff; stroke-linecap: round; stroke-linejoin: round; stroke-width: .4px;"/>
<line id="line2904" x1="596" y1=".7" x2="596" y2="499.4" style="fill: none; stroke: #fff; stroke-linecap: round; stroke-linejoin: round; stroke-width: .4px;"/>
<line id="line2906" x1="624.3" y1=".7" x2="624.3" y2="499.4" style="fill: none; stroke: #fff; stroke-linecap: round; stroke-linejoin: round; stroke-width: .4px;"/>
<line id="line2908" x1="652.7" y1=".7" x2="652.7" y2="522.5" style="fill: none; stroke: #fff; stroke-linecap: round; stroke-linejoin: round; stroke-width: .4px;"/>
<line id="line2910" x1="652.7" y1="522.5" x2="652.7" y2="527.7" style="fill: none; stroke: #fff; stroke-linecap: round; stroke-linejoin: round; stroke-width: .4px;"/>
<line id="line2912" x1="652.6" y1="548.3" x2="652.6" y2="596" style="fill: none; stroke: #fff; stroke-linecap: round; stroke-linejoin: round; stroke-width: .4px;"/>
<line id="line2914" x1="624.3" y1="522.5" x2="624.3" y2="527.7" style="fill: none; stroke: #fff; stroke-linecap: round; stroke-linejoin: round; stroke-width: .4px;"/>
<line id="line2916" x1="624.3" y1="548.3" x2="624.3" y2="596" style="fill: none; stroke: #fff; stroke-linecap: round; stroke-linejoin: round; stroke-width: .4px;"/>
<line id="line2918" x1="596" y1="522.5" x2="596" y2="527.7" style="fill: none; stroke: #fff; stroke-linecap: round; stroke-linejoin: round; stroke-width: .4px;"/>
<line id="line2920" x1="596" y1="548.3" x2="596" y2="596" style="fill: none; stroke: #fff; stroke-linecap: round; stroke-linejoin: round; stroke-width: .4px;"/>
<path id="path3098" d="m548.6,532h2.1v4.8h0c.1-.2.3-.4.5-.6.2-.2.4-.3.7-.5.2-.1.5-.2.8-.3.3,0,.5-.1.8,0,.7,0,1.3.1,1.9.4.5.2,1,.6,1.4,1.1.4.5.6,1,.8,1.6.2.6.3,1.3.3,1.9,0,.6,0,1.2-.2,1.8-.2.6-.4,1.1-.7,1.6-.7,1-1.8,1.5-3,1.5-.3,0-.6,0-1,0-.3,0-.6-.1-.9-.2-.3-.1-.5-.3-.8-.5-.2-.2-.4-.5-.6-.8h0v1.3h-2v-12.9Zm7.2,8.2c0-.4,0-.8-.2-1.2-.1-.4-.3-.7-.5-1-.2-.3-.5-.6-.8-.7-.3-.2-.7-.3-1.1-.3-.8,0-1.5.3-2,.9-.5.7-.7,1.6-.7,2.4,0,.4,0,.9.2,1.3.1.4.3.7.5,1,.2.3.5.5.8.7.3.2.7.3,1.1.2.4,0,.8,0,1.2-.3.3-.2.6-.4.8-.7.2-.3.4-.7.5-1,0-.4.1-.8.1-1.2Z" style="fill: #fff;"/>
<path id="path3100" d="m558.5,535.6h2.2l2.4,7h0l2.4-7h2.1l-3.6,9.8c-.2.4-.3.8-.5,1.2-.2.4-.4.7-.6,1-.2.3-.5.5-.9.7-.4.2-.9.3-1.3.3-.5,0-1,0-1.4-.1v-1.7h.5c.2,0,.3,0,.5,0,.2,0,.4,0,.6,0,.1,0,.3-.1.4-.3.1-.1.2-.3.3-.4,0-.2.1-.4.2-.5l.2-.7-3.5-9.2Z" style="fill: #fff;"/>
<path id="path3102" d="m581.1,540.8c0,.5,0,1.1-.1,1.6,0,.5-.3,1-.6,1.4-.3.4-.8.8-1.3,1-.7.3-1.5.4-2.2.4-.6,0-1.2,0-1.7-.3-.5-.2-.9-.5-1.2-.9-.3-.4-.6-.8-.7-1.3-.1-.5-.2-1.1-.2-1.6v-.7h2.2v.7c0,.6,0,1.2.4,1.7.3.4.9.7,1.4.6.3,0,.6,0,.9-.2.2-.1.4-.3.6-.5.1-.2.2-.5.3-.8,0-.4,0-.7,0-1.1v-8.8h2.2v8.7Z" style="fill: #fff;"/>
<path id="path3104" d="m587.8,545.1c-.7,0-1.4-.1-2-.4-.6-.2-1.1-.6-1.5-1-.4-.4-.7-1-.9-1.5-.2-.6-.3-1.3-.3-2,0-.7,0-1.4.3-2,.2-.6.5-1.1.9-1.5.4-.4.9-.8,1.5-1,1.3-.5,2.7-.5,4,0,.6.2,1.1.6,1.5,1,.4.4.7,1,.9,1.5.2.6.3,1.3.3,2,0,.7,0,1.4-.3,2-.2.6-.5,1.1-.9,1.5-.4.4-.9.8-1.5,1-.6.3-1.3.4-2,.4Zm0-1.6c.4,0,.8,0,1.2-.3.3-.2.6-.4.8-.8.2-.3.4-.7.5-1.1.1-.4.2-.8.2-1.2,0-.4,0-.8-.2-1.2,0-.4-.3-.7-.5-1-.2-.3-.5-.6-.8-.8-.7-.4-1.6-.4-2.4,0-.3.2-.6.4-.8.8-.2.3-.4.6-.5,1,0,.4-.1.8-.2,1.2,0,.4,0,.8.2,1.2,0,.4.3.7.5,1.1.2.3.5.6.8.8.4.2.8.3,1.2.3h0Z" style="fill: #fff;"/>
<path id="path3106" d="m595.7,541.9c0,.5.3,1,.7,1.3.4.2.9.4,1.4.4.2,0,.4,0,.7,0,.2,0,.5,0,.7-.2.2,0,.4-.2.5-.4.3-.4.2-.9,0-1.3-.2-.2-.4-.3-.7-.4-.3-.1-.7-.2-1-.3l-1.1-.2c-.4,0-.7-.2-1.1-.3-.4-.1-.7-.3-1-.5-.3-.2-.5-.5-.6-.9-.2-.4-.3-.8-.2-1.2,0-.5.1-.9.4-1.3.2-.3.6-.6.9-.8.4-.2.8-.4,1.3-.4.4,0,.9-.1,1.3-.1.5,0,.9,0,1.4.2.4,0,.8.3,1.2.5.4.2.7.5.9.9.2.4.4.9.4,1.3h-2.1c0-.4-.3-.8-.7-1-.4-.2-.8-.3-1.2-.2-.2,0-.3,0-.5,0-.2,0-.4,0-.6.1-.2,0-.3.2-.4.3-.1.1-.2.3-.2.5,0,.2,0,.5.3.6.2.2.4.3.7.4.3.1.7.2,1,.3l1.1.2c.4,0,.7.2,1.1.3.4.1.7.3,1,.5.3.2.5.5.7.8.2.4.3.8.3,1.2,0,.5-.1,1-.4,1.4-.3.4-.6.7-1,.9-.4.2-.9.4-1.3.5-.5.1-1,.2-1.5.2-.5,0-1.1,0-1.6-.2-.5-.1-.9-.3-1.3-.6-.4-.3-.7-.6-.9-1-.2-.4-.3-.9-.3-1.4h2Z" style="fill: #fff;"/>
<path id="path3108" d="m605.1,540.8c0,.3,0,.7.2,1,0,.3.3.6.5.9.2.3.5.5.8.6.4.2.7.2,1.1.2.5,0,1.1,0,1.5-.3.4-.3.7-.7.8-1.2h1.9c0,.5-.3.9-.6,1.3-.3.4-.6.7-1,1-.4.3-.8.5-1.2.6-.5.1-1,.2-1.5.2-.7,0-1.3-.1-1.9-.4-.5-.2-1-.6-1.4-1-.4-.4-.7-1-.9-1.5-.2-.6-.3-1.3-.3-2,0-.6.1-1.3.3-1.9.2-.6.5-1.1.9-1.6.8-1,2-1.5,3.3-1.5.7,0,1.4.1,2,.5.6.3,1.1.7,1.5,1.2.4.5.7,1.1.8,1.7.2.7.2,1.3.1,2h-6.9Zm4.8-1.3c0-.3,0-.6-.2-.9-.1-.3-.3-.6-.5-.8-.2-.2-.4-.4-.7-.5-.3-.1-.6-.2-.9-.2-.3,0-.7,0-1,.2-.3.1-.5.3-.8.5-.2.2-.4.5-.5.8-.1.3-.2.7-.2,1h4.8Z" style="fill: #fff;"/>
<path id="path3110" d="m612.6,535.6h1.5v-.8c0-.5,0-1,.2-1.4.1-.3.3-.6.6-.8.2-.2.5-.3.8-.4.3,0,.7-.1,1,0,.5,0,.9,0,1.4,0v1.6c-.1,0-.3,0-.4,0h-.5c-.3,0-.5,0-.7.2-.2.2-.3.5-.3.8v1h1.7v1.5h-1.7v7.8h-2v-7.8h-1.5v-1.5Z" style="fill: #fff;"/>
<path id="path3112" d="m624.5,532h5.7c.8,0,1.5.1,2.2.4.5.2,1,.6,1.3,1,.3.4.5.8.6,1.3.1.4.2.9.2,1.3,0,.4,0,.9-.2,1.3-.1.5-.3.9-.6,1.3-.4.4-.8.8-1.3,1-.7.3-1.5.4-2.2.4h-3.4v4.9h-2.2v-12.9Zm2.2,6.1h3.3c.3,0,.5,0,.8-.1.3,0,.5-.2.7-.3.2-.2.4-.4.5-.7.1-.3.2-.7.2-1,0-.3,0-.7-.2-1-.2-.5-.7-.9-1.2-1-.3,0-.6,0-.8,0h-3.3v4.2Z" style="fill: #fff;"/>
<path id="path3114" d="m636.2,535.6h1.9v1.8h0c0-.3.2-.5.4-.7.2-.2.4-.5.6-.7.2-.2.5-.4.8-.5.3-.1.6-.2.9-.2h.8v2h-.4c-.1,0-.3,0-.5,0-.7,0-1.3.3-1.8.8-.2.3-.4.6-.5,1-.1.4-.2.9-.2,1.4v4.4h-2v-9.3Z" style="fill: #fff;"/>
<path id="path3116" d="m650.7,544.9h-2v-1.3h0c-.3.5-.7.9-1.1,1.1-.5.3-1,.4-1.5.4-1,0-1.9-.2-2.7-.9-.6-.8-.9-1.8-.8-2.7v-5.9h2v5.7c0,.6.1,1.2.5,1.7.3.3.8.5,1.3.5.4,0,.7,0,1.1-.2.3-.1.5-.3.7-.5.2-.2.3-.5.4-.8,0-.3.1-.7.1-1v-5.4h2v9.3Z" style="fill: #fff;"/>
<path id="path3118" d="m654.4,541.9c0,.5.3,1,.7,1.2.4.2.9.4,1.4.4.2,0,.4,0,.7,0,.2,0,.5,0,.7-.2.2,0,.4-.2.5-.4.1-.2.2-.4.2-.6,0-.2-.1-.5-.3-.7-.2-.2-.4-.3-.7-.4-.3-.1-.7-.2-1-.3l-1.1-.2c-.4,0-.7-.2-1.1-.3-.4-.1-.7-.3-1-.5-.3-.2-.5-.5-.7-.8-.2-.4-.3-.8-.3-1.2,0-.5.1-.9.4-1.3.2-.3.6-.6.9-.8.4-.2.8-.4,1.3-.4.4,0,.9-.1,1.3-.1.5,0,.9,0,1.4.2.4.1.8.3,1.2.5.4.2.7.5.9.9.2.4.4.9.4,1.3h-2.1c0-.4-.3-.8-.7-1-.4-.2-.8-.3-1.2-.2-.2,0-.3,0-.5,0-.2,0-.4,0-.5.1-.2,0-.3.2-.4.3-.1.1-.2.3-.2.5,0,.2,0,.5.3.6.2.2.4.3.7.4.3.1.7.2,1,.3l1.1.2c.4,0,.7.2,1.1.3.4.1.7.3,1,.5.3.2.5.5.7.8.2.4.3.8.3,1.2,0,.5-.1,1-.4,1.4-.3.4-.6.7-1,.9-.4.2-.9.4-1.3.5-.5.1-1,.2-1.5.2-.6,0-1.1,0-1.6-.2-.5-.1-.9-.3-1.3-.6-.4-.3-.7-.6-.9-1-.2-.4-.3-.9-.3-1.4h2.1Z" style="fill: #fff;"/>
<path id="path3120" d="m670,542.8c0,.2,0,.4,0,.5,0,.1.2.2.4.2h.5v1.4h-.3c0,0-.3.2-.3.2h-.4c-.1,0-.2,0-.3,0-.3,0-.7,0-1-.2-.3-.2-.5-.5-.5-.9-.4.4-.9.7-1.5.9-.6.2-1.1.3-1.7.3-.4,0-.8,0-1.2-.2-.4-.1-.7-.3-1-.5-.3-.2-.5-.5-.7-.8-.2-.4-.3-.8-.3-1.2,0-.5,0-1,.3-1.4.2-.3.5-.6.8-.8.4-.2.7-.4,1.2-.4.4,0,.9-.2,1.3-.2.3,0,.7-.1,1.1-.2.3,0,.6,0,.9-.2.2,0,.4-.2.6-.3.2-.2.2-.4.2-.7,0-.2,0-.5-.2-.7-.1-.2-.3-.3-.5-.4-.2,0-.4-.2-.6-.2-.2,0-.4,0-.7,0-.5,0-1,.1-1.4.4-.4.3-.6.7-.6,1.1h-2c0-.5.2-1,.4-1.5.3-.4.6-.7,1-1,.4-.2.9-.4,1.3-.5.5-.1,1-.2,1.5-.2.5,0,.9,0,1.3.2.4,0,.8.2,1.2.5.3.2.6.5.8.8.2.4.3.8.3,1.2v4.8Zm-2.2-2.6c-.3.2-.7.3-1.2.4-.5,0-.9.1-1.4.2-.2,0-.4,0-.6.2-.2,0-.4.1-.5.3-.2.1-.3.3-.4.5,0,.2-.1.4-.1.7,0,.2,0,.4.2.6.1.2.3.3.5.4.2,0,.4.2.6.2.2,0,.4,0,.6,0,.2,0,.5,0,.7,0,.3,0,.5-.2.8-.3.2-.1.4-.3.6-.5.2-.2.2-.5.2-.8v-1.5Z" style="fill: #fff;"/>
<g>
<path d="m385.6,511.3c0-1.3.2-2.6.6-3.7s1-2.1,1.7-3c.7-.9,1.7-1.5,2.8-2,1.1-.5,2.3-.7,3.7-.7s2.6.2,3.7.7c1.1.5,2,1.2,2.8,2s1.3,1.8,1.7,3,.6,2.4.6,3.7-.2,2.5-.6,3.6-1,2.1-1.7,2.9c-.7.8-1.7,1.5-2.8,2-1.1.5-2.3.7-3.7.7s-2.6-.2-3.7-.7c-1.1-.5-2-1.1-2.8-2-.7-.8-1.3-1.8-1.7-2.9-.4-1.1-.6-2.3-.6-3.6Zm3.9,0c0,.7.1,1.5.3,2.2.2.7.4,1.3.9,1.9.4.6.9,1,1.5,1.4s1.4.5,2.2.5,1.6-.2,2.2-.5,1.1-.8,1.5-1.4c.4-.6.6-1.2.9-1.9.2-.7.3-1.4.3-2.2s-.1-1.5-.3-2.3c-.2-.7-.4-1.4-.9-1.9-.4-.6-.9-1-1.5-1.4-.6-.3-1.4-.5-2.2-.5s-1.6.2-2.2.5c-.6.3-1.1.8-1.5,1.4s-.6,1.2-.9,1.9c-.2.7-.3,1.5-.3,2.3Z" style="fill: #959998;"/>
<path d="m404.8,502.3h9.6c.8,0,1.5.1,2.1.4.6.3,1.2.6,1.6,1.1.4.4.8,1,1.1,1.6.2.6.4,1.2.4,1.9,0,1.1-.2,1.9-.6,2.7-.4.7-1.2,1.4-2.1,1.7h0c.5.2.9.4,1.2.6s.6.6.8,1c.2.4.3.8.4,1.2s.2.9.2,1.3,0,.6,0,1,0,.8.1,1.2c0,.4.1.8.2,1.1.1.4.2.7.4.9h-3.9c-.1-.3-.2-.6-.3-.9,0-.3-.1-.7-.1-1.1s0-.7-.1-1.2c0-.4,0-.7-.1-1.1-.1-.9-.4-1.6-.9-2s-1.1-.6-2.1-.6h-3.9v6.9h-3.9v-17.7Zm3.9,8.1h4.3c.9,0,1.6-.2,2-.6.4-.4.7-1.1.7-1.9s-.2-1.5-.7-1.9-1.1-.6-2-.6h-4.3v5Z" style="fill: #959998;"/>
<path d="m421.8,502.3h3.9v17.8h-3.9v-17.8Z" style="fill: #959998;"/>
<path d="m441,518.1c-.7.9-1.5,1.5-2.3,1.9s-1.7.5-2.6.5c-1.4,0-2.6-.2-3.7-.7-1.1-.5-2-1.1-2.8-2-.7-.8-1.3-1.8-1.7-2.9-.4-1.1-.6-2.3-.6-3.6s.2-2.6.6-3.7,1-2.1,1.7-3c.7-.9,1.7-1.5,2.8-2,1.1-.5,2.3-.7,3.7-.7s1.8.1,2.7.4c.9.3,1.6.7,2.3,1.2s1.3,1.2,1.7,2,.7,1.7.9,2.7h-3.7c-.2-1-.7-1.7-1.4-2.2s-1.5-.7-2.4-.7-1.6.2-2.2.5-1.1.8-1.5,1.4c-.4.6-.6,1.2-.9,1.9-.2.7-.3,1.5-.3,2.3s.1,1.5.3,2.2c.2.7.4,1.3.9,1.9.4.6.9,1,1.5,1.4.6.3,1.4.5,2.2.5,1.3,0,2.3-.3,3-1s1.1-1.6,1.3-2.9h-3.9v-2.9h7.5v9.6h-2.5l-.4-2Z" style="fill: #959998;"/>
<path d="m446.3,502.3h3.9v17.8h-3.9v-17.8Z" style="fill: #959998;"/>
<path d="m452.7,502.3h3.9l7.4,11.9h0v-11.9h3.7v17.8h-3.9l-7.4-11.9h0v11.9h-3.7v-17.8Z" style="fill: #959998;"/>
<path d="m475.1,502.3h4l6.6,17.8h-4l-1.4-3.9h-6.6l-1.4,3.9h-3.9l6.7-17.8Zm-.4,10.9h4.6l-2.2-6.5h0l-2.3,6.5Z" style="fill: #959998;"/>
<path d="m486.5,502.3h3.9v14.5h8.6v3.3h-12.6v-17.8h0Z" style="fill: #959998;"/>
</g>
<g>
<path d="m506.9,502.3h8c1.1,0,2.1.2,2.8.5s1.4.7,1.9,1.3c.5.5.9,1.2,1.1,1.8.2.7.3,1.4.3,2.1s-.1,1.4-.3,2.1-.6,1.3-1.1,1.8c-.5.5-1.1,1-1.9,1.3s-1.7.5-2.8.5h-4.1v6.4h-3.9v-17.8Zm3.9,8.4h3c.4,0,.9,0,1.3-.1.4,0,.8-.2,1.1-.4.3-.2.6-.5.7-.8.2-.3.3-.8.3-1.4s-.1-1-.3-1.4c-.2-.3-.4-.6-.7-.8-.3-.2-.7-.3-1.1-.4s-.9-.1-1.3-.1h-3v5.3Z" style="fill: #fff;"/>
<path d="m522.2,502.3h9.6c.8,0,1.5.1,2.1.4.6.3,1.2.6,1.6,1.1.4.4.8,1,1.1,1.6.2.6.4,1.2.4,1.9,0,1.1-.2,1.9-.6,2.7-.4.7-1.2,1.4-2.1,1.7h0c.5.2.9.4,1.2.6s.6.6.8,1c.2.4.3.8.4,1.2s.2.9.2,1.3,0,.6,0,1,0,.8.1,1.2c0,.4.1.8.2,1.1.1.4.2.7.4.9h-3.9c-.1-.3-.2-.6-.3-.9,0-.3-.1-.7-.1-1.1s0-.7-.1-1.2c0-.4,0-.7-.1-1.1-.1-.9-.4-1.6-.9-2s-1.1-.6-2.1-.6h-3.9v6.9h-3.9v-17.7Zm3.9,8.1h4.3c.9,0,1.5-.2,2-.6.4-.4.7-1.1.7-1.9s-.2-1.5-.7-1.9-1.1-.6-2-.6h-4.3v5Z" style="fill: #fff;"/>
<path d="m554.1,513.3c0,2.4-.7,4.2-2,5.4-1.4,1.2-3.2,1.8-5.6,1.8s-4.3-.6-5.6-1.8c-1.3-1.2-2-3-2-5.4v-11.1h3.9v11.1c0,.5,0,1,.1,1.4,0,.5.3.9.5,1.3.3.4.6.6,1.1.9.5.2,1.1.3,1.9.3,1.4,0,2.3-.3,2.9-.9s.8-1.6.8-2.9v-11.1h3.9v11h0Z" style="fill: #fff;"/>
<path d="m559.1,514.2c0,.6.1,1.1.3,1.5.2.4.5.7.9,1s.8.4,1.3.6,1,.2,1.5.2.7,0,1.1-.1.8-.2,1.1-.3.6-.4.9-.7c.2-.3.3-.6.3-1.1s-.2-.9-.5-1.2-.7-.5-1.2-.7c-.5-.2-1.1-.4-1.7-.5s-1.3-.3-1.9-.5c-.7-.2-1.3-.4-1.9-.6-.6-.2-1.2-.5-1.7-.9s-.9-.9-1.2-1.4c-.3-.6-.5-1.3-.5-2.1s.2-1.7.6-2.4.9-1.2,1.5-1.7,1.4-.8,2.1-1,1.6-.3,2.4-.3,1.8.1,2.7.3,1.6.5,2.3,1,1.2,1.1,1.6,1.8.6,1.6.6,2.6h-3.8c0-.5-.1-1-.3-1.3s-.4-.6-.7-.8-.7-.3-1.1-.4-.9-.1-1.3-.1-.6,0-1,.1c-.3,0-.6.2-.9.3-.3.2-.5.4-.6.6s-.2.6-.2.9,0,.6.2.9c.1.2.4.4.8.6s.9.4,1.6.5c.7.2,1.6.4,2.7.7.2,0,.5.1.9.2.3.1.7.2,1.1.4.4.2.8.4,1.2.6.4.2.7.5,1.1.9.3.4.6.8.8,1.3.2.5.3,1.1.3,1.8s-.2,1.6-.5,2.3c-.3.7-.8,1.3-1.4,1.8s-1.4.9-2.3,1.2c-.9.3-2,.4-3.2.4s-1.9-.1-2.9-.4-1.7-.6-2.4-1.2-1.3-1.2-1.7-2c-.4-.8-.6-1.7-.6-2.8h3.8Z" style="fill: #fff;"/>
<path d="m576.5,502.3h4l6.6,17.8h-4l-1.4-3.9h-6.6l-1.4,3.9h-3.9l6.7-17.8Zm-.4,10.9h4.6l-2.2-6.5h0l-2.3,6.5Z" style="fill: #fff;"/>
</g>
<g>
<path d="m594.3,502.3h5.5l4.1,12.2h0l3.9-12.2h5.5v17.8h-3.7v-12.6h0l-4.4,12.6h-3l-4.4-12.5h0v12.5h-3.7v-17.8h0Z" style="fill: #ed6b21;"/>
<path d="m616,502.3h3.9v7.4l6.9-7.4h4.9l-6.9,7,7.6,10.7h-4.9l-5.3-8-2.2,2.3v5.7h-3.9v-17.8Z" style="fill: #ed6b21;"/>
<path d="m638.4,516.1h-7.4v-3.2l7.6-10.2h3.2v10.5h2.3v2.9h-2.3v4h-3.4v-4h0Zm0-9h0l-4.5,6.1h4.6v-6.1Z" style="fill: #ed6b21;"/>
</g>
</svg>

After

Width:  |  Height:  |  Size: 20 KiB

Binary file not shown.

View File

@ -70,6 +70,7 @@ class _Item {
int binid_{BIN_ID_UNSET}, priority_{0}; int binid_{BIN_ID_UNSET}, priority_{0};
bool fixed_{false}; bool fixed_{false};
std::function<void(_Item&)> on_packed_;
public: public:
@ -205,6 +206,23 @@ public:
sl::vertex(sh_, idx) = v; sl::vertex(sh_, idx) = v;
} }
void setShape(RawShape rsh)
{
sh_ = std::move(rsh);
invalidateCache();
}
void setOnPackedFn(std::function<void(_Item&)> onpackedfn)
{
on_packed_ = onpackedfn;
}
void onPacked()
{
if (on_packed_)
on_packed_(*this);
}
/** /**
* @brief Calculate the shape area. * @brief Calculate the shape area.
* *

View File

@ -901,6 +901,7 @@ public:
if(can_pack) { if(can_pack) {
ret = PackResult(item); ret = PackResult(item);
item.onPacked();
merged_pile_ = nfp::merge(merged_pile_, item.transformedShape()); merged_pile_ = nfp::merge(merged_pile_, item.transformedShape());
} else { } else {
ret = PackResult(best_overfit); ret = PackResult(best_overfit);

View File

@ -522,44 +522,6 @@ static bool has_missing_twin_edge(const SkeletalTrapezoidationGraph &graph)
return false; return false;
} }
inline static std::unordered_map<Point, Point, PointHash> try_to_fix_degenerated_voronoi_diagram_by_rotation(
Geometry::VoronoiDiagram &voronoi_diagram,
const Polygons &polys,
Polygons &polys_rotated,
std::vector<SkeletalTrapezoidation::Segment> &segments,
const double fix_angle)
{
std::unordered_map<Point, Point, PointHash> vertex_mapping;
for (Polygon &poly : polys_rotated)
poly.rotate(fix_angle);
assert(polys_rotated.size() == polys.size());
for (size_t poly_idx = 0; poly_idx < polys.size(); ++poly_idx) {
assert(polys_rotated[poly_idx].size() == polys[poly_idx].size());
for (size_t point_idx = 0; point_idx < polys[poly_idx].size(); ++point_idx)
vertex_mapping.insert({polys_rotated[poly_idx][point_idx], polys[poly_idx][point_idx]});
}
segments.clear();
for (size_t poly_idx = 0; poly_idx < polys_rotated.size(); poly_idx++)
for (size_t point_idx = 0; point_idx < polys_rotated[poly_idx].size(); point_idx++)
segments.emplace_back(&polys_rotated, poly_idx, point_idx);
voronoi_diagram.clear();
construct_voronoi(segments.begin(), segments.end(), &voronoi_diagram);
#ifdef ARACHNE_DEBUG_VORONOI
{
static int iRun = 0;
dump_voronoi_to_svg(debug_out_path("arachne_voronoi-diagram-rotated-%d.svg", iRun++).c_str(), voronoi_diagram, to_points(polys), to_lines(polys));
}
#endif
assert(Geometry::VoronoiUtilsCgal::is_voronoi_diagram_planar_intersection(voronoi_diagram));
return vertex_mapping;
}
inline static void rotate_back_skeletal_trapezoidation_graph_after_fix(SkeletalTrapezoidationGraph &graph, inline static void rotate_back_skeletal_trapezoidation_graph_after_fix(SkeletalTrapezoidationGraph &graph,
const double fix_angle, const double fix_angle,
const std::unordered_map<Point, Point, PointHash> &vertex_mapping) const std::unordered_map<Point, Point, PointHash> &vertex_mapping)
@ -626,6 +588,56 @@ VoronoiDiagramStatus detect_voronoi_diagram_known_issues(const Geometry::Voronoi
return VoronoiDiagramStatus::NO_ISSUE_DETECTED; return VoronoiDiagramStatus::NO_ISSUE_DETECTED;
} }
inline static std::pair<std::unordered_map<Point, Point, PointHash>, double> try_to_fix_degenerated_voronoi_diagram_by_rotation(
Geometry::VoronoiDiagram &voronoi_diagram,
const Polygons &polys,
Polygons &polys_rotated,
std::vector<SkeletalTrapezoidation::Segment> &segments,
const std::vector<double> &fix_angles)
{
const Polygons polys_rotated_original = polys_rotated;
double fixed_by_angle = fix_angles.front();
std::unordered_map<Point, Point, PointHash> vertex_mapping;
for (const double &fix_angle : fix_angles) {
vertex_mapping.clear();
polys_rotated = polys_rotated_original;
fixed_by_angle = fix_angle;
for (Polygon &poly : polys_rotated)
poly.rotate(fix_angle);
assert(polys_rotated.size() == polys.size());
for (size_t poly_idx = 0; poly_idx < polys.size(); ++poly_idx) {
assert(polys_rotated[poly_idx].size() == polys[poly_idx].size());
for (size_t point_idx = 0; point_idx < polys[poly_idx].size(); ++point_idx)
vertex_mapping.insert({polys_rotated[poly_idx][point_idx], polys[poly_idx][point_idx]});
}
segments.clear();
for (size_t poly_idx = 0; poly_idx < polys_rotated.size(); poly_idx++)
for (size_t point_idx = 0; point_idx < polys_rotated[poly_idx].size(); point_idx++)
segments.emplace_back(&polys_rotated, poly_idx, point_idx);
voronoi_diagram.clear();
construct_voronoi(segments.begin(), segments.end(), &voronoi_diagram);
#ifdef ARACHNE_DEBUG_VORONOI
{
static int iRun = 0;
dump_voronoi_to_svg(debug_out_path("arachne_voronoi-diagram-rotated-%d.svg", iRun++).c_str(), voronoi_diagram, to_points(polys), to_lines(polys));
}
#endif
if (detect_voronoi_diagram_known_issues(voronoi_diagram, segments) == VoronoiDiagramStatus::NO_ISSUE_DETECTED)
break;
}
assert(Geometry::VoronoiUtilsCgal::is_voronoi_diagram_planar_intersection(voronoi_diagram));
return {vertex_mapping, fixed_by_angle};
}
void SkeletalTrapezoidation::constructFromPolygons(const Polygons& polys) void SkeletalTrapezoidation::constructFromPolygons(const Polygons& polys)
{ {
#ifdef ARACHNE_DEBUG #ifdef ARACHNE_DEBUG
@ -669,8 +681,9 @@ void SkeletalTrapezoidation::constructFromPolygons(const Polygons& polys)
// When any Voronoi vertex is missing, the Voronoi diagram is not planar, or some voronoi edge is // When any Voronoi vertex is missing, the Voronoi diagram is not planar, or some voronoi edge is
// intersecting input segment, rotate the input polygon and try again. // intersecting input segment, rotate the input polygon and try again.
VoronoiDiagramStatus status = detect_voronoi_diagram_known_issues(voronoi_diagram, segments); VoronoiDiagramStatus status = detect_voronoi_diagram_known_issues(voronoi_diagram, segments);
const double fix_angle = PI / 6; const std::vector<double> fix_angles = {PI / 6, PI / 5, PI / 7, PI / 11};
double fixed_by_angle = fix_angles.front();
std::unordered_map<Point, Point, PointHash> vertex_mapping; std::unordered_map<Point, Point, PointHash> vertex_mapping;
// polys_copy is referenced through items stored in the std::vector segments. // polys_copy is referenced through items stored in the std::vector segments.
@ -683,16 +696,15 @@ void SkeletalTrapezoidation::constructFromPolygons(const Polygons& polys)
else if (status == VoronoiDiagramStatus::VORONOI_EDGE_INTERSECTING_INPUT_SEGMENT) else if (status == VoronoiDiagramStatus::VORONOI_EDGE_INTERSECTING_INPUT_SEGMENT)
BOOST_LOG_TRIVIAL(warning) << "Detected Voronoi edge intersecting input segment, input polygons will be rotated back and forth."; BOOST_LOG_TRIVIAL(warning) << "Detected Voronoi edge intersecting input segment, input polygons will be rotated back and forth.";
vertex_mapping = try_to_fix_degenerated_voronoi_diagram_by_rotation(voronoi_diagram, polys, polys_copy, segments, fix_angle); std::tie(vertex_mapping, fixed_by_angle) = try_to_fix_degenerated_voronoi_diagram_by_rotation(voronoi_diagram, polys, polys_copy, segments, fix_angles);
assert(!detect_missing_voronoi_vertex(voronoi_diagram, segments)); VoronoiDiagramStatus status_after_fix = detect_voronoi_diagram_known_issues(voronoi_diagram, segments);
assert(Geometry::VoronoiUtilsCgal::is_voronoi_diagram_planar_angle(voronoi_diagram, segments)); assert(status_after_fix == VoronoiDiagramStatus::NO_ISSUE_DETECTED);
assert(!detect_voronoi_edge_intersecting_input_segment(voronoi_diagram, segments)); if (status_after_fix == VoronoiDiagramStatus::MISSING_VORONOI_VERTEX)
if (detect_missing_voronoi_vertex(voronoi_diagram, segments))
BOOST_LOG_TRIVIAL(error) << "Detected missing Voronoi vertex even after the rotation of input."; BOOST_LOG_TRIVIAL(error) << "Detected missing Voronoi vertex even after the rotation of input.";
else if (!Geometry::VoronoiUtilsCgal::is_voronoi_diagram_planar_angle(voronoi_diagram, segments)) else if (status_after_fix == VoronoiDiagramStatus::NON_PLANAR_VORONOI_DIAGRAM)
BOOST_LOG_TRIVIAL(error) << "Detected non-planar Voronoi diagram even after the rotation of input."; BOOST_LOG_TRIVIAL(error) << "Detected non-planar Voronoi diagram even after the rotation of input.";
else if (detect_voronoi_edge_intersecting_input_segment(voronoi_diagram, segments)) else if (status_after_fix == VoronoiDiagramStatus::VORONOI_EDGE_INTERSECTING_INPUT_SEGMENT)
BOOST_LOG_TRIVIAL(error) << "Detected Voronoi edge intersecting input segment even after the rotation of input."; BOOST_LOG_TRIVIAL(error) << "Detected Voronoi edge intersecting input segment even after the rotation of input.";
} }
@ -759,8 +771,8 @@ process_voronoi_diagram:
// diagram on slightly rotated input polygons that usually make the Voronoi generator generate a non-degenerated Voronoi diagram. // diagram on slightly rotated input polygons that usually make the Voronoi generator generate a non-degenerated Voronoi diagram.
if (status == VoronoiDiagramStatus::NO_ISSUE_DETECTED && has_missing_twin_edge(this->graph)) { if (status == VoronoiDiagramStatus::NO_ISSUE_DETECTED && has_missing_twin_edge(this->graph)) {
BOOST_LOG_TRIVIAL(warning) << "Detected degenerated Voronoi diagram, input polygons will be rotated back and forth."; BOOST_LOG_TRIVIAL(warning) << "Detected degenerated Voronoi diagram, input polygons will be rotated back and forth.";
status = VoronoiDiagramStatus::OTHER_TYPE_OF_VORONOI_DIAGRAM_DEGENERATION; status = VoronoiDiagramStatus::OTHER_TYPE_OF_VORONOI_DIAGRAM_DEGENERATION;
vertex_mapping = try_to_fix_degenerated_voronoi_diagram_by_rotation(voronoi_diagram, polys, polys_copy, segments, fix_angle); std::tie(vertex_mapping, fixed_by_angle) = try_to_fix_degenerated_voronoi_diagram_by_rotation(voronoi_diagram, polys, polys_copy, segments, fix_angles);
assert(!detect_missing_voronoi_vertex(voronoi_diagram, segments)); assert(!detect_missing_voronoi_vertex(voronoi_diagram, segments));
if (detect_missing_voronoi_vertex(voronoi_diagram, segments)) if (detect_missing_voronoi_vertex(voronoi_diagram, segments))
@ -784,7 +796,7 @@ process_voronoi_diagram:
} }
if (status != VoronoiDiagramStatus::NO_ISSUE_DETECTED) if (status != VoronoiDiagramStatus::NO_ISSUE_DETECTED)
rotate_back_skeletal_trapezoidation_graph_after_fix(this->graph, fix_angle, vertex_mapping); rotate_back_skeletal_trapezoidation_graph_after_fix(this->graph, fixed_by_angle, vertex_mapping);
#ifdef ARACHNE_DEBUG #ifdef ARACHNE_DEBUG
assert(Geometry::VoronoiUtilsCgal::is_voronoi_diagram_planar_intersection(voronoi_diagram)); assert(Geometry::VoronoiUtilsCgal::is_voronoi_diagram_planar_intersection(voronoi_diagram));

View File

@ -113,8 +113,7 @@ void ExtrusionLine::simplify(const int64_t smallest_line_segment_squared, const
//h^2 = (L / b)^2 [square it] //h^2 = (L / b)^2 [square it]
//h^2 = L^2 / b^2 [factor the divisor] //h^2 = L^2 / b^2 [factor the divisor]
const auto height_2 = int64_t(double(area_removed_so_far) * double(area_removed_so_far) / double(base_length_2)); const auto height_2 = int64_t(double(area_removed_so_far) * double(area_removed_so_far) / double(base_length_2));
coord_t weighted_average_width; const int64_t extrusion_area_error = calculateExtrusionAreaDeviationError(previous, current, next);
const int64_t extrusion_area_error = calculateExtrusionAreaDeviationError(previous, current, next, weighted_average_width);
if ((height_2 <= scaled<coord_t>(0.001) //Almost exactly colinear (barring rounding errors). if ((height_2 <= scaled<coord_t>(0.001) //Almost exactly colinear (barring rounding errors).
&& Line::distance_to_infinite(current.p, previous.p, next.p) <= scaled<double>(0.001)) // Make sure that height_2 is not small because of cancellation of positive and negative areas && Line::distance_to_infinite(current.p, previous.p, next.p) <= scaled<double>(0.001)) // Make sure that height_2 is not small because of cancellation of positive and negative areas
// We shouldn't remove middle junctions of colinear segments if the area changed for the C-P segment is exceeding the maximum allowed // We shouldn't remove middle junctions of colinear segments if the area changed for the C-P segment is exceeding the maximum allowed
@ -189,8 +188,7 @@ void ExtrusionLine::simplify(const int64_t smallest_line_segment_squared, const
junctions = new_junctions; junctions = new_junctions;
} }
int64_t ExtrusionLine::calculateExtrusionAreaDeviationError(ExtrusionJunction A, ExtrusionJunction B, ExtrusionJunction C, coord_t& weighted_average_width) int64_t ExtrusionLine::calculateExtrusionAreaDeviationError(ExtrusionJunction A, ExtrusionJunction B, ExtrusionJunction C) {
{
/* /*
* A B C A C * A B C A C
* --------------- ************** * --------------- **************
@ -208,27 +206,19 @@ int64_t ExtrusionLine::calculateExtrusionAreaDeviationError(ExtrusionJunction A,
* weighted-average width for the entire extrusion line. * weighted-average width for the entire extrusion line.
* *
* */ * */
const int64_t ab_length = (B - A).cast<int64_t>().norm(); const int64_t ab_length = (B.p - A.p).cast<int64_t>().norm();
const int64_t bc_length = (C - B).cast<int64_t>().norm(); const int64_t bc_length = (C.p - B.p).cast<int64_t>().norm();
const coord_t width_diff = std::max(std::abs(B.w - A.w), std::abs(C.w - B.w)); if (const coord_t width_diff = std::max(std::abs(B.w - A.w), std::abs(C.w - B.w)); width_diff > 1) {
if (width_diff > 1)
{
// Adjust the width only if there is a difference, or else the rounding errors may produce the wrong // Adjust the width only if there is a difference, or else the rounding errors may produce the wrong
// weighted average value. // weighted average value.
const int64_t ab_weight = (A.w + B.w) / 2; const int64_t ab_weight = (A.w + B.w) / 2;
const int64_t bc_weight = (B.w + C.w) / 2; const int64_t bc_weight = (B.w + C.w) / 2;
assert(((ab_length * ab_weight + bc_length * bc_weight) / (C - A).cast<int64_t>().norm()) <= std::numeric_limits<coord_t>::max()); const int64_t weighted_average_width = (ab_length * ab_weight + bc_length * bc_weight) / (ab_length + bc_length);
weighted_average_width = (ab_length * ab_weight + bc_length * bc_weight) / (C - A).cast<int64_t>().norm(); const int64_t ac_length = (C.p - A.p).cast<int64_t>().norm();
assert((int64_t(std::abs(ab_weight - weighted_average_width)) * ab_length + int64_t(std::abs(bc_weight - weighted_average_width)) * bc_length) <= double(std::numeric_limits<int64_t>::max())); return std::abs((ab_weight * ab_length + bc_weight * bc_length) - (weighted_average_width * ac_length));
return std::abs(ab_weight - weighted_average_width) * ab_length + std::abs(bc_weight - weighted_average_width) * bc_length; } else {
}
else
{
// If the width difference is very small, then select the width of the segment that is longer // If the width difference is very small, then select the width of the segment that is longer
weighted_average_width = ab_length > bc_length ? A.w : B.w; return ab_length > bc_length ? int64_t(width_diff) * bc_length : int64_t(width_diff) * ab_length;
assert((int64_t(width_diff) * int64_t(bc_length)) <= std::numeric_limits<coord_t>::max());
assert((int64_t(width_diff) * int64_t(ab_length)) <= std::numeric_limits<coord_t>::max());
return ab_length > bc_length ? width_diff * bc_length : width_diff * ab_length;
} }
} }

View File

@ -186,9 +186,8 @@ struct ExtrusionLine
* \param A Start point of the 3-point-straight line * \param A Start point of the 3-point-straight line
* \param B Intermediate point of the 3-point-straight line * \param B Intermediate point of the 3-point-straight line
* \param C End point of the 3-point-straight line * \param C End point of the 3-point-straight line
* \param weighted_average_width The weighted average of the widths of the two colinear extrusion segments
* */ * */
static int64_t calculateExtrusionAreaDeviationError(ExtrusionJunction A, ExtrusionJunction B, ExtrusionJunction C, coord_t& weighted_average_width); static int64_t calculateExtrusionAreaDeviationError(ExtrusionJunction A, ExtrusionJunction B, ExtrusionJunction C);
bool is_contour() const; bool is_contour() const;

View File

@ -583,8 +583,12 @@ static void process_arrangeable(const ArrangePolygon &arrpoly,
outp.emplace_back(std::move(p)); outp.emplace_back(std::move(p));
outp.back().rotation(rotation); outp.back().rotation(rotation);
outp.back().translation({offs.x(), offs.y()}); outp.back().translation({offs.x(), offs.y()});
outp.back().inflate(arrpoly.inflation);
outp.back().binId(arrpoly.bed_idx); outp.back().binId(arrpoly.bed_idx);
outp.back().priority(arrpoly.priority); outp.back().priority(arrpoly.priority);
outp.back().setOnPackedFn([&arrpoly](Item &itm){
itm.inflate(-arrpoly.inflation);
});
} }
template<class Fn> auto call_with_bed(const Points &bed, Fn &&fn) template<class Fn> auto call_with_bed(const Points &bed, Fn &&fn)

View File

@ -71,7 +71,7 @@ static const constexpr int UNARRANGED = -1;
/// polygon belongs: UNARRANGED means no place for the polygon /// polygon belongs: UNARRANGED means no place for the polygon
/// (also the initial state before arrange), 0..N means the index of the bed. /// (also the initial state before arrange), 0..N means the index of the bed.
/// Zero is the physical bed, larger than zero means a virtual bed. /// Zero is the physical bed, larger than zero means a virtual bed.
struct ArrangePolygon { struct ArrangePolygon {
ExPolygon poly; /// The 2D silhouette to be arranged ExPolygon poly; /// The 2D silhouette to be arranged
Vec2crd translation{0, 0}; /// The translation of the poly Vec2crd translation{0, 0}; /// The translation of the poly
double rotation{0.0}; /// The rotation of the poly in radians double rotation{0.0}; /// The rotation of the poly in radians

View File

@ -2958,6 +2958,8 @@ namespace Slic3r {
unsigned int object_cnt = 0; unsigned int object_cnt = 0;
for (const ModelObject* object : model.objects) { for (const ModelObject* object : model.objects) {
if (!object->is_cut())
continue;
object_cnt++; object_cnt++;
pt::ptree& obj_tree = tree.add("objects.object", ""); pt::ptree& obj_tree = tree.add("objects.object", "");

View File

@ -438,6 +438,10 @@ void GCode::PlaceholderParserIntegration::reset()
this->opt_e_position = nullptr; this->opt_e_position = nullptr;
this->opt_e_retracted = nullptr; this->opt_e_retracted = nullptr;
this->opt_e_restart_extra = nullptr; this->opt_e_restart_extra = nullptr;
this->opt_extruded_volume = nullptr;
this->opt_extruded_weight = nullptr;
this->opt_extruded_volume_total = nullptr;
this->opt_extruded_weight_total = nullptr;
this->num_extruders = 0; this->num_extruders = 0;
this->position.clear(); this->position.clear();
this->e_position.clear(); this->e_position.clear();
@ -463,10 +467,19 @@ void GCode::PlaceholderParserIntegration::init(const GCodeWriter &writer)
this->output_config.set_key_value("e_position", opt_e_position); this->output_config.set_key_value("e_position", opt_e_position);
} }
} }
this->opt_extruded_volume = new ConfigOptionFloats(this->num_extruders, 0.f);
this->opt_extruded_weight = new ConfigOptionFloats(this->num_extruders, 0.f);
this->opt_extruded_volume_total = new ConfigOptionFloat(0.f);
this->opt_extruded_weight_total = new ConfigOptionFloat(0.f);
this->parser.set("extruded_volume", this->opt_extruded_volume);
this->parser.set("extruded_weight", this->opt_extruded_weight);
this->parser.set("extruded_volume_total", this->opt_extruded_volume_total);
this->parser.set("extruded_weight_total", this->opt_extruded_weight_total);
// Reserve buffer for current position. // Reserve buffer for current position.
this->position.assign(3, 0); this->position.assign(3, 0);
this->opt_position = new ConfigOptionFloats(this->position); this->opt_position = new ConfigOptionFloats(this->position);
this->output_config.set_key_value("position", this->opt_position);
// Store zhop variable into the parser itself, it is a read-only variable to the script. // Store zhop variable into the parser itself, it is a read-only variable to the script.
this->opt_zhop = new ConfigOptionFloat(writer.get_zhop()); this->opt_zhop = new ConfigOptionFloat(writer.get_zhop());
this->parser.set("zhop", this->opt_zhop); this->parser.set("zhop", this->opt_zhop);
@ -483,10 +496,22 @@ void GCode::PlaceholderParserIntegration::update_from_gcodewriter(const GCodeWri
assert(! extruders.empty() && num_extruders == extruders.back().id() + 1); assert(! extruders.empty() && num_extruders == extruders.back().id() + 1);
this->e_retracted.assign(num_extruders, 0); this->e_retracted.assign(num_extruders, 0);
this->e_restart_extra.assign(num_extruders, 0); this->e_restart_extra.assign(num_extruders, 0);
this->opt_extruded_volume->values.assign(num_extruders, 0);
this->opt_extruded_weight->values.assign(num_extruders, 0);
double total_volume = 0.;
double total_weight = 0.;
for (const Extruder &e : extruders) { for (const Extruder &e : extruders) {
this->e_retracted[e.id()] = e.retracted(); this->e_retracted[e.id()] = e.retracted();
this->e_restart_extra[e.id()] = e.restart_extra(); this->e_restart_extra[e.id()] = e.restart_extra();
double v = e.extruded_volume();
double w = v * e.filament_density() * 0.001;
this->opt_extruded_volume->values[e.id()] = v;
this->opt_extruded_weight->values[e.id()] = w;
total_volume += v;
total_weight += w;
} }
opt_extruded_volume_total->value = total_volume;
opt_extruded_weight_total->value = total_weight;
opt_e_retracted->values = this->e_retracted; opt_e_retracted->values = this->e_retracted;
opt_e_restart_extra->values = this->e_restart_extra; opt_e_restart_extra->values = this->e_restart_extra;
if (! writer.config.use_relative_e_distances) { if (! writer.config.use_relative_e_distances) {
@ -3384,10 +3409,12 @@ Vec2d GCode::point_to_gcode_quantized(const Point &point) const
// convert a model-space scaled point into G-code coordinates // convert a model-space scaled point into G-code coordinates
Point GCode::gcode_to_point(const Vec2d &point) const Point GCode::gcode_to_point(const Vec2d &point) const
{ {
Vec2d extruder_offset = EXTRUDER_CONFIG(extruder_offset); Vec2d pt = point - m_origin;
return Point( if (const Extruder *extruder = m_writer.extruder(); extruder)
scale_(point(0) - m_origin(0) + extruder_offset(0)), // This function may be called at the very start from toolchange G-code when the extruder is not assigned yet.
scale_(point(1) - m_origin(1) + extruder_offset(1))); pt += m_config.extruder_offset.get_at(extruder->id());
return scaled<coord_t>(pt);
} }
} // namespace Slic3r } // namespace Slic3r

View File

@ -362,6 +362,10 @@ private:
ConfigOptionFloats *opt_e_position { nullptr }; ConfigOptionFloats *opt_e_position { nullptr };
ConfigOptionFloats *opt_e_retracted { nullptr }; ConfigOptionFloats *opt_e_retracted { nullptr };
ConfigOptionFloats *opt_e_restart_extra { nullptr }; ConfigOptionFloats *opt_e_restart_extra { nullptr };
ConfigOptionFloats *opt_extruded_volume { nullptr };
ConfigOptionFloats *opt_extruded_weight { nullptr };
ConfigOptionFloat *opt_extruded_volume_total { nullptr };
ConfigOptionFloat *opt_extruded_weight_total { nullptr };
// Caches of the data passed to the script. // Caches of the data passed to the script.
size_t num_extruders; size_t num_extruders;
std::vector<double> position; std::vector<double> position;

View File

@ -3508,6 +3508,17 @@ void GCodeProcessor::post_process()
ret += buf; ret += buf;
} }
} }
for (size_t i = 0; i < static_cast<size_t>(PrintEstimatedStatistics::ETimeMode::Count); ++i) {
const TimeMachine& machine = m_time_processor.machines[i];
PrintEstimatedStatistics::ETimeMode mode = static_cast<PrintEstimatedStatistics::ETimeMode>(i);
if (mode == PrintEstimatedStatistics::ETimeMode::Normal || machine.enabled) {
char buf[128];
sprintf(buf, "; estimated first layer printing time (%s mode) = %s\n",
(mode == PrintEstimatedStatistics::ETimeMode::Normal) ? "normal" : "silent",
get_time_dhms(machine.layers_time.empty() ? 0.f : machine.layers_time.front()).c_str());
ret += buf;
}
}
} }
} }

View File

@ -566,6 +566,11 @@ public:
SpanOfConstPtrs<PrintObject> objects() const { return SpanOfConstPtrs<PrintObject>(const_cast<const PrintObject* const* const>(m_objects.data()), m_objects.size()); } SpanOfConstPtrs<PrintObject> objects() const { return SpanOfConstPtrs<PrintObject>(const_cast<const PrintObject* const* const>(m_objects.data()), m_objects.size()); }
PrintObject* get_object(size_t idx) { return const_cast<PrintObject*>(m_objects[idx]); } PrintObject* get_object(size_t idx) { return const_cast<PrintObject*>(m_objects[idx]); }
const PrintObject* get_object(size_t idx) const { return m_objects[idx]; } const PrintObject* get_object(size_t idx) const { return m_objects[idx]; }
const PrintObject* get_print_object_by_model_object_id(ObjectID object_id) const {
auto it = std::find_if(m_objects.begin(), m_objects.end(),
[object_id](const PrintObject* obj) { return obj->model_object()->id() == object_id; });
return (it == m_objects.end()) ? nullptr : *it;
}
// PrintObject by its ObjectID, to be used to uniquely bind slicing warnings to their source PrintObjects // PrintObject by its ObjectID, to be used to uniquely bind slicing warnings to their source PrintObjects
// in the notification center. // in the notification center.
const PrintObject* get_object(ObjectID object_id) const { const PrintObject* get_object(ObjectID object_id) const {

View File

@ -1373,43 +1373,56 @@ void PrintObject::discover_vertical_shells()
} }
#endif /* SLIC3R_DEBUG_SLICE_PROCESSING */ #endif /* SLIC3R_DEBUG_SLICE_PROCESSING */
polygons_append(holes, cache_top_botom_regions[idx_layer].holes); polygons_append(holes, cache_top_botom_regions[idx_layer].holes);
auto combine_holes = [&holes](const Polygons &holes2) {
if (holes.empty() || holes2.empty())
holes.clear();
else
holes = intersection(holes, holes2);
};
auto combine_shells = [&shell](const Polygons &shells2) {
if (shell.empty())
shell = std::move(shells2);
else if (! shells2.empty()) {
polygons_append(shell, shells2);
// Running the union_ using the Clipper library piece by piece is cheaper
// than running the union_ all at once.
shell = union_(shell);
}
};
static constexpr const bool one_more_layer_below_top_bottom_surfaces = false;
if (int n_top_layers = region_config.top_solid_layers.value; n_top_layers > 0) { if (int n_top_layers = region_config.top_solid_layers.value; n_top_layers > 0) {
// Gather top regions projected to this layer. // Gather top regions projected to this layer.
coordf_t print_z = layer->print_z; coordf_t print_z = layer->print_z;
for (int i = int(idx_layer) + 1; int i = int(idx_layer) + 1;
i < int(cache_top_botom_regions.size()) && int itop = int(idx_layer) + n_top_layers;
(i < int(idx_layer) + n_top_layers || for (; i < int(cache_top_botom_regions.size()) &&
m_layers[i]->print_z - print_z < region_config.top_solid_min_thickness - EPSILON); (i < itop || m_layers[i]->print_z - print_z < region_config.top_solid_min_thickness - EPSILON);
++ i) { ++ i) {
const DiscoverVerticalShellsCacheEntry &cache = cache_top_botom_regions[i]; const DiscoverVerticalShellsCacheEntry &cache = cache_top_botom_regions[i];
if (! holes.empty()) combine_holes(cache.holes);
holes = intersection(holes, cache.holes); combine_shells(cache.top_surfaces);
if (! cache.top_surfaces.empty()) {
polygons_append(shell, cache.top_surfaces);
// Running the union_ using the Clipper library piece by piece is cheaper
// than running the union_ all at once.
shell = union_(shell);
}
} }
if (one_more_layer_below_top_bottom_surfaces)
if (i < int(cache_top_botom_regions.size()) &&
(i <= itop || m_layers[i]->bottom_z() - print_z < region_config.top_solid_min_thickness - EPSILON))
combine_holes(cache_top_botom_regions[i].holes);
} }
if (int n_bottom_layers = region_config.bottom_solid_layers.value; n_bottom_layers > 0) { if (int n_bottom_layers = region_config.bottom_solid_layers.value; n_bottom_layers > 0) {
// Gather bottom regions projected to this layer. // Gather bottom regions projected to this layer.
coordf_t bottom_z = layer->bottom_z(); coordf_t bottom_z = layer->bottom_z();
for (int i = int(idx_layer) - 1; int i = int(idx_layer) - 1;
i >= 0 && int ibottom = int(idx_layer) - n_bottom_layers;
(i > int(idx_layer) - n_bottom_layers || for (; i >= 0 &&
bottom_z - m_layers[i]->bottom_z() < region_config.bottom_solid_min_thickness - EPSILON); (i > ibottom || bottom_z - m_layers[i]->bottom_z() < region_config.bottom_solid_min_thickness - EPSILON);
-- i) { -- i) {
const DiscoverVerticalShellsCacheEntry &cache = cache_top_botom_regions[i]; const DiscoverVerticalShellsCacheEntry &cache = cache_top_botom_regions[i];
if (! holes.empty()) combine_holes(cache.holes);
holes = intersection(holes, cache.holes); combine_shells(cache.bottom_surfaces);
if (! cache.bottom_surfaces.empty()) {
polygons_append(shell, cache.bottom_surfaces);
// Running the union_ using the Clipper library piece by piece is cheaper
// than running the union_ all at once.
shell = union_(shell);
}
} }
if (one_more_layer_below_top_bottom_surfaces)
if (i >= 0 &&
(i > ibottom || bottom_z - m_layers[i]->print_z < region_config.bottom_solid_min_thickness - EPSILON))
combine_holes(cache_top_botom_regions[i].holes);
} }
#ifdef SLIC3R_DEBUG_SLICE_PROCESSING #ifdef SLIC3R_DEBUG_SLICE_PROCESSING
{ {

View File

@ -511,8 +511,7 @@ void SLAPrint::Steps::slice_model(SLAPrintObject &po)
if(slindex_it == po.m_slice_index.end()) if(slindex_it == po.m_slice_index.end())
//TRN To be shown at the status bar on SLA slicing error. //TRN To be shown at the status bar on SLA slicing error.
throw Slic3r::RuntimeError( throw Slic3r::RuntimeError(
_u8L("Slicing had to be stopped due to an internal error: " format(_u8L("Model named: %s can not be sliced. Please check if the model is sane."), po.model_object()->name));
"Inconsistent slice index."));
po.m_model_height_levels.clear(); po.m_model_height_levels.clear();
po.m_model_height_levels.reserve(po.m_slice_index.size()); po.m_model_height_levels.reserve(po.m_slice_index.size());

View File

@ -579,6 +579,71 @@ int GLVolumeCollection::load_wipe_tower_preview(
return int(volumes.size() - 1); return int(volumes.size() - 1);
} }
// Load SLA auxiliary GLVolumes (for support trees or pad).
// This function produces volumes for multiple instances in a single shot,
// as some object specific mesh conversions may be expensive.
void GLVolumeCollection::load_object_auxiliary(
const SLAPrintObject* print_object,
int obj_idx,
// pairs of <instance_idx, print_instance_idx>
const std::vector<std::pair<size_t, size_t>>& instances,
SLAPrintObjectStep milestone,
// Timestamp of the last change of the milestone
size_t timestamp)
{
if (print_object->get_mesh_to_print() == nullptr)
return;
const Transform3d mesh_trafo_inv = print_object->trafo().inverse();
auto add_volume = [this, &instances, timestamp](int obj_idx, int inst_idx, const ModelInstance& model_instance, SLAPrintObjectStep step,
const TriangleMesh& mesh, const ColorRGBA& color, std::optional<const TriangleMesh> convex_hull = std::nullopt) {
if (mesh.empty())
return;
GLVolume& v = *this->volumes.emplace_back(new GLVolume(color));
#if ENABLE_SMOOTH_NORMALS
v.model.init_from(mesh, true);
#else
v.model.init_from(mesh);
v.model.set_color(color);
v.mesh_raycaster = std::make_unique<GUI::MeshRaycaster>(std::make_shared<const TriangleMesh>(mesh));
#endif // ENABLE_SMOOTH_NORMALS
v.composite_id = GLVolume::CompositeID(obj_idx, -int(step), inst_idx);
v.geometry_id = std::pair<size_t, size_t>(timestamp, model_instance.id().id);
if (convex_hull.has_value())
v.set_convex_hull(*convex_hull);
v.is_modifier = false;
v.shader_outside_printer_detection_enabled = (step == slaposSupportTree);
v.set_instance_transformation(model_instance.get_transformation());
};
// Get the support mesh.
if (milestone == SLAPrintObjectStep::slaposSupportTree) {
TriangleMesh supports_mesh = print_object->support_mesh();
if (!supports_mesh.empty()) {
supports_mesh.transform(mesh_trafo_inv);
TriangleMesh convex_hull = supports_mesh.convex_hull_3d();
for (const std::pair<size_t, size_t>& instance_idx : instances) {
const ModelInstance& model_instance = *print_object->model_object()->instances[instance_idx.first];
add_volume(obj_idx, (int)instance_idx.first, model_instance, slaposSupportTree, supports_mesh, GLVolume::SLA_SUPPORT_COLOR, convex_hull);
}
}
}
// Get the pad mesh.
if (milestone == SLAPrintObjectStep::slaposPad) {
TriangleMesh pad_mesh = print_object->pad_mesh();
if (!pad_mesh.empty()) {
pad_mesh.transform(mesh_trafo_inv);
TriangleMesh convex_hull = pad_mesh.convex_hull_3d();
for (const std::pair<size_t, size_t>& instance_idx : instances) {
const ModelInstance& model_instance = *print_object->model_object()->instances[instance_idx.first];
add_volume(obj_idx, (int)instance_idx.first, model_instance, slaposPad, pad_mesh, GLVolume::SLA_PAD_COLOR, convex_hull);
}
}
}
}
GLVolume* GLVolumeCollection::new_toolpath_volume(const ColorRGBA& rgba) GLVolume* GLVolumeCollection::new_toolpath_volume(const ColorRGBA& rgba)
{ {
GLVolume* out = new_nontoolpath_volume(rgba); GLVolume* out = new_nontoolpath_volume(rgba);

View File

@ -430,6 +430,16 @@ public:
float pos_x, float pos_y, float width, float depth, float height, float cone_angle, float rotation_angle, bool size_unknown, float brim_width); float pos_x, float pos_y, float width, float depth, float height, float cone_angle, float rotation_angle, bool size_unknown, float brim_width);
#endif // ENABLE_OPENGL_ES #endif // ENABLE_OPENGL_ES
// Load SLA auxiliary GLVolumes (for support trees or pad).
void load_object_auxiliary(
const SLAPrintObject* print_object,
int obj_idx,
// pairs of <instance_idx, print_instance_idx>
const std::vector<std::pair<size_t, size_t>>& instances,
SLAPrintObjectStep milestone,
// Timestamp of the last change of the milestone
size_t timestamp);
GLVolume* new_toolpath_volume(const ColorRGBA& rgba); GLVolume* new_toolpath_volume(const ColorRGBA& rgba);
GLVolume* new_nontoolpath_volume(const ColorRGBA& rgba); GLVolume* new_nontoolpath_volume(const ColorRGBA& rgba);
// Render the volumes by OpenGL. // Render the volumes by OpenGL.

View File

@ -1879,6 +1879,15 @@ void GLCanvas3D::reload_scene(bool refresh_immediately, bool force_full_scene_re
size_t volume_idx; size_t volume_idx;
}; };
// SLA steps to pull the preview meshes for.
typedef std::array<SLAPrintObjectStep, 3> SLASteps;
SLASteps sla_steps = { slaposDrillHoles, slaposSupportTree, slaposPad };
struct SLASupportState {
std::array<PrintStateBase::StateWithTimeStamp, std::tuple_size<SLASteps>::value> step;
};
// State of the sla_steps for all SLAPrintObjects.
std::vector<SLASupportState> sla_support_state;
std::vector<size_t> instance_ids_selected; std::vector<size_t> instance_ids_selected;
std::vector<size_t> map_glvolume_old_to_new(m_volumes.volumes.size(), size_t(-1)); std::vector<size_t> map_glvolume_old_to_new(m_volumes.volumes.size(), size_t(-1));
std::vector<GLVolumeState> deleted_volumes; std::vector<GLVolumeState> deleted_volumes;
@ -1904,6 +1913,37 @@ void GLCanvas3D::reload_scene(bool refresh_immediately, bool force_full_scene_re
} }
} }
if (printer_technology == ptSLA) {
const SLAPrint* sla_print = this->sla_print();
#ifndef NDEBUG
// Verify that the SLAPrint object is synchronized with m_model.
check_model_ids_equal(*m_model, sla_print->model());
#endif // NDEBUG
sla_support_state.reserve(sla_print->objects().size());
for (const SLAPrintObject* print_object : sla_print->objects()) {
SLASupportState state;
for (size_t istep = 0; istep < sla_steps.size(); ++istep) {
state.step[istep] = print_object->step_state_with_timestamp(sla_steps[istep]);
if (state.step[istep].state == PrintStateBase::State::Done) {
std::shared_ptr<const indexed_triangle_set> m = print_object->get_mesh_to_print();
if (m == nullptr || m->empty())
// Consider the DONE step without a valid mesh as invalid for the purpose
// of mesh visualization.
state.step[istep].state = PrintStateBase::State::Invalidated;
else {
for (const ModelInstance* model_instance : print_object->model_object()->instances) {
// Only the instances, which are currently printable, will have the SLA support structures kept.
// The instances outside the print bed will have the GLVolumes of their support structures released.
if (model_instance->is_printable())
aux_volume_state.emplace_back(state.step[istep].timestamp, model_instance->id());
}
}
}
}
sla_support_state.emplace_back(state);
}
}
std::sort(model_volume_state.begin(), model_volume_state.end(), model_volume_state_lower); std::sort(model_volume_state.begin(), model_volume_state.end(), model_volume_state_lower);
std::sort(aux_volume_state.begin(), aux_volume_state.end(), model_volume_state_lower); std::sort(aux_volume_state.begin(), aux_volume_state.end(), model_volume_state_lower);
// Release all ModelVolume based GLVolumes not found in the current Model. Find the GLVolume of a hollowed mesh. // Release all ModelVolume based GLVolumes not found in the current Model. Find the GLVolume of a hollowed mesh.
@ -2018,6 +2058,75 @@ void GLCanvas3D::reload_scene(bool refresh_immediately, bool force_full_scene_re
} }
} }
if (printer_technology == ptSLA) {
size_t idx = 0;
const SLAPrint *sla_print = this->sla_print();
std::vector<double> shift_zs(m_model->objects.size(), 0);
double relative_correction_z = sla_print->relative_correction().z();
if (relative_correction_z <= EPSILON)
relative_correction_z = 1.;
for (const SLAPrintObject *print_object : sla_print->objects()) {
SLASupportState &state = sla_support_state[idx ++];
const ModelObject *model_object = print_object->model_object();
// Find an index of the ModelObject
int object_idx;
// There may be new SLA volumes added to the scene for this print_object.
// Find the object index of this print_object in the Model::objects list.
auto it = std::find(sla_print->model().objects.begin(), sla_print->model().objects.end(), model_object);
assert(it != sla_print->model().objects.end());
object_idx = it - sla_print->model().objects.begin();
// Cache the Z offset to be applied to all volumes with this object_idx.
shift_zs[object_idx] = print_object->get_current_elevation() / relative_correction_z;
// Collect indices of this print_object's instances, for which the SLA support meshes are to be added to the scene.
// pairs of <instance_idx, print_instance_idx>
std::vector<std::pair<size_t, size_t>> instances[std::tuple_size<SLASteps>::value];
for (size_t print_instance_idx = 0; print_instance_idx < print_object->instances().size(); ++ print_instance_idx) {
const SLAPrintObject::Instance &instance = print_object->instances()[print_instance_idx];
// Find index of ModelInstance corresponding to this SLAPrintObject::Instance.
auto it = std::find_if(model_object->instances.begin(), model_object->instances.end(),
[&instance](const ModelInstance *mi) { return mi->id() == instance.instance_id; });
assert(it != model_object->instances.end());
int instance_idx = it - model_object->instances.begin();
for (size_t istep = 0; istep < sla_steps.size(); ++istep) {
if (state.step[istep].state == PrintStateBase::State::Done) {
// Check whether there is an existing auxiliary volume to be updated, or a new auxiliary volume to be created.
ModelVolumeState key(state.step[istep].timestamp, instance.instance_id.id);
auto it = std::lower_bound(aux_volume_state.begin(), aux_volume_state.end(), key, model_volume_state_lower);
assert(it != aux_volume_state.end() && it->geometry_id == key.geometry_id);
if (it->new_geometry()) {
// This can be an SLA support structure that should not be rendered (in case someone used undo
// to revert to before it was generated). If that's the case, we should not generate anything.
if (model_object->sla_points_status != sla::PointsStatus::NoPoints)
instances[istep].emplace_back(std::pair<size_t, size_t>(instance_idx, print_instance_idx));
else
shift_zs[object_idx] = 0.;
}
else {
// Recycling an old GLVolume. Update the Object/Instance indices into the current Model.
m_volumes.volumes[it->volume_idx]->composite_id = GLVolume::CompositeID(object_idx, m_volumes.volumes[it->volume_idx]->volume_idx(), instance_idx);
m_volumes.volumes[it->volume_idx]->set_instance_transformation(model_object->instances[instance_idx]->get_transformation());
}
}
}
}
for (size_t istep = 0; istep < sla_steps.size(); ++istep) {
if (!instances[istep].empty())
m_volumes.load_object_auxiliary(print_object, object_idx, instances[istep], sla_steps[istep], state.step[istep].timestamp);
}
}
// Shift-up all volumes of the object so that it has the right elevation with respect to the print bed
for (GLVolume* volume : m_volumes.volumes) {
const ModelObject* model_object = (volume->object_idx() < (int)m_model->objects.size()) ? m_model->objects[volume->object_idx()] : nullptr;
if (model_object != nullptr && model_object->instances[volume->instance_idx()]->is_printable()) {
const SLAPrintObject* po = sla_print->get_print_object_by_model_object_id(model_object->id());
if (po != nullptr)
volume->set_sla_shift_z(po->get_current_elevation() / sla_print->relative_correction().z());
}
}
}
if (printer_technology == ptFFF && m_config->has("nozzle_diameter")) { if (printer_technology == ptFFF && m_config->has("nozzle_diameter")) {
// Should the wipe tower be visualized ? // Should the wipe tower be visualized ?
unsigned int extruders_count = (unsigned int)dynamic_cast<const ConfigOptionFloats*>(m_config->option("nozzle_diameter"))->values.size(); unsigned int extruders_count = (unsigned int)dynamic_cast<const ConfigOptionFloats*>(m_config->option("nozzle_diameter"))->values.size();
@ -2054,7 +2163,7 @@ void GLCanvas3D::reload_scene(bool refresh_immediately, bool force_full_scene_re
} }
update_volumes_colors_by_extruder(); update_volumes_colors_by_extruder();
// Update selection indices based on the old/new GLVolumeCollection. // Update selection indices based on the old/new GLVolumeCollection.
if (m_selection.get_mode() == Selection::Instance) if (m_selection.get_mode() == Selection::Instance)
m_selection.instances_changed(instance_ids_selected); m_selection.instances_changed(instance_ids_selected);
else else
@ -3486,6 +3595,9 @@ void GLCanvas3D::do_move(const std::string& snapshot_type)
int instance_idx = v->instance_idx(); int instance_idx = v->instance_idx();
int volume_idx = v->volume_idx(); int volume_idx = v->volume_idx();
if (volume_idx < 0)
continue;
std::pair<int, int> done_id(object_idx, instance_idx); std::pair<int, int> done_id(object_idx, instance_idx);
if (0 <= object_idx && object_idx < (int)m_model->objects.size()) { if (0 <= object_idx && object_idx < (int)m_model->objects.size()) {
@ -3500,7 +3612,7 @@ void GLCanvas3D::do_move(const std::string& snapshot_type)
#else #else
model_object->instances[instance_idx]->set_offset(v->get_instance_offset()); model_object->instances[instance_idx]->set_offset(v->get_instance_offset());
#endif // ENABLE_WORLD_COORDINATE #endif // ENABLE_WORLD_COORDINATE
else if (volume_idx >= 0 && selection_mode == Selection::Volume) else if (selection_mode == Selection::Volume)
#if ENABLE_WORLD_COORDINATE #if ENABLE_WORLD_COORDINATE
model_object->volumes[volume_idx]->set_transformation(v->get_volume_transformation()); model_object->volumes[volume_idx]->set_transformation(v->get_volume_transformation());
#else #else
@ -3591,6 +3703,9 @@ void GLCanvas3D::do_rotate(const std::string& snapshot_type)
const int instance_idx = v->instance_idx(); const int instance_idx = v->instance_idx();
const int volume_idx = v->volume_idx(); const int volume_idx = v->volume_idx();
if (volume_idx < 0)
continue;
done.insert(std::pair<int, int>(object_idx, instance_idx)); done.insert(std::pair<int, int>(object_idx, instance_idx));
// Rotate instances/volumes. // Rotate instances/volumes.
@ -3667,6 +3782,9 @@ void GLCanvas3D::do_scale(const std::string& snapshot_type)
const int instance_idx = v->instance_idx(); const int instance_idx = v->instance_idx();
const int volume_idx = v->volume_idx(); const int volume_idx = v->volume_idx();
if (volume_idx < 0)
continue;
done.insert(std::pair<int, int>(object_idx, instance_idx)); done.insert(std::pair<int, int>(object_idx, instance_idx));
// Rotate instances/volumes // Rotate instances/volumes
@ -3680,7 +3798,7 @@ void GLCanvas3D::do_scale(const std::string& snapshot_type)
model_object->instances[instance_idx]->set_offset(v->get_instance_offset()); model_object->instances[instance_idx]->set_offset(v->get_instance_offset());
#endif // ENABLE_WORLD_COORDINATE #endif // ENABLE_WORLD_COORDINATE
} }
else if (selection_mode == Selection::Volume && volume_idx >= 0) { else if (selection_mode == Selection::Volume) {
#if ENABLE_WORLD_COORDINATE #if ENABLE_WORLD_COORDINATE
model_object->instances[instance_idx]->set_transformation(v->get_instance_transformation()); model_object->instances[instance_idx]->set_transformation(v->get_instance_transformation());
model_object->volumes[volume_idx]->set_transformation(v->get_volume_transformation()); model_object->volumes[volume_idx]->set_transformation(v->get_volume_transformation());
@ -6795,9 +6913,9 @@ void GLCanvas3D::_load_sla_shells()
// adds objects' volumes // adds objects' volumes
for (const SLAPrintObject* obj : print->objects()) { for (const SLAPrintObject* obj : print->objects()) {
unsigned int initial_volumes_count = (unsigned int)m_volumes.volumes.size(); unsigned int initial_volumes_count = (unsigned int)m_volumes.volumes.size();
for (const SLAPrintObject::Instance& instance : obj->instances()) { std::shared_ptr<const indexed_triangle_set> m = obj->get_mesh_to_print();
std::shared_ptr<const indexed_triangle_set> m = obj->get_mesh_to_print(); if (m && !m->empty()) {
if (m && !m->empty()) { for (const SLAPrintObject::Instance& instance : obj->instances()) {
add_volume(*obj, 0, instance, *m, GLVolume::MODEL_COLOR[0], true); add_volume(*obj, 0, instance, *m, GLVolume::MODEL_COLOR[0], true);
// Set the extruder_id and volume_id to achieve the same color as in the 3D scene when // Set the extruder_id and volume_id to achieve the same color as in the 3D scene when
// through the update_volumes_colors_by_extruder() call. // through the update_volumes_colors_by_extruder() call.
@ -6808,7 +6926,7 @@ void GLCanvas3D::_load_sla_shells()
add_volume(*obj, -int(slaposPad), instance, pad_mesh, GLVolume::SLA_PAD_COLOR, false); add_volume(*obj, -int(slaposPad), instance, pad_mesh, GLVolume::SLA_PAD_COLOR, false);
} }
} }
double shift_z = obj->get_current_elevation(); const double shift_z = obj->get_current_elevation();
for (unsigned int i = initial_volumes_count; i < m_volumes.volumes.size(); ++ i) { for (unsigned int i = initial_volumes_count; i < m_volumes.volumes.size(); ++ i) {
// apply shift z // apply shift z
m_volumes.volumes[i]->set_sla_shift_z(shift_z); m_volumes.volumes[i]->set_sla_shift_z(shift_z);

View File

@ -875,6 +875,7 @@ void GLGizmoCut3D::on_set_state()
} }
m_selected.clear(); m_selected.clear();
m_parent.set_use_color_clip_plane(false); m_parent.set_use_color_clip_plane(false);
m_c->selection_info()->set_use_shift(false);
} }
} }
@ -1238,7 +1239,10 @@ BoundingBoxf3 GLGizmoCut3D::transformed_bounding_box(const Vec3d& plane_center,
{ {
const Selection& selection = m_parent.get_selection(); const Selection& selection = m_parent.get_selection();
const Vec3d& instance_offset = selection.get_first_volume()->get_instance_offset(); const auto first_volume = selection.get_first_volume();
Vec3d instance_offset = first_volume->get_instance_offset();
instance_offset[Z] += first_volume->get_sla_shift_z();
const auto cut_matrix = Transform3d::Identity() * rotation_m.inverse() * translation_transform(instance_offset - plane_center); const auto cut_matrix = Transform3d::Identity() * rotation_m.inverse() * translation_transform(instance_offset - plane_center);
const Selection::IndicesList& idxs = selection.get_volume_idxs(); const Selection::IndicesList& idxs = selection.get_volume_idxs();
@ -1356,6 +1360,12 @@ void GLGizmoCut3D::render_clipper_cut()
void GLGizmoCut3D::on_render() void GLGizmoCut3D::on_render()
{ {
if (m_state == On) {
// This gizmo is showing the object elevated. Tell the common
// SelectionInfo object to lie about the actual shift.
m_c->selection_info()->set_use_shift(true);
}
update_clipper(); update_clipper();
init_picking_models(); init_picking_models();
@ -2288,7 +2298,10 @@ bool GLGizmoCut3D::process_cut_line(SLAGizmoEventType action, const Vec2d& mouse
const Vec3d new_plane_center = m_bb_center + cross_dir * cross_dir.dot(pt - m_bb_center); const Vec3d new_plane_center = m_bb_center + cross_dir * cross_dir.dot(pt - m_bb_center);
// update transformed bb // update transformed bb
const auto new_tbb = transformed_bounding_box(new_plane_center, m); const auto new_tbb = transformed_bounding_box(new_plane_center, m);
const Vec3d& instance_offset = m_parent.get_selection().get_first_volume()->get_instance_offset(); const GLVolume* first_volume = m_parent.get_selection().get_first_volume();
Vec3d instance_offset = first_volume->get_instance_offset();
instance_offset[Z] += first_volume->get_sla_shift_z();
const Vec3d trans_center_pos = m.inverse() * (new_plane_center - instance_offset) + new_tbb.center(); const Vec3d trans_center_pos = m.inverse() * (new_plane_center - instance_offset) + new_tbb.center();
if (new_tbb.contains(trans_center_pos)) { if (new_tbb.contains(trans_center_pos)) {
Plater::TakeSnapshot snapshot(wxGetApp().plater(), _L("Cut by line"), UndoRedo::SnapshotType::GizmoAction); Plater::TakeSnapshot snapshot(wxGetApp().plater(), _L("Cut by line"), UndoRedo::SnapshotType::GizmoAction);

View File

@ -223,6 +223,10 @@ enum class IconType : unsigned {
system_selector, system_selector,
open_file, open_file,
exclamation, exclamation,
lock,
lock_bold,
unlock,
unlock_bold,
// automatic calc of icon's count // automatic calc of icon's count
_count _count
}; };
@ -779,12 +783,12 @@ GLGizmoEmboss::GuiCfg GLGizmoEmboss::create_gui_configuration()
ImGui::CalcTextSize(tr.boldness.c_str()).x, ImGui::CalcTextSize(tr.boldness.c_str()).x,
ImGui::CalcTextSize(tr.skew_ration.c_str()).x, ImGui::CalcTextSize(tr.skew_ration.c_str()).x,
ImGui::CalcTextSize(tr.from_surface.c_str()).x, ImGui::CalcTextSize(tr.from_surface.c_str()).x,
ImGui::CalcTextSize(tr.rotation.c_str()).x, ImGui::CalcTextSize(tr.rotation.c_str()).x + cfg.icon_width + 2*space,
ImGui::CalcTextSize(tr.keep_up.c_str()).x, ImGui::CalcTextSize(tr.keep_up.c_str()).x,
ImGui::CalcTextSize(tr.collection.c_str()).x }); ImGui::CalcTextSize(tr.collection.c_str()).x });
cfg.advanced_input_offset = max_advanced_text_width cfg.advanced_input_offset = max_advanced_text_width
+ 3 * space + cfg.indent; + 3 * space + cfg.indent;
cfg.lock_offset = cfg.advanced_input_offset - (cfg.icon_width + space);
// calculate window size // calculate window size
float window_title = line_height + 2*style.FramePadding.y + 2 * style.WindowTitleAlign.y; float window_title = line_height + 2*style.FramePadding.y + 2 * style.WindowTitleAlign.y;
float input_height = line_height_with_spacing + 2*style.FramePadding.y; float input_height = line_height_with_spacing + 2*style.FramePadding.y;
@ -806,9 +810,9 @@ GLGizmoEmboss::GuiCfg GLGizmoEmboss::create_gui_configuration()
+ 2 * (cfg.icon_width + space); + 2 * (cfg.icon_width + space);
cfg.minimal_window_size = ImVec2(window_width, window_height); cfg.minimal_window_size = ImVec2(window_width, window_height);
// 9 = useSurface, charGap, lineGap, bold, italic, surfDist, rotation, keepUp, textFaceToCamera // 8 = useSurface, charGap, lineGap, bold, italic, surfDist, rotation, textFaceToCamera
// 4 = 1px for fix each edit image of drag float // 4 = 1px for fix each edit image of drag float
float advance_height = input_height * 9 + 8; float advance_height = input_height * 8 + 8;
cfg.minimal_window_size_with_advance = cfg.minimal_window_size_with_advance =
ImVec2(cfg.minimal_window_size.x, ImVec2(cfg.minimal_window_size.x,
cfg.minimal_window_size.y + advance_height); cfg.minimal_window_size.y + advance_height);
@ -2652,8 +2656,8 @@ void GLGizmoEmboss::draw_height(bool use_inch)
{ {
float &value = m_style_manager.get_style().prop.size_in_mm; float &value = m_style_manager.get_style().prop.size_in_mm;
const EmbossStyle* stored_style = m_style_manager.get_stored_style(); const EmbossStyle* stored_style = m_style_manager.get_stored_style();
const float *stored = ((stored_style)? &stored_style->prop.size_in_mm : nullptr); const float *stored = (stored_style != nullptr)? &stored_style->prop.size_in_mm : nullptr;
const char *size_format = ((use_inch) ? "%.2f in" : "%.1f mm"); const char *size_format = use_inch ? "%.2f in" : "%.1f mm";
const std::string revert_text_size = _u8L("Revert text size."); const std::string revert_text_size = _u8L("Revert text size.");
const std::string& name = m_gui_cfg->translations.height; const std::string& name = m_gui_cfg->translations.height;
if (rev_input_mm(name, value, stored, revert_text_size, 0.1f, 1.f, size_format, use_inch, m_scale_height)) if (rev_input_mm(name, value, stored, revert_text_size, 0.1f, 1.f, size_format, use_inch, m_scale_height))
@ -2913,10 +2917,10 @@ void GLGizmoEmboss::draw_advanced()
&stored_style->prop.distance : nullptr; &stored_style->prop.distance : nullptr;
m_imgui->disabled_begin(!allowe_surface_distance); m_imgui->disabled_begin(!allowe_surface_distance);
bool use_inch = wxGetApp().app_config->get_bool("use_inches");
const std::string undo_move_tooltip = _u8L("Undo translation"); const std::string undo_move_tooltip = _u8L("Undo translation");
const wxString move_tooltip = _L("Distance of the center of text from model surface"); const wxString move_tooltip = _L("Distance of the center of text from model surface");
bool is_moved = false; bool is_moved = false;
bool use_inch = wxGetApp().app_config->get_bool("use_inches");
if (use_inch) { if (use_inch) {
std::optional<float> distance_inch; std::optional<float> distance_inch;
if (distance.has_value()) distance_inch = (*distance * ObjectManipulation::mm_to_in); if (distance.has_value()) distance_inch = (*distance * ObjectManipulation::mm_to_in);
@ -2982,16 +2986,23 @@ void GLGizmoEmboss::draw_advanced()
process(); process();
} }
ImGui::Text("%s", tr.keep_up.c_str()); // Keep up - lock button icon
ImGui::SameLine(m_gui_cfg->advanced_input_offset); ImGui::SameLine(m_gui_cfg->lock_offset);
if (ImGui::Checkbox("##keep_up", &m_keep_up)) { const IconManager::Icon &icon = get_icon(m_icons, m_keep_up ? IconType::lock : IconType::unlock, IconState::activable);
const IconManager::Icon &icon_hover = get_icon(m_icons, m_keep_up ? IconType::lock_bold : IconType::unlock_bold, IconState::activable);
const IconManager::Icon &icon_disable = get_icon(m_icons, m_keep_up ? IconType::lock : IconType::unlock, IconState::disabled);
if (button(icon, icon_hover, icon_disable)) {
m_keep_up = !m_keep_up;
if (m_keep_up) { if (m_keep_up) {
// copy angle to volume // copy angle to volume
m_volume->text_configuration->style.prop.angle = font_prop.angle; m_volume->text_configuration->style.prop.angle = font_prop.angle;
} }
} }
if (ImGui::IsItemHovered()) if (ImGui::IsItemHovered())
ImGui::SetTooltip("%s", _u8L("Lock the text's rotation when moving text along the object's surface.").c_str()); ImGui::SetTooltip("%s", (m_keep_up?
_u8L("Unlock the text's rotation when moving text along the object's surface."):
_u8L("Lock the text's rotation when moving text along the object's surface.")
).c_str());
// when more collection add selector // when more collection add selector
if (ff.font_file->infos.size() > 1) { if (ff.font_file->infos.size() > 1) {
@ -3270,7 +3281,11 @@ void GLGizmoEmboss::init_icons()
"make_unbold.svg", "make_unbold.svg",
"search.svg", "search.svg",
"open.svg", "open.svg",
"exclamation.svg" "exclamation.svg",
"lock_closed.svg", // lock,
"lock_closed_f.svg",// lock_bold,
"lock_open.svg", // unlock,
"lock_open_f.svg" // unlock_bold,
}; };
assert(filenames.size() == static_cast<size_t>(IconType::_count)); assert(filenames.size() == static_cast<size_t>(IconType::_count));
std::string path = resources_dir() + "/icons/"; std::string path = resources_dir() + "/icons/";
@ -3395,6 +3410,7 @@ bool priv::start_create_volume_on_surface_job(
{ {
assert(gl_volume != nullptr); assert(gl_volume != nullptr);
if (gl_volume == nullptr) return false; if (gl_volume == nullptr) return false;
if (gl_volume->volume_idx() < 0) return false;
Plater *plater = wxGetApp().plater(); Plater *plater = wxGetApp().plater();
const ModelObjectPtrs &objects = plater->model().objects; const ModelObjectPtrs &objects = plater->model().objects;
@ -3445,7 +3461,7 @@ void priv::find_closest_volume(const Selection &selection,
for (unsigned int id : indices) { for (unsigned int id : indices) {
const GLVolume *gl_volume = selection.get_volume(id); const GLVolume *gl_volume = selection.get_volume(id);
const ModelVolume *volume = get_model_volume(*gl_volume, objects); const ModelVolume *volume = get_model_volume(*gl_volume, objects);
if (!volume->is_model_part()) continue; if (volume == nullptr || !volume->is_model_part()) continue;
Slic3r::Polygon hull = CameraUtils::create_hull2d(camera, *gl_volume); Slic3r::Polygon hull = CameraUtils::create_hull2d(camera, *gl_volume);
Vec2d c = hull.centroid().cast<double>(); Vec2d c = hull.centroid().cast<double>();
Vec2d d = c - screen_center; Vec2d d = c - screen_center;

View File

@ -182,6 +182,8 @@ private:
float input_offset = 0.f; float input_offset = 0.f;
float advanced_input_offset = 0.f; float advanced_input_offset = 0.f;
float lock_offset = 0.f;
ImVec2 text_size; ImVec2 text_size;
// maximal size of face name image // maximal size of face name image
@ -254,7 +256,7 @@ private:
std::vector<wxString> bad = {}; std::vector<wxString> bad = {};
// Configuration of font encoding // Configuration of font encoding
static const wxFontEncoding encoding = wxFontEncoding::wxFONTENCODING_SYSTEM; static constexpr wxFontEncoding encoding = wxFontEncoding::wxFONTENCODING_SYSTEM;
// Identify if preview texture exists // Identify if preview texture exists
GLuint texture_id = 0; GLuint texture_id = 0;

View File

@ -101,6 +101,8 @@ void GLGizmoHollow::on_render()
m_selection_rectangle.render(m_parent); m_selection_rectangle.render(m_parent);
m_c->object_clipper()->render_cut(); m_c->object_clipper()->render_cut();
if (are_sla_supports_shown())
m_c->supports_clipper()->render_cut();
glsafe(::glDisable(GL_BLEND)); glsafe(::glDisable(GL_BLEND));
} }
@ -772,6 +774,14 @@ RENDER_AGAIN:
if (m_imgui->slider_float("##clp_dist", &clp_dist, 0.f, 1.f, "%.2f")) if (m_imgui->slider_float("##clp_dist", &clp_dist, 0.f, 1.f, "%.2f"))
m_c->object_clipper()->set_position_by_ratio(clp_dist, true); m_c->object_clipper()->set_position_by_ratio(clp_dist, true);
// make sure supports are shown/hidden as appropriate
ImGui::Separator();
bool show_sups = are_sla_supports_shown();
if (m_imgui->checkbox(m_desc["show_supports"], show_sups)) {
show_sla_supports(show_sups);
force_refresh = true;
}
m_imgui->disabled_end(); m_imgui->disabled_end();
m_imgui->end(); m_imgui->end();

View File

@ -30,7 +30,8 @@ CommonGizmosDataID GLGizmoSlaBase::on_get_requirements() const
int(CommonGizmosDataID::SelectionInfo) int(CommonGizmosDataID::SelectionInfo)
| int(CommonGizmosDataID::InstancesHider) | int(CommonGizmosDataID::InstancesHider)
| int(CommonGizmosDataID::Raycaster) | int(CommonGizmosDataID::Raycaster)
| int(CommonGizmosDataID::ObjectClipper)); | int(CommonGizmosDataID::ObjectClipper)
| int(CommonGizmosDataID::SupportsClipper));
} }
void GLGizmoSlaBase::update_volumes() void GLGizmoSlaBase::update_volumes()
@ -50,27 +51,54 @@ void GLGizmoSlaBase::update_volumes()
TriangleMesh backend_mesh; TriangleMesh backend_mesh;
std::shared_ptr<const indexed_triangle_set> preview_mesh_ptr = po->get_mesh_to_print(); std::shared_ptr<const indexed_triangle_set> preview_mesh_ptr = po->get_mesh_to_print();
if (preview_mesh_ptr) if (preview_mesh_ptr != nullptr)
backend_mesh = TriangleMesh{*preview_mesh_ptr}; backend_mesh = TriangleMesh(*preview_mesh_ptr);
if (!backend_mesh.empty()) { if (!backend_mesh.empty()) {
// The backend has generated a valid mesh. Use it
backend_mesh.transform(po->trafo().inverse());
m_volumes.volumes.emplace_back(new GLVolume());
GLVolume* new_volume = m_volumes.volumes.back();
new_volume->model.init_from(backend_mesh);
new_volume->set_instance_transformation(po->model_object()->instances[m_parent.get_selection().get_instance_idx()]->get_transformation());
new_volume->set_sla_shift_z(po->get_current_elevation());
new_volume->mesh_raycaster = std::make_unique<GUI::MeshRaycaster>(backend_mesh);
auto last_comp_step = static_cast<int>(po->last_completed_step()); auto last_comp_step = static_cast<int>(po->last_completed_step());
if (last_comp_step == slaposCount) if (last_comp_step == slaposCount)
last_comp_step = -1; last_comp_step = -1;
m_input_enabled = last_comp_step >= m_min_sla_print_object_step; m_input_enabled = last_comp_step >= m_min_sla_print_object_step;
if (m_input_enabled)
new_volume->selected = true; // to set the proper color const int object_idx = m_parent.get_selection().get_object_idx();
else const int instance_idx = m_parent.get_selection().get_instance_idx();
new_volume->set_color(DISABLED_COLOR); const Geometry::Transformation& inst_trafo = po->model_object()->instances[instance_idx]->get_transformation();
const double current_elevation = po->get_current_elevation();
auto add_volume = [this, object_idx, instance_idx, &inst_trafo, current_elevation](const TriangleMesh& mesh, int volume_id, bool add_mesh_raycaster = false) {
GLVolume* volume = m_volumes.volumes.emplace_back(new GLVolume());
volume->model.init_from(mesh);
volume->set_instance_transformation(inst_trafo);
volume->set_sla_shift_z(current_elevation);
if (add_mesh_raycaster)
volume->mesh_raycaster = std::make_unique<GUI::MeshRaycaster>(mesh);
if (m_input_enabled)
volume->selected = true; // to set the proper color
else
volume->set_color(DISABLED_COLOR);
volume->composite_id = GLVolume::CompositeID(object_idx, volume_id, instance_idx);
};
const Transform3d po_trafo_inverse = po->trafo().inverse();
// main mesh
backend_mesh.transform(po_trafo_inverse);
add_volume(backend_mesh, 0, true);
// supports mesh
TriangleMesh supports_mesh = po->support_mesh();
if (!supports_mesh.empty()) {
supports_mesh.transform(po_trafo_inverse);
add_volume(supports_mesh, -int(slaposSupportTree));
}
// pad mesh
TriangleMesh pad_mesh = po->pad_mesh();
if (!pad_mesh.empty()) {
pad_mesh.transform(po_trafo_inverse);
add_volume(pad_mesh, -int(slaposPad));
}
} }
if (m_volumes.volumes.empty()) { if (m_volumes.volumes.empty()) {
@ -110,16 +138,20 @@ void GLGizmoSlaBase::render_volumes()
clipping_plane.set_normal(-clipping_plane.get_normal()); clipping_plane.set_normal(-clipping_plane.get_normal());
m_volumes.set_clipping_plane(clipping_plane.get_data()); m_volumes.set_clipping_plane(clipping_plane.get_data());
m_volumes.render(GLVolumeCollection::ERenderType::Opaque, false, camera.get_view_matrix(), camera.get_projection_matrix()); for (GLVolume* v : m_volumes.volumes) {
shader->stop_using(); v->is_active = m_show_sla_supports || (!v->is_sla_pad() && !v->is_sla_support());
}
m_volumes.render(GLVolumeCollection::ERenderType::Opaque, true, camera.get_view_matrix(), camera.get_projection_matrix());
shader->stop_using();
} }
void GLGizmoSlaBase::register_volume_raycasters_for_picking() void GLGizmoSlaBase::register_volume_raycasters_for_picking()
{ {
for (size_t i = 0; i < m_volumes.volumes.size(); ++i) { for (size_t i = 0; i < m_volumes.volumes.size(); ++i) {
const GLVolume* v = m_volumes.volumes[i]; const GLVolume* v = m_volumes.volumes[i];
m_volume_raycasters.emplace_back(m_parent.add_raycaster_for_picking(SceneRaycaster::EType::Gizmo, VOLUME_RAYCASTERS_BASE_ID + (int)i, *v->mesh_raycaster, v->world_matrix())); if (!v->is_sla_pad() && !v->is_sla_support())
m_volume_raycasters.emplace_back(m_parent.add_raycaster_for_picking(SceneRaycaster::EType::Gizmo, VOLUME_RAYCASTERS_BASE_ID + (int)i, *v->mesh_raycaster, v->world_matrix()));
} }
} }

View File

@ -40,11 +40,15 @@ protected:
bool unproject_on_mesh(const Vec2d& mouse_pos, std::pair<Vec3f, Vec3f>& pos_and_normal); bool unproject_on_mesh(const Vec2d& mouse_pos, std::pair<Vec3f, Vec3f>& pos_and_normal);
bool are_sla_supports_shown() const { return m_show_sla_supports; }
void show_sla_supports(bool show) { m_show_sla_supports = show; }
const GLVolumeCollection &volumes() const { return m_volumes; } const GLVolumeCollection &volumes() const { return m_volumes; }
private: private:
GLVolumeCollection m_volumes; GLVolumeCollection m_volumes;
bool m_input_enabled{ false }; bool m_input_enabled{ false };
bool m_show_sla_supports{ false };
int m_min_sla_print_object_step{ -1 }; int m_min_sla_print_object_step{ -1 };
std::vector<std::shared_ptr<SceneRaycasterItem>> m_volume_raycasters; std::vector<std::shared_ptr<SceneRaycasterItem>> m_volume_raycasters;
}; };

View File

@ -24,8 +24,10 @@ namespace Slic3r {
namespace GUI { namespace GUI {
GLGizmoSlaSupports::GLGizmoSlaSupports(GLCanvas3D& parent, const std::string& icon_filename, unsigned int sprite_id) GLGizmoSlaSupports::GLGizmoSlaSupports(GLCanvas3D& parent, const std::string& icon_filename, unsigned int sprite_id)
: GLGizmoSlaBase(parent, icon_filename, sprite_id, slaposDrillHoles) : GLGizmoSlaBase(parent, icon_filename, sprite_id, slaposDrillHoles)
{} {
show_sla_supports(true);
}
bool GLGizmoSlaSupports::on_init() bool GLGizmoSlaSupports::on_init()
{ {
@ -126,6 +128,7 @@ void GLGizmoSlaSupports::on_render()
m_selection_rectangle.render(m_parent); m_selection_rectangle.render(m_parent);
m_c->object_clipper()->render_cut(); m_c->object_clipper()->render_cut();
m_c->supports_clipper()->render_cut();
glsafe(::glDisable(GL_BLEND)); glsafe(::glDisable(GL_BLEND));
} }

View File

@ -453,83 +453,74 @@ void SupportsClipper::on_update()
if (! mo || ! is_sla) if (! mo || ! is_sla)
return; return;
const GLCanvas3D* canvas = get_pool()->get_canvas(); const SLAPrintObject* po = get_pool()->selection_info()->print_object();
const PrintObjects& print_objects = canvas->sla_print()->objects(); if (po == nullptr)
const SLAPrintObject* print_object = (m_print_object_idx >= 0 && m_print_object_idx < int(print_objects.size())) return;
? print_objects[m_print_object_idx]
: nullptr;
// Find the respective SLAPrintObject. if (po->get_mesh_to_print() == nullptr) {
if (m_print_object_idx < 0 || m_print_objects_count != int(print_objects.size())) { // The object has been not sliced yet. We better dump the cached data.
m_print_objects_count = print_objects.size(); m_supports_clipper.reset();
m_print_object_idx = -1; m_pad_clipper.reset();
for (const SLAPrintObject* po : print_objects) { return;
++m_print_object_idx;
if (po->model_object()->id() == mo->id()) {
print_object = po;
break;
}
}
} }
if (print_object const TriangleMesh& support_mesh = po->support_mesh();
&& print_object->is_step_done(slaposSupportTree) if (support_mesh.empty()) {
&& ! print_object->support_mesh().empty()) // The supports are not available yet. We better dump the cached data.
{ m_supports_clipper.reset();
// If the supports are already calculated, save the timestamp of the respective step }
// so we can later tell they were recalculated. else {
size_t timestamp = print_object->step_state_with_timestamp(slaposSupportTree).timestamp; m_supports_clipper.reset(new MeshClipper);
if (! m_clipper || timestamp != m_old_timestamp) { m_supports_clipper->set_mesh(support_mesh.its);
// The timestamp has changed. }
m_clipper.reset(new MeshClipper);
// The mesh should already have the shared vertices calculated. const TriangleMesh& pad_mesh = po->pad_mesh();
m_clipper->set_mesh(print_object->support_mesh().its); if (pad_mesh.empty()) {
m_old_timestamp = timestamp; // The supports are not available yet. We better dump the cached data.
} m_pad_clipper.reset();
}
else {
m_pad_clipper.reset(new MeshClipper);
m_pad_clipper->set_mesh(pad_mesh.its);
} }
else
// The supports are not valid. We better dump the cached data.
m_clipper.reset();
} }
void SupportsClipper::on_release() void SupportsClipper::on_release()
{ {
m_clipper.reset(); m_supports_clipper.reset();
m_old_timestamp = 0; m_pad_clipper.reset();
m_print_object_idx = -1; m_print_object_idx = -1;
} }
void SupportsClipper::render_cut() const void SupportsClipper::render_cut() const
{ {
const CommonGizmosDataObjects::ObjectClipper* ocl = get_pool()->object_clipper(); const CommonGizmosDataObjects::ObjectClipper* ocl = get_pool()->object_clipper();
if (ocl->get_position() == 0. if (ocl->get_position() == 0.)
|| ! m_clipper)
return; return;
const SLAPrintObject* po = get_pool()->selection_info()->print_object();
if (po == nullptr)
return;
Geometry::Transformation po_trafo(po->trafo());
const SelectionInfo* sel_info = get_pool()->selection_info(); const SelectionInfo* sel_info = get_pool()->selection_info();
const ModelObject* mo = sel_info->model_object(); Geometry::Transformation inst_trafo = sel_info->model_object()->instances[sel_info->get_active_instance()]->get_transformation();
const Geometry::Transformation inst_trafo = mo->instances[sel_info->get_active_instance()]->get_transformation(); inst_trafo = Geometry::Transformation(inst_trafo.get_matrix() * po_trafo.get_matrix().inverse());
//Geometry::Transformation vol_trafo = mo->volumes.front()->get_transformation(); inst_trafo.set_offset(inst_trafo.get_offset() + Vec3d(0.0, 0.0, sel_info->get_sla_shift()));
Geometry::Transformation trafo = inst_trafo;// * vol_trafo;
trafo.set_offset(trafo.get_offset() + Vec3d(0., 0., sel_info->get_sla_shift()));
if (m_supports_clipper != nullptr) {
m_supports_clipper->set_plane(*ocl->get_clipping_plane());
m_supports_clipper->set_transformation(inst_trafo);
m_supports_clipper->render_cut({ 1.0f, 0.f, 0.37f, 1.0f });
}
// Get transformation of supports if (m_pad_clipper != nullptr) {
Geometry::Transformation supports_trafo = trafo; m_pad_clipper->set_plane(*ocl->get_clipping_plane());
supports_trafo.set_scaling_factor(Vec3d::Ones()); m_pad_clipper->set_transformation(inst_trafo);
supports_trafo.set_offset(Vec3d(trafo.get_offset()(0), trafo.get_offset()(1), sel_info->get_sla_shift())); m_pad_clipper->render_cut({ 0.6f, 0.f, 0.222f, 1.0f });
supports_trafo.set_rotation(Vec3d(0., 0., trafo.get_rotation()(2))); }
// I don't know why, but following seems to be correct.
supports_trafo.set_mirror(Vec3d(trafo.get_mirror()(0) * trafo.get_mirror()(1) * trafo.get_mirror()(2),
1,
1.));
m_clipper->set_plane(*ocl->get_clipping_plane());
m_clipper->set_transformation(supports_trafo);
m_clipper->render_cut({ 1.0f, 0.f, 0.37f, 1.0f });
m_clipper->render_contour({ 1.f, 1.f, 1.f, 1.f });
} }

View File

@ -293,10 +293,10 @@ protected:
void on_release() override; void on_release() override;
private: private:
size_t m_old_timestamp = 0;
int m_print_object_idx = -1; int m_print_object_idx = -1;
int m_print_objects_count = 0; int m_print_objects_count = 0;
std::unique_ptr<MeshClipper> m_clipper; std::unique_ptr<MeshClipper> m_supports_clipper;
std::unique_ptr<MeshClipper> m_pad_clipper;
}; };
} // namespace CommonGizmosDataObjects } // namespace CommonGizmosDataObjects

View File

@ -2,6 +2,9 @@
#include "libslic3r/BuildVolume.hpp" #include "libslic3r/BuildVolume.hpp"
#include "libslic3r/Model.hpp" #include "libslic3r/Model.hpp"
#include "libslic3r/Print.hpp"
#include "libslic3r/SLAPrint.hpp"
#include "libslic3r/Geometry/ConvexHull.hpp"
#include "slic3r/GUI/Plater.hpp" #include "slic3r/GUI/Plater.hpp"
#include "slic3r/GUI/GLCanvas3D.hpp" #include "slic3r/GUI/GLCanvas3D.hpp"
@ -11,6 +14,7 @@
#include "slic3r/GUI/NotificationManager.hpp" #include "slic3r/GUI/NotificationManager.hpp"
#include "slic3r/GUI/format.hpp" #include "slic3r/GUI/format.hpp"
#include "libnest2d/common.hpp" #include "libnest2d/common.hpp"
#include <numeric> #include <numeric>
@ -93,28 +97,28 @@ void ArrangeJob::prepare_all() {
void ArrangeJob::prepare_selected() { void ArrangeJob::prepare_selected() {
clear_input(); clear_input();
Model &model = m_plater->model(); Model &model = m_plater->model();
double stride = bed_stride(m_plater); double stride = bed_stride(m_plater);
std::vector<const Selection::InstanceIdxsList *> std::vector<const Selection::InstanceIdxsList *>
obj_sel(model.objects.size(), nullptr); obj_sel(model.objects.size(), nullptr);
for (auto &s : m_plater->get_selection().get_content()) for (auto &s : m_plater->get_selection().get_content())
if (s.first < int(obj_sel.size())) if (s.first < int(obj_sel.size()))
obj_sel[size_t(s.first)] = &s.second; obj_sel[size_t(s.first)] = &s.second;
// Go through the objects and check if inside the selection // Go through the objects and check if inside the selection
for (size_t oidx = 0; oidx < model.objects.size(); ++oidx) { for (size_t oidx = 0; oidx < model.objects.size(); ++oidx) {
const Selection::InstanceIdxsList * instlist = obj_sel[oidx]; const Selection::InstanceIdxsList * instlist = obj_sel[oidx];
ModelObject *mo = model.objects[oidx]; ModelObject *mo = model.objects[oidx];
std::vector<bool> inst_sel(mo->instances.size(), false); std::vector<bool> inst_sel(mo->instances.size(), false);
if (instlist) if (instlist)
for (auto inst_id : *instlist) for (auto inst_id : *instlist)
inst_sel[size_t(inst_id)] = true; inst_sel[size_t(inst_id)] = true;
for (size_t i = 0; i < inst_sel.size(); ++i) { for (size_t i = 0; i < inst_sel.size(); ++i) {
ModelInstance * mi = mo->instances[i]; ModelInstance * mi = mo->instances[i];
ArrangePolygon &&ap = get_arrange_poly_(mi); ArrangePolygon &&ap = get_arrange_poly_(mi);
@ -123,11 +127,11 @@ void ArrangeJob::prepare_selected() {
(inst_sel[i] ? m_selected : (inst_sel[i] ? m_selected :
m_unselected) : m_unselected) :
m_unprintable; m_unprintable;
cont.emplace_back(std::move(ap)); cont.emplace_back(std::move(ap));
} }
} }
if (auto wti = get_wipe_tower(*m_plater)) { if (auto wti = get_wipe_tower(*m_plater)) {
ArrangePolygon &&ap = get_arrange_poly(wti, m_plater); ArrangePolygon &&ap = get_arrange_poly(wti, m_plater);
@ -135,14 +139,120 @@ void ArrangeJob::prepare_selected() {
m_unselected; m_unselected;
cont.emplace_back(std::move(ap)); cont.emplace_back(std::move(ap));
} }
// If the selection was empty arrange everything // If the selection was empty arrange everything
if (m_selected.empty()) m_selected.swap(m_unselected); if (m_selected.empty())
m_selected.swap(m_unselected);
// The strides have to be removed from the fixed items. For the // The strides have to be removed from the fixed items. For the
// arrangeable (selected) items bed_idx is ignored and the // arrangeable (selected) items bed_idx is ignored and the
// translation is irrelevant. // translation is irrelevant.
for (auto &p : m_unselected) p.translation(X) -= p.bed_idx * stride; for (auto &p : m_unselected)
p.translation(X) -= p.bed_idx * stride;
}
static void update_arrangepoly_slaprint(arrangement::ArrangePolygon &ret,
const SLAPrintObject &po,
const ModelInstance &inst)
{
// The 1.1 multiplier is a safety gap, as the offset might be bigger
// in sharp edges of a polygon, depending on clipper's offset algorithm
coord_t pad_infl = 0;
{
double infl = po.config().pad_enable.getBool() * (
po.config().pad_brim_size.getFloat() +
po.config().pad_around_object.getBool() *
po.config().pad_object_gap.getFloat() );
pad_infl = scaled(1.1 * infl);
}
auto laststep = po.last_completed_step();
if (laststep < slaposCount && laststep > slaposSupportTree) {
auto omesh = po.get_mesh_to_print();
auto &smesh = po.support_mesh();
Vec3d rotation = inst.get_rotation();
rotation.z() = 0.;
Transform3f trafo_instance =
Geometry::assemble_transform(inst.get_offset().z() * Vec3d::UnitZ(),
rotation,
inst.get_scaling_factor(),
inst.get_mirror()).cast<float>();
trafo_instance = trafo_instance * po.trafo().cast<float>().inverse();
auto polys = reserve_vector<Polygon>(3);
auto zlvl = -po.get_elevation();
if (omesh) {
polys.emplace_back(its_convex_hull_2d_above(*omesh, trafo_instance, zlvl));
ret.poly.contour = polys.back();
ret.poly.holes = {};
}
polys.emplace_back(its_convex_hull_2d_above(smesh.its, trafo_instance, zlvl));
ret.poly.contour = Geometry::convex_hull(polys);
ret.poly.holes = {};
}
ret.inflation = pad_infl;
}
static coord_t brim_offset(const PrintObject &po, const ModelInstance &inst)
{
const BrimType brim_type = po.config().brim_type.value;
const float brim_separation = po.config().brim_separation.getFloat();
const float brim_width = po.config().brim_width.getFloat();
const bool has_outer_brim = brim_type == BrimType::btOuterOnly ||
brim_type == BrimType::btOuterAndInner;
// How wide is the brim? (in scaled units)
return has_outer_brim ? scaled(brim_width + brim_separation) : 0;
}
template<class It>
Polygon support_layers_chull (Points &pts, It from_lyr, It to_lyr) {
size_t cap = 0;
for (auto it = from_lyr; it != to_lyr; ++it) {
for (const ExPolygon &expoly : (*it)->support_islands)
cap += expoly.contour.points.size();
}
pts.reserve(pts.size() + cap);
for (auto it = from_lyr; it != to_lyr; ++it) {
for (const ExPolygon &expoly : (*it)->support_islands)
std::copy(expoly.contour.begin(), expoly.contour.end(),
std::back_inserter(pts));
}
Polygon ret = Geometry::convex_hull(pts);
return ret;
}
static void update_arrangepoly_fffprint(arrangement::ArrangePolygon &ret,
const PrintObject &po,
const ModelInstance &inst)
{
auto laststep = po.last_completed_step();
coord_t infl = brim_offset(po, inst);
if (laststep < posCount && laststep > posSupportMaterial) {
Points pts = std::move(ret.poly.contour.points);
Polygon poly = support_layers_chull(pts,
po.support_layers().begin(),
po.support_layers().end());
ret.poly.contour = std::move(poly);
ret.poly.holes = {};
}
ret.inflation = infl;
} }
arrangement::ArrangePolygon ArrangeJob::get_arrange_poly_(ModelInstance *mi) arrangement::ArrangePolygon ArrangeJob::get_arrange_poly_(ModelInstance *mi)
@ -159,9 +269,38 @@ arrangement::ArrangePolygon ArrangeJob::get_arrange_poly_(ModelInstance *mi)
return ap; return ap;
} }
coord_t get_skirt_offset(const Plater* plater) {
float skirt_inset = 0.f;
// Try to subtract the skirt from the bed shape so we don't arrange outside of it.
if (plater->printer_technology() == ptFFF && plater->fff_print().has_skirt()) {
const auto& print = plater->fff_print();
skirt_inset = print.config().skirts.value * print.skirt_flow().width() +
print.config().skirt_distance.value;
}
return scaled(skirt_inset);
}
void ArrangeJob::prepare() void ArrangeJob::prepare()
{ {
wxGetKeyState(WXK_SHIFT) ? prepare_selected() : prepare_all(); wxGetKeyState(WXK_SHIFT) ? prepare_selected() : prepare_all();
coord_t min_offset = 0;
for (auto &ap : m_selected) {
min_offset = std::max(ap.inflation, min_offset);
}
if (m_plater->printer_technology() == ptSLA) {
// Apply the max offset for all the objects
for (auto &ap : m_selected) {
ap.inflation = min_offset;
}
} else { // it's fff, brims only need to be minded from bed edges
for (auto &ap : m_selected) {
ap.inflation = 0;
}
m_min_bed_inset = min_offset;
}
} }
void ArrangeJob::process(Ctl &ctl) void ArrangeJob::process(Ctl &ctl)
@ -174,6 +313,8 @@ void ArrangeJob::process(Ctl &ctl)
prepare(); prepare();
params = get_arrange_params(m_plater); params = get_arrange_params(m_plater);
get_bed_shape(*m_plater->config(), bed); get_bed_shape(*m_plater->config(), bed);
coord_t min_inset = get_skirt_offset(m_plater) + m_min_bed_inset;
params.min_bed_distance = std::max(params.min_bed_distance, min_inset);
}).wait(); }).wait();
auto count = unsigned(m_selected.size() + m_unprintable.size()); auto count = unsigned(m_selected.size() + m_unprintable.size());
@ -286,7 +427,26 @@ template<>
arrangement::ArrangePolygon get_arrange_poly(ModelInstance *inst, arrangement::ArrangePolygon get_arrange_poly(ModelInstance *inst,
const Plater * plater) const Plater * plater)
{ {
return get_arrange_poly(PtrWrapper{inst}, plater); auto ap = get_arrange_poly(PtrWrapper{inst}, plater);
auto obj_id = inst->get_object()->id();
if (plater->printer_technology() == ptSLA) {
const SLAPrintObject *po =
plater->sla_print().get_print_object_by_model_object_id(obj_id);
if (po) {
update_arrangepoly_slaprint(ap, *po, *inst);
}
} else {
const PrintObject *po =
plater->fff_print().get_print_object_by_model_object_id(obj_id);
if (po) {
update_arrangepoly_fffprint(ap, *po, *inst);
}
}
return ap;
} }
arrangement::ArrangeParams get_arrange_params(Plater *p) arrangement::ArrangeParams get_arrange_params(Plater *p)

View File

@ -21,6 +21,8 @@ class ArrangeJob : public Job
ArrangePolygons m_selected, m_unselected, m_unprintable; ArrangePolygons m_selected, m_unselected, m_unprintable;
std::vector<ModelInstance*> m_unarranged; std::vector<ModelInstance*> m_unarranged;
coord_t m_min_bed_inset = 0.;
Plater *m_plater; Plater *m_plater;
// clear m_selected and m_unselected, reserve space for next usage // clear m_selected and m_unselected, reserve space for next usage
@ -102,6 +104,8 @@ arrangement::ArrangePolygon get_arrange_poly(ModelInstance *inst,
arrangement::ArrangeParams get_arrange_params(Plater *p); arrangement::ArrangeParams get_arrange_params(Plater *p);
coord_t get_skirt_offset(const Plater* plater);
}} // namespace Slic3r::GUI }} // namespace Slic3r::GUI
#endif // ARRANGEJOB_HPP #endif // ARRANGEJOB_HPP

View File

@ -483,10 +483,46 @@ TriangleMesh priv::create_default_mesh()
return triangle_mesh; return triangle_mesh;
} }
namespace{
void update_volume_name(const ModelVolume &volume, const ObjectList *obj_list)
{
if (obj_list == nullptr)
return;
const std::vector<ModelObject *>* objects = obj_list->objects();
if (objects == nullptr)
return;
int object_idx = -1;
int volume_idx = -1;
for (size_t oi = 0; oi < objects->size(); ++oi) {
const ModelObject *mo = objects->at(oi);
if (mo == nullptr)
continue;
if (volume.get_object()->id() != mo->id())
continue;
const ModelVolumePtrs& volumes = mo->volumes;
for (size_t vi = 0; vi < volumes.size(); ++vi) {
const ModelVolume *mv = volumes[vi];
if (mv == nullptr)
continue;
if (mv->id() == volume.id()){
object_idx = static_cast<int>(oi);
volume_idx = static_cast<int>(vi);
break;
}
}
if (volume_idx > 0)
break;
}
obj_list->update_name_in_list(object_idx, volume_idx);
}
}
void UpdateJob::update_volume(ModelVolume *volume, void UpdateJob::update_volume(ModelVolume *volume,
TriangleMesh &&mesh, TriangleMesh &&mesh,
const TextConfiguration &text_configuration, const TextConfiguration &text_configuration,
const std::string &volume_name) std::string_view volume_name)
{ {
// check inputs // check inputs
bool is_valid_input = bool is_valid_input =
@ -506,19 +542,12 @@ void UpdateJob::update_volume(ModelVolume *volume,
// discard information about rotation, should not be stored in volume // discard information about rotation, should not be stored in volume
volume->text_configuration->style.prop.angle.reset(); volume->text_configuration->style.prop.angle.reset();
GUI_App &app = wxGetApp(); // may be move to input GUI_App &app = wxGetApp(); // may be move ObjectList and Plater to input?
GLCanvas3D *canvas = app.plater()->canvas3D();
const Selection &selection = canvas->get_selection();
const GLVolume *gl_volume = selection.get_volume(*selection.get_volume_idxs().begin());
int object_idx = gl_volume->object_idx();
// update volume name in right panel( volume / object name)
if (volume->name != volume_name) { if (volume->name != volume_name) {
volume->name = volume_name; volume->name = volume_name;
update_volume_name(*volume, app.obj_list());
// update volume name in right panel( volume / object name)
int volume_idx = gl_volume->volume_idx();
ObjectList *obj_list = app.obj_list();
obj_list->update_name_in_list(object_idx, volume_idx);
} }
// When text is object. // When text is object.
@ -528,11 +557,12 @@ void UpdateJob::update_volume(ModelVolume *volume,
volume->get_object()->ensure_on_bed(); volume->get_object()->ensure_on_bed();
// redraw scene // redraw scene
bool refresh_immediately = false; Plater *plater = app.plater();
canvas->reload_scene(refresh_immediately); if (plater == nullptr)
return;
// Change buttons "Export G-code" into "Slice now" // Update Model and redraw scene
canvas->post_event(SimpleEvent(EVT_GLCANVAS_SCHEDULE_BACKGROUND_PROCESS)); plater->update();
} }
void priv::update_volume(TriangleMesh &&mesh, const DataUpdate &data, Transform3d* tr) void priv::update_volume(TriangleMesh &&mesh, const DataUpdate &data, Transform3d* tr)
@ -646,8 +676,9 @@ void priv::create_volume(
if (manager.get_current_type() != GLGizmosManager::Emboss) if (manager.get_current_type() != GLGizmosManager::Emboss)
manager.open_gizmo(GLGizmosManager::Emboss); manager.open_gizmo(GLGizmosManager::Emboss);
// redraw scene // update model and redraw scene
canvas->reload_scene(true); //canvas->reload_scene(true);
plater->update();
} }
ModelVolume *priv::get_volume(ModelObjectPtrs &objects, ModelVolume *priv::get_volume(ModelObjectPtrs &objects,

View File

@ -145,7 +145,7 @@ public:
static void update_volume(ModelVolume *volume, static void update_volume(ModelVolume *volume,
TriangleMesh &&mesh, TriangleMesh &&mesh,
const TextConfiguration &text_configuration, const TextConfiguration &text_configuration,
const std::string &volume_name); std::string_view volume_name);
}; };
struct SurfaceVolumeData struct SurfaceVolumeData

View File

@ -18,6 +18,7 @@ void FillBedJob::prepare()
m_selected.clear(); m_selected.clear();
m_unselected.clear(); m_unselected.clear();
m_bedpts.clear(); m_bedpts.clear();
m_min_bed_inset = 0.;
m_object_idx = m_plater->get_selected_object_idx(); m_object_idx = m_plater->get_selected_object_idx();
if (m_object_idx == -1) if (m_object_idx == -1)
@ -29,7 +30,7 @@ void FillBedJob::prepare()
m_selected.reserve(model_object->instances.size()); m_selected.reserve(model_object->instances.size());
for (ModelInstance *inst : model_object->instances) for (ModelInstance *inst : model_object->instances)
if (inst->printable) { if (inst->printable) {
ArrangePolygon ap = get_arrange_poly(PtrWrapper{inst}, m_plater); ArrangePolygon ap = get_arrange_poly(inst, m_plater);
// Existing objects need to be included in the result. Only // Existing objects need to be included in the result. Only
// the needed amount of object will be added, no more. // the needed amount of object will be added, no more.
++ap.priority; ++ap.priority;
@ -101,6 +102,23 @@ void FillBedJob::prepare()
for (auto &p : m_unselected) for (auto &p : m_unselected)
if (p.bed_idx > 0) if (p.bed_idx > 0)
p.translation(X) -= p.bed_idx * stride; p.translation(X) -= p.bed_idx * stride;
coord_t min_offset = 0;
for (auto &ap : m_selected) {
min_offset = std::max(ap.inflation, min_offset);
}
if (m_plater->printer_technology() == ptSLA) {
// Apply the max offset for all the objects
for (auto &ap : m_selected) {
ap.inflation = min_offset;
}
} else { // it's fff, brims only need to be minded from bed edges
for (auto &ap : m_selected) {
ap.inflation = 0;
}
m_min_bed_inset = min_offset;
}
} }
void FillBedJob::process(Ctl &ctl) void FillBedJob::process(Ctl &ctl)
@ -110,6 +128,8 @@ void FillBedJob::process(Ctl &ctl)
ctl.call_on_main_thread([this, &params] { ctl.call_on_main_thread([this, &params] {
prepare(); prepare();
params = get_arrange_params(m_plater); params = get_arrange_params(m_plater);
coord_t min_inset = get_skirt_offset(m_plater) + m_min_bed_inset;
params.min_bed_distance = std::max(params.min_bed_distance, min_inset);
}).wait(); }).wait();
ctl.update_status(0, statustxt); ctl.update_status(0, statustxt);

View File

@ -16,6 +16,7 @@ class FillBedJob : public Job
ArrangePolygons m_selected; ArrangePolygons m_selected;
ArrangePolygons m_unselected; ArrangePolygons m_unselected;
coord_t m_min_bed_inset = 0.;
Points m_bedpts; Points m_bedpts;

View File

@ -1396,6 +1396,18 @@ void Selection::scale_to_fit_print_volume(const BuildVolume& volume)
// used to keep track whether the undo/redo snapshot has already been taken // used to keep track whether the undo/redo snapshot has already been taken
bool undoredo_snapshot = false; bool undoredo_snapshot = false;
if (wxGetApp().plater()->printer_technology() == ptSLA) {
// remove SLA auxiliary volumes from the selection to ensure that the proper bounding box is calculated
std::vector<unsigned int> to_remove;
for (unsigned int i : m_list) {
if ((*m_volumes)[i]->volume_idx() < 0)
to_remove.push_back(i);
}
if (!to_remove.empty())
remove_volumes(m_mode, to_remove);
}
switch (volume.type()) switch (volume.type())
{ {
case BuildVolume::Type::Rectangle: { undoredo_snapshot = fit_rectangle(volume, !undoredo_snapshot); break; } case BuildVolume::Type::Rectangle: { undoredo_snapshot = fit_rectangle(volume, !undoredo_snapshot); break; }
@ -3006,7 +3018,7 @@ static void verify_instances_rotation_synchronized(const Model &model, const GLV
continue; continue;
const Transform3d::ConstLinearPart& rotation0 = volumes[idx_volume_first]->get_instance_transformation().get_matrix().linear(); const Transform3d::ConstLinearPart& rotation0 = volumes[idx_volume_first]->get_instance_transformation().get_matrix().linear();
for (int i = idx_volume_first + 1; i < (int)volumes.size(); ++i) for (int i = idx_volume_first + 1; i < (int)volumes.size(); ++i)
if (volumes[i]->object_idx() == idx_object) { if (volumes[i]->object_idx() == idx_object && volumes[i]->volume_idx() >= 0) {
const Transform3d::ConstLinearPart& rotation = volumes[i]->get_instance_transformation().get_matrix().linear(); const Transform3d::ConstLinearPart& rotation = volumes[i]->get_instance_transformation().get_matrix().linear();
assert(is_rotation_xy_synchronized(rotation, rotation0)); assert(is_rotation_xy_synchronized(rotation, rotation0));
} }

View File

@ -9,50 +9,6 @@
#include "libslic3r/Emboss.hpp" #include "libslic3r/Emboss.hpp"
namespace Slic3r::GUI { namespace Slic3r::GUI {
/// <summary>
/// Calculate offset from mouse position to center of text
/// </summary>
/// <param name="screen_coor">Position on screen[in Px] e.g. mouse position</param>
/// <param name="volume">Selected volume(text)</param>
/// <param name="camera">Actual position and view direction of camera</param>
/// <returns>Offset in screen coordinate</returns>
static Vec2d calc_screen_offset_to_volume_center(const Vec2d &screen_coor, const ModelVolume &volume, const Camera &camera)
{
const Transform3d &volume_tr = volume.get_matrix();
assert(volume.text_configuration.has_value());
auto calc_offset = [&screen_coor, &volume_tr, &camera, &volume](const Transform3d &instrance_tr) -> Vec2d {
Transform3d to_world = instrance_tr * volume_tr;
// Use fix of .3mf loaded tranformation when exist
if (volume.text_configuration->fix_3mf_tr.has_value())
to_world = to_world * (*volume.text_configuration->fix_3mf_tr);
// zero point of volume in world coordinate system
Vec3d volume_center = to_world.translation();
// screen coordinate of volume center
Vec2i coor = CameraUtils::project(camera, volume_center);
return coor.cast<double>() - screen_coor;
};
auto object = volume.get_object();
assert(!object->instances.empty());
// Speed up for one instance
if (object->instances.size() == 1)
return calc_offset(object->instances.front()->get_matrix());
Vec2d nearest_offset;
double nearest_offset_size = std::numeric_limits<double>::max();
for (const ModelInstance *instance : object->instances) {
Vec2d offset = calc_offset(instance->get_matrix());
double offset_size = offset.norm();
if (nearest_offset_size < offset_size)
continue;
nearest_offset_size = offset_size;
nearest_offset = offset;
}
return nearest_offset;
}
// Calculate scale in world for check in debug // Calculate scale in world for check in debug
[[maybe_unused]] static std::optional<double> calc_scale(const Matrix3d &from, const Matrix3d &to, const Vec3d &dir) [[maybe_unused]] static std::optional<double> calc_scale(const Matrix3d &from, const Matrix3d &to, const Vec3d &dir)
@ -109,7 +65,8 @@ bool on_mouse_surface_drag(const wxMouseEvent &mouse_event,
gl_volumes[hovered_idx_] != gl_volume) gl_volumes[hovered_idx_] != gl_volume)
return false; return false;
const ModelObject *object = get_model_object(*gl_volume, canvas.get_model()->objects); const ModelObjectPtrs &objects = canvas.get_model()->objects;
const ModelObject *object = get_model_object(*gl_volume, objects);
assert(object != nullptr); assert(object != nullptr);
if (object == nullptr) if (object == nullptr)
return false; return false;
@ -148,7 +105,26 @@ bool on_mouse_surface_drag(const wxMouseEvent &mouse_event,
// wxCoord == int --> wx/types.h // wxCoord == int --> wx/types.h
Vec2i mouse_coord(mouse_event.GetX(), mouse_event.GetY()); Vec2i mouse_coord(mouse_event.GetX(), mouse_event.GetY());
Vec2d mouse_pos = mouse_coord.cast<double>(); Vec2d mouse_pos = mouse_coord.cast<double>();
Vec2d mouse_offset = calc_screen_offset_to_volume_center(mouse_pos, *volume, camera);
// world_matrix_fixed() without sla shift
Transform3d to_world = world_matrix_fixed(*gl_volume, objects);
// zero point of volume in world coordinate system
Vec3d volume_center = to_world.translation();
// screen coordinate of volume center
Vec2i coor = CameraUtils::project(camera, volume_center);
Vec2d mouse_offset = coor.cast<double>() - mouse_pos;
Vec2d mouse_offset_without_sla_shift = mouse_offset;
if (double sla_shift = gl_volume->get_sla_shift_z(); !is_approx(sla_shift, 0.)) {
Transform3d to_world_without_sla_move = instance->get_matrix() * volume->get_matrix();
if (volume->text_configuration.has_value() && volume->text_configuration->fix_3mf_tr.has_value())
to_world_without_sla_move = to_world_without_sla_move * (*volume->text_configuration->fix_3mf_tr);
// zero point of volume in world coordinate system
volume_center = to_world_without_sla_move.translation();
// screen coordinate of volume center
coor = CameraUtils::project(camera, volume_center);
mouse_offset_without_sla_shift = coor.cast<double>() - mouse_pos;
}
Transform3d volume_tr = gl_volume->get_volume_transformation().get_matrix(); Transform3d volume_tr = gl_volume->get_volume_transformation().get_matrix();
@ -165,7 +141,7 @@ bool on_mouse_surface_drag(const wxMouseEvent &mouse_event,
std::optional<float> start_angle; std::optional<float> start_angle;
if (up_limit.has_value()) if (up_limit.has_value())
start_angle = Emboss::calc_up(world_tr, *up_limit); start_angle = Emboss::calc_up(world_tr, *up_limit);
surface_drag = SurfaceDrag{mouse_offset, world_tr, instance_tr_inv, gl_volume, condition, start_angle}; surface_drag = SurfaceDrag{mouse_offset, world_tr, instance_tr_inv, gl_volume, condition, start_angle, true, mouse_offset_without_sla_shift};
// disable moving with object by mouse // disable moving with object by mouse
canvas.enable_moving(false); canvas.enable_moving(false);
@ -181,7 +157,7 @@ bool on_mouse_surface_drag(const wxMouseEvent &mouse_event,
// wxCoord == int --> wx/types.h // wxCoord == int --> wx/types.h
Vec2i mouse_coord(mouse_event.GetX(), mouse_event.GetY()); Vec2i mouse_coord(mouse_event.GetX(), mouse_event.GetY());
Vec2d mouse_pos = mouse_coord.cast<double>(); Vec2d mouse_pos = mouse_coord.cast<double>();
Vec2d offseted_mouse = mouse_pos + surface_drag->mouse_offset; Vec2d offseted_mouse = mouse_pos + surface_drag->mouse_offset_without_sla_shift;
std::optional<RaycastManager::Hit> hit = ray_from_camera( std::optional<RaycastManager::Hit> hit = ray_from_camera(
raycast_manager, offseted_mouse, camera, &surface_drag->condition); raycast_manager, offseted_mouse, camera, &surface_drag->condition);

View File

@ -39,6 +39,9 @@ struct SurfaceDrag
// Flag whether coordinate hit some volume // Flag whether coordinate hit some volume
bool exist_hit = true; bool exist_hit = true;
// hold screen coor offset of cursor from object center without SLA shift
Vec2d mouse_offset_without_sla_shift;
}; };
/// <summary> /// <summary>

View File

@ -689,4 +689,58 @@ TEST_CASE("Arachne - #8846 - Degenerated Voronoi diagram - Voronoi edges interse
// Total extrusion length should be around 500mm when the part is ok and 680mm when it has perimeters in places where they shouldn't be. // Total extrusion length should be around 500mm when the part is ok and 680mm when it has perimeters in places where they shouldn't be.
REQUIRE(total_extrusion_length <= scaled<int64_t>(500.)); REQUIRE(total_extrusion_length <= scaled<int64_t>(500.));
}
// This test case was distilled from GitHub issue #10034.
// In this test case previous rotation by PI / 6 wasn't able to fix non-planar Voronoi diagram.
TEST_CASE("Arachne - #10034 - Degenerated Voronoi diagram - That wasn't fixed by rotation by PI / 6", "[ArachneDegeneratedDiagram10034RotationNotWorks]") {
Polygon poly_0 = {
Point(43612632, -25179766), Point(58456010, 529710), Point(51074898, 17305660), Point(49390982, 21042355),
Point(48102357, 23840161), Point(46769686, 26629546), Point(45835761, 28472742), Point(45205450, 29623133),
Point(45107431, 29878059), Point(45069846, 30174950), Point(45069846, 50759533), Point(-45069846, 50759533),
Point(-45069852, 29630557), Point(-45105780, 29339980), Point(-45179725, 29130704), Point(-46443313, 26398986),
Point(-52272109, 13471493), Point(-58205450, 95724), Point(-29075091, -50359531), Point(29075086, -50359531),
};
Polygon poly_1 = {
Point(-37733905, 45070445), Point(-37813254, 45116257), Point(-39353851, 47784650), Point(-39353851, 47876274),
Point(-38632470, 49125743), Point(-38553121, 49171555), Point(-33833475, 49171555), Point(-33754126, 49125743),
Point(-33032747, 47876277), Point(-33032747, 47784653), Point(-34007855, 46095721), Point(-34573350, 45116257),
Point(-34652699, 45070445),
};
Polygon poly_2 = {
Point(-44016799, 40706401), Point(-44116953, 40806555), Point(-44116953, 46126289), Point(-44016799, 46226443),
Point(-42211438, 46226443), Point(-42132089, 46180631), Point(-40591492, 43512233), Point(-40591492, 43420609),
Point(-41800123, 41327194), Point(-42132089, 40752213), Point(-42211438, 40706401),
};
Polygon poly_3 = {
Point(6218189, 10966609), Point(6138840, 11012421), Point(4598238, 13680817), Point(4598238, 13772441), Point(6138840, 16440843),
Point(6218189, 16486655), Point(9299389, 16486655), Point(9378738, 16440843), Point(10919340, 13772441), Point(10919340, 13680817),
Point(10149039, 12346618), Point(9378738, 11012421), Point(9299389, 10966609),
};
Polygon poly_4 = {
Point(13576879, 6718065), Point(13497530, 6763877), Point(11956926, 9432278), Point(11956926, 9523902),
Point(13497528, 12192302), Point(13576877, 12238114), Point(16658079, 12238112), Point(16737428, 12192300),
Point(18278031, 9523904), Point(18278031, 9432280), Point(17507729, 8098077), Point(16737428, 6763877),
Point(16658079, 6718065),
};
Polygons polygons = {
poly_0, poly_1, poly_2, poly_3, poly_4,
};
coord_t ext_perimeter_spacing = 407079;
coord_t perimeter_spacing = 407079;
coord_t inset_count = 1;
Arachne::WallToolPaths wall_tool_paths(polygons, ext_perimeter_spacing, perimeter_spacing, inset_count, 0, 0.2, PrintObjectConfig::defaults(), PrintConfig::defaults());
wall_tool_paths.generate();
std::vector<Arachne::VariableWidthLines> perimeters = wall_tool_paths.getToolPaths();
#ifdef ARACHNE_DEBUG_OUT
export_perimeters_to_svg(debug_out_path("arachne-degenerated-diagram-10034-rotation-not-works.svg"), polygons, perimeters, union_ex(wall_tool_paths.getInnerContour()));
#endif
} }