How to perform boolean operation on a PartDesign body?

Need help, or want to share a macro? Post here!
Forum rules
Be nice to others! Respect the FreeCAD code of conduct!
Post Reply
visionok
Posts: 7
Joined: Wed Oct 28, 2020 8:21 am

How to perform boolean operation on a PartDesign body?

Post by visionok »

I've searched, but I can't find any examples. So I resorted to copying the code from the python console after performing the operation manually in the gui. It gave the following code (I changed it bit to generalise it):

Code: Select all

def boolean_cut(base_body, sub_body1, sub_body2):
	partdesign_body_basename = base_body.split('__')[0]
	Gui.Selection.clearSelection()
	Gui.Selection.addSelection(App.ActiveDocument.Label,base_body)
	Gui.ActiveDocument.ActiveView.setActiveObject('pdbody',App.ActiveDocument.getObject(base_body))
	Gui.Selection.addSelection(App.ActiveDocument.Label,sub_body1)
	Gui.Selection.addSelection(App.ActiveDocument.Label,sub_body2)
	App.ActiveDocument.getObject(base_body).newObject('PartDesign::Boolean','Boolean')
	App.ActiveDocument.getObject('Boolean').addObjects([App.ActiveDocument.getObject(sub_body1),App.ActiveDocument.getObject(sub_body2),])
	App.ActiveDocument.recompute()

	App.ActiveDocument.getObject('Boolean').ViewObject.ShapeColor=getattr(App.ActiveDocument.getObject(partdesign_body_basename + '__Pocket').getLinkedObject(True).ViewObject,'ShapeColor',App.ActiveDocument.getObject('Boolean').ViewObject.ShapeColor)
	App.ActiveDocument.getObject('Boolean').ViewObject.LineColor=getattr(App.ActiveDocument.getObject(partdesign_body_basename + '__Pocket').getLinkedObject(True).ViewObject,'LineColor',App.ActiveDocument.getObject('Boolean').ViewObject.LineColor)
	App.ActiveDocument.getObject('Boolean').ViewObject.PointColor=getattr(App.ActiveDocument.getObject(partdesign_body_basename + '__Pocket').getLinkedObject(True).ViewObject,'PointColor',App.ActiveDocument.getObject('Boolean').ViewObject.PointColor)
	App.ActiveDocument.getObject('Boolean').ViewObject.Transparency=getattr(App.ActiveDocument.getObject(partdesign_body_basename + '__Pocket').getLinkedObject(True).ViewObject,'Transparency',App.ActiveDocument.getObject('Boolean').ViewObject.Transparency)
	App.ActiveDocument.getObject('Boolean').ViewObject.DisplayMode=getattr(App.ActiveDocument.getObject(partdesign_body_basename + '__Pocket').getLinkedObject(True).ViewObject,'DisplayMode',App.ActiveDocument.getObject('Boolean').ViewObject.DisplayMode)
	Gui.ActiveDocument.setEdit(App.ActiveDocument.getObject(base_body),0,'Boolean.')
	Gui.Selection.clearSelection()
	App.ActiveDocument.getObject('Boolean').setObjects( [App.ActiveDocument.getObject(sub_body1),App.ActiveDocument.getObject(sub_body2),])
	App.ActiveDocument.getObject('Boolean').Type = 1
	App.ActiveDocument.recompute()
	Gui.activeDocument().resetEdit()
I have 3 PartDesign bodies:
base_body
sub_body1
sub_body2
I want to cut sub_body1 and sub_body2 out of base_body.

So the above code works, but only once. I need to be able to run it multiple times, so all the object references will need to be abstracted to variables, eg the Boolean object that it creates), and also this part:

