[Bf-extensions-cvs] [44ab060e] master: io_export_paper_model: update to github rev 0cbd76ff

Adam Dominec noreply at git.blender.org
Thu Apr 13 16:53:15 CEST 2017


Commit: 44ab060ed391888e88dd358d6a280bdcb76b9c31
Author: Adam Dominec
Date:   Thu Apr 13 16:52:06 2017 +0200
Branches: master
https://developer.blender.org/rBAC44ab060ed391888e88dd358d6a280bdcb76b9c31

io_export_paper_model: update to github rev 0cbd76ff

 * check for correct geometry before export
 * customizable angle limit for hidden edges

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

M	io_export_paper_model.py

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

diff --git a/io_export_paper_model.py b/io_export_paper_model.py
index 9842305d..508b953f 100644
--- a/io_export_paper_model.py
+++ b/io_export_paper_model.py
@@ -212,6 +212,7 @@ class Unfolder:
     def __init__(self, ob):
         self.ob = ob
         self.mesh = Mesh(ob.data, ob.matrix_world)
+        self.mesh.check_correct()
         self.tex = None
 
     def prepare(self, cage_size=None, create_uvmap=False, mark_seams=False, priority_effect=default_priority_effect, scale=1):
@@ -294,7 +295,7 @@ class Unfolder:
                 rd.bake_type = lookup[properties.output_type]
                 rd.use_bake_selected_to_active = (properties.output_type == 'SELECTED_TO_ACTIVE')
                 rd.bake_margin, rd.bake_distance, rd.bake_bias, rd.use_bake_to_vertex_color, rd.use_bake_clear = 0, 0, 0.001, False, False
-            
+
             if image_packing == 'PAGE_LINK':
                 self.mesh.save_image(tex, printable_size * ppm, filepath)
             elif image_packing == 'ISLAND_LINK':
@@ -311,7 +312,7 @@ class Unfolder:
                 tex.active = True
                 bpy.ops.mesh.uv_texture_remove()
 
-        exporter = Exporter(page_size, properties.style, properties.output_margin, (properties.output_type == 'NONE'))
+        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.write(self.mesh, filepath)
@@ -343,6 +344,27 @@ class Mesh:
             if edge.main_faces:
                 edge.calculate_angle()
 
+    def check_correct(self, epsilon=1e-6):
+        """Check for invalid geometry"""
+        null_edges = {i for i, e in self.edges.items() if e.length < epsilon and e.faces}
+        null_faces = {i for i, f in self.faces.items() if f.normal.length_squared < epsilon}
+        twisted_faces = {i for i, f in self.faces.items() if f.is_twisted()}
+        if not (null_edges or null_faces or twisted_faces):
+            return
+        bpy.context.tool_settings.mesh_select_mode = False, bool(null_edges), bool(null_faces or twisted_faces)
+        for vertex in self.data.vertices:
+            vertex.select = False
+        for edge in self.data.edges:
+            edge.select = (edge.index in null_edges)
+        for face in self.data.polygons:
+            face.select = (face.index in null_faces or face.index in twisted_faces)
+        cure = "Remove Doubles and Triangulate" if (null_edges or null_faces) and twisted_faces else "Triangulate" if twisted_faces else "Remove Doubles"
+        raise UnfoldError("The model contains:\n" +
+            (" {} zero-length edge(s)\n".format(len(null_edges)) if null_edges else "") +
+            (" {} zero-area face(s)\n".format(len(null_faces)) if null_faces else "") +
+            (" {} twisted polygon(s)\n".format(len(twisted_faces)) if twisted_faces else "") +
+            "The offenders are selected and you can use {} to fix them. Export failed.".format(cure))
+
     def generate_cuts(self, page_size, priority_effect):
         """Cut the mesh so that it can be unfolded to a flat net."""
         # warning: this constructor modifies its parameter (face)
@@ -751,26 +773,14 @@ class Face:
     __slots__ = ('index', 'edges', 'verts', 'uvface',
         'loop_start', 'area', 'normal')
 
-    def __init__(self, bpy_face, mesh, matrix=1):
+    def __init__(self, bpy_face, mesh):
         self.index = bpy_face.index
         self.edges = list()
         self.verts = [mesh.verts[i] for i in bpy_face.vertices]
         self.loop_start = bpy_face.loop_start
         self.area = bpy_face.area
         self.uvface = None
-
-        # calculate the face normal explicitly
-        if len(self.verts) == 3:
-            # normal of a triangle can be calculated directly
-            self.normal = (self.verts[1].co - self.verts[0].co).cross(self.verts[2].co - self.verts[0].co).normalized()
-        else:
-            # Newell's method
-            nor = M.Vector((0, 0, 0))
-            for a, b in pairs(self.verts):
-                p, m = a.co + b.co, a.co - b.co
-                nor.x, nor.y, nor.z = nor.x + m.y*p.z, nor.y + m.z*p.x, nor.z + m.x*p.y
-            self.normal = nor.normalized()
-
+        self.normal = M.geometry.normal(v.co for v in self.verts)
         for verts_indices in bpy_face.edge_keys:
             edge = mesh.edges_by_verts_indices[verts_indices]
             self.edges.append(edge)
@@ -778,7 +788,7 @@ class Face:
 
     def is_twisted(self):
         if len(self.verts) > 3:
-            center = sum(vertex.co for vertex in self.verts) / len(self.verts)
+            center = sum((vertex.co for vertex in self.verts), M.Vector((0, 0, 0))) / len(self.verts)
             plane_d = center.dot(self.normal)
             diameter = max((center - vertex.co).length for vertex in self.verts)
             for vertex in self.verts:
@@ -1358,7 +1368,7 @@ class NumberAlone:
 class SVG:
     """Simple SVG exporter"""
 
