[Discussion] A different approach to Arch modelling (Arch Assembly?)

A forum dedicated to the Draft, Arch and BIM workbenches development.
Forum rules
Be nice to others! Respect the FreeCAD code of conduct!
paullee
Veteran
Posts: 5136
Joined: Wed May 04, 2016 3:58 pm

Re: [Discussion] A different approach to Arch modelling (Arch Assembly?)

Post by paullee »

So this would not no longer use Sketch like currently ArchWindow does to generate shape right ?
User avatar
onekk
Veteran
Posts: 6222
Joined: Sat Jan 17, 2015 7:48 am
Contact:

Re: [Discussion] A different approach to Arch modelling (Arch Assembly?)

Post by onekk »

Code: Select all

"""
 FreeCAD script - macro
 to be used with the OnePartLib part library

 copyright 2019 Carlo Dormeletti (onekk)
 carlo.dormeletti@yahoo.com

 Version:   

"""

import sys
import datetime
import time
import importlib

import FreeCAD
import FreeCADGui
from FreeCAD import Rotation, Vector
import Part
import Draft
#import Mesh
#import MeshPart

# BEGIN DOC Settings
DEBUG = True
DBG_LOAD = False
DOC_NAME = "finestra"
# END DOC settings


#from math import pi cos, sin, pi, sqrt
import numpy as np


def activate_doc():
    """activate document"""
    FreeCAD.setActiveDocument(DOC_NAME)
    FreeCAD.ActiveDocument = FreeCAD.getDocument(DOC_NAME)
    FreeCADGui.ActiveDocument = FreeCADGui.getDocument(DOC_NAME)
    if DBG_LOAD is True:    
        print("{0} activated".format(DOC_NAME))


def setview():
    """Rearrange View"""
    DOC.recompute()
    VIEW.viewAxometric()
    VIEW.setAxisCross(True)
    VIEW.fitAll()


def clear_doc():
    """Clear the active document deleting all the objects"""
    for obj in DOC.Objects:
        DOC.removeObject(obj.Name)

if FreeCAD.ActiveDocument is None:
    FreeCAD.newDocument(DOC_NAME)
    if DBG_LOAD is True:    
        print("Document: {0} Created".format(DOC_NAME))

# test if there is an active document with a "proper" name
if FreeCAD.ActiveDocument.Name == DOC_NAME:
    if DBG_LOAD is True:    
        print("DOC_NAME exist")
else:
    if DBG_LOAD is True:    
        print("DOC_NAME is not active")
    # test if there is a document with a "proper" name
    try:
        FreeCAD.getDocument(DOC_NAME)
    except NameError:
        if DBG_LOAD is True:    
            print("No Document: {0}".format(DOC_NAME))
        FreeCAD.newDocument(DOC_NAME)
        if DBG_LOAD is True:    
            print("Document {0} Created".format(DOC_NAME))

DOC = FreeCAD.getDocument(DOC_NAME)
GUI = FreeCADGui.getDocument(DOC_NAME)
VIEW = GUI.ActiveView    
if DBG_LOAD is True:    
    print("DOC : {0} GUI : {1}".format(DOC, GUI))

activate_doc()

if DBG_LOAD is True:    
    print(FreeCAD.ActiveDocument.Name)

clear_doc()

EPS = 0.002

VZOR = Vector(0, 0, 0)
ROT0 = Rotation(0, 0, 0)
ROTX90 = Rotation(0, 0, 90)
ROTXN90 = Rotation(0, 0, -90)
ROTY90 = Rotation(0, 90, 0)
ROTZ180 = Rotation(180, 0, 0)
#Used to shorten most Placements
PL0 = FreeCAD.Placement(VZOR, ROT0)

# DOCUMENT START HERE

fin_h = 1400
fin_w = 1200
fin_th = 50
cont_w = 50
vetro_th = 3
vetro_add = 5

