collection_manager: add to contrib. Task: T69577
[blender-addons-contrib.git] / render_to_print.py
1 # ##### BEGIN GPL LICENSE BLOCK #####
2 #
3 #  This program is free software; you can 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.
7 #
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.
12 #
13 #  You should have received a copy of the GNU General Public License
14 #  along with this program; if not, write to the Free Software Foundation,
15 #  Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
16 #
17 # ##### END GPL LICENSE BLOCK #####
18
19 # <pep8 compliant>
20
21 bl_info = {
22     "name": "Render to Print",
23     "author": "Marco Crippa <thekrypt77@tiscali.it>, Dealga McArdle, zebus3d",
24     "version": (0, 3, 0),
25     "blender": (2, 80, 0),
26     "location": "Output > Render to Print",
27     "description": "Set the size of the render for a print",
28     "wiki_url": "http://wiki.blender.org/index.php/Extensions:2.6/ \
29                 Py/Scripts/Render/Render to Print",
30     "tracker_url": "https://developer.blender.org/maniphest/task/edit/form/2/",
31     "category": "Render"}
32
33
34 import math
35 import bpy
36 from bpy.types import Panel, Operator, Scene, PropertyGroup
37 from bpy.props import (
38                         IntProperty,
39                         FloatProperty,
40                         StringProperty,
41                         EnumProperty,
42                         PointerProperty,
43                     )
44
45
46 paper_presets = (
47     ("custom_1_1", "custom", ""),
48     ("A0_84.1_118.9", "A0 (84.1x118.9 cm)", ""),
49     ("A1_59.4_84.1", "A1 (59.4x84.1 cm)", ""),
50     ("A2_42.0_59.4", "A2 (42.0x59.4 cm)", ""),
51     ("A3_29.7_42.0", "A3 (29.7 42.0 cm)", ""),
52     ("A4_21.0_29.7", "A4 (21.0x29.7 cm)", ""),
53     ("A5_14.8_21.0", "A5 (14.8x21.0 cm)", ""),
54     ("A6_10.5_14.8", "A6 (10.5x14.8 cm)", ""),
55     ("A7_7.4_10.5", "A7 (7.4x10.5 cm)", ""),
56     ("A8_5.2_7.4", "A8 (5.2x7.4 cm)", ""),
57     ("A9_3.7_5.2", "A9 (3.7x5.2 cm)", ""),
58     ("A10_2.6_3.7", "A10 (2.6x3.7 cm)", ""),
59
60     ("B0_100.0_141.4", "B0 (100.0x141.4 cm)", ""),
61     ("B1_70.7_100.0", "B1 (70.7x100.0 cm)", ""),
62     ("B2_50.0_70.7", "B2 (50.0x70.7 cm)", ""),
63     ("B3_35.3_50.0", "B3 (35.3x50.0 cm)", ""),
64     ("B4_25.0_35.3", "B4 (25.0x35.3 cm)", ""),
65     ("B5_17.6_25.0", "B5 (17.6x25.0 cm)", ""),
66     ("B6_12.5_17.6", "B6 (12.5x17.6 cm)", ""),
67     ("B7_8.8_12.5", "B7 (8.8x12.5 cm)", ""),
68     ("B8_6.2_8.8", "B8 (6.2x8.8 cm)", ""),
69     ("B9_4.4_6.2", "B9 (4.4x6.2 cm)", ""),
70     ("B10_3.1_4.4", "B10 (3.1x4.4 cm)", ""),
71
72     ("C0_91.7_129.7", "C0 (91.7x129.7 cm)", ""),
73     ("C1_64.8_91.7", "C1 (64.8x91.7 cm)", ""),
74     ("C2_45.8_64.8", "C2 (45.8x64.8 cm)", ""),
75     ("C3_32.4_45.8", "C3 (32.4x45.8 cm)", ""),
76     ("C4_22.9_32.4", "C4 (22.9x32.4 cm)", ""),
77     ("C5_16.2_22.9", "C5 (16.2x22.9 cm)", ""),
78     ("C6_11.4_16.2", "C6 (11.4x16.2 cm)", ""),
79     ("C7_8.1_11.4", "C7 (8.1x11.4 cm)", ""),
80     ("C8_5.7_8.1", "C8 (5.7x8.1 cm)", ""),
81     ("C9_4.0_5.7", "C9 (4.0x5.7 cm)", ""),
82     ("C10_2.8_4.0", "C10 (2.8x4.0 cm)", ""),
83
84     ("Letter_21.6_27.9", "Letter (21.6x27.9 cm)", ""),
85     ("Legal_21.6_35.6", "Legal (21.6x35.6 cm)", ""),
86     ("Legal junior_20.3_12.7", "Legal junior (20.3x12.7 cm)", ""),
87     ("Ledger_43.2_27.9", "Ledger (43.2x27.9 cm)", ""),
88     ("Tabloid_27.9_43.2", "Tabloid (27.9x43.2 cm)", ""),
89
90     ("ANSI C_43.2_55.9", "ANSI C (43.2x55.9 cm)", ""),
91     ("ANSI D_55.9_86.4", "ANSI D (55.9x86.4 cm)", ""),
92     ("ANSI E_86.4_111.8", "ANSI E (86.4x111.8 cm)", ""),
93
94     ("Arch A_22.9_30.5", "Arch A (22.9x30.5 cm)", ""),
95     ("Arch B_30.5_45.7", "Arch B (30.5x45.7 cm)", ""),
96     ("Arch C_45.7_61.0", "Arch C (45.7x61.0 cm)", ""),
97     ("Arch D_61.0_91.4", "Arch D (61.0x91.4 cm)", ""),
98     ("Arch E_91.4_121.9", "Arch E (91.4x121.9 cm)", ""),
99     ("Arch E1_76.2_106.7", "Arch E1 (76.2x106.7 cm)", ""),
100     ("Arch E2_66.0_96.5", "Arch E2 (66.0x96.5 cm)", ""),
101     ("Arch E3_68.6_99.1", "Arch E3 (68.6x99.1 cm)", ""),
102     )
103
104
105 def paper_enum_parse(idname):
106     tipo, dim_w, dim_h = idname.split("_")
107     return tipo, float(dim_w), float(dim_h)
108
109
110 paper_presets_data = {
111     idname: paper_enum_parse(idname)
112     for idname, name, descr in paper_presets
113     }
114
115
116 def update_settings_cb(self, context):
117     # annoying workaround for recursive call
118     if update_settings_cb.level is False:
119         update_settings_cb.level = True
120         pixels_from_print(self)
121         update_settings_cb.level = False
122
123 update_settings_cb.level = False
124
125
126 class RenderPrintSertings(PropertyGroup):
127     unit_from: EnumProperty(
128             name="Set from",
129             description="Set from",
130             items=(
131                 ("CM_TO_PIXELS", "CM -> Pixel", "Centermeters to Pixels"),
132                 ("PIXELS_TO_CM", "Pixel -> CM", "Pixels to Centermeters")
133                 ),
134             default="CM_TO_PIXELS",
135             )
136     orientation: EnumProperty(
137             name="Page Orientation",
138             description="Set orientation",
139             items=(
140                 ("Portrait", "Portrait", "Portrait"),
141                 ("Landscape", "Landscape", "Landscape")
142             ),
143             default="Portrait",
144             update=update_settings_cb,
145             )
146     preset: EnumProperty(
147             name="Select Preset",
148             description="Select from preset",
149             items=paper_presets,
150             default="custom_1_1",
151             update=update_settings_cb,
152             )
153     dpi: IntProperty(
154             name="DPI",
155             description="Dots per Inch",
156             default=300,
157             min=72, max=1800,
158             update=update_settings_cb,
159             )
160     width_cm: FloatProperty(
161             name="Width",
162             description="Width in CM",
163             default=5.0,
164             min=1.0, max=100000.0,
165             update=update_settings_cb,
166             )
167     height_cm: FloatProperty(
168             name="Height",
169             description="Height in CM",
170             default=3.0,
171             min=1.0, max=100000.0,
172             update=update_settings_cb,
173             )
174     width_px: IntProperty(
175             name="Pixel Width",
176             description="Pixel Width",
177             default=900,
178             min=4, max=10000,
179             update=update_settings_cb,
180             )
181     height_px: IntProperty(
182             name="Pixel Height",
183             description="Pixel Height",
184             default=600,
185             min=4, max=10000,
186             update=update_settings_cb,
187             )
188
189
190 def pixels_from_print(ps):
191     tipo, dim_w, dim_h = paper_presets_data[ps.preset]
192
193     if ps.unit_from == "CM_TO_PIXELS":
194         if tipo == "custom":
195             dim_w = ps.width_cm
196             dim_h = ps.height_cm
197             ps.width_cm = dim_w
198             ps.height_cm = dim_h
199         elif tipo != "custom" and ps.orientation == "Landscape":
200             ps.width_cm = dim_h
201             ps.height_cm = dim_w
202         elif tipo != "custom" and ps.orientation == "Portrait":
203             ps.width_cm = dim_w
204             ps.height_cm = dim_h
205
206         ps.width_px = math.ceil((ps.width_cm * ps.dpi) / 2.54)
207         ps.height_px = math.ceil((ps.height_cm * ps.dpi) / 2.54)
208     else:
209         if tipo != "custom" and ps.orientation == "Landscape":
210             ps.width_cm = dim_h
211             ps.height_cm = dim_w
212             ps.width_px = math.ceil((ps.width_cm * ps.dpi) / 2.54)
213             ps.height_px = math.ceil((ps.height_cm * ps.dpi) / 2.54)
214         elif tipo != "custom" and ps.orientation == "Portrait":
215             ps.width_cm = dim_w
216             ps.height_cm = dim_h
217             ps.width_px = math.ceil((ps.width_cm * ps.dpi) / 2.54)
218             ps.height_px = math.ceil((ps.height_cm * ps.dpi) / 2.54)
219
220         ps.width_cm = (ps.width_px / ps.dpi) * 2.54
221         ps.height_cm = (ps.height_px / ps.dpi) * 2.54
222
223
224 class RENDER_PT_print(Panel):
225     bl_label = "Render to Print"
226     bl_space_type = 'PROPERTIES'
227     bl_region_type = 'WINDOW'
228     bl_context = "output"
229     bl_options = {'DEFAULT_CLOSED'}
230
231     def draw(self, context):
232
233         layout = self.layout
234         scene = context.scene
235         ps = scene.print_settings
236
237         row = layout.row(align=True)
238         row1 = layout.row(align=True)
239         row2 = layout.row(align=True)
240         row3 = layout.row(align=True)
241         row4 = layout.row(align=True)
242         row5 = layout.row(align=True)
243         row6 = layout.row(align=True)
244         row7 = layout.row(align=True)
245         col = layout.column(align=True)
246
247         row.prop(ps, "unit_from")
248         row1.prop(ps, "orientation")
249         row2.prop(ps, "preset")
250
251         col.separator()
252         row3.prop(ps, "width_cm")
253         row3.separator()
254         row3.prop(ps, "height_cm")
255         col.separator()
256         row4.prop(ps, "dpi")
257         col.separator()
258         row5.prop(ps, "width_px")
259         row5.separator()
260         row5.prop(ps, "height_px")
261
262         col.separator()
263         row6.label(text="Inch Width: %.2f" % (ps.width_cm / 2.54))
264         row6.label(text="Inch Height: %.2f" % (ps.height_cm / 2.54))
265         col.separator()
266
267         row7.operator("render.apply_size", icon="RENDER_STILL")
268
269         #  this if else deals with hiding UI elements when logic demands it.
270         tipo = paper_presets_data[ps.preset][0]
271
272         if tipo != "custom":
273             row.active = False
274             row.enabled = False
275
276         if ps.unit_from == "CM_TO_PIXELS":
277             row5.active = False
278             row5.enabled = False
279
280             if tipo == "custom":
281                 row3.active = True
282                 row3.enabled = True
283                 row1.active = False
284                 row1.enabled = False
285             elif tipo != "custom" and ps.orientation == "Landscape":
286                 row3.active = False
287                 row3.enabled = False
288                 row1.active = True
289                 row1.enabled = True
290             elif tipo != "custom" and ps.orientation == "Portrait":
291                 row3.active = False
292                 row3.enabled = False
293                 row1.active = True
294                 row1.enabled = True
295         else:
296             row3.active = False
297             row3.enabled = False
298
299             if tipo == "custom":
300                 row1.active = False
301                 row1.enabled = False
302             elif tipo != "custom" and ps.orientation == "Landscape":
303                 row1.active = True
304                 row1.enabled = True
305                 row5.active = False
306                 row5.enabled = False
307             elif tipo != "custom" and ps.orientation == "Portrait":
308                 row1.active = True
309                 row1.enabled = True
310                 row5.active = False
311                 row5.enabled = False
312
313
314 class RENDER_OT_apply_size(Operator):
315     bl_idname = "render.apply_size"
316     bl_label = "Apply Print to Render"
317     bl_description = "Set the render dimension"
318
319     def execute(self, context):
320
321         scene = context.scene
322         ps = scene.print_settings
323
324         pixels_from_print(ps)
325
326         render = scene.render
327         render.resolution_x = ps.width_px
328         render.resolution_y = ps.height_px
329
330         return {'FINISHED'}
331
332
333 classes = ( 
334             RENDER_OT_apply_size, 
335             RENDER_PT_print, 
336             RenderPrintSertings
337             )
338
339 def register():
340     from bpy.utils import register_class
341
342     for cls in classes:
343         register_class(cls)
344
345     Scene.print_settings = PointerProperty(type=RenderPrintSertings)
346
347
348 def unregister():
349     from bpy.utils import unregister_class
350
351     for cls in classes:
352         unregister_class(cls)
353         
354     del Scene.print_settings
355
356
357 if __name__ == "__main__":
358     register()