Python Topological Namer/Tracker

Here's the place for discussion related to coding in FreeCAD, C++ or Python. Design, interfaces and structures.
Forum rules
Be nice to others! Respect the FreeCAD code of conduct!
ezzieyguywuf
Posts: 656
Joined: Tue May 19, 2015 1:11 am

Python Topological Namer/Tracker

Post by ezzieyguywuf »

Hello everyone. As some of you are aware, I did a bit of work last year looking into OpenCascade's built-in topological naming library, i.e. TNaming. Since then, I've been a bit busy getting married and stuff, but I've come back to this problem and decided to try and approach it from a different angle. Let me explain why.

The main issue I have with OpenCascade's TNaming solution is that it is a 'black box'. I've spent time looking at the source code, but it is poorly documented and rather cryptic in its approach. The more I dived into it, the more confused I got. This became a problem mostly during debugging/development of the FreeCAD Topological Naming solution. It was so difficult to obtain sane, sensible error messages from the OCC TNaming module that it ended up being not very fun at all to continue development using this library. At the end of the day, if we HAVE to use OCC TNaming, I suppose we could make it work. But I wanted to take a break and try something else.

So, that's what I did. Based on some papers I have read (suggested by some of you), it seems that a sensible way to address the Topological Naming issue is to keep track of all the faces in your Shape. If you have a history of all your Faces, any given Edge can be identified by the two Faces that make it up. The hard part is keeping track of changes to your Faces: has a Face been modified? Has it been deleted? Has a new face been added?

Fortunately, this is an area in which OpenCascade can help: most (all?) off the classes in OpenCascade that modify a solid provide methods that detail the changes to the Faces in that solid: in fact, that is the bases with which the TNaming module is built. In order to use the TNaming module, I was forced to create a list of Modified/Deleted/Created Faces and pass that information along to the TNaming module.

So, what I decided to do was take the TNaming module out of the equation and develop my own Topological Naming tracker that uses the same OpenCascade faculties (i.e. the list of Modified/Deleted/Created faces) to keep track of the topology. I decided to create this tracker in python because I am most familiar and comfortable with coding in python, therefore it would be more fun (for me) as well as quicker to Develop->Test->Modify my code.

You can see the current state of this code base here on github. The code itself consists of 5 classes, but the majority of the work is done by the TopoTracker class. I created some Mock classes to mock the FreeCAD and OpenCascade functionality that this tracker relies on: this makes development faster because I don't have to compile FreeCAD every time that I make a change or develop a new feature.

If you'd like to get an idea of how the tracker works at the very high level, take a look at the testTopoNamer.py test case. I have documented this thoroughly in order to (hopefully) make it pretty easy to understand how it works.

The next step that I have is to figure a way of incorporating this tracker into FreeCAD for an implementation test. This will necessitate (I think) adding some functionality to the TopoShape class in FreeCAD. Namely, I'll need to expose the Modified/Deleted/Created faces from any given OpenCascade operation via the TopoShapePy implementation. I have some limited experience with exposing OCC things to the FreeCAD python framework, so I think I should be able to figure this out.

Anyway, I was inspired by jnxd's work on the GSoC stuff to clean up the current state of this PyTopoNamer enough to get to a point where it can be tested within FreeCAD. Any comments, criticisms, concerns, whatever are welcome.
User avatar
Kunda1
Veteran
Posts: 13434
Joined: Thu Jan 05, 2017 9:03 pm

Re: Python Topological Namer/Tracker

Post by Kunda1 »

jnxd wrote:...
CC @jnxd
Alone you go faster. Together we go farther
Please mark thread [Solved]
Want to contribute back to FC? Checkout:
'good first issues' | Open TODOs and FIXMEs | How to Help FreeCAD | How to report Bugs
User avatar
microelly2
Veteran
Posts: 4688
Joined: Tue Nov 12, 2013 4:06 pm
Contact:

Re: Python Topological Namer/Tracker

Post by microelly2 »

Thank you for sharing this. Having a python framework to check ideas is a good start point.
triplus
Veteran
Posts: 9471
Joined: Mon Dec 12, 2011 4:45 pm

Re: Python Topological Namer/Tracker

Post by triplus »

