awv3
Version:
⚡ AWV3 embedded CAD
699 lines (609 loc) • 22.5 kB
JavaScript
import _extends from "@babel/runtime/helpers/extends";
import _regeneratorRuntime from "@babel/runtime/regenerator";
import _taggedTemplateLiteralLoose from "@babel/runtime/helpers/taggedTemplateLiteralLoose";
import _asyncToGenerator from "@babel/runtime/helpers/asyncToGenerator";
var _templateObject = /*#__PURE__*/ _taggedTemplateLiteralLoose(["Url undefined"], ["Url undefined"]),
_templateObject2 = /*#__PURE__*/ _taggedTemplateLiteralLoose(["Blob undefined"], ["Blob undefined"]),
_templateObject3 = /*#__PURE__*/ _taggedTemplateLiteralLoose(["Context undefined"], ["Context undefined"]),
_templateObject4 = /*#__PURE__*/ _taggedTemplateLiteralLoose(["Object undefined"], ["Object undefined"]);
// TODO
// 1. doesn't need to be coupled to canvas AT ALL
// 2. Canvas events should be renamed to broadcasts to make sense
import * as THREE from 'three';
import v4 from 'uuid-v4';
import { errUndefined, mergePoints } from './helpers';
import Object3 from '../three/object3';
import Region from '../three/region';
import Model from '../three/model';
import Defaults from './defaults';
import { parseMesh, parseLine } from './geometry'; // options.pool = THREE.Object3D
// options.factory = function()
// options.
// when a pool exists operations can be fulfilled without needing a complex factory
// this applies to assemblies, or even connected classcad transmissions
// operations should always be awaited, the geometry parser should return promises as well
// structural elements should be visible and uncompressed, only geometry should go to the worker
// if the structure is laid out clear it can be processed in sequence
var Parser =
/*#__PURE__*/
function () {
function Parser() {// ...
}
var _proto = Parser.prototype;
_proto.stream =
/*#__PURE__*/
function () {
var _stream = _asyncToGenerator(
/*#__PURE__*/
_regeneratorRuntime.mark(function _callee2(url, options) {
var requests, results, context;
return _regeneratorRuntime.wrap(function _callee2$(_context2) {
while (1) {
switch (_context2.prev = _context2.next) {
case 0:
if (url === void 0) {
url = errUndefined(_templateObject);
}
if (options === void 0) {
options = {};
}
// Always assume an array
requests = Array.isArray(url) ? url : [url]; // Map request-array into an array of promises and await them through Promises.all
_context2.next = 5;
return Promise.all(requests.map(
/*#__PURE__*/
function () {
var _ref = _asyncToGenerator(
/*#__PURE__*/
_regeneratorRuntime.mark(function _callee(item) {
var context, response;
return _regeneratorRuntime.wrap(function _callee$(_context) {
while (1) {
switch (_context.prev = _context.next) {
case 0:
// This function is allowed to run async, each request must race
// A for-of loop instead would run requests in sequence, but it would waste performance
// Create context for each request
context = createContext(options); // Request URL, wait until downloaded, then parse
_context.next = 3;
return fetch(item).then(function (r) {
return r.json();
});
case 3:
response = _context.sent;
_context.next = 6;
return handleResult(context, response);
case 6:
_context.next = 8;
return Promise.all(context.promises);
case 8:
context.mapitem = item; // Return result
return _context.abrupt("return", context);
case 10:
case "end":
return _context.stop();
}
}
}, _callee, this);
}));
return function (_x3) {
return _ref.apply(this, arguments);
};
}()));
case 5:
results = _context2.sent;
// All promises have been fulfilled, return results
context = mergeContext(results);
context.options.callback({
type: Parser.Factory.Finished,
context: context
});
return _context2.abrupt("return", context);
case 9:
case "end":
return _context2.stop();
}
}
}, _callee2, this);
}));
return function stream(_x, _x2) {
return _stream.apply(this, arguments);
};
}(); // Same pattern as above
_proto.parse =
/*#__PURE__*/
function () {
var _parse = _asyncToGenerator(
/*#__PURE__*/
_regeneratorRuntime.mark(function _callee4(blob, options) {
var requests, results, context;
return _regeneratorRuntime.wrap(function _callee4$(_context4) {
while (1) {
switch (_context4.prev = _context4.next) {
case 0:
if (blob === void 0) {
blob = errUndefined(_templateObject2);
}
if (options === void 0) {
options = {};
}
requests = Array.isArray(blob) ? blob : [blob];
_context4.next = 5;
return Promise.all(requests.map(
/*#__PURE__*/
function () {
var _ref2 = _asyncToGenerator(
/*#__PURE__*/
_regeneratorRuntime.mark(function _callee3(item) {
var context;
return _regeneratorRuntime.wrap(function _callee3$(_context3) {
while (1) {
switch (_context3.prev = _context3.next) {
case 0:
context = createContext(options);
_context3.next = 3;
return handleResult(context, item);
case 3:
_context3.next = 5;
return Promise.all(context.promises);
case 5:
return _context3.abrupt("return", context);
case 6:
case "end":
return _context3.stop();
}
}
}, _callee3, this);
}));
return function (_x6) {
return _ref2.apply(this, arguments);
};
}()));
case 5:
results = _context4.sent;
context = mergeContext(results);
context.options.callback({
type: Parser.Factory.Finished,
context: context
});
return _context4.abrupt("return", context);
case 9:
case "end":
return _context4.stop();
}
}
}, _callee4, this);
}));
return function parse(_x4, _x5) {
return _parse.apply(this, arguments);
};
}();
return Parser;
}();
export { Parser as default };
Parser.Factory = {
Blob: 'Blob',
Link: 'Link',
Assembly: 'Assembly',
Part: 'Part',
Model: 'Model',
Mesh: 'Mesh',
Line: 'Line',
Cone: 'Cone',
Vertex: 'Vertex',
Csys: 'Csys',
Text: 'Text',
Transform: 'Transform',
Remove: 'Remove',
Started: 'Started',
Finished: 'Finished'
};
export function createContext(options, resolve, reject, command) {
if (options === void 0) {
options = {};
}
if (resolve === void 0) {
resolve = undefined;
}
if (reject === void 0) {
reject = undefined;
}
if (command === void 0) {
command = '';
}
// Set defaults
options = _extends({
callback: typeof options === 'function' ? options : function () {
return null;
},
session: undefined,
id: v4()
}, Defaults.all, options);
return {
// Transaction ID
id: options.id,
// Transaction resolve
resolve: resolve,
// Transaction reject
reject: reject,
// All generated promises
promises: [],
// Hint for mapping results later
command: command || '',
// Options
options: options,
// An array of context objects, for instance when several urls are parsed at once
array: [],
// Part hashtable using load-time hints like filenames
map: {},
// An array of resulting 3D models
models: [],
// An array of JSON patches
patches: [],
// An array of ClassCAD results
results: [],
// An array of ClassCAD error messages
errors: [],
// Bytes processed (compressed)
bytes: 0,
// Bytes processed (uncompressed)
bytesUncompressed: 0,
// Timer
socketTime: null,
// Parsing time
time: 0
};
}
export function mergeContext(context) {
if (context === void 0) {
context = errUndefined(_templateObject3);
}
if (Array.isArray(context)) {
if (context.length === 1) return mergeContext(context[0]);else {
var result = createContext();
result.array = context;
return mergeContext(result);
}
}
delete context.resolve;
delete context.reject;
delete context.promises;
delete context.mapitem;
if (context.array.length > 0 && context.models.length === 0) {
for (var _iterator = context.array, _isArray = Array.isArray(_iterator), _i = 0, _iterator = _isArray ? _iterator : _iterator[Symbol.iterator]();;) {
var _ref3;
if (_isArray) {
if (_i >= _iterator.length) break;
_ref3 = _iterator[_i++];
} else {
_i = _iterator.next();
if (_i.done) break;
_ref3 = _i.value;
}
var _part = _ref3;
if (!!_part.mapitem) context.map[_part.mapitem] = _part;
var partContext = mergeContext(_part);
partContext.models.forEach(function (model) {
return context.models.push(model);
});
partContext.patches.forEach(function (patch) {
return context.patches.push(patch);
});
}
}
context.firstModel = undefined;
if (context.models.length > 0) context.firstModel = context.models[0];
context.firstResult = undefined;
if (context.results.length > 0) context.firstResult = context.results[0].result;
return context;
}
export function parseGeometry(data, context) {
var models = context.models,
options = context.options;
var callback = options.callback,
session = options.session;
var _data$properties = data.properties,
min = _data$properties.min,
max = _data$properties.max,
material = _data$properties.material;
var color = material.color,
emissive = material.emissive,
opacity = material.opacity,
map = material.map;
var model = new Model();
model.name = 'geometry';
model.userData = {
id: data.id,
meta: data.meta
};
if (color) {
data.properties.material.color = new THREE.Color(color[0] / 255, color[1] / 255, color[2] / 255);
}
if (emissive) {
data.properties.material.emissive = new THREE.Color(emissive[0] / 255, emissive[1] / 255, emissive[2] / 255);
}
if (map && session) {
var _map = null;
if (data.properties.material.map) {
var key = data.properties.material.map;
_map = session.materials[key];
if (!_map) {
var path = session.globals.resources[key];
_map = session.materials[key] = new THREE.TextureLoader().setCrossOrigin('anonymous').load(path);
_map.wrapS = THREE.RepeatWrapping;
_map.wrapT = THREE.RepeatWrapping;
}
}
data.properties.material.map = _map;
}
if (!!min && !!max) {
data.properties.box = new THREE.Box3(new (Function.prototype.bind.apply(THREE.Vector3, [null].concat(min)))(), new (Function.prototype.bind.apply(THREE.Vector3, [null].concat(max)))());
var boundingSphere = new THREE.Sphere();
data.properties.box.getBoundingSphere(boundingSphere);
data.properties.sphere = boundingSphere;
model.bounds = model.bounds || {
box: new THREE.Box3(),
sphere: new THREE.Sphere()
};
model.bounds.box = data.properties.box;
model.bounds.sphere = data.properties.sphere;
}
if (data.mesh !== undefined) {
var mesh = parseMesh(data, context);
if (mesh) {
model.add(mesh);
callback({
type: Parser.Factory.Mesh,
model: model,
data: mesh,
meta: mesh.meta,
material: mesh.material
});
}
}
if (data.line !== undefined) {
var _mesh = parseLine(data, context);
if (_mesh) {
model.add(_mesh);
callback({
type: Parser.Factory.Line,
model: model,
data: _mesh,
meta: _mesh.meta,
material: _mesh.material
});
}
if (data.line.points != undefined && data.line.points.length) {
data.line.points = mergePoints(data.line.points);
var region = new Region();
region.points = data.line.points;
region.points.forEach(function (point) {
return point.meta.position = new (Function.prototype.bind.apply(THREE.Vector3, [null].concat(point.meta.position)))();
});
model.add(region);
callback({
type: Parser.Factory.Vertex,
model: model,
data: region
});
}
} // cones
if (!!data.cones) {
for (var _iterator2 = data.cones, _isArray2 = Array.isArray(_iterator2), _i2 = 0, _iterator2 = _isArray2 ? _iterator2 : _iterator2[Symbol.iterator]();;) {
var _ref4;
if (_isArray2) {
if (_i2 >= _iterator2.length) break;
_ref4 = _iterator2[_i2++];
} else {
_i2 = _iterator2.next();
if (_i2.done) break;
_ref4 = _i2.value;
}
var _cone = _ref4;
callback({
type: Parser.Factory.Cone,
model: model,
data: _cone,
meta: _cone.meta,
material: _cone.material
});
}
} // csys
if (!!data.coordinateSystems) {
for (var _iterator3 = data.coordinateSystems, _isArray3 = Array.isArray(_iterator3), _i3 = 0, _iterator3 = _isArray3 ? _iterator3 : _iterator3[Symbol.iterator]();;) {
var _ref5;
if (_isArray3) {
if (_i3 >= _iterator3.length) break;
_ref5 = _iterator3[_i3++];
} else {
_i3 = _iterator3.next();
if (_i3.done) break;
_ref5 = _i3.value;
}
var _csys = _ref5;
callback({
type: Parser.Factory.Csys,
model: model,
data: _csys,
meta: _csys.meta
});
}
} //text
if (!!data.text) {
for (var _iterator4 = data.text, _isArray4 = Array.isArray(_iterator4), _i4 = 0, _iterator4 = _isArray4 ? _iterator4 : _iterator4[Symbol.iterator]();;) {
var _ref6;
if (_isArray4) {
if (_i4 >= _iterator4.length) break;
_ref6 = _iterator4[_i4++];
} else {
_i4 = _iterator4.next();
if (_i4.done) break;
_ref6 = _i4.value;
}
var _text = _ref6;
callback({
type: Parser.Factory.Text,
model: model,
data: _text,
meta: _text.meta
});
}
}
models.push(model);
callback({
type: Parser.Factory.Model,
model: model,
data: data,
meta: data.meta
});
}
export function handleResult(_x7, _x8) {
return _handleResult.apply(this, arguments);
}
function _handleResult() {
_handleResult = _asyncToGenerator(
/*#__PURE__*/
_regeneratorRuntime.mark(function _callee6(context, object) {
var promise;
return _regeneratorRuntime.wrap(function _callee6$(_context6) {
while (1) {
switch (_context6.prev = _context6.next) {
case 0:
if (context === void 0) {
context = errUndefined(_templateObject3);
}
if (object === void 0) {
object = errUndefined(_templateObject4);
}
promise = new Promise(
/*#__PURE__*/
function () {
var _ref7 = _asyncToGenerator(
/*#__PURE__*/
_regeneratorRuntime.mark(function _callee5(resolve) {
var length, result, tempResolve, results, json;
return _regeneratorRuntime.wrap(function _callee5$(_context5) {
while (1) {
switch (_context5.prev = _context5.next) {
case 0:
if (object.type === 'Buffer' && !!object.data) {
// Node Buffer
object = Uint8Array.from(object.data);
}
if (object instanceof Uint8Array) {
// Wrap binary data into json Blob
object = {
command: 'Blob',
type: 'Binary',
data: object
};
}
if (typeof object === 'string' || object instanceof String) {
// Plain text package
length = object.length;
object = JSON.parse(object);
context.bytes += length;
if (object.command !== 'Blob') context.bytesUncompressed += length;
}
if (!Array.isArray(object)) {
_context5.next = 8;
break;
}
_context5.next = 6;
return Promise.all(object.map(function (item) {
return handleResult(context, item);
}));
case 6:
_context5.next = 29;
break;
case 8:
if (!(object.command === 'Endpoint')) {
_context5.next = 12;
break;
}
// Scaled endpoints
context.results.push({
command: 'Result',
from: 'ip',
result: object
});
_context5.next = 29;
break;
case 12:
if (!(object.command === 'Blob')) {
_context5.next = 19;
break;
}
// Pass options to worker
object.options = {
materials: context.options.materials,
interpolatePoints: context.options.interpolatePoints // Process package
};
_context5.next = 16;
return Defaults.unpack(object, function (data) {
if (context.options.materials.lazy) requestAnimationFrame(function () {
return parseGeometry(data, context);
});else parseGeometry(data, context);
});
case 16:
result = _context5.sent;
_context5.next = 29;
break;
case 19:
if (!(context.resolve && object.command === 'Result' && object.from === 'EndFrame' && object.transactionID === context.id)) {
_context5.next = 28;
break;
}
// EndFrames are valid only for transactions that have explicit resolves marked in their respective contexts
// Everything else will be resolved manually through context.promises
tempResolve = context.resolve;
_context5.next = 23;
return Promise.all(context.promises);
case 23:
results = _context5.sent;
context = mergeContext(context);
tempResolve(context);
_context5.next = 29;
break;
case 28:
if (object.command === 'Result' && object.from !== 'BeginFrame' && object.from !== 'EndFrame') {
// Decode result, it *should* be JSON
try {
json = JSON.parse(object.result);
object.result = json;
} catch (e) {}
context.results.push(object);
} else if (object.command === 'Patch') {
context.patches = context.patches.concat(object.ops);
} else if (object.command === 'ErrorMessage') {
// ClassCAD error messages
context.errors.push(object.attributes);
console.warn('ClassCAD > State: ' + object.attributes.errorState + ', Code: ' + object.attributes.errorCode + ', Message: ' + object.attributes.errorMessage);
}
case 29:
resolve(context);
case 30:
case "end":
return _context5.stop();
}
}
}, _callee5, this);
}));
return function (_x9) {
return _ref7.apply(this, arguments);
};
}());
context.promises.push(promise);
_context6.next = 6;
return promise;
case 6:
return _context6.abrupt("return", _context6.sent);
case 7:
case "end":
return _context6.stop();
}
}
}, _callee6, this);
}));
return _handleResult.apply(this, arguments);
}