awv3
Version:
⚡ AWV3 embedded CAD
526 lines (439 loc) • 17.3 kB
JavaScript
import _extends from "@babel/runtime/helpers/extends";
import _createClass from "@babel/runtime/helpers/createClass";
import _inheritsLoose from "@babel/runtime/helpers/inheritsLoose";
import _assertThisInitialized from "@babel/runtime/helpers/assertThisInitialized";
var _class, _temp;
import * as THREE from 'three';
import v4 from 'uuid-v4';
import { queryDom, setPrefixedValue } from './helpers';
import { lastCreated } from './canvas';
import DomEvents from './dom';
import Interaction from './interaction';
import Stats from '../misc/stats';
import Orbit from '../controls/orbit';
import CombinedCamera from '../three/combinedcamera';
import Events from '../core/events';
import { resizeListen, resizeUnlisten } from 'dom-resize';
/** 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 = (_temp = _class =
/*#__PURE__*/
function (_Events) {
_inheritsLoose(View, _Events);
/** Construct a new View
@param {Object} [canvas=lastCreated] - The parent canvas, if none defined the lastCreated will be used
@param {Object} [options={}] - this.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(canvas, options) {
var _this;
if (canvas === void 0) {
canvas = lastCreated;
}
if (options === void 0) {
options = {};
}
_this = _Events.call(this) || this;
_this.id = v4();
_this.canvas = canvas;
_this.renderer = canvas.renderer;
_this.resolution = _this.renderer.resolution;
_this.invalidateFrames = 2;
_this.force = 0;
_this.dirty = true;
_this.bounds = {
box: new THREE.Box3(),
sphere: new THREE.Sphere()
};
_this.options = _extends({
dom: canvas.dom,
renderAlways: false,
visible: true,
callback: undefined,
callbackRender: undefined,
callbackAfter: undefined,
background: _this.renderer.clearColor,
opacity: 0,
defaultCursor: 'auto'
}, options);
_this.dom = queryDom(_this.options.dom);
_this.renderAlways = _this.options.renderAlways;
_this.visible = _this.options.visible;
_this.callbackBefore = _this.options.callback;
_this.callbackRender = _this.options.callbackRender;
_this.callbackAfter = _this.options.callbackAfter;
_this.background = _this.options.background;
_this.opacity = _this.options.opacity;
_this.defaultCursor = _this.options.defaultCursor; // Make sure the view hides overflow and is not selectable
_this.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 (_this.options.background !== _this.renderer.clearColor) _this.dom.style.border = "2px solid #" + _this.background.getHexString();
_this.input = new DomEvents(_assertThisInitialized(_this), {
wheel: function wheel(state) {
_this.controls.onMouseWheel(state);
_this.hud.forEach(function (hud) {
return hud.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.forEach(function (hud) {
return hud.onMouseMove(state);
});
},
mousedown: function mousedown(state) {
_this.interaction.onMouseDown(state);
_this.controls.onMouseDown(state);
_this.hud.forEach(function (hud) {
return hud.onMouseDown(state);
});
},
mouseup: function mouseup(state) {
_this.interaction.onMouseUp(state);
_this.controls.onMouseUp(state);
_this.hud.forEach(function (hud) {
return hud.onMouseUp(state);
});
},
touchstart: function touchstart(state) {
_this.interaction.onMouseDown(state);
_this.controls.onTouchStart(state);
_this.hud.forEach(function (hud) {
return hud.onTouchStart(state);
});
},
touchmove: function touchmove(state) {
_this.interaction.onMouseMove(state);
_this.controls.onTouchMove(state);
_this.hud.forEach(function (hud) {
return hud.onTouchMove(state);
});
},
touchend: function touchend(state) {
_this.interaction.onMouseUp(state);
_this.controls.onTouchEnd(state);
_this.hud.forEach(function (hud) {
return hud.onTouchEnd(state);
});
}
});
_this.scene = new THREE.Scene();
_this.scene.canvas = canvas;
_this.scene.view = _assertThisInitialized(_this);
_this.ambient = new THREE.AmbientLight(_this.options.ambientColor ? _this.options.ambientColor : 0xffffff);
_this.ambient.intensity = typeof _this.options.ambientIntensity !== 'undefined' ? _this.options.ambientIntensity : 1.0;
_this.ambient.keep = true;
_this.ambient.view = _assertThisInitialized(_this);
_this.scene.add(_this.ambient);
_this.camera = new CombinedCamera(_assertThisInitialized(_this), _this.options); // his.camera = Being set by the above
_this.controls = new Orbit(_assertThisInitialized(_this), _extends({
maxPolarAngle: Math.PI,
minDistance: 1,
maxDistance: 20000
}, _this.options));
_this.hud = [];
_this.interaction = new Interaction(_assertThisInitialized(_this));
_this.canvas.views.push(_assertThisInitialized(_this));
_this.renderer.resize();
resizeListen(_this.dom, function () {
return _this.invalidate(30);
});
if (_this.options.stats) {
_this.showStats = true;
}
return _this;
}
var _proto = View.prototype;
_proto.destroy = function destroy() {
if (!this.__destroyed) {
this.__destroyed = true;
this.input.detach();
this.input.removeListeners();
this.input.removeInspectors();
this.scene.destroy({
keep: false
});
this.render = function () {};
this.clear = function () {};
resizeUnlisten(this.dom);
this.dom = undefined;
this.view = undefined;
this.canvas = undefined;
this.renderer = undefined;
this.bound = undefined;
this.input = undefined;
this.scene = undefined;
this.hud.forEach(function (hud) {
return hud.destroy();
});
this.hud = [];
}
};
_proto.addHud = function addHud(obj) {
this.hud = this.hud.concat(obj);
};
_proto.removeHud = function removeHud(obj) {
this.hud = this.hud.filter(function (hud) {
return hud !== obj;
});
};
/** 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'); */
_proto.setCursor = function setCursor(style, fallback) {
if (style === void 0) {
style = this.defaultCursor;
}
if (style != this.cursor) {
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 */
_proto.getPoint2 = function getPoint2(point3, camera) {
if (camera === void 0) {
camera = this.camera.display;
}
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)
*/
_proto.getPoint3 = function getPoint3(point2, camera, zValue) {
if (camera === void 0) {
camera = this.camera.display;
}
if (zValue === void 0) {
zValue = 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
_proto.getViewLine3 = function getViewLine3(point2, camera) {
if (camera === void 0) {
camera = this.camera.display;
}
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(camera);
far.unproject(camera);
return new THREE.Line3(near, far);
};
/**
* Calculate the scale factor that will make the object at the point3 position be radiusPx pixels wide.
* Example use: object.scale.set(calculateScaleFactor(object.getWorldPosition(), radiusPx));
* @param point3 Vector3 position of the object
* @param radiusPx Number desired radius of the object in pixels
* @return Number scale factor for the object
*/
_proto.calculateScaleFactor = function calculateScaleFactor(point3, radiusPx, camera) {
if (camera === void 0) {
camera = this.camera.display;
}
var point2 = this.getPoint2(point3.clone());
var scale = 0;
for (var i = 0; i < 2; ++i) {
var point2off = point2.clone().setComponent(i, point2.getComponent(i) + radiusPx);
var point3off = this.getPoint3(point2off, camera, point2off.z);
scale = Math.max(scale, point3.distanceTo(point3off));
}
return scale;
};
_proto.clear = 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.forEach(function (hud) {
return hud.update(time);
}); // Update interaction
this.interaction.update();
if (this.dirty || this.renderAlways || this.controls.inMotion || this.hudInMotion) {
// Clear canvas only if dirty, it isn't necessary for simple interaction
if (this.dirty) {
this.renderer.dirty = true;
if (this.renderer.options.scissorTest) {
this.renderer.gl.setViewport(this.old[0], this.old[1], this.old[2], this.old[3]);
this.renderer.gl.setScissor(this.old[0] * this.resolution, this.old[1] * this.resolution, this.old[2] * this.resolution, this.old[3] * this.resolution);
}
this.renderer.gl.setClearColor(this.renderer.clearColor, 0);
this.renderer.gl.clear();
}
}
} // Remove frames
if (this.invalidateFrames > 0) this.invalidateFrames--;
return this.dirty;
};
_proto.render = function render(time) {
if (this.force > 0 || this.dirty || this.renderAlways || this.controls.inMotion || this.stats || this.hudInMotion) {
this.callbackBefore && this.callbackBefore();
if (this.renderer.options.scissorTest) {
this.renderer.gl.setViewport(this.new[0], this.new[1], this.new[2], this.new[3]);
this.renderer.gl.setScissor(this.new[0] * this.resolution, this.new[1] * this.resolution, this.new[2] * this.resolution, this.new[3] * this.resolution);
}
var camera = this.camera;
if (!this.callbackRender) {
this.renderer.gl.setClearColor(this.background, this.opacity);
this.renderer.gl.clear();
this.renderer.gl.render(this.scene, this.camera.display);
this.renderer.gl.clearDepth();
this.hud.forEach(function (hud) {
return hud.render();
});
} else this.callbackRender();
this.callbackAfter && this.callbackAfter();
this.stats && this.stats.update();
if (this.force > 0) this.force--;
}
};
_proto.invalidate = function invalidate(frames) {
if (frames === void 0) {
frames = 1;
}
this.force += frames;
if (this.force > 60) this.force = 60;
this.invalidateFrames += frames;
if (this.invalidateFrames > 60) this.invalidateFrames = 60;
return this;
};
_proto.measure = 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;
this.emit(View.Events.SizeChanged, offset);
} // Position changed, invalidate!
if (force || offset.top != this.top || offset.left != this.left) {
this.invalidate(10);
dirty = true;
this.emit(View.Events.PositionChanged, offset);
} // 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;
};
_proto.calibrate = function calibrate(width, height) {
this.aspect = width / height;
this.camera.aspect = this.aspect;
this.camera.updateProjectionMatrix();
this.camera.radius = (width + height) / 4;
this.hud.forEach(function (hud) {
return hud.calibrate(width, height);
});
};
_proto.updateBounds = function updateBounds(box) {
if (box === void 0) {
box = undefined;
}
this.scene.updateBounds(box);
this.bounds.box = this.scene.bounds.box;
this.bounds.sphere = this.scene.bounds.sphere;
return this;
};
_createClass(View, [{
key: "hudInMotion",
get: function get() {
return this.hud.length > 0 && this.hud.reduce(function (acc, val) {
return {
inMotion: acc.inMotion && val.inMotion
};
}).inMotion;
}
}, {
key: "showStats",
set: function set(value) {
if (this.stats) {
this.stats.remove();
this.stats = undefined;
}
if (value) {
this.stats = new Stats();
this.dom.appendChild(this.stats.dom);
}
}
}]);
return View;
}(Events), Object.defineProperty(_class, "Events", {
configurable: true,
enumerable: true,
writable: true,
value: {
SizeChanged: 'SizeChanged',
PositionChanged: 'PositionChanged'
}
}), _temp);
export { View as default };