Sounds interesting but likely way over my head to do some tests and provide some valuable feedback. ;)

P.S. Once implemented in FreeCAD for sure the fun can start when working with real geometry.
chrisb
Veteran
Posts: 53945
Joined: Tue Mar 17, 2015 9:14 am

Re: Python Topological Namer/Tracker

Post by chrisb »

Thank you for tackling this last big Problem-Rucksack FreeCAD carries around. All other things are only issues.
A Sketcher Lecture with in-depth information is available in English, auf Deutsch, en français, en español.
User avatar
tanderson69
Veteran
Posts: 1626
Joined: Thu Feb 18, 2010 1:07 am

Re: Python Topological Namer/Tracker

Post by tanderson69 »

ezzieyguywuf wrote: Wed Jul 12, 2017 8:32 pmThe main issue I have with OpenCascade's TNaming solution is that it is a 'black box'. I've spent time looking at the source code, but it is poorly documented and rather cryptic in its approach. The more I dived into it, the more confused I got. This became a problem mostly during debugging/development of the FreeCAD Topological Naming solution. It was so difficult to obtain sane, sensible error messages from the OCC TNaming module that it ended up being not very fun at all to continue development using this library. At the end of the day, if we HAVE to use OCC TNaming, I suppose we could make it work. But I wanted to take a break and try something else.
congratulations! you have earned your OCCT 'wings' ;)
ezzieyguywuf
Posts: 656
Joined: Tue May 19, 2015 1:11 am

Re: Python Topological Namer/Tracker

Post by ezzieyguywuf »

Update: I did a bit of preliminary testing this evening. Unfortunately, it didn't go so well.

The PyTopoNamer I wrote relies on the OpenCascade `isEqual` method to determine of two Edges are the same. This is done in order to figure out which Edge two Faces have in common on a Solid.

My assumption was that in, say, a Cube, despite there being 6 faces each with their own 4 Edges that the Cube itself would only have a total of 12 Edges. In other words, each Face would have an Edge that `isEqual` to an Edge on another Face.

My preliminary testing shows that this may not be the case. Using the Part workbench in FreeCAD v0.16 I have created a Cube primitive. Then, I do something like the following:

Code: Select all

box = App.ActiveDocument.getObjectFromLabel('Cube')
anEdge = box.Shape.Edges[9]

for i,face in enumerate(box.Shape.Faces):
    for j,edge in enumerate(face.Edges):
        if edge.isEqual(anEdge):
            print('face {} has an equal edge at Index {}'.format(i, j))
I expect this code to print two Edge Indices on two separate Face indices. However, I only get one hit.

Does anyone know what I might be missing here? Is it not true that any given Edge on a cube should be shared by two different Faces?

Edit : actually I took a look at the occ documentation for TopoDS_Shape and I believe the method I'm looking for is `isSame` as this checks for equality in tshape and location but disregards orientation. I will test this tomorrow.
ezzieyguywuf
Posts: 656
Joined: Tue May 19, 2015 1:11 am

Re: Python Topological Namer/Tracker

Post by ezzieyguywuf »

Alright, so I have completed a very basic integration test within FreeCAD and I'm happy to say that I've had some success! If you take a look at my github repo you'll notice I've added a 'FreeCAD_scripts' folder. In here, you can see a very basic test case that shows how my python topological namer should work. Let me explain a bit.

My current benchmark for dealing with the Topological Naming issue is the following issue in vanilla FreeCAD:
  1. Create New Document
  2. Open 'Part' workbench
  3. Create Box primitive
  4. Create Cylinder primitive
  5. Fuse the two primitives together
  6. Chamfer one of the Edges (I usually choose 'Edge10')
  7. Make cylinder height smaller
When the cylinder is resized, the chamfered Edge moves when FreeCAD executes its recompute algorithm. This is because 'Edge10', i.e. the 10th Edge in the 'Fusion' shape (i.e. App.ActiveDocument.getObject('Fusion').Shape.Edges[9]) is no longer the same Edge that we are interested in. It has moved due to various OCC internals (I think because the total number of Faces changes?)