def telaio():
    e_tel_w = fin_w
    e_tel_h = fin_h
    i_tel_w = fin_w - cont_w * 2
    i_tel_h = fin_h - cont_w * 2
    v_w = i_tel_w + vetro_add * 2
    v_h = i_tel_h + vetro_add * 2

    ep0 = (e_tel_w * -0.5, 0, 0)
    ep1 = (e_tel_w * 0.5, 0, 0)
    ep2 = (e_tel_w * 0.5, 0, e_tel_h)
    ep3 = (e_tel_w * -0.5, 0, e_tel_h)

    ip0 = (i_tel_w * -0.5, 0, cont_w)
    ip1 = (i_tel_w * 0.5, 0, cont_w)
    ip2 = (i_tel_w * 0.5, 0, cont_w + i_tel_h)
    ip3 = (i_tel_w * -0.5, 0, cont_w + i_tel_h)
 
    vp0 = (v_w * -0.5, 0, cont_w - vetro_add)
    vp1 = (v_w * 0.5, 0, cont_w  - vetro_add)
    vp2 = (v_w * 0.5, 0, cont_w - vetro_add + v_h)
    vp3 = (v_w * -0.5, 0, cont_w - vetro_add + v_h)
   
    tel_b = (ep0, ep1, ip1, ip0, ep0)
    tel_bp = Part.makePolygon([Vector(*vtx) for vtx in tel_b])

    tel_r = (ep1, ep2, ip2, ip1, ep1)
    tel_rp = Part.makePolygon([Vector(*vtx) for vtx in tel_r])

    tel_t = (ep2, ep3, ip3, ip2, ep2)
    tel_tp = Part.makePolygon([Vector(*vtx) for vtx in tel_t])

    tel_l = (ep3, ep0, ip0, ip3, ep3)
    tel_lp = Part.makePolygon([Vector(*vtx) for vtx in tel_l])

    tel_fb = Part.makeFilledFace(tel_bp.Edges) 
    tel_fbs = tel_fb.extrude(Vector(0, fin_th, 0))

    tel_fr = Part.makeFilledFace(tel_rp.Edges) 
    tel_frs = tel_fr.extrude(Vector(0, fin_th, 0))
    
    tel_ft = Part.makeFilledFace(tel_tp.Edges) 
    tel_fts = tel_ft.extrude(Vector(0, fin_th, 0))

    tel_fl = Part.makeFilledFace(tel_lp.Edges) 
    tel_fls = tel_fl.extrude(Vector(0, fin_th, 0))

    vetro_pt = (vp0, vp1, vp2, vp3, vp0)    
    vetro_p = Part.makePolygon([Vector(*vtx) for vtx in vetro_pt])

    vetro_f = Part.makeFilledFace(vetro_p.Edges) 
    vetro_s = vetro_f.extrude(Vector(0, vetro_th, 0))
    vetro_s.Placement = FreeCAD.Placement(Vector(0, (fin_th - vetro_th) * 0.5, 0), ROT0)

    obj0 = DOC.addObject("Part::Feature", "telaio")
    obj0.Shape = Part.makeCompound((tel_fbs, tel_frs, tel_fts, tel_fls))
    obj0.ViewObject.ShapeColor = (0.54, 0.27, 0.07) # rgb(139, 69, 19)

    obj1 = DOC.addObject("Part::Feature", "vetro")
    obj1.Shape = vetro_s
    obj1.ViewObject.ShapeColor = (0.33, 0.67, 1.00)
    obj1.ViewObject.Transparency = 50

    obj_f = DOC.addObject("Part::Compound", "finestra")
    obj_f.Links = [obj0, obj1]  
    
    return obj_f

telaio()
See if this is a good approach, I have returned the object as a compound made from the frame and the glass, in this compound we could add the sill and whatever we want, as a compound the colors are retained.

If you use my code, the axes are shown you will see that the window is created with the bottom center around Vector(0,0,0) and the window "extruding" by Y axes positive, seems to be a viable reference point, it lacks of the offset in y for the placement on the wall thickness and the sill dimensions (width length and thickness) to calculate the protrusion


#paulee no this is generating directly the shapes, draft object are returned as Part.Shapes with some additional properties.

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/
carlopav
Veteran
Posts: 2062
Joined: Mon Dec 31, 2018 1:49 pm
Location: Venice, Italy

Re: [Discussion] A different approach to Arch modelling (Arch Assembly?)

Post by carlopav »

onekk wrote: Sun Jun 07, 2020 5:02 pm See if this is a good approach, I have returned the object as a compound made from the frame and the glass, in this compound we could add the sill and whatever we want, as a compound the colors are retained.
Nice! The compound is a good approach, but I'd like the default window shape to be completely embedded into the opening object (so we spare several objects in the final count). Basically it's the Opening object that makes the final compound grouping the window and the sill (https://github.com/yorikvanhavre/BIM_Wo ... ing.py#L47).
The "get_window_shape" should be called right here: https://github.com/yorikvanhavre/BIM_Wo ... ng.py#L187

If you use my code, the axes are shown you will see that the window is created with the bottom center around Vector(0,0,0) and the window "extruding" by Y axes positive, seems to be a viable reference point, it lacks of the offset in y for the placement on the wall thickness and the sill dimensions (width length and thickness) to calculate the protrusion
Hmm, I need the Placement.Base of the window in this red point:
Cattura.JPG
Cattura.JPG (30 KiB) Viewed 945 times

@paullee, this is just a prototype for some default windows. Do not worry, I do not intend to loose the generation by sketch, but its slightly more complicated, so will come after :)
follow my experiments on BIM modelling for architecture design
paullee
Veteran
Posts: 5136
Joined: Wed May 04, 2016 3:58 pm

