Steps forward to assembly infrastructure

Hello all,

after an epic underestimation of needed time and quite some headaches today the extension code has been merged, and with this yet another large step for the assembly infrastructure has been taken. I like to talk a bit about the next steps as I think they are needed, and hope to get some guys to hepl me out on this (hust deepsoic hust). So here is what I belive should be done next:

  1. PartDesign adoptions. Here the extension code started and we should finalize our goals
  2. Port PartDesign Body to use OriginExtension so that it can be recognized as a local CS in a uniform manner.
  3. Move the Body specific “linear Group” features to the Group extension. I think this special behaviour with a defined (and changable) order is needed at many places, having it in the extension and being able to choose the behavior by developers is worth it
  4. App adoptions
  5. Prevent links into different coordinate systems. There should be a special link property which allows this, but in all default cases this should be forbidden.
  6. Port tools to recognize local coordinate systems. Thinks like duplicate for example should add the new object to the same CS as the initial one
  7. Document dependencies
  8. Port Jriegels adoption of the document graph to master, as it is better suited for the now often needed bottom-to-top traversal
  9. Extend Document/DocumentObject API for stacked coordinate systems, e.g. add methods to get all possible paths to the object (as it can be used multiple times), calculate global placement, calculate placement in other CS etc. . This all needs enhanced graph handling.
  10. Extend Selection to also store path of selected object (as it can be used multiple times and the multiple instances only differ in the CS)
  11. Adopt importing/exporting to use the CS
  12. Gui adoptions
  13. Enable the tree to show a object multiple times

Any additional input for needed code? Personally I like to work on point 3 next and have already started to port jregels code over. . DeepSOIC, you already have done quite some work on point 1. Would you like to help out there?

That’s great news ickby. :slight_smile:

Had to look it up. Take your syrup and stop coughing in German, ickby! :smiley:

OK, now I saw this!

As for 1, all I’ve done is not what you want - I have another body aimed at non-linear things like Part features. I don’t know what else is this linear stuff is needed for (Path?), but I now will include that linear layer to structure.
Without extensions, I see it like:
Part::BodyBase: a container that has shape, Tip property, and maybe origin. Not to be used directly.
Part::Body: the general-purpose body container aimed at Part features, Draft, etc.
Part::SequenceBody: the body to support/manage linear part-design-like sequences
PartDesign::Body: PartDesign’s specialization of Part::SequenceBody
How to deal with extensions here I’m not yet sure…

As for 2, I have a quite big plan, I will try to post it soon to start a discussion, as it potentially influences other stuff.

As for 3, I have a strong opinion against having a feature in multiple containers at once. I think an instance object should be used instead, to have one object displayed in multiple places. I think this needs discussion.

Ok I’m looking forward to your ideas. I’m postponing all work on that till then.

If the work you done on bodies is not easily transferable to the new System it is of course also ok to Split work differently if you are more interrested in the other points. I’m rather indifferent to what I’m Work on next.

Good to see this progressing and i guess more discussion will be needed in the future around point 1. To align different views better. Likely once the debate will go further it will be easier to diagnose if there is fundamental difference in views or just specific details. Hopefully it is only about specific details and single solution will prevail.

As we can hardly cope with things like having one new container. Having 2 of them would further add to the complexity and what would be next? Building docker for all this containers? :wink:

Here’s my proposal, mostly relevant to point 2. Many elements of this proposal have been influenced by my experiment called Part-o-magic.

  1. Container tree or container graph?
    With tree, it is easy to figure out the container path to an object given just the object. That makes things simpler, because otherwise we must implement link type that will track changes to container tree. And in general, I think this makes things simpler and easier to manage overall.

As for full graph support for containers, it’s not clear to me, what it should be used for. Multiple instances of same part? Like:

Part
    solid1
    solid2
Part001
    solid1
    solid2

If that is supposed to present two instances of the same part, it’s unclear how would we add another solid to both parts. Multiple active containers at once? I don’t know.

