framework/cg/freecad/utils/export_freecad_scene.py

146 lines
4.7 KiB
Python
Raw Normal View History

# -*- coding: utf-8 -*-
# Copyright (C) 2023 Ilia Kurochkin <brothermechanic@gmail.com>
#
# 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)