Re: [Discussion] A different approach to Arch modelling (Arch Assembly?)

Post by paullee »

Thank, have fun :lol:
User avatar
onekk
Veteran
Posts: 6222
Joined: Sat Jan 17, 2015 7:48 am
Contact:

Re: [Discussion] A different approach to Arch modelling (Arch Assembly?)

Post by onekk »

carlopav wrote: Sun Jun 07, 2020 5:32 pm
If you use my code, the axes are shown you will see that the window is created with the bottom center around Vector(0,0,0) and the window "extruding" by Y axes positive, seems to be a viable reference point, it lacks of the offset in y for the placement on the wall thickness and the sill dimensions (width length and thickness) to calculate the protrusion
Hmm, I need the Placement.Base of the window in this red point:
Cattura.JPG

This is the reference point, the center bottom of the frame, with the window extending in the z direction of an amount equal to opening_height

@paullee, this is just a prototype for some default windows. Do not worry, I do not intend to loose the generation by sketch, but its slightly more complicated, so will come after :)

The compound is retaining the colors, if I don't remember well if i fuse the object i will loose the colors and the ability to color the different faces, it is treated as a unique solid.

Let me see some more work, on the make the frame is only the external frame, now the opening part could be generated, 50mm for the frame and 50 mm for the opening simply "nested boxes" to reatin the seam each side has to be generated as a trapezoid.


More to come.

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/
carlopav
Veteran
Posts: 2062
Joined: Mon Dec 31, 2018 1:49 pm
Location: Venice, Italy

Re: [Discussion] A different approach to Arch modelling (Arch Assembly?)

Post by carlopav »

great, have fun!
follow my experiments on BIM modelling for architecture design
User avatar
onekk
Veteran
Posts: 6222
Joined: Sat Jan 17, 2015 7:48 am
Contact:

Re: [Discussion] A different approach to Arch modelling (Arch Assembly?)

Post by onekk »

Code: Select all

"""
 FreeCAD script - macro
 to be used with the OnePartLib part library

 copyright 2019 Carlo Dormeletti (onekk)
 carlo.dormeletti@yahoo.com

 Version:   

"""

import sys
import datetime
import time

import FreeCAD
import FreeCADGui
from FreeCAD import Rotation, Vector
import Part
import Draft
#import Mesh
#import MeshPart

# BEGIN DOC Settings
DEBUG = True
DBG_LOAD = False
DOC_NAME = "finestra"
# END DOC settings


#from math import pi cos, sin, pi, sqrt
import numpy as np


def activate_doc():
    """activate document"""
    FreeCAD.setActiveDocument(DOC_NAME)
    FreeCAD.ActiveDocument = FreeCAD.getDocument(DOC_NAME)
    FreeCADGui.ActiveDocument = FreeCADGui.getDocument(DOC_NAME)
    if DBG_LOAD is True:    
        print("{0} activated".format(DOC_NAME))


def setview():
    """Rearrange View"""
    DOC.recompute()
    VIEW.viewAxometric()
    VIEW.setAxisCross(True)
    VIEW.fitAll()


def clear_doc():
    """Clear the active document deleting all the objects"""
    for obj in DOC.Objects:
        DOC.removeObject(obj.Name)

if FreeCAD.ActiveDocument is None:
    FreeCAD.newDocument(DOC_NAME)
    if DBG_LOAD is True:    
        print("Document: {0} Created".format(DOC_NAME))

# test if there is an active document with a "proper" name
if FreeCAD.ActiveDocument.Name == DOC_NAME:
    if DBG_LOAD is True:    
        print("DOC_NAME exist")
else:
    if DBG_LOAD is True:    
        print("DOC_NAME is not active")
    # test if there is a document with a "proper" name
    try:
        FreeCAD.getDocument(DOC_NAME)
    except NameError:
        if DBG_LOAD is True:    
            print("No Document: {0}".format(DOC_NAME))
        FreeCAD.newDocument(DOC_NAME)
        if DBG_LOAD is True:    
            print("Document {0} Created".format(DOC_NAME))

