Compare commits

..

41 Commits

Author SHA1 Message Date
DeepSOIC
50f4372b02 Resample: re-implement, + reference placement support
Now uses ValueSeriesGenerator for extra flexibility
2018-09-07 17:40:32 +03:00
DeepSOIC
af75a3a0f0 fixup 2018-09-07 16:38:21 +03:00
DeepSOIC
f874b338e7 fixup 2018-09-07 16:18:14 +03:00
DeepSOIC
e18a270636 ProjectArray: reference placement support 2018-09-07 16:05:14 +03:00
DeepSOIC
dfa4377013 PopulateCopies: change command logic
collapse into one command
2018-09-07 02:48:07 +03:00
DeepSOIC
0224b6f98a JoinArrays: improve reference placement support 2018-09-07 01:18:56 +03:00
DeepSOIC
f7bbd41b2c BaseFeature: add dereferencing to getPlacementsList 2018-09-07 01:18:02 +03:00
DeepSOIC
e4c342f5ba BaseFeature: fix reference marker sometimes show up on non-placement results 2018-09-07 00:24:08 +03:00
DeepSOIC
b8d3df590c BaseFeature: fix readonlyness 2018-09-07 00:17:43 +03:00
DeepSOIC
56c39bd493 ArrayFilter: fix reference placement support 2018-09-06 23:38:30 +03:00
DeepSOIC
870ef63088 ReferencePlacement: refactor CS 2018-09-06 23:38:00 +03:00
DeepSOIC
a192e19209 BaseFeature: improve attacher disabling 2018-09-06 01:33:56 +03:00
DeepSOIC
7d2d62bdea BaseFeature: add updateReadonlyness global method
+ make basic Lattice properties such as MarkerSize hidden on objects with shape results.
2018-09-06 01:33:56 +03:00
DeepSOIC
32e840a941 AttachableArray, LinearArray, PolarArray: add 'external' reference option 2018-09-06 01:33:56 +03:00
DeepSOIC
d8a3293dbc ReferencePlacement: fix viewprovider update when setting refplm to None 2018-09-06 01:33:56 +03:00
DeepSOIC
64879965ba ReferencePlacement: add reference placement link 2018-09-06 01:33:56 +03:00
DeepSOIC
3a2662685e AttachedPlacement: subsequence-array reference placement support 2018-09-06 01:33:56 +03:00
DeepSOIC
22925c8187 BaseFeature: add assureProperties method 2018-09-06 01:33:56 +03:00
DeepSOIC
c3bb345c3f AttachedPlacement: subsequence-array an attached array support
now, one can "array an attached placement" a polar array.
2018-09-06 01:33:55 +03:00
DeepSOIC
7331c60ce5 ReferencePlacement: all arrays have reference placement 2018-09-06 01:33:55 +03:00
DeepSOIC
340c51b108 ReferencePlacement: workaround for FC bug 3564
https://freecadweb.org/tracker/view.php?id=3564
2018-09-06 01:33:55 +03:00
DeepSOIC
c566979574 fix refplm visibility after restore 2018-09-06 01:33:55 +03:00
DeepSOIC
a80c075b4a JoinArrays: reference placement support 2018-09-06 01:33:55 +03:00
DeepSOIC
8cb8ab7bd9 ReferencePlacement: fix expose placement 2018-09-06 01:33:55 +03:00
DeepSOIC
1e4416dd9f ArrayFilter: reference placement support 2018-09-06 01:33:55 +03:00
DeepSOIC
fb031132e3 ArrayFilter: fix 2018-09-06 01:33:55 +03:00
DeepSOIC
5a72dedd4c Invert: clean up imports 2018-09-06 01:33:55 +03:00
DeepSOIC
8f15765047 PolarArray: 'center' reference option 2018-09-06 01:33:55 +03:00
DeepSOIC
65c6dad39f Invert: support for reference placement 2018-09-06 01:33:55 +03:00
DeepSOIC
9d2043f546 ReferencePlacement: add missing tetra-marker 2018-09-06 01:33:55 +03:00
DeepSOIC
7d0d5d63a3 PopulateChildren: reference inheritance 2018-09-06 01:33:55 +03:00
DeepSOIC
618bbc331f LinearArray: fix 2018-09-06 01:33:55 +03:00
DeepSOIC
bd3ac15fd2 fixup for icons 2018-09-06 01:33:55 +03:00
DeepSOIC
681c7330ba LinearArray: reference placement support 2018-09-06 01:33:55 +03:00
DeepSOIC
c97e733c6a Populate tools: icons 2018-09-06 01:33:55 +03:00
DeepSOIC
eecd808a22 ReferencePlacement: tweak
increase refplm marker a bit, to make it more visible
2018-09-06 01:33:55 +03:00
DeepSOIC
62c6b6d7cc PopulateCopies: Reference placement propagation 2018-09-06 01:33:55 +03:00
DeepSOIC
f4b88d1a2e ReferencePlacement: fixes 2018-09-06 01:33:55 +03:00
DeepSOIC
6c620b130f ReferencePlacement: show on screen
initial implementation. to be improved...
2018-09-06 01:33:55 +03:00
DeepSOIC
9449dd9352 fixup
fix basefeature source function
2018-09-06 01:33:55 +03:00
DeepSOIC
c7dd44868e ReferencePlacement: the very basics
* ref plm support in polar array
* referencing options for populate features
2018-09-06 01:33:55 +03:00
51 changed files with 3242 additions and 1972 deletions

View File

@ -63,7 +63,7 @@ class Lattice2Workbench (Workbench):
+ Lattice2.ArrayFeatures.JoinArrays.exportedCommands
+ Lattice2.ArrayFeatures.ArrayFilter.exportedCommands
+ Lattice2.ArrayFeatures.ProjectArray.exportedCommands
+ Lattice2.ArrayFeatures.InterpolateGroup.exportedCommands
+ Lattice2.ArrayFeatures.Resample2.exportedCommands
+ Lattice2.ArrayFeatures.PopulateCopies.exportedCommands
+ Lattice2.ArrayFeatures.PopulateChildren.exportedCommands
+ Lattice2.ArrayFeatures.Mirror.exportedCommands
@ -94,7 +94,6 @@ class Lattice2Workbench (Workbench):
cmdsGuiTools = ([]
+ Lattice2.GuiTools.Inspect.exportedCommands
+ Lattice2.GuiTools.SubstituteObject.exportedCommands
+ Lattice2.GuiTools.ViewFromPlacement.exportedCommands
)
self.appendToolbar('Lattice2GuiTools', cmdsGuiTools)
self.appendMenu('Lattice2', cmdsGuiTools + Lattice2.GuiTools.ExposeLinkSub.exportedCommands)

View File

@ -2,7 +2,6 @@ import lattice2ArrayFilter as ArrayFilter
import lattice2ArrayFromShape as ArrayFromShape
import lattice2AttachablePlacement as AttachablePlacement
import lattice2BaseFeature as BaseFeature
import lattice2InterpolateGroup as InterpolateGroup
import lattice2Invert as Invert
import lattice2JoinArrays as JoinArrays
import lattice2LinearArray as LinearArray
@ -13,5 +12,5 @@ import lattice2PopulateChildren as PopulateChildren
import lattice2PopulateCopies as PopulateCopies
import lattice2ProjectArray as ProjectArray
import lattice2Resample as Resample
import lattice2ScLERP as ScLERP
import lattice2Resample2 as Resample2
import lattice2Mirror as Mirror

View File

@ -8,4 +8,5 @@ import lattice2Markers as Markers
import lattice2ValueSeriesGenerator as ValueSeriesGenerator
import lattice2ShapeCopy as ShapeCopy
import lattice2Subsequencer as Subsequencer
import lattice2Utils as Utils
import lattice2Utils as Utils
import lattice2CoinGlue as CoinGlue

View File

@ -1,5 +1,4 @@
import lattice2Inspect as Inspect
import lattice2RecomputeLocker as RecomputeLocker
import lattice2SubstituteObject as SubstituteObject
import lattice2ExposeLinkSub as ExposeLinkSub
import lattice2ViewFromPlacement as ViewFromPlacement
import lattice2Inspect as Inspect
import lattice2RecomputeLocker as RecomputeLocker
import lattice2SubstituteObject as SubstituteObject
import lattice2ExposeLinkSub as ExposeLinkSub

View File

@ -34,15 +34,19 @@
<file>icons/Lattice2_PopulateChildren_Array.svg</file>
<file>icons/Lattice2_PopulateChildren_Move.svg</file>
<file>icons/Lattice2_PopulateChildren_Normal.svg</file>
<file>icons/Lattice2_PopulateChildren_Ref.svg</file>
<file>icons/Lattice2_PopulateChildren_Plms_Array.svg</file>
<file>icons/Lattice2_PopulateChildren_Plms_Normal.svg</file>
<file>icons/Lattice2_PopulateChildren_Plms_Move.svg</file>
<file>icons/Lattice2_PopulateChildren_Plms_Ref.svg</file>
<file>icons/Lattice2_PopulateCopies_Array.svg</file>
<file>icons/Lattice2_PopulateCopies_Move.svg</file>
<file>icons/Lattice2_PopulateCopies_Normal.svg</file>
<file>icons/Lattice2_PopulateCopies_Ref.svg</file>
<file>icons/Lattice2_PopulateCopies_Plms_Array.svg</file>
<file>icons/Lattice2_PopulateCopies_Plms_Normal.svg</file>
<file>icons/Lattice2_PopulateCopies_Plms_Move.svg</file>
<file>icons/Lattice2_PopulateCopies_Plms_Ref.svg</file>
<file>icons/Lattice2_ProjectArray.svg</file>
<file>icons/Lattice2_RecomputeLocker_ForceRecompute.svg</file>
<file>icons/Lattice2_RecomputeLocker_Locked.svg</file>
@ -67,6 +71,5 @@
<file>icons/Lattice2_SubLinkSubsequence_Vertex.svg</file>
<file>icons/Lattice2_SubstituteObject.svg</file>
<file>icons/Lattice2_TopoSeries.svg</file>
<file>icons/Lattice2_ViewFromPlacement.svg</file>
</qresource>
</RCC>

View File

@ -0,0 +1,448 @@
<?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="svg2568"
sodipodi:version="0.32"
inkscape:version="0.48.4 r9939"
sodipodi:docname="Lattice2_PopulateChildren_Plms_Move.svg"
inkscape:output_extension="org.inkscape.output.svg.inkscape"
version="1.1"
inkscape:export-filename="/home/user/Downloads/cad/mystuff/icons/Part/mystuff/Part_Compound_5_32px.png"
inkscape:export-xdpi="45"
inkscape:export-ydpi="45">
<defs
id="defs2570">
<linearGradient
id="linearGradient3864">
<stop
id="stop3866"
offset="0"
style="stop-color:#71b2f8;stop-opacity:1;" />
<stop
id="stop3868"
offset="1"
style="stop-color:#002795;stop-opacity:1;" />
</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="perspective2576" />
<linearGradient
id="linearGradient3377">
<stop
id="stop3379"
offset="0"
style="stop-color:#71b2f8;stop-opacity:1;" />
<stop
id="stop3381"
offset="1"
style="stop-color:#002795;stop-opacity:1;" />
</linearGradient>
<linearGradient
id="linearGradient3023">
<stop
id="stop3025"
offset="0"
style="stop-color:#71b2f8;stop-opacity:1;" />
<stop
id="stop3027"
offset="1"
style="stop-color:#002795;stop-opacity:1;" />
</linearGradient>
<radialGradient
inkscape:collect="always"
xlink:href="#linearGradient3377"
id="radialGradient3692"
cx="45.883327"
cy="28.869568"
fx="45.883327"
fy="28.869568"
r="19.467436"
gradientUnits="userSpaceOnUse" />
<linearGradient
id="linearGradient3030">
<stop
id="stop3032"
offset="0"
style="stop-color:#71b2f8;stop-opacity:1;" />
<stop
id="stop3034"
offset="1"
style="stop-color:#002795;stop-opacity:1;" />
</linearGradient>
<radialGradient
inkscape:collect="always"
xlink:href="#linearGradient3377"
id="radialGradient3837"
gradientUnits="userSpaceOnUse"
gradientTransform="matrix(0.98773287,-0.06324662,0.02642229,1.230404,-216.68819,-80.013158)"
cx="148.88333"
cy="81.869568"
fx="148.88333"
fy="81.869568"
r="19.467436" />
<radialGradient
inkscape:collect="always"
xlink:href="#linearGradient3377"
id="radialGradient3839"
gradientUnits="userSpaceOnUse"
gradientTransform="matrix(0.69474204,0.27707782,-0.32964185,2.4645588,-139.05338,-247.09727)"
cx="135.38333"
cy="97.369568"
fx="135.38333"
fy="97.369568"
r="19.467436" />
<radialGradient
inkscape:collect="always"
xlink:href="#linearGradient3377"
id="radialGradient3841"
gradientUnits="userSpaceOnUse"
gradientTransform="matrix(0.71303129,0,0,1.2312496,-173.62652,-89.498759)"
cx="45.883327"
cy="28.869568"
fx="45.883327"
fy="28.869568"
r="19.467436" />
<radialGradient
inkscape:collect="always"
xlink:href="#linearGradient3377"
id="radialGradient3804"
gradientUnits="userSpaceOnUse"
gradientTransform="matrix(0.98773287,-0.06324662,0.02642229,1.230404,-216.68819,-80.013158)"
cx="148.88333"
cy="81.869568"
fx="148.88333"
fy="81.869568"
r="19.467436" />
<radialGradient
inkscape:collect="always"
xlink:href="#linearGradient3377"
id="radialGradient3806"
gradientUnits="userSpaceOnUse"
gradientTransform="matrix(0.69474204,0.27707782,-0.32964185,2.4645588,-139.05338,-247.09727)"
cx="135.38333"
cy="97.369568"
fx="135.38333"
fy="97.369568"
r="19.467436" />
<radialGradient
inkscape:collect="always"
xlink:href="#linearGradient3377"
id="radialGradient3808"
gradientUnits="userSpaceOnUse"
gradientTransform="matrix(0.71303129,0,0,1.2312496,-173.62652,-89.498759)"
cx="45.883327"
cy="28.869568"
fx="45.883327"
fy="28.869568"
r="19.467436" />
<radialGradient
inkscape:collect="always"
xlink:href="#linearGradient3377-5"
id="radialGradient3804-3"
gradientUnits="userSpaceOnUse"
gradientTransform="matrix(0.98773287,-0.06324662,0.02642229,1.230404,-216.68819,-80.013158)"
cx="148.88333"
cy="81.869568"
fx="148.88333"
fy="81.869568"
r="19.467436" />
<linearGradient
id="linearGradient3377-5">
<stop
id="stop3379-4"
offset="0"
style="stop-color:#71b2f8;stop-opacity:1;" />
<stop
id="stop3381-8"
offset="1"
style="stop-color:#002795;stop-opacity:1;" />
</linearGradient>
<radialGradient
inkscape:collect="always"
xlink:href="#linearGradient3377-5"
id="radialGradient3806-9"
gradientUnits="userSpaceOnUse"
gradientTransform="matrix(0.69474204,0.27707782,-0.32964185,2.4645588,-139.05338,-247.09727)"
cx="135.38333"
cy="97.369568"
fx="135.38333"
fy="97.369568"
r="19.467436" />
<linearGradient
id="linearGradient3080">
<stop
id="stop3082"
offset="0"
style="stop-color:#71b2f8;stop-opacity:1;" />
<stop
id="stop3084"
offset="1"
style="stop-color:#002795;stop-opacity:1;" />
</linearGradient>
<radialGradient
inkscape:collect="always"
xlink:href="#linearGradient3377-5"
id="radialGradient3808-3"
gradientUnits="userSpaceOnUse"
gradientTransform="matrix(0.71303129,0,0,1.2312496,-173.62652,-89.498759)"
cx="45.883327"
cy="28.869568"
fx="45.883327"
fy="28.869568"
r="19.467436" />
<linearGradient
id="linearGradient3087">
<stop
id="stop3089"
offset="0"
style="stop-color:#71b2f8;stop-opacity:1;" />
<stop
id="stop3091"
offset="1"
style="stop-color:#002795;stop-opacity:1;" />
</linearGradient>
<radialGradient
inkscape:collect="always"
xlink:href="#linearGradient3377-5"
id="radialGradient4366"
gradientUnits="userSpaceOnUse"
gradientTransform="matrix(0.98773287,-0.06324662,0.02642229,1.230404,-216.68819,-80.013158)"
cx="148.88333"
cy="81.869568"
fx="148.88333"
fy="81.869568"
r="19.467436" />
<radialGradient
inkscape:collect="always"
xlink:href="#linearGradient3377-5"
id="radialGradient4368"
gradientUnits="userSpaceOnUse"
gradientTransform="matrix(0.69474204,0.27707782,-0.32964185,2.4645588,-139.05338,-247.09727)"
cx="135.38333"
cy="97.369568"
fx="135.38333"
fy="97.369568"
r="19.467436" />
<radialGradient
inkscape:collect="always"
xlink:href="#linearGradient3377-5"
id="radialGradient4370"
gradientUnits="userSpaceOnUse"
gradientTransform="matrix(0.71303129,0,0,1.2312496,-173.62652,-89.498759)"
cx="45.883327"
cy="28.869568"
fx="45.883327"
fy="28.869568"
r="19.467436" />
</defs>
<sodipodi:namedview
id="base"
pagecolor="#ffffff"
bordercolor="#666666"
borderopacity="1.0"
inkscape:pageopacity="0.0"
inkscape:pageshadow="2"
inkscape:zoom="8.4172297"
inkscape:cx="41.893894"
inkscape:cy="31.769057"
inkscape:current-layer="layer1"
showgrid="true"
inkscape:document-units="px"
inkscape:grid-bbox="true"
inkscape:window-width="1920"
inkscape:window-height="1001"
inkscape:window-x="-9"
inkscape:window-y="-9"
inkscape:window-maximized="1"
inkscape:object-nodes="true"
inkscape:snap-global="false"
showguides="true"
inkscape:guide-bbox="true" />
<metadata
id="metadata2573">
<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>
</cc:Work>
</rdf:RDF>
</metadata>
<g
id="layer1"
inkscape:label="Layer 1"
inkscape:groupmode="layer">
<g
id="g3794"
transform="matrix(0.66209119,0,0,0.66209119,16.510051,9.6145811)">
<path
inkscape:connector-curvature="0"
id="path3018"
d="m -18.909091,25.818182 4.272727,17 30.454546,-11.545455 z"
style="fill:none;stroke:#000000;stroke-width:1.51036596px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1" />
<path
inkscape:connector-curvature="0"
id="path3020"
d="M -14.636364,42.818182 15.818182,31.272727 -15.090909,28.545455 z"
style="fill:#e9a60a;fill-opacity:1;stroke:#000000;stroke-width:1.51036596px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1" />
<path
inkscape:connector-curvature="0"
id="path3022"
d="m -18.909091,25.818182 4.272727,17 -0.454545,-14.272727 z"
style="fill:#4c390a;fill-opacity:1;stroke:#000000;stroke-width:1.51036596px;stroke-linecap:butt;stroke-linejoin:round;stroke-opacity:1" />
<path
inkscape:connector-curvature="0"
id="path3024"
d="m -15.090909,28.545455 30.909091,2.727272 -34.727273,-5.454545 z"
style="fill:#2f240a;fill-opacity:1;stroke:#000000;stroke-width:1.51036596px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1" />
</g>
<g
id="g3049"
style="stroke-width:2;stroke-miterlimit:4;stroke-dasharray:none"
transform="matrix(0.77833186,0,0,0.71925582,-2.7462371,5.126137)">
<path
sodipodi:nodetypes="cccc"
inkscape:connector-curvature="0"
id="path3045"
d="m 10.351377,24.493827 -5.6980057,0 0,25.925926 5.6980057,0"
style="fill:none;stroke:#000000;stroke-width:2.67304277;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:4;stroke-opacity:1;stroke-dasharray:none" />
<use
height="64"
width="64"
transform="matrix(-1,0,0,1,47.081698,0)"
id="use3047"
xlink:href="#path3045"
y="0"
x="0"
style="stroke-width:2;stroke-miterlimit:4;stroke-dasharray:none" />
</g>
<path
sodipodi:type="arc"
style="fill:#ffffff;fill-opacity:1;stroke:#000000;stroke-width:2;stroke-linecap:round;stroke-linejoin:round;stroke-miterlimit:4;stroke-opacity:1;stroke-dasharray:none;stroke-dashoffset:0"
id="path3823"
sodipodi:cx="53.466286"
sodipodi:cy="26.488129"
sodipodi:rx="7.5973411"
sodipodi:ry="7.5973411"
d="m 61.063627,26.488129 a 7.5973411,7.5973411 0 1 1 -15.194682,0 7.5973411,7.5973411 0 1 1 15.194682,0 z"
transform="translate(-1.0080847,-16.969427)" />
<path
style="fill:none;stroke:#000000;stroke-width:1.49999988;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:4;stroke-opacity:1;stroke-dasharray:none"
d="m 33.365217,54.049894 18.817583,0 0,-36.737441"
id="path3866"
inkscape:connector-curvature="0" />
<path
style="fill:none;stroke:#000000;stroke-width:1.50000012;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:4;stroke-opacity:1;stroke-dasharray:none"
d="m 32.813497,9.2273923 12.494629,0"
id="path3868"
inkscape:connector-curvature="0" />
<path
style="fill:none;stroke:#000000;stroke-width:1.5;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:4;stroke-opacity:1;stroke-dasharray:none"
d="m 60.821116,9.5634205 7.392622,0"
id="path3868-3"
inkscape:connector-curvature="0" />
<path
sodipodi:type="arc"
style="fill:#000000;fill-opacity:1;stroke:none"
id="path3888"
sodipodi:cx="51.748352"
sodipodi:cy="14.099802"
sodipodi:rx="2.3521979"
sodipodi:ry="2.3521979"
d="m 54.10055,14.099802 a 2.3521979,2.3521979 0 1 1 -4.704396,0 2.3521979,2.3521979 0 1 1 4.704396,0 z"
transform="translate(0.84007066,-4.5363816)" />
<path
transform="translate(-0.88928087,5.3103559)"
d="m 61.063627,26.488129 a 7.5973411,7.5973411 0 1 1 -15.194682,0 7.5973411,7.5973411 0 1 1 15.194682,0 z"
sodipodi:ry="7.5973411"
sodipodi:rx="7.5973411"
sodipodi:cy="26.488129"
sodipodi:cx="53.466286"
id="path4352"
style="fill:#ffffff;fill-opacity:1;stroke:#000000;stroke-width:2;stroke-linecap:round;stroke-linejoin:round;stroke-miterlimit:4;stroke-opacity:1;stroke-dasharray:none;stroke-dashoffset:0"
sodipodi:type="arc" />
<path
style="fill:none;stroke:#000000;stroke-width:1.5;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:4;stroke-opacity:1;stroke-dasharray:none"
d="m 48.072426,31.793994 9.072763,0"
id="path4358"
inkscape:connector-curvature="0" />
<path
style="fill:none;stroke:#000000;stroke-width:1.5;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:4;stroke-opacity:1;stroke-dasharray:none"
d="m 32.730167,31.744784 11.756572,0"
id="path4376"
inkscape:connector-curvature="0" />
<g
id="g3794-9"
transform="matrix(0.66209119,0,0,0.66209119,16.746967,-12.543942)">
<path
inkscape:connector-curvature="0"
id="path3018-4"
d="m -18.909091,25.818182 4.272727,17 30.454546,-11.545455 z"
style="fill:none;stroke:#000000;stroke-width:1.51036596px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1" />
<path
inkscape:connector-curvature="0"
id="path3020-5"
d="M -14.636364,42.818182 15.818182,31.272727 -15.090909,28.545455 z"
style="fill:#e9a60a;fill-opacity:1;stroke:#000000;stroke-width:1.51036596px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1" />
<path
inkscape:connector-curvature="0"
id="path3022-5"
d="m -18.909091,25.818182 4.272727,17 -0.454545,-14.272727 z"
style="fill:#4c390a;fill-opacity:1;stroke:#000000;stroke-width:1.51036596px;stroke-linecap:butt;stroke-linejoin:round;stroke-opacity:1" />
<path
inkscape:connector-curvature="0"
id="path3024-4"
d="m -15.090909,28.545455 30.909091,2.727272 -34.727273,-5.454545 z"
style="fill:#2f240a;fill-opacity:1;stroke:#000000;stroke-width:1.51036596px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1" />
</g>
<use
x="0"
y="0"
xlink:href="#g3049"
id="use3040"
width="64"
height="64"
transform="translate(0.11880393,-21.6427)" />
<g
transform="matrix(0.66209119,0,0,0.66209119,16.586547,31.892723)"
id="use4372">
<path
style="fill:none;stroke:#007ce0;stroke-width:1.51036596px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1"
d="m -18.909091,25.818182 4.272727,17 30.454546,-11.545455 z"
id="path3042"
inkscape:connector-curvature="0" />
<path
style="fill:#1798ff;fill-opacity:0.50210972;stroke:#007ce0;stroke-width:1.51036596px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1"
d="M -14.636364,42.818182 15.818182,31.272727 -15.090909,28.545455 z"
id="path3044"
inkscape:connector-curvature="0" />
<path
style="fill:#006eca;fill-opacity:0.50196078;stroke:#1798ff;stroke-width:1.51036596px;stroke-linecap:butt;stroke-linejoin:round;stroke-opacity:0.50196078"
d="m -18.909091,25.818182 4.272727,17 -0.454545,-14.272727 z"
id="path3046"
inkscape:connector-curvature="0" />
<path
style="fill:#0065b7;fill-opacity:0.63291141;stroke:#007ce0;stroke-width:1.51036596px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1"
d="m -15.090909,28.545455 30.909091,2.727272 -34.727273,-5.454545 z"
id="path3048"
inkscape:connector-curvature="0" />
</g>
</g>
</svg>

After

Width:  |  Height:  |  Size: 16 KiB

View File

