Coder Social home page Coder Social logo

sgenoud / replicad Goto Github PK

View Code? Open in Web Editor NEW
328.0 17.0 38.0 68.59 MB

The library to build browser based 3D models with code.

Home Page: https://replicad.xyz

License: MIT License

JavaScript 34.97% TypeScript 62.50% CSS 1.23% HTML 0.18% MDX 1.12%
3d-models opencascade code-cad brep step stl typescript javascript

replicad's Introduction

replicad

The library to build browser based 3D models with code.

As an abstraction over opencascade, it gives developers the power to integrate it in their web application.

So there are two ways you might be interested in this library:

  • how do I use it to build a 3D model?
  • how do I integrate replicad in my web application?

For more information, check the documentation

A simple example

ab4e3c12f4257659

replicad's People

Contributors

ahdinosaur avatar billiam avatar github-actions[bot] avatar misiur avatar niels-io avatar origamifreak2 avatar paulftw avatar sgenoud avatar wezm avatar

Stargazers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

Watchers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

replicad's Issues

generic sweeps must start at origin?

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:

image

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" }
  ]
}

init function from replicad_single.js expects 0 arguments

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.

Enhancement: generate UV for mesh

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

Bug in blueprint fusing algorigthm

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])
)

How to build RepliCAD?

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!

shape intersection with complex object fails

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:

  • the colorful version to the left shows the individual parts that are fused
  • the light grey version to the right is the fused final object
  • in the center is the final object in light green and a box I want to intersect with in light blue

image

But when I perform the intersection I get:

image

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...

Model source: https://studio.replicad.xyz/workbench?code=UEsDBAoAAAAIABiL9lbruTHXfwUAAIQPAAAHAAAAY29kZS5qc41XW2%252FbNhR%252B96848F6kVJYdF8HWAh2WS7s9bGjQpkvaog%252BMRFlaaFKlqDhGkB%252B0v7FftsObRMl2tiCGeTnX73yHpOdz%252BFRvKp6ftXm%252BBfLAKKxFyxUUQgJh7bri7RrWpFEJ%252FHk8mc%252FhXNRbWa1KBf%252F8vVwsXyZwVQrZKMrhXnB4W2V3lE8m8yNYMXFLGEhasyojORzNcfUIflHbmua0gEfQI1FAta6FVNHUS05jeBqoTTLBGwUS3nTLfq3IFK4uJ%252FRB2wC7iNZJy9QlkWTd4PbjBABDX5OKmxxxqr8uKvIajl8lbvo75ThdLGCurfrVa8LYa3iZWBMl4bnRtwOj8mOgYZcvRRMsa71aNJWqEB%252FCBF%252F5IKz0KV8x%252Bhp%252BOjGSRM9ACWgYwTpYEWjrDZF5Y4NoGKX3RqYULO%252FLRB%252BUbBv0gmJWxsZ30sdnl21O1p8qsWKcNg1gJTa4AUQiA3L4BLUURWUCba%252BrXJUandRqbfRca0S3QimxxmE81LigtdF4aeRLaiijFZoqp82u%252FJWOA%252BXTPWE5MROex0BJyleqpLxCPHGXUTVXsjLwoQgOXT1d5ZpM0o3GjInsTmM2hEvvGj6cJH76vij6eERRNBTpJjHXjHJF5eRpMilanpmyZlhWeiol2UYkRmZLqlqJ1U7XpI4ogzc%252FA2WpkYpipPfEE9ZRFTpLmqRRHRvOWjabrkTmo607er5l2K5URnXqKIzFXSZgp5hynKIigBSKKBq9WiTwFT%252F6%252FxsOZ8duHKc6K2YwBGyLz%252BimaoyqkoQj%252BRT9MnQSGw2yBcERxpvPUCND6aSL01G1i1RgWyORogCbr8aBb0WnkLmUbNz%252FkWXXdy7PLlO%252FZbppmDR%252Bm7x3kp7dmKyRk0zTk21t09Hc91vsfByEJNmPicuyaWtd5Pkd3W7INsjvjDT0TDxEQT6%252BhKOKekfpOJJoFihbDZ1vnHjnd1zc%252BrH1%252BrEuqaR9BkfYzSfOshHKJdmcVzJju0LNHVVZ%252BZ5f6vyiOJX0XrB7Gn09ThaJRnZo5VKwrWn0kaEElov%252Fa%252BygJTiC3lbWqshKfqCZMu3fSwYoxmkPnSbEEN5nY%252FId82wJjNTAKLwI%252BYpB%252F0FUmWaiGXIVN2R68fbX5YfTi1gT7lvsXHWOPncaeLMcbPCBc8d7S3tTFyMPjfBtV4uKqwaZ3newvR3MyY1tXKf2EMc8lhhknfaXx0jD3A5Www6f09BH629i%252F5Hmj%252BE5sjkw%252FWI5rl6wOV%252B%252BcIp4YOtOj0fRdY76rutuxyTMeeDTwWxvlsg3oGabP3cO2cTsj5MOvaRDpSNQSCFfuh4pe%252BbsUmAWVqc%252FEnREHaZa11x3Nn%252B8BHV8CADHu0%252Bm70hG31ms47RyVJ%252FefJnqgyMtcCeyThJ4bHn1vcVnCd6R9KmHtDd5sOmwHPb2PRpWcTaq4qCG4wa0US0Hjq9E3fkdH1MBIgNwRwKHj%252Bt97mc%252Bk0EYGIMNJmWiUNeVKiOHisbQ0AU5aF8dWBjddQgtvoTF7V%252BIVn%252Bv41vr2ctSR564dvVIJto3nhBD%252B4pqtlP7gsDMspLiI6eoFD7UkC32hViNnoguIVT9iJpI4p1GWS4G%252FWEONAuI5vhiUF2zsq8CsxP9iQcOT03qCIB%252FEoWkdQENBZCu%252BObSKUZBxN2LaicdXaRducBKYNuA6V5s9oXyCE1JamS%252FluqyuYnw1wFe%252BZyscWuKe8iRTDAhcfZDUVD8m8JTMjKBVQxtzIZGcDewIlButceIJcFhM3Y%252FMHTL2j1mbL0Om7H7gZmVpJTvseMPnGdMeZHA2pYyJjbTsTWk1GE7uBniw6r7PXn5UvdabiFQrVtZs2d0kSY76rgWWMAfuZTtB9aResfA6YAj5pUZQvoNf0T8C1BLAQIUAAoAAAAIABiL9lbruTHXfwUAAIQPAAAHAAAAAAAAAAAAAAAAAAAAAABjb2RlLmpzUEsFBgAAAAABAAEANQAAAKQFAAAAAA%253D%253D