So I would like to see Part - Instance pair. However, that has problems too. For example, how do I make a derivative of a Part, which is slightly different (e.g. has a number engraved onto some surface)? That will require another kind of object, something like “DerivedPart” container, and I’m not sure how to organize it.

  1. Single active container.
    There should be exactly one active container, because dealing with current situation that allows two active containers, one of type Body and one of type Part, doesn’t make any sense IMO. What if we have active body that doesn’t belong to active part, what is it for?

It’s enough to have only one, because from it, we can easily figure out all upper level containers in case we need them.

If container structure is to support graphs, active container has to store the whole path, not just pointer to object.

  1. Introduce ActiveContainer as member of App, so that we can use “App.ActiveContainer.addObject(‘class’,‘name’)”. And then, replace all “App.ActiveDocument.addObject(..)” with calls to ActiveContainer, in all other workbenches.
    That will allow object type filtering on container side. For example, if I try to add a Part Cube when I have a PartDesign Body active, App.ActiveContainer.addObject is to throw an error that this container doesn’t accept this object.

A more ugly approach to this problem is to re-use App.ActiveDocument for App.ActiveContainer. This will have a benefit that no porting of other code is necessary, but is ugly because the attribute is named not according to what it actually is.
A compromise approach is to make App.ActiveDocument.addObject(…) act as if it is App.ActiveContainer.addObject. Again, no porting of other workbenches is necessary, and there is no terribly wrong naming.

  1. Selection
    I think, this is the easiest way to ban cross-container links: make getSelection and getSelectionEx, by default, only return selected objects from active container. And make it return more only when supplied with additional argument, like so: Gui.Selection.getSelection(App.ActiveDocument)

Next tricky part is selection of objects in instances. This is definitely needed for assemblies, and I don’t know how technically easy it is to do that. Maybe, for each selection object, provide a Path attribute, something similar to coin’s SoPath.

This will require specialized link type. If we are not dealing with graph container structure, but with tree, then it’s enough to provide the actual object and the list of instance objects to use when resolving placement. If we go for full graph, then it becomes necessary (I think) to include the whole container path into the link, and track/update it as the container structure changes. I think this tracking is very tricky, and this is the main reason to stick with container tree rather than graph. If you have a solution to that, then we can go for full graph of course, as it is more flexible.

I think that’s it for now.

summary (my preferred way).

  • Container structure must form a tree. Multiple instantiations of objects are to be realized with Instance object, which is NOT a container. Instance object is to have special meaning for new propertylink type.
  • only one active container. Expose it in very accessible App.ActiveContainer (not hidden as some attribute of viewer), and it must work without Gui.
  • App.ActiveDocument.addObject is to act as if it were App.ActiveContainer.addObject (and there must be a way to suppress it, maybe extra argument). → no porting of other workbenches required
  • Gui.Selection.getSelection() should return the selection limited to active container. Gui.Selection.getSelection(App.ActiveDocument) should be required to get full selection. This automatically provides the general ban for cross-container links.
  • Selection is to be able to provide path by listing instance objects the actual selected object is to be reached through.
  • new PropertyLink type, to store list of instances through which the linked object is to be reached. All container-related transformations can be computed on the fly, so they should not be stored by the property.


    More minor points:
  • ShapeBinder should become the tool to make cross-container shape links. I propose to move it to Part module.

Great plans guys!

My 2 cents:

This seems very PartDesign-centric for me, I think many parts of freecad will still work totally outside local CS infrastructure… And there are probably a lot of external workbenches that would need to have their code changed.

I made already some experiments with changeable Group order in the Path WB (the Path Compound object), indeed that would be useful in many places. Would be a good example for an extension :slight_smile:

