Path adaptive operation added

This commit is contained in:
kreso-t
2018-08-19 01:05:45 +02:00
committed by wmayer
parent 28c57af9a3
commit 3bde81ec85
12 changed files with 2964 additions and 3 deletions

View File

@@ -105,6 +105,8 @@ SET(PathScripts_SRCS
PathScripts/PathUtils.py
PathScripts/PathSimulatorGui.py
PathScripts/PostUtils.py
PathScripts/PathAdaptiveGui.py
PathScripts/PathAdaptive.py
PathScripts/__init__.py
)

View File

@@ -75,6 +75,7 @@
<file>icons/edge-join-round.svg</file>
<file>icons/edge-join-round-not.svg</file>
<file>icons/preferences-path.svg</file>
<file>icons/Path-Adaptive.svg</file>
<file>panels/DlgJobChooser.ui</file>
<file>panels/DlgJobCreate.ui</file>
<file>panels/DlgJobModelSelect.ui</file>

View File

@@ -0,0 +1,636 @@
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
<!-- Created with Inkscape (http://www.inkscape.org/) -->
<svg
xmlns:dc="http://purl.org/dc/elements/1.1/"
xmlns:cc="http://creativecommons.org/ns#"
xmlns:rdf="http://www.w3.org/1999/02/22-rdf-syntax-ns#"
xmlns:svg="http://www.w3.org/2000/svg"
xmlns="http://www.w3.org/2000/svg"
xmlns:xlink="http://www.w3.org/1999/xlink"
xmlns:sodipodi="http://sodipodi.sourceforge.net/DTD/sodipodi-0.dtd"
xmlns:inkscape="http://www.inkscape.org/namespaces/inkscape"
width="64px"
height="64px"
id="svg2816"
version="1.1"
inkscape:version="0.91 r13725"
sodipodi:docname="Path-Adaptive.svg">
<defs
id="defs2818">
<linearGradient
inkscape:collect="always"
id="linearGradient4066">
<stop
style="stop-color:#4e9a06;stop-opacity:1"
offset="0"
id="stop4068" />
<stop
style="stop-color:#8ae234;stop-opacity:1"
offset="1"
id="stop4070" />
</linearGradient>
<linearGradient
id="linearGradient4513">
<stop
style="stop-color:#ffffff;stop-opacity:1;"
offset="0"
id="stop4515" />
<stop
style="stop-color:#999999;stop-opacity:1;"
offset="1"
id="stop4517" />
</linearGradient>
<linearGradient
id="linearGradient3681">
<stop
id="stop3697"
offset="0"
style="stop-color:#fff110;stop-opacity:1;" />
<stop
style="stop-color:#cf7008;stop-opacity:1;"
offset="1"
id="stop3685" />
</linearGradient>
<inkscape:perspective
sodipodi:type="inkscape:persp3d"
inkscape:vp_x="0 : 32 : 1"
inkscape:vp_y="0 : 1000 : 0"
inkscape:vp_z="64 : 32 : 1"
inkscape:persp3d-origin="32 : 21.333333 : 1"
id="perspective2824" />
<inkscape:perspective
id="perspective3622"
inkscape:persp3d-origin="0.5 : 0.33333333 : 1"
inkscape:vp_z="1 : 0.5 : 1"
inkscape:vp_y="0 : 1000 : 0"
inkscape:vp_x="0 : 0.5 : 1"
sodipodi:type="inkscape:persp3d" />
<inkscape:perspective
id="perspective3622-9"
inkscape:persp3d-origin="0.5 : 0.33333333 : 1"
inkscape:vp_z="1 : 0.5 : 1"
inkscape:vp_y="0 : 1000 : 0"
inkscape:vp_x="0 : 0.5 : 1"
sodipodi:type="inkscape:persp3d" />
<inkscape:perspective
id="perspective3653"
inkscape:persp3d-origin="0.5 : 0.33333333 : 1"
inkscape:vp_z="1 : 0.5 : 1"
inkscape:vp_y="0 : 1000 : 0"
inkscape:vp_x="0 : 0.5 : 1"
sodipodi:type="inkscape:persp3d" />
<inkscape:perspective
id="perspective3675"
inkscape:persp3d-origin="0.5 : 0.33333333 : 1"
inkscape:vp_z="1 : 0.5 : 1"
inkscape:vp_y="0 : 1000 : 0"
inkscape:vp_x="0 : 0.5 : 1"
sodipodi:type="inkscape:persp3d" />
<inkscape:perspective
id="perspective3697"
inkscape:persp3d-origin="0.5 : 0.33333333 : 1"
inkscape:vp_z="1 : 0.5 : 1"
inkscape:vp_y="0 : 1000 : 0"
inkscape:vp_x="0 : 0.5 : 1"
sodipodi:type="inkscape:persp3d" />
<inkscape:perspective
id="perspective3720"
inkscape:persp3d-origin="0.5 : 0.33333333 : 1"
inkscape:vp_z="1 : 0.5 : 1"
inkscape:vp_y="0 : 1000 : 0"
inkscape:vp_x="0 : 0.5 : 1"
sodipodi:type="inkscape:persp3d" />
<inkscape:perspective
id="perspective3742"
inkscape:persp3d-origin="0.5 : 0.33333333 : 1"
inkscape:vp_z="1 : 0.5 : 1"
inkscape:vp_y="0 : 1000 : 0"
inkscape:vp_x="0 : 0.5 : 1"
sodipodi:type="inkscape:persp3d" />
<inkscape:perspective
id="perspective3764"
inkscape:persp3d-origin="0.5 : 0.33333333 : 1"
inkscape:vp_z="1 : 0.5 : 1"
inkscape:vp_y="0 : 1000 : 0"
inkscape:vp_x="0 : 0.5 : 1"
sodipodi:type="inkscape:persp3d" />
<inkscape:perspective
id="perspective3785"
inkscape:persp3d-origin="0.5 : 0.33333333 : 1"
inkscape:vp_z="1 : 0.5 : 1"
inkscape:vp_y="0 : 1000 : 0"
inkscape:vp_x="0 : 0.5 : 1"
sodipodi:type="inkscape:persp3d" />
<inkscape:perspective
id="perspective3806"
inkscape:persp3d-origin="0.5 : 0.33333333 : 1"
inkscape:vp_z="1 : 0.5 : 1"
inkscape:vp_y="0 : 1000 : 0"
inkscape:vp_x="0 : 0.5 : 1"
sodipodi:type="inkscape:persp3d" />
<inkscape:perspective
id="perspective3806-3"
inkscape:persp3d-origin="0.5 : 0.33333333 : 1"
inkscape:vp_z="1 : 0.5 : 1"
inkscape:vp_y="0 : 1000 : 0"
inkscape:vp_x="0 : 0.5 : 1"
sodipodi:type="inkscape:persp3d" />
<inkscape:perspective
id="perspective3835"
inkscape:persp3d-origin="0.5 : 0.33333333 : 1"
inkscape:vp_z="1 : 0.5 : 1"
inkscape:vp_y="0 : 1000 : 0"
inkscape:vp_x="0 : 0.5 : 1"
sodipodi:type="inkscape:persp3d" />
<inkscape:perspective
id="perspective3614"
inkscape:persp3d-origin="0.5 : 0.33333333 : 1"
inkscape:vp_z="1 : 0.5 : 1"
inkscape:vp_y="0 : 1000 : 0"
inkscape:vp_x="0 : 0.5 : 1"
sodipodi:type="inkscape:persp3d" />
<inkscape:perspective
id="perspective3614-8"
inkscape:persp3d-origin="0.5 : 0.33333333 : 1"
inkscape:vp_z="1 : 0.5 : 1"
inkscape:vp_y="0 : 1000 : 0"
inkscape:vp_x="0 : 0.5 : 1"
sodipodi:type="inkscape:persp3d" />
<inkscape:perspective
id="perspective3643"
inkscape:persp3d-origin="0.5 : 0.33333333 : 1"
inkscape:vp_z="1 : 0.5 : 1"
inkscape:vp_y="0 : 1000 : 0"
inkscape:vp_x="0 : 0.5 : 1"
sodipodi:type="inkscape:persp3d" />
<inkscape:perspective
id="perspective3643-3"
inkscape:persp3d-origin="0.5 : 0.33333333 : 1"
inkscape:vp_z="1 : 0.5 : 1"
inkscape:vp_y="0 : 1000 : 0"
inkscape:vp_x="0 : 0.5 : 1"
sodipodi:type="inkscape:persp3d" />
<inkscape:perspective
id="perspective3672"
inkscape:persp3d-origin="0.5 : 0.33333333 : 1"
inkscape:vp_z="1 : 0.5 : 1"
inkscape:vp_y="0 : 1000 : 0"
inkscape:vp_x="0 : 0.5 : 1"
sodipodi:type="inkscape:persp3d" />
<inkscape:perspective
id="perspective3672-5"
inkscape:persp3d-origin="0.5 : 0.33333333 : 1"
inkscape:vp_z="1 : 0.5 : 1"
inkscape:vp_y="0 : 1000 : 0"
inkscape:vp_x="0 : 0.5 : 1"
sodipodi:type="inkscape:persp3d" />
<inkscape:perspective
id="perspective3701"
inkscape:persp3d-origin="0.5 : 0.33333333 : 1"
inkscape:vp_z="1 : 0.5 : 1"
inkscape:vp_y="0 : 1000 : 0"
inkscape:vp_x="0 : 0.5 : 1"
sodipodi:type="inkscape:persp3d" />
<inkscape:perspective
id="perspective3701-8"
inkscape:persp3d-origin="0.5 : 0.33333333 : 1"
inkscape:vp_z="1 : 0.5 : 1"
inkscape:vp_y="0 : 1000 : 0"
inkscape:vp_x="0 : 0.5 : 1"
sodipodi:type="inkscape:persp3d" />
<inkscape:perspective
id="perspective3746"
inkscape:persp3d-origin="0.5 : 0.33333333 : 1"
inkscape:vp_z="1 : 0.5 : 1"
inkscape:vp_y="0 : 1000 : 0"
inkscape:vp_x="0 : 0.5 : 1"
sodipodi:type="inkscape:persp3d" />
<pattern
patternTransform="matrix(0.67643728,-0.81829155,2.4578314,1.8844554,-26.450606,18.294947)"
id="pattern5231"
xlink:href="#Strips1_1-4"
inkscape:collect="always" />
<inkscape:perspective
id="perspective5224"
inkscape:persp3d-origin="0.5 : 0.33333333 : 1"
inkscape:vp_z="1 : 0.5 : 1"
inkscape:vp_y="0 : 1000 : 0"
inkscape:vp_x="0 : 0.5 : 1"
sodipodi:type="inkscape:persp3d" />
<pattern
inkscape:stockid="Stripes 1:1"
id="Strips1_1-4"
patternTransform="matrix(0.66772843,-1.0037085,2.4261878,2.3114548,3.4760987,3.534923)"
height="1"
width="2"
patternUnits="userSpaceOnUse"
inkscape:collect="always">
<rect
id="rect4483-4"
height="2"
width="1"
y="-0.5"
x="0"
style="fill:black;stroke:none" />
</pattern>
<inkscape:perspective
id="perspective5224-9"
inkscape:persp3d-origin="0.5 : 0.33333333 : 1"
inkscape:vp_z="1 : 0.5 : 1"
inkscape:vp_y="0 : 1000 : 0"
inkscape:vp_x="0 : 0.5 : 1"
sodipodi:type="inkscape:persp3d" />
<pattern
patternTransform="matrix(0.66772843,-1.0037085,2.4261878,2.3114548,39.618381,8.9692804)"
id="pattern5231-4"
xlink:href="#Strips1_1-6"
inkscape:collect="always" />
<inkscape:perspective
id="perspective5224-3"
inkscape:persp3d-origin="0.5 : 0.33333333 : 1"
inkscape:vp_z="1 : 0.5 : 1"
inkscape:vp_y="0 : 1000 : 0"
inkscape:vp_x="0 : 0.5 : 1"
sodipodi:type="inkscape:persp3d" />
<pattern
inkscape:stockid="Stripes 1:1"
id="Strips1_1-6"
patternTransform="matrix(0.66772843,-1.0037085,2.4261878,2.3114548,3.4760987,3.534923)"
height="1"
width="2"
patternUnits="userSpaceOnUse"
inkscape:collect="always">
<rect
id="rect4483-0"
height="2"
width="1"
y="-0.5"
x="0"
style="fill:black;stroke:none" />
</pattern>
<pattern
patternTransform="matrix(0.66513382,-1.0631299,2.4167603,2.4482973,-49.762569,2.9546807)"
id="pattern5296"
xlink:href="#pattern5231-3"
inkscape:collect="always" />
<inkscape:perspective
id="perspective5288"
inkscape:persp3d-origin="0.5 : 0.33333333 : 1"
inkscape:vp_z="1 : 0.5 : 1"
inkscape:vp_y="0 : 1000 : 0"
inkscape:vp_x="0 : 0.5 : 1"
sodipodi:type="inkscape:persp3d" />
<pattern
patternTransform="matrix(0.66772843,-1.0037085,2.4261878,2.3114548,-26.336284,10.887197)"
id="pattern5231-3"
xlink:href="#Strips1_1-4-3"
inkscape:collect="always" />
<pattern
inkscape:stockid="Stripes 1:1"
id="Strips1_1-4-3"
patternTransform="matrix(0.66772843,-1.0037085,2.4261878,2.3114548,3.4760987,3.534923)"
height="1"
width="2"
patternUnits="userSpaceOnUse"
inkscape:collect="always">
<rect
id="rect4483-4-6"
height="2"
width="1"
y="-0.5"
x="0"
style="fill:black;stroke:none" />
</pattern>
<pattern
patternTransform="matrix(0.42844886,-0.62155849,1.5567667,1.431396,27.948414,13.306456)"
id="pattern5330"
xlink:href="#Strips1_1-9"
inkscape:collect="always" />
<inkscape:perspective
id="perspective5323"
inkscape:persp3d-origin="0.5 : 0.33333333 : 1"
inkscape:vp_z="1 : 0.5 : 1"
inkscape:vp_y="0 : 1000 : 0"
inkscape:vp_x="0 : 0.5 : 1"
sodipodi:type="inkscape:persp3d" />
<pattern
inkscape:stockid="Stripes 1:1"
id="Strips1_1-9"
patternTransform="matrix(0.66772843,-1.0037085,2.4261878,2.3114548,3.4760987,3.534923)"
height="1"
width="2"
patternUnits="userSpaceOnUse"
inkscape:collect="always">
<rect
id="rect4483-3"
height="2"
width="1"
y="-0.5"
x="0"
style="fill:black;stroke:none" />
</pattern>
<inkscape:perspective
id="perspective5361"
inkscape:persp3d-origin="0.5 : 0.33333333 : 1"
inkscape:vp_z="1 : 0.5 : 1"
inkscape:vp_y="0 : 1000 : 0"
inkscape:vp_x="0 : 0.5 : 1"
sodipodi:type="inkscape:persp3d" />
<inkscape:perspective
id="perspective5383"
inkscape:persp3d-origin="0.5 : 0.33333333 : 1"
inkscape:vp_z="1 : 0.5 : 1"
inkscape:vp_y="0 : 1000 : 0"
inkscape:vp_x="0 : 0.5 : 1"
sodipodi:type="inkscape:persp3d" />
<inkscape:perspective
id="perspective5411"
inkscape:persp3d-origin="0.5 : 0.33333333 : 1"
inkscape:vp_z="1 : 0.5 : 1"
inkscape:vp_y="0 : 1000 : 0"
inkscape:vp_x="0 : 0.5 : 1"
sodipodi:type="inkscape:persp3d" />
<linearGradient
inkscape:collect="always"
xlink:href="#linearGradient3681"
id="linearGradient3687"
x1="37.89756"
y1="41.087898"
x2="4.0605712"
y2="40.168594"
gradientUnits="userSpaceOnUse"
gradientTransform="translate(127.27273,-51.272729)" />
<linearGradient
inkscape:collect="always"
xlink:href="#linearGradient3681"
id="linearGradient3695"
x1="37.894287"
y1="40.484772"
x2="59.811455"
y2="43.558987"
gradientUnits="userSpaceOnUse"
gradientTransform="translate(127.27273,-51.272729)" />
<linearGradient
id="linearGradient3681-3">
<stop
id="stop3697-3"
offset="0"
style="stop-color:#fff110;stop-opacity:1;" />
<stop
style="stop-color:#cf7008;stop-opacity:1;"
offset="1"
id="stop3685-4" />
</linearGradient>
<linearGradient
y2="43.558987"
x2="59.811455"
y1="40.484772"
x1="37.894287"
gradientTransform="translate(-37.00068,-20.487365)"
gradientUnits="userSpaceOnUse"
id="linearGradient3608"
xlink:href="#linearGradient3681-3"
inkscape:collect="always" />
<linearGradient
id="linearGradient4513-2">
<stop
style="stop-color:#ffffff;stop-opacity:1;"
offset="0"
id="stop4515-2" />
<stop
style="stop-color:#999999;stop-opacity:1;"
offset="1"
id="stop4517-4" />
</linearGradient>
<radialGradient
r="23.634638"
fy="7.9319997"
fx="32.151962"
cy="7.9319997"
cx="32.151962"
gradientTransform="matrix(1,0,0,1.1841158,-8.5173246,-3.4097568)"
gradientUnits="userSpaceOnUse"
id="radialGradient4538"
xlink:href="#linearGradient4513-2"
inkscape:collect="always" />
<linearGradient
id="linearGradient4513-1">
<stop
style="stop-color:#ffffff;stop-opacity:1;"
offset="0"
id="stop4515-8" />
<stop
style="stop-color:#999999;stop-opacity:1;"
offset="1"
id="stop4517-6" />
</linearGradient>
<radialGradient
r="23.634638"
fy="7.9319997"
fx="32.151962"
cy="7.9319997"
cx="32.151962"
gradientTransform="matrix(1,0,0,1.1841158,-8.5173246,-3.4097568)"
gradientUnits="userSpaceOnUse"
id="radialGradient4538-6"
xlink:href="#linearGradient4513-1"
inkscape:collect="always" />
<linearGradient
id="linearGradient4513-1-3">
<stop
style="stop-color:#ffffff;stop-opacity:1;"
offset="0"
id="stop4515-8-7" />
<stop
style="stop-color:#999999;stop-opacity:1;"
offset="1"
id="stop4517-6-5" />
</linearGradient>
<radialGradient
r="23.634638"
fy="35.869175"
fx="32.151962"
cy="35.869175"
cx="32.151962"
gradientTransform="matrix(0.39497909,0,0,1.1841158,-2.716491,-26.067007)"
gradientUnits="userSpaceOnUse"
id="radialGradient3069"
xlink:href="#linearGradient4513-1-3"
inkscape:collect="always" />
<linearGradient
id="linearGradient4513-1-2">
<stop
style="stop-color:#ffffff;stop-opacity:1;"
offset="0"
id="stop4515-8-6" />
<stop
style="stop-color:#999999;stop-opacity:1;"
offset="1"
id="stop4517-6-6" />
</linearGradient>
<radialGradient
r="23.634638"
fy="35.869175"
fx="32.151962"
cy="35.869175"
cx="32.151962"
gradientTransform="matrix(0.39497909,0,0,1.1841158,-2.716491,-26.067007)"
gradientUnits="userSpaceOnUse"
id="radialGradient3102"
xlink:href="#linearGradient4513-1-2"
inkscape:collect="always" />
<radialGradient
inkscape:collect="always"
xlink:href="#linearGradient4513-1"
id="radialGradient3132"
gradientUnits="userSpaceOnUse"
gradientTransform="matrix(0.39497909,0,0,1.1841158,64.952609,-7.0541574)"
cx="32.151962"
cy="27.950663"
fx="32.151962"
fy="27.950663"
r="23.634638" />
<linearGradient
inkscape:collect="always"
xlink:href="#linearGradient4066"
id="linearGradient4072"
x1="41.610756"
y1="59.853931"
x2="32.101425"
y2="10.99928"
gradientUnits="userSpaceOnUse" />
<linearGradient
inkscape:collect="always"
xlink:href="#linearGradient4031"
id="linearGradient4055"
gradientUnits="userSpaceOnUse"
gradientTransform="translate(71.494719,-3.1982556)"
x1="30.000002"
y1="14"
x2="36"
y2="54.227272" />
<linearGradient
id="linearGradient4031">
<stop
id="stop4033"
offset="0"
style="stop-color:#d3d7cf;stop-opacity:1" />
<stop
id="stop4035"
offset="1"
style="stop-color:#888a85;stop-opacity:1" />
</linearGradient>
<linearGradient
inkscape:collect="always"
xlink:href="#linearGradient4513"
id="linearGradient4233"
x1="19"
y1="33"
x2="49.566406"
y2="33"
gradientUnits="userSpaceOnUse" />
</defs>
<sodipodi:namedview
id="base"
pagecolor="#ffffff"
bordercolor="#666666"
borderopacity="1.0"
inkscape:pageopacity="0.0"
inkscape:pageshadow="2"
inkscape:zoom="11"
inkscape:cx="-5.6052856"
inkscape:cy="28.449344"
inkscape:current-layer="layer1"
showgrid="true"
inkscape:document-units="px"
inkscape:grid-bbox="true"
inkscape:snap-bbox="true"
inkscape:bbox-paths="true"
inkscape:bbox-nodes="true"
inkscape:snap-bbox-edge-midpoints="true"
inkscape:snap-bbox-midpoints="true"
inkscape:object-paths="true"
inkscape:object-nodes="true"
inkscape:window-width="1920"
inkscape:window-height="1013"
inkscape:window-x="0"
inkscape:window-y="1050"
inkscape:window-maximized="1"
inkscape:snap-global="true">
<inkscape:grid
type="xygrid"
id="grid3234"
empspacing="2"
visible="true"
enabled="true"
snapvisiblegridlinesonly="true" />
</sodipodi:namedview>
<metadata
id="metadata2821">
<rdf:RDF>
<cc:Work
rdf:about="">
<dc:format>image/svg+xml</dc:format>
<dc:type
rdf:resource="http://purl.org/dc/dcmitype/StillImage" />
<dc:title />
<dc:title>Path-FaceProfile</dc:title>
<dc:date>2016-01-19</dc:date>
<dc:relation>http://www.freecadweb.org/wiki/index.php?title=Artwork</dc:relation>
<dc:publisher>
<cc:Agent>
<dc:title>FreeCAD</dc:title>
</cc:Agent>
</dc:publisher>
<dc:identifier>FreeCAD/src/Mod/Path/Gui/Resources/icons/Path-</dc:identifier>
<dc:rights>
<cc:Agent>
<dc:title>FreeCAD LGPL2+</dc:title>
</cc:Agent>
</dc:rights>
<cc:license>https://www.gnu.org/copyleft/lesser.html</cc:license>
<dc:contributor>
<cc:Agent>
<dc:title>[agryson] Alexander Gryson</dc:title>
</cc:Agent>
</dc:contributor>
</cc:Work>
</rdf:RDF>
</metadata>
<g
id="layer1"
inkscape:label="Layer 1"
inkscape:groupmode="layer">
<path
style="color:#000000;fill:none;stroke:#172a04;stroke-width:8;stroke-linecap:round;stroke-linejoin:miter;stroke-miterlimit:4;stroke-opacity:1;stroke-dasharray:none;stroke-dashoffset:0;marker:none;visibility:visible;display:inline;overflow:visible;enable-background:accumulate"
d="M 50,13 39,11 c -4,-1 -5.171573,-1.4142136 -8,0 -2,1 -3,2 -5,5 L 7,46 c -3,4 -1,7 3,8 l 28,4 c 5,1 9,-2 12,-6 l 4,-5"
id="rect3083-0"
inkscape:connector-curvature="0"
sodipodi:nodetypes="ccscccccc" />
<path
style="color:#000000;fill:none;stroke:url(#linearGradient4072);stroke-width:4;stroke-linecap:round;stroke-linejoin:miter;stroke-miterlimit:4;stroke-opacity:1;stroke-dasharray:none;stroke-dashoffset:0;marker:none;visibility:visible;display:inline;overflow:visible;enable-background:accumulate"
d="M 50,13 39,11 c -4,-1 -5.171573,-1.414214 -8,0 -2,1 -3,2 -5,5 L 7,46 c -3,4 -1,7 3,8 l 28,4 c 5,1 9,-2 12,-6 l 4,-5"
id="rect3083-0-6"
inkscape:connector-curvature="0"
sodipodi:nodetypes="ccscccccc" />
<path
style="color:#000000;fill:none;stroke:#8ae234;stroke-width:2;stroke-linecap:round;stroke-linejoin:miter;stroke-miterlimit:4;stroke-opacity:1;stroke-dasharray:none;stroke-dashoffset:0;marker:none;visibility:visible;display:inline;overflow:visible;enable-background:accumulate"
d="M 50.064283,12 38.517882,9.919647 c -4,-1.0000001 -5.171573,-1.4142141 -8,0 -2,1 -3,2 -5,5 l -19.0000003,30 c -2.9999999,4 -1,7 2.9999998,8 l 28.0000005,4 c 5,1 9,-2 12,-6 L 53,46"
id="rect3083-0-6-7"
inkscape:connector-curvature="0"
sodipodi:nodetypes="ccscccccc" />
<text
xml:space="preserve"
style="font-style:normal;font-variant:normal;font-weight:bold;font-stretch:normal;font-size:40px;line-height:125%;font-family:sans-serif;-inkscape-font-specification:'sans-serif Bold';letter-spacing:0px;word-spacing:0px;fill:url(#linearGradient4233);fill-opacity:1.0;stroke:#000000;stroke-width:2.5;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1;stroke-miterlimit:4;stroke-dasharray:none"
x="18.804688"
y="47.580078"
id="text4223"
sodipodi:linespacing="125%"><tspan
sodipodi:role="line"
id="tspan4225"
x="18.804688"
y="47.580078"
style="fill-opacity:1.0;fill:url(#linearGradient4233);stroke:#000000;stroke-opacity:1;stroke-width:2.5;stroke-miterlimit:4;stroke-dasharray:none">A</tspan></text>
</g>
</svg>

After

Width:  |  Height:  |  Size: 22 KiB

View File

@@ -102,13 +102,14 @@ class PathWorkbench (Workbench):
from PathScripts import PathToolController
from PathScripts import PathToolLibraryManager
from PathScripts import PathSimulatorGui
from PathScripts import PathAdaptiveGui
import PathCommands
# build commands list
projcmdlist = ["Path_Job", "Path_Post"]
toolcmdlist = ["Path_Inspect", "Path_Simulator", "Path_ToolLibraryEdit", "Path_SelectLoop"]
prepcmdlist = ["Path_Fixture", "Path_Comment", "Path_Stop", "Path_Custom"]
twodopcmdlist = ["Path_Contour", "Path_Profile_Faces", "Path_Profile_Edges", "Path_Pocket_Shape", "Path_Drilling", "Path_MillFace", "Path_Helix"]
twodopcmdlist = ["Path_Contour", "Path_Profile_Faces", "Path_Profile_Edges", "Path_Pocket_Shape", "Path_Drilling", "Path_MillFace", "Path_Helix", "Path_Adaptive"]
threedopcmdlist = ["Path_Pocket_3D"]
engravecmdlist = ["Path_Engrave"]
modcmdlist = ["Path_OperationCopy", "Path_Array", "Path_SimpleCopy" ]

View File

@@ -0,0 +1,421 @@
import PathScripts.PathOp as PathOp
import Path
import FreeCAD
import FreeCADGui
from FreeCAD import Console
import time
import json
import math
import area
from pivy import coin
__doc__ = "Class and implementation of the Adaptive path operation."
def discretize(edge, flipDirection=False):
pts=edge.discretize(Deflection=0.01)
if flipDirection: pts.reverse()
return pts
def IsEqualInXYPlane(e1, e2):
return math.sqrt((e2.x-e1.x)*(e2.x-e1.x) +
(e2.y - e1.y) * (e2.y - e1.y))<0.01
def connectEdges(edges):
''' Makes the list of connected discretized paths '''
# find edge
lastPoint=None
remaining = []
pathArray = []
combined = []
#print "Input edges , remove duplicate projections to xy plane"
for edge in edges:
p1 = edge.valueAt(edge.FirstParameter)
p2 = edge.valueAt(edge.LastParameter)
duplicate = False
for ex in remaining:
exp1 = ex.valueAt(ex.FirstParameter)
exp2 = ex.valueAt(ex.LastParameter)
if IsEqualInXYPlane(exp1, p1) and IsEqualInXYPlane(exp2, p2):
duplicate = True
if IsEqualInXYPlane(exp1, p2) and IsEqualInXYPlane(exp2, p1):
duplicate = True
if not duplicate:
remaining.append(edge)
#print "remaining:", remaining
newPath=True
while len(remaining)>0:
if newPath:
#print "new iteration"
edge=remaining[0]
p1 = edge.valueAt(edge.FirstParameter)
p2 = edge.valueAt(edge.LastParameter)
#print edge, p1, p2
if len(combined)>0: pathArray.append(combined)
combined = []
combined.append(discretize(edge))
remaining.remove(edge)
lastPoint=p2
newPath=False
anyMatch=False
for e in remaining:
p1 = e.valueAt(e.FirstParameter)
p2 = e.valueAt(e.LastParameter)
#print "chk",e, p1, p2
if IsEqualInXYPlane(lastPoint,p1):
#print "last Point equal p1"
combined.append(discretize(e))
remaining.remove(e)
lastPoint=p2
anyMatch=True
break
elif IsEqualInXYPlane(lastPoint,p2):
#print "reversed"
combined.append(discretize(e,True))
remaining.remove(e)
lastPoint=p1
anyMatch=True
break
if not anyMatch:
newPath=True
#make sure last path is appended
if len(combined)>0: pathArray.append(combined)
combined = []
return pathArray
def convertTo2d(pathArray):
output = []
for path in pathArray:
pth2 = []
for edge in path:
for pt in edge:
pth2.append([pt[0],pt[1]])
output.append(pth2)
return output
sceneGraph = None
scenePathNodes = [] #for scene cleanup aftewards
topZ = 10
def sceneDrawPath(path, color=(0, 0, 1)):
global sceneGraph
global scenePathNodes
coPoint = coin.SoCoordinate3()
pts = []
for pt in path:
pts.append([pt[0], pt[1], topZ])
coPoint.point.setValues(0, len(pts), pts)
ma = coin.SoBaseColor()
ma.rgb = color
li = coin.SoLineSet()
li.numVertices.setValue(len(pts))
pathNode = coin.SoSeparator()
pathNode.addChild(coPoint)
pathNode.addChild(ma)
pathNode.addChild(li)
sceneGraph.addChild(pathNode)
scenePathNodes.append(pathNode) #for scene cleanup afterwards
def sceneClean():
global scenePathNodes
for n in scenePathNodes:
sceneGraph.removeChild(n)
del scenePathNodes[:]
def GenerateGCode(op,obj,adaptiveResults, helixDiameter):
if len(adaptiveResults)==0 or len(adaptiveResults[0]["AdaptivePaths"])==0:
return
minLiftDistance = op.tool.Diameter
p1 = adaptiveResults[0]["HelixCenterPoint"]
p2 = adaptiveResults[0]["StartPoint"]
helixRadius =math.sqrt((p1[0]-p2[0]) * (p1[0]-p2[0]) + (p1[1]-p2[1]) * (p1[1]-p2[1]))
stepDown = obj.StepDown.Value
passStartDepth=obj.StartDepth.Value
if stepDown<0.1 : stepDown=0.1
length = 2*math.pi * helixRadius
if obj.HelixAngle<1: obj.HelixAngle=1
helixAngleRad = math.pi * obj.HelixAngle/180.0
depthPerOneCircle=length * math.tan(helixAngleRad)
stepUp = obj.LiftDistance.Value
if stepUp<0:
stepUp=0
lx=adaptiveResults[0]["HelixCenterPoint"][0]
ly=adaptiveResults[0]["HelixCenterPoint"][1]
step=0
while passStartDepth>obj.FinalDepth.Value and step<1000:
step=step+1
passEndDepth=passStartDepth-stepDown
if passEndDepth<obj.FinalDepth.Value: passEndDepth=obj.FinalDepth.Value
for region in adaptiveResults:
startAngle = math.atan2(region["StartPoint"][1] - region["HelixCenterPoint"][1], region["StartPoint"][0] - region["HelixCenterPoint"][0])
lx=region["HelixCenterPoint"][0]
ly=region["HelixCenterPoint"][1]
r = helixRadius - 0.01
#helix ramp
passDepth = (passStartDepth - passEndDepth)
maxfi = passDepth / depthPerOneCircle * 2 * math.pi
fi = 0
offsetFi =-maxfi + startAngle-math.pi/16
helixStart = [region["HelixCenterPoint"][0] + r * math.cos(offsetFi), region["HelixCenterPoint"][1] + r * math.sin(offsetFi)]
op.commandlist.append(Path.Command("(helix to depth: %f)"%passEndDepth))
#if step == 1:
#rapid move to start point
op.commandlist.append(Path.Command(
"G0", {"X": helixStart[0], "Y": helixStart[1], "Z": obj.ClearanceHeight.Value}))
#rapid move to safe height
op.commandlist.append(Path.Command(
"G0", {"X": helixStart[0], "Y": helixStart[1], "Z": obj.SafeHeight.Value}))
op.commandlist.append(Path.Command("G1", {
"X": helixStart[0], "Y": helixStart[1], "Z": passStartDepth, "F": op.vertFeed}))
while fi<maxfi:
x = region["HelixCenterPoint"][0] + r * math.cos(fi+offsetFi)
y = region["HelixCenterPoint"][1] + r * math.sin(fi+offsetFi)
z = passStartDepth - fi / maxfi * (passStartDepth - passEndDepth)
op.commandlist.append(Path.Command("G1", { "X": x, "Y":y, "Z":z, "F": op.vertFeed}))
lx=x
ly=y
fi=fi+math.pi/16
op.commandlist.append(Path.Command("(adaptive - depth: %f)"%passEndDepth))
#add adaptive paths
for pth in region["AdaptivePaths"]:
#print pth.Points
motionType = pth[0] #[0] contains motion type
for pt in pth[1]: #[1] contains list of points
x=pt[0]
y =pt[1]
dist=math.sqrt((x-lx)*(x-lx) + (y-ly)*(y-ly))
if motionType == area.AdaptiveMotionType.Cutting:
op.commandlist.append(Path.Command("G1", { "X": x, "Y":y, "Z":passEndDepth, "F": op.horizFeed}))
elif motionType == area.AdaptiveMotionType.LinkClear:
if dist > minLiftDistance:
if lx!=x or ly!=y:
op.commandlist.append(Path.Command("G0", { "X": lx, "Y":ly, "Z":passEndDepth+stepUp}))
op.commandlist.append(Path.Command("G0", { "X": x, "Y":y, "Z":passEndDepth+stepUp}))
elif motionType == area.AdaptiveMotionType.LinkNotClear:
if lx!=x or ly!=y:
op.commandlist.append(Path.Command("G0", { "X": lx, "Y":ly, "Z":obj.ClearanceHeight.Value}))
op.commandlist.append(Path.Command("G0", { "X": x, "Y":y, "Z":obj.ClearanceHeight.Value}))
elif motionType == area.AdaptiveMotionType.LinkClearAtPrevPass:
if lx!=x or ly!=y:
op.commandlist.append(Path.Command("G0", { "X": lx, "Y":ly, "Z":passStartDepth+stepUp}))
op.commandlist.append(Path.Command("G0", { "X": x, "Y":y, "Z":passStartDepth+stepUp}))
lx=x
ly=y
passStartDepth=passEndDepth
#return to safe height in this Z pass
op.commandlist.append(Path.Command("G0", { "X": lx, "Y":ly, "Z":obj.ClearanceHeight.Value}))
op.commandlist.append(Path.Command("G0", { "X": lx, "Y":ly, "Z":obj.ClearanceHeight.Value}))
def Execute(op,obj):
global sceneGraph
global topZ
sceneGraph = FreeCADGui.ActiveDocument.ActiveView.getSceneGraph()
Console.PrintMessage("*** Adaptive toolpath processing started...\n")
#hide old toolpaths during recalculation
obj.Path = Path.Path("(calculating...)")
#store old visibility state
job = op.getJob(obj)
oldObjVisibility = obj.ViewObject.Visibility
oldJobVisibility = job.ViewObject.Visibility
obj.ViewObject.Visibility = False
job.ViewObject.Visibility = False
FreeCADGui.updateGui()
try:
Console.PrintMessage("Tool diam: %f \n"%op.tool.Diameter)
helixDiameter = min(op.tool.Diameter,1000.0 if obj.HelixDiameterLimit.Value==0.0 else obj.HelixDiameterLimit.Value )
nestingLimit=0
topZ=op.stock.Shape.BoundBox.ZMax
opType = area.AdaptiveOperationType.Clearing
obj.Stopped = False
obj.StopProcessing = False
if obj.Tolerance<0.001: obj.Tolerance=0.001
edges=[]
for base, subs in obj.Base:
#print (base,subs)
for sub in subs:
shape=base.Shape.getElement(sub)
for edge in shape.Edges:
edges.append(edge)
pathArray=connectEdges(edges)
if obj.OperationType == "Clearing":
if obj.Side == "Outside":
stockBB = op.stock.Shape.BoundBox
v=[]
v.append(FreeCAD.Vector(stockBB.XMin,stockBB.YMin,0))
v.append(FreeCAD.Vector(stockBB.XMax,stockBB.YMin,0))
v.append(FreeCAD.Vector(stockBB.XMax,stockBB.YMax,0))
v.append(FreeCAD.Vector(stockBB.XMin,stockBB.YMax,0))
v.append(FreeCAD.Vector(stockBB.XMin,stockBB.YMin,0))
pathArray.append([v])
if not obj.ProcessHoles: nestingLimit = 2
elif not obj.ProcessHoles: nestingLimit = 1
opType = area.AdaptiveOperationType.Clearing
else: # profiling
if obj.Side == "Outside":
opType = area.AdaptiveOperationType.ProfilingOutside
else:
opType = area.AdaptiveOperationType.ProfilingInside
if not obj.ProcessHoles: nestingLimit = 1
path2d = convertTo2d(pathArray)
# put here all properties that influence calculation of adaptive base paths,
inputStateObject = {
"tool": op.tool.Diameter,
"tolerance": obj.Tolerance,
"geometry" : path2d,
"stepover" :obj.StepOver,
"effectiveHelixDiameter": helixDiameter,
"operationType": obj.OperationType,
"side": obj.Side,
"processHoles": obj.ProcessHoles
}
inputStateChanged=False
adaptiveResults=None
if obj.AdaptiveOutputState !=None and obj.AdaptiveOutputState != "":
adaptiveResults = obj.AdaptiveOutputState
if json.dumps(obj.AdaptiveInputState) != json.dumps(inputStateObject):
inputStateChanged=True
adaptiveResults=None
# progress callback fn, if return true it will stop processing
def progressFn(tpaths):
for path in tpaths: #path[0] contains the MotionType,#path[1] contains list of points
sceneDrawPath(path[1])
FreeCADGui.updateGui()
return obj.StopProcessing
start=time.time()
if inputStateChanged or adaptiveResults==None:
a2d = area.Adaptive2d()
a2d.stepOverFactor = 0.01*obj.StepOver
a2d.toolDiameter = op.tool.Diameter
a2d.helixRampDiameter = helixDiameter
a2d.tolerance = obj.Tolerance
a2d.opType = opType
a2d.polyTreeNestingLimit = nestingLimit
#EXECUTE
results = a2d.Execute(path2d,progressFn)
#need to convert results to python object to be JSON serializable
adaptiveResults = []
for result in results:
adaptiveResults.append({
"HelixCenterPoint": result.HelixCenterPoint,
"StartPoint": result.StartPoint,
"AdaptivePaths": result.AdaptivePaths,
"ReturnMotionType": result.ReturnMotionType })
GenerateGCode(op,obj,adaptiveResults,helixDiameter)
if not obj.StopProcessing:
Console.PrintMessage("*** Done. Elapsed: %f sec\n\n" %(time.time()-start))
obj.AdaptiveOutputState = adaptiveResults
obj.AdaptiveInputState=inputStateObject
else:
Console.PrintMessage("*** Processing cancelled (after: %f sec).\n\n" %(time.time()-start))
finally:
obj.ViewObject.Visibility = oldObjVisibility
job.ViewObject.Visibility = oldJobVisibility
sceneClean()
class PathAdaptive(PathOp.ObjectOp):
def opFeatures(self, obj):
'''opFeatures(obj) ... returns the OR'ed list of features used and supported by the operation.
The default implementation returns "FeatureTool | FeatureDeptsh | FeatureHeights | FeatureStartPoint"
Should be overwritten by subclasses.'''
return PathOp.FeatureTool | PathOp.FeatureBaseEdges | PathOp.FeatureDepths | PathOp.FeatureStepDown | PathOp.FeatureHeights | PathOp.FeatureBaseGeometry
def initOperation(self, obj):
'''initOperation(obj) ... implement to create additional properties.
Should be overwritten by subclasses.'''
obj.addProperty("App::PropertyEnumeration", "Side", "Adaptive", "Side of selected faces that tool should cut")
obj.Side = ['Outside', 'Inside'] # side of profile that cutter is on in relation to direction of profile
obj.addProperty("App::PropertyEnumeration", "OperationType", "Adaptive", "Type of adaptive operation")
obj.OperationType = ['Clearing', 'Profiling'] # side of profile that cutter is on in relation to direction of profile
obj.addProperty("App::PropertyFloat", "Tolerance", "Adaptive", "Influences accuracy and performance")
obj.addProperty("App::PropertyPercent", "StepOver", "Adaptive", "Percent of cutter diameter to step over on each pass")
obj.addProperty("App::PropertyDistance", "LiftDistance", "Adaptive", "Lift distance for rapid moves")
obj.addProperty("App::PropertyBool", "ProcessHoles", "Adaptive","Process holes as well as the face outline")
obj.addProperty("App::PropertyBool", "Stopped",
"Adaptive", "Stop processing")
obj.setEditorMode('Stopped', 2) #hide this property
obj.addProperty("App::PropertyBool", "StopProcessing",
"Adaptive", "Stop processing")
obj.setEditorMode('StopProcessing', 2) # hide this property
obj.addProperty("App::PropertyPythonObject", "AdaptiveInputState",
"Adaptive", "Internal input state")
obj.addProperty("App::PropertyPythonObject", "AdaptiveOutputState",
"Adaptive", "Internal output state")
obj.setEditorMode('AdaptiveInputState', 2) #hide this property
obj.setEditorMode('AdaptiveOutputState', 2) #hide this property
obj.addProperty("App::PropertyAngle", "HelixAngle", "Adaptive", "Helix ramp entry angle (degrees)")
obj.addProperty("App::PropertyLength", "HelixDiameterLimit", "Adaptive", "Limit helix entry diameter, if limit larger than tool diameter or 0, tool diameter is used")
def opSetDefaultValues(self, obj):
obj.Side="Inside"
obj.OperationType = "Clearing"
obj.Tolerance = 0.1
obj.StepOver = 20
obj.LiftDistance=1.0
obj.ProcessHoles = True
obj.Stopped = False
obj.StopProcessing = False
obj.HelixAngle = 5
obj.HelixDiameterLimit = 0.0
obj.AdaptiveInputState =""
obj.AdaptiveOutputState = ""
def opExecute(self, obj):
'''opExecute(obj) ... called whenever the receiver needs to be recalculated.
See documentation of execute() for a list of base functionality provided.
Should be overwritten by subclasses.'''
Execute(self,obj)
def Create(name):
'''Create(name) ... Creates and returns a Pocket operation.'''
obj = FreeCAD.ActiveDocument.addObject("Path::FeaturePython", name)
proxy = PathAdaptive(obj)
return obj

View File

@@ -0,0 +1,168 @@
import FreeCAD
import FreeCADGui
import PathScripts.PathLog as PathLog
import PathScripts.PathGui as PathGui
import PathScripts.PathOpGui as PathOpGui
from PySide import QtCore, QtGui
import PathAdaptive
class TaskPanelOpPage(PathOpGui.TaskPanelPage):
def initPage(self, obj):
self.setTitle("Adaptive path operation")
def getForm(self):
form = QtGui.QWidget()
layout = QtGui.QVBoxLayout()
#tool contoller
hlayout = QtGui.QHBoxLayout()
form.ToolController = QtGui.QComboBox()
form.ToolControllerLabel=QtGui.QLabel("Tool Controller")
hlayout.addWidget(form.ToolControllerLabel)
hlayout.addWidget(form.ToolController)
layout.addLayout(hlayout)
#cut region
formLayout = QtGui.QFormLayout()
form.Side = QtGui.QComboBox()
form.Side.addItem("Inside")
form.Side.addItem("Outside")
form.Side.setToolTip("Cut inside or outside of the selected face")
formLayout.addRow(QtGui.QLabel("Cut Region"),form.Side)
#operation type
form.OperationType = QtGui.QComboBox()
form.OperationType.addItem("Clearing")
form.OperationType.addItem("Profiling")
form.OperationType.setToolTip("Type of adaptive operation")
formLayout.addRow(QtGui.QLabel("Operation Type"),form.OperationType)
#step over
form.StepOver = QtGui.QSpinBox()
form.StepOver.setMinimum(15)
form.StepOver.setMaximum(50)
form.StepOver.setSingleStep(1)
form.StepOver.setValue(25)
form.StepOver.setToolTip("Tool step over percentage")
formLayout.addRow(QtGui.QLabel("Step Over Percent"),form.StepOver)
#tolerance
form.Tolerance = QtGui.QSlider(QtCore.Qt.Horizontal)
form.Tolerance.setMinimum(5)
form.Tolerance.setMaximum(15)
form.Tolerance.setTickInterval(1)
form.Tolerance.setValue(10)
form.Tolerance.setTickPosition(QtGui.QSlider.TicksBelow)
form.Tolerance.setToolTip("Influences calculation performace vs stability and accuracy")
formLayout.addRow(QtGui.QLabel("Accuracy vs Performance"),form.Tolerance)
#helix angle
form.HelixAngle = QtGui.QDoubleSpinBox()
form.HelixAngle.setMinimum(0.1)
form.HelixAngle.setMaximum(90)
form.HelixAngle.setSingleStep(0.1)
form.HelixAngle.setValue(5)
form.HelixAngle.setToolTip("Angle of the helix ramp entry")
formLayout.addRow(QtGui.QLabel("Helix Ramp Angle"),form.HelixAngle)
#helix diam. limit
form.HelixDiameterLimit = QtGui.QDoubleSpinBox()
form.HelixDiameterLimit.setMinimum(0.0)
form.HelixDiameterLimit.setMaximum(90)
form.HelixDiameterLimit.setSingleStep(0.1)
form.HelixDiameterLimit.setValue(0)
form.HelixDiameterLimit.setToolTip("If non zero it limits the size helix diameter, otherwise the tool radius is taken as the helix diameter")
formLayout.addRow(QtGui.QLabel("Helix Max Diameter"),form.HelixDiameterLimit)
#lift distance
form.LiftDistance = QtGui.QDoubleSpinBox()
form.LiftDistance.setMinimum(0.0)
form.LiftDistance.setMaximum(1000)
form.LiftDistance.setSingleStep(0.1)
form.LiftDistance.setValue(1.0)
form.LiftDistance.setToolTip("How much to lift the tool up during the rapid repositioning moves (used when no obstacles)")
formLayout.addRow(QtGui.QLabel("Lift Distance"),form.LiftDistance)
#process holes
form.ProcessHoles = QtGui.QCheckBox()
form.ProcessHoles.setChecked(True)
formLayout.addRow(QtGui.QLabel("Process Holes"),form.ProcessHoles)
layout.addLayout(formLayout)
#stop button
form.StopButton=QtGui.QPushButton("Stop")
form.StopButton.setCheckable(True)
layout.addWidget(form.StopButton)
form.setLayout(layout)
return form
def getSignalsForUpdate(self, obj):
'''getSignalsForUpdate(obj) ... return list of signals for updating obj'''
signals = []
#signals.append(self.form.button.clicked)
signals.append(self.form.Side.currentIndexChanged)
signals.append(self.form.OperationType.currentIndexChanged)
signals.append(self.form.ToolController.currentIndexChanged)
signals.append(self.form.StepOver.valueChanged)
signals.append(self.form.Tolerance.valueChanged)
signals.append(self.form.HelixAngle.valueChanged)
signals.append(self.form.HelixDiameterLimit.valueChanged)
signals.append(self.form.LiftDistance.valueChanged)
signals.append(self.form.ProcessHoles.stateChanged)
signals.append(self.form.StopButton.toggled)
return signals
def setFields(self, obj):
self.selectInComboBox(obj.Side, self.form.Side)
self.selectInComboBox(obj.OperationType, self.form.OperationType)
self.form.StepOver.setValue(obj.StepOver)
self.form.Tolerance.setValue(int(obj.Tolerance*100))
self.form.HelixAngle.setValue(obj.HelixAngle)
self.form.HelixDiameterLimit.setValue(obj.HelixDiameterLimit)
self.form.LiftDistance.setValue(obj.LiftDistance)
self.form.ProcessHoles.setChecked(obj.ProcessHoles)
self.setupToolController(obj, self.form.ToolController)
self.form.StopButton.setChecked(obj.Stopped)
obj.setEditorMode('AdaptiveInputState', 2) #hide this property
obj.setEditorMode('AdaptiveOutputState', 2) #hide this property
obj.setEditorMode('StopProcessing', 2) # hide this property
obj.setEditorMode('Stopped', 2) # hide this property
def getFields(self, obj):
if obj.Side != str(self.form.Side.currentText()):
obj.Side = str(self.form.Side.currentText())
if obj.OperationType != str(self.form.OperationType.currentText()):
obj.OperationType = str(self.form.OperationType.currentText())
obj.StepOver = self.form.StepOver.value()
obj.Tolerance = 1.0*self.form.Tolerance.value()/100.0
obj.HelixAngle = self.form.HelixAngle.value()
obj.HelixDiameterLimit = self.form.HelixDiameterLimit.value()
obj.LiftDistance = self.form.LiftDistance.value()
obj.ProcessHoles = self.form.ProcessHoles.isChecked()
obj.Stopped = self.form.StopButton.isChecked()
if(obj.Stopped):
self.form.StopButton.setChecked(False) #reset the button
obj.StopProcessing=True
self.updateToolController(obj, self.form.ToolController)
obj.setEditorMode('AdaptiveInputState', 2) #hide this property
obj.setEditorMode('AdaptiveOutputState', 2) #hide this property
obj.setEditorMode('StopProcessing', 2) # hide this property
obj.setEditorMode('Stopped', 2) # hide this property
Command = PathOpGui.SetupOperation('Adaptive',
PathAdaptive.Create,
TaskPanelOpPage,
'Path-Adaptive',
QtCore.QT_TRANSLATE_NOOP("PathAdaptive", "Adaptive"),
QtCore.QT_TRANSLATE_NOOP("PathPocket", "Adaptive clearing and profiling"))

View File

@@ -157,6 +157,31 @@ class POCKETGate:
return pocketable
class ADAPTIVEGate:
def allow(self, doc, obj, sub):
adaptive = False
try:
obj = obj.Shape
except:
return False
if obj.ShapeType == 'Edge':
adaptive = False
elif obj.ShapeType == 'Face':
adaptive = True
elif obj.ShapeType == 'Solid':
if sub and sub[0:4] == 'Face':
adaptive = True
elif obj.ShapeType == 'Compound':
if sub and sub[0:4] == 'Face':
adaptive = True
return adaptive
class CONTOURGate:
def allow(self, doc, obj, sub):
pass
@@ -189,6 +214,10 @@ def pocketselect():
FreeCADGui.Selection.addSelectionGate(POCKETGate())
FreeCAD.Console.PrintWarning("Pocketing Select Mode\n")
def adaptiveselect():
FreeCADGui.Selection.addSelectionGate(ADAPTIVEGate())
FreeCAD.Console.PrintWarning("Adaptive Select Mode\n")
def surfaceselect():
FreeCADGui.Selection.addSelectionGate(MESHGate())
FreeCAD.Console.PrintWarning("Surfacing Select Mode\n")
@@ -207,6 +236,7 @@ def select(op):
opsel['Profile Edges'] = eselect
opsel['Profile Faces'] = profileselect
opsel['Surface'] = surfaceselect
opsel['Adaptive'] = adaptiveselect
return opsel[op]
def clear():

File diff suppressed because it is too large Load Diff

View File

@@ -0,0 +1,109 @@
#include "clipper.hpp"
#include <vector>
#include <list>
#ifndef ADAPTIVE_HPP
#define ADAPTIVE_HPP
//#define DEV_MODE
#define NTOL 1.0e-7 // numeric tolerance
namespace AdaptivePath {
using namespace ClipperLib;
enum MotionType { mtCutting = 0, mtLinkClear = 1, mtLinkNotClear = 2, mtLinkClearAtPrevPass = 3 };
enum OperationType { otClearing = 0, otProfilingInside = 1, otProfilingOutside = 2 };
typedef std::pair<double,double> DPoint;
typedef std::vector<DPoint> DPath;
typedef std::vector<DPath> DPaths;
typedef std::pair<int,DPath> TPath; // first parameter is MotionType, must use int due to problem with serialization to JSON in python
// struct TPath { #this does not work correctly with pybind, changed to pair
// DPath Points;
// MotionType MType;
// };
typedef std::vector<TPath> TPaths;
struct AdaptiveOutput {
DPoint HelixCenterPoint;
DPoint StartPoint;
TPaths AdaptivePaths;
int ReturnMotionType; // MotionType enum, problem with serialization if enum is used
};
// used to isolate state -> enable potential adding of multi-threaded processing of separate regions
class Adaptive2d {
public:
Adaptive2d();
double toolDiameter=5;
double helixRampDiameter=0;
double stepOverFactor = 0.2;
int polyTreeNestingLimit=0;
double tolerance=0.1;
OperationType opType = OperationType::otClearing;
std::list<AdaptiveOutput> Execute(const DPaths &paths, std::function<bool(TPaths)> progressCallbackFn);
#ifdef DEV_MODE
/*for debugging*/
std::function<void(double cx,double cy, double radius, int color)> DrawCircleFn;
std::function<void(const DPath &, int color)> DrawPathFn;
std::function<void()> ClearScreenFn;
#endif
private:
std::list<AdaptiveOutput> results;
Paths inputPaths;
double scaleFactor=100;
long toolRadiusScaled=10;
long finishPassOffsetScaled=0;
long helixRampRadiusScaled=0;
long bbox_size=0;
double referenceCutArea=0;
double optimalCutAreaPD=0;
double minCutAreaPD=0;
bool stopProcessing=false;
time_t lastProgressTime = 0;
std::function<bool(TPaths)> * progressCallback=NULL;
Path toolGeometry; // tool geometry at coord 0,0, should not be modified
void ProcessPolyNode(Paths & boundPaths, Paths & toolBoundPaths);
bool FindEntryPoint(const Paths & toolBoundPaths,const Paths &bound, Paths &cleared /*output*/, IntPoint &entryPoint /*output*/);
double CalcCutArea(Clipper & clip,const IntPoint &toolPos, const IntPoint &newToolPos, const Paths &cleared_paths);
void AppendToolPath(AdaptiveOutput & output,const Path & passToolPath,const Paths & cleared, bool close=false);
bool CheckCollision(const IntPoint &lastPoint,const IntPoint &nextPoint,const Paths & cleared);
friend class EngagePoint; // for CalcCutArea
void CheckReportProgress(TPaths &progressPaths,bool force=false);
private: // constants for fine tuning
const bool preventConvetionalMode = true;
const double RESOLUTION_FACTOR = 8.0;
const int MAX_ITERATIONS = 16;
const double AREA_ERROR_FACTOR = 0.05; /* how precise to match the cut area to optimal, reasonable value: 0.05 = 5%*/
const size_t ANGLE_HISTORY_POINTS=3; // used for angle prediction
const int DIRECTION_SMOOTHING_BUFLEN=3; // gyro points - used for angle smoothing
const double ENGAGE_AREA_THR_FACTOR=0.2; // influences minimal engage area (factor relation to optimal)
const double ENGAGE_SCAN_DISTANCE_FACTOR=0.1; // influences the engage scan/stepping distance
const double CLEAN_PATH_TOLERANCE = 1;
const double FINISHING_CLEAN_PATH_TOLERANCE = 0.5;
// used for filtering out of insignificant cuts:
const double MIN_CUT_AREA_FACTOR = 0.02; // influences filtering of cuts that with cumulative area below threshold, reasonable value is between 0.01 and 0.1
const long PASSES_LIMIT = __LONG_MAX__; // limit used while debugging
const long POINTS_PER_PASS_LIMIT = __LONG_MAX__; // limit used while debugging
const time_t PROGRESS_TICKS = CLOCKS_PER_SEC/20; // progress report interval
};
}
#endif

View File

@@ -60,6 +60,7 @@ set(AREA_SRC_COMMON
set(AREA_SRC_CLIPPER
AreaClipper.cpp
Adaptive.cpp
clipper.cpp
)
@@ -99,6 +100,7 @@ add_library(
${PYAREA_SRC}
)
if(MSVC)
set(area_native_LIBS
debug MSVCRTD.LIB

View File

@@ -8,6 +8,7 @@
#include "Point.h"
#include "AreaDxf.h"
#include "kurve/geometry.h"
#include "Adaptive.hpp"
#if defined (_POSIX_C_SOURCE)
# undef _POSIX_C_SOURCE
@@ -38,6 +39,7 @@
#include <boost/python/wrapper.hpp>
#include <boost/python/call.hpp>
#include "clipper.hpp"
using namespace ClipperLib;
@@ -237,7 +239,6 @@ boost::python::list spanIntersect(const Span& span1, const Span& span2) {
}
//Matrix(boost::python::list &l){}
boost::shared_ptr<geoff_geometry::Matrix> matrix_constructor(const boost::python::list& lst) {
double m[16] = {1,0,0,0,0,1,0,0, 0,0,1,0, 0,0,0,1};
@@ -291,8 +292,70 @@ double AreaGetArea(const CArea& a)
return a.GetArea();
}
// Adaptive2d.Execute wrapper
bp::list AdaptiveExecute(AdaptivePath::Adaptive2d& ada,const boost::python::list &in_paths, boost::python::object progressCallbackFn) {
bp::list out_list;
// convert inputs
AdaptivePath::DPaths dpaths;
for(bp::ssize_t i=0;i<bp::len(in_paths);i++) {
bp::list in_path=bp::extract<boost::python::list>(in_paths[i]);
AdaptivePath::DPath dpath;
for(bp::ssize_t j=0;j<bp::len(in_path);j++) {
bp::list in_point = bp::extract<bp::list>(in_path[j]);
dpath.push_back(pair<double,double>(bp::extract<double>(in_point[0]),bp::extract<double>(in_point[1])));
}
dpaths.push_back(dpath);
}
// Execute with callback
std::list<AdaptivePath::AdaptiveOutput> result=ada.Execute(dpaths,[progressCallbackFn](AdaptivePath::TPaths tp)->bool {
bp::list out_paths;
for(const auto & in_pair : tp) {
bp::list path;
for(const auto & in_pt : in_pair.second) {
path.append(bp::make_tuple(in_pt.first,in_pt.second));
}
out_paths.append(bp::make_tuple(in_pair.first,path));
}
return bp::extract<bool>(progressCallbackFn(out_paths));
});
// convert outputs back
BOOST_FOREACH(const auto & res, result) {
out_list.append(res);
}
return out_list;
}
// Converts a std::pair instance to a Python tuple.
template <typename T1, typename T2>
struct std_pair_to_tuple
{
static PyObject* convert(std::pair<T1, T2> const& p)
{
return boost::python::incref(
boost::python::make_tuple(p.first, p.second).ptr());
}
static PyTypeObject const *get_pytype () {
return &PyTuple_Type;
}
};
boost::python::list AdaptiveOutput_AdaptivePaths(const AdaptivePath::AdaptiveOutput &ado) {
bp::list olist;
for(auto & ap : ado.AdaptivePaths) {
bp::list op;
for(auto & pt : ap.second) {
op.append(bp::make_tuple(pt.first, pt.second));
}
olist.append(bp::make_tuple(ap.first, op));
}
return olist;
}
BOOST_PYTHON_MODULE(area) {
bp::class_<Point>("Point")
bp::class_<Point>("Point")
.def(bp::init<double, double>())
.def(bp::init<Point>())
.def(bp::other<double>() * bp::self)
@@ -418,4 +481,43 @@ BOOST_PYTHON_MODULE(area) {
bp::def("holes_linked", holes_linked);
bp::def("AreaFromDxf", AreaFromDxf);
bp::def("TangentialArc", TangentialArc);
using namespace AdaptivePath;
boost::python::to_python_converter<std::pair<double, double>, std_pair_to_tuple<double, double>,true>();
bp::enum_<MotionType>("AdaptiveMotionType")
.value("Cutting", MotionType::mtCutting)
.value("LinkClear", MotionType::mtLinkClear)
.value("LinkNotClear", MotionType::mtLinkNotClear)
.value("LinkClearAtPrevPass", MotionType::mtLinkClearAtPrevPass);
bp::enum_<OperationType>("AdaptiveOperationType")
.value("Clearing", OperationType::otClearing)
.value("ProfilingInside", OperationType::otProfilingInside)
.value("ProfilingOutside", OperationType::otProfilingOutside);
bp::class_<AdaptiveOutput> ("AdaptiveOutput")
.def(bp::init<>())
.add_property("HelixCenterPoint", bp::make_getter(&AdaptiveOutput::HelixCenterPoint, bp::return_value_policy<bp::return_by_value>()))
.add_property("StartPoint", bp::make_getter(&AdaptiveOutput::StartPoint, bp::return_value_policy<bp::return_by_value>()))
.add_property("AdaptivePaths", &AdaptiveOutput_AdaptivePaths)
.def_readonly("ReturnMotionType",&AdaptiveOutput::ReturnMotionType);
bp::class_<Adaptive2d>("Adaptive2d")
.def(bp::init<>())
.def("Execute",&AdaptiveExecute)
.def_readwrite("stepOverFactor", &Adaptive2d::stepOverFactor)
.def_readwrite("toolDiameter", &Adaptive2d::toolDiameter)
.def_readwrite("helixRampDiameter", &Adaptive2d::helixRampDiameter)
.def_readwrite("polyTreeNestingLimit", &Adaptive2d::polyTreeNestingLimit)
.def_readwrite("tolerance", &Adaptive2d::tolerance)
.def_readwrite("opType", &Adaptive2d::opType);
}

View File

@@ -11,6 +11,7 @@
#include "Point.h"
#include "AreaDxf.h"
#include "kurve/geometry.h"
#include "Adaptive.hpp"
#include <pybind11/pybind11.h>
#include <pybind11/stl.h>
@@ -358,9 +359,46 @@ void init_pyarea(py::module &m){
m.def("holes_linked", holes_linked);
m.def("AreaFromDxf", AreaFromDxf);
m.def("TangentialArc", TangentialArc);
using namespace AdaptivePath;
py::enum_<MotionType>(m, "AdaptiveMotionType")
.value("Cutting", MotionType::mtCutting)
.value("LinkClear", MotionType::mtLinkClear)
.value("LinkNotClear", MotionType::mtLinkNotClear)
.value("LinkClearAtPrevPass", MotionType::mtLinkClearAtPrevPass);
py::enum_<OperationType>(m, "AdaptiveOperationType")
.value("Clearing", OperationType::otClearing)
.value("ProfilingInside", OperationType::otProfilingInside)
.value("ProfilingOutside", OperationType::otProfilingOutside);
py::class_<AdaptiveOutput>(m, "AdaptiveOutput")
.def(py::init<>())
.def_readwrite("HelixCenterPoint",&AdaptiveOutput::HelixCenterPoint)
.def_readwrite("StartPoint",&AdaptiveOutput::StartPoint)
.def_readwrite("AdaptivePaths",&AdaptiveOutput::AdaptivePaths)
.def_readwrite("ReturnMotionType",&AdaptiveOutput::ReturnMotionType);
py::class_<Adaptive2d>(m, "Adaptive2d")
.def(py::init<>())
.def("Execute",&Adaptive2d::Execute)
.def_readwrite("stepOverFactor", &Adaptive2d::stepOverFactor)
.def_readwrite("toolDiameter", &Adaptive2d::toolDiameter)
.def_readwrite("helixRampDiameter", &Adaptive2d::helixRampDiameter)
.def_readwrite("polyTreeNestingLimit", &Adaptive2d::polyTreeNestingLimit)
.def_readwrite("tolerance", &Adaptive2d::tolerance)
.def_readwrite("opType", &Adaptive2d::opType);
}
PYBIND11_MODULE(area, m){
m.doc()= "not yet";
init_pyarea(m);
};
using namespace AdaptivePath;
PYBIND11_MODULE(AdaptivePath, m){
}