PR #4074 The section cut feature

Post here if you have re-based and finalised code to integrate into master, which was discussed, agreed to and tested in other forums. You can also submit your PR directly on github.
User avatar
uwestoehr
Posts: 2675
Joined: Sun Jan 27, 2019 3:21 am
Location: Germany

PR #4074 The section cut feature

Postby uwestoehr » Mon Nov 23, 2020 12:43 am

(This is a longer description that I must split to several posts (to show more than 4 files).)

Motivation

I had to present my work in an assembly. During the meeting the project partners requested to have a cut section view of my assembly in order to understand and discuss design details. So I used the clipping tool: But with these views nobody could understand what my construction is doing. Then I had to explain why I don't use proper software...
After the meeting I was pissed because despite how they stated it, they were in fact right: FreeCAD misses a proper cut feature to explain designs, to take these cut views for marketing brochures, to extract out technical drawings providing additional dimensions etc.

Since the motto is, don't start complaining, start coding, I started to implement this and after just 3 weeks of coding :o , here it is: https://github.com/FreeCAD/FreeCAD/pull/4074

Idea

- we need a cut feature giving you real cuts, no hollow structures
- there should be sliders for some animation
- the cut result should be persistent to do something with it later on

So the main idea is:
- take the currently visible and cutable objects and create each a link for them
- create a box with the size of the scene
- move it to the cut position and perform a cut operation with a compound of the links.

This has some limitations but also advantages I will discuss in a further post.

Implementation

I decided to take the existing Clipping feature and add the cutting functionality to it. The reason is that sometimes one wants the clipping as quick method and for comparison.
I designed the dialog so that it appears at the right side to keep the tree visible. Because only the visible objects should be cut and in practice you need to switch visibilities around while cutting.
You can cut in X, Y and Z direction like for clipping. The cut position ranges are automatically adjusted according to what is geometrically possible.
The following objects can be cut:
- PartDesign bodies
- Part primitives
- Part operation results that are shapes
- Assembly 4 models
- A2plus models

Examples

A: a typical use case is to inspect the depth an position of holes. Lets do this with the standard clipping and then switch to cutting:
YNX1UqkKy6.gif
hole inspection
YNX1UqkKy6.gif (525.38 KiB) Viewed 831 times
As you can see when clipping you cannot see the second hole that is right besides the hole you see. First with cutting it becomes visible.


B: cutting is super useful for assemblies. Take for example an Assembly 4 file:
FreeCAD_Biwagl4kLF.png
Assembly clipping
FreeCAD_Biwagl4kLF.png (39.17 KiB) Viewed 831 times

Do you get what is shown here? No? OK, then let's cut it at the same position:
FreeCAD_8wYmQxL5Ap.png
Assembly cutting
FreeCAD_8wYmQxL5Ap.png (32.76 KiB) Viewed 831 times

C: Here is a test with Part and PartDesign objects and cuts in all 3 directions:
I1uxVYMmFQ.gif
Part objects
I1uxVYMmFQ.gif (481.89 KiB) Viewed 831 times
You can see that cutting becomes of course more time-consuming but it is possible to make animations if you like.
Last edited by uwestoehr on Mon Nov 23, 2020 2:52 am, edited 4 times in total.
User avatar
uwestoehr
Posts: 2675
Joined: Sun Jan 27, 2019 3:21 am
Location: Germany

Re: PR #xxxx The section cut feature

Postby uwestoehr » Mon Nov 23, 2020 1:18 am

Advantages

The main feature is clearly to see what is happening in different sections of your parts/assemblies. But since the result is a Part Cut object, you can for example create a technical drawing out of it to get some additional dimensions:
ttt.png
Technical drawing
ttt.png (61.11 KiB) Viewed 815 times
As you can see the drawings can contain more than if you would create a Part cross section outline.

You can also create a mesh and for example 3D-pring just this cut view to e.g. for marketing and teaching purposes.

Another nice thing is that you can use the feature to check if you have collisions/intersections on your assembly. For example in this (A2plus) assembly, the inner objects are not cut because the 2 parts are assembled as press fit:
FreeCAD_mIQn0aQAwV.png
Press fit
FreeCAD_mIQn0aQAwV.png (44.33 KiB) Viewed 815 times

In a real-live use case I found this way out that I accidentally designed a hole for an M1 screw despite the screw to be used is M1.2.