This is not too obvious for me either… The tree is indeed something super simple and clear to the user. Graphs can be quickly really messy and not understandable. On the other hand, since a long time models are not purely linear anymore in FreeCAD, and the tree doesn’t reflect that at the moment. I don’t like the object appearing multiple times in the tree either, this is hard to solve… :neutral_face:



Wouldn’t that more or less impose PartDesign style to the whole FreeCAD? I think we should take care to avoid that. For ex, I’m not convinced yet that working with containers is appropriate in Arch… There are just too many independent objects that don’t really fit into a same structure. I didn’t really make my mind yet, but I think it might be safe to consider that many workbenches will not adopt the whole PartDesign/Body/Container paradigm.

I don’t know what do you mean by “PartDesign style”. Containers are here to isolate things. This isolation is needed because containers have placement, but that placement is not directly applied to contained objects. Placement leads to:

  • the object that links to object in another container in geometric manner must take care of the difference in placements explicitly. It can simply ignore placements of containers, which is how things happen now. This is very counterintuitive, but I also find it very useful.
  • the Placements of all containers crossed by link thus becomes something your feature is implicitly depending on. If handled simply, by declaring these dependencies as OutList, this will very likely cause a dependency loop.
    So, the isolation is here to solve it by avoiding it, and special tools (like PartDesign ShapeBinder) are going to be needed for crossing container boundaries. Then, a slow process of porting many many tools to directly support cross-container links will take place.

But. We can have unmoveable containers (like DocumentObjectGroup), that won’t be isolated.

EDIT: container is like a subdocument. Within a container, a regular graph of features can be established. If you don’t use containers at all, FreeCAD would behave exactly as it did before.

And the movable geometry not being put in container will have basically the same assembly capabilities support as the one put in container? If yes good if no Yorik has a point.

I think we must get the terminology clear here, I think calling everything container is missleading as they include totally different behaviour. Thinking in coordinate systems is more appropriate. A normal Group as it is used now is not a local Coordinate system and hence links between those groups are of course possible. So current behavior does not change at all. But the newly introduced local coordinate systems (GeoFeatureGroup and OriginGroup) must not have links in between. The reason is simple: Their child position in the global CS depends on the local CS. Now imagine you have a boolean which has one tool in CS1 and annother in CS2. If one moves CS1 the boolean must change. Two reasons this is bad: It is completely strange for the user as he expects to have separated things and also nearly impossible to implement in FreeCAD, as it does not fit in the recomputation model.


  1. Container tree or container graph?

Let’s call it coordinate-System tree or coordinates-ystem path :slight_smile:. I think our both approaches are not that different. I think we agree that it must be possible to reuse objects and whole assemblies in an efficient manner. What I don’t like about the InstanceObject.

  1. This works well for individual DocumentObjects. But assemblies will reuse subassemblies multiple times (e.g. wheel assembly is used 4x in car). In your design you make a InstanceObject of a Subassembly. This gets IMHO confusing very quickly, as you can’t open a subassembly in the tree to see what is inside etc.
  2. The internal structure does only know exactly one position of a Object. Even if it is used in multiple instances and seen multiple times on the screen by the user, it is impossible to get more than the single original position. So what you have done is to virtually allow a object to be at multiple positions like my graph, at least the user see it like that, but deny that code wise and make it impossible to handle. Getting the global position of a shape? Not possible.
  3. When selecting something in the 3d view, it must select the original object. Or the instance? A bit confusing.
  4. What if document formats allow to reuse subassemblies at multiple positions? I think STEP does that (need to double check that) and so we would loose an easy import/export and must change the document structure
  5. Operations would always be done on the orginal object in the 3d scene and not where the user selects it. For example the user selects a sketch in the Instance of a Part. Than he hits “edit”. The edit mode would be at a totally different position in the 3d scene as it edits the original sketch. such behavior is strange.

My main argument is point 2. Assemblies will always reuse objects and whole assemblies at multiple positions. That is just how it works and how everyone want to use it. Not reflecting that in the code will bring all kind of problems. We should accept that fact and making handling it in the code work well by providing everything needed for the graph structure. An object just will be in multiple positions, we must be able to retrieve those and handle those.

  1. Single active container.

