Jump to content

The "Chess problem" On the WAN show 2023-09-08

Nereth

I apologize if this is the wrong place. I'm new here. Made this account just for this question.

In the WAN Show just gone past, at the 3:12:08 timestamp, L.L.D. were talking about a shape ("J shape"?) that they could easily machine, but couldn't easily model in CAD, that had been through several engineers that had not been able to solve it.

This just gets to me, both my curiosity and a bit of my competitiveness since I work with these programs... Can you guys take a photo or otherwise describe the shape you are after so the rest of us can have a go at modelling it? I can't even imagine what it might be from the description I heard during the show!

Who knows, maybe it will be of use to you in documenting your parts, if you haven't solved it yet 馃檪

Link to comment
Share on other sites

Link to post
Share on other sites

Time-code for anyone who's curious about what OP's talking about (3:04:11)

| Remember to mark Solutions! | Quote Posts if you want a Reply! |
| Tell us everything! Budget? Currency? Country? Retailers? | Help us help You! |

Link to comment
Share on other sites

Link to post
Share on other sites

I was also intrigued by this problem and since I'm a Product Design Engineer and a Solidworks user, I thought I would try to solve it.

No idea if they already did solve it, but here is my suggested solution. I made a quick video to demonstrate.

I hope that Dan will find this and that he find some joy in seeing someone else take interest in this technical challenge 馃槉 if anyone knows a way to get this too him that would be much appreciated聽

Link to comment
Share on other sites

Link to post
Share on other sites

I made a follow up video to answer some of the comments like a Q&A

Link to comment
Share on other sites

Link to post
Share on other sites

I did it in CADQuery, figured may as well clean it up a little and share it here both as an example solution and as a "hey look at CADQuery it's awesome!" thing:

import cadquery as cq
from math import radians, sqrt, cos, sin

## Parameters
export = True       # Export to STEP and STL

sleeve_rad = 9      # Radius of sleeve
wall_thickness = 1  # Width of sleeve wall

slot_angle = 70     # Angle subtended by the arc (i.e. angle between legs) (degrees)
unlock_length = 11  # Length of unlocked side of slot
lock_length = 1     # Length of locked side of slot
slot_offset = 1     # Distance between slot cutout and edge of sleeve
slot_width = 3      # Width of slot cutout

interp_points = 100 # Number of points to use along curve when building the J hook spline

## Calculated
arc_height = sleeve_rad*radians(slot_angle)/2    # Radius of hook of arc
sleeve_height = (max(unlock_length, lock_length) # Total length of sleeve
                 + arc_height
                 + 2*slot_offset
                 + slot_width)

## Helper Functions
def makeFullCurve(wp, r, h, a, N):
    # Create a wire defining the wrapped path of the hook of the J
    #   wp: Context workplane
    #    r: radius to wrap
    #    h: Max height of arc
    #    a: Slot angle (degrees)
    #    N: Number of points to use for spline
    
    # Function defining the curve of the arc
    def curveFunc(t):
        theta = t*radians(a)
        x = r*cos(theta)
        y = r*sin(theta)
        z = h*sqrt(1 - (2*t - 1)**2)
        return (x, y, z)
    
    arc = cq.Edge.makeSpline(
        wp._toVectors(
            (curveFunc(n/N) for n in range(N + 1)),
            includeCurrent = False),
        tangents = [
            cq.Vector(0, 0, 1),
            cq.Vector(0, 0, -1)],
        scale = False)
    
    return (cq.Workplane().copyWorkplane(wp)
        .add(arc)
        .toPending()
        .consolidateWires()
        .wires())

## Plugins
def _showDebugDynamic(self):
    # Used to show context solid with current working features overlayed for debug
    if self.solids().size() > 0:
        show_object(self)
    else:
        try:
            base = self.findSolid()
            show_object(base)
            debug(self)
        except:
            show_object(self)
    return self
cq.Workplane.showDebugDynamic = _showDebugDynamic

def _isolateStartPlugin(self, id=0):
    # Used to set a checkpoint for isolate-combine workflow
    return self.solids().tag(f'isolated_unit_plugin_checkpoint_{id}').end()
def _isolateUnionPlugin(self, id=0):
    # Used to combine current solid with checkpoint solid into a new solid
    return self.union(self.solids(tag=f'isolated_unit_plugin_checkpoint_{id}'))
def _isolateCutPlugin(self, id=0):
    # Used to cut current solid from checkpoint solid
    return self.solids(tag=f'isolated_unit_plugin_checkpoint_{id}').cut(self)
cq.Workplane.saveCurrent = _isolateStartPlugin
cq.Workplane.recombine = _isolateUnionPlugin
cq.Workplane.recombineCut = _isolateCutPlugin

