"""Fabex 'oclSample.py' Functions to sample mesh or curve data for OpenCAMLib processing. """ from math import radians, tan try: import ocl except ImportError: try: import opencamlib as ocl except ImportError: pass try: from bl_ext.blender_org.stl_format_legacy import blender_utils except ImportError: pass import mathutils from ..constants import OCL_SCALE, _PREVIOUS_OCL_MESH from ..simple import activate from ..exception import CamException from ..operators.async_op import progress_async def get_oclSTL(operation): """Get the oclSTL representation from the provided operation. This function iterates through the objects in the given operation and constructs an oclSTL object by extracting triangle data from mesh, curve, font, or surface objects. It activates each object and checks its type to determine if it can be processed. If no valid objects are found, it raises an exception. Args: operation (Operation): An object containing a collection of objects Returns: ocl.STLSurf: An oclSTL object containing the triangles derived from the valid objects. Raises: CamException: If no mesh, curve, or equivalent object is found in """ me = None oclSTL = ocl.STLSurf() found_mesh = False for collision_object in operation.objects: activate(collision_object) if ( collision_object.type == "MESH" or collision_object.type == "CURVE" or collision_object.type == "FONT" or collision_object.type == "SURFACE" ): found_mesh = True global_matrix = mathutils.Matrix.Identity(4) faces = blender_utils.faces_from_mesh( collision_object, global_matrix, operation.use_modifiers ) for face in faces: t = ocl.Triangle( ocl.Point( face[0][0] * OCL_SCALE, face[0][1] * OCL_SCALE, (face[0][2] + operation.skin) * OCL_SCALE, ), ocl.Point( face[1][0] * OCL_SCALE, face[1][1] * OCL_SCALE, (face[1][2] + operation.skin) * OCL_SCALE, ), ocl.Point( face[2][0] * OCL_SCALE, face[2][1] * OCL_SCALE, (face[2][2] + operation.skin) * OCL_SCALE, ), ) oclSTL.addTriangle(t) # FIXME needs to work with collections if not found_mesh: raise CamException( "This Operation Requires a Mesh or Curve Object or Equivalent (e.g. Text, Volume)." ) return oclSTL async def ocl_sample(operation, chunks, use_cached_mesh=False): """Sample points using a specified cutter and operation. This function takes an operation and a list of chunks, and samples points based on the specified cutter type and its parameters. It supports various cutter types such as 'END', 'BALLNOSE', 'VCARVE', 'CYLCONE', 'BALLCONE', and 'BULLNOSE'. The function can also utilize a cached mesh for efficiency. The sampled points are returned after processing all chunks. Args: operation (Operation): An object containing the cutter type, diameter, minimum Z value, tip angle, and other relevant parameters. chunks (list): A list of chunk objects that contain point data to be processed. use_cached_mesh (bool): A flag indicating whether to use a cached mesh if available. Defaults to False. Returns: list: A list of sampled CL points generated by the cutter. """ global _PREVIOUS_OCL_MESH op_cutter_type = operation.cutter_type op_cutter_diameter = operation.cutter_diameter op_minz = operation.min_z op_cutter_tip_angle = radians(operation.cutter_tip_angle) / 2 if op_cutter_type == "VCARVE": cutter_length = (op_cutter_diameter / tan(op_cutter_tip_angle)) / 2 else: cutter_length = 10 cutter = None if op_cutter_type == "END": cutter = ocl.CylCutter((op_cutter_diameter + operation.skin * 2) * 1000, cutter_length) elif op_cutter_type == "BALLNOSE": cutter = ocl.BallCutter((op_cutter_diameter + operation.skin * 2) * 1000, cutter_length) elif op_cutter_type == "VCARVE": cutter = ocl.ConeCutter( (op_cutter_diameter + operation.skin * 2) * 1000, op_cutter_tip_angle, cutter_length ) elif op_cutter_type == "CYLCONE": cutter = ocl.CylConeCutter( (operation.cylcone_diameter / 2 + operation.skin) * 2000, (op_cutter_diameter + operation.skin * 2) * 1000, op_cutter_tip_angle, ) elif op_cutter_type == "BALLCONE": cutter = ocl.BallConeCutter( (operation.ball_radius + operation.skin) * 2000, (op_cutter_diameter + operation.skin * 2) * 1000, op_cutter_tip_angle, ) elif op_cutter_type == "BULLNOSE": cutter = ocl.BullCutter( (op_cutter_diameter + operation.skin * 2) * 1000, operation.bull_corner_radius * 1000, cutter_length, ) else: print("Cutter Unsupported: {0}\n".format(op_cutter_type)) quit() bdc = ocl.BatchDropCutter() if use_cached_mesh and _PREVIOUS_OCL_MESH is not None: oclSTL = _PREVIOUS_OCL_MESH else: oclSTL = get_oclSTL(operation) _PREVIOUS_OCL_MESH = oclSTL bdc.setSTL(oclSTL) bdc.setCutter(cutter) for chunk in chunks: for coord in chunk.get_points_np(): bdc.appendPoint(ocl.CLPoint(coord[0] * 1000, coord[1] * 1000, op_minz * 1000)) await progress_async("OpenCAMLib Sampling") bdc.run() cl_points = bdc.getCLPoints() return cl_points