[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