I like this idea. I agree that the active Part/ active Body is not appropriate anymore. I think JRiegel envisioned the structure a bit different to what we are doing now, so IMHO we are free to change “active” stuff as needed.

  1. Introduce ActiveContainer as member of App, so that we can use “App.ActiveContainer.addObject(‘class’,‘name’)”. And then, replace all “App.ActiveDocument.addObject(..)” with calls to ActiveContainer, in all other workbenches.
    That will allow object type filtering on container side. For example, if I try to add a Part Cube when I have a PartDesign Body active, App.ActiveContainer.addObject is to throw an error that this container doesn’t accept this object.

I like the ActiveContainer object. The general Idea is very good, seem very “Freecad’ish”.

  1. Selection

Need to think about that stuff, seems good on first thought.

Thanks for the explanations! I think I begin to understand better and agree with both of you about containers and not allowing to link from one to something inside another. It is a new kind of object after all, it makes sense to decides its rules early…

I like App.ActiveContainer too, rockn worked on something like that with groups some time ago (that you would have an “active group” and every new object would go in it automatically).

About the instance issue, I’m more of ickby’s opinion too. A typical example for me: You have a sloped roof, and you have walls under it. You would want to use the roof object to “trim” the walls so they meet the roof line and don’t go above it. But it seems weird to me that each wall would need a separate instance of the roof to perform the subtraction…

And if the objects that are used multiple times appeared multiple times in the tree, but with a different aspect/color, and would behave differently? For example, you would click one, and they would become all selected together in the tree…

Yorik, I think you got me wrong. My intent is that Instance is like Draft Clone of a group. Except that it is very lightweight, in that it only renders the part in another place and provides special selection behavior. For the rest, the usual graph way of doing things will still hold; what I’m against is a graph structure of containment. I think it’s very confusing.

Well, that can be fixed easily by making the instance to claimchildren the contents of Part it is linking to. But I actually would like it to not show its contents, just for emphasizing that this is just a copy, that can’t be edited independently.

I already said how it is to be done: Selection object should include the list of instances, through which the object was selected. Nothing impossible.

Kind of both, see above. In graph container structure, you still need to know which containers the object was selected through, so they are kinda selected, just like the instance. If we are to stick with tree, we don’t need to keep track of containers, only instances.

Same happens with containers, no? Either edit mode has to remember the CS path to selected object. In my case, it has to remember only the instances though which it is activated; in your case it will have to remember the whole container path. But in my case, if sketch editing doesn’t take care about instances, it will still work correctly, even though displayed in a slightly unexpected place (but not totally unexpected, because it will be the place where the sketch was originally drawn at, which isn’t a totally unexpected place).

Suggestion on terminology for now.

CONTAINER, or more specifically PLAIN CONTAINER, or maybe UNMOVEABLE CONTAINER = document object that can be activated to receive newly created objects. I would also like it to parent all viewproviders of contained objects, so that it controls their visibility.

LOCAL CS = document object that has placement, and renders some other object(s) in the new place. In Lattice it is called simply “Placement”.

LOCAL CS CONTAINER, or MOVEABLE CONTAINER = a container with placement.


“Part” object is a local CS container. Body is, too.
DocumentObjectGroup is a container, but not a local CS.
Free links are to be forbidden across local CS containers. Free links are to be allowed across plain containers.

I think your example is a different thing altogether. At least from the non-programmer end user point of view.

For me, a typical instance example is an assembly of parts that contains 50 identical bolts. Or a few identical parts or sub-assemblies. What you are describing is more about a single object linked to multiple other objects.

OK, thanks to Norm, I have another advantage about going for instances. BOM! (BOM = bill of materials).

