awv3
Version:
⚡ AWV3 embedded CAD
823 lines (693 loc) • 26.6 kB
JavaScript
import _regeneratorRuntime from "@babel/runtime/regenerator";
import _asyncToGenerator from "@babel/runtime/helpers/asyncToGenerator";
import _extends from "@babel/runtime/helpers/extends";
import _taggedTemplateLiteralLoose from "@babel/runtime/helpers/taggedTemplateLiteralLoose";
import _inheritsLoose from "@babel/runtime/helpers/inheritsLoose";
import _assertThisInitialized from "@babel/runtime/helpers/assertThisInitialized";
var _templateObject = /*#__PURE__*/ _taggedTemplateLiteralLoose(["connection must be initialized with a session"], ["connection must be initialized with a session"]);
import * as THREE from 'three';
import throttle from 'lodash/throttle';
import Lifecycle from './lifecycle.js';
import { arrayDiff, emptyObject, collectNestedIds } from './helpers';
import { actions } from './store/connections';
import { actions as globalActions } from './store/globals';
import { actions as pluginActions, base as pluginBase } from './store/plugins';
import { errUndefined } from '../core/helpers';
import Base from '../communication/base';
/**
* @class Connection manages a local or remote connection to a running ClassCAD instances.
ClassCAD will then drive the tree-state and cause visual actions, models, meshes, movement, etc.
In theory a ClassCAD instance is not needed, results could be fed statically using parse or stream.
*/
var Connection =
/*#__PURE__*/
function (_Lifecycle) {
_inheritsLoose(Connection, _Lifecycle);
function Connection(session, props) {
var _this;
if (session === void 0) {
session = errUndefined(_templateObject);
}
_this = _Lifecycle.call(this, session, actions, function (state) {
return state.connections[_this.id];
}, _extends({
name: 'Part'
}, props, {
connected: false,
activeFeature: undefined,
activeRoot: undefined,
defaultFeatureVisibility: false,
undo: {
count: 0,
current: 0
},
messages: [],
plugins: [],
tree: {
'1': {
id: 1,
name: '',
class: '',
parent: null,
dimensions: [],
coordinateSystem: [[0, 0, 0], [1, 0, 0], [0, 1, 0], [0, 0, 1]]
},
root: 1
},
managed: false
})) || this;
Object.defineProperty(_assertThisInitialized(_this), "updateView", {
configurable: true,
enumerable: true,
writable: true,
value: throttle(function (_temp) {
var _ref = _temp === void 0 ? _this.session.options.updateView : _temp,
_ref$focus = _ref.focus,
focus = _ref$focus === void 0 ? true : _ref$focus,
_ref$zoom = _ref.zoom,
zoom = _ref$zoom === void 0 ? true : _ref$zoom,
_ref$rotate = _ref.rotate,
rotate = _ref$rotate === void 0 ? false : _ref$rotate,
_ref$layers = _ref.layers,
layers = _ref$layers === void 0 ? false : _ref$layers;
_this.pool.viewFound().then(function () {
var _this$pool$view$contr;
_this.pool.view.updateBounds();
focus && _this.pool.view.controls.focus();
zoom && _this.pool.view.controls.zoom();
rotate && (_this$pool$view$contr = _this.pool.view.controls).rotate.apply(_this$pool$view$contr, rotate);
_this.pool.update();
layers && _this.session.showLayer(_this.session.layerNames);
_this.session.selector && _this.session.selector.update();
_this.pool.view.invalidate();
_this.pool.view.emit(THREE.Object3D.Events.Lifecycle.Updated);
});
}, _this.session.options.throttle)
});
window.g_activeConnection = _assertThisInitialized(_this); //TODO: ugly hack.
_this.pool = new session.options.pool(_extends({
session: session,
name: "connection.pool." + _this.name
}, session.options.materials));
_this.primitives = {};
_this.waiting = {};
_this.featurePlugins = {};
_this.sequence = Promise.resolve(); // Observe root
_this.observe(function (state) {
return state.tree.root;
}, function (root, oldRoot) {
var old = _this.pool.find(function (item) {
var _item$userData;
return ((_item$userData = item.userData) === null || _item$userData === void 0 ? void 0 : _item$userData.root) === oldRoot;
});
if (old) {
old.destroy();
}
_this._observeLink(root);
}); // Observe active feature
_this.observe(function (state) {
return state.activeFeature;
}, function (feature, oldFeature) {
if (oldFeature) {
var data = _this.featurePlugins[oldFeature];
if (data) {
_this.store.dispatch(pluginActions.update(data.plugin.id, {
enabled: false,
collapsed: true
}));
if (!data.pluginPrototype.persistent) {
_this._destroyFeatures(oldFeature);
}
}
}
if (feature) {
var _data = _this.featurePlugins[feature];
if (_data) {
if (_data.pluginPrototype.persistent) {
_this.store.dispatch(pluginActions.update(_data.plugin.id, {
enabled: true,
collapsed: false
}));
_this.activeFeaturePlugin = _data.plugin;
} else {
_this.activeFeaturePlugin = _this._instanciateFeature(feature);
_this.store.dispatch(pluginActions.update(_this.activeFeaturePlugin.id, {
enabled: true,
collapsed: false
}));
}
}
}
}); // Connect to server
if (_this.session.options.url && _this.session.options.protocol) {
_this.connect(_this.session.options.url);
}
return _this;
}
var _proto = Connection.prototype;
_proto.defer =
/*#__PURE__*/
function () {
var _defer = _asyncToGenerator(
/*#__PURE__*/
_regeneratorRuntime.mark(function _callee2(func) {
return _regeneratorRuntime.wrap(function _callee2$(_context2) {
while (1) {
switch (_context2.prev = _context2.next) {
case 0:
return _context2.abrupt("return", new Promise(function (resolve) {
return requestIdleCallback(
/*#__PURE__*/
_asyncToGenerator(
/*#__PURE__*/
_regeneratorRuntime.mark(function _callee() {
return _regeneratorRuntime.wrap(function _callee$(_context) {
while (1) {
switch (_context.prev = _context.next) {
case 0:
_context.t0 = resolve;
_context.next = 3;
return func();
case 3:
_context.t1 = _context.sent;
return _context.abrupt("return", (0, _context.t0)(_context.t1));
case 5:
case "end":
return _context.stop();
}
}
}, _callee, this);
})));
}));
case 1:
case "end":
return _context2.stop();
}
}
}, _callee2, this);
}));
return function defer(_x) {
return _defer.apply(this, arguments);
};
}();
_proto.undo = function undo(factory) {
if (factory === void 0) {
factory = this.session.options.materials.factory;
}
return this.store.dispatch(this.actions.request(this.id, undefined, factory, 'undo'));
};
_proto.redo = function redo(factory) {
if (factory === void 0) {
factory = this.session.options.materials.factory;
}
return this.store.dispatch(this.actions.request(this.id, undefined, factory, 'redo'));
};
_proto.request = function request(command, factory) {
if (factory === void 0) {
factory = this.session.options.materials.factory;
}
return this.store.dispatch(this.actions.request(this.id, command, factory));
};
_proto.stream = function stream(url, factory) {
if (factory === void 0) {
factory = this.session.options.materials.factory;
}
return this.store.dispatch(this.actions.stream(this.id, url, factory));
};
_proto.parse = function parse(blob, factory) {
if (factory === void 0) {
factory = this.session.options.materials.factory;
}
return this.store.dispatch(this.actions.parse(this.id, blob, factory));
};
_proto.connect = function connect(url) {
return this.store.dispatch(this.actions.connect(this.id, url, this.session.options.protocol));
};
_proto.setTree = function setTree(payload) {
this.store.dispatch(actions.destroyTree(this.id));
this.store.dispatch(actions.setTree(this.id, payload));
};
_proto.patchTree = function patchTree(payload) {
return this.store.dispatch(this.actions.patchTree(this.id, payload));
};
_proto.removePrimitives = function removePrimitives() {
Object.values(this.primitives).forEach(function (item) {
return item.destroy();
});
this.primitives = {};
this.updateView();
};
_proto.findSolids = function findSolids(arg) {
var _this2 = this;
if (typeof arg === 'string') {
var primitive = this.primitives[arg];
if (primitive) {
return primitive.references;
}
} else if (typeof arg === 'function') {
var key = Object.keys(primitives).find(function (key) {
return arg(_this2.primitives[key]);
});
if (key) {
return this.primitives[key].references;
}
}
};
_proto._observeLink = function _observeLink(id, parent, instance) {
var _this3 = this;
var tree = this.getState().tree;
var object = tree[id];
switch (object.class) {
case 'CC_Assembly':
var assembly = this._observeAssembly(id, parent, instance);
assembly.userData = _extends({}, assembly.userData, {
meta: object.meta
});
return assembly;
case 'CC_Part':
case 'CC_SheetPart':
var part = this._observePart(id, parent, instance);
this.defer(function () {
return _this3._observeFeatures(id, part);
});
part.userData = _extends({}, part.userData, {
meta: object.meta
});
return part;
}
};
_proto._observeAssembly = function _observeAssembly(root, parent, instance) {
var _this4 = this;
if (parent === void 0) {
parent = this.pool;
}
var instances = {};
var container = new this.session.options.objectPrototype({
session: this.session
});
container.name = container.type = 'Assembly';
parent.add(container);
var array = collectNestedIds(container).concat([instance || root]);
container.userData = {
id: instance || root,
nestedId: array.join(','),
root: root
};
this.observe( // selector
function (state) {
return (state.tree[root] || emptyObject).instances;
},
/*#__PURE__*/
// onChange
_asyncToGenerator(
/*#__PURE__*/
_regeneratorRuntime.mark(function _callee3() {
var tree,
_len,
args,
_key,
_args3 = arguments;
return _regeneratorRuntime.wrap(function _callee3$(_context3) {
while (1) {
switch (_context3.prev = _context3.next) {
case 0:
tree = _this4.tree;
for (_len = _args3.length, args = new Array(_len), _key = 0; _key < _len; _key++) {
args[_key] = _args3[_key];
}
_context3.next = 4;
return arrayDiff.apply(void 0, args.concat([function (newItems) {
return newItems.forEach(function (item) {
var instance = tree[item];
var link = undefined;
var object = undefined; // Observe changes to it
_this4.observe(function (state) {
return state.tree[item];
}, function (instance) {
// Links should be reactive as well
if (instance.link !== link) {
if (object) {
object.destroy();
delete instances[instance.id];
}
object = instances[item] = _this4._observeLink(instance.link, container, item);
object.matrixAutoUpdate = false;
link = instance.link;
}
object.userData.name = instance.name;
object.name = instance.name;
object.visible = typeof instance.visible !== 'undefined' ? instance.visible : true;
object.userData = _extends({}, object.userData, {
meta: _extends({}, object.userData.meta, instance.meta)
});
var csys = instance.coordinateSystem.map(function (entry) {
return new (Function.prototype.bind.apply(THREE.Vector3, [null].concat(entry)))();
});
object.matrix = new THREE.Matrix4().makeBasis(csys[1], csys[2], csys[3]).setPosition(csys[0]);
_this4.updateView(); //console.log(` moving instance ${instance.name} to ${JSON.stringify(csys[0])}`);
}, {
fireOnStart: true,
unsubscribeOnUndefined: true
});
});
}, function (deletedItems) {
return deletedItems.forEach(function (item) {
var instance = instances[item]; //console.log(` removing instance ${instance.name}`);
delete instances[item];
return instance.destroy();
});
}]));
case 4:
_this4.updateView();
case 5:
case "end":
return _context3.stop();
}
}
}, _callee3, this);
})), // options
{
onRemove: function onRemove() {
console.log('assembly is gone', container.name);
Object.values(instances).forEach(function (item) {
return item.destroy();
});
instances = {};
container.destroy();
parent === _this4.pool && _this4.removePrimitives();
},
fireOnStart: true,
unsubscribeOnUndefined: true
});
return container;
};
_proto._observePart = function _observePart(root, parent, instance) {
var _this5 = this;
if (parent === void 0) {
parent = this.pool;
}
var clones = {};
var container = new this.session.options.objectPrototype({
session: this.session
});
container.name = container.type = 'Part';
container.temporary = new THREE.Group();
container.temporary.name = 'Part.temporaray';
container.temporary.updateParentMaterials = false;
container.add(container.temporary);
parent.add(container);
var array = collectNestedIds(container).concat([instance || root]);
container.userData = {
id: instance || root,
nestedId: array.join(','),
root: root
};
this.observe( // selector
function (state) {
return (state.tree[root] || emptyObject).solids;
},
/*#__PURE__*/
// onChange
_asyncToGenerator(
/*#__PURE__*/
_regeneratorRuntime.mark(function _callee4() {
var part,
_len2,
args,
_key2,
_args4 = arguments;
return _regeneratorRuntime.wrap(function _callee4$(_context4) {
while (1) {
switch (_context4.prev = _context4.next) {
case 0:
part = _this5.tree[root];
for (_len2 = _args4.length, args = new Array(_len2), _key2 = 0; _key2 < _len2; _key2++) {
args[_key2] = _args4[_key2];
}
_context4.next = 4;
return arrayDiff.apply(void 0, args.concat([function (newItems) {
newItems.map(function (item) {
var primitive = _this5.primitives[item];
if (!primitive) {
// primitive is not yet known
// Create bounds representation
var mesh;
if (part.min && part.max) {
var min = part.min;
var max = part.max;
mesh = new THREE.Mesh(new THREE.BoxBufferGeometry(max[0] - min[0], max[1] - min[1], max[2] - min[2]), new _this5.session.options.meshShader(_extends({
color: new THREE.Color("rgb(" + (part.color ? part.color.join(',') : '190, 190, 190') + ")")
}, _this5.session.options.meshShaderOptions)));
if (_this5.session.options.materials.shadows) {
mesh.castShadow = true;
mesh.receiveShadow = true;
}
mesh.position.set((max[0] + min[0]) / 2, (max[1] + min[1]) / 2, (max[2] + min[2]) / 2);
mesh.material.meta = {
material: {
color: mesh.material.color.clone(),
opacity: mesh.material.opacity
}
};
container.add(mesh.fadeIn({
opacity: 0
}));
} // If none exists, create empty waiting list entry from item
if (!_this5.waiting[item]) _this5.waiting[item] = []; // Register item on the waiting-list
_this5.waiting[item].push(function (primitive) {
// Delete bounds
mesh && mesh.destroy(); // Create real clone, finally
_this5._createClone({
root: root,
parent: parent,
clones: clones,
container: container,
primitive: primitive,
item: item
});
});
} else _this5._createClone({
root: root,
parent: parent,
clones: clones,
container: container,
primitive: primitive,
item: item
});
});
_this5.updateView();
}, function (deletedItems) {
return deletedItems.map(function (item) {
var clone = clones[item]; // Remove clone from primitives
var primitive = _this5.primitives[item];
if (primitive) primitive.references.splice(primitive.references.indexOf(clone), 1);
delete clones[item];
return clone.destroy();
});
}]));
case 4:
case "end":
return _context4.stop();
}
}
}, _callee4, this);
})), // options
{
onRemove: function onRemove() {
console.log('part is gone', container.name);
Object.values(clones).forEach(function (item) {
return item.destroy();
});
clones = {};
container.destroy();
parent === _this5.pool && _this5.removePrimitives();
},
fireOnStart: true,
unsubscribeOnUndefined: true
});
return container;
};
_proto._instanciateFeature = function _instanciateFeature(id, props) {
var _featurePlugins$id = this.featurePlugins[id],
parent = _featurePlugins$id.parent,
feature = _featurePlugins$id.feature,
persistent = _featurePlugins$id.persistent,
pluginPrototype = _featurePlugins$id.pluginPrototype; // If activeSelection is on we must reset it
!persistent && this.session.globals.activeSelection && this.store.dispatch(globalActions.setActiveSelection()); // Each plugin gets a distinct pool to draw into
var pool = new THREE.Group();
pool.name = 'Instance';
pool.updateParentMaterials = false;
pool.measurable = pluginPrototype.measurable;
pool.name = feature.name;
parent.add(pool); // Intanciate and link new plugin
var plugin = new pluginPrototype(this.session, _extends({
name: feature.name,
feature: feature,
pool: pool,
connection: this
}, props)); // Link plugins
this.featurePlugins[id].plugin = plugin;
this.store.dispatch(actions.linkPlugins(this.id, plugin.id));
return plugin;
};
_proto._mapFeatures = function _mapFeatures(parent) {
for (var _len3 = arguments.length, features = new Array(_len3 > 1 ? _len3 - 1 : 0), _key3 = 1; _key3 < _len3; _key3++) {
features[_key3 - 1] = arguments[_key3];
}
for (var _i = 0; _i < features.length; _i++) {
var id = features[_i];
var feature = this.tree[id];
var type = feature.class;
var pluginPrototype = this.session.featurePlugins[type];
if (!pluginPrototype && this.session.defaultFeaturePlugin) pluginPrototype = this.session.defaultFeaturePlugin;
if (pluginPrototype) {
// Skip if already present
if (this.featurePlugins[id]) continue; // Map feature plugin
this.featurePlugins[id] = {
feature: feature,
parent: parent.temporary,
pluginPrototype: pluginPrototype,
persistent: pluginPrototype.persistent
};
if (pluginPrototype.persistent) {
// Return persistent plugins
this._instanciateFeature(id);
}
}
}
};
_proto._unmapFeatures = function _unmapFeatures() {
var _this6 = this;
for (var _len4 = arguments.length, features = new Array(_len4), _key4 = 0; _key4 < _len4; _key4++) {
features[_key4] = arguments[_key4];
}
this._destroyFeatures(features);
features.forEach(function (id) {
return delete _this6.featurePlugins[id];
});
};
_proto._destroyFeatures = function _destroyFeatures() {
var _this7 = this;
for (var _len5 = arguments.length, features = new Array(_len5), _key5 = 0; _key5 < _len5; _key5++) {
features[_key5] = arguments[_key5];
}
var plugins = features.map(function (id) {
return _this7.featurePlugins[id];
}).filter(function (data) {
return data && data.plugin;
}).map(function (data) {
return data.plugin.id;
});
if (plugins.length > 0) {
this.store.dispatch(actions.unlinkPlugins(this.id, plugins));
}
features.forEach(function (id) {
// Find real plugin class
var _ref5 = _this7.featurePlugins[id] || {},
plugin = _ref5.plugin,
parent = _ref5.parent,
persistent = _ref5.persistent;
if (plugin) {
if (!persistent) {
delete _this7.featurePlugins[id].plugin;
}
parent.remove(plugin.pool);
_this7.defer(function () {
return _this7.store.dispatch(pluginActions.unregister(plugin.id));
});
}
});
};
_proto._observeFeatures = function _observeFeatures(root, parent) {
var _this8 = this;
if (parent === void 0) {
parent = this.pool;
}
//console.log("observeFeatures...")
return this.observe( // selector
function (state) {
return (state.tree[root] || emptyObject).features;
}, // onChange
function () {
for (var _len6 = arguments.length, args = new Array(_len6), _key6 = 0; _key6 < _len6; _key6++) {
args[_key6] = arguments[_key6];
}
arrayDiff.apply(void 0, args.concat([function (newItems) {
return _this8._mapFeatures.apply(_this8, [parent].concat(newItems));
}, function (deletedItems) {
return _this8._unmapFeatures.apply(_this8, deletedItems);
}]));
}, // options
{
fireOnStart: true,
unsubscribeOnUndefined: true
});
};
_proto.__onDestroyed = function __onDestroyed() {
var _this9 = this;
Object.values(this.primitives).forEach(function (item) {
return item.destroy();
});
this.primitives = {};
Object.values(this.session.plugins).forEach(function (plugin) {
return plugin.connection === _this9.id && _this9.store.dispatch(pluginActions.unregister(plugin.id));
});
if (this.session.globals.activeConnection === this.id) {
this.session.dispatch(globalActions.setActiveConnection());
}
this.socket && this.socket.disconnect();
};
_proto._createClone = function _createClone(_ref6) {
var root = _ref6.root,
parent = _ref6.parent,
clones = _ref6.clones,
container = _ref6.container,
primitive = _ref6.primitive,
item = _ref6.item;
var clone = undefined;
if (this.session.options.clone) {
(function () {
clone = clones[item] = primitive.clone();
var array = collectNestedIds(container).concat([item]);
clone.userData.nestedId = array.join(','); // deep clone materials;
for (var _iterator = clone.children.keys(), _isArray = Array.isArray(_iterator), _i2 = 0, _iterator = _isArray ? _iterator : _iterator[Symbol.iterator]();;) {
var _ref7;
if (_isArray) {
if (_i2 >= _iterator.length) break;
_ref7 = _iterator[_i2++];
} else {
_i2 = _iterator.next();
if (_i2.done) break;
_ref7 = _i2.value;
}
var _child = _ref7;
var c = clone.children[_child];
var p = primitive.children[_child];
if (c.material) {
var isMultiMaterial = Array.isArray(c.material);
if (isMultiMaterial) {
c.material = p.material.map(function (pMaterial) {
var cMaterial = pMaterial.clone(true);
cMaterial.meta = _extends({}, pMaterial.meta, {
nestedId: array.concat([pMaterial.meta.id]).join(',')
});
return cMaterial;
});
} else {
c.material = p.material.clone(true);
c.material.meta = _extends({}, p.material.meta);
}
}
}
})();
} else {
clone = clones[item] = primitive;
}
primitive.references.push(clone);
return container.add(clone.fadeIn({
opacity: 0
}));
};
return Connection;
}(Lifecycle(Base, false));
export { Connection as default };