-    def __init__(self, page_size: M.Vector, style, margin, pure_net=True):
+    def __init__(self, page_size: M.Vector, style, margin, pure_net=True, angle_epsilon=0.01):
         """Initialize document settings.
         page_size: document dimensions in meters
         pure_net: if True, do not use image"""
@@ -1367,6 +1377,7 @@ class SVG:
         self.style = style
         self.margin = margin
         self.text_size = 12
+        self.angle_epsilon = angle_epsilon
 
     @classmethod
     def encode_image(cls, bpy_image):
@@ -1512,9 +1523,9 @@ class SVG:
                             data_freestyle.append(data_uvedge)
                         # each uvedge is in two opposite-oriented variants; we want to add each only once
                         if uvedge.sticker or uvedge.uvface.flipped != (uvedge.va.vertex.index > uvedge.vb.vertex.index):
-                            if edge.angle > 0.01:
+                            if edge.angle > self.angle_epsilon:
                                 data_convex.append(data_uvedge)
-                            elif edge.angle < -0.01:
+                            elif edge.angle < -self.angle_epsilon:
                                 data_concave.append(data_uvedge)
                     if island.is_inside_out:
                         data_convex, data_concave = data_concave, data_convex
@@ -1617,30 +1628,31 @@ class SVG:
 
 class PDF:
     """Simple PDF exporter"""
-    
+
     mm_to_pt = 72 / 25.4
-    def __init__(self, page_size: M.Vector, style, margin, pure_net=True):
+    def __init__(self, page_size: M.Vector, style, margin, pure_net=True, angle_epsilon=0.01):
         self.page_size = page_size
         self.style = style
         self.margin = M.Vector((margin, margin))
         self.pure_net = pure_net
-    
+        self.angle_epsilon = angle_epsilon
+
     character_width_packed = {833: 'mM', 834: '¼½¾', 260: '¦|', 389: '*', 584: '>~+¬±<×÷=', 778: 'ÒGÖÕQÔØÓO', 333: '¹\xad\x98\x84²¨\x94\x9b¯¡´()\x8b\x93¸³-\x88`r', 334: '{}', 400: '°', 722: 'DÛÚUÑwRÐÜCÇNÙH', 611: '¿øTßZF\x8e', 469: '^', 278: 'ì\x05\x06 ;\x01/\x08I\x07,\x13\x11\x04\\.![\x15\r\x10:\x18]\x0c\x00\x1bÍf\xa0\x14\x1c\n\t\x1e\x1dïí\x12·\x16\x0bî\x0e\x03tÏ\x17\x1fÎ\x19\x0f\x02Ì\x1a', 537: '¶', 667: 'ÄË\x8aÃÀBÊVX&AKSÈÞPÁYÉ\x9fÝEÅÂ', 222: 'jl\x92\x91i\x82', 737: '©®', 355: '"', 100 [...]
     character_width = {c: value for (value, chars) in character_width_packed.items() for c in chars}
     def text_width(self, text, scale=None):
         return (scale or self.text_size) * sum(self.character_width.get(c, 556) for c in text) / 1000
-    
+
     @classmethod
     def encode_image(cls, bpy_image):
         data = bytes(int(255 * px) for (i, px) in enumerate(bpy_image.pixels) if i % 4 != 3)
         image = {"Type": "XObject", "Subtype": "Image", "Width": bpy_image.size[0], "Height": bpy_image.size[1], "ColorSpace": "DeviceRGB", "BitsPerComponent": 8, "Interpolate": True, "Filter": ["ASCII85Decode", "FlateDecode"], "stream": data}
         return image
 
-    
+
     def write(self, mesh, filename):
         def format_dict(obj, refs=tuple()):
             return "<< " + "".join("/{} {}\n".format(key, format_value(value, refs)) for (key, value) in obj.items()) + ">>"
-        
+
         def line_through(seq):
             return "".join("{0.x:.6f} {0.y:.6f} {1} ".format(1000*v.co, c) for (v, c) in zip(seq, chain("m", repeat("l"))))
 
@@ -1659,7 +1671,7 @@ class PDF:
                 return "true" if value else "false"
             else:
                 return "/{}".format(value)  # this script can output only PDF names, no strings
-        
+
         def write_object(index, obj, refs, f, stream=None):
             byte_count = f.write("{} 0 obj\n".format(index))
             if type(obj) is not dict:
@@ -1677,7 +1689,7 @@ class PDF:
                 byte_count += f.write(stream)
                 byte_count += f.write("\nendstream")
             return byte_count + f.write("\nendobj\n")
-        
+
         def encode(data):
             from base64 import a85encode
             from zlib import compress
@@ -1689,7 +1701,7 @@ class PDF:
         root = {"Type": "Pages", "MediaBox": [0, 0, page_size_pt.x, page_size_pt.y], "Kids": list()}
         catalog = {"Type": "Catalog", "Pages": root}
         font = {"Type": "Font", "Subtype": "Type1", "Name": "F1", "BaseFont": "Helvetica", "Encoding": "MacRomanEncoding"}
-        
+
         dl = [length * self.style.line_width * 1000 for length in (1, 4, 9)]
         format_style = {'SOLID': list(), 'DOT': [dl[0], dl[1]], 'DASH': [dl[1], dl[2]], 'LONGDASH': [dl[2], dl[1]], 'DASHDOT': [dl[2], dl[1], dl[0], dl[1]]}
         styles = {
@@ -1721,7 +1733,7 @@ class PDF:
                     commands.append("q {0.x:.6f} 0 0 {0.y:.6f} 0 0 cm 1 0 0 -1 0 1 cm /{1} Do Q".format(10

@@ Diff output truncated at 10240 characters. @@



More information about the Bf-extensions-cvs mailing list