note_socketService_SocketService.js
import { UI } from '../note';
import { moveHtmlToWorldPosition } from '../../utils';
import { ScriptBase } from '@ud-viz/game_browser';
import { Command, RenderComponent } from '@ud-viz/game_shared';
import { constant } from '@ud-viz/game_shared_template';
import * as THREE from 'three';
import './style.css';
/** @classdesc - Manage note for a specific socket */
export class SocketService extends ScriptBase {
init() {
/**
* determine if this is the socket script of the user
*
@type {boolean} */
this.isSocketScript =
this.variables.socketID == this.context.socketIOWrapper.socket.id;
const pointerObject = this.object3D.getObjectByProperty(
'uuid',
this.variables.pointerUUID
);
// ui
this.domElement = document.createElement('div');
this.domElement.classList.add('root_html_pointer_note');
// fetch root ui
const noteUI = this.context.findExternalScriptWithID(UI.ID_SCRIPT);
noteUI.appendToHtml(this.domElement);
// color
const renderComp = pointerObject.getComponent(RenderComponent.TYPE);
const colorPointer = renderComp.getModel().getColor();
const contrastedColor = function (r, g, b) {
// https://stackoverflow.com/a/3943023/112731
return r * 0.299 + g * 0.587 + b * 0.114 > 186 ? '#000000' : '#FFFFFF';
};
this.domElement.style.color = contrastedColor(
colorPointer[0] * 255,
colorPointer[1] * 255,
colorPointer[2] * 255
);
this.domElement.style.backgroundColor =
'rgb(' +
colorPointer[0] * 255 +
',' +
colorPointer[1] * 255 +
',' +
colorPointer[2] * 255 +
')';
if (this.isSocketScript) {
const nameInput = document.createElement('input');
nameInput.setAttribute('type', 'text');
nameInput.value = this.variables.nameSocket;
this.domElement.appendChild(nameInput);
// to not conflict with other key event while typing
nameInput.onfocus = () => {
this.context.inputManager.setPause(true);
};
nameInput.onblur = () => {
this.context.inputManager.setPause(false);
};
// edit name
nameInput.onchange = () => {
this.context.sendCommandsToGameContext([
new Command({
type: constant.COMMAND.UPDATE_EXTERNALSCRIPT_VARIABLES,
data: {
object3DUUID: this.object3D.uuid,
variableName: 'nameSocket',
variableValue: nameInput.value,
},
}),
]);
};
} else {
this.nameLabel = document.createElement('div');
this.nameLabel.innerText = this.variables.nameSocket;
this.domElement.appendChild(this.nameLabel);
}
// edit pointer sphere attr ui
if (this.isSocketScript) {
const stepScale = 2;
const minScale = 1;
const maxScale = 500;
const sphereScale = document.createElement('input');
sphereScale.type = 'range';
sphereScale.step = stepScale;
sphereScale.min = minScale;
sphereScale.max = maxScale;
sphereScale.value = pointerObject.scale.x; // scale is the same on all dim
this.domElement.appendChild(sphereScale);
const sendCommandScale = (value) => {
this.context.sendCommandsToGameContext([
new Command({
type: constant.COMMAND.UPDATE_TRANSFORM,
data: {
object3DUUID: pointerObject.uuid,
scale: {
x: value,
y: value,
z: value,
},
},
}),
]);
};
sphereScale.onchange = () => {
sendCommandScale(sphereScale.value);
};
this.context.inputManager.addKeyInput('+', 'keypress', () => {
let newValue = pointerObject.scale.x + stepScale;
newValue = Math.max(Math.min(newValue, maxScale), minScale);
sendCommandScale(newValue);
sphereScale.value = newValue; // update ui
});
this.context.inputManager.addKeyInput('-', 'keypress', () => {
let newValue = pointerObject.scale.x - stepScale;
newValue = Math.max(Math.min(newValue, maxScale), minScale);
sendCommandScale(newValue);
sphereScale.value = newValue; // update ui
});
// update pointer note position
this.context.inputManager.addMouseCommand(
'update_pointer_object3D',
'mousemove',
(event) => {
const mouse = new THREE.Vector2(event.clientX, event.clientY);
const worldPosition = new THREE.Vector3();
this.context.frame3D.itownsView.getPickingPositionFromDepth(
mouse,
worldPosition
);
const parentWorldPosition = new THREE.Vector3();
pointerObject.parent.matrixWorld.decompose(
parentWorldPosition,
new THREE.Quaternion(),
new THREE.Vector3()
);
return new Command({
type: constant.COMMAND.UPDATE_TRANSFORM,
data: {
object3DUUID: pointerObject.uuid,
position: worldPosition.sub(parentWorldPosition),
},
});
}
);
/**
* menu to edit note
*
@type {MenuEditNote} */
this.menuEditNote = null;
this.context.inputManager.addKeyInput('n', 'keyup', () => {
if (this.menuEditNote) return; // there is already a this.menuEditNote
// register position and scale cursor
const p = pointerObject.position.clone();
const s = pointerObject.scale.clone();
this.menuEditNote = new MenuEditNote(
pointerObject.getWorldPosition(new THREE.Vector3())
);
this.menuEditNote.setAddNoteButtonCallback((message) => {
this.menuEditNote.domElement.remove();
this.menuEditNote = null;
this.context.sendCommandsToGameContext([
new Command({
type: constant.COMMAND.ADD_NOTE,
data: {
socketID: this.variables.socketID,
position: p,
scale: s,
color: colorPointer,
message: message,
},
}),
]);
});
this.menuEditNote.setCloseButtonCallback(() => {
this.menuEditNote.domElement.remove();
this.menuEditNote = null;
});
this.context.frame3D.domElementUI.appendChild(
this.menuEditNote.domElement
);
});
}
}
tick() {
if (this.menuEditNote) {
moveHtmlToWorldPosition(
this.menuEditNote.domElement,
this.menuEditNote.getWorldPosition().clone(),
this.context.frame3D.camera
);
}
}
addNoteButton(el) {
this.domElement.appendChild(el);
}
onOutdated() {
if (this.nameLabel) {
// update innerHtml name
this.nameLabel.innerText = this.variables.nameSocket;
}
}
onRemove() {
this.domElement.remove();
}
static get ID_SCRIPT() {
return constant.ID_SCRIPT.NOTE_SOCKET_SERVICE;
}
}
class MenuEditNote {
/**
*
* @param {THREE.Vector3} worldPosition - where in scene this menu should be
*/
constructor(worldPosition) {
/** @type {THREE.Vector3} */
this.worldPosition = worldPosition;
this.domElement = document.createElement('div');
this.domElement.classList.add('root_menu_message_note');
this.textAreaMessage = document.createElement('textarea');
this.domElement.appendChild(this.textAreaMessage);
this.closeButton = document.createElement('button');
this.closeButton.innerText = 'Close';
this.domElement.appendChild(this.closeButton);
this.addNoteButton = document.createElement('button');
this.addNoteButton.innerText = 'Add note';
this.domElement.appendChild(this.addNoteButton);
this.textAreaMessage.focus(); // cant focus textarea force it there (patch)
}
/**
*
* @returns {THREE.Vector3} - menu position in world
*/
getWorldPosition() {
return this.worldPosition;
}
/**
*
* @param {Function} f - callback call when close button is clicked
*/
setCloseButtonCallback(f) {
this.closeButton.onclick = f;
}
/**
* @callback addButtonNoteCallback
* @param {string} textAreaValue - current value of the textarea
*/
/**
*
* @param {addButtonNoteCallback} f - callback call when add note button is clicked (first param is the textarea value of menu)
*/
setAddNoteButtonCallback(f) {
this.addNoteButton.onclick = () => {
f(this.textAreaMessage.value);
};
}
}