Sketch based Arch_Roof and wall substraction

Hi everyone!

I’m trying to create a complex shaped roof(Arch_Roof) based on a sketch.
I started with a simple example.

I have following plan:

  • Create sketch for my roof
  • Extrude it, and make solid
  • Convert to Arch Roof object
  • Substract roof from my walls

And I’m stuck on the last point.

  • Previously, I compared the results of the work with a ArchRoof created in the usual way (using wire), and everything works fine here (green houses in the picture). But my roof has a complex shape and that’s why I want to use a sketch.

I got following problems:

  1. Subtraction only works for the roof itself, but I expect it to work across the entire z-plane(the walls don’t go higher than the roof) (Like it works in * ). Recompute dosen’t help.
    wrong_substract.png
  2. If i move my walls up (change z coordinate) i got following stacktrace on a Wall object
    (i need such walls for second level of my house)
   19:50:17 Traceback (most recent call last):
File "/Applications/FreeCAD.app/Contents/Resources/Mod/Arch/ArchWall.py", line 968, in execute
base = self.processSubShapes(obj,base,pl)
File "/Applications/FreeCAD.app/Contents/Resources/Mod/Arch/ArchComponent.py", line 802, in processSubShapes
subvolume.Placement = placement.multiply(subvolume.Placement)
<class 'ReferenceError'>: This object is immutable, you can not set any attribute or call a method
19:50:17 Recompute failed!

If roof created by wire method everything works fine:
default_roof_ok.png

Please help me understand what I’m doing wrong.
I am attaching an example project to make it easier to understand me.

OS: macOS 13.5
Word size of FreeCAD: 64-bit
Version: 0.21.1.33694 (Git)
Build type: Release
Branch: (HEAD detached at 0.21.1)
Hash: f6708547a9bb3f71a4aaade12109f511a72c207c
Python 3.10.12, Qt 5.15.8, Coin 4.0.0, Vtk 9.2.5, OCC 7.6.3
Locale: C/Default (C)
Installed mods: 
  * QuickMeasure 2022.10.28
  * BIM 2021.12.0

You can use Part Slice to separate the wall into two segments, then delete the top one.

slice.JPG

Thank you! This is an interesting approach.
At least it looks nice now.


And I don’t really understand how correct it is to work with architectural objects in this way? I read somewhere that for Arch_* it is better to use subtraction rather than Part operations.

If someone can explain the nature of these errors, I would be grateful.

Because I’m new to CAD, and I don’t understand whether this is a bug or I haven’t understood some basics.
I expected that this should work through normal subtraction, like it works with a Arch_Roof created with regular wire(not sketch)

To my understanding either a DWire or Sketch works, only when they are on horizontal disposition. The algorithm only deduce the 3d roof form using the DWire/Sketch as its outline of the roof edge.

https://wiki.freecad.org/Arch_Roof
CounterclockwiseWire.png
However Arch Window has a SubVolume attribute, user can create a custom made subtraction volume e.g. to subtract an opening at a wall.

Interesting to know Arch Roof does not have that attribute. Anyway can workaround, see attached model, hope it helps :slight_smile:

Maybe you can file a feature request ? :smiley:

sketch_roof_issue_example - r.FCStd
Screenshot from 2024-01-17 23-55-54.png
Screenshot from 2024-01-17 23-55-57.png

An Arch_Roof created from a wire has a built-in subtraction volume.

