From 939bf6d3ab44e3ce08116c2e9fdab160c35e894b Mon Sep 17 00:00:00 2001 From: Daniel Karbach Date: Mon, 13 Feb 2023 13:55:37 +0100 Subject: [PATCH 1/1] UW grid overlay --- resources/js/components/map/Buttons.js | 50 +- resources/js/components/map/Overlay.js | 57 ++- resources/js/components/map/UWSuperTiles.js | 504 ++++++++++++++++++++ resources/js/components/pages/Map.js | 8 +- resources/js/i18n/de.js | 1 + resources/js/i18n/en.js | 1 + resources/sass/map.scss | 91 ++++ 7 files changed, 684 insertions(+), 28 deletions(-) create mode 100644 resources/js/components/map/UWSuperTiles.js diff --git a/resources/js/components/map/Buttons.js b/resources/js/components/map/Buttons.js index fdffe92..fa02392 100644 --- a/resources/js/components/map/Buttons.js +++ b/resources/js/components/map/Buttons.js @@ -1,26 +1,48 @@ +import PropTypes from 'prop-types'; import React from 'react'; -import { Button } from 'react-bootstrap'; +import { Button, Form } from 'react-bootstrap'; import { useTranslation } from 'react-i18next'; import { useOpenSeadragon } from './OpenSeadragon'; -const Buttons = () => { +const Buttons = ({ setUWOverlay, uwOverlay }) => { const { activeMap, setActiveMap } = useOpenSeadragon(); const { t } = useTranslation(); - return
- {['lw', 'dw', 'sp', 'uw'].map(map => - - )} + return
+
+ {['lw', 'dw', 'sp', 'uw'].map(map => + + )} +
+ {activeMap === 'uw' ? +
+ setUWOverlay(e.target.checked)} + type="checkbox" + /> + + {t('map.uwOverlay')} + +
+ : null}
; }; +Buttons.propTypes = { + setUWOverlay: PropTypes.func, + uwOverlay: PropTypes.bool, +}; + export default Buttons; diff --git a/resources/js/components/map/Overlay.js b/resources/js/components/map/Overlay.js index 4068bea..4ec503a 100644 --- a/resources/js/components/map/Overlay.js +++ b/resources/js/components/map/Overlay.js @@ -5,31 +5,64 @@ import { createPortal } from 'react-dom'; import { useOpenSeadragon } from './OpenSeadragon'; -const Overlay = ({ children, onClick, x, y }) => { +const Overlay = ({ children, height, onClick, page, width, x, y }) => { const { viewer } = useOpenSeadragon(); const [element] = React.useState(document.createElement('div')); React.useEffect(() => { if (!viewer) return; - viewer.addOverlay( - element, - new OpenSeadragon.Point(x, y), - OpenSeadragon.Placement.CENTER, - ); - if (onClick) { - new OpenSeadragon.MouseTracker({ - element, - clickHandler: onClick, - }); + const add = () => { + if (width && height) { + viewer.addOverlay( + element, + new OpenSeadragon.Rect(x, y, width, height), + ); + } else { + viewer.addOverlay( + element, + new OpenSeadragon.Point(x, y), + OpenSeadragon.Placement.CENTER, + ); + } + if (onClick) { + new OpenSeadragon.MouseTracker({ + element, + clickHandler: onClick, + }); + } + }; + const addPage = () => { + if (viewer.currentPage() === page) { + add(); + } + }; + if (typeof page !== 'undefined') { + viewer.addHandler('page', addPage); + return () => { + viewer.removeHandler('page', addPage); + viewer.removeOverlay(element); + }; } - }, [onClick, viewer, x, y]); + if (viewer.isOpen()) { + add(); + } else { + viewer.addHandler('open', add); + return () => { + viewer.removeHandler('open', add); + viewer.removeOverlay(element); + }; + } + }, [onClick, height, page, viewer, width, x, y]); return createPortal(children, element); }; Overlay.propTypes = { children: PropTypes.node, + height: PropTypes.number, onClick: PropTypes.func, + page: PropTypes.number, + width: PropTypes.number, x: PropTypes.number, y: PropTypes.number, }; diff --git a/resources/js/components/map/UWSuperTiles.js b/resources/js/components/map/UWSuperTiles.js new file mode 100644 index 0000000..2d7afda --- /dev/null +++ b/resources/js/components/map/UWSuperTiles.js @@ -0,0 +1,504 @@ +import OpenSeadragon from 'openseadragon'; +import PropTypes from 'prop-types'; +import React from 'react'; + +import Overlay from './Overlay'; +import { useOpenSeadragon } from './OpenSeadragon'; + +const dropMap = { + '00': '10', + '03': '02', + '05': '05', + '06': '05', + '07': '17', + '08': '07', + '09': '4B', + '0A': '09', + '0B': '6A', + '0D': '0B', + '0F': '01', + '10': '01', + '12': '0D', + '13': '0D', + '14': '0D', + '17': '27', + '18': '12', + '19': '07', + '1E': '3E', + '20': '01', + '21': '0D', + '22': '0D', + '23': '0D', + '24': '08', + '27': '31', + '29': '07', + '2A': '07', + '2C': '12', + '2D': '06', + '2E': '06', + '2F': '02', + '31': '77', + '33': '08', + '35': '08', + '36': '08', + '37': '08', + '39': '29', + '3A': '0A', + '3C': '0E', + '3D': '96', + '43': '0A', + '44': '0A', + '46': '09', + '47': '07', + '48': '07', + '49': '07', + '4B': '09', + '4D': 'A6', + '4F': 'BE', + '54': '34', + '55': '09', + '56': '09', + '57': '09', + '58': '09', + '59': '07', + '5A': '0E', + '5B': '0E', + '5E': '7E', + '65': 'AC', + '67': '09', + '68': '07', + '6D': '0B', + '73': '05', + '74': '05', + '75': '08', + '77': 'A7', + '78': '9D', + '79': '9D', + '7A': '9D', + '7B': '9D', + '7C': '0E', + '7D': '9B', + '7E': '9E', + '81': '05', + '82': '05', + '83': '05', + '84': '05', + '85': '05', + '88': 'A9', + '89': 'A9', + '8A': '0E', + '8B': '0E', + '8C': '1C', + '8D': '0B', + '8F': '0C', + '90': '0C', + '92': '0C', + '94': '0E', + '95': '0E', + '97': 'D1', + '9A': '7D', + '9B': '7D', + '9C': '0E', + '9D': '7B', + '9E': 'BE', + '9F': '0C', + 'A1': '0C', + 'A3': '0C', + 'A4': '0E', + 'A7': '17', + 'A8': '05', + 'A9': '89', + 'AA': '0A', + 'AC': '0B', + 'AF': '02', + 'B1': 'B2', + 'B2': '0C', + 'B3': '0D', + 'B7': '0D', + 'B8': '05', + 'B9': '05', + 'BA': '0A', + 'BB': '0A', + 'BD': '4F', + 'BE': '4F', + 'BF': '02', + 'C1': '0C', + 'C2': '0C', + 'C3': '0D', + 'C5': '0D', + 'C6': '0D', + 'C7': '05', + 'C8': '05', + 'C9': '0A', + 'CA': '0B', + 'CB': '0B', + 'CC': '0B', + 'CD': 'DE', + 'CE': 'DE', + 'D1': 'B1', + 'D3': '05', + 'D4': '05', + 'D5': '0D', + 'D6': '0D', + 'D7': '05', + 'D8': '05', + 'D9': '05', + 'DB': '0B', + 'DC': '0B', + 'DD': '06', + 'DE': '06', + 'E1': '06', + 'E2': '06', + 'E3': '14', + 'E4': '06', + 'E5': '06', + 'E6': '06', + 'E7': '06', + 'E8': 'F8', + 'E9': 'FA', + 'EA': 'FA', + 'EB': 'FB', + 'EC': 'FD', + 'ED': 'FD', + 'EE': 'FE', + 'EF': 'FF', + 'F0': '06', + 'F1': '06', + 'F4': '06', + 'F5': '06', + 'F9': '06', + 'FE': '06', +}; + +const strongEG = [ + '08', + '0C', + '15', + '2F', + '40', + '51', + '52', + '59', + '5B', + '60', + '62', + '66', + '71', + '72', + '81', + 'A2', + 'A8', + 'A9', + 'AA', + 'B2', + 'B3', + 'B9', + 'C2', + 'C3', + 'CB', + 'CC', + 'DB', + 'DC', + 'DF', + 'E1', + 'E3', + 'FA', +]; + +const weakEG = [ + '07', + '0A', + '16', + '28', + '2A', + '2B', + '34', + '35', + '36', + '37', + '3A', + '4D', + '55', + '61', + '76', + '99', + 'A0', + 'C9', + 'E2', + 'E4', + 'F0', + 'FD', + 'FE', +]; + +const kick = [ + '1B', + '29', + '3E', + '43', + '97', +]; + +const dark = [ + '0B', + '19', + '21', + '22', + '32', + '41', + '42', + '69', + '6A', + '92', + '93', + 'B5', + 'BA', + 'C0', + 'D0', + 'E5', + 'E6', + 'E7', + 'F0', + 'F1', +]; + +const camera = { + '00': ['x', 'y'], + '01': ['y'], + '02': ['y'], + '07': ['x', 'y'], + '0A': ['x', 'y'], + '0C': ['x', 'y'], + '0D': ['x', 'y'], + '0E': ['yu'], + '10': ['x'], + '11': ['x'], + '12': ['x', 'y'], + '13': ['x'], + '14': ['x', 'y'], + '15': ['x', 'y'], + '16': ['y'], + '17': ['x', 'y'], + '18': ['x'], + '19': ['xr'], + '1A': ['xl'], + '1B': ['yu'], + '1D': ['y'], + '1F': ['yu'], + '20': ['x', 'y'], + '21': ['y'], + '22': ['y'], + '26': ['yd'], + '27': ['x', 'y'], + '28': ['x', 'y'], + '2A': ['x', 'y'], + '2B': ['xl'], + '2F': ['yd'], + '31': ['yu'], + '32': ['x', 'y'], + '34': ['x', 'y'], + '35': ['y'], + '36': ['x', 'y'], + '37': ['y'], + '38': ['x'], + '39': ['yu'], + '3A': ['x', 'y'], + '3B': ['x'], + '3C': ['x', 'y'], + '3E': ['yd'], + '3F': ['yu'], + '40': ['xl'], + '41': ['x', 'y'], + '42': ['y'], + '43': ['yu'], + '44': ['xr'], + '45': ['xr'], + '46': ['y'], + '49': ['xr'], + '4A': ['y'], + '4B': ['yd'], + '4C': ['x'], + '4D': ['x', 'y'], + '4E': ['yd'], + '50': ['x'], + '51': ['x', 'y'], + '52': ['xl', 'yd'], + '53': ['xr'], + '54': ['x', 'y'], + '55': ['y'], + '56': ['xr'], + '58': ['xr'], + '59': ['x'], + '5B': ['x'], + '5C': ['yu'], + '60': ['x'], + '61': ['x', 'y'], + '62': ['x', 'y'], + '63': ['xr'], + '64': ['yu'], + '65': ['yu'], + '66': ['yd'], + '67': ['x'], + '68': ['x', 'y'], + '6A': ['x'], + '6B': ['yu'], + '6D': ['xr'], + '72': ['y'], + '74': ['y'], + '75': ['xr'], + '76': ['xl'], + '77': ['x', 'y'], + '7B': ['yu'], + '7C': ['x'], + '7D': ['yu'], + '7E': ['xr'], + '7F': ['xr'], + '80': ['y'], + '81': ['x', 'y'], + '82': ['x', 'y'], + '83': ['xr'], + '84': ['x', 'y'], + '85': ['xl'], + '89': ['y'], + '8B': ['xl'], + '8D': ['xr'], + '91': ['x'], + '92': ['xr'], + '93': ['yu'], + '95': ['x'], + '96': ['xl'], + '97': ['xr'], + '98': ['yd'], + '99': ['yd'], + '9B': ['yd'], + '9C': ['x', 'y'], + '9D': ['yd'], + 'A0': ['y'], + 'A1': ['xr', 'yu'], + 'A2': ['x', 'y'], + 'A3': ['x'], + 'A5': ['yd'], + 'A6': ['x', 'y'], + 'A7': ['yd'], + 'A8': ['xr'], + 'A9': ['x', 'y'], + 'AA': ['xl'], + 'B1': ['xr'], + 'B2': ['yu'], + 'B3': ['x'], + 'B4': ['x', 'y'], + 'B5': ['x', 'y'], + 'B7': ['x'], + 'B8': ['x'], + 'B9': ['x', 'y'], + 'BB': ['xl'], + 'BC': ['xr'], + 'BE': ['xl'], + 'C0': ['xl'], + 'C2': ['x', 'y'], + 'C3': ['x'], + 'C4': ['x', 'y'], + 'C5': ['x'], + 'C6': ['x', 'y'], + 'C7': ['x', 'y'], + 'C9': ['y'], + 'CB': ['x', 'y'], + 'CC': ['x', 'y'], + 'D0': ['xl'], + 'D2': ['xr'], + 'D5': ['x'], + 'D6': ['x'], + 'D8': ['xl'], + 'D9': ['yu'], + 'DA': ['yu'], + 'DB': ['x', 'y'], + 'DC': ['x', 'y'], + 'DF': ['yd'], + 'E1': ['x'], + 'E2': ['x'], + 'E4': ['x'], + 'E5': ['x', 'y'], + 'E6': ['xr', 'yd'], + 'E7': ['xr', 'yu'], + 'E8': ['x', 'yu'], + 'EA': ['xl', 'yu'], + 'EB': ['x'], + 'ED': ['x', 'yu'], + 'EE': ['x', 'y'], + 'EF': ['yd'], + 'F0': ['x', 'y'], + 'F1': ['x', 'y'], + 'F8': ['x', 'y'], + 'F9': ['xr', 'yd'], + 'FA': ['x', 'y'], + 'FB': ['xr', 'yd'], + 'FD': ['x', 'y'], + 'FE': ['xr'], + 'FF': ['yd'], +}; + +const getClassName = key => { + const classNames = []; + if (strongEG.includes(key)) { + classNames.push('strong-eg'); + } + if (weakEG.includes(key)) { + classNames.push('weak-eg'); + } + if (kick.includes(key)) { + classNames.push('kick'); + } + if (dark.includes(key)) { + classNames.push('dark'); + } + if (camera[key]) { + camera[key].forEach(c => { + classNames.push(`cam-${c}`); + }); + } + return classNames.join(' '); +}; + +const UWSuperTiles = ({ show }) => { + const { viewer } = useOpenSeadragon(); + + const onClick = React.useCallback(e => { + if (e.originalTarget.tagName !== 'A') return; + if (e.originalTarget.className !== 'cell-link') return; + const key = e.originalTarget.dataset.key; + + const x = (parseInt(key[1], 16) + 0.5) / 16; + const y = (parseInt(key[0], 16) + 0.5) / 16; + if (viewer && viewer.viewport) { + viewer.viewport.panTo(new OpenSeadragon.Point(x, y)); + viewer.viewport.zoomTo(4); + viewer.element.scrollIntoView(); + } + }, [viewer]); + + return +
+ {[...Array(16).keys()].map(x => + [...Array(16).keys()].map(y => { + const key = `${x.toString(16).toUpperCase()}${y.toString(16).toUpperCase()}`; + return
+

{key}

+ {dropMap[key] ? +

+ + {`▶ ${dropMap[key]}`} + +

+ : null} +
; + }) + )} +
+
; +}; + +UWSuperTiles.propTypes = { + show: PropTypes.bool, +}; + +export default UWSuperTiles; diff --git a/resources/js/components/pages/Map.js b/resources/js/components/pages/Map.js index cbd3581..b275602 100644 --- a/resources/js/components/pages/Map.js +++ b/resources/js/components/pages/Map.js @@ -7,8 +7,11 @@ import Buttons from '../map/Buttons'; import List from '../map/List'; import OpenSeadragon from '../map/OpenSeadragon'; import Pins from '../map/Pins'; +import UWSuperTiles from '../map/UWSuperTiles'; const Map = () => { + const [uwOverlay, setUWOverlay] = React.useState(false); + const container = React.useRef(); const { t } = useTranslation(); @@ -18,12 +21,13 @@ const Map = () => { -
+

{t('map.heading')}

- +
+ ; diff --git a/resources/js/i18n/de.js b/resources/js/i18n/de.js index fef88f4..6ec3c39 100644 --- a/resources/js/i18n/de.js +++ b/resources/js/i18n/de.js @@ -391,6 +391,7 @@ export default { spLong: 'Spezielle Gebiete', spShort: 'SP', uwLong: 'Underworld', + uwOverlay: 'Gitter zigen', uwShort: 'UW', }, menu: { diff --git a/resources/js/i18n/en.js b/resources/js/i18n/en.js index 6820f30..5e9668d 100644 --- a/resources/js/i18n/en.js +++ b/resources/js/i18n/en.js @@ -391,6 +391,7 @@ export default { spLong: 'Special Areas', spShort: 'SP', uwLong: 'Underworld', + uwOverlay: 'Show grid', uwShort: 'UW', }, menu: { diff --git a/resources/sass/map.scss b/resources/sass/map.scss index 38376a1..3f8ae06 100644 --- a/resources/sass/map.scss +++ b/resources/sass/map.scss @@ -32,3 +32,94 @@ text-decoration: none; } } + +.uw-super-tiles { + display: grid; + height: 100%; + grid-template-columns: repeat(16, 1fr); + + > div { + position: relative; + border: thin solid black; + padding: .25ex; + + &.dark { + border: medium solid black; + } + &.kick { + border: medium solid yellow; + } + &.weak-eg { + border: medium solid orange; + } + &.strong-eg { + border: medium solid green; + } + + &::before, + &::after { + position: absolute; + top: 0; + left: 0; + width: 100%; + height: 100%; + pointer-events: none; + } + + &.cam-x::before { + content: ''; + height: 50%; + border-bottom: thin solid cyan; + } + &.cam-xl::before { + content: ''; + position: absolute; + width: 50%; + height: 50%; + border-bottom: thin solid cyan; + } + &.cam-xr::before { + content: ''; + position: absolute; + left: 50%; + width: 50%; + height: 50%; + border-bottom: thin solid cyan; + } + &.cam-y::after { + content: ''; + position: absolute; + width: 50%; + border-right: thin solid cyan; + } + &.cam-yu::after { + content: ''; + position: absolute; + width: 50%; + height: 50%; + border-right: thin solid cyan; + } + &.cam-yd::after { + content: ''; + position: absolute; + top: 50%; + width: 50%; + height: 50%; + border-right: thin solid cyan; + } + } + + .cell-id, + .cell-drop { + margin: 0; + line-height: 1; + } + .cell-drop { + font-size: 80%; + } + .cell-link { + color: inherit; + cursor: pointer; + text-decoration: none; + } +} -- 2.39.2