[Bf-extensions-cvs] [e00751ae] master: Export Paper Model: correct PDF (upstream 5f749f2)

Adam Dominec noreply at git.blender.org
Sun Oct 25 12:06:51 CET 2020


Commit: e00751ae47f85f90aa232b9cb042bd8b02f2779d
Author: Adam Dominec
Date:   Sun Oct 25 12:03:00 2020 +0100
Branches: master
https://developer.blender.org/rBAe00751ae47f85f90aa232b9cb042bd8b02f2779d

Export Paper Model: correct PDF (upstream 5f749f2)

* fix PDF for Acrobat Reader
* fix export Presets
* add switch for automatic scaling
* improved code quality

===================================================================

M	io_export_paper_model.py

===================================================================

diff --git a/io_export_paper_model.py b/io_export_paper_model.py
index f44038f1..5f8e70f5 100644
--- a/io_export_paper_model.py
+++ b/io_export_paper_model.py
@@ -1,6 +1,6 @@
 # -*- coding: utf-8 -*-
 # This script is Free software. Please share and reuse.
-# ♡2010-2019 Adam Dominec <adominec at gmail.com>
+# ♡2010-2020 Adam Dominec <adominec at gmail.com>
 
 ## Code structure
 # This file consists of several components, in this order:
@@ -12,8 +12,8 @@
 bl_info = {
     "name": "Export Paper Model",
     "author": "Addam Dominec",
-    "version": (1, 1),
-    "blender": (2, 80, 0),
+    "version": (1, 2),
+    "blender": (2, 83, 0),
     "location": "File > Export > Paper Model",
     "warning": "",
     "description": "Export printable net of the active mesh",
@@ -22,21 +22,16 @@ bl_info = {
 }
 
 # Task: split into four files (SVG and PDF separately)
-    # does any portion of baking belong into the export module?
-    # sketch out the code for GCODE and two-sided export
+# * does any portion of baking belong into the export module?
+# * sketch out the code for GCODE and two-sided export
 
 # TODO:
-# sanitize the constructors Edge, Face, UVFace so that they don't edit their parent object
-# The Exporter classes should take parameters as a whole pack, and parse it themselves
-# remember objects selected before baking (except selected to active)
-# add 'estimated number of pages' to the export UI
 # QuickSweepline is very much broken -- it throws GeometryError for all nets > ~15 faces
 # rotate islands to minimize area -- and change that only if necessary to fill the page size
-# Sticker.vertices should be of type Vector
 
 # check conflicts in island naming and either:
-#  * append a number to the conflicting names or
-#  * enumerate faces uniquely within all islands of the same name (requires a check that both label and abbr. equals)
+# * append a number to the conflicting names or
+# * enumerate faces uniquely within all islands of the same name (requires a check that both label and abbr. equals)
 
 import bpy
 import bl_operators
@@ -127,7 +122,6 @@ def cage_fit(points, aspect):
             rot_polygon = [rot @ p for p in polygon]
             left, right = [fn(rot_polygon, key=lambda p: p.to_tuple()) for fn in (min, max)]
             bottom, top = [fn(rot_polygon, key=lambda p: p.yx.to_tuple()) for fn in (min, max)]
-            #print(f"{rot_polygon.index(left)}-{rot_polygon.index(right)}, {rot_polygon.index(bottom)}-{rot_polygon.index(top)}")
             horz, vert = right - left, top - bottom
             # solve (rot * a).y == (rot * b).y
             yield max(aspect * horz.x, vert.y), sinx, cosx
@@ -143,9 +137,7 @@ def cage_fit(points, aspect):
             rot = M.Matrix(((cosy, -siny), (siny, cosy)))
             for p in rot_polygon:
                 p[:] = rot @ p  # note: this also modifies left, right, bottom, top
-            #print(f"solve {aspect * (right - left).x} == {(top - bottom).y} with aspect = {aspect}")
             if left.x < right.x and bottom.y < top.y and all(left.x <= p.x <= right.x and bottom.y <= p.y <= top.y for p in rot_polygon):
-                #print(f"yield {max(aspect * (right - left).x, (top - bottom).y)}")
                 yield max(aspect * (right - left).x, (top - bottom).y), sinx*cosy + cosx*siny, cosx*cosy - sinx*siny
     polygon = [points[i] for i in M.geometry.convex_hull_2d(points)]
     height, sinx, cosx = min(guesses(polygon))
@@ -166,6 +158,16 @@ def create_blank_image(image_name, dimensions, alpha=1):
     return image
 
 
+def store_rna_properties(*datablocks):
+    return [{prop.identifier: getattr(data, prop.identifier) for prop in data.rna_type.properties if not prop.is_readonly} for data in datablocks]
+
+
+def apply_rna_properties(memory, *datablocks):
+    for recall, data in zip(memory, datablocks):
+        for key, value in recall.items():
+            setattr(data, key, value)
+
+
 class UnfoldError(ValueError):
     def mesh_select(self):
         if len(self.args) > 1:
@@ -217,7 +219,7 @@ class Unfolder:
         # Note about scale: input is directly in blender length
         # Mesh.scale_islands multiplies everything by a user-defined ratio
         # exporters (SVG or PDF) multiply everything by 1000 (output in millimeters)
-        Exporter = SVG if properties.file_format == 'SVG' else PDF
+        Exporter = Svg if properties.file_format == 'SVG' else Pdf
         filepath = properties.filepath
         extension = properties.file_format.lower()
         filepath = bpy.path.ensure_ext(filepath, "." + extension)
@@ -249,11 +251,9 @@ class Unfolder:
             sce = bpy.context.scene
             rd = sce.render
             bk = rd.bake
-            # TODO: do we really need all this recollection?
-            recall = rd.engine, sce.cycles.bake_type, sce.cycles.samples, bk.use_selected_to_active, bk.margin, bk.cage_extrusion, bk.use_cage, bk.use_clear
+            recall = store_rna_properties(rd, bk, sce.cycles)
             rd.engine = 'CYCLES'
-            recall_pass = {p: getattr(bk, f"use_pass_{p}") for p in ('ambient_occlusion', 'color', 'diffuse', 'direct', 'emit', 'glossy', 'indirect', 'transmission')}
-            for p in recall_pass:
+            for p in ('ambient_occlusion', 'color', 'diffuse', 'direct', 'emit', 'glossy', 'indirect', 'transmission'):
                 setattr(bk, f"use_pass_{p}", (properties.output_type != 'TEXTURE'))
             lookup = {'TEXTURE': 'DIFFUSE', 'AMBIENT_OCCLUSION': 'AO', 'RENDER': 'COMBINED', 'SELECTED_TO_ACTIVE': 'COMBINED'}
             sce.cycles.bake_type = lookup[properties.output_type]
@@ -276,13 +276,9 @@ class Unfolder:
             elif image_packing == 'ISLAND_EMBED':
                 self.mesh.save_separate_images(ppm, filepath, embed=Exporter.encode_image)
 
-            rd.engine, sce.cycles.bake_type, sce.cycles.samples, bk.use_selected_to_active, bk.margin, bk.cage_extrusion, bk.use_cage, bk.use_clear = recall
-            for p, v in recall_pass.items():
-                setattr(bk, f"use_pass_{p}", v)
+            apply_rna_properties(recall, rd, bk, sce.cycles)
 
-        exporter = Exporter(page_size, properties.style, properties.output_margin, (properties.output_type == 'NONE'), properties.angle_epsilon)
-        exporter.do_create_stickers = properties.do_create_stickers
-        exporter.text_size = properties.sticker_width
+        exporter = Exporter(properties)
         exporter.write(self.mesh, filepath)
 
 
@@ -336,8 +332,9 @@ class Mesh:
         if not (null_edges or null_faces or twisted_faces or inverted_scale):
             return True
         if inverted_scale:
-            raise UnfoldError("The object is flipped inside-out.\n"
-            "You can use Object -> Apply -> Scale to fix it. Export failed.")
+            raise UnfoldError(
+                "The object is flipped inside-out.\n"
+                "You can use Object -> Apply -> Scale to fix it. Export failed.")
         disease = [("Remove Doubles", null_edges or null_faces), ("Triangulate", twisted_faces)]
         cure = " and ".join(s for s, k in disease if k)
         raise UnfoldError(
@@ -514,14 +511,13 @@ class Mesh:
             angle, _ = cage_fit(points, (cage_size.y - title_height) / cage_size.x)
             rot = M.Matrix.Rotation(angle, 2)
             for point in points:
-                # note: we need an in-place operation, and Vector.rotate() seems to work for 3d vectors only
-                point[:] = rot @ point
+                point.rotate(rot)
             for marker in island.markers:
                 marker.rot = rot @ marker.rot
             bottom_left = M.Vector((min(v.x for v in points), min(v.y for v in points) - title_height))
-            #DEBUG
-            top_right = M.Vector((max(v.x for v in points), max(v.y for v in points) - title_height))
-            #print(f"fitted aspect: {(top_right.y - bottom_left.y) / (top_right.x - bottom_left.x)}")
+            # DEBUG
+            # top_right = M.Vector((max(v.x for v in points), max(v.y for v in points) - title_height))
+            # print(f"fitted aspect: {(top_right.y - bottom_left.y) / (top_right.x - bottom_left.x)}")
             for point in points:
                 point -= bottom_left
             island.bounding_box = M.Vector((max(v.x for v in points), max(v.y for v in points)))
@@ -666,7 +662,8 @@ class Mesh:
 
 class Edge:
     """Wrapper for BPy Edge"""
-    __slots__ = ('data', 'va', 'vb', 'main_faces', 'uvedges',
+    __slots__ = (
+        'data', 'va', 'vb', 'main_faces', 'uvedges',
         'vector', 'angle',
         'is_main_cut', 'force_cut', 'priority', 'freestyle')
 
@@ -689,10 +686,11 @@ class Edge:
 
     def choose_main_faces(self):
         """Choose two main faces that might get connected in an island"""
-        from itertools import combinations
-        loops = self.data.link_loops
+
         def score(pair):
             return abs(pair[0].face.normal.dot(pair[1].face.normal))
+
+        loops = self.data.link_loops
         if len(loops) == 2:
             self.main_faces = list(loops)
         elif len(loops) > 2:
@@ -709,7 +707,7 @@ class Edge:
             self.angle = -3  # just a very sharp angle
         else:
             s = normal_a.cross(normal_b).dot(self.vector.normalized())
-            s = max(min(s, 1.0), -1.0) # deal with rounding errors
+            s = max(min(s, 1.0), -1.0)  # deal with rounding errors
             self.angle = asin(s)
             if loop_a.link_loop_next.vert != loop_b.vert or loop_b.link_loop_next.vert != loop_a.vert:
                 self.angle = abs(self.angle)
@@ -741,7 +739,8 @@ class Edge:
 
 class Island:
     """Part of the net to be exported"""
-    __slots__ = ('mesh', 'faces', 'edges', 'vertices', 'fake_vertices', 'boundary', 'markers',
+    __slots__ = (
+        'mesh', 'faces', 'edges', 'vertices', 'fake_vertices', 'boundary', 'markers',
         'pos', 'bounding_box',
         'image_path', 'embedded_image',
         'number', 'label', 'abbreviation', 'title',
@@ -803,6 +802,7 @@ class Island:
         for loop, uvvertex in self.vertices.items():
             loop[tex].uv = uvvertex.co.x * scale_x, uvvertex.co.y * scale_y
 
+
 def join(uvedge_a, uvedge_

@@ Diff output truncated at 10240 characters. @@



More information about the Bf-extensions-cvs mailing list