But indeed it should also have a way to define a custom subtraction volume. IIRC everything is there in the code ( https://github.com/FreeCAD/FreeCAD/blob/main/src/Mod/Arch/ArchComponent.py#L790 ) and the only missing thing is for the roof object to have a Subvolume property.

Maybe one of you could help testing this? It basically boils down to:

  • Create a roof object
  • Right-click the roof object’s properties → Show All
  • Right-click again → Add new property
  • Make a Part::TopoShape property, name it “Subvolume” (make sure the group prefix is not used)
  • Create a shape object to be the subtraction volume
  • Set it as the Subvolume property of the roof

If that works, then we can simply add the property by default in the FreeCAD code.

Noted you and Roy_043 just merged a commit and the line become L792 (rather than L790).

I had a look earlier, #L789 would be run if it is Roof

           if (Draft.getType(o.getLinkedObject()) == "Window") or (Draft.isClone(o,"Window",True)):
                    # windows can be additions or subtractions, treated the same way
                    subvolume = o.getLinkedObject().Proxy.getSubVolume(o)
#L789      elif (Draft.getType(o) == "Roof") or (Draft.isClone(o,"Roof")):
                    # roofs define their own special subtraction volume
                    subvolume = o.Proxy.getSubVolume(o)
#L792      elif hasattr(o,"Subvolume") and hasattr(o.Subvolume,"Shape"):
                    # Any other object with a Subvolume property
                    subvolume = o.Subvolume.Shape.copy()
                    if hasattr(o,"Placement"):
                        subvolume.Placement = o.Placement.multiply(subvolume.Placement)
                    ...

Now, seems something needs to be done at ArchRoof.py. Anyone would like to test what Yorik set out and implement the feature ? :slight_smile:

    def getSubVolume(self, obj):
        '''returns a volume to be subtracted'''
        if obj.Base:
            if hasattr(obj.Base, "Shape"):
                if obj.Base.Shape.Solids:
                    return obj.Shape
                else :
                    if hasattr(self, "sub"):
                        if self.sub:
                            return self.sub
                        else :
                            self.execute(obj)
                            return self.sub
                    else :
                        self.execute(obj)
                        return self.sub
        return None

Maybe i do something wrong, but i fail to do that.

  1. There is no Part::TopoShape property-type in selector
    Most closest type is Part::PropertyTopoShapeList, and i even put there some shape(as single-element list) - but I didn’t really understand what effect to expect (nothing happened), and I thought that probably it just not working due wrong datatype

  2. I try add property via python-console, got following stacktrace:

>>> obj.addProperty('Part::TopoShape','Subvolume')
Traceback (most recent call last):
  File "<input>", line 1, in <module>
TypeError: Invalid type Part::TopoShape for property sketch_roof_issue_example#Roof003.Subvolume In App::Property *App::DynamicProperty::addDynamicProperty(App::PropertyContainer &, const char *, const char *, const char *, const char *, short, bool, bool) in src/App/DynamicProperty.cpp:181

(maybe this is the reason, why this datatype is not avaliable in selector)

Thanks for idea!

I think a lot about this roof, and I also invented the same, but drop such approach, because it didn’t work if we bump z-coordinates of our walls(immutable error like in my first message, strange thing)
While PartSlice method suggested by @NewJoker works in both cases.

In my question about working with architectural objects, I meant specifically working with Part.Slice methods - after slice, in essence, we kind of “destroy” our wall object, and now, instead of an architectural object, we have an object of another category(family maybe, I hope you understand what i mean), with which we must do further work - add windows to it, and so on. And I assume that some non-obvious problems may arise here.


Did I understand you correctly that i cannot use a non-horizontal sketch for roof-modeling? I also read this documentation, but no ideas appeared.

But in my case, I use the sketch not to create a roof, but to create a shape! ( On the basis of which I then make the roof).

I read somewhere that architectural objects can have any shape, and this is officially supported. Did I understand correctly?

To my understanding, current Arch Roof algorithm:

  1. use a 2d closed wire, either a DWire, or a Sketch
  2. probably only in horizontal disposition
  3. one one hand, to generate the Roof form, with other parameters use input,
  4. and on the other hand, generate a ‘subtraction volume’
  5. so if the Roof object is in the Subtraction of the Wall object
  6. the Wall subtract the so Roof generated ‘subtraction volume’ from the Wall shape itself

So, if the Roof form is not generated according to above workflow, the current algorithm would not help;
until someone add other algorithm - maybe like extruding whatever Shape (solid, face?) the Roof has vertically, for subtraction.

Interested in implementing more features for your usecases ? :smiley:

Thanks paullee, after reading your explanations I looked at the Arch_Roof code a little and realized that the algorithm actually has separate processing for these two different scenarios.

I draw the following conclusion: it is possible to create a roof from an solid object, but only this shape (and not everything that is above it in the horizontal plane) will be subtracted from the walls of the building. By the way, this can be seen in my examples, and after I looked at the code, I seem to understand why this happens.

And it looks like yorik advice won’t work without modifying the code, because… we will always go to the function

o.Proxy.getSubVolume(o)

if our object is Roof (even Subvolume property exists).
due following if:

elif (Draft.getType(o) == "Roof") or (Draft.isClone(o,"Roof")):

To implement this feature by myself, I need learn a lot about internal freecad structure, I don’t understand half of the code :smiley:. This will take a very long time. If we have discussed the essence of the problem and solutions, this is already half the work.

My thoughts are the following:

  1. Create sub field even roof created from custom solid shape(not null obj.Base.Shape.Solids)
    https://github.com/FreeCAD/FreeCAD/blob/main/src/Mod/Arch/ArchRoof.py#L712
if hasattr(obj.Base, "Shape"):  
    if obj.Base.Shape.Solids:  
        base = obj.Base.Shape  
        self.sub = base.extrude(Vector(0.0, 0.0, 1000000.0)) # extrude base by z-plane UP 
        # (not sure base.extrude will works - just for illustrate my idea)
        #pl = obj.Base.Placement
  1. Respect self.sub field even we are created from solid object (not wire)
    https://github.com/FreeCAD/FreeCAD/blob/main/src/Mod/Arch/ArchRoof.py#L826
def getSubVolume(self, obj):  
    '''returns a volume to be subtracted'''  
    if not obj.Base:  
        return None  
  
    if hasattr(self, "sub"):  
        if self.sub:  
            return self.sub  
  
    self.execute(obj)  
    return self.sub

Honestly, I would be happy with the solution you proposed (with extrusion), but it doesn’t work if the walls are z != 0 (immutable error)

https://github.com/FreeCAD/FreeCAD/blob/main/src/Mod/Arch/ArchComponent.py#L803
It’s interesting that the comment on this line (where the error occurs) leads to your message: https://devtalk.freecad.org/t/pr-merged-moving-windowsdoors/59243/21

Maybe you understand why this error occurs?

Glad to have another developer on board :smiley:

(Roy_043, other than Yorik, is an experienced and very active developer here, particularly in Arch/BIM/Draft.)

My 2 cents here, should have 2 possibilities / workflow :-
(EDIT - I have similar findings as yours as discussed earlier)

  1. If user create a custom roof form (a solid like you did, not sure if it can be plane, shell or otherwise though) -
  2. then should have a switch (/boolean attribute) e.g. ‘Generate Subtraction From Custom Roof Shape’ (i.e. not from a Wire as default workflow)
  3. user can switch on this so your proposed code should run
  4. Not sure if extruding a solid would generate another solid or it would have problem - please test
  5. If user want a custom subtraction form (e.g. the solid I created based on your Sketch to generate your custom Roof form) -
    ( This workflow is in fact same as those already in Arch Window )
  6. there is a SubVolume (not checked if exactly the same word or not), can add in Arch Roof here,
  7. so if this is linked to the custom object with solid, it would be used as the subtraction volume
  8. (or the attribute ‘Generate Subtraction From Custom Roof Shape’ discussed above should become ‘Subtraction Volume from’ with options like ‘Wire’, ‘SubVolume’, ‘Custom Roof Sold’, etc. …?

Feel free to discuss and propose a PR when it is ready :smiley:

Can you elaborate more how your test doesn’t work if the walls are z !=0 ? :slight_smile:

I realised that there was some minor problem with ArchRoof subtraction algorithm previously but have no time to report or even check - the subtraction sometime needs to be recomputed twice or more to make the subtraction z correct.

My PR fixed some basic superimposition calculation mathematical problem (error in multiplication order) but I guess these 2 problems are not related somehow.