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

Simple square bar frame assembly doesn't line up #1025

Closed
spike77453 opened this issue Mar 10, 2022 · 7 comments · Fixed by #1063
Closed

Simple square bar frame assembly doesn't line up #1025

spike77453 opened this issue Mar 10, 2022 · 7 comments · Fixed by #1063
Labels

Comments

@spike77453
Copy link

If I'm not mistaken, this should produce a simple square bar frame:

import cadquery as cq

def make_beam(size, length, draft_angle):
  beam = (
    cq.Workplane("XZ")
    .rect(size, size)
    .extrude(-length)
  )

  if draft_angle != 0:
    beam = (
      beam
      .faces(">Y").workplane().transformed(offset=cq.Vector(size/2,0,0),rotate=cq.Vector(0,-draft_angle,0))
      .split(keepBottom=True)
      .faces("<Y").workplane().transformed(rotate=cq.Vector(0,draft_angle,0))
      .split(keepBottom=True)
    )

  return beam


y_beam = make_beam(length=800, size=60, draft_angle=45)
x_beam = make_beam(length=700, size=60, draft_angle=45)

frame = (
  cq.Assembly()
  .add(x_beam, name='X_front')
  .add(x_beam, name='X_back')
  .add(y_beam, name='Y_left')
  .add(y_beam, name='Y_right')

    # Constrain beams to make frame
  .constrain('Y_left@faces@<Z', 'X_front@faces@<Z', 'Axis', param=0)
  .constrain('Y_left@faces@<Y', 'X_front@faces@>Y', 'Plane')

  .constrain('Y_left@faces@<Z', 'X_back@faces@<Z', 'Axis', param=0)
  .constrain('Y_left@faces@>Y', 'X_back@faces@<Y', 'Plane')

  .constrain('Y_left@faces@<Z', 'Y_right@faces@<Z', 'Axis', param=0)
  .constrain('X_front@faces@<Y', 'Y_right@faces@>Y', 'Plane')
)
frame.solve()

if "show_object" in locals():
  show_object(frame, 'frame')

However, the result looks like this:
frame_1

Changing

.constrain('X_front@faces@<Y', 'Y_right@faces@>Y', 'Plane')

to

.constrain('X_back@faces@>Y', 'Y_right@faces@<Y', 'Plane')

(which just moves the constraint from the front right corner to the back right corner) fixes the issue.

Including

.constrain('X_back@faces@>Y', 'Y_right@faces@<Y', 'Point')

(which should overconstrain the assembly) also works:
frame_2

It's quite possible that I'm missing something obvious here but as this point I think this might also be a bug.
The problem with both "fixes" is that once the assembly becomes more complex, things break apart in different corners again for no obvious (at least to me) reasons:
frame_3
vs.
frame_4

@bernhard-42
Copy link
Contributor

@spike77453 FWIW, to avoid numerical solving for assemblies, I created MAssembly.
It works a little bit like you would assemble some IKEA furniture:

The first step is to define mates, i.e. connection points (the color and loc parameters are for demonstration only):

image

Then assemble part after part like in the physical world:

image

The assemble algorithm is rather simple, that means one needs to take care of the order of assembly (again, as in the physical world)

MAssembly is supported well in Jupyter-CadQuery, which allows to render mates.

@spike77453
Copy link
Author

@bernhard-42 This is bloody brilliant! Thanks so much for making that and also taking the time to leave a comment here. I've played around with the examples and it seems like this is exactly what I need.

@lorenzncode
Copy link
Member

With the original contraints in the description, the final cost is 89.35, after 2000 iterations.
The result improved increasing MAXITER.

I experimented with the following constraints (b in table) and got a better result. Applying Axis to the X face made sense to me because the front/back left/right beams are mirrored.

    .constrain("X_front@faces@>X", "X_back@faces@>X", "Axis")
    .constrain("Y_left@faces@>X", "Y_right@faces@>X", "Axis")

    .constrain("Y_left@faces@<Y", "X_front@faces@>Y", "Plane")
    .constrain("Y_left@faces@>Y", "X_back@faces@>Y", "Plane")

    .constrain("Y_right@faces@>Y", "X_front@faces@<Y", "Plane")
    .constrain("Y_right@faces@<Y", "X_back@faces@<Y", "Plane")
Constraints Cost Iterations
orig 89.35 2000
orig 5.20e-4 5000
orig 5.64e-6 9125 (10K max)
b 1.02e-3 2000
b 8.22e-10 5000
b 2.81e-12 6413 (10K max)

@lorenzncode
Copy link
Member

I experimented with DIR_SCALING and found the result improved with increase in the DIR_SCALING value.

Constraints Cost Iterations DIR_SCALING (100 default)
orig 89.3 2000 100
orig 1.84e-07 2000 200
orig 1.72e-13 992 400
orig 2.83e-13 415 800

@lorenzncode
Copy link
Member

I'm testing changes to automatically set dir_scaling to potentially resolve this issue. Here is a plot of solver iterations and cost vs dir_scaling (and dir_scaling / max_dim).

dir_scaling_200

max_dim is set to the maximum value of the BoundBox DiagonalLength over all constrained objects. Here an appropriate value of dir_scaling is ~2*max_dim.

dir_scaling_21

@adam-urbanczyk
Copy link
Member

Interesting, but TBH I think that the current implementation is just flawed. FYI I'm working on a new version here: https://github.com/CadQuery/cadquery/tree/assy-casadi

@lorenzncode
Copy link
Member

Ah, good to know! I'm happy to do some testing when it is ready.

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

Successfully merging a pull request may close this issue.

4 participants