awv3
Version:
⚡ AWV3 embedded CAD
819 lines (683 loc) • 37.7 kB
JavaScript
'use strict';
Object.defineProperty(exports, "__esModule", {
value: true
});
exports.MaterialSelector = undefined;
var _typeof2 = require('babel-runtime/helpers/typeof');
var _typeof3 = _interopRequireDefault(_typeof2);
var _getIterator2 = require('babel-runtime/core-js/get-iterator');
var _getIterator3 = _interopRequireDefault(_getIterator2);
var _toConsumableArray2 = require('babel-runtime/helpers/toConsumableArray');
var _toConsumableArray3 = _interopRequireDefault(_toConsumableArray2);
var _extends2 = require('babel-runtime/helpers/extends');
var _extends3 = _interopRequireDefault(_extends2);
var _set = require('babel-runtime/core-js/set');
var _set2 = _interopRequireDefault(_set);
var _classCallCheck2 = require('babel-runtime/helpers/classCallCheck');
var _classCallCheck3 = _interopRequireDefault(_classCallCheck2);
var _createClass2 = require('babel-runtime/helpers/createClass');
var _createClass3 = _interopRequireDefault(_createClass2);
var _three = require('three');
var THREE = _interopRequireWildcard(_three);
var _materialstore = require('../../misc/materialstore');
var _materialstore2 = _interopRequireDefault(_materialstore);
var _object = require('../../three/object3');
var _object2 = _interopRequireDefault(_object);
var _orbit = require('../../controls/orbit');
var _orbit2 = _interopRequireDefault(_orbit);
var _perspective = require('../../three/perspective');
var _perspective2 = _interopRequireDefault(_perspective);
var _region = require('../../three/region');
var _region2 = _interopRequireDefault(_region);
var _elements = require('../store/elements');
var _helpers = require('../helpers');
function _interopRequireWildcard(obj) { if (obj && obj.__esModule) { return obj; } else { var newObj = {}; if (obj != null) { for (var key in obj) { if (Object.prototype.hasOwnProperty.call(obj, key)) newObj[key] = obj[key]; } } newObj.default = obj; return newObj; } }
function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { default: obj }; }
/**
* @class MaterialSelector is used to select faces, edges, lines or points
* (Filter settings: Mesh, LineSegments, Points)
* It includes single selection as well as rubberband selection. This selector work
* only for materials with meta-data. If the selection-filter includes 'Point',
* then point representations are generated for LineSegments and Regions in the
* scene and scaled to achieve the desired radius (pointRadiusPx).
*/
var MaterialSelector = exports.MaterialSelector = function () {
function MaterialSelector(session) {
var _this = this;
(0, _classCallCheck3.default)(this, MaterialSelector);
this.session = session;
// Wait until the sessions pool has been added into the view (happens in <App/>)
this.session.pool.viewFound().then(function (view) {
return _this.view = view;
});
//the currently active selecion-element
this.element = undefined;
//stores some of the original material properties
this.materialStore = new _materialstore2.default();
// stores the material(s) of the selected object(s)
this.selectedSet = new _set2.default();
//the unobserve function is assigned when selector is activated
//and must be called when deactivated
this.unobserve = undefined;
// initial size of point geometry
this.pointSize = 1;
// desired radius of point sphere in px
this.pointRadiusPx = 5;
// desired tolerance for picking lines
this.linePrecisionPx = 5;
// property changes for hovered materials
this.hoveredProps = { color: new THREE.Color(0x28d79f), opacity: 1, linewidth: 3 };
// property changes for selected materials
this.selectedProps = { color: new THREE.Color(0xc23369), opacity: 1, linewidth: 3 };
}
/**
* Deactivates the material-selector. Observation of selection-element is stopped.
* Rubberband selection handlers are removed.
*/
(0, _createClass3.default)(MaterialSelector, [{
key: 'deactivate',
value: function deactivate() {
this.removeAll(false);
this.shiftHandler && document.removeEventListener('keydown', this.shiftHandler);
this.shiftHandler = undefined;
this.element = undefined;
this.unobserve && this.unobserve();
this.points && this.points.destroy();
}
/**
* Activates the material-selector. The Given selection-element is observed in
* order to register changes when click-deleting labels. Handlers for
* rubberband selection are created.
*/
}, {
key: 'activate',
value: function activate() {
var _this2 = this;
var element = arguments.length > 0 && arguments[0] !== undefined ? arguments[0] : this.element;
//observe changes in element's children
this.unobserve = this.session.observe(function (state) {
return (state.elements[element.id] || {}).children;
}, function () {
for (var _len = arguments.length, args = Array(_len), _key = 0; _key < _len; _key++) {
args[_key] = arguments[_key];
}
_helpers.arrayDiff.apply(undefined, args.concat([function (newItems) {}, function (deletedItems) {
var candidates = [];
var _loop = function _loop(i) {
_this2.selectedSet.forEach(function (material) {
if (deletedItems[i] === material.meta.id) {
candidates.push(material);
}
});
};
for (var i = 0; i < deletedItems.length; i++) {
_loop(i);
}
candidates.forEach(function (material) {
return _this2.remove({ 'material': material });
});
}]));
});
this.element = element;
// Create new group for points
this.points = new _object2.default();
this.session.pool.add(this.points);
// check for multiple points with same position
this.positions = [];
// Create points, midpoints, etc
if (element.types.includes('Point')) {
this.session.pool.traverse(function (item) {
if (item.type === 'Region') {
item.points.forEach(function (obj) {
if (_this2.session.isVisibleLayer(obj.meta.layer)) {
_this2.addPoint(obj.meta.position, (0, _extends3.default)({}, obj.meta, { id: obj.id }));
}
});
} else if (item.type === 'LineSegments' && item.interactive) {
item.material.materials.forEach(function (line) {
switch (line.meta.type) {
case 'arc':
if (_this2.session.isVisibleLayer(line.meta.layer)) {
_this2.addPoint(line.meta.center, (0, _extends3.default)({}, line.meta, { id: line.meta.id + '_c', position: line.meta.center, type: 'point' }), 0.1);
}
break;
case 'line':
if (_this2.session.isVisibleLayer(line.meta.layer)) {
_this2.addPoint(line.meta.start, (0, _extends3.default)({}, line.meta, { id: line.meta.id + '_s', position: line.meta.start, type: 'point' }));
_this2.addPoint(line.meta.end, (0, _extends3.default)({}, line.meta, { id: line.meta.id + '_e', position: line.meta.end, type: 'point' }));
}
break;
case 'circle':
if (_this2.session.isVisibleLayer(line.meta.layer)) {
_this2.addPoint(line.meta.center, (0, _extends3.default)({}, line.meta, { id: line.meta.id + '_c', position: line.meta.center, type: 'point' }));
}
break;
}
});
}
});
}
this.points.children && this.points.children.forEach(function (point) {
return point.setRenderOrder({ Mesh: 0, LineSegments: 100 });
});
// restore previously selected elements
if (this.element.children.length > 0) {
this.session.pool.traverse(function (obj) {
if (obj.material) {
if (obj.material instanceof THREE.MultiMaterial) {
obj.material.materials.forEach(function (mat) {
if (mat.meta && _this2.element.children.indexOf(mat.meta.id) > -1) {
_this2.session.store.dispatch(_elements.actions.removeChild(_this2.element.id, mat.meta.id));
_this2.add({ material: mat });
}
});
} else if (obj.material.meta && _this2.element.children.indexOf(obj.material.meta.id) > -1) {
_this2.session.store.dispatch(_elements.actions.removeChild(_this2.element.id, obj.material.meta.id));
_this2.add({ material: obj.material });
}
}
});
}
var types = ['Scene'];
if (Array.isArray(element.types)) types = [].concat((0, _toConsumableArray3.default)(types), (0, _toConsumableArray3.default)(element.types));else if (typeof element.types === 'function') types = function types(object) {
return object.type === 'Scene' || element.types(object);
};
// SHIFT KEY selection handler
var candidates = [];
this.shiftHandler = function (event) {
if (!_this2.shiftHandler.handled && event.keyCode === 16) {
(function () {
_this2.shiftHandler.handled = true;
_this2.view.input.debounce = false;
_this2.view.controls.enabled = false;
_this2.view.interaction.enabled = false;
var boundingBoxes2D = [];
_this2.view.scene.traverseMaterials(function (material, item) {
// let value = Object3.RenderOrder.MeshesFirst[item.type];
// if (value !== undefined) item.renderOrder = value;
//project bounding box to view coordinates
if (item.visible && item.interactive && material.meta && types.includes(item.type)) {
item.updateMatrixWorld(true);
var minPt = material.meta.box.min.clone(); //TODO: get all 8 points
var maxPt = material.meta.box.max.clone(); //TODO: get all 8 points
//project to view-coordinates
var p1 = _this2.view.getPoint2(minPt);
var p2 = _this2.view.getPoint2(maxPt);
//bounding box in 2D
var bounds2D = new THREE.Box2();
bounds2D.setFromPoints([p1, p2]);
bounds2D.__material = material;
bounds2D.__object = item;
boundingBoxes2D.push(bounds2D);
}
});
//draw rectangle
var nearDistance = _this2.view.camera.near;
var farDistance = _this2.view.camera.far;
_this2.view.hud = true;
_this2.view.controlsHud = undefined;
_this2.view.cameraHud = new _perspective2.default({ near: nearDistance, far: farDistance, fov: _this2.view.camera.fov });
_this2.view.measure(true);
var modelBounds = _this2.view.scene.updateBounds().bounds;
var geom = new THREE.PlaneGeometry(1, 1);
var box = new THREE.Mesh(geom, new THREE.MeshBasicMaterial({ transparent: true, opacity: 0.05, color: new THREE.Color(0) }));
box.renderOrder = -1;
_this2.view.sceneHud.add(box);
var clickDown = void 0,
mousePos = void 0,
rubberBandLine = void 0,
clickHud = void 0;
var handler = function handler(event) {
var _ret3 = function () {
switch (event.type) {
case 'mousedown':
clickDown = new THREE.Vector2(event.offsetX, event.offsetY);
mousePos = new THREE.Vector2(event.offsetX + 11, event.offsetY + 1);
clickHud = _this2.view.getPoint3({ x: event.offsetX, y: event.offsetY }, _this2.view.cameraHud, 0);
box.position.copy(clickHud);
break;
case 'mousemove':
if (!clickDown) return {
v: void 0
};
//update rectangle
var tempPoint = _this2.view.getPoint3({ x: event.offsetX, y: event.offsetY }, _this2.view.cameraHud, 0);
var delta = tempPoint.sub(clickHud);
var width = Math.abs(delta.x);
var height = Math.abs(delta.y);
var halfHeight = height / 2;
var halfWidth = width / 2;
var halfDeltaX = delta.x / 2;
var halfDeltaY = delta.y / 2;
geom.vertices[0].set(-halfWidth + halfDeltaX, halfHeight + halfDeltaY, 0);
geom.vertices[1].set(width - halfWidth + halfDeltaX, halfHeight + halfDeltaY, 0);
geom.vertices[2].set(-halfWidth + halfDeltaX, -(height - halfHeight) + halfDeltaY, 0);
geom.vertices[3].set(width - halfWidth + halfDeltaX, -(height - halfHeight) + halfDeltaY, 0);
geom.verticesNeedUpdate = true;
mousePos = new THREE.Vector2(event.offsetX, event.offsetY);
//check intersection with rectangle in display-coordinates
var rubberBandRectangle = new THREE.Box2();
rubberBandRectangle.setFromPoints([clickDown, mousePos]);
var newCandidates = [];
var _iteratorNormalCompletion = true;
var _didIteratorError = false;
var _iteratorError = undefined;
try {
for (var _iterator = (0, _getIterator3.default)(boundingBoxes2D), _step; !(_iteratorNormalCompletion = (_step = _iterator.next()).done); _iteratorNormalCompletion = true) {
var _box = _step.value;
if (rubberBandRectangle.containsBox(_box)) {
// create new object identical to raycast-hit
newCandidates.push({
distance: 0,
point: undefined,
face: undefined,
faceIndex: undefined,
indices: undefined,
object: _box.__object,
material: _box.__material,
meta: _box.__material.meta
});
}
}
// Remove items not present any longer
} catch (err) {
_didIteratorError = true;
_iteratorError = err;
} finally {
try {
if (!_iteratorNormalCompletion && _iterator.return) {
_iterator.return();
}
} finally {
if (_didIteratorError) {
throw _iteratorError;
}
}
}
var _iteratorNormalCompletion2 = true;
var _didIteratorError2 = false;
var _iteratorError2 = undefined;
try {
for (var _iterator2 = (0, _getIterator3.default)(candidates), _step2; !(_iteratorNormalCompletion2 = (_step2 = _iterator2.next()).done); _iteratorNormalCompletion2 = true) {
var item = _step2.value;
var found = false;
var _iteratorNormalCompletion4 = true;
var _didIteratorError4 = false;
var _iteratorError4 = undefined;
try {
for (var _iterator4 = (0, _getIterator3.default)(newCandidates), _step4; !(_iteratorNormalCompletion4 = (_step4 = _iterator4.next()).done); _iteratorNormalCompletion4 = true) {
var newItem = _step4.value;
if (item.material.meta.id === newItem.material.meta.id) {
found = true;
break;
}
}
} catch (err) {
_didIteratorError4 = true;
_iteratorError4 = err;
} finally {
try {
if (!_iteratorNormalCompletion4 && _iterator4.return) {
_iterator4.return();
}
} finally {
if (_didIteratorError4) {
throw _iteratorError4;
}
}
}
if (!found) {
_this2.remove(item, false);
}
}
} catch (err) {
_didIteratorError2 = true;
_iteratorError2 = err;
} finally {
try {
if (!_iteratorNormalCompletion2 && _iterator2.return) {
_iterator2.return();
}
} finally {
if (_didIteratorError2) {
throw _iteratorError2;
}
}
}
candidates = newCandidates;
// Add new items, but only if they aren't known yet
var _iteratorNormalCompletion3 = true;
var _didIteratorError3 = false;
var _iteratorError3 = undefined;
try {
for (var _iterator3 = (0, _getIterator3.default)(candidates), _step3; !(_iteratorNormalCompletion3 = (_step3 = _iterator3.next()).done); _iteratorNormalCompletion3 = true) {
var _item = _step3.value;
_this2.add(_item, false);
}
} catch (err) {
_didIteratorError3 = true;
_iteratorError3 = err;
} finally {
try {
if (!_iteratorNormalCompletion3 && _iterator3.return) {
_iterator3.return();
}
} finally {
if (_didIteratorError3) {
throw _iteratorError3;
}
}
}
_this2.view.invalidate();
break;
case 'mouseup':
box && box.destroy();
var tempIds = [];
candidates.forEach(function (item) {
return tempIds.push(item.material.meta.id);
});
clickDown = undefined;
if (rubberBandLine != undefined) {
_this2.view.scene.remove(rubberBandLine);
rubberBandLine = undefined;
}
// Update UI after mouse-up
_this2.session.store.dispatch(_elements.actions.update(_this2.element.id, { children: candidates.map(function (item) {
return item.material.meta.id;
}) }));
break;
}
}();
if ((typeof _ret3 === 'undefined' ? 'undefined' : (0, _typeof3.default)(_ret3)) === "object") return _ret3.v;
};
_this2.view.input.on(['mousedown', 'mousemove', 'mouseup'], handler);
_this2.tempHandler = function () {
box && box.destroy();
_this2.shiftHandler.handled = false;
_this2.view.input.removeListener(['mousedown', 'mousemove', 'mouseup'], handler);
document.removeEventListener('keyup', _this2.tempHandler);
_this2.view.input.debounce = true;
_this2.view.controls.enabled = true;
_this2.view.interaction.enabled = true;
_this2.view.scene.setRenderOrder();
};
document.addEventListener('keyup', _this2.tempHandler);
})();
}
};
this.shiftHandler.handled = false;
document.addEventListener('keydown', this.shiftHandler);
}
/**
* Add material to the set of selected elements
* @param {RayCast-Hit} event - Object generated by the raycaster including the
* hit object, material and meta-information
*/
}, {
key: 'add',
value: function add(event) {
var notify = arguments.length > 1 && arguments[1] !== undefined ? arguments[1] : true;
if (!this.isSelected(event.material)) {
this.select(event.material);
this.selectedSet.add(event.material);
notify && this.session.store.dispatch(_elements.actions.addChild(this.element.id, event.material.meta.id));
}
return event;
}
/**
* Remove material from the set of selected elements
* @param {RayCast-Hit} event - Object generated by the raycaster including the
* hit object, material and meta-information
*/
}, {
key: 'remove',
value: function remove(event) {
var notify = arguments.length > 1 && arguments[1] !== undefined ? arguments[1] : true;
if (this.isSelected(event.material)) {
this.unselect(event.material);
this.selectedSet.delete(event.material);
notify && this.session.store.dispatch(_elements.actions.removeChild(this.element.id, event.material.meta.id));
}
}
/**
* Remove all items (Material) from the set of selected elements
* @param {Boolean} keepLocals - If true, selected ids are kept within the selection-element
*/
}, {
key: 'removeAll',
value: function removeAll() {
var _this3 = this;
var removeElements = arguments.length > 0 && arguments[0] !== undefined ? arguments[0] : true;
this.selectedSet.forEach(function (material) {
return _this3.unselect(material);
});
this.selectedSet.clear();
if (removeElements) {
this.session.store.dispatch(_elements.actions.removeAllChilds(this.element.id));
}
}
/**
* Called by selector when event is triggered.
*/
}, {
key: 'hover',
value: function hover(event) {
if (this.shiftHandler.handled) return;
this.session.pool.view.setCursor('pointer');
if (event.material && !this.isSelected(event.material)) this.materialStore.store(event.material, this.hoveredProps).start(500);
}
/**
* Called by selector when event is triggered.
*/
}, {
key: 'unhover',
value: function unhover(event) {
if (this.shiftHandler.handled) return;
if (event.material && !this.isSelected(event.material)) this.materialStore.restore(event.material).start(500);
}
/**
* Called by selector when event is triggered.
*/
}, {
key: 'clicked',
value: function clicked(event) {
if (this.shiftHandler.handled) return;
if (this.isSelected(event.material)) {
this.remove(event);
} else {
this.add(event);
}
}
/**
* Called by selector when event is triggered.
*/
}, {
key: 'missed',
value: function missed(event) {
this.removeAll(true);
}
/**
* Called by selector when event is triggered.
*/
}, {
key: 'rendered',
value: function rendered(event) {
this.scalePoints();
this.calculateLinePrecision();
}
/**
* Highlight the selected material. Store original material for unhighlight.
* @param {THREE.Material} - Material to select.
*/
}, {
key: 'select',
value: function select(material) {
this.materialStore.store(material, this.selectedProps).start(500);
}
/**
* Unhighlights the selected material, restoring the original appearance.
* @param {THREE.Material} - Material to unselect.
*/
}, {
key: 'unselect',
value: function unselect(material) {
if (this.view.interaction.hitsArray.length) {
this.materialStore.store(material, this.hoveredProps).start(500);
} else {
this.materialStore.restore(material).start(500);
}
}
/**
* Check if material object is already selected
* @return {bool} TRUE if selected, FALSE otherwise
*/
}, {
key: 'isSelected',
value: function isSelected(material) {
return this.selectedSet.has(material);
}
/**
* Returns the selected ids in an array
* @return {array} selectedIds - Array with the selected ids
*/
}, {
key: 'getSelectedIds',
value: function getSelectedIds() {
var iter = this.selectedSet.values();
var selectedIds = [];
var item = iter.next();
while (item.value !== undefined) {
selectedIds.push(item.value.meta.id);
item = iter.next();
}
return selectedIds;
}
/**
* Returns the selected elements in an array
* @return {array} selectedElements - Array with the selected elements
*/
}, {
key: 'getSelectedElements',
value: function getSelectedElements() {
var iter = this.selectedSet.values();
var selectedElements = [];
var item = iter.next();
while (item.value !== undefined) {
selectedElements.push(item.value);
item = iter.next();
}
return selectedElements;
}
/**
* Scales all the created points to the desired radius (pointRadiusPx)
*/
}, {
key: 'scalePoints',
value: function scalePoints() {
var _this4 = this;
if (this.points && this.points.children.length > 0) {
this.points.children.forEach(function (point) {
var scaleFactor = _this4.calculatePointScaleFactor(point.position);
point.scale.set(scaleFactor, scaleFactor, scaleFactor);
});
}
}
/**
* Calculate scale factor for the given position based on the desired radius.
* @return {real} scaleFactor - Calculated scale factor for the given position.
*/
}, {
key: 'calculatePointScaleFactor',
value: function calculatePointScaleFactor(position) {
var scaleFactor = 1.0;
if (this.session.pool.view) {
var point2 = this.session.pool.view.getPoint2(position.clone());
var screenPoint1 = new THREE.Vector3(0, 0, point2.z);
var screenPoint2 = new THREE.Vector3(0, 0, point2.z);
screenPoint2.x = this.pointRadiusPx;
var worldPoint1 = this.session.pool.view.getPoint3(screenPoint1, this.session.pool.view.camera, screenPoint1.z);
var worldPoint2 = this.session.pool.view.getPoint3(screenPoint2, this.session.pool.view.camera, screenPoint2.z);
var distance = worldPoint1.sub(worldPoint2).length();
scaleFactor = distance / this.pointSize;
}
return scaleFactor;
}
/**
* Add a point to the points object in the scene. Duplicate points are
* filtered out.
* @param {THREE.Vector3} position - Position of the point to be added.
* @param {object} meta - Meta-data of the object/material this point belongs to.
*/
}, {
key: 'addPoint',
value: function addPoint(position, meta) {
var opacity = arguments.length > 2 && arguments[2] !== undefined ? arguments[2] : 0;
if (this.isDuplicatePoint(position)) {
return;
}
var geometry = new THREE.SphereBufferGeometry(this.pointSize, 32, 32);
geometry.computeBoundingSphere();
geometry.computeBoundingBox();
// material
var material = new THREE.MeshBasicMaterial({ color: 0x28b4d7, transparent: true, opacity: opacity });
// point as sphere-mesh
var point = new THREE.Mesh(geometry, material);
point.renderOrder = 10000;
point.type = 'Point';
point.position.copy(position);
var vec = new THREE.Vector3(this.pointSize, this.pointSize, this.pointSize);
var box = new THREE.Box3(position.sub(vec), position.add(vec));
point.userData.meta = material.meta = meta;
var scaleFactor = this.calculatePointScaleFactor(position);
point.scale.set(scaleFactor, scaleFactor, scaleFactor);
this.points.add(point);
this.positions.push(position);
}
/**
* Checks for existing points within proximity of given position
* @return true if a point exists already for the given position (within tolerance epsilon)
*/
}, {
key: 'isDuplicatePoint',
value: function isDuplicatePoint(position) {
var epsilon = 1e-4;
for (var i = 0; i < this.positions.length; i++) {
if (Math.abs(this.positions[i].x - position.x) < epsilon && Math.abs(this.positions[i].y - position.y) < epsilon && Math.abs(this.positions[i].z - position.z) < epsilon) {
return true;
}
}
return false;
}
/**
* Calculate and set the linePrecision the given position based on the given
* precision in pixel.
*/
}, {
key: 'calculateLinePrecision',
value: function calculateLinePrecision() {
if (this.session.pool.view && this.session.pool.scene.bounds.sphere) {
var center = this.session.pool.scene.bounds.sphere.center;
var point2 = this.session.pool.view.getPoint2(center.clone());
var screenPoint1 = new THREE.Vector3(0, 0, point2.z);
var screenPoint2 = new THREE.Vector3(0, 0, point2.z);
screenPoint2.x = this.linePrecisionPx;
var worldPoint1 = this.session.pool.view.getPoint3(screenPoint1, this.session.pool.view.camera, screenPoint1.z);
var worldPoint2 = this.session.pool.view.getPoint3(screenPoint2, this.session.pool.view.camera, screenPoint2.z);
this.session.pool.view.interaction.raycaster.linePrecision = worldPoint1.sub(worldPoint2).length();
}
}
/**
* Removes the given elements from the current selection.
* @param {array} elements - elements to be removed from the selection.
*/
}, {
key: 'unselectElements',
value: function unselectElements(elements) {
var _this5 = this;
elements.forEach(function (element) {
return _this5.remove({ material: element });
});
}
}]);
return MaterialSelector;
}();