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:
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
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.
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
Maybe you can file a feature request ?
sketch_roof_issue_example - r.FCStd
Screenshot from 2024-01-17 23-55-54.png
Screenshot from 2024-01-17 23-55-57.png
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 ?
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.
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
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)
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?
one one hand, to generate the Roof form, with other parameters use input,
and on the other hand, generate a ‘subtraction volume’
so if the Roof object is in the Subtraction of the Wall object
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 ?
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 . This will take a very long time. If we have discussed the essence of the problem and solutions, this is already half the work.
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
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
(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)
If user create a custom roof form (a solid like you did, not sure if it can be plane, shell or otherwise though) -
then should have a switch (/boolean attribute) e.g. ‘Generate Subtraction From Custom Roof Shape’ (i.e. not from a Wire as default workflow)
user can switch on this so your proposed code should run
Not sure if extruding a solid would generate another solid or it would have problem - please test
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 )
there is a SubVolume (not checked if exactly the same word or not), can add in Arch Roof here,
so if this is linked to the custom object with solid, it would be used as the subtraction volume
(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
Can you elaborate more how your test doesn’t work if the walls are z !=0 ?
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.