Skip to content

Commit

Permalink
Zware refactoring van situatieplan tekenmodule met het oog op stabili…
Browse files Browse the repository at this point in the history
…teit en performance

- Nieuwe class ElectroItemZoeker
- Nieuwe file voor geometrische functies
- Nieuwe class voor drag en drop functies
- Code om missing links naar eendraadschema te verwijderen verplaatst naar SituationPlan
- Nieuwe eigenschap needsViewUpdate in SituationPlanElement om te vermijden dan alles altijd opnieuw getekend wordt
- Hernoemen van de file waarin de eigenschappen van situatieplan elementen kunnen gewijzigd worden
- Aanzienlijke wijzigingen in SituationPlanView met het oog op performance, vooral in google Chrome. Door gebruik te maken van een parameter needsViewUpdate en van documentfragments wordt het aantal DOM updates sterk terug gedrongen en komt de performance van google chrome opnieuw in de buurt van die van firefox
- class EventManager heeft zijn eigen file gekregen
- outerbox heet nu outerdiv
- Er kan niet meer geklikked worden op de undo functies in de ribbon als er geen undo mogelijk is.
  • Loading branch information
igoethal committed Jan 12, 2025
1 parent f3349b6 commit cdbe383
Show file tree
Hide file tree
Showing 18 changed files with 1,434 additions and 768 deletions.
2 changes: 1 addition & 1 deletion builddate.js
Original file line number Diff line number Diff line change
@@ -1 +1 @@
var CONF_builddate="20250105-150906"
var CONF_builddate="20250112-145253"
10 changes: 4 additions & 6 deletions css/styles.css
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@

:root {
--selectPadding: 2;
--paperPadding: 10px;

--menu-height: 40px;
--ribbon-height: 68px;
Expand Down Expand Up @@ -48,9 +49,6 @@ body {
display: block;
}




button, select, select option, input[type="checkbox"] {
cursor: pointer;
}
Expand Down Expand Up @@ -278,7 +276,7 @@ ul#minitabs a:hover {
flex-direction: row; /* Ensure columns are side by side */
}

#outerbox {
#outerdiv {
display: flex;
position: absolute;
top: calc(var(--menu-height) + var(--ribbon-height));
Expand All @@ -292,8 +290,8 @@ ul#minitabs a:hover {
}

#paper {
top: 10px;
left: 10px;
top: var(--paperPadding);
left: var(--paperPadding);
width: 277mm; /* Useful drawing area on A4 */
height: 150mm; /* Useful drawing area on A4 */
background-color: white;
Expand Down
1,055 changes: 693 additions & 362 deletions eendraadschema.js

Large diffs are not rendered by default.

