// // overlayManager.js // examples/libraries // // Created by Zander Otavka on 7/24/15 // Copyright 2015 High Fidelity, Inc. // // Distributed under the Apache License, Version 2.0. // See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html // // Manage overlays with object oriented goodness, instead of ugly `Overlays.h` methods. // Instead of: // // var billboard = Overlays.addOverlay("image3d", { visible: false }); // ... // Overlays.editOverlay(billboard, { visible: true }); // ... // Overlays.deleteOverlay(billboard); // // You can now do: // // var billboard = new Image3DOverlay({ visible: false }); // ... // billboard.visible = true; // ... // billboard.destroy(); // // More on usage below. Examples in `examples/example/overlayPanelExample.js`. // // Note that including this file will delete `Overlays` from the global scope. All the // functionality of `Overlays` is represented here, just better. If you try to use `Overlays` // in tandem, there may be performance problems or nasty surprises. // (function() { // Delete `Overlays` from the global scope. var Overlays = this.Overlays; delete this.Overlays; var ABSTRACT = null; var overlays = {}; var panels = {}; var overlayTypes = {}; function generateOverlayClass(superclass, type, properties) { var that; if (type == ABSTRACT) { that = function(type, params) { superclass.call(this, type, params); }; } else { that = function(params) { superclass.call(this, type, params); }; overlayTypes[type] = that; } that.prototype = new superclass(); that.prototype.constructor = that; properties.forEach(function(prop) { Object.defineProperty(that.prototype, prop, { get: function() { //V8TODO: return Overlays.getProperty(this._id, prop); }, set: function(newValue) { var keyValuePair = {}; keyValuePair[prop] = newValue; this.setProperties(keyValuePair); }, configurable: false }); }); return that; } // // Create a new JavaScript object for an overlay of given ID. // function makeOverlayFromId(id) { var type = Overlays.getOverlayType(id); if (!type) { return null; } var overlay = new overlayTypes[type](); overlay._id = id; overlays[id] = overlay; return overlay; } // // Get or create an overlay object from the id. // // @param knownOverlaysOnly (Optional: Boolean) // If true, a new object will not be created. // @param searchList (Optional: Object) // Map of overlay id's and overlay objects. Can be generated with // `OverlayManager.makeSearchList`. // function findOverlay(id, knownOverlaysOnly, searchList) { if (id > 0) { knownOverlaysOnly = Boolean(knownOverlaysOnly); searchList = searchList || overlays; var foundOverlay = searchList[id]; if (foundOverlay) { return foundOverlay; } if (!knownOverlaysOnly) { return makeOverlayFromId(id); } } return null; } // // Create a new JavaScript object for a panel of given ID. // function makePanelFromId(id) { if (!Overlays.isAddedPanel(id)) { return null; } var panel = new OverlayPanel(); panel._id = id; overlays[id] = overlay; return overlay; } // // Get or create a panel object from the id. // // @param knownOverlaysOnly (Optional: Boolean) // If true, a new object will not be created. // @param searchList (Optional: Object) // Map of overlay id's and overlay objects. Can be generated with // `OverlayManager.makeSearchList`. // function findPanel(id, knownPanelsOnly, searchList) { if (id > 0) { knownPanelsOnly = Boolean(knownPanelsOnly); searchList = searchList || panels; var foundPanel = searchList[id]; if (foundPanel) { return foundPanel; } if (!knownPanelsOnly) { return makePanelFromId(id); } } return null; } function findOverlayOrPanel(id, knownObjectsOnly, searchList) { return findOverlay(id, knownObjectsOnly, searchList) || findPanel(id, knownObjectsOnly, searchList); } var Overlay = (function() { var that = function(type, params) { if (type && params) { this._id = Overlays.addOverlay(type, params); overlays[this._id] = this; } else { this._id = 0; } }; that.prototype.constructor = that; Object.defineProperty(that.prototype, "isLoaded", { get: function() { return Overlays.isLoaded(this._id); } }); Object.defineProperty(that.prototype, "parentPanel", { get: function() { return findPanel(Overlays.getParentPanel(this._id)); } }); that.prototype.getTextSize = function(text) { return Overlays.textSize(this._id, text); }; that.prototype.setProperties = function(properties) { Overlays.editOverlay(this._id, properties); }; that.prototype.clone = function() { return makeOverlayFromId(Overlays.cloneOverlay(this._id)); }; that.prototype.destroy = function() { Overlays.deleteOverlay(this._id); }; that.prototype.isPanelAttachable = function() { return false; }; return generateOverlayClass(that, ABSTRACT, [ "alpha", "pulseMax", "pulseMin", "pulsePeriod", "alphaPulse", "colorPulse", "visible", "anchor" ]); })(); // Supports multiple inheritance of properties. Just `concat` them onto the end of the // properties list. var PanelAttachable = ["offsetPosition", "offsetRotation", "offsetScale"]; var Billboardable = ["isFacingAvatar"]; var Overlay2D = generateOverlayClass(Overlay, ABSTRACT, [ "bounds", "x", "y", "width", "height" ]); var Base3DOverlay = generateOverlayClass(Overlay, ABSTRACT, [ "position", "lineWidth", "rotation", "isSolid", "isFilled", "isWire", "isDashedLine", "ignoreRayIntersection", "drawInFront" ]); var Planar3DOverlay = generateOverlayClass(Base3DOverlay, ABSTRACT, [ "dimensions" ]); var Billboard3DOverlay = generateOverlayClass(Planar3DOverlay, ABSTRACT, [ ].concat(PanelAttachable).concat(Billboardable)); Billboard3DOverlay.prototype.isPanelAttachable = function() { return true; }; var Volume3DOverlay = generateOverlayClass(Base3DOverlay, ABSTRACT, [ "dimensions" ]); Image3DOverlay = generateOverlayClass(Billboard3DOverlay, "image3d", [ "url", "subImage" ]); Text3DOverlay = generateOverlayClass(Billboard3DOverlay, "text3d", [ "text", "backgroundColor", "backgroundAlpha", "lineHeight", "leftMargin", "topMargin", "rightMargin", "bottomMargin" ]); Cube3DOverlay = generateOverlayClass(Volume3DOverlay, "cube", [ "borderSize" ]); Sphere3DOverlay = generateOverlayClass(Volume3DOverlay, "sphere", [ ]); Circle3DOverlay = generateOverlayClass(Planar3DOverlay, "circle3d", [ "startAt", "endAt", "outerRadius", "innerRadius", "hasTickMarks", "majorTickMarksAngle", "minorTickMarksAngle", "majorTickMarksLength", "minorTickMarksLength", "majorTickMarksColor", "minorTickMarksColor" ]); Rectangle3DOverlay = generateOverlayClass(Planar3DOverlay, "rectangle3d", [ ]); Line3DOverlay = generateOverlayClass(Base3DOverlay, "line3d", [ "start", "end" ]); Grid3DOverlay = generateOverlayClass(Planar3DOverlay, "grid", [ "minorGridWidth", "majorGridEvery" ]); ModelOverlay = generateOverlayClass(Volume3DOverlay, "model", [ "url", "dimensions", "textures" ]); OverlayPanel = (function() { var that = function(params) { this._id = Overlays.addPanel(params); panels[this._id] = this; }; that.prototype.constructor = that; var props = [ "anchorPosition", "anchorPositionBinding", "anchorRotation", "anchorRotationBinding", "anchorScale", "visible" ].concat(PanelAttachable).concat(Billboardable) props.forEach(function(prop) { Object.defineProperty(that.prototype, prop, { get: function() { return Overlays.getPanelProperty(this._id, prop); }, set: function(newValue) { var keyValuePair = {}; keyValuePair[prop] = newValue; this.setProperties(keyValuePair); }, configurable: false }); }); Object.defineProperty(that.prototype, "parentPanel", { get: function() { return findPanel(Overlays.getParentPanel(this._id)); } }); Object.defineProperty(that.prototype, "children", { get: function() { var idArray = Overlays.getPanelProperty(this._id, "children"); var objArray = []; for (var i = 0; i < idArray.length; i++) { objArray[i] = findOverlayOrPanel(idArray[i]); } return objArray; } }); that.prototype.addChild = function(child) { Overlays.setParentPanel(child._id, this._id); return child; }; that.prototype.removeChild = function(child) { if (child.parentPanel === this) { Overlays.setParentPanel(child._id, 0); } }; that.prototype.setProperties = function(properties) { Overlays.editPanel(this._id, properties); }; that.prototype.setChildrenVisible = function() { this.children.forEach(function(child) { child.visible = true; if (child.setChildrenVisible !== undefined) { child.setChildrenVisible(); } }); }; that.prototype.destroy = function() { Overlays.deletePanel(this._id); }; return that; })(); OverlayManager = { findOnRay: function(pickRay, knownOverlaysOnly, searchList) { var rayPickResult = Overlays.findRayIntersection(pickRay); if (rayPickResult.intersects) { return findOverlay(rayPickResult.overlayID, knownOverlaysOnly, searchList); } return null; }, findAtPoint: function(point, knownOverlaysOnly, searchList) { var foundID = Overlays.getOverlayAtPoint(point); if (foundID) { return findOverlay(foundID, knownOverlaysOnly, searchList); } else { var pickRay = Camera.computePickRay(point.x, point.y); return OverlayManager.findOnRay(pickRay, knownOverlaysOnly, searchList); } }, makeSearchList: function(array) { var searchList = {}; array.forEach(function(object) { searchList[object._id] = object; }); return searchList; }, findRayIntersection: function(pickRay) { return Overlays.findRayIntersection(pickRay); } }; // Threadsafe cleanup of JavaScript objects. function onOverlayDeleted(id) { if (id in overlays) { if (overlays[id].parentPanel) { overlays[id].parentPanel.removeChild(overlays[id]); } delete overlays[id]; } } function onPanelDeleted(id) { if (id in panels) { if (panels[id].parentPanel) { panels[id].parentPanel.removeChild(panels[id]); } delete panels[id]; } } Overlays.overlayDeleted.connect(onOverlayDeleted); Overlays.panelDeleted.connect(onPanelDeleted); })();