This collision checking is possible because the objects to be cut are thrown into a Compound. Putting them into a boolean fragment also cuts intersections:
FreeCAD_IaiJsrxshj.png
cut with intersections
FreeCAD_IaiJsrxshj.png (35.7 KiB) Viewed 815 times

Therefore the cut feature will get an option to allow intersections (not yet in the PR). The price for this is that you loose the part colors.

You can furthermore decide if you want to keep the cut as persistent feature or not. When you either uncheck all 3 cut options or switch to clipping mode before closing the dialog, the cut will be removed and you get your document back as it was before.


Limitations

The main limitation is CPU power. Assemblies with many and each complex parts are not quickly cut. With OCC 7.4 it is acceptable while OCC 7.3 is so slow that it makes no fun.
However, the interactive feature with the sliders is an offer. You can but you Needn't to use is. For Assembly 4 files I even disabled them because they are quite useless then.

Another limitation is that the cut faces don't have the same color as the cut objects. Sometimes this works, but in most cases not. This is limitation of OCC as it seems.

Custom cut directions are not yet possible. I am reluctant because its implementation is a lot of work and can be done in a second step and because one gets what is wanted also by simply rotating the base part (A2plus) or the base coordinate system (assembly4) to the direction you like. For example in this 2 plus assembly I rotated the base by 45° around the z axis:
FreeCAD_4BGceIagNg.png
custom cut
FreeCAD_4BGceIagNg.png (65.2 KiB) Viewed 808 times
I see however, that changing the base part of an assembly is not simple because different constraints can break. Fixing them costs sometimes more time than to create a cut box in the custom direction manually. However, in any case support for custom cuts can and even should be done as next step.

Conclusions

This feature should help the users becoming more productive. For me this does this job quite well.

Since this is my first big FreeCAD project, there might be issues in how I implemented the feature. For example I purposely made it a STD feature like Clipping because its intentions is to be used for several WBs. I am happy with the UI implementation so far and I am curious to learn what the coding experts think.
Piero69
Posts: 151
Joined: Thu Jul 04, 2019 1:22 pm
Location: Parma - Italy

Re: PR #4074 The section cut feature

Postby Piero69 » Mon Nov 23, 2020 8:15 am

good job, it's like Solidworks "live section" :D
User avatar
watsug
Posts: 100
Joined: Sat Sep 26, 2020 10:51 pm

Re: PR #4074 The section cut feature

Postby watsug » Mon Nov 23, 2020 8:38 am

Nice work!
uwestoehr wrote:
Mon Nov 23, 2020 1:18 am
You can furthermore decide if you want to keep the cut as persistent feature or not.
So if it is persistent, how does it show in the feature tree? Like a boolean cut?
It would be really handy to have the section Cut show as a toggleable element in the feature tree, like distance measurements are today.

Edit: A useful continuation of this is to mark the cut face with diagonal section lines. I understand if this isn't a priority now.
wmayer
Site Admin
Posts: 17264
Joined: Thu Feb 19, 2009 10:32 am

Re: PR #4074 The section cut feature

Postby wmayer » Mon Nov 23, 2020 11:35 am

What are you doing? I told you twice that the core system must not depend on any application module. Here again the post with an image about the basic architecture: https://forum.freecadweb.org/viewtopic. ... 20#p448980

It is disallowed that any lower layer directly accesses functions of an upper layer.

Here some more details about the basics of the architecture: https://medium.com/omarelgabrys-blog/pl ... c207291800 where plugin means the same as application module.

As said the core system defines the interfaces and the plugins must then implement them. For the core system it's irrelevant how a plugin implements it (implementation detail). The idea behind is to easily extend an application with new functionality without touching the core system and to reduce dependencies.

Your code breaks this fundamental rule by adding a direct dependency to the Part module and making assumptions about implementation details because it has to know the class names Part2DObject, Datum, Compound, ...

If we decided to rename some classes in the Part module then the clipping function will silently fail. So, when changing anything relevant in the Part module we also have to touch the core system which actually must not happen.

To avoid all these issues one way e.g. could be to extend the class ComplexGeoData with a new virtual method clipWithPlane which as arguments gets a plane (e.g. as base point + normal) and returns a clipped copy of ComplexGeoData. The method can be defined as pure virtual so that every sub-class is forced to implement it or the default implementation can return a null pointer or raise an exception which must be handled by the clipping function, then.

Another option is to implement a specialized clipping function in the PartGui module and then you are free to directly use Part (but not PartDesign) features as much as you want.

