Compare commits

...

9 Commits

Author SHA1 Message Date
8294edc1d1 Annotation sequence in editor skips unused in set
Signed-off-by: Justin Georgi <justin.georgi@gmail.com>
2025-05-05 21:28:11 -07:00
846106a1a8 Fix: View menu width on mobile
Signed-off-by: Justin Georgi <justin.georgi@gmail.com>
2025-05-04 09:47:59 -07:00
717ef152f1 Fix: Successfully delete final annotation
Signed-off-by: Justin Georgi <justin.georgi@gmail.com>
2025-05-03 20:35:23 -07:00
859183fe2e Add disable hs buttons when 0 annotations
Signed-off-by: Justin Georgi <justin.georgi@gmail.com>
2025-05-03 20:04:00 -07:00
9953dff4a0 Add better reset image
Signed-off-by: Justin Georgi <justin.georgi@gmail.com>
2025-05-03 11:28:55 -07:00
8ed4e1f679 Fix: reset after annotation selection
Signed-off-by: Justin Georgi <justin.georgi@gmail.com>
2025-05-03 11:26:02 -07:00
7e353bee24 Add reset view position button to model menu
Signed-off-by: Justin Georgi <justin.georgi@gmail.com>
2025-05-03 10:02:43 -07:00
383818b6f8 Fix: annotation drag
Signed-off-by: Justin Georgi <justin.georgi@gmail.com>
2025-05-03 07:59:39 -07:00
88cd5e4727 Fix: set initial view for current selected view
Signed-off-by: Justin Georgi <justin.georgi@gmail.com>
2025-05-02 21:03:41 -07:00
6 changed files with 65 additions and 25 deletions

View File