def _makeArcCutter(self, r0, r1, h, a, w, ul, ll, N, combine=True, fudge=0.01):
    # Create the solid needed to cut the J
    #      self: Workplane object
    #        r0: Inner radius
    #        r1: Outer radius
    #         h: Max height of arc
    #         a: Angle between legs of slot (degrees)
    #         w: Width of slot cut
    #        ul: Unlock leg length
    #        ll: Lock leg length
    #         N: Number of points to use for spline
    #   combine: Whether to combine with the parent solid
    #     fudge: How far to go past tangent points to prevent errors
    
    sagitta = r0 - sqrt(r0**2 - (w**2)/4)                # Sagitta of arc made with cutter and inner wall, used to determine cutter depth
    path_arc = makeFullCurve(self, r1, h, a, N)          # Path along which to sweep cutter cross-section
    aux_arc = makeFullCurve(self, r0 - sagitta, h, a, N) # Secondary path to constrain the cross-section's orientation along sweep
    cutter_depth = abs(r1 - r0) + sagitta + 2*fudge      # Depth of cutter cross-section
    
    return (self
        ## Arc
        ##   Make a rectangle for the cutter 
        ##   cross-section and sweep it along the
        ##   curve to make the hook of the J.
        .center((r0 + r1 - sagitta)/2, 0)
        .rect(cutter_depth, w)
        .sweep(path_arc,
             auxSpine = aux_arc,
             combine = combine)
        
        ## Unlock Slot
        ##   Select the unlock-side bottom face,
        ##   make a wire from the face's edges,
        ##   then extrude the straight slot.
        .saveCurrent("pre_unlock_slot")
        .faces("-Z").faces(">X")
        .tag("slot_base")
        .workplane(centerOption="CenterOfBoundBox")
        .faces(tag="slot_base")
        .edges()
        .toPending()
        .consolidateWires()
        .extrude(ul, combine = False)
        
        ## Unlock Slot Cap
        ##   Cap the slot with a half-circle
        ##   (Cadquery doesn't like it when you
        ##   try to do a total fillet, so I just
        ##   manually make the semicircle)
        .faces("-Z")
        .workplane()
        .transformed(
            offset = (cutter_depth/2, -w/2, 0),
            rotate = (0, -90, 0))
        .sagittaArc((0, w), -w/2)
        .close()
        .extrude(cutter_depth)
        .recombine("pre_unlock_slot")
   
        ## Lock Slot
        ##   Do the same as the unlock-side slot
        ##   but for the lock side.
        .saveCurrent("pre_lock_slot")
        .faces("-Z").faces(f'>({cos(radians(a)):f}, {sin(radians(a)):f}, 0)')
        .tag("lock_base")
        .workplane(centerOption="CenterOfBoundBox")
        .faces(tag="lock_base")
        .edges()
        .toPending()
        .consolidateWires()
        .extrude(ll, combine = False)
        
        ## Lock Slot Cap
        ##   Do the same as the unlock-side cap
        ##   but for the lock side.
        .faces("-Z")
        .workplane()
        .transformed(rotate = (0, 0, -a))
        .transformed(
            offset = (cutter_depth/2, -w/2, 0),
            rotate = (0, -90, 0))
        .sagittaArc((0, w), -w/2)
        .close()
        .extrude(cutter_depth)
        .recombine("pre_lock_slot"))
cq.Workplane.makeArcCutter = _makeArcCutter

## Constructed
sleeve = (cq.Workplane("XY")
    ## Base
    ##   Extrude a hollow cylinder with open ends.
    .circle(sleeve_rad)
    .circle(sleeve_rad - wall_thickness)
    .extrude(sleeve_height)
    
    ## Slot Arc Cut
    ##   Make a cutter and use it to cut the J.
    .saveCurrent("body_pre_cut")
    .faces("+Z")
    .workplane(offset = unlock_length + slot_width/2 + slot_offset - sleeve_height)
    .makeArcCutter(
        sleeve_rad - wall_thickness,
        sleeve_rad,
        arc_height,
        slot_angle,
        slot_width,
        unlock_length,
        lock_length,
        interp_points,
        combine=False)
    .recombineCut("body_pre_cut")
    
    ## Show Object
    ##   Render solid in preview window (if using CQ-Editor),
    ##   and if there are pending wires, selected faces,
    ##   or other in-progress features, show them as
    ##   debug objects.
    .showDebugDynamic())

if export:
    ## If enabled, automatically export to both STEP and STL.
    cq.exporters.export(sleeve, "bolt_action_sleeve.step")
    cq.exporters.export(sleeve, "bolt_action_sleeve.stl")

image.thumb.png.f270ec3f40d50a913c8e93cd99f12b03.png

I went through a couple approaches, best one ended up being to directly define the path of the cutter and sweep a cross-section along it. CADQuery lets you do that pretty easily, so it ended up being a --relatively-- simple script. It's also fully parametric, so you can play around with the dimensions and whatnot if you want. I recommend CQ-Editor.

While you can define the shape exactly mathematically, I don't think most BREP (boundary representation) standards let you define surfaces directly as functions, so most CAD software can't technically represent the shape exactly. This isn't really that significant though, since you can use splines to approximate the curve. The difference between a spline through points on the curve and a direct realization of the mathematical curve, especially for simple shapes like this, ends up being purely academic.

You could probably define it exactly with FREP (functional representation), since its whole deal is operations on volumes. You can't represent any curve exactly in OpenSCAD though, and most other FREP tools are even more niche and clunky. I might take a stab at Curv, I think that lets you do exact representation.

"Do as聽I say, not as I do."

-Because you actually care if it makes sense.

Link to comment
Share on other sites

Link to post
Share on other sites

Soo ... has Dan and the Engineering people at LMG see this thread and watch JamesdB's videos on this?聽 Might be worth a bit of an update on WAN?

Link to comment
Share on other sites

Link to post
Share on other sites

sorry replying this late but I don't usually聽watch the wan show
all you just need is a good sweep聽

it is not that hard
also you should use wrap only to get the good trajectory for the sweep

Link to comment
Share on other sites

Link to post
Share on other sites

Create an account or sign in to comment

You need to be a member in order to leave a comment

Create an account

Sign up for a new account in our community. It's easy!

Register a new account

Sign in

Already have an account? Sign in here.

Sign In Now