-
¡Welcome to
+ Welcome to
dmX
- !
+
diff --git a/js/draw.js b/js/draw.js
index 09aa8a6b..6bfdd8ae 100644
--- a/js/draw.js
+++ b/js/draw.js
@@ -1,4 +1,8 @@
-export function drawAll(ctx, parentLinks, childrenLinks, infoBoxes) {
+import { canvas, ctx } from "./main.js";
+
+export function drawAll(ctx, currentParticles) {
+ const { parentLinks, childrenLinks, infoBoxes } = currentParticles;
+
ctx.clearRect(0, 0, canvas.width, canvas.height);
// console.time("drawParentLinks");
for (const link of parentLinks) {
@@ -16,3 +20,32 @@ export function drawAll(ctx, parentLinks, childrenLinks, infoBoxes) {
}
// console.timeEnd("drawBoxes");
}
+
+export function drawVisible(currentParticles, visibleParticles) {
+ const {
+ infoBoxes: visibleBoxes,
+ parentLinks: visibleParentLinks,
+ childrenLinks: visibleChildrenLinks,
+ } = visibleParticles;
+
+ const { parentLinks, childrenLinks, infoBoxes } = currentParticles;
+
+ const boundigClientRect = canvas.getBoundingClientRect();
+ ctx.clearRect(
+ 0 - boundigClientRect.x,
+ 0 - boundigClientRect.y,
+ window.innerWidth,
+ window.innerHeight
+ );
+ for (const linkId of visibleParentLinks) {
+ if (parentLinks[linkId] !== undefined)
+ parentLinks[linkId].draw(ctx, infoBoxes);
+ }
+ for (const linkId of visibleChildrenLinks) {
+ if (childrenLinks[linkId] !== undefined)
+ childrenLinks[linkId].draw(ctx, infoBoxes);
+ }
+ for (const boxId of visibleBoxes) {
+ infoBoxes[boxId].draw(ctx);
+ }
+}
diff --git a/js/events.js b/js/events.js
new file mode 100644
index 00000000..193d71e9
--- /dev/null
+++ b/js/events.js
@@ -0,0 +1,147 @@
+import { canvas, ctx } from "./main.js";
+import { drawAll, drawVisible } from "./draw.js";
+
+const mouseDown = function (
+ event,
+ currentParticles,
+ visibleParticles,
+ dragTools
+) {
+ event.preventDefault();
+ const boundigClientRect = canvas.getBoundingClientRect();
+ const mouseX = parseInt(event.clientX - boundigClientRect.x);
+ const mouseY = parseInt(event.clientY - boundigClientRect.y);
+
+ dragTools.prevMouseX = mouseX;
+ dragTools.prevMouseY = mouseY;
+
+ const infoBoxes = currentParticles.infoBoxes;
+ const visibleBoxes = visibleParticles.infoBoxes;
+ for (let i = visibleBoxes.length - 1; i >= 0; i--) {
+ if (infoBoxes[visibleBoxes[i]].isHere(mouseX, mouseY)) {
+ dragTools.draggedInfoBox = visibleBoxes[i];
+ dragTools.isDragging = true;
+ return;
+ }
+ }
+};
+
+const mouseUp = function (event, particlesHandler, dragTools) {
+ if (!dragTools.isDragging) {
+ return;
+ }
+
+ event.preventDefault();
+ dragTools.isDragging = false;
+
+ // console.time("drawAll");
+ drawAll(ctx, particlesHandler);
+ // console.timeEnd("drawAll");
+};
+
+const mouseOut = function (event, dragTools) {
+ if (!dragTools.isDragging) {
+ return;
+ }
+
+ event.preventDefault();
+ dragTools.isDragging = false;
+};
+
+const mouseMove = function (
+ event,
+ currentParticles,
+ visibleParticles,
+ dragTools
+) {
+ if (!dragTools.isDragging) {
+ return;
+ }
+ event.preventDefault();
+
+ const boundigClientRect = canvas.getBoundingClientRect();
+ const mouseX = parseInt(event.clientX - boundigClientRect.x);
+ const mouseY = parseInt(event.clientY - boundigClientRect.y);
+
+ const dx = mouseX - dragTools.prevMouseX;
+ const dy = mouseY - dragTools.prevMouseY;
+
+ const infoBox = currentParticles.infoBoxes[dragTools.draggedInfoBox];
+ infoBox.x += dx;
+ infoBox.y += dy;
+
+ // console.time("drawVisible");
+ drawVisible(currentParticles, visibleParticles);
+ // console.timeEnd("drawVisible");
+
+ dragTools.prevMouseX = mouseX;
+ dragTools.prevMouseY = mouseY;
+};
+
+const getVisible = function (currentParticles, visibleParticles) {
+ const boundigClientRect = canvas.getBoundingClientRect();
+
+ const { infoBoxes, parentLinks, childrenLinks } = currentParticles;
+
+ const visibleBoxes = [];
+ const visibleParentLinks = [];
+ const visibleChildrenLinks = [];
+
+ for (const box of infoBoxes) {
+ if (box === null) continue;
+ if (
+ box.isVisible(
+ 0 - boundigClientRect.x,
+ 0 - boundigClientRect.y,
+ window.innerWidth,
+ window.innerHeight
+ )
+ ) {
+ visibleBoxes.push(box.id);
+ }
+ }
+
+ for (const link of parentLinks) {
+ if (
+ link.isVisible(
+ 0 - boundigClientRect.x,
+ 0 - boundigClientRect.y,
+ window.innerWidth,
+ window.innerHeight,
+ infoBoxes
+ )
+ ) {
+ visibleParentLinks.push(link.id);
+ }
+ }
+
+ for (const link of childrenLinks) {
+ if (
+ link.isVisible(
+ 0 - boundigClientRect.x,
+ 0 - boundigClientRect.y,
+ window.innerWidth,
+ window.innerHeight,
+ infoBoxes
+ )
+ ) {
+ visibleChildrenLinks.push(link.id);
+ }
+ }
+
+ /*
+ console.log("Visible boxes: ", visibleBoxes);
+ console.log("Visible parentLinks: ", visibleParentLinks);
+ console.log("Visible childrenLinks: ", visibleChildrenLinks);
+ */
+
+ visibleParticles.infoBoxes = visibleBoxes;
+ visibleParticles.parentLinks = visibleParentLinks;
+ visibleParticles.childrenLinks = visibleChildrenLinks;
+};
+
+const onScroll = function (currentParticles, visibleParticles) {
+ getVisible(currentParticles, visibleParticles);
+};
+
+export { mouseDown, mouseUp, mouseOut, mouseMove, getVisible, onScroll };
diff --git a/js/main.js b/js/main.js
index 2f342da6..3187794d 100644
--- a/js/main.js
+++ b/js/main.js
@@ -1,200 +1,63 @@
import { errorMsg, loadMCParticles } 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";
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 draggedInfoBox = -1;
-let isDragging = false;
-let prevMouseX = 0;
-let prevMouseY = 0;
-
let jsonData = {};
-const infoBoxes = [];
-const parentLinks = [];
-const childrenLinks = [];
-let visibleBoxes = [];
-let visibleParentLinks = [];
-let visibleChildrenLinks = [];
-
-const mouseDown = function (event) {
- event.preventDefault();
-
- const boundigClientRect = canvas.getBoundingClientRect();
- const mouseX = parseInt(event.clientX - boundigClientRect.x);
- const mouseY = parseInt(event.clientY - boundigClientRect.y);
-
- prevMouseX = mouseX;
- prevMouseY = mouseY;
-
- for (let i = visibleBoxes.length - 1; i >= 0; i--) {
- if (infoBoxes[visibleBoxes[i]].isHere(mouseX, mouseY)) {
- draggedInfoBox = visibleBoxes[i];
- isDragging = true;
- return;
- }
- }
-};
-
-const mouseUp = function (event) {
- if (!isDragging) {
- return;
- }
- event.preventDefault();
- isDragging = false;
-
- // console.time("drawAll");
- drawAll(ctx, parentLinks, childrenLinks, infoBoxes);
- // console.timeEnd("drawAll");
+const dragTools = {
+ draggedInfoBox: -1,
+ isDragging: false,
+ prevMouseX: 0,
+ prevMouseY: 0,
};
-const mouseOut = function (event) {
- if (!isDragging) {
- return;
- }
-
- event.preventDefault();
- isDragging = false;
+const particlesHandler = {
+ infoBoxes: [],
+ parentLinks: [],
+ childrenLinks: [],
};
-const mouseMove = function (event) {
- if (!isDragging) {
- return;
- }
- event.preventDefault();
-
- const boundigClientRect = canvas.getBoundingClientRect();
- const mouseX = parseInt(event.clientX - boundigClientRect.x);
- const mouseY = parseInt(event.clientY - boundigClientRect.y);
-
- const dx = mouseX - prevMouseX;
- const dy = mouseY - prevMouseY;
-
- const infoBox = infoBoxes[draggedInfoBox];
- infoBox.x += dx;
- infoBox.y += dy;
-
- // console.time("drawVisible");
- drawVisible(visibleParentLinks, visibleChildrenLinks, visibleBoxes);
- // console.timeEnd("drawVisible");
-
- prevMouseX = mouseX;
- prevMouseY = mouseY;
+const currentParticles = {
+ infoBoxes: [],
+ parentLinks: [],
+ childrenLinks: [],
};
-const onScroll = function () {
- getVisible();
-};
-
-const getVisible = function () {
- const boundigClientRect = canvas.getBoundingClientRect();
-
- visibleBoxes = [];
- visibleParentLinks = [];
- visibleChildrenLinks = [];
-
- for (const box of infoBoxes) {
- if (
- box.isVisible(
- 0 - boundigClientRect.x,
- 0 - boundigClientRect.y,
- window.innerWidth,
- window.innerHeight
- )
- ) {
- visibleBoxes.push(box.id);
- }
- }
-
- for (const boxId of visibleBoxes) {
- for (const linkId of infoBoxes[boxId].parentLinks) {
- visibleParentLinks.push(linkId);
- }
- for (const parentBoxId of infoBoxes[boxId].parents) {
- for (const linkId of infoBoxes[parentBoxId].childrenLinks) {
- visibleChildrenLinks.push(linkId);
- }
- }
- }
- for (const link of parentLinks) {
- if (
- link.isVisible(
- 0 - boundigClientRect.x,
- 0 - boundigClientRect.y,
- window.innerWidth,
- window.innerHeight,
- infoBoxes
- )
- ) {
- visibleParentLinks.push(link.id);
- }
- }
-
- for (const boxId of visibleBoxes) {
- for (const linkId of infoBoxes[boxId].childrenLinks) {
- visibleChildrenLinks.push(linkId);
- }
- for (const childrenBoxId of infoBoxes[boxId].children) {
- for (const linkId of infoBoxes[childrenBoxId].parentLinks) {
- visibleParentLinks.push(linkId);
- }
- }
- }
- for (const link of childrenLinks) {
- if (
- link.isVisible(
- 0 - boundigClientRect.x,
- 0 - boundigClientRect.y,
- window.innerWidth,
- window.innerHeight,
- infoBoxes
- )
- ) {
- visibleChildrenLinks.push(link.id);
- }
- }
-
- visibleParentLinks = [...new Set(visibleParentLinks)];
- visibleChildrenLinks = [...new Set(visibleChildrenLinks)];
-
- /*
- console.log("Visible boxes: ", visibleBoxes);
- console.log("Visible parentLinks: ", visibleParentLinks);
- console.log("Visible childrenLinks: ", visibleChildrenLinks);
- */
+const visibleParticles = {
+ infoBoxes: [],
+ parentLinks: [],
+ childrenLinks: [],
};
-const drawVisible = function (
- visibleParentLinks,
- visibleChildrenLinks,
- visibleBoxes
-) {
- const boundigClientRect = canvas.getBoundingClientRect();
- ctx.clearRect(
- 0 - boundigClientRect.x,
- 0 - boundigClientRect.y,
- window.innerWidth,
- window.innerHeight
- );
- for (const linkId of visibleParentLinks) {
- parentLinks[linkId].draw(ctx, infoBoxes);
- }
- for (const linkId of visibleChildrenLinks) {
- childrenLinks[linkId].draw(ctx, infoBoxes);
- }
- for (const boxId of visibleBoxes) {
- infoBoxes[boxId].draw(ctx);
- }
-};
+function start(particlesHandler, visibleParticles) {
+ const { infoBoxes } = particlesHandler;
-function start() {
if (!infoBoxes) {
return;
}
@@ -259,16 +122,26 @@ function start() {
}
}
- drawAll(ctx, parentLinks, childrenLinks, infoBoxes);
+ drawAll(ctx, particlesHandler);
- getVisible();
+ getVisible(particlesHandler, visibleParticles);
}
-canvas.onmousedown = mouseDown;
-canvas.onmouseup = mouseUp;
-canvas.onmouseout = mouseOut;
-canvas.onmousemove = mouseMove;
-window.onscroll = onScroll;
+canvas.onmousedown = (event) => {
+ mouseDown(event, currentParticles, visibleParticles, dragTools);
+};
+canvas.onmouseup = (event) => {
+ mouseUp(event, currentParticles, dragTools);
+};
+canvas.onmouseout = (event) => {
+ mouseOut(event, dragTools);
+};
+canvas.onmousemove = (event) => {
+ mouseMove(event, currentParticles, visibleParticles, dragTools);
+};
+window.onscroll = () => {
+ onScroll(currentParticles, visibleParticles);
+};
/*
function showInputModal() {
@@ -313,15 +186,21 @@ document
.addEventListener("click", (event) => {
event.preventDefault();
const eventNum = document.getElementById("event-number").value;
- loadMCParticles(jsonData, eventNum, infoBoxes, parentLinks, childrenLinks);
- if (infoBoxes.length === 0) {
+
+ loadMCParticles(jsonData, eventNum, particlesHandler);
+
+ currentParticles.infoBoxes = particlesHandler.infoBoxes;
+ currentParticles.parentLinks = particlesHandler.parentLinks;
+ currentParticles.childrenLinks = particlesHandler.childrenLinks;
+
+ if (particlesHandler.infoBoxes.length === 0) {
errorMsg("Provided file does not contain any MC particle tree!");
return;
}
for (const eventNum in jsonData) {
delete jsonData[eventNum];
}
- start();
+ start(currentParticles, visibleParticles);
hideInputModal();
window.scroll((canvas.width - window.innerWidth) / 2, 0);
@@ -329,12 +208,24 @@ document
tool.style.display = "flex";
}
+ const { infoBoxes } = currentParticles;
+
+ infoBoxes.forEach((infoBox) => {
+ genStatus.add(infoBox.genStatus);
+ });
+ 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(infoBoxes, () => {
- drawAll(ctx, parentLinks, childrenLinks, infoBoxes);
+ drawAll(ctx, currentParticles);
});
});
});
-export { parentLinks, childrenLinks, infoBoxes, ctx };
+export { canvas, ctx, visibleParticles, particlesHandler, currentParticles };
diff --git a/js/menu/filter/builders.js b/js/menu/filter/builders.js
new file mode 100644
index 00000000..118da614
--- /dev/null
+++ b/js/menu/filter/builders.js
@@ -0,0 +1,52 @@
+import { ValueCheckBox, BitfieldCheckbox } from "./parameters.js";
+
+export class CheckboxBuilder {
+ constructor(name, fullName) {
+ this.uniqueValues = new Set();
+ this.checkBoxes = [];
+ this.name = name;
+ this.fullName = fullName;
+ }
+
+ add(val) {
+ this.uniqueValues.add(val);
+ }
+
+ setCheckBoxes() {
+ this.checkBoxes = Array.from(this.uniqueValues).map(
+ (option) => new ValueCheckBox(this.name, option)
+ );
+ this.checkBoxes.sort((a, b) => a.value - b.value);
+ }
+
+ render(container) {
+ const section = document.createElement("div");
+ section.style.maxWidth = "fit-content";
+ this.checkBoxes.forEach((checkbox) => (checkbox.checked = false));
+ const title = document.createElement("p");
+ title.style.fontWeight = "bold";
+ title.textContent = this.fullName;
+ section.appendChild(title);
+ const options = document.createElement("div");
+ options.style.display = "flex";
+ options.style.flexDirection = "row";
+ options.style.flexWrap = "wrap";
+ section.appendChild(options);
+ container.appendChild(section);
+ this.checkBoxes.forEach((checkbox) => checkbox.render(options));
+ }
+}
+
+export class BitFieldBuilder extends CheckboxBuilder {
+ constructor(name, fullName, dictionary) {
+ super(name, fullName);
+ this.dictionary = dictionary;
+ }
+
+ setCheckBoxes() {
+ this.checkBoxes = Object.entries(this.dictionary).map(
+ ([key, value]) => new BitfieldCheckbox(this.name, key, value)
+ );
+ this.checkBoxes.sort((a, b) => a.value - b.value);
+ }
+}
diff --git a/js/menu/filter/filter.js b/js/menu/filter/filter.js
index 93d9c08f..8399e8fb 100644
--- a/js/menu/filter/filter.js
+++ b/js/menu/filter/filter.js
@@ -1,13 +1,22 @@
-import { Link } from "../../objects.js";
import { drawAll } from "../../draw.js";
-import { parentLinks, childrenLinks, infoBoxes, ctx } from "../../main.js";
+import {
+ ctx,
+ particlesHandler,
+ currentParticles,
+ visibleParticles,
+} from "../../main.js";
+import { CheckboxBuilder, BitFieldBuilder } from "./builders.js";
import { Range, Checkbox, buildCriteriaFunction } from "./parameters.js";
import { reconnect } from "./reconnect.js";
+import { getVisible } from "../../events.js";
const filterButton = document.getElementById("filter-button");
const openFilter = document.getElementById("open-filter");
const closeFilter = document.getElementById("close-filter");
const filterContent = document.getElementById("filter-content");
+const filters = document.getElementById("filters");
+const apply = document.getElementById("filter-apply");
+const reset = document.getElementById("filter-reset");
let active = false;
@@ -25,54 +34,146 @@ filterButton.addEventListener("click", () => {
}
});
-const filters = document.getElementById("filters");
-const apply = document.getElementById("filter-apply");
-const reset = document.getElementById("filter-reset");
-
-let parametersRange = ["momentum", "vertex", "time", "mass", "charge"];
+export function renderRangeParameters(container, rangeParameters) {
+ const rangeFilters = document.createElement("div");
+ rangeFilters.id = "range-filters";
+ rangeFilters.style.display = "grid";
+ rangeFilters.style.width = "fit-content";
+ rangeFilters.style.columnGap = "10px";
+ rangeFilters.style.rowGap = "5px";
+ rangeFilters.style.alignItems = "center";
+ rangeFilters.style.gridTemplateColumns =
+ "fit-content(100%) fit-content(100%)";
+ rangeParameters.forEach((parameter) => {
+ parameter.min = undefined;
+ parameter.max = undefined;
+ parameter.render(rangeFilters);
+ });
+ container.appendChild(rangeFilters);
+}
+
+export function getWidthFilterContent() {
+ const filterContent = document.getElementById("filter-content");
+ filterContent.style.display = "flex";
+ const rangeFilters = document.getElementById("range-filters");
+ const width = rangeFilters.offsetWidth;
+ filterContent.style.display = "none";
+ return `${width}px`;
+}
+
+export function renderGenSim(sim, gen, container) {
+ const div = document.createElement("div");
+ div.style.display = "flex";
+ div.style.flexDirection = "column";
+ div.style.width = "fit-content";
+ div.style.alignItems = "start";
+ sim.render(div);
+ gen.render(div);
+ container.appendChild(div);
+}
+
+// TODO: Check in the future for other types of particles
+let parametersRange = [
+ {
+ property: "momentum",
+ unit: "GeV",
+ },
+ {
+ property: "mass",
+ unit: "GeV",
+ },
+ {
+ property: "charge",
+ unit: "e",
+ },
+ {
+ property: "vertex",
+ unit: "mm",
+ },
+ {
+ property: "time",
+ unit: "ns",
+ },
+];
+
+parametersRange = parametersRange.sort((a, b) =>
+ a.property.localeCompare(b.property)
+);
parametersRange = parametersRange.map((parameter) => new Range(parameter));
-parametersRange.forEach((parameter) => parameter.render(filters));
-
-let bitsCheckbox = [23, 24, 25, 26, 27, 28, 29, 30];
-
-bitsCheckbox = bitsCheckbox.map((bit) => new Checkbox("simStatus", bit));
-
-bitsCheckbox.forEach((checkbox) => checkbox.render(filters));
-
-apply.addEventListener("click", () => {
+const SimStatusBitFieldDisplayValues = {
+ 23: "Overlay",
+ 24: "Stopped",
+ 25: "LeftDetector",
+ 26: "DecayedInCalorimeter",
+ 27: "DecayedInTracker",
+ 28: "VertexIsNotEndpointOfParent",
+ 29: "Backscatter",
+ 30: "CreatedInSimulation",
+};
+
+const bits = new BitFieldBuilder(
+ "simStatus",
+ "Simulation status",
+ SimStatusBitFieldDisplayValues
+);
+bits.setCheckBoxes();
+
+const genStatus = new CheckboxBuilder("genStatus", "Generator status");
+
+function applyFilter(particlesHandler, currentParticles, visibleParticles) {
const rangeFunctions = Range.buildFilter(parametersRange);
- const checkboxFunctions = Checkbox.buildFilter(bitsCheckbox);
+ const checkboxFunctions = Checkbox.buildFilter(bits.checkBoxes);
+ const genStatusFunctions = Checkbox.buildFilter(genStatus.checkBoxes);
const criteriaFunction = buildCriteriaFunction(
rangeFunctions,
- checkboxFunctions
+ checkboxFunctions,
+ genStatusFunctions
);
const [newParentLinks, newChildrenLinks, filteredParticles] = reconnect(
criteriaFunction,
- parentLinks,
- childrenLinks,
- infoBoxes
+ particlesHandler
);
- drawAll(ctx, newParentLinks, newChildrenLinks, filteredParticles);
-});
+ currentParticles.parentLinks = newParentLinks;
+ currentParticles.childrenLinks = newChildrenLinks;
+ currentParticles.infoBoxes = filteredParticles;
+
+ drawAll(ctx, currentParticles);
+
+ getVisible(currentParticles, visibleParticles);
+}
+
+function removeFilter(particlesHandler, currentParticles, visibleParticles) {
+ currentParticles.parentLinks = particlesHandler.parentLinks;
+ currentParticles.childrenLinks = particlesHandler.childrenLinks;
+ currentParticles.infoBoxes = particlesHandler.infoBoxes;
-reset.addEventListener("click", () => {
- drawAll(ctx, parentLinks, childrenLinks, infoBoxes);
+ drawAll(ctx, currentParticles);
+
+ getVisible(currentParticles, visibleParticles);
filters.innerHTML = "";
- parametersRange.forEach((parameter) => {
- parameter.min = undefined;
- parameter.max = undefined;
- parameter.render(filters);
- });
+ renderRangeParameters(filters, parametersRange);
+ renderGenSim(bits, genStatus, filters);
+}
- bitsCheckbox.forEach((checkbox) => {
- checkbox.checked = false;
- checkbox.render(filters);
- });
+apply.addEventListener("click", () =>
+ applyFilter(particlesHandler, currentParticles, visibleParticles)
+);
+
+document.addEventListener("keydown", (event) => {
+ if (event.key === "Enter" && active) {
+ applyFilter(particlesHandler, currentParticles, visibleParticles);
+ }
});
+
+reset.addEventListener("click", () =>
+ removeFilter(particlesHandler, currentParticles, visibleParticles)
+);
+
+export { bits, genStatus, parametersRange };
diff --git a/js/menu/filter/parameters.js b/js/menu/filter/parameters.js
index 60f1d6d6..7555cbb0 100644
--- a/js/menu/filter/parameters.js
+++ b/js/menu/filter/parameters.js
@@ -15,39 +15,56 @@ class FilterParameter {
}
}
+function createNumberInput(placeholder) {
+ const input = document.createElement("input");
+ input.type = "number";
+ input.placeholder = placeholder;
+ input.style.width = "35px";
+
+ return input;
+}
+
export class Range extends FilterParameter {
min;
max;
- constructor(property) {
+ constructor({ property, unit }) {
super(property);
+ this.unit = unit;
}
render(container) {
const label = document.createElement("label");
- label.textContent = this.property;
- container.appendChild(label);
-
- const inputMin = document.createElement("input");
- inputMin.type = "number";
- inputMin.placeholder = "min";
- container.appendChild(inputMin);
+ label.textContent = `${this.property}`;
- const separator = document.createTextNode(" - ");
- container.appendChild(separator);
+ const inputMin = createNumberInput("min");
+ inputMin.addEventListener("input", (e) => {
+ this.min = e.target.value;
+ });
- const inputMax = document.createElement("input");
- inputMax.type = "number";
- inputMax.placeholder = "max";
- container.appendChild(inputMax);
+ const separator = document.createTextNode("-");
- inputMin.addEventListener("input", () => {
- this.min = inputMin.value;
+ const inputMax = createNumberInput("max");
+ inputMax.addEventListener("input", (e) => {
+ this.max = e.target.value;
});
- inputMax.addEventListener("input", () => {
- this.max = inputMax.value;
- });
+ const unitElement = document.createTextNode(`${this.unit}`);
+
+ const content = document.createElement("div");
+ content.appendChild(inputMin);
+ content.appendChild(separator);
+ content.appendChild(inputMax);
+ content.appendChild(unitElement);
+ content.style.display = "grid";
+ content.style.gridAutoFlow = "column";
+ content.style.columnGap = "5px";
+ content.style.display = "flex";
+ content.style.flexDirection = "row";
+ content.style.justifyContent = "flex-start";
+
+ container.appendChild(label);
+ container.appendChild(content);
}
buildCondition() {
@@ -85,9 +102,14 @@ export class Range extends FilterParameter {
export class Checkbox extends FilterParameter {
value;
- constructor(property, value) {
+ constructor(property, value, displayValue = null) {
super(property);
this.value = value;
+ if (displayValue) {
+ this.displayValue = displayValue;
+ } else {
+ this.displayValue = value;
+ }
}
render(container) {
@@ -95,7 +117,7 @@ export class Checkbox extends FilterParameter {
container.appendChild(div);
const label = document.createElement("label");
- label.textContent = `${this.property}: ${this.value}`;
+ label.textContent = this.displayValue;
div.appendChild(label);
const input = document.createElement("input");
@@ -103,8 +125,11 @@ export class Checkbox extends FilterParameter {
div.appendChild(input);
div.style.display = "flex";
+ div.style.flexDirection = "row";
div.style.alignItems = "center";
- div.style.justifyContent = "space-between";
+ div.style.backgroundColor = "#dddddd";
+ div.style.borderRadius = "5px";
+ div.style.margin = "3px";
input.addEventListener("change", () => {
this.checked = input.checked;
@@ -133,6 +158,55 @@ export class Checkbox extends FilterParameter {
}
}
+export class ValueCheckBox extends Checkbox {
+ // Classic checkbox
+ constructor(property, value, displayValue) {
+ super(property, value, displayValue);
+ }
+}
+
+export class BitfieldCheckbox extends Checkbox {
+ // Bit manipulation EDM4hep
+ constructor(property, value, displayValue) {
+ super(property, value, displayValue);
+ }
+
+ buildCondition() {
+ if (!this.checked) return null;
+
+ return (particle) =>
+ (parseInt(particle[this.property]) & (1 << parseInt(this.value))) !== 0;
+ }
+
+ render(container) {
+ const div = document.createElement("div");
+ container.appendChild(div);
+
+ const input = document.createElement("input");
+ input.type = "checkbox";
+ div.appendChild(input);
+
+ const label = document.createElement("label");
+ label.textContent = this.displayValue;
+ div.appendChild(label);
+
+ div.style.display = "flex";
+ div.style.flexDirection = "row";
+ div.style.alignItems = "center";
+ div.style.backgroundColor = "#dddddd";
+ div.style.borderRadius = "5px";
+ div.style.margin = "3px";
+
+ input.addEventListener("change", () => {
+ this.checked = input.checked;
+ });
+ }
+
+ static getDisplayValue(dictionary, option) {
+ return dictionary[option] ?? option;
+ }
+}
+
export function buildCriteriaFunction(...functions) {
const filterFunctions = functions.filter((fn) => typeof fn === "function");
diff --git a/js/menu/filter/reconnect.js b/js/menu/filter/reconnect.js
index 97f6c35e..c60b2f1f 100644
--- a/js/menu/filter/reconnect.js
+++ b/js/menu/filter/reconnect.js
@@ -1,18 +1,13 @@
import { Link } from "../../objects.js";
-export function reconnect(
- criteriaFunction,
- parentLinks,
- childrenLinks,
- particles
-) {
+export function reconnect(criteriaFunction, particlesHandler) {
+ const { parentLinks, childrenLinks, infoBoxes } = particlesHandler;
+
const newParentLinks = [];
const newChildrenLinks = [];
const filteredParticles = [];
- for (const particle of particles) {
- if (!particle) continue;
-
+ for (const particle of infoBoxes) {
if (!criteriaFunction(particle)) {
filteredParticles.push(null);
@@ -20,13 +15,13 @@ export function reconnect(
const childrenParticles = [];
for (const parent of particle.parents) {
- if (criteriaFunction(particles[parent])) {
+ if (criteriaFunction(infoBoxes[parent])) {
parentParticles.push(parent);
}
}
for (const child of particle.children) {
- if (criteriaFunction(particles[child])) {
+ if (criteriaFunction(infoBoxes[child])) {
childrenParticles.push(child);
}
}
@@ -48,19 +43,30 @@ export function reconnect(
for (const parentLinkId of particle.parentLinks) {
const parentLink = parentLinks[parentLinkId];
- if (!parentLink) continue;
- const parent = particles[parentLink.from];
+ const parent = infoBoxes[parentLink.from];
if (criteriaFunction(parent)) {
- newParentLinks.push(parentLink);
+ const parentLinkCopy = new Link(
+ newParentLinks.length,
+ parentLink.from,
+ parentLink.to
+ );
+ parentLinkCopy.xShift = 3;
+ newParentLinks.push(parentLinkCopy);
}
}
for (const childrenLinkId of particle.childrenLinks) {
const childrenLink = childrenLinks[childrenLinkId];
- if (!childrenLink) continue;
- const child = particles[childrenLink.to];
+ const child = infoBoxes[childrenLink.to];
if (criteriaFunction(child)) {
- newChildrenLinks.push(childrenLink);
+ const childrenLinkCopy = new Link(
+ newChildrenLinks.length,
+ childrenLink.from,
+ childrenLink.to
+ );
+ childrenLinkCopy.color = "#0A0";
+ childrenLinkCopy.xShift = -3;
+ newChildrenLinks.push(childrenLinkCopy);
}
}
}
diff --git a/js/objects.js b/js/objects.js
index c9bef14e..7249e2b9 100644
--- a/js/objects.js
+++ b/js/objects.js
@@ -159,15 +159,6 @@ export class Link {
const boxFrom = infoBoxes[this.from];
const boxTo = infoBoxes[this.to];
- if (
- boxFrom === null ||
- boxTo === null ||
- boxFrom === undefined ||
- boxTo === undefined
- ) {
- return;
- }
-
const fromX = boxFrom.x + boxFrom.width / 2;
const fromY = boxFrom.y + boxFrom.height;
const toX = boxTo.x + boxTo.width / 2;
diff --git a/js/tools.js b/js/tools.js
index 99e40a3a..680e3dc9 100644
--- a/js/tools.js
+++ b/js/tools.js
@@ -15,13 +15,9 @@ export function errorMsg(msg) {
msgDiv.innerHTML = "
ERROR: " + msg + "
";
}
-export function loadMCParticles(
- jsonData,
- eventNum,
- infoBoxes,
- parentLinks,
- childrenLinks
-) {
+export function loadMCParticles(jsonData, eventNum, particlesHandler) {
+ const { infoBoxes, parentLinks, childrenLinks } = particlesHandler;
+
const eventData = jsonData["Event " + eventNum];
try {
const mcParticles = Object.values(eventData).find(
diff --git a/test/filter.test.js b/test/filter.test.js
index 01e08c5f..a4046fc3 100644
--- a/test/filter.test.js
+++ b/test/filter.test.js
@@ -8,7 +8,7 @@ import {
const parentLinks = [];
const childrenLinks = [];
-const particles = [];
+const infoBoxes = [];
beforeAll(() => {
for (let i = 0; i < 5; i++) {
@@ -17,7 +17,7 @@ beforeAll(() => {
particle.charge = i;
particle.mass = i * 10;
particle.simStatus = i + 23;
- particles.push(particle);
+ infoBoxes.push(particle);
}
parentLinks.push(new Link(0, 0, 1));
@@ -32,31 +32,40 @@ beforeAll(() => {
childrenLinks.push(new Link(3, 2, 4));
childrenLinks.push(new Link(4, 3, 4));
- particles[0].children = [1, 2];
- particles[0].childrenLinks = [0, 1];
+ infoBoxes[0].children = [1, 2];
+ infoBoxes[0].childrenLinks = [0, 1];
- particles[1].parents = [0];
- particles[1].children = [3];
- particles[1].parentLinks = [0];
- particles[1].childrenLinks = [2];
+ infoBoxes[1].parents = [0];
+ infoBoxes[1].children = [3];
+ infoBoxes[1].parentLinks = [0];
+ infoBoxes[1].childrenLinks = [2];
- particles[2].parents = [0];
- particles[2].children = [4];
- particles[2].parentLinks = [1];
- particles[2].childrenLinks = [3];
+ infoBoxes[2].parents = [0];
+ infoBoxes[2].children = [4];
+ infoBoxes[2].parentLinks = [1];
+ infoBoxes[2].childrenLinks = [3];
- particles[3].parents = [1];
- particles[3].children = [4];
- particles[3].parentLinks = [3];
- particles[3].childrenLinks = [4];
+ infoBoxes[3].parents = [1];
+ infoBoxes[3].children = [4];
+ infoBoxes[3].parentLinks = [3];
+ infoBoxes[3].childrenLinks = [4];
- particles[4].parents = [2, 3];
- particles[4].parentLinks = [2, 4];
+ infoBoxes[4].parents = [2, 3];
+ infoBoxes[4].parentLinks = [2, 4];
});
+const particlesHandler = {
+ parentLinks,
+ childrenLinks,
+ infoBoxes,
+};
+
describe("filter by ranges", () => {
it("filter by a single range parameter", () => {
- const momentum = new Range("momentum");
+ const momentum = new Range({
+ property: "momentum",
+ unit: "GeV",
+ });
momentum.min = 300;
momentum.max = 1000;
const rangeFilters = Range.buildFilter([momentum]);
@@ -64,22 +73,26 @@ describe("filter by ranges", () => {
const [newParentLinks, newChildrenLinks, filteredParticles] = reconnect(
criteriaFunction,
- parentLinks,
- childrenLinks,
- particles
+ particlesHandler
);
- expect(newChildrenLinks.map((link) => link.id)).toEqual([4]);
- expect(newParentLinks.map((link) => link.id)).toEqual([4]);
+ expect(newChildrenLinks.map((link) => link.id)).toEqual([0]);
+ expect(newParentLinks.map((link) => link.id)).toEqual([0]);
expect(
filteredParticles.filter((p) => p).map((particle) => particle.id)
).toEqual([3, 4]);
});
it("filter by a combination of ranges", () => {
- const charge = new Range("charge");
+ const charge = new Range({
+ property: "charge",
+ unit: "e",
+ });
charge.min = 3;
- const mass = new Range("mass");
+ const mass = new Range({
+ property: "mass",
+ unit: "GeV",
+ });
mass.min = 20;
mass.max = 40;
const rangeFilters = Range.buildFilter([mass, charge]);
@@ -87,13 +100,11 @@ describe("filter by ranges", () => {
const [newParentLinks, newChildrenLinks, filteredParticles] = reconnect(
criteriaFunction,
- parentLinks,
- childrenLinks,
- particles
+ particlesHandler
);
- expect(newChildrenLinks.map((link) => link.id)).toEqual([4]);
- expect(newParentLinks.map((link) => link.id)).toEqual([4]);
+ expect(newChildrenLinks.map((link) => link.id)).toEqual([0]);
+ expect(newParentLinks.map((link) => link.id)).toEqual([0]);
expect(
filteredParticles.filter((p) => p).map((particle) => particle.id)
).toEqual([3, 4]);
@@ -109,9 +120,7 @@ describe("filter by checkboxes", () => {
const [newParentLinks, newChildrenLinks, filteredParticles] = reconnect(
criteriaFunction,
- parentLinks,
- childrenLinks,
- particles
+ particlesHandler
);
expect(newChildrenLinks.map((link) => link.id)).toEqual([]);
@@ -137,13 +146,11 @@ describe("filter by checkboxes", () => {
const [newParentLinks, newChildrenLinks, filteredParticles] = reconnect(
criteriaFunction,
- parentLinks,
- childrenLinks,
- particles
+ particlesHandler
);
- expect(newChildrenLinks.map((link) => link.id)).toEqual([0, 1, 4]);
- expect(newParentLinks.map((link) => link.id)).toEqual([0, 1, 4]);
+ expect(newChildrenLinks.map((link) => link.id)).toEqual([0, 1, 2]);
+ expect(newParentLinks.map((link) => link.id)).toEqual([0, 1, 2]);
expect(
filteredParticles.filter((p) => p).map((particle) => particle.id)
).toEqual([0, 3, 4]);
@@ -152,7 +159,10 @@ describe("filter by checkboxes", () => {
describe("filter by ranges and checkboxes", () => {
it("show all particles when no kind of filter is applied", () => {
- const charge = new Range("charge");
+ const charge = new Range({
+ property: "charge",
+ unit: "e",
+ });
const simulatorStatus = new Checkbox("simStatus", 26);
const rangeFilters = Range.buildFilter([charge]);
const checkboxFilters = Checkbox.buildFilter([simulatorStatus]);
@@ -163,9 +173,7 @@ describe("filter by ranges and checkboxes", () => {
const [newParentLinks, newChildrenLinks, filteredParticles] = reconnect(
criteriaFunction,
- parentLinks,
- childrenLinks,
- particles
+ particlesHandler
);
expect(newParentLinks.map((link) => link.id).sort()).toEqual([
@@ -180,7 +188,10 @@ describe("filter by ranges and checkboxes", () => {
});
it("filter by a combination of ranges and checkboxes", () => {
- const charge = new Range("charge");
+ const charge = new Range({
+ property: "charge",
+ unit: "e",
+ });
charge.max = 3;
const simulatorStatus = new Checkbox("simStatus", 23);
simulatorStatus.checked = true;
@@ -193,9 +204,7 @@ describe("filter by ranges and checkboxes", () => {
const [newParentLinks, newChildrenLinks, filteredParticles] = reconnect(
criteriaFunction,
- parentLinks,
- childrenLinks,
- particles
+ particlesHandler
);
expect(newChildrenLinks.map((link) => link.id)).toEqual([]);