@ -0,0 +1,461 @@
<?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="svg2568"
sodipodi:version="0.32"
inkscape:version="0.48.4 r9939"
sodipodi:docname="Lattice2_PopulateChildren_Move.svg"
inkscape:output_extension="org.inkscape.output.svg.inkscape"
version="1.1"
inkscape:export-filename="/home/user/Downloads/cad/mystuff/icons/Part/mystuff/Part_Compound_5_32px.png"
inkscape:export-xdpi="45"
inkscape:export-ydpi="45">
<defs
id="defs2570">
<linearGradient
id="linearGradient3864">
<stop
id="stop3866"
offset="0"
style="stop-color:#71b2f8;stop-opacity:1;" />
<stop
id="stop3868"
offset="1"
style="stop-color:#002795;stop-opacity:1;" />
</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="perspective2576" />
<linearGradient
id="linearGradient3377">
<stop
id="stop3379"
offset="0"
style="stop-color:#71b2f8;stop-opacity:1;" />
<stop
id="stop3381"
offset="1"
style="stop-color:#002795;stop-opacity:1;" />
</linearGradient>
<linearGradient
id="linearGradient3023">
<stop
id="stop3025"
offset="0"
style="stop-color:#71b2f8;stop-opacity:1;" />
<stop
id="stop3027"
offset="1"
style="stop-color:#002795;stop-opacity:1;" />
</linearGradient>
<radialGradient
inkscape:collect="always"
xlink:href="#linearGradient3377"
id="radialGradient3692"
cx="45.883327"
cy="28.869568"
fx="45.883327"
fy="28.869568"
r="19.467436"
gradientUnits="userSpaceOnUse" />
<linearGradient
id="linearGradient3030">
<stop
id="stop3032"
offset="0"
style="stop-color:#71b2f8;stop-opacity:1;" />
<stop
id="stop3034"
offset="1"
style="stop-color:#002795;stop-opacity:1;" />
</linearGradient>
<radialGradient
inkscape:collect="always"
xlink:href="#linearGradient3377"
id="radialGradient3837"
gradientUnits="userSpaceOnUse"
gradientTransform="matrix(0.98773287,-0.06324662,0.02642229,1.230404,-216.68819,-80.013158)"
cx="148.88333"
cy="81.869568"
fx="148.88333"
fy="81.869568"
r="19.467436" />
<radialGradient
inkscape:collect="always"
xlink:href="#linearGradient3377"
id="radialGradient3839"
gradientUnits="userSpaceOnUse"
gradientTransform="matrix(0.69474204,0.27707782,-0.32964185,2.4645588,-139.05338,-247.09727)"
cx="135.38333"
cy="97.369568"
fx="135.38333"
fy="97.369568"
r="19.467436" />
<radialGradient
inkscape:collect="always"
xlink:href="#linearGradient3377"
id="radialGradient3841"
gradientUnits="userSpaceOnUse"
gradientTransform="matrix(0.71303129,0,0,1.2312496,-173.62652,-89.498759)"
cx="45.883327"
cy="28.869568"
fx="45.883327"
fy="28.869568"
r="19.467436" />
<radialGradient
inkscape:collect="always"
xlink:href="#linearGradient3377"
id="radialGradient3804"
gradientUnits="userSpaceOnUse"
gradientTransform="matrix(0.98773287,-0.06324662,0.02642229,1.230404,-216.68819,-80.013158)"
cx="148.88333"
cy="81.869568"
fx="148.88333"
fy="81.869568"
r="19.467436" />
<radialGradient
inkscape:collect="always"
xlink:href="#linearGradient3377"
id="radialGradient3806"
gradientUnits="userSpaceOnUse"
gradientTransform="matrix(0.69474204,0.27707782,-0.32964185,2.4645588,-139.05338,-247.09727)"
cx="135.38333"
cy="97.369568"
fx="135.38333"
fy="97.369568"
r="19.467436" />
<radialGradient
inkscape:collect="always"
xlink:href="#linearGradient3377"
id="radialGradient3808"
gradientUnits="userSpaceOnUse"
gradientTransform="matrix(0.71303129,0,0,1.2312496,-173.62652,-89.498759)"
cx="45.883327"
cy="28.869568"
fx="45.883327"
fy="28.869568"
r="19.467436" />
<radialGradient
inkscape:collect="always"
xlink:href="#linearGradient3377-5"
id="radialGradient3804-3"
gradientUnits="userSpaceOnUse"
gradientTransform="matrix(0.98773287,-0.06324662,0.02642229,1.230404,-216.68819,-80.013158)"
cx="148.88333"
cy="81.869568"
fx="148.88333"
fy="81.869568"
r="19.467436" />
<linearGradient
id="linearGradient3377-5">
<stop
id="stop3379-4"
offset="0"
style="stop-color:#71b2f8;stop-opacity:1;" />
<stop
id="stop3381-8"
offset="1"
style="stop-color:#002795;stop-opacity:1;" />
</linearGradient>
<radialGradient
inkscape:collect="always"
xlink:href="#linearGradient3377-5"
id="radialGradient3806-9"
gradientUnits="userSpaceOnUse"
gradientTransform="matrix(0.69474204,0.27707782,-0.32964185,2.4645588,-139.05338,-247.09727)"
cx="135.38333"
cy="97.369568"
fx="135.38333"
fy="97.369568"
r="19.467436" />
<linearGradient
id="linearGradient3080">
<stop
id="stop3082"
offset="0"
style="stop-color:#71b2f8;stop-opacity:1;" />
<stop
id="stop3084"
offset="1"
style="stop-color:#002795;stop-opacity:1;" />
</linearGradient>
<radialGradient
inkscape:collect="always"
xlink:href="#linearGradient3377-5"
id="radialGradient3808-3"
gradientUnits="userSpaceOnUse"
gradientTransform="matrix(0.71303129,0,0,1.2312496,-173.62652,-89.498759)"
cx="45.883327"
cy="28.869568"
fx="45.883327"
fy="28.869568"
r="19.467436" />
<linearGradient
id="linearGradient3087">
<stop
id="stop3089"
offset="0"
style="stop-color:#71b2f8;stop-opacity:1;" />
<stop
id="stop3091"
offset="1"
style="stop-color:#002795;stop-opacity:1;" />
</linearGradient>
<radialGradient
inkscape:collect="always"
xlink:href="#linearGradient3377-5"
id="radialGradient4366"
gradientUnits="userSpaceOnUse"
gradientTransform="matrix(0.98773287,-0.06324662,0.02642229,1.230404,-216.68819,-80.013158)"
cx="148.88333"
cy="81.869568"
fx="148.88333"
fy="81.869568"
r="19.467436" />
<radialGradient
inkscape:collect="always"
xlink:href="#linearGradient3377-5"
id="radialGradient4368"
gradientUnits="userSpaceOnUse"
gradientTransform="matrix(0.69474204,0.27707782,-0.32964185,2.4645588,-139.05338,-247.09727)"
cx="135.38333"
cy="97.369568"
fx="135.38333"
fy="97.369568"
r="19.467436" />
<radialGradient
inkscape:collect="always"
xlink:href="#linearGradient3377-5"
id="radialGradient4370"
gradientUnits="userSpaceOnUse"
gradientTransform="matrix(0.71303129,0,0,1.2312496,-173.62652,-89.498759)"
cx="45.883327"
cy="28.869568"
fx="45.883327"
fy="28.869568"
r="19.467436" />
</defs>
<sodipodi:namedview
id="base"
pagecolor="#ffffff"
bordercolor="#666666"
borderopacity="1.0"
inkscape:pageopacity="0.0"
inkscape:pageshadow="2"
inkscape:zoom="5.9518802"
inkscape:cx="39.412699"
inkscape:cy="28.156141"
inkscape:current-layer="layer1"
showgrid="true"
inkscape:document-units="px"
inkscape:grid-bbox="true"
inkscape:window-width="1920"
inkscape:window-height="1001"
inkscape:window-x="-9"
inkscape:window-y="-9"
inkscape:window-maximized="1"
inkscape:object-nodes="true"
inkscape:snap-global="false"
showguides="true"
inkscape:guide-bbox="true" />
<metadata
id="metadata2573">
<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>
</cc:Work>
</rdf:RDF>
</metadata>
<g
id="layer1"
inkscape:label="Layer 1"
inkscape:groupmode="layer">
<g
id="g3794"
transform="matrix(0.66209119,0,0,0.66209119,16.9445,9.1766352)">
<path
inkscape:connector-curvature="0"
id="path3018"
d="m -18.909091,25.818182 4.272727,17 30.454546,-11.545455 z"
style="fill:none;stroke:#000000;stroke-width:1.51036596px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1" />
<path
inkscape:connector-curvature="0"
id="path3020"
d="M -14.636364,42.818182 15.818182,31.272727 -15.090909,28.545455 z"
style="fill:#e9a60a;fill-opacity:1;stroke:#000000;stroke-width:1.51036596px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1" />
<path
inkscape:connector-curvature="0"
id="path3022"
d="m -18.909091,25.818182 4.272727,17 -0.454545,-14.272727 z"
style="fill:#4c390a;fill-opacity:1;stroke:#000000;stroke-width:1.51036596px;stroke-linecap:butt;stroke-linejoin:round;stroke-opacity:1" />
<path
inkscape:connector-curvature="0"
id="path3024"
d="m -15.090909,28.545455 30.909091,2.727272 -34.727273,-5.454545 z"
style="fill:#2f240a;fill-opacity:1;stroke:#000000;stroke-width:1.51036596px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1" />
</g>
<g
id="g3049"
style="stroke-width:2;stroke-miterlimit:4;stroke-dasharray:none"
transform="matrix(0.77833186,0,0,0.61958115,-2.2218112,8.2680831)">
<path
sodipodi:nodetypes="cccc"
inkscape:connector-curvature="0"
id="path3045"
d="m 10.351377,24.493827 -5.6980057,0 0,25.925926 5.6980057,0"
style="fill:none;stroke:#000000;stroke-width:2.88003969;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:4;stroke-opacity:1;stroke-dasharray:none" />
<use
height="64"
width="64"
transform="matrix(-1,0,0,1,47.081698,0)"
id="use3047"
xlink:href="#path3045"
y="0"
x="0"
style="stroke-width:2;stroke-miterlimit:4;stroke-dasharray:none" />
</g>
<path
sodipodi:type="arc"
style="fill:#ffffff;fill-opacity:1;stroke:#000000;stroke-width:2;stroke-linecap:round;stroke-linejoin:round;stroke-miterlimit:4;stroke-opacity:1;stroke-dasharray:none;stroke-dashoffset:0"
id="path3823"
sodipodi:cx="53.466286"
sodipodi:cy="26.488129"
sodipodi:rx="7.5973411"
sodipodi:ry="7.5973411"
d="m 61.063627,26.488129 a 7.5973411,7.5973411 0 1 1 -15.194682,0 7.5973411,7.5973411 0 1 1 15.194682,0 z"
transform="translate(-1.0080847,-16.969427)" />
<g
id="g3798"
transform="matrix(0.94897349,0,0,0.94897349,-24.695619,-10.087879)">
<g
id="g3843"
transform="matrix(0.57659255,0,0,0.32499812,79.277735,9.6499853)">
<path
style="fill:url(#radialGradient4366);fill-opacity:1;fill-rule:evenodd;stroke:#000137;stroke-width:2.17218089;stroke-linecap:butt;stroke-linejoin:round;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 -65.878042,4.7970936 -12.866966,10.1036774 21.744289,5.108296 0.30338,44.045609 11.486763,-13.03934 0.410614,-42.3363302 z"
id="path3845"
sodipodi:nodetypes="ccccccc"
inkscape:connector-curvature="0" />
<path
style="fill:url(#radialGradient4368);fill-opacity:1;fill-rule:evenodd;stroke:#000137;stroke-width:2.17218089;stroke-linecap:butt;stroke-linejoin:round;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 -78.552461,15.155385 21.896364,4.143912 0,44.912523 -22.192265,-6.236008 0.295901,-42.820427 z"
id="path3847"
sodipodi:nodetypes="ccccc"
inkscape:connector-curvature="0" />
<path
style="fill:url(#radialGradient4370);fill-opacity:1;fill-rule:evenodd;stroke:#000137;stroke-width:2.17218089;stroke-linecap:butt;stroke-linejoin:round;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 -56.822875,19.354658 -44.896728,9.001326"
id="path3849"
inkscape:connector-curvature="0" />
</g>
</g>
<path
style="fill:none;stroke:#000000;stroke-width:1.49999988;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:4;stroke-opacity:1;stroke-dasharray:none"
d="m 33.602825,52.743051 18.817583,0 0,-35.787009"
id="path3866"
inkscape:connector-curvature="0" />
<path
style="fill:none;stroke:#000000;stroke-width:1.50000012;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:4;stroke-opacity:1;stroke-dasharray:none"
d="m 34.261979,9.2273923 11.114739,0"
id="path3868"
inkscape:connector-curvature="0" />
<path
style="fill:none;stroke:#000000;stroke-width:1.5;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:4;stroke-opacity:1;stroke-dasharray:none"
d="m 60.821116,9.5634205 7.392622,0"
id="path3868-3"
inkscape:connector-curvature="0" />
<path
sodipodi:type="arc"
style="fill:#000000;fill-opacity:1;stroke:none"
id="path3888"
sodipodi:cx="51.748352"
sodipodi:cy="14.099802"
sodipodi:rx="2.3521979"
sodipodi:ry="2.3521979"
d="m 54.10055,14.099802 a 2.3521979,2.3521979 0 1 1 -4.704396,0 2.3521979,2.3521979 0 1 1 4.704396,0 z"
transform="translate(0.84007066,-4.5363816)" />
<path
transform="translate(-1.0080848,3.5282969)"
d="m 61.063627,26.488129 a 7.5973411,7.5973411 0 1 1 -15.194682,0 7.5973411,7.5973411 0 1 1 15.194682,0 z"
sodipodi:ry="7.5973411"
sodipodi:rx="7.5973411"
sodipodi:cy="26.488129"
sodipodi:cx="53.466286"
id="path4352"
style="fill:#ffffff;fill-opacity:1;stroke:#000000;stroke-width:2;stroke-linecap:round;stroke-linejoin:round;stroke-miterlimit:4;stroke-opacity:1;stroke-dasharray:none;stroke-dashoffset:0"
sodipodi:type="arc" />
<path
style="fill:none;stroke:#000000;stroke-width:1.5;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:4;stroke-opacity:1;stroke-dasharray:none"
d="m 47.716014,29.893131 9.072763,0"
id="path4358"
inkscape:connector-curvature="0" />
<path
style="fill:none;stroke:#000000;stroke-width:1.5;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:4;stroke-opacity:1;stroke-dasharray:none"
d="m 33.266798,29.725117 10.920919,0"
id="path4376"
inkscape:connector-curvature="0" />
<g
transform="matrix(0.81161259,0,0,0.63682405,-2.3339197,-14.471639)"
style="stroke-width:2;stroke-miterlimit:4;stroke-dasharray:none"
id="g3040">
<path
style="fill:none;stroke:#000000;stroke-width:2.78192782;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:4;stroke-opacity:1;stroke-dasharray:none"
d="m 10.351377,24.493827 -5.6980057,0 0,25.925926 5.6980057,0"
id="path3042"
inkscape:connector-curvature="0"
sodipodi:nodetypes="cccc" />
<use
style="stroke-width:2;stroke-miterlimit:4;stroke-dasharray:none"
x="0"
y="0"
xlink:href="#path3045"
id="use3044"
transform="matrix(-1,0,0,1,47.081698,0)"
width="64"
height="64" />
</g>
<g
transform="matrix(0.66209119,0,0,0.66209119,16.81656,31.029372)"
id="use4372">
<path
style="fill:none;stroke:#007ce0;stroke-width:1.51036596px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1"
d="m -18.909091,25.818182 4.272727,17 30.454546,-11.545455 z"
id="path3042-7"
inkscape:connector-curvature="0" />
<path
style="fill:#1798ff;fill-opacity:0.50210972;stroke:#007ce0;stroke-width:1.51036596px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1"
d="M -14.636364,42.818182 15.818182,31.272727 -15.090909,28.545455 z"
id="path3044"
inkscape:connector-curvature="0" />
<path
style="fill:#006eca;fill-opacity:0.50196078;stroke:#1798ff;stroke-width:1.51036596px;stroke-linecap:butt;stroke-linejoin:round;stroke-opacity:0.50196078"
d="m -18.909091,25.818182 4.272727,17 -0.454545,-14.272727 z"
id="path3046"
inkscape:connector-curvature="0" />
<path
style="fill:#0065b7;fill-opacity:0.63291141;stroke:#007ce0;stroke-width:1.51036596px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1"
d="m -15.090909,28.545455 30.909091,2.727272 -34.727273,-5.454545 z"
id="path3048"
inkscape:connector-curvature="0" />
</g>
</g>
</svg>

After

Width:  |  Height:  |  Size: 18 KiB

View File

@ -0,0 +1,440 @@
<?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="svg2568"
sodipodi:version="0.32"
inkscape:version="0.48.4 r9939"
sodipodi:docname="Lattice2_PopulateCopies_Plms_Move.svg"
inkscape:output_extension="org.inkscape.output.svg.inkscape"
version="1.1"
inkscape:export-filename="/home/user/Downloads/cad/mystuff/icons/Part/mystuff/Part_Compound_5_32px.png"
inkscape:export-xdpi="45"
inkscape:export-ydpi="45">
<defs
id="defs2570">
<linearGradient
id="linearGradient3864">
<stop
id="stop3866"
offset="0"
style="stop-color:#71b2f8;stop-opacity:1;" />
<stop
id="stop3868"
offset="1"
style="stop-color:#002795;stop-opacity:1;" />
</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="perspective2576" />
<linearGradient
id="linearGradient3377">
<stop
id="stop3379"
offset="0"
style="stop-color:#71b2f8;stop-opacity:1;" />
<stop
id="stop3381"
offset="1"
style="stop-color:#002795;stop-opacity:1;" />
</linearGradient>
<linearGradient
id="linearGradient3023">
<stop
id="stop3025"
offset="0"
style="stop-color:#71b2f8;stop-opacity:1;" />
<stop
id="stop3027"
offset="1"
style="stop-color:#002795;stop-opacity:1;" />
</linearGradient>
<radialGradient
inkscape:collect="always"
xlink:href="#linearGradient3377"
id="radialGradient3692"
cx="45.883327"
cy="28.869568"
fx="45.883327"
fy="28.869568"
r="19.467436"
gradientUnits="userSpaceOnUse" />
<linearGradient
id="linearGradient3030">
<stop
id="stop3032"
offset="0"
style="stop-color:#71b2f8;stop-opacity:1;" />
<stop
id="stop3034"
offset="1"
style="stop-color:#002795;stop-opacity:1;" />
</linearGradient>
<radialGradient
inkscape:collect="always"
xlink:href="#linearGradient3377"
id="radialGradient3837"
gradientUnits="userSpaceOnUse"
gradientTransform="matrix(0.98773287,-0.06324662,0.02642229,1.230404,-216.68819,-80.013158)"
cx="148.88333"
cy="81.869568"
fx="148.88333"
fy="81.869568"
r="19.467436" />
<radialGradient
inkscape:collect="always"
xlink:href="#linearGradient3377"
id="radialGradient3839"
gradientUnits="userSpaceOnUse"
gradientTransform="matrix(0.69474204,0.27707782,-0.32964185,2.4645588,-139.05338,-247.09727)"
cx="135.38333"
cy="97.369568"
fx="135.38333"
fy="97.369568"
r="19.467436" />
<radialGradient
inkscape:collect="always"
xlink:href="#linearGradient3377"
id="radialGradient3841"
gradientUnits="userSpaceOnUse"
gradientTransform="matrix(0.71303129,0,0,1.2312496,-173.62652,-89.498759)"
cx="45.883327"
cy="28.869568"
fx="45.883327"
fy="28.869568"
r="19.467436" />
<radialGradient
inkscape:collect="always"
xlink:href="#linearGradient3377"
id="radialGradient3804"
gradientUnits="userSpaceOnUse"
gradientTransform="matrix(0.98773287,-0.06324662,0.02642229,1.230404,-216.68819,-80.013158)"
cx="148.88333"
cy="81.869568"
fx="148.88333"
fy="81.869568"
r="19.467436" />
<radialGradient
inkscape:collect="always"
xlink:href="#linearGradient3377"
id="radialGradient3806"
gradientUnits="userSpaceOnUse"
gradientTransform="matrix(0.69474204,0.27707782,-0.32964185,2.4645588,-139.05338,-247.09727)"
cx="135.38333"
cy="97.369568"
fx="135.38333"
fy="97.369568"
r="19.467436" />
<radialGradient
inkscape:collect="always"
xlink:href="#linearGradient3377"
id="radialGradient3808"
gradientUnits="userSpaceOnUse"
gradientTransform="matrix(0.71303129,0,0,1.2312496,-173.62652,-89.498759)"
cx="45.883327"
cy="28.869568"
fx="45.883327"
fy="28.869568"
r="19.467436" />
<radialGradient
inkscape:collect="always"
xlink:href="#linearGradient3377-5"
id="radialGradient3804-3"
gradientUnits="userSpaceOnUse"
gradientTransform="matrix(0.98773287,-0.06324662,0.02642229,1.230404,-216.68819,-80.013158)"
cx="148.88333"
cy="81.869568"
fx="148.88333"
fy="81.869568"
r="19.467436" />
<linearGradient
id="linearGradient3377-5">
<stop
id="stop3379-4"
offset="0"
style="stop-color:#71b2f8;stop-opacity:1;" />
<stop
id="stop3381-8"
offset="1"
style="stop-color:#002795;stop-opacity:1;" />
</linearGradient>
<radialGradient
inkscape:collect="always"
xlink:href="#linearGradient3377-5"
id="radialGradient3806-9"
gradientUnits="userSpaceOnUse"
gradientTransform="matrix(0.69474204,0.27707782,-0.32964185,2.4645588,-139.05338,-247.09727)"
cx="135.38333"
cy="97.369568"
fx="135.38333"
fy="97.369568"
r="19.467436" />
<linearGradient
id="linearGradient3080">
<stop
id="stop3082"
offset="0"
style="stop-color:#71b2f8;stop-opacity:1;" />
<stop
id="stop3084"
offset="1"
style="stop-color:#002795;stop-opacity:1;" />
</linearGradient>
<radialGradient
inkscape:collect="always"
xlink:href="#linearGradient3377-5"
id="radialGradient3808-3"
gradientUnits="userSpaceOnUse"
gradientTransform="matrix(0.71303129,0,0,1.2312496,-173.62652,-89.498759)"
cx="45.883327"
cy="28.869568"
fx="45.883327"
fy="28.869568"
r="19.467436" />
<linearGradient
id="linearGradient3087">
<stop
id="stop3089"
offset="0"
style="stop-color:#71b2f8;stop-opacity:1;" />
<stop
id="stop3091"
offset="1"
style="stop-color:#002795;stop-opacity:1;" />
</linearGradient>
<radialGradient
inkscape:collect="always"
xlink:href="#linearGradient3377-5"
id="radialGradient4366"
gradientUnits="userSpaceOnUse"
gradientTransform="matrix(0.98773287,-0.06324662,0.02642229,1.230404,-216.68819,-80.013158)"
cx="148.88333"
cy="81.869568"
fx="148.88333"
fy="81.869568"
r="19.467436" />
<radialGradient
inkscape:collect="always"
xlink:href="#linearGradient3377-5"
id="radialGradient4368"
gradientUnits="userSpaceOnUse"
gradientTransform="matrix(0.69474204,0.27707782,-0.32964185,2.4645588,-139.05338,-247.09727)"
cx="135.38333"
cy="97.369568"
fx="135.38333"
fy="97.369568"
r="19.467436" />
<radialGradient
inkscape:collect="always"
xlink:href="#linearGradient3377-5"
id="radialGradient4370"
gradientUnits="userSpaceOnUse"
gradientTransform="matrix(0.71303129,0,0,1.2312496,-173.62652,-89.498759)"
cx="45.883327"
cy="28.869568"
fx="45.883327"
fy="28.869568"
r="19.467436" />
</defs>
<sodipodi:namedview
id="base"
pagecolor="#ffffff"
bordercolor="#666666"
borderopacity="1.0"
inkscape:pageopacity="0.0"
inkscape:pageshadow="2"
inkscape:zoom="5.9518802"
inkscape:cx="39.412699"
inkscape:cy="28.156141"
inkscape:current-layer="layer1"
showgrid="true"
inkscape:document-units="px"
inkscape:grid-bbox="true"
inkscape:window-width="1920"
inkscape:window-height="1001"
inkscape:window-x="-9"
inkscape:window-y="-9"
inkscape:window-maximized="1"
inkscape:object-nodes="true"
inkscape:snap-global="false"
showguides="true"
inkscape:guide-bbox="true" />
<metadata
id="metadata2573">
<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>
</cc:Work>
</rdf:RDF>
</metadata>
<g
id="layer1"
inkscape:label="Layer 1"
inkscape:groupmode="layer">
<g
id="g3794"
transform="matrix(0.66209119,0,0,0.66209119,16.272443,7.8325221)">
<path
inkscape:connector-curvature="0"
id="path3018"
d="m -18.909091,25.818182 4.272727,17 30.454546,-11.545455 z"
style="fill:none;stroke:#000000;stroke-width:1.51036596px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1" />
<path
inkscape:connector-curvature="0"
id="path3020"
d="M -14.636364,42.818182 15.818182,31.272727 -15.090909,28.545455 z"
style="fill:#e9a60a;fill-opacity:1;stroke:#000000;stroke-width:1.51036596px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1" />
<path
inkscape:connector-curvature="0"
id="path3022"
d="m -18.909091,25.818182 4.272727,17 -0.454545,-14.272727 z"
style="fill:#4c390a;fill-opacity:1;stroke:#000000;stroke-width:1.51036596px;stroke-linecap:butt;stroke-linejoin:round;stroke-opacity:1" />
<path
inkscape:connector-curvature="0"
id="path3024"
d="m -15.090909,28.545455 30.909091,2.727272 -34.727273,-5.454545 z"
style="fill:#2f240a;fill-opacity:1;stroke:#000000;stroke-width:1.51036596px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1" />
</g>
<g
id="g3049"
style="stroke-width:2;stroke-miterlimit:4;stroke-dasharray:none"
transform="matrix(0.77833186,0,0,0.77833186,-2.3898253,0.89367113)">
<path
sodipodi:nodetypes="cccc"
inkscape:connector-curvature="0"
id="path3045"
d="m 10.351377,24.493827 -5.6980057,0 0,25.925926 5.6980057,0"
style="fill:none;stroke:#000000;stroke-width:2.56959796;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:4;stroke-opacity:1;stroke-dasharray:none" />
<use
height="64"
width="64"
transform="matrix(-1,0,0,1,47.081698,0)"
id="use3047"
xlink:href="#path3045"
y="0"
x="0"
style="stroke-width:2;stroke-miterlimit:4;stroke-dasharray:none" />
</g>
<path
sodipodi:type="arc"
style="fill:#ffffff;fill-opacity:1;stroke:#000000;stroke-width:2;stroke-linecap:round;stroke-linejoin:round;stroke-miterlimit:4;stroke-opacity:1;stroke-dasharray:none;stroke-dashoffset:0"
id="path3823"
sodipodi:cx="53.466286"
sodipodi:cy="26.488129"
sodipodi:rx="7.5973411"
sodipodi:ry="7.5973411"
d="m 61.063627,26.488129 a 7.5973411,7.5973411 0 1 1 -15.194682,0 7.5973411,7.5973411 0 1 1 15.194682,0 z"
transform="translate(-1.0080847,-16.969427)" />
<path
style="fill:none;stroke:#000000;stroke-width:1.49999988;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:4;stroke-opacity:1;stroke-dasharray:none"
d="m 33.602825,52.743051 18.817583,0 0,-35.787009"
id="path3866"
inkscape:connector-curvature="0" />
<path
style="fill:none;stroke:#000000;stroke-width:1.50000012;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:4;stroke-opacity:1;stroke-dasharray:none"
d="m 30.091119,9.2273923 15.088091,0"
id="path3868"
inkscape:connector-curvature="0" />
<path
style="fill:none;stroke:#000000;stroke-width:1.5;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:4;stroke-opacity:1;stroke-dasharray:none"
d="m 60.821116,9.5634205 7.392622,0"
id="path3868-3"
inkscape:connector-curvature="0" />
<path
sodipodi:type="arc"
style="fill:#000000;fill-opacity:1;stroke:none"
id="path3888"
sodipodi:cx="51.748352"
sodipodi:cy="14.099802"
sodipodi:rx="2.3521979"
sodipodi:ry="2.3521979"
d="m 54.10055,14.099802 a 2.3521979,2.3521979 0 1 1 -4.704396,0 2.3521979,2.3521979 0 1 1 4.704396,0 z"
transform="translate(0.84007066,-4.5363816)" />
<path
transform="translate(-1.0080848,3.5282969)"
d="m 61.063627,26.488129 a 7.5973411,7.5973411 0 1 1 -15.194682,0 7.5973411,7.5973411 0 1 1 15.194682,0 z"
sodipodi:ry="7.5973411"
sodipodi:rx="7.5973411"
sodipodi:cy="26.488129"
sodipodi:cx="53.466286"
id="path4352"
style="fill:#ffffff;fill-opacity:1;stroke:#000000;stroke-width:2;stroke-linecap:round;stroke-linejoin:round;stroke-miterlimit:4;stroke-opacity:1;stroke-dasharray:none;stroke-dashoffset:0"
sodipodi:type="arc" />
<path
style="fill:none;stroke:#000000;stroke-width:1.5;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:4;stroke-opacity:1;stroke-dasharray:none"
d="m 47.716014,29.893131 9.072763,0"
id="path4358"
inkscape:connector-curvature="0" />
<path
style="fill:none;stroke:#000000;stroke-width:1.5;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:4;stroke-opacity:1;stroke-dasharray:none"
d="m 33.266798,29.725117 10.920919,0"
id="path4376"
inkscape:connector-curvature="0" />
<g
id="g3794-9"
transform="matrix(0.66209119,0,0,0.66209119,16.984575,-13.494373)">
<path
inkscape:connector-curvature="0"
id="path3018-4"
d="m -18.909091,25.818182 4.272727,17 30.454546,-11.545455 z"
style="fill:none;stroke:#000000;stroke-width:1.51036596px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1" />
<path
inkscape:connector-curvature="0"
id="path3020-5"
d="M -14.636364,42.818182 15.818182,31.272727 -15.090909,28.545455 z"
style="fill:#e9a60a;fill-opacity:1;stroke:#000000;stroke-width:1.51036596px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1" />
<path
inkscape:connector-curvature="0"
id="path3022-5"
d="m -18.909091,25.818182 4.272727,17 -0.454545,-14.272727 z"
style="fill:#4c390a;fill-opacity:1;stroke:#000000;stroke-width:1.51036596px;stroke-linecap:butt;stroke-linejoin:round;stroke-opacity:1" />
<path
inkscape:connector-curvature="0"
id="path3024-4"
d="m -15.090909,28.545455 30.909091,2.727272 -34.727273,-5.454545 z"
style="fill:#2f240a;fill-opacity:1;stroke:#000000;stroke-width:1.51036596px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1" />
</g>
<g
transform="matrix(0.66209119,0,0,0.66209119,16.312518,30.861358)"
id="use4372">
<path
style="fill:none;stroke:#007ce0;stroke-width:1.51036596px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1"
d="m -18.909091,25.818182 4.272727,17 30.454546,-11.545455 z"
id="path3042"
inkscape:connector-curvature="0" />
<path
style="fill:#1798ff;fill-opacity:0.50210972;stroke:#007ce0;stroke-width:1.51036596px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1"
d="M -14.636364,42.818182 15.818182,31.272727 -15.090909,28.545455 z"
id="path3044"
inkscape:connector-curvature="0" />
<path
style="fill:#006eca;fill-opacity:0.50196078;stroke:#1798ff;stroke-width:1.51036596px;stroke-linecap:butt;stroke-linejoin:round;stroke-opacity:0.50196078"
d="m -18.909091,25.818182 4.272727,17 -0.454545,-14.272727 z"
id="path3046"
inkscape:connector-curvature="0" />
<path
style="fill:#0065b7;fill-opacity:0.63291141;stroke:#007ce0;stroke-width:1.51036596px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1"
d="m -15.090909,28.545455 30.909091,2.727272 -34.727273,-5.454545 z"
id="path3048"
inkscape:connector-curvature="0" />
</g>
</g>
</svg>

After

Width:  |  Height:  |  Size: 16 KiB

View File

@ -0,0 +1,441 @@
<?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="svg2568"
sodipodi:version="0.32"
inkscape:version="0.48.4 r9939"
sodipodi:docname="Lattice2_PopulateCopies_Move.svg"
inkscape:output_extension="org.inkscape.output.svg.inkscape"
version="1.1"
inkscape:export-filename="/home/user/Downloads/cad/mystuff/icons/Part/mystuff/Part_Compound_5_32px.png"
inkscape:export-xdpi="45"
inkscape:export-ydpi="45">
<defs
id="defs2570">
<linearGradient
id="linearGradient3864">
<stop
id="stop3866"
offset="0"
style="stop-color:#71b2f8;stop-opacity:1;" />
<stop
id="stop3868"
offset="1"
style="stop-color:#002795;stop-opacity:1;" />
</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="perspective2576" />
<linearGradient
id="linearGradient3377">
<stop
id="stop3379"
offset="0"
style="stop-color:#71b2f8;stop-opacity:1;" />
<stop
id="stop3381"
offset="1"
style="stop-color:#002795;stop-opacity:1;" />
</linearGradient>
<linearGradient
id="linearGradient3023">
<stop
id="stop3025"
offset="0"
style="stop-color:#71b2f8;stop-opacity:1;" />
<stop
id="stop3027"
offset="1"
style="stop-color:#002795;stop-opacity:1;" />
</linearGradient>
<radialGradient
inkscape:collect="always"
xlink:href="#linearGradient3377"
id="radialGradient3692"
cx="45.883327"
cy="28.869568"
fx="45.883327"
fy="28.869568"
r="19.467436"
gradientUnits="userSpaceOnUse" />
<linearGradient
id="linearGradient3030">
<stop
id="stop3032"
offset="0"
style="stop-color:#71b2f8;stop-opacity:1;" />
<stop
id="stop3034"
offset="1"
style="stop-color:#002795;stop-opacity:1;" />
</linearGradient>
<radialGradient
inkscape:collect="always"
xlink:href="#linearGradient3377"
id="radialGradient3837"
gradientUnits="userSpaceOnUse"
gradientTransform="matrix(0.98773287,-0.06324662,0.02642229,1.230404,-216.68819,-80.013158)"
cx="148.88333"
cy="81.869568"
fx="148.88333"
fy="81.869568"
r="19.467436" />
<radialGradient
inkscape:collect="always"
xlink:href="#linearGradient3377"
id="radialGradient3839"
gradientUnits="userSpaceOnUse"
gradientTransform="matrix(0.69474204,0.27707782,-0.32964185,2.4645588,-139.05338,-247.09727)"
cx="135.38333"
cy="97.369568"
fx="135.38333"
fy="97.369568"
r="19.467436" />
<radialGradient
inkscape:collect="always"
xlink:href="#linearGradient3377"
id="radialGradient3841"
gradientUnits="userSpaceOnUse"
gradientTransform="matrix(0.71303129,0,0,1.2312496,-173.62652,-89.498759)"
cx="45.883327"
cy="28.869568"
fx="45.883327"
fy="28.869568"
r="19.467436" />
<radialGradient
inkscape:collect="always"
xlink:href="#linearGradient3377"
id="radialGradient3804"
gradientUnits="userSpaceOnUse"
gradientTransform="matrix(0.98773287,-0.06324662,0.02642229,1.230404,-216.68819,-80.013158)"
cx="148.88333"
cy="81.869568"
fx="148.88333"
fy="81.869568"
r="19.467436" />
<radialGradient
inkscape:collect="always"
xlink:href="#linearGradient3377"
id="radialGradient3806"
gradientUnits="userSpaceOnUse"
gradientTransform="matrix(0.69474204,0.27707782,-0.32964185,2.4645588,-139.05338,-247.09727)"
cx="135.38333"
cy="97.369568"
fx="135.38333"
fy="97.369568"
r="19.467436" />
<radialGradient
inkscape:collect="always"
xlink:href="#linearGradient3377"
id="radialGradient3808"
gradientUnits="userSpaceOnUse"
gradientTransform="matrix(0.71303129,0,0,1.2312496,-173.62652,-89.498759)"
cx="45.883327"
cy="28.869568"
fx="45.883327"
fy="28.869568"
r="19.467436" />
<radialGradient
inkscape:collect="always"
xlink:href="#linearGradient3377-5"
id="radialGradient3804-3"
gradientUnits="userSpaceOnUse"
gradientTransform="matrix(0.98773287,-0.06324662,0.02642229,1.230404,-216.68819,-80.013158)"
cx="148.88333"
cy="81.869568"
fx="148.88333"
fy="81.869568"
r="19.467436" />
<linearGradient
id="linearGradient3377-5">
<stop
id="stop3379-4"
offset="0"
style="stop-color:#71b2f8;stop-opacity:1;" />
<stop
id="stop3381-8"
offset="1"
style="stop-color:#002795;stop-opacity:1;" />
</linearGradient>
<radialGradient
inkscape:collect="always"
xlink:href="#linearGradient3377-5"
id="radialGradient3806-9"
gradientUnits="userSpaceOnUse"
gradientTransform="matrix(0.69474204,0.27707782,-0.32964185,2.4645588,-139.05338,-247.09727)"
cx="135.38333"
cy="97.369568"
fx="135.38333"
fy="97.369568"
r="19.467436" />
<linearGradient
id="linearGradient3080">
<stop
id="stop3082"
offset="0"
style="stop-color:#71b2f8;stop-opacity:1;" />
<stop
id="stop3084"
offset="1"
style="stop-color:#002795;stop-opacity:1;" />
</linearGradient>
<radialGradient
inkscape:collect="always"
xlink:href="#linearGradient3377-5"
id="radialGradient3808-3"
gradientUnits="userSpaceOnUse"
gradientTransform="matrix(0.71303129,0,0,1.2312496,-173.62652,-89.498759)"
cx="45.883327"
cy="28.869568"
fx="45.883327"
fy="28.869568"
r="19.467436" />
<linearGradient
id="linearGradient3087">
<stop
id="stop3089"
offset="0"
style="stop-color:#71b2f8;stop-opacity:1;" />
<stop
id="stop3091"
offset="1"
style="stop-color:#002795;stop-opacity:1;" />
</linearGradient>
<radialGradient
inkscape:collect="always"
xlink:href="#linearGradient3377-5"
id="radialGradient4366"
gradientUnits="userSpaceOnUse"
gradientTransform="matrix(0.98773287,-0.06324662,0.02642229,1.230404,-216.68819,-80.013158)"
cx="148.88333"
cy="81.869568"
fx="148.88333"
fy="81.869568"
r="19.467436" />
<radialGradient
inkscape:collect="always"
xlink:href="#linearGradient3377-5"
id="radialGradient4368"
gradientUnits="userSpaceOnUse"
gradientTransform="matrix(0.69474204,0.27707782,-0.32964185,2.4645588,-139.05338,-247.09727)"
cx="135.38333"
cy="97.369568"
fx="135.38333"
fy="97.369568"
r="19.467436" />
<radialGradient
inkscape:collect="always"
xlink:href="#linearGradient3377-5"
id="radialGradient4370"
gradientUnits="userSpaceOnUse"
gradientTransform="matrix(0.71303129,0,0,1.2312496,-173.62652,-89.498759)"
cx="45.883327"
cy="28.869568"
fx="45.883327"
fy="28.869568"
r="19.467436" />
</defs>
<sodipodi:namedview
id="base"
pagecolor="#ffffff"
bordercolor="#666666"
borderopacity="1.0"
inkscape:pageopacity="0.0"
inkscape:pageshadow="2"
inkscape:zoom="5.9518802"
inkscape:cx="8.9181336"
inkscape:cy="28.156141"
inkscape:current-layer="layer1"
showgrid="true"
inkscape:document-units="px"
inkscape:grid-bbox="true"
inkscape:window-width="1920"
inkscape:window-height="1001"
inkscape:window-x="-9"
inkscape:window-y="-9"
inkscape:window-maximized="1"
inkscape:object-nodes="true"
inkscape:snap-global="false"
showguides="true"
inkscape:guide-bbox="true" />
<metadata
id="metadata2573">
<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>
</cc:Work>
</rdf:RDF>
</metadata>
<g
id="layer1"
inkscape:label="Layer 1"
inkscape:groupmode="layer">
<g
id="g3794"
transform="matrix(0.66209119,0,0,0.66209119,16.272443,7.8325221)">
<path
inkscape:connector-curvature="0"
id="path3018"
d="m -18.909091,25.818182 4.272727,17 30.454546,-11.545455 z"
style="fill:none;stroke:#000000;stroke-width:1.51036596px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1" />
<path
inkscape:connector-curvature="0"
id="path3020"
d="M -14.636364,42.818182 15.818182,31.272727 -15.090909,28.545455 z"
style="fill:#e9a60a;fill-opacity:1;stroke:#000000;stroke-width:1.51036596px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1" />
<path
inkscape:connector-curvature="0"
id="path3022"
d="m -18.909091,25.818182 4.272727,17 -0.454545,-14.272727 z"
style="fill:#4c390a;fill-opacity:1;stroke:#000000;stroke-width:1.51036596px;stroke-linecap:butt;stroke-linejoin:round;stroke-opacity:1" />
<path
inkscape:connector-curvature="0"
id="path3024"
d="m -15.090909,28.545455 30.909091,2.727272 -34.727273,-5.454545 z"
style="fill:#2f240a;fill-opacity:1;stroke:#000000;stroke-width:1.51036596px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1" />
</g>
<g
id="g3049"
style="stroke-width:2;stroke-miterlimit:4;stroke-dasharray:none"
transform="matrix(0.77833186,0,0,0.77833186,-2.3898253,0.89367113)">
<path
sodipodi:nodetypes="cccc"
inkscape:connector-curvature="0"
id="path3045"
d="m 10.351377,24.493827 -5.6980057,0 0,25.925926 5.6980057,0"
style="fill:none;stroke:#000000;stroke-width:2.56959796;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:4;stroke-opacity:1;stroke-dasharray:none" />
<use
height="64"
width="64"
transform="matrix(-1,0,0,1,47.081698,0)"
id="use3047"
xlink:href="#path3045"
y="0"
x="0"
style="stroke-width:2;stroke-miterlimit:4;stroke-dasharray:none" />
</g>
<path
sodipodi:type="arc"
style="fill:#ffffff;fill-opacity:1;stroke:#000000;stroke-width:2;stroke-linecap:round;stroke-linejoin:round;stroke-miterlimit:4;stroke-opacity:1;stroke-dasharray:none;stroke-dashoffset:0"
id="path3823"
sodipodi:cx="53.466286"
sodipodi:cy="26.488129"
sodipodi:rx="7.5973411"
sodipodi:ry="7.5973411"
d="m 61.063627,26.488129 a 7.5973411,7.5973411 0 1 1 -15.194682,0 7.5973411,7.5973411 0 1 1 15.194682,0 z"
transform="translate(-1.0080847,-16.969427)" />
<g
id="g3798"
transform="matrix(0.94897349,0,0,0.94897349,-24.695619,-10.087879)">
<g
id="g3843"
transform="matrix(0.57659255,0,0,0.32499812,79.277735,9.6499853)">
<path
style="fill:url(#radialGradient4366);fill-opacity:1;fill-rule:evenodd;stroke:#000137;stroke-width:2.17218089;stroke-linecap:butt;stroke-linejoin:round;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 -65.878042,4.7970936 -12.866966,10.1036774 21.744289,5.108296 0.30338,44.045609 11.486763,-13.03934 0.410614,-42.3363302 z"
id="path3845"
sodipodi:nodetypes="ccccccc"
inkscape:connector-curvature="0" />
<path
style="fill:url(#radialGradient4368);fill-opacity:1;fill-rule:evenodd;stroke:#000137;stroke-width:2.17218089;stroke-linecap:butt;stroke-linejoin:round;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 -78.552461,15.155385 21.896364,4.143912 0,44.912523 -22.192265,-6.236008 0.295901,-42.820427 z"
id="path3847"
sodipodi:nodetypes="ccccc"
inkscape:connector-curvature="0" />
<path
style="fill:url(#radialGradient4370);fill-opacity:1;fill-rule:evenodd;stroke:#000137;stroke-width:2.17218089;stroke-linecap:butt;stroke-linejoin:round;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 -56.822875,19.354658 -44.896728,9.001326"
id="path3849"
inkscape:connector-curvature="0" />
</g>
</g>
<path
style="fill:none;stroke:#000000;stroke-width:1.49999988;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:4;stroke-opacity:1;stroke-dasharray:none"
d="m 33.602825,52.743051 18.817583,0 0,-35.787009"
id="path3866"
inkscape:connector-curvature="0" />
<path
style="fill:none;stroke:#000000;stroke-width:1.50000012;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:4;stroke-opacity:1;stroke-dasharray:none"
d="m 30.091119,9.2273923 15.088091,0"
id="path3868"
inkscape:connector-curvature="0" />
<path
style="fill:none;stroke:#000000;stroke-width:1.5;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:4;stroke-opacity:1;stroke-dasharray:none"
d="m 60.821116,9.5634205 7.392622,0"
id="path3868-3"
inkscape:connector-curvature="0" />
<path
sodipodi:type="arc"
style="fill:#000000;fill-opacity:1;stroke:none"
id="path3888"
sodipodi:cx="51.748352"
sodipodi:cy="14.099802"
sodipodi:rx="2.3521979"
sodipodi:ry="2.3521979"
d="m 54.10055,14.099802 a 2.3521979,2.3521979 0 1 1 -4.704396,0 2.3521979,2.3521979 0 1 1 4.704396,0 z"
transform="translate(0.84007066,-4.5363816)" />
<path
transform="translate(-1.0080848,3.5282969)"
d="m 61.063627,26.488129 a 7.5973411,7.5973411 0 1 1 -15.194682,0 7.5973411,7.5973411 0 1 1 15.194682,0 z"
sodipodi:ry="7.5973411"
sodipodi:rx="7.5973411"
sodipodi:cy="26.488129"
sodipodi:cx="53.466286"
id="path4352"
style="fill:#ffffff;fill-opacity:1;stroke:#000000;stroke-width:2;stroke-linecap:round;stroke-linejoin:round;stroke-miterlimit:4;stroke-opacity:1;stroke-dasharray:none;stroke-dashoffset:0"
sodipodi:type="arc" />
<path
style="fill:none;stroke:#000000;stroke-width:1.5;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:4;stroke-opacity:1;stroke-dasharray:none"
d="m 47.716014,29.893131 9.072763,0"
id="path4358"
inkscape:connector-curvature="0" />
<g
transform="matrix(0.66209119,0,0,0.66209119,16.272443,31.186487)"
id="use4372">
<path
style="fill:none;stroke:#007ce0;stroke-width:1.51036595999999990px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1"
d="m -18.909091,25.818182 4.272727,17 30.454546,-11.545455 z"
id="path3042"
inkscape:connector-curvature="0" />
<path
style="fill:#1798ff;fill-opacity:0.50210971000000004;stroke:#007ce0;stroke-width:1.51036595999999990px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1"
d="M -14.636364,42.818182 15.818182,31.272727 -15.090909,28.545455 z"
id="path3044"
inkscape:connector-curvature="0" />
<path
style="fill:#006eca;fill-opacity:0.50196081;stroke:#1798ff;stroke-width:1.51036595999999990px;stroke-linecap:butt;stroke-linejoin:round;stroke-opacity:0.50196081000000004"
d="m -18.909091,25.818182 4.272727,17 -0.454545,-14.272727 z"
id="path3046"
inkscape:connector-curvature="0" />
<path
style="fill:#0065b7;fill-opacity:0.63291138;stroke:#007ce0;stroke-width:1.51036595999999990px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1"
d="m -15.090909,28.545455 30.909091,2.727272 -34.727273,-5.454545 z"
id="path3048"
inkscape:connector-curvature="0" />
</g>
<path
style="fill:none;stroke:#000000;stroke-width:1.5;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:4;stroke-opacity:1;stroke-dasharray:none"
d="m 33.266798,29.725117 10.920919,0"
id="path4376"
inkscape:connector-curvature="0" />
</g>
</svg>

