Merge remote-tracking branch 'origin/master' into ys_selection
							
								
								
									
										17
									
								
								resources/icons/cog.svg
									
										
									
									
									
										Normal file
									
								
							
							
						
						|  | @ -0,0 +1,17 @@ | |||
| <?xml version="1.0" encoding="utf-8"?> | ||||
| <!-- Generator: Adobe Illustrator 23.0.3, SVG Export Plug-In . SVG Version: 6.00 Build 0)  --> | ||||
| <svg version="1.0" id="Layer_1" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" x="0px" y="0px" | ||||
| 	 viewBox="0 0 16 16" enable-background="new 0 0 16 16" xml:space="preserve"> | ||||
| <g id="machine_x2B_cog"> | ||||
| 	<path fill="#808080" d="M13.77,6.39c-0.13-0.47-0.32-0.92-0.55-1.33l0.43-1.3l-1.41-1.41l-1.3,0.43c-0.42-0.23-0.86-0.42-1.33-0.55 | ||||
| 		L9,1H7L6.39,2.23C5.92,2.36,5.47,2.54,5.06,2.78l-1.3-0.43L2.34,3.76l0.43,1.3C2.54,5.47,2.36,5.92,2.23,6.39L1,7v2l1.23,0.61 | ||||
| 		c0.13,0.47,0.32,0.92,0.55,1.33l-0.43,1.3l1.41,1.41l1.3-0.43c0.42,0.23,0.86,0.42,1.33,0.55L7,15h2l0.61-1.23 | ||||
| 		c0.47-0.13,0.92-0.32,1.33-0.55l1.3,0.43l1.41-1.41l-0.43-1.3c0.23-0.42,0.42-0.86,0.55-1.33L15,9V7L13.77,6.39z M8,13 | ||||
| 		c-2.76,0-5-2.24-5-5s2.24-5,5-5s5,2.24,5,5S10.76,13,8,13z"/> | ||||
| 	<path fill="#ED6B21" d="M11.3,7.08c-0.07-0.27-0.18-0.52-0.31-0.76l0.25-0.74l-0.81-0.81L9.68,5.01C9.45,4.88,9.19,4.78,8.92,4.7 | ||||
| 		L8.57,4H7.43L7.08,4.7C6.81,4.78,6.55,4.88,6.32,5.01L5.58,4.77L4.77,5.58l0.25,0.74C4.88,6.55,4.78,6.81,4.7,7.08L4,7.43v1.14 | ||||
| 		l0.7,0.35c0.07,0.27,0.18,0.52,0.31,0.76l-0.25,0.74l0.81,0.81l0.74-0.25c0.24,0.13,0.49,0.24,0.76,0.31L7.43,12h1.14l0.35-0.7 | ||||
| 		c0.27-0.07,0.52-0.18,0.76-0.31l0.74,0.25l0.81-0.81l-0.25-0.74c0.13-0.24,0.24-0.49,0.31-0.76L12,8.57V7.43L11.3,7.08z M8,10.86 | ||||
| 		c-1.58,0-2.86-1.28-2.86-2.86S6.42,5.14,8,5.14S10.86,6.42,10.86,8S9.58,10.86,8,10.86z"/> | ||||
| </g> | ||||
| </svg> | ||||
| After Width: | Height: | Size: 1.5 KiB | 
							
								
								
									
										25
									
								
								resources/icons/cooling.svg
									
										
									
									
									
										Normal file
									
								
							
							
						
						|  | @ -0,0 +1,25 @@ | |||
| <?xml version="1.0" encoding="utf-8"?> | ||||
| <!-- Generator: Adobe Illustrator 23.0.3, SVG Export Plug-In . SVG Version: 6.00 Build 0)  --> | ||||
| <svg version="1.0" id="Layer_1" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" x="0px" y="0px" | ||||
| 	 viewBox="0 0 16 16" enable-background="new 0 0 16 16" xml:space="preserve"> | ||||
| <g id="cooling"> | ||||
| 	<path fill="#808080" d="M14,1H2C1.45,1,1,1.45,1,2v12c0,0.55,0.45,1,1,1h12c0.55,0,1-0.45,1-1V2C15,1.45,14.55,1,14,1z M8,14 | ||||
| 		c-3.31,0-6-2.69-6-6s2.69-6,6-6s6,2.69,6,6S11.31,14,8,14z"/> | ||||
| 	<g> | ||||
| 		<path fill="#ED6B21" d="M7.86,7.14C7.94,7.41,8,7.7,8,8c0,1.66-1.34,3-3,3c-0.3,0-0.59-0.06-0.86-0.14C4.51,12.09,5.64,13,7,13 | ||||
| 			c1.66,0,3-1.34,3-3C10,8.64,9.09,7.51,7.86,7.14z"/> | ||||
| 	</g> | ||||
| 	<g> | ||||
| 		<path fill="#ED6B21" d="M8,8C6.34,8,5,6.66,5,5c0-0.3,0.06-0.59,0.14-0.86C3.91,4.51,3,5.64,3,7c0,1.66,1.34,3,3,3 | ||||
| 			c1.36,0,2.49-0.91,2.86-2.14C8.59,7.94,8.3,8,8,8z"/> | ||||
| 	</g> | ||||
| 	<g> | ||||
| 		<path fill="#ED6B21" d="M10,6C8.64,6,7.51,6.91,7.14,8.14C7.41,8.06,7.7,8,8,8c1.66,0,3,1.34,3,3c0,0.3-0.06,0.59-0.14,0.86 | ||||
| 			C12.09,11.49,13,10.36,13,9C13,7.34,11.66,6,10,6z"/> | ||||
| 	</g> | ||||
| 	<g> | ||||
| 		<path fill="#ED6B21" d="M8,8c0-1.66,1.34-3,3-3c0.3,0,0.59,0.06,0.86,0.14C11.49,3.91,10.36,3,9,3C7.34,3,6,4.34,6,6 | ||||
| 			c0,1.36,0.91,2.49,2.14,2.86C8.06,8.59,8,8.3,8,8z"/> | ||||
| 	</g> | ||||
| </g> | ||||
| </svg> | ||||
| After Width: | Height: | Size: 1.3 KiB | 
							
								
								
									
										15
									
								
								resources/icons/funnel.svg
									
										
									
									
									
										Normal file
									
								
							
							
						
						|  | @ -0,0 +1,15 @@ | |||
| <?xml version="1.0" encoding="utf-8"?> | ||||
| <!-- Generator: Adobe Illustrator 23.0.3, SVG Export Plug-In . SVG Version: 6.00 Build 0)  --> | ||||
| <svg version="1.0" id="Layer_1" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" x="0px" y="0px" | ||||
| 	 viewBox="0 0 16 16" enable-background="new 0 0 16 16" xml:space="preserve"> | ||||
| <g id="extruder_x2B_funnel"> | ||||
| 	<rect x="1" y="1" display="none" fill="#808080" width="14" height="6"/> | ||||
| 	<line fill="none" stroke="#ED6B21" stroke-linecap="round" stroke-miterlimit="10" x1="8" y1="9.37" x2="8" y2="15"/> | ||||
| 	<polygon display="none" fill="#808080" points="5,7 5,8 8,10 11,8 11,7 	"/> | ||||
| 	<g> | ||||
| 		<path fill="#808080" d="M14,2l0,4h-3c-0.55,0-1,0.45-1,1v0.47L8,8.8L6,7.46V7c0-0.55-0.45-1-1-1L2,6l0-4H14 M14,1H2 | ||||
| 			C1.45,1,1,1.45,1,2v4c0,0.55,0.45,1,1,1h3v1l2.45,1.63C7.61,9.74,7.81,9.8,8,9.8c0.19,0,0.39-0.06,0.55-0.17L11,8V7h3 | ||||
| 			c0.55,0,1-0.45,1-1V2C15,1.45,14.55,1,14,1L14,1z"/> | ||||
| 	</g> | ||||
| </g> | ||||
| </svg> | ||||
| After Width: | Height: | Size: 941 B | 
							
								
								
									
										33
									
								
								resources/icons/infill.svg
									
										
									
									
									
										Normal file
									
								
							
							
						
						|  | @ -0,0 +1,33 @@ | |||
| <?xml version="1.0" encoding="utf-8"?> | ||||
| <!-- Generator: Adobe Illustrator 23.0.3, SVG Export Plug-In . SVG Version: 6.00 Build 0)  --> | ||||
| <svg version="1.0" id="Layer_1" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" x="0px" y="0px" | ||||
| 	 viewBox="0 0 16 16" enable-background="new 0 0 16 16" xml:space="preserve"> | ||||
| <g id="infill"> | ||||
| 	<g> | ||||
| 		<defs> | ||||
| 			<polygon id="SVGID_1_" points="8,1.03 2,5.03 2,7.03 2,11.03 8,15.03 14,11.03 14,7.03 14,5.03 			"/> | ||||
| 		</defs> | ||||
| 		<clipPath id="SVGID_2_"> | ||||
| 			<use xlink:href="#SVGID_1_"  overflow="visible"/> | ||||
| 		</clipPath> | ||||
| 		<g clip-path="url(#SVGID_2_)"> | ||||
| 			 | ||||
| 				<line fill="none" stroke="#ED6B21" stroke-width="0.7" stroke-linecap="round" stroke-linejoin="round" stroke-miterlimit="10" x1="2.32" y1="11.59" x2="8.56" y2="1.34"/> | ||||
| 			 | ||||
| 				<line fill="none" stroke="#ED6B21" stroke-width="0.7" stroke-linecap="round" stroke-linejoin="round" stroke-miterlimit="10" x1="4.88" y1="13.15" x2="11.12" y2="2.9"/> | ||||
| 			 | ||||
| 				<line fill="none" stroke="#ED6B21" stroke-width="0.7" stroke-linecap="round" stroke-linejoin="round" stroke-miterlimit="10" x1="7.44" y1="14.71" x2="13.68" y2="4.46"/> | ||||
| 			 | ||||
| 				<line fill="none" stroke="#ED6B21" stroke-width="0.7" stroke-linecap="round" stroke-linejoin="round" stroke-miterlimit="10" x1="4.44" y1="2.34" x2="14.68" y2="8.59"/> | ||||
| 			 | ||||
| 				<line fill="none" stroke="#ED6B21" stroke-width="0.7" stroke-linecap="round" stroke-linejoin="round" stroke-miterlimit="10" x1="2.88" y1="4.91" x2="13.12" y2="11.15"/> | ||||
| 			 | ||||
| 				<line fill="none" stroke="#ED6B21" stroke-width="0.7" stroke-linecap="round" stroke-linejoin="round" stroke-miterlimit="10" x1="1.32" y1="7.47" x2="11.56" y2="13.71"/> | ||||
| 		</g> | ||||
| 	</g> | ||||
| 	<g> | ||||
| 		<path fill="#808080" d="M8,2.23l5,3.33v1.46v3.46l-5,3.33l-5-3.33V7.03V5.56L8,2.23 M8,1.03l-6,4v2v4l6,4l6-4v-4v-2L8,1.03L8,1.03 | ||||
| 			z"/> | ||||
| 	</g> | ||||
| </g> | ||||
| </svg> | ||||
| After Width: | Height: | Size: 1.8 KiB | 
|  | @ -1,25 +1,27 @@ | |||
| <?xml version="1.0" encoding="utf-8"?> | ||||
| <!-- Generator: Adobe Illustrator 23.0.1, SVG Export Plug-In . SVG Version: 6.00 Build 0)  --> | ||||
| <!-- Generator: Adobe Illustrator 23.0.3, SVG Export Plug-In . SVG Version: 6.00 Build 0)  --> | ||||
| <svg version="1.0" id="Layer_1" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" x="0px" y="0px" | ||||
| 	 viewBox="0 0 128 128" enable-background="new 0 0 128 128" xml:space="preserve"> | ||||
| <g> | ||||
| 	 viewBox="0 0 16 16" enable-background="new 0 0 16 16" xml:space="preserve"> | ||||
| <g id="layers"> | ||||
| 	<g> | ||||
| 		<rect x="7.98" y="105" fill="#FFFFFF" width="112.04" height="15"/> | ||||
| 	</g> | ||||
| 	<g> | ||||
| 		<rect x="7.98" y="85.67" fill="#FFFFFF" width="112.04" height="13"/> | ||||
| 	</g> | ||||
| 	<g> | ||||
| 		<rect x="7.98" y="66.33" fill="#FFFFFF" width="112.04" height="11"/> | ||||
| 	</g> | ||||
| 	<g> | ||||
| 		<rect x="7.98" y="47" fill="#ED6B21" width="112.04" height="9"/> | ||||
| 	</g> | ||||
| 	<g> | ||||
| 		<rect x="7.98" y="27.67" fill="#ED6B21" width="112.04" height="7"/> | ||||
| 	</g> | ||||
| 	<g> | ||||
| 		<rect x="7.98" y="8.33" fill="#ED6B21" width="112.04" height="5"/> | ||||
| 		<g> | ||||
| 			<rect x="1" y="13" fill="#808080" width="14" height="2"/> | ||||
| 		</g> | ||||
| 		<g> | ||||
| 			<rect x="1" y="10.6" fill="#808080" width="14" height="1.74"/> | ||||
| 		</g> | ||||
| 		<g> | ||||
| 			<rect x="1" y="8.19" fill="#808080" width="14" height="1.47"/> | ||||
| 		</g> | ||||
| 		<g> | ||||
| 			<rect x="1" y="5.79" fill="#ED6B21" width="14" height="1.2"/> | ||||
| 		</g> | ||||
| 		<g> | ||||
| 			<rect x="1" y="3.39" fill="#ED6B21" width="14" height="0.93"/> | ||||
| 		</g> | ||||
| 		<g> | ||||
| 			<rect x="1" y="0.99" fill="#808080" width="14" height="0.67"/> | ||||
| 		</g> | ||||
| 	</g> | ||||
| </g> | ||||
| </svg> | ||||
|  |  | |||
| Before Width: | Height: | Size: 841 B After Width: | Height: | Size: 845 B | 
							
								
								
									
										25
									
								
								resources/icons/note.svg
									
										
									
									
									
										Normal file
									
								
							
							
						
						|  | @ -0,0 +1,25 @@ | |||
