awv3
Version:
⚡ AWV3 embedded CAD
504 lines (421 loc) • 20.9 kB
JavaScript
'use strict';
Object.defineProperty(exports, "__esModule", {
value: true
});
var _extends2 = require('babel-runtime/helpers/extends');
var _extends3 = _interopRequireDefault(_extends2);
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 _uuidV = require('uuid-v4');
var _uuidV2 = _interopRequireDefault(_uuidV);
var _error = require('./error');
var Error = _interopRequireWildcard(_error);
var _helpers = require('./helpers');
var _canvas = require('./canvas');
var _dom = require('./dom');
var _dom2 = _interopRequireDefault(_dom);
var _interaction = require('./interaction');
var _interaction2 = _interopRequireDefault(_interaction);
var _stats = require('../misc/stats');
var _stats2 = _interopRequireDefault(_stats);
var _orbit = require('../controls/orbit');
var _orbit2 = _interopRequireDefault(_orbit);
var _perspective = require('../three/perspective');
var _perspective2 = _interopRequireDefault(_perspective);
var _elementResizeEvent = require('element-resize-event');
var _elementResizeEvent2 = _interopRequireDefault(_elementResizeEvent);
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 }; }
/** A view represents a portion of canvas on which webGL can draw.
The view is defined and tracked by a dom node on which the drawing take place. */
var View = function () {
/** Construct a new View
@param {Object} [canvas=lastCreated] - The parent canvas, if none defined the lastCreated will be used
@param {Object} [options={}] - Options to initialize the View with
@param {HTMLElement} [options.dom=canvas.dom] - The HTML element on which the view will draw
@param {Boolean} [options.renderAlways=false] - Set to true the view will render 60fps,
set to false it will render on changes (default, recommended)
@param {Boolean} [options.visible=true] - Set to true the view will render
@param {Function} [options.callbackBefore=undefined] - Callback before the render pass
@param {Function} [options.callbackRender=undefined] - Callback to custom-render the scene
@param {Function} [options.callbackAfter=undefined] - Callback after the render pass
@param {Number} [options.background=canvas.renderer.clearColor] - Background color
@param {Number} [options.opacity=0.0] - Background opacity
@param {Number} [options.ambientColor=0xffffff] - Ambient color
@param {Number} [options.ambientIntensity=1.0] - Ambient intensity
@example
import View from 'view';
// Create a view, defaults into the same dom as the canvas
const view = new View(canvas, { dom: '#view', ambient: 0x909090 });
// Add model to the view's scene
view.scene.add(model);
@returns {Object} The constructed View */
function View() {
var _this = this;
var canvas = arguments.length > 0 && arguments[0] !== undefined ? arguments[0] : _canvas.lastCreated;
var options = arguments.length > 1 && arguments[1] !== undefined ? arguments[1] : {};
(0, _classCallCheck3.default)(this, View);
this.id = (0, _uuidV2.default)();
this.canvas = canvas;
this.renderer = canvas.renderer;
this.invalidateFrames = 2;
this.force = 0;
this.dirty = true;
this.bounds = {
box: new THREE.Box3(),
sphere: new THREE.Sphere()
};
options = (0, _extends3.default)({
dom: canvas.dom,
renderAlways: false,
visible: true,
callback: undefined,
callbackRender: undefined,
callbackAfter: undefined,
background: this.renderer.clearColor,
opacity: 0,
defaultCursor: 'auto'
}, options);
this.dom = (0, _helpers.queryDom)(options.dom);
this.renderAlways = options.renderAlways;
this.visible = options.visible;
this.callbackBefore = options.callback;
this.callbackRender = options.callbackRender;
this.callbackAfter = options.callbackAfter;
this.background = options.background;
this.opacity = options.opacity;
this.defaultCursor = options.defaultCursor;
// Make sure the view hides overflow and is not selectable
options.dom.style.cssText += "-webkit-user-select: none; -moz-user-select: none; -ms-user-select: none; -o-user-select: none; user-select: none; overlay: hidden";
// A border will hide gaps caused by imprecise layout returns
if (options.background !== this.renderer.clearColor) this.dom.style.border = '2px solid #' + this.background.getHexString();
this.input = new _dom2.default(this, {
wheel: function wheel(state) {
_this.controls.onMouseWheel(state);
_this.hud && _this.controlsHud && _this.controlsHud !== _this.controls && _this.controlsHud.onMouseWheel(state);
},
mouseout: function mouseout(state) {
_this.interaction.onMouseOut(state);
},
mousemove: function mousemove(state) {
_this.interaction.onMouseMove(state);
_this.input.mouse.down && _this.controls.onMouseMove(state);
_this.input.mouse.down && _this.hud && _this.controlsHud && _this.controlsHud !== _this.controls && _this.controlsHud.onMouseMove(state);
},
mousedown: function mousedown(state) {
_this.interaction.onMouseDown(state);
_this.controls.onMouseDown(state);
_this.hud && _this.controlsHud && _this.controlsHud !== _this.controls && _this.controlsHud.onMouseDown(state);
},
mouseup: function mouseup(state) {
_this.interaction.onMouseUp(state);
_this.controls.onMouseUp(state);
_this.hud && _this.controlsHud && _this.controlsHud !== _this.controls && _this.controlsHud.onMouseUp(state);
},
touchstart: function touchstart(state) {
_this.interaction.onMouseDown(state);
_this.controls.onTouchStart(state);
_this.hud && _this.controlsHud && _this.controlsHud !== _this.controls && _this.controlsHud.onTouchStart(state);
},
touchmove: function touchmove(state) {
_this.interaction.onMouseMove(state);
_this.controls.onTouchMove(state);
_this.hud && _this.controlsHud && _this.controlsHud !== _this.controls && _this.controlsHud.onTouchMove(state);
},
touchend: function touchend(state) {
_this.interaction.onMouseUp(state);
_this.controls.onTouchEnd(state);
_this.hud && _this.controlsHud && _this.controlsHud !== _this.controls && _this.controlsHud.onTouchEnd(state);
}
});
this.scene = new THREE.Scene();
this.scene.canvas = canvas;
this.scene.view = this;
this.ambient = new THREE.AmbientLight(options.ambientColor ? options.ambientColor : 0xffffff);
this.ambient.intensity = typeof options.ambientIntensity !== 'undefined' ? options.ambientIntensity : 1.0;
this.ambient.keep = true;
this.ambient.view = this;
this.scene.add(this.ambient);
this.camera = new _perspective2.default(options);
this.controls = new _orbit2.default(this, (0, _extends3.default)({
maxPolarAngle: Math.PI,
minDistance: 1,
maxDistance: 20000
}, options));
this.hud = false;
this.sceneHud = new THREE.Scene();
this.sceneHud.canvas = canvas;
this.sceneHud.view = this;
this.cameraHud = this.camera;
this.controlsHud = this.controls;
this.ambientHud = new THREE.AmbientLight(options.ambientColor ? options.ambientColor : 0xffffff);
this.ambientHud.intensity = typeof options.ambientIntensity !== 'undefined' ? options.ambientIntensity : 1.0;
this.ambientHud.keep = true;
this.ambientHud.view = this;
this.sceneHud.add(this.ambientHud);
this.interaction = new _interaction2.default(this);
this.updateScopes();
this.canvas.views.push(this);
this.renderer.resize();
(0, _elementResizeEvent2.default)(this.dom, function () {
return _this.invalidate(30);
});
}
(0, _createClass3.default)(View, [{
key: 'destroy',
value: function destroy() {
if (!this.__destroyed) {
this.__destroyed = true;
this.input.detach();
this.input.removeListeners();
this.input.removeInspectors();
this.scene.destroy({ keep: false });
this.dom.querySelector('object').remove();
this.render = function () {};
this.clear = function () {};
this.view = undefined;
this.canvas = undefined;
this.renderer = undefined;
this.bound = undefined;
this.input = undefined;
this.scene = undefined;
this.sceneHud = undefined;
this.controls = undefined;
this.controlsHud = undefined;
this.interaction = undefined;
this.camera = undefined;
this.cameraHud = undefined;
this.ambient = undefined;
this.ambientHud = undefined;
this.dom = undefined;
}
}
}, {
key: 'setCursor',
/** Sets the cursor style. Applies when the mouse is inside the view's rect.
@param {String} style='auto' - CSS cursor style
@param {String} [fallback] - Fallback style, should the target style not be available
@example
// 'grab' is available in WebKit and Blink only
view.setCursor('grab', 'move'); */
value: function setCursor() {
var style = arguments.length > 0 && arguments[0] !== undefined ? arguments[0] : this.defaultCursor;
var fallback = arguments[1];
if (style != this.cursor) {
(0, _helpers.setPrefixedValue)(this.dom, "cursor", style, fallback);
this.cursor = style;
}
return this;
}
/** Projects 2-dimensional coordinate from a 3-dimensional point within the view's scene.
@param {THREE.Vector3} point3 - Input point
@example
// Grab x and y off THREE's projected Vector2
let {x, y} = view.getPoint2D(new THREE.Vector3(10, 20, 100));
@returns {THREE.Vector2} The projected point */
}, {
key: 'getPoint2',
value: function getPoint2(point3) {
var camera = arguments.length > 1 && arguments[1] !== undefined ? arguments[1] : this.camera;
var widthHalf = this.width / 2,
heightHalf = this.height / 2;
var vector = point3.project(camera);
vector.x = vector.x * widthHalf + widthHalf;
vector.y = -(vector.y * heightHalf) + heightHalf;
return vector;
}
/**
* Projects a 2D point into 3D space. Z-Value for the 2D-Point can be passed within bounds of 0 to 1.
* Note: maximal depth is used by default (z = 1), so the point is on the "far" frustum (with huge coordinates)
*/
}, {
key: 'getPoint3',
value: function getPoint3(point2) {
var camera = arguments.length > 1 && arguments[1] !== undefined ? arguments[1] : this.camera;
var zValue = arguments.length > 2 && arguments[2] !== undefined ? arguments[2] : 1;
var vector = new THREE.Vector3(point2.x / this.width * 2 - 1, -(point2.y / this.height) * 2 + 1, zValue);
vector.unproject(camera);
return vector;
}
///returns a line of all the points corresponding to given 2D view coords
}, {
key: 'getViewLine3',
value: function getViewLine3(point2) {
var near = new THREE.Vector3(point2.x / this.width * 2 - 1, -(point2.y / this.height) * 2 + 1, 0);
var far = new THREE.Vector3(point2.x / this.width * 2 - 1, -(point2.y / this.height) * 2 + 1, 1);
near.unproject(this.camera);
far.unproject(this.camera);
return new THREE.Line3(near, far);
}
}, {
key: 'updateOverlays',
value: function updateOverlays() {}
}, {
key: 'updateScopes',
value: function updateScopes() {
var parent = this.dom;
while (!!parent && parent != this.canvas.dom) {
var scope = this.canvas.scopes.get(parent);
if (scope) scope.push(this);else this.canvas.scopes.set(parent, [this]);
parent = parent.parentNode;
}
}
}, {
key: 'clear',
value: function clear(time) {
// Measure and check if dirty (size & position changed)
this.dirty = this.invalidateFrames > 0 && this.measure();
if (this.visible) {
// Call event scheduler
this.input.debounce && this.input.update();
// Update controls
this.controls.update(time);
this.hud && this.controlsHud && this.controlsHud != this.controls && this.controlsHud.update(time);
// Update interaction
this.interaction.update();
if (this.dirty || this.renderAlways || this.controls.inMotion || this.controlsHud && this.controlsHud.inMotion) {
// Clear canvas only if dirty, it isn't necessary for simple interaction
if (this.dirty) {
this.renderer.dirty = true;
this.renderer.gl.setViewport(this.old[0], this.old[1], this.old[2], this.old[3]);
this.renderer.gl.setScissor(this.old[0], this.old[1], this.old[2], this.old[3]);
this.renderer.gl.setClearColor(this.renderer.clearColor, 0);
this.renderer.gl.clear();
}
}
}
// Remove frames
if (this.invalidateFrames > 0) this.invalidateFrames--;
return this.dirty;
}
}, {
key: 'render',
value: function render(time) {
if (this.force > 0 || this.dirty || this.renderAlways || this.controls.inMotion || this.controlsHud && this.controlsHud.inMotion || this.stats) {
this.callbackBefore && this.callbackBefore();
this.renderer.gl.setViewport(this.new[0], this.new[1], this.new[2], this.new[3]);
this.renderer.gl.setScissor(this.new[0], this.new[1], this.new[2], this.new[3]);
this.renderer.gl.setClearColor(this.background, this.opacity);
this.renderer.gl.clear();
if (!this.callbackRender) {
this.renderer.gl.render(this.scene, this.camera);
this.renderer.gl.clearDepth();
this.hud && this.renderer.gl.render(this.sceneHud, this.cameraHud);
} else this.callbackRender();
this.callbackAfter && this.callbackAfter();
this.stats && this.stats.update();
if (this.force > 0) this.force--;
}
}
}, {
key: 'invalidate',
value: function invalidate() {
var frames = arguments.length > 0 && arguments[0] !== undefined ? arguments[0] : 1;
this.force += frames;
if (this.force > 60) this.force = 60;
this.invalidateFrames += frames;
if (this.invalidateFrames > 60) this.invalidateFrames = 60;
return this;
}
}, {
key: 'measure',
value: function measure(force) {
var dirty = false;
var bounds = this.dom.getBoundingClientRect();
var offset = {
top: bounds.top,
left: bounds.left,
width: bounds.width,
height: bounds.height
};
offset.top -= this.renderer.offset.top;
offset.left -= this.renderer.offset.left;
// Size changed, calibrate & invalidate
if (force || offset.width != this.width || offset.height != this.height) {
this.calibrate(offset.width, offset.height);
this.invalidate(10);
dirty = true;
}
// Position changed, invalidate!
if (force || offset.top != this.top || offset.left != this.left) {
this.invalidate(10);
dirty = true;
}
// Save old data
this.old = [this.left * this.renderer.resolution, this.bottom * this.renderer.resolution, this.width * this.renderer.resolution, this.height * this.renderer.resolution];
// Apply new
this.width = offset.width;
this.height = offset.height;
this.top = offset.top;
this.left = offset.left;
this.bottom = this.renderer.offset.height - offset.height - offset.top;
// Premultiply new
this.new = [this.left * this.renderer.resolution, this.bottom * this.renderer.resolution, this.width * this.renderer.resolution, this.height * this.renderer.resolution];
// Check visibility
var visible = !(this.height <= 0 || this.width <= 0 || this.top >= this.renderer.offset.height || this.left >= this.renderer.offset.width || this.top + this.height <= 0 || this.left + this.width <= 0);
if (this.visible != visible) {
this.visible = visible;
this.visible && this.invalidate(10);
dirty = true;
}
return dirty;
}
}, {
key: 'calibrate',
value: function calibrate(width, height) {
this.aspect = width / height;
if (this.camera instanceof THREE.PerspectiveCamera) {
this.camera.aspect = this.aspect;
this.camera.updateProjectionMatrix();
this.camera.radius = (width + height) / 4;
if (this.hud && this.cameraHud != this.camera) {
this.cameraHud.aspect = this.aspect;
this.cameraHud.updateProjectionMatrix();
this.cameraHud.radius = (width + height) / 4;
}
} else if (this.camera instanceof THREE.OrthographicCamera) {
width = this.camera.size * this.aspect;
height = this.camera.size;
this.camera.left = width / -2;
this.camera.right = width / 2;
this.camera.top = height / 2;
this.camera.bottom = height / -2;
this.camera.updateProjectionMatrix();
if (this.hud && this.cameraHud != this.camera) {
this.cameraHud.left = width / -2;
this.cameraHud.right = width / 2;
this.cameraHud.top = height / 2;
this.cameraHud.bottom = height / -2;
this.cameraHud.updateProjectionMatrix();
}
}
}
}, {
key: 'updateBounds',
value: function updateBounds() {
var box = arguments.length > 0 && arguments[0] !== undefined ? arguments[0] : undefined;
this.scene.updateBounds(box);
this.bounds.box = this.scene.bounds.box;
this.bounds.sphere = this.scene.bounds.sphere;
return this;
}
}, {
key: 'showStats',
set: function set(value) {
if (this.stats) {
this.stats.remove();
this.stats = undefined;
}
if (value) {
this.stats = new _stats2.default();
this.dom.appendChild(this.stats.dom);
}
}
}]);
return View;
}();
exports.default = View;