diff --git a/public/styles.css b/public/styles.css index 4b63413..6167a2c 100644 --- a/public/styles.css +++ b/public/styles.css @@ -11,11 +11,11 @@ body { color: #4444ee; } + .scene { width: 100vw; height: 100vh; overflow: hidden; - } #gameCanvas { @@ -26,184 +26,6 @@ body { background: transparent; } -div.overlay { - position: absolute; - display: block; - transform: translate(-50%, -50%); - z-index: 12; - height: 120px; -} - - -div.overlay div { - width: 100%; - margin: 10px; - text-align: center; -} - -div.overlay div a { - display: flex; - text-decoration: none; - padding-top: 1em; - padding-left: 5px; - vertical-align: middle; - border-color: #FFD700; - border-style: outset; - border-width: 2px; - border-radius: 10px; - - -} - -div.overlay div a:visited, div.overlay div a:link { - color: white; - background-color: #999922; - height: 2em; -} - -div.overlay div a:hover { - background-color: #FFD700; - color: #000000; -} - -div.overlay div a { - -} - -div.overlay input { - display: inline-block; - margin: 10px auto; - text-decoration: none; - border-color: #FFD700; - border-style: solid; - border-width: 1px; - padding: 10px; - width: 200px; -} - -div#create { - left: 600px; - top: 400px; - transform: translate(-50%, -50%); - z-index: 14; - width: 320px; - height: 344px; - border: 3px inset #FFD700; - display: none; - background-color: #000; -} - -div.overlay div a.cancel { - font-size: small; - font-weight: lighter; - font-style: italic; - background-color: #222211; - color: #EEC755; -} - -#diagramListContent ul { - list-style-type: none; - text-align: left; - padding: 0; - padding-inline-start: 0; - -} - -#diagramList { - overflow: scroll; -} - -#diagramList > h1 { - width: 100%; - text-align: center; -} - -#diagramListContent li { - margin: 12px; -} - -#diagramListContent li a { - width: 90%; -} - -div.overlay div a.cancel:hover { - background-color: #EEC700; - color: #000000; -} - - -#main.mini { - left: 100px; - top: 200px; - width: 160px; -} - -#main.mini img, #tutorial img { - width: 160px; - height: 60px; -} - -#main.mini div a, #tutorial div a { - -} - -h1 { - font-size: x-large; - font-weight: bolder; - text-align: center; - color: #F9F9E9; -} - -#tutorial { - z-index: 15; - left: 100px; - top: 750px; - width: 160px; - height: 210px; -} - -#password { - display: none; - left: 50%; - top: 50%; -} -#diagramList { - left: 340px; - top: 400px; - height: 500px; - background-color: #000; - padding: 5px; -} - -#create { - left: 500px; - top: 340px; -} - -#create div { - margin: 0; - margin-top: 10px; -} - -#closekey, #closekey a:active, #closekey a:visited, #closekey a:link { - position: relative; - color: #ffffff; -} - -#enterXR { - -} - -div.overlay div.inactive a { - background-color: #222222; - color: #555555; - border-color: #222222; - cursor: not-allowed; -} - -#enterXR.inactive { - -} #loadingGrid { position: relative; diff --git a/src/controllers/webController.ts b/src/controllers/webController.ts index 8016b45..b1b7abc 100644 --- a/src/controllers/webController.ts +++ b/src/controllers/webController.ts @@ -1,11 +1,9 @@ -import {AbstractMesh, KeyboardEventTypes, MeshBuilder, Scene} from "@babylonjs/core"; +import {AbstractMesh, KeyboardEventTypes, Scene} from "@babylonjs/core"; import {Rigplatform} from "./rigplatform"; -import {ControllerEventType, Controllers} from "./controllers"; +import {Controllers} from "./controllers"; import {DiagramManager} from "../diagram/diagramManager"; -import {GridMaterial} from "@babylonjs/materials"; import {wheelHandler} from "./functions/wheelHandler"; import log, {Logger} from "loglevel"; -import {isDiagramEntity} from "../diagram/functions/isDiagramEntity"; export class WebController { private readonly scene: Scene; @@ -30,15 +28,15 @@ export class WebController { this.diagramManager = diagramManager; this.controllers = controllers; this.canvas = document.querySelector('#gameCanvas'); - this.referencePlane = MeshBuilder.CreatePlane('referencePlane', {size: 10}, this.scene); - this.referencePlane.setEnabled(false); - this.referencePlane.visibility = 0.5; - const material = new GridMaterial('grid', this.scene); + //this.referencePlane = MeshBuilder.CreatePlane('referencePlane', {size: 10}, this.scene); + //this.referencePlane.setEnabled(false); + //this.referencePlane.visibility = 0.5; + /*const material = new GridMaterial('grid', this.scene); material.gridRatio = 1; material.backFaceCulling = false; material.antialias = true; this.referencePlane.material = material; - +*/ this.scene.onKeyboardObservable.add((kbInfo) => { this.logger.debug(kbInfo); @@ -88,13 +86,15 @@ export class WebController { this.speed *= .5; break; case " ": - if (kbInfo.event.ctrlKey) { + /*if (kbInfo.event.ctrlKey) { if (this.controllers) { this.controllers.controllerObservable.notifyObservers( {type: ControllerEventType.X_BUTTON, value: 1} ) } } + + */ break; default: @@ -109,11 +109,13 @@ export class WebController { if (kbInfo.type == 1) { //this.referencePlane.setEnabled(true); } else { - this.referencePlane.setEnabled(false); - if (this.pickedMesh) { - this.pickedMesh.showBoundingBox = false; - this.pickedMesh = null; - } + /* this.referencePlane.setEnabled(false); + if (this.pickedMesh) { + this.pickedMesh.showBoundingBox = false; + this.pickedMesh = null; + } + + */ } } }); @@ -121,11 +123,11 @@ export class WebController { this.scene.onPointerUp = () => { this.mouseDown = false; this.rig.turn(0); - if (this.pickedMesh) { + /*if (this.pickedMesh) { this.referencePlane.setEnabled(false); this.pickedMesh.showBoundingBox = false; this.pickedMesh = null; - } + }*/ }; @@ -162,7 +164,8 @@ export class WebController { }); this.scene.onPointerDown = (evt, state) => { if (evt.pointerType == "mouse") { - if (evt.shiftKey) { + this.mouseDown = true; + /*if (evt.shiftKey) { //setMenuPosition(this.referencePlane, this.scene, new Vector3(0, 0, 5)); //this.referencePlane.rotation = scene.activeCamera.absoluteRotation.toEulerAngles(); this.pickedMesh = state.pickedMesh; @@ -173,15 +176,15 @@ export class WebController { this.pickedMesh.rotation = scene.activeCamera.absoluteRotation.toEulerAngles(); this.referencePlane.setEnabled(true); } else { - this.mouseDown = true; - /* + + if (state.pickedMesh) { new ClickMenu(state.pickedMesh, state.gripTransform, this.diagramManager.onDiagramEventObservable); } - */ - } + + } */ } }; this.scene.onPointerMove = (evt) => { @@ -191,7 +194,7 @@ export class WebController { if (this.mouseDown) { this.rig.turn(evt.movementX); } - const meshPickInfo = scene.pick(this.scene.pointerX, this.scene.pointerY, (mesh) => { + /*const meshPickInfo = scene.pick(this.scene.pointerX, this.scene.pointerY, (mesh) => { return isDiagramEntity(mesh); }); const planePickInfo = scene.pick(this.scene.pointerX, this.scene.pointerY, (mesh) => { @@ -208,16 +211,17 @@ export class WebController { } } else { if (this.mesh) { - /*this.diagramManager.onDiagramEventObservable.notifyObservers({ + this.diagramManager.onDiagramEventObservable.notifyObservers({ type: DiagramEventType.MODIFY, entity: toDiagramEntity(this.mesh) - }, DiagramEventObserverMask.ALL); */ + }, DiagramEventObserverMask.ALL); } this.mesh = null; } if (this.pickedMesh && planePickInfo.hit) { this.pickedMesh.position = planePickInfo.pickedPoint; } + */ } } diff --git a/src/diagram/diagramMenuManager.ts b/src/diagram/diagramMenuManager.ts index 91f2827..8c5d760 100644 --- a/src/diagram/diagramMenuManager.ts +++ b/src/diagram/diagramMenuManager.ts @@ -13,6 +13,7 @@ import {ConnectionPreview} from "../menus/connectionPreview"; import {ScaleMenu2} from "../menus/ScaleMenu2"; import {CameraMenu} from "../menus/cameraMenu"; import {viewOnly} from "../util/functions/getPath"; +import {GroupMenu} from "../menus/groupMenu"; export class DiagramMenuManager { @@ -21,6 +22,7 @@ export class DiagramMenuManager { public readonly configMenu: ConfigMenu; private readonly _notifier: Observable; private readonly _inputTextView: InputTextView; + private _groupMenu: GroupMenu; private readonly _scene: Scene; private _cameraMenu: CameraMenu; private _logger = log.getLogger('DiagramMenuManager'); @@ -28,8 +30,6 @@ export class DiagramMenuManager { constructor(notifier: Observable, controllers: Controllers, config: AppConfig, readyObservable: Observable) { this._scene = DefaultScene.Scene; - - this._notifier = notifier; this._inputTextView = new InputTextView(controllers); this.configMenu = new ConfigMenu(config); @@ -92,6 +92,7 @@ export class DiagramMenuManager { const clickMenu = new ClickMenu(mesh); clickMenu.onClickMenuObservable.add((evt: ActionEvent) => { this._logger.debug(evt); + switch (evt.source.id) { case "remove": this.notifyAll({type: DiagramEventType.REMOVE, entity: {id: clickMenu.mesh.id}}); @@ -105,6 +106,9 @@ export class DiagramMenuManager { case "size": this.scaleMenu.show(clickMenu.mesh); break; + case "group": + this._groupMenu = new GroupMenu(clickMenu.mesh); + break; case "close": this.scaleMenu.hide(); break; diff --git a/src/diagram/functions/toDiagramEntity.ts b/src/diagram/functions/toDiagramEntity.ts index 4709186..e597e8c 100644 --- a/src/diagram/functions/toDiagramEntity.ts +++ b/src/diagram/functions/toDiagramEntity.ts @@ -19,6 +19,7 @@ export function toDiagramEntity(mesh: AbstractMesh): DiagramEntity { entity.position = vectoxys(mesh.absolutePosition); entity.rotation = vectoxys(mesh.absoluteRotationQuaternion.toEulerAngles()); entity.last_seen = new Date(); + entity.image = mesh?.metadata?.image; entity.template = mesh?.metadata?.template; entity.text = mesh?.metadata?.text; entity.from = mesh?.metadata?.from; diff --git a/src/integration/pouchdbPersistenceManager.ts b/src/integration/pouchdbPersistenceManager.ts index 3de93b1..4d2a76b 100644 --- a/src/integration/pouchdbPersistenceManager.ts +++ b/src/integration/pouchdbPersistenceManager.ts @@ -131,6 +131,10 @@ export class PouchdbPersistenceManager { if (!entity) { return; } + if (entity.template == '#image-template' && !entity.image) { + this._logger.error('no image data', entity); + return; + } if (this._encKey && !this._encryption.ready) { await this._encryption.setPassword(this._encKey); } diff --git a/src/menus/clickMenu.ts b/src/menus/clickMenu.ts index d89a70b..cfec6aa 100644 --- a/src/menus/clickMenu.ts +++ b/src/menus/clickMenu.ts @@ -1,7 +1,7 @@ import {AbstractMesh, ActionEvent, Observable, Scene, TransformNode, Vector3} from "@babylonjs/core"; import {Button} from "../objects/Button"; - -const POINTER_UP = "pointerup"; +import {positionNode} from "./functions/positionNode"; +import {isUp} from "./functions/isUp"; export class ClickMenu { private readonly _mesh: AbstractMesh; @@ -46,7 +46,7 @@ export class ClickMenu { } }, -1, false, this, false); - this.makeNewButton("Close", "close", scene, x += .11) + this.makeNewButton("Group", "group", scene, x += .11) .onPointerObservable.add((eventData) => { if (isUp(eventData)) { this.onClickMenuObservable.notifyObservers(eventData); @@ -54,16 +54,14 @@ export class ClickMenu { } }, -1, false, this, false); - - const platform = scene.getMeshByName("platform"); - const ray = scene.activeCamera.getForwardRay(1); - ray.direction.y = 0; - const fpos = scene.activeCamera.globalPosition.clone().add(ray.direction.scale(1)); - this._transformNode.position = fpos; - this._transformNode.position.y -= .4; - this._transformNode.lookAt(scene.activeCamera.globalPosition); - this._transformNode.rotate(Vector3.Up(), Math.PI); - this._transformNode.setParent(platform); + this.makeNewButton("Close", "close", scene, x += .11) + .onPointerObservable.add((eventData) => { + if (isUp(eventData)) { + this.onClickMenuObservable.notifyObservers(eventData); + this.dispose(); + } + }, -1, false, this, false); + positionNode(this._transformNode); } public get mesh(): AbstractMesh { @@ -85,7 +83,3 @@ export class ClickMenu { return button; } } - -function isUp(event: ActionEvent): boolean { - return event?.sourceEvent?.type == POINTER_UP; -} \ No newline at end of file diff --git a/src/menus/functions/isUp.ts b/src/menus/functions/isUp.ts new file mode 100644 index 0000000..7d37d77 --- /dev/null +++ b/src/menus/functions/isUp.ts @@ -0,0 +1,7 @@ +import {ActionEvent} from "@babylonjs/core"; + +const POINTER_UP = "pointerup"; + +export function isUp(event: ActionEvent): boolean { + return event?.sourceEvent?.type == POINTER_UP; +} \ No newline at end of file diff --git a/src/menus/functions/positionNode.ts b/src/menus/functions/positionNode.ts new file mode 100644 index 0000000..11fe071 --- /dev/null +++ b/src/menus/functions/positionNode.ts @@ -0,0 +1,15 @@ +import {TransformNode, Vector3} from "@babylonjs/core"; +import {DefaultScene} from "../../defaultScene"; + +export function positionNode(transformNode: TransformNode) { + const scene = DefaultScene.Scene; + const platform = scene.getMeshByName("platform"); + const ray = scene.activeCamera.getForwardRay(1); + ray.direction.y = 0; + const fpos = scene.activeCamera.globalPosition.clone().add(ray.direction.scale(1)); + transformNode.position = fpos; + transformNode.position.y -= .4; + transformNode.lookAt(scene.activeCamera.globalPosition); + transformNode.rotate(Vector3.Up(), Math.PI); + transformNode.setParent(platform); +} \ No newline at end of file diff --git a/src/menus/groupMenu.ts b/src/menus/groupMenu.ts new file mode 100644 index 0000000..052fc37 --- /dev/null +++ b/src/menus/groupMenu.ts @@ -0,0 +1,37 @@ +import {AbstractMesh, Scene, TransformNode, Vector3} from "@babylonjs/core"; +import {Button} from "../objects/Button"; +import {positionNode} from "./functions/positionNode"; +import {isUp} from "./functions/isUp"; + +export class GroupMenu { + private readonly _mesh: AbstractMesh; + private readonly _scene: Scene; + private _transformNode: TransformNode; + + constructor(mesh: AbstractMesh) { + this._mesh = mesh; + this._scene = mesh.getScene(); + this._transformNode = new TransformNode("graoupTransform", this._scene); + positionNode(this._transformNode); + const button = this.buildButton("Done", "groupdone", this._scene, 0); + button.onPointerObservable.add((eventData) => { + if (isUp(eventData)) { + this.dispose(); + } + }, -1, false, this, false); + } + + private buildButton(name: string, id: string, scene: Scene, x: number): Button { + const button = new Button(name, id, scene) + button.transform.scaling = new Vector3(.2, .2, .2); + button.transform.rotate(Vector3.Up(), Math.PI); + const transform = button.transform; + transform.parent = this._transformNode; + transform.position.x = x; + return button; + } + + private dispose() { + this._transformNode.dispose(false, true); + } +} \ No newline at end of file diff --git a/src/react/components/createMenu.tsx b/src/react/components/createMenu.tsx new file mode 100644 index 0000000..0a62498 --- /dev/null +++ b/src/react/components/createMenu.tsx @@ -0,0 +1,69 @@ +import axios from "axios"; + +export function CreateMenu({display, toggleCreateMenu}) { + const onCreateClick = (evt) => { + evt.preventDefault(); + const name = (document.querySelector('#createName') as HTMLInputElement).value; + let password = (document.querySelector('#createPassword') as HTMLInputElement).value; + const password2 = (document.querySelector('#createPassword2') as HTMLInputElement).value; + if (password !== password2) { + window.alert('Passwords do not match'); + return; + } + + const id = window.crypto.randomUUID().replace(/-/g, '_'); + if (password.length == 0) { + password = id; + } + const encrypted = (password != id); + + localStorage.setItem(id, name); + if (name && name.length > 4) { + axios.post(import.meta.env.VITE_CREATE_ENDPOINT, + { + "_id": "org.couchdb.user:" + id, + "name": id, + "password": password, + "roles": ["readers"], + "type": "user" + } + ).then(response => { + console.log(response); + const evt = new CustomEvent('dbcreated', { + detail: { + id: id, + name: name, + password: password, + encrypted: encrypted + } + }); + document.dispatchEvent(evt); + + }).catch(error => { + console.error(error); + }); + + + } else { + window.alert('Name must be longer than 4 characters'); + } + } + return ( +
+
+
+ +
+
+ + +
+
+ +
+ + +
+
+ ) +} diff --git a/src/react/components/diagramList.tsx b/src/react/components/diagramList.tsx new file mode 100644 index 0000000..243f79c --- /dev/null +++ b/src/react/components/diagramList.tsx @@ -0,0 +1,33 @@ +import {useEffect, useState} from "react"; + +export function DiagramList({display, onClick}) { + const [dbList, setDbList] = useState([]); + useEffect(() => { + const listDb = async () => { + const data = await indexedDB.databases(); + let i = 0; + setDbList(data.filter((item) => item.name.indexOf('_pouch_') > -1).map((item) => { + const dbid = item.name.replace('_pouch_', ''); + let friendlyName = localStorage.getItem(dbid); + if (!friendlyName) { + friendlyName = dbid; + } + return {key: dbid, name: friendlyName} + })); + }; + listDb(); + }, []); + + + return ( +
+

Diagrams

+ +
+ +
+
+ ) +} \ No newline at end of file diff --git a/src/react/components/keyboardHelp.tsx b/src/react/components/keyboardHelp.tsx new file mode 100644 index 0000000..c938fc4 --- /dev/null +++ b/src/react/components/keyboardHelp.tsx @@ -0,0 +1,9 @@ +export function KeyboardHelp({display, onClick}) { + return ( +
+ + + +
+ ) +} \ No newline at end of file diff --git a/src/react/components/mainMenu.tsx b/src/react/components/mainMenu.tsx new file mode 100644 index 0000000..51afb6f --- /dev/null +++ b/src/react/components/mainMenu.tsx @@ -0,0 +1,27 @@ +import {viewOnly} from "../../util/functions/getPath"; +import {QuestLink} from "./questLink"; + +export function MainMenu({onClick}) { + if (viewOnly()) { + return ( + ) + } else { + return ( + + ) + } + +} \ No newline at end of file diff --git a/src/react/components/menu.tsx b/src/react/components/menu.tsx new file mode 100644 index 0000000..48b22a9 --- /dev/null +++ b/src/react/components/menu.tsx @@ -0,0 +1,51 @@ +import {useState} from "react"; +import {uploadImage} from "../functions/uploadImage"; +import {MainMenu} from "./mainMenu"; +import {CreateMenu} from "./createMenu"; +import {DiagramList} from "./diagramList"; + +export function Menu() { + + const [createState, setCreateState] = useState('none'); + const [desktopTutorialState, setDesktopTutorialState] = useState('none'); + const [diagramListState, setDiagramListState] = useState('none'); + + function handleCreateClick(evt: React.MouseEvent) { + evt.preventDefault(); + setCreateState(createState == 'none' ? 'block' : 'none'); + } + + function handleDesktopTutorialClick(evt: React.MouseEvent) { + evt.preventDefault(); + setDesktopTutorialState(desktopTutorialState == 'none' ? 'block' : 'none'); + } + + function handleDiagramListClick(evt: React.MouseEvent) { + evt.preventDefault(); + if (!evt.currentTarget.id) { + return; + } + switch (evt.currentTarget.id) { + case 'imageUploadLink': + const input = document.createElement('input'); + input.type = 'file'; + input.accept = 'image/*'; + input.onchange = uploadImage; + + document.body.appendChild(input); + input.click(); + break; + default: + setDiagramListState(diagramListState == 'none' ? 'block' : 'none'); + } + } + + return ( +
+ + + +
+ + ) +} \ No newline at end of file diff --git a/src/react/components/passwordDialog.tsx b/src/react/components/passwordDialog.tsx new file mode 100644 index 0000000..77adf13 --- /dev/null +++ b/src/react/components/passwordDialog.tsx @@ -0,0 +1,26 @@ +export function PasswordDialog() { + const onsubmitClick = (evt) => { + evt.preventDefault(); + const password = (document.querySelector('#passwordInput') as HTMLInputElement).value; + if (password.length < 4) { + window.alert('Password must be longer than 4 characters'); + } else { + const event = new CustomEvent('passwordset', {detail: password}); + document.dispatchEvent(event); + (document.querySelector('#password') as HTMLInputElement).style.display = 'none'; + } + } + const onCancelClick = (evt) => { + evt.preventDefault(); + (document.querySelector('#password') as HTMLInputElement).style.display = 'none'; + } + return ( +
+
+
+ + +
+
+ ) +} \ No newline at end of file diff --git a/src/react/components/questLink.tsx b/src/react/components/questLink.tsx new file mode 100644 index 0000000..ad0e22d --- /dev/null +++ b/src/react/components/questLink.tsx @@ -0,0 +1,8 @@ +export function QuestLink() { + const link = "https://www.oculus.com/open_url/?url=https://www.deepdiagram.com" + document.location.pathname; + return ( + + ) +} diff --git a/src/react/components/tutorialMenu.tsx b/src/react/components/tutorialMenu.tsx new file mode 100644 index 0000000..6382662 --- /dev/null +++ b/src/react/components/tutorialMenu.tsx @@ -0,0 +1,8 @@ +export function TutorialMenu({onClick}) { + return ( +
+

Help

+ +
+ ) +} \ No newline at end of file diff --git a/src/react/styles.css b/src/react/styles.css new file mode 100644 index 0000000..f917cd2 --- /dev/null +++ b/src/react/styles.css @@ -0,0 +1,148 @@ +ul { + list-style-type: none; + padding-inline-start: 0; +} + +li { + margin: 5px; +} + +label { + color: #999922; + width: 100%; + font-size: medium; + font-weight: bold; + display: inline-block; +} + + +a { + display: block; + padding: 5px; + text-decoration: none; + vertical-align: middle; + border-color: #FFD700; + border-style: outset; + border-width: 2px; + border-radius: 10px; + text-align: center; + line-height: 36px; + margin: 5px; +} + + +a.cancel:link { + font-size: smaller; + font-weight: lighter; + font-style: italic; + border-color: #E0C000; + color: #FFEFBB; + background-color: #888822; +} + +a:visited, a:link { + color: white; + background-color: #999922; +} + +a:hover { + background-color: #FFD700; + color: #000000; +} + +li a { + text-align: left; + border-radius: 2px; + background-color: #338822; +} + +input { + display: inline-block; + margin: 10px auto; + text-decoration: none; + border-color: #FFD700; + border-style: solid; + border-width: 1px; + padding: 10px; + width: 200px; +} + +h1 { + font-size: x-large; + font-weight: bolder; + text-align: center; + color: #F9F9E9; +} + +div.overlay { + position: absolute; + display: block; + border: 1px solid #555555; + transform: translate(-50%, -50%); + z-index: 12; + height: 120px; + padding: 10px; +} + + +div#create { + left: 390px; + top: 400px; + transform: translate(-50%, -50%); + z-index: 14; + width: 320px; + height: 344px; + border: 1px solid #222222; + display: none; + background-color: #000; +} + + +#diagramList { + overflow: scroll; + left: 390px; + top: 400px; + height: 500px; + background-color: #000; + padding: 5px; +} + + +#main.mini { + left: 110px; + top: 50%; + + height: fit-content; + background-color: #000; +} + +#main.mini img, #tutorial img { + width: 160px; + height: 60px; +} + +#tutorial { + z-index: 15; + left: 100px; + top: 750px; + width: 160px; + height: 210px; +} + +#password { + display: none; + left: 50%; + top: 50%; +} + +#closekey, #closekey a:active, #closekey a:visited, #closekey a:link { + position: relative; + color: #ffffff; +} + +div.inactive a { + background-color: #222222; + color: #555555; + border-color: #222222; + cursor: not-allowed; +} \ No newline at end of file diff --git a/src/react/webApp.tsx b/src/react/webApp.tsx index 71f1882..ad50286 100644 --- a/src/react/webApp.tsx +++ b/src/react/webApp.tsx @@ -1,232 +1,12 @@ -import {useEffect, useState} from "react"; -import {uploadImage} from "./functions/uploadImage"; -import {viewOnly} from "../util/functions/getPath"; -import axios from "axios"; - -function MainMenu({onClick}) { - if (viewOnly()) { - return ( - ) - } else { - return ( - - ) - } - -} - -function PasswordDialog() { - const onsubmitClick = (evt) => { - evt.preventDefault(); - const password = (document.querySelector('#passwordInput') as HTMLInputElement).value; - if (password.length < 4) { - window.alert('Password must be longer than 4 characters'); - } else { - const event = new CustomEvent('passwordset', {detail: password}); - document.dispatchEvent(event); - document.querySelector('#password').style.display = 'none'; - } - } - const onCancelClick = (evt) => { - evt.preventDefault(); - document.querySelector('#password').style.display = 'none'; - } - return ( -
-
-
- - -
-
- ) -} - -function CreateMenu({display, toggleCreateMenu}) { - const onCreateClick = (evt) => { - evt.preventDefault(); - const name = (document.querySelector('#createName') as HTMLInputElement).value; - let password = (document.querySelector('#createPassword') as HTMLInputElement).value; - const password2 = (document.querySelector('#createPassword2') as HTMLInputElement).value; - if (password !== password2) { - window.alert('Passwords do not match'); - return; - } - - const id = window.crypto.randomUUID().replace(/-/g, '_'); - if (password.length == 0) { - password = id; - } - const encrypted = (password != id); - - localStorage.setItem(id, name); - if (name && name.length > 4) { - axios.post(import.meta.env.VITE_CREATE_ENDPOINT, - { - "_id": "org.couchdb.user:" + id, - "name": id, - "password": password, - "roles": ["readers"], - "type": "user" - } - ).then(response => { - console.log(response); - const evt = new CustomEvent('dbcreated', { - detail: { - id: id, - name: name, - password: password, - encrypted: encrypted - } - }); - document.dispatchEvent(evt); - - }).catch(error => { - console.error(error); - }); - - - } else { - window.alert('Name must be longer than 4 characters'); - } - } - return ( -
-
-
-
-
- - -
-
- ) -} - -function TutorialMenu({onClick}) { - return ( -
-

Help

- -
- ) -} - -function KeyboardHelp({display, onClick}) { - return ( -
- - - -
- ) -} - -function QuestLink() { - const link = "https://www.oculus.com/open_url/?url=https://www.deepdiagram.com" + document.location.pathname; - return ( - - ) -} - -function DiagramList({display, onClick}) { - const [dbList, setDbList] = useState([]); - useEffect(() => { - const listDb = async () => { - const data = await indexedDB.databases(); - let i = 0; - setDbList(data.filter((item) => item.name.indexOf('_pouch_') > -1).map((item) => { - const dbid = item.name.replace('_pouch_', ''); - let friendlyName = localStorage.getItem(dbid); - if (!friendlyName) { - friendlyName = dbid; - } - return {key: dbid, name: friendlyName} - })); - }; - listDb(); - }, []); - - - return ( -
-

Diagrams

- -
- -
-
- ) -} - -function Menu() { - - const [createState, setCreateState] = useState('none'); - const [desktopTutorialState, setDesktopTutorialState] = useState('none'); - const [diagramListState, setDiagramListState] = useState('none'); - - function handleCreateClick(evt: React.MouseEvent) { - evt.preventDefault(); - setCreateState(createState == 'none' ? 'block' : 'none'); - } - - function handleDesktopTutorialClick(evt: React.MouseEvent) { - evt.preventDefault(); - setDesktopTutorialState(desktopTutorialState == 'none' ? 'block' : 'none'); - } - - function handleDiagramListClick(evt: React.MouseEvent) { - evt.preventDefault(); - if (!evt.currentTarget.id) { - return; - } - switch (evt.currentTarget.id) { - case 'imageUploadLink': - const input = document.createElement('input'); - input.type = 'file'; - input.accept = 'image/*'; - input.onchange = uploadImage; - - document.body.appendChild(input); - input.click(); - break; - default: - setDiagramListState(diagramListState == 'none' ? 'block' : 'none'); - } - } - - return ( -
- - - -
- - ) -} +import {PasswordDialog} from "./components/passwordDialog"; +import {Menu} from "./components/menu"; +import "./styles.css"; export default function WebApp() { document.addEventListener('promptpassword', (evt) => { const password = document.querySelector('#password'); if (password) { - password.style.display = 'block'; + (password as HTMLInputElement).style.display = 'block'; } }); return ( diff --git a/src/toolbox/functions/buildColor.ts b/src/toolbox/functions/buildColor.ts index 9617461..affa5ad 100644 --- a/src/toolbox/functions/buildColor.ts +++ b/src/toolbox/functions/buildColor.ts @@ -17,7 +17,9 @@ export async function buildColor(color: Color3, scene: Scene, parent: TransformN const height = .1; const material = new StandardMaterial("material-" + color.toHexString(), scene); material.diffuseColor = color; - + material.ambientColor = color; + //material.roughness = 1; + material.specularPower = 64; // material.ambientColor = color; //material.roughness = .1; //material.maxSimultaneousLights = 2; diff --git a/src/util/customEnvironment.ts b/src/util/customEnvironment.ts index 68ae481..652aa29 100644 --- a/src/util/customEnvironment.ts +++ b/src/util/customEnvironment.ts @@ -1,13 +1,13 @@ import { Color3, GroundMesh, + HemisphericLight, Material, MeshBuilder, Observable, PBRMaterial, PhysicsAggregate, PhysicsShapeType, - PointLight, PointsCloudSystem, Scene, Sound, @@ -33,14 +33,15 @@ export class CustomEnvironment { if (loading) { loading.remove(); } - this.scene.ambientColor = new Color3(1, 1, 1); - //const light = new HemisphericLight("light1", new Vector3(1, 2, 1), this.scene); - //light.groundColor = new Color3(.1, .1, .1) - //light.diffuse = new Color3(1, 1, 1); + this.scene.ambientColor = new Color3(.1, .1, .1); + const light = new HemisphericLight("light1", new Vector3(.5, 1, 1).normalize(), this.scene); + light.groundColor = new Color3(0, 0, 0); + light.diffuse = new Color3(1, 1, 1); + light.intensity = .8; //light.setDirectionToTarget(new Vector3(.4, .5, .5).normalize()); //light.intensity = .7; - const light = new PointLight("light1", new Vector3(0, 10, 10), this.scene); - const light2 = new PointLight("light1", new Vector3(0, 10, -10), this.scene); + //const light = new PointLight("light1", new Vector3(0, 10, 10), this.scene); + //const light2 = new PointLight("light1", new Vector3(0, 10, -10), this.scene); const physics = new CustomPhysics(this.scene, config); physics .initializeAsync()