Skip to content

Commit

Permalink
add experimental option for gaps between fingers and part
Browse files Browse the repository at this point in the history
  • Loading branch information
FlorianPommerening committed Aug 7, 2021
1 parent 1370c5a commit f6f4fc3
Show file tree
Hide file tree
Showing 6 changed files with 70 additions and 21 deletions.
2 changes: 1 addition & 1 deletion FingerJoints.manifest
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,7 @@
"description": {
"": "An add-in for making finger joints."
},
"version": "1.1.0",
"version": "1.2.0",
"runOnStartup": true,
"supportedOS": "windows|mac",
"editEnabled": true
Expand Down
9 changes: 8 additions & 1 deletion README.md
Original file line number Diff line number Diff line change
Expand Up @@ -40,10 +40,17 @@ Finally, instead of fixing the number of fingers, we might want to have both fin


Starting with version 1.1 the add-in can also add a small gap between the fingers and notches for an easier fit
of the produces parts.
of the produced parts.

![Gaps between notches and fingers](resources/doc/gaps.png)

An experimental option of adding a gap between the joint and the parts was added in version 1.2.
Currently, the gap size will not always match the entered value and might be different on both parts
if their overlap is not a square. Use at your own risk and experiment with different settings
for a useful distance.

![Gaps between fingers and parts](resources/doc/gaps2.png)

Of course, the objects you join do not have to be rectangular and can overlap in multiple places.

![General objects can be joined](resources/doc/usage5.png)
Expand Down
68 changes: 50 additions & 18 deletions geometry.py
Original file line number Diff line number Diff line change
Expand Up @@ -16,27 +16,44 @@ def findOrthogonalUnitVectors(z):


class CoordinateSystem(object):
def __init__(self, direction):
def __init__(self, direction, body):
"""Creates a coordinate system where the z axis is in the given direction
and the bounding box of the given body is centered around this axis"""
# Define the axes of the coordinate system.
if isinstance(direction, adsk.fusion.BRepEdge):
self.origin = direction.startVertex.geometry
self.direction = self.origin.vectorTo(direction.endVertex.geometry)
origin = direction.startVertex.geometry
zAxis = origin.vectorTo(direction.endVertex.geometry)
else:
assert(isinstance(direction, adsk.fusion.SketchLine))
self.origin = direction.startSketchPoint.worldGeometry
self.direction = self.origin.vectorTo(direction.endSketchPoint.worldGeometry)
self.direction.normalize()
origin = direction.startSketchPoint.worldGeometry
zAxis = origin.vectorTo(direction.endSketchPoint.worldGeometry)
zAxis.normalize()
xAxis, yAxis = findOrthogonalUnitVectors(zAxis)

xAxis, yAxis = findOrthogonalUnitVectors(self.direction)
self.transform = adsk.core.Matrix3D.create()
self.transform.setWithCoordinateSystem(
self.origin,
xAxis,
yAxis,
self.direction)
# Get a preliminary transformation (axis are correct but origin will be shifted later).
preliminaryTransform = adsk.core.Matrix3D.create()
preliminaryTransform.setWithCoordinateSystem(origin, xAxis, yAxis, zAxis)
inversePreliminaryTransform = preliminaryTransform.copy()
inversePreliminaryTransform.invert()

# Find center of body's bounding box in local coordinates.
temporaryBRepManager = adsk.fusion.TemporaryBRepManager.get()
body_local = temporaryBRepManager.copy(body)
temporaryBRepManager.transform(body_local, inversePreliminaryTransform)
bb = body_local.boundingBox
minx, miny, minz = bb.minPoint.asArray()
maxx, maxy, maxz = bb.maxPoint.asArray()
cx = (minx + maxx) / 2
cy = (miny + maxy) / 2

# Create the coordinate system with the correct origin
origin = adsk.core.Point3D.create(cx, cy, minz)
origin.transformBy(preliminaryTransform)
self.transform = adsk.core.Matrix3D.create()
self.transform.setWithCoordinateSystem(origin, xAxis, yAxis, zAxis)
self.inverseTransform = self.transform.copy()
self.inverseTransform.invert()

def transformToLocalCoordinates(self, body):
temporaryBRepManager = adsk.fusion.TemporaryBRepManager.get()
temporaryBRepManager.transform(body, self.inverseTransform)
Expand All @@ -53,7 +70,7 @@ def createBox(x, y, z, length, width, height):
return adsk.core.OrientedBoundingBox3D.create(centerPoint, lengthDirection, widthDirection, length, width, height)


def createToolBody(body, slices, debug=False):
def createToolBody(body, slices, gapToPart, debug=False):
bb = body.boundingBox
minx, miny, minz = bb.minPoint.asArray()
maxx, maxy, maxz = bb.maxPoint.asArray()
Expand Down Expand Up @@ -87,6 +104,21 @@ def createToolBody(body, slices, debug=False):
feature.finishEdit()

temporaryBRepManager.booleanOperation(targetBody, body, adsk.fusion.BooleanTypes.IntersectionBooleanType)