But I am not sure if performing a boolean operation is the right way to go anyway. Have you ever tried it with one of the huge STEP models with a size of 100MB or more? I fear that the clipping is very time and memory consuming and it won't be any fun for the user to work with it.

For volumetric rendering you usually use OpenGL techniques like the stencil buffer: https://tech.metail.com/the-stencil-buf ... rsections/
Coin already provides a library for this purpose https://github.com/coin3d/simvoleon and it's worth to have a look at it.
chrisb
Posts: 33587
Joined: Tue Mar 17, 2015 9:14 am

Re: PR #4074 The section cut feature

Postby chrisb » Mon Nov 23, 2020 12:12 pm

Looks good; I like especially the sliders!
A Sketcher Lecture with in-depth information is available in English, auf Deutsch, en français, en español.
wmayer
Site Admin
Posts: 17264
Joined: Thu Feb 19, 2009 10:32 am

Re: PR #4074 The section cut feature

Postby wmayer » Mon Nov 23, 2020 12:58 pm

In the second part I will comment on some of the programming aspects and its weaknesses.

First of all, the current clipping dialog is a function that does not touch the document and recently have been moved to a separate dock widget in order to allow to have opened at the same time a task dialog that modifies the document.

Your function however also modifies the document and thus I strongly recommend to implement it as a separate function and realize it as task dialog so that no other function can be opened at the same time.

The constructor of the Clipping class gets a View3DInventor and this allows you to access the document it belongs to. Thus, you shouldn't access the active dialog by the application.

Especially, you shouldn't access the active document over and over again because FreeCAD is designed with a multiple document interface and there the user is able to switch to another document and thus your functions all the sudden will work with the wrong document.

