Merge branch 'dev' into builder_context_selectors
|
|
@ -1,7 +1,11 @@
|
|||
[MAIN]
|
||||
|
||||
extension-pkg-allow-list=OCP
|
||||
|
||||
[BASIC]
|
||||
|
||||
# Good variable names which should always be accepted, separated by a comma
|
||||
good-names=i,j,k,x,y,z,ex,Run,_,X,Y,Z,XY,YZ,ZX,XZ,YX,ZY
|
||||
good-names=i,j,k,u,v,x,y,z,ex,Run,_,X,Y,Z,XY,YZ,ZX,XZ,YX,ZY
|
||||
|
||||
disable=
|
||||
unsubscriptable-object, # False positives
|
||||
|
|
@ -9,3 +13,6 @@ disable=
|
|||
protected-access, # _variable to be hiddened from external users
|
||||
too-many-arguments, # CAD is complex
|
||||
too-few-public-methods # Objects and Operations will not have methods outside of _init
|
||||
|
||||
ignore-paths=
|
||||
./src/build123d/_version.py # Generated
|
||||
|
|
|
|||
12
CONTRIBUTING.md
Normal file
|
|
@ -0,0 +1,12 @@
|
|||
When writing code for inclusion in build123d please add docs and
|
||||
tests, ensure they build and pass, and ensure that `pylint` and `mypy`
|
||||
are happy with your code.
|
||||
|
||||
- Install `pip` following their [documentation](https://pip.pypa.io/en/stable/installation/).
|
||||
- Install development dependencies: `pip install pylint pytest mypy sphinx`
|
||||
- Install docs dependencies: `pip install -r docs/requirements.txt` (might need to comment out the build123d line in that file)
|
||||
- Install `build123d` in editable mode from current dir: `pip install -e .`
|
||||
- Run tests with: `python -m pytest`
|
||||
- Build docs with: `cd docs && make html`
|
||||
- Check added files' style with: `pylint <path/to/file.py>`
|
||||
- Check added files' type annotations with: `mypy <path/to/file.py>`
|
||||
|
|
@ -12,7 +12,7 @@ Objects and arithmetic
|
|||
|
||||
:math:`C^2` is the set of all ``Sketch`` objects ``s`` with ``s._dim = 2``
|
||||
|
||||
:math:`C^1` is the set of all ``Curve`` objects ``c`` with ``c._dim = 3``
|
||||
:math:`C^1` is the set of all ``Curve`` objects ``c`` with ``c._dim = 1``
|
||||
|
||||
**Neutral elements:**
|
||||
|
||||
|
|
@ -49,7 +49,7 @@ with :math:`B^3 \subset C^3, B^2 \subset C^2` and :math:`B^1 \subset C^1`
|
|||
|
||||
:math:`\; a \; \& \; b :=` ``a.intersect(b)`` for each operation
|
||||
|
||||
* :math:`\&` is not defined for :math:`n=1` in build123d
|
||||
* :math:`\&` is not defined for :math:`n=1` in build123d
|
||||
* The following relationship holds: :math:`a \; \& \; b = (a + b) + -(a + (-b)) + -(b + (-a))`
|
||||
|
||||
|
||||
|
|
@ -66,12 +66,12 @@ Locations, planes and location arithmentic
|
|||
|
||||
**Set definitions:**
|
||||
|
||||
:math:`L := \lbrace` ``Location((x, y, z), (a, b, c))`` :math:`: x,y,z \in R \land a,b,c \in R \rbrace\;`
|
||||
|
||||
:math:`L := \lbrace` ``Location((x, y, z), (a, b, c))`` :math:`: x,y,z \in R \land a,b,c \in R \rbrace\;`
|
||||
|
||||
with :math:`a,b,c` being angles in degrees.
|
||||
|
||||
:math:`P := \lbrace` ``Plane(o, x, z)`` :math:`: o,x,z ∈ R^3 \land \|x\| = \|z\| = 1\rbrace`
|
||||
|
||||
:math:`P := \lbrace` ``Plane(o, x, z)`` :math:`: o,x,z ∈ R^3 \land \|x\| = \|z\| = 1\rbrace`
|
||||
|
||||
with ``o`` being the origin and ``x``, ``z`` the x- and z-direction of the plane.
|
||||
|
||||
Neutral element: :math:`\; l_0 \in L`: ``Location()``
|
||||
|
|
@ -79,7 +79,7 @@ Neutral element: :math:`\; l_0 \in L`: ``Location()``
|
|||
**Operations:**
|
||||
|
||||
:math:`*: L \times L \rightarrow L` with :math:`(l_1,l_2) \mapsto l_1 * l_2`
|
||||
|
||||
|
||||
:math:`\; l_1 * l_2 :=` ``l1 * l2`` (multiply two locations)
|
||||
|
||||
:math:`*: P \times L \rightarrow P` with :math:`(p,l) \mapsto p * l`
|
||||
|
|
|
|||
113
docs/assets/packed_boxes_input.svg
Normal file
|
|
@ -0,0 +1,113 @@
|
|||
<?xml version='1.0' encoding='utf-8'?>
|
||||
<svg width="100.09mm" height="98.074625mm" viewBox="-12.957705 -12.696793 25.91541 25.393587" version="1.1" xmlns="http://www.w3.org/2000/svg">
|
||||
<g transform="scale(1,-1)" stroke-linecap="round">
|
||||
<g fill="none" stroke="rgb(0,0,0)" stroke-width="0.023302896428222534" id="Visible">
|
||||
<line x1="5.41638" y1="-12.685142" x2="3.121076" y2="-11.694396" />
|
||||
<line x1="-10.650749" y1="-5.749916" x2="-12.946054" y2="-4.75917" />
|
||||
<line x1="3.121076" y1="-11.694396" x2="-10.650749" y2="-5.749916" />
|
||||
<line x1="3.841048" y1="-12.549755" x2="4.039197" y2="-12.090694" />
|
||||
<line x1="-11.76702" y1="-5.812679" x2="-11.568871" y2="-5.353618" />
|
||||
<line x1="3.841048" y1="-12.549755" x2="-11.76702" y2="-5.812679" />
|
||||
<line x1="5.41638" y1="-12.685142" x2="8.983067" y2="-4.422047" />
|
||||
<line x1="8.983067" y1="-4.422047" x2="9.379366" y2="-3.503925" />
|
||||
<line x1="9.379366" y1="-3.503925" x2="12.946054" y2="4.75917" />
|
||||
<line x1="-12.946054" y1="-4.75917" x2="-9.379366" y2="3.503925" />
|
||||
<line x1="-9.379366" y1="3.503925" x2="-8.983067" y2="4.422047" />
|
||||
<line x1="-8.983067" y1="4.422047" x2="-5.41638" y2="12.685142" />
|
||||
<line x1="-10.650749" y1="-5.749916" x2="-9.858152" y2="-3.913673" />
|
||||
<line x1="8.002184" y1="-2.909477" x2="10.776274" y2="3.517374" />
|
||||
<line x1="10.776274" y1="3.517374" x2="9.858152" y2="3.913673" />
|
||||
<line x1="9.858152" y1="3.913673" x2="10.650749" y2="5.749916" />
|
||||
<line x1="10.650749" y1="5.749916" x2="-3.121076" y2="11.694396" />
|
||||
<line x1="-3.913673" y1="9.858152" x2="-3.121076" y2="11.694396" />
|
||||
<line x1="-3.913673" y1="9.858152" x2="-4.831795" y2="10.254451" />
|
||||
<line x1="-7.605885" y1="3.827599" x2="-4.831795" y2="10.254451" />
|
||||
<line x1="-7.605885" y1="3.827599" x2="-8.983067" y2="4.422047" />
|
||||
<line x1="-8.002184" y1="2.909477" x2="-9.379366" y2="3.503925" />
|
||||
<line x1="-10.776274" y1="-3.517374" x2="-8.002184" y2="2.909477" />
|
||||
<line x1="-9.858152" y1="-3.913673" x2="-10.776274" y2="-3.517374" />
|
||||
<line x1="3.121076" y1="-11.694396" x2="3.913673" y2="-9.858152" />
|
||||
<line x1="4.831795" y1="-10.254451" x2="3.913673" y2="-9.858152" />
|
||||
<line x1="4.831795" y1="-10.254451" x2="7.605885" y2="-3.827599" />
|
||||
<line x1="8.983067" y1="-4.422047" x2="7.605885" y2="-3.827599" />
|
||||
<line x1="9.379366" y1="-3.503925" x2="8.002184" y2="-2.909477" />
|
||||
<line x1="12.946054" y1="4.75917" x2="10.650749" y2="5.749916" />
|
||||
<line x1="-3.121076" y1="11.694396" x2="-5.41638" y2="12.685142" />
|
||||
<line x1="-4.039197" y1="12.090694" x2="-3.841048" y2="12.549755" />
|
||||
<line x1="11.568871" y1="5.353618" x2="11.76702" y2="5.812679" />
|
||||
<line x1="11.76702" y1="5.812679" x2="-3.841048" y2="12.549755" />
|
||||
</g>
|
||||
<g fill="none" stroke="rgb(99,99,99)" stroke-width="0.023302896428222534" id="Hidden" stroke-dasharray="0.000592 0.070205">
|
||||
<line x1="4.039197" y1="-12.090694" x2="-11.568871" y2="-5.353618" />
|
||||
<line x1="-11.568871" y1="-5.353618" x2="4.039197" y2="-12.090694" />
|
||||
<line x1="5.41638" y1="-12.685142" x2="3.121076" y2="-11.694396" />
|
||||
<line x1="3.121076" y1="-11.694396" x2="-10.650749" y2="-5.749916" />
|
||||
<line x1="-10.650749" y1="-5.749916" x2="-12.946054" y2="-4.75917" />
|
||||
<line x1="-11.76702" y1="-5.812679" x2="-11.568871" y2="-5.353618" />
|
||||
<line x1="3.841048" y1="-12.549755" x2="-11.76702" y2="-5.812679" />
|
||||
<line x1="3.841048" y1="-12.549755" x2="4.039197" y2="-12.090694" />
|
||||
<line x1="3.913673" y1="-9.858152" x2="3.121076" y2="-11.694396" />
|
||||
<line x1="4.831795" y1="-10.254451" x2="3.913673" y2="-9.858152" />
|
||||
<line x1="4.831795" y1="-10.254451" x2="7.605885" y2="-3.827599" />
|
||||
<line x1="7.605885" y1="-3.827599" x2="8.983067" y2="-4.422047" />
|
||||
<line x1="5.41638" y1="-12.685142" x2="8.983067" y2="-4.422047" />
|
||||
<line x1="9.379366" y1="-3.503925" x2="12.946054" y2="4.75917" />
|
||||
<line x1="8.983067" y1="-4.422047" x2="9.379366" y2="-3.503925" />
|
||||
<line x1="3.913673" y1="-9.858152" x2="4.831795" y2="-10.254451" />
|
||||
<line x1="4.831795" y1="-10.254451" x2="7.605885" y2="-3.827599" />
|
||||
<line x1="7.605885" y1="-3.827599" x2="8.983067" y2="-4.422047" />
|
||||
<line x1="3.913673" y1="-9.858152" x2="3.121076" y2="-11.694396" />
|
||||
<line x1="3.121076" y1="-11.694396" x2="3.913673" y2="-9.858152" />
|
||||
<line x1="-10.650749" y1="-5.749916" x2="-9.858152" y2="-3.913673" />
|
||||
<line x1="4.831795" y1="-10.254451" x2="3.913673" y2="-9.858152" />
|
||||
<line x1="4.831795" y1="-10.254451" x2="7.605885" y2="-3.827599" />
|
||||
<line x1="8.983067" y1="-4.422047" x2="7.605885" y2="-3.827599" />
|
||||
<line x1="9.379366" y1="-3.503925" x2="8.002184" y2="-2.909477" />
|
||||
<line x1="8.002184" y1="-2.909477" x2="10.776274" y2="3.517374" />
|
||||
<line x1="10.776274" y1="3.517374" x2="9.858152" y2="3.913673" />
|
||||
<line x1="9.858152" y1="3.913673" x2="10.650749" y2="5.749916" />
|
||||
<line x1="10.650749" y1="5.749916" x2="-3.121076" y2="11.694396" />
|
||||
<line x1="-3.913673" y1="9.858152" x2="-3.121076" y2="11.694396" />
|
||||
<line x1="-3.913673" y1="9.858152" x2="-4.831795" y2="10.254451" />
|
||||
<line x1="-7.605885" y1="3.827599" x2="-4.831795" y2="10.254451" />
|
||||
<line x1="-7.605885" y1="3.827599" x2="-8.983067" y2="4.422047" />
|
||||
<line x1="-9.379366" y1="3.503925" x2="-8.983067" y2="4.422047" />
|
||||
<line x1="-8.002184" y1="2.909477" x2="-9.379366" y2="3.503925" />
|
||||
<line x1="-10.776274" y1="-3.517374" x2="-8.002184" y2="2.909477" />
|
||||
<line x1="-9.858152" y1="-3.913673" x2="-10.776274" y2="-3.517374" />
|
||||
<line x1="-9.858152" y1="-3.913673" x2="-10.650749" y2="-5.749916" />
|
||||
<line x1="-12.946054" y1="-4.75917" x2="-9.379366" y2="3.503925" />
|
||||
<line x1="-9.379366" y1="3.503925" x2="-8.002184" y2="2.909477" />
|
||||
<line x1="-10.776274" y1="-3.517374" x2="-8.002184" y2="2.909477" />
|
||||
<line x1="-10.776274" y1="-3.517374" x2="-9.858152" y2="-3.913673" />
|
||||
<line x1="-8.983067" y1="4.422047" x2="-5.41638" y2="12.685142" />
|
||||
<line x1="-9.379366" y1="3.503925" x2="-8.002184" y2="2.909477" />
|
||||
<line x1="-10.776274" y1="-3.517374" x2="-8.002184" y2="2.909477" />
|
||||
<line x1="-9.858152" y1="-3.913673" x2="-10.776274" y2="-3.517374" />
|
||||
<line x1="-9.858152" y1="-3.913673" x2="-10.650749" y2="-5.749916" />
|
||||
<line x1="8.002184" y1="-2.909477" x2="9.379366" y2="-3.503925" />
|
||||
<line x1="8.002184" y1="-2.909477" x2="10.776274" y2="3.517374" />
|
||||
<line x1="9.858152" y1="3.913673" x2="10.776274" y2="3.517374" />
|
||||
<line x1="10.650749" y1="5.749916" x2="9.858152" y2="3.913673" />
|
||||
<line x1="12.946054" y1="4.75917" x2="10.650749" y2="5.749916" />
|
||||
<line x1="-3.121076" y1="11.694396" x2="-5.41638" y2="12.685142" />
|
||||
<line x1="11.568871" y1="5.353618" x2="-4.039197" y2="12.090694" />
|
||||
<line x1="-4.039197" y1="12.090694" x2="11.568871" y2="5.353618" />
|
||||
<line x1="8.002184" y1="-2.909477" x2="9.379366" y2="-3.503925" />
|
||||
<line x1="8.002184" y1="-2.909477" x2="10.776274" y2="3.517374" />
|
||||
<line x1="9.858152" y1="3.913673" x2="10.776274" y2="3.517374" />
|
||||
<line x1="10.650749" y1="5.749916" x2="9.858152" y2="3.913673" />
|
||||
<line x1="-3.121076" y1="11.694396" x2="-3.913673" y2="9.858152" />
|
||||
<line x1="-4.831795" y1="10.254451" x2="-3.913673" y2="9.858152" />
|
||||
<line x1="-7.605885" y1="3.827599" x2="-4.831795" y2="10.254451" />
|
||||
<line x1="-8.983067" y1="4.422047" x2="-7.605885" y2="3.827599" />
|
||||
<line x1="-8.983067" y1="4.422047" x2="-7.605885" y2="3.827599" />
|
||||
<line x1="-3.121076" y1="11.694396" x2="-3.913673" y2="9.858152" />
|
||||
<line x1="-4.831795" y1="10.254451" x2="-3.913673" y2="9.858152" />
|
||||
<line x1="-7.605885" y1="3.827599" x2="-4.831795" y2="10.254451" />
|
||||
<line x1="-4.039197" y1="12.090694" x2="-3.841048" y2="12.549755" />
|
||||
<line x1="11.568871" y1="5.353618" x2="11.76702" y2="5.812679" />
|
||||
<line x1="11.76702" y1="5.812679" x2="-3.841048" y2="12.549755" />
|
||||
</g>
|
||||
</g>
|
||||
</svg>
|
||||
|
After Width: | Height: | Size: 8 KiB |
609
docs/assets/packed_boxes_output.svg
Normal file
|
|
@ -0,0 +1,609 @@
|
|||
<?xml version='1.0' encoding='utf-8'?>
|
||||
<svg width="100.09mm" height="94.828286mm" viewBox="-75.158411 -79.079813 143.393259 135.8551" version="1.1" xmlns="http://www.w3.org/2000/svg">
|
||||
<g transform="scale(1,-1)" stroke-linecap="round">
|
||||
<g fill="none" stroke="rgb(0,0,0)" stroke-width="0.12893788891198896" id="Visible">
|
||||
<line x1="-5.693759" y1="67.888004" x2="-5.693759" y2="68.634199" />
|
||||
<line x1="-5.693759" y1="68.634199" x2="-6.509998" y2="69.170199" />
|
||||
<line x1="-6.509998" y1="68.424004" x2="-6.509998" y2="69.170199" />
|
||||
<line x1="-5.693759" y1="67.888004" x2="-6.509998" y2="68.424004" />
|
||||
<line x1="0.083391" y1="75.461001" x2="0.083391" y2="76.207196" />
|
||||
<line x1="0.083391" y1="76.207196" x2="-0.732847" y2="76.743196" />
|
||||
<line x1="-5.693759" y1="67.888004" x2="0.083391" y2="75.461001" />
|
||||
<line x1="-5.693759" y1="68.634199" x2="0.083391" y2="76.207196" />
|
||||
<line x1="-6.509998" y1="69.170199" x2="-0.732847" y2="76.743196" />
|
||||
<line x1="43.105509" y1="48.532875" x2="43.105509" y2="48.905973" />
|
||||
<line x1="43.105509" y1="48.905973" x2="40.656793" y2="50.513971" />
|
||||
<line x1="40.656793" y1="50.140874" x2="40.656793" y2="50.513971" />
|
||||
<line x1="43.105509" y1="48.532875" x2="40.656793" y2="50.140874" />
|
||||
<line x1="43.683224" y1="49.290175" x2="43.683224" y2="49.663272" />
|
||||
<line x1="43.683224" y1="49.663272" x2="41.234508" y2="51.271271" />
|
||||
<line x1="43.105509" y1="48.532875" x2="43.683224" y2="49.290175" />
|
||||
<line x1="43.105509" y1="48.905973" x2="43.683224" y2="49.663272" />
|
||||
<line x1="40.656793" y1="50.513971" x2="41.234508" y2="51.271271" />
|
||||
<line x1="-6.562586" y1="29.998376" x2="-6.562586" y2="30.371473" />
|
||||
<line x1="-6.562586" y1="30.371473" x2="-7.378825" y2="30.907473" />
|
||||
<line x1="-7.378825" y1="30.534375" x2="-7.378825" y2="30.907473" />
|
||||
<line x1="-6.562586" y1="29.998376" x2="-7.378825" y2="30.534375" />
|
||||
<line x1="-1.363151" y1="36.814073" x2="-1.363151" y2="37.18717" />
|
||||
<line x1="-1.363151" y1="37.18717" x2="-2.179389" y2="37.72317" />
|
||||
<line x1="-6.562586" y1="29.998376" x2="-1.363151" y2="36.814073" />
|
||||
<line x1="-6.562586" y1="30.371473" x2="-1.363151" y2="37.18717" />
|
||||
<line x1="-7.378825" y1="30.907473" x2="-2.179389" y2="37.72317" />
|
||||
<line x1="8.945945" y1="19.627836" x2="8.945945" y2="20.374031" />
|
||||
<line x1="8.945945" y1="20.374031" x2="-4.113871" y2="28.950023" />
|
||||
<line x1="-4.113871" y1="28.203828" x2="-4.113871" y2="28.950023" />
|
||||
<line x1="8.945945" y1="19.627836" x2="-4.113871" y2="28.203828" />
|
||||
<line x1="13.567666" y1="25.686234" x2="13.567666" y2="26.432428" />
|
||||
<line x1="13.567666" y1="26.432428" x2="0.50785" y2="35.008421" />
|
||||
<line x1="8.945945" y1="19.627836" x2="13.567666" y2="25.686234" />
|
||||
<line x1="8.945945" y1="20.374031" x2="13.567666" y2="26.432428" />
|
||||
<line x1="-4.113871" y1="28.950023" x2="0.50785" y2="35.008421" />
|
||||
<line x1="28.737006" y1="13.451644" x2="28.737006" y2="14.197839" />
|
||||
<line x1="28.737006" y1="14.197839" x2="14.860951" y2="23.309831" />
|
||||
<line x1="14.860951" y1="22.563636" x2="14.860951" y2="23.309831" />
|
||||
<line x1="28.737006" y1="13.451644" x2="14.860951" y2="22.563636" />
|
||||
<line x1="29.314721" y1="14.208944" x2="29.314721" y2="14.955139" />
|
||||
<line x1="29.314721" y1="14.955139" x2="15.438666" y2="24.06713" />
|
||||
<line x1="28.737006" y1="13.451644" x2="29.314721" y2="14.208944" />
|
||||
<line x1="28.737006" y1="14.197839" x2="29.314721" y2="14.955139" />
|
||||
<line x1="14.860951" y1="23.309831" x2="15.438666" y2="24.06713" />
|
||||
<line x1="24.242248" y1="30.229782" x2="24.242248" y2="30.602879" />
|
||||
<line x1="24.242248" y1="30.602879" x2="14.447386" y2="37.034874" />
|
||||
<line x1="14.447386" y1="36.661776" x2="14.447386" y2="37.034874" />
|
||||
<line x1="24.242248" y1="30.229782" x2="14.447386" y2="36.661776" />
|
||||
<line x1="31.752544" y1="40.074678" x2="31.752544" y2="40.447775" />
|
||||
<line x1="31.752544" y1="40.447775" x2="21.957682" y2="46.879769" />
|
||||
<line x1="24.242248" y1="30.229782" x2="31.752544" y2="40.074678" />
|
||||
<line x1="24.242248" y1="30.602879" x2="31.752544" y2="40.447775" />
|
||||
<line x1="14.447386" y1="37.034874" x2="21.957682" y2="46.879769" />
|
||||
<line x1="11.908895" y1="7.079047" x2="11.908895" y2="8.571436" />
|
||||
<line x1="11.908895" y1="8.571436" x2="10.276418" y2="9.643436" />
|
||||
<line x1="10.276418" y1="8.151046" x2="10.276418" y2="9.643436" />
|
||||
<line x1="11.908895" y1="7.079047" x2="10.276418" y2="8.151046" />
|
||||
<line x1="15.375185" y1="11.622845" x2="15.375185" y2="13.115235" />
|
||||
<line x1="15.375185" y1="13.115235" x2="13.742708" y2="14.187234" />
|
||||
<line x1="11.908895" y1="7.079047" x2="15.375185" y2="11.622845" />
|
||||
<line x1="11.908895" y1="8.571436" x2="15.375185" y2="13.115235" />
|
||||
<line x1="10.276418" y1="9.643436" x2="13.742708" y2="14.187234" />
|
||||
<line x1="-16.134326" y1="-12.015848" x2="-16.134326" y2="-10.523458" />
|
||||
<line x1="-16.134326" y1="-10.523458" x2="-28.377904" y2="-2.483465" />
|
||||
<line x1="-28.377904" y1="-3.975855" x2="-28.377904" y2="-2.483465" />
|
||||
<line x1="-16.134326" y1="-12.015848" x2="-28.377904" y2="-3.975855" />
|
||||
<line x1="-6.31317" y1="0.858246" x2="-6.31317" y2="2.350636" />
|
||||
<line x1="-6.31317" y1="2.350636" x2="-18.556748" y2="10.390629" />
|
||||
<line x1="-16.134326" y1="-12.015848" x2="-6.31317" y2="0.858246" />
|
||||
<line x1="-16.134326" y1="-10.523458" x2="-6.31317" y2="2.350636" />
|
||||
<line x1="-28.377904" y1="-2.483465" x2="-18.556748" y2="10.390629" />
|
||||
<line x1="-39.868724" y1="-7.983404" x2="-39.868724" y2="-6.117917" />
|
||||
<line x1="-39.868724" y1="-6.117917" x2="-44.766155" y2="-2.90192" />
|
||||
<line x1="-44.766155" y1="-4.767407" x2="-44.766155" y2="-2.90192" />
|
||||
<line x1="-39.868724" y1="-7.983404" x2="-44.766155" y2="-4.767407" />
|
||||
<line x1="-30.047568" y1="4.89069" x2="-30.047568" y2="6.756178" />
|
||||
<line x1="-30.047568" y1="6.756178" x2="-34.944999" y2="9.972175" />
|
||||
<line x1="-39.868724" y1="-7.983404" x2="-30.047568" y2="4.89069" />
|
||||
<line x1="-39.868724" y1="-6.117917" x2="-30.047568" y2="6.756178" />
|
||||
<line x1="-44.766155" y1="-2.90192" x2="-34.944999" y2="9.972175" />
|
||||
<line x1="-6.063754" y1="-27.16259" x2="-6.063754" y2="-26.789493" />
|
||||
<line x1="-6.063754" y1="-26.789493" x2="-7.696231" y2="-25.717494" />
|
||||
<line x1="-7.696231" y1="-26.090591" x2="-7.696231" y2="-25.717494" />
|
||||
<line x1="-6.063754" y1="-27.16259" x2="-7.696231" y2="-26.090591" />
|
||||
<line x1="4.335117" y1="-13.531196" x2="4.335117" y2="-13.158099" />
|
||||
<line x1="4.335117" y1="-13.158099" x2="2.70264" y2="-12.0861" />
|
||||
<line x1="-6.063754" y1="-27.16259" x2="4.335117" y2="-13.531196" />
|
||||
<line x1="-6.063754" y1="-26.789493" x2="4.335117" y2="-13.158099" />
|
||||
<line x1="-7.696231" y1="-25.717494" x2="2.70264" y2="-12.0861" />
|
||||
<line x1="15.300811" y1="27.398486" x2="15.300811" y2="29.263974" />
|
||||
<line x1="15.300811" y1="29.263974" x2="12.035857" y2="31.407972" />
|
||||
<line x1="12.035857" y1="29.542484" x2="12.035857" y2="31.407972" />
|
||||
<line x1="15.300811" y1="27.398486" x2="12.035857" y2="29.542484" />
|
||||
<line x1="17.033956" y1="29.670385" x2="17.033956" y2="31.535873" />
|
||||
<line x1="17.033956" y1="31.535873" x2="13.769002" y2="33.679871" />
|
||||
<line x1="15.300811" y1="27.398486" x2="17.033956" y2="29.670385" />
|
||||
<line x1="15.300811" y1="29.263974" x2="17.033956" y2="31.535873" />
|
||||
<line x1="12.035857" y1="31.407972" x2="13.769002" y2="33.679871" />
|
||||
<line x1="7.827702" y1="10.132142" x2="7.827702" y2="10.878337" />
|
||||
<line x1="7.827702" y1="10.878337" x2="5.378987" y2="12.486335" />
|
||||
<line x1="5.378987" y1="11.74014" x2="5.378987" y2="12.486335" />
|
||||
<line x1="7.827702" y1="10.132142" x2="5.378987" y2="11.74014" />
|
||||
<line x1="9.560847" y1="12.404041" x2="9.560847" y2="13.150236" />
|
||||
<line x1="9.560847" y1="13.150236" x2="7.112132" y2="14.758234" />
|
||||
<line x1="7.827702" y1="10.132142" x2="9.560847" y2="12.404041" />
|
||||
<line x1="7.827702" y1="10.878337" x2="9.560847" y2="13.150236" />
|
||||
<line x1="5.378987" y1="12.486335" x2="7.112132" y2="14.758234" />
|
||||
<line x1="28.308039" y1="-19.416585" x2="28.308039" y2="-18.297293" />
|
||||
<line x1="28.308039" y1="-18.297293" x2="12.799507" y2="-8.113302" />
|
||||
<line x1="12.799507" y1="-9.232595" x2="12.799507" y2="-8.113302" />
|
||||
<line x1="28.308039" y1="-19.416585" x2="12.799507" y2="-9.232595" />
|
||||
<line x1="34.662904" y1="-11.086289" x2="34.662904" y2="-9.966997" />
|
||||
<line x1="34.662904" y1="-9.966997" x2="19.154373" y2="0.216994" />
|
||||
<line x1="28.308039" y1="-19.416585" x2="34.662904" y2="-11.086289" />
|
||||
<line x1="28.308039" y1="-18.297293" x2="34.662904" y2="-9.966997" />
|
||||
<line x1="12.799507" y1="-8.113302" x2="19.154373" y2="0.216994" />
|
||||
<line x1="41.595485" y1="-2.37179" x2="41.595485" y2="-0.506303" />
|
||||
<line x1="41.595485" y1="-0.506303" x2="27.719431" y2="8.605689" />
|
||||
<line x1="27.719431" y1="6.740201" x2="27.719431" y2="8.605689" />
|
||||
<line x1="41.595485" y1="-2.37179" x2="27.719431" y2="6.740201" />
|
||||
<line x1="50.261211" y1="8.987705" x2="50.261211" y2="10.853192" />
|
||||
<line x1="50.261211" y1="10.853192" x2="36.385157" y2="19.965184" />
|
||||
<line x1="41.595485" y1="-2.37179" x2="50.261211" y2="8.987705" />
|
||||
<line x1="41.595485" y1="-0.506303" x2="50.261211" y2="10.853192" />
|
||||
<line x1="27.719431" y1="8.605689" x2="36.385157" y2="19.965184" />
|
||||
<line x1="48.99422" y1="31.789489" x2="48.99422" y2="32.908781" />
|
||||
<line x1="48.99422" y1="32.908781" x2="39.199358" y2="39.340775" />
|
||||
<line x1="39.199358" y1="38.221483" x2="39.199358" y2="39.340775" />
|
||||
<line x1="48.99422" y1="31.789489" x2="39.199358" y2="38.221483" />
|
||||
<line x1="55.349086" y1="40.119785" x2="55.349086" y2="41.239077" />
|
||||
<line x1="55.349086" y1="41.239077" x2="45.554224" y2="47.671071" />
|
||||
<line x1="48.99422" y1="31.789489" x2="55.349086" y2="40.119785" />
|
||||
<line x1="48.99422" y1="32.908781" x2="55.349086" y2="41.239077" />
|
||||
<line x1="39.199358" y1="39.340775" x2="45.554224" y2="47.671071" />
|
||||
<line x1="-4.580025" y1="3.316694" x2="-4.580025" y2="4.435987" />
|
||||
<line x1="-4.580025" y1="4.435987" x2="-7.844979" y2="6.579985" />
|
||||
<line x1="-7.844979" y1="5.460692" x2="-7.844979" y2="6.579985" />
|
||||
<line x1="-4.580025" y1="3.316694" x2="-7.844979" y2="5.460692" />
|
||||
<line x1="0.041696" y1="9.375092" x2="0.041696" y2="10.494384" />
|
||||
<line x1="0.041696" y1="10.494384" x2="-3.223258" y2="12.638382" />
|
||||
<line x1="-4.580025" y1="3.316694" x2="0.041696" y2="9.375092" />
|
||||
<line x1="-4.580025" y1="4.435987" x2="0.041696" y2="10.494384" />
|
||||
<line x1="-7.844979" y1="6.579985" x2="-3.223258" y2="12.638382" />
|
||||
<line x1="-8.958713" y1="70.218551" x2="-8.958713" y2="70.591649" />
|
||||
<line x1="-8.958713" y1="70.591649" x2="-11.407429" y2="72.199647" />
|
||||
<line x1="-11.407429" y1="71.82655" x2="-11.407429" y2="72.199647" />
|
||||
<line x1="-8.958713" y1="70.218551" x2="-11.407429" y2="71.82655" />
|
||||
<line x1="-3.759278" y1="77.034248" x2="-3.759278" y2="77.407346" />
|
||||
<line x1="-3.759278" y1="77.407346" x2="-6.207993" y2="79.015344" />
|
||||
<line x1="-8.958713" y1="70.218551" x2="-3.759278" y2="77.034248" />
|
||||
<line x1="-8.958713" y1="70.591649" x2="-3.759278" y2="77.407346" />
|
||||
<line x1="-11.407429" y1="72.199647" x2="-6.207993" y2="79.015344" />
|
||||
<line x1="10.350792" y1="-7.811145" x2="10.350792" y2="-6.318755" />
|
||||
<line x1="10.350792" y1="-6.318755" x2="-1.892786" y2="1.721238" />
|
||||
<line x1="-1.892786" y1="0.228848" x2="-1.892786" y2="1.721238" />
|
||||
<line x1="10.350792" y1="-7.811145" x2="-1.892786" y2="0.228848" />
|
||||
<line x1="14.394797" y1="-2.510047" x2="14.394797" y2="-1.017657" />
|
||||
<line x1="14.394797" y1="-1.017657" x2="2.15122" y2="7.022335" />
|
||||
<line x1="10.350792" y1="-7.811145" x2="14.394797" y2="-2.510047" />
|
||||
<line x1="10.350792" y1="-6.318755" x2="14.394797" y2="-1.017657" />
|
||||
<line x1="-1.892786" y1="1.721238" x2="2.15122" y2="7.022335" />
|
||||
<line x1="-2.391618" y1="57.389814" x2="-2.391618" y2="58.882204" />
|
||||
<line x1="-2.391618" y1="58.882204" x2="-3.207857" y2="59.418203" />
|
||||
<line x1="-3.207857" y1="57.925813" x2="-3.207857" y2="59.418203" />
|
||||
<line x1="-2.391618" y1="57.389814" x2="-3.207857" y2="57.925813" />
|
||||
<line x1="-1.236188" y1="58.904413" x2="-1.236188" y2="60.396803" />
|
||||
<line x1="-1.236188" y1="60.396803" x2="-2.052427" y2="60.932802" />
|
||||
<line x1="-2.391618" y1="57.389814" x2="-1.236188" y2="58.904413" />
|
||||
<line x1="-2.391618" y1="58.882204" x2="-1.236188" y2="60.396803" />
|
||||
<line x1="-3.207857" y1="59.418203" x2="-2.052427" y2="60.932802" />
|
||||
<line x1="38.118302" y1="21.11779" x2="38.118302" y2="21.490888" />
|
||||
<line x1="38.118302" y1="21.490888" x2="26.690963" y2="28.994881" />
|
||||
<line x1="26.690963" y1="28.621783" x2="26.690963" y2="28.994881" />
|
||||
<line x1="38.118302" y1="21.11779" x2="26.690963" y2="28.621783" />
|
||||
<line x1="40.429162" y1="24.146989" x2="40.429162" y2="24.520086" />
|
||||
<line x1="40.429162" y1="24.520086" x2="29.001823" y2="32.02408" />
|
||||
<line x1="38.118302" y1="21.11779" x2="40.429162" y2="24.146989" />
|
||||
<line x1="38.118302" y1="21.490888" x2="40.429162" y2="24.520086" />
|
||||
<line x1="26.690963" y1="28.994881" x2="29.001823" y2="32.02408" />
|
||||
<line x1="2.203808" y1="44.51522" x2="2.203808" y2="45.261415" />
|
||||
<line x1="2.203808" y1="45.261415" x2="-8.407292" y2="52.229409" />
|
||||
<line x1="-8.407292" y1="51.483214" x2="-8.407292" y2="52.229409" />
|
||||
<line x1="2.203808" y1="44.51522" x2="-8.407292" y2="51.483214" />
|
||||
<line x1="5.670099" y1="49.059018" x2="5.670099" y2="49.805213" />
|
||||
<line x1="5.670099" y1="49.805213" x2="-4.941002" y2="56.773207" />
|
||||
<line x1="2.203808" y1="44.51522" x2="5.670099" y2="49.059018" />
|
||||
<line x1="2.203808" y1="45.261415" x2="5.670099" y2="49.805213" />
|
||||
<line x1="-8.407292" y1="52.229409" x2="-4.941002" y2="56.773207" />
|
||||
<line x1="25.997178" y1="-22.818882" x2="25.997178" y2="-20.953394" />
|
||||
<line x1="25.997178" y1="-20.953394" x2="9.672408" y2="-10.233404" />
|
||||
<line x1="9.672408" y1="-12.098891" x2="9.672408" y2="-10.233404" />
|
||||
<line x1="25.997178" y1="-22.818882" x2="9.672408" y2="-12.098891" />
|
||||
<line x1="26.574893" y1="-22.061582" x2="26.574893" y2="-20.196094" />
|
||||
<line x1="26.574893" y1="-20.196094" x2="10.250123" y2="-9.476104" />
|
||||
<line x1="25.997178" y1="-22.818882" x2="26.574893" y2="-22.061582" />
|
||||
<line x1="25.997178" y1="-20.953394" x2="26.574893" y2="-20.196094" />
|
||||
<line x1="9.672408" y1="-10.233404" x2="10.250123" y2="-9.476104" />
|
||||
<line x1="-28.314423" y1="7.162589" x2="-28.314423" y2="9.028077" />
|
||||
<line x1="-28.314423" y1="9.028077" x2="-31.579377" y2="11.172075" />
|
||||
<line x1="-31.579377" y1="9.306587" x2="-31.579377" y2="11.172075" />
|
||||
<line x1="-28.314423" y1="7.162589" x2="-31.579377" y2="9.306587" />
|
||||
<line x1="-20.226412" y1="17.764785" x2="-20.226412" y2="19.630272" />
|
||||
<line x1="-20.226412" y1="19.630272" x2="-23.491366" y2="21.77427" />
|
||||
<line x1="-28.314423" y1="7.162589" x2="-20.226412" y2="17.764785" />
|
||||
<line x1="-28.314423" y1="9.028077" x2="-20.226412" y2="19.630272" />
|
||||
<line x1="-31.579377" y1="11.172075" x2="-23.491366" y2="21.77427" />
|
||||
<line x1="-10.144946" y1="-25.228788" x2="-10.144946" y2="-23.3633" />
|
||||
<line x1="-10.144946" y1="-23.3633" x2="-16.674854" y2="-19.075304" />
|
||||
<line x1="-16.674854" y1="-20.940792" x2="-16.674854" y2="-19.075304" />
|
||||
<line x1="-10.144946" y1="-25.228788" x2="-16.674854" y2="-20.940792" />
|
||||
<line x1="-0.32379" y1="-12.354693" x2="-0.32379" y2="-10.489206" />
|
||||
<line x1="-0.32379" y1="-10.489206" x2="-6.853698" y2="-6.20121" />
|
||||
<line x1="-10.144946" y1="-25.228788" x2="-0.32379" y2="-12.354693" />
|
||||
<line x1="-10.144946" y1="-23.3633" x2="-0.32379" y2="-10.489206" />
|
||||
<line x1="-16.674854" y1="-19.075304" x2="-6.853698" y2="-6.20121" />
|
||||
<line x1="12.709732" y1="-39.490579" x2="12.709732" y2="-39.117482" />
|
||||
<line x1="12.709732" y1="-39.117482" x2="-1.166323" y2="-30.00549" />
|
||||
<line x1="-1.166323" y1="-30.378587" x2="-1.166323" y2="-30.00549" />
|
||||
<line x1="12.709732" y1="-39.490579" x2="-1.166323" y2="-30.378587" />
|
||||
<line x1="24.264033" y1="-24.344586" x2="24.264033" y2="-23.971488" />
|
||||
<line x1="24.264033" y1="-23.971488" x2="10.387979" y2="-14.859496" />
|
||||
<line x1="12.709732" y1="-39.490579" x2="24.264033" y2="-24.344586" />
|
||||
<line x1="12.709732" y1="-39.117482" x2="24.264033" y2="-23.971488" />
|
||||
<line x1="-1.166323" y1="-30.00549" x2="10.387979" y2="-14.859496" />
|
||||
<line x1="11.99867" y1="37.52358" x2="11.99867" y2="39.389067" />
|
||||
<line x1="11.99867" y1="39.389067" x2="4.652524" y2="44.213063" />
|
||||
<line x1="4.652524" y1="42.347575" x2="4.652524" y2="44.213063" />
|
||||
<line x1="11.99867" y1="37.52358" x2="4.652524" y2="42.347575" />
|
||||
<line x1="19.508966" y1="47.368475" x2="19.508966" y2="49.233963" />
|
||||
<line x1="19.508966" y1="49.233963" x2="12.16282" y2="54.057958" />
|
||||
<line x1="11.99867" y1="37.52358" x2="19.508966" y2="47.368475" />
|
||||
<line x1="11.99867" y1="39.389067" x2="19.508966" y2="49.233963" />
|
||||
<line x1="4.652524" y1="44.213063" x2="12.16282" y2="54.057958" />
|
||||
<line x1="36.750643" y1="39.642932" x2="36.750643" y2="41.135322" />
|
||||
<line x1="36.750643" y1="41.135322" x2="26.955781" y2="47.567316" />
|
||||
<line x1="26.955781" y1="46.074927" x2="26.955781" y2="47.567316" />
|
||||
<line x1="36.750643" y1="39.642932" x2="26.955781" y2="46.074927" />
|
||||
<line x1="37.906073" y1="41.157532" x2="37.906073" y2="42.649922" />
|
||||
<line x1="37.906073" y1="42.649922" x2="28.111211" y2="49.081916" />
|
||||
<line x1="36.750643" y1="39.642932" x2="37.906073" y2="41.157532" />
|
||||
<line x1="36.750643" y1="41.135322" x2="37.906073" y2="42.649922" />
|
||||
<line x1="26.955781" y1="47.567316" x2="28.111211" y2="49.081916" />
|
||||
<line x1="3.873472" y1="34.902479" x2="3.873472" y2="36.767967" />
|
||||
<line x1="3.873472" y1="36.767967" x2="2.240995" y2="37.839966" />
|
||||
<line x1="2.240995" y1="35.974478" x2="2.240995" y2="37.839966" />
|
||||
<line x1="3.873472" y1="34.902479" x2="2.240995" y2="35.974478" />
|
||||
<line x1="6.184332" y1="37.931678" x2="6.184332" y2="39.797166" />
|
||||
<line x1="6.184332" y1="39.797166" x2="4.551855" y2="40.869165" />
|
||||
<line x1="3.873472" y1="34.902479" x2="6.184332" y2="37.931678" />
|
||||
<line x1="3.873472" y1="36.767967" x2="6.184332" y2="39.797166" />
|
||||
<line x1="2.240995" y1="37.839966" x2="4.551855" y2="40.869165" />
|
||||
<line x1="-10.293694" y1="7.255239" x2="-10.293694" y2="8.001434" />
|
||||
<line x1="-10.293694" y1="8.001434" x2="-11.926171" y2="9.073433" />
|
||||
<line x1="-11.926171" y1="8.327238" x2="-11.926171" y2="9.073433" />
|
||||
<line x1="-10.293694" y1="7.255239" x2="-11.926171" y2="8.327238" />
|
||||
<line x1="-5.671974" y1="13.313637" x2="-5.671974" y2="14.059832" />
|
||||
<line x1="-5.671974" y1="14.059832" x2="-7.304451" y2="15.131831" />
|
||||
<line x1="-10.293694" y1="7.255239" x2="-5.671974" y2="13.313637" />
|
||||
<line x1="-10.293694" y1="8.001434" x2="-5.671974" y2="14.059832" />
|
||||
<line x1="-11.926171" y1="9.073433" x2="-7.304451" y2="15.131831" />
|
||||
<line x1="36.39605" y1="-8.627841" x2="36.39605" y2="-7.881646" />
|
||||
<line x1="36.39605" y1="-7.881646" x2="21.703757" y2="1.766345" />
|
||||
<line x1="21.703757" y1="1.02015" x2="21.703757" y2="1.766345" />
|
||||
<line x1="36.39605" y1="-8.627841" x2="21.703757" y2="1.02015" />
|
||||
<line x1="39.86234" y1="-4.084043" x2="39.86234" y2="-3.337848" />
|
||||
<line x1="39.86234" y1="-3.337848" x2="25.170047" y2="6.310143" />
|
||||
<line x1="36.39605" y1="-8.627841" x2="39.86234" y2="-4.084043" />
|
||||
<line x1="36.39605" y1="-7.881646" x2="39.86234" y2="-3.337848" />
|
||||
<line x1="21.703757" y1="1.766345" x2="25.170047" y2="6.310143" />
|
||||
<line x1="61.237798" y1="23.936045" x2="61.237798" y2="24.68224" />
|
||||
<line x1="61.237798" y1="24.68224" x2="51.442936" y2="31.114234" />
|
||||
<line x1="51.442936" y1="30.368039" x2="51.442936" y2="31.114234" />
|
||||
<line x1="61.237798" y1="23.936045" x2="51.442936" y2="30.368039" />
|
||||
<line x1="68.170379" y1="33.023641" x2="68.170379" y2="33.769836" />
|
||||
<line x1="68.170379" y1="33.769836" x2="58.375517" y2="40.20183" />
|
||||
<line x1="61.237798" y1="23.936045" x2="68.170379" y2="33.023641" />
|
||||
<line x1="61.237798" y1="24.68224" x2="68.170379" y2="33.769836" />
|
||||
<line x1="51.442936" y1="31.114234" x2="58.375517" y2="40.20183" />
|
||||
<line x1="25.270715" y1="8.721297" x2="25.270715" y2="9.84059" />
|
||||
<line x1="25.270715" y1="9.84059" x2="11.394661" y2="18.952581" />
|
||||
<line x1="11.394661" y1="17.833289" x2="11.394661" y2="18.952581" />
|
||||
<line x1="25.270715" y1="8.721297" x2="11.394661" y2="17.833289" />
|
||||
<line x1="27.003861" y1="10.993196" x2="27.003861" y2="12.112489" />
|
||||
<line x1="27.003861" y1="12.112489" x2="13.127806" y2="21.22448" />
|
||||
<line x1="25.270715" y1="8.721297" x2="27.003861" y2="10.993196" />
|
||||
<line x1="25.270715" y1="9.84059" x2="27.003861" y2="12.112489" />
|
||||
<line x1="11.394661" y1="18.952581" x2="13.127806" y2="21.22448" />
|
||||
<line x1="-28.266343" y1="-27.732592" x2="-28.266343" y2="-26.6133" />
|
||||
<line x1="-28.266343" y1="-26.6133" x2="-42.142397" y2="-17.501308" />
|
||||
<line x1="-42.142397" y1="-18.620601" x2="-42.142397" y2="-17.501308" />
|
||||
<line x1="-28.266343" y1="-27.732592" x2="-42.142397" y2="-18.620601" />
|
||||
<line x1="-17.867472" y1="-14.101198" x2="-17.867472" y2="-12.981906" />
|
||||
<line x1="-17.867472" y1="-12.981906" x2="-31.743526" y2="-3.869914" />
|
||||
<line x1="-28.266343" y1="-27.732592" x2="-17.867472" y2="-14.101198" />
|
||||
<line x1="-28.266343" y1="-26.6133" x2="-17.867472" y2="-12.981906" />
|
||||
<line x1="-42.142397" y1="-17.501308" x2="-31.743526" y2="-3.869914" />
|
||||
<line x1="0.0" y1="-56.710818" x2="0.0" y2="-55.218428" />
|
||||
<line x1="0.0" y1="-55.218428" x2="-16.32477" y2="-44.498438" />
|
||||
<line x1="-16.32477" y1="-45.990828" x2="-16.32477" y2="-44.498438" />
|
||||
<line x1="0.0" y1="-56.710818" x2="-16.32477" y2="-45.990828" />
|
||||
<line x1="10.976586" y1="-42.322124" x2="10.976586" y2="-40.829734" />
|
||||
<line x1="10.976586" y1="-40.829734" x2="-5.348184" y2="-30.109744" />
|
||||
<line x1="0.0" y1="-56.710818" x2="10.976586" y2="-42.322124" />
|
||||
<line x1="0.0" y1="-55.218428" x2="10.976586" y2="-40.829734" />
|
||||
<line x1="-16.32477" y1="-44.498438" x2="-5.348184" y2="-30.109744" />
|
||||
<line x1="42.162308" y1="25.859242" x2="42.162308" y2="27.351632" />
|
||||
<line x1="42.162308" y1="27.351632" x2="30.734969" y2="34.855625" />
|
||||
<line x1="30.734969" y1="33.363235" x2="30.734969" y2="34.855625" />
|
||||
<line x1="42.162308" y1="25.859242" x2="30.734969" y2="33.363235" />
|
||||
<line x1="43.895453" y1="28.131141" x2="43.895453" y2="29.623531" />
|
||||
<line x1="43.895453" y1="29.623531" x2="32.468114" y2="37.127524" />
|
||||
<line x1="42.162308" y1="25.859242" x2="43.895453" y2="28.131141" />
|
||||
<line x1="42.162308" y1="27.351632" x2="43.895453" y2="29.623531" />
|
||||
<line x1="30.734969" y1="34.855625" x2="32.468114" y2="37.127524" />
|
||||
<line x1="1.652387" y1="62.504363" x2="1.652387" y2="64.36985" />
|
||||
<line x1="1.652387" y1="64.36985" x2="-3.245044" y2="67.585847" />
|
||||
<line x1="-3.245044" y1="65.72036" x2="-3.245044" y2="67.585847" />
|
||||
<line x1="1.652387" y1="62.504363" x2="-3.245044" y2="65.72036" />
|
||||
<line x1="7.429538" y1="70.077359" x2="7.429538" y2="71.942847" />
|
||||
<line x1="7.429538" y1="71.942847" x2="2.532107" y2="75.158844" />
|
||||
<line x1="1.652387" y1="62.504363" x2="7.429538" y2="70.077359" />
|
||||
<line x1="1.652387" y1="64.36985" x2="7.429538" y2="71.942847" />
|
||||
<line x1="-3.245044" y1="67.585847" x2="2.532107" y2="75.158844" />
|
||||
<line x1="51.994357" y1="12.005799" x2="51.994357" y2="12.378896" />
|
||||
<line x1="51.994357" y1="12.378896" x2="40.567018" y2="19.882889" />
|
||||
<line x1="40.567018" y1="19.509792" x2="40.567018" y2="19.882889" />
|
||||
<line x1="51.994357" y1="12.005799" x2="40.567018" y2="19.509792" />
|
||||
<line x1="59.504653" y1="21.850694" x2="59.504653" y2="22.223792" />
|
||||
<line x1="59.504653" y1="22.223792" x2="48.077314" y2="29.727785" />
|
||||
<line x1="51.994357" y1="12.005799" x2="59.504653" y2="21.850694" />
|
||||
<line x1="51.994357" y1="12.378896" x2="59.504653" y2="22.223792" />
|
||||
<line x1="40.567018" y1="19.882889" x2="48.077314" y2="29.727785" />
|
||||
<line x1="31.047866" y1="16.480843" x2="31.047866" y2="17.227038" />
|
||||
<line x1="31.047866" y1="17.227038" x2="20.436766" y2="24.195031" />
|
||||
<line x1="20.436766" y1="23.448836" x2="20.436766" y2="24.195031" />
|
||||
<line x1="31.047866" y1="16.480843" x2="20.436766" y2="23.448836" />
|
||||
<line x1="33.936441" y1="20.267341" x2="33.936441" y2="21.013536" />
|
||||
<line x1="33.936441" y1="21.013536" x2="23.325341" y2="27.98153" />
|
||||
<line x1="31.047866" y1="16.480843" x2="33.936441" y2="20.267341" />
|
||||
<line x1="31.047866" y1="17.227038" x2="33.936441" y2="21.013536" />
|
||||
<line x1="20.436766" y1="24.195031" x2="23.325341" y2="27.98153" />
|
||||
<line x1="-18.493266" y1="20.409781" x2="-18.493266" y2="21.529074" />
|
||||
<line x1="-18.493266" y1="21.529074" x2="-21.75822" y2="23.673072" />
|
||||
<line x1="-21.75822" y1="22.553779" x2="-21.75822" y2="23.673072" />
|
||||
<line x1="-18.493266" y1="20.409781" x2="-21.75822" y2="22.553779" />
|
||||
<line x1="-10.405255" y1="31.011976" x2="-10.405255" y2="32.131269" />
|
||||
<line x1="-10.405255" y1="32.131269" x2="-13.670209" y2="34.275267" />
|
||||
<line x1="-18.493266" y1="20.409781" x2="-10.405255" y2="31.011976" />
|
||||
<line x1="-18.493266" y1="21.529074" x2="-10.405255" y2="32.131269" />
|
||||
<line x1="-21.75822" y1="23.673072" x2="-13.670209" y2="34.275267" />
|
||||
<line x1="13.079726" y1="55.560016" x2="13.079726" y2="56.306211" />
|
||||
<line x1="13.079726" y1="56.306211" x2="4.101103" y2="62.202205" />
|
||||
<line x1="4.101103" y1="61.45601" x2="4.101103" y2="62.202205" />
|
||||
<line x1="13.079726" y1="55.560016" x2="4.101103" y2="61.45601" />
|
||||
<line x1="18.279162" y1="62.375713" x2="18.279162" y2="63.121908" />
|
||||
<line x1="18.279162" y1="63.121908" x2="9.300538" y2="69.017902" />
|
||||
<line x1="13.079726" y1="55.560016" x2="18.279162" y2="62.375713" />
|
||||
<line x1="13.079726" y1="56.306211" x2="18.279162" y2="63.121908" />
|
||||
<line x1="4.101103" y1="62.202205" x2="9.300538" y2="69.017902" />
|
||||
<line x1="24.507065" y1="48.242571" x2="24.507065" y2="48.615669" />
|
||||
<line x1="24.507065" y1="48.615669" x2="15.528442" y2="54.511663" />
|
||||
<line x1="15.528442" y1="54.138566" x2="15.528442" y2="54.511663" />
|
||||
<line x1="24.507065" y1="48.242571" x2="15.528442" y2="54.138566" />
|
||||
<line x1="30.861931" y1="56.572868" x2="30.861931" y2="56.945965" />
|
||||
<line x1="30.861931" y1="56.945965" x2="21.883308" y2="62.84196" />
|
||||
<line x1="24.507065" y1="48.242571" x2="30.861931" y2="56.572868" />
|
||||
<line x1="24.507065" y1="48.615669" x2="30.861931" y2="56.945965" />
|
||||
<line x1="15.528442" y1="54.511663" x2="21.883308" y2="62.84196" />
|
||||
<line x1="-18.773486" y1="-44.569378" x2="-18.773486" y2="-42.703891" />
|
||||
<line x1="-18.773486" y1="-42.703891" x2="-31.017063" y2="-34.663898" />
|
||||
<line x1="-31.017063" y1="-36.529385" x2="-31.017063" y2="-34.663898" />
|
||||
<line x1="-18.773486" y1="-44.569378" x2="-31.017063" y2="-36.529385" />
|
||||
<line x1="-7.796899" y1="-30.180684" x2="-7.796899" y2="-28.315197" />
|
||||
<line x1="-7.796899" y1="-28.315197" x2="-20.040477" y2="-20.275204" />
|
||||
<line x1="-18.773486" y1="-44.569378" x2="-7.796899" y2="-30.180684" />
|
||||
<line x1="-18.773486" y1="-42.703891" x2="-7.796899" y2="-28.315197" />
|
||||
<line x1="-31.017063" y1="-34.663898" x2="-20.040477" y2="-20.275204" />
|
||||
<line x1="19.255041" y1="2.255051" x2="19.255041" y2="3.747441" />
|
||||
<line x1="19.255041" y1="3.747441" x2="14.35761" y2="6.963438" />
|
||||
<line x1="14.35761" y1="5.471048" x2="14.35761" y2="6.963438" />
|
||||
<line x1="19.255041" y1="2.255051" x2="14.35761" y2="5.471048" />
|
||||
<line x1="22.143617" y1="6.041549" x2="22.143617" y2="7.533939" />
|
||||
<line x1="22.143617" y1="7.533939" x2="17.246186" y2="10.749936" />
|
||||
<line x1="19.255041" y1="2.255051" x2="22.143617" y2="6.041549" />
|
||||
<line x1="19.255041" y1="3.747441" x2="22.143617" y2="7.533939" />
|
||||
<line x1="14.35761" y1="6.963438" x2="17.246186" y2="10.749936" />
|
||||
<line x1="-33.465779" y1="-34.548289" x2="-33.465779" y2="-33.428997" />
|
||||
<line x1="-33.465779" y1="-33.428997" x2="-48.97431" y2="-23.245006" />
|
||||
<line x1="-48.97431" y1="-24.364299" x2="-48.97431" y2="-23.245006" />
|
||||
<line x1="-33.465779" y1="-34.548289" x2="-48.97431" y2="-24.364299" />
|
||||
<line x1="-29.999488" y1="-30.004491" x2="-29.999488" y2="-28.885199" />
|
||||
<line x1="-29.999488" y1="-28.885199" x2="-45.50802" y2="-18.701208" />
|
||||
<line x1="-33.465779" y1="-34.548289" x2="-29.999488" y2="-30.004491" />
|
||||
<line x1="-33.465779" y1="-33.428997" x2="-29.999488" y2="-28.885199" />
|
||||
<line x1="-48.97431" y1="-23.245006" x2="-45.50802" y2="-18.701208" />
|
||||
<line x1="39.639218" y1="43.61598" x2="39.639218" y2="44.735272" />
|
||||
<line x1="39.639218" y1="44.735272" x2="32.293072" y2="49.559268" />
|
||||
<line x1="32.293072" y1="48.439975" x2="32.293072" y2="49.559268" />
|
||||
<line x1="39.639218" y1="43.61598" x2="32.293072" y2="48.439975" />
|
||||
<line x1="41.372363" y1="45.887879" x2="41.372363" y2="47.007171" />
|
||||
<line x1="41.372363" y1="47.007171" x2="34.026217" y2="51.831167" />
|
||||
<line x1="39.639218" y1="43.61598" x2="41.372363" y2="45.887879" />
|
||||
<line x1="39.639218" y1="44.735272" x2="41.372363" y2="47.007171" />
|
||||
<line x1="32.293072" y1="49.559268" x2="34.026217" y2="51.831167" />
|
||||
<line x1="-51.423026" y1="-22.383203" x2="-51.423026" y2="-22.010105" />
|
||||
<line x1="-51.423026" y1="-22.010105" x2="-62.034126" y2="-15.042112" />
|
||||
<line x1="-62.034126" y1="-15.415209" x2="-62.034126" y2="-15.042112" />
|
||||
<line x1="-51.423026" y1="-22.383203" x2="-62.034126" y2="-15.415209" />
|
||||
<line x1="-41.601869" y1="-9.509108" x2="-41.601869" y2="-9.136011" />
|
||||
<line x1="-41.601869" y1="-9.136011" x2="-52.21297" y2="-2.168017" />
|
||||
<line x1="-51.423026" y1="-22.383203" x2="-41.601869" y2="-9.509108" />
|
||||
<line x1="-51.423026" y1="-22.010105" x2="-41.601869" y2="-9.136011" />
|
||||
<line x1="-62.034126" y1="-15.042112" x2="-52.21297" y2="-2.168017" />
|
||||
<line x1="7.403244" y1="51.517466" x2="7.403244" y2="51.890563" />
|
||||
<line x1="7.403244" y1="51.890563" x2="0.057097" y2="56.714559" />
|
||||
<line x1="0.057097" y1="56.341461" x2="0.057097" y2="56.714559" />
|
||||
<line x1="7.403244" y1="51.517466" x2="0.057097" y2="56.341461" />
|
||||
<line x1="8.558674" y1="53.032065" x2="8.558674" y2="53.405163" />
|
||||
<line x1="8.558674" y1="53.405163" x2="1.212527" y2="58.229158" />
|
||||
<line x1="7.403244" y1="51.517466" x2="8.558674" y2="53.032065" />
|
||||
<line x1="7.403244" y1="51.890563" x2="8.558674" y2="53.405163" />
|
||||
<line x1="0.057097" y1="56.714559" x2="1.212527" y2="58.229158" />
|
||||
<line x1="9.587142" y1="31.896678" x2="9.587142" y2="32.269775" />
|
||||
<line x1="9.587142" y1="32.269775" x2="6.322188" y2="34.413773" />
|
||||
<line x1="6.322188" y1="34.040676" x2="6.322188" y2="34.413773" />
|
||||
<line x1="9.587142" y1="31.896678" x2="6.322188" y2="34.040676" />
|
||||
<line x1="11.320287" y1="34.168577" x2="11.320287" y2="34.541674" />
|
||||
<line x1="11.320287" y1="34.541674" x2="8.055333" y2="36.685672" />
|
||||
<line x1="9.587142" y1="31.896678" x2="11.320287" y2="34.168577" />
|
||||
<line x1="9.587142" y1="32.269775" x2="11.320287" y2="34.541674" />
|
||||
<line x1="6.322188" y1="34.413773" x2="8.055333" y2="36.685672" />
|
||||
<line x1="-64.482842" y1="-13.993759" x2="-64.482842" y2="-13.247564" />
|
||||
<line x1="-64.482842" y1="-13.247564" x2="-75.093942" y2="-6.279571" />
|
||||
<line x1="-75.093942" y1="-7.025766" x2="-75.093942" y2="-6.279571" />
|
||||
<line x1="-64.482842" y1="-13.993759" x2="-75.093942" y2="-7.025766" />
|
||||
<line x1="-56.394831" y1="-3.391564" x2="-56.394831" y2="-2.645369" />
|
||||
<line x1="-56.394831" y1="-2.645369" x2="-67.005931" y2="4.322625" />
|
||||
<line x1="-64.482842" y1="-13.993759" x2="-56.394831" y2="-3.391564" />
|
||||
<line x1="-64.482842" y1="-13.247564" x2="-56.394831" y2="-2.645369" />
|
||||
<line x1="-75.093942" y1="-6.279571" x2="-67.005931" y2="4.322625" />
|
||||
<line x1="-54.661685" y1="-0.933116" x2="-54.661685" y2="-0.560019" />
|
||||
<line x1="-54.661685" y1="-0.560019" x2="-61.191593" y2="3.727977" />
|
||||
<line x1="-61.191593" y1="3.35488" x2="-61.191593" y2="3.727977" />
|
||||
<line x1="-54.661685" y1="-0.933116" x2="-61.191593" y2="3.35488" />
|
||||
<line x1="-46.573674" y1="9.669079" x2="-46.573674" y2="10.042177" />
|
||||
<line x1="-46.573674" y1="10.042177" x2="-53.103582" y2="14.330173" />
|
||||
<line x1="-54.661685" y1="-0.933116" x2="-46.573674" y2="9.669079" />
|
||||
<line x1="-54.661685" y1="-0.560019" x2="-46.573674" y2="10.042177" />
|
||||
<line x1="-61.191593" y1="3.727977" x2="-53.103582" y2="14.330173" />
|
||||
</g>
|
||||
<g fill="none" stroke="rgb(99,99,99)" stroke-width="0.12893788891198896" id="Hidden" stroke-dasharray="0.003275 0.388451">
|
||||
<line x1="-0.732847" y1="75.997001" x2="-0.732847" y2="76.743196" />
|
||||
<line x1="0.083391" y1="75.461001" x2="-0.732847" y2="75.997001" />
|
||||
<line x1="-6.509998" y1="68.424004" x2="-0.732847" y2="75.997001" />
|
||||
<line x1="41.234508" y1="50.898173" x2="41.234508" y2="51.271271" />
|
||||
<line x1="43.683224" y1="49.290175" x2="41.234508" y2="50.898173" />
|
||||
<line x1="40.656793" y1="50.140874" x2="41.234508" y2="50.898173" />
|
||||
<line x1="-2.179389" y1="37.350072" x2="-2.179389" y2="37.72317" />
|
||||
<line x1="-1.363151" y1="36.814073" x2="-2.179389" y2="37.350072" />
|
||||
<line x1="-7.378825" y1="30.534375" x2="-2.179389" y2="37.350072" />
|
||||
<line x1="0.50785" y1="34.262226" x2="0.50785" y2="35.008421" />
|
||||
<line x1="13.567666" y1="25.686234" x2="0.50785" y2="34.262226" />
|
||||
<line x1="-4.113871" y1="28.203828" x2="0.50785" y2="34.262226" />
|
||||
<line x1="15.438666" y1="23.320935" x2="15.438666" y2="24.06713" />
|
||||
<line x1="29.314721" y1="14.208944" x2="15.438666" y2="23.320935" />
|
||||
<line x1="14.860951" y1="22.563636" x2="15.438666" y2="23.320935" />
|
||||
<line x1="21.957682" y1="46.506672" x2="21.957682" y2="46.879769" />
|
||||
<line x1="31.752544" y1="40.074678" x2="21.957682" y2="46.506672" />
|
||||
<line x1="14.447386" y1="36.661776" x2="21.957682" y2="46.506672" />
|
||||
<line x1="13.742708" y1="12.694844" x2="13.742708" y2="14.187234" />
|
||||
<line x1="15.375185" y1="11.622845" x2="13.742708" y2="12.694844" />
|
||||
<line x1="10.276418" y1="8.151046" x2="13.742708" y2="12.694844" />
|
||||
<line x1="-18.556748" y1="8.898239" x2="-18.556748" y2="10.390629" />
|
||||
<line x1="-6.31317" y1="0.858246" x2="-18.556748" y2="8.898239" />
|
||||
<line x1="-28.377904" y1="-3.975855" x2="-18.556748" y2="8.898239" />
|
||||
<line x1="-34.944999" y1="8.106687" x2="-34.944999" y2="9.972175" />
|
||||
<line x1="-30.047568" y1="4.89069" x2="-34.944999" y2="8.106687" />
|
||||
<line x1="-44.766155" y1="-4.767407" x2="-34.944999" y2="8.106687" />
|
||||
<line x1="2.70264" y1="-12.459197" x2="2.70264" y2="-12.0861" />
|
||||
<line x1="4.335117" y1="-13.531196" x2="2.70264" y2="-12.459197" />
|
||||
<line x1="-7.696231" y1="-26.090591" x2="2.70264" y2="-12.459197" />
|
||||
<line x1="13.769002" y1="31.814383" x2="13.769002" y2="33.679871" />
|
||||
<line x1="17.033956" y1="29.670385" x2="13.769002" y2="31.814383" />
|
||||
<line x1="12.035857" y1="29.542484" x2="13.769002" y2="31.814383" />
|
||||
<line x1="7.112132" y1="14.012039" x2="7.112132" y2="14.758234" />
|
||||
<line x1="9.560847" y1="12.404041" x2="7.112132" y2="14.012039" />
|
||||
<line x1="5.378987" y1="11.74014" x2="7.112132" y2="14.012039" />
|
||||
<line x1="19.154373" y1="-0.902298" x2="19.154373" y2="0.216994" />
|
||||
<line x1="34.662904" y1="-11.086289" x2="19.154373" y2="-0.902298" />
|
||||
<line x1="12.799507" y1="-9.232595" x2="19.154373" y2="-0.902298" />
|
||||
<line x1="36.385157" y1="18.099696" x2="36.385157" y2="19.965184" />
|
||||
<line x1="50.261211" y1="8.987705" x2="36.385157" y2="18.099696" />
|
||||
<line x1="27.719431" y1="6.740201" x2="36.385157" y2="18.099696" />
|
||||
<line x1="45.554224" y1="46.551779" x2="45.554224" y2="47.671071" />
|
||||
<line x1="55.349086" y1="40.119785" x2="45.554224" y2="46.551779" />
|
||||
<line x1="39.199358" y1="38.221483" x2="45.554224" y2="46.551779" />
|
||||
<line x1="-3.223258" y1="11.51909" x2="-3.223258" y2="12.638382" />
|
||||
<line x1="0.041696" y1="9.375092" x2="-3.223258" y2="11.51909" />
|
||||
<line x1="-7.844979" y1="5.460692" x2="-3.223258" y2="11.51909" />
|
||||
<line x1="-6.207993" y1="78.642247" x2="-6.207993" y2="79.015344" />
|
||||
<line x1="-3.759278" y1="77.034248" x2="-6.207993" y2="78.642247" />
|
||||
<line x1="-11.407429" y1="71.82655" x2="-6.207993" y2="78.642247" />
|
||||
<line x1="2.15122" y1="5.529945" x2="2.15122" y2="7.022335" />
|
||||
<line x1="14.394797" y1="-2.510047" x2="2.15122" y2="5.529945" />
|
||||
<line x1="-1.892786" y1="0.228848" x2="2.15122" y2="5.529945" />
|
||||
<line x1="-2.052427" y1="59.440412" x2="-2.052427" y2="60.932802" />
|
||||
<line x1="-1.236188" y1="58.904413" x2="-2.052427" y2="59.440412" />
|
||||
<line x1="-3.207857" y1="57.925813" x2="-2.052427" y2="59.440412" />
|
||||
<line x1="29.001823" y1="31.650982" x2="29.001823" y2="32.02408" />
|
||||
<line x1="40.429162" y1="24.146989" x2="29.001823" y2="31.650982" />
|
||||
<line x1="26.690963" y1="28.621783" x2="29.001823" y2="31.650982" />
|
||||
<line x1="-4.941002" y1="56.027012" x2="-4.941002" y2="56.773207" />
|
||||
<line x1="5.670099" y1="49.059018" x2="-4.941002" y2="56.027012" />
|
||||
<line x1="-8.407292" y1="51.483214" x2="-4.941002" y2="56.027012" />
|
||||
<line x1="10.250123" y1="-11.341592" x2="10.250123" y2="-9.476104" />
|
||||
<line x1="26.574893" y1="-22.061582" x2="10.250123" y2="-11.341592" />
|
||||
<line x1="9.672408" y1="-12.098891" x2="10.250123" y2="-11.341592" />
|
||||
<line x1="-23.491366" y1="19.908783" x2="-23.491366" y2="21.77427" />
|
||||
<line x1="-20.226412" y1="17.764785" x2="-23.491366" y2="19.908783" />
|
||||
<line x1="-31.579377" y1="9.306587" x2="-23.491366" y2="19.908783" />
|
||||
<line x1="-6.853698" y1="-8.066697" x2="-6.853698" y2="-6.20121" />
|
||||
<line x1="-0.32379" y1="-12.354693" x2="-6.853698" y2="-8.066697" />
|
||||
<line x1="-16.674854" y1="-20.940792" x2="-6.853698" y2="-8.066697" />
|
||||
<line x1="10.387979" y1="-15.232594" x2="10.387979" y2="-14.859496" />
|
||||
<line x1="24.264033" y1="-24.344586" x2="10.387979" y2="-15.232594" />
|
||||
<line x1="-1.166323" y1="-30.378587" x2="10.387979" y2="-15.232594" />
|
||||
<line x1="12.16282" y1="52.192471" x2="12.16282" y2="54.057958" />
|
||||
<line x1="19.508966" y1="47.368475" x2="12.16282" y2="52.192471" />
|
||||
<line x1="4.652524" y1="42.347575" x2="12.16282" y2="52.192471" />
|
||||
<line x1="28.111211" y1="47.589526" x2="28.111211" y2="49.081916" />
|
||||
<line x1="37.906073" y1="41.157532" x2="28.111211" y2="47.589526" />
|
||||
<line x1="26.955781" y1="46.074927" x2="28.111211" y2="47.589526" />
|
||||
<line x1="4.551855" y1="39.003677" x2="4.551855" y2="40.869165" />
|
||||
<line x1="6.184332" y1="37.931678" x2="4.551855" y2="39.003677" />
|
||||
<line x1="2.240995" y1="35.974478" x2="4.551855" y2="39.003677" />
|
||||
<line x1="-7.304451" y1="14.385636" x2="-7.304451" y2="15.131831" />
|
||||
<line x1="-5.671974" y1="13.313637" x2="-7.304451" y2="14.385636" />
|
||||
<line x1="-11.926171" y1="8.327238" x2="-7.304451" y2="14.385636" />
|
||||
<line x1="25.170047" y1="5.563948" x2="25.170047" y2="6.310143" />
|
||||
<line x1="39.86234" y1="-4.084043" x2="25.170047" y2="5.563948" />
|
||||
<line x1="21.703757" y1="1.02015" x2="25.170047" y2="5.563948" />
|
||||
<line x1="58.375517" y1="39.455635" x2="58.375517" y2="40.20183" />
|
||||
<line x1="68.170379" y1="33.023641" x2="58.375517" y2="39.455635" />
|
||||
<line x1="51.442936" y1="30.368039" x2="58.375517" y2="39.455635" />
|
||||
<line x1="13.127806" y1="20.105188" x2="13.127806" y2="21.22448" />
|
||||
<line x1="27.003861" y1="10.993196" x2="13.127806" y2="20.105188" />
|
||||
<line x1="11.394661" y1="17.833289" x2="13.127806" y2="20.105188" />
|
||||
<line x1="-31.743526" y1="-4.989207" x2="-31.743526" y2="-3.869914" />
|
||||
<line x1="-17.867472" y1="-14.101198" x2="-31.743526" y2="-4.989207" />
|
||||
<line x1="-42.142397" y1="-18.620601" x2="-31.743526" y2="-4.989207" />
|
||||
<line x1="-5.348184" y1="-31.602134" x2="-5.348184" y2="-30.109744" />
|
||||
<line x1="10.976586" y1="-42.322124" x2="-5.348184" y2="-31.602134" />
|
||||
<line x1="-16.32477" y1="-45.990828" x2="-5.348184" y2="-31.602134" />
|
||||
<line x1="32.468114" y1="35.635134" x2="32.468114" y2="37.127524" />
|
||||
<line x1="43.895453" y1="28.131141" x2="32.468114" y2="35.635134" />
|
||||
<line x1="30.734969" y1="33.363235" x2="32.468114" y2="35.635134" />
|
||||
<line x1="2.532107" y1="73.293356" x2="2.532107" y2="75.158844" />
|
||||
<line x1="7.429538" y1="70.077359" x2="2.532107" y2="73.293356" />
|
||||
<line x1="-3.245044" y1="65.72036" x2="2.532107" y2="73.293356" />
|
||||
<line x1="48.077314" y1="29.354687" x2="48.077314" y2="29.727785" />
|
||||
<line x1="59.504653" y1="21.850694" x2="48.077314" y2="29.354687" />
|
||||
<line x1="40.567018" y1="19.509792" x2="48.077314" y2="29.354687" />
|
||||
<line x1="23.325341" y1="27.235335" x2="23.325341" y2="27.98153" />
|
||||
<line x1="33.936441" y1="20.267341" x2="23.325341" y2="27.235335" />
|
||||
<line x1="20.436766" y1="23.448836" x2="23.325341" y2="27.235335" />
|
||||
<line x1="-13.670209" y1="33.155974" x2="-13.670209" y2="34.275267" />
|
||||
<line x1="-10.405255" y1="31.011976" x2="-13.670209" y2="33.155974" />
|
||||
<line x1="-21.75822" y1="22.553779" x2="-13.670209" y2="33.155974" />
|
||||
<line x1="9.300538" y1="68.271707" x2="9.300538" y2="69.017902" />
|
||||
<line x1="18.279162" y1="62.375713" x2="9.300538" y2="68.271707" />
|
||||
<line x1="4.101103" y1="61.45601" x2="9.300538" y2="68.271707" />
|
||||
<line x1="21.883308" y1="62.468862" x2="21.883308" y2="62.84196" />
|
||||
<line x1="30.861931" y1="56.572868" x2="21.883308" y2="62.468862" />
|
||||
<line x1="15.528442" y1="54.138566" x2="21.883308" y2="62.468862" />
|
||||
<line x1="-20.040477" y1="-22.140692" x2="-20.040477" y2="-20.275204" />
|
||||
<line x1="-7.796899" y1="-30.180684" x2="-20.040477" y2="-22.140692" />
|
||||
<line x1="-31.017063" y1="-36.529385" x2="-20.040477" y2="-22.140692" />
|
||||
<line x1="17.246186" y1="9.257546" x2="17.246186" y2="10.749936" />
|
||||
<line x1="22.143617" y1="6.041549" x2="17.246186" y2="9.257546" />
|
||||
<line x1="14.35761" y1="5.471048" x2="17.246186" y2="9.257546" />
|
||||
<line x1="-45.50802" y1="-19.820501" x2="-45.50802" y2="-18.701208" />
|
||||
<line x1="-29.999488" y1="-30.004491" x2="-45.50802" y2="-19.820501" />
|
||||
<line x1="-48.97431" y1="-24.364299" x2="-45.50802" y2="-19.820501" />
|
||||
<line x1="34.026217" y1="50.711874" x2="34.026217" y2="51.831167" />
|
||||
<line x1="41.372363" y1="45.887879" x2="34.026217" y2="50.711874" />
|
||||
<line x1="32.293072" y1="48.439975" x2="34.026217" y2="50.711874" />
|
||||
<line x1="-52.21297" y1="-2.541115" x2="-52.21297" y2="-2.168017" />
|
||||
<line x1="-41.601869" y1="-9.509108" x2="-52.21297" y2="-2.541115" />
|
||||
<line x1="-62.034126" y1="-15.415209" x2="-52.21297" y2="-2.541115" />
|
||||
<line x1="1.212527" y1="57.856061" x2="1.212527" y2="58.229158" />
|
||||
<line x1="8.558674" y1="53.032065" x2="1.212527" y2="57.856061" />
|
||||
<line x1="0.057097" y1="56.341461" x2="1.212527" y2="57.856061" />
|
||||
<line x1="8.055333" y1="36.312575" x2="8.055333" y2="36.685672" />
|
||||
<line x1="11.320287" y1="34.168577" x2="8.055333" y2="36.312575" />
|
||||
<line x1="6.322188" y1="34.040676" x2="8.055333" y2="36.312575" />
|
||||
<line x1="-67.005931" y1="3.57643" x2="-67.005931" y2="4.322625" />
|
||||
<line x1="-56.394831" y1="-3.391564" x2="-67.005931" y2="3.57643" />
|
||||
<line x1="-75.093942" y1="-7.025766" x2="-67.005931" y2="3.57643" />
|
||||
<line x1="-53.103582" y1="13.957075" x2="-53.103582" y2="14.330173" />
|
||||
<line x1="-46.573674" y1="9.669079" x2="-53.103582" y2="13.957075" />
|
||||
<line x1="-61.191593" y1="3.35488" x2="-53.103582" y2="13.957075" />
|
||||
</g>
|
||||
</g>
|
||||
</svg>
|
||||
|
After Width: | Height: | Size: 44 KiB |
|
Before Width: | Height: | Size: 990 KiB After Width: | Height: | Size: 990 KiB |
|
Before Width: | Height: | Size: 33 KiB After Width: | Height: | Size: 33 KiB |
BIN
docs/assets/ttt/ttt-ppp0101.png
Normal file
|
After Width: | Height: | Size: 325 KiB |
41
docs/assets/ttt/ttt-ppp0101.py
Normal file
|
|
@ -0,0 +1,41 @@
|
|||
"""
|
||||
Too Tall Toby Party Pack 01-01 Bearing Bracket
|
||||
"""
|
||||
from build123d import *
|
||||
from ocp_vscode import *
|
||||
|
||||
densa = 7800 / 1e6 # carbon steel density g/mm^3
|
||||
densb = 2700 / 1e6 # aluminum alloy
|
||||
densc = 1020 / 1e6 # ABS
|
||||
|
||||
with BuildPart() as p:
|
||||
with BuildSketch() as s:
|
||||
Rectangle(115, 50)
|
||||
with Locations((5 / 2, 0)):
|
||||
SlotOverall(90, 12, mode=Mode.SUBTRACT)
|
||||
extrude(amount=15)
|
||||
|
||||
with BuildSketch(Plane.XZ.offset(50 / 2)) as s3:
|
||||
with Locations((-115 / 2 + 26, 15)):
|
||||
SlotOverall(42 + 2 * 26 + 12, 2 * 26, rotation=90)
|
||||
zz = extrude(amount=-12)
|
||||
split(bisect_by=Plane.XY)
|
||||
edgs = p.part.edges().filter_by(Axis.Y).group_by(Axis.X)[-2]
|
||||
fillet(edgs, 9)
|
||||
|
||||
with Locations(zz.faces().sort_by(Axis.Y)[0]):
|
||||
with Locations((42 / 2 + 6, 0)):
|
||||
CounterBoreHole(24 / 2, 34 / 2, 4)
|
||||
mirror(about=Plane.XZ)
|
||||
|
||||
with BuildSketch() as s4:
|
||||
RectangleRounded(115, 50, 6)
|
||||
extrude(amount=80, mode=Mode.INTERSECT)
|
||||
# fillet does not work right, mode intersect is safer
|
||||
|
||||
with BuildSketch(Plane.YZ) as s2:
|
||||
Trapezoid(18, 8, 180 - 60, align=(Align.CENTER, Align.MIN))
|
||||
extrude(amount=80, both=True, mode=Mode.SUBTRACT)
|
||||
|
||||
show(p)
|
||||
print(f"\npart mass = {p.part.volume*densa:0.2f}")
|
||||
BIN
docs/assets/ttt/ttt-ppp0101_object.png
Normal file
|
After Width: | Height: | Size: 78 KiB |
BIN
docs/assets/ttt/ttt-ppp0102.png
Normal file
|
After Width: | Height: | Size: 317 KiB |
48
docs/assets/ttt/ttt-ppp0102.py
Normal file
|
|
@ -0,0 +1,48 @@
|
|||
"""
|
||||
Too Tall Toby Party Pack 01-02 Post Cap
|
||||
"""
|
||||
from build123d import *
|
||||
from ocp_vscode import *
|
||||
|
||||
densa = 7800 / 1e6 # carbon steel density g/mm^3
|
||||
densb = 2700 / 1e6 # aluminum alloy
|
||||
densc = 1020 / 1e6 # ABS
|
||||
|
||||
|
||||
# TTT Party Pack 01: PPP0102, mass(abs) = 43.09g
|
||||
with BuildPart() as p:
|
||||
with BuildSketch(Plane.XZ) as sk1:
|
||||
Rectangle(49, 48 - 8, align=(Align.CENTER, Align.MIN))
|
||||
Rectangle(9, 48, align=(Align.CENTER, Align.MIN))
|
||||
with Locations((9 / 2, 40)):
|
||||
Ellipse(20, 8)
|
||||
split(bisect_by=Plane.YZ)
|
||||
revolve(axis=Axis.Z)
|
||||
|
||||
with BuildSketch(Plane.YZ.offset(-15)) as xc1:
|
||||
with Locations((0, 40 / 2 - 17)):
|
||||
Ellipse(10 / 2, 4 / 2)
|
||||
with BuildLine(Plane.XZ) as l1:
|
||||
CenterArc((-15, 40 / 2), 17, 90, 180)
|
||||
sweep(path=l1)
|
||||
|
||||
fillet(p.edges().filter_by(GeomType.CIRCLE, reverse=True).group_by(Axis.X)[0], 1)
|
||||
|
||||
with BuildLine(mode=Mode.PRIVATE) as lc1:
|
||||
PolarLine(
|
||||
(42 / 2, 0), 37, 94, length_mode=LengthMode.VERTICAL
|
||||
) # construction line
|
||||
|
||||
pts = [
|
||||
(0, 0),
|
||||
(42 / 2, 0),
|
||||
((lc1.line @ 1).X, (lc1.line @ 1).Y),
|
||||
(0, (lc1.line @ 1).Y),
|
||||
]
|
||||
with BuildSketch(Plane.XZ) as sk2:
|
||||
Polygon(*pts, align=None)
|
||||
fillet(sk2.vertices().group_by(Axis.X)[1], 3)
|
||||
revolve(axis=Axis.Z, mode=Mode.SUBTRACT)
|
||||
|
||||
show(p)
|
||||
print(f"\npart mass = {p.part.volume*densa:0.2f}")
|
||||
BIN
docs/assets/ttt/ttt-ppp0102_object.png
Normal file
|
After Width: | Height: | Size: 75 KiB |
BIN
docs/assets/ttt/ttt-ppp0103.png
Normal file
|
After Width: | Height: | Size: 310 KiB |
33
docs/assets/ttt/ttt-ppp0103.py
Normal file
|
|
@ -0,0 +1,33 @@
|
|||
"""
|
||||
Too Tall Toby Party Pack 01-03 C Clamp Base
|
||||
"""
|
||||
from build123d import *
|
||||
from ocp_vscode import *
|
||||
|
||||
densa = 7800 / 1e6 # carbon steel density g/mm^3
|
||||
densb = 2700 / 1e6 # aluminum alloy
|
||||
densc = 1020 / 1e6 # ABS
|
||||
|
||||
|
||||
with BuildPart() as ppp0103:
|
||||
with BuildSketch() as sk1:
|
||||
RectangleRounded(34 * 2, 95, 18)
|
||||
with Locations((0, -2)):
|
||||
RectangleRounded((34 - 16) * 2, 95 - 18 - 14, 7, mode=Mode.SUBTRACT)
|
||||
with Locations((-34 / 2, 0)):
|
||||
Rectangle(34, 95, 0, mode=Mode.SUBTRACT)
|
||||
extrude(amount=16)
|
||||
with BuildSketch(Plane.XZ.offset(-95 / 2)) as cyl1:
|
||||
with Locations((0, 16 / 2)):
|
||||
Circle(16 / 2)
|
||||
extrude(amount=18)
|
||||
with BuildSketch(Plane.XZ.offset(95 / 2 - 14)) as cyl2:
|
||||
with Locations((0, 16 / 2)):
|
||||
Circle(16 / 2)
|
||||
extrude(amount=23)
|
||||
with Locations(Plane.XZ.offset(95 / 2 + 9)):
|
||||
with Locations((0, 16 / 2)):
|
||||
CounterSinkHole(5.5 / 2, 11.2 / 2, None, 90)
|
||||
|
||||
show(ppp0103)
|
||||
print(f"\npart mass = {ppp0103.part.volume*densb:0.2f}")
|
||||
BIN
docs/assets/ttt/ttt-ppp0103_object.png
Normal file
|
After Width: | Height: | Size: 43 KiB |
BIN
docs/assets/ttt/ttt-ppp0104.png
Normal file
|
After Width: | Height: | Size: 336 KiB |
56
docs/assets/ttt/ttt-ppp0104.py
Normal file
|
|
@ -0,0 +1,56 @@
|
|||
"""
|
||||
Too Tall Toby Party Pack 01-04 Angle Bracket
|
||||
"""
|
||||
from build123d import *
|
||||
from ocp_vscode import *
|
||||
|
||||
densa = 7800 / 1e6 # carbon steel density g/mm^3
|
||||
densb = 2700 / 1e6 # aluminum alloy
|
||||
densc = 1020 / 1e6 # ABS
|
||||
|
||||
d1, d2, d3 = 38, 26, 16
|
||||
h1, h2, h3, h4 = 20, 8, 7, 23
|
||||
w1, w2, w3 = 80, 10, 5
|
||||
f1, f2, f3 = 4, 10, 5
|
||||
sloth1, sloth2 = 18, 12
|
||||
slotw1, slotw2 = 17, 14
|
||||
|
||||
with BuildPart() as p:
|
||||
with BuildSketch() as s:
|
||||
Circle(d1 / 2)
|
||||
extrude(amount=h1)
|
||||
with BuildSketch(Plane.XY.offset(h1)) as s2:
|
||||
Circle(d2 / 2)
|
||||
extrude(amount=h2)
|
||||
with BuildSketch(Plane.YZ) as s3:
|
||||
Rectangle(d1 + 15, h3, align=(Align.CENTER, Align.MIN))
|
||||
extrude(amount=w1 - d1 / 2)
|
||||
# fillet workaround \/
|
||||
ped = p.part.edges().group_by(Axis.Z)[2].filter_by(GeomType.CIRCLE)
|
||||
fillet(ped, f1)
|
||||
with BuildSketch(Plane.YZ) as s3a:
|
||||
Rectangle(d1 + 15, 15, align=(Align.CENTER, Align.MIN))
|
||||
Rectangle(d1, 15, mode=Mode.SUBTRACT, align=(Align.CENTER, Align.MIN))
|
||||
extrude(amount=w1 - d1 / 2, mode=Mode.SUBTRACT)
|
||||
# end fillet workaround /\
|
||||
with BuildSketch() as s4:
|
||||
Circle(d3 / 2)
|
||||
extrude(amount=h1 + h2, mode=Mode.SUBTRACT)
|
||||
with BuildSketch() as s5:
|
||||
with Locations((w1 - d1 / 2 - w2 / 2, 0)):
|
||||
Rectangle(w2, d1)
|
||||
extrude(amount=-h4)
|
||||
fillet(p.part.edges().group_by(Axis.X)[-1].sort_by(Axis.Z)[-1], f2)
|
||||
fillet(p.part.edges().group_by(Axis.X)[-4].sort_by(Axis.Z)[-2], f3)
|
||||
pln = Plane.YZ.offset(w1 - d1 / 2)
|
||||
with BuildSketch(pln) as s6:
|
||||
with Locations((0, -h4)):
|
||||
SlotOverall(slotw1 * 2, sloth1, 90)
|
||||
extrude(amount=-w3, mode=Mode.SUBTRACT)
|
||||
with BuildSketch(pln) as s6b:
|
||||
with Locations((0, -h4)):
|
||||
SlotOverall(slotw2 * 2, sloth2, 90)
|
||||
extrude(amount=-w2, mode=Mode.SUBTRACT)
|
||||
|
||||
show(p)
|
||||
print(f"\npart mass = {p.part.volume*densa:0.2f}")
|
||||
BIN
docs/assets/ttt/ttt-ppp0104_object.png
Normal file
|
After Width: | Height: | Size: 55 KiB |
BIN
docs/assets/ttt/ttt-ppp0105.png
Normal file
|
After Width: | Height: | Size: 344 KiB |
29
docs/assets/ttt/ttt-ppp0105.py
Normal file
|
|
@ -0,0 +1,29 @@
|
|||
"""
|
||||
Too Tall Toby Party Pack 01-05 Paste Sleeve
|
||||
"""
|
||||
from build123d import *
|
||||
from ocp_vscode import *
|
||||
|
||||
densa = 7800 / 1e6 # carbon steel density g/mm^3
|
||||
densb = 2700 / 1e6 # aluminum alloy
|
||||
densc = 1020 / 1e6 # ABS
|
||||
|
||||
with BuildPart() as p:
|
||||
with BuildSketch() as s:
|
||||
SlotOverall(45, 38)
|
||||
offset(amount=3)
|
||||
with BuildSketch(Plane.XY.offset(133 - 30)) as s2:
|
||||
SlotOverall(60, 4)
|
||||
offset(amount=3)
|
||||
loft()
|
||||
|
||||
with BuildSketch() as s3:
|
||||
SlotOverall(45, 38)
|
||||
with BuildSketch(Plane.XY.offset(133 - 30)) as s4:
|
||||
SlotOverall(60, 4)
|
||||
loft(mode=Mode.SUBTRACT)
|
||||
|
||||
extrude(p.part.faces().sort_by(Axis.Z)[0], amount=30)
|
||||
|
||||
show(p)
|
||||
print(f"\npart mass = {p.part.volume*densc:0.2f}")
|
||||
BIN
docs/assets/ttt/ttt-ppp0105_object.png
Normal file
|
After Width: | Height: | Size: 82 KiB |
BIN
docs/assets/ttt/ttt-ppp0106.png
Normal file
|
After Width: | Height: | Size: 341 KiB |
51
docs/assets/ttt/ttt-ppp0106.py
Normal file
|
|
@ -0,0 +1,51 @@
|
|||
"""
|
||||
Too Tall Toby Party Pack 01-06 Bearing Jig
|
||||
"""
|
||||
from build123d import *
|
||||
from ocp_vscode import *
|
||||
|
||||
densa = 7800 / 1e6 # carbon steel density g/mm^3
|
||||
densb = 2700 / 1e6 # aluminum alloy
|
||||
densc = 1020 / 1e6 # ABS
|
||||
|
||||
r1, r2, r3, r4, r5 = 30 / 2, 13 / 2, 12 / 2, 10, 6 # radii used
|
||||
x1 = 44 # lengths used
|
||||
y1, y2, y3, y4, y_tot = 36, 36 - 22 / 2, 22 / 2, 42, 69 # widths used
|
||||
|
||||
with BuildSketch(Location((0, -r1, y3))) as sk_body:
|
||||
with BuildLine() as l:
|
||||
c1 = Line((r1, 0), (r1, y_tot), mode=Mode.PRIVATE) # construction line
|
||||
m1 = Line((0, y_tot), (x1 / 2, y_tot))
|
||||
m2 = JernArc(m1 @ 1, m1 % 1, r4, -90 - 45)
|
||||
m3 = IntersectingLine(m2 @ 1, m2 % 1, c1)
|
||||
m4 = Line(m3 @ 1, (r1, r1))
|
||||
m5 = JernArc(m4 @ 1, m4 % 1, r1, -90)
|
||||
m6 = Line(m5 @ 1, m1 @ 0)
|
||||
mirror(make_face(l.line), Plane.YZ)
|
||||
fillet(sk_body.vertices().group_by(Axis.Y)[1], 12)
|
||||
with Locations((x1 / 2, y_tot - 10), (-x1 / 2, y_tot - 10)):
|
||||
Circle(r2, mode=Mode.SUBTRACT)
|
||||
# Keyway
|
||||
with Locations((0, r1)):
|
||||
Circle(r3, mode=Mode.SUBTRACT)
|
||||
Rectangle(4, 3 + 6, align=(Align.CENTER, Align.MIN), mode=Mode.SUBTRACT)
|
||||
|
||||
with BuildPart() as p:
|
||||
Box(200, 200, 22) # Oversized plate
|
||||
# Cylinder underneath
|
||||
Cylinder(r1, y2, align=(Align.CENTER, Align.CENTER, Align.MAX))
|
||||
fillet(p.edges(Select.NEW), r5) # Weld together
|
||||
extrude(sk_body.sketch, amount=-y1, mode=Mode.INTERSECT) # Cut to shape
|
||||
# Remove slot
|
||||
with Locations((0, y_tot - r1 - y4, 0)):
|
||||
Box(
|
||||
y_tot,
|
||||
y_tot,
|
||||
10,
|
||||
align=(Align.CENTER, Align.MIN, Align.CENTER),
|
||||
mode=Mode.SUBTRACT,
|
||||
)
|
||||
|
||||
show(p)
|
||||
print(f"\npart mass = {p.part.volume*densa:0.2f}")
|
||||
print(p.part.bounding_box().size)
|
||||
BIN
docs/assets/ttt/ttt-ppp0106_object.png
Normal file
|
After Width: | Height: | Size: 72 KiB |
BIN
docs/assets/ttt/ttt-ppp0107.png
Normal file
|
After Width: | Height: | Size: 467 KiB |
51
docs/assets/ttt/ttt-ppp0107.py
Normal file
|
|
@ -0,0 +1,51 @@
|
|||
"""
|
||||
Too Tall Toby Party Pack 01-07 Flanged Hub
|
||||
"""
|
||||
from build123d import *
|
||||
from ocp_vscode import *
|
||||
|
||||
densa = 7800 / 1e6 # carbon steel density g/mm^3
|
||||
densb = 2700 / 1e6 # aluminum alloy
|
||||
densc = 1020 / 1e6 # ABS
|
||||
|
||||
with BuildPart() as p:
|
||||
with BuildSketch() as s:
|
||||
Circle(130 / 2)
|
||||
extrude(amount=8)
|
||||
with BuildSketch(Plane.XY.offset(8)) as s2:
|
||||
Circle(84 / 2)
|
||||
extrude(amount=25 - 8)
|
||||
with BuildSketch(Plane.XY.offset(25)) as s3:
|
||||
Circle(35 / 2)
|
||||
extrude(amount=52 - 25)
|
||||
with BuildSketch() as s4:
|
||||
Circle(73 / 2)
|
||||
extrude(amount=18, mode=Mode.SUBTRACT)
|
||||
pln2 = p.part.faces().sort_by(Axis.Z)[5]
|
||||
with BuildSketch(Plane.XY.offset(52)) as s5:
|
||||
Circle(20 / 2)
|
||||
extrude(amount=-52, mode=Mode.SUBTRACT)
|
||||
fillet(
|
||||
p.part.edges()
|
||||
.filter_by(GeomType.CIRCLE)
|
||||
.sort_by(Axis.Z)[2:-2]
|
||||
.sort_by(SortBy.RADIUS)[1:],
|
||||
3,
|
||||
)
|
||||
pln = Plane(pln2)
|
||||
pln.origin = pln.origin + Vector(20 / 2, 0, 0)
|
||||
pln = pln.rotated((0, 45, 0))
|
||||
pln = pln.offset(-25 + 3 + 0.10)
|
||||
with BuildSketch(pln) as s6:
|
||||
Rectangle((73 - 35) / 2 * 1.414 + 5, 3)
|
||||
zz = extrude(amount=15, taper=-20 / 2, mode=Mode.PRIVATE)
|
||||
zz2 = split(zz, bisect_by=Plane.XY.offset(25), mode=Mode.PRIVATE)
|
||||
zz3 = split(zz2, bisect_by=Plane.YZ.offset(35 / 2 - 1), mode=Mode.PRIVATE)
|
||||
with PolarLocations(0, 3):
|
||||
add(zz3)
|
||||
with Locations(Plane.XY.offset(8)):
|
||||
with PolarLocations(107.95 / 2, 6):
|
||||
CounterBoreHole(6 / 2, 13 / 2, 4)
|
||||
|
||||
show(p)
|
||||
print(f"\npart mass = {p.part.volume*densb:0.2f}")
|
||||
BIN
docs/assets/ttt/ttt-ppp0107_object.png
Normal file
|
After Width: | Height: | Size: 95 KiB |
BIN
docs/assets/ttt/ttt-ppp0108.png
Normal file
|
After Width: | Height: | Size: 315 KiB |
46
docs/assets/ttt/ttt-ppp0108.py
Normal file
|
|
@ -0,0 +1,46 @@
|
|||
"""
|
||||
Too Tall Toby Party Pack 01-08 Tie Plate
|
||||
"""
|
||||
from build123d import *
|
||||
from ocp_vscode import *
|
||||
|
||||
densa = 7800 / 1e6 # carbon steel density g/mm^3
|
||||
densb = 2700 / 1e6 # aluminum alloy
|
||||
densc = 1020 / 1e6 # ABS
|
||||
|
||||
with BuildPart() as p:
|
||||
with BuildSketch() as s1:
|
||||
Rectangle(188 / 2 - 33, 162, align=(Align.MIN, Align.CENTER))
|
||||
with Locations((188 / 2 - 33, 0)):
|
||||
SlotOverall(190, 33 * 2, rotation=90)
|
||||
mirror(about=Plane.YZ)
|
||||
with GridLocations(188 - 2 * 33, 190 - 2 * 33, 2, 2):
|
||||
Circle(29 / 2, mode=Mode.SUBTRACT)
|
||||
Circle(84 / 2, mode=Mode.SUBTRACT)
|
||||
extrude(amount=16)
|
||||
|
||||
with BuildPart() as p2:
|
||||
with BuildSketch(Plane.XZ) as s2:
|
||||
with BuildLine() as l1:
|
||||
l1 = Polyline(
|
||||
(222 / 2 + 14 - 40 - 40, 0),
|
||||
(222 / 2 + 14 - 40, -35 + 16),
|
||||
(222 / 2 + 14, -35 + 16),
|
||||
(222 / 2 + 14, -35 + 16 + 30),
|
||||
(222 / 2 + 14 - 40 - 40, -35 + 16 + 30),
|
||||
close=True,
|
||||
)
|
||||
make_face()
|
||||
with Locations((222 / 2, -35 + 16 + 14)):
|
||||
Circle(11 / 2, mode=Mode.SUBTRACT)
|
||||
extrude(amount=20 / 2, both=True)
|
||||
with BuildSketch() as s3:
|
||||
with Locations(l1 @ 0):
|
||||
Rectangle(40 + 40, 8, align=(Align.MIN, Align.CENTER))
|
||||
with Locations((40, 0)):
|
||||
Rectangle(40, 20, align=(Align.MIN, Align.CENTER))
|
||||
extrude(amount=30, both=True, mode=Mode.INTERSECT)
|
||||
mirror(about=Plane.YZ)
|
||||
|
||||
show(p)
|
||||
print(f"\npart mass = {p.part.volume*densa:0.2f}")
|
||||
BIN
docs/assets/ttt/ttt-ppp0108_object.png
Normal file
|
After Width: | Height: | Size: 41 KiB |
BIN
docs/assets/ttt/ttt-ppp0109.png
Normal file
|
After Width: | Height: | Size: 344 KiB |
55
docs/assets/ttt/ttt-ppp0109.py
Normal file
|
|
@ -0,0 +1,55 @@
|
|||
"""
|
||||
Too Tall Toby Party Pack 01-09 Corner Tie
|
||||
"""
|
||||
from math import sqrt
|
||||
from build123d import *
|
||||
from ocp_vscode import *
|
||||
|
||||
densa = 7800 / 1e6 # carbon steel density g/mm^3
|
||||
densb = 2700 / 1e6 # aluminum alloy
|
||||
densc = 1020 / 1e6 # ABS
|
||||
|
||||
with BuildPart() as ppp109:
|
||||
with BuildSketch() as one:
|
||||
Rectangle(69, 75, align=(Align.MAX, Align.CENTER))
|
||||
fillet(one.vertices().group_by(Axis.X)[0], 17)
|
||||
extrude(amount=13)
|
||||
centers = [
|
||||
arc.arc_center
|
||||
for arc in ppp109.edges().filter_by(GeomType.CIRCLE).group_by(Axis.Z)[-1]
|
||||
]
|
||||
with Locations(*centers):
|
||||
CounterBoreHole(radius=8 / 2, counter_bore_radius=15 / 2, counter_bore_depth=4)
|
||||
|
||||
with BuildSketch(Plane.YZ) as two:
|
||||
with Locations((0, 45)):
|
||||
Circle(15)
|
||||
with BuildLine() as bl:
|
||||
c = Line((75 / 2, 0), (75 / 2, 60), mode=Mode.PRIVATE)
|
||||
u = two.edge().find_tangent(75 / 2 + 90)[0] # where is the slope 75/2?
|
||||
l1 = IntersectingLine(
|
||||
two.edge().position_at(u), -two.edge().tangent_at(u), other=c
|
||||
)
|
||||
Line(l1 @ 0, (0, 45))
|
||||
Polyline((0, 0), c @ 0, l1 @ 1)
|
||||
mirror(about=Plane.YZ)
|
||||
make_face()
|
||||
with Locations((0, 45)):
|
||||
Circle(12 / 2, mode=Mode.SUBTRACT)
|
||||
extrude(amount=-13)
|
||||
|
||||
with BuildSketch(Plane((0, 0, 0), x_dir=(1, 0, 0), z_dir=(1, 0, 1))) as three:
|
||||
Rectangle(45 * 2 / sqrt(2) - 37.5, 75, align=(Align.MIN, Align.CENTER))
|
||||
with Locations(three.edges().sort_by(Axis.X)[-1].center()):
|
||||
Circle(37.5)
|
||||
Circle(33 / 2, mode=Mode.SUBTRACT)
|
||||
split(bisect_by=Plane.YZ)
|
||||
extrude(amount=6)
|
||||
f = ppp109.faces().filter_by(Axis((0, 0, 0), (-1, 0, 1)))[0]
|
||||
# extrude(f, until=Until.NEXT) # throws a warning
|
||||
extrude(f, amount=10)
|
||||
fillet(ppp109.edge(Select.NEW), 16)
|
||||
|
||||
|
||||
show(ppp109)
|
||||
print(f"\npart mass = {ppp109.part.volume*densb:0.2f}")
|
||||
BIN
docs/assets/ttt/ttt-ppp0109_object.png
Normal file
|
After Width: | Height: | Size: 48 KiB |
BIN
docs/assets/ttt/ttt-ppp0110.png
Normal file
|
After Width: | Height: | Size: 288 KiB |
51
docs/assets/ttt/ttt-ppp0110.py
Normal file
|
|
@ -0,0 +1,51 @@
|
|||
"""
|
||||
Too Tall Toby Party Pack 01-10 Light Cap
|
||||
"""
|
||||
from build123d import *
|
||||
from ocp_vscode import *
|
||||
|
||||
densa = 7800 / 1e6 # carbon steel density g/mm^3
|
||||
densb = 2700 / 1e6 # aluminum alloy
|
||||
densc = 1020 / 1e6 # ABS
|
||||
|
||||
with BuildPart() as p:
|
||||
with BuildSketch(Plane.YZ.rotated((90, 0, 0))) as s:
|
||||
with BuildLine() as l:
|
||||
n2 = JernArc((0, 46), (1, 0), 40, -90)
|
||||
n3 = Line(n2 @ 1, n2 @ 0)
|
||||
make_face()
|
||||
|
||||
with BuildLine() as l2:
|
||||
m1 = Line((0, 0), (42, 0))
|
||||
m2 = Line((0, 0.01), (42, 0.01))
|
||||
m3 = Line(m1 @ 0, m2 @ 0)
|
||||
m4 = Line(m1 @ 1, m2 @ 1)
|
||||
make_face()
|
||||
make_hull()
|
||||
extrude(amount=100 / 2)
|
||||
revolve(s.sketch, axis=Axis.Y.reverse(), revolution_arc=-90)
|
||||
mirror(about=Plane(p.part.faces().sort_by(Axis.X)[-1]))
|
||||
mirror(about=Plane.XY)
|
||||
|
||||
with BuildPart() as p2:
|
||||
add(p.part)
|
||||
offset(amount=-8)
|
||||
|
||||
with BuildPart() as pzzz:
|
||||
add(p2.part)
|
||||
split(bisect_by=Plane.XZ.offset(46 - 16), keep=Keep.BOTTOM)
|
||||
fillet(pzzz.part.faces().filter_by(Axis.Y).sort_by(Axis.Y)[0].edges(), 12)
|
||||
|
||||
with BuildPart() as p3:
|
||||
with BuildSketch(Plane.XZ) as s2:
|
||||
add(p.part.faces().sort_by(Axis.Y)[-1])
|
||||
offset(amount=-8)
|
||||
loft([p2.part.faces().sort_by(Axis.Y)[-5], s2.sketch.faces()[0]])
|
||||
|
||||
with BuildPart() as ppp0110:
|
||||
add(p.part)
|
||||
add(pzzz.part, mode=Mode.SUBTRACT)
|
||||
add(p3.part, mode=Mode.SUBTRACT)
|
||||
|
||||
show(ppp0110)
|
||||
print(f"\npart mass = {ppp0110.part.volume*densc:0.2f}")
|
||||
BIN
docs/assets/ttt/ttt-ppp0110_object.png
Normal file
|
After Width: | Height: | Size: 68 KiB |
|
|
@ -662,7 +662,7 @@ example.
|
|||
|
||||
* **Algebra mode**
|
||||
|
||||
Use the operator ``*`` to relocate the plane (post-mulitplication!).
|
||||
Use the operator ``*`` to relocate the plane (post-multiplication!).
|
||||
|
||||
.. literalinclude:: general_examples_algebra.py
|
||||
:start-after: [Ex. 22]
|
||||
|
|
|
|||
|
|
@ -26,7 +26,6 @@ For the following use the helper function:
|
|||
|
||||
show_object(face, name="face")
|
||||
show_object(location_symbol(loc), name="location")
|
||||
|
||||
|
||||
.. image:: assets/location-example-01.png
|
||||
|
||||
|
|
@ -40,7 +39,6 @@ For the following use the helper function:
|
|||
|
||||
show_object(face, name="face")
|
||||
show_object(plane_symbol(plane), name="plane")
|
||||
|
||||
|
||||
.. image:: assets/location-example-07.png
|
||||
|
||||
|
|
@ -53,7 +51,7 @@ Relative positioning to a plane
|
|||
1. **Position an object on a plane relative to the plane**
|
||||
|
||||
.. code-block:: python
|
||||
|
||||
|
||||
loc = Location((0.1, 0.2, 0.3), (10, 20, 30))
|
||||
|
||||
face = loc * Rectangle(1,2)
|
||||
|
|
@ -65,11 +63,10 @@ Relative positioning to a plane
|
|||
show_object(face, name="face")
|
||||
show_object(location_symbol(loc), name="location")
|
||||
show_object(box, name="box")
|
||||
|
||||
|
||||
.. image:: assets/location-example-02.png
|
||||
|
||||
The ``x``, ``y``, ``z`` components of ``Pos(0.2, 0.4, 0.1)`` are relative to the ``x``-axis, ``y``-axis or
|
||||
The ``x``, ``y``, ``z`` components of ``Pos(0.2, 0.4, 0.1)`` are relative to the ``x``-axis, ``y``-axis or
|
||||
``z``-axis of the underlying location ``loc``.
|
||||
|
||||
Note: ``Plane(loc) *``, ``Plane(face.location) *`` and ``loc *`` are equivalent in this example.
|
||||
|
|
@ -87,7 +84,6 @@ Relative positioning to a plane
|
|||
show_object(face, name="face")
|
||||
show_object(location_symbol(loc), name="location")
|
||||
show_object(box, name="box")
|
||||
|
||||
|
||||
.. image:: assets/location-example-03.png
|
||||
|
||||
|
|
@ -107,7 +103,6 @@ Relative positioning to a plane
|
|||
show_object(face, name="face")
|
||||
show_object(location_symbol(loc), name="location")
|
||||
show_object(box, name="box")
|
||||
|
||||
|
||||
.. image:: assets/location-example-04.png
|
||||
|
||||
|
|
@ -127,11 +122,10 @@ Relative positioning to a plane
|
|||
show_object(location_symbol(loc), name="location")
|
||||
show_object(box, name="box")
|
||||
show_object(location_symbol(loc * Rot(20, 40, 80), 0.5), options={"color":(0, 255, 255)}, name="local_location")
|
||||
|
||||
|
||||
.. image:: assets/location-example-05.png
|
||||
|
||||
The box is positioned via ``Pos(0.2, 0.4, 0.1)`` relativce to the location ``loc * Rot(20, 40, 80)``
|
||||
The box is positioned via ``Pos(0.2, 0.4, 0.1)`` relative to the location ``loc * Rot(20, 40, 80)``
|
||||
|
||||
4. **Position and rotate an object relative to a location**
|
||||
|
||||
|
|
@ -147,7 +141,6 @@ Relative positioning to a plane
|
|||
show_object(location_symbol(loc), name="location")
|
||||
show_object(box, name="box")
|
||||
show_object(location_symbol(loc * Pos(0.2, 0.4, 0.1), 0.5), options={"color":(0, 255, 255)}, name="local_location")
|
||||
|
||||
|
||||
.. image:: assets/location-example-06.png
|
||||
|
||||
|
|
|
|||
|
|
@ -22,7 +22,7 @@ with BuildPart() as latch:
|
|||
offset(amount=-2)
|
||||
fillet(slide_hole.vertices(), 1)
|
||||
extrude(amount=-68, mode=Mode.SUBTRACT)
|
||||
# Slot for the hangle to slide in
|
||||
# Slot for the handle to slide in
|
||||
with BuildSketch(latch.faces().sort_by(Axis.Z)[-1]):
|
||||
SlotOverall(32, 8)
|
||||
extrude(amount=-2, mode=Mode.SUBTRACT)
|
||||
|
|
|
|||
|
|
@ -131,8 +131,27 @@ interchange objects between the two systems by transferring the ``wrapped`` obje
|
|||
Self Intersection
|
||||
*****************
|
||||
|
||||
Avoid creating objects that intersect themselves - even if at a single vertex - as these topoplogies
|
||||
Avoid creating objects that intersect themselves - even if at a single vertex - as these topologies
|
||||
will almost certainly be invalid (even if :meth:`~topology.Shape.is_valid` reports a ``True`` value).
|
||||
An example of where this my arise is with the thread of a screw (or any helical shape) where after
|
||||
one complete revolution the part may contact itself. One is likely be more successful if the part
|
||||
is split into multiple sections - say 180° of a helix - which are then stored in an assembly.
|
||||
|
||||
|
||||
**************************
|
||||
Packing Objects on a Plane
|
||||
**************************
|
||||
|
||||
When designing independent shapes it's common to place each at or near
|
||||
the global origin, which can make it tricky to visualize many shapes at
|
||||
once. :meth:`pack.pack` will translate the :class:`~topology.Shape`'s passed to it so
|
||||
that they don't overlap, with an optional padding/spacing. Here's the
|
||||
result of packing a bunch of overlapping boxes (left) using some
|
||||
padding (right):
|
||||
|
||||
.. image:: assets/packed_boxes_input.svg
|
||||
:width: 200
|
||||
:align: left
|
||||
|
||||
.. image:: assets/packed_boxes_output.svg
|
||||
:align: right
|
||||
|
|
|
|||
213
docs/tttt.rst
|
|
@ -9,27 +9,228 @@ To enhance users' proficiency with Build123D, this section offers a series of ch
|
|||
In these challenges, users are presented with a CAD drawing and tasked with designing the
|
||||
part. Their goal is to match the part's mass to a specified target.
|
||||
|
||||
These drawings were skillfully crafted and generously provided to Build123D by TooTallToby,
|
||||
a renowned figure in the realm of 3D CAD. TooTallToby is the host of the World Championship
|
||||
These drawings were skillfully crafted and generously provided to Build123D by Too Tall Toby,
|
||||
a renowned figure in the realm of 3D CAD. Too Tall Toby is the host of the World Championship
|
||||
of 3D CAD Speedmodeling. For additional 3D CAD challenges and content, be sure to
|
||||
visit `Toby's youtube channel <https://www.Youtube.com/TooTallToby>`_.
|
||||
|
||||
Feel free to click on the parts below to embark on these engaging challenges.
|
||||
|
||||
.. grid:: 2
|
||||
.. grid:: 3
|
||||
|
||||
.. grid-item-card:: Party Pack 01-01 Bearing Bracket
|
||||
:img-top: assets/ttt/ttt-ppp0101_object.png
|
||||
:link: ttt-ppp0101
|
||||
:link-type: ref
|
||||
|
||||
.. grid-item-card:: Party Pack 01-02 Post Cap
|
||||
:img-top: assets/ttt/ttt-ppp0102_object.png
|
||||
:link: ttt-ppp0102
|
||||
:link-type: ref
|
||||
|
||||
.. grid-item-card:: Party Pack 01-03 C Clamp Base
|
||||
:img-top: assets/ttt/ttt-ppp0103_object.png
|
||||
:link: ttt-ppp0103
|
||||
:link-type: ref
|
||||
|
||||
.. grid-item-card:: Party Pack 01-04 Angle Bracket
|
||||
:img-top: assets/ttt/ttt-ppp0104_object.png
|
||||
:link: ttt-ppp0104
|
||||
:link-type: ref
|
||||
|
||||
.. grid-item-card:: Party Pack 01-05 Paste Sleeve
|
||||
:img-top: assets/ttt/ttt-ppp0105_object.png
|
||||
:link: ttt-ppp0105
|
||||
:link-type: ref
|
||||
|
||||
.. grid-item-card:: Party Pack 01-06 Bearing Jig
|
||||
:img-top: assets/ttt/ttt-ppp0106_object.png
|
||||
:link: ttt-ppp0106
|
||||
:link-type: ref
|
||||
|
||||
.. grid-item-card:: Party Pack 01-07 Flanged Hub
|
||||
:img-top: assets/ttt/ttt-ppp0107_object.png
|
||||
:link: ttt-ppp0107
|
||||
:link-type: ref
|
||||
|
||||
.. grid-item-card:: Party Pack 01-08 Tie Plate
|
||||
:img-top: assets/ttt/ttt-ppp0108_object.png
|
||||
:link: ttt-ppp0108
|
||||
:link-type: ref
|
||||
|
||||
.. grid-item-card:: Party Pack 01-09 Corner Tie
|
||||
:img-top: assets/ttt/ttt-ppp0109_object.png
|
||||
:link: ttt-ppp0109
|
||||
:link-type: ref
|
||||
|
||||
.. grid-item-card:: Party Pack 01-10 Light Cap
|
||||
:img-top: assets/ttt/ttt-ppp0110_object.png
|
||||
:link: ttt-ppp0110
|
||||
:link-type: ref
|
||||
|
||||
.. grid-item-card:: 23-T-24 Curved Support
|
||||
:img-top: assets/ttt-23-t-24-curved_support_object.png
|
||||
:img-top: assets/ttt/ttt-23-t-24-curved_support_object.png
|
||||
:link: ttt-23-t-24
|
||||
:link-type: ref
|
||||
|
||||
|
||||
.. _ttt-ppp0101:
|
||||
|
||||
Party Pack 01-01 Bearing Bracket
|
||||
--------------------------------
|
||||
.. image:: assets/ttt/ttt-ppp0101.png
|
||||
:align: center
|
||||
|
||||
.. dropdown:: Object Mass
|
||||
|
||||
797.15 g
|
||||
|
||||
.. dropdown:: Reference Implementation
|
||||
|
||||
.. literalinclude:: assets/ttt/ttt-ppp0101.py
|
||||
|
||||
|
||||
.. _ttt-ppp0102:
|
||||
|
||||
Party Pack 01-02 Post Cap
|
||||
--------------------------------
|
||||
.. image:: assets/ttt/ttt-ppp0102.png
|
||||
:align: center
|
||||
|
||||
.. dropdown:: Object Mass
|
||||
|
||||
43.09 g
|
||||
|
||||
.. dropdown:: Reference Implementation
|
||||
|
||||
.. literalinclude:: assets/ttt/ttt-ppp0102.py
|
||||
|
||||
.. _ttt-ppp0103:
|
||||
|
||||
Party Pack 01-03 C Clamp Base
|
||||
--------------------------------
|
||||
.. image:: assets/ttt/ttt-ppp0103.png
|
||||
:align: center
|
||||
|
||||
.. dropdown:: Object Mass
|
||||
|
||||
96.13 g
|
||||
|
||||
.. dropdown:: Reference Implementation
|
||||
|
||||
.. literalinclude:: assets/ttt/ttt-ppp0103.py
|
||||
|
||||
.. _ttt-ppp0104:
|
||||
|
||||
Party Pack 01-04 Angle Bracket
|
||||
--------------------------------
|
||||
.. image:: assets/ttt/ttt-ppp0104.png
|
||||
:align: center
|
||||
|
||||
.. dropdown:: Object Mass
|
||||
|
||||
310.00 g
|
||||
|
||||
.. dropdown:: Reference Implementation
|
||||
|
||||
.. literalinclude:: assets/ttt/ttt-ppp0104.py
|
||||
|
||||
.. _ttt-ppp0105:
|
||||
|
||||
Party Pack 01-05 Paste Sleeve
|
||||
--------------------------------
|
||||
.. image:: assets/ttt/ttt-ppp0105.png
|
||||
:align: center
|
||||
|
||||
.. dropdown:: Object Mass
|
||||
|
||||
57.08 g
|
||||
|
||||
.. dropdown:: Reference Implementation
|
||||
|
||||
.. literalinclude:: assets/ttt/ttt-ppp0105.py
|
||||
|
||||
.. _ttt-ppp0106:
|
||||
|
||||
Party Pack 01-06 Bearing Jig
|
||||
--------------------------------
|
||||
.. image:: assets/ttt/ttt-ppp0106.png
|
||||
:align: center
|
||||
|
||||
.. dropdown:: Object Mass
|
||||
|
||||
328.02 g
|
||||
|
||||
.. dropdown:: Reference Implementation
|
||||
|
||||
.. literalinclude:: assets/ttt/ttt-ppp0106.py
|
||||
|
||||
.. _ttt-ppp0107:
|
||||
|
||||
Party Pack 01-07 Flanged Hub
|
||||
--------------------------------
|
||||
.. image:: assets/ttt/ttt-ppp0107.png
|
||||
:align: center
|
||||
|
||||
.. dropdown:: Object Mass
|
||||
|
||||
372.99 g
|
||||
|
||||
.. dropdown:: Reference Implementation
|
||||
|
||||
.. literalinclude:: assets/ttt/ttt-ppp0107.py
|
||||
|
||||
.. _ttt-ppp0108:
|
||||
|
||||
Party Pack 01-08 Tie Plate
|
||||
--------------------------------
|
||||
.. image:: assets/ttt/ttt-ppp0108.png
|
||||
:align: center
|
||||
|
||||
.. dropdown:: Object Mass
|
||||
|
||||
3387.06 g
|
||||
|
||||
.. dropdown:: Reference Implementation
|
||||
|
||||
.. literalinclude:: assets/ttt/ttt-ppp0108.py
|
||||
|
||||
.. _ttt-ppp0109:
|
||||
|
||||
Party Pack 01-09 Corner Tie
|
||||
--------------------------------
|
||||
.. image:: assets/ttt/ttt-ppp0109.png
|
||||
:align: center
|
||||
|
||||
.. dropdown:: Object Mass
|
||||
|
||||
307.23 g
|
||||
|
||||
.. dropdown:: Reference Implementation
|
||||
|
||||
.. literalinclude:: assets/ttt/ttt-ppp0109.py
|
||||
|
||||
.. _ttt-ppp0110:
|
||||
|
||||
Party Pack 01-10 Light Cap
|
||||
--------------------------------
|
||||
.. image:: assets/ttt/ttt-ppp0110.png
|
||||
:align: center
|
||||
|
||||
.. dropdown:: Object Mass
|
||||
|
||||
211.30 g
|
||||
|
||||
.. dropdown:: Reference Implementation
|
||||
|
||||
.. literalinclude:: assets/ttt/ttt-ppp0110.py
|
||||
|
||||
.. _ttt-23-t-24:
|
||||
|
||||
23-T-24 Curved Support
|
||||
----------------------
|
||||
|
||||
.. image:: assets/ttt-23-t-24-curved_support.png
|
||||
.. image:: assets/ttt/ttt-23-t-24-curved_support.png
|
||||
:align: center
|
||||
|
||||
.. dropdown:: Object Mass
|
||||
|
|
@ -38,4 +239,4 @@ Feel free to click on the parts below to embark on these engaging challenges.
|
|||
|
||||
.. dropdown:: Reference Implementation
|
||||
|
||||
.. literalinclude:: assets/ttt-23-t-24-curved_support.py
|
||||
.. literalinclude:: assets/ttt/ttt-23-t-24-curved_support.py
|
||||
|
|
|
|||
34
examples/packed_boxes.py
Normal file
|
|
@ -0,0 +1,34 @@
|
|||
"""
|
||||
|
||||
name: packed_boxes.py
|
||||
by: fischman
|
||||
date: November 9th 2023
|
||||
|
||||
desc: Demo packing a bunch of boxes in 2D.
|
||||
|
||||
"""
|
||||
import functools
|
||||
import operator
|
||||
import random
|
||||
import build123d as bd
|
||||
|
||||
random.seed(123456)
|
||||
test_boxes = [bd.Box(random.randint(1, 20), random.randint(1, 20), random.randint(1, 5))
|
||||
for _ in range(50)]
|
||||
packed = bd.pack(test_boxes, 3)
|
||||
|
||||
# Lifted from https://build123d.readthedocs.io/en/latest/import_export.html#d-to-2d-projection
|
||||
def export_svg(parts, name):
|
||||
part = functools.reduce(operator.add, parts, bd.Part())
|
||||
view_port_origin=(0, 0, 150)
|
||||
visible, hidden = part.project_to_viewport(view_port_origin)
|
||||
max_dimension = max(*bd.Compound(children=visible + hidden).bounding_box().size)
|
||||
exporter = bd.ExportSVG(scale=100 / max_dimension)
|
||||
exporter.add_layer("Visible")
|
||||
exporter.add_layer("Hidden", line_color=(99, 99, 99), line_type=bd.LineType.ISO_DOT)
|
||||
exporter.add_shape(visible, layer="Visible")
|
||||
exporter.add_shape(hidden, layer="Hidden")
|
||||
exporter.write(f"../docs/assets/{name}.svg")
|
||||
|
||||
export_svg(test_boxes, "packed_boxes_input")
|
||||
export_svg(packed, "packed_boxes_output")
|
||||
|
|
@ -15,10 +15,10 @@ from build123d.objects_sketch import *
|
|||
from build123d.operations_generic import *
|
||||
from build123d.operations_part import *
|
||||
from build123d.operations_sketch import *
|
||||
from build123d.pack import *
|
||||
from build123d.topology import *
|
||||
from build123d.drafting import *
|
||||
from build123d.persistence import modify_copyreg
|
||||
from build123d.drafting import *
|
||||
|
||||
from .version import version as __version__
|
||||
|
||||
|
|
@ -159,10 +159,11 @@ __all__ = [
|
|||
"import_svg",
|
||||
"import_svg_as_buildline_code",
|
||||
# Other functions
|
||||
"polar",
|
||||
"delta",
|
||||
"new_edges",
|
||||
"edges_to_wires",
|
||||
"new_edges",
|
||||
"pack",
|
||||
"polar",
|
||||
# Context aware selectors
|
||||
"solids",
|
||||
"faces",
|
||||
|
|
|
|||
|
|
@ -2,7 +2,7 @@
|
|||
import os.path as pth
|
||||
|
||||
try:
|
||||
from setuptools_scm import get_version
|
||||
from setuptools_scm import get_version # pylint: disable=import-error
|
||||
|
||||
version = get_version(root=pth.join("..", "..", ".."), relative_to=__file__)
|
||||
except Exception as exc:
|
||||
|
|
|
|||
|
|
@ -594,8 +594,7 @@ class Builder(ABC):
|
|||
faces = self.faces(select)
|
||||
face_count = len(faces)
|
||||
if face_count != 1:
|
||||
msg = f"Found {face_count} faces, returning first"
|
||||
warnings.warn(msg)
|
||||
warnings.warn(f"Found {face_count} faces, returning first")
|
||||
return faces[0]
|
||||
|
||||
def solids(self, select: Select = Select.ALL) -> ShapeList[Solid]:
|
||||
|
|
@ -801,7 +800,7 @@ class HexLocations(LocationList):
|
|||
align (Union[Align, tuple[Align, Align]], optional): align min, center, or max of object.
|
||||
Defaults to (Align.CENTER, Align.CENTER).
|
||||
|
||||
Atributes:
|
||||
Attributes:
|
||||
apothem (float): radius of the inscribed circle
|
||||
xCount (int): number of points ( > 0 )
|
||||
yCount (int): number of points ( > 0 )
|
||||
|
|
@ -890,7 +889,7 @@ class PolarLocations(LocationList):
|
|||
endpoint (bool, optional): If True, `start_angle` + `angular_range` is the last sample.
|
||||
Otherwise, it is not included. Defaults to False.
|
||||
|
||||
Atributes:
|
||||
Attributes:
|
||||
local_locations (list{Location}): locations relative to workplane
|
||||
|
||||
Raises:
|
||||
|
|
@ -908,7 +907,7 @@ class PolarLocations(LocationList):
|
|||
):
|
||||
if count < 1:
|
||||
raise ValueError(f"At least 1 elements required, requested {count}")
|
||||
elif count == 1:
|
||||
if count == 1:
|
||||
angle_step = 0
|
||||
else:
|
||||
angle_step = angular_range / (count - int(endpoint))
|
||||
|
|
@ -937,7 +936,7 @@ class Locations(LocationList):
|
|||
Args:
|
||||
pts (Union[VectorLike, Vertex, Location]): sequence of points to push
|
||||
|
||||
Atributes:
|
||||
Attributes:
|
||||
local_locations (list{Location}): locations relative to workplane
|
||||
|
||||
"""
|
||||
|
|
|
|||
|
|
@ -108,6 +108,8 @@ class BuildSketch(Builder):
|
|||
wires = Wire.combine(self.pending_edges)
|
||||
return wires if len(wires) > 1 else wires[0]
|
||||
|
||||
def _add_to_pending(self, *objects: Edge):
|
||||
def _add_to_pending(self, *objects: Edge, face_plane: Plane = None):
|
||||
"""Integrate a sequence of objects into existing builder object"""
|
||||
if face_plane:
|
||||
raise NotImplementedError("face_plane arg not supported for this method")
|
||||
self.pending_edges.extend(objects)
|
||||
|
|
|
|||
|
|
@ -44,12 +44,12 @@ from build123d.build_enums import (
|
|||
)
|
||||
from build123d.build_line import BuildLine
|
||||
from build123d.build_sketch import BuildSketch
|
||||
from build123d.geometry import Axis, Color, Location, Plane, Pos, Vector, VectorLike
|
||||
from build123d.geometry import Axis, Location, Plane, Pos, Vector, VectorLike
|
||||
from build123d.objects_curve import Line, TangentArc
|
||||
from build123d.objects_sketch import BaseSketchObject, Polygon, Text
|
||||
from build123d.operations_generic import fillet, mirror, sweep
|
||||
from build123d.operations_sketch import make_face, trace
|
||||
from build123d.topology import Compound, Curve, Edge, Sketch, Vertex, Wire
|
||||
from build123d.topology import Compound, Edge, Sketch, Vertex, Wire
|
||||
|
||||
|
||||
class ArrowHead(BaseSketchObject):
|
||||
|
|
@ -166,7 +166,7 @@ class Draft:
|
|||
arrow_length (float): arrow head length. Defaults to 3.0.
|
||||
line_width (float): thickness of all lines. Defaults to 0.5.
|
||||
pad_around_text (float): amount of padding around text. Defaults to 2.0.
|
||||
unit (Unit): measurement unit. Defautls to Unit.MM.
|
||||
unit (Unit): measurement unit. Defaults to Unit.MM.
|
||||
number_display (NumberDisplay): numbers as decimal or fractions.
|
||||
Default to NumberDisplay.DECIMAL.
|
||||
display_units (bool): control the display of units with numbers. Defaults to True.
|
||||
|
|
@ -177,6 +177,7 @@ class Draft:
|
|||
Defaults to 2.0.
|
||||
|
||||
"""
|
||||
# pylint: disable=too-many-instance-attributes
|
||||
|
||||
# Class Attributes
|
||||
unit_LUT: ClassVar[dict] = {True: "mm", False: '"'}
|
||||
|
|
@ -371,6 +372,8 @@ class DimensionLine(BaseSketchObject):
|
|||
label_angle: bool = False,
|
||||
mode: Mode = Mode.ADD,
|
||||
) -> Sketch:
|
||||
# pylint: disable=too-many-locals
|
||||
|
||||
context = BuildSketch._get_context(self)
|
||||
if sketch is None and not (context is None or context.sketch is None):
|
||||
sketch = context.sketch
|
||||
|
|
@ -398,7 +401,7 @@ class DimensionLine(BaseSketchObject):
|
|||
# Calculate the arrow shaft length for up to three types
|
||||
if arrows.count(True) == 0:
|
||||
raise ValueError("No output - no arrows selected")
|
||||
elif label_length + arrows.count(True) * draft.arrow_length < path_length:
|
||||
if label_length + arrows.count(True) * draft.arrow_length < path_length:
|
||||
shaft_length = (path_length - label_length) / 2 - draft.pad_around_text
|
||||
shaft_pair = [
|
||||
path_obj.trim(0.0, shaft_length / path_length),
|
||||
|
|
@ -506,6 +509,8 @@ class ExtensionLine(BaseSketchObject):
|
|||
project_line: VectorLike = None,
|
||||
mode: Mode = Mode.ADD,
|
||||
):
|
||||
# pylint: disable=too-many-locals
|
||||
|
||||
context = BuildSketch._get_context(self)
|
||||
if sketch is None and not (context is None or context.sketch is None):
|
||||
sketch = context.sketch
|
||||
|
|
@ -582,7 +587,7 @@ class TechnicalDrawing(BaseSketchObject):
|
|||
sub_title (str, optional): drawing sub title. Defaults to "Sub Title".
|
||||
drawing_number (str, optional): Defaults to "B3D-1".
|
||||
sheet_number (int, optional): Defaults to None.
|
||||
drawing_scale (float, optional): displayes as 1:value. Defaults to 1.0.
|
||||
drawing_scale (float, optional): displays as 1:value. Defaults to 1.0.
|
||||
nominal_text_size (float, optional): size of title text. Defaults to 10.0.
|
||||
line_width (float, optional): Defaults to 0.5.
|
||||
mode (Mode, optional): combination mode. Defaults to Mode.ADD.
|
||||
|
|
@ -620,6 +625,8 @@ class TechnicalDrawing(BaseSketchObject):
|
|||
line_width: float = 0.5,
|
||||
mode: Mode = Mode.ADD,
|
||||
):
|
||||
# pylint: disable=too-many-locals
|
||||
|
||||
page_dim = TechnicalDrawing.page_sizes[page_size]
|
||||
# Frame
|
||||
frame_width = page_dim[0] - 2 * TechnicalDrawing.margin - 2 * nominal_text_size
|
||||
|
|
|
|||
|
|
@ -29,6 +29,7 @@ license:
|
|||
|
||||
# pylint has trouble with the OCP imports
|
||||
# pylint: disable=no-name-in-module, import-error
|
||||
# pylint: disable=too-many-lines
|
||||
|
||||
import math
|
||||
import xml.etree.ElementTree as ET
|
||||
|
|
@ -38,21 +39,9 @@ from copy import copy
|
|||
|
||||
import ezdxf
|
||||
import svgpathtools as PT
|
||||
from build123d.topology import (
|
||||
BoundBox,
|
||||
Compound,
|
||||
Edge,
|
||||
Wire,
|
||||
GeomType,
|
||||
Shape,
|
||||
Vector,
|
||||
VectorLike,
|
||||
)
|
||||
from build123d.build_enums import Unit
|
||||
from ezdxf import zoom
|
||||
from ezdxf.colors import RGB, aci2rgb
|
||||
from ezdxf.math import Vec2
|
||||
from ezdxf.tools.standards import linetypes as ezdxf_linetypes
|
||||
from OCP.BRepLib import BRepLib # type: ignore
|
||||
from OCP.BRepTools import BRepTools_WireExplorer # type: ignore
|
||||
from OCP.Geom import Geom_BezierCurve # type: ignore
|
||||
|
|
@ -65,6 +54,19 @@ from OCP.TopAbs import TopAbs_Orientation, TopAbs_ShapeEnum # type: ignore
|
|||
from OCP.TopExp import TopExp_Explorer # type: ignore
|
||||
from typing_extensions import Self
|
||||
|
||||
from build123d.build_enums import Unit
|
||||
from build123d.geometry import TOLERANCE
|
||||
from build123d.topology import (
|
||||
BoundBox,
|
||||
Compound,
|
||||
Edge,
|
||||
Wire,
|
||||
GeomType,
|
||||
Shape,
|
||||
Vector,
|
||||
VectorLike,
|
||||
)
|
||||
|
||||
PathSegment = Union[PT.Line, PT.Arc, PT.QuadraticBezier, PT.CubicBezier]
|
||||
|
||||
# ---------------------------------------------------------------------------
|
||||
|
|
@ -84,6 +86,7 @@ class Drawing:
|
|||
with_hidden: bool = True,
|
||||
focus: Union[float, None] = None,
|
||||
):
|
||||
# pylint: disable=too-many-locals
|
||||
hlr = HLRBRep_Algo()
|
||||
hlr.Add(shape.wrapped)
|
||||
|
||||
|
|
@ -129,15 +132,11 @@ class Drawing:
|
|||
if not hidden_contour_edges.IsNull():
|
||||
hidden.append(hidden_contour_edges)
|
||||
|
||||
# magic number from CQ
|
||||
# TODO: figure out the proper source of this value.
|
||||
tolerance = 1e-6
|
||||
|
||||
# Fix the underlying geometry - otherwise we will get segfaults
|
||||
for el in visible:
|
||||
BRepLib.BuildCurves3d_s(el, tolerance)
|
||||
BRepLib.BuildCurves3d_s(el, TOLERANCE)
|
||||
for el in hidden:
|
||||
BRepLib.BuildCurves3d_s(el, tolerance)
|
||||
BRepLib.BuildCurves3d_s(el, TOLERANCE)
|
||||
|
||||
# Convert and store the results.
|
||||
self.visible_lines = Compound.make_compound(map(Shape, visible))
|
||||
|
|
@ -149,6 +148,7 @@ class Drawing:
|
|||
|
||||
|
||||
class AutoNameEnum(Enum):
|
||||
"""An enum class that automatically sets members' value to their name."""
|
||||
@staticmethod
|
||||
def _generate_next_value_(name, start, count, last_values):
|
||||
return name
|
||||
|
|
@ -261,6 +261,7 @@ UNITS_PER_METER = {
|
|||
|
||||
|
||||
def unit_conversion_scale(from_unit: Unit, to_unit: Unit) -> float:
|
||||
"""Return the multiplicative conversion factor to go from from_unit to to_unit."""
|
||||
result = UNITS_PER_METER[to_unit] / UNITS_PER_METER[from_unit]
|
||||
return result
|
||||
|
||||
|
|
@ -270,7 +271,7 @@ def unit_conversion_scale(from_unit: Unit, to_unit: Unit) -> float:
|
|||
# ---------------------------------------------------------------------------
|
||||
|
||||
|
||||
class Export2D(object):
|
||||
class Export2D:
|
||||
"""Base class for 2D exporters (DXF, SVG)."""
|
||||
|
||||
# When specifying a parametric interval [u1, u2] on a spline,
|
||||
|
|
@ -519,6 +520,7 @@ class ExportDXF(Export2D):
|
|||
line_weight: Optional[float] = None,
|
||||
line_type: Optional[LineType] = None,
|
||||
):
|
||||
self._non_planar_point_count = 0
|
||||
if unit not in self._UNITS_LOOKUP:
|
||||
raise ValueError(f"unit `{unit.name}` not supported.")
|
||||
if unit in ExportDXF.METRIC_UNITS:
|
||||
|
|
@ -619,14 +621,13 @@ class ExportDXF(Export2D):
|
|||
Returns:
|
||||
Self: Document with additional shape
|
||||
"""
|
||||
self._non_planar_point_count = 0
|
||||
if isinstance(shape, Shape):
|
||||
self._add_single_shape(shape, layer)
|
||||
else:
|
||||
for s in shape:
|
||||
self._add_single_shape(s, layer)
|
||||
if self._non_planar_point_count > 0:
|
||||
print(f"WARNING, exporting non-planar shape to 2D format.")
|
||||
print("WARNING, exporting non-planar shape to 2D format.")
|
||||
print(" This is probably not what you want.")
|
||||
print(
|
||||
f" {self._non_planar_point_count} points found outside the XY plane."
|
||||
|
|
@ -653,8 +654,8 @@ class ExportDXF(Export2D):
|
|||
"""
|
||||
# Reset the main CAD viewport of the model space to the
|
||||
# extents of its entities.
|
||||
# TODO: Expose viewport control to the user.
|
||||
# Do the same for ExportSVG.
|
||||
# https://github.com/gumyr/build123d/issues/382 tracks
|
||||
# exposing viewport control to the user.
|
||||
zoom.extents(self._modelspace)
|
||||
|
||||
self._document.saveas(file_name)
|
||||
|
|
@ -806,9 +807,6 @@ class ExportDXF(Export2D):
|
|||
|
||||
def _convert_edge(self, edge: Edge, attribs: dict):
|
||||
geom_type = edge.geom_type()
|
||||
if False and geom_type not in self._CONVERTER_LOOKUP:
|
||||
article = "an" if geom_type[0] in "AEIOU" else "a"
|
||||
print(f"Hey neat, {article} {geom_type}!")
|
||||
convert = self._CONVERTER_LOOKUP.get(geom_type, ExportDXF._convert_other)
|
||||
convert(self, edge, attribs)
|
||||
|
||||
|
|
@ -867,7 +865,7 @@ class ExportSVG(Export2D):
|
|||
ValueError: Invalid unit.
|
||||
|
||||
"""
|
||||
|
||||
# pylint: disable=too-many-instance-attributes
|
||||
_Converter = Callable[[Edge], ET.Element]
|
||||
|
||||
# These are the units which are available in the Unit enum *and*
|
||||
|
|
@ -878,7 +876,7 @@ class ExportSVG(Export2D):
|
|||
Unit.IN: "in",
|
||||
}
|
||||
|
||||
class _Layer(object):
|
||||
class _Layer:
|
||||
def __init__(
|
||||
self,
|
||||
name: str,
|
||||
|
|
@ -893,8 +891,7 @@ class ExportSVG(Export2D):
|
|||
aci2rgb() function. We prefer (0,0,0)."""
|
||||
if ci == ColorIndex.BLACK:
|
||||
return (0, 0, 0)
|
||||
else:
|
||||
return aci2rgb(ci.value)
|
||||
return aci2rgb(ci.value)
|
||||
|
||||
if isinstance(fill_color, ColorIndex):
|
||||
fill_color = color_from_index(fill_color)
|
||||
|
|
@ -925,8 +922,8 @@ class ExportSVG(Export2D):
|
|||
):
|
||||
if unit not in ExportSVG._UNIT_STRING:
|
||||
raise ValueError(
|
||||
"Invalid unit. Supported units are %s."
|
||||
% ", ".join(ExportSVG._UNIT_STRING.values())
|
||||
"Invalid unit. Supported units are "
|
||||
f"{', '.join(ExportSVG._UNIT_STRING.values())}."
|
||||
)
|
||||
self.unit = unit
|
||||
self.scale = scale
|
||||
|
|
@ -977,7 +974,7 @@ class ExportSVG(Export2D):
|
|||
|
||||
Raises:
|
||||
ValueError: Duplicate layer name
|
||||
ValueError: Unknow linetype
|
||||
ValueError: Unknown linetype
|
||||
|
||||
Returns:
|
||||
Self: Drawing with an additional layer
|
||||
|
|
@ -985,7 +982,7 @@ class ExportSVG(Export2D):
|
|||
if name in self._layers:
|
||||
raise ValueError(f"Duplicate layer name '{name}'.")
|
||||
if line_type.value not in Export2D.LINETYPE_DEFS:
|
||||
raise ValueError(f"Unknow linetype `{line_type.value}`.")
|
||||
raise ValueError(f"Unknown linetype `{line_type.value}`.")
|
||||
layer = ExportSVG._Layer(
|
||||
name=name,
|
||||
fill_color=fill_color,
|
||||
|
|
@ -1029,6 +1026,7 @@ class ExportSVG(Export2D):
|
|||
self._add_single_shape(s, layer, reverse_wires)
|
||||
|
||||
def _add_single_shape(self, shape: Shape, layer: _Layer, reverse_wires: bool):
|
||||
# pylint: disable=too-many-locals
|
||||
self._non_planar_point_count = 0
|
||||
bb = shape.bounding_box()
|
||||
self._bounds = self._bounds.add(bb) if self._bounds else bb
|
||||
|
|
@ -1096,7 +1094,7 @@ class ExportSVG(Export2D):
|
|||
|
||||
layer.elements.extend(elements)
|
||||
if self._non_planar_point_count > 0:
|
||||
print(f"WARNING, exporting non-planar shape to 2D format.")
|
||||
print("WARNING, exporting non-planar shape to 2D format.")
|
||||
print(" This is probably not what you want.")
|
||||
print(
|
||||
f" {self._non_planar_point_count} points found outside the XY plane."
|
||||
|
|
@ -1192,6 +1190,7 @@ class ExportSVG(Export2D):
|
|||
# - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
|
||||
|
||||
def _circle_segments(self, edge: Edge, reverse: bool) -> list[PathSegment]:
|
||||
# pylint: disable=too-many-locals
|
||||
curve = edge._geom_adaptor()
|
||||
circle = curve.Circle()
|
||||
radius = circle.Radius()
|
||||
|
|
@ -1219,7 +1218,7 @@ class ExportSVG(Export2D):
|
|||
|
||||
def _circle_element(self, edge: Edge) -> ET.Element:
|
||||
"""Converts a Circle object into an SVG circle element."""
|
||||
if edge.is_closed():
|
||||
if edge.is_closed:
|
||||
curve = edge._geom_adaptor()
|
||||
circle = curve.Circle()
|
||||
radius = circle.Radius()
|
||||
|
|
@ -1237,6 +1236,7 @@ class ExportSVG(Export2D):
|
|||
# - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
|
||||
|
||||
def _ellipse_segments(self, edge: Edge, reverse: bool) -> list[PathSegment]:
|
||||
# pylint: disable=too-many-locals
|
||||
curve = edge._geom_adaptor()
|
||||
ellipse = curve.Ellipse()
|
||||
minor_radius = ellipse.MinorRadius()
|
||||
|
|
@ -1352,12 +1352,8 @@ class ExportSVG(Export2D):
|
|||
def _edge_segments(self, edge: Edge, reverse: bool) -> list[PathSegment]:
|
||||
edge_reversed = edge.wrapped.Orientation() == TopAbs_Orientation.TopAbs_REVERSED
|
||||
geom_type = edge.geom_type()
|
||||
if False and geom_type not in self._SEGMENT_LOOKUP:
|
||||
article = "an" if geom_type[0] in "AEIOU" else "a"
|
||||
print(f"Hey neat, {article} {geom_type}!")
|
||||
segments = self._SEGMENT_LOOKUP.get(geom_type, ExportSVG._other_segments)
|
||||
result = segments(self, edge, reverse ^ edge_reversed)
|
||||
# print(f"{geom_type} {edge.wrapped.Orientation().name} reverse={reverse^edge_reversed} {result}")
|
||||
return result
|
||||
|
||||
_ELEMENT_LOOKUP = {
|
||||
|
|
@ -1369,9 +1365,6 @@ class ExportSVG(Export2D):
|
|||
|
||||
def _edge_element(self, edge: Edge) -> ET.Element:
|
||||
geom_type = edge.geom_type()
|
||||
if False and geom_type not in self._ELEMENT_LOOKUP:
|
||||
article = "an" if geom_type[0] in "AEIOU" else "a"
|
||||
print(f"Hey neat, {article} {geom_type}!")
|
||||
element = self._ELEMENT_LOOKUP.get(geom_type, ExportSVG._other_element)
|
||||
result = element(self, edge)
|
||||
return result
|
||||
|
|
@ -1382,10 +1375,7 @@ class ExportSVG(Export2D):
|
|||
ltname = layer.line_type.value
|
||||
_, pattern = Export2D.LINETYPE_DEFS[ltname]
|
||||
|
||||
try:
|
||||
d = self.dot_length.value
|
||||
except:
|
||||
d = self.dot_length
|
||||
d = self.dot_length.value if isinstance(self.dot_length, DotLength) else self.dot_length
|
||||
pattern = copy(pattern)
|
||||
plen = len(pattern)
|
||||
for i in range(0, plen):
|
||||
|
|
@ -1400,7 +1390,9 @@ class ExportSVG(Export2D):
|
|||
|
||||
# - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
|
||||
|
||||
def _group_for_layer(self, layer: _Layer, attribs: dict = {}) -> ET.Element:
|
||||
def _group_for_layer(self, layer: _Layer, attribs: dict = None) -> ET.Element:
|
||||
if attribs is None:
|
||||
attribs = {}
|
||||
if layer.fill_color:
|
||||
(r, g, b) = layer.fill_color
|
||||
fill = f"rgb({r},{g},{b})"
|
||||
|
|
@ -1444,6 +1436,7 @@ class ExportSVG(Export2D):
|
|||
Args:
|
||||
path (str): The file path where the SVG data will be written.
|
||||
"""
|
||||
# pylint: disable=too-many-locals
|
||||
bb = self._bounds
|
||||
doc_margin = self.margin
|
||||
if self.fit_to_stroke:
|
||||
|
|
@ -1472,7 +1465,7 @@ class ExportSVG(Export2D):
|
|||
container_group = ET.Element(
|
||||
"g",
|
||||
{
|
||||
"transform": f"scale(1,-1)",
|
||||
"transform": "scale(1,-1)",
|
||||
"stroke-linecap": "round",
|
||||
},
|
||||
)
|
||||
|
|
|
|||
|
|
@ -69,6 +69,10 @@ from OCP.Quantity import Quantity_ColorRGBA
|
|||
from OCP.TopLoc import TopLoc_Location
|
||||
from OCP.TopoDS import TopoDS_Face, TopoDS_Shape
|
||||
|
||||
from build123d.build_enums import (
|
||||
Align,
|
||||
)
|
||||
|
||||
# Create a build123d logger to distinguish these logs from application logs.
|
||||
# If the user doesn't configure logging, all build123d logs will be discarded.
|
||||
logging.getLogger("build123d").addHandler(logging.NullHandler())
|
||||
|
|
@ -101,58 +105,66 @@ class Vector:
|
|||
_dim = 0
|
||||
|
||||
@overload
|
||||
def __init__(self, x: float, y: float, z: float): # pragma: no cover
|
||||
def __init__(self, X: float, Y: float, Z: float): # pragma: no cover
|
||||
...
|
||||
|
||||
@overload
|
||||
def __init__(self, x: float, y: float): # pragma: no cover
|
||||
def __init__(self, X: float, Y: float): # pragma: no cover
|
||||
...
|
||||
|
||||
@overload
|
||||
def __init__(self, vec: Vector): # pragma: no cover
|
||||
def __init__(self, v: Vector): # pragma: no cover
|
||||
...
|
||||
|
||||
@overload
|
||||
def __init__(self, vec: Sequence[float]): # pragma: no cover
|
||||
def __init__(self, v: Sequence[float]): # pragma: no cover
|
||||
...
|
||||
|
||||
@overload
|
||||
def __init__(self, vec: Union[gp_Vec, gp_Pnt, gp_Dir, gp_XYZ]): # pragma: no cover
|
||||
def __init__(self, v: Union[gp_Vec, gp_Pnt, gp_Dir, gp_XYZ]): # pragma: no cover
|
||||
...
|
||||
|
||||
@overload
|
||||
def __init__(self): # pragma: no cover
|
||||
...
|
||||
|
||||
def __init__(self, *args):
|
||||
def __init__(self, *args, **kwargs):
|
||||
self.vector_index = 0
|
||||
if len(args) == 3:
|
||||
f_v = gp_Vec(*args)
|
||||
elif len(args) == 2:
|
||||
f_v = gp_Vec(*args, 0)
|
||||
elif len(args) == 1:
|
||||
if isinstance(args[0], Vector):
|
||||
f_v = gp_Vec(args[0].wrapped.XYZ())
|
||||
elif isinstance(args[0], (tuple, Iterable)):
|
||||
x, y, z, ocp_vec = 0, 0, 0, None
|
||||
|
||||
unknown_args = ", ".join(set(kwargs.keys()).difference(["v", "X", "Y", "Z"]))
|
||||
if unknown_args:
|
||||
raise ValueError(f"Unexpected argument(s) {unknown_args}")
|
||||
|
||||
if args and all(isinstance(args[i], (int, float)) for i in range(len(args))):
|
||||
values = list(args)
|
||||
values += [0.0] * max(0, (3 - len(args)))
|
||||
x, y, z = values[0:3]
|
||||
elif len(args) == 1 or "v" in kwargs:
|
||||
first_arg = args[0] if args else None
|
||||
first_arg = kwargs.get("v", first_arg) # override with kwarg
|
||||
if isinstance(first_arg, Vector):
|
||||
ocp_vec = gp_Vec(first_arg.wrapped.XYZ())
|
||||
elif isinstance(first_arg, (tuple, Iterable)):
|
||||
try:
|
||||
values = [float(value) for value in args[0]]
|
||||
except ValueError:
|
||||
raise TypeError("Expected floats")
|
||||
values = [float(value) for value in first_arg]
|
||||
except (TypeError, ValueError) as exc:
|
||||
raise TypeError("Expected floats") from exc
|
||||
if len(values) < 3:
|
||||
values += [0.0] * (3 - len(values))
|
||||
f_v = gp_Vec(*values)
|
||||
elif isinstance(args[0], (gp_Vec, gp_Pnt, gp_Dir)):
|
||||
f_v = gp_Vec(args[0].XYZ())
|
||||
elif isinstance(args[0], gp_XYZ):
|
||||
f_v = gp_Vec(args[0])
|
||||
ocp_vec = gp_Vec(*values[0:3])
|
||||
elif isinstance(first_arg, (gp_Vec, gp_Pnt, gp_Dir)):
|
||||
ocp_vec = gp_Vec(first_arg.XYZ())
|
||||
elif isinstance(first_arg, gp_XYZ):
|
||||
ocp_vec = gp_Vec(first_arg)
|
||||
else:
|
||||
raise TypeError("Expected floats, OCC gp_, or 3-tuple")
|
||||
elif len(args) == 0:
|
||||
f_v = gp_Vec(0, 0, 0)
|
||||
else:
|
||||
raise TypeError("Expected floats, OCC gp_, or 3-tuple")
|
||||
raise TypeError("Expected floats, OCC gp_, or iterable")
|
||||
x = kwargs.get("X", x)
|
||||
y = kwargs.get("Y", y)
|
||||
z = kwargs.get("Z", z)
|
||||
ocp_vec = gp_Vec(x, y, z) if ocp_vec is None else ocp_vec
|
||||
|
||||
self._wrapped = f_v
|
||||
self._wrapped = ocp_vec
|
||||
|
||||
def __iter__(self):
|
||||
"""Initialize to beginning"""
|
||||
|
|
@ -361,7 +373,7 @@ class Vector:
|
|||
return self - normal * (((self - base).dot(normal)) / normal.length**2)
|
||||
|
||||
def __neg__(self) -> Vector:
|
||||
"""Flip direction of vector opertor -"""
|
||||
"""Flip direction of vector operator -"""
|
||||
return self * -1
|
||||
|
||||
def __abs__(self) -> float:
|
||||
|
|
@ -429,7 +441,9 @@ VectorLike = Union[
|
|||
]
|
||||
|
||||
|
||||
class Axis_meta(type):
|
||||
class AxisMeta(type):
|
||||
"""Axis meta class to enable class properties"""
|
||||
|
||||
@property
|
||||
def X(cls) -> Axis:
|
||||
"""X Axis"""
|
||||
|
|
@ -446,7 +460,7 @@ class Axis_meta(type):
|
|||
return Axis((0, 0, 0), (0, 0, 1))
|
||||
|
||||
|
||||
class Axis(metaclass=Axis_meta):
|
||||
class Axis(metaclass=AxisMeta):
|
||||
"""Axis
|
||||
|
||||
Axis defined by point and direction
|
||||
|
|
@ -490,10 +504,8 @@ class Axis(metaclass=Axis_meta):
|
|||
origin = args[0]
|
||||
direction = args[1]
|
||||
|
||||
if "origin" in kwargs:
|
||||
origin = kwargs["origin"]
|
||||
if "direction" in kwargs:
|
||||
direction = kwargs["direction"]
|
||||
origin = kwargs.get("origin", origin)
|
||||
direction = kwargs.get("direction", direction)
|
||||
if "edge" in kwargs and type(kwargs["edge"]).__name__ == "Edge":
|
||||
origin = kwargs["edge"].position_at(0)
|
||||
direction = kwargs["edge"].tangent_at(0)
|
||||
|
|
@ -820,6 +832,19 @@ class BoundBox:
|
|||
and second_box.max.Z < self.max.Z
|
||||
)
|
||||
|
||||
def to_align_offset(self, align: Tuple[float, float]) -> Tuple[float, float]:
|
||||
align_offset = []
|
||||
for i in range(2):
|
||||
if align[i] == Align.MIN:
|
||||
align_offset.append(-self.min.to_tuple()[i])
|
||||
elif align[i] == Align.CENTER:
|
||||
align_offset.append(
|
||||
-(self.min.to_tuple()[i] + self.max.to_tuple()[i]) / 2
|
||||
)
|
||||
elif align[i] == Align.MAX:
|
||||
align_offset.append(-self.max.to_tuple()[i])
|
||||
return align_offset
|
||||
|
||||
|
||||
class Color:
|
||||
"""
|
||||
|
|
@ -867,14 +892,10 @@ class Color:
|
|||
blue = args[2]
|
||||
if len(args) == 4:
|
||||
alpha = args[3]
|
||||
if "red" in kwargs:
|
||||
red = kwargs["red"]
|
||||
if "green" in kwargs:
|
||||
green = kwargs["green"]
|
||||
if "blue" in kwargs:
|
||||
blue = kwargs["blue"]
|
||||
if "alpha" in kwargs:
|
||||
alpha = kwargs["alpha"]
|
||||
red = kwargs.get("red", red)
|
||||
green = kwargs.get("green", green)
|
||||
blue = kwargs.get("blue", blue)
|
||||
alpha = kwargs.get("alpha", alpha)
|
||||
|
||||
if name:
|
||||
self.wrapped = Quantity_ColorRGBA()
|
||||
|
|
@ -1203,27 +1224,23 @@ class Rotation(Location):
|
|||
"""Subclass of Location used only for object rotation
|
||||
|
||||
Attributes:
|
||||
about_x (float): rotation in degrees about X axis
|
||||
about_y (float): rotation in degrees about Y axis
|
||||
about_z (float): rotation in degrees about Z axis
|
||||
X (float): rotation in degrees about X axis
|
||||
Y (float): rotation in degrees about Y axis
|
||||
Z (float): rotation in degrees about Z axis
|
||||
|
||||
"""
|
||||
|
||||
def __init__(self, about_x: float = 0, about_y: float = 0, about_z: float = 0):
|
||||
self.about_x = about_x
|
||||
self.about_y = about_y
|
||||
self.about_z = about_z
|
||||
def __init__(self, X: float = 0, Y: float = 0, Z: float = 0):
|
||||
self.X = X
|
||||
self.Y = Y
|
||||
self.Z = Z
|
||||
super().__init__((0, 0, 0), (X, Y, Z))
|
||||
|
||||
quaternion = gp_Quaternion()
|
||||
quaternion.SetEulerAngles(
|
||||
gp_EulerSequence.gp_Intrinsic_XYZ,
|
||||
radians(about_x),
|
||||
radians(about_y),
|
||||
radians(about_z),
|
||||
)
|
||||
transformation = gp_Trsf()
|
||||
transformation.SetRotationPart(quaternion)
|
||||
super().__init__(transformation)
|
||||
|
||||
Rot = Rotation # Short form for Algebra users who like compact notation
|
||||
|
||||
#:TypeVar("RotationLike"): Three tuple of angles about x, y, z or Rotation
|
||||
RotationLike = Union[tuple[float, float, float], Rotation]
|
||||
|
||||
|
||||
class Pos(Location):
|
||||
|
|
@ -1253,6 +1270,10 @@ class Pos(Location):
|
|||
elif 1 <= len(args) <= 3 and all([isinstance(v, (float, int)) for v in args]):
|
||||
position = list(args) + [0] * (3 - len(args))
|
||||
|
||||
unknown_args = ", ".join(set(kwargs.keys()).difference(["v", "X", "Y", "Z"]))
|
||||
if unknown_args:
|
||||
raise ValueError(f"Unexpected argument(s) {unknown_args}")
|
||||
|
||||
if "X" in kwargs:
|
||||
position[0] = kwargs["X"]
|
||||
if "Y" in kwargs:
|
||||
|
|
@ -1263,17 +1284,6 @@ class Pos(Location):
|
|||
super().__init__(tuple(position))
|
||||
|
||||
|
||||
class Rot(Location):
|
||||
"""A rotation only sub-class of Location"""
|
||||
|
||||
def __init__(self, x: float = 0, y: float = 0, z: float = 0):
|
||||
super().__init__((0, 0, 0), (x, y, z))
|
||||
|
||||
|
||||
#:TypeVar("RotationLike"): Three tuple of angles about x, y, z or Rotation
|
||||
RotationLike = Union[tuple[float, float, float], Rotation]
|
||||
|
||||
|
||||
class Matrix:
|
||||
"""A 3d , 4x4 transformation matrix.
|
||||
|
||||
|
|
@ -1407,7 +1417,9 @@ class Matrix:
|
|||
return f"Matrix([{matrix_str}])"
|
||||
|
||||
|
||||
class Plane_meta(type):
|
||||
class PlaneMeta(type):
|
||||
"""Plane meta class to enable class properties"""
|
||||
|
||||
@property
|
||||
def XY(cls) -> Plane:
|
||||
"""XY Plane"""
|
||||
|
|
@ -1469,7 +1481,7 @@ class Plane_meta(type):
|
|||
return Plane((0, 0, 0), (1, 0, 0), (0, 0, -1))
|
||||
|
||||
|
||||
class Plane(metaclass=Plane_meta):
|
||||
class Plane(metaclass=PlaneMeta):
|
||||
"""Plane
|
||||
|
||||
A plane is positioned in space with a coordinate system such that the plane is defined by
|
||||
|
|
@ -1716,21 +1728,22 @@ class Plane(metaclass=Plane_meta):
|
|||
self, other: Union[Location, "Shape"]
|
||||
) -> Union[Plane, List[Plane], "Shape"]:
|
||||
if isinstance(other, Location):
|
||||
return Plane(self.location * other)
|
||||
result = Plane(self.location * other)
|
||||
elif ( # LocationList
|
||||
hasattr(other, "local_locations") and hasattr(other, "location_index")
|
||||
) or ( # tuple of locations
|
||||
isinstance(other, (list, tuple))
|
||||
and all([isinstance(o, Location) for o in other])
|
||||
):
|
||||
return [self * loc for loc in other]
|
||||
result = [self * loc for loc in other]
|
||||
elif hasattr(other, "wrapped") and not isinstance(other, Vector): # Shape
|
||||
return self.location * other
|
||||
result = self.location * other
|
||||
|
||||
else:
|
||||
raise TypeError(
|
||||
"Planes can only be multiplied with Locations or Shapes to relocate them"
|
||||
)
|
||||
return result
|
||||
|
||||
def __repr__(self):
|
||||
"""To String
|
||||
|
|
@ -1775,7 +1788,7 @@ class Plane(metaclass=Plane_meta):
|
|||
ValueError: Axis doesn't intersect plane
|
||||
|
||||
Returns:
|
||||
Plane: plane with new ogin
|
||||
Plane: plane with new origin
|
||||
|
||||
"""
|
||||
if type(locator).__name__ == "Vertex":
|
||||
|
|
|
|||
|
|
@ -34,8 +34,6 @@ from pathlib import Path
|
|||
from typing import TextIO, Union
|
||||
|
||||
import OCP.IFSelect
|
||||
from build123d.geometry import Color
|
||||
from build123d.topology import Compound, Face, Shape, ShapeList, Wire
|
||||
from OCP.BRep import BRep_Builder
|
||||
from OCP.BRepTools import BRepTools
|
||||
from OCP.RWStl import RWStl
|
||||
|
|
@ -44,6 +42,8 @@ from OCP.TopoDS import TopoDS_Face, TopoDS_Shape, TopoDS_Wire
|
|||
from ocpsvg import ColorAndLabel, import_svg_document
|
||||
from svgpathtools import svg2paths
|
||||
|
||||
from build123d.geometry import Color
|
||||
from build123d.topology import Compound, Face, Shape, ShapeList, Wire
|
||||
|
||||
def import_brep(file_name: str) -> Shape:
|
||||
"""Import shape from a BREP file
|
||||
|
|
@ -85,7 +85,8 @@ def import_step(file_name: str) -> Compound:
|
|||
# Now read and return the shape
|
||||
reader = STEPControl_Reader()
|
||||
read_status = reader.ReadFile(file_name)
|
||||
if read_status != OCP.IFSelect.IFSelect_RetDone:
|
||||
# pylint fails to understand OCP's module here, so suppress on the next line.
|
||||
if read_status != OCP.IFSelect.IFSelect_RetDone: # pylint: disable=no-member
|
||||
raise ValueError(f"STEP File {file_name} could not be loaded")
|
||||
for i in range(reader.NbRootsForTransfer()):
|
||||
reader.TransferRoot(i + 1)
|
||||
|
|
@ -155,7 +156,8 @@ def import_svg_as_buildline_code(file_name: str) -> tuple[str, str]:
|
|||
"sweep",
|
||||
],
|
||||
}
|
||||
paths, _path_attributes = svg2paths(file_name)
|
||||
paths_info = svg2paths(file_name)
|
||||
paths, _path_attributes = paths_info[0], paths_info[1]
|
||||
builder_name = os.path.basename(file_name).split(".")[0]
|
||||
builder_name = builder_name if builder_name.isidentifier() else "builder"
|
||||
buildline_code = [
|
||||
|
|
|
|||
|
|
@ -87,7 +87,7 @@ class RigidJoint(Joint):
|
|||
super().__init__(label, to_part)
|
||||
|
||||
@overload
|
||||
def connect_to(self, other: BallJoint, *, angles: RotationLike = None):
|
||||
def connect_to(self, other: BallJoint, *, angles: RotationLike = None, **kwargs):
|
||||
"""Connect RigidJoint and BallJoint"""
|
||||
|
||||
@overload
|
||||
|
|
@ -113,7 +113,7 @@ class RigidJoint(Joint):
|
|||
|
||||
Args:
|
||||
other (Joint): joint to connect to
|
||||
angle (float, optional): angle in degrees. Deaults to range min.
|
||||
angle (float, optional): angle in degrees. Defaults to range min.
|
||||
angles (RotationLike, optional): angles about axes in degrees. Defaults to
|
||||
range minimums.
|
||||
position (float, optional): linear position. Defaults to linear range min.
|
||||
|
|
@ -148,13 +148,14 @@ class RigidJoint(Joint):
|
|||
|
||||
Args:
|
||||
other (RigidJoint): relative to joint
|
||||
angle (float, optional): angle in degrees. Deaults to range min.
|
||||
angle (float, optional): angle in degrees. Defaults to range min.
|
||||
angles (RotationLike, optional): angles about axes in degrees. Defaults to
|
||||
range minimums.
|
||||
position (float, optional): linear position. Defaults to linear range min.
|
||||
|
||||
Raises:
|
||||
TypeError: other must of type BallJoint, CylindricalJoint, LinearJoint, RevoluteJoint, RigidJoint
|
||||
TypeError: other must be of a type in: BallJoint, CylindricalJoint,
|
||||
LinearJoint, RevoluteJoint, RigidJoint.
|
||||
|
||||
"""
|
||||
if isinstance(other, RigidJoint):
|
||||
|
|
@ -184,8 +185,9 @@ class RigidJoint(Joint):
|
|||
other_location = other.relative_to(self, angles=angles).inverse()
|
||||
else:
|
||||
raise TypeError(
|
||||
f"other must one of type BallJoint, CylindricalJoint, LinearJoint, RevoluteJoint, RigidJoint"
|
||||
f" not {type(other)}"
|
||||
"other must one of type "
|
||||
"BallJoint, CylindricalJoint, LinearJoint, RevoluteJoint, RigidJoint "
|
||||
f"not {type(other)}"
|
||||
)
|
||||
|
||||
return other_location
|
||||
|
|
@ -206,7 +208,7 @@ class RevoluteJoint(Joint):
|
|||
|
||||
Attributes:
|
||||
angle (float): angle of joint
|
||||
angle_reference (Vector): reference for angular poitions
|
||||
angle_reference (Vector): reference for angular positions
|
||||
angular_range (tuple[float,float]): min and max angular position of joint
|
||||
relative_axis (Axis): joint axis relative to bound part
|
||||
|
||||
|
|
@ -259,7 +261,7 @@ class RevoluteJoint(Joint):
|
|||
|
||||
Args:
|
||||
other (RigidJoint): relative to joint
|
||||
angle (float, optional): angle in degrees. Deaults to range min.
|
||||
angle (float, optional): angle in degrees. Defaults to range min.
|
||||
|
||||
Returns:
|
||||
TypeError: other must of type RigidJoint
|
||||
|
|
@ -274,7 +276,7 @@ class RevoluteJoint(Joint):
|
|||
|
||||
Args:
|
||||
other (RigidJoint): relative to joint
|
||||
angle (float, optional): angle in degrees. Deaults to range min.
|
||||
angle (float, optional): angle in degrees. Defaults to range min.
|
||||
|
||||
Raises:
|
||||
TypeError: other must of type RigidJoint
|
||||
|
|
@ -373,7 +375,7 @@ class LinearJoint(Joint):
|
|||
|
||||
Args:
|
||||
other (Joint): joint to connect to
|
||||
angle (float, optional): angle in degrees. Deaults to range min.
|
||||
angle (float, optional): angle in degrees. Defaults to range min.
|
||||
position (float, optional): linear position. Defaults to linear range min.
|
||||
|
||||
Raises:
|
||||
|
|
@ -400,7 +402,7 @@ class LinearJoint(Joint):
|
|||
|
||||
Args:
|
||||
other (Joint): joint to connect to
|
||||
angle (float, optional): angle in degrees. Deaults to range min.
|
||||
angle (float, optional): angle in degrees. Defaults to range min.
|
||||
position (float, optional): linear position. Defaults to linear range min.
|
||||
|
||||
Raises:
|
||||
|
|
@ -480,7 +482,7 @@ class CylindricalJoint(Joint):
|
|||
axis (Axis): joint axis
|
||||
linear_position (float): linear joint position
|
||||
rotational_position (float): revolute joint angle in degrees
|
||||
angle_reference (Vector): reference for angular poitions
|
||||
angle_reference (Vector): reference for angular positions
|
||||
angular_range (tuple[float,float]): min and max angular position of joint
|
||||
linear_range (tuple[float,float]): min and max positional values
|
||||
relative_axis (Axis): joint axis relative to bound part
|
||||
|
|
@ -490,6 +492,7 @@ class CylindricalJoint(Joint):
|
|||
Raises:
|
||||
ValueError: angle_reference must be normal to axis
|
||||
"""
|
||||
# pylint: disable=too-many-instance-attributes
|
||||
|
||||
@property
|
||||
def symbol(self) -> Compound:
|
||||
|
|
@ -551,7 +554,7 @@ class CylindricalJoint(Joint):
|
|||
Args:
|
||||
other (Joint): joint to connect to
|
||||
position (float, optional): linear position. Defaults to linear range min.
|
||||
angle (float, optional): angle in degrees. Deaults to range min.
|
||||
angle (float, optional): angle in degrees. Defaults to range min.
|
||||
|
||||
Raises:
|
||||
TypeError: other must be of type RigidJoint
|
||||
|
|
@ -568,7 +571,7 @@ class CylindricalJoint(Joint):
|
|||
Args:
|
||||
other (Joint): joint to connect to
|
||||
position (float, optional): linear position. Defaults to linear range min.
|
||||
angle (float, optional): angle in degrees. Deaults to range min.
|
||||
angle (float, optional): angle in degrees. Defaults to range min.
|
||||
|
||||
Raises:
|
||||
TypeError: other must be of type RigidJoint
|
||||
|
|
|
|||
|
|
@ -220,14 +220,12 @@ def display(shape: Any) -> Javascript:
|
|||
if not hasattr(shape, "wrapped"): # Is a "Shape"
|
||||
raise ValueError(f"Type {type(shape)} is not supported")
|
||||
|
||||
payload.append(
|
||||
dict(
|
||||
shape=to_vtkpoly_string(shape),
|
||||
color=DEFAULT_COLOR,
|
||||
position=[0, 0, 0],
|
||||
orientation=[0, 0, 0],
|
||||
)
|
||||
)
|
||||
payload.append({
|
||||
"shape": to_vtkpoly_string(shape),
|
||||
"color": DEFAULT_COLOR,
|
||||
"position": [0, 0, 0],
|
||||
"orientation": [0, 0, 0],
|
||||
})
|
||||
code = TEMPLATE.format(data=dumps(payload), element="element", ratio=0.5)
|
||||
|
||||
return Javascript(code)
|
||||
|
|
|
|||
|
|
@ -6,7 +6,7 @@ by: Gumyr
|
|||
date: Aug 9th 2023
|
||||
|
||||
desc:
|
||||
This module provides the Mesher class that implements exporting and importing
|
||||
This module provides the Mesher class that implements exporting and importing
|
||||
both 3MF and STL mesh files. It uses the 3MF Consortium's Lib3MF library
|
||||
(see https://github.com/3MFConsortium/lib3mf).
|
||||
|
||||
|
|
@ -188,7 +188,7 @@ class Mesher:
|
|||
name_space (str): categorizer of different metadata entries
|
||||
name (str): metadata label
|
||||
value (str): metadata content
|
||||
metadata_type (str): metadata trype
|
||||
metadata_type (str): metadata type
|
||||
must_preserve (bool): metadata must not be removed if unused
|
||||
"""
|
||||
# Get an existing meta data group if there is one
|
||||
|
|
@ -367,7 +367,7 @@ class Mesher:
|
|||
part_number (str, optional): part #. Defaults to None.
|
||||
uuid_value (uuid, optional): value from uuid package. Defaults to None.
|
||||
|
||||
Rasises:
|
||||
Raises:
|
||||
RuntimeError: 3mf mesh is invalid
|
||||
Warning: Degenerate shape skipped
|
||||
Warning: 3mf mesh is not manifold
|
||||
|
|
|
|||
|
|
@ -61,18 +61,7 @@ class BaseSketchObject(Sketch):
|
|||
):
|
||||
if align is not None:
|
||||
align = tuplify(align, 2)
|
||||
bbox = obj.bounding_box()
|
||||
align_offset = []
|
||||
for i in range(2):
|
||||
if align[i] == Align.MIN:
|
||||
align_offset.append(-bbox.min.to_tuple()[i])
|
||||
elif align[i] == Align.CENTER:
|
||||
align_offset.append(
|
||||
-(bbox.min.to_tuple()[i] + bbox.max.to_tuple()[i]) / 2
|
||||
)
|
||||
elif align[i] == Align.MAX:
|
||||
align_offset.append(-bbox.max.to_tuple()[i])
|
||||
obj.move(Location(Vector(*align_offset)))
|
||||
obj.move(Location(Vector(*obj.bounding_box().to_align_offset(align))))
|
||||
|
||||
context: BuildSketch = BuildSketch._get_context(self, log=False)
|
||||
if context is None:
|
||||
|
|
@ -298,6 +287,7 @@ class RegularPolygon(BaseSketchObject):
|
|||
align: tuple[Align, Align] = (Align.CENTER, Align.CENTER),
|
||||
mode: Mode = Mode.ADD,
|
||||
):
|
||||
# pylint: disable=too-many-locals
|
||||
context = BuildSketch._get_context(self)
|
||||
validate_inputs(context, self)
|
||||
|
||||
|
|
@ -495,14 +485,17 @@ class SlotOverall(BaseSketchObject):
|
|||
self.width = width
|
||||
self.slot_height = height
|
||||
|
||||
face = Face.make_from_wires(
|
||||
Wire.make_wire(
|
||||
[
|
||||
Edge.make_line(Vector(-width / 2 + height / 2, 0, 0), Vector()),
|
||||
Edge.make_line(Vector(), Vector(+width / 2 - height / 2, 0, 0)),
|
||||
]
|
||||
).offset_2d(height / 2)
|
||||
)
|
||||
if width != height:
|
||||
face = Face.make_from_wires(
|
||||
Wire.make_wire(
|
||||
[
|
||||
Edge.make_line(Vector(-width / 2 + height / 2, 0, 0), Vector()),
|
||||
Edge.make_line(Vector(), Vector(+width / 2 - height / 2, 0, 0)),
|
||||
]
|
||||
).offset_2d(height / 2)
|
||||
)
|
||||
else:
|
||||
face = Circle(width/2, mode=mode).face()
|
||||
super().__init__(face, rotation, align, mode)
|
||||
|
||||
|
||||
|
|
@ -525,7 +518,7 @@ class Text(BaseSketchObject):
|
|||
rotation (float, optional): angles to rotate objects. Defaults to 0.
|
||||
mode (Mode, optional): combination mode. Defaults to Mode.ADD.
|
||||
"""
|
||||
|
||||
# pylint: disable=too-many-instance-attributes
|
||||
_applies_to = [BuildSketch._tag]
|
||||
|
||||
def __init__(
|
||||
|
|
|
|||
|
|
@ -230,23 +230,23 @@ def bounding_box(
|
|||
if context is not None:
|
||||
context._add_to_context(*new_faces, mode=mode)
|
||||
return Sketch(Compound.make_compound(new_faces).wrapped)
|
||||
else:
|
||||
new_objects = []
|
||||
for obj in object_list:
|
||||
if isinstance(obj, Vertex):
|
||||
continue
|
||||
bbox = obj.bounding_box()
|
||||
new_objects.append(
|
||||
Solid.make_box(
|
||||
bbox.size.X,
|
||||
bbox.size.Y,
|
||||
bbox.size.Z,
|
||||
Plane((bbox.min.X, bbox.min.Y, bbox.min.Z)),
|
||||
)
|
||||
|
||||
new_objects = []
|
||||
for obj in object_list:
|
||||
if isinstance(obj, Vertex):
|
||||
continue
|
||||
bbox = obj.bounding_box()
|
||||
new_objects.append(
|
||||
Solid.make_box(
|
||||
bbox.size.X,
|
||||
bbox.size.Y,
|
||||
bbox.size.Z,
|
||||
Plane((bbox.min.X, bbox.min.Y, bbox.min.Z)),
|
||||
)
|
||||
if context is not None:
|
||||
context._add_to_context(*new_objects, mode=mode)
|
||||
return Part(Compound.make_compound(new_objects).wrapped)
|
||||
)
|
||||
if context is not None:
|
||||
context._add_to_context(*new_objects, mode=mode)
|
||||
return Part(Compound.make_compound(new_objects).wrapped)
|
||||
|
||||
|
||||
#:TypeVar("ChamferFilletType"): Type of objects which can be chamfered or filleted
|
||||
|
|
@ -325,7 +325,7 @@ def chamfer(
|
|||
context._add_to_context(new_part, mode=Mode.REPLACE)
|
||||
return Part(Compound.make_compound([new_part]).wrapped)
|
||||
|
||||
elif target._dim == 2:
|
||||
if target._dim == 2:
|
||||
# Convert BaseSketchObject into Sketch so casting into Sketch during construction works
|
||||
target = (
|
||||
Sketch(target.wrapped) if isinstance(target, BaseSketchObject) else target
|
||||
|
|
@ -347,7 +347,7 @@ def chamfer(
|
|||
context._add_to_context(new_sketch, mode=Mode.REPLACE)
|
||||
return new_sketch
|
||||
|
||||
elif target._dim == 1:
|
||||
if target._dim == 1:
|
||||
target = (
|
||||
Wire(target.wrapped)
|
||||
if isinstance(target, BaseLineObject)
|
||||
|
|
@ -356,7 +356,7 @@ def chamfer(
|
|||
if not all([isinstance(obj, Vertex) for obj in object_list]):
|
||||
raise ValueError("1D fillet operation takes only Vertices")
|
||||
# Remove any end vertices as these can't be filleted
|
||||
if not target.is_closed():
|
||||
if not target.is_closed:
|
||||
object_list = filter(
|
||||
lambda v: not (
|
||||
(Vector(*v.to_tuple()) - target.position_at(0)).length == 0
|
||||
|
|
@ -420,7 +420,7 @@ def fillet(
|
|||
context._add_to_context(new_part, mode=Mode.REPLACE)
|
||||
return Part(Compound.make_compound([new_part]).wrapped)
|
||||
|
||||
elif target._dim == 2:
|
||||
if target._dim == 2:
|
||||
# Convert BaseSketchObject into Sketch so casting into Sketch during construction works
|
||||
target = (
|
||||
Sketch(target.wrapped) if isinstance(target, BaseSketchObject) else target
|
||||
|
|
@ -441,7 +441,7 @@ def fillet(
|
|||
context._add_to_context(new_sketch, mode=Mode.REPLACE)
|
||||
return new_sketch
|
||||
|
||||
elif target._dim == 1:
|
||||
if target._dim == 1:
|
||||
target = (
|
||||
Wire(target.wrapped)
|
||||
if isinstance(target, BaseLineObject)
|
||||
|
|
@ -450,7 +450,7 @@ def fillet(
|
|||
if not all([isinstance(obj, Vertex) for obj in object_list]):
|
||||
raise ValueError("1D fillet operation takes only Vertices")
|
||||
# Remove any end vertices as these can't be filleted
|
||||
if not target.is_closed():
|
||||
if not target.is_closed:
|
||||
object_list = filter(
|
||||
lambda v: not (
|
||||
(Vector(*v.to_tuple()) - target.position_at(0)).length == 0
|
||||
|
|
@ -508,12 +508,11 @@ def mirror(
|
|||
mirrored_compound = Compound.make_compound(mirrored)
|
||||
if all([obj._dim == 3 for obj in object_list]):
|
||||
return Part(mirrored_compound.wrapped)
|
||||
elif all([obj._dim == 2 for obj in object_list]):
|
||||
if all([obj._dim == 2 for obj in object_list]):
|
||||
return Sketch(mirrored_compound.wrapped)
|
||||
elif all([obj._dim == 1 for obj in object_list]):
|
||||
if all([obj._dim == 1 for obj in object_list]):
|
||||
return Curve(mirrored_compound.wrapped)
|
||||
else:
|
||||
return mirrored_compound
|
||||
return mirrored_compound
|
||||
|
||||
|
||||
#:TypeVar("OffsetType"): Type of objects which can be offset
|
||||
|
|
@ -641,12 +640,11 @@ def offset(
|
|||
offset_compound = Compound.make_compound(new_objects)
|
||||
if all([obj._dim == 3 for obj in object_list]):
|
||||
return Part(offset_compound.wrapped)
|
||||
elif all([obj._dim == 2 for obj in object_list]):
|
||||
if all([obj._dim == 2 for obj in object_list]):
|
||||
return Sketch(offset_compound.wrapped)
|
||||
elif all([obj._dim == 1 for obj in object_list]):
|
||||
if all([obj._dim == 1 for obj in object_list]):
|
||||
return Curve(offset_compound.wrapped)
|
||||
else:
|
||||
return offset_compound
|
||||
return offset_compound
|
||||
|
||||
|
||||
#:TypeVar("ProjectType"): Type of objects which can be projected
|
||||
|
|
@ -693,7 +691,7 @@ def project(
|
|||
|
||||
if not objects and context is None:
|
||||
raise ValueError("No object to project")
|
||||
elif not objects and context is not None and isinstance(context, BuildPart):
|
||||
if not objects and context is not None and isinstance(context, BuildPart):
|
||||
object_list = context.pending_edges + context.pending_faces
|
||||
context.pending_edges = []
|
||||
context.pending_faces = []
|
||||
|
|
@ -870,12 +868,11 @@ def scale(
|
|||
scale_compound = Compound.make_compound(new_objects)
|
||||
if all([obj._dim == 3 for obj in object_list]):
|
||||
return Part(scale_compound.wrapped)
|
||||
elif all([obj._dim == 2 for obj in object_list]):
|
||||
if all([obj._dim == 2 for obj in object_list]):
|
||||
return Sketch(scale_compound.wrapped)
|
||||
elif all([obj._dim == 1 for obj in object_list]):
|
||||
if all([obj._dim == 1 for obj in object_list]):
|
||||
return Curve(scale_compound.wrapped)
|
||||
else:
|
||||
return scale_compound
|
||||
return scale_compound
|
||||
|
||||
|
||||
#:TypeVar("SplitType"): Type of objects which can be offset
|
||||
|
|
@ -925,12 +922,11 @@ def split(
|
|||
split_compound = Compound.make_compound(new_objects)
|
||||
if all([obj._dim == 3 for obj in object_list]):
|
||||
return Part(split_compound.wrapped)
|
||||
elif all([obj._dim == 2 for obj in object_list]):
|
||||
if all([obj._dim == 2 for obj in object_list]):
|
||||
return Sketch(split_compound.wrapped)
|
||||
elif all([obj._dim == 1 for obj in object_list]):
|
||||
if all([obj._dim == 1 for obj in object_list]):
|
||||
return Curve(split_compound.wrapped)
|
||||
else:
|
||||
return split_compound
|
||||
return split_compound
|
||||
|
||||
|
||||
#:TypeVar("SweepType"): Type of objects which can be swept
|
||||
|
|
@ -1051,5 +1047,4 @@ def sweep(
|
|||
|
||||
if new_solids:
|
||||
return Part(Compound.make_compound(new_solids).wrapped)
|
||||
else:
|
||||
return Sketch(Compound.make_compound(new_faces).wrapped)
|
||||
return Sketch(Compound.make_compound(new_faces).wrapped)
|
||||
|
|
|
|||
|
|
@ -80,6 +80,7 @@ def extrude(
|
|||
Returns:
|
||||
Part: extruded object
|
||||
"""
|
||||
# pylint: disable=too-many-locals
|
||||
context: BuildPart = BuildPart._get_context("extrude")
|
||||
validate_inputs(context, "extrude", to_extrude)
|
||||
|
||||
|
|
|
|||
|
|
@ -117,8 +117,8 @@ def trace(
|
|||
Convert edges, wires or pending edges into faces by sweeping a perpendicular line along them.
|
||||
|
||||
Args:
|
||||
lines (Union[Curve, Edge, Wire, Iterable[Union[Curve, Edge, Wire]]], optional): lines to trace.
|
||||
Defaults to sketch pending edges.
|
||||
lines (Union[Curve, Edge, Wire, Iterable[Union[Curve, Edge, Wire]]], optional): lines to
|
||||
trace. Defaults to sketch pending edges.
|
||||
line_width (float, optional): Defaults to 1.
|
||||
mode (Mode, optional): combination mode. Defaults to Mode.ADD.
|
||||
|
||||
|
|
|
|||
125
src/build123d/pack.py
Normal file
|
|
@ -0,0 +1,125 @@
|
|||
"""
|
||||
build123d Pack
|
||||
|
||||
name: pack.py
|
||||
by: fischman
|
||||
date: November 9th 2023
|
||||
|
||||
desc:
|
||||
Utility code for packing objects in a squarish 2D area.
|
||||
"""
|
||||
|
||||
from __future__ import annotations
|
||||
|
||||
from dataclasses import dataclass
|
||||
from typing import Callable, Collection, Optional, cast
|
||||
|
||||
from build123d import Location, Shape
|
||||
|
||||
def _pack2d(objects: Collection[object],
|
||||
width_fn: Callable[[object], float],
|
||||
length_fn: Callable[[object], float]) -> Collection[tuple[float,float]]:
|
||||
"""Takes an iterable of objects to pack into a square(ish) 2D
|
||||
arrangement, and return a list of (x,y) locations to place each to
|
||||
achieve the packing.
|
||||
Based on https://codeincomplete.com/articles/bin-packing/ and
|
||||
implemented as a straight-forward port of
|
||||
https://github.com/jakesgordon/bin-packing/blob/master/js/packer.growing.js
|
||||
"""
|
||||
|
||||
@dataclass
|
||||
class _Node:
|
||||
used: bool = False
|
||||
x: float = 0
|
||||
y: float = 0
|
||||
w: float = 0
|
||||
h: float = 0
|
||||
down: Optional["_Node"] = None
|
||||
right: Optional["_Node"] = None
|
||||
|
||||
def find_node(start, w, h):
|
||||
if start.used:
|
||||
return find_node(start.right, w, h) or find_node(start.down, w, h)
|
||||
if o[1] <= start.w and o[2] <= start.h:
|
||||
return start
|
||||
return None
|
||||
|
||||
def split_node(node, w, h):
|
||||
assert not node.used
|
||||
node.used = True
|
||||
node.down = _Node(x=node.x, y=node.y+h, w=node.w, h=node.h-h)
|
||||
node.right = _Node(x=node.x+w, y=node.y, w=node.w-w, h=h)
|
||||
return node
|
||||
|
||||
def grow_node(w, h):
|
||||
nonlocal root
|
||||
can_grow_down = w <= root.w
|
||||
can_grow_right = h <= root.h
|
||||
should_grow_right = can_grow_right and (root.h >= (root.w + w))
|
||||
should_grow_down = can_grow_down and (root.w >= (root.h + h))
|
||||
if should_grow_right:
|
||||
return grow_right(w, h)
|
||||
if should_grow_down:
|
||||
return grow_down(w, h)
|
||||
if can_grow_right:
|
||||
return grow_right(w, h)
|
||||
if can_grow_down:
|
||||
return grow_down(w, h)
|
||||
assert False, f"Failed to grow! root: {root}, w: {w}, h: {h}"
|
||||
|
||||
def grow_right(w, h):
|
||||
nonlocal root
|
||||
root = _Node(used=True, x=0, y=0, w=root.w+w, h=root.h,
|
||||
down=root, right=_Node(x=root.w, w=w, h=root.h))
|
||||
node = find_node(root, w, h)
|
||||
assert node, "Failed to grow right! root: {root}, w: {w}, h: {h}"
|
||||
return split_node(node, w, h)
|
||||
|
||||
def grow_down(w, h):
|
||||
nonlocal root
|
||||
root = _Node(used=True, x=0, y=0, w=root.w, h=root.h+h,
|
||||
down=_Node(y=root.h, w=root.w, h=h), right=root)
|
||||
node = find_node(root, w, h)
|
||||
assert node, "Failed to grow down! root: {root}, w: {w}, h: {h}"
|
||||
return split_node(node, w, h)
|
||||
|
||||
assert len(objects)>0
|
||||
sorted_objects = sorted([(i, width_fn(o), length_fn(o)) for (i, o) in enumerate(objects)],
|
||||
key=lambda d: min(d[1], d[2]), reverse=True)
|
||||
sorted_objects = sorted(sorted_objects, key=lambda d: max(d[1], d[2]), reverse=True)
|
||||
root = _Node(False, w=sorted_objects[0][1], h=sorted_objects[0][2])
|
||||
translations = []
|
||||
for o in sorted_objects:
|
||||
node = find_node(root, o[1], o[2])
|
||||
if node:
|
||||
node = split_node(node, o[1], o[2])
|
||||
else:
|
||||
node = grow_node(o[1], o[2])
|
||||
translations.append((o[0], node.x, node.y))
|
||||
return [(t[1], t[2]) for t in sorted(translations, key=lambda t: t[0])]
|
||||
|
||||
def pack(objects: Collection[Shape], padding: float) -> Collection[Shape]:
|
||||
"""Pack objects in a squarish area in Plane.XY."""
|
||||
bounding_boxes = {o: o.bounding_box().size + (padding, padding) for o in objects}
|
||||
translations = _pack2d(
|
||||
objects,
|
||||
width_fn=lambda o: bounding_boxes[cast(Shape, o)].X,
|
||||
length_fn=lambda o: bounding_boxes[cast(Shape, o)].Y)
|
||||
translated = [
|
||||
Location((t[0]-o.bounding_box().min.X, t[1]-o.bounding_box().min.Y, 0)) * o
|
||||
for (o,t) in zip(objects, translations)
|
||||
]
|
||||
|
||||
# Assert the packing didn't cause any overlaps.
|
||||
def _overlapping(bb1, bb2):
|
||||
# Boundaries of the intersection of the two bounding boxes.
|
||||
min_x = max(bb1.min.X, bb2.min.X)
|
||||
min_y = max(bb1.min.Y, bb2.min.Y)
|
||||
max_x = min(bb1.max.X, bb2.max.X)
|
||||
max_y = min(bb1.max.Y, bb2.max.Y)
|
||||
return max_x > min_x and max_y > min_y
|
||||
bb = [t.bounding_box() for t in translated]
|
||||
for (i, bb_i) in enumerate(bb):
|
||||
for (j, bb_j) in enumerate(bb[i+1:]):
|
||||
assert not _overlapping(bb_i, bb_j), f"Objects at indexes {i} and {j} overlap!"
|
||||
return translated
|
||||
|
|
@ -83,9 +83,9 @@ def serialize_location(location: TopLoc_Location) -> bytes:
|
|||
"""
|
||||
if location is None:
|
||||
return None
|
||||
transfo = location.Transformation()
|
||||
translation = transfo.TranslationPart()
|
||||
rotation = transfo.GetRotation()
|
||||
transform = location.Transformation()
|
||||
translation = transform.TranslationPart()
|
||||
rotation = transform.GetRotation()
|
||||
# convert floats in bytes
|
||||
translation_bytes = bytearray()
|
||||
for i in range(1, 4):
|
||||
|
|
@ -126,10 +126,10 @@ def deserialize_location(buffer: bytes) -> TopLoc_Location:
|
|||
)
|
||||
|
||||
# Create the TopLoc_Location object
|
||||
transfo = gp_Trsf()
|
||||
transfo.SetTransformation(rotation, translation)
|
||||
transform = gp_Trsf()
|
||||
transform.SetTransformation(rotation, translation)
|
||||
|
||||
return TopLoc_Location(transfo)
|
||||
return TopLoc_Location(transform)
|
||||
|
||||
|
||||
def reduce_shape(shape: TopoDS_Shape) -> tuple:
|
||||
|
|
|
|||
|
|
@ -30,6 +30,7 @@ from __future__ import annotations
|
|||
|
||||
# pylint has trouble with the OCP imports
|
||||
# pylint: disable=no-name-in-module, import-error
|
||||
# pylint: disable=too-many-lines
|
||||
# other pylint warning to temp remove:
|
||||
# too-many-arguments, too-many-locals, too-many-public-methods,
|
||||
# too-many-statements, too-many-instance-attributes, too-many-branches
|
||||
|
|
@ -41,10 +42,9 @@ import platform
|
|||
import sys
|
||||
import warnings
|
||||
from abc import ABC, abstractmethod
|
||||
from datetime import datetime
|
||||
from io import BytesIO
|
||||
from itertools import combinations
|
||||
from math import degrees, radians, inf, pi, sqrt, sin, cos, tan, copysign, ceil, floor
|
||||
from math import radians, inf, pi, sin, cos, tan, copysign, ceil, floor
|
||||
from typing import (
|
||||
Any,
|
||||
Callable,
|
||||
|
|
@ -60,13 +60,10 @@ from typing import (
|
|||
overload,
|
||||
)
|
||||
from typing import cast as tcast
|
||||
import xml.etree.cElementTree as ET
|
||||
from zipfile import ZipFile, ZIP_DEFLATED, ZIP_STORED
|
||||
from typing_extensions import Self, Literal
|
||||
|
||||
from anytree import NodeMixin, PreOrderIter, RenderTree
|
||||
from scipy.spatial import ConvexHull
|
||||
from scipy.optimize import minimize
|
||||
from vtkmodules.vtkCommonDataModel import vtkPolyData
|
||||
from vtkmodules.vtkFiltersCore import vtkPolyDataNormals, vtkTriangleFilter
|
||||
|
||||
|
|
@ -88,7 +85,6 @@ from OCP.BRepAlgoAPI import (
|
|||
BRepAlgoAPI_Cut,
|
||||
BRepAlgoAPI_Fuse,
|
||||
BRepAlgoAPI_Splitter,
|
||||
BRepAlgoAPI_Section,
|
||||
)
|
||||
from OCP.BRepBuilderAPI import (
|
||||
BRepBuilderAPI_Copy,
|
||||
|
|
@ -152,8 +148,7 @@ from OCP.Font import (
|
|||
)
|
||||
from OCP.GC import GC_MakeArcOfCircle, GC_MakeArcOfEllipse # geometry construction
|
||||
from OCP.gce import gce_MakeLin
|
||||
from OCP.GCE2d import GCE2d_MakeSegment
|
||||
from OCP.GCPnts import GCPnts_AbscissaPoint, GCPnts_QuasiUniformDeflection
|
||||
from OCP.GCPnts import GCPnts_AbscissaPoint
|
||||
from OCP.Geom import (
|
||||
Geom_BezierCurve,
|
||||
Geom_ConicalSurface,
|
||||
|
|
@ -164,19 +159,16 @@ from OCP.Geom import (
|
|||
Geom_Line,
|
||||
)
|
||||
from OCP.Geom2d import Geom2d_Curve, Geom2d_Line, Geom2d_TrimmedCurve
|
||||
from OCP.Geom2dAdaptor import Geom2dAdaptor_Curve
|
||||
from OCP.Geom2dAPI import Geom2dAPI_InterCurveCurve
|
||||
from OCP.GeomAbs import GeomAbs_C0, GeomAbs_Intersection, GeomAbs_JoinType
|
||||
from OCP.GeomAPI import (
|
||||
GeomAPI_Interpolate,
|
||||
GeomAPI_IntCS,
|
||||
GeomAPI_IntSS,
|
||||
GeomAPI_PointsToBSpline,
|
||||
GeomAPI_PointsToBSplineSurface,
|
||||
GeomAPI_ProjectPointOnSurf,
|
||||
GeomAPI_ProjectPointOnCurve,
|
||||
)
|
||||
from OCP.GeomConvert import GeomConvert
|
||||
from OCP.GeomFill import (
|
||||
GeomFill_CorrectedFrenet,
|
||||
GeomFill_Frenet,
|
||||
|
|
@ -190,13 +182,10 @@ from OCP.gp import (
|
|||
gp_Dir,
|
||||
gp_Dir2d,
|
||||
gp_Elips,
|
||||
gp_Lin,
|
||||
gp_Lin2d,
|
||||
gp_Pnt,
|
||||
gp_Pnt2d,
|
||||
gp_Trsf,
|
||||
gp_Vec,
|
||||
gp_Vec2d,
|
||||
)
|
||||
|
||||
# properties used to store mass calculation result
|
||||
|
|
@ -218,7 +207,6 @@ from OCP.ShapeFix import (
|
|||
ShapeFix_Face,
|
||||
ShapeFix_Shape,
|
||||
ShapeFix_Solid,
|
||||
ShapeFix_Wire,
|
||||
ShapeFix_Wireframe,
|
||||
)
|
||||
from OCP.ShapeUpgrade import ShapeUpgrade_UnifySameDomain
|
||||
|
|
@ -275,7 +263,6 @@ from OCP.TopTools import (
|
|||
from build123d.build_enums import (
|
||||
Align,
|
||||
AngularDirection,
|
||||
ApproxOption,
|
||||
CenterOf,
|
||||
FontStyle,
|
||||
FrameMethod,
|
||||
|
|
@ -286,34 +273,27 @@ from build123d.build_enums import (
|
|||
Side,
|
||||
SortBy,
|
||||
Transition,
|
||||
Unit,
|
||||
Until,
|
||||
)
|
||||
from build123d.geometry import (
|
||||
DEG2RAD,
|
||||
TOLERANCE,
|
||||
|
||||
Axis,
|
||||
BoundBox,
|
||||
Color,
|
||||
Location,
|
||||
Matrix,
|
||||
Plane,
|
||||
Rotation,
|
||||
RotationLike,
|
||||
Vector,
|
||||
VectorLike,
|
||||
|
||||
logger,
|
||||
)
|
||||
|
||||
# Create a build123d logger to distinguish these logs from application logs.
|
||||
# If the user doesn't configure logging, all build123d logs will be discarded.
|
||||
logging.getLogger("build123d").addHandler(logging.NullHandler())
|
||||
logger = logging.getLogger("build123d")
|
||||
|
||||
TOLERANCE = 1e-6
|
||||
TOL = 1e-2
|
||||
DEG2RAD = pi / 180.0
|
||||
RAD2DEG = 180 / pi
|
||||
HASH_CODE_MAX = 2147483647 # max 32bit signed int, required by OCC.Core.HashCode
|
||||
|
||||
|
||||
shape_LUT = {
|
||||
ta.TopAbs_VERTEX: "Vertex",
|
||||
ta.TopAbs_EDGE: "Edge",
|
||||
|
|
@ -412,13 +392,15 @@ Geoms = Literal[
|
|||
]
|
||||
|
||||
|
||||
def tuplify(obj, dim):
|
||||
def tuplify(obj: Any, dim: int) -> tuple:
|
||||
"""Create a size tuple"""
|
||||
if obj is None:
|
||||
return None
|
||||
result = None
|
||||
elif isinstance(obj, (tuple, list)):
|
||||
return obj
|
||||
result = tuple(obj)
|
||||
else:
|
||||
return tuple([obj] * dim)
|
||||
result = tuple([obj] * dim)
|
||||
return result
|
||||
|
||||
|
||||
class Mixin1D:
|
||||
|
|
@ -672,6 +654,7 @@ class Mixin1D:
|
|||
"""Does the Edge/Wire loop forward or reverse"""
|
||||
return self.wrapped.Orientation() == TopAbs_Orientation.TopAbs_FORWARD
|
||||
|
||||
@property
|
||||
def is_closed(self) -> bool:
|
||||
"""Are the start and end points equal?"""
|
||||
return BRep_Tool.IsClosed_s(self.wrapped)
|
||||
|
|
@ -857,8 +840,8 @@ class Mixin1D:
|
|||
obj = downcast(offset_builder.Shape())
|
||||
if isinstance(obj, TopoDS_Compound):
|
||||
offset_wire = None
|
||||
for i, el in enumerate(Compound(obj)):
|
||||
offset_wire = Wire(el.wrapped)
|
||||
for i, shape in enumerate(Compound(obj)):
|
||||
offset_wire = Wire(shape.wrapped)
|
||||
if i >= 1:
|
||||
raise RuntimeError("Multiple Wires generated")
|
||||
if offset_wire is None:
|
||||
|
|
@ -899,20 +882,17 @@ class Mixin1D:
|
|||
end0 = offset_wire.position_at(0)
|
||||
end1 = offset_wire.position_at(1)
|
||||
if (self0 - end0).length - distance <= TOLERANCE:
|
||||
e0 = Edge.make_line(self0, end0)
|
||||
e1 = Edge.make_line(self1, end1)
|
||||
edge0 = Edge.make_line(self0, end0)
|
||||
edge1 = Edge.make_line(self1, end1)
|
||||
else:
|
||||
e0 = Edge.make_line(self0, end1)
|
||||
e1 = Edge.make_line(self1, end0)
|
||||
edge0 = Edge.make_line(self0, end1)
|
||||
edge1 = Edge.make_line(self1, end0)
|
||||
offset_wire = Wire.make_wire(
|
||||
line.edges() + offset_wire.edges() + [e0, e1]
|
||||
line.edges() + offset_wire.edges() + [edge0, edge1]
|
||||
)
|
||||
|
||||
offset_edges = offset_wire.edges()
|
||||
if len(offset_edges) == 1:
|
||||
return offset_edges[0]
|
||||
else:
|
||||
return offset_wire
|
||||
return offset_edges[0] if len(offset_edges) == 1 else offset_wire
|
||||
|
||||
def perpendicular_line(
|
||||
self, length: float, u_value: float, plane: Plane = Plane.XY
|
||||
|
|
@ -1494,40 +1474,44 @@ class Shape(NodeMixin):
|
|||
bool: is the shape manifold or water tight
|
||||
"""
|
||||
if isinstance(self, Compound):
|
||||
return all([sub_shape.is_manifold for sub_shape in self])
|
||||
else:
|
||||
# Create an empty indexed data map to store the edges and their corresponding faces.
|
||||
map = TopTools_IndexedDataMapOfShapeListOfShape()
|
||||
# pylint: disable=not-an-iterable
|
||||
return all(sub_shape.is_manifold for sub_shape in self)
|
||||
|
||||
# Fill the map with edges and their associated faces in the given shape. Each edge in
|
||||
# the map is associated with a list of faces that share that edge.
|
||||
TopExp.MapShapesAndAncestors_s(
|
||||
self.wrapped, ta.TopAbs_EDGE, ta.TopAbs_FACE, map
|
||||
)
|
||||
result = True
|
||||
# Create an empty indexed data map to store the edges and their corresponding faces.
|
||||
shape_map = TopTools_IndexedDataMapOfShapeListOfShape()
|
||||
|
||||
# Iterate over the edges in the map and checks if each edge is non-degenerate and has
|
||||
# exactly two faces associated with it.
|
||||
for i in range(map.Extent()):
|
||||
# Access each edge in the map sequentially
|
||||
edge = downcast(map.FindKey(i + 1))
|
||||
# Fill the map with edges and their associated faces in the given shape. Each edge in
|
||||
# the map is associated with a list of faces that share that edge.
|
||||
TopExp.MapShapesAndAncestors_s(
|
||||
self.wrapped, ta.TopAbs_EDGE, ta.TopAbs_FACE, shape_map
|
||||
)
|
||||
|
||||
vertex0 = TopoDS_Vertex()
|
||||
vertex1 = TopoDS_Vertex()
|
||||
# Iterate over the edges in the map and checks if each edge is non-degenerate and has
|
||||
# exactly two faces associated with it.
|
||||
for i in range(shape_map.Extent()):
|
||||
# Access each edge in the map sequentially
|
||||
edge = downcast(shape_map.FindKey(i + 1))
|
||||
|
||||
# Extract the two vertices of the current edge and stores them in vertex0 and vertex1.
|
||||
TopExp.Vertices_s(edge, vertex0, vertex1)
|
||||
vertex0 = TopoDS_Vertex()
|
||||
vertex1 = TopoDS_Vertex()
|
||||
|
||||
# Check if both vertices are null and if they are the same vertex. If so, the edge is
|
||||
# considered degenerate (i.e., has zero length), and it is skipped.
|
||||
if vertex0.IsNull() and vertex1.IsNull() and vertex0.IsSame(vertex1):
|
||||
continue
|
||||
# Extract the two vertices of the current edge and stores them in vertex0/1.
|
||||
TopExp.Vertices_s(edge, vertex0, vertex1)
|
||||
|
||||
# Check if the current edge has exactly two faces associated with it. If not, it means
|
||||
# the edge is not shared by exactly two faces, indicating that the shape is not manifold.
|
||||
if map.FindFromIndex(i + 1).Extent() != 2:
|
||||
return False
|
||||
# Check if both vertices are null and if they are the same vertex. If so, the
|
||||
# edge is considered degenerate (i.e., has zero length), and it is skipped.
|
||||
if vertex0.IsNull() and vertex1.IsNull() and vertex0.IsSame(vertex1):
|
||||
continue
|
||||
|
||||
return True
|
||||
# Check if the current edge has exactly two faces associated with it. If not,
|
||||
# it means the edge is not shared by exactly two faces, indicating that the
|
||||
# shape is not manifold.
|
||||
if shape_map.FindFromIndex(i + 1).Extent() != 2:
|
||||
result = False
|
||||
break
|
||||
|
||||
return result
|
||||
|
||||
class _DisplayNode(NodeMixin):
|
||||
"""Used to create anytree structures from TopoDS_Shapes"""
|
||||
|
|
@ -1590,12 +1574,13 @@ class Shape(NodeMixin):
|
|||
# Calculate the size of the tree labels
|
||||
size_tuples = [(node.height, len(node.label)) for node in root_node.descendants]
|
||||
size_tuples.append((root_node.height, len(root_node.label)))
|
||||
# pylint: disable=cell-var-from-loop
|
||||
size_tuples_per_level = [
|
||||
list(filter(lambda ll: ll[0] == l, size_tuples))
|
||||
for l in range(root_node.height + 1)
|
||||
]
|
||||
max_sizes_per_level = [
|
||||
max(4, max([l[1] for l in level])) for level in size_tuples_per_level
|
||||
max(4, max(l[1] for l in level)) for level in size_tuples_per_level
|
||||
]
|
||||
level_sizes_per_level = [
|
||||
l + i * 4 for i, l in enumerate(reversed(max_sizes_per_level))
|
||||
|
|
@ -1605,7 +1590,7 @@ class Shape(NodeMixin):
|
|||
# Build the tree line by line
|
||||
result = ""
|
||||
for pre, _fill, node in RenderTree(root_node):
|
||||
treestr = ("%s%s" % (pre, node.label)).ljust(tree_label_width)
|
||||
treestr = f"{pre}{node.label}".ljust(tree_label_width)
|
||||
if hasattr(root_node, "address"):
|
||||
address = node.address
|
||||
name = ""
|
||||
|
|
@ -1760,6 +1745,10 @@ class Shape(NodeMixin):
|
|||
)
|
||||
return [loc * self for loc in other]
|
||||
|
||||
def center(self) -> Vector:
|
||||
"""All of the derived classes from Shape need a center method"""
|
||||
raise NotImplementedError
|
||||
|
||||
def clean(self) -> Self:
|
||||
"""clean
|
||||
|
||||
|
|
@ -2558,7 +2547,7 @@ class Shape(NodeMixin):
|
|||
Args:
|
||||
to_fuse (sequence Shape): shapes to fuse
|
||||
glue (bool, optional): performance improvement for some shapes. Defaults to False.
|
||||
tol (float, optional): tolerarance. Defaults to None.
|
||||
tol (float, optional): tolerance. Defaults to None.
|
||||
|
||||
Returns:
|
||||
Shape: fused shape
|
||||
|
|
@ -2657,13 +2646,15 @@ class Shape(NodeMixin):
|
|||
|
||||
if vertex_intersections:
|
||||
if shape_intersections.is_null():
|
||||
shape_intersections = Compound.fuse(*vertex_intersections)
|
||||
shape_intersections = vertex_intersections.pop().fuse(
|
||||
*vertex_intersections
|
||||
)
|
||||
else:
|
||||
shape_intersections = shape_intersections.fuse(*vertex_intersections)
|
||||
|
||||
if edge_intersections:
|
||||
if shape_intersections.is_null():
|
||||
shape_intersections = Compound.fuse(*edge_intersections)
|
||||
shape_intersections = edge_intersections.pop().fuse(*edge_intersections)
|
||||
else:
|
||||
shape_intersections = shape_intersections.fuse(*edge_intersections)
|
||||
|
||||
|
|
@ -2741,7 +2732,7 @@ class Shape(NodeMixin):
|
|||
if keep == Keep.BOTH:
|
||||
result = Compound(downcast(splitter.Shape()))
|
||||
else:
|
||||
parts = [shape for shape in Compound(downcast(splitter.Shape()))]
|
||||
parts = list(Compound(downcast(splitter.Shape())))
|
||||
tops = []
|
||||
bottoms = []
|
||||
for part in parts:
|
||||
|
|
@ -3095,7 +3086,7 @@ class Shape(NodeMixin):
|
|||
Extrude self in the provided direction.
|
||||
|
||||
Args:
|
||||
direction (VectorLike): direction and magnitue of extrusion
|
||||
direction (VectorLike): direction and magnitude of extrusion
|
||||
|
||||
Raises:
|
||||
ValueError: Unsupported class
|
||||
|
|
@ -3147,7 +3138,7 @@ class Shape(NodeMixin):
|
|||
* Shells generate Compounds
|
||||
|
||||
Args:
|
||||
direction (VectorLike): direction and magnitue of extrusion
|
||||
direction (VectorLike): direction and magnitude of extrusion
|
||||
|
||||
Raises:
|
||||
ValueError: Unsupported class
|
||||
|
|
@ -3255,6 +3246,8 @@ K = TypeVar("K")
|
|||
|
||||
|
||||
class ShapePredicate(Protocol):
|
||||
"""Predicate for shape filters"""
|
||||
|
||||
def __call__(self, shape: Shape) -> bool:
|
||||
...
|
||||
|
||||
|
|
@ -3287,7 +3280,9 @@ class ShapeList(list[T]):
|
|||
objects.
|
||||
|
||||
Args:
|
||||
filter_by (Union[Axis,Plane,GeomType]): axis, plane, or geom type to filter and possibly sort by. Filtering by a plane returns faces/edges parallel to that plane.
|
||||
filter_by (Union[Axis,Plane,GeomType]): axis, plane, or geom type to filter
|
||||
and possibly sort by. Filtering by a plane returns faces/edges parallel
|
||||
to that plane.
|
||||
reverse (bool, optional): invert the geom type filter. Defaults to False.
|
||||
tolerance (float, optional): maximum deviation from axis. Defaults to 1e-5.
|
||||
|
||||
|
|
@ -3340,13 +3335,19 @@ class ShapeList(list[T]):
|
|||
elif isinstance(filter_by, Plane):
|
||||
predicate = plane_parallel_predicate(filter_by, tolerance=tolerance)
|
||||
elif isinstance(filter_by, GeomType):
|
||||
predicate = lambda o: o.geom_type() == filter_by.name
|
||||
|
||||
def predicate(obj):
|
||||
return obj.geom_type() == filter_by.name
|
||||
|
||||
else:
|
||||
raise ValueError(f"Unsupported filter_by predicate: {filter_by}")
|
||||
|
||||
# final predicate is negated if `reverse=True`
|
||||
if reverse:
|
||||
actual_predicate = lambda shape: not predicate(shape)
|
||||
|
||||
def actual_predicate(shape):
|
||||
return not predicate(shape)
|
||||
|
||||
else:
|
||||
actual_predicate = predicate
|
||||
|
||||
|
|
@ -3428,27 +3429,46 @@ class ShapeList(list[T]):
|
|||
|
||||
if isinstance(group_by, Axis):
|
||||
axis_as_location = group_by.location.inverse()
|
||||
key_f = lambda obj: round(
|
||||
# group_by.to_plane().to_local_coords(obj).center().Z, tol_digits
|
||||
(axis_as_location * Location(obj.center())).position.Z,
|
||||
tol_digits,
|
||||
)
|
||||
|
||||
def key_f(obj):
|
||||
return round(
|
||||
(axis_as_location * Location(obj.center())).position.Z,
|
||||
tol_digits,
|
||||
)
|
||||
|
||||
elif isinstance(group_by, (Edge, Wire)):
|
||||
key_f = lambda obj: round(
|
||||
group_by.param_at_point(obj.center()),
|
||||
tol_digits,
|
||||
)
|
||||
|
||||
def key_f(obj):
|
||||
return round(
|
||||
group_by.param_at_point(obj.center()),
|
||||
tol_digits,
|
||||
)
|
||||
|
||||
elif isinstance(group_by, SortBy):
|
||||
if group_by == SortBy.LENGTH:
|
||||
key_f = lambda obj: round(obj.length, tol_digits)
|
||||
|
||||
def key_f(obj):
|
||||
return round(obj.length, tol_digits)
|
||||
|
||||
elif group_by == SortBy.RADIUS:
|
||||
key_f = lambda obj: round(obj.radius, tol_digits)
|
||||
|
||||
def key_f(obj):
|
||||
return round(obj.radius, tol_digits)
|
||||
|
||||
elif group_by == SortBy.DISTANCE:
|
||||
key_f = lambda obj: round(obj.center().length, tol_digits)
|
||||
|
||||
def key_f(obj):
|
||||
return round(obj.center().length, tol_digits)
|
||||
|
||||
elif group_by == SortBy.AREA:
|
||||
key_f = lambda obj: round(obj.area, tol_digits)
|
||||
|
||||
def key_f(obj):
|
||||
return round(obj.area, tol_digits)
|
||||
|
||||
elif group_by == SortBy.VOLUME:
|
||||
key_f = lambda obj: round(obj.volume, tol_digits)
|
||||
|
||||
def key_f(obj):
|
||||
return round(obj.volume, tol_digits)
|
||||
|
||||
elif callable(group_by):
|
||||
key_f = group_by
|
||||
|
|
@ -3482,11 +3502,12 @@ class ShapeList(list[T]):
|
|||
)
|
||||
elif isinstance(sort_by, (Edge, Wire)):
|
||||
|
||||
def u_of_closest_center(o) -> float:
|
||||
def u_of_closest_center(obj) -> float:
|
||||
"""u-value of closest point between object center and sort_by"""
|
||||
p1, _p2 = sort_by.closest_points(o.center())
|
||||
return sort_by.param_at_point(p1)
|
||||
pnt1, _pnt2 = sort_by.closest_points(obj.center())
|
||||
return sort_by.param_at_point(pnt1)
|
||||
|
||||
# pylint: disable=unnecessary-lambda
|
||||
objects = sorted(
|
||||
self, key=lambda o: u_of_closest_center(o), reverse=reverse
|
||||
)
|
||||
|
|
@ -3719,16 +3740,18 @@ class GroupBy:
|
|||
return self.groups[key]
|
||||
|
||||
def group(self, key: K):
|
||||
"""Select group by key"""
|
||||
for k, i in self.key_to_group_index:
|
||||
if key == k:
|
||||
return self.groups[i]
|
||||
raise KeyError(key)
|
||||
|
||||
def group_for(self, shape: Shape):
|
||||
"""Select group by shape"""
|
||||
return self.group(self.key_f(shape))
|
||||
|
||||
|
||||
class Compound(Shape, Mixin3D):
|
||||
class Compound(Mixin3D, Shape):
|
||||
"""Compound
|
||||
|
||||
A collection of Shapes
|
||||
|
|
@ -4012,18 +4035,8 @@ class Compound(Shape, Mixin3D):
|
|||
|
||||
# Align the text from the bounding box
|
||||
align = tuplify(align, 2)
|
||||
bbox = text_flat.bounding_box()
|
||||
align_offset = []
|
||||
for i in range(2):
|
||||
if align[i] == Align.MIN:
|
||||
align_offset.append(-bbox.min.to_tuple()[i])
|
||||
elif align[i] == Align.CENTER:
|
||||
align_offset.append(
|
||||
-(bbox.min.to_tuple()[i] + bbox.max.to_tuple()[i]) / 2
|
||||
)
|
||||
elif align[i] == Align.MAX:
|
||||
align_offset.append(-bbox.max.to_tuple()[i])
|
||||
text_flat = text_flat.translate(Vector(*align_offset))
|
||||
text_flat = text_flat.translate(Vector(
|
||||
*text_flat.bounding_box().to_align_offset(align)))
|
||||
|
||||
if text_path is not None:
|
||||
path_length = text_path.length
|
||||
|
|
@ -4224,7 +4237,7 @@ class Curve(Compound):
|
|||
return Wire.combine(self.edges())
|
||||
|
||||
|
||||
class Edge(Shape, Mixin1D):
|
||||
class Edge(Mixin1D, Shape):
|
||||
"""A trimmed curve that represents the border of a face"""
|
||||
|
||||
_dim = 1
|
||||
|
|
@ -4235,7 +4248,7 @@ class Edge(Shape, Mixin1D):
|
|||
|
||||
def close(self) -> Union[Edge, Wire]:
|
||||
"""Close an Edge"""
|
||||
if not self.is_closed():
|
||||
if not self.is_closed:
|
||||
return_value = Wire.make_wire([self]).close()
|
||||
else:
|
||||
return_value = self
|
||||
|
|
@ -4265,7 +4278,6 @@ class Edge(Shape, Mixin1D):
|
|||
def find_tangent(
|
||||
self,
|
||||
angle: float,
|
||||
plane: Plane = Plane.XY,
|
||||
) -> list[float]:
|
||||
"""find_tangent
|
||||
|
||||
|
|
@ -4273,7 +4285,6 @@ class Edge(Shape, Mixin1D):
|
|||
|
||||
Args:
|
||||
angle (float): target angle in degrees
|
||||
plane (Plane, optional): plane that Edge was constructed on. Defaults to Plane.XY.
|
||||
|
||||
Returns:
|
||||
list[float]: u values between 0.0 and 1.0
|
||||
|
|
@ -4287,7 +4298,7 @@ class Edge(Shape, Mixin1D):
|
|||
u_values = []
|
||||
else:
|
||||
# Solve this problem geometrically by creating a tangent curve and finding intercepts
|
||||
periodic = int(self.is_closed()) # if closed don't include end point
|
||||
periodic = int(self.is_closed) # if closed don't include end point
|
||||
tan_pnts = []
|
||||
previous_tangent = None
|
||||
|
||||
|
|
@ -4392,8 +4403,8 @@ class Edge(Shape, Mixin1D):
|
|||
else:
|
||||
if 0.0 <= self.param_at_point(pnt) <= 1.0:
|
||||
valid_crosses.append(pnt)
|
||||
except:
|
||||
pass
|
||||
except ValueError:
|
||||
pass # skip invalid points
|
||||
|
||||
return ShapeList(valid_crosses)
|
||||
|
||||
|
|
@ -4457,16 +4468,12 @@ class Edge(Shape, Mixin1D):
|
|||
curve = BRep_Tool.Curve_s(self.wrapped, 0, 1)
|
||||
param_min = _project_point_on_curve(curve, self.position_at(0).to_pnt())
|
||||
param_value = _project_point_on_curve(curve, point.to_pnt())
|
||||
if self.is_closed():
|
||||
if self.is_closed:
|
||||
u_value = (param_value - param_min) / (self.param_at(1) - self.param_at(0))
|
||||
else:
|
||||
param_max = _project_point_on_curve(curve, self.position_at(1).to_pnt())
|
||||
u_value = (param_value - param_min) / (param_max - param_min)
|
||||
|
||||
# if not (-TOLERANCE <= u_value <= 1.0 + TOLERANCE):
|
||||
# raise RuntimeError(
|
||||
# f"param_at_point returned {u_value}, which is invalid {param_value=}, {param_min=}, {param_max=}"
|
||||
# )
|
||||
return u_value
|
||||
|
||||
@classmethod
|
||||
|
|
@ -5167,7 +5174,7 @@ class Face(Shape):
|
|||
def wire(self) -> Wire:
|
||||
"""Return the outerwire, generate a warning if inner_wires present"""
|
||||
if self.inner_wires():
|
||||
warnings.warn(f"Found holes, returning outer_wire")
|
||||
warnings.warn("Found holes, returning outer_wire")
|
||||
return self.outer_wire()
|
||||
|
||||
@classmethod
|
||||
|
|
@ -5254,7 +5261,7 @@ class Face(Shape):
|
|||
Returns:
|
||||
Face: planar face potentially with holes
|
||||
"""
|
||||
if inner_wires and not outer_wire.is_closed():
|
||||
if inner_wires and not outer_wire.is_closed:
|
||||
raise ValueError("Cannot build face(s): outer wire is not closed")
|
||||
inner_wires = inner_wires if inner_wires else []
|
||||
|
||||
|
|
@ -5273,7 +5280,7 @@ class Face(Shape):
|
|||
face_builder = BRepBuilderAPI_MakeFace(topo_wire, True)
|
||||
|
||||
for inner_wire in inner_wires:
|
||||
if not inner_wire.is_closed():
|
||||
if not inner_wire.is_closed:
|
||||
raise ValueError("Cannot build face(s): inner wire is not closed")
|
||||
face_builder.Add(inner_wire.wrapped)
|
||||
|
||||
|
|
@ -5778,7 +5785,7 @@ class Shell(Shape):
|
|||
return Vector(properties.CentreOfMass())
|
||||
|
||||
|
||||
class Solid(Shape, Mixin3D):
|
||||
class Solid(Mixin3D, Shape):
|
||||
"""a single solid"""
|
||||
|
||||
_dim = 3
|
||||
|
|
@ -6225,11 +6232,11 @@ class Solid(Shape, Mixin3D):
|
|||
# and exclude the planar faces normal to the direction of extrusion and these
|
||||
# will have no volume when extruded
|
||||
faces = []
|
||||
for f in section.project_to_shape(target_object, direction):
|
||||
if isinstance(f, Face):
|
||||
faces.append(f)
|
||||
for face in section.project_to_shape(target_object, direction):
|
||||
if isinstance(face, Face):
|
||||
faces.append(face)
|
||||
else:
|
||||
faces += f.faces()
|
||||
faces += face.faces()
|
||||
|
||||
clip_faces = [
|
||||
f
|
||||
|
|
@ -6464,7 +6471,7 @@ class Vertex(Shape):
|
|||
"""Default Vertext at the origin"""
|
||||
|
||||
@overload
|
||||
def __init__(self, obj: TopoDS_Vertex): # pragma: no cover
|
||||
def __init__(self, v: TopoDS_Vertex): # pragma: no cover
|
||||
"""Vertex from OCCT TopoDS_Vertex object"""
|
||||
|
||||
@overload
|
||||
|
|
@ -6472,36 +6479,51 @@ class Vertex(Shape):
|
|||
"""Vertex from three float values"""
|
||||
|
||||
@overload
|
||||
def __init__(self, values: Iterable[float]):
|
||||
def __init__(self, v: Iterable[float]):
|
||||
"""Vertex from Vector or other iterators"""
|
||||
|
||||
@overload
|
||||
def __init__(self, values: tuple[float]):
|
||||
def __init__(self, v: tuple[float]):
|
||||
"""Vertex from tuple of floats"""
|
||||
|
||||
def __init__(self, *args):
|
||||
def __init__(self, *args, **kwargs):
|
||||
self.vertex_index = 0
|
||||
if len(args) == 0:
|
||||
self.wrapped = downcast(
|
||||
BRepBuilderAPI_MakeVertex(gp_Pnt(0.0, 0.0, 0.0)).Vertex()
|
||||
)
|
||||
elif len(args) == 1 and isinstance(args[0], TopoDS_Vertex):
|
||||
self.wrapped = args[0]
|
||||
elif len(args) == 1 and isinstance(args[0], (Iterable, tuple)):
|
||||
values = [float(value) for value in args[0]]
|
||||
if len(values) < 3:
|
||||
values += [0.0] * (3 - len(values))
|
||||
self.wrapped = downcast(BRepBuilderAPI_MakeVertex(gp_Pnt(*values)).Vertex())
|
||||
elif len(args) == 3 and all(isinstance(v, (int, float)) for v in args):
|
||||
self.wrapped = downcast(
|
||||
BRepBuilderAPI_MakeVertex(gp_Pnt(args[0], args[1], args[2])).Vertex()
|
||||
)
|
||||
else:
|
||||
raise ValueError(
|
||||
"Invalid Vertex - expected three floats or OCC TopoDS_Vertex"
|
||||
)
|
||||
x, y, z, ocp_vx = 0, 0, 0, None
|
||||
|
||||
unknown_args = ", ".join(set(kwargs.keys()).difference(["v", "X", "Y", "Z"]))
|
||||
if unknown_args:
|
||||
raise ValueError(f"Unexpected argument(s) {unknown_args}")
|
||||
|
||||
if args and all(isinstance(args[i], (int, float)) for i in range(len(args))):
|
||||
values = list(args)
|
||||
values += [0.0] * max(0, (3 - len(args)))
|
||||
x, y, z = values[0:3]
|
||||
elif len(args) == 1 or "v" in kwargs:
|
||||
first_arg = args[0] if args else None
|
||||
first_arg = kwargs.get("v", first_arg) # override with kwarg
|
||||
if isinstance(first_arg, (tuple, Iterable)):
|
||||
try:
|
||||
values = [float(value) for value in first_arg]
|
||||
except (TypeError, ValueError) as exc:
|
||||
raise TypeError("Expected floats") from exc
|
||||
if len(values) < 3:
|
||||
values += [0.0] * (3 - len(values))
|
||||
x, y, z = values
|
||||
elif isinstance(first_arg, TopoDS_Vertex):
|
||||
ocp_vx = first_arg
|
||||
else:
|
||||
raise TypeError("Expected floats, TopoDS_Vertex, or iterable")
|
||||
x = kwargs.get("X", x)
|
||||
y = kwargs.get("Y", y)
|
||||
z = kwargs.get("Z", z)
|
||||
ocp_vx = (
|
||||
downcast(BRepBuilderAPI_MakeVertex(gp_Pnt(x, y, z)).Vertex())
|
||||
if ocp_vx is None
|
||||
else ocp_vx
|
||||
)
|
||||
|
||||
super().__init__(ocp_vx)
|
||||
self.X, self.Y, self.Z = self.to_tuple()
|
||||
super().__init__(self.wrapped)
|
||||
|
||||
def to_tuple(self) -> tuple[float, float, float]:
|
||||
"""Return vertex as three tuple of floats"""
|
||||
|
|
@ -6510,7 +6532,7 @@ class Vertex(Shape):
|
|||
|
||||
def center(self) -> Vector:
|
||||
"""The center of a vertex is itself!"""
|
||||
return Vector(self.to_tuple())
|
||||
return Vector(self)
|
||||
|
||||
def __add__(
|
||||
self, other: Union[Vertex, Vector, Tuple[float, float, float]]
|
||||
|
|
@ -6611,7 +6633,7 @@ class Vertex(Shape):
|
|||
return value
|
||||
|
||||
|
||||
class Wire(Shape, Mixin1D):
|
||||
class Wire(Mixin1D, Shape):
|
||||
"""A series of connected, ordered edges, that typically bounds a Face"""
|
||||
|
||||
_dim = 1
|
||||
|
|
@ -6623,7 +6645,7 @@ class Wire(Shape, Mixin1D):
|
|||
def close(self) -> Wire:
|
||||
"""Close a Wire"""
|
||||
|
||||
if not self.is_closed():
|
||||
if not self.is_closed:
|
||||
edge = Edge.make_line(self.end_point(), self.start_point())
|
||||
return_value = Wire.combine((self, edge))[0]
|
||||
else:
|
||||
|
|
@ -6682,7 +6704,7 @@ class Wire(Shape, Mixin1D):
|
|||
def param_at_point(self, point: VectorLike) -> float:
|
||||
"""Parameter at point on Wire"""
|
||||
|
||||
# OCP doesn't support this so this algoritm finds the edge that contains the
|
||||
# OCP doesn't support this so this algorithm finds the edge that contains the
|
||||
# point, finds the u value/fractional distance of the point on that edge and
|
||||
# sums up the length of the edges from the start to the edge with the point.
|
||||
|
||||
|
|
@ -6714,8 +6736,7 @@ class Wire(Shape, Mixin1D):
|
|||
else:
|
||||
distance += u_value * edge.length
|
||||
break
|
||||
else:
|
||||
distance += edge.length
|
||||
distance += edge.length
|
||||
|
||||
if not found:
|
||||
raise ValueError(f"{point} not on wire")
|
||||
|
|
@ -6758,25 +6779,25 @@ class Wire(Shape, Mixin1D):
|
|||
# Trim edges containing start or end points
|
||||
degenerate = False
|
||||
if contains_start:
|
||||
u = edge.param_at_point(trim_start_point)
|
||||
u_value = edge.param_at_point(trim_start_point)
|
||||
if not flipped:
|
||||
degenerate = u == 1.0
|
||||
degenerate = u_value == 1.0
|
||||
if not degenerate:
|
||||
edge = edge.trim(u, 1.0)
|
||||
edge = edge.trim(u_value, 1.0)
|
||||
elif flipped:
|
||||
degenerate = u == 0.0
|
||||
degenerate = u_value == 0.0
|
||||
if not degenerate:
|
||||
edge = edge.trim(0.0, u)
|
||||
edge = edge.trim(0.0, u_value)
|
||||
if contains_end:
|
||||
u = edge.param_at_point(trim_end_point)
|
||||
u_value = edge.param_at_point(trim_end_point)
|
||||
if not flipped:
|
||||
degenerate = u == 0.0
|
||||
degenerate = u_value == 0.0
|
||||
if not degenerate:
|
||||
edge = edge.trim(0.0, u)
|
||||
edge = edge.trim(0.0, u_value)
|
||||
elif flipped:
|
||||
degenerate = u == 1.0
|
||||
degenerate = u_value == 1.0
|
||||
if not degenerate:
|
||||
edge = edge.trim(u, 1.0)
|
||||
edge = edge.trim(u_value, 1.0)
|
||||
if not degenerate:
|
||||
if contains_start or contains_end:
|
||||
modified_edges.append(edge)
|
||||
|
|
@ -6785,7 +6806,7 @@ class Wire(Shape, Mixin1D):
|
|||
|
||||
# Select the wire containing the start and end points
|
||||
wire_segments = edges_to_wires(modified_edges + original_edges)
|
||||
trimed_wire = filter(
|
||||
trimmed_wire = filter(
|
||||
lambda w: all(
|
||||
[
|
||||
w.distance_to(p) <= TOLERANCE
|
||||
|
|
@ -6794,9 +6815,9 @@ class Wire(Shape, Mixin1D):
|
|||
),
|
||||
wire_segments,
|
||||
)
|
||||
if not trimed_wire:
|
||||
if not trimmed_wire:
|
||||
raise RuntimeError("Invalid trim result")
|
||||
return next(trimed_wire)
|
||||
return next(trimmed_wire)
|
||||
|
||||
def order_edges(self) -> ShapeList[Edge]:
|
||||
"""Return the edges in self ordered by wire direction and orientation"""
|
||||
|
|
@ -6993,7 +7014,7 @@ class Wire(Shape, Mixin1D):
|
|||
distance (float): chamfer length
|
||||
distance2 (float): chamfer length
|
||||
vertices (Iterable[Vertex]): vertices to chamfer
|
||||
edge (Edge): identifies the side where length is measured. The virtices must be
|
||||
edge (Edge): identifies the side where length is measured. The vertices must be
|
||||
part of the edge
|
||||
|
||||
Returns:
|
||||
|
|
@ -7282,7 +7303,7 @@ class Joint(ABC):
|
|||
self.connected_to = other
|
||||
|
||||
@abstractmethod
|
||||
def connect_to(self, other: Joint, **kwags):
|
||||
def connect_to(self, other: Joint, **kwargs):
|
||||
"""All derived classes must provide a connect_to method"""
|
||||
raise NotImplementedError
|
||||
|
||||
|
|
@ -7359,9 +7380,10 @@ def shapetype(obj: TopoDS_Shape) -> TopAbs_ShapeEnum:
|
|||
return obj.ShapeType()
|
||||
|
||||
|
||||
def unwrapped_shapetype(obj: Shape):
|
||||
def unwrapped_shapetype(obj: Shape) -> TopAbs_ShapeEnum:
|
||||
"""Return Shape's TopAbs_ShapeEnum"""
|
||||
if isinstance(obj, Compound):
|
||||
shapetypes = set([shapetype(o.wrapped) for o in obj])
|
||||
shapetypes = set(shapetype(o.wrapped) for o in obj)
|
||||
if len(shapetypes) == 1:
|
||||
result = shapetypes.pop()
|
||||
else:
|
||||
|
|
@ -7422,8 +7444,8 @@ def polar(length: float, angle: float) -> tuple[float, float]:
|
|||
|
||||
def delta(shapes_one: Iterable[Shape], shapes_two: Iterable[Shape]) -> list[Shape]:
|
||||
"""Compare the OCCT objects of each list and return the differences"""
|
||||
occt_one = set([shape.wrapped for shape in shapes_one])
|
||||
occt_two = set([shape.wrapped for shape in shapes_two])
|
||||
occt_one = set(shape.wrapped for shape in shapes_one)
|
||||
occt_two = set(shape.wrapped for shape in shapes_two)
|
||||
occt_delta = list(occt_one - occt_two)
|
||||
|
||||
all_shapes = []
|
||||
|
|
@ -7462,10 +7484,10 @@ def new_edges(*objects: Shape, combined: Shape) -> ShapeList[Edge]:
|
|||
operation.SetRunParallel(True)
|
||||
operation.Build()
|
||||
|
||||
new_edges = Shape.cast(operation.Shape()).edges()
|
||||
for edge in new_edges:
|
||||
edges = Shape.cast(operation.Shape()).edges()
|
||||
for edge in edges:
|
||||
edge.topo_parent = combined
|
||||
return ShapeList(new_edges)
|
||||
return ShapeList(edges)
|
||||
|
||||
|
||||
class SkipClean:
|
||||
|
|
@ -7529,10 +7551,11 @@ def _axis_intersect(self: Axis, *to_intersect: Union[Shape, Axis, Plane]) -> Sha
|
|||
if isinstance(intersector, Shape):
|
||||
intersections.extend(self_i_edge.intersect(intersector))
|
||||
|
||||
if len(intersections) == 1:
|
||||
return intersections[0]
|
||||
else:
|
||||
return Compound(children=intersections)
|
||||
return (
|
||||
intersections[0]
|
||||
if len(intersections) == 1
|
||||
else Compound(children=intersections)
|
||||
)
|
||||
|
||||
|
||||
Axis.intersect = _axis_intersect
|
||||
|
|
|
|||
|
|
@ -1,15 +1,18 @@
|
|||
"""
|
||||
Export a version string.
|
||||
"""
|
||||
try:
|
||||
try:
|
||||
from ._dev.scm_version import version
|
||||
from ._dev.scm_version import version # pylint: disable=unused-import
|
||||
except ImportError:
|
||||
from ._version import version
|
||||
except Exception:
|
||||
except Exception: # pylint: disable=broad-exception-caught
|
||||
import warnings
|
||||
|
||||
warnings.warn(
|
||||
f'could not determine {__name__.split(".")[0]} package version; '
|
||||
f'could not determine {__name__.split(".", maxsplit=1)[0]} package version; '
|
||||
"this indicates a broken installation"
|
||||
)
|
||||
del warnings
|
||||
|
||||
version = "0.0.0"
|
||||
version = "0.0.0" # pylint: disable=invalid-name
|
||||
|
|
|
|||
|
|
@ -512,7 +512,7 @@ class AlgebraTests(unittest.TestCase):
|
|||
l2 = Line((1, 1), (0, 0))
|
||||
l = l1 + l2
|
||||
w = Wire.make_wire(l)
|
||||
self.assertTrue(w.is_closed())
|
||||
self.assertTrue(w.is_closed)
|
||||
self.assertTupleAlmostEquals(
|
||||
w.center(CenterOf.MASS), (0.6464466094067263, 0.35355339059327373, 0.0), 6
|
||||
)
|
||||
|
|
@ -626,7 +626,7 @@ class LocationTests(unittest.TestCase):
|
|||
|
||||
def test_wheels(self):
|
||||
plane = Plane.ZX
|
||||
rotations = [Rot(y=a) for a in (0, 45, 90, 135)]
|
||||
rotations = [Rot(Y=a) for a in (0, 45, 90, 135)]
|
||||
|
||||
s = Sketch()
|
||||
for i, outer_loc in enumerate(GridLocations(3, 3, 2, 2)):
|
||||
|
|
|
|||
|
|
@ -169,7 +169,7 @@ class BuildLineTests(unittest.TestCase):
|
|||
|
||||
with BuildLine() as l:
|
||||
l1 = JernArc(start=(0, 0, 0), tangent=(1, 0, 0), radius=1, arc_size=360)
|
||||
self.assertTrue(l1.is_closed())
|
||||
self.assertTrue(l1.is_closed)
|
||||
circle_face = Face.make_from_wires(l1)
|
||||
self.assertAlmostEqual(circle_face.area, pi, 5)
|
||||
self.assertTupleAlmostEquals(circle_face.center().to_tuple(), (0, 1, 0), 5)
|
||||
|
|
|
|||
|
|
@ -55,6 +55,7 @@ from build123d.geometry import (
|
|||
Location,
|
||||
Matrix,
|
||||
Pos,
|
||||
Rot,
|
||||
Rotation,
|
||||
Vector,
|
||||
VectorLike,
|
||||
|
|
@ -438,7 +439,7 @@ class TestCadObjects(DirectApiTestCase):
|
|||
tangent_arc = Edge.make_tangent_arc(
|
||||
Vector(1, 1), # starts at 1, 1
|
||||
Vector(0, 1), # tangent at start of arc is in the +y direction
|
||||
Vector(2, 1), # arc cureturn_valuees 180 degrees and ends at 2, 1
|
||||
Vector(2, 1), # arc cureturn_values 180 degrees and ends at 2, 1
|
||||
)
|
||||
self.assertVectorAlmostEquals(tangent_arc.start_point(), (1, 1, 0), 3)
|
||||
self.assertVectorAlmostEquals(tangent_arc.end_point(), (2, 1, 0), 3)
|
||||
|
|
@ -1521,12 +1522,12 @@ class TestLocation(DirectApiTestCase):
|
|||
|
||||
def test_eq(self):
|
||||
loc = Location((1, 2, 3), (4, 5, 6))
|
||||
diff_posistion = Location((10, 20, 30), (4, 5, 6))
|
||||
diff_position = Location((10, 20, 30), (4, 5, 6))
|
||||
diff_orientation = Location((1, 2, 3), (40, 50, 60))
|
||||
same = Location((1, 2, 3), (4, 5, 6))
|
||||
|
||||
self.assertEqual(loc, same)
|
||||
self.assertNotEqual(loc, diff_posistion)
|
||||
self.assertNotEqual(loc, diff_position)
|
||||
self.assertNotEqual(loc, diff_orientation)
|
||||
|
||||
def test_neg(self):
|
||||
|
|
@ -1647,11 +1648,11 @@ class TestMatrix(DirectApiTestCase):
|
|||
mz.rotate(Axis.Z, 30 * DEG2RAD)
|
||||
matrix_almost_equal(mz, m_rotate_z_30)
|
||||
|
||||
# Test matrix multipy vector
|
||||
# Test matrix multiply vector
|
||||
v = Vector(1, 0, 0)
|
||||
self.assertVectorAlmostEquals(mz.multiply(v), (root_3_over_2, 1 / 2, 0), 7)
|
||||
|
||||
# Test matrix multipy matrix
|
||||
# Test matrix multiply matrix
|
||||
m_rotate_xy_30 = [
|
||||
[root_3_over_2, 0, 1 / 2, 0],
|
||||
[1 / 4, root_3_over_2, -root_3_over_2 / 2, 0],
|
||||
|
|
@ -1816,7 +1817,7 @@ class TestMixin1D(DirectApiTestCase):
|
|||
corner = base_wire.vertices().group_by(Axis.Y)[0].sort_by(Axis.X)[-1]
|
||||
base_wire = base_wire.fillet_2d(0.4, [corner])
|
||||
offset_wire = base_wire.offset_2d(0.1, side=Side.LEFT)
|
||||
self.assertTrue(offset_wire.is_closed())
|
||||
self.assertTrue(offset_wire.is_closed)
|
||||
self.assertEqual(len(offset_wire.edges().filter_by(GeomType.LINE)), 6)
|
||||
self.assertEqual(len(offset_wire.edges().filter_by(GeomType.CIRCLE)), 2)
|
||||
offset_wire_right = base_wire.offset_2d(0.1, side=Side.RIGHT)
|
||||
|
|
@ -2034,7 +2035,7 @@ class TestPlane(DirectApiTestCase):
|
|||
with self.assertRaises(TypeError):
|
||||
Plane()
|
||||
with self.assertRaises(TypeError):
|
||||
Plane(o, z_dir=1)
|
||||
Plane(o, z_dir="up")
|
||||
|
||||
# rotated location around z
|
||||
loc = Location((0, 0, 0), (0, 0, 45))
|
||||
|
|
@ -2414,6 +2415,16 @@ class TestProjection(DirectApiTestCase):
|
|||
Edge.make_circle(1, end_angle=30).to_axis()
|
||||
|
||||
|
||||
class TestRotation(DirectApiTestCase):
|
||||
def test_rotation_parameters(self):
|
||||
r = Rotation(10, 20, 30)
|
||||
self.assertVectorAlmostEquals(r.orientation, (10, 20, 30), 5)
|
||||
r = Rotation(10, 20, Z=30)
|
||||
self.assertVectorAlmostEquals(r.orientation, (10, 20, 30), 5)
|
||||
with self.assertRaises(TypeError):
|
||||
Rotation(x=10)
|
||||
|
||||
|
||||
class TestShape(DirectApiTestCase):
|
||||
"""Misc Shape tests"""
|
||||
|
||||
|
|
@ -2617,7 +2628,7 @@ class TestShape(DirectApiTestCase):
|
|||
self.assertEqual(len(visible), 6)
|
||||
self.assertEqual(len(hidden), 2)
|
||||
|
||||
# Hidden coutour edges
|
||||
# Hidden contour edges
|
||||
hole = box - cyl
|
||||
visible, hidden = hole.project_to_viewport((-20, 20, 20))
|
||||
self.assertEqual(len(visible), 13)
|
||||
|
|
@ -3074,15 +3085,18 @@ class TestVector(DirectApiTestCase):
|
|||
v3 = Vector(gp_Vec(1, 2, 3))
|
||||
v4 = Vector([1, 2, 3])
|
||||
v5 = Vector(gp_XYZ(1, 2, 3))
|
||||
v5b = Vector(X=1, Y=2, Z=3)
|
||||
v5c = Vector(v=gp_XYZ(1, 2, 3))
|
||||
|
||||
for v in [v1, v2, v3, v4, v5]:
|
||||
for v in [v1, v2, v3, v4, v5, v5b, v5c]:
|
||||
self.assertVectorAlmostEquals(v, (1, 2, 3), 4)
|
||||
|
||||
v6 = Vector((1, 2))
|
||||
v7 = Vector([1, 2])
|
||||
v8 = Vector(1, 2)
|
||||
v8b = Vector(X=1, Y=2)
|
||||
|
||||
for v in [v6, v7, v8]:
|
||||
for v in [v6, v7, v8, v8b]:
|
||||
self.assertVectorAlmostEquals(v, (1, 2, 0), 4)
|
||||
|
||||
v9 = Vector()
|
||||
|
|
@ -3092,11 +3106,19 @@ class TestVector(DirectApiTestCase):
|
|||
v9.Y = 2.0
|
||||
v9.Z = 3.0
|
||||
self.assertVectorAlmostEquals(v9, (1, 2, 3), 4)
|
||||
self.assertVectorAlmostEquals(Vector(1, 2, 3, 4), (1, 2, 3), 4)
|
||||
|
||||
v10 = Vector(1)
|
||||
v11 = Vector((1,))
|
||||
v12 = Vector([1])
|
||||
v13 = Vector(X=1)
|
||||
for v in [v10, v11, v12]:
|
||||
self.assertVectorAlmostEquals(v, (1, 0, 0), 4)
|
||||
|
||||
with self.assertRaises(TypeError):
|
||||
Vector("vector")
|
||||
with self.assertRaises(TypeError):
|
||||
Vector(1, 2, 3, 4)
|
||||
with self.assertRaises(ValueError):
|
||||
Vector(x=1)
|
||||
|
||||
def test_vector_rotate(self):
|
||||
"""Validate vector rotate methods"""
|
||||
|
|
@ -3318,8 +3340,12 @@ class TestVertex(DirectApiTestCase):
|
|||
self.assertVectorAlmostEquals(Vector(Vertex(0, 0, 0)), (0.0, 0.0, 0.0), 7)
|
||||
|
||||
def test_vertex_init_error(self):
|
||||
with self.assertRaises(TypeError):
|
||||
Vertex(Axis.Z)
|
||||
with self.assertRaises(ValueError):
|
||||
Vertex(0.0, 1.0)
|
||||
Vertex(x=1)
|
||||
with self.assertRaises(TypeError):
|
||||
Vertex((Axis.X, Axis.Y, Axis.Z))
|
||||
|
||||
def test_no_intersect(self):
|
||||
with self.assertRaises(NotImplementedError):
|
||||
|
|
|
|||
|
|
@ -297,9 +297,8 @@ class ExtensionLineTestCase(unittest.TestCase):
|
|||
|
||||
class TestTechnicalDrawing(unittest.TestCase):
|
||||
def test_basic_drawing(self):
|
||||
with BuildSketch() as drawing:
|
||||
TechnicalDrawing(design_date=date(2023, 9, 17), sheet_number=1)
|
||||
bbox = drawing.sketch.bounding_box()
|
||||
drawing = TechnicalDrawing(design_date=date(2023, 9, 17), sheet_number=1)
|
||||
bbox = drawing.bounding_box()
|
||||
self.assertGreater(bbox.size.X, 280)
|
||||
self.assertGreater(bbox.size.Y, 195)
|
||||
self.assertGreater(len(drawing.faces()), 110)
|
||||
|
|
|
|||
58
tests/test_pack.py
Normal file
|
|
@ -0,0 +1,58 @@
|
|||
"""
|
||||
build123d Pack tests
|
||||
|
||||
name: build_pack.py
|
||||
by: fischman
|
||||
date: November 9th 2023
|
||||
|
||||
desc: Unit tests for the build123d pack module
|
||||
"""
|
||||
import operator
|
||||
import random
|
||||
import unittest
|
||||
from functools import reduce
|
||||
|
||||
from build123d import *
|
||||
|
||||
class TestPack(unittest.TestCase):
|
||||
"""Tests for the pack helper."""
|
||||
def test_simple(self):
|
||||
"""Test pack with hand-picked data against expected output."""
|
||||
packed = pack([Box(10, 2, 1), Box(1, 5, 1), Box(1, 5, 1)], padding=1)
|
||||
self.assertEqual(
|
||||
# Nothing magically interesting here, and other packings
|
||||
# would also be fine, but this shows that padding is
|
||||
# working, as is the preference towards square(ish)
|
||||
# output.
|
||||
"[bbox: 0.0 <= x <= 10.0, 0.0 <= y <= 2.0, -0.5 <= z <= 0.5,"
|
||||
" bbox: 0.0 <= x <= 1.0, 3.0 <= y <= 8.0, -0.5 <= z <= 0.5,"
|
||||
" bbox: 2.0 <= x <= 3.0, 3.0 <= y <= 8.0, -0.5 <= z <= 0.5]",
|
||||
str([p.bounding_box() for p in packed]))
|
||||
|
||||
def test_random_boxes(self):
|
||||
"""Test pack with larger (and randomized) inputs."""
|
||||
random.seed(123456)
|
||||
# 50 is an arbitrary number that is large enough to exercise
|
||||
# different aspects of the packer while still completing quickly.
|
||||
test_boxes = [Box(random.randint(1, 20), random.randint(1, 20), 1)
|
||||
for _ in range(50)]
|
||||
# Not raising in this call shows successfull non-overlap.
|
||||
packed = pack(test_boxes, 1)
|
||||
self.assertEqual(
|
||||
"bbox: 0.0 <= x <= 94.0, 0.0 <= y <= 86.0, -0.5 <= z <= 0.5",
|
||||
str(reduce(operator.add, packed, Part()).bounding_box()))
|
||||
|
||||
def test_random_slots(self):
|
||||
"""Test pack for 2D objects."""
|
||||
random.seed(123456)
|
||||
# 50 is an arbitrary number that is large enough to exercise
|
||||
# different aspects of the packer while still completing quickly.
|
||||
inputs = [SlotOverall(random.randint(1,20), random.randint(1,20)) for _ in range(50)]
|
||||
# Not raising in this call shows successfull non-overlap.
|
||||
packed = pack(inputs, 1)
|
||||
self.assertEqual(
|
||||
"bbox: 0.0 <= x <= 124.0, 0.0 <= y <= 105.0, 0.0 <= z <= 0.0",
|
||||
str(reduce(operator.add, packed, Sketch()).bounding_box()))
|
||||
|
||||
if __name__ == "__main__":
|
||||
unittest.main()
|
||||
|
|
@ -1,4 +1,4 @@
|
|||
# write me the test for testing the persistence module with unnitest
|
||||
# write me the test for testing the persistence module with unittest
|
||||
|
||||
import unittest
|
||||
from build123d.persistence import (
|
||||
|
|
|
|||