-
I'm sweeping a rectangle outline along a helix path, but Here's the code:const r = replicad
const wireSize = 1
const wireGap = 0.5
const innerRadius = 3
const vertTurns = 1
const main = () => {
const shapes = []
const path1 = r.makeHelix(2*(wireSize+wireGap), 2*vertTurns*(wireSize+wireGap), innerRadius)
const outline = r.drawRectangle(1,1).translate(innerRadius,0).sketchOnPlane('XZ')
const swp1 = r.genericSweep(
outline.wire,
r.assembleWire(path1.wires),
{ forceProfileSpineOthogonality: true },
false,
)
shapes.push(swp1)
const swp2 = r.genericSweep(
outline.wire,
r.assembleWire(path1.wires),
{ forceProfileSpineOthogonality: false },
false,
).rotate(180, [0,0,0], [0,0,1])
shapes.push(swp2)
return shapes
} and here is what it generates: What I would like instead is something like this: Any ideas how to fix this? |
Beta Was this translation helpful? Give feedback.
Replies: 12 comments 10 replies
-
I am trying to get it right, but my first idea is that it has to do with the |
Beta Was this translation helpful? Give feedback.
-
Beta Was this translation helpful? Give feedback.
-
I think the option you are looking for is the "frenet" one. Is this what you want to do? P.S. I am not sure why or how, but this is what I have found out with playing with the different options opencascade offers. |
Beta Was this translation helpful? Give feedback.
-
An interesting difference between these solutions is that with the @sgenoud: Do you have any idea why the wire created with const path1 = makeHelix(2*(wireSize+wireGap), 2*vertTurns*(wireSize+wireGap), innerRadius)
const path2 = assembleWire(path1.wires)
const swp3 = path1.sweepSketch((plane,origin)=>sketchRectangle(1,1,{plane,origin})) In the code above using |
Beta Was this translation helpful? Give feedback.
-
Wow, you are both amazing! I would like to mark every reply as "the answer" because every one has valuable gems in it. |
Beta Was this translation helpful? Give feedback.
-
Quick question that I don't really want to open up a completely separate Issue or Discussion topic for... Is there a way to run your workbench within a VSCode/Zed panel so that a save of the code on the left immediately updates the viewer on the right? Something along the lines of glsl-viewer? I love the official workbench (although I would love a dark mode for the viewer side even more 😄 ), but keep wanting to hit "save" for fear of losing my design, and I would like to work out of a repo so I can put the design under git version control. I saw this discussion but it was not obvious to me how to start the local visualizer. I also found this and tried it out, but again couldn't find the visualizer, sorry for the stupid quesitons. EDIT: Ah, sorry, I think I found it! https://replicad.xyz/docs/tutorial-overview/using-the-workbench#working-with-local-files |
Beta Was this translation helpful? Give feedback.
-
@sgenoud - I'm noticing that the start and end faces of the pipe mechanism above don't have the same normal as the original wire outline, and it seems to get worse for smaller radii helices. I've attached a screenshot and highlighted in red what I'm talking about. (Oh, I forgot to mention that this view is from clicking on the 'Z' in the gizmo on the lower right, so this should be an orthogonal 'top' view.) |
Beta Was this translation helpful? Give feedback.
-
|
Beta Was this translation helpful? Give feedback.
-
Just for reference, a few years back I had created a viewer gizmo in three.js where if you click on any of the labeled faces, you get an orthogonal view, but if you click on a corner, you get a perspective view, very similar to Onshape: |
Beta Was this translation helpful? Give feedback.
-
Update: I exported the model as STL and imported into Blender and verified that the faces are indeed not facing the same direction as the original rectangle outline. I'll go edit my earlier comments to reduce confusion, hopefully. |
Beta Was this translation helpful? Give feedback.
-
For reference, here is my design so far: const r = replicad
const fuseAll = false
// tweakable parameters:
const wireWidth = 1
const wireGap = 0.5
const innerRadius = 3
const vertTurns = 1 // 15
const maxDiameter = 38
const connectorThickness = 1
// calculated constants:
const numPairs = Math.floor((maxDiameter - 2*innerRadius - 2*connectorThickness) / (2 * (wireWidth + wireGap)))
const deltaY = (numPairs > 1) ? (wireWidth + wireGap) / (numPairs - 1) : 0
const coilPair = (i) => {
const zOffset = (i - 1) * deltaY // - wireWidth/2
const radius = innerRadius + wireWidth/2 + i*(wireWidth + wireGap)
const path1 = r.makeHelix(2*(wireWidth+wireGap), 2*vertTurns*(wireWidth+wireGap), radius)
const outline = r.drawRectangle(1,1).translate(radius,0).sketchOnPlane('XZ')
const makeCoil = () => r.genericSweep(
outline.wire,
r.assembleWire(path1.wires),
{ forceProfileSpineOthogonality: true, frenet:true },
false,
).translateZ(zOffset)
const swp1 = makeCoil()
const swp2 = makeCoil().rotate(180, [0,0,0], [0,0,1])
return [swp1, swp2]
}
// From: https://en.wikipedia.org/wiki/Sagitta_(geometry)#Formulas
const sagittaRadius = (chordLength, arcHeight) => {
const [l, s] = [chordLength, arcHeight]
return s/2 + l*l/(8*s)
}
const sagittaChordLength = (radius, arcHeight) => {
const [r, s] = [radius, arcHeight]
return 2 * Math.sqrt(2*r*s - s*s)
}
const sagittaArcHeight = (chordLength, radius) => {
const [l, r] = [chordLength, radius]
return r - Math.sqrt(r*r - l*l/4)
}
const vec2dLength = (p) => Math.sqrt(p[0]*p[0] + p[1]*p[1])
const lineLength = (p1, p2) => vec2dLength([p1[0]-p2[0], p1[1]-p2[1]])
const connectorRailPair = (i) => {
// const zOffset = ((numPairs - i) % numPairs) * deltaY - wireWidth/2
const railHeight = wireWidth + vertTurns * 2*(wireWidth + wireGap) + (numPairs-1)*deltaY
const railAngleDelta = Math.PI / numPairs
const railInnerRadius = innerRadius + numPairs*(wireWidth + wireGap)
const innerDeltaTheta = Math.asin(wireGap / (2*railInnerRadius))
const innerStartPoint = [railInnerRadius,0]
const innerEndAngle = railAngleDelta - 2*innerDeltaTheta
const innerEndPoint = [railInnerRadius*Math.cos(innerEndAngle),railInnerRadius*Math.sin(innerEndAngle)]
const innerArcHeight = sagittaArcHeight(lineLength(innerStartPoint,innerEndPoint), railInnerRadius)
const railOuterRadius = railInnerRadius + wireWidth
const outerDeltaTheta = Math.asin(wireGap / (2*railOuterRadius))
const outerStartPoint = [railOuterRadius,0]
const outerEndAngle = railAngleDelta - 2*outerDeltaTheta
const outerEndPoint = [railOuterRadius*Math.cos(outerEndAngle),railOuterRadius*Math.sin(outerEndAngle)]
const outerArcHeight = sagittaArcHeight(lineLength(outerStartPoint,outerEndPoint), railOuterRadius)
const makeRail = (rotation) => r.draw()
.movePointerTo(outerStartPoint)
.sagittaArcTo(outerEndPoint, -outerArcHeight)
.lineTo(innerEndPoint)
.sagittaArcTo(innerStartPoint, innerArcHeight)
.close()
.sketchOnPlane()
.extrude(railHeight)
.translateZ(-wireWidth/2)
.rotate(rotation)
const rail1 = makeRail(i*180/numPairs)
const rail2 = makeRail(180 + i*180/numPairs)
return [rail1, rail2]
}
const main = () => {
const shapes = []
for (let i = 0; i < numPairs; i++) {
shapes.push(...coilPair(i))
shapes.push(...connectorRailPair(i))
}
if (fuseAll) {
let final = shapes[0]
for (const shape of shapes.slice(1)) { final = final.fuse(shape) }
return final
}
return shapes
} |
Beta Was this translation helpful? Give feedback.
-
I'm realizing now that I don't actually want a "frenet" helix. I actually had to do this before for Blackjack: https://github.com/gmlewis/blackjack/blob/gmlewis-master/blackjack_lua/run/helix.lua |
Beta Was this translation helpful? Give feedback.
I think the option you are looking for is the "frenet" one. Is this what you want to do?
P.S. I am not sure why or how, but this is what I have found out with playing with the different options opencascade offers.