I seek to improve the Roof tool. I have make a script to generate a roof with parameters for each side of roof.
I would like to divide with you what I imagine for the interface and unfolding so that one can discuss it.
The principle:
On the basis of closed surface, one assigns with each edges a profile of roof.
This profile will be composed of several parameters:
- the slope: angle in degrees of the roof with the horizontal one, to put 0° for calculations automatic, 90° to make a pinion
- measurement in plan: width horizontal enters the bottom of slope and the top of slope, to put 0 for calculations automatic the
- overflow of roof: exit of roof compared to the edge
- the height: the height of the ridge sheathing
- the thickness: the thickness of the cover
- associated profile: automatic basic profile for calculations.
That gives us wire that one will be able to extrude.
For each edge one draws the profile of roof and one extrudes it.
The side of roof is the intersection of two extrusions.
Hardest is of course to calculate the connections of roof. For example, for the moment script does not manage the connections with different height of ridge sheathing.
The GUI
I imagine a table which recence the edges of the object, automatically filled the parameters and the user adjust them. A table as the system of axis appears to me well.
If not like the parameters of profile of roof risks growing, one can create a specific interface to each edge.
What do you think, what are your expectations of the tools?
Limitations:
The wire one must be carried out while following the anti-clockwise direction and the first line of the wire is the first profil.
Script
This script is a proof of concept. To use it a base should be drawn. Then populated the following lists manually:
line 33 : angles is the slope of the roof
Code: Select all
angles = [30.,45.,30.,30.,0.,0.,30.,30.,90.,]
Code: Select all
largeurs = [20.,0.,20.,20.,14.14,14.14,20.,0.,10.,]
Code: Select all
relation = [0,1,0,0,4,4,0,1,0,] # profil a partir de 1
Code: Select all
epaisseurToit = 1.
Code: Select all
#***************************************************************************
#* *
#* Copyright (c) 2014 *
#* Jonathan Wiedemann <wood.galaxy@gmail.com> *
#* *
#* 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. *
#* *
#* This program 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 this program; if not, write to the Free Software *
#* Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 *
#* USA *
#* *
#***************************************************************************
import FreeCAD
import FreeCADGui
import DraftGeomUtils
import DraftVecUtils
import Part
import math
import Draft
angles = [30.,45.,30.,30.,0.,0.,30.,30.,90.,]
largeurs = [20.,0.,20.,20.,14.14,14.14,20.,0.,10.,]
debords = [4.,4.,4.,4.,4.,4.,4.,4.,4.,]
relation = [0,1,0,0,4,4,0,1,0,] # profil a partir de 1
hauteurs = []
epaisseurToit = 1.
profilsDico = []
def createProfilDico(id, name, pente, largeur, debord, hauteur):
profilDico = {}
profilDico["id"]=id
profilDico["name"]=name
profilDico["pente"]=pente
profilDico["largeur"]=largeur
profilDico["debord"]=debord
profilDico["hauteur"]=hauteur
profilDico["wires"] = []
profilDico["refEdge"] = []
profilsDico.append(profilDico)
# Hauteur relatives
def getHeightRelative(id):
print("getheightRelative id : "+str(id))
#print("id " + str( id))
idRel = relation[id]
idRel = idRel - 1
print("idRel " +str(idRel))
print(largeurs[idRel])
print(angles[idRel])
print(math.radians(angles[idRel]))
print(math.tan(math.radians(angles[idRel])))
htRel = largeurs[idRel]*(math.tan(math.radians(angles[idRel])))
print("quit getHeightRelative")
return htRel
def getRightPaneHeight(id):
if relation[id-1] == 0:
rhtRel = largeurs[id-1]
else :
rhtRel=getHeightRelative((id-1))
print("rhtRel " + str(rhtRel))
return rhtRel
def getLeftPaneHeight(id):
if relation[id+1] == 0:
lhtRel = largeurs[id+1]
print("lht")
else:
lhtRel=getHeightRelative(id+1)
print("lht")
print("lhtRel " + str(lhtRel))
return lhtRel
# Largeur relative
def getWidthRelative(id):
htRel = getHeightRelative(id)
#idRel = relation[id]
#idRel = idRel - 1
lgRel = htRel/(math.tan(math.radians(angles[id])))
return lgRel
def getRightPaneWidth(id):
rlgRel=getWidthRelative(id-1)
return rlgRel
def getLeftPaneWidth(id):
llgRel=getWidthRelative(id+1)
return llgRel
#valeur
def getAngle(id):
print("getAngle"+str(id))
#print(getHeightRelative(id))
#print(largeurs[id])
a = math.degrees(math.atan(getHeightRelative(id)/largeurs[id]))
print("quit getAngle")
return a
def getHeight(id):
ht = largeurs[id]*(math.tan(math.radians(angles[id])))
return ht
l = len(angles)
#traitement liste :
for i in range(l):
if angles[i] == 90.:
largeurs[i] = 0.
largeurs.append(largeurs[0])
relation.append(relation[0])
angles.append(angles[0])
debords.append(debords[0])
print("Pour chaque i dans range(l) l = 9")
for i in range(l):
print("Indent "+str(i))
a = angles[i]
lg = largeurs[i]
db = debords[i]
if a == 0.0 or lg ==0.0 :
print("Si a = 0 ou lg = 0")
if relation[i] == 0 and a !=90.:
"Si relation[i] = 0 et est different de 90 alors"
print("Impossible de calculer sans profil relatif")
else :
if lg == 0:
print("Si lg auto = 0")
if a == 90. :
print("Si a = 90 alors Pignon")
name = "Pignon"+str(i)
print(getRightPaneHeight(i))
print(getLeftPaneHeight(i))
#htRel = max(getRightPaneHeight(i),getLeftPaneHeight(i))
htRel = getHeightRelative(i)
createProfilDico(i,name, 90.,0.,db,htRel)
else :
print("Si a != 90 alors calculs de la largeur par rapport a la hauteur des aux autre pans")
#htRel = max(getRightPaneHeight(i),getLeftPaneHeight(i))
htRel = getHeightRelative(i)
print("htRel " + str(htRel))
lgRel = getWidthRelative(i)
name = "Pan"+str(i)
createProfilDico(i,name,a,lgRel,db,htRel)
elif a == 0. :
print("Si a = 0 alors Angle automatique par rapport a hauteur autres pans")
a = getAngle(i)
print(a)
#htRel = max(getRightPaneHeight(i),getLeftPaneHeight(i))
htRel = getHeightRelative(i)
name = "Pan"+str(i)
createProfilDico(i,name,a,lg,db,htRel)
else :
print("Tout est normal")
ht = getHeight(i)
name = "Pan"+str(i)
createProfilDico(i,name,a,lg,db,ht)
def getPerpendicular(vec, angleEdge,l):
#print(angleEdge)
#print(l)
norm = FreeCAD.Vector(0,0,1)
perpendicular = vec.cross(norm)
#perpendicular.scale(l,l,l)
#print("perpendicularI"+ str(perpendicular))
if -180. <= angleEdge < -90.:
perpendicular[0] = abs(perpendicular[0])*-1
perpendicular[1] = abs(perpendicular[1])*-1
elif -90. <= angleEdge <= 0.:
perpendicular[0] = abs(perpendicular[0])*-1
perpendicular[1] = abs(perpendicular[1])
elif 0. < angleEdge <= 90.:
perpendicular[0] = abs(perpendicular[0])
perpendicular[1] = abs(perpendicular[1])
elif 90. < angleEdge <= 180.:
perpendicular[0] = abs(perpendicular[0])
perpendicular[1] = abs(perpendicular[1])*-1
else:
print("Angle inconnue")
perpendicular[2] = abs(perpendicular[2])
#print("perpendicularII"+ str(perpendicular))
# ramener a longueur 1
perpendicular.normalize()
#print("perpendicularIII"+ str(perpendicular))
#perpendicular.scale(l,l,l)
perpendicular = perpendicular.multiply(l)
#print("perpendicular"+ str(perpendicular))
return perpendicular
sel = FreeCADGui.Selection.getSelectionEx()[0].Object
#points=[FreeCAD.Vector(0.0,0.0,0.0),FreeCAD.Vector(100.0,0.0,0.0),FreeCAD.Vector(100.0,40.0,0.0),FreeCAD.Vector(70.0,40.0,0.0),FreeCAD.Vector(70.0,60.0,0.0),FreeCAD.Vector(50.0,80.0,0.0),FreeCAD.Vector(30.0,60.0,0.0),FreeCAD.Vector(30.0,40.0,0.0),FreeCAD.Vector(0,40.0,0.0)]
#base = Draft.makeWire(points,closed=True,face=True,support=None)
base = sel.Shape.Wires[0]
edges = DraftGeomUtils.sortEdges(base.Edges)
l = len(edges)
print("l = " + str(l))
edgesForward = edges[:]
edgesForward.append(edges[0])
#print(edgesForward[0])
#edgesForward.append(edges[0])
edgesBack = edges[:]
edgesBack.insert(0,edges[-1])
print edges
print edgesForward
print edgesBack
#edges.append(edges[0])
#edges.insert(0,edges[-1])
#edges.append(edges[0])
wireList = []
for i in range(l):
print(i)
#points = [FreeCAD.Vector(0.0,0.0,0.0),]
points=[]
#if i == 0:
profil0 = profilsDico[i-1]
#else :
# profil0 = profilsDico[i-1]
profil1 = profilsDico[i]
if i == l-1:
profil2 = profilsDico[0]
else:
profil2 = profilsDico[i+1]
vec0 = edges[i-1].Vertexes[-1].Point.sub(edges[i-1].Vertexes[0].Point)
vec1 = edges[i].Vertexes[-1].Point.sub(edges[i].Vertexes[0].Point)
#if i == l-1:
vec2 = edgesForward[i+1].Vertexes[-1].Point.sub(edgesForward[i+1].Vertexes[0].Point)
#angleEdge0 = math.degrees(DraftVecUtils.angle(vec1,vec0))
angleEdge0 = math.degrees(DraftVecUtils.angle(vec0))
angleEdge1 = math.degrees(DraftVecUtils.angle(vec1))
#angleEdge2 = math.degrees(DraftVecUtils.angle(vec1,vec2))
angleEdge2 = math.degrees(DraftVecUtils.angle(vec2))
long = vec1.Length
#points.append(FreeCAD.Vector(long,0.0,0.0))
points=[edges[i].Vertexes[0].Point,edges[i].Vertexes[-1].Point]
if profil1["pente"] != 90.:
lg = profil1["largeur"]
print(lg)
faitage = DraftGeomUtils.offset(edges[i],getPerpendicular(vec1,angleEdge1,lg))
midpoint = DraftGeomUtils.findMidpoint(edges[i])
print("perpendicular"+ str(getPerpendicular(vec1,angleEdge1,lg)))
print("angleEdge" + str(angleEdge1))
if profil2["pente"] == 90. :
edge = DraftGeomUtils.offset(edgesForward[i+1],FreeCAD.Vector(0,0,0))
point = DraftGeomUtils.findIntersection(faitage,edge,infinite1=True,infinite2=True,)
points.append(FreeCAD.Vector(point[0]))
print(points)
elif profil2["hauteur"] == profil1["hauteur"]:
edge = DraftGeomUtils.offset(edgesForward[i+1],getPerpendicular(vec2,angleEdge2,profil2["largeur"]))
point = DraftGeomUtils.findIntersection(faitage,edge,infinite1=True,infinite2=True,)
points.append(FreeCAD.Vector(point[0]))
elif profil1["hauteur"] > profil2["hauteur"]:
edge = DraftGeomUtils.offset(edgesForward[i+1],getPerpendicular(vec2,angleEdge2,profil2["largeur"]))
point = DraftGeomUtils.findIntersection(faitage,edge,infinite1=True,infinite2=True,)
points.append(FreeCAD.Vector(point[0]))
#edge2 = DraftGeomUtils.offset(edges[i+1],getPerpendicular(vec2,angleEdge2,profil2["largeur"]))
#dec = profil2["hauteur"]/math.tan(math.degrees(profil1["pente"]))
#dec = dec*-1
#edge1 = DraftGeomUtils.offset(edges[i],getPerpendicular(vec1,angleEdge1,dec))
#point = DraftGeomUtils.findIntersection(edge1,edge2,infinite1=True,infinite2=True,)
#points.append(FreeCAD.Vector(point[0]))
#print("Point pointe",point)
#point = DraftGeomUtils.findPerpendicular(point,[faitage])
#print("Projection sur faitage",point)
#points.append(FreeCAD.Vector(point[0]))
elif profil1["hauteur"] < profil2["hauteur"]:
edge = DraftGeomUtils.offset(edges[i+1],getPerpendicular(vec2,angleEdge2,profil2["largeur"]))
point = DraftGeomUtils.findIntersection(faitage,edge,infinite1=True,infinite2=True,)
points.append(FreeCAD.Vector(point[0]))
"""
edge2 = DraftGeomUtils.offset(edges[i+1],getPerpendicular(vec2,angleEdge2,profil2["largeur"]))
print("Edge2",edge2.Vertexes[-1].Point,edge2.Vertexes[0].Point)
dec = profil2["hauteur"]/math.tan(math.degrees(profil1["pente"]))
print("dec",dec)
edge1 = DraftGeomUtils.offset(edges[i],getPerpendicular(vec1,angleEdge1,dec))
print("Edge1",edge1.Vertexes[-1].Point,edge1.Vertexes[0].Point)
print("Faitage",edge.Vertexes[-1].Point,edge.Vertexes[0].Point)
#point = DraftGeomUtils.findIntersection(faitage,edge,infinite1=True,infinite2=True,)
point = DraftGeomUtils.findIntersection(edge1,edge2,infinite1=True,infinite2=True,)
points.append(FreeCAD.Vector(point[0]))
#point = DraftGeomUtils.findIntersection(faitage,edge,infinite1=True,infinite2=True,)
point = DraftGeomUtils.findPerpendicular(point, [faitage,],)
print("Projection sur faitage",point)
points.append(FreeCAD.Vector(point[0]))
"""
#pass
else:
print("Cas de figure non pris en charge")
if profil0["pente"] == 90. :
edge = edgesBack[i]
point = DraftGeomUtils.findIntersection(faitage,edge,infinite1=True,infinite2=True,)
points.append(FreeCAD.Vector(point[0]))
elif profil0["hauteur"] == profil1["hauteur"]:
edge = DraftGeomUtils.offset(edgesBack[i],getPerpendicular(vec0,angleEdge0,profil0["largeur"]))
point = DraftGeomUtils.findIntersection(faitage,edge,infinite1=True,infinite2=True,)
points.append(FreeCAD.Vector(point[0]))
elif profil1["hauteur"] > profil0["hauteur"]:
edge = DraftGeomUtils.offset(edgesBack[i],getPerpendicular(vec0,angleEdge0,profil0["largeur"]))
point = DraftGeomUtils.findIntersection(faitage,edge,infinite1=True,infinite2=True,)
points.append(FreeCAD.Vector(point[0]))
elif profil1["hauteur"] < profil0["hauteur"]:
edge = DraftGeomUtils.offset(edgesBack[i],getPerpendicular(vec0,angleEdge0,profil0["largeur"]))
point = DraftGeomUtils.findIntersection(faitage,edge,infinite1=True,infinite2=True,)
points.append(FreeCAD.Vector(point[0]))
#edge2 = DraftGeomUtils.offset(edges[i-1],getPerpendicular(vec0,angleEdge0,profil0["largeur"]))
#dec = profil2["hauteur"]/math.tan(math.degrees(profil1["pente"]))
#dec = dec*-1
#edge1 = DraftGeomUtils.offset(edges[i],getPerpendicular(vec1,angleEdge1,dec))
#point = DraftGeomUtils.findIntersection(edge1,edge2,infinite1=True,infinite2=True,)
#points.append(FreeCAD.Vector(point[0]))
else:
print("Cas de figure non pris en charge")
else:
#Points en Z directement pour construire pignon
#if profil0["hauteur"] == profil1["hauteur"] == profil2["hauteur"]:
pass
points = DraftVecUtils.removeDoubles(points)
print("points a ferme",points)
profilsDico[i]["wires"] = points
wireList.append(points)
#print("len profilsDico : "+str(len(profilsDico)))
#for d in profilsDico:
# print(d)
#n = 0
#for p in profilsDico:
# print(n)
#print(p["pente"])
#print("WireListe"+str(n),p)
#pDi = profilsDico[i]
f = Draft.makeWire(profil1["wires"],closed=True,face=True,support=None)
f.Label = profil1["name"]
d = f.Shape.Wires[0].BoundBox.DiagonalLength
if profil1["pente"] != 90.:
epaisseurVToit = epaisseurToit/(math.cos(math.radians(profil1["pente"])))
f = Part.Face(f.Shape.Wires[0])
f = f.extrude(FreeCAD.Vector(0,0,profil1["hauteur"]+2*epaisseurVToit))
#Part.show(f)
points=[FreeCAD.Vector(0.0,-epaisseurVToit,0.0),FreeCAD.Vector(profil1["largeur"],profil1["hauteur"]-epaisseurVToit,0.0),FreeCAD.Vector(profil1["largeur"],profil1["hauteur"],0.0),FreeCAD.Vector(0.0,0.0,0.0)]
profilCouv = Draft.makeWire(points,closed=True,face=True,support=None,)
profilCouv.Label = "Profil-"+str(profil1["name"])
Draft.move(profilCouv,midpoint,copy=False)
Draft.rotate(profilCouv,90+angleEdge1*-1,midpoint,axis=FreeCAD.Vector(0.0,0.0,1.0),copy=False)
perp = getPerpendicular(vec1,angleEdge1,profil1["largeur"])
Draft.rotate(profilCouv,90*1,midpoint,axis=perp,copy=False)
vecT = vec1.normalize()
#vecT.negative()
vecT.multiply(d)
Draft.move(profilCouv,vecT,copy=False)
vecE = vecT.multiply(-2)
profilVol = profilCouv.Shape.extrude(vecE)
#Part.show(profilVol)
panVol = f.common(profilVol)
Part.show(panVol)
# n=n+1