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

Subtracting hundreds of square holes (net) #766

Closed
ghost opened this issue May 13, 2021 · 14 comments
Closed

Subtracting hundreds of square holes (net) #766

ghost opened this issue May 13, 2021 · 14 comments
Labels
question Further information is requested

Comments

@ghost
Copy link

ghost commented May 13, 2021

Coming from openscad, I was able to create a grid net with hundreds of square holes quite easily.

As there are several ways to subtract pieces repeatedly in cadquery, I would like to know which one is more suitable.

Something like this, is what I am talking about:

preview16

@ghost ghost added the question Further information is requested label May 13, 2021
@Jojain
Copy link
Contributor

Jojain commented May 13, 2021

I think you could do it like this :

import cadquery as cq
result = cq.Workplane("XY" ).box(50, 50, 0.5, centered = False)

def mesh_grid(x,y,N):
    pts = []
    for i in range(1,N):   
        for j in range(1,N):
            pts.append((i*x/N, j*y/N))
    return pts
pts = mesh_grid(50,50, 10)
result = result.pushPoints(pts).rect(1,1).cutThruAll()

Which gives the following result :

image

@ghost
Copy link
Author

ghost commented May 13, 2021

Oh so traditional iteration and collecting points, fair enough, thank you!

@Jojain
Copy link
Contributor

Jojain commented May 13, 2021

Well that seems the most obvious to me. You would probably prefer to extrude your grid cross section directly with holes rather than cutting them because with high number of holes the cutting process takes really long.

import cadquery as cq
result = cq.Workplane("XY" ).rect(50,50, centered =False)

def mesh_grid(x,y,N):
    pts = []
    for i in range(1,N):   
        for j in range(1,N):
            pts.append((i*x/N, j*y/N))
    return pts
pts = mesh_grid(50,50, 30)
result = result.pushPoints(pts).rect(1,1).extrude(2)

@ghost
Copy link
Author

ghost commented May 13, 2021

I've tried your examples, the first one won't run, the second works as expected but takes a while to load. There is no faster way?

@jmwright
Copy link
Member

@infosisio As @Jojain alluded to, you can draw the outer rectangle with all of the inner rectangles and then extrude them together. CadQuery should figure out that the inner rects should be holes. I'm guessing that will be the fastest method for making the grid.

@ghost
Copy link
Author

ghost commented May 13, 2021

@jmwright Are you referring to the second example, exactly as it's written now? I am just wondering why should this take around 15 secs to render.

@Jojain
Copy link
Contributor

Jojain commented May 13, 2021

It indeed took a while when raising the N number. ( In the first example quickly took ages, in the second it run but it also gets quickly long with high N numbers)

@ghost
Copy link
Author

ghost commented May 14, 2021

I am just wondering, why, the tech is much better than openscad.

In openscad I was able to use union and have it render way more holes instantly.

I was able to do this:

import cadquery as cq
result = cq.Workplane("XY")

def mesh_grid(x,y):
    pts_x = []    
    pts_y = []
    offset = y/2
    for i in range(0,round(x/2)-4):   
        pts_x.append((i*x/int(x/2-5)-offset,offset))  
    for i in range(0,round(y/2)-4):   
        pts_y.append((0,i*y/int(x/2-5)))      
    return pts_x, pts_y

grid_x = 50
grid_y = 50
pts_x, pts_y = mesh_grid(grid_x,grid_y)
result = result.pushPoints(pts_x).rect(1,grid_y+1).extrude(2)
result = result.pushPoints(pts_y).rect(grid_x+1,1).extrude(2)

Not perfect but much faster than 15 seconds.

Anyhow, thanks to both of you for the help!

@marcus7070
Copy link
Member

There is a native OCCT method for repeating shapes. I haven't benchmarked to see if it's any better than just creating all the wires first then extruding.

import cadquery as cq
import OCP


def repeat(self, x=None, y=None, z=None, run_parallel=False):
    """
    Make this shape periodic.

    TODO: auto period from self.BoundingBox

    :param x: Parameters for the periodic repetition along the x axis. A two
      tuple, the first value is the period and the second is the number of
      repetitions (excluding the base Shape).
    :param y: Parameters for the periodic repetition along the y axis. Same two
      tuple as the x axis.
    :param z: Parameters for the periodic repetition along the z axis. Same two
      tuple as the x axis.
    :param run_parallel: Should the make parallel process be run in multiple threads?
    """

    mp = OCP.BOPAlgo.BOPAlgo_MakePeriodic()
    mp.SetShape(self.wrapped)
    if x is not None:
        mp.MakeXPeriodic(True, x[0])
    if y is not None:
        mp.MakeYPeriodic(True, y[0])
    if z is not None:
        mp.MakeZPeriodic(True, z[0])
    mp.SetRunParallel(run_parallel)
    mp.Perform()
    if x is not None:
        mp.XRepeat(x[1])
    if y is not None:
        mp.YRepeat(y[1])
    if z is not None:
        mp.ZRepeat(z[1])
    return cq.Shape.cast(mp.RepeatedShape())