To see the result of the intersection select only the "testSect" object in the viewer.

Missing documentation on 3D operations

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?

Cutting a fused shape shows only edges

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);
};
Screenshot 2023-07-31 165913

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);
};

Screenshot 2023-08-01 094442

Some typing issues

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'.

drawProjection import from node package failing

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?

Convert Entities Function

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)
}

Section view broken for some models, hard to fix

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.

Steps to reproduce:

  1. Visit model url: https://studio.replicad.xyz/workbench?code=UEsDBAoAAAAIAASYG1fIglyYsgIAAGYHAAAHAAAAY29kZS5qc3VUXWvbMBR9968QeShyo7hJ28AIpGNtN%252FYy6MNgH6UM1b5pRBW5k%252BWlpeS%252F7%252BrDsp04UNLofpxz79FRkrxUlSHvpNB8y8iGP8M1r%252BC6fPWHmzcpVAGa7MiSaHiRIudFkviuAla8luaOa76pMP%252BeEFIIvgEDekFmswuGAbMW%252BbOCqlqQD9mljeRlrcyCzJkr%252FycQHpNG15DsGuhcAqKqHBB2ml2E6JZL%252Bb3Bw0wTX8my1EOJAN9NTbN5Q7LhQmGE4hzaDmPnbzdg7hTH90c3Owt1fnQ87JKULK9cv0d%252BLF9%252FiMKsEf0bN%252Bus%252BqsNdd9eyi2lDQUZt4umZ%252BeMnKdkQmJdLDvFqWc2m%252BLXc%252BzqC4GxLvNXEE9rg9S9%252FstYEga%252FsatglVsJaWdJrBAKu0Sp0Qa38OLWoFGI3tAI7fvHhDaCkI%252BHup%252F2WRdkmlo6CW5ixO84jzp5Gwm92AcTDWjAmj4vAJ7SuFFem7I2gacxtSc6ehuDxD56P2XE%252Fj20x5k9Ws7MIEIluQGK4QndR3AXvefY8ZEp3M3NnVReJvzMcBfq93EJsepIf3LSF%252FoKhQ6%252B9gAKtuST1vyNduvSbCWkpKqWMs00FHUOlD4y8gckIyKa27%252BAjoeGLq57eWir3jWxWLHvENbtDQ5GT85CuKtqrJy2TZN9lc%252FsM9knccFBI%252BN3gWIPu3wAKW2p44jhvwZTa0Ues1VdQaOyT%252B6Y3S51vxgHj%252B1z8QRf%252FI%252FtksDyCjKhoqj3k0ZStwOabWjjiU109cOfjcainX5XNihYr33s2t0z6vrPwTm%252FgKFoZbDusMPeCg25EaWio9%252Bj1G%252Bc5Wu%252BWeFTQ%252BSw053kCujo568Ra9%252FRsXKuCnofhB3oj8PG%252BzhU0yUeUvc0w9SH6MdnixQPgcOVqtLQQypLYldJWhegXrvkP1BLAQIUAAoAAAAIAASYG1fIglyYsgIAAGYHAAAHAAAAAAAAAAAAAAAAAAAAAABjb2RlLmpzUEsFBgAAAAABAAEANQAAANcCAAAAAA%253D%253D
  2. Click section view button in toolbar