Code: Select all

    // get all objects in the document
        {
    auto doc = Application::Instance->activeDocument()->getDocument();
            minlen = minlen / steps;
    if (!doc) {
            int dim = static_cast<int>(log10(minlen));
        Base::Console().Error("Clipping error: there is document\n");
            double singleStep = pow(10.0, dim);
        return;
            d->ui.clipView->setSingleStep(singleStep);
    }
            minDecimals = std::max(minDecimals, -dim);
    ObjectsList = doc->getObjects();
    if (ObjectsList.empty()) {
        Base::Console().Error("Clipping error: there are no objects in the document\n");
        return;
    }
These are things to check before opening the dialog.

Code: Select all

    // delete the objects we might have already created to cut
    // we must do this because we support several cuts at once and
    // it is dangerous to deal with the fact that the user is free
    // to uncheck cutting planes and to add/remove objects while this dialog is open
    // We must remove in this order because the tree hierary of the feautures is Z->Y->X and Cut->Box
    if (doc->getObject("CutViewCutZ")) {
        // the deleted object might have been visible before, thus check and delete it from the list
        for (it = ObjectsListVisible.begin(); it != ObjectsListVisible.end(); ++it) {
            if ((*it) == doc->getObject("CutViewCutZ")) {
                ObjectsListVisible.erase((it));
                break;
            }
        }
        }
        {
        doc->removeObject("CutViewCutZ");
            lenx = lenx / steps;
    }
This is quite inefficient because the call of getObject() internally has to search inside a std::map which has a complexity of O(log n) and doing this in a loop can cause a complexity of O(n log n). Access it only once and store the pointer. Then, instead of looping through ObjectsListVisible use the function std::find http://www.cplusplus.com/reference/algorithm/find/


When you add an object with

Code: Select all

auto CutFeature = doc->addObject("Part::Cut", "CutViewCutZ");
there is no guarantee that its name will be "CutViewCutZ" and thus it's not safe to try to access it this way as this can point to the wrong object. Either store the name of the returned object in a member variable std::string or even better use the class DocumentObjectWeakPtrT or DocumentObjectT.

Code: Select all

Part::Compound* pcCompoundDel = static_cast<Part::Compound*>(compoundObject);
Same as above and thus the static_cast is dangerous.

Code: Select all

if ((*it)->getTypeId() == Base::Type::fromName("App::Part")) {
Use

Code: Select all

if ((*it)->getTypeId().isDerivedFrom(App::Part::getClassTypeId())) {

Code: Select all

            // since the TypeString begins with control characters, we need to check it from the end
            if (!TypeString.empty() && TypeString.substr(TypeString.length() - 15) == "Assembly4 Model") {
Code smell: Assembly4 is an add-on. So, do not make any assumptions of how things are implemented.

Code: Select all

        // get all shapes
        if ((*it)->getPropertyByName("Shape")) {
            // sort out 2D objets, datums, App:Parts, compounds and objects that are part of a PartDesign body
            if (!(*it)->getTypeId().isDerivedFrom(Base::Type::fromName("Part::Part2DObject"))
                && !(*it)->getTypeId().isDerivedFrom(Base::Type::fromName("Part::Datum"))
                && !(*it)->getTypeId().isDerivedFrom(Base::Type::fromName("PartDesign::Feature"))
                && !(*it)->getTypeId().isDerivedFrom(Base::Type::fromName("Part::Compound"))
                && (*it)->getTypeId() != Base::Type::fromName("App::Part"))
                ObjectsListCut.push_back((*it));
        }
Unsafe check. A feature can be created with a property called Shape that is not a Part::Feature. So, you will pick it up with unexpected behaviour afterwards.

Code: Select all

    // sort out objects that are part of Part::Boolean, Part::MultiCommon, Part::MultiFuse,
    // Part::Thickness and Part::FilletBase
What is the intention? These explicit type checks are very problematic as new types can be added in the future so that each time this function must be extended, too. You should find a better criterion independent of the real type.
When you know that an object is a Part::Feature then use its OutList to remove all objects you don't want to have.
User avatar
uwestoehr
Posts: 2675
Joined: Sun Jan 27, 2019 3:21 am
Location: Germany

Re: PR #4074 The section cut feature

Postby uwestoehr » Mon Nov 23, 2020 1:15 pm

watsug wrote:
Mon Nov 23, 2020 8:38 am
So if it is persistent, how does it show in the feature tree? Like a boolean cut?
Yes, it appears as boolean cut. As I wrote you can decide to keep it in the tree when closing the dialog or not.
User avatar
uwestoehr
Posts: 2675
Joined: Sun Jan 27, 2019 3:21 am
Location: Germany

Re: PR #4074 The section cut feature

Postby uwestoehr » Mon Nov 23, 2020 1:32 pm

wmayer wrote:
Mon Nov 23, 2020 11:35 am
What are you doing? I told you twice that the core system must not depend on any application module.
Hi Werner, I read your posts and understood it. As I wrote, for the first unveiling, I wanted to demonstrate that and how the cutting feature works.
I understand that the code depends in Part and thus should not be in FreeCADGui.
But before spending even more spare time with maybe the wrong approach, I made a demo implementation. It is a RFC and now the discussion is open and I am happy that you already gave it a look. And the RFC is also important to get feedback about the UI, the usecases etc.

I would like if you are less upset since you are a master and I am a beginner. Things that are obvious for you are not obvious and new for me, so when I make mistakes is not because I don't want to learn, I need time to learn and become better and you have seen in the past that I followed your advises. I will carefully read you feedback later this evening.
Last edited by uwestoehr on Mon Nov 23, 2020 2:00 pm, edited 1 time in total.
User avatar
uwestoehr
Posts: 2675
Joined: Sun Jan 27, 2019 3:21 am
Location: Germany

Re: PR #4074 The section cut feature

Postby uwestoehr » Mon Nov 23, 2020 1:59 pm

wmayer wrote:
Mon Nov 23, 2020 11:35 am
But I am not sure if performing a boolean operation is the right way to go anyway. Have you ever tried it with one of the huge STEP models with a size of 100MB or more?
It is a very valid question if my cutting approach is the right way. I thought about this a lot and in the end I decided to collect the use cases I had the last months at work. Cutting alone is very useful for live-presentations and videos but this is just one application. I have to create marketing datasheets, flyers etc. therefore I need a result that appears in the tree as "normal" feature that I can modify further. At the moment I also use cut boxes, just have to do everything manually. For example I needed to create technical drawings for cut views. For a product presentation, I needed to color the cut faces and for rapid prototyping I had to 3D part a slice of a part (sic!). I also need a collision check for assemblies.
I understand that I am not objective when I collect my personal use cases but I think I am not the only user having such use cases.

I took the largest real-life STEP I could find. Some months ago I had to cut it already and did it manually using a box too. So cutting works, but of course not with a slider. However, in most cases you know where to cut in advance and can enter the position and save recomputes.

For volumetric rendering you usually use OpenGL techniques like the stencil buffer: https://tech.metail.com/the-stencil-buf ... rsections/
Coin already provides a library for this purpose https://github.com/coin3d/simvoleon and it's worth to have a look at it.
Interesting. The question is if their result can lead to a feature in the document tree that one can modify further. I cannot figure that out.