# Scale up tool body, so its length and width are increased by the gap we want to leave to the other part.
# The correct way to do so would be to compute an offset. Scaling works if the intersection is a square but
# will otherwise have a too large gap on one side. Note that we have to scale in x and y direction with the
# same factor because the axes may not be aligned with the axis of the intersection.
scaleFactorX = (length + 2*gapToPart) / length
scaleFactorY = (width + 2*gapToPart) / width
scaleFactor = max(scaleFactorX, scaleFactorY)
transform = adsk.core.Matrix3D.create()
transform.setWithArray([scaleFactor, 0, 0, 0,
0, scaleFactor, 0, 0,
0, 0, 1, 0,
0, 0, 0, 1])
temporaryBRepManager.transform(targetBody, transform)

return targetBody


Expand All @@ -98,8 +130,8 @@ def createBodyFromOverlap(body0, body1):


def createToolBodies(body0, body1, direction, options):
coordinateSystem = CoordinateSystem(direction)
overlap = createBodyFromOverlap(body0, body1)
coordinateSystem = CoordinateSystem(direction, overlap)
coordinateSystem.transformToLocalCoordinates(overlap)
# TODO: look at MeasureManager.getOrientedBoundingBox to see if this can be simplified, probably with direction.geometry/worldGeometry

Expand All @@ -111,9 +143,9 @@ def createToolBodies(body0, body1, direction, options):
if fingerDimensions is None or notchDimensions is None:
return False

fingerToolBody = createToolBody(overlap, fingerDimensions)
fingerToolBody = createToolBody(overlap, fingerDimensions, options.gapToPart)
coordinateSystem.transformToGlobalCoordinates(fingerToolBody)
notchToolBody = createToolBody(overlap, notchDimensions)
notchToolBody = createToolBody(overlap, notchDimensions, options.gapToPart)
coordinateSystem.transformToGlobalCoordinates(notchToolBody)
return fingerToolBody, notchToolBody

Expand Down
3 changes: 3 additions & 0 deletions options.py
Original file line number Diff line number Diff line change
Expand Up @@ -33,6 +33,7 @@ def __init__(self):
self.minNotchSize = 2
self.fixedNumFingers = 3
self.gap = 0
self.gapToPart = 0
self.isPreviewEnabled = True
self.readDefaults()

Expand All @@ -47,6 +48,7 @@ def writeDefaults(self):
'minNotchSize': self.minNotchSize,
'fixedNumFingers': self.fixedNumFingers,
'gap': self.gap,
'gapToPart': self.gapToPart,
'isPreviewEnabled': self.isPreviewEnabled,
}
with open(self.DEFAULTS_FILENAME, 'w', encoding='UTF-8') as json_file:
Expand All @@ -70,5 +72,6 @@ def readDefaults(self):
self.minNotchSize = defaultData.get('minNotchSize', self.minNotchSize)
self.fixedNumFingers = defaultData.get('fixedNumFingers', self.fixedNumFingers)
self.gap = defaultData.get('gap', self.gap)
self.gapToPart = defaultData.get('gapToPart', self.gapToPart)
self.isPreviewEnabled = defaultData.get('isPreviewEnabled', self.isPreviewEnabled)

Binary file added resources/doc/gaps2.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
9 changes: 8 additions & 1 deletion ui.py
Original file line number Diff line number Diff line change
Expand Up @@ -83,7 +83,10 @@ def __init__(self, inputs, defaults):
self._inputMinFingerSize = inputs.addValueInput('inputMinFingerSize', 'Minimal Finger Size', defaultUnit, defaultMinFingerSize)

defaultGap = adsk.core.ValueInput.createByReal(defaults.gap)
self._inputGap = inputs.addValueInput('inputGap', 'Gap', defaultUnit, defaultGap)
self._inputGap = inputs.addValueInput('inputGap', 'Gap between fingers', defaultUnit, defaultGap)

defaultGapToPart = adsk.core.ValueInput.createByReal(defaults.gapToPart)
self._inputGapToPart = inputs.addValueInput('inputGapToPart', 'Gap to part\n(experimental)', defaultUnit, defaultGapToPart)

self._inputIsPreviewEnabled = inputs.addBoolValueInput('inputIsPreviewEnabled', 'Show Preview', True, '', defaults.isPreviewEnabled)

Expand Down Expand Up @@ -146,6 +149,9 @@ def getMinFingerSize(self):
def getGap(self):
return self._getDistanceInputValue(self._inputGap)

def getGapToPart(self):
return self._getDistanceInputValue(self._inputGapToPart)

def isPreviewEnabled(self):
return self._inputIsPreviewEnabled.value

Expand Down Expand Up @@ -186,6 +192,7 @@ def setRelevantOptions(self, options):
if self.getMinFingerSize() is not None:
options.minFingerSize = self.getMinFingerSize()
options.gap = self.getGap()
options.gapToPart = self.getGapToPart()
options.isPreviewEnabled = self.isPreviewEnabled()

def areInputsValid(self):
Expand Down

0 comments on commit f6f4fc3

Please sign in to comment.