I have found one more reason to have instances. Array (aka pattern)! An array can be a subclass of Instance, that renders Part in multiple places in a pattern.

If i understand different views correctly.

  • Container provide local coordinate system to features and features added to containers use that local coordinate system.
  • Moving around the whole container therefore doesn’t affect features placement inside container.

Hopefully this is correct and hopefully it doesn’t have to be more complex than that. That is having movable/non-movable, PartDesign/Part and placed/super-placed containers in the future. :wink:

Anyway using link to external geometry between features in different containers in theory should be possible but as the use case is small and implementation effort is substantial and due to potential problems this won’t be allowed. As for using expressions. That will be possible and end users will decide when to use it or not.

After this point you two start to have slightly different view on how should container be used in assembly.

@DeepSOIC

You propose “instance object”. I see this as new assembly feature similar to Draft Clone. The feature itself is more or less the result of everything in the container.

@ickby

You propose to have a system for using container with all its features directly. Therefore using a container in assembly should add the whole feature tree from the inside of the container to the assembly tree.

If i got this correct the main difference i see is when using single part or sub-assembly in assembly the workflow remains similar to the one used in PartDesign. That is you can easily select a feature in the assembly tree view and edit it in place. Using “instance object” on the other hand likely does simplify some things but editing features inside container will likely be considered as a separate operation done outside the assembly tree.

The different views is on how to make copies of Parts for assemblies.

My way: create multiple instances of same object using specialized Instance object (instance object is mimicked by clone in this example, as one isn’t yet implemented). My way allows to simplify container structure to a tree, which I think makes things much easier to understand (one object can only belong to one container).
instance by instance object (clone).FCStd (4.87 KB)
instance by instance object (clone).png

ickby’s way (probably originated by jriegel). This requires full graph support for container structure (one object can belong to many containers)
instance by multiple containment.FCStd (3.85 KB)
instance by multiple containment.png
More differences:
DeepSOIC: there is a master Part, that is editable, and instances of the Part, that aren’t editable. Deleting master part will delete actual geometry, and break all its instances.
vs.
ickby: there is no master part. Any “instance” of part is just another part re-using some objects. Any “instance” can be deleted with no consequences; only when the very last instance that uses the objects is deleted, the actual objects (geometry) will get lost.

I hope I got it right :mrgreen: and I hope it should become clearer.

I will look into it tomorrow in more detail. It does look like we are progressing. Now there are nice images involved and that for sure makes things easier to understand. :wink:

And i was wondering could an estimate in man years be made:

  • Assembly2 reviewed, solver improved and additional constraint or two added.
  • @DeepSOIC way.
  • @ickby way.

Thanks.

Without pretending to understand the whole issue, I think this is where implementing specific file extensions would have a huge advantage: for example *.FCPart for a document containing a single part, .FCAsm for an assembly file linked to other files. All the major commercial parametric CAD programs do that. They even have a separate file extension for drawings (.FCDwg :smiley: ).

In the FCPart document tree, you see everything that you normally see in the current FCStd document: Part container, bodies, other stuff.

In the FCAsm document tree, there is no need to display the full structure of an imported FCPart (what’s nested under its container), only the container itself is shown. Okay, maybe its PartOrigin as well. It is thus possible to have multiple instances of the same part (or even assembly) listed in the tree but not make it very complex. All that is needed is to append some suffix with a digit indicating which instance it is. To differentiate from the usual 3 digits added to primitives or features (Pad001, Pad002…) and make it visually clear they are instances, maybe some separator could be added, or the suffix placed inside parenthesis.

M6x25_Bolt(001)
M6x25_Bolt(002)
M6x25_Bolt(003)

The CAD software I use at work adds a colon:

M6x25_Bolt:1
M6x25_Bolt:2
M6x25_Bolt:3

Editing the part would open the FCPart document separately. It could be done from any instance shown in the tree, not only from the “original” one.

Now that I think about it, it is exactly what the Assembly2 module does.