From 377a1a54aeaa8e9343325b7b589b056d6a8d242d Mon Sep 17 00:00:00 2001 From: Zander Otavka Date: Wed, 15 Jul 2015 15:32:14 -0700 Subject: [PATCH] Add abstraction layer for Overlays.h. `examples/libraries/overlayUtils.js` allows you to manage overlays in an object oriented manner. Instead of: var billboard = Overlays.addOverlay("billboard", { visible: false }); ... Overlays.editOverlay(billboard, { visible: true }); ... Overlays.deleteOverlay(billboard); You can now do: var billboard = new BillboardOverlay({ visible: false }); ... billboard.visible = true; ... billboard.destroy(); --- examples/example/ui/floatingUIExample.js | 98 ++++----- examples/libraries/overlayUtils.js | 257 ++++++++++++++++++++++- interface/src/ui/overlays/Overlays.h | 6 + 3 files changed, 298 insertions(+), 63 deletions(-) diff --git a/examples/example/ui/floatingUIExample.js b/examples/example/ui/floatingUIExample.js index 09deca4ec7..3b555efce3 100644 --- a/examples/example/ui/floatingUIExample.js +++ b/examples/example/ui/floatingUIExample.js @@ -9,7 +9,10 @@ // See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html // -Script.include(["../../libraries/globals.js"]); +Script.include([ + "../../libraries/globals.js", + "../../libraries/overlayUtils.js", +]); var BG_IMAGE_URL = HIFI_PUBLIC_BUCKET + "images/card-bg.svg"; var RED_DOT_IMAGE_URL = HIFI_PUBLIC_BUCKET + "images/red-dot.svg"; @@ -24,13 +27,11 @@ function isBlank(rotation) { rotation.w == BLANK_ROTATION.w; } -var panel = Overlays.addPanel({ +var panel = new FloatingUIPanel({ offsetPosition: { x: 0, y: 0, z: 1 }, }); -var panelChildren = []; - -var bg = Overlays.addOverlay("billboard", { +var bg = panel.addChild(new BillboardOverlay({ url: BG_IMAGE_URL, dimensions: { x: 0.5, @@ -40,11 +41,9 @@ var bg = Overlays.addOverlay("billboard", { visible: true, alpha: 1.0, ignoreRayIntersection: false, - attachedPanel: panel, -}); -panelChildren.push(bg); +})); -var redDot = Overlays.addOverlay("billboard", { +var redDot = panel.addChild(new BillboardOverlay({ url: RED_DOT_IMAGE_URL, dimensions: { x: 0.1, @@ -54,16 +53,14 @@ var redDot = Overlays.addOverlay("billboard", { visible: true, alpha: 1.0, ignoreRayIntersection: false, - attachedPanel: panel, offsetPosition: { x: -0.15, y: -0.15, z: -0.001 } -}); -panelChildren.push(redDot); +})); -var redDot2 = Overlays.addOverlay("billboard", { +var redDot2 = panel.addChild(new BillboardOverlay({ url: RED_DOT_IMAGE_URL, dimensions: { x: 0.1, @@ -73,16 +70,14 @@ var redDot2 = Overlays.addOverlay("billboard", { visible: true, alpha: 1.0, ignoreRayIntersection: false, - attachedPanel: panel, offsetPosition: { x: -0.15, y: 0, z: -0.001 } -}); -panelChildren.push(redDot2); +})); -var blueSquare = Overlays.addOverlay("billboard", { +var blueSquare = panel.addChild(new BillboardOverlay({ url: BLUE_SQUARE_IMAGE_URL, dimensions: { x: 0.1, @@ -92,16 +87,14 @@ var blueSquare = Overlays.addOverlay("billboard", { visible: true, alpha: 1.0, ignoreRayIntersection: false, - attachedPanel: panel, offsetPosition: { x: 0.1, y: 0, z: -0.001 } -}); -panelChildren.push(blueSquare); +})); -var blueSquare2 = Overlays.addOverlay("billboard", { +var blueSquare2 = panel.addChild(new BillboardOverlay({ url: BLUE_SQUARE_IMAGE_URL, dimensions: { x: 0.1, @@ -111,63 +104,46 @@ var blueSquare2 = Overlays.addOverlay("billboard", { visible: true, alpha: 1.0, ignoreRayIntersection: false, - attachedPanel: panel, offsetPosition: { x: 0.1, y: 0.11, z: -0.001 } -}); -panelChildren.push(blueSquare2); +})); -var blueSquare3 = Overlays.addOverlay("billboard", { - url: BLUE_SQUARE_IMAGE_URL, - dimensions: { - x: 0.1, - y: 0.1, - }, - isFacingAvatar: false, - visible: true, - alpha: 1.0, - ignoreRayIntersection: false, - attachedPanel: panel, - offsetPosition: { - x: -0.01, - y: 0.11, - z: -0.001 - } -}); -panelChildren.push(blueSquare3); +var blueSquare3 = blueSquare2.clone(); +blueSquare3.offsetPosition = { + x: -0.01, + y: 0.11, + z: -0.001 +}; +blueSquare3.ignoreRayIntersection = false; Controller.mousePressEvent.connect(function(event) { if (event.isRightButton) { - var newOffsetRotation = BLANK_ROTATION; - if (isBlank(Overlays.getPanelProperty(panel, "offsetRotation"))) { + var newOffsetRotation; + print(JSON.stringify(panel.offsetRotation)) + if (isBlank(panel.offsetRotation)) { newOffsetRotation = Quat.multiply(MyAvatar.orientation, { x: 0, y: 1, z: 0, w: 0 }); + } else { + newOffsetRotation = BLANK_ROTATION; } - Overlays.editPanel(panel, { - offsetRotation: newOffsetRotation - }); + panel.offsetRotation = newOffsetRotation; } else if (event.isLeftButton) { var pickRay = Camera.computePickRay(event.x, event.y) - var rayPickResult = Overlays.findRayIntersection(pickRay); - print(String(rayPickResult.overlayID)); - if (rayPickResult.intersects) { - for (var i in panelChildren) { - if (panelChildren[i] == rayPickResult.overlayID) { - var oldPos = Overlays.getProperty(rayPickResult.overlayID, "offsetPosition"); - var newPos = { - x: Number(oldPos.x), - y: Number(oldPos.y), - z: Number(oldPos.z) + 0.1 - } - Overlays.editOverlay(rayPickResult.overlayID, { offsetPosition: newPos }); - } + var overlay = panel.findRayIntersection(pickRay); + if (overlay) { + var oldPos = overlay.offsetPosition; + var newPos = { + x: Number(oldPos.x), + y: Number(oldPos.y), + z: Number(oldPos.z) + 0.1 } + overlay.offsetPosition = newPos; } } }); Script.scriptEnding.connect(function() { - Overlays.deletePanel(panel); + panel.destroy(); }); \ No newline at end of file diff --git a/examples/libraries/overlayUtils.js b/examples/libraries/overlayUtils.js index 636ea40825..3acab0103b 100644 --- a/examples/libraries/overlayUtils.js +++ b/examples/libraries/overlayUtils.js @@ -1,6 +1,39 @@ +// +// overlayUtils.js +// examples/libraries +// +// Modified by Zander Otavka on 7/15/15 +// Copyright 2014 High Fidelity, Inc. +// +// Manage overlays with object oriented goodness, instead of ugly `Overlays.h` methods. +// Instead of: +// +// var billboard = Overlays.addOverlay("billboard", { visible: false }); +// ... +// Overlays.editOverlay(billboard, { visible: true }); +// ... +// Overlays.deleteOverlay(billboard); +// +// You can now do: +// +// var billboard = new BillboardOverlay({ visible: false }); +// ... +// billboard.visible = true; +// ... +// billboard.destroy(); +// +// See more on usage below. +// +// Distributed under the Apache License, Version 2.0. +// See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html +// + + /** - * OverlayGroup provides a way to create composite overlays and control their - * position relative to a settable rootPosition and rootRotation. + * DEPRECATION WARNING: Will be deprecated soon in favor of FloatingUIPanel. + * + * OverlayGroup provides a way to create composite overlays and control their + * position relative to a settable rootPosition and rootRotation. */ OverlayGroup = function(opts) { var that = {}; @@ -62,3 +95,223 @@ OverlayGroup = function(opts) { return that; }; + + +/** + * Object oriented abstraction layer for overlays. + * + * Usage: + * // Create an overlay + * var billboard = new BillboardOverlay({ + * visible: true, + * isFacingAvatar: true, + * ignoreRayIntersections: false + * }); + * + * // Get a property + * var isVisible = billboard.visible; + * + * // Set a single property + * billboard.position = { x: 1, y: 3, z: 2 }; + * + * // Set multiple properties at the same time + * billboard.setProperties({ + * url: "http://images.com/overlayImage.jpg", + * dimensions: { x: 2, y: 2 } + * }); + * + * // Clone an overlay + * var clonedBillboard = billboard.clone(); + * + * // Remove an overlay from the world + * billboard.destroy(); + * + * // Remember, there is a poor orphaned JavaScript object left behind. You should remove any + * // references to it so you don't accidentally try to modify an overlay that isn't there. + * billboard = undefined; + */ +(function() { + var ABSTRACT = null; + + function generateOverlayClass(superclass, type, properties) { + var that; + if (type == ABSTRACT) { + that = function(type, params) { + superclass.apply(this, [type, params]); + }; + } else { + that = function(params) { + superclass.apply(this, [type, params]); + }; + } + + that.prototype = new superclass(); + that.prototype.constructor = that; + + properties.forEach(function(prop) { + Object.defineProperty(that.prototype, prop, { + get: function() { + return Overlays.getProperty(this._id, prop); + }, + set: function(newValue) { + var keyValuePair = {}; + keyValuePair[prop] = newValue; + this.setProperties(keyValuePair); + }, + configurable: true + }); + }); + + return that; + } + + + // Supports multiple inheritance of properties. Just `concat` them onto the end of the + // properties list. + var PANEL_ATTACHABLE_FIELDS = ["attachedPanel"]; + + // TODO: finish exposing all overlay classes. + + var Overlay = (function() { + var BaseOverlay = (function() { + var that = function(type, params) { + Object.apply(this, []); + if (type && params) { + this._type = type; + this._id = Overlays.addOverlay(type, params); + } else { + this._type = ""; + this._id = 0; + } + this._attachedPanelPointer = null; + }; + + that.prototype = new Object(); + that.prototype.constructor = that; + + Object.defineProperty(that.prototype, "overlayType", { + get: function() { + return this._type; + } + }); + + that.prototype.setProperties = function(properties) { + Overlays.editOverlay(this._id, properties); + }; + + that.prototype.clone = function() { + var clone = new this.constructor(); + clone._type = this._type; + clone._id = Overlays.cloneOverlay(this._id); + if (this._attachedPanelPointer) { + this._attachedPanelPointer.addChild(clone); + } + return clone; + }; + + that.prototype.destroy = function() { + Overlays.deleteOverlay(this._id); + }; + + return that; + }()); + + return generateOverlayClass(BaseOverlay, ABSTRACT, [ + "alpha", "glowLevel", "pulseMax", "pulseMin", "pulsePeriod", "glowLevelPulse", + "alphaPulse", "colorPulse", "visible", "anchor" + ]); + }()); + + var Base3DOverlay = generateOverlayClass(Overlay, ABSTRACT, [ + "position", "lineWidth", "rotation", "isSolid", "isFilled", "isWire", "isDashedLine", + "ignoreRayIntersection", "drawInFront", "drawOnHUD" + ]); + + var Planar3DOverlay = generateOverlayClass(Base3DOverlay, ABSTRACT, [ + "dimensions" + ]); + + BillboardOverlay = generateOverlayClass(Planar3DOverlay, "billboard", [ + "url", "subImage", "isFacingAvatar", "offsetPosition" + ].concat(PANEL_ATTACHABLE_FIELDS)); +}()); + + +/** + * Object oriented abstraction layer for panels. + */ +FloatingUIPanel = (function() { + var that = function(params) { + this._id = Overlays.addPanel(params); + this._children = []; + }; + + var FIELDS = ["offsetPosition", "offsetRotation", "facingRotation"]; + FIELDS.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, "children", { + get: function() { + return this._children.slice(); + } + }) + + that.prototype.addChild = function(overlay) { + overlay.attachedPanel = this._id; + overlay._attachedPanelPointer = this; + this._children.push(overlay); + return overlay; + }; + + that.prototype.removeChild = function(overlay) { + var i = this._children.indexOf(overlay); + if (i >= 0) { + overlay.attachedPanel = 0; + overlay._attachedPanelPointer = null; + this._children.splice(i, 1); + } + }; + + that.prototype.setVisible = function(visible) { + for (var i in this._children) { + this._children[i].visible = visible; + } + }; + + that.prototype.setProperties = function(properties) { + Overlays.editPanel(this._id, properties); + }; + + that.prototype.destroy = function() { + Overlays.deletePanel(this._id); + var i = _panels.indexOf(this); + if (i >= 0) { + _panels.splice(i, 1); + } + }; + + that.prototype.findRayIntersection = function(pickRay) { + var rayPickResult = Overlays.findRayIntersection(pickRay); + if (rayPickResult.intersects) { + for (var i in this._children) { + if (this._children[i]._id == rayPickResult.overlayID) { + return this._children[i]; + } + } + } + return null; + }; + + return that; +}()); diff --git a/interface/src/ui/overlays/Overlays.h b/interface/src/ui/overlays/Overlays.h index 2ba2ec8f45..80da2e6db1 100644 --- a/interface/src/ui/overlays/Overlays.h +++ b/interface/src/ui/overlays/Overlays.h @@ -2,8 +2,14 @@ // Overlays.h // interface/src/ui/overlays // +// Modified by Zander Otavka on 7/15/15 // Copyright 2014 High Fidelity, Inc. // +// Exposes methods for managing `Overlay`s and `FloatingUIPanel`s to scripts. +// +// YOU SHOULD NOT USE `Overlays` DIRECTLY, unless you like pain and deprecation. Instead, use the +// object oriented abstraction layer found in `examples/libraries/overlayUtils.js`. +// // Distributed under the Apache License, Version 2.0. // See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html //