-
Notifications
You must be signed in to change notification settings - Fork 0
/
Copy pathrecreate_openings.FCMacro
172 lines (136 loc) · 5.57 KB
/
recreate_openings.FCMacro
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
# Window/Door recreation macro / python script by mrEvL
# Take a look at accompanied window_demo.FCst file and read the README.MD
# See https://github.com/mrevl/freecad_opening_recreator
import math
import Draft
import Arch
debug = True
def main():
# docname = 'window_demo'
# doc = FreeCAD.getDocument(docname)
doc = FreeCAD.activeDocument()
# the spreadsheet containing the parameterization of the windows
sc = doc.getObjectsByLabel("sc_openings")
if not sc:
print("No spreadsheet called sc_openings found - bailing")
return
sc = sc[0]
# the group containing all the sketch objects referring to points
op = doc.getObjectsByLabel('openings')
if not op:
print("No group called openings found - bailing")
return
op = op[0]
# the group with generated items
gop = doc.getObjectsByLabel('generated_openings')
if not gop:
print("No group called generated_openings found - bailing")
return
gop = gop[0]
for sketch in op.OutList:
win = None
win = rebuild_opening(doc, sketch, sc)
for x in win:
gop.addObject(x)
#FIXME NativeIFC still complains, probably not triggering something in their codepath
doc.commitTransaction()
doc.recompute()
def remove_generated(doc, label):
# remove the previous window + underlying sketch and regenerate
if (doc.getObjectsByLabel('gen_' + label)):
prev = doc.getObjectsByLabel('gen_' + label)[0]
print(f'Removing:{prev.Label}')
if (prev.Base):
doc.removeObject(prev.Base.Name)
doc.removeObject(prev.Name)
def rebuild_opening(doc, sketch, sc):
# try to find the host object via the external reference
fh = None
for h in sketch.ExternalGeometry[0][0].InList:
if h.TypeId == 'Part::FeaturePython':
print(f'Assuming {h.Name} / {h.Label} for host object')
fh = h
break
gen_win = []
found_named_constraint = False
for c in sketch.Constraints:
onr = 0
ho = None
#print(sketch.Name)
if c.Name.rstrip() != '' and sc.getCellFromAlias(c.Name):
onr = sc.getCellFromAlias(c.Name)[1:]
if onr == 0:
continue
found_named_constraint = True
try:
ho = doc.getObjectsByLabel(sc.get('B' + onr))[0]
except ValueError as ve:
print(f'No explicit host object defined at B{str(onr)}')
if (ho):
print(f'Using host {ho.Label}')
elif (fh):
print(f'Using host {fh.Label}')
ho = fh
else:
print(f'No host object found - skipping {sketch.Name}')
return
# remove any previously generated windows with this constraint name
remove_generated(doc, c.Name)
g = sketch.Geometry[c.First]
z = sc.get('C' + onr)
# g.EndPoint, g.StartPoint
x1,y1,z1 = g.StartPoint
x2,y2,z2 = g.EndPoint
# Determine the angle of the opening by using the start and endpoint of the
# constraint. Think: I want to place an opening in this 'direction'.
# This part needs some re-thinking. Part of this might already be in the
# window code in FreeCAD, somewhere. I doubt this is a new problem.
dx = x2 -x1
dy = y2 -y1
if dx:
angle = math.degrees(math.atan(dy/dx))
else:
angle = 270
angle = round(angle,0)
if angle < 0:
angle = 360 + angle
angle = abs(angle) # for -0.0, rounding error
if debug:
print(f'DEBUG: x1:{x1} x2:{x2} y1:{y1} y2:{y2} - Angle is at {str(angle)}')
# FIXME - determine what the automatic values actually become
# Rotate the opening over the normal (extrusion direction) of the wall.
# Walls have the tendency to have a (0,0,0) / automatic unless specified.
# If 0,0,0 it probably reflects back to the base object, which could have been drawn on the
# y/z plane for all we know and henceforth extrudes to the x plane. For now, assume we're doing
# x/y plane and rewrite (0,0,0) to (0,0,1), the z plane / "up"
n_ax = ho.Normal
if (n_ax == (0,0,0)):
n_ax = (0,0,1)
# this is the actual window call
preset = sc.get('D' + onr)
width = sc.get('E' + onr)
height = sc.get('F' + onr)
h1 = sc.get('G' + onr)
h2 = sc.get('H' + onr)
h3 = sc.get('I' + onr)
w1 = sc.get('J' + onr)
w2 = sc.get('K' + onr)
o1 = sc.get('L' + onr)
o2 = sc.get('M' + onr)
rot = FreeCAD.Rotation(0, 0, 90) # 90 degrees so it's up, as our sketch (and template!) is "flat" (x/y)
pl = FreeCAD.Placement(g.EndPoint, rot)
#raise the base by z as defined in the spreadsheet for this opening
pl.Base = pl.Base + FreeCAD.Vector([0,0,z])
win = Arch.makeWindowPreset(preset, width=width, height=height, h1=h1, h2=h2, h3=h3, w1=w1, w2=w1, o1=o1, o2=o2, placement=pl)
# FIXME - preferably there is no post rotation, but it's part of the initial placement.
# Rotate the opening over the z axis rotation to align with the wall
z_ax = FreeCAD.Vector(0,0,1) #(x,y,z)
Draft.rotate([win], angle, center=pl.Base, axis=z_ax, copy=False)
win.Hosts = [ho]
win.Label = 'gen_' + c.Name
print(f'Created: {win.Label}')
gen_win.append(win)
if (not found_named_constraint):
print(f'No named constraints found to work on in {sketch.Name}')
return gen_win
main()