Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Convert Entities Function #144

Open
andrewkrippner opened this issue Feb 24, 2024 · 4 comments
Open

Convert Entities Function #144

andrewkrippner opened this issue Feb 24, 2024 · 4 comments

Comments

@andrewkrippner
Copy link

andrewkrippner commented Feb 24, 2024

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)
}
@sgenoud
Copy link
Owner

sgenoud commented Feb 25, 2024

I would like to incorporate something in that spirit with pantograph (my attempt at doing the 2d kernel myself). And I definitely see the point in it (I would call it something like drawEdges)!

In addition to working only with straight lines your implementation does not handle self intersecting loops - and working with organising the loops to have insides and outsides.

That said I would be curious of what you do with the "sketched" entities? It might be possible to achive what you are trying to do without actually drawing again the edges.

@andrewkrippner
Copy link
Author

Thanks for bringing pantograph to my attention, it looks great. I have used makerjs before for a 2d DXF export application but it didn't work well for my needs (commonly created odd wavy artifacts in the boolean combine operation).

And yes, my implementation was simply to solve my immediate problem so it's not close to a real solution. I have since figured out how to add circular arcs as well by digging down to the opencascade properties of the curves and backsolving for the arc mid-point.

Can you provide an example of how self-intersecting loops would occur on a face? It seemed impossible to occur to me but I could easily be overlooking something.

As far as application, I am using it to capture the edges on this top face formed by a number of sweeps and other operations. The perimeter would be otherwise difficult to do unless I did the 2d boolean operations separately (pantograph?)

After I grab the perimeters, I offset the outside loops inwards and the inside loops outwards to create this relief feature.

image image

@andrewkrippner
Copy link
Author

I would like to incorporate something in that spirit with pantograph (my attempt at doing the 2d kernel myself). And I definitely see the point in it (I would call it something like drawEdges)!

In addition to working only with straight lines your implementation does not handle self intersecting loops - and working with organising the loops to have insides and outsides.

That said I would be curious of what you do with the "sketched" entities? It might be possible to achive what you are trying to do without actually drawing again the edges.

Also, can pantograph drawings be used in replicad?

@sgenoud
Copy link
Owner

sgenoud commented Feb 28, 2024

Thanks for the summary! I was thinking of potentially just creating a sketch from the wires in the face - but it would not work (as I think you cannot do the offsets the way you wanted.

You could also draw the shape within the face in 2d with a simple draw operation, but you would need to either do the math to have thickness (but prone to errors and boring to you yourself when a machine could do) or build it with different offsets (but if memory serves I have only implemented it correctly for loops). So, for now, this is probably the best implementation.

Also, can pantograph drawings be used in replicad?

Not yet. This is my aim - but there are some things that do not work well enough yet to be ready to be publicized outside of issues in the repo 😉 . I have to implement a better way to handle tolerances with geometries first (and potentially also good offsets for some shapes).

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

No branches or pull requests

2 participants