| <?xml version="1.0" encoding="utf-8"?> | ||||
| <!-- Generator: Adobe Illustrator 23.0.3, SVG Export Plug-In . SVG Version: 6.00 Build 0)  --> | ||||
| <svg version="1.0" id="Layer_1" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" x="0px" y="0px" | ||||
| 	 viewBox="0 0 16 16" enable-background="new 0 0 16 16" xml:space="preserve"> | ||||
| <g id="notes"> | ||||
| 	<g> | ||||
| 		<path fill="#808080" d="M11,2l0,12l-9,0L2,2H11 M11,1H2C1.45,1,1,1.45,1,2V14c0,0.55,0.45,1,1,1H11c0.55,0,1-0.45,1-1V2 | ||||
| 			C12,1.45,11.55,1,11,1L11,1z"/> | ||||
| 	</g> | ||||
| 	<path fill="#ED6B21" d="M14,3L14,3c-0.55,0-1,0.45-1,1v10c0,0.55,0.45,1,1,1h0c0.55,0,1-0.45,1-1V4C15,3.45,14.55,3,14,3z"/> | ||||
| 	<polygon fill="#ED6B21" points="15,4 13,4 14,1 	"/> | ||||
| 	<g> | ||||
| 		<line fill="none" stroke="#808080" stroke-linecap="round" stroke-miterlimit="10" x1="3" y1="4" x2="10" y2="4"/> | ||||
| 	</g> | ||||
| 	<g> | ||||
| 		<line fill="none" stroke="#808080" stroke-linecap="round" stroke-miterlimit="10" x1="3" y1="6" x2="10" y2="6"/> | ||||
| 	</g> | ||||
| 	<g> | ||||
| 		<line fill="none" stroke="#808080" stroke-linecap="round" stroke-miterlimit="10" x1="3" y1="8" x2="10" y2="8"/> | ||||
| 	</g> | ||||
| 	<g> | ||||
| 		<line fill="none" stroke="#808080" stroke-linecap="round" stroke-miterlimit="10" x1="3" y1="10" x2="7" y2="10"/> | ||||
| 	</g> | ||||
| </g> | ||||
| </svg> | ||||
| After Width: | Height: | Size: 1.2 KiB | 
							
								
								
									
										21
									
								
								resources/icons/output+page_white.svg
									
										
									
									
									
										Normal file
									
								
							
							
						
						|  | @ -0,0 +1,21 @@ | |||
| <?xml version="1.0" encoding="utf-8"?> | ||||
| <!-- Generator: Adobe Illustrator 23.0.3, SVG Export Plug-In . SVG Version: 6.00 Build 0)  --> | ||||
| <svg version="1.0" id="Layer_1" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" x="0px" y="0px" | ||||
| 	 viewBox="0 0 16 16" enable-background="new 0 0 16 16" xml:space="preserve"> | ||||
| <g id="output_x2B_page_x5F_white"> | ||||
| 	<g> | ||||
| 		<path fill="#808080" d="M14.5,10c-0.27,0-0.5,0.23-0.5,0.5v3c0,0.27-0.23,0.5-0.5,0.5h-11C2.22,14,2,13.77,2,13.5v-11 | ||||
| 			C2,2.22,2.22,2,2.5,2h11C13.77,2,14,2.22,14,2.5v3C14,5.78,14.23,6,14.5,6l0,0C14.77,6,15,5.78,15,5.5v-4C15,1.23,14.77,1,14.5,1 | ||||
| 			h-13C1.23,1,1,1.23,1,1.5v13C1,14.77,1.23,15,1.5,15h13c0.27,0,0.5-0.23,0.5-0.5v-4C15,10.23,14.77,10,14.5,10L14.5,10z"/> | ||||
| 	</g> | ||||
| 	<path fill="#808080" d="M11,10h-0.04c-0.39,0.57-0.94,1.04-1.63,1.32c-1.83,0.73-3.91-0.16-4.64-1.99s0.16-3.91,1.99-4.64 | ||||
| 		C8.26,4.05,10.03,4.64,10.96,6H11V4.99c-0.11-0.11-0.2-0.23-0.32-0.33l-0.06-0.98L9.31,3.12L8.57,3.75 | ||||
| 		C8.23,3.71,7.88,3.7,7.54,3.74L6.81,3.09L5.48,3.62L5.4,4.6C5.12,4.81,4.87,5.05,4.66,5.32L3.69,5.37L3.12,6.69l0.63,0.75 | ||||
| 		C3.71,7.77,3.7,8.12,3.74,8.46L3.09,9.19l0.53,1.33L4.6,10.6c0.21,0.28,0.45,0.52,0.72,0.74l0.06,0.98l1.31,0.56l0.75-0.63 | ||||
| 		c0.34,0.05,0.68,0.05,1.03,0.01l0.73,0.65l1.33-0.53l0.08-0.98c0.15-0.11,0.27-0.26,0.4-0.39V10z"/> | ||||
| 	<g> | ||||
| 		<path fill="#ED6B21" d="M14.65,8.35c0.19-0.19,0.19-0.51,0-0.71l-2.29-2.29C12.16,5.16,12,5.22,12,5.5v1C12,6.78,11.77,7,11.5,7 | ||||
| 			h-3C8.23,7,8,7.22,8,7.5v1C8,8.77,8.23,9,8.5,9h3C11.77,9,12,9.23,12,9.5v1c0,0.27,0.16,0.34,0.35,0.15L14.65,8.35z"/> | ||||
| 	</g> | ||||
| </g> | ||||
| </svg> | ||||
| After Width: | Height: | Size: 1.5 KiB | 
							
								
								
									
										14
									
								
								resources/icons/printer.svg
									
										
									
									
									
										Normal file
									
								
							
							
						
						|  | @ -0,0 +1,14 @@ | |||
| <?xml version="1.0" encoding="utf-8"?> | ||||
| <!-- Generator: Adobe Illustrator 23.0.3, SVG Export Plug-In . SVG Version: 6.00 Build 0)  --> | ||||
| <svg version="1.0" id="Layer_1" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" x="0px" y="0px" | ||||
| 	 viewBox="0 0 16 16" enable-background="new 0 0 16 16" xml:space="preserve"> | ||||
| <g id="printer"> | ||||
| 	<rect x="1" y="1" fill="#808080" width="1" height="14"/> | ||||
| 	<rect x="14" y="1" fill="#808080" width="1" height="14"/> | ||||
| 	<rect x="7.5" y="-1.5" transform="matrix(-1.836970e-16 1 -1 -1.836970e-16 13.5 -2.5)" fill="#808080" width="1" height="14"/> | ||||
| 	<rect x="7.5" y="-5.5" transform="matrix(-1.836970e-16 1 -1 -1.836970e-16 9.5 -6.5)" fill="#808080" width="1" height="14"/> | ||||
| 	<rect x="7" y="7" transform="matrix(-1.836970e-16 1 -1 -1.836970e-16 22 6)" fill="#808080" width="2" height="14"/> | ||||
| 	<rect x="3" y="4" fill="#ED6B21" width="4" height="4"/> | ||||
| 	<polygon fill="#ED6B21" points="5,9 4,8 6,8 	"/> | ||||
| </g> | ||||
| </svg> | ||||
| After Width: | Height: | Size: 958 B | 
							
								
								
									
										19
									
								
								resources/icons/skirt+brim.svg
									
										
									
									
									
										Normal file
									
								
							
							
						
						|  | @ -0,0 +1,19 @@ | |||
| <?xml version="1.0" encoding="utf-8"?> | ||||
| <!-- Generator: Adobe Illustrator 23.0.3, SVG Export Plug-In . SVG Version: 6.00 Build 0)  --> | ||||
| <svg version="1.0" id="Layer_1" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" x="0px" y="0px" | ||||
| 	 viewBox="0 0 16 16" enable-background="new 0 0 16 16" xml:space="preserve"> | ||||
| <g id="skirt_x2B_brim"> | ||||
| 	<g id="skirt_x2B_brim_-_box"> | ||||
| 		<g> | ||||
| 			<path fill="#808080" d="M8,3.26L12.12,6v1.17v2.88L8,12.8l-4.12-2.74V7.17V6L8,3.26 M8,2.06L2.88,5.47v1.71v3.41L8,14l5.12-3.41 | ||||
| 				V7.17V5.47L8,2.06L8,2.06z"/> | ||||
| 		</g> | ||||
| 	</g> | ||||
| 	<g id="skirt_x2B_brim_-_box_1_"> | ||||
| 		<g> | ||||
| 			<path fill="#ED6B21" d="M8,1.63l5.5,3.67v1.73v3.73L8,14.43l-5.5-3.67V7.03V5.3L8,1.63 M8,1.03l-6,4v2v4l6,4l6-4v-4v-2L8,1.03 | ||||
| 				L8,1.03z"/> | ||||
| 		</g> | ||||
| 	</g> | ||||
| </g> | ||||
| </svg> | ||||
| After Width: | Height: | Size: 780 B | 
							
								
								
									
										21
									
								
								resources/icons/spool.svg
									
										
									
									
									
										Normal file
									
								
							
							
						
						|  | @ -0,0 +1,21 @@ | |||
| <?xml version="1.0" encoding="utf-8"?> | ||||
| <!-- Generator: Adobe Illustrator 23.0.3, SVG Export Plug-In . SVG Version: 6.00 Build 0)  --> | ||||
| <svg version="1.0" id="Layer_1" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" x="0px" y="0px" | ||||
| 	 viewBox="0 0 16 16" enable-background="new 0 0 16 16" xml:space="preserve"> | ||||
| <g id="spool"> | ||||
| 	 | ||||
| 		<line fill="none" stroke="#808080" stroke-width="2" stroke-linecap="round" stroke-miterlimit="10" x1="2" y1="2" x2="2" y2="14"/> | ||||
| 	 | ||||
| 		<line fill="none" stroke="#808080" stroke-width="2" stroke-linecap="round" stroke-miterlimit="10" x1="14" y1="2" x2="14" y2="14"/> | ||||
| 	 | ||||
| 		<line fill="none" stroke="#ED6B21" stroke-width="1.5" stroke-linecap="round" stroke-miterlimit="10" x1="4" y1="3" x2="4" y2="13"/> | ||||
| 	 | ||||
| 		<line fill="none" stroke="#ED6B21" stroke-width="1.5" stroke-linecap="round" stroke-miterlimit="10" x1="6" y1="3" x2="6" y2="13"/> | ||||
| 	 | ||||
| 		<line fill="none" stroke="#ED6B21" stroke-width="1.5" stroke-linecap="round" stroke-miterlimit="10" x1="8" y1="3" x2="8" y2="13"/> | ||||
| 	 | ||||
| 		<line fill="none" stroke="#ED6B21" stroke-width="1.5" stroke-linecap="round" stroke-miterlimit="10" x1="10" y1="3" x2="10" y2="13"/> | ||||
| 	 | ||||
| 		<line fill="none" stroke="#ED6B21" stroke-width="1.5" stroke-linecap="round" stroke-miterlimit="10" x1="12" y1="3" x2="12" y2="13"/> | ||||
| </g> | ||||
| </svg> | ||||
| After Width: | Height: | Size: 1.3 KiB | 
							
								
								
									
										25
									
								
								resources/icons/support.svg
									
										
									
									
									
										Normal file
									
								
							
							
						
						|  | @ -0,0 +1,25 @@ | |||
