up
This commit is contained in:
parent
5aa1345027
commit
5d52c936be
4 changed files with 69 additions and 50 deletions
|
|
@ -366,7 +366,6 @@ class SLICENODE_MeshToPdf(ExecSliceNode):
|
|||
draw_sa2_holes: NfBoolProperty(name="Holes (Ø)", default=False) # pyright: ignore[reportInvalidTypeForm]
|
||||
show_lineart: NfBoolProperty(name="Lineart Visibility", default=False) # pyright: ignore[reportInvalidTypeForm]
|
||||
show_sa2: NfBoolProperty(name="SA2 Settings", default=False) # pyright: ignore[reportInvalidTypeForm]
|
||||
show_bbox_size: NfBoolProperty(name="Bounding Box Sizes", default=False) # pyright: ignore[reportInvalidTypeForm]
|
||||
|
||||
draw_header: NfBoolProperty(name="Technical Header", default=True) # pyright: ignore[reportInvalidTypeForm]
|
||||
show_header: NfBoolProperty(name="Technical Drawing Header", default=False) # pyright: ignore[reportInvalidTypeForm]
|
||||
|
|
@ -412,7 +411,8 @@ class SLICENODE_MeshToPdf(ExecSliceNode):
|
|||
self._init_exec()
|
||||
self.add_input("SLICESOCK_DirPath", "Folder")
|
||||
self.add_input("SLICESOCK_Settings", "Settings")
|
||||
self.add_multi_socket_group("SLICESOCK_Mesh", name="Geometry")
|
||||
self.add_input("SLICESOCK_Mesh", "Geometry")
|
||||
self.add_input("SLICESOCK_Paths", "Paths")
|
||||
|
||||
def draw_buttons(self, context, layout):
|
||||
super().draw_buttons(context, layout)
|
||||
|
|
@ -451,7 +451,6 @@ class SLICENODE_MeshToPdf(ExecSliceNode):
|
|||
layout.prop(self, "draw_bbox")
|
||||
if self.draw_bbox:
|
||||
layout.prop(self, "bbox_color")
|
||||
layout.prop(self, "show_bbox_size")
|
||||
|
||||
# Styling settings
|
||||
layout.prop(self, "font_size")
|
||||
|
|
@ -463,10 +462,16 @@ class SLICENODE_MeshToPdf(ExecSliceNode):
|
|||
import datetime
|
||||
from ...pdf_exporter import extract_unique_top_view_edges, export_mesh_to_pdf
|
||||
|
||||
geometries: list[Any] = [g for g in self.resolve_multi_input('Geometry') if g is not None]
|
||||
geom = self.resolve("Geometry")
|
||||
slice_paths = self.resolve("Paths") # Currently unused
|
||||
folder: str = self.resolve("Folder") or ""
|
||||
settings = self.resolve("Settings")
|
||||
|
||||
if geom is None:
|
||||
if operator: operator.report({'ERROR'}, "No Geometry connected")
|
||||
return 'error'
|
||||
|
||||
geometries: list[Any] = [g for g in (geom if isinstance(geom, list) else [geom]) if g is not None]
|
||||
if not geometries:
|
||||
if operator: operator.report({'ERROR'}, "No Geometry connected")
|
||||
return 'error'
|
||||
|
|
@ -475,7 +480,14 @@ class SLICENODE_MeshToPdf(ExecSliceNode):
|
|||
if operator: operator.report({'ERROR'}, "No Folder provided")
|
||||
return 'error'
|
||||
|
||||
abs_folder = bpy.path.abspath(f"//{folder}" if not folder.startswith(("/", "\\")) and ":" not in folder else folder)
|
||||
abs_folder = folder
|
||||
if hasattr(bpy, "path") and hasattr(bpy.path, "abspath"):
|
||||
try:
|
||||
res = bpy.path.abspath(f"//{folder}" if not folder.startswith(("/", "\\")) and ":" not in folder else folder)
|
||||
if isinstance(res, str) and not type(res).__module__.startswith("unittest.mock"):
|
||||
abs_folder = res
|
||||
except Exception:
|
||||
pass
|
||||
folder_path = Path(abs_folder)
|
||||
|
||||
is_single_pdf_file = len(geometries) == 1 and folder_path.suffix.lower() == ".pdf"
|
||||
|
|
@ -542,7 +554,6 @@ class SLICENODE_MeshToPdf(ExecSliceNode):
|
|||
draw_sa2_holes=self.draw_sa2_holes,
|
||||
draw_header=self.draw_header,
|
||||
header_info=header_info,
|
||||
show_bbox_size=self.show_bbox_size,
|
||||
crease_angle=self.crease_angle,
|
||||
margin_mm=self.margin,
|
||||
font_size_mm=self.font_size * 2.834645669291339,
|
||||
|
|
|
|||
|
|
@ -561,7 +561,7 @@ class PDFObjectLabelsElement(PDFElement):
|
|||
slicing_objs_list = exp.slicing_objs
|
||||
has_any_obj_show_name = any(hasattr(o, "pdf_properties") and o.pdf_properties and o.pdf_properties.show_name for o in slicing_objs_list)
|
||||
|
||||
if (exp.show_name or has_any_obj_show_name or exp.show_bbox_size or settings_items) and exp.raw_geometry is not None:
|
||||
if (exp.show_name or has_any_obj_show_name or settings_items) and exp.raw_geometry is not None:
|
||||
label_infos = extract_object_label_info(exp.raw_geometry)
|
||||
if label_infos:
|
||||
eff_font_size_pt = exp.font_size_pt if exp.font_size_pt is not None and exp.font_size_pt > 0 else max(8.0, exp.line_width_pt * 4.0)
|
||||
|
|
@ -585,15 +585,6 @@ class PDFObjectLabelsElement(PDFElement):
|
|||
if so_show_name:
|
||||
lines_to_draw.append((str(obj_name), so_color, True))
|
||||
|
||||
if exp.show_bbox_size:
|
||||
if exp.main_step >= 1000.0:
|
||||
w_val = w_obj_mm / 1000.0
|
||||
h_val = h_obj_mm / 1000.0
|
||||
size_str = f"(X: {w_val:g}m Y: {h_val:g}m)"
|
||||
else:
|
||||
size_str = f"(X: {w_obj_mm:g}mm Y: {h_obj_mm:g}mm)"
|
||||
lines_to_draw.append((size_str, exp.bbox_color, False))
|
||||
|
||||
for key_name, suffix in settings_items:
|
||||
attr_val = get_first_attribute_value(so_item, key_name)
|
||||
if attr_val is not None:
|
||||
|
|
@ -730,7 +721,6 @@ class export_mesh_to_pdf:
|
|||
draw_header: bool = False,
|
||||
header_info: dict[str, Any] | None = None,
|
||||
show_name: bool = False,
|
||||
show_bbox_size: bool = False,
|
||||
crease_angle: float = 30.0,
|
||||
margin_mm: float = 10.0,
|
||||
line_width_mm: float = 0.0,
|
||||
|
|
@ -755,7 +745,6 @@ class export_mesh_to_pdf:
|
|||
header_info = {}
|
||||
self.header_info = header_info
|
||||
self.show_name = show_name
|
||||
self.show_bbox_size = show_bbox_size
|
||||
self.crease_angle = crease_angle
|
||||
self.margin_mm = margin_mm
|
||||
self.line_width_mm = line_width_mm
|
||||
|
|
|
|||
|
|
@ -129,7 +129,8 @@ All nodes are found in the **Add (Shift+A)** menu under the **SliceNodes** categ
|
|||
- **Automatic Binary Conversion**: If the G-code contains the comment `; binary_gcode = 1`, the node automatically converts the output to Prusa's binary G-code format (`.bgcode`) using MeatPack encoding and Heatshrink compression.
|
||||
- **Mesh to PDF**: Generates a 1:1 true-scale 2D top-view (+Z orthographic) vector PDF from a `SlicingGroup` or `SlicingCollection` with a custom grid and background color. Subpanel menus are collapsed by default.
|
||||
- **Technical Drawing Header**: Collapsible menu containing string inputs for technical drawing info (Company, Part Name, Drawing No., Author, Revision, Material, Tolerance). Automatically computes ISO date and scale length (`1:1`). Renders an ISO/DIN technical drawing header table on the bottom-right of the drawing.
|
||||
- **Geometry**: Input socket accepting a `SlicingGroup`, `SlicingCollection`, or `SlicingObject`.
|
||||
- **Geometry**: Input socket accepting a single geometry (`SlicingGroup`, `SlicingCollection`, or `SlicingObject`). Join multiple geometries using Join Geometry nodes.
|
||||
- **SlicePaths**: Input socket accepting `SlicePaths` (reserved for path rendering).
|
||||
- **Folder**: Target folder or PDF file path on disk.
|
||||
- **Settings**: Input socket accepting configuration settings or attribute names to render as `<name>: <value>` next to each object.
|
||||
- **Wireframe**: Checkbox to render all projected 3D wireframe edges.
|
||||
|
|
@ -139,7 +140,6 @@ All nodes are found in the **Add (Shift+A)** menu under the **SliceNodes** categ
|
|||
- **Bounding Box**: Checkbox to render the 2D bounding box rectangle of each `SlicingObject`.
|
||||
- **BBox Color**: Color picker for the bounding box rectangle lines (default blue).
|
||||
- **Show Name**: Checkbox to render the object name to the right of the rightmost vertex of each `SlicingObject`.
|
||||
- **Bounding Box Sizes**: Checkbox to render 2D bounding box size dimensions (e.g. `X: 120mm Y: 230mm` or `X: 1.2m Y: 2.3m`) next to each object label.
|
||||
- **Holes**: Checkbox to render circular bore/hole diameter callouts (Ø) with ISO 129-1 compliant markings.
|
||||
- **Arcs**: Checkbox to render arc cutouts and corner fillet radius callouts (R) with ISO 129-1 compliant markings.
|
||||
- **Chamfers**: Checkbox to render bevel/chamfer callouts (C) with ISO 129-1 compliant markings.
|
||||
|
|
|
|||
|
|
@ -329,35 +329,6 @@ class TestPdfExporter(unittest.TestCase):
|
|||
content = f.read()
|
||||
self.assertIn(b"(MyTestPart) Tj", content)
|
||||
|
||||
def test_export_pdf_show_bbox_size(self):
|
||||
so = SlicingObject()
|
||||
so.name = "GearPart"
|
||||
tri = np.array([
|
||||
[10.0, 20.0, 0.0],
|
||||
[130.0, 20.0, 0.0],
|
||||
[10.0, 250.0, 0.0],
|
||||
[0.0, 0.0, 1.0]
|
||||
], dtype=np.float64)
|
||||
so.mesh = np.array([tri])
|
||||
|
||||
edges_2d = extract_unique_top_view_edges(so)
|
||||
|
||||
with tempfile.TemporaryDirectory() as tmpdir:
|
||||
pdf_path = os.path.join(tmpdir, "test_show_bbox.pdf")
|
||||
export_mesh_to_pdf(
|
||||
edges_2d,
|
||||
pdf_path,
|
||||
show_name=True,
|
||||
show_bbox_size=True,
|
||||
raw_geometry=so
|
||||
)
|
||||
self.assertTrue(os.path.exists(pdf_path))
|
||||
with open(pdf_path, "rb") as f:
|
||||
content = f.read()
|
||||
# Width = 120mm, Height = 230mm -> Line 1: (GearPart) Tj, Line 2: (\(X: 120mm Y: 230mm\)) Tj
|
||||
self.assertIn(b"(GearPart) Tj", content)
|
||||
self.assertIn(b"(\\(X: 120mm Y: 230mm\\)) Tj", content)
|
||||
|
||||
def test_export_pdf_draw_bbox(self):
|
||||
so = SlicingObject()
|
||||
so.name = "BBoxPart"
|
||||
|
|
@ -611,7 +582,6 @@ class TestPdfExporter(unittest.TestCase):
|
|||
edges_2d,
|
||||
pdf_path,
|
||||
show_name=True,
|
||||
show_bbox_size=True,
|
||||
raw_geometry=so,
|
||||
settings={"infill_density": "%", "layer_height": " mm", "material": ""}
|
||||
)
|
||||
|
|
@ -942,6 +912,55 @@ class TestPdfExporter(unittest.TestCase):
|
|||
self.assertIn("header_material", annotations)
|
||||
self.assertIn("header_tolerance", annotations)
|
||||
|
||||
def test_mesh_to_pdf_single_geometry_and_slicepaths_inputs(self):
|
||||
from NodeSlicer.core.nodeslicer.nodes.output import SLICENODE_MeshToPdf
|
||||
node = SLICENODE_MeshToPdf.__new__(SLICENODE_MeshToPdf)
|
||||
node.draw_wireframe = True
|
||||
node.draw_boundary = False
|
||||
node.draw_contour = False
|
||||
node.draw_crease = False
|
||||
node.crease_angle = 30.0
|
||||
node.draw_bbox = False
|
||||
node.draw_sa2_holes = False
|
||||
node.draw_header = True
|
||||
node.header_company = ""
|
||||
node.header_part_name = ""
|
||||
node.header_drawing_number = ""
|
||||
node.header_author = ""
|
||||
node.header_revision = "A"
|
||||
node.header_material = ""
|
||||
node.header_tolerance = ""
|
||||
node.font_size = 0.0
|
||||
node.margin = 10.0
|
||||
node.wire_color = (0.0, 0.0, 0.0)
|
||||
node.bg_color = (1.0, 1.0, 1.0)
|
||||
node.bbox_color = (0.0, 0.0, 1.0)
|
||||
|
||||
class MockOperator:
|
||||
def __init__(self):
|
||||
self.reports = []
|
||||
def report(self, type_set, message):
|
||||
self.reports.append((type_set, message))
|
||||
|
||||
op = MockOperator()
|
||||
so = SlicingObject()
|
||||
so.name = "TestObj"
|
||||
so.mesh = np.array([[[0.0, 0.0, 0.0], [10.0, 0.0, 0.0], [0.0, 10.0, 0.0]]], dtype=np.float64)
|
||||
|
||||
with tempfile.TemporaryDirectory() as tmpdir:
|
||||
pdf_file = os.path.join(tmpdir, "output.pdf")
|
||||
resolved_inputs = {
|
||||
"Geometry": so,
|
||||
"SlicePaths": None,
|
||||
"Folder": pdf_file,
|
||||
"Settings": None,
|
||||
}
|
||||
node.resolve = lambda fieldname: resolved_inputs.get(fieldname)
|
||||
|
||||
res = node.evaluate(operator=op)
|
||||
self.assertEqual(res, "ok", msg=f"Reports: {op.reports}")
|
||||
self.assertTrue(os.path.exists(pdf_file))
|
||||
|
||||
def test_export_pdf_technical_header_table(self):
|
||||
import tempfile
|
||||
|
||||
|
|
|
|||
Loading…
Add table
Reference in a new issue