GSoC 2017 Dev Log: jnxd

Here's the place for discussion related to coding in FreeCAD, C++ or Python. Design, interfaces and structures.
User avatar
yorik
Site Admin
Posts: 11567
Joined: Tue Feb 17, 2009 9:16 pm
Location: São Paulo, Brazil
Contact:

Re: GSoC 2017 Dev Log: jnxd

Postby yorik » Fri Jul 28, 2017 5:05 pm

jnxd wrote:
Thu Jul 27, 2017 4:03 pm
I wonder how that would go about.
The idea I had when reading you post is having the two systems in parallel: the existing Face1, Face2, etc... and another system maybe like ezzieyguywuf suggests, a map that contains faces referenced by names such as TopFace, BottomFace, etc. Both would refer to the same face.

Of course this would only work for very controllable objects like a box. But imagine an extrusion could also easily define a BaseFace, SideFace1,2... and and EndFace. Then a fillet between two named faces could still be named (some FilletBetweenTopandLeft). An Arch wall always knows what is its "base" face or footprint, etc, etc.

This is a bit hackish and unperfect system, because there would be no guarantee that names are always available for all the faces of an object. So it would be hard to rely on it. It would also quickly become very complicated and unuseful (FilletBetweenFilletOfFilletBetweenAFaceAndAnotherFace, whatever form it would take) But, it would have the advantage of distributing the responsibility of correct naming to each FreeCAD feature, and it's also implementable step by step, since it doesn't replace the current system. And it's a "viral" system, it could spread over FreeCAD and get bettered over time...

Not a very usable idea in itself, but maybe it can give you further ideas..
ezzieyguywuf
Posts: 558
Joined: Tue May 19, 2015 1:11 am

Re: GSoC 2017 Dev Log: jnxd

Postby ezzieyguywuf » Fri Jul 28, 2017 7:46 pm

yorik wrote:
Fri Jul 28, 2017 5:05 pm
Of course this would only work for very controllable objects like a box. But imagine an extrusion could also easily define a BaseFace
<snip>
(FilletBetweenFilletOfFilletBetweenAFaceAndAnotherFace, whatever form it would take)
<snip>
Not a very usable idea in itself, but maybe it can give you further ideas..
@yorik, i"m not sure if I follow your logic here. Or perhaps I have done a poor job explaining how the system I've described (and prototyped in Python) works. It seems that when you refer to a "Topological Name", you are looking for a descriptive name that let's you find the given topological entity. In a very simple case of a Cube, a given Edge would be named "EdgeOfTopAndFrontFace" in the system you've described.

However, naming Edges does not need to be so complicated: indeed, the name of the Edge does not need to have a description of the faces that make it up. Rather, any given Edge simply needs to know the "name"s of the two faces that make it up.

Let's go back to my example from earlier:

Code: Select all