vLineTo?

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

Exception in intersectBlueprints

intersectBlueprints throws Cannot read properties of undefined (reading 'length'). Input blueprints seem sane to me.

online demo

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)
}

Interpolate curves

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

Multiple cuts fail in the drawing API

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.

A bigger example:

Screen Shot 2022-07-14 at 5 50 00 PM

  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),
  )

[Request] Option to set parameter panel to uncollapsed in share links

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.

Replicad Share Application doesn't put all objects into same coordinates.

rotating the view is messed up after clicking an axis

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...

Can't save from visualizer?

For some reason, currently trying to save from replicad.xyz just doesn't cause anything to download. Devtools console shows some sort of error:

1670730501

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
}

extrusion of sketches goes in the wrong direction

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" },
  ]
}

image

Visual scripting editor

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

Customizer not appearing?

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);

Import STEP file in my scene

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?

Potential bug in cast() or downcast() of shapes.ts

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));
}

3D curves / surfaces?

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:

Screenshot from 2021-10-19 17-33-12

Screenshot from 2021-10-21 16-22-09

What you think? Thanks!

Set a name for parts / the default scale in the output file

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 ?

Request: Export binary STL

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!

Visualizer sources

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

can't open existing model (.js) file

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.

BUG: makeBaseBox

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?

.movePointerTo([-xLength / 2, yLength / 2])

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);
};

sweepSketch along a drawing not yet available?

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 }) );

Can't fuse a circle on the edge of another circle?

1656378390

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];
};

Cannot fillet bezier curve?

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...

Missing UV mapping on the geometry

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?

Recommend Projects

  • React photo React

    A declarative, efficient, and flexible JavaScript library for building user interfaces.

  • Vue.js photo Vue.js

    ๐Ÿ–– Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.

  • Typescript photo Typescript

    TypeScript is a superset of JavaScript that compiles to clean JavaScript output.

  • TensorFlow photo TensorFlow

    An Open Source Machine Learning Framework for Everyone

  • Django photo Django

    The Web framework for perfectionists with deadlines.

  • D3 photo D3

    Bring data to life with SVG, Canvas and HTML. ๐Ÿ“Š๐Ÿ“ˆ๐ŸŽ‰

Recommend Topics

  • javascript

    JavaScript (JS) is a lightweight interpreted programming language with first-class functions.

  • web

    Some thing interesting about web. New door for the world.

  • server

    A server is a program made to process requests and deliver data to clients.

  • Machine learning

    Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.

  • Game

    Some thing interesting about game, make everyone happy.

Recommend Org

  • Facebook photo Facebook

    We are working to build community through open source technology. NB: members must have two-factor auth.

  • Microsoft photo Microsoft

    Open source projects and samples from Microsoft.

  • Google photo Google

    Google โค๏ธ Open Source for everyone.

  • D3 photo D3

    Data-Driven Documents codes.