Code: Select all

	
...	App.ActiveDocument.getObject('Boolean').ViewObject.ShapeColor=getattr(App.ActiveDocument.getObject(partdesign_body_basename + '__Pocket').getLinkedObject(True).ViewObject,'ShapeColor',App.ActiveDocument.getObject('Boolean').ViewObject.ShapeColor)
...	
references the Tip (I believe that is what it's called?) of the base_body (it consist of 2 features, a pad followed by a pocket).
I tried replacing "partdesign_body_basename + '__Pocket'" with "App.ActiveDocument.getObject(base_body).Tip.Name" but that doesn't seem to work - it returns "a type of <class 'NoneType'>", although it had worked in the console when I was trying to figure out what to use.

My question is, is there a more concise way of doing this?

Code: Select all

OS: openSUSE Leap 15.2 (KDE//usr/share/xsessions/plasma5)
Word size of OS: 64-bit
Word size of FreeCAD: 64-bit
Version: 0.19.22670 (Git) AppImage
Build type: Release
Branch: master
Hash: 12155f4aab09047c5697db0c1b3cf93b02edda03
Python version: 3.8.6
Qt version: 5.12.9
Coin version: 4.0.0
OCC version: 7.4.0
Locale: English/United Kingdom (en_GB)
visionok
Posts: 7
Joined: Wed Oct 28, 2020 8:21 am

Re: How to perform boolean operation on a PartDesign body?

Post by visionok »

I updated the code to

Code: Select all

def boolean_cut(base_body, tipname, sub_body1, sub_body2):
	partdesign_body_basename = base_body.split('__')[0]
	Gui.Selection.clearSelection()
	Gui.Selection.addSelection(App.ActiveDocument.Label,base_body)
	Gui.ActiveDocument.ActiveView.setActiveObject('pdbody',App.ActiveDocument.getObject(base_body))
	Gui.Selection.addSelection(App.ActiveDocument.Label,sub_body1)
	Gui.Selection.addSelection(App.ActiveDocument.Label,sub_body2)
	#App.ActiveDocument.getObject(base_body).newObject('PartDesign::Boolean','Boolean')
	boolean_object = App.ActiveDocument.getObject(base_body).newObject('PartDesign::Boolean')
	boolean_name = boolean_object.Name
	App.ActiveDocument.getObject(boolean_name).addObjects([App.ActiveDocument.getObject(sub_body1),App.ActiveDocument.getObject(sub_body2),])
	App.ActiveDocument.recompute()


#	App.ActiveDocument.PartDesign__Body004.Tip.Name
	#tipname = App.ActiveDocument.getObject(base_body).Tip.Name
	print(tipname)
	App.ActiveDocument.getObject(boolean_name).ViewObject.ShapeColor=getattr(App.ActiveDocument.getObject(tipname).getLinkedObject(True).ViewObject,'ShapeColor',App.ActiveDocument.getObject(boolean_name).ViewObject.ShapeColor)
	App.ActiveDocument.getObject(boolean_name).ViewObject.LineColor=getattr(App.ActiveDocument.getObject(tipname).getLinkedObject(True).ViewObject,'LineColor',App.ActiveDocument.getObject(boolean_name).ViewObject.LineColor)
	App.ActiveDocument.getObject(boolean_name).ViewObject.PointColor=getattr(App.ActiveDocument.getObject(tipname).getLinkedObject(True).ViewObject,'PointColor',App.ActiveDocument.getObject(boolean_name).ViewObject.PointColor)
	App.ActiveDocument.getObject(boolean_name).ViewObject.Transparency=getattr(App.ActiveDocument.getObject(tipname).getLinkedObject(True).ViewObject,'Transparency',App.ActiveDocument.getObject(boolean_name).ViewObject.Transparency)
	App.ActiveDocument.getObject(boolean_name).ViewObject.DisplayMode=getattr(App.ActiveDocument.getObject(tipname).getLinkedObject(True).ViewObject,'DisplayMode',App.ActiveDocument.getObject(boolean_name).ViewObject.DisplayMode)


	Gui.ActiveDocument.setEdit(App.ActiveDocument.getObject(base_body),0,f'{boolean_name}.')
	Gui.Selection.clearSelection()
	App.ActiveDocument.getObject(boolean_name).setObjects( [App.ActiveDocument.getObject(sub_body1),App.ActiveDocument.getObject(sub_body2),])
	App.ActiveDocument.getObject(boolean_name).Type = 1
	App.ActiveDocument.recompute()
	Gui.activeDocument().resetEdit()

Now it will work any number of times within the same document.

I'm still looking for an easier and more general way to accomplish this. I found TestBoolean.py in the FreeCAD source:

Code: Select all

#   (c) Juergen Riegel (FreeCAD@juergen-riegel.net) 2011      LGPL        *
#                                                                         *
#   This file is part of the FreeCAD CAx development system.              *
#                                                                         *
#   This program is free software; you can redistribute it and/or modify  *
#   it under the terms of the GNU Lesser General Public License (LGPL)    *
#   as published by the Free Software Foundation; either version 2 of     *
#   the License, or (at your option) any later version.                   *
#   for detail see the LICENCE text file.                                 *
#                                                                         *
#   FreeCAD is distributed in the hope that it will be useful,            *
#   but WITHOUT ANY WARRANTY; without even the implied warranty of        *
#   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the         *
#   GNU Library General Public License for more details.                  *
#                                                                         *
#   You should have received a copy of the GNU Library General Public     *
#   License along with FreeCAD; if not, write to the Free Software        *
#   Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  *
#   USA                                                                   *
#**************************************************************************
import unittest

import FreeCAD

App = FreeCAD

class TestBoolean(unittest.TestCase):
    def setUp(self):
        self.Doc = FreeCAD.newDocument("PartDesignTestBoolean")

    def testBooleanFuseCase(self):
        self.Body = self.Doc.addObject('PartDesign::Body','Body')
        self.Box = self.Doc.addObject('PartDesign::AdditiveBox','Box')
        self.Box.Length=10
        self.Box.Width=10
        self.Box.Height=10
        self.Body.addObject(self.Box)
        self.Doc.recompute()
        self.Body001 = self.Doc.addObject('PartDesign::Body','Body001')
        self.Box001 = self.Doc.addObject('PartDesign::AdditiveBox','Box001')
        self.Box001.Length=8
        self.Box001.Width=8
        self.Box001.Height=12
        self.Box001.Placement.Base = App.Vector(-5,0,0)
        self.Body001.addObject(self.Box001)
        self.Doc.recompute()
        self.BooleanFuse = self.Doc.addObject('PartDesign::Boolean','BooleanFuse')
        self.Body001.addObject(self.BooleanFuse)
        self.Doc.recompute()
        self.BooleanFuse.setObjects([self.Body,])
        self.BooleanFuse.Type = 0
        self.Doc.recompute()
        #self.assertAlmostEqual(self.BooleanFuse.Shape.Volume, 1500)

    def testBooleanCutCase(self):
        self.Body = self.Doc.addObject('PartDesign::Body','Body')
        self.Box = self.Doc.addObject('PartDesign::AdditiveBox','Box')
        self.Box.Length=10
        self.Box.Width=10
        self.Box.Height=10
        self.Body.addObject(self.Box)
        self.Doc.recompute()
        self.Body001 = self.Doc.addObject('PartDesign::Body','Body001')
        self.Box001 = self.Doc.addObject('PartDesign::AdditiveBox','Box001')
        self.Box001.Length=8
        self.Box001.Width=8
        self.Box001.Height=12
        self.Box001.Placement.Base = App.Vector(-5,0,0)
        self.Body001.addObject(self.Box001)
        self.Doc.recompute()
        self.BooleanCut = self.Doc.addObject('PartDesign::Boolean','BooleanCut')
        self.Body001.addObject(self.BooleanCut)
        self.Doc.recompute()
        self.BooleanCut.setObjects([self.Body,])
        self.BooleanCut.Type = 1
        self.Doc.recompute()
        #self.assertAlmostEqual(self.BooleanCut.Shape.Volume, 500)

    def testBooleanCommonCase(self):
        self.Body = self.Doc.addObject('PartDesign::Body','Body')
        self.Box = self.Doc.addObject('PartDesign::AdditiveBox','Box')
        self.Box.Length=10
        self.Box.Width=10
        self.Box.Height=10
        self.Body.addObject(self.Box)
        self.Doc.recompute()
        self.Body001 = self.Doc.addObject('PartDesign::Body','Body001')
        self.Box001 = self.Doc.addObject('PartDesign::AdditiveBox','Box001')
        self.Box001.Length=8
        self.Box001.Width=8
        self.Box001.Height=12
        self.Box001.Placement.Base = App.Vector(-5,0,0)
        self.Body001.addObject(self.Box001)
        self.Doc.recompute()
        self.BooleanCommon = self.Doc.addObject('PartDesign::Boolean','BooleanCommon')
        self.Body001.addObject(self.BooleanCommon)
        self.Doc.recompute()
        self.BooleanCommon.setObjects([self.Body,])
        self.BooleanCommon.Type = 2
        self.Doc.recompute()
        #self.assertAlmostEqual(self.BooleanCommon.Shape.Volume, 500)

    def tearDown(self):
        #closing doc
        FreeCAD.closeDocument("PartDesignTestBoolean")
        #print ("omit closing document for debugging")

test = TestBoolean
test.setUp(test)
test.testBooleanFuseCase(test)
#test.testBooleanCutCase(test)
#test.testBooleanCommonCase(test)
However all 3 examples (Fuse, Cut, Common) result in the same: an object which is just "Body", ie in all 3 cases the boolean operation just removed Body001, leaving Body as the result. Can anyone help?
juerg5524
Posts: 2
Joined: Mon Sep 12, 2022 5:34 am

Re: How to perform boolean operation on a PartDesign body?

Post by juerg5524 »

FreeCAD Version 0.20

Mein Beispiel dazu mit "makeCut":


import FreeCAD as App
import PartDesign
import Draft

# Tasten
tastenHoehe = 20
printDicke = 1.7
bodenDistanz = 2.3
hub = 4.3
hoeheBass = tastenHoehe - hub + printDicke + bodenDistanz

aussenLaenge = 262
aussenBreite = 145
wandStaerke = 5
innenLaenge = aussenLaenge - 2*wandStaerke # = 134
innenBreite = aussenBreite - 2*wandStaerke # = 134
aussenRadius = 10
hoehe = 40
def xyzVerschiebung(x, y, z):
return App.Placement(App.Vector(x,y,z), App.Rotation(App.Vector(0,0,1),0), App.Vector(0,0,0))

def makeRect(laenge, breite):
r = Draft.make_rectangle(laenge, breite, placement=None, face=True, support=None)
pl=App.Placement()
pl.Rotation.Q= (0.0, 0.0, 0.0, 1.0)
return r

def makeShape(body, obj, hoehe):
pad = body.newObject('PartDesign::Pad','Pad')
pad.Profile = obj
pad.Length = hoehe
pad.recompute()
pad.ReferenceAxis = (obj,['N_Axis'])
#body.Visibility = False
App.ActiveDocument.recompute()
return pad


# inneres Rechteck
doc = App.ActiveDocument;
rect = makeRect(innenLaenge, innenBreite)
rect.FilletRadius = aussenRadius - wandStaerke
innen = doc.addObject('PartDesign::Body', 'Body')
innen.addObject(rect)
rect.Placement = xyzVerschiebung(wandStaerke, wandStaerke, 0)
pad1 = makeShape(innen, rect, hoehe)
App.ActiveDocument.recompute()

# äusseres Rechteck
rect = makeRect(aussenLaenge, aussenBreite)
rect.FilletRadius = aussenRadius
aussen = doc.addObject('PartDesign::Body', 'Body')
aussen.addObject(rect)
pad2 = makeShape(aussen, rect, hoehe)
App.ActiveDocument.recompute()

def makeCut(bodyAussen, padAussen, bodyInnen):
doc = App.ActiveDocument;
Gui.ActiveDocument.ActiveView.setActiveObject('pdbody',bodyAussen)
boolean = bodyAussen.newObject('PartDesign::Boolean','Boolean')
view = pad2.getLinkedObject(True).ViewObject
viewBool = boolean.ViewObject
viewBool.ShapeColor=getattr(view,'ShapeColor',viewBool.ShapeColor)
viewBool.LineColor=getattr(view,'LineColor',viewBool.LineColor)
viewBool.PointColor=getattr(view,'PointColor',viewBool.PointColor)
viewBool.Transparency=getattr(view,'Transparency',viewBool.Transparency)
viewBool.DisplayMode=getattr(view,'DisplayMode',viewBool.DisplayMode)
boolean.setObjects( [bodyInnen,])
boolean.Type = 1
App.ActiveDocument.recompute()
return boolean

# ausschneiden
makeCut(aussen, pad2, innen)
rect.Visibility = False
Attachments
cut_example.FCMacro
(2.44 KiB) Downloaded 21 times
Last edited by juerg5524 on Sat Oct 01, 2022 12:03 pm, edited 1 time in total.
chrisb
Veteran
Posts: 54207
Joined: Tue Mar 17, 2015 9:14 am

Re: How to perform boolean operation on a PartDesign body?

Post by chrisb »

Please edit your post and put the code in code tags using the button </>. That makes the post easier to read and shows immediately if there are indentations or not.
juerg5524 wrote: Sat Oct 01, 2022 9:10 am Mein Beispiel dazu mit "makeCut":
English text in the english forum would be more appropriate
A Sketcher Lecture with in-depth information is available in English, auf Deutsch, en français, en español.
User avatar
dprojects
Posts: 722
Joined: Mon Mar 06, 2017 6:02 pm
Location: Poland
Contact:

Re: How to perform boolean operation on a PartDesign body?

Post by dprojects »

visionok wrote: Wed Oct 28, 2020 5:23 pm I've searched, but I can't find any examples.

Code: Select all

cut = FreeCAD.ActiveDocument.addObject("Part::Cut", cutName)
cut.Base = base
cut.Tool = copy
source: Macro_MultiCuts, It depends what you select, if you select Body you get Body cut.

If you want to access Body and give it as argument:

Code: Select all

body = pad2._Body

Thanks
Darek
github.com/dprojects

workbench for woodworking is available at: github.com/dprojects/Woodworking
juerg5524
Posts: 2
Joined: Mon Sep 12, 2022 5:34 am

Re: How to perform boolean operation on a PartDesign body?

Post by juerg5524 »

short version

Code: Select all

def copyViewAttr(view):
        view[0].ShapeColor=getattr(view[1],'ShapeColor',view[0].ShapeColor)
        view[0].LineColor=getattr(view[1],'LineColor',view[0].LineColor)
        view[0].PointColor=getattr(view[1],'PointColor',view[0].PointColor)
        view[0].Transparency=getattr(view[1],'Transparency',view[0].Transparency)
        view[0].DisplayMode=getattr(view[1],'DisplayMode',view[0].DisplayMode)
        return

# type = 0  fuse
#        1  cut
#        2  common
def makeCut(bodyAussen, padAussen, bodyInnen, type = 1):
        doc = App.ActiveDocument;
        Gui.ActiveDocument.ActiveView.setActiveObject('pdbody',bodyAussen)
        boolean = bodyAussen.newObject('PartDesign::Boolean','Boolean')
        view = padAussen.getLinkedObject(True).ViewObject
        viewBool = boolean.ViewObject
        copyViewAttr([viewBool, view])
        boolean.setObjects( [bodyInnen,])
        boolean.Type = type    
        doc.recompute()
        return (bodyAussen,boolean)
res = makeCut(...)

use res[0] as next "bodyAussen" and res[1] as next "padAussen"
User avatar
onekk
Veteran
Posts: 6208
Joined: Sat Jan 17, 2015 7:48 am
Contact:

Re: How to perform boolean operation on a PartDesign body?

Post by onekk »

Why revive a post two years old?

IMHO, a tentative of scripting something, simply copying "Python Console" echo, is the worst way to do things. and IMHO it deserve only:


https://wiki.freecadweb.org/FreeCAD_Scripting_Basics

https://wiki.freecadweb.org/Part_scripting

https://wiki.freecadweb.org/Topological_data_scripting


But maybe it is only my "actual bad mood".

:D

Kind Regards

Carlo D.
GitHub page: https://github.com/onekk/freecad-doc.
- In deep articles on FreeCAD.
- Learning how to model with scripting.
- Various other stuffs.

Blog: https://okkmkblog.wordpress.com/
User avatar
dprojects
Posts: 722
Joined: Mon Mar 06, 2017 6:02 pm
Location: Poland
Contact:

Re: How to perform boolean operation on a PartDesign body?

Post by dprojects »

onekk wrote: Tue Oct 04, 2022 4:03 pm Why revive a post two years old?
Maybe he is time traveler, and he travel in space and time ;-) so for him 2 years is just blink of the eye ;-)

