Surface Operations
Surface Operations Tutorial.
This tutorial demonstrates operations on 3D surface meshes including creation of primitives, import/export, transformations, boolean operations, mesh processing, diagnostics, and conversion.
Prerequisites
- Volvicon application must be running
- For some operations, an active volume is required
Listing Surfaces​
all_surfaces = app.get_all_surface_names()
all_volumes = app.get_all_volume_names()
print(f"Available surfaces: {all_surfaces}")
print(f"Available volumes: {all_volumes}")
Importing and Exporting Surfaces​
# Supported import formats: stl, ply, obj, wrl, step, iges, vtp, g, x3d, xyz
# Supported export formats: stl, ply, obj, wrl, u3d, 3mf, step, iges, vtp, g, x3d, xyz
# Import a single surface file
# surface_name = SurfaceOperations.import_surface_from_disk(r'C:\tmp\cropBox.stl')
# Import multiple surface files
# surface_names = SurfaceOperations.import_surfaces_from_disk([
# r'C:\tmp\cropBox.stl',
# r'C:\tmp\cropBox.stl'
# ])
# Export a single surface to file
# SurfaceOperations.export_surface_to_disk('cropBox (1)', r'C:\output\model.stl', False) # isASCII=False (bool)
# Export multiple surfaces to a directory
# SurfaceOperations.export_surfaces_to_disk(['cropBox (1)', 'cropBox (2)'], r'C:\output', 'stl', False) # isASCII=False
Creating Primitive Surfaces​
# Create a sphere
# sphere_name = SurfaceOperations.create_sphere(
# "MySphere", # name (str)
# 10.0, # radius (float)
# [0.0, 0.0, 0.0], # center [x, y, z]
# 36, # rings (int, latitude resolution)
# 36 # sectors (int, longitude resolution)
# )
# Create a box
# box_name = SurfaceOperations.create_box(
# "MyBox", # name (str)
# 20.0, # width (float)
# 15.0, # height (float)
# 10.0, # depth (float)
# [0.0, 0.0, 0.0], # center [x, y, z]
# 10 # resolution (int)
# )
# Create a cube
# cube_name = SurfaceOperations.create_cube(
# "MyCube", # name (str)
# 15.0, # edgeLength (float)
# [0.0, 0.0, 0.0], # center [x, y, z]
# 10 # resolution (int)
# )
# Create a cylinder
# cylinder_name = SurfaceOperations.create_cylinder(
# "MyCylinder", # name (str)
# 5.0, # radius (float)
# 20.0, # height (float)
# [0.0, 0.0, 0.0], # center [x, y, z]
# 36, # resolution (int)
# True, # capping (bool)
# 2 # direction (int: 0=X, 1=Y, 2=Z)
# )
# Create a cone
# cone_name = SurfaceOperations.create_cone(
# "MyCone", # name (str)
# 8.0, # radius (float, base radius)
# 15.0, # height (float)
# [0.0, 0.0, 0.0], # center [x, y, z]
# 36, # resolution (int)
# True, # capping (bool)
# 2 # direction (int: 0=X, 1=Y, 2=Z)
# )
# Create a torus
# torus_name = SurfaceOperations.create_torus(
# "MyTorus", # name (str)
# 3.0, # innerRadius (float, ring radius)
# 10.0, # outerRadius (float, total radius)
# [0.0, 0.0, 0.0], # center [x, y, z]
# 36, # resolution (int)
# 2 # direction (int: 0=X, 1=Y, 2=Z)
# )
# Create a tube (hollow cylinder)
# tube_name = SurfaceOperations.create_tube(
# "MyTube", # name (str)
# 10.0, # outerRadius (float)
# 2.0, # thickness (float, wall thickness)
# 20.0, # height (float)
# [0.0, 0.0, 0.0], # center [x, y, z]
# 36, # resolution (int)
# 2 # direction (int: 0=X, 1=Y, 2=Z)
# )
# Create a capsule (cylinder with hemispherical ends)
# capsule_name = SurfaceOperations.create_capsule(
# "MyCapsule", # name (str)
# 5.0, # radius (float)
# 15.0, # height (float, cylindrical part)
# [0.0, 0.0, 0.0], # center [x, y, z]
# 36, # resolution (int)
# 2 # direction (int: 0=X, 1=Y, 2=Z)
# )
# Create an ellipsoid
# ellipsoid_name = SurfaceOperations.create_ellipsoid(
# "MyEllipsoid", # name (str)
# 10.0, # radiusX (float)
# 7.0, # radiusY (float)
# 5.0, # radiusZ (float)
# [0.0, 0.0, 0.0], # center [x, y, z]
# 36, # rings (int)
# 36 # sectors (int)
# )
# Create a capped sphere (sphere with cut)
# capped_sphere_name = SurfaceOperations.create_capped_sphere(
# "MyCappedSphere", # name (str)
# 10.0, # radius (float)
# 90.0, # cutAngleDeg (float, 0-180)
# [0.0, 0.0, 0.0], # center [x, y, z]
# 36, # rings (int)
# 36 # sectors (int)
# )
# Create a disk
# disk_name = SurfaceOperations.create_disk(
# "MyDisk", # name (str)
# 0.0, # innerRadius (float, 0 for solid disk)
# 10.0, # outerRadius (float)
# [0.0, 0.0, 0.0], # center [x, y, z]
# 18, # radialResolution (int)
# 36, # circumferentialResolution (int)
# 2 # direction (int: 0=X, 1=Y, 2=Z)
# )
# Create a plane
# plane_name = SurfaceOperations.create_plane(
# "MyPlane", # name (str)
# 50.0, # width (float)
# 50.0, # height (float)
# [0.0, 0.0, 0.0], # center [x, y, z]
# 1, # resolution (int)
# 2 # direction (int: 0=X, 1=Y, 2=Z)
# )
# Create an arrow
# arrow_name = SurfaceOperations.create_arrow(
# "MyArrow", # name (str)
# 20.0, # totalLength (float)
# 1.0, # shaftRadius (float)
# 5.0, # tipLength (float)
# 3.0, # tipRadius (float)
# [0.0, 0.0, 0.0], # center [x, y, z]
# 32, # resolution (int)
# 2 # direction (int: 0=X, 1=Y, 2=Z)
# )
# Create a platonic solid
# platonic_name = SurfaceOperations.create_platonic_solid(
# "MyIcosahedron", # name (str)
# api.PlatonicSolidType.Icosahedron, # solidType (Tetrahedron, Octahedron, Icosahedron, Dodecahedron)
# 10.0, # radius (float)
# [0.0, 0.0, 0.0] # center [x, y, z]
# )
Surface Mesh Information​
# if all_surfaces:
# basic_surface_mesh_info = SurfaceOperations.get_basic_surface_mesh_info(all_surfaces[0])
# print(f"Vertices: {basic_surface_mesh_info.vertex_count}")
# print(f"Triangles: {basic_surface_mesh_info.polygon_count}")
# print(f"BBox surface area: {basic_surface_mesh_info.bounding_box_area}")
# print(f"BBox volume: {basic_surface_mesh_info.bounding_box_volume}")
Geometric Transformations​
# if all_surfaces:
# # Rotate around axis
# SurfaceOperations.rotate(
# [all_surfaces[0]], # surfaceNames (list)
# 45.0, # angleDegrees (float)
# [0.0, 0.0, 1.0], # axis [x, y, z]
# [0.0, 0.0, 0.0], # center [x, y, z] (ignored if aroundObjectCentroid=True)
# True # aroundObjectCentroid (bool)
# )
#
# # Translate
# SurfaceOperations.translate(
# [all_surfaces[0]], # surfaceNames (list)
# [10.0, 5.0, 0.0] # translation [x, y, z] in mm
# )
#
# # Scale
# SurfaceOperations.scale(
# [all_surfaces[0]], # surfaceNames (list)
# [1.5, 1.5, 1.5] # scaleFactors [x, y, z]
# )
#
# # Mirror across axes
# SurfaceOperations.mirror(
# [all_surfaces[0]], # surfaceNames (list)
# True, # mirrorX (bool)
# False, # mirrorY (bool)
# False # mirrorZ (bool)
# )
#
# # Mirror surface across a custom plane
# SurfaceOperations.mirror_to_plane(
# [all_surfaces[0]], # surfaceNames (list)
# [0.0, 0.0, 0.0], # planeOrigin [x, y, z] in mm
# [1.0, 0.0, 0.0] # planeNormal [x, y, z] (unit vector)
# )
#
# # Apply 4x4 transformation matrix
# # matrix = [1,0,0,0, 0,1,0,0, 0,0,1,0, 10,20,30,1] # Translation example
# # SurfaceOperations.transform([all_surfaces[0]], matrix)
#
# # Move to center of active volume
# SurfaceOperations.move_to_active_volume_center([all_surfaces[0]])
Boolean Operations​
# Combine surfaces using Union, Intersection, or Difference
# if len(all_surfaces) >= 2:
# # Union - combine both surfaces
# result = SurfaceOperations.boolean_operation(
# all_surfaces[0], # surfaceNameInputA (str)
# [all_surfaces[1]], # surfaceNamesInputB (list)
# "Union_Result", # surfaceNameResult (str, used if createNewSurface=False)
# api.SurfaceBooleanOperation.Union,
# True # createNewSurface (bool)
# )
#
# # Intersection - keep only overlapping regions
# result = SurfaceOperations.boolean_operation(
# all_surfaces[0],
# [all_surfaces[1]],
# "Intersection_Result",
# api.SurfaceBooleanOperation.Intersection,
# True
# )
#
# # Difference - subtract second from first
# result = SurfaceOperations.boolean_operation(
# all_surfaces[0],
# [all_surfaces[1]],
# "Difference_Result",
# api.SurfaceBooleanOperation.Difference,
# True
# )
# Voxel-based boolean (more robust for complex geometry)
# if len(all_surfaces) >= 2:
# result = SurfaceOperations.voxel_boolean_operation(
# all_surfaces[0], # surfaceNameInputA (str)
# [all_surfaces[1]], # surfaceNamesInputB (list)
# "VoxelBoolean_Result", # surfaceNameResult (str)
# api.SurfaceBooleanOperation.Union,
# api.SurfaceVoxelizeMethod.Fast, # voxelizationMethod (Fast or Accurate)
# [1, 1, 1], # voxelSpacing [x, y, z]
# True, # smoothArtifacts (bool)
# True # createNewSurface (bool)
# )
Mesh Processing - Remesh​
# if all_surfaces:
# # Standard remesh
# SurfaceOperations.remesh(
# [all_surfaces[0]], # surfaceNames (list)
# api.SurfaceRemeshMethod.Adaptive, # method (Adaptive or Regular)
# api.SurfaceRemeshQuality.Medium, # quality (Medium, High, Maximum)
# 100, # density (int, 11-100, percentage of original vertex count)
# False # preserveEdges (bool)
# )
#
# # Voxel-based remesh
# SurfaceOperations.voxel_remesh(
# [all_surfaces[0]], # surfaceNames (list)
# api.SurfaceVoxelizeMethod.Fast, # voxelizationMethod (Fast or Accurate)
# [1, 1, 1], # voxelSpacing [x, y, z]
# True # smoothArtifacts (bool)
# )
Mesh Processing - Smooth​
# if all_surfaces:
# SurfaceOperations.smooth_smart(
# [all_surfaces[0]], # surfaceNames (list)
# 20, # Number of iterations (int)
# 0.01, # Smoothing factor 0.0001-1.0 (float)
# True, # Enable feature edge smoothing (bool)
# 85 # Feature angle in degrees 0-180 (float)
# )
Mesh Processing - Reduce (Decimate)​
# if all_surfaces:
# # Smart reduction (preserves quality)
# SurfaceOperations.reduce_smart(
# [all_surfaces[0]], # surfaceNames (list)
# 50.0, # desiredPercentage (float, 0-100)
# True, # useDesiredPercentage (bool)
# 3000 # desiredTriangles (int, used if useDesiredPercentage=False)
# )
#
# # Feature-based reduction
# SurfaceOperations.reduce_feature_based(
# [all_surfaces[0]], # surfaceNames (list)
# 60.0, # featureAngle (float, degrees)
# 50.0, # desiredPercentage (float, 0-100)
# True, # useDesiredPercentage (bool)
# 3000 # desiredTriangles (int)
# )
#
# # Reduce by joining nearby points
# SurfaceOperations.reduce_join_near_by_points(
# [all_surfaces[0]], # surfaceNames (list)
# 0.5 # nearByPointDistance (float)
# )
Mesh Processing - Subdivide​
# if all_surfaces:
# SurfaceOperations.subdivide(
# [all_surfaces[0]], # surfaceNames (list)
# api.SurfaceSubdivisionMethod.Loop, # method (Linear, Loop, Butterfly, Adaptive)
# 1, # subdivisions (int, ignored for Adaptive)
# 1.0 # maxEdgeLength (float, for Adaptive method)
# )
Mesh Processing - Hollow​
# if all_surfaces:
# hollow_names = SurfaceOperations.hollow(
# [all_surfaces[0]], # surfaceNames (list)
# api.SurfaceHollowMethod.Fast, # method (Fast or Robust)
# api.SurfaceHollowDirection.Inward, # direction (Inward or Outward)
# 2.0, # thickness (float, wall thickness)
# api.SurfaceHollowResolution.Medium, # resolution (Low, Medium, High, Maximum)
# True # createNewSurface (bool)
# )
Mesh Processing - Fill Holes​
# if all_surfaces:
# SurfaceOperations.fill_holes(
# all_surfaces[0], # surfaceName (str)
# 100, # maxHoleSize (int, in surface units)
# api.SurfaceHoleFillingMethod.Angle # method (Angle, Area, or EarCut)
# )
Mesh Processing - Merge and Split​
# if len(all_surfaces) >= 2:
# # Merge multiple surfaces into one
# merged_name = SurfaceOperations.merge(
# all_surfaces[:2], # surfaceNames (list, at least 2)
# "Merged_Surface", # targetSurfaceName (str)
# True, # mergePoints (bool)
# False, # removeInputSurfaces (bool)
# True # createNewSurface (bool)
# )
# if all_surfaces:
# # Split surface into disconnected regions
# split_names = SurfaceOperations.split(
# all_surfaces[0], # surfaceName (str)
# 0, # numLargestShells (int, 0 = all shells)
# False # removeInputSurface (bool)
# )
Mesh Processing - Filter Shells​
# if all_surfaces:
# params = api.SurfaceFilterShellsParams()
# params.largest_shells = 3 # Number of largest shells to keep (int)
# # params.min_num_triangles = 100 # Minimum triangles (int)
# # params.min_volume = 10.0 # Minimum volume (float)
# # params.min_area = 5.0 # Minimum area (float)
# # params.retain_inside_shells = True # For InsideOrOutside method (bool)
#
# SurfaceOperations.filter_shells(
# all_surfaces[0],
# api.SurfaceFilterShellsMethod.LargestShells, # method
# params
# )
Surface Diagnostics and Repair​
# if all_surfaces:
# # Configure diagnostic checks
# checks = api.SurfaceDiagnosticsChecks()
# checks.unify = True # Merge duplicate points (bool)
# checks.shells = True # Check for shells (bool)
# checks.noise_shells = True # Check for noise shells (bool)
# checks.invalid_triangles = True # Check for invalid triangles (bool)
# checks.holes = True # Check for holes (bool)
# checks.intersection_triangles = True # Check for self-intersections (bool)
# checks.inverted_normals = True # Check for inverted normals (bool)
# checks.boundary_edges = True # Check for boundary edges (bool)
# checks.non_manifold_edges = True # Check for non-manifold edges (bool)
#
# # Run diagnostics
# result = SurfaceOperations.diagnose_surface(all_surfaces[0], checks)
# print(f"Shells: {result.shells}")
# print(f"Noise shells: {result.noise_shells}")
# print(f"Invalid triangles: {result.invalid_triangles}")
# print(f"Holes: {result.holes}")
# print(f"Self-intersections: {result.intersection_triangles}")
# print(f"Inverted normals: {result.inverted_normals}")
# print(f"Boundary edges: {result.boundary_edges}")
# print(f"Non-manifold edges: {result.non_manifold_edges}")
#
# # Fix surface issues
# SurfaceOperations.fix_surface(all_surfaces[0], checks)
Surface Registration (Alignment)​
# if len(all_surfaces) >= 2:
# # Point-based registration (ICP)
# results = SurfaceOperations.point_matching_registration(
# [all_surfaces[0]], # movableSurfaceNames (list)
# all_surfaces[1], # fixedObjectName (str)
# api.RegistrationTargetObject.Surface, # targetObjectType (Surface or Mask)
# api.RegistrationMode.RigidBody, # registrationMode (RigidBody, Similarity, Affine)
# True, # startByMatchingCentroids (bool)
# 1000 # maximumLandmarks (int)
# )
# for row in results:
# print(f"Movable: {row.movable_surface}, Fixed: {row.fixed_target}, RMSE: {row.rmse_millimeters} mm")
#
# # Feature-based registration
# results = SurfaceOperations.feature_matching_registration(
# [all_surfaces[0]], # movableSurfaceNames (list)
# all_surfaces[1], # fixedObjectName (str)
# api.RegistrationTargetObject.Surface, # targetObjectType
# 3, # globalIterations (int)
# 10.0, # overlapRadius (float)
# 0.001 # curvatureThreshold (float)
# )
#
# # Combined point + feature registration
# results = SurfaceOperations.point_plus_feature_matching_registration(
# [all_surfaces[0]], # movableSurfaceNames (list)
# all_surfaces[1], # fixedObjectName (str)
# api.RegistrationTargetObject.Surface, # targetObjectType
# api.RegistrationMode.RigidBody, # registrationMode
# True, # startByMatchingCentroids (bool)
# 1000, # maximumLandmarks (int)
# 3, # globalIterations (int)
# 10.0, # overlapRadius (float)
# 0.001 # curvatureThreshold (float)
# )
Project to Plane​
# if all_surfaces:
# SurfaceOperations.project_to_plane(
# [all_surfaces[0]], # surfaceNames (list)
# [0.0, 0.0, 0.0], # planeOrigin [x, y, z]
# [0.0, 0.0, 1.0] # planeNormal [x, y, z]
# )
Convert Surface to Mask​
# Requires an active volume to be loaded
# if all_surfaces and all_volumes:
# app.set_active_volume(all_volumes[0])
# mask_names = SurfaceOperations.convert_to_mask(
# [all_surfaces[0]], # surfaceNames (list)
# api.SurfaceVoxelConversionMethod.Filled, # voxelizationMethod (Filled, ThickContour, ThinContour, LineContour)
# True # smoothArtifacts (bool)
# )
# print(f"Created masks: {mask_names}")
Convert Surface to Volume Mesh​
# if all_surfaces:
# # Set volume mesh parameters first
# params = api.VolumeMeshParams()
# params.resolution = api.VolumeMeshResolution.Moderate # VeryCoarse, Coarse, Moderate, Fine, VeryFine, Custom
# params.optimize_surface = True # Remesh surface before meshing (bool)
# params.surface_opt_steps = 3 # Surface optimization steps (int)
# params.volume_opt_steps = 3 # Volume optimization steps (int)
# params.growth_rate = 0.3 # Mesh growth rate 0.0-1.0 (float)
# # For Custom resolution:
# # params.max_element_size = 5.0 # Maximum element size (float)
# # params.min_element_size = 1.0 # Minimum element size (float)
#
# SurfaceOperations.set_volume_mesh_parameters(all_surfaces[0], params)
#
# # Convert to volume mesh
# volume_mesh_names = SurfaceOperations.convert_to_volume_mesh(
# [all_surfaces[0]], # surfaceNames (list)
# api.VolumeMeshMethod.Auto3D # method (Auto3D or Grid3D)
# )
# print(f"Created volume meshes: {volume_mesh_names}")
Render Properties​
# RenderOps = SurfaceOperations.get_render_properties_operations()
# if all_surfaces:
# # Set representation mode
# RenderOps.set_representation([all_surfaces[0]], api.SurfaceRepresentation.Solid) # Points, Wireframe, Solid, SolidEdges
#
# # Set color (RGB values 0.0-1.0)
# RenderOps.set_color([all_surfaces[0]], [0.8, 0.2, 0.2]) # Red
#
# # Set random color
# RenderOps.set_random_color([all_surfaces[0]])
#
# # Set opacity (0.0-1.0)
# RenderOps.set_opacity([all_surfaces[0]], 0.8)
#
# # Set interpolation
# RenderOps.set_interpolation([all_surfaces[0]], api.SurfaceInterpolation.Phong) # Flat, Gouraud, Phong
print("Surface operations tutorial completed successfully.")
Related Resources​
- API Reference - API documentation
- Quick Reference - Common methods at a glance