SolidTopoNamer::SolidTopoNamer(TopodDS_Solid aSolid){
    i = 0;
    std::vector<TopoDS_Face> faces = HelperClass.getFaces(aSolid);
    for (auto&& face : faces){
        this->addFace(face);
    }
Notice in the constructor for SolidTopoNamer, there is no mention of a "top" face, "bottom" face, etc. Rather, a simple list of all the faces is taken and given a unique name - "Face1" through "Face6" in this case, based on how the `getName` method works. This solid could just as easily have been a 10-sided shape (dodecahedron?), it would not have made a difference, all that matters is that we give each face a name.

Now, suppose you want to fillet the Edge between the "top" and "front" face. That's fine, all you need to remember is that you have filleted "the Edge between Face1 and Face2".

So, in this simple SolidTopoNamer example, there might be a method 'SolidTopoNamer::nameEdge(TopoDS_Edge anEdge);' which similarly names edges as requested. the "nameEdge" method might look something like

Code: Select all

void SolidNamer::nameEdge(TopoDS_Edge anEdge){
    std::vector<std::string> parentFaceNames;
    // Again, excuse the pseudocode.
    for (face in mySolid.faceNames){
        for (edge in face.Edges){
            if (edge.IsSame(anEdge)){
                parentFaceNames.push_back(face.getName())
            }
        }
    }
    // edgeNames is a key-value list where each value is a pair of FaceNames. In
    // python, it'd be a dictionary that looks something like this:
    // edgeNames = {"Edge001": ['Face1', Face2'],
    //              "Edge002": ["Face1', "Face3"],etc..>}
    this->addEdge(parentFaceNames);
}

TopoDS_Edge SolidNamer::getEdge(std::string edgeName){
    // first, find the two faces that make up this edge
    std::pair<std::string> parentFaceNames = GetValuesFormEdgeNamesDataStructure();

    // Then, it is a simple matter of finding the one Edge that these two edges
    // have in common
    for (edge1 in face1.edges){
        for (edge2 in face2.edges){
            if (edge1.IsSame(edge2)){
                return edge1;
            }
        }
    }
}
Does this make my approach a bit more clear? Again, it's not really important where the entity you want a name for is, all that really matters is that you know how to find it. Also, this is why it is import for `SolidNamer` to have methods such as `ModifyFace` and `AddFace`. If one of its named Faces changes, it needs to know so that the next time `getEdge` is called it is checking the correct object.
User avatar
DeepSOIC
Posts: 7074
Joined: Fri Aug 29, 2014 12:45 am
Location: Saint-Petersburg, Russia

Re: GSoC 2017 Dev Log: jnxd

Postby DeepSOIC » Fri Jul 28, 2017 8:14 pm

ezzieyguywuf wrote:
Fri Jul 28, 2017 7:46 pm
all you need to remember is that you have filleted "the Edge between Face1 and Face2".
This isn't totally correct. Here's an example: a shape with two faces and two edges. Good luck figuring out, which edge is the edge between Face1 and Face2. Because both are.
toponaming-from-faces-problem.png
toponaming-from-faces-problem.png (52.48 KiB) Viewed 1108 times
toponaming-from-faces-problem.FCStd
(7.69 KiB) Downloaded 19 times
For most practical purposes, it'll probably work; we have to live with this idea, because OCC algorithms only give out the information about face inheritance (for solids).
ezzieyguywuf
Posts: 558
Joined: Tue May 19, 2015 1:11 am

Re: GSoC 2017 Dev Log: jnxd

Postby ezzieyguywuf » Fri Jul 28, 2017 9:55 pm

I am not really sure what I am looking at here. Where are the two faces? Where are the two edges? Can either of these edges be filleted or chamfered? Or are they simply seam edges? If they are seam edges, then why would you need a reference to it?

Please excuse my ignorance. I obviously do not have as much experience with these things as some of you veterans here.
User avatar
DeepSOIC
Posts: 7074
Joined: Fri Aug 29, 2014 12:45 am
Location: Saint-Petersburg, Russia

Re: GSoC 2017 Dev Log: jnxd

Postby DeepSOIC » Fri Jul 28, 2017 10:08 pm

ezzieyguywuf wrote:
Fri Jul 28, 2017 9:55 pm
I am not really sure what I am looking at here.
There is a model attached.
ezzieyguywuf wrote:
Fri Jul 28, 2017 9:55 pm
Can either of these edges be filleted or chamfered?
Does it matter? Fillet and chamfer are not the only thing which wants an edge. An external geometry in sketcher needs one, and assembly constraint needs one...

They are not seam edges.
ezzieyguywuf
Posts: 558
Joined: Tue May 19, 2015 1:11 am

Re: GSoC 2017 Dev Log: jnxd

Postby ezzieyguywuf » Sat Jul 29, 2017 1:12 am

DeepSOIC wrote:
Fri Jul 28, 2017 10:08 pm
There is a model attached.
Ah, sorry I was viewing on my phone I missed that. This is actually a really great example - as you've pointed out, the basic approach I've outlined would stumble here. But then again, the approach I've outlined is intended to solve the naming problem for primitive solids and their derivatives, thus I believe that expecting to have two face per given edge is reasonable.

That's not to say that my naming framework is useless in this 'sweep' situation. In fact, I believe that my framework would still work - in the case of your revolved sketch an additional "revolved shape manager" (i'll call it) will need to be created. The job of this RevolvedShapeManager will be to keep track of generated/modified/deleted Faces in the same way that some of the occ helper classes do.

For example, in the case of this revolved shape, the RevolvedShapeManager will need to keep track of the edges that make up the sketch that is revolved, the revolve axis, and the degree of revolution. If any of these changes, RevolvedShapeManager will have to provide a list of modified/added/deleted faces.