2 changes: 1 addition & 1 deletion index.html
Original file line number Diff line number Diff line change
Expand Up @@ -35,7 +35,7 @@
<div id="right_col_inner"></div>
</div>
</div>
<div id="outerbox" style="display:none;"> <!-- Situatieschets -->
<div id="outerdiv" style="display:none;"> <!-- Situatieschets -->
<div id="paper"></div>
</div>
</div>
Expand Down
43 changes: 43 additions & 0 deletions src/EventManager.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,43 @@
/**
* Manages the addition and removal of event listeners on HTML elements.
*/
class EventManager {
private listeners: { element: HTMLElement, type: string, listener: EventListenerOrEventListenerObject }[] = [];

/**
* Adds an event listener to a specified HTML element. If a listener of the same
* type already exists on the element, it is removed before adding the new one.
*
* @param element - The HTML element to attach the event listener to.
* @param type - The type of the event.
* @param listener - The event listener function or object.
*/
addEventListener(element: HTMLElement, type: string, listener: EventListenerOrEventListenerObject) {
const existingListenerIndex = this.listeners.findIndex(l => l.element === element && l.type === type);
if (existingListenerIndex !== -1) {
const existingListener = this.listeners[existingListenerIndex];
element.removeEventListener(type, existingListener.listener);
this.listeners.splice(existingListenerIndex, 1);
}

this.listeners.push({ element, type, listener });
element.addEventListener(type, listener);
}

/**
* Removes all event listeners managed by this EventManager instance.
*/
removeAllEventListeners() {
this.listeners.forEach(({ element, type, listener }) => {
element.removeEventListener(type, listener);
});
this.listeners = [];
}

/**
* Disposes of the EventManager by removing all event listeners.
*/
dispose() {
this.removeAllEventListeners();
}
}
4 changes: 2 additions & 2 deletions src/Hierarchical_List.ts
Original file line number Diff line number Diff line change
Expand Up @@ -525,11 +525,11 @@ class Hierarchical_List {

// Plaats bovenaan de switch van editeer-mode (teken of verplaats) --
output += `
<div class="icon" onclick="undoClicked()" ${(undostruct.undoStackSize() > 0 ? "" : "style=\"filter: opacity(45%)\"")}>
<div class="icon" ${(undostruct.undoStackSize() > 0 ? 'onclick="undoClicked()"' : "style=\"filter: opacity(45%)\"")}>
<img src="gif/undo.png" alt="Ongedaan maken" class="icon-image">
<span class="icon-text">Ongedaan maken</span>
</div>
<div class="icon" onclick="redoClicked()" ${(undostruct.redoStackSize() > 0 ? "" : "style=\"filter: opacity(45%)\"")}>
<div class="icon" ${(undostruct.redoStackSize() > 0 ? 'onclick="redoClicked()"' : "style=\"filter: opacity(45%)\"")}>
<img src="gif/redo.png" alt="Opnieuw" class="icon-image">
<span class="icon-text">Opnieuw</span>
</div>
Expand Down
38 changes: 20 additions & 18 deletions src/general.ts
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,24 @@ function deepClone (obj) {
return _out;
}

/**
* Returns true if the current mode is a development mode.
* This is determined by the presence of a 'dev' parameter in the URL.
*
* @returns {boolean} True if this is a development mode, false otherwise.
*/

function isDevMode(): boolean {
try {
const urlParams = new URLSearchParams(window.location.search);
return urlParams.has('dev');
} catch (error) {
console.error('Error checking for dev mode:', error);
return false;
}
}


// Function for length of a string in 8 bit bytes
const byteSize = str => new Blob([str]).size;

Expand All @@ -35,22 +53,6 @@ function isInt(value) {
!isNaN(parseInt(value, 10));
}

function getPixelsPerMillimeter() {
const div = document.createElement('div');
div.style.width = '10mm';
div.style.position = 'absolute';
document.body.appendChild(div);
const widthInPixels = div.offsetWidth;
document.body.removeChild(div);
const pixelsPerMillimeter = widthInPixels / 10;
return pixelsPerMillimeter;
}

// Example usage
const pixelsPerMM = getPixelsPerMillimeter();
console.log(`Your browser uses approximately ${pixelsPerMM} pixels per millimeter.`);


function svgTextWidth(input:String, fontsize:Number = 10, options:String = '') {
const div = document.createElement('div');
div.innerHTML = '<svg width="1000" height="20"><text x="0" y="10" style="text-anchor:start" font-family="Arial, Helvetica, sans-serif" font-size="' + Number(fontsize) + '" ' + options + '>' + input + '</text></svg>';
Expand All @@ -59,8 +61,8 @@ function svgTextWidth(input:String, fontsize:Number = 10, options:String = '') {

/*if (document.getElementById("configsection").style.display === 'block') {
tryoutdiv = document.getElementById("configsection") as HTMLElement;
} else if (document.getElementById("outerbox").style.display === 'block') {
tryoutdiv = document.getElementById("outerbox") as HTMLElement;
} else if (document.getElementById("outerdiv").style.display === 'block') {
tryoutdiv = document.getElementById("outerdiv") as HTMLElement;
} else {
tryoutdiv = document.getElementById("right_col_inner") as HTMLElement;
}*/
Expand Down
6 changes: 3 additions & 3 deletions src/main.ts
Original file line number Diff line number Diff line change
Expand Up @@ -291,18 +291,18 @@ function toggleAppView(type: '2col' | 'config' | 'draw') {
structure.properties.currentView = type;
if (type === '2col') {
document.getElementById("configsection").style.display = 'none';
document.getElementById("outerbox").style.display = 'none';
document.getElementById("outerdiv").style.display = 'none';
document.getElementById("ribbon").style.display = 'flex';
document.getElementById("canvas_2col").style.display = 'flex';
structure.updateRibbon();
} else if (type === 'config') {
document.getElementById("configsection").style.display = 'block';
document.getElementById("outerbox").style.display = 'none';
document.getElementById("outerdiv").style.display = 'none';
document.getElementById("ribbon").style.display = 'none';
document.getElementById("canvas_2col").style.display = 'none';
} else if (type === 'draw') {
document.getElementById("configsection").style.display = 'none';
document.getElementById("outerbox").style.display = 'flex';
document.getElementById("outerdiv").style.display = 'flex';
document.getElementById("ribbon").style.display = 'flex';
document.getElementById("canvas_2col").style.display = 'none';
}
Expand Down
84 changes: 84 additions & 0 deletions src/sitplan/ElectroItemZoeker.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,84 @@
/**
* Class gebruikt in SituationPlanView om te zoeken naar electroitems op basis van de kringnaam.
* Dit laat toe items to selecteren uit het volledige eendraadschema en ze te plaatsen op het situatieschema.
*
* Deze class refereert naar de volgende globale variabelen:
* - structure
*/

class ElectroItemZoeker {

private excludedTypes = ['Aansluiting','Bord','Kring','Domotica','Domotica module (verticaal)',
'Domotica gestuurde verbruiker','Leiding','Splitsing','Verlenging',
'Vrije ruimte','Meerdere verbruikers'];

private data: {id: number, kringnaam: string, adres: string, type: string}[] = [];

/**
* Constructor van de ElectroItemZoeker.
*
* Initialiseert de lijst van alle toegestane ElectroItems in het situatieplan.
*/

constructor() {
this.reCalculate();
}

/**
* Geeft de lijst van alle toegestane ElectroItems in het situatieplan retour.
* @returns {Object[]} een lijst van objecten met de volgende structuur:
* {id: number, kringnaam: string, adres: string, type: string}
*/

getData() {
return this.data;
}

/**
* Geeft een lijst van alle unieke kringnamen retour uit de lijst van ElectroItems.
* @returns {string[]} een lijst van unieke kringnamen.
*/

getUniqueKringnaam(): string[] {
return Array.from(new Set(this.data.map(x => x.kringnaam)));
}

/**
* Geeft een lijst van alle ElectroItems retour die behoren tot de kring met de naam 'kringnaam'.
* @param {string} kringnaam - de naam van de kring.
* @returns {Object[]} een lijst van objecten met de volgende structuur:
* {id: number, adres: string, type: string}
*/

getElectroItemsByKring(kringnaam: string): {id: number, adres: string, type: string}[] {
return this.data.filter(x => x.kringnaam === kringnaam).map(x => ({id: x.id, adres: x.adres, type: x.type}));
}

/**
* Rekent de lijst van alle toegestane ElectroItems opnieuw uit.
*
* Deze methode wordt gebruikt om de lijst van ElectroItems te vullen die in het situatieplan gebruikt mogen worden.
* De lijst wordt opnieuw uitgerekend door de volgende stappen:
* 1. Doorlopen alle actieve ElectroItems in de structuur.
* 2. Voor elke ElectroItem worden de kringnaam en het type bepaald.
* 3. Als de kringnaam niet leeg is en het type niet voorkomt in de lijst van uitgesloten types, dan wordt de ElectroItem toegevoegd aan de lijst.
* 4. De ElectroItem wordt toegevoegd met de volgende structuur: {id: number, kringnaam: string, adres: string, type: string}
*/

reCalculate() {
for (let i = 0; i<structure.length; i++) {
if (structure.active[i]) {
let id:number = structure.id[i];
let kringnaam:string = structure.findKringName(id).trim();
if (kringnaam != '') {
let type:string = (structure.data[i] as Electro_Item).getType();
if ( (type != null) && (this.excludedTypes.indexOf(type) === -1) ) {
let adres:string = (structure.data[i] as Electro_Item).getReadableAdres();
this.data.push({id: id, kringnaam: kringnaam, adres:adres, type: type});
}
}
}
}
}

}
54 changes: 54 additions & 0 deletions src/sitplan/GeometricFunctions.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,54 @@
/**
* Functie die de breedte en hoogte van een rechthoek als invoer neemt, evenals een rotatie rond het midden van de rechthoek.
* De functie retourneert de breedte en hoogte van de kleinste rechthoek die de geroteerde rechthoek omsluit met zijden langs de X- en Y-assen.
*/

function getRotatedRectangleSize(width: number, height: number, rotation: number) {
const rotationInRadians = rotation * Math.PI / 180;
const cos = Math.cos(rotationInRadians);
const sin = Math.sin(rotationInRadians);
const rotatedWidth = Math.abs(width * cos) + Math.abs(height * sin);
const rotatedHeight = Math.abs(width * sin) + Math.abs(height * cos);
return { width: rotatedWidth, height: rotatedHeight };
}

/**
* Functie die de breedte en hoogte van een rechthoek als invoer neemt, evenals een rotatie rond het midden van de rechthoek.
* De functie retourneert de breedte en hoogte van de rechthoek die voldoet aan de volgende eigenschappen:
* - De zijden zijn parallel aan de X-as en de Y-as.
* - De rechthoek snijdt de X-as en Y-as in dezelfde punten als de originele geroteerde rechthoek
*
* Deze functie kan gebruikt worden om de locatie van labels te bepalen.
*/

function getXYRectangleSize(width: number, height: number, rotation: number) {
rotation = Math.abs(rotation) % 180;
if (rotation > 90) rotation = 180 - rotation;
const rotationInRadians = rotation * Math.PI / 180;
const cos = Math.cos(rotationInRadians);
const sin = Math.sin(rotationInRadians);
return { width: Math.min(width/cos,height/sin), height: Math.min(width/sin,height/cos) };
}

/**
* Cache het resultaat van getPixelsPerMillimeter() om de overhead van het maken en verwijderen van een DOM-element bij elke oproep te voorkomen.
*/
let cachedPixelsPerMillimeter: number | null = null;

/**
* Berekent het aantal pixels in een millimeter op het huidige scherm.
* Maakt gebruik van een cache om de overhead van het maken en verwijderen van een DOM-element bij elke oproep te voorkomen.
* @returns {number} Het aantal pixels in een millimeter.
*/
function getPixelsPerMillimeter(): number {
if (cachedPixelsPerMillimeter === null) {
const div = document.createElement('div');
div.style.width = '10mm';
div.style.position = 'absolute';
document.body.appendChild(div);
const widthInPixels = div.offsetWidth;
document.body.removeChild(div);
cachedPixelsPerMillimeter = widthInPixels / 10;
}
return cachedPixelsPerMillimeter;
}
42 changes: 42 additions & 0 deletions src/sitplan/MouseDrag.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,42 @@
/**
* Class that helps with dragging a box on the situation plan view.
* It keeps track of the start position of the drag and the zoomfactor.
*/
class MouseDrag {

private startDragx: number = 0;
private startDragy: number = 0;
private startOffsetLeft: number = 0;
private startOffsetTop: number = 0;
private zoomfactor: number = 1;

/**
* Start the drag.
* @param mousex The x position of the mouse when the drag starts.
* @param mousey The y position of the mouse when the drag starts.
* @param startOffsetLeft The left position of the box when the drag starts.
* @param startOffsetTop The top position of the box when the drag starts.
* @param zoomfactor The zoomfactor of the situation plan view when the drag starts.
*/
startDrag(mousex: number = 0, mousey: number = 0,
startOffsetLeft: number = 0, startOffsetTop: number = 0,
zoomfactor: number = 1) {
this.startDragx = mousex;
this.startDragy = mousey;
this.startOffsetLeft = startOffsetLeft;
this.startOffsetTop = startOffsetTop;
this.zoomfactor = zoomfactor;
}

/**
* Return the new left and top position of the box based on the current mouse position.
* @param mousex The current x position of the mouse.
* @param mousey The current y position of the mouse.
* @returns An object with the new left and top position of the box.
*/
returnNewLeftTop(mousex: number = 0, mousey: number = 0) {
return ( {
left: (mousex - this.startDragx) / this.zoomfactor + this.startOffsetLeft,
top: (mousey - this.startDragy) / this.zoomfactor + this.startOffsetTop});
}
}
Loading

0 comments on commit cdbe383

Please sign in to comment.