import json import re import os import numpy as np """ These functions are responsible for parsing CSG files (constructive solid geometry), which are files produced by OpenSCAD, containing no loop, variables etc. """ def multmatrix_parse(parameters): matrix = np.array(json.loads(parameters), dtype=float) matrix[0, 3] /= 1000.0 matrix[1, 3] /= 1000.0 matrix[2, 3] /= 1000.0 return matrix def cube_parse(parameters, dilatation): results = re.findall(r'^size = (.+), center = (.+)$', parameters) if len(results) != 1: print("! Can't parse CSG cube parameters: "+parameters) exit() extra = np.array([dilatation]*3) return (extra + np.array(json.loads(results[0][0]), dtype=float)/1000.0), results[0][1] == 'true' def cylinder_parse(parameters, dilatation): results = re.findall( r'h = (.+), r1 = (.+), r2 = (.+), center = (.+)', parameters) if len(results) != 1: print("! Can't parse CSG cylinder parameters: "+parameters) exit() result = results[0] extra = np.array([dilatation/2, dilatation]) return (extra + np.array([result[0], result[1]], dtype=float)/1000.0), result[3] == 'true' def sphere_parse(parameters, dilatation): results = re.findall(r'r = (.+)$', parameters) if len(results) != 1: print("! Can't parse CSG sphere parameters: "+parameters) exit() return dilatation + float(results[0])/1000.0 def extract_node_parameters(line): line = line.strip() parts = line.split('(', 1) node = parts[0] parameters = parts[1] if parameters[-1] == ';': parameters = parameters[:-2] if parameters[-1] == '{': parameters = parameters[:-3] return node, parameters def T(x, y, z): m = np.eye(4) m.T[3, :3] = [x, y, z] return m def parse_csg(data, dilatation): shapes = [] lines = data.split("\n") matrices = [] for line in lines: line = line.strip() if line != '': if line[-1] == '{': node, parameters = extract_node_parameters(line) if node == 'multmatrix': matrix = multmatrix_parse(parameters) else: matrix = np.array(np.identity(4)) matrices.append(matrix) elif line[-1] == '}': matrices.pop() else: node, parameters = extract_node_parameters(line) transform = np.array(np.identity(4)) for entry in matrices: transform = transform@entry if node == 'cube': size, center = cube_parse(parameters, dilatation) if not center: transform = transform @ \ T(size[0]/2.0, size[1]/2.0, size[2]/2.0) shapes.append({ 'type': 'cube', 'parameters': size, 'transform': transform }) if node == 'cylinder': size, center = cylinder_parse(parameters, dilatation) if not center: transform = transform @ T(0, 0, size[0]/2.0) shapes.append({ 'type': 'cylinder', 'parameters': size, 'transform': transform }) if node == 'sphere': shapes.append({ 'type': 'sphere', 'parameters': sphere_parse(parameters, dilatation), 'transform': transform }) return shapes def process(filename, dilatation): tmp_data = os.getcwd()+'/_tmp_data.csg' os.system('openscad '+filename+' -o '+tmp_data) with open(tmp_data, "r", encoding="utf-8") as stream: data = stream.read() os.system('rm '+tmp_data) return parse_csg(data, dilatation)