diff --git a/css/event.css b/css/event.css new file mode 100644 index 00000000..19f506bc --- /dev/null +++ b/css/event.css @@ -0,0 +1,56 @@ +#event-switcher { + position: fixed; + display: none; + flex-direction: row; + justify-content: center; + align-items: center; + z-index: 1; + top: 10px; + left: 50%; + transform: translateX(-50%); + background-color: #e1e1e1; + padding: 5px 10px; + border-radius: 5px; +} + +.event-switch-arrow { + cursor: pointer; +} + +.event-switch-tool { + margin: 0 5px; +} + +#selected-event { + font-weight: 500; + cursor: pointer; +} + +#selected-event:hover { + text-decoration: underline; + background-color: #d1d1d1; +} + +#event-selector-menu { + display: none; + position: fixed; + top: 32px; + flex-direction: column; + align-items: center; + background-color: #e1e1e1; + width: 100px; + left: 50%; + transform: translateX(-50%); + max-height: 175px; + overflow-y: auto; + overflow-x: hidden; + padding: 0 5px; +} + +.event-option { + cursor: pointer; + border: 1px solid #000; + width: 100%; + text-align: center; + margin: 1px 0; +} diff --git a/css/filter.css b/css/filter.css index b12f8d46..721ef365 100644 --- a/css/filter.css +++ b/css/filter.css @@ -2,7 +2,7 @@ min-width: 100px; position: fixed; flex-direction: column; - background-color: #f5f5f5; + background-color: #e1e1e1; border-radius: 5px; padding: 10px; top: 10px; diff --git a/css/main.css b/css/main.css index 6b7406b1..56011873 100644 --- a/css/main.css +++ b/css/main.css @@ -3,6 +3,7 @@ body { padding: 0; /* overflow: hidden; */ font-family: sans-serif; + font-size: 16px; } .manipulation-tool { diff --git a/css/toggle.css b/css/toggle.css index 9a173f24..d877a96c 100644 --- a/css/toggle.css +++ b/css/toggle.css @@ -33,7 +33,7 @@ left: 0; right: 0; bottom: 0; - background-color: #ccc; + background-color: #e1e1e1; -webkit-transition: 0.4s; transition: 0.4s; } diff --git a/img/left_arrow.svg b/img/left_arrow.svg new file mode 100644 index 00000000..bbcd50c6 --- /dev/null +++ b/img/left_arrow.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/img/right_arrow.svg b/img/right_arrow.svg new file mode 100644 index 00000000..eb45cced --- /dev/null +++ b/img/right_arrow.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/index.html b/index.html index cea0bd9b..a76ce976 100644 --- a/index.html +++ b/index.html @@ -12,6 +12,7 @@ + @@ -34,7 +35,7 @@
- +