Thanks
Darek
github.com/dprojects

workbench for woodworking is available at: github.com/dprojects/Woodworking
User avatar
onekk
Veteran
Posts: 6208
Joined: Sat Jan 17, 2015 7:48 am
Contact:

Re: How to perform boolean operation on a PartDesign body?

Post by onekk »

dprojects wrote: Tue Oct 04, 2022 5:43 pm Maybe he is time traveler, and he travel in space and time ;-) so for him 2 years is just blink of the eye ;-)
Ok good joke.


But as "first post" note post count it seems somewhat strange to try to answer in "German" to an English Post 2 years old, without even using code tags.


My first reaction was "more bad" and I've softened the post, but probably it my actual "bad mood" that is making me be "too picky" about "strange posts".

If he is a time traveler I was pleased to see some shematics of his "time machine" done using FreeCAD, it will be way more interesting, hoping it will explain it in "plain English" "for strangers as I'm Italian", we have not developed yet a time machine, but usually it suffice to move few kilometers from home to find something 2000 or more years to old to see. :lol:

Kind Regards

Carlo D.
GitHub page: https://github.com/onekk/freecad-doc.
- In deep articles on FreeCAD.
- Learning how to model with scripting.
- Various other stuffs.

Blog: https://okkmkblog.wordpress.com/
imm
Posts: 267
Joined: Wed Nov 03, 2021 1:00 pm