| <?xml version="1.0" encoding="utf-8"?> | ||||
| <!-- Generator: Adobe Illustrator 23.0.3, SVG Export Plug-In . SVG Version: 6.00 Build 0)  --> | ||||
| <svg version="1.0" id="Layer_1" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" x="0px" y="0px" | ||||
| 	 viewBox="0 0 16 16" enable-background="new 0 0 16 16" xml:space="preserve"> | ||||
| <g id="support"> | ||||
| 	<polygon fill="none" stroke="#808080" stroke-linecap="round" stroke-linejoin="round" stroke-miterlimit="10" points="8,1  | ||||
| 		2.31,4.79 2.31,8.57 2.31,8.79 2.31,10.47 8,14.25 13.69,10.47 13.69,8.79 13.69,8.57 13.69,4.79 	"/> | ||||
| 	 | ||||
| 		<line fill="none" stroke="#ED6B21" stroke-linecap="round" stroke-linejoin="round" stroke-miterlimit="10" x1="12.69" y1="15" x2="12.69" y2="12.44"/> | ||||
| 	 | ||||
| 		<line fill="none" stroke="#ED6B21" stroke-linecap="round" stroke-linejoin="round" stroke-miterlimit="10" x1="13.87" y1="15" x2="13.87" y2="11.64"/> | ||||
| 	 | ||||
| 		<line fill="none" stroke="#ED6B21" stroke-linecap="round" stroke-linejoin="round" stroke-miterlimit="10" x1="2.13" y1="15" x2="2.13" y2="11.64"/> | ||||
| 	 | ||||
| 		<line fill="none" stroke="#ED6B21" stroke-linecap="round" stroke-linejoin="round" stroke-miterlimit="10" x1="3.33" y1="15" x2="3.33" y2="12.44"/> | ||||
| 	 | ||||
| 		<line fill="none" stroke="#ED6B21" stroke-linecap="round" stroke-linejoin="round" stroke-miterlimit="10" x1="4.51" y1="15" x2="4.51" y2="13.22"/> | ||||
| 	 | ||||
| 		<line fill="none" stroke="#ED6B21" stroke-linecap="round" stroke-linejoin="round" stroke-miterlimit="10" x1="5.66" y1="15" x2="5.66" y2="14"/> | ||||
| 	 | ||||
| 		<line fill="none" stroke="#ED6B21" stroke-linecap="round" stroke-linejoin="round" stroke-miterlimit="10" x1="10.34" y1="15" x2="10.34" y2="14"/> | ||||
| 	 | ||||
| 		<line fill="none" stroke="#ED6B21" stroke-linecap="round" stroke-linejoin="round" stroke-miterlimit="10" x1="11.5" y1="15" x2="11.5" y2="13.22"/> | ||||
| </g> | ||||
| </svg> | ||||
| After Width: | Height: | Size: 1.7 KiB | 
							
								
								
									
										16
									
								
								resources/icons/time.svg
									
										
									
									
									
										Normal file
									
								
							
							
						
						|  | @ -0,0 +1,16 @@ | |||
| <?xml version="1.0" encoding="utf-8"?> | ||||
| <!-- Generator: Adobe Illustrator 23.0.3, SVG Export Plug-In . SVG Version: 6.00 Build 0)  --> | ||||
| <svg version="1.0" id="Layer_1" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" x="0px" y="0px" | ||||
| 	 viewBox="0 0 16 16" enable-background="new 0 0 16 16" xml:space="preserve"> | ||||
| <g id="time"> | ||||
| 	<g> | ||||
| 		<path fill="#808080" d="M8,2c3.31,0,6,2.69,6,6s-2.69,6-6,6s-6-2.69-6-6S4.69,2,8,2 M8,1C4.13,1,1,4.13,1,8s3.13,7,7,7s7-3.13,7-7 | ||||
| 			S11.87,1,8,1L8,1z"/> | ||||
| 	</g> | ||||
| 	<g> | ||||
| 		<line fill="none" stroke="#ED6B21" stroke-linecap="round" stroke-miterlimit="10" x1="5" y1="4" x2="8" y2="8"/> | ||||
| 	</g> | ||||
| 	 | ||||
| 		<line fill="none" stroke="#ED6B21" stroke-width="1.5" stroke-linecap="round" stroke-miterlimit="10" x1="11.5" y1="8" x2="8" y2="8"/> | ||||
| </g> | ||||
| </svg> | ||||
| After Width: | Height: | Size: 789 B | 
							
								
								
									
										24
									
								
								resources/icons/wrench.svg
									
										
									
									
									
										Normal file
									
								
							
							
						
						|  | @ -0,0 +1,24 @@ | |||
| <?xml version="1.0" encoding="utf-8"?> | ||||
| <!-- Generator: Adobe Illustrator 23.0.3, SVG Export Plug-In . SVG Version: 6.00 Build 0)  --> | ||||
| <svg version="1.0" id="Layer_1" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" x="0px" y="0px" | ||||
| 	 viewBox="0 0 16 16" enable-background="new 0 0 16 16" xml:space="preserve"> | ||||
| <g id="advanced_x2B_wrench"> | ||||
| 	<path fill="#808080" d="M7.39,3.69C7.32,3.46,7.23,3.24,7.11,3.03l0.22-0.65L6.62,1.67L5.97,1.89C5.76,1.77,5.54,1.68,5.31,1.61 | ||||
| 		L5,1H4L3.69,1.61C3.46,1.68,3.24,1.77,3.03,1.89L2.38,1.67L1.67,2.38l0.22,0.65C1.77,3.24,1.68,3.46,1.61,3.69L1,4v1l0.61,0.31 | ||||
| 		c0.07,0.23,0.16,0.46,0.27,0.67L1.67,6.62l0.71,0.71l0.65-0.22c0.21,0.12,0.43,0.21,0.67,0.28L4,8h1l0.31-0.61 | ||||
| 		c0.23-0.07,0.46-0.16,0.67-0.28l0.65,0.22l0.71-0.71L7.11,5.97c0.12-0.21,0.21-0.43,0.27-0.67L8,5V4L7.39,3.69z"/> | ||||
| 	<path fill="#808080" d="M12.4,2.49c-0.05-0.17-0.11-0.33-0.2-0.48l0.15-0.46l-0.51-0.51L11.39,1.2c-0.15-0.08-0.31-0.15-0.48-0.2 | ||||
| 		l-0.22-0.44H9.98L9.76,1C9.59,1.05,9.43,1.11,9.28,1.2L8.82,1.04L8.31,1.55l0.15,0.46c-0.08,0.15-0.15,0.31-0.2,0.48L7.83,2.71 | ||||
| 		v0.71l0.44,0.22c0.05,0.17,0.11,0.33,0.2,0.48L8.31,4.58l0.51,0.51l0.46-0.15c0.15,0.08,0.31,0.15,0.48,0.2l0.22,0.44h0.71 | ||||
| 		l0.22-0.44c0.17-0.05,0.33-0.11,0.48-0.2l0.46,0.15l0.51-0.51L12.2,4.12c0.08-0.15,0.15-0.31,0.2-0.48l0.44-0.22V2.71L12.4,2.49z" | ||||
| 		/> | ||||
| 	<path fill="#ED6B21" d="M14.36,10.66c0.04-0.34,0.04-0.68,0-1.02L15,8.91L14.46,7.6l-0.97-0.07c-0.21-0.27-0.45-0.51-0.72-0.72 | ||||
| 		L12.7,5.84L11.39,5.3l-0.73,0.64c-0.34-0.04-0.68-0.04-1.02,0L8.91,5.3L7.6,5.84L7.53,6.81C7.26,7.02,7.02,7.26,6.81,7.53L5.84,7.6 | ||||
| 		L5.3,8.91l0.64,0.73c-0.04,0.34-0.04,0.68,0,1.02L5.3,11.39l0.54,1.31l0.97,0.07c0.21,0.27,0.45,0.51,0.72,0.72l0.07,0.97L8.91,15 | ||||
| 		l0.73-0.64c0.34,0.04,0.68,0.04,1.02,0L11.39,15l1.31-0.54l0.07-0.97c0.27-0.21,0.51-0.45,0.72-0.72l0.97-0.07L15,11.39 | ||||
| 		L14.36,10.66z"/> | ||||
| 	<circle fill="#FFFFFF" cx="4.5" cy="4.5" r="1.92"/> | ||||
| 	<circle fill="#FFFFFF" cx="10.33" cy="3.06" r="1.11"/> | ||||
| 	<circle fill="#FFFFFF" cx="10.15" cy="10.15" r="2.85"/> | ||||
| </g> | ||||
| </svg> | ||||
| After Width: | Height: | Size: 2 KiB | 
|  | @ -120,7 +120,7 @@ Slic3r::Polygon ClipperPath_to_Slic3rPolygon(const ClipperLib::Path &input) | |||
| { | ||||
|     Polygon retval; | ||||
|     for (ClipperLib::Path::const_iterator pit = input.begin(); pit != input.end(); ++pit) | ||||
|         retval.points.push_back(Point( (*pit).X, (*pit).Y )); | ||||
|         retval.points.emplace_back(pit->X, pit->Y); | ||||
|     return retval; | ||||
| } | ||||
| 
 | ||||
|  | @ -128,7 +128,7 @@ Slic3r::Polyline ClipperPath_to_Slic3rPolyline(const ClipperLib::Path &input) | |||
| { | ||||
|     Polyline retval; | ||||
|     for (ClipperLib::Path::const_iterator pit = input.begin(); pit != input.end(); ++pit) | ||||
|         retval.points.push_back(Point( (*pit).X, (*pit).Y )); | ||||
|         retval.points.emplace_back(pit->X, pit->Y); | ||||
|     return retval; | ||||
| } | ||||
| 
 | ||||
|  | @ -137,7 +137,7 @@ Slic3r::Polygons ClipperPaths_to_Slic3rPolygons(const ClipperLib::Paths &input) | |||
|     Slic3r::Polygons retval; | ||||
|     retval.reserve(input.size()); | ||||
|     for (ClipperLib::Paths::const_iterator it = input.begin(); it != input.end(); ++it) | ||||
|         retval.push_back(ClipperPath_to_Slic3rPolygon(*it)); | ||||
|         retval.emplace_back(ClipperPath_to_Slic3rPolygon(*it)); | ||||
|     return retval; | ||||
| } | ||||
| 
 | ||||
|  | @ -146,7 +146,7 @@ Slic3r::Polylines ClipperPaths_to_Slic3rPolylines(const ClipperLib::Paths &input | |||
|     Slic3r::Polylines retval; | ||||
|     retval.reserve(input.size()); | ||||
|     for (ClipperLib::Paths::const_iterator it = input.begin(); it != input.end(); ++it) | ||||
|         retval.push_back(ClipperPath_to_Slic3rPolyline(*it)); | ||||
|         retval.emplace_back(ClipperPath_to_Slic3rPolyline(*it)); | ||||
|     return retval; | ||||
| } | ||||
| 
 | ||||
|  | @ -171,7 +171,7 @@ Slic3rMultiPoint_to_ClipperPath(const MultiPoint &input) | |||
| { | ||||
|     ClipperLib::Path retval; | ||||
|     for (Points::const_iterator pit = input.points.begin(); pit != input.points.end(); ++pit) | ||||
|         retval.push_back(ClipperLib::IntPoint( (*pit)(0), (*pit)(1) )); | ||||
|         retval.emplace_back((*pit)(0), (*pit)(1)); | ||||
|     return retval; | ||||
| } | ||||
| 
 | ||||
|  | @ -181,7 +181,7 @@ Slic3rMultiPoint_to_ClipperPath_reversed(const Slic3r::MultiPoint &input) | |||
|     ClipperLib::Path output; | ||||
|     output.reserve(input.points.size()); | ||||
|     for (Slic3r::Points::const_reverse_iterator pit = input.points.rbegin(); pit != input.points.rend(); ++pit) | ||||
|         output.push_back(ClipperLib::IntPoint( (*pit)(0), (*pit)(1) )); | ||||
|         output.emplace_back((*pit)(0), (*pit)(1)); | ||||
|     return output; | ||||
| } | ||||
| 
 | ||||