After

Width:  |  Height:  |  Size: 17 KiB

View File

@ -1,408 +0,0 @@
<?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="svg2726"
sodipodi:version="0.32"
inkscape:version="0.48.4 r9939"
sodipodi:docname="Lattice2_ViewFromPlacement.svg"
inkscape:output_extension="org.inkscape.output.svg.inkscape"
version="1.1">
<defs
id="defs2728">
<linearGradient
id="linearGradient3916">
<stop
style="stop-color:#00ffff;stop-opacity:1;"
offset="0"
id="stop3918" />
<stop
id="stop3924"
offset="0.61475408"
style="stop-color:#00ffff;stop-opacity:0.9927361;" />
<stop
style="stop-color:#00ffff;stop-opacity:0;"
offset="1"
id="stop3920" />
</linearGradient>
<linearGradient
id="linearGradient3908">
<stop
style="stop-color:#ffffff;stop-opacity:1;"
offset="0"
id="stop3910" />
<stop
style="stop-color:#74bdc6;stop-opacity:1;"
offset="1"
id="stop3912" />
</linearGradient>
<linearGradient
id="linearGradient3766">
<stop
style="stop-color:#004bed;stop-opacity:1;"
offset="0"
id="stop3768" />
<stop
style="stop-color:#bbd7ff;stop-opacity:1;"
offset="1"
id="stop3770" />
</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="perspective2734" />
<radialGradient
inkscape:collect="always"
xlink:href="#linearGradient3144-4"
id="radialGradient3850-9"
gradientUnits="userSpaceOnUse"
gradientTransform="matrix(1,0,0,0.6985294,0,202.82863)"
cx="225.26402"
cy="672.79736"
fx="225.26402"
fy="672.79736"
r="34.345188" />
<linearGradient
inkscape:collect="always"
id="linearGradient3144-4">
<stop
style="stop-color:#ffffff;stop-opacity:1;"
offset="0"
id="stop3146-2" />
<stop
style="stop-color:#ffffff;stop-opacity:0;"
offset="1"
id="stop3148-0" />
</linearGradient>
<linearGradient
inkscape:collect="always"
xlink:href="#linearGradient3766"
id="linearGradient3772"
x1="21.905107"
y1="26.587624"
x2="35.65699"
y2="29.344099"
gradientUnits="userSpaceOnUse" />
<radialGradient
inkscape:collect="always"
xlink:href="#linearGradient3908"
id="radialGradient3914"
cx="34.266594"
cy="12.48589"
fx="34.266594"
fy="12.48589"
r="28.220161"
gradientUnits="userSpaceOnUse" />
<radialGradient
inkscape:collect="always"
xlink:href="#linearGradient3916"
id="radialGradient3922"
cx="46.773899"
cy="24.764751"
fx="46.773899"
fy="24.764751"
r="6.4209293"
gradientTransform="matrix(1,0,0,1.1245231,0,-3.083783)"
gradientUnits="userSpaceOnUse" />
<radialGradient
inkscape:collect="always"
xlink:href="#linearGradient3908"
id="radialGradient4080"
gradientUnits="userSpaceOnUse"
cx="34.266594"
cy="12.48589"
fx="34.266594"
fy="12.48589"
r="28.220161" />
<radialGradient
inkscape:collect="always"
xlink:href="#linearGradient3916"
id="radialGradient4082"
gradientUnits="userSpaceOnUse"
gradientTransform="matrix(1,0,0,1.1245231,0,-3.083783)"
cx="46.773899"
cy="24.764751"
fx="46.773899"
fy="24.764751"
r="6.4209293" />
</defs>
<sodipodi:namedview
id="base"
pagecolor="#ffffff"
bordercolor="#666666"
borderopacity="1.0"
inkscape:pageopacity="0.0"
inkscape:pageshadow="2"
inkscape:zoom="8.7548828"
inkscape:cx="35.247365"
inkscape:cy="39.839989"
inkscape:current-layer="g4289"
showgrid="true"
inkscape:document-units="px"
inkscape:grid-bbox="true"
inkscape:window-width="1920"
inkscape:window-height="1001"
inkscape:window-x="-9"
inkscape:window-y="-9"
inkscape:window-maximized="1"
inkscape:snap-global="false"
inkscape:object-paths="false"
inkscape:object-nodes="true"
inkscape:snap-nodes="true" />
<metadata
id="metadata2731">
<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 />
</cc:Work>
</rdf:RDF>
</metadata>
<g
id="layer1"
inkscape:label="Layer 1"
inkscape:groupmode="layer">
<g
id="g4289"
transform="matrix(0.1621282,0,0,0.1621282,6.3605986,-66.108806)">
<g
id="g3081"
transform="matrix(0.7936378,0,0,0.7936378,-16.761355,242.67638)">
<path
transform="matrix(6.1679584,0,0,6.1679584,-39.231908,407.75637)"
inkscape:connector-curvature="0"
id="path3077"
d="M 39.178137,29.84763 39.063915,47.666258 76.985611,41.612493 z"
style="fill:#ffb380;stroke:#000000;stroke-width:1.26002061px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1" />
<path
inkscape:connector-curvature="0"
id="path3075"
d="M 3.6551032,30.190296 38.949693,29.84763 53.79855,22.651645 76.871389,41.498271 z"
style="fill:#ff6600;stroke:#000000;stroke-width:1.26002061px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1"
transform="matrix(6.1679584,0,0,6.1679584,-39.231908,407.75637)" />
<path
transform="matrix(6.1679584,0,0,6.1679584,-39.231908,407.75637)"
inkscape:connector-curvature="0"
id="path3079"
d="M 39.178137,29.733408 76.528723,41.269827"
style="fill:none;stroke:#000000;stroke-width:1.26002061px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1" />
</g>
<g
id="g4048"
transform="matrix(0.75122203,0,0,0.75122203,7.0716755,157.31061)">
<g
id="g4044">
<path
sodipodi:type="arc"
style="fill:url(#radialGradient4080);fill-opacity:1;stroke:#000000;stroke-width:0.93181503;stroke-linecap:round;stroke-linejoin:round;stroke-miterlimit:4;stroke-opacity:1;stroke-dasharray:none;stroke-dashoffset:0"
id="path3088"
sodipodi:cx="41.462578"
sodipodi:cy="22.537424"
sodipodi:rx="27.870161"
sodipodi:ry="27.870161"
d="m 69.332739,22.537424 c 0,15.392265 -12.477896,27.870161 -27.870161,27.870161 -15.392265,0 -27.870161,-12.477896 -27.870161,-27.870161 0,-15.3922648 12.477896,-27.870161 27.870161,-27.870161 15.392265,0 27.870161,12.4778962 27.870161,27.870161 z"
transform="matrix(6.1679584,0,0,6.1679584,-67.412564,417.6196)" />
<path
sodipodi:type="arc"
style="fill:none;stroke:#d0f0ff;stroke-width:1.3141346;stroke-linecap:round;stroke-linejoin:round;stroke-miterlimit:4;stroke-opacity:1;stroke-dasharray:none;stroke-dashoffset:0"
id="path4011"
sodipodi:cx="35.92281"
sodipodi:cy="22.594534"
sodipodi:rx="26.442387"
sodipodi:ry="26.442387"
d="m 62.365196,22.594534 c 0,14.603727 -11.83866,26.442387 -26.442386,26.442387 -14.603727,0 -26.442387,-11.83866 -26.442387,-26.442387 0,-14.603727 11.83866,-26.4423867 26.442387,-26.4423867 14.603726,0 26.442386,11.8386597 26.442386,26.4423867 z"
transform="matrix(6.2478887,0,0,6.2478887,-36.4671,415.1091)" />
</g>
<g
id="g4031">
<path
sodipodi:type="arc"
style="fill:#000000;fill-opacity:1;stroke:#000000;stroke-width:0.93181503;stroke-linecap:round;stroke-linejoin:round;stroke-miterlimit:4;stroke-opacity:1;stroke-dasharray:none;stroke-dashoffset:0"
id="path3096"
sodipodi:cx="47.859009"
sodipodi:cy="24.993196"
sodipodi:rx="9.2519798"
sodipodi:ry="11.479308"
d="m 57.110989,24.993196 c 0,6.339847 -4.142253,11.479309 -9.25198,11.479309 -5.109728,0 -9.25198,-5.139462 -9.25198,-11.479309 0,-6.339846 4.142252,-11.479308 9.25198,-11.479308 5.109727,0 9.25198,5.139462 9.25198,11.479308 z"
transform="matrix(6.1679584,0,0,6.1679584,-39.231908,407.75637)" />
<path
sodipodi:type="arc"
style="fill:url(#radialGradient4082);fill-opacity:1;stroke:#000000;stroke-width:0.82546496;stroke-linecap:round;stroke-linejoin:round;stroke-miterlimit:4;stroke-opacity:1;stroke-dasharray:none;stroke-dashoffset:0"
id="path3102"
sodipodi:cx="46.773899"
sodipodi:cy="24.764751"
sodipodi:rx="6.1108756"
sodipodi:ry="6.9104295"
d="m 52.884775,24.764751 c 0,3.816525 -2.735933,6.91043 -6.110876,6.91043 -3.374943,0 -6.110876,-3.093905 -6.110876,-6.91043 0,-3.816524 2.735933,-6.910429 6.110876,-6.910429 3.374943,0 6.110876,3.093905 6.110876,6.910429 z"
transform="matrix(6.5138252,0,0,7.44233,-56.818481,375.84461)" />
<path
style="fill:#71c2cb;fill-opacity:1;stroke:#000000;stroke-width:1.33116436px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1"
d="m 47.84375,13.5 c -2.838498,0 -5.365342,1.597032 -7.0625,4.09375 L 54.8125,17.4375 C 53.116709,15.036713 50.618807,13.5 47.84375,13.5 z"
transform="matrix(6.1679584,0,0,6.1679584,-39.231908,407.75637)"
id="path3104"
inkscape:connector-curvature="0" />
<path
style="fill:#71c2cb;fill-opacity:1;stroke:#000000;stroke-width:1.33116436px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1"
d="m 55.21875,31.9375 -14.625,0.15625 c 1.694454,2.659483 4.305537,4.375 7.25,4.375 3.00131,0 5.684617,-1.783204 7.375,-4.53125 z"
transform="matrix(6.1679584,0,0,6.1679584,-39.231908,407.75637)"
id="path3106"
inkscape:connector-curvature="0" />
</g>
<g
id="g4037">
<path
sodipodi:type="arc"
style="fill:none;stroke:#000000;stroke-width:0.96294999;stroke-linecap:round;stroke-linejoin:round;stroke-miterlimit:4;stroke-opacity:1;stroke-dasharray:none;stroke-dashoffset:0"
id="path3094"
sodipodi:cx="46.259899"
sodipodi:cy="23.679644"
sodipodi:rx="15.648411"
sodipodi:ry="19.303514"
d="m 61.90831,23.679644 c 0,10.661036 -7.006032,19.303514 -15.648411,19.303514 -8.642379,0 -15.648411,-8.642478 -15.648411,-19.303514 0,-10.661037 7.006032,-19.3035148 15.648411,-19.3035148 8.642379,0 15.648411,8.6424778 15.648411,19.3035148 z"
transform="matrix(6.100426,0,0,5.8394873,-43.505289,423.28413)" />
<path
style="fill:none;stroke:#000000;stroke-width:1.33116436px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1"
d="m 33.58126,-3.5051867 c 3.54906,0.4949619 7.681075,5.9014686 8.338205,9.0235359"
id="path3132"
inkscape:connector-curvature="0"
transform="matrix(6.1679584,0,0,6.1679584,-39.231908,407.75637)"
sodipodi:nodetypes="cc" />
<path
style="fill:none;stroke:#000000;stroke-width:1.33116436px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1"
d="m 43.290128,-2.477189 c 3.173241,0.5667431 6.242341,5.2801913 7.767094,8.909314"
id="path3134"
inkscape:connector-curvature="0"
transform="matrix(6.1679584,0,0,6.1679584,-39.231908,407.75637)"
sodipodi:nodetypes="cc" />
<path
style="fill:none;stroke:#000000;stroke-width:1.33116436px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1"
d="m 34.609258,50.636029 c 3.587197,-0.06637 6.111433,-4.335444 6.739097,-6.510653"
id="path3136"
inkscape:connector-curvature="0"
transform="matrix(6.1679584,0,0,6.1679584,-39.231908,407.75637)"
sodipodi:nodetypes="cc" />
<path
style="fill:none;stroke:#000000;stroke-width:1.33116436px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1"
d="m 42.947462,49.379587 c 2.854355,-0.247839 5.896329,-2.986542 6.739097,-5.711098"
id="path3138"
inkscape:connector-curvature="0"
transform="matrix(6.1679584,0,0,6.1679584,-39.231908,407.75637)"
sodipodi:nodetypes="cc" />
</g>
<g
id="g4021">
<path
style="fill:none;stroke:#000000;stroke-width:5.32465744;stroke-linecap:round;stroke-linejoin:miter;stroke-miterlimit:4;stroke-opacity:1;stroke-dasharray:none"
d="m 59.052761,6.2036811 7.767094,-9.137758"
id="path3985"
inkscape:connector-curvature="0"
transform="matrix(6.1679584,0,0,6.1679584,-39.231908,407.75637)" />
<path
style="fill:none;stroke:#000000;stroke-width:5.32465744;stroke-linecap:round;stroke-linejoin:round;stroke-miterlimit:4;stroke-opacity:1;stroke-dasharray:none"
d="M 27.641718,3.2339098 22.044841,4.0334636 28.098606,-7.5029558"
id="path3983"
inkscape:connector-curvature="0"
transform="matrix(6.1679584,0,0,6.1679584,-39.231908,407.75637)" />
<path
style="fill:none;stroke:#ffffff;stroke-width:1.33116436px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1"
d="M 27.527496,-8.5309536 26.04261,-7.2745119 20.902621,3.0054658"
id="path3993"
inkscape:connector-curvature="0"
transform="matrix(6.1679584,0,0,6.1679584,-39.231908,407.75637)" />
<path
style="fill:none;stroke:#ffffff;stroke-width:1.33116436px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1"
d="m 66.705633,-4.4189625 -1.370664,0.4568879"
id="path3995"
inkscape:connector-curvature="0"
transform="matrix(6.1679584,0,0,6.1679584,-39.231908,407.75637)" />
<path
style="fill:none;stroke:#ffffff;stroke-width:1.33116436px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1"
d="M 63.05053,-0.99230329 57.910541,4.8330173"
id="path3997"
inkscape:connector-curvature="0"
transform="matrix(6.1679584,0,0,6.1679584,-39.231908,407.75637)"
sodipodi:nodetypes="cc" />
<path
style="fill:none;stroke:#000000;stroke-width:3.99349308;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:4;stroke-opacity:1;stroke-dasharray:none"
d="m 27.984384,-6.817624 c 11.913444,0.2990197 23.422626,-0.00835 38.264361,4.683101"
id="path3999"
inkscape:connector-curvature="0"
transform="matrix(6.1679584,0,0,6.1679584,-39.231908,407.75637)"
sodipodi:nodetypes="cc" />
<path
style="fill:none;stroke:#ffffff;stroke-width:1.33116436px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1"
d="m 30.497267,-7.8456217 c 9.550041,0.1098683 18.431209,-0.2261788 34.15237,3.9977691"
id="path4001"
inkscape:connector-curvature="0"
transform="matrix(6.1679584,0,0,6.1679584,-39.231908,407.75637)"
sodipodi:nodetypes="cc" />
<path
style="fill:none;stroke:#ffffff;stroke-width:1.33116436px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1"
d="M 25.928388,1.7490241 28.32705,1.4063582"
id="path4003"
inkscape:connector-curvature="0"
transform="matrix(6.1679584,0,0,6.1679584,-39.231908,407.75637)" />
</g>
<g
id="g4013">
<path
style="fill:none;stroke:#000000;stroke-width:5.32465744;stroke-linecap:round;stroke-linejoin:miter;stroke-miterlimit:4;stroke-opacity:1;stroke-dasharray:none"
d="m 59.395427,41.498271 6.624874,9.480424"
id="path3989"
inkscape:connector-curvature="0"
transform="matrix(6.1679584,0,0,6.1679584,-39.231908,407.75637)" />
<path
style="fill:none;stroke:#000000;stroke-width:5.32465744;stroke-linecap:round;stroke-linejoin:round;stroke-miterlimit:4;stroke-opacity:1;stroke-dasharray:none"
d="M 28.669716,45.49604 24.32928,45.267596 28.555494,57.375125"
id="path3987"
inkscape:connector-curvature="0"
transform="matrix(6.1679584,0,0,6.1679584,-39.231908,407.75637)" />
<path
style="fill:none;stroke:#000000;stroke-width:3.99349308;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:4;stroke-opacity:1;stroke-dasharray:none"
d="m 28.212828,56.004462 c 12.894467,0.0155 25.507911,-0.980681 36.893697,-6.396431"
id="path3991"
inkscape:connector-curvature="0"
transform="matrix(6.1679584,0,0,6.1679584,-39.231908,407.75637)"
sodipodi:nodetypes="cc" />
<path
style="fill:none;stroke:#ffffff;stroke-width:1.33116436px;stroke-linecap:butt;stroke-linejoin:round;stroke-opacity:1"
d="m 29.240825,43.668489 -4.797323,0.342666 0.571111,1.370663 3.540881,11.307976"
id="path4005"
inkscape:connector-curvature="0"
transform="matrix(6.1679584,0,0,6.1679584,-39.231908,407.75637)"
sodipodi:nodetypes="cccc" />
<path
style="fill:none;stroke:#ffffff;stroke-width:1.33116436px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1"
d="M 30.611489,55.090686 C 40.66419,54.628427 50.339639,54.783489 63.62164,49.036921"
id="path4007"
inkscape:connector-curvature="0"
transform="matrix(6.1679584,0,0,6.1679584,-39.231908,407.75637)"
sodipodi:nodetypes="cc" />
<path
style="fill:none;stroke:#ffffff;stroke-width:1.33116436px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1"
d="m 59.509649,41.041383 6.510652,9.25198"
id="path4009"
inkscape:connector-curvature="0"
transform="matrix(6.1679584,0,0,6.1679584,-39.231908,407.75637)"
sodipodi:nodetypes="cc" />
</g>
</g>
</g>
</g>
</svg>

Before

Width:  |  Height:  |  Size: 19 KiB

112
README.md
View File