In your particular example, RevolvedShapeManager can go one step further: since the degree of revolution is 180 and the revolve-axis is coincident with an edge on the sketch, it can treat the two edges that you mention as a single edge. It only makes sense to do this when these two conditions are met, otherwise the two edges really are two distinct edges.

So, once the RevolvedShapeManager as been written, we can go back to using the regular ol' SolidNamer as I outlined earlier.

But, my plan is to take this one step at a time. Start with the primitive solids and their derivatives (fuse, etc..), move on to extruded sketches, and finally go on to revolved and swept sketches.
User avatar
yorik
Site Admin
Posts: 11567
Joined: Tue Feb 17, 2009 9:16 pm
Location: São Paulo, Brazil
Contact:

Re: GSoC 2017 Dev Log: jnxd

Postby yorik » Sat Jul 29, 2017 3:10 pm

ezzieyguywuf wrote:
Fri Jul 28, 2017 7:46 pm
@yorik, i"m not sure if I follow your logic here. Or perhaps I have done a poor job explaining how the system I've described (and prototyped in Python) works. It seems that when you refer to a "Topological Name", you are looking for a descriptive name that let's you find the given topological entity.
Yes, I was just using these "descriptive names" as a kind of pseudo thing to try to explain the idea better, not as what would really happen inside. In any case, don't give this too much thought, it's just a superficial idea that popped out, you guys are much deeper into the subject than me...
User avatar
saso
Posts: 1333
Joined: Fri May 16, 2014 1:14 pm
Contact:

Re: GSoC 2017 Dev Log: jnxd

Postby saso » Sat Jul 29, 2017 9:39 pm

Ok, so here is one of my idea about this problem... Unfortunately it is just an brainstorming idea, so I have no demo or code for it and I am not even sure if it can be implemented or if it would work at all. It generally has nothing to do with toponaming but it is possible that if this idea would even work, that it would not work for all cases (not a generic solution, but it would be nice if it would be :) ) so maybe it could provide a better (stronger) solution together with toponaming. So lets try to imagine it...

I have tried to make a few steps back and look at this problem from a different perspective. That is, when such an issue happens, the user knows it went wrong because he/she sees that it looks wrong. Lets say a fillet was on edge6 and because of some modification to the object that edge6 moves to an unexpected place and the fillet goes with it. From the simple logic, nothing is really wrong with this, fillet just stayed on edge6 as it was originally set to. But from the user perspective it is wrong because it is not what he/she wants, or so to say, it is not behaving as expected. For some time I did not really move much further from this point and so I was trying to just follow a bit the development around toponaming... Until I got this idea, what if "the expected behavior" in such cases is simply that our example fillet should simply prefer not to move. What do I mean by this, instead of trying to reconstruct and find the right edge with the help from toponaming, what if our fillet (or any other feature or object that suffers from this problem) would prefer to behave is such way that it would move with its edge as long as this changes would come from "expected" transformations to its base object such as move, rotate, resize,... But when unexpected changes would come (eg change in the number of edges in its base object), then the preference of the fillet would be to just stay at its last known position and it would then be willing to "snap" to ANY new edge that would be created in the same position or it would go and complain to the user that it should tell it where to snap to or delete it. I guess the implementation would have to be that the fillet wold probably have to be aware of its position, orientation and shape (eg how long it is) and it would have to be willing to snap to any new edge that is similar enough to its old one. With other words, I guess we can say that instead of names this would rely on position/shape.

So, if this could work, it could potentially also be quite powerful, since it should be possible for the developer to for example set how different the position, shape,... of the new edge can be from the old one for the fillet to still be willing to automatically snap to it instead of complaining to the user to make a decision. And I am guessing that it should be possible to work well even in such cases where several new edges would show up in the same position as the old edge so the fillet could be able to decide to fillet them all...

:?: :|

Edit: here is also an idea for a funny prof of concept demo...

