1 # ***** BEGIN GPL LICENSE BLOCK *****
3 # This program is free software; you may redistribute it, and/or
4 # modify it, under the terms of the GNU General Public License
5 # as published by the Free Software Foundation - either version 2
6 # of the License, or (at your option) any later version.
8 # This program is distributed in the hope that it will be useful,
9 # but WITHOUT ANY WARRANTY; without even the implied warranty of
10 # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
11 # GNU General Public License for more details.
13 # You should have received a copy of the GNU General Public License
14 # along with this program. If not, write to:
16 # the Free Software Foundation Inc.
17 # 51 Franklin Street, Fifth Floor
18 # Boston, MA 02110-1301, USA
20 # or go online at: http://www.gnu.org/licenses/ to view license options.
22 # ***** END GPL LICENCE BLOCK *****
25 # This module contains the UI definition, display, and processing (create mesh)
28 # The routines to generate the vertices for the wall are found in the "Blocks" module.
34 from bpy.props import *
35 from add_mesh_walls.Blocks import *
36 #from add_mesh_walls.preset_utils import *
40 class add_mesh_wallb(bpy.types.Operator):
42 bl_idname = "mesh.wall_add"
43 bl_label = "Add A Masonry Wall"
44 bl_options = {'REGISTER', 'UNDO'} # removes object, does not reset to "last" modification.
45 bl_description = "adds a block wall"
47 # UI items - API for properties - User accessable variables...
48 # not all options are via UI, and some operations just don't work yet.
50 # only create object when True
51 # False allows modifying several parameters without creating object
52 ConstructTog = BoolProperty(name="Construct",
53 description="Generate the object",
56 # need to modify so radial makes a tower (normal); want "flat" setting to make disk (alternate)
57 # make the wall circular - if not sloped it's a flat disc
58 RadialTog = BoolProperty(name="Radial",
59 description="Make masonry radial",
62 # curve the wall - if radial creates dome.
63 SlopeTog = BoolProperty(name="Curved",
64 description="Make masonry sloped, or curved",
67 #need to review defaults and limits for all of these UI objects.
70 WallStart = FloatProperty(name="Start",
71 description="Left side, or start angle",
72 default=-10.0, min=-100, max=100.0)
73 WallEnd = FloatProperty(name="End",
74 description="Right side, or end angle",
75 default=10.0, min=0.0, max=100.0)
76 WallBottom = FloatProperty(name="Bottom",
77 description="Lower height or radius",
78 default=0.0, min=-100, max=100)
79 WallTop = FloatProperty(name="Top",
80 description="Upper height or radius",
81 default=15.0, min=0.0, max=100.0)
82 EdgeOffset = FloatProperty(name="Edging",
83 description="Block staggering on wall sides",
84 default=0.6, min=0.0, max=100.0)
87 Width = FloatProperty(name="Width",
88 description="Average width of each block",
89 default=1.5, min=0.01, max=100.0)
90 WidthVariance = FloatProperty(name="Variance",
91 description="Random variance of block width",
92 default=0.5, min=0.0, max=100.0)
93 WidthMinimum = FloatProperty(name="Minimum",
94 description="Absolute minimum block width",
95 default=0.5, min=0.01, max=100.0)
96 Height = FloatProperty(name="Height",
97 description="Average Height of each block",
98 default=0.7, min=0.01, max=100.0)
99 HeightVariance = FloatProperty(name="Variance",
100 description="Random variance of block Height",
101 default=0.3, min=0.0, max=100.0)
102 HeightMinimum = FloatProperty(name="Minimum",
103 description="Absolute minimum block Height",
104 default=0.25, min=0.01, max=100.0)
105 Depth = FloatProperty(name="Depth",
106 description="Average Depth of each block",
107 default=2.0, min=0.01, max=100.0)
108 DepthVariance = FloatProperty(name="Variance",
109 description="Random variance of block Depth",
110 default=0.1, min=0.0, max=100.0)
111 DepthMinimum = FloatProperty(name="Minimum",
112 description="Absolute minimum block Depth",
113 default=1.0, min=0.01, max=100.0)
114 MergeBlock = BoolProperty(name="Merge Blocks",
115 description="Make big blocks (merge closely adjoining blocks)",
119 Grout = FloatProperty(name="Thickness",
120 description="Distance between blocks",
121 default=0.1, min=-10.0, max=10.0)
122 GroutVariance = FloatProperty(name="Variance",
123 description="Random variance of block Grout",
124 default=0.03, min=0.0, max=100.0)
125 GroutDepth = FloatProperty(name="Depth",
126 description="Grout Depth from the face of the blocks",
127 default=0.1, min=0.0001, max=10.0)
128 GroutDepthVariance = FloatProperty(name="Variance",
129 description="Random variance of block Grout Depth",
130 default=0.03, min=0.0, max=100.0)
131 GroutEdge = BoolProperty(name="Edging",
132 description="Grout perimiter",
135 #properties for openings
136 Opening1Tog = BoolProperty(name="Opening(s)",description="Make windows or doors", default = True)
137 Opening1Width = FloatProperty(name="Width",
138 description="The Width of opening 1",
139 default=2.5, min=0.01, max=100.0)
140 Opening1Height = FloatProperty(name="Height",
141 description="The Height of opening 1",
142 default=3.5, min=0.01, max=100.0)
143 Opening1X = FloatProperty(name="Indent",
144 description="The x position or spacing of opening 1",
145 default=5.0, min=-100, max=100.0)
146 Opening1Z = FloatProperty(name="Bottom",
147 description="The z position of opening 1",
148 default=5.0, min=-100, max=100.0)
149 Opening1Repeat = BoolProperty(name="Repeat",
150 description="make multiple openings, with spacing X1",
152 Opening1TopArchTog = BoolProperty(name="Top Arch",
153 description="Add an arch to the top of opening 1",
155 Opening1TopArch = FloatProperty(name="Curve",
156 description="Height of the arch on the top of the opening",
157 default=2.5, min=0.001, max=100.0)
158 Opening1TopArchThickness = FloatProperty(name="Thickness",
159 description="Thickness of the arch on the top of the opening",
160 default=0.75, min=0.001, max=100.0)
161 Opening1BtmArchTog = BoolProperty(name="Bottom Arch",
162 description="Add an arch to the bottom of opening 1",
164 Opening1BtmArch = FloatProperty(name="Curve",
165 description="Height of the arch on the bottom of the opening",
166 default=1.0, min=0.01, max=100.0)
167 Opening1BtmArchThickness = FloatProperty(name="Thickness",
168 description="Thickness of the arch on the bottom of the opening",
169 default=0.5, min=0.01, max=100.0)
170 Opening1Bevel = FloatProperty(name="Bevel",
171 description="Angle block face",
172 default=0.25, min=-10.0, max=10.0)
175 # openings on top of wall.
176 CrenelTog = BoolProperty(name="Crenels",
177 description="Make openings along top of wall",
179 CrenelXP = FloatProperty(name="Width %",
180 description="Gap width in wall based % of wall width",
181 default=0.25, min=0.10, max=1.0)
182 CrenelZP = FloatProperty(name="Height %",
183 description="Crenel Height as % of wall height",
184 default=0.10, min=0.10, max=1.0)
187 # narrow openings in wall.
188 #need to prevent overlap with arch openings - though inversion is an interesting effect.
189 SlotTog = BoolProperty(name="Slots",
190 description="Make narrow openings in wall",
192 SlotRpt = BoolProperty(name="Repeat",
193 description="Repeat slots along wall",
195 SlotWdg = BoolProperty(name="Wedged (n/a)",
196 description="Bevel edges of slots",
198 SlotX = FloatProperty(name="Indent",
199 description="The x position or spacing of slots",
200 default=0.0, min=-100, max=100.0)
201 SlotGap = FloatProperty(name="Opening",
202 description="The opening size of slots",
203 default=0.5, min=0.10, max=100.0)
204 SlotV = BoolProperty(name="Vertical",
205 description="Vertical slots",
207 SlotVH = FloatProperty(name="Height",
208 description="Height of vertical slot",
209 default=3.5, min=0.10, max=100.0)
210 SlotVBtm = FloatProperty(name="Bottom",
211 description="Z position for slot",
212 default=5.00, min=-100.0, max=100.0)
213 SlotH = BoolProperty(name="Horizontal",
214 description="Horizontal slots",
216 SlotHW = FloatProperty(name="Width",
217 description="Width of horizontal slot",
218 default=2.5, min=0.10, max=100.0)
219 #this should offset from VBtm... maybe make a % like crenels?
220 SlotHBtm = FloatProperty(name="Bottom",
221 description="Z position for horizontal slot",
222 default=5.50, min=-100.0, max=100.0)
225 #properties for shelf (extend blocks in area)
226 ShelfTog = BoolProperty(name="Shelf",description="Add blocks in area by depth to make shelf/platform", default = False)
227 ShelfX = FloatProperty(name="Left",
228 description="The x position of Shelf",
229 default=-5.00, min=-100, max=100.0)
230 ShelfZ = FloatProperty(name="Bottom",
231 description="The z position of Shelf",
232 default=10.0, min=-100, max=100.0)
233 ShelfH = FloatProperty(name="Height",
234 description="The Height of Shelf area",
235 default=1.0, min=0.01, max=100.0)
236 ShelfW = FloatProperty(name="Width",
237 description="The Width of shelf area",
238 default=5.0, min=0.01, max=100.0)
239 ShelfD = FloatProperty(name="Depth",
240 description="Depth of each block for shelf (from cursor + 1/2 wall depth)",
241 default=2.0, min=0.01, max=100.0)
242 ShelfBack = BoolProperty(name="Backside",description="Shelf on backside of wall", default = False)
245 #properties for steps (extend blocks in area, progressive width)
246 StepTog = BoolProperty(name="Steps",description="Add blocks in area by depth with progressive width to make steps", default = False)
247 StepX = FloatProperty(name="Left",
248 description="The x position of steps",
249 default=-9.00, min=-100, max=100.0)
250 StepZ = FloatProperty(name="Bottom",
251 description="The z position of steps",
252 default=0.0, min=-100, max=100.0)
253 StepH = FloatProperty(name="Height",
254 description="The Height of step area",
255 default=10.0, min=0.01, max=100.0)
256 StepW = FloatProperty(name="Width",
257 description="The Width of step area",
258 default=8.0, min=0.01, max=100.0)
259 StepD = FloatProperty(name="Depth",
260 description="Depth of each block for steps (from cursor + 1/2 wall depth)",
261 default=1.0, min=0.01, max=100.0)
262 StepV = FloatProperty(name="Riser",
263 description="Height of each step",
264 default=0.70, min=0.01, max=100.0)
265 StepT = FloatProperty(name="Tread",
266 description="Width of each step",
267 default=1.0, min=0.01, max=100.0)
268 StepLeft = BoolProperty(name="High Left",description="Height left; else Height right", default = False)
269 StepOnly = BoolProperty(name="No Blocks",description="Steps only, no supporting blocks", default = False)
270 StepBack = BoolProperty(name="Backside",description="Steps on backside of wall", default = False)
275 # Show the UI - expose the properties.
279 # Display the toolbox options
281 def draw(self, context):
286 box.prop(self, 'ConstructTog')
288 # Wall area (size/position)
290 box.label(text='Wall Size (area)')
291 box.prop(self, 'WallStart')
292 box.prop(self, 'WallEnd')
293 box.prop(self, 'WallBottom')
294 box.prop(self, 'WallTop')
295 box.prop(self, 'EdgeOffset')
299 box.label(text='Block Sizing')
300 box.prop(self, 'MergeBlock')
301 #add checkbox for "fixed" sizing (ignore variance) a.k.a. bricks.
302 box.prop(self, 'Width')
303 box.prop(self, 'WidthVariance')
304 box.prop(self, 'WidthMinimum')
305 box.prop(self, 'Height')
306 box.prop(self, 'HeightVariance')
307 box.prop(self, 'HeightMinimum')
308 box.prop(self, 'Depth')
309 box.prop(self, 'DepthVariance')
310 box.prop(self, 'DepthMinimum')
315 box.label(text='Grout')
316 box.prop(self, 'Grout')
317 box.prop(self, 'GroutVariance')
318 box.prop(self, 'GroutDepth')
319 box.prop(self, 'GroutDepthVariance')
320 # box.prop(self, 'GroutEdge')
322 # Wall shape modifiers
324 box.label(text='Wall Shape')
325 box.prop(self, 'RadialTog')
326 box.prop(self, 'SlopeTog')
328 # Openings (doors, windows; arched)
330 box.prop(self, 'Opening1Tog')
331 if self.properties.Opening1Tog:
332 box.prop(self, 'Opening1Width')
333 box.prop(self, 'Opening1Height')
334 box.prop(self, 'Opening1X')
335 box.prop(self, 'Opening1Z')
336 box.prop(self, 'Opening1Bevel')
337 box.prop(self, 'Opening1Repeat')
338 box.prop(self, 'Opening1TopArchTog')
339 box.prop(self, 'Opening1TopArch')
340 box.prop(self, 'Opening1TopArchThickness')
341 box.prop(self, 'Opening1BtmArchTog')
342 box.prop(self, 'Opening1BtmArch')
343 box.prop(self, 'Opening1BtmArchThickness')
345 # Slots (narrow openings)
347 box.prop(self, 'SlotTog')
348 if self.properties.SlotTog:
349 # box.prop(self, 'SlotWdg')
350 box.prop(self, 'SlotX')
351 box.prop(self, 'SlotGap')
352 box.prop(self, 'SlotRpt')
353 box.prop(self, 'SlotV')
354 box.prop(self, 'SlotVH')
355 box.prop(self, 'SlotVBtm')
356 box.prop(self, 'SlotH')
357 box.prop(self, 'SlotHW')
358 box.prop(self, 'SlotHBtm')
360 # Crenels, gaps in top of wall
362 box.prop(self, 'CrenelTog')
363 if self.properties.CrenelTog:
364 box.prop(self, 'CrenelXP')
365 box.prop(self, 'CrenelZP')
367 # Shelfing (protrusions)
369 box.prop(self, 'ShelfTog')
370 if self.properties.ShelfTog:
371 box.prop(self, 'ShelfX')
372 box.prop(self, 'ShelfZ')
373 box.prop(self, 'ShelfH')
374 box.prop(self, 'ShelfW')
375 box.prop(self, 'ShelfD')
376 box.prop(self, 'ShelfBack')
380 box.prop(self, 'StepTog')
381 if self.properties.StepTog:
382 box.prop(self, 'StepX')
383 box.prop(self, 'StepZ')
384 box.prop(self, 'StepH')
385 box.prop(self, 'StepW')
386 box.prop(self, 'StepD')
387 box.prop(self, 'StepV')
388 box.prop(self, 'StepT')
389 box.prop(self, 'StepLeft')
390 box.prop(self, 'StepOnly')
391 box.prop(self, 'StepBack')
395 # Respond to UI - get the properties set by user.
398 # Check and process UI settings to generate masonry
400 def execute(self, context):
413 # Create the wall when enabled (skip regen iterations when off)
414 if not self.properties.ConstructTog: return {'FINISHED'}
416 #enter the settings for the wall dimensions (area)
417 # start can't be zero - min/max don't matter [if max less than end] but zero don't workie.
418 # start can't exceed end.
419 if not self.properties.WallStart or self.properties.WallStart >= self.properties.WallEnd:
420 self.properties.WallStart = NOTZERO # Reset UI if input out of bounds...
422 dims['s'] = self.properties.WallStart
423 dims['e'] = self.properties.WallEnd
424 dims['b'] = self.properties.WallBottom
425 dims['t'] = self.properties.WallTop
427 settings['eoff'] = self.properties.EdgeOffset
429 #retrieve the settings for the wall block properties
430 settings['w'] = self.properties.Width
431 settings['wv'] = self.properties.WidthVariance
432 settings['wm'] = self.properties.WidthMinimum
433 if not radialized: settings['sdv'] = settings['w']
434 else: settings['sdv'] = 0.12
436 settings['h'] = self.properties.Height
437 settings['hv'] = self.properties.HeightVariance
438 settings['hm'] = self.properties.HeightMinimum
440 settings['d'] = self.properties.Depth
441 settings['dv'] = self.properties.DepthVariance
442 settings['dm'] = self.properties.DepthMinimum
444 if self.properties.MergeBlock:
448 settings['g'] = self.properties.Grout
449 settings['gv'] = self.properties.GroutVariance
450 settings['gd'] = self.properties.GroutDepth
451 settings['gdv'] = self.properties.GroutDepthVariance
453 if self.properties.GroutEdge: settings['ge'] = 1
454 else: settings['ge'] = 0
456 # set wall shape modifiers
457 if self.properties.RadialTog:
459 #eliminate to allow user control for start/completion?
460 dims['s'] = 0.0 # complete radial
461 if dims['e'] > PI*2: dims['e'] = PI*2 # max end for circle
462 if dims['b'] < settings['g']: dims['b'] = settings['g'] # min bottom for grout extension
465 if self.properties.SlopeTog: slope = 1
472 # Add shelf if enabled
473 if self.properties.ShelfTog:
475 shelfSpecs['h'] = self.properties.ShelfH
476 shelfSpecs['w'] = self.properties.ShelfW
477 shelfSpecs['d'] = self.properties.ShelfD
478 shelfSpecs['x'] = self.properties.ShelfX
479 shelfSpecs['z'] = self.properties.ShelfZ
481 if self.properties.ShelfBack:
490 # Make steps if enabled
491 if self.properties.StepTog:
493 stepSpecs['x'] = self.properties.StepX
494 stepSpecs['z'] = self.properties.StepZ
495 stepSpecs['h'] = self.properties.StepH
496 stepSpecs['w'] = self.properties.StepW
497 stepSpecs['d'] = self.properties.StepD
498 stepSpecs['v'] = self.properties.StepV
499 stepSpecs['t'] = self.properties.StepT
501 if self.properties.StepLeft:
504 if self.properties.StepOnly:
507 if self.properties.StepBack:
511 #enter the settings for the openings
512 #when openings overlap they create inverse stonework - interesting but not the desired effect :)
513 #if opening width == indent*2 the edge blocks fail (row of blocks cross opening) - bug.
515 openingIdx = 0 # track opening array references for multiple uses
517 # general openings with arch options - can be windows or doors.
518 if self.properties.Opening1Tog:
520 openingSpecs += [{'w':0.5, 'h':0.5, 'x':0.8, 'z':2.7, 'rp':1, 'b':0.0, 'v':0, 'vl':0, 't':0, 'tl':0}]
522 openingSpecs[openingIdx]['w'] = self.properties.Opening1Width
523 openingSpecs[openingIdx]['h'] = self.properties.Opening1Height
524 openingSpecs[openingIdx]['x'] = self.properties.Opening1X
525 openingSpecs[openingIdx]['z'] = self.properties.Opening1Z
526 openingSpecs[openingIdx]['rp'] = self.properties.Opening1Repeat
528 if self.properties.Opening1TopArchTog:
529 openingSpecs[openingIdx]['v'] = self.properties.Opening1TopArch
530 openingSpecs[openingIdx]['t'] = self.properties.Opening1TopArchThickness
532 if self.properties.Opening1BtmArchTog:
533 openingSpecs[openingIdx]['vl'] = self.properties.Opening1BtmArch
534 openingSpecs[openingIdx]['tl'] = self.properties.Opening1BtmArchThickness
536 openingSpecs[openingIdx]['b'] = self.properties.Opening1Bevel
538 openingIdx += 1 # count window/door/arch openings
540 # Slots (narrow openings)
541 if self.properties.SlotTog:
543 if self.properties.SlotV: # vertical slots
545 openingSpecs += [{'w':0.5, 'h':0.5, 'x':0.0, 'z':2.7, 'rp':0, 'b':0.0, 'v':0, 'vl':0, 't':0, 'tl':0}]
547 openingSpecs[openingIdx]['w'] = self.properties.SlotGap
548 openingSpecs[openingIdx]['h'] = self.properties.SlotVH
549 openingSpecs[openingIdx]['x'] = self.properties.SlotX
550 openingSpecs[openingIdx]['z'] = self.properties.SlotVBtm
551 openingSpecs[openingIdx]['rp'] = self.properties.SlotRpt
553 # make them pointy...
554 openingSpecs[openingIdx]['v'] = self.properties.SlotGap
555 openingSpecs[openingIdx]['t'] = self.properties.SlotGap/2
556 openingSpecs[openingIdx]['vl'] = self.properties.SlotGap
557 openingSpecs[openingIdx]['tl'] = self.properties.SlotGap/2
559 openingIdx += 1 # count vertical slot openings
561 # need to handle overlap of H and V slots...
563 if self.properties.SlotH: # Horizontal slots
565 openingSpecs += [{'w':0.5, 'h':0.5, 'x':0.0, 'z':2.7, 'rp':0, 'b':0.0, 'v':0, 'vl':0, 't':0, 'tl':0}]
567 openingSpecs[openingIdx]['w'] = self.properties.SlotHW
568 openingSpecs[openingIdx]['h'] = self.properties.SlotGap
569 openingSpecs[openingIdx]['x'] = self.properties.SlotX
570 openingSpecs[openingIdx]['z'] = self.properties.SlotHBtm
571 #horizontal repeat isn't same spacing as vertical...
572 openingSpecs[openingIdx]['rp'] = self.properties.SlotRpt
574 # make them pointy...
575 # want arc to go sideways... maybe wedge will be sufficient and can skip horiz arcs.
576 # openingSpecs[openingIdx]['v'] = self.properties.SlotGap
577 # openingSpecs[openingIdx]['t'] = self.properties.SlotGap/2
578 # openingSpecs[openingIdx]['vl'] = self.properties.SlotGap
579 # openingSpecs[openingIdx]['tl'] = self.properties.SlotGap/2
581 openingIdx += 1 # count horizontal slot openings
584 # Crenellations (top row openings)
585 if self.properties.CrenelTog:
587 # add bottom arch option?
588 # perhaps a repeat toggle...
589 # if crenel opening overlaps with arch opening it fills with blocks...
592 openingSpecs += [{'w':0.5, 'h':0.5, 'x':0.0, 'z':2.7, 'rp':1, 'b':0.0, 'v':0, 'vl':0, 't':0, 'tl':0}]
594 wallW = self.properties.WallEnd - self.properties.WallStart
595 crenelW = wallW*self.properties.CrenelXP # Width % opening.
597 wallH = self.properties.WallTop - self.properties.WallBottom
598 crenelH = wallH*self.properties.CrenelZP # % proportional height.
600 openingSpecs[openingIdx]['w'] = crenelW
601 openingSpecs[openingIdx]['h'] = crenelH
603 # calculate the spacing between openings.
604 # this isn't the absolute start (left), it's opening center offset relative to cursor (space between openings)...
605 openingSpecs[openingIdx]['x'] = crenelW*2-1 # assume standard spacing
607 if not radialized: # normal wall?
608 # set indent 0 (center) if opening is 50% or more of wall width, no repeat.
609 if crenelW*2 >= wallW:
610 openingSpecs[openingIdx]['x'] = 0
611 openingSpecs[openingIdx]['rp'] = 0
613 openingSpecs[openingIdx]['z'] = self.properties.WallTop - (crenelH/2) # set bottom of opening (center of hole)
615 openingIdx += 1 # count crenel openings
618 # Process the user settings to generate a wall
620 # generate the list of vertices for the wall...
621 verts_array, faces_array = createWall(radialized, slope, openingSpecs, bigBlock,
622 shelfExt, shelfBack, stepMod, stepLeft, stepOnly, stepBack)
625 mesh = bpy.data.meshes.new("Wall")
627 # Make a mesh from a list of verts/edges/faces.
628 mesh.from_pydata(verts_array, [], faces_array)
630 scene = context.scene
632 # Deselect all objects.
633 bpy.ops.object.select_all(action='DESELECT')
637 ob_new = bpy.data.objects.new("Wall", mesh)
638 scene.objects.link(ob_new)
639 # leave this out to prevent 'Tab key" going into edit mode :):):)
640 # Use rmb click to select and still modify.
641 scene.objects.active = ob_new
644 ob_new.location = tuple(context.scene.cursor_location)
645 ob_new.rotation_quaternion = [1.0,0.0,0.0,0.0]