DOC = FreeCAD.getDocument(DOC_NAME)
GUI = FreeCADGui.getDocument(DOC_NAME)
VIEW = GUI.ActiveView    
if DBG_LOAD is True:    
    print("DOC : {0} GUI : {1}".format(DOC, GUI))

activate_doc()

if DBG_LOAD is True:    
    print(FreeCAD.ActiveDocument.Name)

clear_doc()

EPS = 0.002

VZOR = Vector(0, 0, 0)
ROT0 = Rotation(0, 0, 0)
ROTX90 = Rotation(0, 0, 90)
ROTXN90 = Rotation(0, 0, -90)
ROTY90 = Rotation(0, 90, 0)
ROTZ180 = Rotation(180, 0, 0)
#Used to shorten most Placements
PL0 = FreeCAD.Placement(VZOR, ROT0)

# DOCUMENT START HERE

def telaio(tel_name, tel_w, tel_h , tel_ww, tel_wh, tel_th, et=0):
    i_tel_w = tel_w - tel_ww * 2
    i_tel_h = tel_h - tel_wh * 2

    if et == 0:
        ofz = 0
    else:
        ofz = tel_wh

    ep0 = (tel_w * -0.5, 0, ofz)
    ep1 = (tel_w * 0.5, 0, ofz)
    ep2 = (tel_w * 0.5, 0, tel_h + ofz)
    ep3 = (tel_w * -0.5, 0, tel_h + ofz)

    ip0 = (i_tel_w * -0.5, 0, tel_ww + ofz)
    ip1 = (i_tel_w * 0.5, 0, tel_ww + ofz)
    ip2 = (i_tel_w * 0.5, 0, tel_ww + i_tel_h + ofz)
    ip3 = (i_tel_w * -0.5, 0, tel_ww + i_tel_h + ofz)
  
    tel_b = (ep0, ep1, ip1, ip0, ep0)
    tel_bp = Part.makePolygon([Vector(*vtx) for vtx in tel_b])

    tel_r = (ep1, ep2, ip2, ip1, ep1)
    tel_rp = Part.makePolygon([Vector(*vtx) for vtx in tel_r])

    tel_t = (ep2, ep3, ip3, ip2, ep2)
    tel_tp = Part.makePolygon([Vector(*vtx) for vtx in tel_t])

    tel_l = (ep3, ep0, ip0, ip3, ep3)
    tel_lp = Part.makePolygon([Vector(*vtx) for vtx in tel_l])

    tel_fb = Part.makeFilledFace(tel_bp.Edges) 
    tel_fbs = tel_fb.extrude(Vector(0, tel_th, 0))

    tel_fr = Part.makeFilledFace(tel_rp.Edges) 
    tel_frs = tel_fr.extrude(Vector(0, tel_th, 0))
    
    tel_ft = Part.makeFilledFace(tel_tp.Edges) 
    tel_fts = tel_ft.extrude(Vector(0, tel_th, 0))

    tel_fl = Part.makeFilledFace(tel_lp.Edges) 
    tel_fls = tel_fl.extrude(Vector(0, tel_th, 0))    

    obj0 = DOC.addObject("Part::Feature", tel_name)
    obj0.Shape = Part.makeCompound((tel_fbs, tel_frs, tel_fts, tel_fls))
    obj0.ViewObject.ShapeColor = (0.54, 0.27, 0.07) # rgb(139, 69, 19)

    return obj0


def finestra(opening_th=300, opening_height=1400, opening_width=1200,
             frame_width=50, frame_th=50, glass_th=21):
    vetro_add = 5
    # permit to differentiate from the top-bottom 
    # and left right    
    frame_height = frame_width 
    cont_w = frame_width * 2
    cont_h = frame_height * 2

    ea_w = opening_width - cont_w
    ea_h = opening_height - cont_h

    v_w = ea_w - cont_w + vetro_add * 2
    v_h = ea_h - cont_h + vetro_add * 2
 
    vp0 = (v_w * -0.5, 0, cont_w - vetro_add)
    vp1 = (v_w * 0.5, 0, cont_w  - vetro_add)
    vp2 = (v_w * 0.5, 0, cont_h - vetro_add + v_h)
    vp3 = (v_w * -0.5, 0, cont_h - vetro_add + v_h)

    vetro_pt = (vp0, vp1, vp2, vp3, vp0)    
    vetro_p = Part.makePolygon([Vector(*vtx) for vtx in vetro_pt])

    vetro_f = Part.makeFilledFace(vetro_p.Edges) 
    vetro_s = vetro_f.extrude(Vector(0, glass_th, 0))
    vetro_s.Placement = FreeCAD.Placement(Vector(0, (frame_th - glass_th) * 0.5, 0), ROT0)

    obj_t = telaio("telaio", opening_width, opening_height, frame_width, 
                   frame_height, frame_th, 0)
   
    obj_ea = telaio("elemento apribile", ea_w, ea_h, frame_width, 
                   frame_height, frame_th, 1)

    obj1 = DOC.addObject("Part::Feature", "vetro")
    obj1.Shape = vetro_s
    obj1.ViewObject.ShapeColor = (0.33, 0.67, 1.00)
    obj1.ViewObject.Transparency = 50

    obj_f = DOC.addObject("Part::Compound", "finestra")
    obj_f.Links = [obj_t, obj_ea, obj1]  
 
    return obj_f

