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

How to access individual objects in a Compound object? #1715

Open
huskier opened this issue Nov 28, 2024 · 6 comments
Open

How to access individual objects in a Compound object? #1715

huskier opened this issue Nov 28, 2024 · 6 comments
Labels
question Further information is requested

Comments

@huskier
Copy link
Contributor

huskier commented Nov 28, 2024

We want to access individual letter in the text3D Compound, and we've found there is a function called siblings(self, shape: "Shape", kind: Shapes, level: int = 1). We wonder how to call the siblings function properly??//

import cadquery as cq
from cadquery.vis import show

text_string = "Hello, CadQuery!"
font = "Arial"
size = 10  # in millimeters
text3D = cq.Workplane("XZ").text(text_string, combine = True, font = font, fontsize = size, distance = 2).val()
@huskier huskier changed the title How to call Compound's siblings(...) function properly? How to access individual objects in a Compound object? Nov 29, 2024
@lorenzncode
Copy link
Member

access individual letter in the text3D Compound

You can access individual solid letters with iteration or unpacking.:

letter_o = [*text3D.val()][4]

or with selectors:

letter_nearest_point = text3D.val().solids(
    cq.selectors.NearestToPointSelector((0, 0, 0))
)

There are several ways to access the solids. It depends on your criteria for selection.
Here is an example sorting by volume with the Workplane sort method:

letters_by_vol = text3D.solids().sort(cq.Shape.Volume)
letter_largest_vol = letters_by_vol[-1]

@lorenzncode lorenzncode added the question Further information is requested label Nov 30, 2024
@huskier
Copy link
Contributor Author

huskier commented Dec 1, 2024

@lorenzncode Thank you for your help.

These methods are able to access the individual solid in the text3D Compound; however, we want to manipulate the individual solid which then affect the original text3D Compound. For example, we want to translate the letter "O" 100 in Z direction, and then we could get the changed text3D object with "O" letter translated 100 in Z direction. Can we achieve this effect with CadQuery?

import cadquery as cq
from cadquery.vis import show

text_string = "Hello, CadQuery!"
font = "Arial"
size = 10  # in millimeters
text3D = cq.Workplane("XZ").text(text_string, combine = True, font = font, fontsize = size, distance = 2).val()

letter_o = [*text3D][4]
letter_o.translate((0, 0, 100))
show(text3D)

letter_nearest_point = text3D.solids(
    cq.selectors.NearestToPointSelector((0, 0, 0))
)
letter_nearest_point.translate((0, 0, 100))
show(text3D)

@adam-urbanczyk
Copy link
Member

Currently a copy is made (I think in OCP, TBC). You'd need to recreate the compoud:

letters = list(text3D)
letters[0].move(z=10)
text3D = compound(letters)

@huskier
Copy link
Contributor Author

huskier commented Dec 1, 2024

letters = list(text3D)
letters[0].move(z=10)
text3D = compound(letters)

That's exactly what we want.

The code you provided could run as expected when the Compound is directly made by our own code; However, when the Compound object is imported from a STEP file (saved from our own code), then, we can not unpack the Compound object. The following is the MVP code. The STEP file "text_Hello_CadQuery_3D_asm.step" is saved from the commented code. We are working on an exploding function, and we need to access and manipulate both the Compound and the objects in the Compound.

import cadquery as cq
from cadquery.vis import show
from cadquery.occ_impl.shapes import compound

text_string = "Hello, CadQuery!"
font = "Arial"
size = 10  # in millimeters
text3D = cq.Workplane("XZ").text(text_string, combine = True, font = font, fontsize = size, distance = 2).val()

# letters = list(text3D)
# letters[0].move(z=10)
# text3D = compound(letters)
# show(text3D)

# text_Hello_3D_asm = (
#     cq.Assembly()
#     .add(letters[0], color = cq.Color(1, 1, 0), name = "H")
#     .add(letters[1], color = cq.Color(1, 1, 0), name = "e")
#     .add(letters[2], color = cq.Color(1, 1, 0), name = "l1")
#     .add(letters[3], color = cq.Color(1, 1, 0), name = "l2")
#     .add(letters[4], color = cq.Color(1, 1, 0), name = "o")
#     .add(letters[5], color = cq.Color(1, 1, 0), name = ",")
# )
# show(text_Hello_3D_asm)

# text_CadQuery_3D_asm = (
#     cq.Assembly()
#     .add(letters[6], color = cq.Color(1, 1, 0), name = "C")
#     .add(letters[7], color = cq.Color(1, 1, 0), name = "a")
#     .add(letters[8], color = cq.Color(1, 1, 0), name = "d")
#     .add(letters[9], color = cq.Color(1, 1, 0), name = "Q")
#     .add(letters[10], color = cq.Color(1, 1, 0), name = "u")
#     .add(letters[11], color = cq.Color(1, 1, 0), name = "e")
#     .add(letters[12], color = cq.Color(1, 1, 0), name = "r")
#     .add(letters[13], color = cq.Color(1, 1, 0), name = "y")
#     .add(letters[14], color = cq.Color(1, 1, 0), name = "!")
# )
# show(text_CadQuery_3D_asm)

# text_Hello_CadQuery_3D_asm = (
#     cq.Assembly()
#     .add(text_Hello_3D_asm, color = cq.Color(1, 1, 0), name = "Hello")
#     .add(text_CadQuery_3D_asm, color = cq.Color(1, 1, 0), name = "CadQuery")
# )
# show(text_Hello_CadQuery_3D_asm)
# text_Hello_CadQuery_3D_asm.save("text_Hello_CadQuery_3D_asm.step")

# text_Hello_CadQuery_3D = text_Hello_CadQuery_3D_asm.toCompound()
# text_Hello_CadQuery_3D_list = list(text_Hello_CadQuery_3D)
# print(len(text_Hello_CadQuery_3D_list))
# show(text_Hello_CadQuery_3D_list[0])
# show(text_Hello_CadQuery_3D_list[1])

# sub_text_Hello_CadQuery_3D_list = list(text_Hello_CadQuery_3D_list[0])
# print(len(sub_text_Hello_CadQuery_3D_list))
# show(sub_text_Hello_CadQuery_3D_list[0])
# show(sub_text_Hello_CadQuery_3D_list[1])

# The result of the above code is as expected! 

# The following code is not as expected. We expect that the text_Hello_CadQuery_3D_list  should be 2 at least, 
# but we get 1 instead.
text_Hello_CadQuery_3D = (
        cq.importers
        .importStep("text_Hello_CadQuery_3D_asm.step")
)
text_Hello_CadQuery_3D = text_Hello_CadQuery_3D.val()
print(type(text_Hello_CadQuery_3D))
show(text_Hello_CadQuery_3D)

show([*text_Hello_CadQuery_3D][0])
#show([*text_Hello_CadQuery_3D][1])

text_Hello_CadQuery_3D_list = list(text_Hello_CadQuery_3D)
print(len(text_Hello_CadQuery_3D_list))

@lorenzncode
Copy link
Member

Try flattening the compound, something like this:

def flatten(obj):
    if not isinstance(obj, Compound):
        yield obj
    else:
        for child in obj:
            yield from flatten(child)


print(len(list(flatten(text_Hello_CadQuery_3D))))  # 16

@huskier
Copy link
Contributor Author

huskier commented Dec 2, 2024

@lorenzncode Thank you for your help.

The flatten function has partially solved the problem. However, the flatten function can not keep the feature tree's hierarchical structure.

Right now, the flatten function could be my first stage solution.

Thank you for both of you, @adam-urbanczyk and @lorenzncode.

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

3 participants