Skip to content
Snippets Groups Projects
Commit 2600b4ae authored by Mathias Chouet's avatar Mathias Chouet Committed by mathias.chouet
Browse files

Prebarrage: GUI

grey out Calculate button if basins structure is invalid
use Mermaid Nodes for walls
suffix multiple walls on same basins pair
base Mermaid Nodes on children UIDs
display Mermaid code (debug)
unselect element after deletion
highlight selected element
parent 961297e1
No related branches found
No related tags found
No related merge requests found
......@@ -5,6 +5,6 @@
"Xinit": "Valeur initiale du paramètre recherché",
"X": "Valeur du paramètre recherché",
"select_upstream": "Bassin / cloison amont",
"select_downstream": "Bassin / cloison aval"
"select_upstream": "Bassin amont",
"select_downstream": "Bassin aval"
}
\ No newline at end of file
......@@ -48,6 +48,7 @@ import { MultiDimensionResults } from "../../results/multidimension-results";
import { NgParameter } from "../../formulaire/elements/ngparam";
import { FormulaireFixedVar } from "../../formulaire/definition/form-fixedvar";
import { PbSchema } from "../../formulaire/elements/pb-schema";
import { PbSchemaComponent } from "../pb-schema/pb-schema.component";
import { HotkeysService, Hotkey } from "angular2-hotkeys";
......@@ -77,6 +78,12 @@ export class GenericCalculatorComponent implements OnInit, DoCheck, AfterViewChe
@ViewChild(PabTableComponent)
private _pabTableComponent: PabTableComponent;
/**
* PbSchemaComponent if any
*/
@ViewChild(PbSchemaComponent)
private _pbSchemaComponent: PbSchemaComponent;
/**
* composant d'affichage des résultats
*/
......@@ -503,6 +510,10 @@ export class GenericCalculatorComponent implements OnInit, DoCheck, AfterViewChe
this._isUIValid = this._isUIValid && this._pabTableComponent.isValid;
}
}
if (this._pbSchemaComponent !== undefined) {
this._isUIValid = this._isUIValid && this._pbSchemaComponent.isValid;
}
}
public getElementStyleDisplay(id: string) {
......
......@@ -40,4 +40,6 @@
<div id="schema" #schema></div>
<pre id="debug">{{ graphDef }} </pre>
</mat-card-content>
......@@ -49,3 +49,7 @@ mat-card-content {
margin-bottom: .5em;
text-align: center;
}
#debug {
/* display: none; */
}
import { Component, Input, Output, EventEmitter, OnInit, AfterViewInit, ViewChild } from "@angular/core";
import {
PreBarrage, PbBassin, PbBassinParams, PbCloison
PreBarrage, PbBassin, PbBassinParams, PbCloison, CalculatorType
} from "jalhyd";
import * as mermaid from "mermaid";
......@@ -32,7 +32,7 @@ export class PbSchemaComponent implements AfterViewInit, OnInit {
/** handle on SVG container */
private nativeElement: any;
/** flag de validité des FieldSet enfants */
/** flag de validité du composant */
private _isValid = false;
private upstreamId = "amont";
......@@ -51,7 +51,13 @@ export class PbSchemaComponent implements AfterViewInit, OnInit {
private model: PreBarrage;
/** Latest clicked item: a PbCloison, a PbBassin or undefined if river "Upstream" or "Downstream" was clicked */
private _selectedItem: any;
private _selectedItem: PbCloison | PbBassin;
/** Records existing walls as they are built, to detect if multiple walls connect the same pair of basins */
private existingWalls: { [key: string]: number };
/** Stores appropriate number suffix for a given wall uid (related to existingWalls above) */
private wallsSuffixes: { [key: string]: number };
public constructor(
private i18nService: I18nService,
......@@ -91,7 +97,6 @@ export class PbSchemaComponent implements AfterViewInit, OnInit {
* Builds the interactive schema from the PreBarrage model
*/
private refresh() {
console.log("riz fraîche");
this.render();
this.refreshEventListeners();
this.updateValidity();
......@@ -107,26 +112,29 @@ export class PbSchemaComponent implements AfterViewInit, OnInit {
this.nativeElement.querySelectorAll("g.node").forEach(item => {
item.style.cursor = "pointer";
item.addEventListener("click", () => {
this.selectBasin(item.id);
});
});
this.nativeElement.querySelectorAll("g.edgeLabel").forEach(item => {
item.style.cursor = "pointer";
item.addEventListener("click", () => {
this.selectWall(item);
this.selectNode(item);
});
});
}
/**
* Builds a Mermaid graph text definition
* Builds a Mermaid graph text definition, using Nodes
* to represent basins as well as walls
*/
private graphDefinition() {
this.existingWalls = {};
this.wallsSuffixes = {};
const def: string[] = [ "graph TB" ];
// river upstream / downstream
def.push(`${this.upstreamId}("${this.i18nService.localizeText("INFO_LIB_AMONT")}")`);
def.push(`${this.downstreamId}("${this.i18nService.localizeText("INFO_LIB_AVAL")}")`);
// styles
def.push("classDef wall fill:#e8e8e8,stroke-width:0;");
def.push("classDef node-highlighted fill:orange;");
// def.push("classDef basin fill:black;");
// debug
if (this.model.children.length === 0) {
const b1 = new PbBassin(new PbBassinParams(0.1, 42));
......@@ -139,92 +147,58 @@ export class PbSchemaComponent implements AfterViewInit, OnInit {
this.model.addChild(new PbCloison(b1, undefined));
}
for (const b of this.model.bassins) {
// basin
def.push(`${b.uid}("${this.itemDesription(b)}")`);
// upstream walls
for (const uw of b.cloisonsAmont) {
const upstreamBasinId = uw.bassinAmont === undefined ? this.upstreamId : uw.bassinAmont.uid;
// upstream wall unique identifier
const uwString = `${upstreamBasinId}-->|${this.itemDesription(uw)}|${b.uid}`;
if (! def.includes(uwString)) {
def.push(uwString);
for (const c of this.model.children) {
if (c instanceof PbBassin) {
def.push(`${c.uid}("${this.itemDesription(c)}")`); // rounded edges
// def.push(`class ${c.uid} basin;`);
} else if (c instanceof PbCloison) {
const upstreamBasinId = c.bassinAmont === undefined ? this.upstreamId : c.bassinAmont.uid;
const downstreamBasinId = c.bassinAval === undefined ? this.downstreamId : c.bassinAval.uid;
// record this wall
const basinsPair = upstreamBasinId + "-" + downstreamBasinId;
if (! (basinsPair in this.existingWalls)) {
this.existingWalls[basinsPair] = 0;
}
}
// downstream walls
for (const dw of b.cloisonsAval) {
const downstreamBasinId = dw.bassinAval === undefined ? this.downstreamId : dw.bassinAval.uid;
// downstream wall unique identifier
const dwString = `${b.uid}-->|${this.itemDesription(dw)}|${downstreamBasinId}`;
if (! def.includes(dwString)) {
def.push(dwString);
// affect suffix if needed
if (this.existingWalls[basinsPair] > 0) {
this.wallsSuffixes[c.uid] = this.existingWalls[basinsPair];
}
this.existingWalls[basinsPair]++;
// draw wall Node
def.push(`${c.uid}["${this.itemDesription(c)}"]`); // square edges
def.push(`class ${c.uid} wall;`);
// draw "arrow" with 2 lines
def.push(`${upstreamBasinId}---${c.uid}`); // up line
def.push(`${c.uid}-->${downstreamBasinId}`); // down arrow
}
}
return def.join("\n");
}
private selectBasin(id: string) {
if ([ this.upstreamId, this.downstreamId ].includes(id)) {
console.log("YOU CLICKED EITHER UPSTREAM OR DOWNSTREAM");
private selectNode(item: any) {
// highlight clicked element
this.clearHighlightedItems();
item.classList.add("node-highlighted");
// find what was clicked
if ([ this.upstreamId, this.downstreamId ].includes(item.id)) {
console.log("YOU CLICKED EITHER UPSTREAM OR DOWNSTREAM", item.id);
this._selectedItem = undefined;
} else {
let basin: PbBassin;
for (const b of this.model.bassins) {
if (b.uid === id) {
basin = b;
for (const b of this.model.children) {
if (b.uid === item.id) {
this._selectedItem = b;
}
}
this._selectedItem = basin;
// @TODO highlight node in schema
console.log("BASIN FOUND !", basin);
}
}
private selectWall(item: SVGGElement) {
// Mermaid does not allow to assign IDs to connectors and labels…
const text: string = item.querySelector("span.edgeLabel").textContent;
if (text) {
const [ uBs, dBs ] = text.split("-");
let wall: PbCloison;
// clodo test: is there an upstream basin or is it upstream river ?
if (uBs === this.i18nService.localizeText("INFO_LIB_AMONT")) {
// find wall from downstream basin
const dBi = Number(dBs.substring(1));
const dB = this.model.bassins[dBi - 1];
for (const w of dB.cloisonsAmont) {
// find the one that is connected to upstream river
if (w.bassinAmont === undefined) {
wall = w;
}
}
if (this._selectedItem !== undefined) {
console.log(`${this._selectedItem.calcType === CalculatorType.PbBassin ? "BASIN" : "WALL"} FOUND !`, this._selectedItem);
} else {
// find wall from upstream basin
const uBi = Number(uBs.substring(1));
const uB = this.model.bassins[uBi - 1];
// clodo test again
let dB: PbBassin;
if (dBs !== this.i18nService.localizeText("INFO_LIB_AVAL")) {
const dBi = Number(dBs.substring(1));
dB = this.model.bassins[dBi - 1];
}
for (const w of uB.cloisonsAval) {
// find the one that is connected to dB (either a basin or downstream river)
if (w.bassinAval === dB) {
wall = w;
}
}
console.log("watt ze fyook ?");
}
if (wall === undefined) {
throw new Error(`PbSchemaComponent.selectWall(): cannot find wall for label "${text}"`);
}
this._selectedItem = wall;
// @TODO highlight label and edge in schema
console.log("WALL FOUND !", wall);
}
}
// for debug only
public get graphDef(): string {
return this.graphDefinition();
}
......@@ -274,7 +248,10 @@ export class PbSchemaComponent implements AfterViewInit, OnInit {
? this.i18nService.localizeText("INFO_LIB_AVAL")
: "B" + (this.findBasinPosition(item.bassinAval) + 1);
desc = upstreamBasinName + "-" + downstreamBasinName;
// if a similar wall already exists, suffix !
if (item.uid in this.wallsSuffixes) {
desc += " (" + this.wallsSuffixes[item.uid] + ")";
}
} else if (item instanceof PbBassin) {
desc = this.i18nService.localizeText("INFO_PB_BASSIN_N") + (this.findBasinPosition(item) + 1);
} // else undefined
......@@ -302,6 +279,7 @@ export class PbSchemaComponent implements AfterViewInit, OnInit {
/** Removes a basin or wall, and all related items */
public onRemoveClick() {
this.model.deleteChild(this._selectedItem.findPositionInParent());
this.unselect();
this.refresh();
}
......@@ -309,9 +287,10 @@ export class PbSchemaComponent implements AfterViewInit, OnInit {
return this.i18nService.localizeText("INFO_FIELDSET_REMOVE");
}
/** Adds a new lone basin */
public onAddBasinClick() {
console.log("Ajoute un bassin, coquin !");
this.model.addChild(new PbBassin(new PbBassinParams(20, 99)));
this.unselect();
this.refresh();
}
......@@ -323,36 +302,22 @@ export class PbSchemaComponent implements AfterViewInit, OnInit {
* Computes the global Pab validity : validity of every cell of every row
*/
private updateValidity() {
this._isValid = true;
/* for (const r of this.rows) {
for (const c of r.cells) {
this._isValid = this._isValid && ! this.isInvalid(c);
}
} */
// check that at least 1 basin is present and a route from river
// upstream to river downstream exists (2nd check includes 1st)
this._isValid = this.model.hasUpDownConnection();
this.validChange.emit();
}
/* public exportAsSpreadsheet() {
const elem: any = document.getElementById("geometry");
const elemCopy = (elem as HTMLElement).cloneNode(true) as HTMLElement;
// enrich element copy: replace inputs by their values, so that it appears in the exported spreadsheet
const tables: any = elemCopy.getElementsByTagName("table");
for (const table of tables) {
const tds: any = table.getElementsByTagName("td");
for (const td of tds) {
// if it contains an input, replace it with the input value
const inputs = td.getElementsByTagName("input");
if (inputs.length > 0) {
const input = inputs[0];
td.innerHTML = input.value;
}
}
}
// export the enriched element copy
AppComponent.exportAsSpreadsheet(elemCopy as any);
private clearHighlightedItems() {
this.nativeElement.querySelectorAll("g.node").forEach(item => {
console.log("found an item !");
item.classList.remove("node-highlighted");
});
}
private unselect() {
this._selectedItem = undefined;
this.clearHighlightedItems();
}
public get uitextExportAsSpreadsheet() {
return this.i18nService.localizeText("INFO_RESULTS_EXPORT_AS_SPREADSHEET");
} */
}
0% Loading or .
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment