[solved] Puzzling empty shape script error

Need help, or want to share a macro? Post here!
Forum rules
Be nice to others! Respect the FreeCAD code of conduct!
Post Reply
spanner888
Posts: 326
Joined: Tue May 28, 2019 10:51 am

[solved] Puzzling empty shape script error

Post by spanner888 »

So I was scripting some repetitive shape/cut/array designs, all of which went well, until, I started to use some of the created shape properties with code like:

Code: Select all

for i in range(len(obj.Shape.Faces)):
	face = "Face%d" % (i+1)
	f = obj.Shape.getElement(face)
The above worked well initially, then started giving occasional errors like:

Code: Select all

    f = obj.Shape.getElement(face)
	<class 'Part.OCCError'>: Cannot get sub-shape from empty shape
Investigation showed that some of the compound part shapes, seemed to have zero faces, as the following code produced 0, [] for some shapes.

Code: Select all

print (len(obj.Shape.Faces))
print (obj.Shape.Faces)
However, the shape(s) and faces are clearly visible in the document and also you can select & send the shape to the python console and use the exact same print's in the python console to see the correct properties.

Code: Select all

>>> ### Begin command Std_SendToPythonConsole
>>> obj = App.getDocument("emptyShapeIssue").getObject("cutArray3_cut")
>>> ### End command Std_SendToPythonConsole
>>> print (len(obj.Shape.Faces))
20
>>> print (obj.Shape.Faces)
[<Face object at 000001483D271D20>, <Face object at 000001483D2715A0>, <Face object at 000001483D271BA0>, <Face object at 000001483D271BE0>, <Face object at 000001483D272220>, <Face object at 000001483D2716E0>, <Face object at 000001483D271DA0>, <Face object at 000001483D271720>, <Face object at 000001483D2720E0>, <Face object at 000001483D271C20>, <Face object at 000001483D271960>, <Face object at 000001483D271E20>, <Face object at 000001483D271760>, <Face object at 000001483D271F20>, <Face object at 000001483D2718A0>, <Face object at 000001483D2719E0>, <Face object at 000001483D2713E0>, <Face object at 000001483D271420>, <Face object at 000001483D271A60>, <Face object at 000001483D271AE0>]
Now it would not surprise me one bit if I was doing something silly, but I have attacked this from several angles, for example coding my own arrays using std_link as well as using draft rectangular array with & without using link and fusing.

But I cannot get past this situation.

Can anyone help nudge me along with some suggestions?

A trimmed down macro to reproduce this is below:

Code: Select all

import FreeCAD
from FreeCAD import Base, Rotation, Vector
import Draft

# looking into empty shape issue 

def cutArrayFrom(DOC, treeLabel, arrayShape, modelShape = None,
                x_inc = 20, y_inc = 10, z_inc = 0, 
                x_count = 2, y_count = 2, z_count = 1, use_link=True):

    drftArray = Draft.make_ortho_array(arrayShape, 
                                        Vector(x_inc, 0.0, 0.0), 
                                        Vector(0.0, y_inc, 0.0), 
                                        Vector(0.0, 0.0, z_inc), 
                                        x_count, y_count, z_count, use_link)
    drftArray.Fuse = True
    # next seem smore use in arch wb etc...
    # Draft.autogroup(drftArray)

    if modelShape is None:
        return drftArray
    else:
        cut = DOC.addObject('Part::Cut', treeLabel + '_cut')
        cut.Base = modelShape
        cut.Tool = drftArray
        DOC.recompute
        return cut

DOC_NAME = "emptyShapeIssue"
DOC = FreeCAD.activeDocument()
if DOC is None:
    FreeCAD.newDocument(DOC_NAME)
    FreeCAD.setActiveDocument(DOC_NAME)
    DOC = FreeCAD.activeDocument()
else:
    for obj in DOC.Objects:
        DOC.removeObject(obj.Name)

# for draft array
useLink = True

# 0 create a box only --------------------------------------------------
box = DOC.addObject('Part::Box', 'Box')
box.Width = 2*156.5
box.Length = 2*155
box.Height = 19
box.Placement.Base = Vector(-156.5, -155, 0)

print (box)
print (len(box.Shape.Faces))
print (box.Shape.Faces)
# 0 end ----------------------------------------------------------------

# 1 cut cyl array ------------------------------------------------------
cyl = DOC.addObject('Part::Cylinder', 'oneCylinder')
cyl.Radius = 2.5
cyl.Height = 30
cyl.Angle = 360
cyl.Placement.Base = Vector(-25, -15, 0)

cutArray1 = cutArrayFrom(DOC, 'cutArray1', cyl, box, 30.0, 20.0, 0.0, 2, 3, useLink)

DOC.recompute()
print (cutArray1)
print (cutArray1.Shape)
print (len(cutArray1.Shape.Faces))
print (cutArray1.Shape.Faces)
# 1 end ----------------------------------------------------------------

#2 more cyls
cyl2 = DOC.addObject('Part::Cylinder', 'cyl2')
cyl2.Radius = 2.5
cyl2.Height = 30
cyl2.Angle = 360
cyl2.Placement.Base = Vector(-65, -55, 0)
cutArray2 = cutArrayFrom(DOC, 'cutArray2', cyl2, cutArray1, 30.0, 20.0, 0.0,  2, 2, useLink)

DOC.recompute
print (cutArray2)
print (cutArray2.Shape)
print (len(cutArray2.Shape.Faces))
print (cutArray2.Shape.Faces)
# 2 end ----------------------------------------------------------------

# 3 cut cyl array ------------------------------------------------------
some_cyl = DOC.addObject('Part::Cylinder', 'oneCylinder')
some_cyl.Radius = 6
some_cyl.Height = 30
some_cyl.Angle = 360
some_cyl.Placement.Base = Vector(-100, -100, 0)
cutArray3 = cutArrayFrom(DOC, 'cutArray3', some_cyl, cutArray2, 20, 25, 0,  2, 2, useLink)

                               
DOC.recompute
print('printing just created var/props')
print (cutArray3)
print (cutArray3.Shape)
print (len(cutArray3.Shape.Faces))
print (cutArray3.Shape.Faces)

for i in range(22):
    face = "Face%d" % (i+1)
    f = cutArray3.Shape.getElement(face)
    print(f)
    
# print('printing info from App.getDocument().getObject .. as per send to console')
# sh =  App.getDocument(DOC_NAME).getObject("cutArray3_cut")
# print (sh)
# print (len(sh.Shape.Faces))
# print (sh.Shape.Faces)

# f = sh.Shape.getElement('Face1')
# f1 = cutArray3.Shape.getElement('Face1')
# 3 end --------------------------------------------------------------------

# ----------------------------------
# console script to make solid - works in console, fails here
    # __s__=Part.Solid(Part.Shell(__s__))
    # <class 'Part.OCCError'>: Shape is null
### Begin command Part_MakeSolid
# import Part
# __s__=App.ActiveDocument.cutArray3_cut.Shape.Faces
# __s__=Part.Solid(Part.Shell(__s__))
# __o__=App.ActiveDocument.addObject("Part::Feature","cutArray3_cut_solid")
# __o__.Label="cutArray3_cut (Solid)"
# __o__.Shape=__s__
# del __s__, __o__

# sld =  App.getDocument("emptyShapeIssue").getObject("cutArray3_cut_solid")
# print (sld)
# print (len(sld.Shape.Faces))
# print (sld.Shape.Faces)
# ----------------------------------

Code: Select all

OS: Windows 10 Version 2009
Word size of OS: 64-bit
Word size of FreeCAD: 64-bit
Version: 0.19.24291 (Git)
Build type: Release
Branch: releases/FreeCAD-0-19
Hash: 7b5e18a0759de778b74d3a5c17eba9cb815035ac
Python version: 3.8.6+
Qt version: 5.15.2
Coin version: 4.0.1
OCC version: 7.5.0
Locale: English/Australia (en_AU)
Have also reproduced on some recent Linux versions and a very recent build from master.
Last edited by spanner888 on Fri Jun 25, 2021 9:47 pm, edited 1 time in total.
User avatar
Roy_043
Veteran
Posts: 8456
Joined: Thu Dec 27, 2018 12:28 pm

Re: Puzzling empty shape script error

Post by Roy_043 »

Maybe you should first check this:

Code: Select all

DOC.recompute
vs.

Code: Select all

DOC.recompute()
spanner888
Posts: 326
Joined: Tue May 28, 2019 10:51 am

Re: Puzzling empty shape script error

Post by spanner888 »

Brilliant, that fixed my problem, thanks.

And :oops: , that was a bit sillier mistake than I expected.
Post Reply