What I've done in the test-case that you see in the 'FreeCAD_scripts' folder is recreated this failure up to fusion of the two primitives. If you have the 'Report View' visible, you can see why this is still a good test:
  1. Create New Document
  2. Open 'Part' workbench
  3. Create Box primitive
  4. Create Cylinder primitive
  5. Fuse the two primitives together
  6. Select an Edge and make note of its name (i.e. 'Edge10') in the Report View
  7. Make cylinder height smaller
  8. Select 'same' Edge and make note of its name in the Report View. It should be different
FreeCAD's `recompute` algorithm relies on the position of the Edge in the list of the 'Fusion' shape's edges in order to determine which Edge to fillet, but as we can see (and have known for a while) this method is not reliable.

When you run the test-case, you'll notice three messages:

Code: Select all

The chamfered Edge is named Edge009
RecoveredEdge.isEqual(originalEdge) == True
RecoveredEdge.isEqual(originalEdge) == True
If you follow along in the macro itself, you'll realize what these messages are telling you. The first is telling you about the Topological Name of the Edge that has been selected for chamfer - in this case, the Edge is named 'Edge009'. This name will always refer to this Edge as long as this Edge exists.

The second message is showing how this string can be used to retrieve the Edge. In other words, if we were going to chamfer the Edge, we'd want to use the string 'Edge009' to retrieve an instance of an OpenCascade Edge to chamfer.

Finally, the last message proves that even after the cylinder is resized, 'Edge009' still returns the Edge that we are looking for.

I'm pretty excited about this! For my next step in testing, I want to hack the FreeCAD `recompute` algorithm a bit so that it uses my namer - hopefully this is a quick hack just to finish proving out the namer. After that, it will likely be time to take a step back and determine the best path forward: whether to implement more OCC methods in Python or to re-implement the namer in CPP.
ickby
Veteran
Posts: 3116
Joined: Wed Oct 05, 2011 7:36 am

Re: Python Topological Namer/Tracker

Post by ickby »

Hello,

nice that you are going forward with this. I have two small remarks: When going for the identification by faces only make sure you also have a way to handle sketches. These are wires only, but the user can easily add or remove edges in the wire, and the created faces by extrusion must be named somehow.

The second remark is to give you a hint on work that was done over the last year about that topic. There is a basic test implementation of a custom aproach. Maybe it can give a small inspiration.
Description: https://docs.google.com/document/d/1-d2 ... 2R9Ug/edit#
Code: https://github.com/ickby/FreeCAD_sf_master/tree/Naming
ezzieyguywuf
Posts: 656
Joined: Tue May 19, 2015 1:11 am

Re: Python Topological Namer/Tracker

Post by ezzieyguywuf »

Here is another update: I've added a second script to my git repo that does a better job showing how the PyTopoNamer works. I tried to document the Macro very well, but if there are any questions just let me know.

This example does a much better job than the last example of showing how the PyTopoNamer can help us with our topological naming issue. In fact, this example shows how my PyTopoNamer fixes this bug

Please, take a moment to review this and let me know your thoughts!
ickby wrote: Sat Jul 15, 2017 6:28 pm When going for the identification by faces only make sure you also have a way to handle sketches. These are wires only, but the user can easily add or remove edges in the wire, and the created faces by extrusion must be named somehow.
This is a good point. As far as I'm aware, OCC does not provide the same methods (i.e. modified, deleted, etc...) for an extruded solid. I haven't spent a lot of time considering this particular problem, but I have a feeling that I could add another PyTopoNamer layer specific to the 2-D wire used to extrude the solid. Rather than defining a particular Edge by the two Faces that constitute it (as in my current 3-D implementation) we could define vertices (which become Edges) by the two Edges (which become Faces) that constitute them.

Anyway, one step at a time.
ickby wrote: Sat Jul 15, 2017 6:28 pm The second remark is to give you a hint on work that was done over the last year about that topic. There is a basic test implementation of a custom aproach. Maybe it can give a small inspiration.
Description: https://docs.google.com/document/d/1-d2 ... 2R9Ug/edit#
Code: https://github.com/ickby/FreeCAD_sf_master/tree/Naming
Thank you for that, I will take a look when I have a moment. For now, I'm rather excited about the progress I've made on PyTopoNamer and I want to see how far I can get that.
Post Reply