-
-
Notifications
You must be signed in to change notification settings - Fork 24
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
Merge pull request #45 from gumyr/dev
Release 0.7 Content
- Loading branch information
Showing
30 changed files
with
2,004 additions
and
564 deletions.
There are no files selected for viewing
Binary file not shown.
Binary file not shown.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,120 @@ | ||
################################# | ||
extensions - finger jointed boxes | ||
################################# | ||
CNC controlled laser cutters have enabled the creation of inexpensive finger jointed | ||
boxes constructed from many materials. However, the creation of the patterns to feed | ||
into the laser can be quite time consuming. The finger jointed boxes methods within | ||
the cq_warehouse extensions package allow the creation of these patterns from a simple | ||
solid object. | ||
|
||
.. image:: finger_jointed_boxes.png | ||
:alt: finger_jointed_boxes | ||
|
||
***** | ||
Usage | ||
***** | ||
To create these boxes, one just needs to create the solid object, select edges, and use the | ||
:meth:`~extensions_doc.Workplane.makeFingerJoints` method, as follows: | ||
|
||
.. literalinclude:: ../examples/finger_jointed_boxes.py | ||
:language: python | ||
|
||
.. doctest:: | ||
|
||
>>> polygon_box_assembly.areObjectsValid() | ||
True | ||
>>> polygon_box_assembly.doObjectsIntersect() | ||
False | ||
|
||
|
||
.. warning:: | ||
Not all shapes will result in a valid set of finger jointed box patterns. | ||
|
||
Although the goal of this package is to enable the creation of a finger jointed | ||
box from any object with planar faces, this goal has not been fully achieved. | ||
Carefully inspect the output to ensure it is correct before cutting boxes | ||
to avoid disappointment. | ||
|
||
|
||
The Workplane used to create a finger jointed box must contain a solid object | ||
(actually a Solid or Compound object), and one or more Edges that is to be jointed. | ||
In the above example, all of the edges are selected except for those on the top | ||
of the object which results in an open box. The resulting faces can be further | ||
modified if required. | ||
|
||
Here is an example of finger jointed faces of a simple box: | ||
|
||
.. image:: finger_jointed_box_faces.png | ||
:alt: finger_jointed_box_faces | ||
|
||
Notice how the finger joints on the nearest corner are in a different pattern than | ||
the simple fingers on the other parts of the box? This is done to avoid the | ||
creation of a missing corner. | ||
|
||
Finger joint size is calculated internally such that an integer number of finger joints | ||
are present on each edge - i.e. if the ``targetFingerWidth`` would result in a partial | ||
finger joint, the actual finger joint width will reduced such the number of finger | ||
joints is rounded to an integer. This may result in finger joints on different | ||
edges being different sizes. | ||
|
||
The use of an Assembly is optional but is recommended to aid in the visual validation | ||
of the output. Random colors are assigned to each of the box walls to aid this | ||
validation. Corners are where errors are most likely to appear, either as interference | ||
or as missing corners. | ||
|
||
When working with shapes with non perpendicular faces (i.e. faces that don't meet at | ||
90˚) the depth of the finger joint is calculated to compensate for the angle by either | ||
making the joint extra deep (for angles greater than 90˚) or smaller (for angles less than 90˚). | ||
Unfortunately, not all possible combinations of corner angles have been compensated for | ||
so pay extra care when inspecting these corners. | ||
|
||
The result of the :meth:`~extensions_doc.Workplane.makeFingerJoints` method is a set of | ||
Faces within the Workplane aligned with the original solid object. To store these faces | ||
as DXF files it is necessary to create a workplane oriented in the same way as the face | ||
as shown in the above example. | ||
|
||
The full API is as follows: | ||
|
||
.. py:module:: extensions_doc | ||
:noindex: | ||
|
||
.. automethod:: Workplane.makeFingerJoints | ||
|
||
The ``kerfWidth`` parameter can be used to compensate for the size of the laser cut | ||
thus allowing a path for the laser to the created directly from the face. Check with | ||
the manufacturer to see if this compensation is required. | ||
|
||
********** | ||
Validation | ||
********** | ||
To help validate the finger jointed box two Assembly methods are available: | ||
|
||
* :meth:`~extensions_doc.Assembly.areObjectsValid` | ||
* :meth:`~extensions_doc.Assembly.doObjectsIntersect` | ||
|
||
Checking for intersecting objects within the Assembly (a general purpose method where every | ||
pair of objects within the Assembly - in their given Location - are checked for an intersection) | ||
will identify if there are overlapping finger joints but will not find missing fingers. To check | ||
for missing corners, one can use the ``Volume`` method as follows: | ||
|
||
.. code-block:: python | ||
import cadquery as cq | ||
import cq_warehouse.extensions | ||
simple_box_assembly = Assembly() | ||
simple_box = Workplane("XY").box(100, 80, 50) | ||
simple_box_volume = simple_box.faces(">Z").shell(-5, kind="intersection").val().Volume() | ||
simple_box_faces = ( | ||
simple_box.edges("not >Z").makeFingerJoints( | ||
materialThickness=5, | ||
targetFingerWidth=10, | ||
baseAssembly=simple_box_assembly, | ||
) | ||
) | ||
simple_box_assembly_volume_error = abs( | ||
simple_box_assembly.toCompound().Volume() - simple_box_volume | ||
) | ||
print(f"Is volume correct: {simple_box_assembly_volume_error<1e-5=}") | ||
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,23 @@ | ||
import cadquery as cq | ||
from cq_warehouse.fastener import HexNut, SquareNut | ||
import cq_warehouse.extensions | ||
|
||
hex_nut = HexNut(size="M6-1", fastener_type="iso4033") | ||
square_nut = SquareNut(size="M6-1", fastener_type="din557") | ||
test_assembly = cq.Assembly() | ||
block = ( | ||
cq.Workplane("XY") | ||
.box(50, 50, 10) | ||
.faces(">Z") | ||
.workplane() | ||
.pushPoints([(-12.5, 0)]) | ||
.clearanceHole( | ||
fastener=hex_nut, fit="Loose", captiveNut=True, baseAssembly=test_assembly | ||
) | ||
.pushPoints([(+12.5, 0)]) | ||
.clearanceHole(fastener=square_nut, captiveNut=True, baseAssembly=test_assembly) | ||
) | ||
test_assembly.add(block, color=cq.Color("tan")) | ||
|
||
if "show_object" in locals(): | ||
show_object(test_assembly, name="test_assembly") |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,109 @@ | ||
""" | ||
Extensions Examples | ||
name: extensions_examples.py | ||
by: Gumyr | ||
date: January 10th 2022 | ||
desc: Emboss examples. | ||
license: | ||
Copyright 2022 Gumyr | ||
Licensed under the Apache License, Version 2.0 (the "License"); | ||
you may not use this file except in compliance with the License. | ||
You may obtain a copy of the License at | ||
http://www.apache.org/licenses/LICENSE-2.0 | ||
Unless required by applicable law or agreed to in writing, software | ||
distributed under the License is distributed on an "AS IS" BASIS, | ||
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. | ||
See the License for the specific language governing permissions and | ||
limitations under the License. | ||
""" | ||
import timeit | ||
from enum import Enum, auto | ||
import cadquery as cq | ||
import cq_warehouse.extensions | ||
|
||
# The emboss examples | ||
class Testcase(Enum): | ||
EMBOSS_TEXT = auto() | ||
EMBOSS_WIRE = auto() | ||
|
||
|
||
# A sphere used as a projection target | ||
sphere = cq.Solid.makeSphere(50, angleDegrees1=-90) | ||
|
||
for example in list(Testcase): | ||
|
||
# if example != Testcase.EMBOSS_WIRE: | ||
# continue | ||
|
||
if example == Testcase.EMBOSS_TEXT: | ||
"""Emboss a text string onto a shape""" | ||
|
||
starttime = timeit.default_timer() | ||
|
||
arch_path = ( | ||
cq.Workplane(sphere) | ||
.cut( | ||
cq.Solid.makeCylinder( | ||
80, 100, pnt=cq.Vector(-50, 0, -70), dir=cq.Vector(1, 0, 0) | ||
) | ||
) | ||
.edges("<Z") | ||
.val() | ||
) | ||
projected_text = sphere.embossText( | ||
txt="emboss - 'the quick brown fox jumped over the lazy dog'", | ||
fontsize=14, | ||
font="Serif", | ||
fontPath="/usr/share/fonts/truetype/freefont", | ||
depth=3, | ||
path=arch_path, | ||
) | ||
|
||
print(f"Example #{example} time: {timeit.default_timer() - starttime:0.2f}s") | ||
|
||
if "show_object" in locals(): | ||
show_object(sphere, name="sphere_solid", options={"alpha": 0.8}) | ||
show_object(arch_path, name="arch_path", options={"alpha": 0.8}) | ||
show_object(projected_text, name="embossed_text") | ||
|
||
elif example == Testcase.EMBOSS_WIRE: | ||
"""Emboss a wire and use it to create a feature""" | ||
|
||
starttime = timeit.default_timer() | ||
target_object = cq.Solid.makeCylinder( | ||
50, 100, pnt=cq.Vector(0, 0, -50), dir=cq.Vector(0, 0, 1) | ||
) | ||
path = cq.Workplane(target_object).section().edges().val() | ||
to_emboss_wire = cq.Wire.makeRect( | ||
80, 40, cq.Vector(), cq.Vector(0, 0, 1), cq.Vector(1, 0, 0) | ||
) | ||
embossed_wire = to_emboss_wire.embossToShape( | ||
targetObject=target_object, | ||
surfacePoint=path.positionAt(0), | ||
surfaceXDirection=path.tangentAt(0), | ||
tolerance=0.1, | ||
) | ||
embossed_edges = embossed_wire.Edges() | ||
for i, e in enumerate(to_emboss_wire.Edges()): | ||
target = e.Length() | ||
actual = embossed_edges[i].Length() | ||
print( | ||
f"Edge lengths: target {target}, actual {actual}, difference {abs(target-actual)}" | ||
) | ||
|
||
print(f"Example #{example} time: {timeit.default_timer() - starttime:0.2f}s") | ||
|
||
if "show_object" in locals(): | ||
show_object(target_object, name="target_object", options={"alpha": 0.8}) | ||
show_object(path, name="path") | ||
show_object(to_emboss_wire, name="to_emboss_wire") | ||
show_object(embossed_wire, name="embossed_wire") |
Oops, something went wrong.