| 1 |
# <pep8 compliant> |
| 2 |
# This program is free software; you can redistribute it and/or |
| 3 |
# modify it under the terms of the GNU General Public License |
| 4 |
# as published by the Free Software Foundation; either version 2 |
| 5 |
# of the License, or (at your option) any later version. |
| 6 |
# |
| 7 |
# This program is distributed in the hope that it will be useful, |
| 8 |
# but WITHOUT ANY WARRANTY; without even the implied warranty of |
| 9 |
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the |
| 10 |
# GNU General Public License for more details. |
| 11 |
# |
| 12 |
# You should have received a copy of the GNU General Public License |
| 13 |
# along with this program; if not, write to the Free Software Foundation, |
| 14 |
# Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. |
| 15 |
|
| 16 |
import re |
| 17 |
import os |
| 18 |
from bpy.types import (Panel, Operator, PropertyGroup) |
| 19 |
from bpy.props import (BoolProperty, EnumProperty, PointerProperty) |
| 20 |
import bpy.utils.previews |
| 21 |
|
| 22 |
CMPlist = [ |
| 23 |
'Ceiling-Crown01', 'Ceiling-Crown02', 'Ceiling-Crown03', |
| 24 |
'Ceiling-Crown04', 'Ceiling-Crown05', 'Ceiling-Crown06', 'Ceiling-Crown07', |
| 25 |
'Ceiling-Crown08', 'Ceiling-Crown09', 'Ceiling-Crown10', 'Ceiling-Crown11', |
| 26 |
'Ceiling-Crown12', 'Ceiling-Crown13', 'Ceiling-Crown14', 'Ceiling-Crown15', |
| 27 |
'Ceiling-Crown16', 'Ceiling-Crown17', 'Ceiling-Crown18', 'Ceiling-Crown19', |
| 28 |
'Ceiling-Crown20', 'Ceiling-Crown21', 'Ceiling-Crown22', 'Ceiling-Crown23', |
| 29 |
'Ceiling-Crown24', 'Ceiling-Crown25', 'Ceiling-Crown26', 'Ceiling-Crown27', |
| 30 |
'Ceiling-Crown28', 'Ceiling-Crown29', 'Ceiling-Crown30', 'Ceiling-Crown31', |
| 31 |
'Ceiling-Crown32', 'Ceiling-Crown33', 'Ceiling-Crown34', |
| 32 |
'Corner-Cornerguard01', 'Corner-Cornerguard02', 'Corner-Plycap01', |
| 33 |
'Corner-Plycap02', 'Corner-Plycap03', 'Floor-Base01', 'Floor-Base02', |
| 34 |
'Floor-Base03', 'Floor-Base04', 'Floor-Base05', 'Floor-Base06', |
| 35 |
'Floor-Base07', 'Floor-Base08', 'Floor-Base09', 'Floor-Base10', |
| 36 |
'Floor-Base11', 'Floor-Basecap01', 'Floor-Basecap02', 'Floor-Basecap03', |
| 37 |
'Floor-Basecap04', 'Floor-Basecap05', 'Floor-Basecap06', 'Floor-Lip01', |
| 38 |
'Floor-Lip02', 'Floor-Quarterround01', 'Floor-Shoe01', 'Floor-Shoe02', |
| 39 |
'Floor-Shoe03', 'Floor-Stair01', 'Floor-Stair02', 'Floor-Wallbase01', |
| 40 |
'Panel-Mullion01', 'Panel-Mullion02', 'Panel-Mullion03', 'Panel-Panel01', |
| 41 |
'Panel-Panel02', 'Panel-Panel03', 'Panel-Panel04', 'Panel-Panel05', |
| 42 |
'Panel-Panel06', 'Panel-Trimmid01', 'Panel-Trimmid02', 'Panel-Trimmid03', |
| 43 |
'Panel-Trimmid04', 'Panel-Trimmid05', 'Picture-Frame01', 'Picture-Frame02', |
| 44 |
'Picture-Frame03', 'Picture-Frame04', 'Picture-Frame05', 'Picture-Frame06', |
| 45 |
'Picture-Frame07', 'Picture-Frame08', 'Picture-Frame09', 'Picture-Frame10', |
| 46 |
'Picture-Frame11', 'Picture-Frame12', 'Picture-Frame13', 'Picture-Frame14', |
| 47 |
'Picture-Frame15', 'Picture-Frame16', 'Picture-Frame17', 'Picture-Frame18', |
| 48 |
'Picture-Frame19', 'Picture-Frame20', 'Rail-Round01', 'Rail-Top01', |
| 49 |
'Rail-Top02', 'Rail-Top03', 'Wall-Chairrail01', 'Wall-Chairrail02', |
| 50 |
'Wall-Chairrail03', 'Wall-Chairrail04', 'Wall-Chairrail05', |
| 51 |
'Wall-Chairrail06', 'Wall-Chairrail07', 'Wall-Chairrail08', |
| 52 |
'Wall-Chairrail09', 'Wall-Chairrail10', 'Window-Trimbottom01', |
| 53 |
'Window-Trimbottom02', 'Window-Trimbottom03', 'Window-Trimbottom04', |
| 54 |
'Window-Trimbottom05', 'Window-Trimbottom06', 'Window-Trimbottom07', |
| 55 |
'Window-Trimbottom08', 'Window-Trimbottom09', 'Window-Trimbottom10', |
| 56 |
'Window-Trimbottom11', 'Window-Trimbottom12', 'Window-Trimbottom13', |
| 57 |
'Window-Trimbottom14', 'Window-Trimbottom15', 'Window-Trimbottom16', |
| 58 |
'Window-Trimbottom17', 'Window-Trimbottom18', 'Window-Trimbottom19', |
| 59 |
'Window-Trimbottom20', 'Window-Trimbottom21', 'Window-Trimbottom22', |
| 60 |
'Window-Trimbottom23', 'Window-Trimbottom24', 'Window-Trimbottom25', |
| 61 |
'Window-Trimtop01', 'Window-Trimtop02', 'Window-Trimtop03', |
| 62 |
'Window-Trimtop04', 'Window-Trimtop05', 'Window-Trimtop06', |
| 63 |
'Window-Trimtop07', 'Window-Trimtop08', 'Window-Trimtop09', |
| 64 |
'Window-Trimtop10', 'Window-Trimtop11', 'Window-Trimtop12', |
| 65 |
'Window-Trimtop13', 'Window-Trimtop14', 'Window-Trimtop15', |
| 66 |
'Window-Trimtop16', 'Window-Trimtop17', 'Window-Trimtop18', |
| 67 |
'Window-Trimtop19', 'Window-Trimtop20', 'Window-Trimtop21', |
| 68 |
'Window-Trimtop22', 'Window-Trimtop23', 'Window-Trimtop24', |
| 69 |
'Window-Trimtop25' |
| 70 |
] |
| 71 |
|
| 72 |
|
| 73 |
# ------------------------------------------------------------------------ |
| 74 |
# Operators |
| 75 |
# ------------------------------------------------------------------------ |
| 76 |
|
| 77 |
|
| 78 |
class WM_OT_SelectAllCMP(Operator): |
| 79 |
"""Select All CMP Paths""" |
| 80 |
bl_label = "Select CMP Paths" |
| 81 |
bl_idname = "wm.selallcmp" |
| 82 |
|
| 83 |
def execute(self, context): |
| 84 |
bpy.ops.object.select_by_type(type='CURVE') |
| 85 |
selectedobjects = bpy.context.selected_objects |
| 86 |
if selectedobjects: |
| 87 |
for i in selectedobjects: |
| 88 |
try: |
| 89 |
print(i.data.bevel_object.name) |
| 90 |
# is cmp, do nothing really, don't delete the line above |
| 91 |
except AttributeError: |
| 92 |
i.select_set(False) |
| 93 |
return {'FINISHED'} |
| 94 |
|
| 95 |
|
| 96 |
class WM_OT_TiltCurvePositive(Operator): |
| 97 |
"""Tilts Curve +90 Degrees""" |
| 98 |
bl_label = "Tilt +" |
| 99 |
bl_idname = "wm.tiltcurvepositive" |
| 100 |
|
| 101 |
def execute(self, context): |
| 102 |
selectedobjects = bpy.context.selected_objects |
| 103 |
if selectedobjects: |
| 104 |
for i in selectedobjects: |
| 105 |
if (i.type == 'CURVE'): |
| 106 |
if i.data.dimensions == '3D': |
| 107 |
# select object |
| 108 |
bpy.ops.object.select_all(action='DESELECT') |
| 109 |
bcvo = bpy.context.view_layer.objects |
| 110 |
bcvo.active.select_set(state=False) |
| 111 |
bpy.data.objects[i.name].select_set(True) |
| 112 |
bpy.context.view_layer.objects.active = i |
| 113 |
# rotate it |
| 114 |
bpy.ops.object.mode_set(mode='EDIT', toggle=False) |
| 115 |
bpy.ops.curve.select_all(action='SELECT') |
| 116 |
bpy.ops.transform.tilt(value=1.570796) |
| 117 |
bpy.ops.object.mode_set(mode='OBJECT', toggle=False) |
| 118 |
else: |
| 119 |
self.report( |
| 120 |
{'ERROR'}, "Can't tilt " + i.name |
| 121 |
+ ". Convert curve to 3D.") |
| 122 |
# reselect objects |
| 123 |
for m in selectedobjects: |
| 124 |
bpy.data.objects[m.name].select_set(True) |
| 125 |
return {'FINISHED'} |
| 126 |
|
| 127 |
|
| 128 |
class WM_OT_TiltCurveNegative(Operator): |
| 129 |
"""Tilts Curve -90 Degrees""" |
| 130 |
bl_label = "Tilt -" |
| 131 |
bl_idname = "wm.tiltcurvenegative" |
| 132 |
|
| 133 |
def execute(self, context): |
| 134 |
selectedobjects = bpy.context.selected_objects |
| 135 |
if selectedobjects: |
| 136 |
for i in selectedobjects: |
| 137 |
if (i.type == 'CURVE'): |
| 138 |
if i.data.dimensions == '3D': |
| 139 |
# select object |
| 140 |
bpy.ops.object.select_all(action='DESELECT') |
| 141 |
bcvo = bpy.context.view_layer.objects |
| 142 |
bcvo.active.select_set(state=False) |
| 143 |
bpy.data.objects[i.name].select_set(True) |
| 144 |
bpy.context.view_layer.objects.active = i |
| 145 |
# rotate it |
| 146 |
bpy.ops.object.mode_set(mode='EDIT', toggle=False) |
| 147 |
bpy.ops.curve.select_all(action='SELECT') |
| 148 |
bpy.ops.transform.tilt(value=-1.570796) |
| 149 |
bpy.ops.object.mode_set(mode='OBJECT', toggle=False) |
| 150 |
else: |
| 151 |
self.report( |
| 152 |
{'ERROR'}, "Can't tilt " + i.name |
| 153 |
+ ". Convert curve to 3D.") |
| 154 |
# reselect objects |
| 155 |
for m in selectedobjects: |
| 156 |
bpy.data.objects[m.name].select_set(True) |
| 157 |
return {'FINISHED'} |
| 158 |
|
| 159 |
|
| 160 |
class WM_OT_TiltCurveFlip(Operator): |
| 161 |
"""Tilts Curve 180 Degrees""" |
| 162 |
bl_label = "Tilt <>" |
| 163 |
bl_idname = "wm.tiltcurveflip" |
| 164 |
|
| 165 |
def execute(self, context): |
| 166 |
selectedobjects = bpy.context.selected_objects |
| 167 |
if selectedobjects: |
| 168 |
for i in selectedobjects: |
| 169 |
if (i.type == 'CURVE'): |
| 170 |
if i.data.dimensions == '3D': |
| 171 |
# select object |
| 172 |
bpy.ops.object.select_all(action='DESELECT') |
| 173 |
bcvo = bpy.context.view_layer.objects |
| 174 |
bcvo.active.select_set(state=False) |
| 175 |
bpy.data.objects[i.name].select_set(True) |
| 176 |
bpy.context.view_layer.objects.active = i |
| 177 |
# rotate it |
| 178 |
bpy.ops.object.mode_set(mode='EDIT', toggle=False) |
| 179 |
bpy.ops.curve.select_all(action='SELECT') |
| 180 |
bpy.ops.transform.tilt(value=3.141592) |
| 181 |
bpy.ops.object.mode_set(mode='OBJECT', toggle=False) |
| 182 |
else: |
| 183 |
self.report( |
| 184 |
{'ERROR'}, "Can't tilt " + i.name |
| 185 |
+ ". Convert curve to 3D.") |
| 186 |
# reselect objects |
| 187 |
for m in selectedobjects: |
| 188 |
bpy.data.objects[m.name].select_set(True) |
| 189 |
return {'FINISHED'} |
| 190 |
|
| 191 |
|
| 192 |
class WM_OT_MirrorCurveX(Operator): |
| 193 |
"""Mirror Object on X Axis""" |
| 194 |
bl_label = "Mirror X" |
| 195 |
bl_idname = "wm.mirrorcurvex" |
| 196 |
|
| 197 |
def execute(self, context): |
| 198 |
previouspivot = bpy.context.scene.tool_settings.transform_pivot_point |
| 199 |
bpy.ops.object.mode_set(mode='OBJECT', toggle=False) |
| 200 |
bpy.ops.transform.mirror( |
| 201 |
orient_type='GLOBAL', orient_matrix=( |
| 202 |
(1, 0, 0), (0, 1, 0), (0, 0, 1) |
| 203 |
), orient_matrix_type='GLOBAL', |
| 204 |
constraint_axis=(True, False, False)) |
| 205 |
bpy.context.scene.tool_settings.transform_pivot_point = previouspivot |
| 206 |
return {'FINISHED'} |
| 207 |
|
| 208 |
|
| 209 |
class WM_OT_MirrorCurveY(Operator): |
| 210 |
"""Mirror Object on Y Axis""" |
| 211 |
bl_label = "Mirror Y" |
| 212 |
bl_idname = "wm.mirrorcurvey" |
| 213 |
|
| 214 |
def execute(self, context): |
| 215 |
previouspivot = bpy.context.scene.tool_settings.transform_pivot_point |
| 216 |
bcst = bpy.context.scene.tool_settings |
| 217 |
bcst.transform_pivot_point = 'INDIVIDUAL_ORIGINS' |
| 218 |
bpy.ops.object.mode_set(mode='OBJECT', toggle=False) |
| 219 |
bpy.ops.transform.mirror( |
| 220 |
orient_type='GLOBAL', orient_matrix=( |
| 221 |
(1, 0, 0), (0, 1, 0), (0, 0, 1) |
| 222 |
), orient_matrix_type='GLOBAL', |
| 223 |
constraint_axis=(False, True, False)) |
| 224 |
bpy.context.scene.tool_settings.transform_pivot_point = previouspivot |
| 225 |
|
| 226 |
return {'FINISHED'} |
| 227 |
|
| 228 |
|
| 229 |
class WM_OT_MirrorCurveZ(Operator): |
| 230 |
"""Mirror Object on Z Axis""" |
| 231 |
bl_label = "Mirror Z" |
| 232 |
bl_idname = "wm.mirrorcurvez" |
| 233 |
|
| 234 |
def execute(self, context): |
| 235 |
previouspivot = bpy.context.scene.tool_settings.transform_pivot_point |
| 236 |
bpy.ops.object.mode_set(mode='OBJECT', toggle=False) |
| 237 |
bpy.ops.transform.mirror( |
| 238 |
orient_type='GLOBAL', orient_matrix=( |
| 239 |
(1, 0, 0), (0, 1, 0), (0, 0, 1) |
| 240 |
), orient_matrix_type='GLOBAL', |
| 241 |
constraint_axis=(False, False, True)) |
| 242 |
bpy.context.scene.tool_settings.transform_pivot_point = previouspivot |
| 243 |
return {'FINISHED'} |
| 244 |
|
| 245 |
|
| 246 |
class WM_OT_ScaleCurvePositive(Operator): |
| 247 |
"""Increase the Scale of the Curve Bevel""" |
| 248 |
bl_label = "Scale +" |
| 249 |
bl_idname = "wm.scalecurvepositive" |
| 250 |
|
| 251 |
def execute(self, context): |
| 252 |
selectedobjects = bpy.context.selected_objects |
| 253 |
if selectedobjects: |
| 254 |
for i in selectedobjects: |
| 255 |
if (i.type == 'CURVE'): |
| 256 |
# select object |
| 257 |
bpy.ops.object.select_all(action='DESELECT') |
| 258 |
bcvo = bpy.context.view_layer.objects |
| 259 |
bcvo.active.select_set(state=False) |
| 260 |
bpy.data.objects[i.name].select_set(True) |
| 261 |
bpy.context.view_layer.objects.active = i |
| 262 |
# scale it |
| 263 |
bpy.ops.object.mode_set(mode='EDIT', toggle=False) |
| 264 |
bpy.ops.curve.select_all(action='SELECT') |
| 265 |
bpy.ops.transform.transform( |
| 266 |
mode='CURVE_SHRINKFATTEN', value=(1.1, 0, 0, 0)) |
| 267 |
bpy.ops.object.mode_set(mode='OBJECT', toggle=False) |
| 268 |
else: |
| 269 |
self.report( |
| 270 |
{'ERROR'}, "Can't scale " + i.name |
| 271 |
+ ". Not a curve.") |
| 272 |
# reselect objects |
| 273 |
for m in selectedobjects: |
| 274 |
bpy.data.objects[m.name].select_set(True) |
| 275 |
return {'FINISHED'} |
| 276 |
|
| 277 |
|
| 278 |
class WM_OT_ScaleCurveNegative(Operator): |
| 279 |
"""Decrease the Scale of the Curve Bevel""" |
| 280 |
bl_label = "Scale -" |
| 281 |
bl_idname = "wm.scalecurvenegative" |
| 282 |
|
| 283 |
def execute(self, context): |
| 284 |
selectedobjects = bpy.context.selected_objects |
| 285 |
if selectedobjects: |
| 286 |
for i in selectedobjects: |
| 287 |
if (i.type == 'CURVE'): |
| 288 |
# select object |
| 289 |
bpy.ops.object.select_all(action='DESELECT') |
| 290 |
bcvo = bpy.context.view_layer.objects |
| 291 |
bcvo.active.select_set(state=False) |
| 292 |
bpy.data.objects[i.name].select_set(True) |
| 293 |
bpy.context.view_layer.objects.active = i |
| 294 |
# scale it |
| 295 |
bpy.ops.object.mode_set(mode='EDIT', toggle=False) |
| 296 |
bpy.ops.curve.select_all(action='SELECT') |
| 297 |
bpy.ops.transform.transform( |
| 298 |
mode='CURVE_SHRINKFATTEN', value=(0.9, 0, 0, 0)) |
| 299 |
bpy.ops.object.mode_set(mode='OBJECT', toggle=False) |
| 300 |
else: |
| 301 |
self.report( |
| 302 |
{'ERROR'}, "Can't scale " + i.name |
| 303 |
+ ". Not a curve.") |
| 304 |
# reselect objects |
| 305 |
for m in selectedobjects: |
| 306 |
bpy.data.objects[m.name].select_set(True) |
| 307 |
return {'FINISHED'} |
| 308 |
|
| 309 |
|
| 310 |
class WM_OT_Curve3DToggle2D(Operator): |
| 311 |
"""Toggle between 2D and 3D Curve Dimensions""" |
| 312 |
bl_label = "2D/3D" |
| 313 |
bl_idname = "wm.curve3dtoggle2d" |
| 314 |
|
| 315 |
def execute(self, context): |
| 316 |
selectedobjects = bpy.context.selected_objects |
| 317 |
if selectedobjects: |
| 318 |
for i in selectedobjects: |
| 319 |
if (i.type == 'CURVE'): |
| 320 |
if i.data.dimensions == '2D': |
| 321 |
i.data.dimensions = '3D' |
| 322 |
elif i.data.dimensions == '3D': |
| 323 |
i.data.dimensions = '2D' |
| 324 |
else: |
| 325 |
self.report( |
| 326 |
{'ERROR'}, "Can't convert " + i.name |
| 327 |
+ ". Not a curve.") |
| 328 |
return {'FINISHED'} |
| 329 |
|
| 330 |
|
| 331 |
class WM_OT_AllCurves3D(Operator): |
| 332 |
"""Set all Curves to 3D Curve Dimension""" |
| 333 |
bl_label = "Curve 3D" |
| 334 |
bl_idname = "wm.curve3d" |
| 335 |
|
| 336 |
def execute(self, context): |
| 337 |
selectedobjects = bpy.context.selected_objects |
| 338 |
if selectedobjects: |
| 339 |
for i in selectedobjects: |
| 340 |
if (i.type == 'CURVE'): |
| 341 |
i.data.dimensions = '3D' |
| 342 |
else: |
| 343 |
self.report( |
| 344 |
{'ERROR'}, "Can't convert " + i.name |
| 345 |
+ ". Not a curve.") |
| 346 |
return {'FINISHED'} |
| 347 |
|
| 348 |
|
| 349 |
class WM_OT_AllCurves2D(Operator): |
| 350 |
"""Set all Curves to 2D Curve Dimension""" |
| 351 |
bl_label = "Curve 2D" |
| 352 |
bl_idname = "wm.curve2d" |
| 353 |
|
| 354 |
def execute(self, context): |
| 355 |
selectedobjects = bpy.context.selected_objects |
| 356 |
if selectedobjects: |
| 357 |
for i in selectedobjects: |
| 358 |
if (i.type == 'CURVE'): |
| 359 |
i.data.dimensions = '2D' |
| 360 |
else: |
| 361 |
self.report( |
| 362 |
{'ERROR'}, "Can't convert " + i.name |
| 363 |
+ ". Not a curve.") |
| 364 |
return {'FINISHED'} |
| 365 |
|
| 366 |
|
| 367 |
class WM_OT_SwitchDirection(Operator): |
| 368 |
"""Toggle Direction for Curves between 2D and 3D""" |
| 369 |
bl_label = "<> Direction" |
| 370 |
bl_idname = "wm.switchdirection" |
| 371 |
|
| 372 |
def execute(self, context): |
| 373 |
selectedobjects = bpy.context.selected_objects |
| 374 |
if selectedobjects: |
| 375 |
for i in selectedobjects: |
| 376 |
if (i.type == 'CURVE'): |
| 377 |
# select object |
| 378 |
bpy.ops.object.select_all(action='DESELECT') |
| 379 |
bpy.context.view_layer.objects.active.select_set( |
| 380 |
state=False) |
| 381 |
bpy.data.objects[i.name].select_set(True) |
| 382 |
bpy.context.view_layer.objects.active = i |
| 383 |
# switch curve direction |
| 384 |
bpy.ops.object.mode_set(mode='EDIT', toggle=False) |
| 385 |
bpy.ops.curve.switch_direction() |
| 386 |
bpy.ops.object.mode_set(mode='OBJECT', toggle=False) |
| 387 |
else: |
| 388 |
self.report( |
| 389 |
{'ERROR'}, "Can't edit " + i.name |
| 390 |
+ ". Not a curve.") |
| 391 |
# reselect objects |
| 392 |
for m in selectedobjects: |
| 393 |
bpy.data.objects[m.name].select_set(True) |
| 394 |
return {'FINISHED'} |
| 395 |
|
| 396 |
|
| 397 |
class WM_OT_ConvertToCMP(Operator): |
| 398 |
"""Converts a selected face, edge loop,""" \ |
| 399 |
""" or path into a crown molding path""" |
| 400 |
bl_label = "Convert to CMP Path" |
| 401 |
bl_idname = "wm.converttocmp" |
| 402 |
bl_options = {'REGISTER', 'UNDO'} |
| 403 |
|
| 404 |
def execute(self, context): |
| 405 |
scene = context.scene |
| 406 |
cmptool = scene.cmptool |
| 407 |
# gather current state |
| 408 |
previouspivot = bpy.context.scene.tool_settings.transform_pivot_point |
| 409 |
if bpy.context.active_object.mode == 'EDIT': |
| 410 |
bpy.ops.object.mode_set(mode='OBJECT', toggle=False) |
| 411 |
bpy.ops.object.transform_apply( |
| 412 |
location=False, rotation=False, scale=True) |
| 413 |
bpy.ops.object.mode_set(mode='EDIT', toggle=False) |
| 414 |
bpy.ops.mesh.duplicate_move( |
| 415 |
MESH_OT_duplicate={"mode": 1}, |
| 416 |
TRANSFORM_OT_translate={"value": (0, 0, 0)}) |
| 417 |
bpy.ops.mesh.separate(type='SELECTED') |
| 418 |
bcvo = bpy.context.view_layer.objects.active |
| 419 |
bcvo.name = bpy.context.view_layer.objects.active.name + \ |
| 420 |
"---ORIGINAL---" |
| 421 |
bpy.context.view_layer.objects.active.select_set(state=False) |
| 422 |
newActiveObjects = [] |
| 423 |
for j in bpy.context.selected_objects: |
| 424 |
newActiveObjects.append(j.name) |
| 425 |
for k in newActiveObjects: |
| 426 |
nobj = bpy.data.objects.get(k) |
| 427 |
if nobj: |
| 428 |
bpy.context.view_layer.objects.active = nobj |
| 429 |
bpy.ops.object.mode_set(mode='OBJECT', toggle=False) |
| 430 |
bpy.ops.object.origin_set(type='ORIGIN_GEOMETRY', center='MEDIAN') |
| 431 |
bpy.ops.object.mode_set(mode='EDIT', toggle=False) |
| 432 |
bpy.ops.mesh.select_all(action='SELECT') |
| 433 |
bpy.ops.mesh.separate(type='LOOSE') |
| 434 |
bpy.ops.object.mode_set(mode='OBJECT', toggle=False) |
| 435 |
bpy.ops.object.origin_set(type='ORIGIN_GEOMETRY', center='MEDIAN') |
| 436 |
bpy.ops.object.convert(target='CURVE') |
| 437 |
bpy.ops.object.mode_set(mode='EDIT', toggle=False) |
| 438 |
bpy.ops.curve.select_all(action='SELECT') |
| 439 |
bpy.ops.curve.spline_type_set(type='BEZIER') |
| 440 |
bpy.ops.object.mode_set(mode='OBJECT', toggle=False) |
| 441 |
bpy.ops.object.transform_apply( |
| 442 |
location=False, rotation=True, scale=True) |
| 443 |
for b in bpy.context.visible_objects: |
| 444 |
if '---ORIGINAL---' in b.name: |
| 445 |
b.name = re.sub('---ORIGINAL---', '', b.name) |
| 446 |
selectedobjects = bpy.context.selected_objects |
| 447 |
bpy.ops.object.select_all(action='DESELECT') |
| 448 |
bpy.context.view_layer.objects.active.select_set(state=False) |
| 449 |
if selectedobjects: |
| 450 |
# loop through new objects |
| 451 |
for i in selectedobjects: |
| 452 |
bpy.ops.object.select_all(action='DESELECT') |
| 453 |
bcvo = bpy.context.view_layer.objects |
| 454 |
bcvo.active.select_set(state=False) |
| 455 |
bpy.data.objects[i.name].select_set(True) |
| 456 |
# This was the last line of code for this v0.0.1 function |
| 457 |
# a celebration commenced after figuring this out |
| 458 |
bpy.context.view_layer.objects.active = i |
| 459 |
bpy.ops.object.origin_set( |
| 460 |
type='ORIGIN_GEOMETRY', center='MEDIAN') |
| 461 |
# rotate to fix 2D path issues |
| 462 |
if cmptool.advanced_rotate_curve: |
| 463 |
curve = i.data |
| 464 |
spline = curve.splines[0] |
| 465 |
xGlobalPathPoints = [] |
| 466 |
yGlobalPathPoints = [] |
| 467 |
zGlobalPathPoints = [] |
| 468 |
for p in range(0, len(spline.bezier_points)): |
| 469 |
if float( |
| 470 |
"{:.3f}".format(float(i.location.x)) |
| 471 |
) + 0 != 0: |
| 472 |
xGlobalPathPoint = i.location.x * \ |
| 473 |
spline.bezier_points[p].co.x |
| 474 |
else: |
| 475 |
xGlobalPathPoint = spline.bezier_points[p].co.x |
| 476 |
xGlobalPathPoint = "{:.3f}".format( |
| 477 |
float(xGlobalPathPoint)) |
| 478 |
xGlobalPathPoints.append( |
| 479 |
float(xGlobalPathPoint) + 0) |
| 480 |
if float( |
| 481 |
"{:.3f}".format(float(i.location.y)) |
| 482 |
) + 0 != 0: |
| 483 |
yGlobalPathPoint = i.location.y * \ |
| 484 |
spline.bezier_points[p].co.y |
| 485 |
else: |
| 486 |
yGlobalPathPoint = spline.bezier_points[p].co.y |
| 487 |
yGlobalPathPoint = "{:.3f}".format( |
| 488 |
float(yGlobalPathPoint)) |
| 489 |
yGlobalPathPoints.append( |
| 490 |
float(yGlobalPathPoint) + 0) |
| 491 |
if float( |
| 492 |
"{:.3f}".format(float(i.location.z)) |
| 493 |
) + 0 != 0: |
| 494 |
zGlobalPathPoint = i.location.z * \ |
| 495 |
spline.bezier_points[p].co.z |
| 496 |
else: |
| 497 |
zGlobalPathPoint = spline.bezier_points[p].co.z |
| 498 |
zGlobalPathPoint = "{:.3f}".format( |
| 499 |
float(zGlobalPathPoint)) |
| 500 |
zGlobalPathPoints.append( |
| 501 |
float(zGlobalPathPoint) + 0) |
| 502 |
if len(set(xGlobalPathPoints)) == 1: |
| 503 |
# faces X |
| 504 |
bcst = bpy.context.scene.tool_settings |
| 505 |
bcst.transform_pivot_point = 'MEDIAN_POINT' |
| 506 |
bpy.ops.object.mode_set(mode='EDIT', toggle=False) |
| 507 |
bpy.ops.transform.rotate( |
| 508 |
value=1.570796, orient_axis='Y', |
| 509 |
orient_type='GLOBAL', orient_matrix=( |
| 510 |
(1, 0, 0), (0, 1, 0), (0, 0, 1) |
| 511 |
), orient_matrix_type='GLOBAL', |
| 512 |
constraint_axis=( |
| 513 |
False, True, False), mirror=True) |
| 514 |
bpy.ops.object.mode_set( |
| 515 |
mode='OBJECT', toggle=False) |
| 516 |
bpy.ops.transform.rotate( |
| 517 |
value=-1.570796, orient_axis='Y', |
| 518 |
orient_type='GLOBAL', orient_matrix=( |
| 519 |
(1, 0, 0), (0, 1, 0), (0, 0, 1) |
| 520 |
), orient_matrix_type='GLOBAL', |
| 521 |
constraint_axis=( |
| 522 |
False, True, False), mirror=True) |
| 523 |
if len(set(yGlobalPathPoints)) == 1: |
| 524 |
# faces Y |
| 525 |
bcst = bpy.context.scene.tool_settings |
| 526 |
bcst.transform_pivot_point = 'MEDIAN_POINT' |
| 527 |
bpy.ops.object.mode_set(mode='EDIT', toggle=False) |
| 528 |
bpy.ops.transform.rotate( |
| 529 |
value=1.570796, orient_axis='X', |
| 530 |
orient_type='GLOBAL', orient_matrix=( |
| 531 |
(1, 0, 0), (0, 1, 0), (0, 0, 1) |
| 532 |
), orient_matrix_type='GLOBAL', |
| 533 |
constraint_axis=( |
| 534 |
True, False, False), mirror=True) |
| 535 |
bpy.ops.object.mode_set( |
| 536 |
mode='OBJECT', toggle=False) |
| 537 |
bpy.ops.transform.rotate( |
| 538 |
value=-1.570796, orient_axis='X', |
| 539 |
orient_type='GLOBAL', orient_matrix=( |
| 540 |
(1, 0, 0), (0, 1, 0), (0, 0, 1) |
| 541 |
), orient_matrix_type='GLOBAL', |
| 542 |
constraint_axis=( |
| 543 |
True, False, False), mirror=True) |
| 544 |
if len(set(zGlobalPathPoints)) == 1: |
| 545 |
print("faces Z") |
| 546 |
# Leave print or else you get error |
| 547 |
# set bevel object |
| 548 |
# new in 2.91, if current blender version is above |
| 549 |
# 2.91.0, set bevel mode to object (previously default) |
| 550 |
if (2, 91, 0) <= bpy.app.version: |
| 551 |
i.data.bevel_mode = 'OBJECT' |
| 552 |
# set bevel profile to crown molding |
| 553 |
selection = bpy.data.window_managers["WinMan"]. \ |
| 554 |
cmpy_previews.replace(".png", "") |
| 555 |
if bpy.data.objects.get(selection) is not None: |
| 556 |
i.data.bevel_object = \ |
| 557 |
bpy.data.objects[selection] |
| 558 |
else: |
| 559 |
self.report( |
| 560 |
{'ERROR'}, "Unable to auto add bevel object. " |
| 561 |
+ "Append the CrownMoldingPack .blend file to " |
| 562 |
+ "fix this in the future. Or, manually set " |
| 563 |
+ "in Path > Object Data > Geometry > Bevel " |
| 564 |
+ "> Object" |
| 565 |
) |
| 566 |
# make path look nice |
| 567 |
i.data.use_fill_caps = True |
| 568 |
bpy.ops.object.shade_smooth() |
| 569 |
bpy.ops.object.modifier_add(type='EDGE_SPLIT') |
| 570 |
if cmptool.advanced_rotate_curve: |
| 571 |
i.data.dimensions = '2D' |
| 572 |
else: |
| 573 |
i.data.dimensions = '3D' |
| 574 |
# rename object |
| 575 |
needle = re.compile(r"\.\d{2,}") |
| 576 |
if needle.search(bpy.context.object.name): |
| 577 |
i.name = re.sub(r"(\.\d{2,})", r".CMP\1", i.name) |
| 578 |
else: |
| 579 |
i.name = i.name + ".CMP" |
| 580 |
bpy.ops.object.select_all(action='DESELECT') |
| 581 |
# reselect new objects |
| 582 |
bcst = bpy.context.scene.tool_settings |
| 583 |
bcst.transform_pivot_point = previouspivot |
| 584 |
for m in selectedobjects: |
| 585 |
bpy.data.objects[m.name].select_set(True) |
| 586 |
return {'FINISHED'} |
| 587 |
|
| 588 |
|
| 589 |
class WM_OT_CycleBack(Operator): |
| 590 |
"""Change the Curve Profile to the Previous Option""" |
| 591 |
bl_label = "< Previous" |
| 592 |
bl_idname = "wm.cycleback" |
| 593 |
|
| 594 |
def execute(self, context): |
| 595 |
selectedobjects = bpy.context.selected_objects |
| 596 |
if selectedobjects: |
| 597 |
for i in selectedobjects: |
| 598 |
if (i.type == 'CURVE'): |
| 599 |
if i.data.bevel_object.name in CMPlist: |
| 600 |
# figure out the index of our current |
| 601 |
currentindex = CMPlist.index(i.data.bevel_object.name) |
| 602 |
previndex = currentindex - 1 |
| 603 |
if previndex < 0: |
| 604 |
previndex = len(CMPlist) - 1 |
| 605 |
prevname = CMPlist[previndex] |
| 606 |
# set bevel object to previous in list |
| 607 |
i.data.bevel_object = bpy.data.objects[prevname] |
| 608 |
else: |
| 609 |
self.report( |
| 610 |
{'ERROR'}, "Current Bevel Object not in " + |
| 611 |
"defined list. Can't choose previous option." + |
| 612 |
" Edit CMPlist in add-on python file.") |
| 613 |
else: |
| 614 |
self.report( |
| 615 |
{'ERROR'}, "Can't edit " + i.name + |
| 616 |
". Not a curve.") |
| 617 |
# reselect objects |
| 618 |
for m in selectedobjects: |
| 619 |
bpy.data.objects[m.name].select_set(True) |
| 620 |
return {'FINISHED'} |
| 621 |
|
| 622 |
|
| 623 |
class WM_OT_CycleForward(Operator): |
| 624 |
"""Change the Curve Profile to the Next Option""" |
| 625 |
bl_label = "Next >" |
| 626 |
bl_idname = "wm.cycleforward" |
| 627 |
|
| 628 |
def execute(self, context): |
| 629 |
selectedobjects = bpy.context.selected_objects |
| 630 |
if selectedobjects: |
| 631 |
for i in selectedobjects: |
| 632 |
if (i.type == 'CURVE'): |
| 633 |
if i.data.bevel_object.name in CMPlist: |
| 634 |
# figure out the index of our current |
| 635 |
currentindex = CMPlist.index(i.data.bevel_object.name) |
| 636 |
nextindex = currentindex + 1 |
| 637 |
if nextindex >= len(CMPlist): |
| 638 |
nextindex = 0 |
| 639 |
nextname = CMPlist[nextindex] |
| 640 |
# set bevel object to next in list |
| 641 |
i.data.bevel_object = bpy.data.objects[nextname] |
| 642 |
else: |
| 643 |
self.report( |
| 644 |
{'ERROR'}, "Current Bevel Object not in " |
| 645 |
+ "defined list. Can't choose next option. " |
| 646 |
+ "Edit CMPlist in add-on python file.") |
| 647 |
else: |
| 648 |
self.report( |
| 649 |
{'ERROR'}, "Can't edit " + i.name |
| 650 |
+ ". Not a curve.") |
| 651 |
# reselect objects |
| 652 |
for m in selectedobjects: |
| 653 |
bpy.data.objects[m.name].select_set(True) |
| 654 |
return {'FINISHED'} |
| 655 |
|
| 656 |
|
| 657 |
class WM_OT_ChangeSelected(Operator): |
| 658 |
"""Change the curve profile to the picked option""" |
| 659 |
bl_label = "Change Path" |
| 660 |
bl_idname = "wm.changeselected" |
| 661 |
|
| 662 |
def execute(self, context): |
| 663 |
selectedobjects = bpy.context.selected_objects |
| 664 |
if selectedobjects: |
| 665 |
for i in selectedobjects: |
| 666 |
if (i.type == 'CURVE'): |
| 667 |
selection = bpy.data.window_managers["WinMan"]. \ |
| 668 |
cmpy_previews.replace(".png", "") |
| 669 |
if bpy.data.objects.get(selection) is not None: |
| 670 |
i.data.bevel_object = \ |
| 671 |
bpy.data.objects[selection] |
| 672 |
else: |
| 673 |
self.report( |
| 674 |
{'ERROR'}, "Unable to auto add bevel object. " |
| 675 |
+ "You should let Jeff know if you see this." |
| 676 |
) |
| 677 |
else: |
| 678 |
self.report( |
| 679 |
{'ERROR'}, "Can't edit " + i.name |
| 680 |
+ ". Not a curve.") |
| 681 |
# reselect objects |
| 682 |
for m in selectedobjects: |
| 683 |
bpy.data.objects[m.name].select_set(True) |
| 684 |
return {'FINISHED'} |
| 685 |
|
| 686 |
|
| 687 |
class WM_OT_SetOptions(Operator): |
| 688 |
# you can use this function to make a list of paths for the CMPlist |
| 689 |
"""Append .blend""" |
| 690 |
bl_label = "Set Options" |
| 691 |
bl_idname = "wm.cycleset" |
| 692 |
|
| 693 |
def execute(self, context): |
| 694 |
selectedobjects = bpy.context.selected_objects |
| 695 |
cycleset = [] |
| 696 |
if selectedobjects: |
| 697 |
for i in selectedobjects: |
| 698 |
if (i.type == 'CURVE'): |
| 699 |
# build a list |
| 700 |
cycleset.append(i.name) |
| 701 |
else: |
| 702 |
self.report( |
| 703 |
{'ERROR'}, "Cycleset Can't edit " + i.name |
| 704 |
+ ". Not a curve.") |
| 705 |
# reselect objects |
| 706 |
for m in selectedobjects: |
| 707 |
bpy.data.objects[m.name].select_set(True) |
| 708 |
print(sorted(cycleset)) |
| 709 |
# leave above print since that's the purpose of this class |
| 710 |
return {'FINISHED'} |
| 711 |
|
| 712 |
|
| 713 |
class WM_OT_AppendCMP(Operator): |
| 714 |
# append .blend file, set collection to hidden / excluded so it doesn't |
| 715 |
# show up or render |
| 716 |
# https://blender.stackexchange.com/questions/264622/ |
| 717 |
# https://blender.stackexchange.com/questions/34540/ |
| 718 |
"""Set Options to Pick From by Selecting All Desired Choices""" |
| 719 |
bl_label = "Append .blend File" |
| 720 |
bl_idname = "wm.appendcmp" |
| 721 |
|
| 722 |
def execute(self, context): |
| 723 |
cmpfilepath = os.path.join(os.path.dirname(__file__), |
| 724 |
"CrownMoldingPack-v3.1.blend") |
| 725 |
link = False |
| 726 |
# link all collections starting with 'MyCollection' |
| 727 |
with bpy.data.libraries. \ |
| 728 |
load(cmpfilepath, link=link) as (data_from, data_to): |
| 729 |
data_to.collections = [c for c in data_from.collections] |
| 730 |
|
| 731 |
# link collection to scene collection |
| 732 |
for coll in data_to.collections: |
| 733 |
if coll is not None: |
| 734 |
bpy.context.scene.collection.children.link(coll) |
| 735 |
|
| 736 |
included_collection_list_names = [ |
| 737 |
"CrownMoldingPack-v3.0", "CrownMoldingPack-v3.1", |
| 738 |
"CrownMoldingPack-v3.2"] |
| 739 |
layer_collections = list( |
| 740 |
bpy.context.view_layer.layer_collection.children) |
| 741 |
|
| 742 |
while layer_collections: |
| 743 |
layer_collection = layer_collections.pop(0) |
| 744 |
layer_collections.extend(layer_collection.children) |
| 745 |
layer_collection.exclude = layer_collection.name \ |
| 746 |
in included_collection_list_names |
| 747 |
return {'FINISHED'} |
| 748 |
|
| 749 |
|
| 750 |
# custom icons for dropdown |
| 751 |
preview_collections = {} |
| 752 |
CMPicons = bpy.utils.previews.new() |
| 753 |
# the path is calculated relative to this py file inside the addon folder |
| 754 |
# '..' goes up a level, seems to be needed when in dev? |
| 755 |
cmpy_icons_dir = os.path.join(os.path.dirname(__file__), "cmp-icons") |
| 756 |
ceil_dir = os.path.join(os.path.dirname(__file__), |
| 757 |
"cmp-path-thumbnails/ceil") |
| 758 |
flor_dir = os.path.join(os.path.dirname(__file__), |
| 759 |
"cmp-path-thumbnails/flor") |
| 760 |
corn_dir = os.path.join(os.path.dirname(__file__), |
| 761 |
"cmp-path-thumbnails/corn") |
| 762 |
panl_dir = os.path.join(os.path.dirname(__file__), |
| 763 |
"cmp-path-thumbnails/panl") |
| 764 |
picf_dir = os.path.join(os.path.dirname(__file__), |
| 765 |
"cmp-path-thumbnails/picf") |
| 766 |
rail_dir = os.path.join(os.path.dirname(__file__), |
| 767 |
"cmp-path-thumbnails/rail") |
| 768 |
wall_dir = os.path.join(os.path.dirname(__file__), |
| 769 |
"cmp-path-thumbnails/wall") |
| 770 |
wind_dir = os.path.join(os.path.dirname(__file__), |
| 771 |
"cmp-path-thumbnails/wind") |
| 772 |
|
| 773 |
# load a preview thumbnail of a file and store in the previews collection |
| 774 |
CMPicons.load("icon_ceil", |
| 775 |
os.path.join(cmpy_icons_dir, "ceiling.png"), 'IMAGE') |
| 776 |
CMPicons.load("icon_wind", |
| 777 |
os.path.join(cmpy_icons_dir, "window.png"), 'IMAGE') |
| 778 |
CMPicons.load("icon_panl", |
| 779 |
os.path.join(cmpy_icons_dir, "panel.png"), 'IMAGE') |
| 780 |
CMPicons.load("icon_wall", |
| 781 |
os.path.join(cmpy_icons_dir, "wall.png"), 'IMAGE') |
| 782 |
CMPicons.load("icon_flor", |
| 783 |
os.path.join(cmpy_icons_dir, "floor.png"), 'IMAGE') |
| 784 |
CMPicons.load("icon_rail", |
| 785 |
os.path.join(cmpy_icons_dir, "rail.png"), 'IMAGE') |
| 786 |
CMPicons.load("icon_corn", |
| 787 |
os.path.join(cmpy_icons_dir, "corner.png"), 'IMAGE') |
| 788 |
CMPicons.load("icon_picf", |
| 789 |
os.path.join(cmpy_icons_dir, "pictureframe.png"), 'IMAGE') |
| 790 |
preview_collections["main"] = CMPicons |
| 791 |
# create aliases for icon names |
| 792 |
customicons = preview_collections["main"] |
| 793 |
ic1 = customicons["icon_ceil"] |
| 794 |
ic2 = customicons["icon_wind"] |
| 795 |
ic3 = customicons["icon_panl"] |
| 796 |
ic4 = customicons["icon_wall"] |
| 797 |
ic5 = customicons["icon_flor"] |
| 798 |
ic6 = customicons["icon_rail"] |
| 799 |
ic7 = customicons["icon_corn"] |
| 800 |
ic8 = customicons["icon_picf"] |
| 801 |
# end custom icons for dropdown |
| 802 |
|
| 803 |
|
| 804 |
class CrownMoldingProperties(PropertyGroup): |
| 805 |
|
| 806 |
advanced_rotate_curve: BoolProperty( |
| 807 |
name="Auto-rotate Curve", |
| 808 |
description="Disable to prevent add-on from rotating" |
| 809 |
+ " the curve automatically. Helpful for arbitrarily" |
| 810 |
+ " rotated faces. Manual Rotation with CTRL + T required" |
| 811 |
+ " if disabled", |
| 812 |
default=True |
| 813 |
) |
| 814 |
|
| 815 |
curvecategory: EnumProperty( |
| 816 |
name="Curve Category:", |
| 817 |
description="Type of profiles to display", |
| 818 |
items=[('ceiling', "Ceiling", |
| 819 |
"Crown Molding and others aligned to top", |
| 820 |
ic1.icon_id, 1), |
| 821 |
('window', "Window", |
| 822 |
"Window Trim in both top and bottom alignment", |
| 823 |
ic2.icon_id, 2), |
| 824 |
('panel', "Panel", |
| 825 |
"panel edges for accents aligned to middle", |
| 826 |
ic3.icon_id, 3), |
| 827 |
('wall', "Wall", |
| 828 |
"Chair rail for walls aligned to middle", |
| 829 |
ic4.icon_id, 4), |
| 830 |
('floor', "Floor", |
| 831 |
"Base trim, stair lips mostly aligned to bottom", |
| 832 |
ic5.icon_id, 5), |
| 833 |
('rail', "Rail", |
| 834 |
"Hand rails aligned to middle", |
| 835 |
ic6.icon_id, 6), |
| 836 |
('corner', "Corner", |
| 837 |
"Corner gaurds and caps aligned to inset edge", |
| 838 |
ic7.icon_id, 7), |
| 839 |
('pictureframe', "Picture Frame", |
| 840 |
"Frames for photos or art aligned to wall", |
| 841 |
ic8.icon_id, 8) |
| 842 |
], |
| 843 |
default='ceiling' |
| 844 |
) |
| 845 |
|
| 846 |
|
| 847 |
# ------------------------------------------------------------------------ |
| 848 |
# Panel in Object Mode |
| 849 |
# ------------------------------------------------------------------------ |
| 850 |
|
| 851 |
|
| 852 |
def enum_previews_from_directory_items(self, context): |
| 853 |
"""EnumProperty callback""" |
| 854 |
enum_items = [] |
| 855 |
|
| 856 |
if context is None: |
| 857 |
return enum_items |
| 858 |
|
| 859 |
scene = context.scene |
| 860 |
cmptool = scene.cmptool |
| 861 |
if cmptool.curvecategory == 'ceiling': |
| 862 |
directory = ceil_dir |
| 863 |
elif cmptool.curvecategory == 'window': |
| 864 |
directory = wind_dir |
| 865 |
elif cmptool.curvecategory == 'panel': |
| 866 |
directory = panl_dir |
| 867 |
elif cmptool.curvecategory == 'wall': |
| 868 |
directory = wall_dir |
| 869 |
elif cmptool.curvecategory == 'floor': |
| 870 |
directory = flor_dir |
| 871 |
elif cmptool.curvecategory == 'corner': |
| 872 |
directory = corn_dir |
| 873 |
elif cmptool.curvecategory == 'pictureframe': |
| 874 |
directory = picf_dir |
| 875 |
elif cmptool.curvecategory == 'rail': |
| 876 |
directory = rail_dir |
| 877 |
|
| 878 |
# Get the preview collection (defined in register func). |
| 879 |
pcoll = preview_collections["main"] |
| 880 |
|
| 881 |
if directory == pcoll.cmpy_previews_dir: |
| 882 |
return pcoll.cmpy_previews |
| 883 |
|
| 884 |
if directory and os.path.exists(directory): |
| 885 |
# Scan the directory for png files |
| 886 |
image_paths = [] |
| 887 |
for fn in os.listdir(directory): |
| 888 |
if fn.lower().endswith(".png"): |
| 889 |
image_paths.append(fn) |
| 890 |
|
| 891 |
for i, name in enumerate(image_paths): |
| 892 |
# generates a thumbnail preview for a file. |
| 893 |
filepath = os.path.join(directory, name) |
| 894 |
icon = pcoll.get(name) |
| 895 |
if not icon: |
| 896 |
thumb = pcoll.load(name, filepath, 'IMAGE') |
| 897 |
else: |
| 898 |
thumb = pcoll[name] |
| 899 |
enum_items.append((name, name, |
| 900 |
"Path Selection", thumb.icon_id, i)) |
| 901 |
|
| 902 |
sorted_data = sorted(enum_items, key=lambda x: x[0]) |
| 903 |
pcoll.cmpy_previews = sorted_data |
| 904 |
pcoll.cmpy_previews_dir = directory |
| 905 |
return pcoll.cmpy_previews |
| 906 |
|
| 907 |
|
| 908 |
class OBJECT_PT_CrownMoldingTools(Panel): |
| 909 |
bl_label = "Crown Molding Tools v3.1" |
| 910 |
bl_idname = "OBJECT_PT_crown_molding_tools" |
| 911 |
bl_space_type = "VIEW_3D" |
| 912 |
bl_region_type = "UI" |
| 913 |
bl_category = "Tool" # label in tab if different than defaults |
| 914 |
|
| 915 |
@classmethod |
| 916 |
def poll(self, context): |
| 917 |
return context.object is not None |
| 918 |
|
| 919 |
def draw(self, context): |
| 920 |
layout = self.layout |
| 921 |
scene = context.scene |
| 922 |
cmptool = scene.cmptool |
| 923 |
wm = context.window_manager |
| 924 |
|
| 925 |
if bpy.data.objects.get('Ceiling-Crown01') is not None: |
| 926 |
# ====== BEGIN IMAGE SECTION ====== |
| 927 |
row = layout.row() |
| 928 |
row.prop(cmptool, "curvecategory", text="Category") |
| 929 |
|
| 930 |
row = layout.row() |
| 931 |
row.template_icon_view(wm, "cmpy_previews") |
| 932 |
|
| 933 |
row = layout.row() |
| 934 |
row.prop(wm, "cmpy_previews", text="Path Selection") |
| 935 |
# ====== END IMAGE SECTION ====== |
| 936 |
row = layout.row() |
| 937 |
row.operator("wm.converttocmp") |
| 938 |
|
| 939 |
row = layout.row() |
| 940 |
row.operator("wm.changeselected") |
| 941 |
row.operator("wm.selallcmp") |
| 942 |
|
| 943 |
row = layout.row() |
| 944 |
row.operator("wm.cycleback") |
| 945 |
# developer / custom use only |
| 946 |
# row.operator("wm.cycleset") |
| 947 |
row.operator("wm.cycleforward") |
| 948 |
|
| 949 |
row = layout.row() |
| 950 |
row.operator("wm.tiltcurvenegative") |
| 951 |
row.operator("wm.tiltcurveflip") |
| 952 |
row.operator("wm.tiltcurvepositive") |
| 953 |
|
| 954 |
row = layout.row() |
| 955 |
row.operator("wm.mirrorcurvex") |
| 956 |
row.operator("wm.mirrorcurvey") |
| 957 |
row.operator("wm.mirrorcurvez") |
| 958 |
|
| 959 |
row = layout.row() |
| 960 |
row.operator("wm.scalecurvenegative") |
| 961 |
row.operator("wm.switchdirection") |
| 962 |
row.operator("wm.scalecurvepositive") |
| 963 |
|
| 964 |
row = layout.row() |
| 965 |
row.operator("wm.curve2d") |
| 966 |
row.operator("wm.curve3dtoggle2d") |
| 967 |
row.operator("wm.curve3d") |
| 968 |
|
| 969 |
row = layout.row() |
| 970 |
row.prop(cmptool, "advanced_rotate_curve") |
| 971 |
else: |
| 972 |
row = layout.row() |
| 973 |
row.label(icon="ERROR", text="Paths Not Appended") |
| 974 |
row = layout.row() |
| 975 |
row.operator("wm.appendcmp") |
| 976 |
|
| 977 |
box = layout.box() |
| 978 |
box.label(text="Support") |
| 979 |
column = box.column() |
| 980 |
|
| 981 |
row = column.row() |
| 982 |
row.scale_y = 1.2 |
| 983 |
row.operator("wm.url_open", text="Documentation", |
| 984 |
icon='WORLD').url = "https://jefftml.com/cmp3doc" |
| 985 |
|
| 986 |
|
| 987 |
# ------------------------------------------------------------------------ |
| 988 |
# Registration |
| 989 |
# ------------------------------------------------------------------------ |
| 990 |
|
| 991 |
classes = ( |
| 992 |
CrownMoldingProperties, |
| 993 |
OBJECT_PT_CrownMoldingTools, |
| 994 |
WM_OT_AllCurves2D, |
| 995 |
WM_OT_AllCurves3D, |
| 996 |
WM_OT_AppendCMP, |
| 997 |
WM_OT_ChangeSelected, |
| 998 |
WM_OT_ConvertToCMP, |
| 999 |
WM_OT_Curve3DToggle2D, |
| 1000 |
WM_OT_CycleBack, |
| 1001 |
WM_OT_CycleForward, |
| 1002 |
WM_OT_MirrorCurveX, |
| 1003 |
WM_OT_MirrorCurveY, |
| 1004 |
WM_OT_MirrorCurveZ, |
| 1005 |
WM_OT_ScaleCurveNegative, |
| 1006 |
WM_OT_ScaleCurvePositive, |
| 1007 |
WM_OT_SelectAllCMP, |
| 1008 |
WM_OT_SetOptions, |
| 1009 |
WM_OT_SwitchDirection, |
| 1010 |
WM_OT_TiltCurveFlip, |
| 1011 |
WM_OT_TiltCurveNegative, |
| 1012 |
WM_OT_TiltCurvePositive |
| 1013 |
) |
| 1014 |
|
| 1015 |
|
| 1016 |
def register(): |
| 1017 |
from bpy.utils import register_class |
| 1018 |
from bpy.types import WindowManager |
| 1019 |
from bpy.props import (StringProperty, EnumProperty) |
| 1020 |
|
| 1021 |
# ====== BEGIN IMAGE SECTION ====== |
| 1022 |
WindowManager.cmpy_previews_dir = StringProperty( |
| 1023 |
name="Folder Path", |
| 1024 |
subtype='DIR_PATH', |
| 1025 |
default="" |
| 1026 |
) |
| 1027 |
WindowManager.ceil_dir = StringProperty( |
| 1028 |
name="Ceiling Folder Path", |
| 1029 |
subtype='DIR_PATH', |
| 1030 |
default="" |
| 1031 |
) |
| 1032 |
WindowManager.flor_dir = StringProperty( |
| 1033 |
name="Floor Folder Path", |
| 1034 |
subtype='DIR_PATH', |
| 1035 |
default="" |
| 1036 |
) |
| 1037 |
WindowManager.corn_dir = StringProperty( |
| 1038 |
name="Corner Folder Path", |
| 1039 |
subtype='DIR_PATH', |
| 1040 |
default="" |
| 1041 |
) |
| 1042 |
WindowManager.panl_dir = StringProperty( |
| 1043 |
name="Panel Folder Path", |
| 1044 |
subtype='DIR_PATH', |
| 1045 |
default="" |
| 1046 |
) |
| 1047 |
WindowManager.picf_dir = StringProperty( |
| 1048 |
name="Picture Frame Folder Path", |
| 1049 |
subtype='DIR_PATH', |
| 1050 |
default="" |
| 1051 |
) |
| 1052 |
WindowManager.rail_dir = StringProperty( |
| 1053 |
name="Rail Folder Path", |
| 1054 |
subtype='DIR_PATH', |
| 1055 |
default="" |
| 1056 |
) |
| 1057 |
WindowManager.wall_dir = StringProperty( |
| 1058 |
name="Wall Folder Path", |
| 1059 |
subtype='DIR_PATH', |
| 1060 |
default="" |
| 1061 |
) |
| 1062 |
WindowManager.wind_dir = StringProperty( |
| 1063 |
name="Window Folder Path", |
| 1064 |
subtype='DIR_PATH', |
| 1065 |
default="" |
| 1066 |
) |
| 1067 |
|
| 1068 |
WindowManager.cmpy_previews = EnumProperty( |
| 1069 |
items=enum_previews_from_directory_items |
| 1070 |
) |
| 1071 |
import bpy.utils.previews |
| 1072 |
pcoll = bpy.utils.previews.new() |
| 1073 |
pcoll.cmpy_previews_dir = "" |
| 1074 |
pcoll.cmpy_previews = () |
| 1075 |
|
| 1076 |
preview_collections["main"] = pcoll |
| 1077 |
# ====== END IMAGE SECTION ====== |
| 1078 |
|
| 1079 |
# registers list from above |
| 1080 |
for cls in classes: |
| 1081 |
register_class(cls) |
| 1082 |
|
| 1083 |
bpy.types.Scene.cmptool = PointerProperty(type=CrownMoldingProperties) |
| 1084 |
|
| 1085 |
|
| 1086 |
def unregister(): |
| 1087 |
from bpy.utils import unregister_class |
| 1088 |
from bpy.types import WindowManager |
| 1089 |
|
| 1090 |
# ====== BEGIN IMAGE SECTION ====== |
| 1091 |
del WindowManager.cmpy_previews |
| 1092 |
for pcoll in preview_collections.values(): |
| 1093 |
bpy.utils.previews.remove(pcoll) |
| 1094 |
preview_collections.clear() |
| 1095 |
# ====== END IMAGE SECTION ====== |
| 1096 |
|
| 1097 |
# unregisters list from above |
| 1098 |
for cls in reversed(classes): |
| 1099 |
unregister_class(cls) |
| 1100 |
del bpy.types.Scene.cmptool |
| 1101 |
|
| 1102 |
|
| 1103 |
if __name__ == "__main__": |
| 1104 |
# The path of this text (if saved) |
| 1105 |
__file__ = bpy.context.space_data.text.filepath |
| 1106 |
register() |
| 1107 |
|