@ -1,112 +0,0 @@
# Lattice2 Workbench
[![Total alerts](https://img.shields.io/lgtm/alerts/g/DeepSOIC/Lattice2.svg?logo=lgtm&logoWidth=18)](https://lgtm.com/projects/g/DeepSOIC/Lattice2/alerts/) [![Language grade: Python](https://img.shields.io/lgtm/grade/python/g/DeepSOIC/Lattice2.svg?logo=lgtm&logoWidth=18)](https://lgtm.com/projects/g/DeepSOIC/Lattice2/context:python)
Lattice Workbench is a plug-in module/addon (workbench) for FreeCAD.
It's purpose is working with placements and arrays of placements. It functions similar to what an Assembly workbench does, but with emphasis on arrays. There are **no** constraints and relations, there are just arrays of placements that can be generated, combined, transformed, superimposed, and populated with shapes.
Ever wondered how to create a protractor with FreeCAD? That's the aim of this workbench (including tick labeling). Also, exploded assemblies can be made with this workbench.
Additionally, the workbench features a few general-purpose tools, such as parametric downgrade, bounding boxes, shape info tool, and tools for working with collections of shapes (compounds).
One of the big design goals of the workbench is being as parametric as possible.
## Getting started
Follow through the [Basic Tutorial](https://github.com/DeepSOIC/Lattice2/wiki/Basic-Tutorial) to get the basic concept of Lattice2.
## Highlights
![Lattice2-FreeCAD-wormcutter](https://raw.githubusercontent.com/wiki/DeepSOIC/Lattice2/gallery/worm-cutter-done.png)
![Lattice2-FreeCAD-placement-interpolator](https://raw.githubusercontent.com/wiki/DeepSOIC/Lattice2/gallery/placement_interpolator_fixed.png)
Take a look at other examples in the [Gallery of screenshots](https://github.com/DeepSOIC/Lattice2/wiki/Gallery).
## Features
Let's have a glance over the most important capabilities that the workbench adds to FreeCAD:
* Re-use arrays as many times as you need. Unlike Draft array, which directly generates the array of shapes, lattice array tools generate arrays of placements. These can later be populated with shapes, as many times as necessary, without having to set up another array.
* Extends PartDesign workflow, offering a way to reuse a sequence of features in arbitrary bodies and places.
* Elements of array can be different. Unlike Draft Arrays, which always generate a set of equal shapes, Lattice arrays can be used to arrange a set of different shapes. Pack the shapes to be arranged into a Compound, and use Lattice [Populate with children](https://github.com/DeepSOIC/Lattice2/wiki/Feature-PopulateChildren) feature to arrange them.
* Arrays of placements can be combined, inverted, generated from existing shape arrangements, made from individual placements, projected onto shapes, filtered, etc. This allows to produce complex arrangements without scripting.
* Single placements can be used for primitive assembling of parts.
* linear arrays and polar arrays can have their axes linked to edges of shapes
* [ParaSeries](https://github.com/DeepSOIC/Lattice2/wiki/Feature-ParaSeries) feature allows to generate a series of parts with some parameter varied over a list of values.
## Why Lattice2, not just Lattice?
Lattice2 was created at the moment when breaking changes needed to be made to Lattice, but there were a few things made with Lattice. So, it was decided to keep the workbench in that time's state indefinitely as version 1.0, and start development of a new version.
The goal was to allow editing old projects made with Lattice v1, by having both versions installed at the same time. So a new repository was started, and all the files were renamed to start with 'lattice2' or otherwise differ from those of Lattice v1.
Lattice3 (if ever) will be a standalone repository, for the same reasons.
## Installation
### Prerequisites
* FreeCAD >= `v0.16.5155`
* PartDesign tools require `v0.17+`
* Both Py2/Qt4 and Py3/Qt5 builds are supported.
The workbench is OS independent, it should work on any system FreeCAD can be run on. If you find that it doesn't - that is a bug. Please open an ticket in the [issue queue](https://github.com/DeepSOIC/Lattice2/issues).
**Note:** Lattice2 is written in FreeCAD's Python, and **must be run from within FreeCAD**. It requires no compilation, and can be installed by copying the repository to a special location.
### Automated install
There are several options to automate installation of this workbench.
* The **most recommended method** is to use FreeCAD's built-in [Addon Manager](https://github.com/FreeCAD/FreeCAD-addons#1-builtin-addon-manager) (Tools > Addon manager).
* Another method; Lattice2 workbench is packaged to Launchpad in the Ubuntu FreeCAD Community PPA (thanks to @abdullahtahiriyo).
* Lattice2 can be installed via @microelly's [Plugin Loader](https://github.com/microelly2/freecad-pluginloader) (this option is deprecated).
**Note:** Any of the above options will require a restart of FreeCAD in order for the workbench to function.
### Manual install
<details>
<summary>Expand this section if you prefer to manually install Lattice2</summary>
1. Download the workbench. There are several ways to do this, you can choose either:
* Scroll to the top of the page, and click 'clone or download' -> 'download zip' button
* `git clone https://github.com/DeepSOIC/Lattice2`
2. If you downloaded the .zip, unpack the archive and rename it to `Lattice2`. If you used `git clone` then ignore this step.
3. Move the newly created `Lattice2` directory to where your default FreeCAD install directory is located:
* Windows: (sytem-wide install) `%AppData%\FreeCAD\Mod\Lattice2`
* Windows: (for individual installs)
`C:\Program Files\FreeCAD\Mod\Lattice2`
* Linux: `~/.FreeCAD/Mod/Lattice2`
* MacOS: `~/.FreeCAD/Mod/Lattice2`
3. Restart FreeCAD
**Important Note:** Make sure that `InitGui.py` (and the rest of `.py` files) end up directly under `Mod\Lattice2` directory (**not** under nested directory like `Mod\Lattice2\Lattice2`).
</details>
## Usage
After installing the workbench and restarting FC, Lattice2 should now appear in the workbench dropdown menu. It will be listed down towards the bottom of list. Now, you can familiarize yourself with Lattice2 through the [Basic Tutorial](https://github.com/DeepSOIC/Lattice2/wiki/Basic-Tutorial).
Side Note: If you want to install the workbench for development, `git clone` the repository wherever you like, and make a symlink in where FreeCAD can pick it up as an add-on.
## Status
The workbench is stable. I will take care to not make breaking changes, and some new functionality may keep coming.
If you make your FreeCAD project using Lattice2, all further changes to the project must be done with Lattice2 installed, even if you don't touch the relevant features. Otherwise, the parametric features in the project will lose their bound functionality, and will not recompute, even if you install Lattice2 later. This is the case for all add-ons in FreeCAD, not just Lattice2.
## Getting Help
For Documentation see the [Lattice2 wiki](https://github.com/DeepSOIC/Lattice2/wiki) on Github. As the word "wiki" suggests, you can help by editing the documentation.
If you need help on something specific, you can ask a question on [FreeCAD forum](http://forum.freecadweb.org/) (there is no Lattice forum yet...). You can also ask me directly. **Note:** If you post to the forum, please add this to your post so that I get a notification:
`[quote=DeepSOIC user_id=3888]Ding![/quote]`
## Contributing
If you have found a bug or are requesting a new feature, please first check to see if it has been previously reported already on the [Lattice2 Github repo issue tracker](https://github.com/DeepSOIC/Lattice2/issues). If not, feel free to open a ticket.
If you have fixed a bug or implemented a new feature you think suits the workbench, feel free to make a pull-request on Github.
## License
Lattice workbench is licensed under LGPL V2, just like FreeCAD. For more info, see [copying.lib](copying.lib) file in this repository.

View File

@ -60,9 +60,9 @@ class LatticeArrayFilter(lattice2BaseFeature.LatticeFeature):
obj.addProperty("App::PropertyLink","Stencil","Lattice ArrayFilter","Object that defines filtering")
obj.addProperty("App::PropertyLength","WindowFrom","Lattice ArrayFilter","Elements closer to stencil than this value are rejected by the filter.")
obj.addProperty("App::PropertyLength","WindowFrom","Lattice ArrayFilter","Elements closer to stencil than this vaule are rejected by the filter.")
obj.WindowFrom = 0.0
obj.addProperty("App::PropertyLength","WindowTo","Lattice ArrayFilter","Elements farther from stencil than this value are rejected by the filter.")
obj.addProperty("App::PropertyLength","WindowTo","Lattice ArrayFilter","Elements farther from stencil than this vaule are rejected by the filter.")
obj.WindowTo = 1.0
obj.addProperty("App::PropertyBool","Invert","Lattice ArrayFilter","Output elements that are rejected by filter, instead")
@ -70,6 +70,20 @@ class LatticeArrayFilter(lattice2BaseFeature.LatticeFeature):
obj.Proxy = self
def updateReadonlyness(self, selfobj, bypass_set = set()):
my_hidden = set([
'ReferencePlacement',
'ReferencePlacementLink',
'ReferencePlacementLinkIndex',
])
super(LatticeArrayFilter, self).updateReadonlyness(selfobj, bypass_set | my_hidden)
for prop in my_hidden:
if prop in bypass_set: continue
if hasattr(selfobj, prop):
selfobj.setEditorMode(prop, 2)
def recomputeReferencePlm(self, selfobj, selfplacements):
pass #suppress standard refplm recompute
def derivedExecute(self,obj):
#validity check
@ -128,7 +142,9 @@ class LatticeArrayFilter(lattice2BaseFeature.LatticeFeature):
output.append(input[i])
else:
raise ValueError('Filter mode not implemented:'+obj.FilterType)
self.setReferencePlm(obj, lattice2BaseFeature.getReferencePlm(obj.Base))
return output
@ -188,11 +204,11 @@ def makeItemListFromSelection(sel, bMakeString = True):
def CreateLatticeArrayFilter(name,mode):
sel = FreeCADGui.Selection.getSelectionEx()
# selection order independence logic (lattice object and generic shape stencil can be told apart)
# selection order independece logic (lattice object and generic shape stencil can be told apart)
iLtc = 0 #index of lattice object in selection
iStc = 1 #index of stencil object in selection
for i in range(0,len(sel)):
if lattice2BaseFeature.isObjectLattice(sel[i]):
if lattice2BaseFeature.isObjectLattice(sel[i].Object):
iLtc = i
iStc = i-1 #this may give negative index, but python accepts negative indexes
break
@ -301,7 +317,7 @@ class GroupCommandLatticeArrayFilter:
def GetResources(self):
return { 'MenuText': 'Array filter:',
'ToolTip': 'Array filter: tool to extract specific elements from lattice2 arrays.'}
'ToolTip': 'Array filter: tool to exctract specific elements from lattice2 arrays.'}
def IsActive(self): # optional
return bool(App.ActiveDocument)

View File

@ -40,7 +40,7 @@ EDIT_ATTACHMENT = 56 # Viewprovider edit mode number
def makeAttachablePlacement(name):
'''makeAttachablePlacement(name): makes an attachable Placement object.'''
if Compat.attach_extension_era:
obj = lattice2BaseFeature.makeLatticeFeature(name, AttachablePlacement, ViewProviderAttachablePlacement, no_disable_attacher= True)
obj = lattice2BaseFeature.makeLatticeFeature(name, AttachablePlacement, ViewProviderAttachablePlacement)
else:
#obsolete!
obj = FreeCAD.ActiveDocument.addObject("Part::AttachableObjectPython",name)
@ -53,15 +53,12 @@ def makeAttachablePlacement(name):
class AttachableFeature(lattice2BaseFeature.LatticeFeature):
"Base class for attachable features"
attachable = True
def derivedInit(self,obj):
if Compat.attach_extension_era:
if not obj.hasExtension('Part::AttachExtension'): #PartDesign-related hack: the placement already has attachextension if created in PD
obj.addExtension('Part::AttachExtensionPython', None)
def onDocumentRestored(self, selfobj):
#PartDesign-related hack: this dummy override disables disabling of attacher
pass
class AttachablePlacement(AttachableFeature):
"Attachable Lattice Placement object"
@ -79,9 +76,8 @@ class AttachablePlacement(AttachableFeature):
class ViewProviderAttachableFeature(lattice2BaseFeature.ViewProviderLatticeFeature):
always_edit_attachment = False
def setEdit(self,vobj,mode):
if not (mode == EDIT_ATTACHMENT or (mode == 0 and self.always_edit_attachment)): raise NotImplementedError()
if not (mode == EDIT_ATTACHMENT or (mode == 0 and always_edit_attachment)): raise NotImplementedError()
import PartGui
import FreeCADGui as Gui
PartGui.AttachmentEditor.editAttachment(self.Object,
@ -90,7 +86,7 @@ class ViewProviderAttachableFeature(lattice2BaseFeature.ViewProviderLatticeFeatu
return True
def unsetEdit(self,vobj,mode):
if not (mode == EDIT_ATTACHMENT or (mode == 0 and self.always_edit_attachment)): raise NotImplementedError()
if not (mode == EDIT_ATTACHMENT or (mode == 0 and always_edit_attachment)): raise NotImplementedError()
import FreeCADGui as Gui
Gui.Control.closeDialog()
return True
@ -123,9 +119,26 @@ class AttachedPlacementSubsequence(lattice2BaseFeature.LatticeFeature):
obj.addProperty("App::PropertyLink", "Base", "Lattice Attached Placement Subsequence", "Link to Lattice Attached Placement, which is to be subsequenced.")
obj.addProperty("App::PropertyString", "RefIndexFilter","Lattice Attached Placement Subsequence","Sets which references of attachment to cycle through children. '0000' = no cycle, '1000' = cycle only ref1. '' = cycle all if possible")
obj.addProperty("App::PropertyEnumeration", "CycleMode","Lattice Attached Placement Subsequence", "How to cycle through chidren. Open = advance each link till one reaches the end of array. Periodic = if array end reached, continue from begin if any children left.")
obj.addProperty("App::PropertyEnumeration", "CycleMode","Lattice Attached Placement Subsequence", "How to cycle through children. Open = advance each link till one reaches the end of array. Periodic = if array end reached, continue from begin if any children left.")
obj.CycleMode = ['Open','Periodic']
def assureProperties(self, selfobj, creating_new = False):
super(AttachedPlacementSubsequence, self).assureProperties(selfobj, creating_new)
created = self.assureProperty(selfobj,
'App::PropertyEnumeration',
'ReferencePlacementOption',
['external', 'origin', 'inherit', 'first placement', 'last placement'],
"Lattice Attached Placement Subsequence",
"Reference placement, corresponds to the original occurrence of the object to be populated. 'inherit' = use reference placement of the base attached placement."
)
if created:
selfobj.ReferencePlacementOption = 'inherit'
def recomputeReferencePlm(self, selfobj, selfplacements): #override
if selfobj.ReferencePlacementOption == 'external':
super(AttachedPlacementSubsequence, self).recomputeReferencePlm(selfobj, selfplacements)
#the remaining options are handled in derivedExecute
def derivedExecute(self,obj):
attacher = Part.AttachEngine(screen(obj.Base).AttacherType)
attacher.readParametersFromFeature(screen(obj.Base))
@ -134,10 +147,31 @@ class AttachedPlacementSubsequence(lattice2BaseFeature.LatticeFeature):
sublinks = Subsequencer.Subsequence_auto(attacher.References,
loop= ('Till end' if obj.CycleMode == 'Open' else 'All around'),
index_filter= ifilt)
deref = obj.Base.Placement.inverse()
basearray = [deref.multiply(plm) for plm in lattice2BaseFeature.getPlacementsList(obj.Base)]
plms = []
for lnkval in sublinks:
attacher.References = lnkval
plms.append(attacher.calculateAttachedPlacement(screen(obj.Base).Placement))
attplm = attacher.calculateAttachedPlacement(obj.Base.Placement)
plms.extend([attplm.multiply(plm) for plm in basearray])
#reference
ref = obj.ReferencePlacementOption
if ref == 'external':
pass #gets done in recomputeReferencePlm
elif ref == 'origin':
self.setReferencePlm(obj, None)
elif ref == 'inherit':
self.setReferencePlm(obj, lattice2BaseFeature.getReferencePlm(obj.Base))
elif ref == 'first placement':
self.setReferencePlm(obj, plms[0])
elif ref == 'last placement':
self.setReferencePlm(obj, plms[-1])
else:
raise NotImplementedError("Reference option not implemented: " + ref)
return plms
class ViewProviderAttachedPlacementSubsequence(lattice2BaseFeature.ViewProviderLatticeFeature):
@ -168,11 +202,8 @@ def CreateAttachablePlacement(name):
FreeCAD.ActiveDocument.openTransaction("Create Attachable Placement")
FreeCADGui.addModule("lattice2AttachablePlacement")
FreeCADGui.addModule("lattice2Executer")
FreeCADGui.addModule("lattice2Base.Autosize")
FreeCADGui.addModule("PartGui")
FreeCADGui.doCommand("f = lattice2AttachablePlacement.makeAttachablePlacement(name='"+name+"')")
FreeCADGui.doCommand("f.Placement.Base = lattice2Base.Autosize.convenientPosition()")
FreeCADGui.doCommand("f.MarkerSize = lattice2Base.Autosize.convenientMarkerSize()")
FreeCADGui.doCommand("lattice2Executer.executeFeature(f)")
FreeCADGui.doCommand("lattice2AttachablePlacement.editNewAttachment(f)")
#FreeCAD.ActiveDocument.commitTransaction()
@ -235,7 +266,7 @@ class CommandAttachedPlacementSubsequence:
"\n\nPlease select an attached placement object, first. Then invoke this tool. Adjust the properties of the created object if necessary." )
else:
if len(sel)!=1:
raise SelectionError("PlacementSubsequence", "Please select just one object, an attached placement. You have selected {num}.".format(num= len(sel)))
raise SelectionError("PlacementSubsequence", "Please select just one object, an attached placement. You have seleced {num}.".format(num= len(sel)))
cmdCreateAttachedPlacementSubsequence(name= "PlacementSubsequence")
except Exception as err:
msgError(err)

View File

@ -1,239 +0,0 @@
#***************************************************************************
#* *
#* Copyright (c) 2018 - Victor Titov (DeepSOIC) *
#* <vv.titov@gmail.com> *
#* *
#* This program is free software; you can redistribute it and/or modify *
#* it under the terms of the GNU Lesser General Public License (LGPL) *
#* as published by the Free Software Foundation; either version 2 of *
#* the License, or (at your option) any later version. *
#* for detail see the LICENCE text file. *
#* *
#* This program is distributed in the hope that it will be useful, *
#* but WITHOUT ANY WARRANTY; without even the implied warranty of *
#* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the *
#* GNU Library General Public License for more details. *
#* *
#* You should have received a copy of the GNU Library General Public *
#* License along with this program; if not, write to the Free Software *
#* Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 *
#* USA *
#* *
#***************************************************************************
__title__= "Lattice2 Autosize module"
__author__ = "DeepSOIC"
__url__ = ""
__doc__ = (
"""helper module for Lattice add-on workbench for FreeCAD. Routines used to guess sizes for primitives.
"""
)
from . import Rounder
from . import Containers
import FreeCAD as App
import math
from math import radians
def convenientModelWidth():
"""convenientModelWidth(): returns a size that will conveniently fit in the width of screen"""
return Autosize().convenientModelWidth()
def convenientModelSize():
"""convenientModelSize(): returns a size of a box that will conveniently fit in the screen"""
return Autosize().convenientModelSize()
def minimalSize():
"""minimalSize(): returns a size that will be barely recognizable on the screen (it is a rounded pick radius in model space)"""
return Autosize().minimalSize()
def convenientMarkerSize():
"""convenientMarkerSize(): size of object to be able to comfortably select faces"""
return Autosize().convenientMarkerSize()
def convenientFeatureSize():
"""convenientFeatureSize(): size in between marker size and model size. Should be reasonable to edit, but not fill the whole screen."""
return Autosize().convenientFeatureSize()
def convenientPosition():
return Autosize().convenientPosition()
def getLocalOriginPosition():
ac = Containers.activeContainer()
if ac is None:
return App.Vector()
elif ac.isDerivedFrom('App::Document'): #special case for v0.16
return App.Vector()
else:
return Containers.Container(ac).getFullTransform().Base
def _printTraceback(err):
import traceback
tb = traceback.format_exc()
App.Console.PrintError("Lattice Autosize error: {err}\n{tb}\n\n".format(err= str(err), tb= tb))
class ViewportInfo(object):
camera_type = 'perspective' #string: perspective or orthographic
camera_placement = App.Placement(App.Vector(0,0,1), App.Rotation())
camera_focalplacement = App.Placement()
camera_focaldist = 1.0
camera_heightangle = radians(45) #total horizontal view angle, in radians (for perspective camera)
camera_height = 1 #screen height in model space (mm), for orthographic camera
viewport_size_px = (1800,1000) #width, height of viewport, in pixels
viewport_size_mm = (1.8,1.0) #width, height of viewport (on focal plane), in mm
mm_per_px = 1.0 / 1000.0 #rough mm-to-pixel ratio (accurate on focal plane)
false_viewport = False # if true, no actual viewport was queried (e.g. non-gui mode, or activeview is non-3d)
pickradius_px = 5
pickradius_mm = 0.1
def __init__(self, viewer = None):
try:
if not(App.GuiUp or viewer is not None):
return
if viewer == None:
import FreeCADGui as Gui
viewer = Gui.ActiveDocument.ActiveView
if not hasattr(viewer, 'getCameraNode'):
return
import pivy
cam = viewer.getCameraNode()
self.camera_type = 'perspective' if isinstance(cam, pivy.coin.SoPerspectiveCamera) else 'orthographic'
self.camera_placement = App.Placement(
App.Vector(cam.position.getValue().getValue()),
App.Rotation(*cam.orientation.getValue().getValue())
)
self.camera_focaldist = cam.focalDistance.getValue()
self.camera_focalplacement = self.camera_placement.multiply(App.Placement(App.Vector(0,0,-self.camera_focaldist), App.Rotation()))
if self.camera_type == 'perspective':
self.camera_heightangle = cam.heightAngle.getValue()
self.camera_height = math.tan(self.camera_heightangle / 2) * self.camera_focaldist * 2
else:
self.camera_height = cam.height.getValue()
self.camera_focaldist = self.camera_height / 2 / math.tan(radians(45)/2) #in parallel projection, focal distance has strange values. Reconstructing a focal distance for a typical perspective camera...
self.false_viewport = False
rman = viewer.getViewer().getSoRenderManager()
self.viewport_size_px = tuple(rman.getWindowSize())
mmppx = self.camera_height/self.viewport_size_px[1]
self.mm_per_px = mmppx
self.viewport_size_mm = (self.viewport_size_px[0]*mmppx, self.viewport_size_px[1]*mmppx)
self.pickradius_px = App.ParamGet("User parameter:BaseApp/Preferences/View").GetFloat("PickRadius", 5.0)
self.pickradius_mm = self.pickradius_px * mmppx
except Exception as err:
import traceback
tb = traceback.format_exc()
App.Console.PrintError("Lattice Autosize: failed to query viewport: {err}\n{tb}\n\n".format(err= str(err), tb= tb))
class Autosize(ViewportInfo):
convenient_model_size_multiplier = 0.4
def __init__(self, viewer = None):
super(Autosize, self).__init__(viewer)
def convenientModelWidth(self):
"""convenientModelWidth(): returns a size that will conveniently fit in the width of screen"""
try:
return Rounder.roundToNiceValue(self._convenientModelWidth())
except Exception as err:
_printTraceback(err)
return 10.0
def convenientModelSize(self):
"""convenientModelSize(): returns a size of a box that will conveniently fit in the screen"""
try:
return Rounder.roundToNiceValue(self._convenientModelSize())
except Exception as err:
_printTraceback(err)
return 10.0
def minimalSize(self):
"""minimalSize(): returns a size that will be barely recognizable on the screen (it is a rounded pick radius in model space)"""
try:
return Rounder.roundToNiceValue(self._minimalSize())
except Exception as err:
_printTraceback(err)
return 0.1
def convenientMarkerSize(self):
"""convenientMarkerSize(): size of object to be able to comfortably select faces"""
try:
return Rounder.roundToNiceValue(self._convenientMarkerSize())
except Exception as err:
_printTraceback(err)
return 1.0
def convenientFeatureSize(self):
"""convenientFeatureSize(): size in between marker size and model size. Should be reasonable to edit, but not fill the whole screen."""
try:
return Rounder.roundToNiceValue(self._convenientFeatureSize())
except Exception as err:
_printTraceback(err)
return 5.0
def convenientPosition(self):
try:
if self.isPointInWorkingArea(getLocalOriginPosition()):
return App.Vector()
else:
roundfocal = Rounder.roundToNiceValue(self.camera_focaldist*0.5)
result = App.Vector(
[Rounder.roundToPrecision(coord, roundfocal) for coord in tuple(self.camera_focalplacement.Base)]
)
return result
except Exception as err:
_printTraceback(err)
return App.Vector()
def _convenientModelWidth(self):
if self.false_viewport:
return 10.0
else:
return self.viewport_size_mm[0] * self.convenient_model_size_multiplier
def _convenientModelSize(self):
"""_convenientMarkerSize(): (unrounded) returns size of an object that would fill most of the working area"""
if self.false_viewport:
return 10.0
else:
return min(self.viewport_size_mm[0], self.viewport_size_mm[1]) * self.convenient_model_size_multiplier
def _minimalSize(self):
"""_minimalSize(): (unrounded) returns minimum object size that can be seen on screen on focal plane"""
if self.false_viewport:
return 0.1
else:
return self.pickradius_mm
def _convenientMarkerSize(self):
"""_convenientMarkerSize(): (unrounded) returns maker size that is usefully large to select faces and understand its rotation"""
if self is None:
self = ViewportInfo()
if self.false_viewport:
return 1.0
else:
return self.pickradius_mm * 10
def _convenientFeatureSize(self):
"""_convenientMarkerSize(): (unrounded) returns size of an object that would fill most of the working area"""
if self is None:
self = ViewportInfo()
return math.sqrt(self._convenientModelSize() * self._convenientMarkerSize())
def isPointInWorkingArea(self, point = App.Vector()):
"""isPointInWorkingArea(): returns True if point is not far from the visible area of focal plane. Point should be given in document coordinate system."""
p_foc = self.camera_focalplacement.inverse().multVec(point)
#p_foc is point in focal-plane CS. X and Y are along focal plane. Z is against view direction (positive = towards the camera).
mheight = self.viewport_size_mm[1]*0.8
mwidth = self.viewport_size_mm[0]*0.8
f = self.camera_focaldist
if abs(p_foc.x) > mwidth*0.5 or abs(p_foc.y) > mheight*0.5 or p_foc.z > f*0.5 or p_foc.z < -2*f:
return False
else:
return True

View File

@ -1,278 +0,0 @@
#/***************************************************************************
# * Copyright (c) Victor Titov (DeepSOIC) *
# * (vv.titov@gmail.com) 2018 *
# * *
# * This file is part of the FreeCAD CAx development system. *
# * *
# * This library is free software; you can redistribute it and/or *
# * modify it under the terms of the GNU Library General Public *
# * License as published by the Free Software Foundation; either *
# * version 2 of the License, or (at your option) any later version. *
# * *
# * This library is distributed in the hope that it will be useful, *
# * but WITHOUT ANY WARRANTY; without even the implied warranty of *
# * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the *
# * GNU Library General Public License for more details. *
# * *
# * You should have received a copy of the GNU Library General Public *
# * License along with this library; see the file COPYING.LIB. If not, *
# * write to the Free Software Foundation, Inc., 59 Temple Place, *
# * Suite 330, Boston, MA 02111-1307, USA *
# * *
# ***************************************************************************/
#This is a temporary replacement for C++-powered Container class that should be eventually introduced into FreeCAD
import FreeCAD as App
class Container(object):
"""Container class: a unified interface for container objects, such as Group, Part, Body, or Document.
This is a temporary implementation."""
Object = None #DocumentObject or Document, the actual container
def __init__(self, obj):
self.Object = obj
def self_check(self):
if self.Object is None:
raise ValueError("Null!")
if not isAContainer(self.Object):
raise NotAContainerError(self.Object)
def getAllChildren(self):
"""Returns all objects directly contained by the container. all = static + dynamic."""
return self.getStaticChildren() + self.getDynamicChildren()
def getStaticChildren(self):
"""Returns children tightly bound to the container, such as Origin. The key thing
about them is that they are not supposed to be removed or added from/to the container."""
self.self_check()
container = self.Object
if container.isDerivedFrom('App::Document'):
return []
elif container.hasExtension('App::OriginGroupExtension'):
if container.Origin is not None:
return [container.Origin]
else:
return []
elif container.isDerivedFrom('App::Origin'):
return container.OriginFeatures
elif container.hasExtension('App::GroupExtension'):
return []
raise RuntimeError("getStaticChildren: unexpected container type!")
def getDynamicChildren(self):
"""Returns dynamic children, i.e. the stuff that can be removed from the container."""
self.self_check()
container = self.Object
if container.isDerivedFrom('App::Document'):
# find all objects not contained by any Part or Body
result = set(container.Objects)
for obj in container.Objects:
if isAContainer(obj):
children = set(Container(obj).getAllChildren())
result = result - children
return list(result)
elif container.hasExtension('App::GroupExtension'):
result = container.Group
if container.hasExtension('App::GeoFeatureGroupExtension'):
#geofeaturegroup's group contains all objects within the CS, we don't want that
result = [obj for obj in result if obj.getParentGroup() is not container]
return result
elif container.isDerivedFrom('App::Origin'):
return []
raise RuntimeError("getDynamicChildren: unexpected container type!")
def isACS(self):
"""isACS(): returns true if the container forms internal coordinate system."""
self.self_check()
container = self.Object
if container.isDerivedFrom('App::Document'):
return True #Document is a special thing... is it a CS or not is a matter of coding convenience.
elif container.hasExtension('App::GeoFeatureGroupExtension'):
return True
else:
return False
def isAVisGroup(self):
"""isAVisGroup(): returns True if the container consumes viewproviders of children, and thus affects their visibility."""
self.self_check()
container = self.Object
if container.isDerivedFrom('App::Document'):
return True #Document is a special thing... Return value is a matter of coding convenience.
elif container.hasExtension('App::GeoFeatureGroupExtension'):
return True
elif container.isDerivedFrom('App::Origin'):
return True
else:
return False
def getCSChildren(self):
if not self.isACS():
raise TypeError("Container is not a coordinate system")
container = self.Object
return _getMetacontainerChildren(self, Container.isACS)
def getVisGroupChildren(self):
if not self.isAVisGroup():
raise TypeError("Container is not a visibility group")
container = self.Object
return _getMetacontainerChildren(self, Container.isAVisGroup)
def hasObject(self, obj):
"""Returns True if the container contains specified object directly."""
return obj in self.getAllChildren()
def hasObjectRecursive(self, obj):
return self.Object in ContainerChain(obj)
def Placement(self):
if self.isACS():
if hasattr(self.Object, 'Placement'):
return self.Object.Placement
return App.Placement()
def getFullTransform(self):
"""getFullTransform(): returns Placement that converts coordinates of objects in this container to global CS."""
chain = ContainerChain(self.Object) + [self.Object]
plm = App.Placement()
for cnt in chain:
plm = plm.multiply(Container(cnt).Placement())
return plm
def _getMetacontainerChildren(container, isrightcontainer_func):
"""Gathers up children of metacontainer - a container structure formed by containers of specific type.
For example, coordinate systems form a kind of container structure.
container: instance of Container class
isrightcontainer_func: a function f(cnt)->bool, where cnt is a Container object."""
result = []
list_traversing_now = [container] #list of Container instances
list_to_be_traversed_next = [] #list of Container instances
visited_containers = set([container.Object]) #set of DocumentObjects
while len(list_traversing_now) > 0:
list_to_be_traversed_next = []
for itcnt in list_traversing_now:
children = itcnt.getAllChildren()
result.extend(children)
for child in children:
if isAContainer(child):
newcnt = Container(child)
if not isrightcontainer_func(newcnt):
list_to_be_traversed_next.append(newcnt)
list_traversing_now = list_to_be_traversed_next
return result
def isAContainer(obj):
'''isAContainer(obj): returns True if obj is an object container, such as
Group, Part, Body. The important characteristic of an object being a
container is that it can be activated to receive new objects. Documents
are considered containers, too.'''
if obj.isDerivedFrom('App::Document'):
return True
if obj.hasExtension('App::GroupExtension'):
return True
if obj.isDerivedFrom('App::Origin'):
return True
return False
#from Part-o-magic...
def ContainerOf(obj):
"""ContainerOf(obj): returns the container that immediately has obj."""
cnt = None
for dep in obj.InList:
if isAContainer(dep):
if Container(dep).hasObject(obj):
if cnt is not None and dep is not cnt:
raise ContainerTreeError("Container tree is not a tree")
cnt = dep
if cnt is None:
return obj.Document
return cnt
def getVisGroupOf(obj):
chain = VisGroupChain(obj)
return chain[-1]
#from Part-o-magic... over-engineered, but proven to work
def ContainerChain(feat):
'''ContainerChain(feat): container path to feat (not including feat itself).
Last container directly contains the feature.
Example of output: [<document>,<SuperPart>,<Part>,<Body>]'''
if feat.isDerivedFrom('App::Document'):
return []
list_traversing_now = [feat]
set_of_deps = set()
list_of_deps = []
while len(list_traversing_now) > 0:
list_to_be_traversed_next = []
for feat in list_traversing_now:
for dep in feat.InList:
if isAContainer(dep) and Container(dep).hasObject(feat):
if not (dep in set_of_deps):
set_of_deps.add(dep)
list_of_deps.append(dep)
list_to_be_traversed_next.append(dep)
if len(list_to_be_traversed_next) > 1:
raise ContainerTreeError("Container tree is not a tree")
list_traversing_now = list_to_be_traversed_next
return [feat.Document] + list_of_deps[::-1]
def CSChain(feat):
cnt_chain = ContainerChain(feat)
return [cnt for cnt in cnt_chain if Container(cnt).isACS()]
def VisGroupChain(feat):
cnt_chain = ContainerChain(feat)
return [cnt for cnt in cnt_chain if Container(cnt).isAVisGroup()]
def activeContainer():
'''activeContainer(): returns active container.
If there is an active body, it is returned as active container. ActivePart is ignored.
If there is no active body, active Part is returned.
If there is no active Part either, active Document is returned.
If no active document, None is returned.'''
import FreeCAD as App
import FreeCADGui as Gui
if hasattr(App, "ActiveContainer"):
return App.ActiveContainer.Object
if Gui.ActiveDocument is None:
return None
vw = Gui.ActiveDocument.ActiveView
if vw is None:
return None
if not hasattr(vw, 'getActiveObject'): #v0.16
return App.ActiveDocument
activeBody = vw.getActiveObject("pdbody")
activePart = vw.getActiveObject("part")
if activeBody:
return activeBody
elif activePart:
return activePart
else:
return App.ActiveDocument
class ContainerError(RuntimeError):
pass
class NotAContainerError(ContainerError):
def __init__(self):
ContainerError.__init__(self, u"{obj} is not recognized as container".format(obj.Name))
class ContainerTreeError(ContainerError):
pass

View File

@ -1,106 +0,0 @@
#***************************************************************************
#* *
#* Copyright (c) 2018 - Victor Titov (DeepSOIC) *
#* <vv.titov@gmail.com> *
#* *
#* This program is free software; you can redistribute it and/or modify *
#* it under the terms of the GNU Lesser General Public License (LGPL) *
#* as published by the Free Software Foundation; either version 2 of *
#* the License, or (at your option) any later version. *
#* for detail see the LICENCE text file. *
#* *
#* This program is distributed in the hope that it will be useful, *
#* but WITHOUT ANY WARRANTY; without even the implied warranty of *
#* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the *
#* GNU Library General Public License for more details. *
#* *
#* You should have received a copy of the GNU Library General Public *
#* License along with this program; if not, write to the Free Software *
#* Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 *
#* USA *
#* *
#***************************************************************************
__title__= "Lattice2 rounder module"
__author__ = "DeepSOIC"
__url__ = ""
__doc__ = "helper module for Lattice add-on workbench for FreeCAD. Provides special rounding routines."
import math
from math import log
# copied from FreeCAD /src/Base/UnitsSchema.h, enum UnitSystem
class UnitSystem(object):
SI1 = 0 #, /** internal (mm,kg,s) SI system (http://en.wikipedia.org/wiki/International_System_of_Units) */
SI2 = 1 #, /** MKS (m,kg,s) SI system */
Imperial1 = 2 #, /** the Imperial system (http://en.wikipedia.org/wiki/Imperial_units) */
ImperialDecimal = 3 #, /** Imperial with length in inch only */
Centimeters = 4 #, /** All lengths in centimeters, areas and volumes in square/cubic meters */
ImperialBuilding = 5 #, /** All lengths in feet + inches + fractions */
MmMin = 6 #, /** Lengths in mm, Speed in mm/min. Angle in degrees. Useful for small parts & CNC */
@staticmethod
def getActiveSchema():
import FreeCAD as App
return App.ParamGet("User parameter:BaseApp/Preferences/Units").GetInt("UserSchema", 0)
def getNiceLengths(unitschema = None):
if unitschema is None:
unitschema = UnitSystem.getActiveSchema()
if unitschema == UnitSystem.SI1 or unitschema == UnitSystem.SI2 or unitschema == UnitSystem.Centimeters or unitschema == UnitSystem.MmMin:
nice_numbers = [1.0, 2.0, 5.0]
nice_magnitudes = []
for degree in range(-3,6):
order = 10.0 ** degree
nice_magnitudes.extend([order * val for val in nice_numbers])
return nice_magnitudes
elif unitschema == UnitSystem.Imperial1 or unitschema == UnitSystem.ImperialBuilding or unitschema == UnitSystem.ImperialDecimal:
inch = 25.4
foot = 304.8
yard = 914.4
mile = 1609344.0
#https://forum.freecadweb.org/viewtopic.php?f=8&t=32565#p271923
#.005" .010", .025", .050", .100", .250", 1", 6", 1', 8', 50', 100', 500', 1000', 2500', 1 mile, 10 miles, 100 miles
return [
0.005*inch,
0.010*inch, 0.025*inch, 0.050*inch,
0.10*inch, 0.25*inch, 0.50*inch,
1*inch, 2*inch, 6*inch,
1*foot, 2*foot, 4*foot, 8*foot,
16*foot, 32*foot, 50*foot,
100*foot, 250*foot, 500*foot,
1000*foot, 2500*foot,
1*mile, 2*mile, 5*mile,
10*mile, 25*mile, 50*mile,
100*mile
]
else:
#unit unsupported? fall back to metric
import FreeCAD as App
App.PrintWarning("Lattice Autosize: Unit schema {n} is not yet supported.\n".format(n= unitschema))
return getNiceLengths(UnitSystem.SI1)
def roundToNiceValue(value, nice_value_list = None):
if value == 0.0:
return 0.0
if nice_value_list is None:
nice_value_list = getNiceLengths()
bestmatch_logdist = log(1e10)
bestmatch = None
for nice_val in nice_value_list:
logdist = abs(log(abs(value)) - log(nice_val))
if logdist < bestmatch_logdist:
bestmatch_logdist = logdist
bestmatch = nice_val
return math.copysign(bestmatch, value)
def roundToPrecision(value, precision):
if precision < 1e-12:
return value
return round(value/precision)*precision

View File

@ -33,6 +33,7 @@ import lattice2CompoundExplorer as LCE
import lattice2Markers
import lattice2Executer
from lattice2ShapeCopy import shallowCopy
import lattice2CoinGlue as CoinGlue
def getDefLatticeFaceColor():
@ -49,11 +50,10 @@ def getDefShapeColor():
return (r/255.0, g/255.0, b/255.0, (255-o)/255.0)
def makeLatticeFeature(name, AppClass, ViewClass, no_body = False, no_disable_attacher = False):
def makeLatticeFeature(name, AppClass, ViewClass, no_body = False):
'''makeLatticeFeature(name, AppClass, ViewClass, no_body = False): makes a document object for a LatticeFeature-derived object.
no_body: if False, the Lattice object will end up in an active body, and Part2DObject will be used.
no_disable_attacher: if True, attachment properties of Part2DObject won't be hidden'''
no_body: if False, the Lattice object will end up in an active body, and Part2DObject will be used.'''
body = activeBody()
if body and not no_body:
@ -73,17 +73,16 @@ def makeLatticeFeature(name, AppClass, ViewClass, no_body = False, no_disable_at
def isObjectLattice(documentObject):
'''isObjectLattice(documentObject): When operating on the object, it is to be treated as a lattice object. If False, treat as a regular shape.'''
transform, src = source(documentObject)
ret = False
if hasattr(documentObject,"isLattice"):
if 'On' in documentObject.isLattice:
if hasattr(src,'isLattice'):
if 'On' in src.isLattice:
ret = True
if documentObject.isDerivedFrom('App::Placement') or documentObject.isDerivedFrom('PartDesign::CoordinateSystem'):
ret = True
if documentObject.isDerivedFrom('PartDesign::ShapeBinder'):
if len(documentObject.Support) == 1 and documentObject.Support[0][1] == ('',):
ret = isObjectLattice(documentObject.Support[0][0])
if hasattr(documentObject, 'IAm') and documentObject.IAm == 'PartOMagic.Ghost':
ret = isObjectLattice(documentObject.Base)
#if documentObject.isDerivedFrom('PartDesign::ShapeBinder'):
# if len(documentObject.Support) == 1 and documentObject.Support[0][1] == ('',):
# ret = isObjectLattice(documentObject.Support[0][0])
#if hasattr(documentObject, 'IAm') and documentObject.IAm == 'PartOMagic.Ghost':
# ret = isObjectLattice(documentObject.Base)
return ret
def getMarkerSizeEstimate(ListOfPlacements):
@ -105,13 +104,15 @@ def getMarkerSizeEstimate(ListOfPlacements):
class LatticeFeature(object):
"Base object for lattice objects (arrays of placements)"
attachable = False
def __init__(self,obj):
# please, don't override. Override derivedInit instead.
obj.addProperty('App::PropertyString', 'Type', "Lattice", "module_name.class_name of this object, for proxy recovery", 0, True, True)
obj.Type = self.__module__ + '.' + type(self).__name__
prop = "NumElements"
obj.addProperty("App::PropertyInteger",prop,"Lattice","Info: number of placements in the array")
obj.addProperty("App::PropertyInteger",prop,"Lattice","Info: number of placements in the array", 0, True)
obj.setEditorMode(prop, 1) # set read-only
obj.addProperty("App::PropertyLength","MarkerSize","Lattice","Size of placement markers (set to zero for automatic).")
@ -125,25 +126,115 @@ class LatticeFeature(object):
# Auto-On an Auto-Off can be modified when recomputing. Force values are going to stay.
prop = "ExposePlacement"
obj.addProperty("App::PropertyBool",prop,"Lattice","Makes the placement synchronized to Placement property. This will often make this object unmovable. Not applicable to arrays.")
obj.addProperty("App::PropertyBool",prop,"Lattice","Makes the placement syncronized to Placement property. This will often make this object unmoveable. Not applicable to arrays.")
self.derivedInit(obj)
self.assureProperties(obj, creating_new= True)
self.updateReadonlyness(obj)
if not self.attachable:
self.disableAttacher(obj)
obj.Proxy = self
def assureProperties(self, selfobj, creating_new = False):
"""#overrideme Method to reconstruct missing properties, that appeared as new functionality was introduced.
Auto called from __init__ (and before derivedInit), and from execute (before derivedExecute)."""
self.assureProperty(selfobj, 'App::PropertyLink', 'ReferencePlacementLink', None, "Lattice", "Link to placement to use as reference placement")
self.assureProperty(selfobj, 'App::PropertyString', 'ReferencePlacementLinkIndex', None, "Lattice", "Index of placement to take from the link. Can also be 'self.0' for own placements.")
self.assureProperty(selfobj, 'App::PropertyBool', 'ReferencePlacementInGlobal', True, "Lattice", "True if reference placement property is in global cs. ", readonly= True)
self.assureProperty(
selfobj,
'App::PropertyPlacement',
'ReferencePlacement',
None,
"Lattice",
"Reference placement, used by 'Populate: build array'. For it, all placements in this array are reinterpreted as relative to this one.",
readonly= True
)
def updateReadonlyness(self, selfobj, bypass_set = set()):
is_lattice = isObjectLattice(selfobj)
extref = 0
if hasattr(selfobj, 'ReferencePlacementOption'):
extref = 0 if selfobj.ReferencePlacementOption == 'external' else 1
rodict = {
'NumElements': 1,
'MarkerSize': 0,
'MarkerShape': 0,
'ReferencePlacement': 1,
'ReferencePlacementLink': extref,
'ReferencePlacementLinkIndex': extref,
'ReferencePlacementInGlobal': 1,
}
for prop in rodict:
if prop in bypass_set: continue
if hasattr(selfobj, prop):
selfobj.setEditorMode(prop, rodict[prop] if is_lattice else 2)
def assureProperty(self, selfobj, proptype, propname, defvalue, group, tooltip):
def assureProperty(self, selfobj, proptype, propname, defvalue, group, tooltip, readonly = False, hidden = False):
"""assureProperty(selfobj, proptype, propname, defvalue, group, tooltip): adds
a property if one is missing, and sets its value to default. Does nothing if property
already exists. Returns True if property was created, or False if not."""
return assureProperty(selfobj, proptype, propname, defvalue, group, tooltip)
return assureProperty(selfobj, proptype, propname, defvalue, group, tooltip, readonly, hidden)
def setReferencePlm(self, selfobj, refplm, in_global = False):
"""setReferencePlm(selfobj, refplm, in_global = False): sets reference placement, in internal CS."""
attr = 'ReferencePlacement'
if refplm is None:
refplm = App.Placement()
in_global = True
if selfobj.ExposePlacement:
in_global = True
selfobj.ReferencePlacementInGlobal = in_global
selfobj.ReferencePlacement = refplm
def getReferencePlm(self, selfobj, in_global = False):
"""getReferencePlm(self, selfobj): Returns reference placement in internal CS, or in global CS."""
if not isObjectLattice(selfobj):
return None
if not hasattr(selfobj, 'ReferencePlacement'):
return App.Placement() if in_global else selfobj.Placement.inverse()
if in_global == selfobj.ReferencePlacementInGlobal:
return selfobj.ReferencePlacement
elif in_global == True and selfobj.ReferencePlacementInGlobal == False:
#goal: return == selfobj.Placement * refplm
return selfobj.Placement.multiply(selfobj.ReferencePlacement)
elif in_global == False and selfobj.ReferencePlacementInGlobal == True:
#goal: self.Placement * return == refplm
return selfobj.Placement.inverse().multiply(selfobj.ReferencePlacement)
def recomputeReferencePlm(self, selfobj, selfplacements):
lnk = selfobj.ReferencePlacementLink
strindex = selfobj.ReferencePlacementLinkIndex
is_selfref = lnk is None and strindex.startswith('self.')
ref = selfobj if is_selfref else lnk
if ref is None:
self.setReferencePlm(selfobj, None)
else:
if is_selfref:
index = int(strindex[len('self.'):])
elif len(strindex)>0:
index = int(strindex)
else:
index = 0
if is_selfref:
refplm = selfplacements[index]
self.setReferencePlm(selfobj, refplm, in_global= False)
else:
refplm = getPlacementsList(ref)[index]
self.setReferencePlm(selfobj, refplm, in_global= True)
def derivedInit(self, obj):
'''for overriding by derived classes'''
pass
def execute(self,obj):
# please, don't override. Override derivedExecute instead.
self.assureProperties(obj)
plms = self.derivedExecute(obj)
@ -156,6 +247,8 @@ class LatticeFeature(object):
if markerSize < DistConfusion:
markerSize = getMarkerSizeEstimate(plms)
marker = lattice2Markers.getPlacementMarker(scale= markerSize, markerID= obj.MarkerShape)
self.assureProperty(obj, 'App::PropertyLength', 'MarkerSizeActual', markerSize, "Lattice", "Size of placement markers of this array", hidden= True)
obj.MarkerSizeActual = markerSize
bExposing = False
if obj.ExposePlacement:
@ -185,6 +278,7 @@ class LatticeFeature(object):
if obj.isLattice == 'Auto-Off':
obj.isLattice = 'Auto-On'
self.recomputeReferencePlm(obj, plms)
else:
# DerivedExecute didn't return anything. Thus we assume it
# has assigned the shape, and thus we don't do anything.
@ -192,6 +286,7 @@ class LatticeFeature(object):
if obj.isLattice == 'Auto-On':
obj.isLattice = 'Auto-Off'
# i don't remember, wtf is going on here...
if obj.ExposePlacement:
if obj.Shape.ShapeType == "Compound":
children = obj.Shape.childShapes()
@ -203,7 +298,7 @@ class LatticeFeature(object):
else:
#nothing to do - FreeCAD will take care to make obj.Placement and obj.Shape.Placement synchronized.
pass
return
self.updateReadonlyness(obj)
def derivedExecute(self,obj):
'''For overriding by derived class. If this returns a list of placements,
@ -264,13 +359,36 @@ class LatticeFeature(object):
selfobj.MapMode = selfobj.MapMode #trigger attachment, to make it update property states
def onDocumentRestored(self, selfobj):
#override to have attachment!
self.disableAttacher(selfobj)
if not self.attachable:
self.disableAttacher(selfobj)
self.assureProperties(selfobj)
self.updateReadonlyness(selfobj)
class ViewProviderLatticeFeature(object):
"A View Provider for base lattice object"
Object = None # documentobject the vp is attached to
ViewObject = None # viewprovider this proxy is attached to
#coin graph:
# transform #in sync with Placement
# coordinate3 #coordinates for main shape rendering
# switch: #main display mode switch, == self.modenode
# ...
# separator: #reference placement related stuff, == self.refplm_node
# transform #reference placement, == self.refplm_tr
# separator: #actual shape of reference placement, == self.refplm_sh
# ...
# switch: #mode switch of reference placement, == self.modenode_refplm
# ...
modenode = None # main displaymode switch node
refplm_node = None # the node containing everything related to reference placement
refplm_tr = None #transform node of reference placement
refplm_sh = None #node containing the shape of reference placement
modenode_refplm = None # displaymode switch node for reference placement
def __init__(self,vobj):
'''Don't override. Override derivedInit, please!'''
vobj.Proxy = self
@ -293,13 +411,44 @@ class ViewProviderLatticeFeature(object):
FreeCAD.Console.PrintError("__init__() of lattice object view provider is overridden. Please don't! Fix it!\n")
except AttributeError as err:
pass # quick-n-dirty fix for Py3. TODO: restore the functionality in Py3, or remove this routine altogether.
def fixProxy(self, vobj):
if vobj is not self.ViewObject:
vobj.Proxy = vobj.ProxyBackup
raise RuntimeError('fixing broken proxy...')
def getIcon(self):
return getIconPath("Lattice.svg")
def attach(self, vobj):
self.ViewObject = vobj
self.Object = vobj.Object
try:
vobj.ProxyBackup = self
except Exception:
vobj.addProperty('App::PropertyPythonObject', 'ProxyBackup', 'Base', "helper property for workaround FC bug #3564", 0, False, True)
vobj.ProxyBackup = self
self.makeRefplmVisual(vobj)
from pivy import coin
self.modenode = next((node for node in vobj.RootNode.getChildren() if node.isOfType(coin.SoSwitch.getClassTypeId())))
def makeRefplmVisual(self, vobj):
import pivy
from pivy import coin
refplm = self.Object.Proxy.getReferencePlm(self.Object)
if refplm is not None:
if not hasattr(self.Object, 'MarkerSizeActual'): return
if self.refplm_node is None:
self.refplm_node, self.refplm_tr, self.refplm_sh = lattice2Markers.getRefPlmMarker(self.Object.MarkerShape)
vobj.RootNode.addChild(self.refplm_node)
self.modenode_refplm = next((node for node in self.refplm_sh.getChildren() if node.isOfType(coin.SoSwitch.getClassTypeId())))
CoinGlue.cointransform(refplm, float(self.Object.MarkerSizeActual) * 1.1, self.refplm_tr)
else:
if hasattr(self, 'refplm_node') and self.refplm_node is not None:
vobj.RootNode.removeChild(self.refplm_node)
self.refplm_node, self.refplm_tr, self.refplm_sh = None, None, None
self.modenode_refplm = None
def __getstate__(self):
return None
@ -326,10 +475,21 @@ class ViewProviderLatticeFeature(object):
# catch all exceptions, because we don't want to prevent deletion if something goes wrong
FreeCAD.Console.PrintError("Error in onDelete: " + str(err))
return True
def updateData(self, obj, prop):
if prop in ['ReferencePlacement', 'MarkerSizeActual', 'Placement', 'ReferencePlacementInGlobal', 'isLattice']:
self.fixProxy(obj.ViewObject)
self.makeRefplmVisual(obj.ViewObject)
def onChanged(self, vobj, prop):
if prop == 'Visibility':
if self.modenode_refplm is not None:
self.modenode_refplm.whichChild.setValue(0 if vobj.Visibility == True else -1)
def assureProperty(docobj, proptype, propname, defvalue, group, tooltip):
"""assureProperty(docobj, proptype, propname, defvalue, group, tooltip): adds
def assureProperty(docobj, proptype, propname, defvalue, group, tooltip, readonly = False, hidden = False):
"""assureProperty(docobj, proptype, propname, defvalue, group, tooltip, readonly = False, hidden = False): adds
a property if one is missing, and sets its value to default. Does nothing if property
already exists. Returns True if property was created, or False if not."""
@ -337,7 +497,7 @@ def assureProperty(docobj, proptype, propname, defvalue, group, tooltip):
#todo: check type match
return False
docobj.addProperty(proptype, propname, group, tooltip)
docobj.addProperty(proptype, propname, group, tooltip, 0, readonly, hidden)
if defvalue is not None:
setattr(docobj, propname, defvalue)
return True
@ -350,23 +510,60 @@ def makeMoveFromTo(plmFrom, plmTo):
from one placement to another placement'''
return plmTo.multiply(plmFrom.inverse())
def getPlacementsList(documentObject, context = None, suppressWarning = False):
def getPlacementsList(documentObject, context = None, suppressWarning = False, dereferenced = False, torefplm = App.Placement()):
'''getPlacementsList(documentObject, context = None): extract list of placements
from an array object. Context is an object to report as context, when displaying
a warning if the documentObject happens to be a non-lattice.'''
if not isObjectLattice(documentObject):
if not suppressWarning:
lattice2Executer.warning(context, documentObject.Name + " is not a placement or an array of placements. Results may be unexpected.")
if documentObject.isDerivedFrom('App::Placement') or documentObject.isDerivedFrom('PartDesign::CoordinateSystem'):
return [documentObject.Placement]
leaves = LCE.AllLeaves(documentObject.Shape)
return [leaf.Placement for leaf in leaves]
if not dereferenced:
return [leaf.Placement for leaf in leaves]
else:
# goal: refplm * derefplm == it
# return = torefplm * derefplm
# => return = torefplm * refplm.inverse * it
operator = torefplm.multiply(getReferencePlm(documentObject).inverse())
return [operator.multiply(leaf.Placement) for leaf in leaves]
def getReferencePlm(feature):
"""Obtains reference placement, in container's CS (includes feature.Placement)."""
transform, src = source(feature)
if not isObjectLattice(src):
raise TypeError('getReferencePlm: array of placements expected, got something else.')
return transform.multiply(src.Proxy.getReferencePlm(src))
def source(feature):
"""source(feature): finds the original Lattice array feature from behind shapebinders and such. Returns (transform, lattice_feature).
transform: placement that converts feature's local coordinates into global.
lattice_feature may not actually be an array of placements."""
def _source(feature, visitset):
if hasattr(feature,'isLattice'):
return (feature.Placement, feature)
if feature in visitset:
raise RuntimeError("Dependency loop!")
visitset.append(feature)
if feature.isDerivedFrom('PartDesign::ShapeBinder'):
if len(feature.Support) == 1 and feature.Support[0][1] == ('',):
base = feature.Support[0][0]
transform1, src = _source(base, visitset)
transform = feature.Placement.multiply(base.Placement.inverse().multiply(transform1))
return (transform, src)
if hasattr(feature, 'IAm') and feature.IAm == 'PartOMagic.Ghost':
base = feature.Base
transform1, src = _source(base, visitset)
transform = feature.Placement.multiply(base.Placement.inverse().multiply(transform1))
return (transform, src)
return (feature.Placement, feature)
return _source(feature, list())
def splitSelection(sel):
'''splitSelection(sel): splits sel (use getSelectionEx()) into lattices and non-lattices.
returns a tuple: (lattices, shapes). lattices is a list, containing all objects
that are lattices (placements of arrays of placements). shapes contains all
the rest. The lists contain SelectionObjects, not the actual document objects.'''
the rest. The lists conain SelectionObjects, not the actual document objects.'''
lattices = []
shapes = []
for selobj in sel:
@ -375,3 +572,4 @@ def splitSelection(sel):
else:
shapes.append(selobj)
return (lattices, shapes)

View File

@ -113,8 +113,8 @@ class _BoundBox:
obj.CompoundTraversal = ["Use as a whole","Direct children only","Recursive"]
obj.CompoundTraversal = "Use as a whole"
obj.addProperty("App::PropertyBool","Precision","BoundBox","Use precise algorithm (slower).")
obj.Precision = False
obj.addProperty("App::PropertyBool","Precision","BoundBox","Use precise alorithm (slower).")
obj.Precision = True
obj.addProperty("App::PropertyEnumeration","OrientMode","BoundBox","Choose the orientation of bounding boxes to be made.")
obj.OrientMode = ["global","local of compound","local of child","use OrientLink"]
@ -179,7 +179,7 @@ class _BoundBox:
# rotations, some of first three values will be nonzero, and fourth value will
# not be equal to 1. While it's enough to compare absolute value of fourth value
# to 1, precision is seriously lost in such comparison, so we are checking if
# first three values are zero instead.
# fisrt three values are zero instead.
if abs(Q[0])+abs(Q[1])+abs(Q[2]) < ParaConfusion:
orients[i] = None
@ -254,7 +254,7 @@ class _ViewProviderBoundBox:
def CreateBoundBox(ShapeLink,
CompoundTraversal = "Use as a whole",
Precision = False,
Precision = True,
OrientMode = "global",
OrientLink = None):

View File

@ -1,6 +1,6 @@
#***************************************************************************
#* *
#* Copyright (c) 2016 - Victor Titov (DeepSOIC) *
#* Copyright (c) 2018 - Victor Titov (DeepSOIC) *
#* <vv.titov@gmail.com> *
#* *
#* This program is free software; you can redistribute it and/or modify *
@ -21,33 +21,29 @@
#* *
#***************************************************************************
__title__="Group command for interpolation features"
__title__="CoinGlue module of FreeCAD add-on Lattice2"
__author__ = "DeepSOIC"
__url__ = ""
__doc__ = "Glue and conversion routines between coin and freecad"
import lattice2Resample as Resample
import lattice2ScLERP as Sclerp
import FreeCAD as App
if App.GuiUp:
import FreeCADGui
class CommandInterpolateGroup:
def GetCommands(self):
ret = Resample.exportedCommands
return tuple(Sclerp.exportedCommands + Resample.exportedCommands)
def GetDefaultCommand(self): # return the index of the tuple of the default command.
return 0
def GetResources(self):
return { 'MenuText': 'Interpolation features:',
'ToolTip': 'Interpolation features (group): creating placements between occurrences.'}
def IsActive(self): # optional
return App.ActiveDocument is not None
if App.GuiUp:
FreeCADGui.addCommand('Lattice2_Interpolate_GroupCommand',CommandInterpolateGroup())
exportedCommands = ['Lattice2_Interpolate_GroupCommand']
def cointransform(placement, scale = 1.0, modifyme = None):
"""cointransform(placement, scale = 1.0, modifyme = None): creates/updates SoTransform node from FreeCAD.Placement.
modifyme: the existing node to be modified.
Returns: SoTransform node (new if modifyme is None else modifyme)"""
from pivy import coin
tr = coin.SoTransform() if modifyme is None else modifyme
tr.scaleFactor.setValue(scale,scale,scale)
tr.translation.setValue(*tuple(placement.Base))
tr.rotation.setValue(*placement.Rotation.Q)
return tr
def readNodeFromFile(fullpath):
"""readNodeFromFile(fullpath): reads a file. Returns SoSeparator node, containing the read out stuff."""
from pivy import coin
i = coin.SoInput()
if not i.openFile(fullpath): raise ValueError("Failed to open file: {fn}".format(fn= fullpath))
import pivy
sep = pivy.SoDB.readAll(i)
i.closeFile()
return sep

View File

@ -1,37 +1,10 @@
import FreeCAD as App
def get_fc_version():
"""returns tuple like (0,18,4,16154) for 0.18.4 release, and (0,19,0,18234) for pre builds"""
# ['0', '18', '4 (GitTag)', 'git://github.com/FreeCAD/FreeCAD.git releases/FreeCAD-0-18', '2019/10/22 16:53:35', 'releases/FreeCAD-0-18', '980bf9060e28555fecd9e3462f68ca74007b70f8']
# ['0', '19', '18234 (Git)', 'git://github.com/FreeCAD/FreeCAD.git master', '2019/09/15 20:43:17', 'master', '3af5d97e9b2a60823815f662aba25422c4bc45bb']
strmaj, strmi, strrev = App.Version()[0:3]
maj, mi = int(strmaj), int(strmi)
submi, rev = 0, 0
if '(GitTag)' in strrev:
submi = int(strrev.split(" ")[0])
elif '(Git)' in strrev:
try:
rev = int(strrev.split(" ")[0])
except Exception as err:
App.Console.PrintWarning(u"Lattice2 failed to detect FC version number.\n"
" {err}\n".format(err= str(err)))
rev = 19207 #assume fairly modern
if rev < 100:
if mi == 17:
rev = 13544
elif mi == 18:
rev = 16154
else:
rev = 19207 #assume fairly modern
App.Console.PrintWarning(u"Lattice2 failed to detect FC version number: revision is zero / too low, minor version is unexpected.")
return (maj, mi, submi, rev)
try:
rev_number = get_fc_version()[3]
rev_number = int(App.Version()[2].split(" ")[0])
except Exception as err:
App.Console.PrintError(str(err))
rev_number = 10000000
del err
attach_extension_era = rev_number >= 9177

View File

@ -75,7 +75,7 @@ def makeOrientationFromLocalAxes(ZAx, XAx = None):
YAx.normalize()
XAx = YAx.cross(ZAx) # force X perpendicular
#hacky way of constructing rotation to a local coordinate system:
#hacky way of constucting rotation to a local coordinate system:
# make matrix,
m = App.Matrix()
m.A = list(XAx)+[0.0]+list(YAx)+[0.0]+list(ZAx)+[0.0]+[0.0]*3+[1.0]
@ -129,7 +129,7 @@ def makeOrientationFromLocalAxesUni(priorityString, XAx = None, YAx = None, ZAx
# mainAx, secAx, thirdAx, we can't use '=' operator, because '=' reassigns
# the reference, and the variables lose linkage. For that purpose,
# _assignVector routine was introuced. It assigns the coordinates of the
# vector, without replacing references
# vector, without replacing referenes
#force the axes be perpendicular
mainAx.normalize()
@ -160,7 +160,7 @@ def makeOrientationFromLocalAxesUni(priorityString, XAx = None, YAx = None, ZAx
if XAx.cross(YAx).dot(ZAx) < 0.0:
_assignVector(thirdAx, tmpAx * (-1.0))
#hacky way of constructing rotation to a local coordinate system:
#hacky way of constucting rotation to a local coordinate system:
# make matrix,
m = App.Matrix()
m.A = list(XAx)+[0.0]+list(YAx)+[0.0]+list(ZAx)+[0.0]+[0.0]*3+[1.0]

View File

@ -25,16 +25,11 @@ __title__="Lattice Invert object: creates an array of placements from a compound
__author__ = "DeepSOIC"
__url__ = ""
import math
import FreeCAD as App
import Part
from lattice2Common import *
import lattice2BaseFeature
import lattice2CompoundExplorer as LCE
import lattice2GeomUtils as Utils
import lattice2Executer
# -------------------------- document object --------------------------------------------------
@ -46,8 +41,6 @@ class LatticeInvert(lattice2BaseFeature.LatticeFeature):
"The Lattice Invert object"
def derivedInit(self,obj):
self.Type = "LatticeInvert"
obj.addProperty("App::PropertyLink","Base","Lattice Invert","Lattice, all the placements of which are to be inverted.")
obj.addProperty("App::PropertyEnumeration","TranslateMode","Lattice Invert","What to do with translation part of placements")
@ -57,14 +50,27 @@ class LatticeInvert(lattice2BaseFeature.LatticeFeature):
obj.addProperty("App::PropertyEnumeration","OrientMode","Lattice Invert","what to do with orientation part of placements")
obj.OrientMode = ['invert', 'keep', 'reset']
obj.OrientMode = 'invert'
def assureProperties(self, selfobj, creating_new = False):
super(LatticeInvert, self).assureProperties(selfobj, creating_new)
self.assureProperty(selfobj, 'App::PropertyEnumeration', 'Referencing', ['Origin', 'Array\'s reference'], "Lattice Invert", "Sets which placement to use as origin")
def derivedExecute(self,obj):
# cache stuff
base = screen(obj.Base).Shape
if not lattice2BaseFeature.isObjectLattice(screen(obj.Base)):
lattice2Executer.warning(obj, "Base is not a lattice, but lattice is expected. Results may be unexpected.\n")
baseChildren = LCE.AllLeaves(base)
placements = lattice2BaseFeature.getPlacementsList(obj.Base)
# dereference
refplm = App.Placement()
if obj.Referencing == 'Origin':
pass
elif obj.Referencing == 'Array\'s reference':
refplm = self.getReferencePlm(obj.Base)
self.setReferencePlm(obj, refplm)
else:
raise NotImplementedError(obj.Referencing)
inv_refplm = refplm.inverse()
locplms = [inv_refplm.multiply(plm) for plm in placements]
#cache mode comparisons, for speed
posIsInvert = obj.TranslateMode == 'invert'
posIsKeep = obj.TranslateMode == 'keep'
@ -78,26 +84,29 @@ class LatticeInvert(lattice2BaseFeature.LatticeFeature):
outputPlms = [] #list of placements
# the essence
for child in baseChildren:
for plm in locplms:
pos = App.Vector()
ori = App.Rotation()
inverted = child.Placement.inverse()
inverted = plm.inverse()
if posIsInvert:
pos = inverted.Base
elif posIsKeep:
pos = child.Placement.Base
pos = plm.Base
elif posIsReset:
pass
if oriIsInvert:
ori = inverted.Rotation
elif oriIsKeep:
ori = child.Placement.Rotation
ori = plm.Rotation
elif oriIsReset:
pass
plm = App.Placement(pos, ori)
outputPlms.append(plm)
outputPlms.append(App.Placement(pos, ori))
# re-reference
outputPlms = [refplm.multiply(plm) for plm in outputPlms]
return outputPlms
@ -121,6 +130,8 @@ def CreateLatticeInvert(name):
FreeCADGui.addModule("lattice2Executer")
FreeCADGui.doCommand("f = lattice2Invert.makeLatticeInvert(name='"+name+"')")
FreeCADGui.doCommand("f.Base = App.ActiveDocument."+sel[0].ObjectName)
hasref = lattice2BaseFeature.getReferencePlm(sel[0].Object) is not None
FreeCADGui.doCommand("f.Referencing = {r}".format(r= repr('Array\'s reference' if hasref else 'Origin') ))
FreeCADGui.doCommand("for child in f.ViewObject.Proxy.claimChildren():\n"+
" child.ViewObject.hide()")
FreeCADGui.doCommand("lattice2Executer.executeFeature(f)")

View File

@ -49,33 +49,60 @@ class JoinArrays(lattice2BaseFeature.LatticeFeature):
obj.addProperty("App::PropertyBool","Interleave","Lattice JoinArrays","If false, first go all elements of array 1, nect go all elements of array 2, so on. If true, first go all first elements from each array, then all second elements, and so on.")
def assureProperties(self, selfobj, creating_new = False):
super(JoinArrays, self).assureProperties(selfobj, creating_new = False)
created = self.assureProperty(selfobj,
'App::PropertyEnumeration',
'ReferencePlacementOption',
['external', 'origin', 'inherit 1st'],
"Lattice JoinArrays",
"Reference placement, corresponds to the original occurrence of the object to be populated."
)
if created:
selfobj.ReferencePlacementOption = 'inherit 1st'
self.assureProperty(selfobj,
'App::PropertyBool',
'AlignReferences',
False,
"Lattice JoinArrays",
"If true, input arrays will be moved to make their reference placements equal, before joining."
)
def recomputeReferencePlm(self, selfobj, selfplacements): #override
pass #disables standard recompute-reference call. The recompute is handled by derivedExecute
def derivedExecute(self,obj):
#validity check
nonLattices = []
for iArr in range(0, len(obj.Links)):
link = obj.Links[iArr]
if not lattice2BaseFeature.isObjectLattice(link):
nonLattices.append(link.Label)
if len(nonLattices) > 0:
lattice2Executer.warning(obj, "Only lattice objects are expected to be linked as arrays in JoinArrays. There are "
+len(nonLattices)+" objects which are not lattice objects. Results may me unexpected.")
def derivedExecute(self, selfobj):
align = selfobj.AlignReferences
#recompute reference placement
ref = selfobj.ReferencePlacementOption
if ref == 'external':
super(JoinArrays, self).recomputeReferencePlm(selfobj, [])
elif ref == 'origin':
self.setReferencePlm(selfobj, None)
elif ref == 'inherit 1st':
if len(selfobj.Links)>0:
self.setReferencePlm(selfobj, lattice2BaseFeature.getReferencePlm(selfobj.Links[0]))
else:
self.setReferencePlm(selfobj, None)
else:
raise NotImplementedError("Reference option not implemented: " + ref)
refplm = self.getReferencePlm(selfobj)
#extract placements
listlistPlms = []
lengths = []
for link in obj.Links:
leaves = LCE.AllLeaves(link.Shape)
listlistPlms.append([child.Placement for child in leaves])
lengths.append(len(leaves))
for link in selfobj.Links:
plms = lattice2BaseFeature.getPlacementsList(link, context= selfobj, dereferenced= align, torefplm = refplm)
listlistPlms.append(plms)
lengths.append(len(plms))
#processing
output = [] #list of placements
if obj.Interleave:
if selfobj.Interleave:
for l in lengths[1:]:
if l != lengths[0]:
lattice2Executer.warning(obj,"Array lengths are unequal: "+repr(lengths)+". Interleaving will be inconsistent.")
lattice2Executer.warning(selfobj,"Array lengths are unequal: "+repr(lengths)+". Interleaving will be inconsistent.")
break
for iItem in range(0,max(lengths)):
@ -85,6 +112,7 @@ class JoinArrays(lattice2BaseFeature.LatticeFeature):
else:
for list in listlistPlms:
output.extend(list)
return output
class ViewProviderJoinArrays(lattice2BaseFeature.ViewProviderLatticeFeature):
@ -125,14 +153,13 @@ class _CommandJoinArrays:
'ToolTip': QtCore.QT_TRANSLATE_NOOP("Lattice2_JoinArrays","Lattice JoinArrays: concatenate or interleave two or more arrays.")}
def Activated(self):
if len(FreeCADGui.Selection.getSelection()) > 1 :
CreateJoinArrays(name = "Join")
else:
mb = QtGui.QMessageBox()
mb.setIcon(mb.Icon.Warning)
mb.setText(translate("Lattice2_JoinArrays", "Please select at least two lattice objects. Selected lattice objects will be concatenated or interleaved into one array.", None))
mb.setWindowTitle(translate("Lattice2_JoinArrays","Bad selection", None))
mb.exec_()
try:
if len(FreeCADGui.Selection.getSelection()) > 1 :
CreateJoinArrays(name = "Join")
else:
infoMessage("Please select at least two lattice objects. Selected lattice objects will be concatenated or interleaved into one array.")
except Exception as err:
msgError(err)
def IsActive(self):
if FreeCAD.ActiveDocument:

View File

@ -80,16 +80,17 @@ class LinearArray(lattice2BaseFeature.LatticeFeature):
obj.Step = 3.0
obj.Count = 5.0
self.assureProperties(obj)
def updateReadonlyness(self, obj):
super(LinearArray,self).updateReadonlyness(obj)
link = screen(obj.Link)
obj.setEditorMode("Dir", 1 if (link and obj.DirIsDriven) else 0)
obj.setEditorMode("Point", 1 if (link and obj.PointIsDriven) else 0)
obj.setEditorMode("DirIsDriven", 0 if link else 1)
obj.setEditorMode("PointIsDriven", 0 if link else 1)
obj.setEditorMode("DrivenProperty", 0 if link else 1)
obj.setEditorMode('ReferenceValue', 0 if obj.ReferencePlacementOption == 'at custom value' else 2)
self.assureGenerator(obj)
self.generator.updateReadonlyness()
def assureGenerator(self, obj):
@ -101,15 +102,30 @@ class LinearArray(lattice2BaseFeature.LatticeFeature):
groupname_gen= "Lattice Series Generator",
valuesdoc= "List of distances. Distance is measured from Point, along Dir, in millimeters.",
valuestype= "App::PropertyDistance")
self.updateReadonlyness(obj)
def assureProperties(self, selfobj):
def assureProperties(self, selfobj, creating_new = False):
super(LinearArray, self).assureProperties(selfobj, creating_new)
assureProperty(selfobj, "App::PropertyLinkSub", "SubLink", sublinkFromApart(screen(selfobj.Link), selfobj.LinkSubelement), "Lattice Array", "Mirror of Object+SubNames properties")
created = self.assureProperty(selfobj,
'App::PropertyEnumeration',
'ReferencePlacementOption',
['external', 'origin', 'SpanStart', 'SpanEnd', 'at custom value', 'first placement', 'last placement'],
"Lattice Array",
"Reference placement, corresponds to the original occurrence of the object to be populated."
)
if created:
selfobj.ReferencePlacementOption = 'SpanStart'
self.assureProperty(selfobj, 'App::PropertyDistance', 'ReferenceValue', 0.0, "Lattice Array", "Sets the value to use for generating ReferencePlacement. This value sets, what coordinate the object to be populated corresponds to.")
def recomputeReferencePlm(self, selfobj, selfplacements): #override
if selfobj.ReferencePlacementOption == 'external':
super(LinearArray, self).recomputeReferencePlm(selfobj, selfplacements)
#the remaining options are handled in derivedExecute
def derivedExecute(self,obj):
self.assureGenerator(obj)
self.assureProperties(obj)
self.updateReadonlyness(obj)
# Apply links
if screen(obj.Link):
@ -170,9 +186,30 @@ class LinearArray(lattice2BaseFeature.LatticeFeature):
dir.normalize()
# Make the array
def plmByVal(val):
return App.Placement(obj.Point + obj.Dir*val, ori)
output = [] # list of placements
for v in values:
output.append( App.Placement(obj.Point + obj.Dir*v, ori) )
output.append( plmByVal(v) )
# update reference placement
ref = obj.ReferencePlacementOption
if ref == 'external':
pass
elif ref == 'origin':
self.setReferencePlm(obj, None)
elif ref == 'SpanStart':
self.setReferencePlm(obj, plmByVal(float(obj.SpanStart)))
elif ref == 'SpanEnd':
self.setReferencePlm(obj, plmByVal(float(obj.SpanEnd)))
elif ref == 'at custom value':
self.setReferencePlm(obj, plmByVal(float(obj.ReferenceValue)))
elif ref == 'first placement':
self.setReferencePlm(obj, output[0])
elif ref == 'last placement':
self.setReferencePlm(obj, output[-1])
else:
raise NotImplementedError("Reference option not implemented: " + ref)
return output
@ -196,16 +233,12 @@ def CreateLinearArray(name, mode):
FreeCAD.ActiveDocument.openTransaction("Create LinearArray")
FreeCADGui.addModule("lattice2LinearArray")
FreeCADGui.addModule("lattice2Executer")
FreeCADGui.addModule("lattice2Base.Autosize")
FreeCADGui.doCommand("f = lattice2LinearArray.makeLinearArray(name='"+name+"')")
if len(sel) == 1:
FreeCADGui.doCommand("f.Link = App.ActiveDocument."+sel[0].ObjectName)
if sel[0].HasSubObjects:
FreeCADGui.doCommand("f.LinkSubelement = '"+sel[0].SubElementNames[0]+"'")
FreeCADGui.doCommand("f.GeneratorMode = {mode}".format(mode= repr(mode)))
FreeCADGui.doCommand("f.Placement.Base = lattice2Base.Autosize.convenientPosition()")
FreeCADGui.doCommand("f.SpanEnd = lattice2Base.Autosize.convenientModelSize()")
FreeCADGui.doCommand("f.Step = lattice2Base.Autosize.convenientMarkerSize()")
FreeCADGui.doCommand("lattice2Executer.executeFeature(f)")
FreeCAD.ActiveDocument.commitTransaction()

View File

@ -21,15 +21,19 @@
#* *
#***************************************************************************
import Part, os
import Part
import os
import lattice2CoinGlue as CoinGlue
__title__="latticeMarkers module for FreeCAD"
__author__ = "DeepSOIC"
__url__ = ""
__doc__ = "Module for loading marker shapes for Lattice workbench"
_nullShapeShape = 0
_nullShapeShape = None
_ShapeDict = {}
_NodeDict = {}
def getShapePath(shapeName):
"""
@ -44,7 +48,7 @@ def getNullShapeShape(scale = 1.0):
#read shape from file, if not done this before
global _nullShapeShape
if not _nullShapeShape:
if _nullShapeShape is None:
_nullShapeShape = Part.Shape()
f = open(getShapePath("empty-shape.brep"))
_nullShapeShape.importBrep(f)
@ -73,8 +77,7 @@ def loadShape(shapeID):
FreeCAD.Console.PrintError('Failed to load standard shape "'+shapeID+'". \n' + str(err) + '\n')
sh = Part.Point() #Create at least something!
_ShapeDict[shapeID] = sh
return sh
return sh
def getPlacementMarker(scale = 1.0, markerID = None):
'''getPlacementMarker(scale = 1.0, markerID = None): returns a placement marker shape.
@ -87,3 +90,27 @@ def getPlacementMarker(scale = 1.0, markerID = None):
sh = sh.copy()
sh.scale(scale)
return sh
def loadNode(shapeID):
global _NodeDict
nd = _NodeDict.get(shapeID)
if nd is None:
nd = CoinGlue.readNodeFromFile(getShapePath(shapeID + '.iv'))
_NodeDict[shapeID] = nd
nd.ref() # not sure if needed, but won't hurt.
return nd.copy()
def getRefPlmMarker(markerID, placement = None, scale = 1.0):
'''getRefPlmMarker(markerID, placement = None, scale = 1.0): returns a coin placement marker shape, as SoSeparator (+ transform node + shape node).
The shape is scaled according to "scale" argument.
markerID sets the marker file name.'''
sh = loadNode(markerID + '-refplm')
from pivy import coin
sep = coin.SoSeparator()
tr = coin.SoTransform()
if placement is not None:
CoinGlue.cointransform(placement, scale, tr)
sep.addChild(tr)
sep.addChild(sh)
return (sep, tr, sh)

View File

@ -33,7 +33,7 @@ import lattice2BaseFeature
import lattice2Executer
from lattice2ShapeCopy import shallowCopy, transformCopy_Smart
from lattice2PopulateCopies import DereferenceArray
from lattice2PopulateCopies import DereferenceArray, REF_MODES
class FeatureUnsupportedError(RuntimeError):
@ -47,7 +47,7 @@ class ScopeError(RuntimeError):
class MultiTransformSettings(object):
selfintersections = False #if True, take care of intersections between occurrences. If False, optimize assuming occurrences do not intersect.
selfintersections = False #if True, take care of intersections between occurences. If False, optimize assuming occurences do not intersect.
sign_override = +1 #+1 for keep sign, -1 for invert, +2 for force positive, -2 for force negative
@ -160,7 +160,7 @@ def applyFeature(baseshape, feature, transforms, mts):
if mts.selfintersections:
pass #to fuse the shapes to baseshape one by one
else:
actionshapes = [Part.Compound(actionshapes)] #to fuse all at once, saving for computing intersections between the occurrences of the feature
actionshapes = [Part.Compound(actionshapes)] #to fuse all at once, saving for computing intersections between the occurences of the feature
for actionshape in actionshapes:
assert(sign != 0)
@ -182,7 +182,7 @@ class LatticePDPattern(object):
obj.addProperty('App::PropertyLink','PlacementsTo',"Lattice Pattern","Target placements")
obj.addProperty('App::PropertyEnumeration','Referencing',"Lattice Pattern","Reference placement mode (sets what to grab the feature by).")
obj.Referencing = ['Origin','First item', 'Last item', 'Use PlacementsFrom']
obj.Referencing = REF_MODES
obj.addProperty('App::PropertyBool', 'IgnoreUnsupported', "Lattice Pattern", "Skip unsupported features such as fillets, instead of throwing errors")
obj.addProperty('App::PropertyBool', 'SkipFirstInBody', "Lattice Pattern", "Skip first body feature (which may be used as support for the important features).")
@ -190,7 +190,7 @@ class LatticePDPattern(object):
obj.addProperty('App::PropertyEnumeration', 'SignOverride', "Lattice Pattern", "Use it to change Pockets into Pads.")
obj.SignOverride = ['keep', 'invert', 'as additive', 'as subtractive']
obj.addProperty('App::PropertyBool', 'Selfintersections', "Lattice Pattern", "If True, take care of intersections between occurrences. If False, you get a slight speed boost.")
obj.addProperty('App::PropertyBool', 'Selfintersections', "Lattice Pattern", "If True, take care of intersections between occurences. If False, you get a slight speed boost.")
obj.addProperty('App::PropertyBool', 'Refine', "PartDesign", "If True, remove redundant edges after this operation.")
obj.Refine = getParamPDRefine()
@ -250,8 +250,7 @@ class LatticePDPattern(object):
raise ScopeError('Reference placement and the feature are not in the same body (use Shapebinder or Ghost to bring the placement in).')
placements = lattice2BaseFeature.getPlacementsList(selfobj.PlacementsTo, selfobj)
placements = DereferenceArray(selfobj, placements, selfobj.PlacementsFrom, selfobj.Referencing)
placements = DereferenceArray(selfobj, selfobj.PlacementsTo, selfobj.PlacementsFrom, selfobj.Referencing)
if selfobj.Referencing == 'First item' and transforms is None:
placements.pop(0) #to not repeat the feature where it was applied already
elif selfobj.Referencing == 'Last item' and transforms is None:

View File

@ -107,7 +107,7 @@ class LatticeParaSeries(lattice2BaseFeature.LatticeFeature):
obj.addProperty("App::PropertyEnumeration","ParameterType","Lattice ParaSeries","Data type of parameter to vary.")
obj.ParameterType = ['float','int','string']
obj.addProperty("App::PropertyString","ParameterRef","Lattice ParaSeries","Reference to the parameter to vary. Syntax: ObjectName.Property. Examples: 'Box.Height'; 'Sketch.Constraints.myLength'.")
obj.addProperty("App::PropertyString","ParameterRef","Lattice ParaSeries","Reference to the parameter to vary. Syntax: ObjectName.Property. Examples: 'Box.Height'; 'Sketch.Constaints.myLength'.")
obj.addProperty("App::PropertyEnumeration","Recomputing","Lattice ParaSeries","Sets recomputing policy.")
obj.Recomputing = ["Disabled", "Recompute Once", "Enabled"]
@ -123,12 +123,16 @@ class LatticeParaSeries(lattice2BaseFeature.LatticeFeature):
self.generator.addProperties(groupname= "Lattice ParaSeries",
groupname_gen= "Lattice ParaSeries Generator",
valuesdoc= "List of parameter values to compute object for.")
def updateReadonlyness(self, selfobj):
super(LatticeParaSeries, self).updateReadonlyness(selfobj)
self.assureGenerator(selfobj)
self.generator.updateReadonlyness()
def derivedExecute(self,selfobj):
# values generator should be functional even if recomputing is disabled, so do it first
self.assureGenerator(selfobj)
self.generator.updateReadonlyness()
self.generator.execute()
if selfobj.Recomputing == "Disabled":

View File

@ -179,12 +179,9 @@ def CreateLatticePlacement(name,mode = 'Custom'):
FreeCAD.ActiveDocument.openTransaction("Create Lattice Placement")
FreeCADGui.addModule("lattice2Placement")
FreeCADGui.addModule("lattice2Executer")
FreeCADGui.addModule("lattice2Base.Autosize")
FreeCADGui.doCommand("f = lattice2Placement.makeLatticePlacement(name='"+name+"')")
FreeCADGui.doCommand("f.PlacementChoice = '"+mode+"'")
FreeCADGui.doCommand("f.Label = '"+mode+"'")
FreeCADGui.doCommand("f.Placement.Base = lattice2Base.Autosize.convenientPosition()")
FreeCADGui.doCommand("f.MarkerSize = lattice2Base.Autosize.convenientMarkerSize()")
FreeCADGui.doCommand("lattice2Executer.executeFeature(f)")
FreeCADGui.doCommand("Gui.Selection.addSelection(f)")
FreeCADGui.doCommand("f = None")
@ -195,7 +192,6 @@ def CreateLatticePlacementAx(label, priority, XDir, YDir, ZDir):
FreeCAD.ActiveDocument.openTransaction("Create Lattice Placement")
FreeCADGui.addModule("lattice2Placement")
FreeCADGui.addModule("lattice2Executer")
FreeCADGui.addModule("lattice2Base.Autosize")
name = "PlacementAx"
FreeCADGui.doCommand("f = lattice2Placement.makeLatticePlacementAx(name='"+name+"')")
FreeCADGui.doCommand("f.Priority = "+repr(priority))
@ -206,8 +202,6 @@ def CreateLatticePlacementAx(label, priority, XDir, YDir, ZDir):
if ZDir is not None and ZDir.Length > DistConfusion:
FreeCADGui.doCommand("f.ZDir_wanted = App.Vector"+repr(tuple(ZDir)))
FreeCADGui.doCommand("f.Label = "+repr(label))
FreeCADGui.doCommand("f.Placement.Base = lattice2Base.Autosize.convenientPosition()")
FreeCADGui.doCommand("f.MarkerSize = lattice2Base.Autosize.convenientMarkerSize()")
FreeCADGui.doCommand("lattice2Executer.executeFeature(f)")
FreeCADGui.doCommand("Gui.Selection.addSelection(f)")
FreeCADGui.doCommand("f = None")
@ -218,10 +212,7 @@ def CreateLatticePlacementEuler(name):
FreeCAD.ActiveDocument.openTransaction("Create Lattice Placement")
FreeCADGui.addModule("lattice2Placement")
FreeCADGui.addModule("lattice2Executer")
FreeCADGui.addModule("lattice2Base.Autosize")
FreeCADGui.doCommand("f = lattice2Placement.makeLatticePlacementEuler(name='"+name+"')")
FreeCADGui.doCommand("f.Placement.Base = lattice2Base.Autosize.convenientPosition()")
FreeCADGui.doCommand("f.MarkerSize = lattice2Base.Autosize.convenientMarkerSize()")
FreeCADGui.doCommand("lattice2Executer.executeFeature(f)")
FreeCADGui.doCommand("Gui.Selection.addSelection(f)")
FreeCADGui.doCommand("f = None")

View File

@ -75,8 +75,6 @@ class PolarArray(lattice2BaseFeature.LatticeFeature):
obj.EndInclusive = False
obj.Count = 5
self.assureProperties(obj)
def assureGenerator(self, obj):
'''Adds an instance of value series generator, if one doesn't exist yet.'''
if hasattr(self,"generator"):
@ -89,21 +87,24 @@ class PolarArray(lattice2BaseFeature.LatticeFeature):
self.updateReadonlyness(obj)
def updateReadonlyness(self, obj):
super(PolarArray, self).updateReadonlyness(obj)
axislink = screen(obj.AxisLink)
obj.setEditorMode("AxisDir", 1 if (axislink and obj.AxisDirIsDriven) else 0)
obj.setEditorMode("AxisPoint", 1 if (axislink and obj.AxisPointIsDriven) else 0)
obj.setEditorMode("AxisDirIsDriven", 0 if axislink else 1)
obj.setEditorMode("AxisPointIsDriven", 0 if axislink else 1)
self.assureGenerator(obj)
self.generator.updateReadonlyness()
def assureProperties(self, selfobj):
def assureProperties(self, selfobj, creating_new = False):
super(PolarArray, self).assureProperties(selfobj, creating_new)
assureProperty(selfobj, "App::PropertyLinkSub", "AxisSubLink", sublinkFromApart(screen(selfobj.AxisLink), selfobj.AxisLinkSubelement), "Lattice Array", "Mirror of Object+SubNames properties")
def derivedExecute(self,obj):
self.assureGenerator(obj)
self.assureProperties(obj)
self.updateReadonlyness(obj)
# Apply links
if screen(obj.AxisLink):

View File

@ -45,7 +45,7 @@ V = App.Vector
def make():
'''make(): makes a PolarArray object.'''
obj = lattice2BaseFeature.makeLatticeFeature('PolarArray', PolarArray, ViewProviderPolarArray, no_disable_attacher= True)
obj = lattice2BaseFeature.makeLatticeFeature('PolarArray', PolarArray, ViewProviderPolarArray)
return obj
def fetchArc(obj, sub):
@ -85,7 +85,22 @@ class PolarArray(APlm.AttachableFeature):
selfobj.EndInclusive = False
selfobj.Step = 55
selfobj.Count = 7
def assureProperties(self, selfobj, creating_new = False):
super(PolarArray, self).assureProperties(selfobj, creating_new)
# upgrades older versions of the feature
created = self.assureProperty(selfobj,
'App::PropertyEnumeration',
'ReferencePlacementOption',
['external', 'origin', 'center', 'SpanStart', 'SpanEnd', 'at custom value', 'first placement', 'last placement'],
"Polar Array",
"Reference placement, corresponds to the original occurrence of the object to be populated."
)
if created:
selfobj.ReferencePlacementOption = 'SpanStart'
self.assureProperty(selfobj, 'App::PropertyFloat', 'ReferenceValue', 0.0, "Polar Array", "Sets the value to use for generating ReferencePlacement. This value sets, what angle the object to be populated corresponds to.")
def assureGenerator(self, selfobj):
'''Adds an instance of value series generator, if one doesn't exist yet.'''
if hasattr(self,'generator'):
@ -95,13 +110,16 @@ class PolarArray(APlm.AttachableFeature):
groupname_gen= "Lattice Series Generator",
valuesdoc= "List of angles, in degrees.",
valuestype= 'App::PropertyFloat')
self.updateReadonlyness(selfobj)
def updateReadonlyness(self, selfobj):
super(PolarArray, self).updateReadonlyness(selfobj)
self.assureGenerator(selfobj)
self.generator.updateReadonlyness()
arc = self.fetchArc(selfobj)
selfobj.setEditorMode('Radius', 1 if arc and selfobj.UseArcRadius else 0)
selfobj.setEditorMode('ReferenceValue', 0 if selfobj.ReferencePlacementOption == 'at custom value' else 2)
self.generator.setPropertyWritable('SpanEnd', False if arc and selfobj.UseArcRange == 'as Span' else True)
self.generator.setPropertyWritable('SpanStart', False if arc and selfobj.UseArcRange == 'as Span' else True)
self.generator.setPropertyWritable('Step', False if arc and selfobj.UseArcRange == 'as Step' else True)
@ -113,6 +131,11 @@ class PolarArray(APlm.AttachableFeature):
sub = sub[0]
#resolve the link
return fetchArc(lnkobj, sub)
def recomputeReferencePlm(self, selfobj, selfplacements): #override
if selfobj.ReferencePlacementOption == 'external':
super(PolarArray, self).recomputeReferencePlm(selfobj, selfplacements)
#the remaining options are handled in derivedExecute
def derivedExecute(self,selfobj):
self.assureGenerator(selfobj)
@ -189,7 +212,7 @@ class PolarArray(APlm.AttachableFeature):
angleplus = -90.0 if on_arc else 0.0
mm = -1.0 if selfobj.Reverse else +1.0
output = [] # list of placements
for ang in values:
def plmByVal(ang):
localrot = App.Rotation(App.Vector(0,0,1), ang * mm + angleplus)
localtransl = localrot.multVec(App.Vector(radius,0,0))
localplm = App.Placement(localtransl, localrot)
@ -200,7 +223,29 @@ class PolarArray(APlm.AttachableFeature):
elif is_static:
resultplm.Rotation = App.Rotation()
resultplm = resultplm.multiply(flipplm)
output.append(resultplm)
return resultplm
output = [plmByVal(ang) for ang in values]
# update reference placement
ref = selfobj.ReferencePlacementOption
if ref == 'external':
pass #gets done in recomputeReferencePlm
elif ref == 'origin':
self.setReferencePlm(selfobj, None)
elif ref == 'center':
self.setReferencePlm(selfobj, App.Placement())
elif ref == 'SpanStart':
self.setReferencePlm(selfobj, plmByVal(selfobj.SpanStart))
elif ref == 'SpanEnd':
self.setReferencePlm(selfobj, plmByVal(selfobj.SpanEnd))
elif ref == 'at custom value':
self.setReferencePlm(selfobj, plmByVal(selfobj.ReferenceValue))
elif ref == 'first placement':
self.setReferencePlm(selfobj, output[0])
elif ref == 'last placement':
self.setReferencePlm(selfobj, output[-1])
else:
raise NotImplementedError("Reference option not implemented: " + ref)
return output
@ -230,7 +275,6 @@ def CreatePolarArray(genmode = 'SpanN'):
FreeCAD.ActiveDocument.openTransaction("Create PolarArray")
FreeCADGui.addModule('lattice2PolarArray2')
FreeCADGui.addModule('lattice2Executer')
FreeCADGui.addModule("lattice2Base.Autosize")
FreeCADGui.doCommand('f = lattice2PolarArray2.make()')
FreeCADGui.doCommand("f.GeneratorMode = {mode}".format(mode= repr(genmode)))
attached = False
@ -251,9 +295,6 @@ def CreatePolarArray(genmode = 'SpanN'):
.format(lnk= lnk.Name, sub= repr(sub), usearcrange= repr(usearcrange), endinclusive= repr(endinclusive))
)
attached = True
if not attached:
FreeCADGui.doCommand("f.Placement.Base = lattice2Base.Autosize.convenientPosition()")
FreeCADGui.doCommand("f.Radius = lattice2Base.Autosize.convenientModelSize()/2")
FreeCADGui.doCommand('lattice2Executer.executeFeature(f)')
if len(sublinks) > 0 and not attached:
FreeCADGui.addModule('lattice2AttachablePlacement')

View File

@ -34,7 +34,7 @@ from lattice2Common import *
import lattice2BaseFeature
import lattice2CompoundExplorer as LCE
import lattice2Executer
from lattice2PopulateCopies import DereferenceArray, throwBody
from lattice2PopulateCopies import DereferenceArray, throwBody, REF_MODES
import lattice2ShapeCopy as ShapeCopy
# -------------------------- document object --------------------------------------------------
@ -58,7 +58,7 @@ class LatticePopulateChildren(lattice2BaseFeature.LatticeFeature):
obj.addProperty("App::PropertyBool","LoopObjectSequence","Lattice PopulateChildren","If true, children of Object will be traversed in a loop if there are more placements than children. Otherwise, extra placements will be dropped.")
obj.addProperty("App::PropertyEnumeration","Referencing","Lattice PopulateChildren","Reference for array of placements.")
obj.Referencing = ["Origin","First item", "Last item", "Use PlacementsFrom"]
obj.Referencing = REF_MODES
obj.addProperty("App::PropertyLink","PlacementsTo","Lattice PopulateChildren", "Placement or array of placements, containing target locations.")
@ -88,11 +88,10 @@ class LatticePopulateChildren(lattice2BaseFeature.LatticeFeature):
raise ValueError("Traversal mode not implemented: "+obj.ObjectTraversal)
else:
objectPlms = lattice2BaseFeature.getPlacementsList(screen(obj.Object), obj)
placements = lattice2BaseFeature.getPlacementsList(screen(obj.PlacementsTo), obj)
# Precompute referencing
placements = DereferenceArray(obj, placements, screen(obj.PlacementsFrom), obj.Referencing)
placements = DereferenceArray(obj, obj.PlacementsTo, screen(obj.PlacementsFrom), obj.Referencing)
# initialize output containers and loop variables
outputShapes = [] #output list of shapes
@ -101,6 +100,20 @@ class LatticePopulateChildren(lattice2BaseFeature.LatticeFeature):
numChildren = len(objectPlms) if outputIsLattice else len(objectShapes)
copy_method_index = ShapeCopy.getCopyTypeIndex(obj.Copying)
#inherit reference placement from the array being copied
if outputIsLattice:
refplm = None
if obj.Referencing == 'Array\'s reference' or obj.Referencing == 'First item' or obj.Referencing == 'Last item':
#simple cases - we just copy the reference plm from the object
refplm = lattice2BaseFeature.getReferencePlm(obj.Object)
else:
#other cases - apply first transform to reference placement
refplm = lattice2BaseFeature.getReferencePlm(obj.Object)
if refplm is not None and len(placements) > 0:
refplm = placements[0].multiply(refplm)
self.setReferencePlm(obj, refplm)
# the essence
for iPlm in range(len(placements)):
if iChild == numChildren:
@ -176,6 +189,11 @@ def CreateLatticePopulateChildren(name, label, shapeObj, latticeObjFrom, lattice
FreeCADGui.doCommand("f.PlacementsTo = App.ActiveDocument."+latticeObjTo.Name)
if latticeObjFrom is not None:
FreeCADGui.doCommand("f.PlacementsFrom = App.ActiveDocument."+latticeObjFrom.Name)
if refmode == 'Auto':
if lattice2BaseFeature.getReferencePlm(latticeObjTo) is not None:
refmode = 'Array\'s reference'
else:
refmode = 'First item'
FreeCADGui.doCommand("f.Referencing = "+repr(refmode))
FreeCADGui.doCommand("f.Label = " + repr(label))
@ -286,7 +304,7 @@ class _CommandLatticePopulateChildren_Array:
infoMessage("Populate with Children: Build Array",
"Populate with Children: Build Array command. Creates an array from children packed into a compound.\n\n"+
"Please select a compound, and an array of placements. Then invoke the command. It is also allowed to use another array of placements instead of compound.\n\n"+
"Compared to plain 'Populate With Children' command, the placements are treated as being relative to the first placement in the array. As a result, the first child always remains where it was.")
"Compared to plain 'Populate With Children' command, the placements are treated as being relative to the first placement in the array. As a result, the first child always remains wher it was.")
return
cmdPopulate_shapes_nonFromTo("First item")
except Exception as err:
@ -315,7 +333,7 @@ class _CommandLatticePopulateChildren_Move:
infoMessage("Move children",
"Moved Children command. Creates a compound from another compound, by moving its children.\n\n"+
"Each child is moved from one placement to another placement. Please select a compound, then a placement/array to move from, and an array of placements to move to (order matters).\n"+
"An array of placements can be used instead of a compound.")
"An array of placements can be used insead of a compound.")
return
cmdPopulate_shapes_FromTo()
except Exception as err:
@ -350,3 +368,4 @@ if FreeCAD.GuiUp:
exportedCommands = ['Lattice2_PopulateChildrenGroupCommand']
# -------------------------- /Gui command --------------------------------------------------

View File

@ -37,13 +37,16 @@ import lattice2Executer
import lattice2ShapeCopy as ShapeCopy
# ---------------------------shared code--------------------------------------
def DereferenceArray(obj,placements, lnkFrom, refmode):
REF_MODES = ['Origin', 'First item', 'Last item', 'Use PlacementsFrom', 'Array\'s reference']
def DereferenceArray(obj, lnkTo, lnkFrom, refmode):
'''common implementation of treatment Referencing property. Returns a list of placements to use directly.
obj - feature being executed (used for error reporting; can be None)
placements - the array, converted into a list of placements.
obj - feature being executed
lnkTo - the array of target placements (documentobject).
lnkFrom - object linked as a lattice of 'from' placements. Can be None, if mode is not 'Use PlacemenetsFrom'
refmode - a string - enum property item'''
placements = lattice2BaseFeature.getPlacementsList(lnkTo, obj)
plmDeref = App.Placement() #inverse placement of reference (reference is a substitute of origin)
if lnkFrom is not None and refmode != "Use PlacementsFrom":
lattice2Executer.warning(obj,"Referencing mode is '"+refmode+"', doesn't need PlacementsFrom link to be set. The link is set, but it will be ignored.")
@ -52,7 +55,7 @@ def DereferenceArray(obj,placements, lnkFrom, refmode):
elif refmode == "First item":
plmDeref = placements[0].inverse()
elif refmode == "Last item":
plmDeref = placements[0].inverse()
plmDeref = placements[-1].inverse()
elif refmode == "Use PlacementsFrom":
if lnkFrom is None:
raise ValueError("Referencing mode is 'Move from to', but PlacementsFrom link is not set.")
@ -63,8 +66,13 @@ def DereferenceArray(obj,placements, lnkFrom, refmode):
return [lattice2BaseFeature.makeMoveFromTo(placementsFrom[i], placements[i]) for i in range(0, len(placements))]
else:
lattice2Executer.warning(obj,"Lengths of arrays linked as PlacementsTo and PlacementsFrom must equal, or PlacementsFrom can be one placement. Violation: lengths are "+str(len(placements))+ " and "+str(len(placementsFrom)))
elif refmode == 'Array\'s reference':
plmRef = lattice2BaseFeature.getReferencePlm(lnkTo)
if plmRef is None:
raise AttributeError('Object {obj} does not expose a reference placement.')
plmDeref = plmRef.inverse()
else:
raise ValueError("Referencing mode not implemented: "+refmode)
raise ValueError("Referencing mode not implemented: " + refmode)
return [plm.multiply(plmDeref) for plm in placements]
@ -85,40 +93,54 @@ class LatticePopulateCopies(lattice2BaseFeature.LatticeFeature):
obj.addProperty("App::PropertyLink","Object","Lattice PopulateCopies","Base object. Can be any generic shape, as well as another lattice object.")
obj.addProperty("App::PropertyEnumeration","Referencing","Lattice PopulateCopies","Reference for array of placements.")
obj.Referencing = ["Origin","First item", "Last item", "Use PlacementsFrom"]
obj.Referencing = REF_MODES
obj.addProperty("App::PropertyLink","PlacementsTo","Lattice PopulateCopies", "Placement or array of placements, containing target locations.")
obj.addProperty("App::PropertyLink","PlacementsFrom", "Lattice PopulateCopies","Placement or array of placements to be treated as origins for PlacementsTo.")
self.assureProperties(obj)
obj.OutputCompounding = "(autosettle)" # this is default value for new features.
def assureProperties(self, obj):
def assureProperties(self, obj, creating_new = False):
'''Adds properties that might be missing, because of loaded project made with older version. Handles version compatibility.'''
super(LatticePopulateCopies, self).assureProperties(obj, creating_new)
propname = 'OutputCompounding'
if not hasattr(obj,propname):
obj.addProperty("App::PropertyEnumeration", propname, "Lattice PopulateCopies","In case single object copy is made, this property controls, if it's packed into compoud or not.")
setattr(obj,propname,["(autosettle)","always", "only if many"])
setattr(obj,propname,"always") # this is to match the old behavior. This is not the default setting for new features.
if creating_new:
obj.OutputCompounding = "(autosettle)" # this is default value for new features.
else:
setattr(obj,propname,"always") # this is to match the old behavior. This is not the default setting for new features.
if self.assureProperty(obj, "App::PropertyEnumeration","Copying", ShapeCopy.copy_types, "Lattice PopulateChildren", "Sets, what method to use for copying shapes."):
self.Copying = ShapeCopy.copy_types[0]
def derivedExecute(self,obj):
self.assureProperties(obj)
# cache stuff
objectShape = screen(obj.Object).Shape
placements = lattice2BaseFeature.getPlacementsList(screen(obj.PlacementsTo), obj)
outputIsLattice = lattice2BaseFeature.isObjectLattice(screen(obj.Object))
# Pre-collect base placement list, if base is a lattice. For speed.
if outputIsLattice:
objectPlms = lattice2BaseFeature.getPlacementsList(screen(obj.Object),obj)
placements = DereferenceArray(obj, placements, screen(obj.PlacementsFrom), obj.Referencing)
placements = DereferenceArray(obj, obj.PlacementsTo, screen(obj.PlacementsFrom), obj.Referencing)
#inherit reference placement from the array being copied
if outputIsLattice:
refplm = None
if obj.Referencing == 'Array\'s reference' or obj.Referencing == 'First item' or obj.Referencing == 'Last item':
#simple cases - we just copy the reference plm from the object
refplm = lattice2BaseFeature.getReferencePlm(obj.Object)
else:
#other cases - apply first transform to reference placement
refplm = lattice2BaseFeature.getReferencePlm(obj.Object)
if refplm is not None and len(placements) > 0:
refplm = placements[0].multiply(refplm)
self.setReferencePlm(obj, refplm)
# initialize output containers and loop variables
outputShapes = [] #output list of shapes
@ -161,18 +183,20 @@ class ViewProviderLatticePopulateCopies(lattice2BaseFeature.ViewProviderLatticeF
def getIcon(self):
if lattice2BaseFeature.isObjectLattice(self.Object):
return getIconPath(
{"Origin":"Lattice2_PopulateCopies_Plms_Normal.svg",
"First item":"Lattice2_PopulateCopies_Plms_Array.svg",
"Last item":"Lattice2_PopulateCopies_Plms_Array.svg",
"Use PlacementsFrom":"Lattice2_PopulateCopies_Plms_Move.svg",
{'Origin':'Lattice2_PopulateCopies_Plms_Normal.svg',
'First item':'Lattice2_PopulateCopies_Plms_Array.svg',
'Last item':'Lattice2_PopulateCopies_Plms_Array.svg',
'Use PlacementsFrom':'Lattice2_PopulateCopies_Plms_Move.svg',
'Array\'s reference':'Lattice2_PopulateCopies_Plms_Ref.svg',
}[self.Object.Referencing]
)
else:
return getIconPath(
{"Origin":"Lattice2_PopulateCopies_Normal.svg",
"First item":"Lattice2_PopulateCopies_Array.svg",
"Last item":"Lattice2_PopulateCopies_Array.svg",
"Use PlacementsFrom":"Lattice2_PopulateCopies_Move.svg",
{'Origin':'Lattice2_PopulateCopies_Normal.svg',
'First item':'Lattice2_PopulateCopies_Array.svg',
'Last item':'Lattice2_PopulateCopies_Array.svg',
'Use PlacementsFrom':'Lattice2_PopulateCopies_Move.svg',
'Array\'s reference':'Lattice2_PopulateCopies_Ref.svg',
}[self.Object.Referencing]
)
@ -199,6 +223,11 @@ def CreateLatticePopulateCopies(name, label, shapeObj, latticeObjFrom, latticeOb
FreeCADGui.doCommand("f.PlacementsTo = App.ActiveDocument."+latticeObjTo.Name)
if latticeObjFrom is not None:
FreeCADGui.doCommand("f.PlacementsFrom = App.ActiveDocument."+latticeObjFrom.Name)
if refmode == 'Auto':
if lattice2BaseFeature.getReferencePlm(latticeObjTo) is not None:
refmode = 'Array\'s reference'
else:
refmode = 'First item'
FreeCADGui.doCommand("f.Referencing = "+repr(refmode))
FreeCADGui.doCommand("f.Label = " + repr(label))
@ -220,72 +249,83 @@ def CreateLatticePopulateCopies(name, label, shapeObj, latticeObjFrom, latticeOb
def throwBody():
raise SelectionError("PartDesign mode", "You can't use population tools on shapes in partdesign body. Use Lattice PartDesign Pattern instead. Or deactivate active body to use populate tools on shapes.")
def cmdPopulate_shapes_nonFromTo(refmode):
def cmdPopulateCopies():
sel = FreeCADGui.Selection.getSelectionEx()
(lattices, shapes) = lattice2BaseFeature.splitSelection(sel)
if activeBody() and len(shapes)>0:
throwBody()
if len(shapes) > 0 and len(lattices) == 1:
FreeCAD.ActiveDocument.openTransaction("Populate with copies")
lattice = lattices[0]
for shape in shapes:
CreateLatticePopulateCopies("Populate",u"Populate "+lattice.Object.Label+u" with "+shape.Object.Label,shape.Object,None,lattice.Object,refmode)
deselect(sel)
FreeCAD.ActiveDocument.commitTransaction()
elif len(shapes) == 1 and len(lattices) > 1:
shape = shapes[0]
FreeCAD.ActiveDocument.openTransaction("Populate with copies")
for lattice in lattices:
CreateLatticePopulateCopies("Populate",u"Populate "+lattice.Object.Label+u" with "+shape.Object.Label,shape.Object,None,lattice.Object,refmode)
deselect(sel)
FreeCAD.ActiveDocument.commitTransaction()
elif len(shapes) == 0 and len(lattices) == 2:
shape = lattices[0]
lattice = lattices[1]
FreeCAD.ActiveDocument.openTransaction("Populate with copies")
CreateLatticePopulateCopies("Populate",u"Populate "+lattice.Object.Label+u" with "+shape.Object.Label,shape.Object,None,lattice.Object,refmode)
deselect(sel)
FreeCAD.ActiveDocument.commitTransaction()
else:
raise SelectionError("Bad selection","Please select some shapes and some arrays, first. You can select multiple shapes and one array, or multiple arrays and one shape.")
def cmdPopulate_shapes_FromTo():
sel = FreeCADGui.Selection.getSelectionEx()
(lattices, shapes) = lattice2BaseFeature.splitSelection(sel)
if activeBody() and len(shapes)>0:
throwBody()
if len(shapes) == 0 and len(sel) >= 3:
shapes = sel[:-2]
lattices = sel[-2:]
if len(shapes) > 0 and len(lattices) == 2:
FreeCAD.ActiveDocument.openTransaction("Populate with copies")
# move shapes from to
FreeCAD.ActiveDocument.openTransaction("Transport")
latticeFrom = lattices[0]
latticeTo = lattices[1]
for shape in shapes:
CreateLatticePopulateCopies("Populate",u"Moved "+shape.Object.Label, shape.Object, latticeFrom.Object, latticeTo.Object,"Use PlacementsFrom")
CreateLatticePopulateCopies("Populate",u"Moved "+shape.Object.Label, shape.Object, latticeFrom.Object, latticeTo.Object,'Use PlacementsFrom')
deselect(sel)
FreeCAD.ActiveDocument.commitTransaction()
elif len(shapes) == 0 and len(lattices) == 3:
# move array from to
FreeCAD.ActiveDocument.openTransaction("Transport")
shape = lattices[0]
latticeFrom = lattices[1]
latticeTo = lattices[2]
for shape in shapes:
CreateLatticePopulateCopies("Populate",u"Moved "+shape.Object.Label, shape.Object, latticeFrom.Object, latticeTo.Object,'Use PlacementsFrom')
deselect(sel)
FreeCAD.ActiveDocument.commitTransaction()
elif len(shapes) > 0 and len(lattices) == 1:
# populate shapes
FreeCAD.ActiveDocument.openTransaction("Populate with copies")
lattice = lattices[0]
for shape in shapes:
CreateLatticePopulateCopies("Populate",u"Populate "+lattice.Object.Label+u" with "+shape.Object.Label,shape.Object,None,lattice.Object,'Auto')
deselect(sel)
FreeCAD.ActiveDocument.commitTransaction()
elif len(shapes) == 0 and (len(lattices) == 2 or len(lattices) > 3):
# populate placements
FreeCAD.ActiveDocument.openTransaction("Populate with copies")
shapes = lattices[:-1]
lattice = lattices[-1]
for shape in shapes:
CreateLatticePopulateCopies("Populate",u"Populate "+lattice.Object.Label+u" with "+shape.Object.Label, shape.Object, None, lattice.Object,'Auto')
deselect(sel)
FreeCAD.ActiveDocument.commitTransaction()
else:
raise SelectionError("Bad selection","Please select either:\n one or more shapes, and two placements/arrays \nor\nthree placements/arrays")
raise SelectionError("Bad selection",
"You selected {n_lattices} arrays of placements and {n_shapes} shapes. This is not supported.\n\n"
"You can select:\n"
" * N shapes and 1 array, to populate that array with the shapes\n"
" * N shapes and 2 arrays, to move shapes from array1 to array2\n"
" * 2 arrays, to populate array2 with array1\n"
" * 3 arrays, to move array1 from array2 to array3\n"
" * 4 or more arrays, to populate last array with all other arrays"
.format(n_lattices= len(lattices), n_shapes= len(shapes))
)
class _CommandLatticePopulateCopies_Normal:
class CommandLatticePopulateCopies:
"Command to create LatticePopulateCopies feature"
def GetResources(self):
return {'Pixmap' : getIconPath("Lattice2_PopulateCopies_Normal.svg"),
'MenuText': QtCore.QT_TRANSLATE_NOOP("Lattice2_PopulateCopies","Populate with copies"),
return {'Pixmap' : getIconPath("Lattice2_PopulateCopies.svg"),
'MenuText': "Populate with copies",
'Accel': "",
'ToolTip': QtCore.QT_TRANSLATE_NOOP("Lattice2_PopulateCopies","Populate with copies: put copies of an object at every placement in an array. Select the object(s) to be copied, and the placement/array.")}
'ToolTip': "Populate with copies: put copies of an object at every placement in an array. Select the object(s) to be copied, and the placement/array."}
def Activated(self):
try:
if len(FreeCADGui.Selection.getSelection())==0:
infoMessage("Populate with copies",
"Populate with copies command. Places a copy of a selected object placed under selected placement.\n\n"+
"Populate with copies command. Places a copy of a selected object at every placement in an array of placements.\n\n"+
"Please select some objects, and a placement/an array of placements. Then invoke the command.\n\n"+
"A copy of object will pe made and placed in local coordinate system of each placement in an array. Placement of the object is taken into account, and becomes a placement in local coordinates of a placement of the array item.")
"You can select:\n"
" * N shapes and 1 array, to populate that array with the shapes\n"
" * N shapes and 2 arrays, to move shapes from array1 to array2\n"
" * 2 arrays, to populate array2 with array1\n"
" * 3 arrays, to move array1 from array2 to array3\n"
" * 4 or more arrays, to populate last array with all other arrays"
)
return
cmdPopulate_shapes_nonFromTo("Origin")
cmdPopulateCopies()
except Exception as err:
msgError(err)
@ -296,85 +336,9 @@ class _CommandLatticePopulateCopies_Normal:
return False
if FreeCAD.GuiUp:
FreeCADGui.addCommand('Lattice2_PopulateCopies_Normal', _CommandLatticePopulateCopies_Normal())
FreeCADGui.addCommand('Lattice2_PopulateCopies', CommandLatticePopulateCopies())
class _CommandLatticePopulateCopies_Array:
"Command to create LatticePopulateCopies feature"
def GetResources(self):
return {'Pixmap' : getIconPath("Lattice2_PopulateCopies_Array.svg"),
'MenuText': QtCore.QT_TRANSLATE_NOOP("Lattice2_PopulateCopies","Populate with copies: Build Array"),
'Accel': "",
'ToolTip': QtCore.QT_TRANSLATE_NOOP("Lattice2_PopulateCopies","Populate with copies: Build Array: poplate placements with copies so that the array passes through original shape. Select the object(s) to be copied, and the placement/array.")}
def Activated(self):
try:
if len(FreeCADGui.Selection.getSelection())==0:
infoMessage("Populate with copies: Build Array",
"Populate with copies: Build Array command. Creates an array of shapes.\n\n"+
"Please select some objects, and the array of placements. Then invoke the command. Object can also be a placement/array.\n\n"+
"Compared to plain 'Populate With copies' command, the placements are treated as being relative to the first placement in the array. As a result, the array built always includes the original object as-is.")
return
cmdPopulate_shapes_nonFromTo("First item")
except Exception as err:
msgError(err)
def IsActive(self):
if FreeCAD.ActiveDocument:
return True
else:
return False
if FreeCAD.GuiUp:
FreeCADGui.addCommand('Lattice2_PopulateCopies_Array', _CommandLatticePopulateCopies_Array())
class _CommandLatticePopulateCopies_Move:
"Command to create LatticePopulateCopies feature"
def GetResources(self):
return {'Pixmap' : getIconPath("Lattice2_PopulateCopies_Move.svg"),
'MenuText': QtCore.QT_TRANSLATE_NOOP("Lattice2_PopulateCopies","Moved object"),
'Accel': "",
'ToolTip': QtCore.QT_TRANSLATE_NOOP("Lattice2_PopulateCopies","Moved object: move object from one placement to another placement. Select the object, placement to move from, and placement to move to. Arrays of placements are accepted.")}
def Activated(self):
try:
if len(FreeCADGui.Selection.getSelection())==0:
infoMessage("Moved Object",
"Moved Object command. Creates a moved copy of a shape.\n\n"+
"The shape is moved from one placement to another placement. Please select some shapes, then placement to move from, and placement to move to (order matters).\n"+
"Placement 'to' can be an array of placements; the array of objects will be created in this case. If 'to' is an array, 'from' can be either a single placement, or an array of matching length.\n\n"+
"Object can itself be an array of placements.")
return
cmdPopulate_shapes_FromTo()
except Exception as err:
msgError(err)
def IsActive(self):
if FreeCAD.ActiveDocument:
return True
else:
return False
if FreeCAD.GuiUp:
FreeCADGui.addCommand('Lattice2_PopulateCopies_Move', _CommandLatticePopulateCopies_Move())
class _CommandLatticePopulateCopiesGroup:
def GetCommands(self):
return ("Lattice2_PopulateCopies_Normal","Lattice2_PopulateCopies_Array","Lattice2_PopulateCopies_Move")
def GetDefaultCommand(self): # return the index of the tuple of the default command.
return 0
def GetResources(self):
return { 'MenuText': 'Populate with copies:',
'ToolTip': 'Populate with copies: put a copy of an object at every placement in an array of placements.'}
def IsActive(self): # optional
return True
if FreeCAD.GuiUp:
FreeCADGui.addCommand('Lattice2_PopulateCopiesGroupCommand',_CommandLatticePopulateCopiesGroup())
exportedCommands = ['Lattice2_PopulateCopiesGroupCommand']
exportedCommands = ['Lattice2_PopulateCopies']
# -------------------------- /Gui command --------------------------------------------------

View File

@ -64,22 +64,38 @@ class LatticeProjectArray(lattice2BaseFeature.LatticeFeature):
obj.addProperty("App::PropertyEnumeration","Multisolution","Lattice ProjectArray","Specify the way of dealing with multiple solutions of projection")
obj.Multisolution = ['use first','use all']
def assureProperties(self, selfobj, creating_new = False):
super(LatticeProjectArray, self).assureProperties(selfobj, creating_new)
created = self.assureProperty(selfobj,
'App::PropertyEnumeration',
'ReferencePlacementOption',
['external', 'origin', 'inherit', 'projected'],
"Lattice ProjectArray",
"Reference placement, corresponds to the original occurrence of the object to be populated."
)
if created:
selfobj.ReferencePlacementOption = 'inherit'
def recomputeReferencePlm(self, selfobj, selfplacements): #override
if selfobj.ReferencePlacementOption == 'external':
super(LinearProjectArray, self).recomputeReferencePlm(selfobj, selfplacements)
#the remaining options are handled in derivedExecute
def derivedExecute(self,obj):
#validity check
if not lattice2BaseFeature.isObjectLattice(screen(obj.Base)):
lattice2Executer.warning(obj,"A lattice object is expected as Base, but a generic shape was provided. It will be treated as a lattice object; results may be unexpected.")
toolShape = screen(obj.Tool).Shape
if lattice2BaseFeature.isObjectLattice(screen(obj.Tool)):
lattice2Executer.warning(obj,"A lattice object was provided as Tool. It will be converted into points; orientations will be ignored.")
leaves = LCE.AllLeaves(toolShape)
points = [Part.Vertex(leaf.Placement.Base) for leaf in leaves]
lattice2Executer.warning(obj, "A lattice object was provided as Tool. It will be converted into points; orientations will be ignored.")
plms = lattice2BaseFeature.getPlacementsList(obj.Tool)
points = [Part.Vertex(plm.Base) for plm in plms]
toolShape = Part.makeCompound(points)
leaves = LCE.AllLeaves(screen(obj.Base).Shape)
input = [leaf.Placement for leaf in leaves]
ref = obj.ReferencePlacementOption
input = lattice2BaseFeature.getPlacementsList(obj.Base, obj)
if ref == 'projected':
input.append(lattice2BaseFeature.getReferencePlm(obj.Base))
output = [] #variable to receive the final list of placements
@ -170,6 +186,17 @@ class LatticeProjectArray(lattice2BaseFeature.LatticeFeature):
if not isMultiSol:
break
if ref == 'external':
pass
elif ref == 'origin':
self.setReferencePlm(obj, None)
elif ref == 'inherit':
self.setReferencePlm(obj, lattice2BaseFeature.getReferencePlm(obj.Base))
elif ref == 'projected':
self.setReferencePlm(obj, output.pop())
else:
raise NotImplementedError("Reference option not implemented: " + ref)
return output
@ -185,11 +212,11 @@ class ViewProviderProjectArray(lattice2BaseFeature.ViewProviderLatticeFeature):
def CreateLatticeProjectArray(name):
sel = FreeCADGui.Selection.getSelectionEx()
# selection order independence logic (lattice object and generic shape stencil can be told apart)
# selection order independece logic (lattice object and generic shape stencil can be told apart)
iLtc = 0 #index of lattice object in selection
iStc = 1 #index of stencil object in selection
for i in range(0,len(sel)):
if lattice2BaseFeature.isObjectLattice(sel[i]):
if lattice2BaseFeature.isObjectLattice(sel[i].Object):
iLtc = i
iStc = i-1 #this may give negative index, but python accepts negative indexes
break

View File

@ -365,6 +365,7 @@ class _CommandTouch:
def GetResources(self):
return {'Pixmap' : getIconPath("Lattice2_RecomputeLocker_Touch.svg"),
'MenuText': QtCore.QT_TRANSLATE_NOOP("Lattice2_RecomputeLocker","Touch selected features"),
'Accel': "Shift+F5",
'ToolTip': QtCore.QT_TRANSLATE_NOOP("Lattice2_RecomputeLocker","Touch selected features: mark selected features as needing recomputing."),
'CmdType':"ForEdit"}
@ -406,14 +407,8 @@ exportedCommands = [
"Lattice2_RecomputeLocker_ForceRecompute",
"Lattice2_RecomputeLocker_Touch"
]
try:
if float(App.Version()[1]) >= 17.0:
exportedCommands.remove("Lattice2_RecomputeLocker_MakeFeature")
except Exception as err:
App.Console.PrintWarning("Failed to parse version string: {v}".format(v= App.Version()[1]))
#assume modern
if int(App.Version()[1]) >= 17:
exportedCommands.remove("Lattice2_RecomputeLocker_MakeFeature")
class CommandRecomputeGroup:
def GetCommands(self):

View File

@ -106,7 +106,7 @@ class LatticeResample(lattice2BaseFeature.LatticeFeature):
QArrays[iQ].append( Q[iQ] )
prevQ = Q
# construct function objects
# constuct function objects
if posIsInterpolate:
FX = LIU.InterpolateF(IArray,XArray)
FY = LIU.InterpolateF(IArray,YArray)

257
lattice2Resample2.py Normal file
View File

@ -0,0 +1,257 @@
#***************************************************************************
#* *
#* Copyright (c) 2018 - Victor Titov (DeepSOIC) *
#* <vv.titov@gmail.com> *
#* *
#* This program is free software; you can redistribute it and/or modify *
#* it under the terms of the GNU Lesser General Public License (LGPL) *
#* as published by the Free Software Foundation; either version 2 of *
#* the License, or (at your option) any later version. *
#* for detail see the LICENCE text file. *
#* *
#* This program is distributed in the hope that it will be useful, *
#* but WITHOUT ANY WARRANTY; without even the implied warranty of *
#* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the *
#* GNU Library General Public License for more details. *
#* *
#* You should have received a copy of the GNU Library General Public *
#* License along with this program; if not, write to the Free Software *
#* Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 *
#* USA *
#* *
#***************************************************************************
__title__="Lattice Resample object: changes the number of placements in an array, maintaining overall path. Aka interpolation."
__author__ = "DeepSOIC"
__url__ = ""
import FreeCAD as App
import Part
from lattice2Common import *
import lattice2BaseFeature
import lattice2InterpolatorUtil as LIU
import lattice2Executer
from lattice2ValueSeriesGenerator import ValueSeriesGenerator
# -------------------------- document object --------------------------------------------------
def dotProduct(list1,list2):
sum = 0
for i in range(0,len(list1)):
sum += list1[i]*list2[i]
return sum
def makeLatticeResample(name):
'''makeLatticeResample(name): makes a LatticeResample object.'''
return lattice2BaseFeature.makeLatticeFeature(name, LatticeResample, ViewProviderLatticeResample)
class LatticeResample(lattice2BaseFeature.LatticeFeature):
"The Lattice Resample object"
def derivedInit(self,selfobj):
selfobj.addProperty("App::PropertyLink","Base","Lattice Resample","Lattice, the array of placements to be interpolated.")
selfobj.addProperty("App::PropertyEnumeration","TranslateMode","Lattice Resample","What to do with translation part of placements")
selfobj.TranslateMode = ['interpolate', 'reset']
selfobj.TranslateMode = 'interpolate'
selfobj.addProperty("App::PropertyEnumeration","OrientMode","Lattice Resample","what to do with orientation part of placements")
selfobj.OrientMode = ['interpolate', 'reset']
selfobj.OrientMode = 'interpolate'
self.assureGenerator(selfobj)
selfobj.ValuesSource = "Generator"
selfobj.GeneratorMode = "SpanN"
selfobj.EndInclusive = True
selfobj.SpanStart = 0.0
selfobj.SpanEnd = 1.0
selfobj.Step = 1.0/51.0
selfobj.Count = 51
def assureProperties(self, selfobj, creating_new = False):
super(LatticeResample, self).assureProperties(selfobj, creating_new)
created = self.assureProperty(selfobj,
'App::PropertyEnumeration',
'ReferencePlacementOption',
['external', 'origin', 'inherit', 'SpanStart', 'SpanEnd', 'at custom value', 'first placement', 'last placement'],
"Lattice Resample",
"Reference placement, corresponds to the original occurrence of the object to be populated."
)
if created:
selfobj.ReferencePlacementOption = 'SpanStart'
self.assureProperty(selfobj, 'App::PropertyFloat', 'ReferenceValue', 0.0, "Lattice Resample", "Sets the value to use for generating ReferencePlacement. This value sets, what coordinate the object to be populated corresponds to.")
def assureGenerator(self, selfobj):
'''Adds an instance of value series generator, if one doesn't exist yet.'''
if hasattr(self,"generator"):
return
self.generator = ValueSeriesGenerator(selfobj)
self.generator.addProperties(groupname= "Lattice Array",
groupname_gen= "Lattice Series Generator",
valuesdoc= "List of parameter values. Values should be in range 0..n-1 for interpolation, and can be outside for extrapolation.",
valuestype= "App::PropertyFloat")
def updateReadonlyness(self, selfobj):
super(LatticeResample, self).updateReadonlyness(selfobj)
self.assureGenerator(selfobj)
self.generator.updateReadonlyness()
selfobj.setEditorMode('ReferenceValue', 0 if selfobj.ReferencePlacementOption == 'at custom value' else 2)
def recomputeReferencePlm(self, selfobj, selfplacements): #override
if selfobj.ReferencePlacementOption == 'external':
super(LatticeResample, self).recomputeReferencePlm(selfobj, selfplacements)
#the remaining options are handled in derivedExecute
def derivedExecute(self,selfobj):
self.assureGenerator(selfobj)
self.generator.execute()
values = [float(strv) for strv in selfobj.Values]
input = lattice2BaseFeature.getPlacementsList(selfobj.Base)
if len(input) < 2:
raise ValueError("At least 2 placements ar needed to interpolate; there are just "+str(len(input))+" in base array.")
#cache mode comparisons, for speed
posIsInterpolate = selfobj.TranslateMode == 'interpolate'
posIsReset = selfobj.TranslateMode == 'reset'
oriIsInterpolate = selfobj.OrientMode == 'interpolate'
oriIsReset = selfobj.OrientMode == 'reset'
# construct interpolation functions
# prepare lists of input samples
IArray = [float(i) for i in range(0,len(input))]
XArray = [plm.Base.x for plm in input]
YArray = [plm.Base.y for plm in input]
ZArray = [plm.Base.z for plm in input]
QArrays = [[],[],[],[]]
prevQ = [0.0]*4
for plm in input:
Q = plm.Rotation.Q
#test if quaernion has changed sign compared to previous one.
# Quaternions of opposite sign are equivalent in terms of rotation,
# but sign changes confuse interpolation, so we are detecting sign
# changes and discarding them
if dotProduct(Q,prevQ) < -ParaConfusion:
Q = [-v for v in Q]
for iQ in [0,1,2,3]:
QArrays[iQ].append( Q[iQ] )
prevQ = Q
# constuct function objects
if posIsInterpolate:
FX = LIU.InterpolateF(IArray,XArray)
FY = LIU.InterpolateF(IArray,YArray)
FZ = LIU.InterpolateF(IArray,ZArray)
if oriIsInterpolate:
FQs = []
for iQ in [0,1,2,3]:
FQs.append(LIU.InterpolateF(IArray,QArrays[iQ]))
def plmByVal(val):
pos = App.Vector()
ori = App.Rotation()
if posIsInterpolate:
pos = App.Vector(FX.value(val), FY.value(val), FZ.value(val))
if oriIsInterpolate:
ori = App.Rotation(FQs[0].value(val),
FQs[1].value(val),
FQs[2].value(val),
FQs[3].value(val))
return App.Placement(pos, ori)
output = [plmByVal(val) for val in values]
# update reference placement
ref = selfobj.ReferencePlacementOption
if ref == 'external':
pass
elif ref == 'origin':
self.setReferencePlm(selfobj, None)
elif ref == 'inherit':
self.setReferencePlm(selfobj, lattice2BaseFeature.getReferencePlm(selfobj.Base))
elif ref == 'SpanStart':
self.setReferencePlm(selfobj, plmByVal(float(selfobj.SpanStart)))
elif ref == 'SpanEnd':
self.setReferencePlm(selfobj, plmByVal(float(selfobj.SpanEnd)))
elif ref == 'at custom value':
self.setReferencePlm(selfobj, plmByVal(float(selfobj.ReferenceValue)))
elif ref == 'first placement':
self.setReferencePlm(selfobj, output[0])
elif ref == 'last placement':
self.setReferencePlm(selfobj, output[-1])
else:
raise NotImplementedError("Reference option not implemented: " + ref)
return output
class ViewProviderLatticeResample(lattice2BaseFeature.ViewProviderLatticeFeature):
def getIcon(self):
return getIconPath('Lattice2_Resample.svg')
def claimChildren(self):
return [screen(self.Object.Base)]
# -------------------------- /document object --------------------------------------------------
# -------------------------- Gui command --------------------------------------------------
def CreateLatticeResample(name):
sel = FreeCADGui.Selection.getSelectionEx()
FreeCAD.ActiveDocument.openTransaction("Create LatticeResample")
FreeCADGui.addModule("lattice2Resample2")
FreeCADGui.addModule("lattice2Executer")
FreeCADGui.doCommand("f = lattice2Resample2.makeLatticeResample(name='"+name+"')")
FreeCADGui.doCommand("f.Base = App.ActiveDocument."+sel[0].ObjectName)
FreeCADGui.doCommand("f.setExpression('SpanEnd', '{base}.NumElements - 1')".format(base= lattice2BaseFeature.source(sel[0].Object)[1].Name))
FreeCADGui.doCommand("for child in f.ViewObject.Proxy.claimChildren():\n"+
" child.ViewObject.hide()")
FreeCADGui.doCommand("lattice2Executer.executeFeature(f)")
FreeCADGui.doCommand("f = None")
FreeCAD.ActiveDocument.commitTransaction()
class _CommandLatticeResample:
"Command to create LatticeResample feature"
def GetResources(self):
return {'Pixmap' : getIconPath("Lattice2_Resample.svg"),
'MenuText': QtCore.QT_TRANSLATE_NOOP("Lattice2_Resample","Resample Array"),
'Accel': "",
'ToolTip': QtCore.QT_TRANSLATE_NOOP("Lattice2_Resample","Lattice Resample: interpolate placement-path using 3-rd degree b-spline interpolation.")}
def Activated(self):
try:
if len(FreeCADGui.Selection.getSelection()) == 1 :
CreateLatticeResample(name = "Resample")
else:
infoMessage(
"Lattice Resample command. Interpolates an array of placements, using 3-rd dergee bsplines.\n\n"
"Please select one object, first. The object must be an array of placements."
)
except Exception as err:
msgError(err)
def IsActive(self):
if FreeCAD.ActiveDocument:
return True
else:
return False
if FreeCAD.GuiUp:
FreeCADGui.addCommand('Lattice2_Resample2', _CommandLatticeResample())
exportedCommands = ['Lattice2_Resample2']
# -------------------------- /Gui command --------------------------------------------------

View File

@ -1,183 +0,0 @@
#***************************************************************************
#* *
#* Copyright (c) 2019 - Victor Titov (DeepSOIC) *
#* <vv.titov@gmail.com> *
#* *
#* This program is free software; you can redistribute it and/or modify *
#* it under the terms of the GNU Lesser General Public License (LGPL) *
#* as published by the Free Software Foundation; either version 2 of *
#* the License, or (at your option) any later version. *
#* for detail see the LICENCE text file. *
#* *
#* This program is distributed in the hope that it will be useful, *
#* but WITHOUT ANY WARRANTY; without even the implied warranty of *
#* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the *
#* GNU Library General Public License for more details. *
#* *
#* You should have received a copy of the GNU Library General Public *
#* License along with this program; if not, write to the Free Software *
#* Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 *
#* USA *
#* *
#***************************************************************************
__title__="Lattice ScLERP object: interpolation between two placements."
__author__ = "DeepSOIC"
import FreeCAD as App
import Part
from lattice2Common import *
import lattice2BaseFeature
import lattice2Executer
from lattice2ValueSeriesGenerator import ValueSeriesGenerator
# -------------------------- document object --------------------------------------------------
def makeLatticeScLERP(name):
'''makeLatticeScLERP(name): makes a LatticeScLERP object.'''
return lattice2BaseFeature.makeLatticeFeature(name, LatticeScLERP, ViewProviderLatticeScLERP)
class LatticeScLERP(lattice2BaseFeature.LatticeFeature):
"The Lattice ScLERP object"
def derivedInit(self,host):
host.addProperty("App::PropertyLink","Placement1Ref","Lattice ScLERP","First placement, or an array of two placements to interpolate between.")
host.addProperty("App::PropertyLink","Placement2Ref","Lattice ScLERP","Second placement to interpolate between.")
host.addProperty("App::PropertyBool","Shorten","Lattice ScLERP","Use shortest path. (if not, and angle difference of two placement exceeds 180 degrees, longer path will be taken)")
host.Shorten = True
self.assureGenerator(host)
host.ValuesSource = "Generator"
host.GeneratorMode = "SpanN"
host.EndInclusive = True
host.SpanStart = 0.0
host.SpanEnd = 1.0
host.Step = 1.0/11
host.Count = 11
def assureGenerator(self, host):
'''Adds an instance of value series generator, if one doesn't exist yet.'''
if hasattr(self,"generator"):
return
self.generator = ValueSeriesGenerator(host)
self.generator.addProperties(groupname= "Lattice Array",
groupname_gen= "Lattice Series Generator",
valuesdoc= "List of parameter values. Values should be in range 0..1 for interpolation, and can be outside for extrapolation.",
valuestype= "App::PropertyFloat")
def updateReadonlyness(self, host):
super(LatticeScLERP, self).updateReadonlyness(host)
self.assureGenerator(host)
self.generator.updateReadonlyness()
#host.setEditorMode('ReferenceValue', 0 if host.ReferencePlacementOption == 'at custom value' else 2)
def derivedExecute(self,host):
self.assureGenerator(host)
self.generator.execute()
values = [float(strv) for strv in host.Values]
input = lattice2BaseFeature.getPlacementsList(host.Placement1Ref)
if host.Placement2Ref is not None:
input.extend(lattice2BaseFeature.getPlacementsList(host.Placement2Ref))
if len(input) != 2:
raise ValueError("Need exactly 2 placements. {n} provided.".format(n= len(input)))
plm1, plm2 = input
# construct interpolation functions
# prepare lists of input samples
def plmByVal(val):
return plm1.sclerp(plm2, val, host.Shorten)
output = [plmByVal(val) for val in values]
## update reference placement
#ref = host.ReferencePlacementOption
#if ref == 'external':
# pass
#elif ref == 'origin':
# self.setReferencePlm(host, None)
#elif ref == 'inherit':
# self.setReferencePlm(host, lattice2BaseFeature.getReferencePlm(host.Base))
#elif ref == 'SpanStart':
# self.setReferencePlm(host, plmByVal(float(host.SpanStart)))
#elif ref == 'SpanEnd':
# self.setReferencePlm(host, plmByVal(float(host.SpanEnd)))
#elif ref == 'at custom value':
# self.setReferencePlm(host, plmByVal(float(host.ReferenceValue)))
#elif ref == 'first placement':
# self.setReferencePlm(host, output[0])
#elif ref == 'last placement':
# self.setReferencePlm(host, output[-1])
#else:
# raise NotImplementedError("Reference option not implemented: " + ref)
#
return output
class ViewProviderLatticeScLERP(lattice2BaseFeature.ViewProviderLatticeFeature):
def getIcon(self):
return getIconPath('Lattice2_Resample.svg')
def claimChildren(self):
return [child for child in [self.Object.Placement1Ref,self.Object.Placement2Ref] if child is not None]
# -------------------------- /document object --------------------------------------------------
# -------------------------- Gui command --------------------------------------------------
def CreateLatticeScLERP(name):
sel = FreeCADGui.Selection.getSelectionEx()
FreeCAD.ActiveDocument.openTransaction("Create LatticeScLERP")
FreeCADGui.addModule("lattice2ScLERP")
FreeCADGui.addModule("lattice2Executer")
FreeCADGui.doCommand("f = lattice2ScLERP.makeLatticeScLERP(name='"+name+"')")
FreeCADGui.doCommand("f.Placement1Ref = App.ActiveDocument."+sel[0].ObjectName)
if len(sel) > 1:
FreeCADGui.doCommand("f.Placement2Ref = App.ActiveDocument."+sel[1].ObjectName)
FreeCADGui.doCommand("lattice2Executer.executeFeature(f)")
FreeCAD.ActiveDocument.commitTransaction()
class CommandLatticeScLERP:
"Command to create LatticeScLERP feature"
def GetResources(self):
return {'Pixmap' : getIconPath("Lattice2_ScLERP.svg"),
'MenuText': "Helical interpolation (ScLERP)",
'Accel': "",
'ToolTip': "Helical interpolation (ScLERP): divides the move from one placement to another into a number of equal-transform steps."}
def Activated(self):
try:
if len(FreeCADGui.Selection.getSelection()) in [1, 2] :
CreateLatticeScLERP(name = "ScLERP")
else:
infoMessage(
"Helical interpolation (ScLERP)",
"Lattice Helical interpolation (ScLERP) command. Interpolates between two placements using ScLERP."
" It cretes a helical path between two placements, so that the placement moves and rotates by an"
" equal transform for each step. \n\n"
"Please select two placements, first. It can be two placements in one object, or two single placement objects."
)
except Exception as err:
msgError(err)
def IsActive(self):
if FreeCAD.ActiveDocument:
return True
else:
return False
if FreeCAD.GuiUp:
FreeCADGui.addCommand('Lattice2_ScLERP', CommandLatticeScLERP())
exportedCommands = ['Lattice2_ScLERP'] if hasattr(App.Placement, 'sclerp') else []
# -------------------------- /Gui command --------------------------------------------------

View File

@ -23,7 +23,7 @@
__title__= "Lattice ShapeInfo feature for FreeCAD"
__author__ = "DeepSOIC"
__doc__ = "Shape info feature is for getting info on a shape and exposing it in form of properties, that are usable from expressions."
__doc__ = "Shape info feature is for getting info on a shape and exposing it in form of properties, that are useable from expressions."
from lattice2Common import *
import lattice2BaseFeature as LBF

View File

@ -38,7 +38,7 @@ __url__ = ""
def findFont(font_file_name):
'''checks for existence of the file in a few locations and returns the full path of the first one found'''
'''checks for existance of the file in a few locations and returns the full path of the first one found'''
import os
@ -105,7 +105,7 @@ class LatticeShapeString:
self.makeFoolObj(obj)
foolObj = self.foolObj
#add Draft ShapeString's properties to document object in possession of our LatticeShapeString
#add Draft ShapeString's properties to document object in posession of our LatticeShapeString
for (proptype, propname, group, hint) in foolObj.properties:
if propname != "String": #we'll define our own string property
obj.addProperty(proptype,propname,"Lattice ShapeString",hint)
@ -219,10 +219,8 @@ class LatticeShapeString:
scale = 1.0
obj.Shape = markers.getNullShapeShape(scale)
raise ValueError('No strings were converted into shapes') #Feeding empty compounds to FreeCAD seems to cause rendering issues, otherwise it would have been a good idea to output nothing.
result = Part.makeCompound(shapes)
result.Placement = obj.Placement
obj.Shape = result
obj.Shape = Part.makeCompound(shapes)
def __getstate__(self):
return None
@ -256,13 +254,9 @@ def CreateLatticeShapeString(name):
FreeCAD.ActiveDocument.openTransaction("Create LatticeShapeString")
FreeCADGui.addModule("lattice2ShapeString")
FreeCADGui.addModule("lattice2Executer")
FreeCADGui.addModule("lattice2Base.Autosize")
FreeCADGui.doCommand("f = lattice2ShapeString.makeLatticeShapeString(name='"+name+"')")
if len(sel) == 1:
FreeCADGui.doCommand("f.ArrayLink = FreeCADGui.Selection.getSelection()[0]")
else:
FreeCADGui.doCommand("f.Placement.Base = lattice2Base.Autosize.convenientPosition()")
FreeCADGui.doCommand("f.Size = lattice2Base.Autosize.convenientModelSize()/10")
FreeCADGui.doCommand("lattice2Executer.executeFeature(f)")
FreeCADGui.doCommand("f = None")
FreeCAD.ActiveDocument.commitTransaction()

View File

@ -58,7 +58,7 @@ class LatticeSubLink:
self.assureProperties(obj)
def assureProperties(self, selfobj):
assureProperty(selfobj, "App::PropertyEnumeration","Looping", ["Single"] + LSS.LOOP_MODES, "Lattice SubLink", "Sets whether to collect just the element, or all similar from array.")
assureProperty(selfobj, "App::PropertyEnumeration","Looping", ["Single"] + LSS.LOOP_MODES, "Lattice SubLink", "Sets wether to collect just the element, or all similar from array.")
assureProperty(selfobj, "App::PropertyEnumeration","CompoundTraversal", LSS.TRAVERSAL_MODES, "Lattice SubLink", "Sets how to unpack compounds if Looping is not 'Single'.")
assureProperty(selfobj, "App::PropertyLinkSub", "SubLink", sublinkFromApart(screen(selfobj.Object), selfobj.SubNames), "Lattice SubLink", "Mirror of Object+SubNames properties")

View File

@ -88,7 +88,7 @@ element_extractors = {
def getIndexesIntoList(element, list_of_shapes):
"""Finds this element in shapes in list_of_shapes. This is a generator function (to be
used if there are multiple occurrences of the element).
used if there are multiple occurences of the element).
[(index_into_list, element_type_string, subelement_index), ...]"""
element_type_string = element.ShapeType

View File

@ -105,7 +105,7 @@ class CommandSubstituteObject:
except Exception as err:
mb = QtGui.QMessageBox()
mb.setIcon(mb.Icon.Warning)
mb.setText(translate("Lattice2_SubstituteObject", "An error occurred while substituting object:", None)+ u"\n"
mb.setText(translate("Lattice2_SubstituteObject", "An error occured while substituting object:", None)+ u"\n"
+ str(err))
mb.setWindowTitle(translate("Lattice2_SubstituteObject","Error", None))
mb.exec_()

View File

@ -1,7 +1,7 @@
#***************************************************************************
#* *
#* Copyright (c) 2015 - Victor Titov (DeepSOIC) *
#* <vv.titov@gmail.com> *
#* <vv.titov@gmail.com> *
#* *
#* This program is free software; you can redistribute it and/or modify *
#* it under the terms of the GNU Lesser General Public License (LGPL) *
@ -44,26 +44,26 @@ class ValueSeriesGenerator:
self.gen_laws = ['Linear','Exponential']
self.alignment_modes = ['Low', 'Center', 'High', 'Justify', 'Mirrored']
self.readonlynessDict = {} # key = property name (string). Value = boolean (True == writable, non-readonly). Stores property read-only status requested by external code.
def addProperties(self, groupname, groupname_gen, valuesdoc, valuestype = 'App::PropertyFloat'):
# _addProperty(proptype , propname , defvalue, group, tooltip)
# first, try to guess interface version. If we are re-adding properties to old feature,
# it already has some other properties, but not Version. So we should default to 0
# in this case. Otherwise the Version property already exists, so default doesn't matter;
# first, try to guess interface version. If we are re-adding properties to old feature,
# it already has some other properties, but not Version. So we should default to 0
# in this case. Therwise the Version property already exists, so default desn't matter;
# or we are creating a new generator, so default to 1.
self._addProperty("App::PropertyInteger" ,"VSGVersion" , 0 if hasattr(self.documentObject, "Values") else 1 , groupname_gen, "Interface version")
self.documentObject.setEditorMode("VSGVersion", 2) #hide this property
self._addProperty("App::PropertyStringList" ,"Values" , None, groupname, valuesdoc)
self._addProperty("App::PropertyEnumeration","ValuesSource" , self.source_modes, groupname, "Select where to take the value series from.")
self._addProperty("App::PropertyLink" ,"SpreadsheetLink", None, groupname, "Link to spreadsheet to take values from.")
self._addProperty("App::PropertyString" ,"CellStart" , 'A1', groupname, "Starting cell of first value (the rest are scanned downwards till an empty cell is encountered)")
self._addProperty("App::PropertyEnumeration","GeneratorMode" , self.gen_modes, groupname_gen,"")
self._addProperty("App::PropertyEnumeration","DistributionLaw", self.gen_laws, groupname_gen,"")
self._addProperty(valuestype ,"SpanStart" , 1.0, groupname_gen, "Starting value for value series generator")
self._addProperty(valuestype ,"SpanEnd" , 7.0, groupname_gen, "Ending value for value series generator")
self._addProperty("App::PropertyBool" ,"EndInclusive" , True, groupname_gen, "If True, the last value in series will equal SpanEnd. If False, the value equal to SpanEnd will be dropped.")
@ -71,7 +71,7 @@ class ValueSeriesGenerator:
self._addProperty("App::PropertyFloat" ,"Step" , 1.0, groupname_gen, "Step for value generator. For exponential law, it is a natural logarithm of change ratio.") # using float for Step, because step's unit depends n selected distribution law
self._addProperty("App::PropertyFloat" ,"Count" , 7.0, groupname_gen, "Number of values to generate")
self._addProperty("App::PropertyFloat" ,"Offset" , 0.0, groupname_gen, "Extra offset for the series, expressed as fraction of step.")
def _addProperty(self, proptype, propname, defvalue, group, tooltip):
if hasattr(self.documentObject, propname):
return
@ -83,15 +83,15 @@ class ValueSeriesGenerator:
obj = self.documentObject
m = obj.GeneratorMode
src = obj.ValuesSource
self._setPropertyWritable("Values" , src == "Values Property" )
self._setPropertyWritable("ValuesSource" , True )
self._setPropertyWritable("SpreadsheetLink" , src == "Spreadsheet" )
self._setPropertyWritable("CellStart" , src == "Spreadsheet" )
self._setPropertyWritable("GeneratorMode" , not self.isPropertyControlledByGenerator("GeneratorMode" ) )
self._setPropertyWritable("DistributionLaw" , not self.isPropertyControlledByGenerator("DistributionLaw") )
self._setPropertyWritable("SpanStart" , not self.isPropertyControlledByGenerator("SpanStart" ) )
self._setPropertyWritable("SpanEnd" , not self.isPropertyControlledByGenerator("SpanEnd" ) )
self._setPropertyWritable("EndInclusive" , not self.isPropertyControlledByGenerator("EndInclusive" ) )
@ -99,19 +99,19 @@ class ValueSeriesGenerator:
self._setPropertyWritable("Step" , not self.isPropertyControlledByGenerator("Step" ) )
self._setPropertyWritable("Count" , not self.isPropertyControlledByGenerator("Count" ) )
self._setPropertyWritable("Offset" , not self.isPropertyControlledByGenerator("Offset" ) )
def isPropertyControlledByGenerator(self, propname):
obj = self.documentObject
if not hasattr(obj, propname):
raise AttributeError(obj.Name+": has no property named "+propname)
m = obj.GeneratorMode
genOn = obj.ValuesSource == "Generator"
if not genOn:
return False
return False
if propname == "SpanStart":
if propname == "SpanStart":
return False
elif propname == "SpanEnd":
return False
@ -123,21 +123,21 @@ class ValueSeriesGenerator:
return False
def setPropertyWritable(self, propname, bool_writable):
'''setPropertyWritable(self, propname, bool_writable): Use to force a property read-only
(for example, when the property is driven by a link). If set to be writable, the read-onlyness
'''setPropertyWritable(self, propname, bool_writable): Use to force a property read-only
(for example, when the property is driven by a link). If set to be writable, the read-onlyness
will be set according to series generator logic.'''
self.readonlynessDict[propname] = bool_writable
def _setPropertyWritable(self, propname, bool_writable, suppress_warning = False):
if propname in self.readonlynessDict:
bool_writable = bool_writable and self.readonlynessDict[propname]
bool_writable = bool_writable and self.readonlynessDict[propname]
self.documentObject.setEditorMode(propname, 0 if bool_writable else 1)
def execute(self):
obj = self.documentObject #shortcut
values = [] #list to be filled with values, that are giong to be written to obj.Values
if obj.ValuesSource == "Generator":
#read out span and convert it to linear law
if obj.DistributionLaw == 'Linear':
@ -147,13 +147,13 @@ class ValueSeriesGenerator:
elif obj.DistributionLaw == 'Exponential':
vSign = 1 if obj.SpanStart > 0.0 else -1.0
vStart = math.log(obj.SpanStart * vSign)
if obj.SpanEnd * vSign < ParaConfusion:
if obj.SpanEnd * vSign < ParaConfusion:
raise ValueError(obj.Name+": Wrong SpanEnd value. It is either zero, or of different sign compared to SpanStart. In exponential distribution, it is not allowed.")
vEnd = math.log(obj.SpanEnd * vSign)
vStep = float(obj.Step)
else:
raise ValueError(obj.Name+": distribution law not implemented: "+obj.DistributionLaw)
if obj.GeneratorMode == 'SpanN':
n = obj.Count
if obj.EndInclusive:
@ -188,16 +188,16 @@ class ValueSeriesGenerator:
pass
else:
raise ValueError(obj.Name+": Generator mode "+obj.GeneratorMode+" is not implemented")
# Generate the actual array. We can use Step and N directly to
# Generate the actual array. We can use Step and N directly to
# completely avoid mode logic, since we had updated them
# cache properties into variables
# vStart,vEnd are already in sync
vStep = float(obj.Step)
vOffset = float(obj.Offset)
n = int(obj.Count)
# Generate the values
if obj.GeneratorMode == 'Random':
import random
@ -221,9 +221,9 @@ class ValueSeriesGenerator:
if n_tmp == 0:
n_tmp = 1 #justify failed!
vStep_justified = (vEnd - vStart)/n_tmp
list_evenDistrib = [vStart + vOffset*vStep + alignment_offset + vStep_justified*i for i in range(0, n)]
#post-process alignment
if obj.Alignment == "Mirrored":
new_list = []
@ -232,7 +232,7 @@ class ValueSeriesGenerator:
if abs(v) > 1e-12:
new_list.append(-v)
list_evenDistrib = new_list
if obj.DistributionLaw == 'Linear':
values = list_evenDistrib
elif obj.DistributionLaw == 'Exponential':
@ -250,7 +250,7 @@ class ValueSeriesGenerator:
col = addr[0:1]
row = addr[1:]
row = int(row)
#loop until the value can't be read out
values = []
spsh = screen(obj.SpreadsheetLink)
@ -264,7 +264,8 @@ class ValueSeriesGenerator:
pass
else:
raise ValueError(obj.Name+": values source mode not implemented: "+obj.ValuesSource)
# finally. Fill in the values.
if obj.ValuesSource != "Values Property":
obj.Values = [str(v) for v in values]

View File

@ -1,203 +0,0 @@
#***************************************************************************
#* *
#* Copyright (c) 2015 - Victor Titov (DeepSOIC) *
#* <vv.titov@gmail.com> *
#* *
#* This program is free software; you can redistribute it and/or modify *
#* it under the terms of the GNU Lesser General Public License (LGPL) *
#* as published by the Free Software Foundation; either version 2 of *
#* the License, or (at your option) any later version. *
#* for detail see the LICENCE text file. *
#* *
#* This program is distributed in the hope that it will be useful, *
#* but WITHOUT ANY WARRANTY; without even the implied warranty of *
#* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the *
#* GNU Library General Public License for more details. *
#* *
#* You should have received a copy of the GNU Library General Public *
#* License along with this program; if not, write to the Free Software *
#* Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 *
#* USA *
#* *
#***************************************************************************
__title__="Lattice Recompute Locker: hack to manual recompute of documents."
__author__ = "DeepSOIC"
__url__ = ""
import FreeCAD as App
if App.GuiUp:
import FreeCADGui as Gui
from lattice2Common import *
import lattice2BaseFeature as LBF
import lattice2CompoundExplorer as LCE
try:
import Show
except ImportError:
Show = None
try:
from Show.SceneDetail import SceneDetail
except ImportError:
SceneDetail = None
from copy import copy
class SDParameter(SceneDetail if SceneDetail is not None else object):
"""SDParameter(param_path, param_name, param_type, param_val = None): Plugin for TempoVis for changing a parameter."""
class_id = 'SDParameter'
mild_restore = True
param_path = ''
param_name = ''
param_type = ''
def __init__(self, param_path, param_name, param_type, param_val = None):
self.key = '{param_path}/[{param_type}]{param_name}'.format(**vars())
self.param_path = param_path
self.param_name = param_name
self.param_type = param_type
if param_val is not None:
self.data = param_val
def scene_value(self):
import FreeCAD as App
hgrp = App.ParamGet(self.param_path)
getter, def1, def2 = {
'Bool' : (hgrp.GetBool , True, False),
'Float' : (hgrp.GetFloat , 1.0, 2.0 ),
'Int' : (hgrp.GetInt , 1, 2 ),
'String' : (hgrp.GetString , 'a', 'b' ),
'Unsigned': (hgrp.GetUnsigned, 1, 2 ),
}[self.param_type]
val1 = getter(self.param_name, def1)
val2 = getter(self.param_name, def2)
absent = val1 == def1 and val2 == def2
return val1 if not absent else None
def apply_data(self, val):
import FreeCAD as App
hgrp = App.ParamGet(self.param_path)
setter = {
'Bool' : (hgrp.SetBool ),
'Float' : (hgrp.SetFloat ),
'Int' : (hgrp.SetInt ),
'String' : (hgrp.SetString ),
'Unsigned': (hgrp.SetUnsigned),
}[self.param_type]
deleter = {
'Bool' : (hgrp.RemBool ),
'Float' : (hgrp.RemFloat ),
'Int' : (hgrp.RemInt ),
'String' : (hgrp.RemString ),
'Unsigned': (hgrp.RemUnsigned),
}[self.param_type]
if val is None:
deleter(self.param_name)
else:
setter(self.param_name, val)
def getSelectedPlacement(obj, sub):
leaves = LCE.AllLeaves(obj.Shape)
if sub == '':
return 0 if len(leaves) == 1 else None
subshape = obj.Shape.getElement(sub)
getter = {
'Vertex': (lambda sh: sh.Vertexes),
'Edge': (lambda sh: sh.Edges),
'Face': (lambda sh: sh.Faces)
} [subshape.ShapeType]
matches = []
for index, leaf in enumerate(leaves):
for it_sh in getter(leaf):
if it_sh.isSame(subshape):
matches.append(index)
assert(len(matches) < 2)
return matches[0]
# --------------------------------Gui commands----------------------------------
_library = {} #instances of TempoVis, per document
_old_par = None #original value of DragAtCursor parameter
_stack_len = 0 #how many times did we tried to modify the parameter
class CommandViewFromPlacement:
def GetResources(self):
return {'Pixmap' : getIconPath("Lattice2_ViewFromPlacement.svg"),
'MenuText': "View from placement",
'Accel': "",
'ToolTip': "View from placement. Places camera to where selected placement is. Click again to restore view.",
'CmdType':"ForEdit"}
def Activated(self):
import FreeCADGui as Gui
global _stack_len
global _old_par
oldTV = _library.pop(App.ActiveDocument.Name, None)
if oldTV is None:
V = App.Vector
sel = Gui.Selection.getSelectionEx()
if len(sel) == 1 and len(sel[0].SubElementNames) < 2:
#get placement
index = getSelectedPlacement(sel[0].Object, sel[0].SubElementNames[0] if len(sel[0].SubElementNames) == 1 else '')
plm = LBF.getPlacementsList(sel[0].Object)[index]
OZ = plm.Rotation.multVec(V(0,0,1))
OY = plm.Rotation.multVec(V(0,1,0))
OX = plm.Rotation.multVec(V(1,0,0))
print(OX)
print(OZ)
#prepare
tv = Show.TempoVis(App.ActiveDocument)
tv.saveCamera()
if SceneDetail is not None:
tv.modify(SDParameter('User parameter:BaseApp/Preferences/View','DragAtCursor','Bool', False))
else:
dt = SDParameter('User parameter:BaseApp/Preferences/View','DragAtCursor','Bool', False)
if _stack_len == 0:
_old_par = dt.scene_value()
_stack_len += 1
dt.apply_data(dt.data)
#set up camera
Gui.ActiveDocument.ActiveView.setCameraType('Perspective')
from pivy import coin
cam = Gui.ActiveDocument.ActiveView.getCameraNode()
cam.position = tuple(plm.Base)
rot = App.Rotation(V(), -OZ, -OX,'ZYX')
cam.orientation = tuple(rot.Q)
cam.heightAngle = -cam.heightAngle.getValue() #reversing height angle inverts the image. Using it in conjunction with upside-down camera to effectively reverse how view reacts to mouse.
cam.focalDistance = 1e-5 #small focal distance makes camera spin about itself
#all done, remember.
_library[App.ActiveDocument.Name] = tv
else:
infoMessage("Lattice2 View From Placement",
"Lattice2 View From Placement command."
"\n\nPlease select a placement, and invoke this tool. The camera will be placed there,"
" and you'll be able to rotate around."
"\n\nClick again to leave the mode.")
else:
oldTV.restore()
if Gui.ActiveDocument.ActiveView.getCameraType() == 'Perspective':
cam = Gui.ActiveDocument.ActiveView.getCameraNode()
cam.heightAngle = abs(cam.heightAngle.getValue()) #workaround
if SceneDetail is None:
_stack_len -= 1
if _stack_len == 0:
dt = SDParameter('User parameter:BaseApp/Preferences/View','DragAtCursor','Bool', False)
dt.apply_data(_old_par)
def IsActive(self):
if Show is None: return False
return hasattr(Gui.activeView(), 'getCameraNode')
if App.GuiUp:
FreeCADGui.addCommand('Lattice2_ViewFromPlacement', CommandViewFromPlacement())
exportedCommands = [
"Lattice2_ViewFromPlacement",
]

File diff suppressed because one or more lines are too long

94
readme.md Normal file
View File

@ -0,0 +1,94 @@
# What is Lattice2 workbench
Lattice Workbench is a plug-in module (workbench) for FreeCAD.
The workbench purpose is working with placements and arrays of placements. It is a sort of assembly workbench, but with emphasis on arrays. There are no constraints and relations, there are just arrays of placements that can be generated, combined, transformed, superimposed and populated with shapes.
Ever wondered how to create a protractor with FreeCAD? That's the aim of the workbench (including tick labeling). Also, exploded assemblies can be made with the workbench.
Additionally, the workbench features a few general-purpose tools, such as parametric downgrade, bounding boxes, shape info tool, and tools for working with collections of shapes (compounds).
One of the big design goals of the workbench is being as parametric as possible.
# Getting started
Follow through [Basic Tutorial](https://github.com/DeepSOIC/Lattice2/wiki/Basic-Tutorial) to get the basic concept of Lattice2.
# Highlights
![Lattice2-FreeCAD-wormcutter](https://raw.githubusercontent.com/wiki/DeepSOIC/Lattice2/gallery/worm-cutter-done.png)
![Lattice2-FreeCAD-placement-interpolator](https://raw.githubusercontent.com/wiki/DeepSOIC/Lattice2/gallery/placement_interpolator_fixed.png)
Take a look at other examples in the [Gallery of screenshots](https://github.com/DeepSOIC/Lattice2/wiki/Gallery).
Let's have a glance over the most important capabilities that the workbench adds to FreeCAD:
* Re-use arrays as many times as you need. Unlike Draft array, which directly generates the array of shapes, lattice array tools generate arrays of placements. These can later be populated with shapes, as many times as necessary, without having to set up another array.
* Elements of array can be different. Unlike Draft Arrays, which always generate a set of equal shapes, Lattice arrays can be used to arrange a set of different shapes. Pack the shapes to be arranged into a Compound, and use Lattice [Populate with children](https://github.com/DeepSOIC/Lattice2/wiki/Feature-PopulateChildren) feature to arrange them.
* Arrays of placements can be combined, inverted, generated from existing shape arrangements, made from individual placements, projected onto shapes, filtered, etc. This allows to produce complex arrangements without scripting.
* single placements can be used for primitive assembling of parts.
* linear arrays and polar arrays can have their axes linked to edges of shapes
* parametric explode commands allow extraction of specific elements of arrays, without losing parametric relation to the original.
* [ParaSeries](https://github.com/DeepSOIC/Lattice2/wiki/Feature-ParaSeries) feature allows to generate a series of parts with some parameter varied over a list of values.
# Why Lattice2, not just Lattice?
Lattice2 was created at the moment when breaking changes needed to be made, but there were a few things made with Lattice. So, it was decided to keep the workbench in that time's state indefinitely as version 1.0, and start development of a new version.
The goal was to be able to have both Lattice v1 and v2 in a single FreeCAD installation. So a new repository was started, and all the files were renamed to start with 'lattice2' or otherwise differ from those of Lattice v1.
# What's changed in Lattice2
* Population tools now have 'move' mode: a placement/set of placements can be supplied to treat as origins of the objects being populated
* Experimental recompute controlling tools were added
* Most icons were redesigned to follow a concept
* Lattice workbench can now be imported from Py console all at once, like that: `import Lattice2`
* ParaSeries feature was added, which can create a series of parts by changing some parameter of the model.
# Download/install
repository: https://github.com/DeepSOIC/Lattice2
Lattice2 WB requires FreeCAD no less than v0.16.5155.
The workbench is OS independent, it should work on any system FreeCAD can be run on. If you find that it doesn't - that is a bug. Please make an issue.
Lattice2 is written in FreeCAD's Python, and must be run from within FreeCAD. It requires no compilation, and can be installed by copying the repository to a special location.
# Manual install:
1. Scroll to the top of the page, and click 'download zip' button
2. Unpack the contents into a "Lattice2" folder created in \Path\to\FreeCAD\Mod, and restart FreeCAD. <br>
Note that InitGui.py (and the rest of .py files) should end up directly under Mod\Lattice2 (not under nested directory like Mod\Lattice2\Lattice2).
After you install the workbench, it should appear at the bottom of list of workbench selector in FreeCAD.
# Automated installation:
* Lattice2 workbench is packaged to Launchpad in the Ubuntu FreeCAD Community PPA (thanks to @abdullahtahiriyo).
* Lattice2 can be installed via [FreeCAD addons installer macro](https://github.com/FreeCAD/FreeCAD-addons)
* Lattice2 can be installed via @microelly's [Plugin Loader](https://github.com/microelly2/freecad-pluginloader)
# License
Lattice workbench is licenced under LGPL V2, just like FreeCAD. For more info, see copying.lib file in the repository.
# Getting Help
Documentation: see [Lattice2 wiki](https://github.com/DeepSOIC/Lattice2/wiki) on Github. As the word "wiki" suggests, you can help by editing the documentation.
If you need help on something specific, you can ask a question on [FreeCAD forum](http://forum.freecadweb.org/index.php) (there is no Lattice forum yet...). You can also ask me directly.
If you have found a bug, report it [here, in Github's issue tracker for Lattice2](https://github.com/DeepSOIC/Lattice2/issues). You can also post ideas for new features there, as well as plain questions.
If you have fixed a bug, or implemented a new feature you think suits the workbench, or whatever - feel free to make a pull-request here on github.
# Status
This version can be considered a release candidate. I will take care to not make breaking changes, but new functionality may keep coming.

Binary file not shown.

View File

@ -0,0 +1,149 @@
#Inventor V2.1 ascii
PickStyle {
style UNPICKABLE
}
Coordinate3 {
point [ 2.4286127e-017 2.3747374e-017 0,
1 0 0,
-0.28144613 -0.48747897 0,
2.4286127e-017 2.3747374e-017 0,
-0.28144613 0.48747897 0,
1 0 0,
2.4286127e-017 2.3747374e-017 0,
1 0 0,
0 -6.3415941e-017 -0.28560001,
2.4286127e-017 2.3747374e-017 0,
1 0 0,
-0.28144613 -0.48747897 0,
-0.28144613 0.48747897 0,
0 -6.3415941e-017 -0.28560001 ]
}
Switch {
whichChild 0
Separator {
DEF myLines Separator {
MaterialBinding {
value OVERALL
}
Material {
ambientColor 0.2 0.2 0.2
diffuseColor 0.33333334 0.65490198 0.87058824
specularColor 0 0 0
emissiveColor 0 0 0
shininess 1
transparency 0
}
DrawStyle {
style LINES
lineWidth 2
linePattern 0xffff
}
SoBrepEdgeSet {
fields [ SFNode vertexProperty, MFInt32 coordIndex, MFInt32 materialIndex, MFInt32 normalIndex, MFInt32 textureCoordIndex, SFInt32 highlightIndex, MFInt32 selectionIndex ]
coordIndex [ 0, 1, -1, 1, 2, -1, 2, 0,
-1, 3, 4, -1, 4, 5, -1, 8,
6, -1, 7, 8, -1 ]
highlightIndex -1
selectionIndex -1
}
}
PolygonOffset {
}
DEF myFaces Separator {
ShapeHints {
vertexOrdering UNKNOWN_ORDERING
shapeType UNKNOWN_SHAPE_TYPE
}
MaterialBinding {
value OVERALL
}
Material {
ambientColor 0.2 0.2 0.2
diffuseColor 0 0.66666669 1
specularColor 0 0 0
emissiveColor 0 0 0
shininess 0.2
transparency 0.5
}
DrawStyle {
style FILLED
}
Normal {
vector [ 0 0 1,
0 0 1,
0 0 1,
0 0 1,
0 0 1,
0 0 1,
0 -1 2.220446e-016,
0 -1 2.220446e-016,
0 -1 2.220446e-016 ]
}
NormalBinding {
value PER_VERTEX_INDEXED
}
SoBrepFaceSet {
fields [ SFNode vertexProperty, MFInt32 coordIndex, MFInt32 materialIndex, MFInt32 normalIndex, MFInt32 textureCoordIndex, MFInt32 partIndex, SFInt32 highlightIndex, MFInt32 selectionIndex ]
coordIndex [ 1, 0, 2, -1, 5, 4, 3, -1,
7, 6, 8, -1 ]
partIndex [ 1, 1, 1 ]
highlightIndex -1
selectionIndex -1
}
}
DEF myPoints Separator {
MaterialBinding {
value OVERALL
}
Material {
ambientColor 0.2 0.2 0.2
diffuseColor 0.25490198 0.67450982 0.89803922
specularColor 0 0 0
emissiveColor 0 0 0
shininess 1
transparency 0
}
DrawStyle {
style POINTS
pointSize 2
}
SoBrepPointSet {
fields [ SFNode vertexProperty, SFInt32 startIndex, SFInt32 numPoints, SFInt32 highlightIndex, MFInt32 selectionIndex ]
startIndex 9
highlightIndex -1
selectionIndex [ 9, 10, 11, 12, 13 ]
}
}
}
USE myFaces
Separator {
USE myLines
USE myPoints
}
USE myPoints
}

Binary file not shown.

View File

@ -0,0 +1,160 @@
#Inventor V2.1 ascii
PickStyle {
style UNPICKABLE
}
Coordinate3 {
point [ 0 0 0,
-1.7347235e-018 -0.30000001 1.3877788e-017,
1.7347235e-018 0.30000001 -2.1359844e-015,
-1.7347235e-018 8.3193401e-018 0.30000001,
1.7347235e-018 0.30000001 -2.1359844e-015,
0 0 0,
1 -2.7755576e-017 -2.0816682e-017,
-1.7347235e-018 -0.30000001 1.3877788e-017,
-1.7347235e-018 -0.30000001 1.3877788e-017,
1 -2.7755576e-017 -2.0816682e-017,
-1.7347235e-018 8.3193401e-018 0.30000001,
1 -2.7755576e-017 -2.0816682e-017,
-1.7347235e-018 8.3193401e-018 0.30000001,
1.7347235e-018 0.30000001 -2.1359844e-015,
0 0 0,
-1.7347235e-018 -0.30000001 1.3877788e-017,
1.7347235e-018 0.30000001 -2.1359844e-015,
-1.7347235e-018 8.3193401e-018 0.30000001,
1 -2.7755576e-017 -2.0816682e-017 ]
}
Switch {
whichChild 0
Separator {
DEF myLines Separator {
MaterialBinding {
value OVERALL
}
Material {
ambientColor 0.2 0.2 0.2
diffuseColor 0.33333334 0.65490198 0.87058824
specularColor 0 0 0
emissiveColor 0 0 0
shininess 1
transparency 0
}
DrawStyle {
style LINES
lineWidth 2
linePattern 0xffff
}
SoBrepEdgeSet {
fields [ SFNode vertexProperty, MFInt32 coordIndex, MFInt32 materialIndex, MFInt32 normalIndex, MFInt32 textureCoordIndex, SFInt32 highlightIndex, MFInt32 selectionIndex ]
coordIndex [ 0, 1, -1, 2, 0, -1, 1, 3,
-1, 3, 2, -1, 4, 6, -1, 7,
6, -1, 9, 10, -1 ]
highlightIndex 6
selectionIndex -1
}
}
PolygonOffset {
}
DEF myFaces Separator {
ShapeHints {
vertexOrdering UNKNOWN_ORDERING
shapeType UNKNOWN_SHAPE_TYPE
}
MaterialBinding {
value OVERALL
}
Material {
ambientColor 0.2 0.2 0.2
diffuseColor 0 0.66666669 1
specularColor 0 0 0
emissiveColor 0 0 0
shininess 0.2
transparency 0.5
}
DrawStyle {
style FILLED
}
Normal {
vector [ -1 0 0,
-1 0 0,
-1 0 0,
-1 0 0,
0 0 -1,
0 0 -1,
0 0 -1,
0 0 -1,
0.20751435 -0.69171447 0.69171447,
0.20751435 -0.69171447 0.69171447,
0.20751435 -0.69171447 0.69171447,
0.20751435 0.69171447 0.69171447,
0.20751435 0.69171447 0.69171447,
0.20751435 0.69171447 0.69171447 ]
}
NormalBinding {
value PER_VERTEX_INDEXED
}
SoBrepFaceSet {
fields [ SFNode vertexProperty, MFInt32 coordIndex, MFInt32 materialIndex, MFInt32 normalIndex, MFInt32 textureCoordIndex, MFInt32 partIndex, SFInt32 highlightIndex, MFInt32 selectionIndex ]
coordIndex [ 1, 3, 0, -1, 0, 3, 2, -1,
4, 6, 5, -1, 5, 6, 7, -1,
10, 8, 9, -1, 13, 12, 11, -1 ]
partIndex [ 2, 2, 1, 1 ]
highlightIndex -1
selectionIndex -1
}
}
DEF myPoints Separator {
MaterialBinding {
value OVERALL
}
Material {
ambientColor 0.2 0.2 0.2
diffuseColor 0.25490198 0.67450982 0.89803922
specularColor 0 0 0
emissiveColor 0 0 0
shininess 1
transparency 0
}
DrawStyle {
style POINTS
pointSize 2
}
SoBrepPointSet {
fields [ SFNode vertexProperty, SFInt32 startIndex, SFInt32 numPoints, SFInt32 highlightIndex, MFInt32 selectionIndex ]
startIndex 14
highlightIndex -1
selectionIndex [ 14, 15, 16, 17, 18 ]
}
}
}
USE myFaces
Separator {
USE myLines
USE myPoints
}
USE myPoints
}