sgenoud / replicad Goto Github PK
View Code? Open in Web Editor NEWThe library to build browser based 3D models with code.
Home Page: https://replicad.xyz
License: MIT License
The library to build browser based 3D models with code.
Home Page: https://replicad.xyz
License: MIT License
Code below throws a "Bug in blueprint fusing algorigthm" exception.
First two triangles share one vertex, so fuse2D returns Blueprints
. Fusing Blueprints
with a triangle that overlaps only one of the blueprints raises an error.
const triangles = [
[
[8, 27],
[7, 35],
[11, 15],
],
[
[11, 15],
[0, 15],
[3, 17],
],
[
[11, 15],
[3, 17],
[4, 18],
],
]
const colors = ['red', 'green', 'blue']
function main({BlueprintSketcher, fuse2D}) {
const blueprints = triangles.map(([a, b, c]) =>
new BlueprintSketcher().movePointerTo(a).lineTo(b).lineTo(c).close(),
)
// return blueprints.map((b,i) => ({shape:b.sketchOnPlane('XY').extrude(1), color: colors[i]}))
const fused = blueprints.reduce(
(acc, bp) => fuse2D(acc, bp),
new BlueprintSketcher().hLine(1e-5).vLine(1e-5).close(),
)
return fused.sketchOnPlane('XY').extrude(1)
}
(a note for a separate question: all triangles here are in CW order and that works as expected, but if I change them into CCW - fuse gets confused about inside and outside of a blueprint. At the same time sketchRectangle
works in CCW.
Either I am missing something obvious, or there's a difference in how sketches and blueprints work?
one way to test CCW triangles is to change the lambda inside map: triangles.map(([a, **c**, b])
)
As it is now, if the finder related to a fillet (or chamfer) operation does not find any edge the operation crashes with a Kernel error. We should check that condition and return a better message
I just uploaded JS / WASM bindings for SolveSpace in my PR.
This might be fun to play with over here?
solvespace/solvespace#1343 (comment)
The API will probably change a bit before it gets merged, but i'd be interested to hear your thought ;)
I'm new to the library and no very javascript savvy, so this might be a user error.
In the share app it seems to space all the models out.
https://studio.replicad.xyz/share/https%3A%2F%2Fgist.githubusercontent.com%2Fjoeynelson%2Ff4b6915c720cab12c85c966021348257%2Fraw%2Ff44a809bc21fde4298334994bf285c851bf9a17b%2Fgistfile1.txt
Hello,
very nice application, thank you for sharing!
Do you think it would be possible to add a visual scripting (nodes) capability ? At least for basic nodes/features, in order to show how to proceed to complete.
Thank you
When I first see a 3D view I can control the rotation fairly intuitively. But if I click on an axis (e.g. click on the red "X" in the lower right hand corner) the rotation is pretty messed-up. To take the example of the "X", I cannot rotate the view back to the default one.
It's also rather annoying that when I click on "X" then Y points up instead of Z, and if I click on "Y" Z points down. That may be mathematically "normal" but we're looking at a 3D model placed on the XY plane here...
For some reason, currently trying to save from replicad.xyz just doesn't cause anything to download. Devtools console shows some sort of error:
Most likely a very basic issue but an issue nonetheless :)
My rcad file may be a culprit but I'm doubtful:
/home/lee/Code/lee/Kindle-Scribe-Case/main.rcad:1.1,58.1
const extrusion = {
width: 0.6,
};
const base = {
dim: [196, 230],
height: 5.8,
};
const cover = {
dim: [base.dim[0] + extrusion.width * 2, base.dim[1] + extrusion.width * 2]
};
const foot = {
offsets: [
[base.dim[0] / 2 + -18, base.dim[1] / -2 + 12],
[base.dim[0] / -2 + 18, base.dim[1] / -2 + 12],
[base.dim[0] / 2 + -18, base.dim[1] / 2 + -12],
[base.dim[0] / -2 + 18, base.dim[1] / 2 + -12]
],
diameter: 3.8,
height: 7.0,
};
const main = ({ drawRoundedRectangle, drawCircle, Drawing }, {}) => {
const drawingBase = drawRoundedRectangle(...base.dim)
const drawingCover = drawRoundedRectangle(...cover.dim)
const drawingFoot = drawCircle(foot.diameter/2.0);
const shapeCover = new Drawing()
.fuse(drawingCover)
.cut(drawingFoot.translate(foot.offsets[0]))
.cut(drawingFoot.translate(foot.offsets[1]))
.cut(drawingFoot.translate(foot.offsets[2]))
.cut(drawingFoot.translate(foot.offsets[3]))
.sketchOnPlane("XY")
.extrude(base.height + foot.height / 2.0)
.fillet(6.5, e => e.inDirection("Z"))
.fillet(2.0, e => e
.inBox([cover.dim[0] / -2, cover.dim[1] / -2, 0], [cover.dim[0] / 2, cover.dim[1] / 2, base.height / 2])
.not(f => f.ofCurveType("ELLIPSE"))
)
.fillet(1.5, e => e.inBox([cover.dim[0] / -2, cover.dim[1] / -2, base.height], [cover.dim[0] / 2, cover.dim[1] / 2, base.height * 2]));
const shapeBase = drawingBase
.sketchOnPlane("XY")
.extrude(base.height)
.fillet(6.5, e => e.inDirection("Z"))
.fillet(2.0, e => e
.inBox([base.dim[0] / -2, base.dim[1] / -2, 0], [base.dim[0] / 2, base.dim[1] / 2, base.height / 2])
.not(f => f.ofCurveType("ELLIPSE"))
)
.fillet(1.5, e => e.inBox([cover.dim[0] / -2, cover.dim[1] / -2, base.height / 2], [cover.dim[0] / 2, cover.dim[1] / 2, base.height]));
const result = shapeCover.cut(shapeBase.translate([0, 0, foot.height / 2.0]));
return result; // tried with [result] also
}
In the following example I'm extruding a sketch along the Y axis but a positive extrusion value results in extruding towards -Y. A subsequent translateY of the object has the direction of the Y axis correct. Example:
export default function main(p) {
const tri1 = replicad.draw().vLine(10).lineTo([10,0]).close().sketchOnPlane("XZ").extrude(10)
const tri2 = replicad.draw().vLine(10).lineTo([10,0]).close().sketchOnPlane("XZ").extrude(5)
return [
{ shape: tri1, name: "tri1", color: "blue" },
{ shape: tri2.translateY(20), name: "tri1", color: "blue" },
]
}
Would be useful to avoid additions like the one below:
// All measurements in mm
const defaultParams = {
};
const main = ({ Sketcher, sketchCircle }, {}) => {
const inner = new Sketcher("XZ")
.vLine(2.5)
.hLine(6)
.vLine(2)
.hLine(3)
.vLine(3)
.hLine(4)
.vLine(1.5)
.hLine(4)
.vLine(1)
.hLine(58)
.vLine(1)
.hLine(19)
.vLine(-(2.5+2+3+1.5+1+1)) // .vLineTo(0)
.close()
.revolve([1, 0, 0]);
return inner;
};
It's for a toilet paper holder.
Edit: here's the project if you're interested! Always looking for feedback https://github.com/lf94/ToiletPaperHolderCylinder
Feel free to also add it as an example
Below code produces solid disk, but commenting out either .cut
produces a disk with a hole.
function main({drawCircle}) {
return drawCircle(10)
.cut(drawCircle(2).translate(5, 5))
.cut(drawCircle(1))
.sketchOnPlane('XZ')
.extrude(5)
}
Visualizer draws edges where openings are supposed to be, but they appear to be filled.
return [
drawCircle(10),
drawCircle(10).cut(drawCircle(2).translate(5, 5)),
drawCircle(10).cut(drawCircle(2)).cut(drawCircle(2).translate(5, 5)),
].map((x, i) =>
x
.sketchOnPlane('XZ')
.extrude(5)
.translate(i * 25, 0, 25),
)
When using the share link builder, it would be helpful to allow the model parameters/configuration panel to start expanded rather than collapsed.
This would aid in sharing links to replicad models (especially to users who don't normally use replicad) where the purpose is creating customized models to download.
Hello. Are Visualizer's sources available somewhere?
I'd like to use it offline not just so I can work on models without internet but also for easy import of local STEP files.
Thanks
I'm using vivaldi and I have a very hard time opening an existing model file. I have an open tab showing some model. I click on the circular arrow button (that's really a refresh icon, not an "open file" icon!) and get to the file selection dialog. I open my existing .js file. What I get next is the viewer of that file/model but without code pane. I don't have a way to show the code pane. If I use the browser's refresh button I get the previously open model shown normally with a code pane and the preview pane.
It would be helpful to export axes and planes in a STEP file to mark locations and mounting points etc. Happy to sponsor if possible.
It would be great to have the option to export binary STL files built into replicad. The StlAPI_Writer
class used by blobSTL
does not seem to correctly export the option used to turn on binary stl files, but the StlAPI.Write
method has a parameter to specify whether ascii or binary files are written.
Here's the function I've been using:
const done = oc.StlAPI.Write(shape.wrapped, filename, !binary)
Thanks!
Try this:
const main = ({ draw, Plane }, {}) => {
const svg = draw()
.movePointerTo([-179.49317, 229.08022]) // MoveRelative
.cubicBezierCurveTo([-134.57393, 223.51058], [-164.41155, 229.50992], [-153.52899, 220.8208]) // CubicBezierRelative
.cubicBezierCurveTo([-97.78017299999999, 236.07604], [-121.74239999999999, 225.33141], [-110.70716999999999, 235.15067]) // CubicBezierRelative
.cubicBezierCurveTo([-56.19808599999999, 221.49432000000002], [-73.17078899999998, 237.8377], [-70.662489, 224.04875]) // CubicBezierRelative
.cubicBezierCurveTo([-28.07106799999999, 221.87134], [-46.44357099999999, 219.77167000000003], [-37.54805499999999, 224.75315]) // CubicBezierRelative
.cubicBezierCurveTo([0.8247687900000109, 203.31125], [-17.672257999999992, 218.70922000000002], [-6.269769299999989, 208.28335]) // CubicBezierRelative
.cubicBezierCurveTo([23.788634000000012, 202.07363], [7.102314400000011, 198.91173], [16.31533800000001, 203.78046]) // CubicBezierRelative
.cubicBezierCurveTo([51.22988700000001, 190.62543], [33.45101300000001, 199.86683000000002], [41.609496000000014, 193.00862]) // CubicBezierRelative
.cubicBezierCurveTo([70.26589700000001, 188.89925], [57.414324000000015, 189.0934], [64.34909900000001, 191.2627]) // CubicBezierRelative
.cubicBezierCurveTo([82.85932400000002, 178.92971], [75.23789300000001, 186.9132], [78.37061700000001, 181.84804]) // CubicBezierRelative
.cubicBezierCurveTo([97.09665800000002, 171.45717], [87.35285100000002, 176.00825], [92.87883400000001, 174.76424]) // CubicBezierRelative
.cubicBezierCurveTo([108.93390000000002, 158.07532999999998], [101.78319000000002, 167.78260999999998], [105.43517000000001, 162.89454999999998]) // CubicBezierRelative
.cubicBezierCurveTo([128.25199000000003, 125.23221999999998], [116.39573000000003, 147.79726], [121.14360000000002, 135.75781999999998]) // CubicBezierRelative
.cubicBezierCurveTo([179.11347000000004, 59.19189199999998], [143.80255000000002, 102.20605999999998], [164.58317000000005, 82.87509699999998]) // CubicBezierRelative
.cubicBezierCurveTo([218.00120000000004, -22.48978700000002], [194.88322000000002, 33.48848599999998], [202.89865000000003, 3.6112338999999807]) // CubicBezierRelative
.cubicBezierCurveTo([257.88845000000003, -80.20100400000001], [229.71278000000004, -42.730361000000016], [246.64462000000003, -59.69690100000002]) // CubicBezierRelative
.cubicBezierCurveTo([285.85872000000006, -146.64979], [269.44347000000005, -101.27260000000001], [274.36004, -125.54739000000001]) // CubicBezierRelative
.cubicBezierCurveTo([343.9925600000001, -231.72992], [302.2934900000001, -176.81095], [325.30259000000007, -202.91183999999998]) // CubicBezierRelative
.cubicBezierCurveTo([387.84852000000006, -303.0538], [359.1790200000001, -255.14593], [387.84852000000006, -303.0538]) // CubicBezierRelative
.cubicBezierCurveTo([392.2010200000001, -440.01369], [421.50979000000007, -357.79784], [412.96081000000004, -413.28978]) // CubicBezierRelative
.cubicBezierCurveTo([273.8188800000001, -466.22706], [370.8666700000001, -467.47723], [321.77868000000007, -475.70766]) // CubicBezierRelative
.cubicBezierCurveTo([213.27782000000008, -427.05046], [249.6697200000001, -461.4533], [218.4492400000001, -433.61814]) // CubicBezierRelative
.cubicBezierCurveTo([174.68746000000007, -381.45858999999996], [205.49015000000009, -417.16015999999996], [188.66695000000007, -392.50471]) // CubicBezierRelative
.cubicBezierCurveTo([122.99144, -333.90123], [159.50738, -369.46381], [131.7499, -343.83309]) // CubicBezierAbsolute
.cubicBezierCurveTo([23.493637, -223.27303], [117.98028, -328.2187], [55.033721, -261.54896]) // CubicBezierAbsolute
.cubicBezierCurveTo([-131.41307, -17.782939], [-31.055971, -157.07356], [-84.540748, -89.623142]) // CubicBezierAbsolute
.cubicBezierCurveTo([-164.43605, 55.392448], [-145.07156, 3.1511358000000023], [-152.08484, 33.596646]) // CubicBezierRelative
.cubicBezierCurveTo([-198.55822, 102.18889], [-174.72982, 73.557539], [-190.75036, 82.875399]) // CubicBezierRelative
.cubicBezierCurveTo([-219.37006, 181.01762], [-208.74403, 127.38445], [-219.39909, 163.39419]) // CubicBezierRelative
.cubicBezierCurveTo([-179.49317, 229.08022], [-219.33406, 202.87072], [-202.43656, 228.42652999999999]) // CubicBezierRelative
.done()
.sketchOnPlane(new Plane('XY'));
const footBase = svg
.extrude(40)
.fillet(1, e => e.inDirection('Z'));
const foot = footBase;
return [foot];
}
It'll fail no matter what you try it seems...
I'm trying to import drawProjection into my project using the npm install version 0.15.3
` import { drawProjection } from "replicad";`
drawProjection doesn't seem to exist in this version, I can't find it in my node package. Any idea of what might be happening or what I'm doing wrong?
It would be handy if we could create new planes as transformations from existing planes similar to Workplane#transformed
method in CadQuery: https://cadquery.readthedocs.io/en/latest/examples.html#rotated-workplanes
When trying the code below to cutout a fused circle with text it only show the edges of the fused shape instead of it being cut.
const main = (r) => {
function center(drawing) {
const boundingBox = drawing.boundingBox;
drawing = drawing.translate(-boundingBox.center[0], -boundingBox.center[1]);
return drawing;
}
let plate = drawRectangle(5, 5).cut(
drawCircle(0.5)
.fuse(center(drawText('test', { fontSize: 2 })).translate(0, 0.8).rotate(90))
);
return plate.sketchOnPlane('XY').extrude(0.2);
};
This also happens when only using a to be cutout when using this code:
const main = (r) => {
function center(drawing) {
const boundingBox = drawing.boundingBox;
drawing = drawing.translate(-boundingBox.center[0], -boundingBox.center[1]);
return drawing;
}
let text = drawText( 'text');
text = text
.stretch(1 / (text.boundingBox.width / 7.5), [0, 1], text.boundingBox.center)
.stretch(1 / (text.boundingBox.height / 2), [1, 0], text.boundingBox.center);
let plate = drawRectangle(10, 20).cut(
center(text)
);
return plate.sketchOnPlane('XY').extrude(0.2);
};
In the event of broken code or Internet outage, it'd be nice to be able to build a local copy of RepliCAD. The latest issue has caused me to start to look at other options (such as using OpenCASCADE in StandardML). I would like to not have to do this... It's been a cycle of jumping between code CADs and RepliCAD hits some nice points!
A model I was working can't use section view. The preview window shows the message "Something went wrong.", and the console reports this:
index-9310d50d.js:4546 TypeError: (void 0) is not a constructor
at index-9310d50d.js:3852:11254
at $h (index-9310d50d.js:3812:20924)
at ku (index-9310d50d.js:3812:40498)
at rO (index-9310d50d.js:3812:34730)
at Ns (index-9310d50d.js:3810:4702)
at C9 (index-9310d50d.js:3812:39309)
at Ru (index-9310d50d.js:3812:38361)
at nO (index-9310d50d.js:3812:33843)
at T (index-9310d50d.js:3798:1561)
at MessagePort.F (index-9310d50d.js:3798:1927)
Object
componentStack
:
"\n at https://studio.replicad.xyz/assets/index-9310d50d.js:3816:34807\n at aj (https://studio.replicad.xyz/assets/index-9310d50d.js:3816:32530)\n at m (https://studio.replicad.xyz/assets/index-9310d50d.js:3611:6702)\n at Suspense\n at kP (https://studio.replicad.xyz/assets/index-9310d50d.js:3820:25)\n at https://studio.replicad.xyz/assets/index-9310d50d.js:3674:2792\n at https://studio.replicad.xyz/assets/index-9310d50d.js:3674:2792\n at c1e (https://studio.replicad.xyz/assets/index-9310d50d.js:4546:9029)\n at div\n at m (https://studio.replicad.xyz/assets/index-9310d50d.js:3611:6702)\n at div\n at m (https://studio.replicad.xyz/assets/index-9310d50d.js:3611:6702)\n at bz (https://studio.replicad.xyz/assets/index-9310d50d.js:4617:17)\n at div\n at div\n at kW (https://studio.replicad.xyz/assets/index-9310d50d.js:4546:3634)\n at https://studio.replicad.xyz/assets/index-9310d50d.js:3674:2792\n at cV\n at div\n at m (https://studio.replicad.xyz/assets/index-9310d50d.js:3611:6702)\n at Awe\n at Suspense\n at e (https://studio.replicad.xyz/assets/index-9310d50d.js:3600:8548)\n at e (https://studio.replicad.xyz/assets/index-9310d50d.js:3600:9517)\n at Axe\n at e (https://studio.replicad.xyz/assets/index-9310d50d.js:3600:5723)\n at e (https://studio.replicad.xyz/assets/index-9310d50d.js:3600:10581)"
[[Prototype]]
:
Object
When this occurs, it doesn't seem like there's a (UI) path to fixing the issue, short of reloading.
Hi,
I'd like to use replicad to build an object, but one of the elements of this object should be imported from an existing mesh (as .glb or .step file), is that possible?
I have a lot of workflows in traditional CAD applications that make use of a "Convert Entities" function to turn a loop of edges back into a sketch for downstream use. Is this something that can be natively supported? I tried doing this manually with an edge finder but it seems a bit hacky. Here is what I'm using which only captures closed loops of simple straight lines. I reuse some graph utilities I already use in my application, happy to share that if it's helpful.
const convertEntities = (model: AnyShape, plane: Plane) => {
const edgeFinder = new EdgeFinder()
edgeFinder.inPlane(plane)
const edges = edgeFinder.find(model)
const graph: Graph<{ point: Point }> = {}
edges.forEach((edge) => {
const id0 = edge.startPoint.repr
const id1 = edge.endPoint.repr
graph[id0] ??= {
id: id0,
point: edge.startPoint,
adjacencies: [],
}
graph[id0]?.adjacencies.push({ id: id1 })
graph[id1] ??= {
id: id1,
point: edge.endPoint,
adjacencies: [],
}
graph[id1]?.adjacencies.push({ id: id0 })
})
const graphClusters = getGraphClusters(graph)
const drawings = graphClusters.map((graphCluster) => {
const startNodeId = Object.values(graphCluster)[0]?.id
if (!startNodeId) return undefined
let openLoop = false
let lastNodeId: string
const loop: Point[] = []
const recurse = (nodeId: string) => {
const node = graphCluster[nodeId]!
if (node.adjacencies.length !== 2) {
openLoop = true
return
}
loop.push(node.point)
const nextNodeId = node.adjacencies.find(
(adj) => adj.id !== lastNodeId,
)?.id
if (!nextNodeId) {
openLoop = true
return
}
if (nextNodeId === startNodeId && loop.length > 2) {
return
} else {
lastNodeId = nodeId
recurse(nextNodeId)
}
}
recurse(startNodeId)
if (openLoop) return undefined
const drawing = loop
.reduce((drawingPen, point, i) => {
if (i === 0) {
return drawingPen.movePointerTo([point.x, point.y])
} else {
return drawingPen.lineTo([point.x, point.y])
}
}, draw())
.close()
return drawing
})
return drawings.filter((drawing): drawing is Drawing => !!drawing)
}
Hi,
I just found, the following cast() or downcast() code in shapes.ts will emit an error sometimes - not sure why, it happens accidentally.
Error: Could not find a wrapper for this shape type
Not sure why, I believe it is a comparation issue for java object instances. Since the value of enum is just the XXX.value, so I would suggest to fix like this(add .value call):
export function cast(shape: TopoDS_Shape): AnyShape {
const oc = getOC();
const ta = oc.TopAbs_ShapeEnum;
const CAST_MAP = new Map([
[ta.TopAbs_VERTEX.value, Vertex],
[ta.TopAbs_EDGE.value, Edge],
[ta.TopAbs_WIRE.value, Wire],
[ta.TopAbs_FACE.value, Face],
[ta.TopAbs_SHELL.value, Shell],
[ta.TopAbs_SOLID.value, Solid],
[ta.TopAbs_COMPSOLID.value, CompSolid],
[ta.TopAbs_COMPOUND.value, Compound],
]);
const Klass = CAST_MAP.get(shapeType(shape).value);
if (!Klass) throw new Error("Could not find a wrapper for this shape type");
return new Klass(downcast(shape));
}
I have a complex object and am trying to cut out a section so I can print just that to test the fit, but the intersection fails. Here is a screen shot that shows the object 3 times:
But when I perform the intersection I get:
Somehow this cylinder piece is the intersection between the box and one of the cylinders that make up the complex object. Everything else is missing...
To see the result of the intersection select only the "testSect" object in the viewer.
Isn't it a bit weird that the baseBox should be centered on the origin in x / y dimensions, but will not be centered in z dimension?
perhaps it should be this?
const makeBaseBox = (
xLength: number,
yLength: number,
zLength: number
) => {
return new Sketcher()
.movePointerTo([-xLength / 2, yLength / 2])
.hLine(xLength)
.vLine(-yLength)
.hLine(-xLength)
.close()
.extrude(zLength).translateZ(-zLength/2);
};
I think that OpenCascade allows you to set the scale of the file (eg. meters or milimeters), when exporting to STEP / STL, is there a way to set that from replicad?
Also wondering if there's a way to go through all the parts and give them colors / names ?
I'm building some shapes by extruding, and then I'd like to combine them in a global object that I can export / texture / visualize in Three.js, but I don't know how to combine the extruded objects into a single combined object?
I'm currently converting svg2curv
to output replicad code, but it appears to be missing a native "move absolute" method...
Once again, for SVG import :)
intersectBlueprints
throws Cannot read properties of undefined (reading 'length')
. Input blueprints seem sane to me.
import { BlueprintSketcher, fuse2D, intersectBlueprints } from 'replicad'
const triangles = [
[
[-15, -10],
[4.22649730810374, 7.5],
[-15, 30],
],
[
[-15, 30],
[4.22649730810374, 7.5],
[4.226497308103742, 30],
],
[
[7.690598923241496, 30],
[7.690598923241495, 7.5],
[15, 30],
],
[
[4.22649730810374, 7.5],
[15, -10],
[7.690598923241495, 7.5],
],
[
[-15, -10],
[15, -10],
[4.22649730810374, 7.5],
],
[
[7.690598923241495, 7.5],
[15, -10],
[15, 30],
],
]
const triangles2 = [
[
[9.999999999999996, 7.5],
[-15, 30],
[-15, -10],
],
[
[9.999999999999996, 7.5],
[10, 30],
[-15, 30],
],
[
[13.46410161513775, 7.5],
[15, 30],
[13.464101615137752, 30],
],
[
[15, -10],
[13.46410161513775, 7.5],
[9.999999999999996, 7.5],
],
[
[15, -10],
[9.999999999999996, 7.5],
[-15, -10],
],
[
[15, -10],
[15, 30],
[13.46410161513775, 7.5],
],
]
const trianglesToBlueprint = triangles => {
const blueprints = triangles.map(pts => {
const bps = new BlueprintSketcher().movePointerTo(pts[0])
pts.slice(1).forEach(p => bps.lineTo(p))
return bps.close()
})
return blueprints.slice(1).reduce((acc, el) => fuse2D(acc, el), blueprints[0])
}
export function main() {
const a = trianglesToBlueprint(triangles)
const b = trianglesToBlueprint(triangles2)
// return [
// a.sketchOnPlane('XY').extrude(1).translate(0, 0, 20),
// b.sketchOnPlane('XY').extrude(1).translate(0, 0, 30),
// ]
return intersectBlueprints(a, b).extrude(1).translate(0, 0, 50)
}
This could make it easier for beginners to start selecting edges or faces.
Add a way to select for items at the furthest position (like > Z
in cq)
As mentioned in #47 I did not find an equivalent to sweepSketch
in the drawing API.
// draw curve of hook with Drawing API
let hookCurveDraw = draw().cubicBezierCurveTo(tp1,cp1s,cp1e).cubicBezierCurveTo(tp2,cp2s,cp2e).cubicBezierCurveTo(tp3,cp3s,cp3e).cubicBezierCurveTo(tp4,cp4s,cp4e).done().sketchOnPlane("XY")
// draw curve of hook with Sketcher object
let hookCurveSketch = new Sketcher("XY",1).movePointerTo([0,0]).cubicBezierCurveTo(tp1,cp1s,cp1e).cubicBezierCurveTo(tp2,cp2s,cp2e).cubicBezierCurveTo(tp3,cp3s,cp3e).cubicBezierCurveTo(tp4,cp4s,cp4e).done()
// define cross section of hook
let hookWidth = 3;
let hookHeight = 2
// sweep along the sketch works, sweep along the drawing is not available yet?
let loftedHook = hookCurveSketch.sweepSketch((plane, origin) =>
sketchRectangle(hookWidth, hookHeight, { plane, origin }) );
According to https://replicad.xyz/docs/use-as-a-library OpenCascadeInstance should be created by calling the init function exported from replicad-opencascadejs/src/replicad_single.js
with single argument (defining locateFile function). However, TypeScript reports Expected 0 arguments, but got 1.
. Could you please help me to solve this? I think the type is incorrect in the replicad_single.d.ts
as the init function indeed accepts one argument.
Any ideas how to do that in RepliCAD? One of my products (I call them products but they are not sold or anything) which I've done in curv
uses smooth blending of ellipsoids to achieve these surfaces but I'm beginning to think I should properly specify them...
Example of my product:
What you think? Thanks!
Currently, mesh() call will only generate vertices, normals, triangles, and faceGroups. As far as I can see, opencascade can generate UV Node too. Would you like to export uv information, please?
thanks
Hi @sgenoud,
Is there any way to get the volume and surface area of a solid shape with Replicad?
Thanks a lot!
When trying to compile the equivalent of cad.js in typescript I get this error:
error TS2339: Property 'shell' does not exist on type 'Shell | Solid | CompSolid | Compound | Vertex | Edge | Wire | Face'.
Property 'shell' does not exist on type 'Vertex'.
Side question: I see there are a lot of approximation /interpolation code in lib2d to represent all kinds of curves, would it be possible using this code to load a DXF file, and represent the corresponding curve as an regularly-interpolated polygon? This could be useful to me
How come there's this difference?
Basically I'm trying to lay one circle on the edge of another and fuse them, but I get this error - any ideas?
const main = ({ drawCircle, Plane }) => {
const top = drawCircle(44.0 / 2).sketchOnPlane(new Plane("XY")).extrude(3.0);
const base = drawCircle(38.0 / 2).sketchOnPlane(new Plane("XY"))
.loftWith(
drawCircle(37.0 / 2)
.fuse(drawCircle(3.0).translate([37.0 / 2 - 1.5, 0]))
.sketchOnPlane(new Plane([0, 0, -10.0]))
);
const baseWithTop = top.fuse(base);
const withCut = baseWithTop.cut(
drawCircle(38.0 / 2 - 2.0).sketchOnPlane(new Plane([0, 0, -10.0])).extrude(13.0)
);
return [withCut];
};
For some reason it's just not appearing for me...? Try this:
const defaultParams = {
outerRingDiameter: 44.0,
innerRingDiameter: 38.0,
topThickness: 3.0,
gripDiameter: 2.0,
socketDepth: 10.0,
gripsTotal: 6,
};
const main = ({ sketchCircle, Sketcher, Plane }, {
outerRingDiameter,
innerRingDiameter,
topThickness,
gripDiameter,
socketDepth,
gripsTotal,
}) => {
const top = sketchCircle(outerRingDiameter / 2).extrude(topThickness);
const base = sketchCircle((innerRingDiameter - (gripDiameter / 2)) / 2)
.loftWith(sketchCircle((innerRingDiameter - (gripDiameter / 2) - 1.0) / 2, new Plane([0, 0, -socketDepth])));
const baseWithTop = top.fuse(base);
const withCut = baseWithTop.cut(
sketchCircle(
innerRingDiameter / 2 - 2.0,
new Plane([0, 0, -socketDepth])
)
.extrude(socketDepth + topThickness)
);
const withTopFillet1 = withCut.fillet(topThickness - 0.01, e => e.containsPoint([0, outerRingDiameter / 2, topThickness]));
const withTopFillet2 = withTopFillet1.fillet(topThickness / 2, e => e.containsPoint([0, innerRingDiameter / 2 - 2, topThickness]));
const socket = withTopFillet2;
const parentRadii = { start: (innerRingDiameter - gripDiameter / 2) / 2, end: ((innerRingDiameter - gripDiameter / 2) - 1.0) / 2 };
const childRadii = { start: gripDiameter / 2, end: gripDiameter / 2 };
const grips = getAngles(gripsTotal).map(angle =>
modelGrip({ sketchCircle, Plane })({ angle, height: socketDepth, parentRadii, childRadii })
);
return grips.reduce((acc, s) => acc.fuse(s), socket);
};
const toDegrees = radians => (radians / (Math.PI * 2)) * 360;
const toRadians = angle => (angle / 360) * (Math.PI * 2);
const modelGrip = ({ sketchCircle, Plane }) => ({ angle, height, parentRadii, childRadii }) => {
const radiusDiff = parentRadii.start - parentRadii.end;
const parallelAngle = (90 - toDegrees(Math.atan(height / radiusDiff)));
const grip = sketchCircle(childRadii.start)
.loftWith(sketchCircle(childRadii.end, new Plane([0, 0, -height])))
.rotate(parallelAngle, [0,0,0], [Math.cos(toRadians(angle + 90)),Math.sin(toRadians(angle + 90)),0])
.translate([
parentRadii.start * Math.cos(toRadians(angle)),
parentRadii.start * Math.sin(toRadians(angle)),
0
]);
return grip;
};
const getAngles = pieSlices => new Array(pieSlices).fill(0).map((_, index) => (360 / pieSlices) * index);
When I do this
const geometries = syncGeometries([replicadMesh], []);
const geometry = geometries[0].faces;
and use the geometry: BufferGeometry
, it seems to miss the uv
field in geometry.attributes
Do you know if there's an easy way to obtain those?
Is this already possible and just not well exposed?
I would like to import https://gitlab.steamos.cloud/SteamDeck/hardware/-/blob/master/steamdeck_step_20220202.stp and cut it in half using RepliCAD.
Trying to do a sweep along a curve starting anywhere other than the origin produces weird results. Here's a screen shot showing the bezier I'm sweeping and the resulting sweep:
See code
const r = replicad
function main() {
const bz = r.makeBezierCurve([
[-20, 0, -20],
[-30, 0, -20],
[-30, 20, 10]
])
const dia = 6
const c = r.drawRoundedRectangle(2*dia, dia, dia/6).sketchOnPlane("YZ")
const swp = r.genericSweep(
c.wire,
r.assembleWire([bz]),
{ forceProfileSpineOthogonality: true },
false
)
return [
{ shape: bz, name: "curve" },
{ shape: swp, name: "sweep" }
]
}
A declarative, efficient, and flexible JavaScript library for building user interfaces.
๐ Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.
TypeScript is a superset of JavaScript that compiles to clean JavaScript output.
An Open Source Machine Learning Framework for Everyone
The Web framework for perfectionists with deadlines.
A PHP framework for web artisans
Bring data to life with SVG, Canvas and HTML. ๐๐๐
JavaScript (JS) is a lightweight interpreted programming language with first-class functions.
Some thing interesting about web. New door for the world.
A server is a program made to process requests and deliver data to clients.
Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.
Some thing interesting about visualization, use data art
Some thing interesting about game, make everyone happy.
We are working to build community through open source technology. NB: members must have two-factor auth.
Open source projects and samples from Microsoft.
Google โค๏ธ Open Source for everyone.
Alibaba Open Source for everyone
Data-Driven Documents codes.
China tencent open source team.