Re: How to perform boolean operation on a PartDesign body?

Post by imm »

I found it interesting.

For those that may want an updated version of the scripts above
configured as a macro I am providing the attached.

Designed to be used after you select 2 bodies:

First is the body to be Cut.
Second is the body to provide the Tool Shape for Cutting.
dadocut.py
(1.65 KiB) Downloaded 12 times
.... or if you just want to browse....

Code: Select all

import FreeCAD as App
import FreeCADGui as Gui

def copyViewAttr(view):
    view[0].ShapeColor=getattr(view[1],'ShapeColor',view[0].ShapeColor)
    view[0].LineColor=getattr(view[1],'LineColor',view[0].LineColor)
    view[0].PointColor=getattr(view[1],'PointColor',view[0].PointColor)
    view[0].Transparency=getattr(view[1],'Transparency',view[0].Transparency)
    view[0].DisplayMode=getattr(view[1],'DisplayMode',view[0].DisplayMode)
    return

def makeCut(body, tool):
    doc = App.ActiveDocument;
    tip = body.Tip
    boolean = body.newObject('PartDesign::Boolean','Boolean')
    view = tip.getLinkedObject(True).ViewObject
    viewBool = boolean.ViewObject
    copyViewAttr([viewBool, view])
    boolean.setObjects( [tool,])
    boolean.Type = 1
    doc.recompute()
    return (boolean)

# -- get document pointer
doc = App.ActiveDocument

sel = Gui.Selection.getSelection()

# -- sanity checks
IsGoodSelection = False
if len(sel) != 2:
    App.Console.PrintError("Select Body elements. First is in the Body to 'Cut'. 2nd is in the Body to be used as a Tool.\n")
else:
    BodyCnt = 0
    for s in sel:
        if s.TypeId == 'PartDesign::Body':
            BodyCnt += 1
    if BodyCnt != 2:
        App.Console.PrintError("Select Body elements. First is in the Body to 'Cut'. 2nd is in the Body to be used as a Tool.\n")
    else:
        IsGoodSelection = True

# -- now proceed 
if IsGoodSelection:
    tobj = sel[0]
    sobj = sel[1]
    
    binder = doc.getObject(tobj.Name).newObject('PartDesign::SubShapeBinder','Binder')
    binder.Support = sobj.Parents
    doc.recompute()
    
    cobj = makeCut(tobj,binder)
Post Reply