@@ -183,10 +183,10 @@ class GlModelTransformOutput extends MediaTransformOutput {
$attrModelView = array_merge(['src' => $srcUrl, 'class' => 'mv-model', 'interpolation-decay' => '100', 'interaction-prompt' => 'none'], $attrModelView);
$attrModelView['style'] = 'width: 100%; height: 100%;';
$attrModelView['onload'] = 'modelLoaded(event)';
$hotspotHtml = (isset($hotspots)) ? implode($hotspots) : '';
$hotspotHtml = (!empty($hotspots)) ? implode($hotspots) : '';
$elModel = Html::rawElement('model-viewer', $attrModelView, $hotspotHtml);
$elMenu = self::buildViewMenu();
$elMenu = self::buildViewMenu(!empty($hotspots));
$elFileLink = '';
if (!isset($viewParams['preview']) && $context->getTitle() != $this->file->getTitle()) {
@@ -213,9 +213,10 @@ class GlModelTransformOutput extends MediaTransformOutput {
/**
* Build the button menu used for viewer actions
*
* @param bool $annotsDrawn true to enable the annotation buttons
* @return string
*/
private static function buildViewMenu() {
private static function buildViewMenu($annotsDrawn) {
$attrMenu = array(
'class' => 'glmv-menu awaiting-model',
'style' => 'display: none;'
@@ -230,13 +231,15 @@ class GlModelTransformOutput extends MediaTransformOutput {
$gotoUrl = $mainConfig->get( 'ExtensionAssetsPath' ) . '/GlModelViewer/resources/goto_hs.svg';
$attrMenuButtonPrev = array (
'class' => 'glmv-menu-button prev-hs disable-on-hide',
'class' => 'glmv-menu-button prev-hs disable-on-hide disable-on-none',
'disabled' => !$annotsDrawn,
'onclick' => 'prevAnnotation(event.target.closest(".glmv-container").querySelector("model-viewer"))',
'onmousedown' => 'event.stopPropagation()',
'ontouchstart' => 'event.stopPropagation()'
);
$attrMenuButtonNext = array (
'class' => 'glmv-menu-button next-hs disable-on-hide',
'class' => 'glmv-menu-button next-hs disable-on-hide disable-on-none',
'disabled' => !$annotsDrawn,
'onclick' => 'nextAnnotation(event.target.closest(".glmv-container").querySelector("model-viewer"))',
'onmousedown' => 'event.stopPropagation()',
'ontouchstart' => 'event.stopPropagation()'
@@ -244,7 +247,8 @@ class GlModelTransformOutput extends MediaTransformOutput {
$slideUrl = $mainConfig->get( 'ExtensionAssetsPath' ) . '/GlModelViewer/resources/hs_slideshow.svg';
$attrMenuButtonSlides = array (
'class' => 'glmv-menu-button disable-on-hide',
'class' => 'glmv-menu-button disable-on-hide disable-on-none',
'disabled' => !$annotsDrawn,
'onclick' => 'event.target.toggleAttribute("toggled"); slideshowAnnotations(event.target.closest(".glmv-container").querySelector("model-viewer"))',
'onmousedown' => 'event.stopPropagation()',
'ontouchstart' => 'event.stopPropagation()'
@@ -252,7 +256,8 @@ class GlModelTransformOutput extends MediaTransformOutput {
$hideUrl = $mainConfig->get( 'ExtensionAssetsPath' ) . '/GlModelViewer/resources/hs_hide.svg';
$attrMenuButtonHide = array (
'class' => 'glmv-menu-button',
'class' => 'glmv-menu-button disable-on-none',
'disabled' => !$annotsDrawn,
'onclick' => 'event.target.toggleAttribute("toggled"); toggleAnnotations(event.target.closest(".glmv-container").querySelector("model-viewer"))',
'onmousedown' => 'event.stopPropagation()',
'ontouchstart' => 'event.stopPropagation()'
@@ -267,12 +272,21 @@ class GlModelTransformOutput extends MediaTransformOutput {
'ontouchstart' => 'event.stopPropagation()'
);
$resetUrl = $mainConfig->get( 'ExtensionAssetsPath' ) . '/GlModelViewer/resources/reset.svg';
$attrMenuButtonReset = array (
'class' => 'glmv-menu-button',
'onclick' => 'resetView(event.target.closest(".glmv-container").querySelector("model-viewer"))',
'onmousedown' => 'event.stopPropagation()',
'ontouchstart' => 'event.stopPropagation()'
);
$menuButtons = array(
Html::rawElement('div', $attrMenuButtonPrev, '<img class="awaiting-model" src="' . $gotoUrl . '"></image>'),
Html::rawElement('div', $attrMenuButtonSlides, '<img class="awaiting-model" src="' . $slideUrl . '"></image>'),
Html::rawElement('div', $attrMenuButtonNext, '<img class="awaiting-model" src="' . $gotoUrl . '"></image>'),
Html::rawElement('div', $attrMenuButtonHide, '<img class="awaiting-model" src="' . $hideUrl . '"></image>'),
Html::rawElement('div', $attrMenuButtonScreen, '<img class="awaiting-model full-hide" src="' . $screenUpUrl . '"></image><img class="awaiting-model full-show" src="' . $screenDownUrl . '"></image>')
Html::rawElement('div', $attrMenuButtonScreen, '<img class="awaiting-model full-hide" src="' . $screenUpUrl . '"></image><img class="awaiting-model full-show" src="' . $screenDownUrl . '"></image>'),
Html::rawElement('div', $attrMenuButtonReset, '<img class="awaiting-model" src="' . $resetUrl . '"></image>')
);
return Html::rawElement('div', $attrMenu, $menuImg . implode($menuButtons));

View File

@@ -69,13 +69,15 @@ 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()
let currentText = $('#wpTextbox1').val()
let [_, mvconfig] = extractMvconfig(currentText)
delete mvconfig.annotations[anName]
for (anSet in mvconfig.annotationSets) {
mvconfig.annotationSets[anSet]=mvconfig.annotationSets[anSet].filter( x => x !== anName )
}
let newText = currentText.replace(/(.*?<mvconfig>)[\S\s]*?(<\/mvconfig>.*)/,`$1\n${TOML.stringify(mvconfig, null, 2)}\n$2`)
$('#wpTextbox1').val(newText)
readMvconfig()
}
@@ -93,7 +95,7 @@ isDeleting = function() {
*/
grabAnnotation = function(e) {
if (e.ctrlKey) {
grabHotspot = {x: e.x, y: e.y}
grabHotspot = {x: e.x, y: e.y, target: e.target}
const contEl = $('.glmv-container')[0]
contEl.addEventListener('mousemove', moveAnnotation)
const mvEl = $('model-viewer')[0]
@@ -110,7 +112,7 @@ grabAnnotation = function(e) {
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)`
grabHotspot.target.style['transform'] = `translate(${e.x - grabHotspot.x}px, ${e.y - grabHotspot.y}px) scale(1.1,1.1)`
}
}

View File

@@ -19,6 +19,12 @@ extractMvconfig = function() {
if (mvconfig.annotations === undefined) {
mvconfig.annotations = {}
}
const mView = $('model-viewer')[0]
const hsButtons = [...mView.parentElement.querySelectorAll('.disable-on-none')]
hsButtons.forEach( mb => {
mb.toggleAttribute('disabled',$.isEmptyObject(mvconfig.annotations))
})
if (mvconfig.annotationSets === undefined) {
mvconfig.annotationSets = {}
}
@@ -36,7 +42,7 @@ readMvconfig = function() {
let mvconfig
let slotNum = 1
createHotspot = function(hsLabel, hsSlot, hsTag) {
createHotspot = function(hsLabel, hsSlot, hsTag, hsSkip) {
let newHs = document.createElement('button')
newHs.classList.add('Hotspot')
newHs.setAttribute('slot',`hotspot-${hsSlot}`)
@@ -44,6 +50,7 @@ readMvconfig = function() {
newHs.setAttribute('onclick', 'onAnnotation(event)')
newHs.setAttribute('onmousedown', 'grabAnnotation(event)')
newHs.setAttribute('onmouseup', 'releaseAnnotation(event)')
newHs.toggleAttribute('seq-skip', !!hsSkip)
Object.keys(mvconfig.annotations[hsLabel]).forEach((prop) => {
newHs.setAttribute(prop, mvconfig.annotations[hsLabel][prop])
})
@@ -75,7 +82,7 @@ readMvconfig = function() {
return
}
let label = (currentSet != 'default' && mvconfig.annotationSets[currentSet]) ? '-' : null
createHotspot(hs, slotNum, label)
createHotspot(hs, slotNum, label, true)
slotNum += 1
})
@@ -238,6 +245,7 @@ selectViewConfig = function(view) {
mView.removeAttribute(s)
}
})
mView.setAttribute('current-view',selectView)
}
/**
@@ -246,6 +254,7 @@ selectViewConfig = function(view) {
*/
writeCameraOrbit = function() {
const mView = $('model-viewer')[0]
const currentView = mView.getAttribute('current-view') ? mView.getAttribute('current-view') : 'default'
const newOrbit = orb2degree(mView.getCameraOrbit().toString(),[2,2,5])
mView.setAttribute('camera-orbit', newOrbit)
const targetObj = mView.getCameraTarget()
@@ -254,9 +263,9 @@ writeCameraOrbit = function() {
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
mvconfig.viewerConfig[currentView]['camera-orbit'] = newOrbit
mvconfig.viewerConfig[currentView]['camera-target'] = newTarget
mvconfig.viewerConfig[currentView]['field-of-view'] = newField
const textUpdate = currentText.replace(/(?<=<mvconfig>)([\S\s]*?)(?=<\/mvconfig>)/gm,`\n${TOML.stringify(mvconfig, null, 2)}\n`)
$('#wpTextbox1').val(textUpdate)
}

View File

@@ -117,7 +117,7 @@
justify-content: flex-start;
&:hover {
width: 196px;
width: 232px;
& .glmv-menu-image {
transform: rotate(180deg);
@@ -209,7 +209,7 @@
border-radius: 9px;
&:hover {
width: 294px;
width: 342px;
}
}

View File

@@ -72,7 +72,7 @@ selectAnnotation = function(mView, annotId) {
*/
nextAnnotation = function(mView) {
let incrAnnotation = 0
const numSpots = [...mView.querySelectorAll('Button')].length
const numSpots = [...mView.querySelectorAll('Button:not([seq-skip])')].length
const currentAnnotation = mView.querySelectorAll('Button:has(.HotspotAnnotation:not(.HiddenAnnotation))')[0]
if (!currentAnnotation) {
incrAnnotation = 1
@@ -95,7 +95,7 @@ nextAnnotation = function(mView) {
*/
prevAnnotation = function(mView) {
let decrAnnotation = 0
const numSpots = [...mView.querySelectorAll('Button')].length
const numSpots = [...mView.querySelectorAll('Button:not([seq-skip])')].length
const currentAnnotation = mView.querySelectorAll('Button:has(.HotspotAnnotation:not(.HiddenAnnotation))')[0]
if (!currentAnnotation) {
decrAnnotation = numSpots
@@ -158,4 +158,18 @@ toggleFullScreen = function(glCont) {
} else {
glCont.requestFullscreen()
}
}
/**
* Reset view to initial position
*
* @param {ModelViewer} mView
*/
resetView = function(mView) {
const resetOrb = mView.getAttribute('camera-orbit') || 'auto auto auto'
const resetTarg = mView.getAttribute('camera-target') || 'auto auto auto'
const resetFov = mView.getAttribute('field-of-view') || 'auto'
mView.cameraOrbit = resetOrb
mView.cameraTarget = resetTarg
mView.fieldOfView = resetFov
}

1
resources/reset.svg Normal file
View File

@@ -0,0 +1 @@
<svg xmlns="http://www.w3.org/2000/svg" height="24px" viewBox="0 -960 960 960" width="24px" fill="#5f6368"><path d="M320-280q-33 0-56.5-23.5T240-360v-240q0-33 23.5-56.5T320-680h40l40-40h160l40 40h40q33 0 56.5 23.5T720-600v240q0 33-23.5 56.5T640-280H320Zm0-80h320v-240H320v240Zm160-40q33 0 56.5-23.5T560-480q0-33-23.5-56.5T480-560q-33 0-56.5 23.5T400-480q0 33 23.5 56.5T480-400ZM342-940q34-11 68.5-15.5T480-960q94 0 177.5 33.5t148 93Q870-774 911-693.5T960-520h-80q-7-72-38-134.5t-79.5-110Q714-812 651-842t-135-36l62 62-56 56-180-180ZM618-20Q584-9 549.5-4.5T480 0q-94 0-177.5-33.5t-148-93Q90-186 49-266.5T0-440h80q8 72 38.5 134.5t79 110Q246-148 309-118t135 36l-62-62 56-56L618-20ZM480-480Z"/></svg>

After

Width:  |  Height:  |  Size: 696 B