In a normal way this would behave as I have described above, so that it would respect its base object and the normal transformations of it like move, rotate,... But lets imagine for fun to ignore even this normal actions, so we create a Cube and add a Fillet to it, now since our Fillet is, as described in the above idea, respecting and holding on to its position in the coordinate system and since in this example we are ignoring even the movement of its base object it means that the Fillet should fail (disappear) if we just move the Cube, but and this is the fun part, if we move the Cube back to the original position the Fillet should snap back to it and show up. And if we now ignore even the base object, then it means that if we create the same Cube and Fillet as before and now move Cube away (Fillet fails and disappears) and we then create a second Cube001 and move it to the same position as the original Cube was, the fillet should snap to and show up on Cube001. Now this is probably not how we will want it to work in a normal way but I was thinking it is a fun and nice example of the basic idea :)
jnxd
Posts: 162
Joined: Mon Mar 30, 2015 2:30 pm

Re: GSoC 2017 Dev Log: jnxd

Postby jnxd » Tue Aug 29, 2017 12:51 pm

Work Product

Before I put up the work product, I must begin the work product with a disclosure. Due to my movement to the US in the middle of the work period and the ensuing visa restrictions, I had to decline a part of the stipend (the third installment). This part was supposed to be corresponding to the final 4 weeks of the period, a time when I have not been able to stay sufficiently involved with the project. This was due to the preparation for the movement, and later the movement itself. I must sincerely apologize to the FreeCAD community for this, and I do plan to continue developing for FreeCAD as I can make time.

That, being said, following is the summary of my contribution during the work period of Google Summer of Code 2017:

The project was towards adding a topological naming framework for FreeCAD, such that topological features of a 3D object, such as it's vertices, edges and faces, can be described in a stable manner invarant of any modifications in the creation history of the object.

My work:
  • The project plan was to develop a framework based on the following design notes. To summarize, the implementation would keep track of the development of various topological features along the history of the object. When any parameter earlier on in the history is changed, a "para-history" between the old shape at that point in history and its modified shape is generated, which is used to modify the next consecutive shape. Again, another "para-history" can be built for this shape, and so on, until an error occurs, where one can either call for an exception, or leave it to the user to decide what to do.
    design_notes.txt
    (4.75 KiB) Downloaded 25 times
  • I created a class TopoHistory, which is used to describe the developments along the history of the object. At least until the PR, it is effectively a wrapper for OCC's BRepBuilderAPI_MakeShape, and is limited in use to features that use a single instance of that class for their development. I also created a TopoParaHistory class that was intended to be used to encode the relationship between the topological elements of a shape and it's modification.
  • A pull request was made after the TopoHistory class was implemented and used within a few Part::Feature sub-classes.
  • The development of TopoParaHistory and of an independent implementation of TopoHistory not storing BRepBuilderAPI_MakeShapes will continue in a separate branch tnaming-3.
  • Apart from this, there were various small tests I did to see how OCC's TNaming framework works under various cases. They are all consolidated in a single file here.
    fillet_with_cut_testing.zip
    (7.8 KiB) Downloaded 23 times
Future:

The project is definitely far from over. I had initially hoped to at least reach a point where a classical problem of filleting an edge/edges of a box with a slot can be solved, but technical and other difficulties prevented that from happening. This goal still stays a stepping stone towards complete topological naming. Some of the major issues towards this goal are:
  • Where to create the various TopoHistory/TopoParaHistory objects needed to be stored? Initially the idea was to do so in Part::Feature::execute, but it turns out that that method is called multiple times on a single shape before it is called on subsequent shapes, making it harder to find a relation between the old shape and the new.
  • TopoParahistory relies on the TNaming framework, which, unfortunately, only seems to be working well for modifications, but not for generations or deletions.
  • Apart from this there are some more fundamental challenges. One example is that we are so far using just the modification of faces in a solid to track the modification of all edges/vertices. But what happens when there are multiple edges in common between the same face(s)?
To summarize the project has helped me with understanding the problems faced during topological naming, and also gave me an experience with collaboration in a FOSS software. Despite it's failure, I am optimistic that I would be able to develop this project further.
chrisb
Posts: 19042
Joined: Tue Mar 17, 2015 9:14 am

Re: GSoC 2017 Dev Log: jnxd

Postby chrisb » Tue Aug 29, 2017 1:12 pm

Do you have the feeling, that this can be solved after all? I mean a sound, always applicable technique. I can only think of some heuristics to solve this problem, because for us humans it is most of the time easy to see which edges, vertices and faces correspond if we have a state before and after a change, but this is very hard to do by a machine.
Example: if I take something like a simple cube and cut it in two pieces of different size; which face should get the same number as before? Which edges are the same?