-
Notifications
You must be signed in to change notification settings - Fork 306
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
Assembly hierarchy #534
Assembly hierarchy #534
Conversation
Radius selector
* Refactor tags * Use pyparsing for query parsing and implement tags * Black and mypy fixes * Allow "_" in names and tags * Added tests * Fixed constrain with tag test * Better docstring for _query * Typo fix
* Added and cleaned up some docstrings for Shape * a typo
* Initial version of the constraints docs * Adding the assy section * apireference cleanup * Include cutouts * Roadmap cleanup * _query cleanup * Black fix * Add RadiusNthSelector again * Typo fix * Paste error fix * Show detailed selector docs * Another typo fix * Mention other constrain overload * Added test for end()
[WIP] - Trying to move to env variable in Appveyor
Codecov Report
@@ Coverage Diff @@
## assembly-tags #534 +/- ##
=================================================
+ Coverage 94.08% 94.53% +0.45%
=================================================
Files 30 29 -1
Lines 6794 6994 +200
Branches 765 873 +108
=================================================
+ Hits 6392 6612 +220
+ Misses 257 230 -27
- Partials 145 152 +7
Continue to review full report at Codecov.
|
* Add the ability to mirror from a selected face * Fix errors in example.rst * Add mirror from face features (with example now fixed) * recommit merge conflicts in shape.py that were stopping CI merging into master * use correct variable name when selecting face * formatting with black v19 instead of black v21 * add missing string literal types and clean up docstring * Add volume assertions to the test cases * black formatting * Mypy fix * Restructured mirror * update examples to use basePoint instead of basePointVector * Add tests for other workplane strings and non union option to increate code coverage * Add test to check that exception is thown for incorrect input * Go back to basePointVector * Update arg naming in examples * Mirror all objects * Typo fix Co-authored-by: Adam Urbańczyk <adam-urbanczyk@users.noreply.github.com>
Fixed code typos in revolve example
* Change to ProjectedOrigin * Fix most tests * Fix remaining tests * Fix example building * Update selector docs * Fix the bottle+formatting changes * Renamed centered to xycentered * Ignore utils for coverage calculation
Any clue why travis is not triggered? |
Use delimitedList
That's strange. Travis ran fine yesterday for the ProjectOrigin PR. I wonder if the GitHub webhook is having trouble right now. |
this PR is not to master. |
@adam-urbanczyk Final comment on the nested assemblies: I think the assembly tutorial in the docs would need a little update to reflect the nested ids now. |
@adam-urbanczyk Looks like the commit "More strict grammar" breaks For line 285, I think, instead of name: str = query.name it should now be name: str = PATH_DELIM.join(query.name) |
@bernhard-42 I want to get rid of the root in the names and thus keep the original convention for non-nested assemblies. Are you OK with that? Also I'd like to revert the copy changes to not alter the ids of the objects and in the end only keep that paths in the objects dict. |
That should be possible and - the more I think about it - I would also prefer to do so, but I need to look into it.
I also wasn't too much of a fan of it (especially due to uuids in 1 Remove the top levelGiven my example above, assume that there would only be {'bottom': <cadquery.assembly.Assembly at 0x7efdcf51fc10>,
'top': <cadquery.assembly.Assembly at 0x7efdcf51feb0>,
'front_stand': <cadquery.assembly.Assembly at 0x7efdcf51ffa0>,
'back_stand': <cadquery.assembly.Assembly at 0x7efdcf51f7c0>
} Pros:
Cons:
2 Consistency with nested assembliesIf I now add a leg, the naming should not suddenly change for the 2., 3. and 4. element just because it is now a nested assembly. So I'd say the root element then should always be omitted, leading to: {'bottom': <cadquery.assembly.Assembly at 0x7efdcf51fc10>,
'top': <cadquery.assembly.Assembly at 0x7efdcf51feb0>,
'front_stand': <cadquery.assembly.Assembly at 0x7efdcf51ffa0>,
'back_stand': <cadquery.assembly.Assembly at 0x7efdcf51f7c0>,
'right_back': <cadquery.assembly.Assembly at 0x7efdcf51fd60>,
'right_back/lower': <cadquery.assembly.Assembly at 0x7efdcf52e3a0>,
'right_middle': <cadquery.assembly.Assembly at 0x7efdcf51ff70>,
'right_middle/lower': <cadquery.assembly.Assembly at 0x7efdcf52e700>
} Pros:
Cons
3 Alternative: Keep the hierarchy, but make root optional in addressingTo correctly reflect internal dependencies (hierarchy) with the naming convention, keep the root element in the name, i.e. {'bottom': <cadquery.assembly.Assembly at 0x7efdcf51fc10>,
'bottom/top': <cadquery.assembly.Assembly at 0x7efdcf51feb0>,
...
} however, allow addressing with omission of the top level assembly root element (not possible for nested assemblies of course): _query("top")
_query("right_middle/lower") would be shortcuts for the also valid _query("bottom/top")
_query("bottom/right_middle/lower") This would also help avoiding uuids in empty root: a = Assembly().add(obj1, name='obj1').add(obj2, name='obj2') would internally still be {'7b7e6636-3d0d-11eb-bdae-3497f65b2717': <cadquery.assembly.Assembly at 0x7f544ce1b820>,
'7b7e6636-3d0d-11eb-bdae-3497f65b2717/obj1': <cadquery.assembly.Assembly at 0x7f544cea67f0>,
'7b7e6636-3d0d-11eb-bdae-3497f65b2717/obj2': <cadquery.assembly.Assembly at 0x7f544d2faf10>} allowing to change the location of _query("obj1")
_query("obj2") Pros
Cons
4 Mandatory name for assemblies (optional point)This has nothing to do with 1) - 3), more of a sidetrack: I personally would make the name element mandatory and get rid of uuids. They are a great invention, but not very human friendly. Forcing users to write a = Assembly(name="assy").add(obj1, name='obj1').add(obj2, name='obj2') is not much of a change, but would lead to something more readable/addressable. Especially for cases like a = Assembly().add(obj1, name='obj1').add(obj2, name='obj2')
b = Assembly().add(obj2, name="obj3").add(a) since {'2d59d6d0-3d16-11eb-bdae-3497f65b2717': <cadquery.assembly.Assembly at 0x7f544cedfca0>,
'2d59d6d0-3d16-11eb-bdae-3497f65b2717/obj3': <cadquery.assembly.Assembly at 0x7f544ce2eeb0>,
'2d59d6d0-3d16-11eb-bdae-3497f65b2717/2d59cc12-3d16-11eb-bdae-3497f65b2717': <cadquery.assembly.Assembly at 0x7f544ce2ed30>,
'2d59d6d0-3d16-11eb-bdae-3497f65b2717/2d59cc12-3d16-11eb-bdae-3497f65b2717/obj1': <cadquery.assembly.Assembly at 0x7f544ce2eee0>,
'2d59d6d0-3d16-11eb-bdae-3497f65b2717/2d59cc12-3d16-11eb-bdae-3497f65b2717/obj2': <cadquery.assembly.Assembly at 0x7f544ce2ea60>} is really ugly. You could argue that this is the developers choice. Right, however forcing for better style often isn't a bad idea ... SUMMARY
What do you think? |
In your nomenclature 2 is what I meant. I'll open another PR to show what I have in mind in terms of code. The objects dict was not supposed to encode parent/child relations and reflect the loc structure in the original design. This is encoded in the |
Could you take a look if #545 works for your use case? |
It works. However, for my use case to work I had to split |
OK, I added the call to |
Thanks for the For holes I would like to select the center of the hole (i.e. the center of the circle on one face) as the origin of a mate and the self.pnt = obj.val().Center()
self.x_dir = obj.workplane().plane.xDir
self.z_dir = obj.workplane().plane.zDir It did work very well for all my test assemblies. If I now start from |
How is this supposed to work with holes that have a different normal than your workplane? I think you want to get the actual normal using e.g. |
Understood, the normal would be the z direction. To be more concrete, to get z-dir for a selected object that is a
and for x-dir in both cases something like If this approach is OK, then I think I don't need to something |
To be honest the x-direction has to be defined by the user in the most general case. For example given a hexagonal hole you cannot tell a priori which xdir is the desired one. |
I think I just need a defined x-dir, so cross product of normal and (1,0,0) might do it. You can add rotations when you define mates, so the user can finally control it => I will do some tests. |
btw. just to mention, that's why I thought about using the x-dir of the face the circle is on. Just to have a defined direction that makes sense in the context of the face. |
I now had time to try out some ideas, especially on objects which faces are not parallel to the world coord-system planes. Looks like the current Assume I want to place a mate at the center of a face with workplane = cq.Workplane().transformed(rotate=(30, 45, 60)) # rotate for the sake of the argument
box = workplane.box(5,10,15)
a = cq.Assembly(box, name="box")
face = box.faces(f">{workplane.plane.zDir.toTuple()}") If I would get back the actual workplane object from I fully buy into your point around normals of holes and planes, but even for the simple example above I still think that a split into |
Thanks @bernhard-42 for the feedback, you selected an example where the object is clearly aligned with the local coordinate system. How would the res = (
cq.Workplane()
.rect(2,1)
.extrude(.5,taper=45)
.rotateAboutCenter((0,0,1),45)
.faces('>(1,1,1)')
) |
@adam-urbanczyk This is a great example to prove that my approach is wrong, thank you! I wasn't able to come up with one myself. I think I need to base my Mate definitions on the normals of faces and circles returned by But I need to think more about it. I don't think there is a "correct" way to select |
@adam-urbanczyk To continue where we stopped some weeks ago, this is the current logic for creating Mates: self.origin = val.Center()
if val.geomType() in ["CIRCLE", "ELLIPSE"]:
self.z_dir = val.normal()
vertices = val.Vertices()
if len(vertices) == 1: # full circle or ellipse
# Use the vector defined by the circle's/ellipse's vertex and the origin as x direction
self.x_dir = sub(vertices[0], self.origin)
else: # arc
# Use the vector defined by start and end of the arc as x direction
self.x_dir = sub(vertices[1], vertices[0])
elif isinstance(val, Wire):
self.z_dir = val.normal()
vertices = val.Vertices()
if len(vertices) == 1: # e.g. a single closed spline
# Use the vector defined by the vertex and the origin as x direction
self.x_dir = sub(vertices[0], self.origin)
else:
# Use the vector defined by the first two vertices as x direction
self.x_dir = sub(vertices[1], vertices[0])
elif isinstance(val, Face):
self.z_dir = val.normalAt(val.Center())
# x_dir will be derived from the local coord system of the underlying plane
xd = val._geomAdaptor().Position().XDirection()
self.x_dir = Vector(xd.X(), xd.Y(), xd.Z())
else:
raise ValueError("Needs a Face, Wire, Circle or an Ellipse") I am not saying that these rules are the only ones or the correct ones, however, the idea is that for faces, circles, ellipses and wires the xaxis never appears random to the user but follow a clear set of rules. Does this make sense for you? |
I'm going to close this since it has been in limbo for almost 4 years. Feel free to reopen if needed. |
As discussed in #522, here is a pull request that:
_query
into_query
and_query_workplane
(as needed for my mate approach)My hexapod example with pure cadquery Assemblies
now has the following
hexapod.objects
: