// // Created by Bradley Austin Davis on 2015/08/25 // 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 // // FIXME Script paths have to be relative to the caller, in this case libraries/OmniTool.js Script.include("magBalls/constants.js"); Script.include("magBalls/graph.js"); Script.include("magBalls/edgeSpring.js"); Script.include("magBalls/magBalls.js"); Script.include("libraries/avatarRelativeOverlays.js"); OmniToolModuleType = "MagBallsController" getMagBallsData = function(id) { return getEntityCustomData(MAG_BALLS_DATA_NAME, id, {}); } setMagBallsData = function(id, value) { setEntityCustomData(MAG_BALLS_DATA_NAME, id, value); } var UI_BALL_RADIUS = 0.01; var MODE_INFO = { }; MODE_INFO[BALL_EDIT_MODE_ADD] = { uiPosition: { x: 0.15, y: -0.08, z: -0.35, }, colors: [ COLORS.GREEN, COLORS.BLUE ], // FIXME use an http path or find a way to get the relative path to the file url: Script.resolvePath('html/magBalls/addMode.html'), }; MODE_INFO[BALL_EDIT_MODE_DELETE] = { uiPosition: { x: 0.20, y: -0.08, z: -0.32, }, colors: [ COLORS.RED, COLORS.BLUE ], // FIXME use an http path or find a way to get the relative path to the file url: Script.resolvePath('html/magBalls/deleteMode.html'), }; var UI_POSITION_MODE_LABEL = Vec3.multiply(0.5, Vec3.sum(MODE_INFO[BALL_EDIT_MODE_ADD].uiPosition, MODE_INFO[BALL_EDIT_MODE_DELETE].uiPosition)); UI_POSITION_MODE_LABEL.y = -0.02; var UI_BALL_PROTOTYPE = { size: UI_BALL_RADIUS * 2.0, alpha: 1.0, solid: true, visible: true, } OmniToolModules.MagBallsController = function(omniTool, entityId) { this.omniTool = omniTool; this.entityId = entityId; // In hold mode, holding a ball requires that you keep the action // button pressed, while if this is false, clicking on a ball selects // it and clicking again will drop it. this.holdMode = true; this.highlighter = new Highlighter(); this.magBalls = new MagBalls(); this.highlighter.setSize(BALL_SIZE); this.ghostEdges = {}; this.selectionRadiusMultipler = 1.5; this.uiOverlays = new AvatarRelativeOverlays(); // create the overlay relative to the avatar this.uiOverlays.addOverlay("sphere", mergeObjects(UI_BALL_PROTOTYPE, { color: MODE_INFO[BALL_EDIT_MODE_ADD].colors[0], position: MODE_INFO[BALL_EDIT_MODE_ADD].uiPosition, })); this.uiOverlays.addOverlay("sphere", mergeObjects(UI_BALL_PROTOTYPE, { color: MODE_INFO[BALL_EDIT_MODE_DELETE].colors[0], position: MODE_INFO[BALL_EDIT_MODE_DELETE].uiPosition, })); // FIXME find the proper URLs to use this.modeLabel = this.uiOverlays.addOverlay("web3d", { isFacingAvatar: true, alpha: 1.0, dimensions: { x: 0.16, y: 0.12, z: 0.001}, color: "White", position: UI_POSITION_MODE_LABEL, }); this.setMode(BALL_EDIT_MODE_ADD); // DEBUGGING ONLY - Fix old, bad edge bounding boxes //for (var edgeId in this.magBalls.edges) { // Entities.editEntity(edgeId, { // dimensions: LINE_DIMENSIONS, // }); //} // DEBUGGING ONLY - Clear any previous balls // this.magBalls.clear(); // DEBUGGING ONLY - Attempt to fix connections between balls // and delete bad connections. Warning... if you haven't looked around // and caused the domain server to send you all the nearby balls as well as the connections, // this can break your structures // this.magBalls.repair(); } OmniToolModules.MagBallsController.prototype.onUnload = function() { this.clearGhostEdges(); this.uiOverlays.deleteAll(); } OmniToolModules.MagBallsController.prototype.setMode = function(mode) { if (mode === this.mode) { return; } logDebug("Changing mode to '" + mode + "'"); Overlays.editOverlay(this.modeLabel, { url: MODE_INFO[mode].url }); this.mode = mode; var color1; var color2; switch (this.mode) { case BALL_EDIT_MODE_ADD: color1 = COLORS.BLUE; color2 = COLORS.GREEN; break; case BALL_EDIT_MODE_MOVE: color1 = COLORS.GREEN; color2 = COLORS.LIGHT_GREEN; break; case BALL_EDIT_MODE_DELETE: color1 = COLORS.RED; color2 = COLORS.BLUE; break; case BALL_EDIT_MODE_DELETE_SHAPE: color1 = COLORS.RED; color2 = COLORS.YELLOW; break; } this.omniTool.model.setTipColors(color1, color2); } OmniToolModules.MagBallsController.prototype.findUiBallHit = function() { var result = null; for (var mode in MODE_INFO) { var modeInfo = MODE_INFO[mode]; var spherePoint = getEyeRelativePosition(modeInfo.uiPosition); if (findSpherePointHit(spherePoint, UI_BALL_RADIUS * 2, this.tipPosition)) { this.highlighter.highlight(spherePoint); this.highlighter.setColor("White"); // FIXME why doesn't this work? this.highlighter.setSize(UI_BALL_RADIUS * 4); return mode; } } return; } OmniToolModules.MagBallsController.prototype.onUpdateSelected = function(deltaTime) { if (!this.selected) { return; } Entities.editEntity(this.selected, { position: this.tipPosition }); var targetBalls = this.magBalls.findPotentialEdges(this.selected); for (var ballId in targetBalls) { var targetPosition = this.magBalls.getNodePosition(ballId); var distance = Vec3.distance(targetPosition, this.tipPosition); var variance = this.magBalls.getVariance(distance); var mix = Math.abs(variance) / this.magBalls.MAX_VARIANCE; var color = colorMix(COLORS.YELLOW, COLORS.RED, mix); if (!this.ghostEdges[ballId]) { // create the ovleray this.ghostEdges[ballId] = Overlays.addOverlay("line3d", { start: this.magBalls.getNodePosition(ballId), end: this.tipPosition, color: color, alpha: 1, lineWidth: 5, visible: true, }); } else { Overlays.editOverlay(this.ghostEdges[ballId], { end: this.tipPosition, color: color, }); } } for (var ballId in this.ghostEdges) { if (!targetBalls[ballId]) { Overlays.deleteOverlay(this.ghostEdges[ballId]); delete this.ghostEdges[ballId]; } } } OmniToolModules.MagBallsController.prototype.onUpdate = function(deltaTime) { this.tipPosition = this.omniTool.getPosition(); this.uiOverlays.onUpdate(deltaTime); this.onUpdateSelected(); if (this.findUiBallHit()) { return; } if (!this.selected) { // Find the highlight target and set it. var target = this.magBalls.findNearestNode(this.tipPosition, BALL_RADIUS * this.selectionRadiusMultipler); this.highlighter.highlight(target); this.highlighter.setColor(MODE_INFO[this.mode].colors[0]); if (!target) { this.magBalls.onUpdate(deltaTime); } return; } } OmniToolModules.MagBallsController.prototype.deselect = function() { if (!this.selected) { return false } this.clearGhostEdges(); this.magBalls.releaseBall(this.selected); this.selected = null; return true; } OmniToolModules.MagBallsController.prototype.onClick = function() { var newMode = this.findUiBallHit(); if (newMode) { if (this.selected) { this.magBalls.destroyNode(highlighted); this.selected = null; } this.setMode(newMode); return; } if (this.deselect()) { return; } logDebug("MagBallsController onClick: " + vec3toStr(this.tipPosition)); // TODO add checking against UI shapes for adding or deleting balls. var highlighted = this.highlighter.highlighted; if (this.mode == BALL_EDIT_MODE_ADD && !highlighted) { highlighted = this.magBalls.createBall(this.tipPosition); } // Nothing to select or create means we're done here. if (!highlighted) { return; } switch (this.mode) { case BALL_EDIT_MODE_ADD: case BALL_EDIT_MODE_MOVE: this.magBalls.selectBall(highlighted); this.selected = highlighted; logDebug("Selected " + this.selected); break; case BALL_EDIT_MODE_DELETE: this.magBalls.destroyNode(highlighted); break; case BALL_EDIT_MODE_DELETE_SHAPE: logDebug("Not implemented yet"); break; } if (this.selected) { this.highlighter.highlight(null); } } OmniToolModules.MagBallsController.prototype.onRelease = function() { if (this.holdMode) { this.deselect(); } } OmniToolModules.MagBallsController.prototype.clearGhostEdges = function() { for(var ballId in this.ghostEdges) { Overlays.deleteOverlay(this.ghostEdges[ballId]); delete this.ghostEdges[ballId]; } }