From 741f62ea9c0812beb539464b3461f2b5b011f5ce Mon Sep 17 00:00:00 2001 From: Justin Georgi Date: Sun, 17 Nov 2024 20:50:32 -0700 Subject: [PATCH] Subdivide preview js files by function Signed-off-by: Justin Georgi --- extension.json | 4 +- modules/glmv-hs.js | 150 ++++++++++++++ modules/glmv-mvconfig.js | 269 +++++++++++++++++++++++++ modules/glmv-prev.js | 416 +-------------------------------------- 4 files changed, 424 insertions(+), 415 deletions(-) create mode 100644 modules/glmv-hs.js create mode 100644 modules/glmv-mvconfig.js diff --git a/extension.json b/extension.json index 9593480..37ebada 100644 --- a/extension.json +++ b/extension.json @@ -72,7 +72,9 @@ "glmv-prev.css" ], "packageFiles": [ - "glmv-prev.js" + "glmv-prev.js", + "glmv-mvconfig.js", + "glmv-hs.js" ] } } diff --git a/modules/glmv-hs.js b/modules/glmv-hs.js new file mode 100644 index 0000000..b0e9a18 --- /dev/null +++ b/modules/glmv-hs.js @@ -0,0 +1,150 @@ +let deleteHotspot = null +let grabHotspot = null + +/** + * Sets listener and attributes on model-viewer to + * allow for click registering of a new hotspot + */ +readyAddHotspot = function() { + disableViewer('AddingHotspot', clickAddHotspot) +} + +/** + * Event listener callback to retrieve the info + * about the model surface point selected by the + * mouse and add that information to the editor + * text input + * + * @param {PointerEvent} e + */ +clickAddHotspot = function(e) { + let hsPosition = null + let targetModel = enableViewer() + if (targetModel) { + hsPosition = targetModel.positionAndNormalFromPoint(e.clientX, e.clientY) + } + if (hsPosition) { + let currentText = $('#wpTextbox1').val() + let [_, mvconfig] = extractMvconfig(currentText) + let hsOutput = {} + hsOutput['data-position'] = hsPosition.position.toString().replaceAll(/(\d{5})(\d*?m)/g,"$1m") + hsOutput['data-normal'] = hsPosition.normal.toString().replaceAll(/(\d{5})(\d*?m)/g,"$1m") + hsOutput['data-orbit'] = orb2degree(targetModel.getCameraOrbit().toString(),[2,2,5]) + let targetObj = targetModel.getCameraTarget() + hsOutput['data-target'] = `${targetObj.x.toFixed(5)}m ${targetObj.y.toFixed(5)}m ${targetObj.z.toFixed(5)}m` + mvconfig.annotations['Hotspot ' + (Object.keys(mvconfig.annotations).length + 1)] = hsOutput + let newText = currentText.replace(/(.*?)[\S\s]*?(<\/mvconfig>.*)/,`$1\n${JSON.stringify(mvconfig, null, 2)}\n$2`) + $('#wpTextbox1').val(newText) + } + readMvconfig() +} + +/** + * Set flag and attributes on model-viewer to + * delete the next hotspot that is clicked + */ +readyDelHotspot = function() { + deleteHotspot = true + disableViewer('DeletingHotspot', cancelDeleteHotspot) +} + +/** + * Unset deleting flag and return normal + * function and style to model viewer + */ +cancelDeleteHotspot = function() { + deleteHotspot = null + enableViewer() +} + +/** + * Delete the selected hotspot + * + * @param {element} hs hotspot element to delete + */ +clickDeleteHotspot = function (hs) { + deleteHotspot = null + enableViewer() + const anName = hs.target.childNodes[0].innerText + let purgeAnnotation = new RegExp('(?<="annotationSets"[\\S\\s]*?)(^.*?' + anName + '.*\n)','gm') + hs.target.remove() + const editText = $('#wpTextbox1').val() + const newText = editText.replace(purgeAnnotation,'') + const finalText = newText.replace(/(,)(\n\s+])/gm,'$2') + $('#wpTextbox1').val(finalText) + writeMvconfig() + readMvconfig() +} + +/** + * Check status of delete function + */ +isDeleting = function() { + return deleteHotspot +} + +/** + * Prepare to drag a hotspot + * + * @param {MouseEvent} event + */ +grabAnnotation = function(e) { + if (e.ctrlKey) { + grabHotspot = {x: e.x, y: e.y} + const contEl = $('.glmv-container')[0] + contEl.addEventListener('mousemove', moveAnnotation) + const mvEl = $('model-viewer')[0] + } else { + grabHotspot = null + } +} + +/** + * Drag currently clicked hotspot + * + * @param {MouseEvent} event + */ +moveAnnotation = function(e) { + if (grabHotspot) { + grabHotspot.move = true + e.target.style['transform'] = `translate(${e.x - grabHotspot.x}px, ${e.y - grabHotspot.y}px) scale(1.1,1.1)` + } +} + +/** + * End dragging a hotspot and update information + * + * @param {MouseEvent} event + */ +releaseAnnotation = function(e) { + if (grabHotspot && grabHotspot.move) { + e.target.style['transform']='' + const contEl = $('.glmv-container')[0] + contEl.removeEventListener('mousemove', moveAnnotation) + const mvEl = $('model-viewer')[0] + let newPosition = mvEl.positionAndNormalFromPoint(e.clientX, e.clientY) + const newPos = newPosition.position.toString().replaceAll(/(\d{5})(\d*?m)/g,"$1m") + const newNorm = newPosition.normal.toString().replaceAll(/(\d{5})(\d*?m)/g,"$1m") + mvEl.updateHotspot({ + name: e.target.slot, + position: newPos, + normal: newNorm + }) + const newOrb = orb2degree(mvEl.getCameraOrbit().toString(),[2,2,5]) + e.target.setAttribute('data-orbit', newOrb) + let targetObj = mvEl.getCameraTarget() + const newTarg = `${targetObj.x.toFixed(5)}m ${targetObj.y.toFixed(5)}m ${targetObj.z.toFixed(5)}m` + e.target.setAttribute('data-target', newTarg) + let currentText = $('#wpTextbox1').val() + let [_, mvconfig] = extractMvconfig(currentText) + mvconfig.annotations[e.target.childNodes[0].innerText] = { + "data-position": newPos, + "data-normal": newNorm, + "data-orbit": newOrb, + "data-target": newTarg + } + const newText = currentText.replace(/(.*?)[\S\s]*?(<\/mvconfig>.*)/,`$1\n${JSON.stringify(mvconfig, null, 2)}\n$2`) + $('#wpTextbox1').val(newText) + } + grabHotspot = null +} \ No newline at end of file diff --git a/modules/glmv-mvconfig.js b/modules/glmv-mvconfig.js new file mode 100644 index 0000000..5765104 --- /dev/null +++ b/modules/glmv-mvconfig.js @@ -0,0 +1,269 @@ +let currentSet = 'default' + +/** + * Convert text in the preview text editor to js object + * + * @return [string, object] full mvconfig text string and mvconfig object + */ +extractMvconfig = function() { + const editText = $('#wpTextbox1').val() + const extractConfig = editText.match(/([\S\s]*?)<\/mvconfig>/) + let mvconfig = (extractConfig.length >= 2) ? JSON.parse(extractConfig[1]) : {viewerConfig: {}, annotations: {}, annotationSets: {}} + if (mvconfig.viewerConfig === undefined) { + mvconfig.viewerConfig = { + default: { + "camera-controls": true + } + } + } + if (mvconfig.annotations === undefined) { + mvconfig.annotations = {} + } + if (mvconfig.annotationSets === undefined) { + mvconfig.annotationSets = {} + } + return [editText, mvconfig] +} + +/** + * Reads the json string in the edit panel + * and updates hotspot elements + * + * @return {bool} true on successful read and update + */ +readMvconfig = function() { + let hotspotsObj = [] + let mvconfig + let slotNum = 1 + + createHotspot = function(hsLabel, hsSlot, hsTag) { + let newHs = document.createElement('button') + newHs.classList.add('Hotspot') + newHs.setAttribute('slot',`hotspot-${hsSlot}`) + newHs.setAttribute('ontouchstart', 'event.stopPropagation()') + newHs.setAttribute('onclick', 'onAnnotation(event)') + newHs.setAttribute('onmousedown', 'grabAnnotation(event)') + newHs.setAttribute('onmouseup', 'releaseAnnotation(event)') + Object.keys(mvconfig.annotations[hsLabel]).forEach((prop) => { + newHs.setAttribute(prop, mvconfig.annotations[hsLabel][prop]) + }) + let newAn = document.createElement('div') + newAn.classList.add('HotspotAnnotation', 'HiddenAnnotation') + newAn.innerText = hsLabel + newHs.appendChild(newAn) + newLabel = document.createElement('span') + newLabel.innerText = hsTag || (hsSlot) + newHs.appendChild(newLabel) + hotspotsObj.push(newHs) + } + + try { + [_, mvconfig] = extractMvconfig() + } catch (err) { + console.warn('Failed to read model config:' + err.message) + return false + } + if (currentSet != 'default' && mvconfig.annotationSets[currentSet]) { + mvconfig.annotationSets[currentSet].forEach(hs => { + createHotspot(hs, slotNum) + slotNum += 1 + }) + } + Object.keys(mvconfig.annotations).forEach(hs => { + if (currentSet != 'default' && mvconfig.annotationSets[currentSet] && mvconfig.annotationSets[currentSet].includes(hs)) { + return + } + let label = (currentSet != 'default' && mvconfig.annotationSets[currentSet]) ? '-' : null + createHotspot(hs, slotNum, label) + slotNum += 1 + }) + + $('model-viewer button').remove() + const mView = $('model-viewer')[0] + hotspotsObj.forEach(hs => { + mView.appendChild(hs) + }) + + return true +} + +/** + * Parses the current hotspots into json object + * and writes the json string to the edit panel + * + * @return {bool} true on successful write to edit panel + */ +writeMvconfig = function () { + let annotationsObj = {} + currentButtons = $('.Hotspot').each(function() { + let buttonEl = $(this)[0] + annotationsObj[buttonEl.childNodes[0].innerText] = { + "data-position": buttonEl.getAttribute('data-position'), + "data-normal": buttonEl.getAttribute('data-normal'), + "data-orbit": buttonEl.getAttribute('data-orbit'), + "data-target": buttonEl.getAttribute('data-target') + } + }) + if (Object.keys(annotationsObj).length === 0) return false + const [currentText, mvconfig] = extractMvconfig() + mvconfig.annotations = annotationsObj + const newText = currentText.replace(/(.*?)[\S\s]*?(<\/mvconfig>.*)/,`$1\n${JSON.stringify(mvconfig, null, 2)}\n$2`) + $('#wpTextbox1').val(newText) + return true +} + +/** + * Convert all radian values returned by model-viewer to degrees + * + * @param {string} orbString string with any number of `(number)rad` sub strings + * @param {number|array} fix Optional: number of decimal places to return in converted numbers + * @return {string} string with all radian values and units converted to degrees + */ +orb2degree = function(orbString, fix = null) { + let degArray = orbString.split(' ').map(s => { + if (s.includes('rad')) { + return (Number.parseFloat(s) / Math.PI * 180) + 'deg' + } else { + return s + } + }) + if (fix && !['number', 'object'].includes(typeof fix)) { + console.warn('orb2degree: fix parameter invalid type. Ignoring.') + fix = null + } + if (fix) { + degArray = degArray.map((v, idx) => { + let fixReg = new RegExp('(\\d*.\\d{' + (((typeof fix) == 'object') ? fix[idx] : fix) + '})(\\d*)([a-z]*)') + return v.replace(fixReg,'$1$3') + }) + } + return degArray.join(' ') +} + +/** + * Set camera control setting for the current view + * + * @param {string} view + * @return {bool} new camera-controls setting + */ +toggleCameraControl = function(view) { + let [currentText, mvconfig] = extractMvconfig() + const currentView = (mvconfig.viewerConfig[view]) ? view : 'default' + const newControl = !mvconfig.viewerConfig[currentView]['camera-controls'] + if (newControl) { + mvconfig.viewerConfig[currentView]['camera-controls'] = newControl + } else { + delete mvconfig.viewerConfig[currentView]['camera-controls'] + } + const textUpdate = currentText.replace(/(?<=)([\S\s]*?)(?=<\/mvconfig>)/gm,`\n${JSON.stringify(mvconfig, null, 2)}\n`) + $('#wpTextbox1').val(textUpdate) + return newControl +} + +selectViewConfig = function(view) { + const mView = $('model-viewer')[0] + let [_, mvconfig] = extractMvconfig() + const selectView = (mvconfig.viewerConfig[view]) ? view : 'default' + const viewConfig = mvconfig.viewerConfig[selectView] + const settings = [ + "camera-controls", + "disable-pan", + "disable-tap", + "touch-action", + "disable-zoom", + "orbit-sensitivity", + "zoom-sensitivity", + "pan-sensitivity", + "auto-rotate", + "auto-rotate-delay", + "rotation-per-second", + "interaction-prompt-style", + "interaction-prompt-threshold", + "camera-orbit", + "camera-target", + "field-of-view", + "max-camera-orbit", + "min-camera-orbit", + "max-field-of-view", + "min-field-of-view", + "poster", + "ar", + "ar-modes", + "ar-scale", + "ar-placement" + ] + settings.forEach(s => { + if (viewConfig[s]) { + mView.setAttribute(s,viewConfig[s]) + } else { + mView.removeAttribute(s) + } + }) +} + +/** + * Set new default camera orbit and send values to the preview + * editor + */ +writeCameraOrbit = function() { + const mView = $('model-viewer')[0] + const newOrbit = orb2degree(mView.getCameraOrbit().toString(),[2,2,5]) + mView.setAttribute('camera-orbit', newOrbit) + const targetObj = mView.getCameraTarget() + const newTarget = `${targetObj.x.toFixed(5)}m ${targetObj.y.toFixed(5)}m ${targetObj.z.toFixed(5)}m` + mView.setAttribute('camera-target', newTarget) + const newField = mView.getFieldOfView().toFixed(5) + 'deg' + mView.setAttribute('field-of-view',newField) + let [currentText, mvconfig] = extractMvconfig() + mvconfig.viewerConfig.default['camera-orbit'] = newOrbit + mvconfig.viewerConfig.default['camera-target'] = newTarget + mvconfig.viewerConfig.default['field-of-view'] = newField + const textUpdate = currentText.replace(/(?<=)([\S\s]*?)(?=<\/mvconfig>)/gm,`\n${JSON.stringify(mvconfig, null, 2)}\n`) + $('#wpTextbox1').val(textUpdate) +} + +/** + * Set new camera orbit limits and send values to the preview + * editor + * + * @param {string} axis [yaw|pitch] orbit value to set + * @param {string} limit [max|min] limit value to set + */ +writeCameraLimit = function(axis, limit) { + const mView = $('model-viewer')[0] + const newOrbit = orb2degree(mView.getCameraOrbit().toString(),[2,2,5]) + const newOrbitVals = newOrbit.split(' ') + const valueIndex = (axis == 'yaw') ? 0 : 1 + let [currentText, mvconfig] = extractMvconfig() + const oldOrbit = mvconfig.viewerConfig.default[`${limit}-camera-orbit`] + let oldOrbitVals = (oldOrbit) ? oldOrbit.split(' ') : Array(3).fill('auto') + oldOrbitVals[valueIndex] = newOrbitVals[valueIndex] + mvconfig.viewerConfig.default[`${limit}-camera-orbit`] = oldOrbitVals.join(' ') + mView.setAttribute(`${limit}-camera-orbit`, oldOrbitVals.join(' ')) + const textUpdate = currentText.replace(/(?<=)([\S\s]*?)(?=<\/mvconfig>)/gm,`\n${JSON.stringify(mvconfig, null, 2)}\n`) + $('#wpTextbox1').val(textUpdate) +} + +/** + * Change the currently selected annotation set + * + * @param {string} newSet name of annotation set to select + */ +selectAnnotationSet = function(newSet) { + currentSet = newSet + readMvconfig() +} + +/* +export default { + extractMvconfig, + readMvconfig, + writeMvconfig, + orb2degree, + toggleCameraControl, + selectViewConfig, + writeCameraOrbit, + writeCameraLimit, + selectAnnotationSet +} +*/ \ No newline at end of file diff --git a/modules/glmv-prev.js b/modules/glmv-prev.js index 7fd0232..d6af30c 100644 --- a/modules/glmv-prev.js +++ b/modules/glmv-prev.js @@ -1,9 +1,6 @@ //Initialize globals -let currentSet = 'default' -let deleteHotspot = null -let grabHotspot = null - -//EDIT PREVIEW MENU CREATION AND MODIFICATION +require('./glmv-mvconfig.js') +require('./glmv-hs.js') /** * Use the OOui js library to create wikis-style menu @@ -185,7 +182,6 @@ buildPreviewMenu = function() { selectViewConfig(selSet.data) }) selectVC.setDisabled(true) - console.log(selectVC) const cameraButtons = new OO.ui.ButtonGroupWidget({ items: [ downloadViewerImage, setControl, setView, setLims, selectVC ] @@ -224,264 +220,6 @@ addMenuOption = function(menuWidget) { menuWidget.menu.addItems([new OO.ui.MenuOptionWidget({data: 'New option', label: 'New option'})]) } -//MVCONFIG IO - -/** - * Convert text in the preview text editor to js object - * - * @return [string, object] full mvconfig text string and mvconfig object - */ -extractMvconfig = function() { - const editText = $('#wpTextbox1').val() - const extractConfig = editText.match(/([\S\s]*?)<\/mvconfig>/) - let mvconfig = (extractConfig.length >= 2) ? JSON.parse(extractConfig[1]) : {viewerConfig: {}, annotations: {}, annotationSets: {}} - if (mvconfig.viewerConfig === undefined) { - mvconfig.viewerConfig = { - default: { - "camera-controls": true - } - } - } - if (mvconfig.annotations === undefined) { - mvconfig.annotations = {} - } - if (mvconfig.annotationSets === undefined) { - mvconfig.annotationSets = {} - } - return [editText, mvconfig] -} - -/** - * Reads the json string in the edit panel - * and updates hotspot elements - * - * @return {bool} true on successful read and update - */ -readMvconfig = function() { - let hotspotsObj = [] - let mvconfig - let slotNum = 1 - - createHotspot = function(hsLabel, hsSlot, hsTag) { - let newHs = document.createElement('button') - newHs.classList.add('Hotspot') - newHs.setAttribute('slot',`hotspot-${hsSlot}`) - newHs.setAttribute('ontouchstart', 'event.stopPropagation()') - newHs.setAttribute('onclick', 'onAnnotation(event)') - newHs.setAttribute('onmousedown', 'grabAnnotation(event)') - newHs.setAttribute('onmouseup', 'releaseAnnotation(event)') - Object.keys(mvconfig.annotations[hsLabel]).forEach((prop) => { - newHs.setAttribute(prop, mvconfig.annotations[hsLabel][prop]) - }) - let newAn = document.createElement('div') - newAn.classList.add('HotspotAnnotation', 'HiddenAnnotation') - newAn.innerText = hsLabel - newHs.appendChild(newAn) - newLabel = document.createElement('span') - newLabel.innerText = hsTag || (hsSlot) - newHs.appendChild(newLabel) - hotspotsObj.push(newHs) - } - - try { - [_, mvconfig] = extractMvconfig() - } catch (err) { - console.warn('Failed to read model config:' + err.message) - return false - } - if (currentSet != 'default' && mvconfig.annotationSets[currentSet]) { - mvconfig.annotationSets[currentSet].forEach(hs => { - createHotspot(hs, slotNum) - slotNum += 1 - }) - } - Object.keys(mvconfig.annotations).forEach(hs => { - if (currentSet != 'default' && mvconfig.annotationSets[currentSet] && mvconfig.annotationSets[currentSet].includes(hs)) { - return - } - let label = (currentSet != 'default' && mvconfig.annotationSets[currentSet]) ? '-' : null - createHotspot(hs, slotNum, label) - slotNum += 1 - }) - - $('model-viewer button').remove() - const mView = $('model-viewer')[0] - hotspotsObj.forEach(hs => { - mView.appendChild(hs) - }) - - return true -} - -/** - * Parses the current hotspots into json object - * and writes the json string to the edit panel - * - * @return {bool} true on successful write to edit panel - */ -writeMvconfig = function () { - let annotationsObj = {} - currentButtons = $('.Hotspot').each(function() { - let buttonEl = $(this)[0] - annotationsObj[buttonEl.childNodes[0].innerText] = { - "data-position": buttonEl.getAttribute('data-position'), - "data-normal": buttonEl.getAttribute('data-normal'), - "data-orbit": buttonEl.getAttribute('data-orbit'), - "data-target": buttonEl.getAttribute('data-target') - } - }) - if (Object.keys(annotationsObj).length === 0) return false - const [currentText, mvconfig] = extractMvconfig() - mvconfig.annotations = annotationsObj - const newText = currentText.replace(/(.*?)[\S\s]*?(<\/mvconfig>.*)/,`$1\n${JSON.stringify(mvconfig, null, 2)}\n$2`) - $('#wpTextbox1').val(newText) - return true -} - -/** - * Convert all radian values returned by model-viewer to degrees - * - * @param {string} orbString string with any number of `(number)rad` sub strings - * @param {number|array} fix Optional: number of decimal places to return in converted numbers - * @return {string} string with all radian values and units converted to degrees - */ -orb2degree = function(orbString, fix = null) { - let degArray = orbString.split(' ').map(s => { - if (s.includes('rad')) { - return (Number.parseFloat(s) / Math.PI * 180) + 'deg' - } else { - return s - } - }) - if (fix && !['number', 'object'].includes(typeof fix)) { - console.warn('orb2degree: fix parameter invalid type. Ignoring.') - fix = null - } - if (fix) { - degArray = degArray.map((v, idx) => { - let fixReg = new RegExp('(\\d*.\\d{' + (((typeof fix) == 'object') ? fix[idx] : fix) + '})(\\d*)([a-z]*)') - return v.replace(fixReg,'$1$3') - }) - } - return degArray.join(' ') -} - -/** - * Set camera control setting for the current view - * - * @param {string} view - * @return {bool} new camera-controls setting - */ -toggleCameraControl = function(view) { - let [currentText, mvconfig] = extractMvconfig() - const currentView = (mvconfig.viewerConfig[view]) ? view : 'default' - const newControl = !mvconfig.viewerConfig[currentView]['camera-controls'] - if (newControl) { - mvconfig.viewerConfig[currentView]['camera-controls'] = newControl - } else { - delete mvconfig.viewerConfig[currentView]['camera-controls'] - } - const textUpdate = currentText.replace(/(?<=)([\S\s]*?)(?=<\/mvconfig>)/gm,`\n${JSON.stringify(mvconfig, null, 2)}\n`) - $('#wpTextbox1').val(textUpdate) - return newControl -} - -selectViewConfig = function(view) { - const mView = $('model-viewer')[0] - let [_, mvconfig] = extractMvconfig() - const selectView = (mvconfig.viewerConfig[view]) ? view : 'default' - const viewConfig = mvconfig.viewerConfig[selectView] - const settings = [ - "camera-controls", - "disable-pan", - "disable-tap", - "touch-action", - "disable-zoom", - "orbit-sensitivity", - "zoom-sensitivity", - "pan-sensitivity", - "auto-rotate", - "auto-rotate-delay", - "rotation-per-second", - "interaction-prompt-style", - "interaction-prompt-threshold", - "camera-orbit", - "camera-target", - "field-of-view", - "max-camera-orbit", - "min-camera-orbit", - "max-field-of-view", - "min-field-of-view", - "poster", - "ar", - "ar-modes", - "ar-scale", - "ar-placement" - ] - settings.forEach(s => { - if (viewConfig[s]) { - mView.setAttribute(s,viewConfig[s]) - } else { - mView.removeAttribute(s) - } - }) -} - -/** - * Set new default camera orbit and send values to the preview - * editor - */ -writeCameraOrbit = function() { - const mView = $('model-viewer')[0] - const newOrbit = orb2degree(mView.getCameraOrbit().toString(),[2,2,5]) - mView.setAttribute('camera-orbit', newOrbit) - const targetObj = mView.getCameraTarget() - const newTarget = `${targetObj.x.toFixed(5)}m ${targetObj.y.toFixed(5)}m ${targetObj.z.toFixed(5)}m` - mView.setAttribute('camera-target', newTarget) - const newField = mView.getFieldOfView().toFixed(5) + 'deg' - mView.setAttribute('field-of-view',newField) - let [currentText, mvconfig] = extractMvconfig() - mvconfig.viewerConfig.default['camera-orbit'] = newOrbit - mvconfig.viewerConfig.default['camera-target'] = newTarget - mvconfig.viewerConfig.default['field-of-view'] = newField - const textUpdate = currentText.replace(/(?<=)([\S\s]*?)(?=<\/mvconfig>)/gm,`\n${JSON.stringify(mvconfig, null, 2)}\n`) - $('#wpTextbox1').val(textUpdate) -} - -/** - * Set new camera orbit limits and send values to the preview - * editor - * - * @param {string} axis [yaw|pitch] orbit value to set - * @param {string} limit [max|min] limit value to set - */ -writeCameraLimit = function(axis, limit) { - const mView = $('model-viewer')[0] - const newOrbit = orb2degree(mView.getCameraOrbit().toString(),[2,2,5]) - const newOrbitVals = newOrbit.split(' ') - const valueIndex = (axis == 'yaw') ? 0 : 1 - let [currentText, mvconfig] = extractMvconfig() - const oldOrbit = mvconfig.viewerConfig.default[`${limit}-camera-orbit`] - let oldOrbitVals = (oldOrbit) ? oldOrbit.split(' ') : Array(3).fill('auto') - oldOrbitVals[valueIndex] = newOrbitVals[valueIndex] - mvconfig.viewerConfig.default[`${limit}-camera-orbit`] = oldOrbitVals.join(' ') - mView.setAttribute(`${limit}-camera-orbit`, oldOrbitVals.join(' ')) - const textUpdate = currentText.replace(/(?<=)([\S\s]*?)(?=<\/mvconfig>)/gm,`\n${JSON.stringify(mvconfig, null, 2)}\n`) - $('#wpTextbox1').val(textUpdate) -} - -/** - * Change the currently selected annotation set - * - * @param {string} newSet name of annotation set to select - */ -selectAnnotationSet = function(newSet) { - currentSet = newSet - readMvconfig() -} - -//BASIC MODEL-VIEWER SETTINGS - /** * Disable general interaction with model * viewer for specific additional function @@ -540,155 +278,5 @@ downloadImage = function(defName) { }) } -//HOTSPOT EDITING - -/** - * Sets listener and attributes on model-viewer to - * allow for click registering of a new hotspot - */ -readyAddHotspot = function() { - disableViewer('AddingHotspot', clickAddHotspot) -} - -/** - * Event listener callback to retrieve the info - * about the model surface point selected by the - * mouse and add that information to the editor - * text input - * - * @param {PointerEvent} e - */ -clickAddHotspot = function(e) { - let hsPosition = null - let targetModel = enableViewer() - if (targetModel) { - hsPosition = targetModel.positionAndNormalFromPoint(e.clientX, e.clientY) - } - if (hsPosition) { - let currentText = $('#wpTextbox1').val() - let [_, mvconfig] = extractMvconfig(currentText) - let hsOutput = {} - hsOutput['data-position'] = hsPosition.position.toString().replaceAll(/(\d{5})(\d*?m)/g,"$1m") - hsOutput['data-normal'] = hsPosition.normal.toString().replaceAll(/(\d{5})(\d*?m)/g,"$1m") - hsOutput['data-orbit'] = orb2degree(targetModel.getCameraOrbit().toString(),[2,2,5]) - let targetObj = targetModel.getCameraTarget() - hsOutput['data-target'] = `${targetObj.x.toFixed(5)}m ${targetObj.y.toFixed(5)}m ${targetObj.z.toFixed(5)}m` - mvconfig.annotations['Hotspot ' + (Object.keys(mvconfig.annotations).length + 1)] = hsOutput - let newText = currentText.replace(/(.*?)[\S\s]*?(<\/mvconfig>.*)/,`$1\n${JSON.stringify(mvconfig, null, 2)}\n$2`) - $('#wpTextbox1').val(newText) - } - readMvconfig() -} - -/** - * Set flag and attributes on model-viewer to - * delete the next hotspot that is clicked - */ -readyDelHotspot = function() { - deleteHotspot = true - disableViewer('DeletingHotspot', cancelDeleteHotspot) -} - -/** - * Unset deleting flag and return normal - * function and style to model viewer - */ -cancelDeleteHotspot = function() { - deleteHotspot = null - enableViewer() -} - -/** - * Delete the selected hotspot - * - * @param {element} hs hotspot element to delete - */ -clickDeleteHotspot = function (hs) { - deleteHotspot = null - enableViewer() - const anName = hs.target.childNodes[0].innerText - let purgeAnnotation = new RegExp('(?<="annotationSets"[\\S\\s]*?)(^.*?' + anName + '.*\n)','gm') - hs.target.remove() - const editText = $('#wpTextbox1').val() - const newText = editText.replace(purgeAnnotation,'') - const finalText = newText.replace(/(,)(\n\s+])/gm,'$2') - $('#wpTextbox1').val(finalText) - writeMvconfig() - readMvconfig() -} - -/** - * Check status of delete function - */ -isDeleting = function() { - return deleteHotspot -} - -/** - * Prepare to drag a hotspot - * - * @param {MouseEvent} event - */ -grabAnnotation = function(e) { - if (e.ctrlKey) { - grabHotspot = {x: e.x, y: e.y} - const contEl = $('.glmv-container')[0] - contEl.addEventListener('mousemove', moveAnnotation) - const mvEl = $('model-viewer')[0] - } else { - grabHotspot = null - } -} - -/** - * Drag currently clicked hotspot - * - * @param {MouseEvent} event - */ -moveAnnotation = function(e) { - if (grabHotspot) { - grabHotspot.move = true - e.target.style['transform'] = `translate(${e.x - grabHotspot.x}px, ${e.y - grabHotspot.y}px) scale(1.1,1.1)` - } -} - -/** - * End dragging a hotspot and update information - * - * @param {MouseEvent} event - */ -releaseAnnotation = function(e) { - if (grabHotspot && grabHotspot.move) { - e.target.style['transform']='' - const contEl = $('.glmv-container')[0] - contEl.removeEventListener('mousemove', moveAnnotation) - const mvEl = $('model-viewer')[0] - let newPosition = mvEl.positionAndNormalFromPoint(e.clientX, e.clientY) - const newPos = newPosition.position.toString().replaceAll(/(\d{5})(\d*?m)/g,"$1m") - const newNorm = newPosition.normal.toString().replaceAll(/(\d{5})(\d*?m)/g,"$1m") - mvEl.updateHotspot({ - name: e.target.slot, - position: newPos, - normal: newNorm - }) - const newOrb = orb2degree(mvEl.getCameraOrbit().toString(),[2,2,5]) - e.target.setAttribute('data-orbit', newOrb) - let targetObj = mvEl.getCameraTarget() - const newTarg = `${targetObj.x.toFixed(5)}m ${targetObj.y.toFixed(5)}m ${targetObj.z.toFixed(5)}m` - e.target.setAttribute('data-target', newTarg) - let currentText = $('#wpTextbox1').val() - let [_, mvconfig] = extractMvconfig(currentText) - mvconfig.annotations[e.target.childNodes[0].innerText] = { - "data-position": newPos, - "data-normal": newNorm, - "data-orbit": newOrb, - "data-target": newTarg - } - const newText = currentText.replace(/(.*?)[\S\s]*?(<\/mvconfig>.*)/,`$1\n${JSON.stringify(mvconfig, null, 2)}\n$2`) - $('#wpTextbox1').val(newText) - } - grabHotspot = null -} - //Initialize the menu and get required global objects const [modelMenu, viewSelector] = buildPreviewMenu() \ No newline at end of file