I'm trying to model a piano black key, which looks like this
As you can see, it has a variable fillet. which AFAICT CQ does not support (despite Google search AI claiming the contrary and providing code that does not work ROTFL)
The best I can do with "traditional" CQ tools is
key_thickness = 10
key_len = 100
key_height = 24
keytop = Workplane("XY").box(key_len, key_thickness, key_height)
cut_front = Workplane("XY").box(key_len, 2 * key_thickness, 2 * key_height)
cut_front = cut_front.rotate((0, 0, 0), (0, 1, 0), 75)
cut_front = cut_front.translate((0.5 * key_len, 0, 0))
keytop = keytop.cut(cut_front)
cut_corner = Workplane("XY").box(key_height, key_height, key_height)
cut_corner = cut_corner.rotate((0, 0, 0), (0, 1, 0), 45)
cut_corner = cut_corner.rotate((0, 0, 0), (0, 0, 1), 45)
cut_corner = cut_corner.translate((1.3 * key_height, 0.5 * key_thickness, 0.8 * key_height))
keytop = keytop.cut(cut_corner)
cut_corner = Workplane("XY").box(key_height, key_height, key_height)
cut_corner = cut_corner.rotate((0, 0, 0), (0, 1, 0), 45)
cut_corner = cut_corner.rotate((0, 0, 0), (0, 0, 1), -45)
cut_corner = cut_corner.translate((1.3 * key_height, -0.5 * key_thickness, 0.8 * key_height))
keytop = keytop.cut(cut_corner)
keytop = keytop.fillet(2)
cut_bottom = Workplane("XY").box(2 * key_len, 2 * key_thickness, key_height)
cut_bottom = cut_bottom.translate((0, 0, - 0.9 * key_height))
keytop = keytop.cut(cut_bottom)
cut_back = Workplane("XY").box(key_len, 2 * key_thickness, 2 * key_height)
cut_back = cut_back.translate((- 0.9 * key_len, 0, 0))
keytop = keytop.cut(cut_back)
which is completely ugly and not at all similar to what I want/need. Jumping through many hoops, I think I can make design it doing something like the following
profile = Workplane("XY")
profile = profile.workplane(offset= -key_height)
fillet_r = 10
a = key_len / 2 - fillet_r
b = key_thickness / 2 - fillet_r
#profile = profile.radiusArc((a + fillet_r, -key_thickness), 10)
profile = profile.moveTo(+a + fillet_r, +b)
profile = profile.lineTo(+a + fillet_r, -b)
#profile = profile.lineTo(+a, -b + fillet_r)
profile = profile.radiusArc((+a, -b + fillet_r), -fillet_r)
profile = profile.lineTo(-a, -b + fillet_r)
#profile = profile.lineTo(-a - fillet_r, -b)
profile = profile.radiusArc((-a - fillet_r, -b), -fillet_r)
profile = profile.lineTo(-a - fillet_r, +b)
#profile = profile.lineTo(-a, b - fillet_r)
profile = profile.radiusArc((-a, b - fillet_r), -fillet_r)
profile = profile.lineTo(+a, b - fillet_r)
profile = profile.radiusArc((+a + fillet_r, +b), -fillet_r)
profile = profile.close()
old_fillet_r = fillet_r
fillet_r = key_thickness / 2 - 0.0001
a = key_len * 0.8 / 2 - fillet_r
b = key_thickness / 2 - fillet_r
profile = profile.workplane(offset= key_height)
profile = profile.moveTo(+a + fillet_r, +b)
profile = profile.lineTo(+a + fillet_r, -b)
#profile = profile.lineTo(+a, -b + fillet_r)
profile = profile.radiusArc((+a, -b + fillet_r), -fillet_r)
profile = profile.lineTo(-a, -b + fillet_r)
#profile = profile.lineTo(-a - fillet_r, -b)
profile = profile.radiusArc((-a - fillet_r, -b), -fillet_r)
profile = profile.lineTo(-a - fillet_r, +b)
#profile = profile.lineTo(-a, b - fillet_r)
profile = profile.radiusArc((-a, b - fillet_r), -fillet_r)
profile = profile.lineTo(+a, b - fillet_r)
profile = profile.radiusArc((+a + fillet_r, +b), -fillet_r)
profile = profile.close()
keytop = profile.loft(ruled= False, combine= False)
At the moment this is not correct, but I see that all the tools that are needed to make it work are there. I just need to do the math correctly (in dedicated functions) and with multiple levels (as I learned in this chat from the lofted log spiral).
Is that the way to go, or is there an easier/simpler/more robust approach?