Newer
Older
def execute(self, context):
ob = context.active_object
mesh = ob.data
for edge in mesh.edges:
edge.use_seam = False
mesh.paper_island_list.clear()
return {'FINISHED'}
def page_size_preset_changed(self, context):
"""Update the actual document size to correct values"""
if hasattr(self, "limit_by_page") and not self.limit_by_page:
return
2017
2018
2019
2020
2021
2022
2023
2024
2025
2026
2027
2028
2029
2030
2031
2032
2033
2034
2035
2036
2037
2038
if self.page_size_preset == 'A4':
self.output_size_x = 0.210
self.output_size_y = 0.297
elif self.page_size_preset == 'A3':
self.output_size_x = 0.297
self.output_size_y = 0.420
elif self.page_size_preset == 'US_LETTER':
self.output_size_x = 0.216
self.output_size_y = 0.279
elif self.page_size_preset == 'US_LEGAL':
self.output_size_x = 0.216
self.output_size_y = 0.356
class PaperModelStyle(bpy.types.PropertyGroup):
line_styles = [
('SOLID', "Solid (----)", "Solid line"),
('DOT', "Dots (. . .)", "Dotted line"),
('DASH', "Short Dashes (- - -)", "Solid line"),
('LONGDASH', "Long Dashes (-- --)", "Solid line"),
('DASHDOT', "Dash-dotted (-- .)", "Solid line")
]
outer_color: bpy.props.FloatVectorProperty(
name="Outer Lines", description="Color of net outline",
default=(0.0, 0.0, 0.0, 1.0), min=0, max=1, subtype='COLOR', size=4)
outer_style: bpy.props.EnumProperty(
name="Outer Lines Drawing Style", description="Drawing style of net outline",
default='SOLID', items=line_styles)
line_width: bpy.props.FloatProperty(
name="Base Lines Thickness", description="Base thickness of net lines, each actual value is a multiple of this length",
default=1e-4, min=0, soft_max=5e-3, precision=5, step=1e-2, subtype="UNSIGNED", unit="LENGTH")
outer_width: bpy.props.FloatProperty(
name="Outer Lines Thickness", description="Relative thickness of net outline",
default=3, min=0, soft_max=10, precision=1, step=10, subtype='FACTOR')
use_outbg: bpy.props.BoolProperty(
name="Highlight Outer Lines", description="Add another line below every line to improve contrast",
outbg_color: bpy.props.FloatVectorProperty(
name="Outer Highlight", description="Color of the highlight for outer lines",
default=(1.0, 1.0, 1.0, 1.0), min=0, max=1, subtype='COLOR', size=4)
outbg_width: bpy.props.FloatProperty(
name="Outer Highlight Thickness", description="Relative thickness of the highlighting lines",
default=5, min=0, soft_max=10, precision=1, step=10, subtype='FACTOR')
convex_color: bpy.props.FloatVectorProperty(
name="Inner Convex Lines", description="Color of lines to be folded to a convex angle",
default=(0.0, 0.0, 0.0, 1.0), min=0, max=1, subtype='COLOR', size=4)
convex_style: bpy.props.EnumProperty(
name="Convex Lines Drawing Style", description="Drawing style of lines to be folded to a convex angle",
default='DASH', items=line_styles)
convex_width: bpy.props.FloatProperty(
name="Convex Lines Thickness", description="Relative thickness of concave lines",
default=2, min=0, soft_max=10, precision=1, step=10, subtype='FACTOR')
concave_color: bpy.props.FloatVectorProperty(
name="Inner Concave Lines", description="Color of lines to be folded to a concave angle",
default=(0.0, 0.0, 0.0, 1.0), min=0, max=1, subtype='COLOR', size=4)
concave_style: bpy.props.EnumProperty(
name="Concave Lines Drawing Style", description="Drawing style of lines to be folded to a concave angle",
default='DASHDOT', items=line_styles)
concave_width: bpy.props.FloatProperty(
name="Concave Lines Thickness", description="Relative thickness of concave lines",
default=2, min=0, soft_max=10, precision=1, step=10, subtype='FACTOR')
freestyle_color: bpy.props.FloatVectorProperty(
name="Freestyle Edges", description="Color of lines marked as Freestyle Edge",
default=(0.0, 0.0, 0.0, 1.0), min=0, max=1, subtype='COLOR', size=4)
freestyle_style: bpy.props.EnumProperty(
name="Freestyle Edges Drawing Style", description="Drawing style of Freestyle Edges",
default='SOLID', items=line_styles)
freestyle_width: bpy.props.FloatProperty(
name="Freestyle Edges Thickness", description="Relative thickness of Freestyle edges",
default=2, min=0, soft_max=10, precision=1, step=10, subtype='FACTOR')
use_inbg: bpy.props.BoolProperty(
name="Highlight Inner Lines", description="Add another line below every line to improve contrast",
inbg_color: bpy.props.FloatVectorProperty(
name="Inner Highlight", description="Color of the highlight for inner lines",
default=(1.0, 1.0, 1.0, 1.0), min=0, max=1, subtype='COLOR', size=4)
inbg_width: bpy.props.FloatProperty(
name="Inner Highlight Thickness", description="Relative thickness of the highlighting lines",
default=2, min=0, soft_max=10, precision=1, step=10, subtype='FACTOR')
sticker_fill: bpy.props.FloatVectorProperty(
name="Tabs Fill", description="Fill color of sticking tabs",
default=(0.9, 0.9, 0.9, 1.0), min=0, max=1, subtype='COLOR', size=4)
text_color: bpy.props.FloatVectorProperty(
name="Text Color", description="Color of all text used in the document",
default=(0.0, 0.0, 0.0, 1.0), min=0, max=1, subtype='COLOR', size=4)
bpy.utils.register_class(PaperModelStyle)
class ExportPaperModel(bpy.types.Operator):
"""Blender Operator: save the selected object's net and optionally bake its texture"""
bl_idname = "export_mesh.paper_model"
bl_label = "Export Paper Model"
bl_description = "Export the selected object's net and optionally bake its texture"
filepath: bpy.props.StringProperty(
name="File Path", description="Target file to save the SVG", options={'SKIP_SAVE'})
filename: bpy.props.StringProperty(
name="File Name", description="Name of the file", options={'SKIP_SAVE'})
directory: bpy.props.StringProperty(
name="Directory", description="Directory of the file", options={'SKIP_SAVE'})
page_size_preset: bpy.props.EnumProperty(
name="Page Size", description="Size of the exported document",
default='A4', update=page_size_preset_changed, items=global_paper_sizes)
output_size_x: bpy.props.FloatProperty(
name="Page Width", description="Width of the exported document",
default=0.210, soft_min=0.105, soft_max=0.841, subtype="UNSIGNED", unit="LENGTH")
output_size_y: bpy.props.FloatProperty(
name="Page Height", description="Height of the exported document",
default=0.297, soft_min=0.148, soft_max=1.189, subtype="UNSIGNED", unit="LENGTH")
output_margin: bpy.props.FloatProperty(
name="Page Margin", description="Distance from page borders to the printable area",
default=0.005, min=0, soft_max=0.1, step=0.1, subtype="UNSIGNED", unit="LENGTH")
output_type: bpy.props.EnumProperty(
name="Textures", description="Source of a texture for the model",
default='NONE', items=[
('NONE', "No Texture", "Export the net only"),
('TEXTURE', "From Materials", "Render the diffuse color and all painted textures"),
('AMBIENT_OCCLUSION', "Ambient Occlusion", "Render the Ambient Occlusion pass"),
('RENDER', "Full Render", "Render the material in actual scene illumination"),
('SELECTED_TO_ACTIVE', "Selected to Active", "Render all selected surrounding objects as a texture")
])
do_create_stickers: bpy.props.BoolProperty(
name="Create Tabs", description="Create gluing tabs around the net (useful for paper)",
do_create_numbers: bpy.props.BoolProperty(
name="Create Numbers", description="Enumerate edges to make it clear which edges should be sticked together",
sticker_width: bpy.props.FloatProperty(
name="Tabs and Text Size", description="Width of gluing tabs and their numbers",
default=0.005, soft_min=0, soft_max=0.05, step=0.1, subtype="UNSIGNED", unit="LENGTH")
angle_epsilon: bpy.props.FloatProperty(
name="Hidden Edge Angle", description="Folds with angle below this limit will not be drawn",
default=pi/360, min=0, soft_max=pi/4, step=0.01, subtype="ANGLE", unit="ROTATION")
output_dpi: bpy.props.FloatProperty(
name="Resolution (DPI)", description="Resolution of images in pixels per inch",
default=90, min=1, soft_min=30, soft_max=600, subtype="UNSIGNED")
file_format: bpy.props.EnumProperty(
name="Document Format", description="File format of the exported net",
default='PDF', items=[
('PDF', "PDF", "Adobe Portable Document Format 1.4"),
('SVG', "SVG", "W3C Scalable Vector Graphics"),
])
image_packing: bpy.props.EnumProperty(
name="Image Packing Method", description="Method of attaching baked image(s) to the SVG",
default='ISLAND_EMBED', items=[
('PAGE_LINK', "Single Linked", "Bake one image per page of output and save it separately"),
('ISLAND_LINK', "Linked", "Bake images separately for each island and save them in a directory"),
('ISLAND_EMBED', "Embedded", "Bake images separately for each island and embed them into the SVG")
])
name="Scale", description="Divisor of all dimensions when exporting",
default=1, soft_min=1.0, soft_max=10000.0, step=100, subtype='UNSIGNED', precision=1)
do_create_uvmap: bpy.props.BoolProperty(
name="Create UVMap", description="Create a new UV Map showing the islands and page layout",
default=False, options={'SKIP_SAVE'})
ui_expanded_document: bpy.props.BoolProperty(
name="Show Document Settings Expanded", description="Shows the box 'Document Settings' expanded in user interface",
default=True, options={'SKIP_SAVE'})
ui_expanded_style: bpy.props.BoolProperty(
name="Show Style Settings Expanded", description="Shows the box 'Colors and Style' expanded in user interface",
default=False, options={'SKIP_SAVE'})
style: bpy.props.PointerProperty(type=PaperModelStyle)
2181
2182
2183
2184
2185
2186
2187
2188
2189
2190
2191
2192
2193
2194
2195
2196
2197
2198
2199
2200
2201
2202
2203
2204
2205
2206
2207
2208
2209
2210
2211
2212
2213
2214
2215
2216
2217
unfolder = None
largest_island_ratio = 0
@classmethod
def poll(cls, context):
return context.active_object and context.active_object.type == 'MESH'
def execute(self, context):
try:
if self.object.data.paper_island_list:
self.unfolder.copy_island_names(self.object.data.paper_island_list)
self.unfolder.save(self.properties)
self.report({'INFO'}, "Saved a {}-page document".format(len(self.unfolder.mesh.pages)))
return {'FINISHED'}
except UnfoldError as error:
self.report(type={'ERROR_INVALID_INPUT'}, message=error.args[0])
return {'CANCELLED'}
def get_scale_ratio(self, sce):
margin = self.output_margin + self.sticker_width + 1e-5
if min(self.output_size_x, self.output_size_y) <= 2 * margin:
return False
output_inner_size = M.Vector((self.output_size_x - 2*margin, self.output_size_y - 2*margin))
ratio = self.unfolder.mesh.largest_island_ratio(output_inner_size)
return ratio * sce.unit_settings.scale_length / self.scale
def invoke(self, context, event):
sce = context.scene
recall_mode = context.object.mode
bpy.ops.object.mode_set(mode='OBJECT')
self.scale = sce.paper_model.scale
self.object = context.active_object
cage_size = M.Vector((sce.paper_model.output_size_x, sce.paper_model.output_size_y)) if sce.paper_model.limit_by_page else None
try:
self.unfolder = Unfolder(self.object)
self.unfolder.prepare(
cage_size, create_uvmap=self.do_create_uvmap,
scale=sce.unit_settings.scale_length/self.scale)
except UnfoldError as error:
self.report(type={'ERROR_INVALID_INPUT'}, message=error.args[0])
bpy.ops.object.mode_set(mode=recall_mode)
return {'CANCELLED'}
scale_ratio = self.get_scale_ratio(sce)
if scale_ratio > 1:
self.scale = ceil(self.scale * scale_ratio)
wm = context.window_manager
wm.fileselect_add(self)
bpy.ops.object.mode_set(mode=recall_mode)
return {'RUNNING_MODAL'}
def draw(self, context):
layout = self.layout
layout.prop(self.properties, "do_create_uvmap")
row = layout.row(align=True)
row.menu("VIEW3D_MT_paper_model_presets", text=bpy.types.VIEW3D_MT_paper_model_presets.bl_label)
row.operator("export_mesh.paper_model_preset_add", text="", icon='ADD')
row.operator("export_mesh.paper_model_preset_add", text="", icon='REMOVE').remove_active = True
# a little hack: this prints out something like "Scale: 1: 72"
layout.prop(self.properties, "scale", text="Scale: 1")
scale_ratio = self.get_scale_ratio(context.scene)
if scale_ratio > 1:
layout.label(
text="An island is roughly {:.1f}x bigger than page".format(scale_ratio),
icon="ERROR")
elif scale_ratio > 0:
layout.label(text="Largest island is roughly 1/{:.1f} of page".format(1 / scale_ratio))
if context.scene.unit_settings.scale_length != 1:
layout.label(
text="Unit scale {:.1f} makes page size etc. not display correctly".format(
context.scene.unit_settings.scale_length), icon="ERROR")
box = layout.box()
row = box.row(align=True)
row.prop(
self.properties, "ui_expanded_document", text="",
2262
2263
2264
2265
2266
2267
2268
2269
2270
2271
2272
2273
2274
2275
2276
2277
2278
2279
2280
2281
2282
2283
2284
2285
2286
2287
2288
2289
2290
2291
2292
2293
2294
icon=('TRIA_DOWN' if self.ui_expanded_document else 'TRIA_RIGHT'), emboss=False)
row.label(text="Document Settings")
if self.ui_expanded_document:
box.prop(self.properties, "file_format", text="Format")
box.prop(self.properties, "page_size_preset")
col = box.column(align=True)
col.active = self.page_size_preset == 'USER'
col.prop(self.properties, "output_size_x")
col.prop(self.properties, "output_size_y")
box.prop(self.properties, "output_margin")
col = box.column()
col.prop(self.properties, "do_create_stickers")
col.prop(self.properties, "do_create_numbers")
col = box.column()
col.active = self.do_create_stickers or self.do_create_numbers
col.prop(self.properties, "sticker_width")
box.prop(self.properties, "angle_epsilon")
box.prop(self.properties, "output_type")
col = box.column()
col.active = (self.output_type != 'NONE')
if len(self.object.data.uv_textures) == 8:
col.label(text="No UV slots left, No Texture is the only option.", icon='ERROR')
elif context.scene.render.engine not in ('BLENDER_RENDER', 'CYCLES') and self.output_type != 'NONE':
col.label(text="Blender Internal engine will be used for texture baking.", icon='ERROR')
col.prop(self.properties, "output_dpi")
row = col.row()
row.active = self.file_format == 'SVG'
row.prop(self.properties, "image_packing", text="Images")
box = layout.box()
row = box.row(align=True)
row.prop(
self.properties, "ui_expanded_style", text="",
2297
2298
2299
2300
2301
2302
2303
2304
2305
2306
2307
2308
2309
2310
2311
2312
2313
2314
2315
2316
2317
2318
2319
2320
2321
2322
2323
2324
2325
2326
2327
2328
2329
2330
2331
2332
2333
2334
2335
2336
2337
2338
2339
2340
2341
2342
2343
2344
2345
2346
2347
2348
2349
2350
2351
2352
2353
2354
2355
2356
2357
2358
2359
icon=('TRIA_DOWN' if self.ui_expanded_style else 'TRIA_RIGHT'), emboss=False)
row.label(text="Colors and Style")
if self.ui_expanded_style:
box.prop(self.style, "line_width", text="Default line width")
col = box.column()
col.prop(self.style, "outer_color")
col.prop(self.style, "outer_width", text="Relative width")
col.prop(self.style, "outer_style", text="Style")
col = box.column()
col.active = self.output_type != 'NONE'
col.prop(self.style, "use_outbg", text="Outer Lines Highlight:")
sub = col.column()
sub.active = self.output_type != 'NONE' and self.style.use_outbg
sub.prop(self.style, "outbg_color", text="")
sub.prop(self.style, "outbg_width", text="Relative width")
col = box.column()
col.prop(self.style, "convex_color")
col.prop(self.style, "convex_width", text="Relative width")
col.prop(self.style, "convex_style", text="Style")
col = box.column()
col.prop(self.style, "concave_color")
col.prop(self.style, "concave_width", text="Relative width")
col.prop(self.style, "concave_style", text="Style")
col = box.column()
col.prop(self.style, "freestyle_color")
col.prop(self.style, "freestyle_width", text="Relative width")
col.prop(self.style, "freestyle_style", text="Style")
col = box.column()
col.active = self.output_type != 'NONE'
col.prop(self.style, "use_inbg", text="Inner Lines Highlight:")
sub = col.column()
sub.active = self.output_type != 'NONE' and self.style.use_inbg
sub.prop(self.style, "inbg_color", text="")
sub.prop(self.style, "inbg_width", text="Relative width")
col = box.column()
col.active = self.do_create_stickers
col.prop(self.style, "sticker_fill")
box.prop(self.style, "text_color")
def menu_func(self, context):
self.layout.operator("export_mesh.paper_model", text="Paper Model (.svg)")
class VIEW3D_MT_paper_model_presets(bpy.types.Menu):
bl_label = "Paper Model Presets"
preset_subdir = "export_mesh"
preset_operator = "script.execute_preset"
draw = bpy.types.Menu.draw_preset
class AddPresetPaperModel(bl_operators.presets.AddPresetBase, bpy.types.Operator):
"""Add or remove a Paper Model Preset"""
bl_idname = "export_mesh.paper_model_preset_add"
bl_label = "Add Paper Model Preset"
preset_menu = "VIEW3D_MT_paper_model_presets"
preset_subdir = "export_mesh"
preset_defines = ["op = bpy.context.active_operator"]
@property
def preset_values(self):
op = bpy.ops.export_mesh.paper_model
properties = op.get_rna_type().properties.items()
blacklist = bpy.types.Operator.bl_rna.properties.keys()
return [
"op.{}".format(prop_id) for (prop_id, prop) in properties
if not (prop.is_hidden or prop.is_skip_save or prop_id in blacklist)]
class VIEW3D_PT_paper_model_tools(bpy.types.Panel):
bl_label = "Tools"
bl_space_type = "VIEW_3D"
bl_region_type = "TOOLS"
bl_category = "Paper Model"
def draw(self, context):
layout = self.layout
sce = context.scene
obj = context.active_object
mesh = obj.data if obj and obj.type == 'MESH' else None
layout.operator("export_mesh.paper_model")
col = layout.column(align=True)
col.operator("mesh.unfold")
if context.mode == 'EDIT_MESH':
row = layout.row(align=True)
row.operator("mesh.mark_seam", text="Mark Seam").clear = False
row.operator("mesh.mark_seam", text="Clear Seam").clear = True
else:
layout.operator("mesh.clear_all_seams")
props = sce.paper_model
layout.prop(props, "scale", text="Model Scale: 1")
layout.prop(props, "limit_by_page")
col = layout.column()
col.active = props.limit_by_page
col.prop(props, "page_size_preset")
sub = col.column(align=True)
sub.active = props.page_size_preset == 'USER'
sub.prop(props, "output_size_x")
sub.prop(props, "output_size_y")
class VIEW3D_PT_paper_model_islands(bpy.types.Panel):
bl_label = "Islands"
bl_space_type = "VIEW_3D"
bl_region_type = "TOOLS"
bl_category = "Paper Model"
def draw(self, context):
layout = self.layout
sce = context.scene
obj = context.active_object
mesh = obj.data if obj and obj.type == 'MESH' else None
if mesh and mesh.paper_island_list:
layout.label(
text="1 island:" if len(mesh.paper_island_list) == 1 else
"{} islands:".format(len(mesh.paper_island_list)))
layout.template_list(
'UI_UL_list', 'paper_model_island_list', mesh,
'paper_island_list', mesh, 'paper_island_index', rows=1, maxrows=5)
if mesh.paper_island_index >= 0:
list_item = mesh.paper_island_list[mesh.paper_island_index]
sub = layout.column(align=True)
sub.prop(list_item, "auto_label")
sub.prop(list_item, "label")
sub.prop(list_item, "auto_abbrev")
row = sub.row()
row.active = not list_item.auto_abbrev
row.prop(list_item, "abbreviation")
else:
layout.label(text="Not unfolded")
layout.box().label(text="Use the 'Unfold' tool")
2436
2437
2438
2439
2440
2441
2442
2443
2444
2445
2446
2447
2448
2449
2450
2451
2452
2453
2454
2455
2456
2457
2458
2459
2460
2461
2462
2463
2464
2465
2466
2467
2468
2469
2470
2471
2472
2473
2474
2475
2476
2477
2478
2479
2480
2481
2482
2483
sub = layout.column(align=True)
sub.active = bool(mesh and mesh.paper_island_list)
sub.prop(sce.paper_model, "display_islands", icon='RESTRICT_VIEW_OFF')
row = sub.row(align=True)
row.active = bool(sce.paper_model.display_islands and mesh and mesh.paper_island_list)
row.prop(sce.paper_model, "islands_alpha", slider=True)
def display_islands(self, context):
# TODO: save the vertex positions and don't recalculate them always?
ob = context.active_object
if not ob or ob.type != 'MESH':
return
mesh = ob.data
if not mesh.paper_island_list or mesh.paper_island_index == -1:
return
bgl.glMatrixMode(bgl.GL_PROJECTION)
perspMatrix = context.space_data.region_3d.perspective_matrix
perspBuff = bgl.Buffer(bgl.GL_FLOAT, (4, 4), perspMatrix.transposed())
bgl.glLoadMatrixf(perspBuff)
bgl.glMatrixMode(bgl.GL_MODELVIEW)
objectBuff = bgl.Buffer(bgl.GL_FLOAT, (4, 4), ob.matrix_world.transposed())
bgl.glLoadMatrixf(objectBuff)
bgl.glEnable(bgl.GL_BLEND)
bgl.glBlendFunc(bgl.GL_SRC_ALPHA, bgl.GL_ONE_MINUS_SRC_ALPHA)
bgl.glEnable(bgl.GL_POLYGON_OFFSET_FILL)
bgl.glPolygonOffset(0, -10) # offset in Zbuffer to remove flicker
bgl.glPolygonMode(bgl.GL_FRONT_AND_BACK, bgl.GL_FILL)
bgl.glColor4f(1.0, 0.4, 0.0, self.islands_alpha)
island = mesh.paper_island_list[mesh.paper_island_index]
for lface in island.faces:
face = mesh.polygons[lface.id]
bgl.glBegin(bgl.GL_POLYGON)
for vertex_id in face.vertices:
vertex = mesh.vertices[vertex_id]
bgl.glVertex4f(*vertex.co.to_4d())
bgl.glEnd()
bgl.glPolygonOffset(0.0, 0.0)
bgl.glDisable(bgl.GL_POLYGON_OFFSET_FILL)
bgl.glLoadIdentity()
display_islands.handle = None
def display_islands_changed(self, context):
"""Switch highlighting islands on/off"""
if self.display_islands:
if not display_islands.handle:
display_islands.handle = bpy.types.SpaceView3D.draw_handler_add(
display_islands, (self, context), 'WINDOW', 'POST_VIEW')
else:
if display_islands.handle:
bpy.types.SpaceView3D.draw_handler_remove(display_islands.handle, 'WINDOW')
display_islands.handle = None
def label_changed(self, context):
"""The label of an island was changed"""
# accessing properties via [..] to avoid a recursive call after the update
self["auto_label"] = not self.label or self.label.isspace()
island_item_changed(self, context)
def island_item_changed(self, context):
"""The labelling of an island was changed"""
def increment(abbrev, collisions):
letters = "ABCDEFGHIJKLMNPQRSTUVWXYZ123456789"
while abbrev in collisions:
abbrev = abbrev.rstrip(letters[-1])
abbrev = abbrev[:2] + letters[letters.find(abbrev[-1]) + 1 if len(abbrev) == 3 else 0]
return abbrev
# accessing properties via [..] to avoid a recursive call after the update
island_list = context.active_object.data.paper_island_list
if self.auto_label:
self["label"] = "" # avoid self-conflict
number = 1
while any(item.label == "Island {}".format(number) for item in island_list):
number += 1
self["label"] = "Island {}".format(number)
if self.auto_abbrev:
self["abbreviation"] = "" # avoid self-conflict
abbrev = "".join(first_letters(self.label))[:3].upper()
self["abbreviation"] = increment(abbrev, {item.abbreviation for item in island_list})
elif len(self.abbreviation) > 3:
self["abbreviation"] = self.abbreviation[:3]
self.name = "[{}] {} ({} {})".format(
self.abbreviation, self.label, len(self.faces), "faces" if len(self.faces) > 1 else "face")
class FaceList(bpy.types.PropertyGroup):
id: bpy.props.IntProperty(name="Face ID")
bpy.utils.register_class(FaceList)
class IslandList(bpy.types.PropertyGroup):
faces: bpy.props.CollectionProperty(
name="Faces", description="Faces belonging to this island", type=FaceList)
label: bpy.props.StringProperty(
name="Label", description="Label on this island",
default="", update=label_changed)
abbreviation: bpy.props.StringProperty(
name="Abbreviation", description="Three-letter label to use when there is not enough space",
default="", update=island_item_changed)
auto_label: bpy.props.BoolProperty(
name="Auto Label", description="Generate the label automatically",
default=True, update=island_item_changed)
auto_abbrev: bpy.props.BoolProperty(
name="Auto Abbreviation", description="Generate the abbreviation automatically",
default=True, update=island_item_changed)
bpy.utils.register_class(IslandList)
class PaperModelSettings(bpy.types.PropertyGroup):
display_islands: bpy.props.BoolProperty(
name="Highlight selected island", description="Highlight faces corresponding to the selected island in the 3D View",
options={'SKIP_SAVE'}, update=display_islands_changed)
islands_alpha: bpy.props.FloatProperty(
name="Opacity", description="Opacity of island highlighting",
min=0.0, max=1.0, default=0.3)
limit_by_page: bpy.props.BoolProperty(
name="Limit Island Size", description="Do not create islands larger than given dimensions",
default=False, update=page_size_preset_changed)
page_size_preset: bpy.props.EnumProperty(
name="Page Size", description="Maximal size of an island",
default='A4', update=page_size_preset_changed, items=global_paper_sizes)
output_size_x: bpy.props.FloatProperty(
name="Width", description="Maximal width of an island",
default=0.2, soft_min=0.105, soft_max=0.841, subtype="UNSIGNED", unit="LENGTH")
output_size_y: bpy.props.FloatProperty(
name="Height", description="Maximal height of an island",
default=0.29, soft_min=0.148, soft_max=1.189, subtype="UNSIGNED", unit="LENGTH")
name="Scale", description="Divisor of all dimensions when exporting",
default=1, soft_min=1.0, soft_max=10000.0, step=100, subtype='UNSIGNED', precision=1)
bpy.utils.register_class(PaperModelSettings)
def register():
bpy.utils.register_module(__name__)
bpy.types.Scene.paper_model = bpy.props.PointerProperty(
name="Paper Model", description="Settings of the Export Paper Model script",
type=PaperModelSettings, options={'SKIP_SAVE'})
bpy.types.Mesh.paper_island_list = bpy.props.CollectionProperty(
name="Island List", type=IslandList)
bpy.types.Mesh.paper_island_index = bpy.props.IntProperty(
name="Island List Index",
default=-1, min=-1, max=100, options={'SKIP_SAVE'})
Brecht Van Lommel
committed
bpy.types.TOPBAR_MT_file_export.append(menu_func)
def unregister():
bpy.utils.unregister_module(__name__)
Brecht Van Lommel
committed
bpy.types.TOPBAR_MT_file_export.remove(menu_func)
if display_islands.handle:
bpy.types.SpaceView3D.draw_handler_remove(display_islands.handle, 'WINDOW')
display_islands.handle = None
if __name__ == "__main__":
register()