import os import json from io import StringIO import logging log = logging.getLogger(__name__) import cadquery from . import Exporter, register_exporter from .. import Component, Part, Assembly @register_exporter('json', Part) class ThreejsJSONExporter(Exporter): """ Export the :class:`Part ` to a *three.js JSON v3* file format. =============== ====================== **Name** ``json`` **Exports** :class:`Part ` **Spec** `three.js JSON model format v3 specification `_ =============== ====================== For information on how to load in a webpage, look to your WebGL framework of choice: * ThreeJS: https://threejs.org/docs/#api/loaders/ObjectLoader * A-Frame: https://aframe.io/docs/0.7.0/core/asset-management-system.html#lt-a-asset-item-gt """ def __call__(self, filename="out.json", world=False): """ Write to file. :param filename: file to write :type filename: :class:`str` :param world: if True, use world coordinates, otherwise use local :type world: :class:`bool` """ log.debug("exporting: %r", self.obj) log.debug(" to: %s", filename) with open(filename, 'w') as fh: fh.write(self.get_str(world=world)) def get_str(self, *args, **kwargs): """ Get file string. (same arguments as :meth:`get_export_gltf_dict`) :return: JSON string :rtype: :class:`str` """ data = self.get_dict(*args, **kwargs) return json.dumps(data) def get_dict(self, world=False): """ Get the part's geometry as a :class:`dict` :param world: if True, use world coordinates, otherwise use local :type world: :class:`bool` :return: JSON model format :rtype: :class:`dict` """ data = {} with StringIO() as stream: obj = self.obj.world_obj if world else self.obj.local_obj cadquery.exporters.exportShape(obj, 'TJS', stream) stream.seek(0) data = json.load(stream) # Change diffuse colour to that in render properties data['materials'][0]['colorDiffuse'] = [ val / 255. for val in self.obj._render.rgb ] data['materials'][0]['transparency'] = self.obj._render.alpha return data @register_exporter('json', Assembly) class ThreejsJSONAssemblyExporter(Exporter): """ Export an :class:`Assembly` into **multiple** ``json`` files. =============== ====================== **Name** ``json`` **Exports** :class:`Assembly` **Spec** `three.js JSON model format v3 specification `_ =============== ====================== .. warning:: The *three.js JSON v3* format does not support multiple models (or, at least, not as far as I can tell). So this exporter will create multiple files, one per part. If you're after a more modern WebGL supported export, consider using :class:`GLTFExporter ` instead. """ def __call__(self, filename='out.json', world=False): self._write_file(self.obj, filename, world=world) @classmethod def _write_file(cls, obj, filename, world=False): # recursive method to iterate through children if isinstance(obj, Assembly): # Object has no geometry, iter through components obj.solve() for (name, child) in obj.components.items(): s = os.path.splitext(filename) cls._write_file(child, "%s.%s%s" % (s[0], name, s[1]), world=True) else: ThreejsJSONExporter(obj)(filename, world=world)