|  | @ -189,7 +189,7 @@ ClipperLib::Paths Slic3rMultiPoints_to_ClipperPaths(const Polygons &input) | |||
| { | ||||
|     ClipperLib::Paths retval; | ||||
|     for (Polygons::const_iterator it = input.begin(); it != input.end(); ++it) | ||||
|         retval.push_back(Slic3rMultiPoint_to_ClipperPath(*it)); | ||||
|         retval.emplace_back(Slic3rMultiPoint_to_ClipperPath(*it)); | ||||
|     return retval; | ||||
| } | ||||
| 
 | ||||
|  | @ -197,7 +197,7 @@ ClipperLib::Paths Slic3rMultiPoints_to_ClipperPaths(const Polylines &input) | |||
| { | ||||
|     ClipperLib::Paths retval; | ||||
|     for (Polylines::const_iterator it = input.begin(); it != input.end(); ++it) | ||||
|         retval.push_back(Slic3rMultiPoint_to_ClipperPath(*it)); | ||||
|         retval.emplace_back(Slic3rMultiPoint_to_ClipperPath(*it)); | ||||
|     return retval; | ||||
| } | ||||
| 
 | ||||
|  | @ -226,7 +226,7 @@ ClipperLib::Paths _offset(ClipperLib::Paths &&input, ClipperLib::EndType endType | |||
| ClipperLib::Paths _offset(ClipperLib::Path &&input, ClipperLib::EndType endType, const float delta, ClipperLib::JoinType joinType, double miterLimit) | ||||
| { | ||||
|     ClipperLib::Paths paths; | ||||
|     paths.push_back(std::move(input)); | ||||
|     paths.emplace_back(std::move(input)); | ||||
| 	return _offset(std::move(paths), endType, delta, joinType, miterLimit); | ||||
| } | ||||
| 
 | ||||
|  | @ -585,7 +585,7 @@ Polylines _clipper_pl(ClipperLib::ClipType clipType, const Polygons &subject, co | |||
|     Polylines polylines; | ||||
|     polylines.reserve(subject.size()); | ||||
|     for (Polygons::const_iterator polygon = subject.begin(); polygon != subject.end(); ++polygon) | ||||
|         polylines.push_back(*polygon);  // implicit call to split_at_first_point()
 | ||||
|         polylines.emplace_back(polygon->operator Polyline());  // implicit call to split_at_first_point()
 | ||||
|      | ||||
|     // perform clipping
 | ||||
|     Polylines retval = _clipper_pl(clipType, polylines, clip, safety_offset_); | ||||
|  | @ -643,7 +643,7 @@ _clipper_ln(ClipperLib::ClipType clipType, const Lines &subject, const Polygons | |||
|     // convert Polylines to Lines
 | ||||
|     Lines retval; | ||||
|     for (Polylines::const_iterator polyline = polylines.begin(); polyline != polylines.end(); ++polyline) | ||||
|         retval.push_back(*polyline); | ||||
|         retval.emplace_back(polyline->operator Line()); | ||||
|     return retval; | ||||
| } | ||||
| 
 | ||||
|  | @ -673,7 +673,7 @@ void traverse_pt(ClipperLib::PolyNodes &nodes, Polygons* retval) | |||
|     ordering_points.reserve(nodes.size()); | ||||
|     for (ClipperLib::PolyNodes::const_iterator it = nodes.begin(); it != nodes.end(); ++it) { | ||||
|         Point p((*it)->Contour.front().X, (*it)->Contour.front().Y); | ||||
|         ordering_points.push_back(p); | ||||
|         ordering_points.emplace_back(p); | ||||
|     } | ||||
|      | ||||
|     // perform the ordering
 | ||||
|  | @ -684,7 +684,7 @@ void traverse_pt(ClipperLib::PolyNodes &nodes, Polygons* retval) | |||
|     for (ClipperLib::PolyNodes::iterator it = ordered_nodes.begin(); it != ordered_nodes.end(); ++it) { | ||||
|         // traverse the next depth
 | ||||
|         traverse_pt((*it)->Childs, retval); | ||||
|         retval->push_back(ClipperPath_to_Slic3rPolygon((*it)->Contour)); | ||||
|         retval->emplace_back(ClipperPath_to_Slic3rPolygon((*it)->Contour)); | ||||
|         if ((*it)->IsHole()) retval->back().reverse();  // ccw
 | ||||
|     } | ||||
| } | ||||
|  | @ -791,8 +791,8 @@ Polygons top_level_islands(const Slic3r::Polygons &polygons) | |||
|     Polygons out; | ||||
|     out.reserve(polytree.ChildCount()); | ||||
|     for (int i = 0; i < polytree.ChildCount(); ++i) | ||||
|         out.push_back(ClipperPath_to_Slic3rPolygon(polytree.Childs[i]->Contour)); | ||||
|         out.emplace_back(ClipperPath_to_Slic3rPolygon(polytree.Childs[i]->Contour)); | ||||
|     return out; | ||||
| } | ||||
| 
 | ||||
| } | ||||
| } | ||||
|  |  | |||
|  | @ -776,6 +776,9 @@ void GCodeAnalyzer::_calc_gcode_preview_extrusion_layers(GCodePreviewData& previ | |||
|     preview_data.ranges.width.update_from(width_range); | ||||
|     preview_data.ranges.feedrate.update_from(feedrate_range); | ||||
|     preview_data.ranges.volumetric_rate.update_from(volumetric_rate_range); | ||||
| 
 | ||||
|     // we need to sort the layers by their z as they can be shuffled in case of sequential prints
 | ||||
|     std::sort(preview_data.extrusion.layers.begin(), preview_data.extrusion.layers.end(), [](const GCodePreviewData::Extrusion::Layer& l1, const GCodePreviewData::Extrusion::Layer& l2)->bool { return l1.z < l2.z; }); | ||||
| } | ||||
| 
 | ||||
| void GCodeAnalyzer::_calc_gcode_preview_travel(GCodePreviewData& preview_data, std::function<void()> cancel_callback) | ||||
|  | @ -855,6 +858,11 @@ void GCodeAnalyzer::_calc_gcode_preview_travel(GCodePreviewData& preview_data, s | |||
|     preview_data.ranges.height.update_from(height_range); | ||||
|     preview_data.ranges.width.update_from(width_range); | ||||
|     preview_data.ranges.feedrate.update_from(feedrate_range); | ||||
| 
 | ||||
|     // we need to sort the polylines by their min z as they can be shuffled in case of sequential prints
 | ||||
|     std::sort(preview_data.travel.polylines.begin(), preview_data.travel.polylines.end(), | ||||
|         [](const GCodePreviewData::Travel::Polyline& p1, const GCodePreviewData::Travel::Polyline& p2)->bool | ||||
|     { return unscale<double>(p1.polyline.bounding_box().min(2)) < unscale<double>(p2.polyline.bounding_box().min(2)); }); | ||||
| } | ||||
| 
 | ||||
| void GCodeAnalyzer::_calc_gcode_preview_retractions(GCodePreviewData& preview_data, std::function<void()> cancel_callback) | ||||
|  | @ -877,6 +885,11 @@ void GCodeAnalyzer::_calc_gcode_preview_retractions(GCodePreviewData& preview_da | |||
|         Vec3crd position(scale_(move.start_position.x()), scale_(move.start_position.y()), scale_(move.start_position.z())); | ||||
|         preview_data.retraction.positions.emplace_back(position, move.data.width, move.data.height); | ||||
|     } | ||||
| 
 | ||||
|     // we need to sort the positions by their z as they can be shuffled in case of sequential prints
 | ||||
|     std::sort(preview_data.retraction.positions.begin(), preview_data.retraction.positions.end(), | ||||
|         [](const GCodePreviewData::Retraction::Position& p1, const GCodePreviewData::Retraction::Position& p2)->bool | ||||
|     { return unscale<double>(p1.position(2)) < unscale<double>(p2.position(2)); }); | ||||
| } | ||||
| 
 | ||||
| void GCodeAnalyzer::_calc_gcode_preview_unretractions(GCodePreviewData& preview_data, std::function<void()> cancel_callback) | ||||
|  | @ -899,6 +912,11 @@ void GCodeAnalyzer::_calc_gcode_preview_unretractions(GCodePreviewData& preview_ | |||
|         Vec3crd position(scale_(move.start_position.x()), scale_(move.start_position.y()), scale_(move.start_position.z())); | ||||
|         preview_data.unretraction.positions.emplace_back(position, move.data.width, move.data.height); | ||||
|     } | ||||
| 
 | ||||
|     // we need to sort the positions by their z as they can be shuffled in case of sequential prints
 | ||||
|     std::sort(preview_data.unretraction.positions.begin(), preview_data.unretraction.positions.end(), | ||||
|         [](const GCodePreviewData::Retraction::Position& p1, const GCodePreviewData::Retraction::Position& p2)->bool | ||||
|     { return unscale<double>(p1.position(2)) < unscale<double>(p2.position(2)); }); | ||||
| } | ||||
| 
 | ||||
| // Return an estimate of the memory consumed by the time estimator.
 | ||||