@@ -72,6 +73,17 @@
+
+ Previous event +
+ +
+
+ Next event +
+ diff --git a/js/main.js b/js/main.js index 4f43c2cb..f76ce42e 100644 --- a/js/main.js +++ b/js/main.js @@ -1,37 +1,18 @@ import { errorMsg } from "./tools.js"; import { PdgToggle } from "./menu/show-pdg.js"; import { drawAll } from "./draw.js"; -import { - bits, - genStatus, - renderRangeParameters, - parametersRange, - getWidthFilterContent, - renderGenSim, -} from "./menu/filter/filter.js"; -import { - mouseDown, - mouseUp, - mouseOut, - mouseMove, - getVisible, - onScroll, -} from "./events.js"; -import { loadObjects } from "./types/load.js"; -import { objectTypes } from "./types/objects.js"; -import { copyObject } from "./lib/copy.js"; +import { getWidthFilterContent } from "./menu/filter/filter.js"; +import { mouseDown, mouseUp, mouseOut, mouseMove, onScroll } from "./events.js"; +import { showEventSwitcher, loadSelectedEvent } from "./menu/event-number.js"; +import { renderEvent } from "./menu/event-number.js"; const canvas = document.getElementById("canvas"); const ctx = canvas.getContext("2d"); -const manipulationTools = document.getElementsByClassName("manipulation-tool"); -const filter = document.getElementById("filter"); -const filters = document.getElementById("filters"); - canvas.width = window.innerWidth; canvas.height = window.innerHeight; -let jsonData = {}; +const jsonData = {}; const dragTools = { draggedObject: null, @@ -46,17 +27,9 @@ const currentObjects = {}; const visibleObjects = {}; -function start(currentObjects, visibleObjects) { - for (const [key, value] of Object.entries(currentObjects)) { - const classType = objectTypes[key]; - const collection = value.collection; - classType.setup(collection, canvas); - } - - drawAll(ctx, currentObjects); - - getVisible(currentObjects, visibleObjects); -} +const selectedObjectTypes = { + types: ["edm4hep::MCParticle"], +}; canvas.onmousedown = (event) => { mouseDown(event, visibleObjects, dragTools); @@ -74,14 +47,6 @@ window.onscroll = () => { onScroll(currentObjects, visibleObjects); }; -/* -function showInputModal() { - const modal = document.getElementById("input-modal"); - - modal.style.display = "block"; -} -*/ - function hideInputModal() { const modal = document.getElementById("input-modal"); @@ -101,11 +66,37 @@ document.getElementById("input-file").addEventListener("change", (event) => { const reader = new FileReader(); reader.addEventListener("load", (event) => { const fileText = event.target.result; - jsonData = JSON.parse(fileText); + jsonData.data = JSON.parse(fileText); const eventNumberInput = document.getElementById("event-number"); - eventNumberInput.max = Object.keys(jsonData).length - 1; + const options = Object.keys(jsonData.data).map((event) => + parseInt(event.replace("Event ", "")) + ); + eventNumberInput.max = Object.keys(options).length - 1; + if (options.length === 0) { + errorMsg("No events found in the file!"); + return; + } + eventNumberInput.value = options[0]; document.getElementById("event-selector").style.display = "block"; + const eventOptions = document.getElementById("event-number"); + const eventSelectorMenu = document.getElementById("event-selector-menu"); + eventOptions.replaceChildren(); + eventSelectorMenu.replaceChildren(); + options.forEach((option) => { + const optionElement = document.createElement("option"); + optionElement.appendChild(document.createTextNode(option)); + eventOptions.appendChild(optionElement); + + const optionElementMenu = document.createElement("div"); + optionElementMenu.className = "event-option"; + optionElementMenu.appendChild(document.createTextNode(option)); + eventSelectorMenu.appendChild(optionElementMenu); + optionElementMenu.addEventListener("click", () => { + renderEvent(option); + eventSelectorMenu.style.display = "none"; + }); + }); }); reader.readAsText(file); break; @@ -118,42 +109,12 @@ document event.preventDefault(); const eventNum = document.getElementById("event-number").value; - const selectedObjectTypes = ["edm4hep::MCParticle"]; - const objects = loadObjects(jsonData, eventNum, selectedObjectTypes); - - copyObject(objects, loadedObjects); - copyObject(objects, currentObjects); - - const length = Object.values(loadedObjects) - .map((obj) => obj.collection.length) - .reduce((a, b) => a + b, 0); - - if (length === 0) { - errorMsg("Provided file does not contain any MC particle tree!"); - return; - } - for (const eventNum in jsonData) { - delete jsonData[eventNum]; - } - start(currentObjects, visibleObjects); hideInputModal(); - window.scroll((canvas.width - window.innerWidth) / 2, 0); + showEventSwitcher(eventNum); + loadSelectedEvent(); - for (const tool of manipulationTools) { - tool.style.display = "flex"; - } - - const mcObjects = loadedObjects["edm4hep::MCParticle"].collection; - mcObjects.forEach((mcObject) => { - genStatus.add(mcObject.generatorStatus); - }); - genStatus.setCheckBoxes(); - renderRangeParameters(filters, parametersRange); const width = getWidthFilterContent(); filter.style.width = width; - - renderGenSim(bits, genStatus, filters); - const pdgToggle = new PdgToggle("show-pdg"); pdgToggle.init(() => { pdgToggle.toggle(currentObjects, () => { @@ -162,4 +123,12 @@ document }); }); -export { canvas, ctx, loadedObjects, currentObjects, visibleObjects }; +export { + canvas, + ctx, + loadedObjects, + currentObjects, + visibleObjects, + jsonData, + selectedObjectTypes, +}; diff --git a/js/menu/event-number.js b/js/menu/event-number.js new file mode 100644 index 00000000..273a817e --- /dev/null +++ b/js/menu/event-number.js @@ -0,0 +1,135 @@ +import { loadObjects } from "../types/load.js"; +import { copyObject } from "../lib/copy.js"; +import { + loadedObjects, + currentObjects, + visibleObjects, + canvas, + ctx, +} from "../main.js"; +import { getVisible } from "../events.js"; +import { + bits, + genStatus, + renderRangeParameters, + parametersRange, + renderGenSim, +} from "./filter/filter.js"; +import { drawAll } from "../draw.js"; +import { objectTypes } from "../types/objects.js"; +import { jsonData, selectedObjectTypes } from "../main.js"; + +const filters = document.getElementById("filters"); +const eventSwitcher = document.getElementById("event-switcher"); +const eventNumber = document.getElementById("selected-event"); +const previousEvent = document.getElementById("previous-event"); +const nextEvent = document.getElementById("next-event"); +const manipulationTools = document.getElementsByClassName("manipulation-tool"); + +let currentEvent; + +const scrollLocation = {}; + +function updateEventNumber(newEventNumber) { + if (eventNumber.firstChild) { + eventNumber.removeChild(eventNumber.firstChild); + } + eventNumber.appendChild(document.createTextNode(`Event: ${newEventNumber}`)); +} + +function start(currentObjects, visibleObjects) { + for (const [key, value] of Object.entries(currentObjects)) { + const classType = objectTypes[key]; + const collection = value.collection; + classType.setup(collection, canvas); + } + + drawAll(ctx, currentObjects); + + getVisible(currentObjects, visibleObjects); +} + +export function renderEvent(eventNumber) { + const data = jsonData.data[`Event ${eventNumber}`]; + + scrollLocation[currentEvent] = { + x: window.scrollX, + y: window.scrollY, + }; + + if (data === undefined) { + return; + } else { + currentEvent = eventNumber; + loadSelectedEvent(jsonData, selectedObjectTypes.types, eventNumber); + updateEventNumber(eventNumber); + } +} + +export function showEventSwitcher(initialValue) { + eventSwitcher.style.display = "flex"; + updateEventNumber(initialValue); + currentEvent = initialValue; +} + +export function loadSelectedEvent() { + const objects = loadObjects( + jsonData.data, + currentEvent, + selectedObjectTypes.types + ); + + copyObject(objects, loadedObjects); + copyObject(objects, currentObjects); + + const length = Object.values(loadedObjects) + .map((obj) => obj.collection.length) + .reduce((a, b) => a + b, 0); + + if (length === 0) { + errorMsg("Event does not contain any objects!"); + return; + } + + start(currentObjects, visibleObjects); + if (scrollLocation[currentEvent] === undefined) { + scrollLocation[currentEvent] = { + x: (canvas.width - window.innerWidth) / 2, + y: 0, + }; + } + + window.scroll(scrollLocation[currentEvent].x, scrollLocation[currentEvent].y); + + for (const tool of manipulationTools) { + tool.style.display = "flex"; + } + + const mcObjects = loadedObjects["edm4hep::MCParticle"].collection; + genStatus.reset(); + mcObjects.forEach((mcObject) => { + genStatus.add(mcObject.generatorStatus); + }); + genStatus.setCheckBoxes(); + filters.replaceChildren(); + + renderRangeParameters(parametersRange); + renderGenSim(bits, genStatus); +} + +previousEvent.addEventListener("click", () => { + const newEventNum = `${parseInt(currentEvent) - 1}`; + renderEvent(newEventNum); +}); +nextEvent.addEventListener("click", () => { + const newEventNum = `${parseInt(currentEvent) + 1}`; + renderEvent(newEventNum); +}); +eventNumber.addEventListener("click", () => { + const eventSelectorMenu = document.getElementById("event-selector-menu"); + if (eventSelectorMenu.style.display === "flex") { + eventSelectorMenu.style.display = "none"; + } else { + eventSelectorMenu.style.display = "flex"; + } +}); diff --git a/js/menu/filter/builders.js b/js/menu/filter/builders.js index 118da614..6f9cf82b 100644 --- a/js/menu/filter/builders.js +++ b/js/menu/filter/builders.js @@ -35,6 +35,10 @@ export class CheckboxBuilder { container.appendChild(section); this.checkBoxes.forEach((checkbox) => checkbox.render(options)); } + + reset() { + this.uniqueValues = new Set(); + } } export class BitFieldBuilder extends CheckboxBuilder { diff --git a/js/menu/filter/filter.js b/js/menu/filter/filter.js index febe3cd1..a26e577c 100644 --- a/js/menu/filter/filter.js +++ b/js/menu/filter/filter.js @@ -36,7 +36,7 @@ filterButton.addEventListener("click", () => { } }); -export function renderRangeParameters(container, rangeParameters) { +export function renderRangeParameters(rangeParameters) { const rangeFilters = document.createElement("div"); rangeFilters.id = "range-filters"; rangeFilters.style.display = "grid"; @@ -51,7 +51,7 @@ export function renderRangeParameters(container, rangeParameters) { parameter.max = undefined; parameter.render(rangeFilters); }); - container.appendChild(rangeFilters); + filters.appendChild(rangeFilters); } export function getWidthFilterContent() { @@ -63,7 +63,7 @@ export function getWidthFilterContent() { return `${width}px`; } -export function renderGenSim(sim, gen, container) { +export function renderGenSim(sim, gen) { const div = document.createElement("div"); div.style.display = "flex"; div.style.flexDirection = "column"; @@ -71,7 +71,7 @@ export function renderGenSim(sim, gen, container) { div.style.alignItems = "start"; sim.render(div); gen.render(div); - container.appendChild(div); + filters.appendChild(div); } let parametersRange = units.sort((a, b) => @@ -129,8 +129,8 @@ function removeFilter(loadedObjects, currentObjects, visibleObjects) { filters.innerHTML = ""; - renderRangeParameters(filters, parametersRange); - renderGenSim(bits, genStatus, filters); + renderRangeParameters(parametersRange); + renderGenSim(bits, genStatus); } apply.addEventListener("click", () => diff --git a/js/menu/show-pdg.js b/js/menu/show-pdg.js index eb577d5a..a5333e59 100644 --- a/js/menu/show-pdg.js +++ b/js/menu/show-pdg.js @@ -1,4 +1,5 @@ import { Toggle } from "./toggle.js"; +import { selectedObjectTypes } from "../main.js"; export class PdgToggle extends Toggle { constructor(id) { @@ -6,18 +7,20 @@ export class PdgToggle extends Toggle { } toggle(currentObjects, redraw) { - const validParticles = ["edm4hep::MCParticle"]; + const validObjects = selectedObjectTypes.types; if (this.isSliderActive) { - for (const objectType of validParticles) { + for (const objectType of validObjects) { const collection = currentObjects[objectType].collection; + if (collection[0].PDG === undefined) return; for (const object of collection) { object.updateTexImg(`${object.PDG}`); } } } else { - for (const objectType of validParticles) { + for (const objectType of validObjects) { const collection = currentObjects[objectType].collection; + if (collection[0].PDG === undefined) return; for (const object of collection) { object.updateTexImg(`${object.name}`); }