finestra()



setview()
Here the new version, more parametric, let me know what other dimensions you need, it create three elements, the frame the open element and the glass (one box of 21mm thickness tunable in the method call)

As I'have no opening object i have chosen to make all the dimensions explicit, there is provision for a different horizontal frame_height differentiate from the vertical elements

The frame is created byu the same routine, adding an offset value, of frame_height (the heigh of horizontal element).

I hope this combine flexibility and speed, as I have not used primitives, but created objects individually i hope the speed will be not a problem, as only the final elements are created as docObjects.

From my point of view the only things that lacks is the offset Y (the distance from the outer side of the wall to "position" the window).

Just ion case we could add some other things, like the glass stopper, but i think that it will be an unuseful addition.

Maybe we could define one or two "open element" half of the width of the resulting frame.

What do you think

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/
carlopav
Veteran
Posts: 2062
Joined: Mon Dec 31, 2018 1:49 pm
Location: Venice, Italy

Re: [Discussion] A different approach to Arch modelling (Arch Assembly?)

Post by carlopav »

I think that this is cool!

window.gif
window.gif (46.86 KiB) Viewed 908 times
I like that you created a frame object that we can use multiple times inside the window! (edit: i stressed it a bit ad it seems quite fast!)

I modified it slightly to make it return a shape instead of creating a document object.
From my point of view the only things that lacks is the offset Y (the distance from the outer side of the wall to "position" the window).
In my idea this will be handled by the Opening object. (I already setted up the properties FillingAlignment and FillingDisplacement, I still miss the logic, but won't be difficult to implement).

For the colors I have to check how Draft_Clone does it. But I think that it could be feasible also grouping everything inside just one shape.

The prototype is inside BIM workbench experimental tools since ... NOW!
We could think about different presets... for example we could define how many panes the window should have (1-2-3-10?)
follow my experiments on BIM modelling for architecture design
User avatar
onekk
Veteran
Posts: 6222
Joined: Sat Jan 17, 2015 7:48 am
Contact:

Re: [Discussion] A different approach to Arch modelling (Arch Assembly?)

Post by onekk »

carlopav wrote: Sun Jun 07, 2020 7:46 pm I think that this is cool!
Thanks

carlopav wrote: Sun Jun 07, 2020 7:46 pm We could think about different presets... for example we could define how many panes the window should have (1-2-3-10?)
Not to reinvent the wheel, if i reuse my code and add a number of panes in :

Code: Select all

def finestra(opening_th=300, opening_height=1400, opening_width=1200,
             frame_width=50, frame_th=50, glass_th=21, n_pan=1):
let me see if i caould put in place something.

For the sill, it have to be under the "opening" so the sill is flush with the opening partially overlap it and protrude slighlty outwards.

I could do some coding.

But lateral and top sills like is usually seen in some region of Italy (and also in Europe), or maybe we are doing too much.
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/
carlopav
Veteran
Posts: 2062
Joined: Mon Dec 31, 2018 1:49 pm
Location: Venice, Italy

Re: [Discussion] A different approach to Arch modelling (Arch Assembly?)

Post by carlopav »

Great!
For the sill, it should not overlap with the opening but with the wall, so if we have 100x150cm opening, after adding the sill the measures are still ok.
I was thinking about adding a Bool flag "subtract also positive shapes from the wall", so we can easily manage to have sills and windows overlapping with the wall, or bigger than the opening itself, as it often is in the reality.

Having the whole stone frame of the opening should be considered as well as the standard bottom sill, they can be 2 different options perhaps!

What do you think?
follow my experiments on BIM modelling for architecture design
Post Reply