cq.Shape.repeat = repeat


base_shape = (
    cq.Workplane()
    .rect(1, 1)
    .rect(1.1, 1.1)
    .extrude(2)
    .val()
)

num = 49
compound_grid = base_shape.repeat((1.1, num), (1.1, num), run_parallel=True)
solids = compound_grid.Solids()
first_solid = solids.pop(0)
grid = first_solid.fuse(*solids, glue=True).clean()


if "show_object" in locals():
    show_object(grid)

screenshot2021-05-14-113835

If you're going to try out the above code, I would reduce num to 5 to start with and work upwards.

I am just wondering, why, the tech is much better than openscad.
In openscad I was able to use union and have it render way more holes instantly.

I would say creating a series of boxes and subtracting them from a larger box is a perfect use case for a CSG kernel. If you want to see where a BREP kernel is better, then I would sweep some wires along a curve.

@ghost
Copy link
Author

ghost commented May 14, 2021

@marcus7070

Unfortunately it is slower.

There is a native OCCT method for repeating shapes. I haven't benchmarked to see if it's any better than just creating all the wires first then extruding.

Got it.

I would say creating a series of boxes and subtracting them from a larger box is a perfect use case for a CSG kernel. If you want to see where a BREP kernel is better, then I would sweep some wires along a curve.

@marcus7070
Copy link
Member

Interesting, I just benchmarked my code above against the other method (of drawing all edges first then extruding):

import cadquery as cq
from itertools import product

num = 49
hole_dim = 1
wall_thick = 0.2
outer_dim = num * hole_dim + (num + 1) * wall_thick

vals = [wall_thick + (hole_dim + wall_thick) * idx for idx in range(num)]
points = [(x, y) for x, y in product(vals, repeat=2)]

grid = (
    cq.Workplane()
    .rect(outer_dim, outer_dim, centered=False)
    .pushPoints(points)
    .rect(hole_dim, hole_dim, centered=False)
    .extrude(2)
)
> time python repeat.py
python repeat.py  33.97s user 1.46s system 122% cpu 29.008 total
> time python wires.py
python wires.py  111.78s user 0.09s system 99% cpu 1:51.89 total

Repeating the shape was 3x faster for me.

@adam-urbanczyk
Copy link
Member

@infosisio this is the current idiomatic way of constructing such structures:

import cadquery as cq

N = 50
d = 2

res = (
    cq.Workplane()
    .rect(N*d+d, N*d+d)
    .rarray(d, d, N, N).rect(0.9*d, 0.9*d)
    .extrude(1, clean=False)

It is getting very slow above N=20, there is a known bottleneck in cq.Face.makeFromWires.

Please stay with OpenSCAD unless you need a Python based solution or a B-Rep kernel.

@Jojain
Copy link
Contributor

Jojain commented May 14, 2021

I have tried it with CATIA V5, it's way better but still gets really really slow with high N number. I think it's clear that it's not a use case where brep kernel excels

@ghost
Copy link
Author

ghost commented May 14, 2021

@marcus7070

Interesting, I just benchmarked my code above against the other method (of drawing all edges first then extruding):

import cadquery as cq
from itertools import product

num = 49
hole_dim = 1
wall_thick = 0.2
outer_dim = num * hole_dim + (num + 1) * wall_thick

vals = [wall_thick + (hole_dim + wall_thick) * idx for idx in range(num)]
points = [(x, y) for x, y in product(vals, repeat=2)]

grid = (
    cq.Workplane()
    .rect(outer_dim, outer_dim, centered=False)
    .pushPoints(points)
    .rect(hole_dim, hole_dim, centered=False)
    .extrude(2)
)
> time python repeat.py
python repeat.py  33.97s user 1.46s system 122% cpu 29.008 total
> time python wires.py
python wires.py  111.78s user 0.09s system 99% cpu 1:51.89 total

Repeating the shape was 3x faster for me.

Are we comparing exactly the second version of Jojain and yours with the num set to 49 or 50? If so, yours loads at least 3 times slower for me, I am on the latest version of cq-editor.

@adam-urbanczyk

Please stay with OpenSCAD unless you need a Python based solution or a B-Rep kernel.

This is a particular use case, not so common thankfully. CQ is much better in every other way.

Thanks to all for the responses.

This issue was closed.
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
question Further information is requested
Projects
None yet
Development

No branches or pull requests

4 participants