Real CAD part to a 3D-print 3MF plate

Real CAD part to a 3D-print 3MF plate#

A common prototyping job: you need many copies of one part, so you nest them on a build plate and hand a single 3MF to the slicer with each instance named so the slicer tree mirrors the layout.

The part is a real M3 nyloc lock nut from the FreeCAD parts library (CC-BY 3.0, FreeCAD-library contributors). We read its native FCStd, nest a 3x3 grid of instances, and write one 3MF; per-part labels round-trip into the 3MF as <object name>.

from pathlib import Path
import tempfile

import numpy as np
import pandas as pd
import pyvista as pv

import pyvista_cad
from pyvista_cad.examples import downloads

Read the real FCStd nut and tessellate it to a single surface.

nut = pyvista_cad.read_fcstd(downloads.fcstd_nut_path()).combine().extract_surface()
nut
/home/runner/work/pyvista-cad/pyvista-cad/examples/05_workflows/parametric_to_3d_print.py:30: PyVistaFutureWarning: The default value of `algorithm` for the filter
`UnstructuredGrid.extract_surface` will change in the future. It currently defaults to
`'dataset_surface'`, but will change to `None`. Explicitly set the `algorithm` keyword to
silence this warning.
  nut = pyvista_cad.read_fcstd(downloads.fcstd_nut_path()).combine().extract_surface()
PolyData (0x7f6fa5f63e80)
  N Cells:    8116
  N Points:   6988
  N Strips:   0
  X Bounds:   -3.175e+00, 3.175e+00
  Y Bounds:   -3.175e+00, 3.175e+00
  Z Bounds:   -2.000e-01, 3.856e+00
  N Arrays:   5


Nest a 3x3 grid of instances on the build plate, one named block per instance.

pitch = (nut.bounds.x_max - nut.bounds.x_min) * 1.5
plate = pv.MultiBlock()
rows = []
for i in range(3):
    for j in range(3):
        name = f'nut_r{i}c{j}'
        inst = nut.copy()
        inst.translate((i * pitch, j * pitch, 0.0), inplace=True)
        inst.field_data['cad.label'] = np.array([name])
        plate.append(inst, name=name)
        rows.append({'label': name, 'cells': inst.n_cells})

pd.DataFrame(rows)
label cells
0 nut_r0c0 8116
1 nut_r0c1 8116
2 nut_r0c2 8116
3 nut_r1c0 8116
4 nut_r1c1 8116
5 nut_r1c2 8116
6 nut_r2c0 8116
7 nut_r2c1 8116
8 nut_r2c2 8116


Write one 3MF carrying all nine instances, then round-trip it to confirm the layout and names survived.

MultiBlock (0x7f6fa5f81e40)
  N Blocks:   9
  X Bounds:   -3.175e+00, 2.223e+01
  Y Bounds:   -3.175e+00, 2.223e+01
  Z Bounds:   -2.000e-01, 3.856e+00


Render the build plate.

pl = pv.Plotter()
pl.add_mesh(back, multi_colors=True, show_edges=True, edge_color='black')
pl.show()
parametric to 3d print

Total running time of the script: (0 minutes 1.457 seconds)