# -*- coding: utf-8 -*- # Copyright (C) 2023 Ilia Kurochkin # # This program is free software; you can redistribute it and/or modify # it under the terms of the GNU General Public License as published by # the Free Software Foundation; either version 3 of the License, or # (at your option) any later version. # # This program is distributed in the hope that it will be useful, # but WITHOUT ANY WARRANTY; without even the implied warranty of # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the # GNU General Public License for more details. ''' DESCRIPTION. - Reads a FreeCAD .FCStd file. - Set tesselation parts to mesh. - Return scene as JSON dictionary. ''' __version__ = '0.1' import json import FreeCAD import Part import Mesh import MeshPart import logging import math import sys from freecad.utils.is_object_solid import is_object_solid logger = logging.getLogger(__name__) logging.basicConfig(level=logging.INFO) def freecad_to_json(filename, tesselation_method='Standard', linear_deflection=0.1, angular_deflection=30.0, fem_size=50.0, skiphidden=True, nonsolid_property='Robossembler_NonSolid'): ''' Reads a FreeCAD .FCStd file and return json assembly. ''' scene = {} js_objs = {} doc = FreeCAD.open(filename) docname = doc.Name # collect all materials fem_mats = [] for fem_mat in doc.Objects: if fem_mat.isDerivedFrom('App::MaterialObjectPython'): fem_mats.append(fem_mat) for obj in doc.Objects: js_obj = {} if skiphidden: if not obj.Visibility: continue if obj.isDerivedFrom('PartDesign::CoordinateSystem'): js_obj['type'] = 'LCS' elif obj.isDerivedFrom('Part::Feature'): js_obj['type'] = 'PART' # filter for nonsolids if is_object_solid(obj) or hasattr(obj, nonsolid_property): # create mesh from shape shape = obj.Shape shape = obj.Shape.copy() shape.Placement = obj.Placement.inverse().multiply(shape.Placement) meshfromshape = doc.addObject('Mesh::Feature', 'Mesh') if tesselation_method == 'Standard': meshfromshape.Mesh = MeshPart.meshFromShape( Shape=shape, LinearDeflection=linear_deflection, AngularDeflection=math.radians(angular_deflection), Relative=False) elif tesselation_method == 'FEM': meshfromshape.Mesh = MeshPart.meshFromShape( Shape=shape, MaxLength=fem_size) else: raise TypeError('Wrong tesselation method! ' 'Standard and FEM methods are supported only!') break t = meshfromshape.Mesh.Topology verts = [[v.x, v.y, v.z] for v in t[0]] faces = t[1] js_obj['mesh'] = (verts, faces) # one material for the whole object for fem_mat in fem_mats: for ref in fem_mat.References: if ref[0].Label == obj.Label: js_obj['material'] = fem_mat.Material # skip for other object's types else: continue js_obj['fc_location'] = tuple(obj.Placement.Base) js_obj['fc_rotation'] = obj.Placement.Rotation.Q # construct assembly hierarchy obj_parent = obj.getParentGeoFeatureGroup() obj_child_name = None parents = {} deep_index = 0 while obj_parent: parent = {} parent['fc_location'] = tuple(obj_parent.Placement.Base) parent['fc_rotation'] = obj_parent.Placement.Rotation.Q obj_child_name = obj_parent.Label obj_parent = obj_parent.getParentGeoFeatureGroup() if obj_parent: parent['parent'] = obj_parent.Label else: parent['parent'] = None parents[obj_child_name] = parent parent['deep_index'] = deep_index deep_index += 1 js_obj['hierarchy'] = parents js_objs[obj.Label] = js_obj FreeCAD.closeDocument(docname) scene[filename] = js_objs logger.info('Stored %s objects without errors', len(js_objs)) print(json.dumps(scene)) args = sys.argv[2:] for arg in args[2:5]: args[args.index(arg)] = float(arg) for arg in args[5:6]: args[args.index(arg)] = bool(arg) #for num, item in enumerate(args): # print(num, type(item)) freecad_to_json(*args)