|  |  | |||
|  | @ -556,19 +556,9 @@ std::string Model::propose_export_file_name_and_path() const | |||
|     for (const ModelObject *model_object : this->objects) | ||||
|         for (ModelInstance *model_instance : model_object->instances) | ||||
|             if (model_instance->is_printable()) { | ||||
|                 input_file = model_object->input_file; | ||||
|                 if (! model_object->name.empty()) { | ||||
|                     if (input_file.empty()) | ||||
|                         // model_object->input_file was empty, just use model_object->name
 | ||||
|                         input_file = model_object->name; | ||||
|                     else { | ||||
|                         // Replace file name in input_file with model_object->name, but keep the path and file extension.
 | ||||
| 						input_file = (boost::filesystem::path(model_object->name).parent_path().empty()) ? | ||||
| 							(boost::filesystem::path(input_file).parent_path() / model_object->name).make_preferred().string() : | ||||
| 							model_object->name; | ||||
| 					} | ||||
|                 } | ||||
|                 if (! input_file.empty()) | ||||
|                 input_file = model_object->get_export_filename(); | ||||
| 
 | ||||
|                 if (!input_file.empty()) | ||||
|                     goto end; | ||||
|                 // Other instances will produce the same name, skip them.
 | ||||
|                 break; | ||||
|  | @ -1187,8 +1177,9 @@ ModelObjectPtrs ModelObject::cut(size_t instance, coordf_t z, bool keep_upper, b | |||
|         else { | ||||
|             TriangleMesh upper_mesh, lower_mesh; | ||||
| 
 | ||||
|             // Transform the mesh by the combined transformation matrix
 | ||||
|             volume->mesh.transform(instance_matrix * volume_matrix); | ||||
|             // Transform the mesh by the combined transformation matrix.
 | ||||
|             // Flip the triangles in case the composite transformation is left handed.
 | ||||
|             volume->mesh.transform(instance_matrix * volume_matrix, true); | ||||
| 
 | ||||
|             // Perform cut
 | ||||
|             TriangleMeshSlicer tms(&volume->mesh); | ||||
|  | @ -1432,6 +1423,26 @@ void ModelObject::print_info() const | |||
|     cout << "volume = "           << mesh.volume()                  << endl; | ||||
| } | ||||
| 
 | ||||
| std::string ModelObject::get_export_filename() const | ||||
| { | ||||
|     std::string ret = input_file; | ||||
| 
 | ||||
|     if (!name.empty()) | ||||
|     { | ||||
|         if (ret.empty()) | ||||
|             // input_file was empty, just use name
 | ||||
|             ret = name; | ||||
|         else | ||||
|         { | ||||
|             // Replace file name in input_file with name, but keep the path and file extension.
 | ||||
|             ret = (boost::filesystem::path(name).parent_path().empty()) ? | ||||
|                 (boost::filesystem::path(ret).parent_path() / name).make_preferred().string() : name; | ||||
|         } | ||||
|     } | ||||
| 
 | ||||
|     return ret; | ||||
| } | ||||
| 
 | ||||
| void ModelVolume::set_material_id(t_model_material_id material_id) | ||||
| { | ||||
|     m_material_id = material_id; | ||||
|  |  | |||
|  | @ -275,6 +275,8 @@ public: | |||
|     // Print object statistics to console.
 | ||||
|     void print_info() const; | ||||
| 
 | ||||
|     std::string get_export_filename() const; | ||||
| 
 | ||||
| protected: | ||||
|     friend class Print; | ||||
|     friend class SLAPrint; | ||||
|  |  | |||
|  | @ -10,7 +10,7 @@ | |||
| #include "GCode/WipeTowerPrusaMM.hpp" | ||||
| #include "Utils.hpp" | ||||
| 
 | ||||
| #include "PrintExport.hpp" | ||||
| //#include "PrintExport.hpp"
 | ||||
| 
 | ||||
| #include <algorithm> | ||||
| #include <limits> | ||||
|  |  | |||
|  | @ -7,6 +7,7 @@ | |||
| #include <vector> | ||||
| 
 | ||||
| #include <boost/log/trivial.hpp> | ||||
| #include <boost/filesystem/path.hpp> | ||||
| 
 | ||||
| #include "Rasterizer/Rasterizer.hpp" | ||||
| //#include <tbb/parallel_for.h>
 | ||||
|  | @ -72,7 +73,8 @@ public: | |||
|     void finish_layer(); | ||||
| 
 | ||||
|     // Save all the layers into the file (or dir) specified in the path argument
 | ||||
|     void save(const std::string& path); | ||||
|     // An optional project name can be added to be used for the layer file names
 | ||||
|     void save(const std::string& path, const std::string& projectname = ""); | ||||
| 
 | ||||
|     // Save only the selected layer to the file specified in path argument.
 | ||||
|     void save_layer(unsigned lyr, const std::string& path); | ||||
|  | @ -86,7 +88,8 @@ template<class T = void> struct VeryFalse { static const bool value = false; }; | |||
| template<class Fmt> class LayerWriter { | ||||
| public: | ||||
| 
 | ||||
|     LayerWriter(const std::string& /*zipfile_path*/) { | ||||
|     LayerWriter(const std::string& /*zipfile_path*/) | ||||
|     { | ||||
|         static_assert(VeryFalse<Fmt>::value, | ||||
|                       "No layer writer implementation provided!"); | ||||
|     } | ||||
|  | @ -99,10 +102,6 @@ public: | |||
|     void binary_entry(const std::string& /*fname*/, | ||||
|                       const std::uint8_t* buf, size_t len); | ||||
| 
 | ||||
|     // Get the name of the archive but only the name part without the path or
 | ||||
|     // the extension.
 | ||||
|     std::string get_name() { return ""; } | ||||
| 
 | ||||
|     // Test whether the object can still be used for writing.
 | ||||
|     bool is_ok() { return false; } | ||||
| 
 | ||||
|  | @ -253,12 +252,14 @@ public: | |||
|     } | ||||
| 
 | ||||
|     template<class LyrFmt> | ||||
|     inline void save(const std::string& path) { | ||||
|     inline void save(const std::string& fpath, const std::string& prjname = "") | ||||
|     { | ||||
|         try { | ||||
|             LayerWriter<LyrFmt> writer(path); | ||||
|             LayerWriter<LyrFmt> writer(fpath); | ||||
|             if(!writer.is_ok()) return; | ||||
| 
 | ||||
|             std::string project = writer.get_name(); | ||||
|             std::string project = prjname.empty()? | ||||
|                        boost::filesystem::path(fpath).stem().string() : prjname; | ||||
| 
 | ||||
|             writer.next_entry("config.ini"); | ||||
|             if(!writer.is_ok()) return; | ||||
|  |  | |||
|  | @ -1790,15 +1790,16 @@ std::vector<ExPolygons> PrintObject::_slice_volumes(const std::vector<float> &z, | |||
|     if (! volumes.empty()) { | ||||
|         // Compose mesh.
 | ||||
|         //FIXME better to perform slicing over each volume separately and then to use a Boolean operation to merge them.
 | ||||
|         TriangleMesh mesh; | ||||
|         for (const ModelVolume *v : volumes) | ||||
|         { | ||||
|             TriangleMesh vol_mesh(v->mesh); | ||||
|             vol_mesh.transform(v->get_matrix()); | ||||
|         TriangleMesh mesh(volumes.front()->mesh); | ||||
|         mesh.transform(volumes.front()->get_matrix(), true); | ||||
|         for (size_t idx_volume = 1; idx_volume < volumes.size(); ++ idx_volume) { | ||||
|             const ModelVolume &model_volume = *volumes[idx_volume]; | ||||
|             TriangleMesh vol_mesh(model_volume.mesh); | ||||
|             vol_mesh.transform(model_volume.get_matrix(), true); | ||||
|             mesh.merge(vol_mesh); | ||||
|         } | ||||
|         if (mesh.stl.stats.number_of_facets > 0) { | ||||
|             mesh.transform(m_trafo); | ||||
|             mesh.transform(m_trafo, true); | ||||
|             // apply XY shift
 | ||||
|             mesh.translate(- unscale<float>(m_copies_shift(0)), - unscale<float>(m_copies_shift(1)), 0); | ||||
|             // perform actual slicing
 | ||||
|  | @ -1819,9 +1820,9 @@ std::vector<ExPolygons> PrintObject::_slice_volume(const std::vector<float> &z, | |||
|     // Compose mesh.
 | ||||
|     //FIXME better to perform slicing over each volume separately and then to use a Boolean operation to merge them.
 | ||||
|     TriangleMesh mesh(volume.mesh); | ||||
|     mesh.transform(volume.get_matrix()); | ||||
|     mesh.transform(volume.get_matrix(), true); | ||||
|     if (mesh.stl.stats.number_of_facets > 0) { | ||||
|         mesh.transform(m_trafo); | ||||
|         mesh.transform(m_trafo, true); | ||||
|         // apply XY shift
 | ||||
|         mesh.translate(- unscale<float>(m_copies_shift(0)), - unscale<float>(m_copies_shift(1)), 0); | ||||
|         // perform actual slicing
 | ||||
|  |  | |||
|  | @ -1041,31 +1041,37 @@ void SLAPrint::process() | |||
|                 { | ||||
|                     ClipperPolygon poly; | ||||
| 
 | ||||
|                     // We need to reverse if flpXY OR is_lefthanded is true but
 | ||||
|                     // not if both are true which is a logical inequality (XOR)
 | ||||
|                     bool needreverse = flpXY != is_lefthanded; | ||||
| 
 | ||||
|                     // should be a move
 | ||||
|                     poly.Contour.reserve(polygon.contour.size() + 1); | ||||
| 
 | ||||
|                     for(auto& p : polygon.contour.points) | ||||
|                         poly.Contour.emplace_back(p.x(), p.y()); | ||||
| 
 | ||||
|                     auto pfirst = poly.Contour.front(); | ||||
|                     poly.Contour.emplace_back(pfirst); | ||||
|                     auto& cntr = polygon.contour.points; | ||||
|                     if(needreverse) | ||||
|                         for(auto it = cntr.rbegin(); it != cntr.rend(); ++it) | ||||
|                             poly.Contour.emplace_back(it->x(), it->y()); | ||||
|                     else | ||||
|                         for(auto& p : cntr) | ||||
|                             poly.Contour.emplace_back(p.x(), p.y()); | ||||
| 
 | ||||
|                     for(auto& h : polygon.holes) { | ||||
|                         poly.Holes.emplace_back(); | ||||
|                         auto& hole = poly.Holes.back(); | ||||
|                         hole.reserve(h.points.size() + 1); | ||||
| 
 | ||||
|                         for(auto& p : h.points) hole.emplace_back(p.x(), p.y()); | ||||
|                         auto pfirst = hole.front(); hole.emplace_back(pfirst); | ||||
|                         if(needreverse) | ||||
|                             for(auto& p : h.points) | ||||
|                                 hole.emplace_back(p.x(), p.y()); | ||||
|                         else | ||||
|                             for(auto it = h.points.rbegin(); it != h.points.rend(); ++it) | ||||
|                                 hole.emplace_back(it->x(), it->y()); | ||||
|                     } | ||||
| 
 | ||||
|                     if(is_lefthanded) { | ||||
|                         for(auto& p : poly.Contour) p.X = -p.X; | ||||
|                         std::reverse(poly.Contour.begin(), poly.Contour.end()); | ||||
|                         for(auto& h : poly.Holes) { | ||||
|                             for(auto& p : h) p.X = -p.X; | ||||
|                             std::reverse(h.begin(), h.end()); | ||||
|                         } | ||||
|                         for(auto& h : poly.Holes) for(auto& p : h) p.X = -p.X; | ||||
|                     } | ||||
| 
 | ||||
|                     sl::rotate(poly, double(instances[i].rotation)); | ||||
|  | @ -1074,12 +1080,7 @@ void SLAPrint::process() | |||
| 
 | ||||
|                     if (flpXY) { | ||||
|                         for(auto& p : poly.Contour) std::swap(p.X, p.Y); | ||||
|                         std::reverse(poly.Contour.begin(), poly.Contour.end()); | ||||
| 
 | ||||
|                         for(auto& h : poly.Holes) { | ||||
|                             for(auto& p : h) std::swap(p.X, p.Y); | ||||
|                             std::reverse(h.begin(), h.end()); | ||||
|                         } | ||||
|                         for(auto& h : poly.Holes) for(auto& p : h) std::swap(p.X, p.Y); | ||||
|                     } | ||||
| 
 | ||||
|                     polygons.emplace_back(std::move(poly)); | ||||
|  |  | |||
|  | @ -320,10 +320,8 @@ struct SLAPrintStatistics | |||
|     } | ||||
| }; | ||||
| 
 | ||||
| struct SLAminzZipper {}; | ||||
| 
 | ||||
| // The implementation of creating zipped archives with wxWidgets
 | ||||
| template<> class LayerWriter<SLAminzZipper> { | ||||
| template<> class LayerWriter<Zipper> { | ||||
|     Zipper m_zip; | ||||
| public: | ||||
| 
 | ||||
|  | @ -332,16 +330,12 @@ public: | |||
|     void next_entry(const std::string& fname) { m_zip.add_entry(fname); } | ||||
| 
 | ||||
|     void binary_entry(const std::string& fname, | ||||
|                              const std::uint8_t* buf, | ||||
|                              size_t l) | ||||
|                       const std::uint8_t* buf, | ||||
|                       size_t l) | ||||
|     { | ||||
|         m_zip.add_entry(fname, buf, l); | ||||
|     } | ||||
| 
 | ||||
|     std::string get_name() const { | ||||
|         return m_zip.get_name(); | ||||
|     } | ||||
| 
 | ||||
|     template<class T> inline LayerWriter& operator<<(T&& arg) { | ||||
|         m_zip << std::forward<T>(arg); return *this; | ||||
|     } | ||||
|  | @ -389,9 +383,11 @@ public: | |||
|     // Returns true if the last step was finished with success.
 | ||||
|     bool                finished() const override { return this->is_step_done(slaposSliceSupports) && this->Inherited::is_step_done(slapsRasterize); } | ||||
| 
 | ||||
|     template<class Fmt = SLAminzZipper> | ||||
|     void export_raster(const std::string& fname) { | ||||
|         if(m_printer) m_printer->save<Fmt>(fname); | ||||
|     template<class Fmt = Zipper> | ||||
|     inline void export_raster(const std::string& fpath, | ||||
|                        const std::string& projectname = "") | ||||
|     { | ||||
|         if(m_printer) m_printer->save<Fmt>(fpath, projectname); | ||||
|     } | ||||
| 
 | ||||
|     const PrintObjects& objects() const { return m_objects; } | ||||
|  |  | |||
|  | @ -314,10 +314,15 @@ void TriangleMesh::mirror(const Axis &axis) | |||
|     stl_invalidate_shared_vertices(&this->stl); | ||||
| } | ||||
| 
 | ||||
| void TriangleMesh::transform(const Transform3d& t) | ||||
| void TriangleMesh::transform(const Transform3d& t, bool fix_left_handed) | ||||
| { | ||||
|     stl_transform(&stl, t); | ||||
|     stl_invalidate_shared_vertices(&stl); | ||||
| 	if (fix_left_handed && t.matrix().block(0, 0, 3, 3).determinant() < 0.) { | ||||
| 		// Left handed transformation is being applied. It is a good idea to flip the faces and their normals.
 | ||||
| 		this->repair(); | ||||
| 		stl_reverse_all_facets(&stl); | ||||
| 	} | ||||
| } | ||||
| 
 | ||||
| void TriangleMesh::align_to_origin() | ||||
|  |  | |||
|  | @ -49,7 +49,7 @@ public: | |||
|     void mirror_x() { this->mirror(X); } | ||||
|     void mirror_y() { this->mirror(Y); } | ||||
|     void mirror_z() { this->mirror(Z); } | ||||
|     void transform(const Transform3d& t); | ||||
|     void transform(const Transform3d& t, bool fix_left_handed = false); | ||||
|     void align_to_origin(); | ||||
|     void rotate(double angle, Point* center); | ||||
|     TriangleMeshPtrs split() const; | ||||
|  |  | |||
|  | @ -4,7 +4,6 @@ | |||
| 
 | ||||
| #include "Zipper.hpp" | ||||
| #include "miniz/miniz_zip.h" | ||||
| #include <boost/filesystem/path.hpp> | ||||
| #include <boost/log/trivial.hpp> | ||||
| 
 | ||||
| #include "I18N.hpp" | ||||
|  | @ -213,10 +212,6 @@ void Zipper::finish_entry() | |||
|     m_entry.clear(); | ||||
| } | ||||
| 
 | ||||
| std::string Zipper::get_name() const { | ||||
|     return boost::filesystem::path(m_impl->m_zipname).stem().string(); | ||||
| } | ||||
| 
 | ||||
| void Zipper::finalize() | ||||
| { | ||||
|     finish_entry(); | ||||
|  |  | |||
|  | @ -81,9 +81,6 @@ public: | |||
|     /// file is up to minz after the erroneous write.
 | ||||
|     void finish_entry(); | ||||
| 
 | ||||
|     /// Gets the name of the archive without the path or extension.
 | ||||
|     std::string get_name() const; | ||||
| 
 | ||||
|     void finalize(); | ||||
| }; | ||||
| 
 | ||||
|  |  | |||
|  | @ -397,8 +397,9 @@ int CLI::run(int argc, char **argv) | |||
|                             outfile_final = fff_print.print_statistics().finalize_output_path(outfile); | ||||
|                         } else { | ||||
| 							outfile = sla_print.output_filepath(outfile); | ||||
| 							sla_print.export_raster(outfile); | ||||
| 							outfile_final = sla_print.print_statistics().finalize_output_path(outfile); | ||||
|                             // We need to finalize the filename beforehand because the export function sets the filename inside the zip metadata
 | ||||
|                             outfile_final = sla_print.print_statistics().finalize_output_path(outfile); | ||||
| 							sla_print.export_raster(outfile_final); | ||||
|                         } | ||||
|                         if (outfile != outfile_final && Slic3r::rename_file(outfile, outfile_final) != 0) { | ||||
| 							boost::nowide::cerr << "Renaming file " << outfile << " to " << outfile_final << " failed" << std::endl; | ||||
|  |  | |||
|  | @ -98,8 +98,9 @@ void BackgroundSlicingProcess::process_sla() | |||
|     m_print->process(); | ||||
|     if (this->set_step_started(bspsGCodeFinalize)) { | ||||
|         if (! m_export_path.empty()) { | ||||
|             m_sla_print->export_raster(m_export_path); | ||||
|             m_print->set_status(100, "Masked SLA file exported to " + m_export_path); | ||||
|         	const std::string export_path = m_sla_print->print_statistics().finalize_output_path(m_export_path); | ||||
|             m_sla_print->export_raster(export_path); | ||||
|             m_print->set_status(100, "Masked SLA file exported to " + export_path); | ||||
|         } else if (! m_upload_job.empty()) { | ||||
|             prepare_upload(); | ||||
|         } else { | ||||
|  | @ -389,7 +390,7 @@ void BackgroundSlicingProcess::prepare_upload() | |||
| 
 | ||||
| 	// Generate a unique temp path to which the gcode/zip file is copied/exported
 | ||||
| 	boost::filesystem::path source_path = boost::filesystem::temp_directory_path() | ||||
| 		/ boost::filesystem::unique_path(".printhost.%%%%-%%%%-%%%%-%%%%.gcode"); | ||||
| 		/ boost::filesystem::unique_path(".Slic3rPE.upload.%%%%-%%%%-%%%%-%%%%"); | ||||
| 
 | ||||
| 	if (m_print == m_fff_print) { | ||||
| 		m_print->set_status(95, "Running post-processing scripts"); | ||||
|  | @ -399,8 +400,8 @@ void BackgroundSlicingProcess::prepare_upload() | |||
| 		run_post_process_scripts(source_path.string(), m_fff_print->config()); | ||||
| 		m_upload_job.upload_data.upload_path = m_fff_print->print_statistics().finalize_output_path(m_upload_job.upload_data.upload_path.string()); | ||||
|     } else { | ||||
|         m_sla_print->export_raster(source_path.string()); | ||||
| 		// TODO: Also finalize upload path like with FFF when there are statistics for SLA print
 | ||||
| 		m_upload_job.upload_data.upload_path = m_sla_print->print_statistics().finalize_output_path(m_upload_job.upload_data.upload_path.string()); | ||||
|         m_sla_print->export_raster(source_path.string(), m_upload_job.upload_data.upload_path.string()); | ||||
| 	} | ||||
| 
 | ||||
| 	m_print->set_status(100, (boost::format("Scheduling upload to `%1%`. See Window -> Print Host Upload Queue") % m_upload_job.printhost->get_host()).str()); | ||||
|  |  | |||
|  | @ -1,5 +1,7 @@ | |||
| #include "BitmapCache.hpp" | ||||
| 
 | ||||
| #include "libslic3r/Utils.hpp" | ||||
| 
 | ||||
| #if ! defined(WIN32) && ! defined(__APPLE__) | ||||
| #define BROKEN_ALPHA | ||||
| #endif | ||||
|  | @ -9,6 +11,11 @@ | |||
|     #include <wx/rawbmp.h> | ||||
| #endif /* BROKEN_ALPHA */ | ||||
| 
 | ||||
| #define NANOSVG_IMPLEMENTATION | ||||
| #include "nanosvg/nanosvg.h" | ||||
| #define NANOSVGRAST_IMPLEMENTATION | ||||
| #include "nanosvg/nanosvgrast.h" | ||||
| 
 | ||||
| namespace Slic3r { namespace GUI { | ||||
| 
 | ||||
| void BitmapCache::clear() | ||||
|  | @ -155,6 +162,72 @@ wxBitmap* BitmapCache::insert(const std::string &bitmap_key, const wxBitmap *beg | |||
| #endif | ||||
| } | ||||
| 
 | ||||
| wxBitmap* BitmapCache::insert_raw_rgba(const std::string &bitmap_key, unsigned int width, unsigned int height, const unsigned char *raw_data) | ||||
| { | ||||
|     wxImage image(width, height); | ||||
|     image.InitAlpha(); | ||||
|     unsigned char *rgb   = image.GetData(); | ||||
|     unsigned char *alpha = image.GetAlpha(); | ||||
|     unsigned int pixels = width * height; | ||||
|     for (unsigned int i = 0; i < pixels; ++ i) { | ||||
|         *rgb   ++ = *raw_data ++; | ||||
|         *rgb   ++ = *raw_data ++; | ||||
|         *rgb   ++ = *raw_data ++; | ||||
|         *alpha ++ = *raw_data ++; | ||||
|     } | ||||
|     return this->insert(bitmap_key, wxImage_to_wxBitmap_with_alpha(std::move(image))); | ||||
| } | ||||
| 
 | ||||
| wxBitmap* BitmapCache::load_png(const std::string &bitmap_name, unsigned int height) | ||||
| { | ||||
|     std::string bitmap_key = bitmap_name + "-h" + std::to_string(height); | ||||
|     auto it = m_map.find(bitmap_key); | ||||
|     if (it != m_map.end()) | ||||
|         return it->second; | ||||
| 
 | ||||
|     wxImage image; | ||||
|     if (! image.LoadFile(Slic3r::GUI::from_u8(Slic3r::var(bitmap_name + ".png")), wxBITMAP_TYPE_PNG) || | ||||
|         image.GetWidth() == 0 || image.GetHeight() == 0) | ||||
|         return nullptr; | ||||
|     if (image.GetHeight() != height) | ||||
|         image.Rescale(int(0.5f + float(image.GetWidth()) * height / image.GetHeight()), height, wxIMAGE_QUALITY_BILINEAR); | ||||
|     return this->insert(bitmap_key, wxImage_to_wxBitmap_with_alpha(std::move(image))); | ||||
| } | ||||
| 
 | ||||
| wxBitmap* BitmapCache::load_svg(const std::string &bitmap_name, unsigned int target_height) | ||||
| { | ||||
| 	std::string bitmap_key = bitmap_name + "-h" + std::to_string(target_height); | ||||
|     auto it = m_map.find(bitmap_key); | ||||
|     if (it != m_map.end()) | ||||
|         return it->second; | ||||
| 
 | ||||
|     NSVGimage *image = ::nsvgParseFromFile(Slic3r::var(bitmap_name + ".svg").c_str(), "px", 96.0f); | ||||
|     if (image == nullptr) | ||||
|         return nullptr; | ||||
| 
 | ||||
|     float scale    = (float)target_height / image->height; | ||||
|     int   width    = (int)(scale * image->width + 0.5f); | ||||
|     int   height   = (int)(scale * image->height + 0.5f); | ||||
|     int   n_pixels = width * height; | ||||
|     if (n_pixels <= 0) { | ||||
|         ::nsvgDelete(image); | ||||
|         return nullptr; | ||||
|     } | ||||
| 
 | ||||
|     NSVGrasterizer *rast = ::nsvgCreateRasterizer(); | ||||
|     if (rast == nullptr) { | ||||
|         ::nsvgDelete(image); | ||||
|         return nullptr; | ||||
|     } | ||||
| 
 | ||||
|     std::vector<unsigned char> data(n_pixels * 4, 0); | ||||
|     ::nsvgRasterize(rast, image, 0, 0, scale, data.data(), width, height, width * 4); | ||||
|     ::nsvgDeleteRasterizer(rast); | ||||
|     ::nsvgDelete(image); | ||||
| 
 | ||||
|     return this->insert_raw_rgba(bitmap_key, width, height, data.data()); | ||||
| } | ||||
| 
 | ||||
| wxBitmap BitmapCache::mksolid(size_t width, size_t height, unsigned char r, unsigned char g, unsigned char b, unsigned char transparency) | ||||
| { | ||||
|     wxImage image(width, height); | ||||
|  |  | |||
|  | @ -29,6 +29,12 @@ public: | |||
| 	wxBitmap* 		insert(const std::string &name, const wxBitmap &bmp, const wxBitmap &bmp2, const wxBitmap &bmp3); | ||||
| 	wxBitmap* 		insert(const std::string &name, const std::vector<wxBitmap> &bmps) { return this->insert(name, &bmps.front(), &bmps.front() + bmps.size()); } | ||||
| 	wxBitmap* 		insert(const std::string &name, const wxBitmap *begin, const wxBitmap *end); | ||||
| 	wxBitmap* 		insert_raw_rgba(const std::string &bitmap_key, unsigned int width, unsigned int height, const unsigned char *raw_data); | ||||
| 
 | ||||
| 	// Load png from resources/icons. bitmap_key is given without the .png suffix. Bitmap will be rescaled to provided height if nonzero.
 | ||||
| 	wxBitmap* 		load_png(const std::string &bitmap_key, unsigned int height = 0); | ||||
| 	// Load svg from resources/icons. bitmap_key is given without the .svg suffix. SVG will be rasterized to provided height.
 | ||||
| 	wxBitmap* 		load_svg(const std::string &bitmap_key, unsigned int height); | ||||
| 
 | ||||
| 	static wxBitmap mksolid(size_t width, size_t height, unsigned char r, unsigned char g, unsigned char b, unsigned char transparency); | ||||
| 	static wxBitmap mksolid(size_t width, size_t height, const unsigned char rgb[3]) { return mksolid(width, height, rgb[0], rgb[1], rgb[2], wxALPHA_OPAQUE); } | ||||
|  |  | |||
|  | @ -11,15 +11,11 @@ | |||
| #include <vector> | ||||
| #include <algorithm> | ||||
| 
 | ||||
| #define NANOSVG_IMPLEMENTATION | ||||
| #include "nanosvg/nanosvg.h" | ||||
| #define NANOSVGRAST_IMPLEMENTATION | ||||
| #include "nanosvg/nanosvgrast.h" | ||||
| 
 | ||||
| #include "libslic3r/Utils.hpp" | ||||
| 
 | ||||
| #include "libslic3r/Utils.hpp" | ||||
| 
 | ||||
| namespace Slic3r { | ||||
| namespace GUI { | ||||
| 
 | ||||
|  | @ -378,6 +374,7 @@ bool GLTexture::load_from_svg(const std::string& filename, bool use_mipmaps, uns | |||
|     if (n_pixels <= 0) | ||||
|     { | ||||
|         reset(); | ||||
|         nsvgDelete(image); | ||||
|         return false; | ||||
|     } | ||||
| 
 | ||||
|  |  | |||
|  | @ -525,7 +525,6 @@ void ObjectList::key_event(wxKeyEvent& event) | |||
|         || event.GetKeyCode() == WXK_BACK | ||||
| #endif //__WXOSX__
 | ||||
|         ) { | ||||
|         printf("WXK_BACK\n"); | ||||
|         remove(); | ||||
|     } | ||||
|     else if (wxGetKeyState(wxKeyCode('A')) && wxGetKeyState(WXK_CONTROL/*WXK_SHIFT*/)) | ||||
|  |  | |||
|  | @ -181,7 +181,7 @@ void ObjectManipulation::update_settings_value(const Selection& selection) | |||
|                 changed_box = true; | ||||
|             } | ||||
|             if (changed_box || !m_cache.instance.matches_instance(instance_idx) || !m_cache.scale.isApprox(100.0 * m_new_scale)) | ||||
|                 m_new_size = volume->get_instance_transformation().get_matrix(true, true) * m_cache.instance.box_size; | ||||
|                 m_new_size = (volume->get_instance_transformation().get_matrix(true, true) * m_cache.instance.box_size).cwiseAbs(); | ||||
|         } | ||||
|         else | ||||
|             // this should never happen
 | ||||
|  | @ -211,7 +211,7 @@ void ObjectManipulation::update_settings_value(const Selection& selection) | |||
|         m_new_position = volume->get_volume_offset(); | ||||
|         m_new_rotation = volume->get_volume_rotation(); | ||||
|         m_new_scale    = volume->get_volume_scaling_factor(); | ||||
|         m_new_size = volume->get_volume_transformation().get_matrix(true, true) * volume->bounding_box.size(); | ||||
|         m_new_size = (volume->get_volume_transformation().get_matrix(true, true) * volume->bounding_box.size()).cwiseAbs(); | ||||
|         m_new_enabled = true; | ||||
|     } | ||||
|     else if (obj_list->multiple_selection() || obj_list->is_selected(itInstanceRoot)) | ||||
|  |  | |||
|  | @ -85,7 +85,8 @@ void ObjectSettings::update_settings_list() | |||
| #endif // __WXMSW__
 | ||||
| 			btn->Bind(wxEVT_BUTTON, [opt_key, config, this](wxEvent &event) { | ||||
| 				config->erase(opt_key); | ||||
|                 wxTheApp->CallAfter([this]() {  | ||||
|                 wxGetApp().obj_list()->part_settings_changed(); | ||||
|                 wxTheApp->CallAfter([this]() { | ||||
|                     wxWindowUpdateLocker noUpdates(m_parent); | ||||
|                     update_settings_list();  | ||||
|                     m_parent->Layout();  | ||||
|  |  | |||
|  | @ -15,12 +15,6 @@ namespace Slic3r { | |||
| namespace GUI { | ||||
| 
 | ||||
| 
 | ||||
| 
 | ||||
| 
 | ||||
| 
 | ||||
| 
 | ||||
| // GLGizmoCut
 | ||||
| 
 | ||||
| class GLGizmoCutPanel : public wxPanel | ||||
| { | ||||
| public: | ||||
|  | @ -192,7 +186,7 @@ void GLGizmoCut::on_render_input_window(float x, float y, float bottom_limit, co | |||
|     m_imgui->set_next_window_bg_alpha(0.5f); | ||||
|     m_imgui->begin(_(L("Cut")), ImGuiWindowFlags_NoMove | ImGuiWindowFlags_NoResize | ImGuiWindowFlags_NoCollapse); | ||||
| 
 | ||||
|     ImGui::PushItemWidth(100.0f); | ||||
|     ImGui::PushItemWidth(m_imgui->scaled(5.0f)); | ||||
|     bool _value_changed = ImGui::InputDouble("Z", &m_cut_z, 0.0f, 0.0f, "%.2f"); | ||||
| 
 | ||||
|     m_imgui->checkbox(_(L("Keep upper part")), m_keep_upper); | ||||
|  |  | |||
|  | @ -330,8 +330,12 @@ bool GLGizmoSlaSupports::gizmo_event(SLAGizmoEventType action, const Vec2d& mous | |||
|                 m_canvas_width = m_parent.get_canvas_size().get_width(); | ||||
|                 m_canvas_height = m_parent.get_canvas_size().get_height(); | ||||
|             } | ||||
|             else | ||||
|                 select_point(m_hover_id); | ||||
|             else { | ||||
|                 if (m_editing_mode_cache[m_hover_id].selected) | ||||
|                     unselect_point(m_hover_id); | ||||
|                 else | ||||
|                     select_point(m_hover_id); | ||||
|             } | ||||
| 
 | ||||
|             return true; | ||||
|         } | ||||
|  | @ -565,12 +569,12 @@ void GLGizmoSlaSupports::on_render_input_window(float x, float y, float bottom_l | |||
| RENDER_AGAIN: | ||||
|     m_imgui->set_next_window_pos(x, y, ImGuiCond_Always); | ||||
| 
 | ||||
|     const ImVec2 window_size(m_imgui->scaled(15.f, 16.5f)); | ||||
|     const ImVec2 window_size(m_imgui->scaled(17.f, 18.f)); | ||||
|     ImGui::SetNextWindowPos(ImVec2(x, y - std::max(0.f, y+window_size.y-bottom_limit) )); | ||||
|     ImGui::SetNextWindowSize(ImVec2(window_size)); | ||||
| 
 | ||||
|     m_imgui->set_next_window_bg_alpha(0.5f); | ||||
|     m_imgui->begin(on_get_name(), ImGuiWindowFlags_NoMove | ImGuiWindowFlags_NoResize | ImGuiWindowFlags_NoCollapse); | ||||
|     m_imgui->begin(on_get_name(), ImGuiWindowFlags_NoMove | ImGuiWindowFlags_AlwaysAutoResize | ImGuiWindowFlags_NoCollapse); | ||||
| 
 | ||||
|     ImGui::PushItemWidth(100.0f); | ||||
| 
 | ||||
|  | @ -791,6 +795,19 @@ void GLGizmoSlaSupports::select_point(int i) | |||
| } | ||||
| 
 | ||||
| 
 | ||||
| void GLGizmoSlaSupports::unselect_point(int i) | ||||
| { | ||||
|     m_editing_mode_cache[i].selected = false; | ||||
|     m_selection_empty = true; | ||||
|     for (const CacheEntry& ce : m_editing_mode_cache) { | ||||
|         if (ce.selected) { | ||||
|             m_selection_empty = false; | ||||
|             break; | ||||
|         } | ||||
|     } | ||||
| } | ||||
| 
 | ||||
| 
 | ||||
| 
 | ||||
| void GLGizmoSlaSupports::editing_mode_discard_changes() | ||||
| { | ||||
|  |  | |||
|  | @ -93,6 +93,7 @@ private: | |||
|         NoPoints, | ||||
|     }; | ||||
|     void select_point(int i); | ||||
|     void unselect_point(int i); | ||||
|     void editing_mode_apply_changes(); | ||||
|     void editing_mode_discard_changes(); | ||||
|     void editing_mode_reload_cache(); | ||||
|  |  | |||
|  | @ -340,13 +340,11 @@ bool ImGuiWrapper::want_any_input() const | |||
| 
 | ||||
| void ImGuiWrapper::init_font() | ||||
| { | ||||
|     const float font_size = m_font_size * m_style_scaling; | ||||
| 
 | ||||
|     destroy_font(); | ||||
| 
 | ||||
|     ImGuiIO& io = ImGui::GetIO(); | ||||
|     io.Fonts->Clear(); | ||||
|     ImFont* font = io.Fonts->AddFontFromFileTTF((Slic3r::resources_dir() + "/fonts/NotoSans-Regular.ttf").c_str(), font_size, nullptr, m_glyph_ranges); | ||||
|     ImFont* font = io.Fonts->AddFontFromFileTTF((Slic3r::resources_dir() + "/fonts/NotoSans-Regular.ttf").c_str(), m_font_size, nullptr, m_glyph_ranges); | ||||
|     if (font == nullptr) { | ||||
|         font = io.Fonts->AddFontDefault(); | ||||
|         if (font == nullptr) { | ||||
|  |  | |||
|  | @ -45,8 +45,8 @@ public: | |||
|     void new_frame(); | ||||
|     void render(); | ||||
| 
 | ||||
|     float scaled(float x) const { return x * m_font_size * m_style_scaling; } | ||||
|     ImVec2 scaled(float x, float y) const { return ImVec2(x * m_font_size * m_style_scaling, y * m_font_size * m_style_scaling); } | ||||
|     float scaled(float x) const { return x * m_font_size; } | ||||
|     ImVec2 scaled(float x, float y) const { return ImVec2(x * m_font_size, y * m_font_size); } | ||||
|     ImVec2 calc_text_size(const wxString &text); | ||||
| 
 | ||||
|     void set_next_window_pos(float x, float y, int flag); | ||||
|  |  | |||
|  | @ -1199,7 +1199,7 @@ struct Plater::priv | |||
|     BoundingBox scaled_bed_shape_bb() const; | ||||
|     std::vector<size_t> load_files(const std::vector<fs::path>& input_files, bool load_model, bool load_config); | ||||
|     std::vector<size_t> load_model_objects(const ModelObjectPtrs &model_objects); | ||||
|     std::unique_ptr<CheckboxFileDialog> get_export_file(GUI::FileType file_type); | ||||
|     wxString get_export_file(GUI::FileType file_type); | ||||
| 
 | ||||
|     const Selection& get_selection() const; | ||||
|     Selection& get_selection(); | ||||
|  | @ -1617,6 +1617,45 @@ std::vector<size_t> Plater::priv::load_files(const std::vector<fs::path>& input_ | |||
|                 } | ||||
| #if ENABLE_VOLUMES_CENTERING_FIXES | ||||
|             } | ||||
|             else if ((wxGetApp().get_mode() == comSimple) && (type_3mf || type_any_amf)) | ||||
|             { | ||||
|                 bool advanced = false; | ||||
|                 for (const ModelObject* model_object : model.objects) | ||||
|                 { | ||||
|                     // is there more than one instance ?
 | ||||
|                     if (model_object->instances.size() > 1) | ||||
|                     { | ||||
|                         advanced = true; | ||||
|                         break; | ||||
|                     } | ||||
| 
 | ||||
|                     // is there any modifier ?
 | ||||
|                     for (const ModelVolume* model_volume : model_object->volumes) | ||||
|                     { | ||||
|                         if (!model_volume->is_model_part()) | ||||
|                         { | ||||
|                             advanced = true; | ||||
|                             break; | ||||
|                         } | ||||
|                     } | ||||
| 
 | ||||
|                     if (advanced) | ||||
|                         break; | ||||
|                 } | ||||
| 
 | ||||
|                 if (advanced) | ||||
|                 { | ||||
|                     wxMessageDialog dlg(q, _(L("This file cannot be loaded in simple mode. Do you want to switch to expert mode?\n")), | ||||
|                         _(L("Detected advanced data")), wxICON_WARNING | wxYES | wxNO); | ||||
|                     if (dlg.ShowModal() == wxID_YES) | ||||
|                     { | ||||
|                         Slic3r::GUI::wxGetApp().save_mode(comExpert); | ||||
|                         view3D->set_as_dirty(); | ||||
|                     } | ||||
|                     else | ||||
|                         return obj_idxs; | ||||
|                 } | ||||
|             } | ||||
| #endif // ENABLE_VOLUMES_CENTERING_FIXES
 | ||||
| 
 | ||||
| #if !ENABLE_VOLUMES_CENTERING_FIXES | ||||
|  | @ -1642,7 +1681,7 @@ std::vector<size_t> Plater::priv::load_files(const std::vector<fs::path>& input_ | |||
|                         Slic3r::GUI::show_error(nullptr,  | ||||
|                             wxString::Format(_(L("You can't to add the object(s) from %s because of one or some of them is(are) multi-part")),  | ||||
|                                              from_path(filename))); | ||||
|                         return std::vector<size_t>(); | ||||
|                         return obj_idxs; | ||||
|                     } | ||||
|             } | ||||
| 
 | ||||
|  | @ -1784,7 +1823,7 @@ std::vector<size_t> Plater::priv::load_model_objects(const ModelObjectPtrs &mode | |||
|     return obj_idxs; | ||||
| } | ||||
| 
 | ||||
| std::unique_ptr<CheckboxFileDialog> Plater::priv::get_export_file(GUI::FileType file_type) | ||||
| wxString Plater::priv::get_export_file(GUI::FileType file_type) | ||||
| { | ||||
|     wxString wildcard; | ||||
|     switch (file_type) { | ||||
|  | @ -1801,34 +1840,56 @@ std::unique_ptr<CheckboxFileDialog> Plater::priv::get_export_file(GUI::FileType | |||
| 
 | ||||
|     // Update printbility state of each of the ModelInstances.
 | ||||
|     this->update_print_volume_state(); | ||||
|     // Find the file name of the first printable object.
 | ||||
| 	fs::path output_file = this->model.propose_export_file_name_and_path(); | ||||
| 
 | ||||
|     const Selection& selection = get_selection(); | ||||
|     int obj_idx = selection.get_object_idx(); | ||||
| 
 | ||||
|     fs::path output_file; | ||||
|     // first try to get the file name from the current selection
 | ||||
|     if ((0 <= obj_idx) && (obj_idx < (int)this->model.objects.size())) | ||||
|         output_file = this->model.objects[obj_idx]->get_export_filename(); | ||||
| 
 | ||||
|     if (output_file.empty()) | ||||
|         // Find the file name of the first printable object.
 | ||||
|         output_file = this->model.propose_export_file_name_and_path(); | ||||
| 
 | ||||
|     wxString dlg_title; | ||||
|     switch (file_type) { | ||||
|         case FT_STL: output_file.replace_extension("stl"); break; | ||||
|         case FT_AMF: output_file.replace_extension("zip.amf"); break;   // XXX: Problem on OS X with double extension?
 | ||||
|         case FT_3MF: output_file.replace_extension("3mf"); break; | ||||
|         case FT_STL: | ||||
|         { | ||||
|             output_file.replace_extension("stl"); | ||||
|             dlg_title = _(L("Export STL file:")); | ||||
|             break; | ||||
|         } | ||||
|         case FT_AMF: | ||||
|         { | ||||
|             // XXX: Problem on OS X with double extension?
 | ||||
|             output_file.replace_extension("zip.amf"); | ||||
|             dlg_title = _(L("Export AMF file:")); | ||||
|             break; | ||||
|         } | ||||
|         case FT_3MF: | ||||
|         { | ||||
|             output_file.replace_extension("3mf"); | ||||
|             dlg_title = _(L("Save file as:")); | ||||
|             break; | ||||
|         } | ||||
|         default: break; | ||||
|     } | ||||
| 
 | ||||
|     auto dlg = Slic3r::make_unique<CheckboxFileDialog>(q, | ||||
|         ((file_type == FT_AMF) || (file_type == FT_3MF)) ? _(L("Export print config")) : "", | ||||
|         true, | ||||
|         _(L("Save file as:")), | ||||
|         from_path(output_file.parent_path()), | ||||
|         from_path(output_file.filename()), | ||||
|         wildcard, | ||||
|         wxFD_SAVE | wxFD_OVERWRITE_PROMPT | ||||
|     ); | ||||
|     wxFileDialog* dlg = new wxFileDialog(q, dlg_title, | ||||
|         from_path(output_file.parent_path()), from_path(output_file.filename()), | ||||
|         wildcard, wxFD_SAVE | wxFD_OVERWRITE_PROMPT); | ||||
| 
 | ||||
|     if (dlg->ShowModal() != wxID_OK) { | ||||
|         return nullptr; | ||||
|         return wxEmptyString; | ||||
|     } | ||||
| 
 | ||||
|     fs::path path(into_path(dlg->GetPath())); | ||||
|     wxString out_path = dlg->GetPath(); | ||||
|     fs::path path(into_path(out_path)); | ||||
|     wxGetApp().app_config->update_last_output_dir(path.parent_path().string()); | ||||
| 
 | ||||
|     return dlg; | ||||
|     return out_path; | ||||
| } | ||||
| 
 | ||||
| const Selection& Plater::priv::get_selection() const | ||||
|  | @ -3243,11 +3304,8 @@ void Plater::export_stl(bool selection_only) | |||
| { | ||||
|     if (p->model.objects.empty()) { return; } | ||||
| 
 | ||||
|     auto dialog = p->get_export_file(FT_STL); | ||||
|     if (! dialog) { return; } | ||||
| 
 | ||||
|     // Store a binary STL
 | ||||
|     const wxString path = dialog->GetPath(); | ||||
|     wxString path = p->get_export_file(FT_STL); | ||||
|     if (path.empty()) { return; } | ||||
|     const std::string path_u8 = into_u8(path); | ||||
| 
 | ||||
|     wxBusyCursor wait; | ||||
|  | @ -3272,15 +3330,14 @@ void Plater::export_amf() | |||
| { | ||||
|     if (p->model.objects.empty()) { return; } | ||||
| 
 | ||||
|     auto dialog = p->get_export_file(FT_AMF); | ||||
|     if (! dialog) { return; } | ||||
| 
 | ||||
|     const wxString path = dialog->GetPath(); | ||||
|     wxString path = p->get_export_file(FT_AMF); | ||||
|     if (path.empty()) { return; } | ||||
|     const std::string path_u8 = into_u8(path); | ||||
| 
 | ||||
| 	DynamicPrintConfig cfg = wxGetApp().preset_bundle->full_config_secure(); | ||||
|     wxBusyCursor wait; | ||||
| 	if (Slic3r::store_amf(path_u8.c_str(), &p->model, dialog->get_checkbox_value() ? &cfg : nullptr)) { | ||||
|     bool export_config = true; | ||||
|     DynamicPrintConfig cfg = wxGetApp().preset_bundle->full_config_secure(); | ||||
|     if (Slic3r::store_amf(path_u8.c_str(), &p->model, export_config ? &cfg : nullptr)) { | ||||
|         // Success
 | ||||
|         p->statusbar()->set_status_text(wxString::Format(_(L("AMF file exported to %s")), path)); | ||||
|     } else { | ||||
|  | @ -3297,10 +3354,8 @@ void Plater::export_3mf(const boost::filesystem::path& output_path) | |||
|     bool export_config = true; | ||||
|     if (output_path.empty()) | ||||
|     { | ||||
|         auto dialog = p->get_export_file(FT_3MF); | ||||
|         if (!dialog) { return; } | ||||
|         path = dialog->GetPath(); | ||||
|         export_config = dialog->get_checkbox_value(); | ||||
|         path = p->get_export_file(FT_3MF); | ||||
|         if (path.empty()) { return; } | ||||
|     } | ||||
|     else | ||||
|         path = from_path(output_path); | ||||
|  |  | |||
|  | @ -108,21 +108,26 @@ void Selection::add(unsigned int volume_idx, bool as_single_selection) | |||
|     if (is_wipe_tower() && volume->is_wipe_tower) | ||||
|         return; | ||||
| 
 | ||||
|     bool keep_instance_mode = (m_mode == Instance) && !as_single_selection && (is_single_full_instance() || is_multiple_full_instance()); | ||||
| 
 | ||||
|     // resets the current list if needed
 | ||||
|     bool needs_reset = as_single_selection; | ||||
|     needs_reset |= volume->is_wipe_tower; | ||||
|     needs_reset |= is_wipe_tower() && !volume->is_wipe_tower; | ||||
|     needs_reset |= !is_modifier() && volume->is_modifier; | ||||
|     needs_reset |= !keep_instance_mode && !is_modifier() && volume->is_modifier; | ||||
|     needs_reset |= is_modifier() && !volume->is_modifier; | ||||
| 
 | ||||
|     if (needs_reset) | ||||
|         clear(); | ||||
| 
 | ||||
|     if (!contains_volume(volume_idx)) | ||||
|         m_mode = volume->is_modifier ? Volume : Instance; | ||||
|     { | ||||
|         if (!keep_instance_mode) | ||||
|             m_mode = volume->is_modifier ? Volume : Instance; | ||||
|     } | ||||
|     else | ||||
|         // keep current mode
 | ||||
|         return; | ||||
|       // keep current mode
 | ||||
|       return; | ||||
| 
 | ||||
|     switch (m_mode) | ||||
|     { | ||||
|  | @ -1142,16 +1147,12 @@ void Selection::_update_type() | |||
|                         } | ||||
| 
 | ||||
|                         if (modifiers_count == 0) | ||||
|                         { | ||||
|                             m_type = MultipleVolume; | ||||
|                             requires_disable = true; | ||||
|                         } | ||||
|                         else if (modifiers_count == (unsigned int)m_list.size()) | ||||
|                         { | ||||
|                             m_type = MultipleModifier; | ||||
|                             requires_disable = true; | ||||
|                         } | ||||
|                     } | ||||
| 
 | ||||
|                     requires_disable = true; | ||||
|                 } | ||||
|                 else if ((selected_instances_count > 1) && (selected_instances_count * volumes_count == (unsigned int)m_list.size())) | ||||
|                 { | ||||
|  |  | |||
|  | @ -1446,7 +1446,7 @@ void TabFilament::build() | |||
| 		line.append_option(optgroup->get_option("bed_temperature")); | ||||
| 		optgroup->append_line(line); | ||||
| 
 | ||||
| 	page = add_options_page(_(L("Cooling")), "hourglass.png"); | ||||
| 	page = add_options_page(_(L("Cooling")), "cooling"); | ||||
| 		optgroup = page->new_optgroup(_(L("Enable"))); | ||||
| 		optgroup->append_single_option_line("fan_always_on"); | ||||
| 		optgroup->append_single_option_line("cooling"); | ||||
|  |  | |||
|  | @ -8,6 +8,8 @@ | |||
| #include <wx/dcclient.h> | ||||
| #include <wx/numformatter.h> | ||||
| 
 | ||||
| #include <boost/algorithm/string/replace.hpp> | ||||
| 
 | ||||
| #include "BitmapCache.hpp" | ||||
| #include "GUI.hpp" | ||||
| #include "GUI_App.hpp" | ||||
|  | @ -421,19 +423,16 @@ void PrusaCollapsiblePaneMSW::Collapse(bool collapse) | |||
| // PrusaObjectDataViewModelNode
 | ||||
| // ----------------------------------------------------------------------------
 | ||||
| 
 | ||||
| wxBitmap create_scaled_bitmap(const std::string& bmp_name) | ||||
| wxBitmap create_scaled_bitmap(const std::string& bmp_name_in) | ||||
| { | ||||
|     const double scale_f = Slic3r::GUI::wxGetApp().em_unit()* 0.1;//GetContentScaleFactor();
 | ||||
|     if (scale_f == 1.0) | ||||
|         return wxBitmap(Slic3r::GUI::from_u8(Slic3r::var(bmp_name)), wxBITMAP_TYPE_PNG); | ||||
| //     else if (scale_f == 2.0) // use biger icon
 | ||||
| //         return wxBitmap(Slic3r::GUI::from_u8(Slic3r::var(bmp_name_X2)), wxBITMAP_TYPE_PNG);
 | ||||
| 
 | ||||
|     wxImage img = wxImage(Slic3r::GUI::from_u8(Slic3r::var(bmp_name)), wxBITMAP_TYPE_PNG); | ||||
|     const int sz_w = int(img.GetWidth()*scale_f); | ||||
|     const int sz_h = int(img.GetHeight()*scale_f); | ||||
|     img.Rescale(sz_w, sz_h, wxIMAGE_QUALITY_BILINEAR); | ||||
|     return wxBitmap(img); | ||||
| 	static Slic3r::GUI::BitmapCache cache; | ||||
| 	const auto height = (unsigned int)(Slic3r::GUI::wxGetApp().em_unit() * 1.6f + 0.5f); | ||||
| 	std::string bmp_name = bmp_name_in; | ||||
| 	boost::replace_last(bmp_name, ".png", ""); | ||||
|     wxBitmap *bmp = cache.load_svg(bmp_name, height); | ||||
|     if (bmp == nullptr) | ||||
|         bmp = cache.load_png(bmp_name, height); | ||||
|     return *bmp; | ||||
| } | ||||
| 
 | ||||
| void PrusaObjectDataViewModelNode::set_object_action_icon() { | ||||
|  |  | |||
 YuSanka
						YuSanka