multiCut?

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!
Post Reply
greyltc
Posts: 24
Joined: Thu Jan 14, 2016 2:24 pm
Location: England

multiCut?

Post by greyltc »

I see that @wmayer brought the multFuse function to FreeCAD about a year ago. It's great, especially because of the fuzzy Boolean logic it employs.

Is anyone aware of any specific reason why there's no multiCut analog in the code base?
jmaustpc
Veteran
Posts: 11207
Joined: Tue Jul 26, 2011 6:28 am
Location: Australia

Re: multiCut?

Post by jmaustpc »

greyltc wrote:I see that @wmayer brought the multFuse function to FreeCAD about a year ago. It's great, especially because of the fuzzy Boolean logic it employs.

Is anyone aware of any specific reason why there's no multiCut analog in the code base?
Hi
This was discussed a long time ago on these forums. Multi cut is a logical impossibility because "order of operation" effects multi cut but this is not the case with multi fuse. A multi cut tool would have to include some complex method to allow the user to define the order of operations and hence define what they want, otherwise the out come is not defined.

For example Large box - small shoe box - a shoe = what?
1)If you cut the small box from the large box you get a large hollow box, if you then cut the shoe from that ...you still get the same large hollow box
2) if you cut the shoe from the small box first, you get a small box with a shoe shaped hollow in side, if you then cut that from the large box ...you get a similar large hollow box to 1) except there would also be a shoe shape inside the void.
greyltc
Posts: 24
Joined: Thu Jan 14, 2016 2:24 pm
Location: England

Re: multiCut?

Post by greyltc »

jmaustpc wrote:Multi cut is a logical impossibility because "order of operation" effects
I see your point.

I'm not sure the implementation really needs to be so complicated though. I was actually only hoping to find a dead simple approach: the first object in the list of objects passed to multiCut is the "master" and the remaining objects are subtracted from that.
jmaustpc
Veteran
Posts: 11207
Joined: Tue Jul 26, 2011 6:28 am
Location: Australia

Re: multiCut?

Post by jmaustpc »

greyltc wrote:
jmaustpc wrote:Multi cut is a logical impossibility because "order of operation" effects
I see your point.

I'm not sure the implementation really needs to be so complicated though. I was actually only hoping to find a dead simple approach: the first object in the list of objects passed to multiCut is the "master" and the remaining objects are subtracted from that.
And as I said above even given that, it is still not defined behaviour, in some simple cases it would work but not in others...just think about it, if the cut objects intersect then you have a problem.
greyltc
Posts: 24
Joined: Thu Jan 14, 2016 2:24 pm
Location: England

Re: multiCut?

Post by greyltc »

There's nothing ambiguous about the "dead simple" approach. I must not be explaining myself clearly here.

Another try:
A list of at least two objects is passed to multiCut.
The first object is the "master" object.
The remaining objects are "child" objects. These child objects don't interact with each other, they only interact with the master (or what remains of it).
Each child object is subtracted (logical AND NOT) from the master (the order of subtraction is irrelevant).
Whatever is left of the master is returned.

If I pass [largeBox, shoeBox, shoe] to multiCut the answer is always the same as the result largeBox.cut(shoeBox), even though maybe (depending on the order the subtraction was done in) there was an intermediate object where the hollowed out shape was the shape of a shoe.

The answer is actually firstObject.cut(multiFuse([list, of, all, the, other, objects])
jmaustpc
Veteran
Posts: 11207
Joined: Tue Jul 26, 2011 6:28 am
Location: Australia

Re: multiCut?

Post by jmaustpc »

greyltc wrote:The answer is actually firstObject.cut(multiFuse([list, of, all, the, other, objects])
If that is what you want then simply do that ...as we all do...that is create a Fuse or compound etc. and the do a cut. You could write a macro to do as you said if you want to and if you don't want to do so manually.

Your interpretation is only one possible out come. This was explained at great length in repeated posts a year or two ago, if you go find that topic you will get more information. In the end the point is that it is an undefined situation and the idea was rejected by the lead developers for that reason.
ickby
Veteran
Posts: 3116
Joined: Wed Oct 05, 2011 7:36 am

Re: multiCut?

Post by ickby »

There is defnitly some room for Interpretation for a multicut. Greyltec assumes that

Code: Select all

a.multicut (b,c,d)
Means a-b-c-d which would Work well defined and would be undependend of order of b,c and d. However it could also mean a-(b-(c-d)). So it is imho a question of documenting it right so that the User knows what happens. Version 1 is what occ has recently implemented and where they gain some speed, so we could also implement it. Of course someone would Need to do the work.
greyltc
Posts: 24
Joined: Thu Jan 14, 2016 2:24 pm
Location: England

Re: multiCut?

Post by greyltc »

This can get more complicated when one of the cut operations yields multiple disconnected objects. The multicut algorithm would then need to branch to handle the extra objects that are created in this case
greyltc
Posts: 24
Joined: Thu Jan 14, 2016 2:24 pm
Location: England

Re: multiCut?

Post by greyltc »

Below is what I've come up with for a multiCut function (in python). Thoughts?

Note: The commented multiFuse() bit I have there at the start is not needed, but it will reduce the number of iterations the while loop needs to complete, in case some of the child objects touch/overlap.

Edit: fixed a bug that would modify inputs

Code: Select all

# multiCut() subtracts a list of childObjects away from a parentObject

# input objects can be faces or solids (don't even think about mixing 'em!)
# childObjects can be a list or one face/solid
# the output will be a list only if it needs to be
def multiCut(parentObject,childObjects,tol=1e-5):
    if type(childObjects) is not list:
        childObjectsInternal = [childObjects]
    else:
        childObjectsInternal = list(childObjects)

    #if len(childObjectsInternal) > 1: #fuse cutting objects
        #childFuse = childObjectsInternal[0].multiFuse(childObjectsInternal[1::],tol).removeSplitter()
        #if len(childFuse.Solids) > 0:
            #childObjectsInternal = childFuse.Solids
        #else:
            #childObjectsInternal = childFuse.Faces

    cuttingTools = childObjectsInternal # we'll call our child objects cutting tools
    workpieces = [parentObject]
    while (len(cuttingTools) is not 0): # let's cut away until our tools run out
        nPieces = len(workpieces)
        for i in range(nPieces):
            cutResult = workpieces[i].cut(cuttingTools[0]).removeSplitter()
            if len(cutResult.Solids) > 0:
                cutResult = cutResult.Solids # there's a solid in our results, so we'll assume to be operating on those
            else:
                cutResult = cutResult.Faces # no solids, so we must be operating on faces
                
            # let's inspect the result of our cut. there are three options:
            if len(cutResult) is 0: # the cut has eliminated the workpiece
                workpieces[i] = [] # mark this workpiece for removal
            elif len(cutResult) > 1: # the workpiece has been segmented into two or more pieces by the cut
                workpieces[i] = [] # mark this workpiece for removal, it was split up
                workpieces += cutResult # add the new split pieces to our list of things to be cut
            else: # the piece being cut was not split and not consumed by the cut
                workpieces[i] = cutResult[0]
                
        # we've finished cutting all the workpieces; throw away the current cutting tool
        del cuttingTools[0] 
        
        # before we move onto the next cutting tool, delete all the extraneous workpieces
        workpieces = [x for x in workpieces if x != []]
    if (len(workpieces) is 1):
        return workpieces[0]
    else:
        return workpieces
Post Reply