diff --git a/CHANGELOG.md b/CHANGELOG.md index 78c916125b298bd5fb7552a0180d03335479d05a..1ab7f4a4808c25ba24328419c91ed4259ac86826 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,27 +1,43 @@ # Historique des versions -### 4.16.0 (en cours) +### 4.16.0 #### Nouvelles fonctionnalités * URL de routeur "/loadsession" pour charger un exemple ([nghyd#476](https://gitlab.irstea.fr/cassiopee/nghyd/-/issues/476)) * Deploy devel branch on cassiopee-dev.g-eau.fr ([nghyd#564](https://gitlab.irstea.fr/cassiopee/nghyd/-/issues/564)) +* PAB : ajout de la charge et l'ennoiement dans le tableau de résultat et l'export ([jalhyd#324](https://gitlab.irstea.fr/cassiopee/jalhyd/-/issues/324), [nghyd#518](https://gitlab.irstea.fr/cassiopee/nghyd/-/issues/518)) +* Courbe de remous (et bief) : remontée d'une erreur quand le pas de discrétisation est supérieur à la longueur du bief ([jalhyd#316](https://gitlab.irstea.fr/cassiopee/jalhyd/-/issues/316), [nghyd#565](https://gitlab.irstea.fr/cassiopee/nghyd/-/issues/565)) +* Section paramétrée : profil de section : option axes orthonormés ([nghyd#497](https://gitlab.irstea.fr/cassiopee/nghyd/-/issues/497)) #### Changements * Mise à jour vers Angular 14 ([nghyd#500](https://gitlab.irstea.fr/cassiopee/nghyd/-/issues/500)) * CI : MAJ de l'image Docker vers Debian Bullseye -* Angular : compiler avec Ivy ([nghyd#369](https://gitlab.irstea.fr/cassiopee/nghyd/-/issues/369)) +* Angular : compilation avec Ivy ([nghyd#369](https://gitlab.irstea.fr/cassiopee/nghyd/-/issues/369)) * Déplacer le répertoire Jalhyd dans celui de Nghyd ([nghyd#558](https://gitlab.irstea.fr/cassiopee/nghyd/-/issues/558)) +* Ouvrages: modification des types d'ouvrages (ajout de seuil/orifice rectangulaire, vanne rectangulaire renommée en vanne de fond rectangulaire) ([jalhyd#326](https://gitlab.irstea.fr/cassiopee/jalhyd/-/issues/326), [nghyd#511](https://gitlab.irstea.fr/cassiopee/nghyd/-/issues/511)) +* Documentation : corrections diverses ([nghyd#559](https://gitlab.irstea.fr/cassiopee/nghyd/-/issues/559)) +* Déménagement de l'intégration continue sur les serveurs gitlab à Lyon ([nghyd#557](https://gitlab.irstea.fr/cassiopee/nghyd/-/issues/557)) +* Mise à jour de Chartjs ([nghyd#554](https://gitlab.irstea.fr/cassiopee/nghyd/-/issues/554)) +* Documentation : MacroRugo : ajout d'un schéma rugosité de fond ([nghyd#524](https://gitlab.irstea.fr/cassiopee/nghyd/-/issues/524)) +* Prébarrages : regroupement de la saisie des bassins ([nghyd#522](https://gitlab.irstea.fr/cassiopee/nghyd/-/issues/522)) +* Documentation : lois d'ouvrages : définition seuil mince/épais ([nghyd#514](https://gitlab.irstea.fr/cassiopee/nghyd/-/issues/514)) +* Documentation : ajout d'un tableau synthétiques des lois d'ouvrages ([nghyd#513](https://gitlab.irstea.fr/cassiopee/nghyd/-/issues/513)) +* MAJ de l'image Docker à la version Debian Bullseye (compatibilité TLS) ([cassiopee2-integration#10](https://gitlab.irstea.fr/cassiopee/cassiopee2-integration/-/issues/10)) +* MAJ vers PrimeNG 10 ([nghyd#481](https://gitlab.irstea.fr/cassiopee/nghyd/-/issues/481)) +* Documentation : MAJ de la documentation des grilles avec les données de Lemkecher et al. (2020) ([nghyd#438](https://gitlab.irstea.fr/cassiopee/nghyd/-/issues/438)) #### Correction de bogues * Les caractères UTF8 ne sont pas imprimés dans la doc PDF ([nghyd#556](https://gitlab.irstea.fr/cassiopee/nghyd/-/issues/556)) * PréBarrages: La sélection de l'amont ou l'aval n'est pas visible au premier clic ([nghyd#560](https://gitlab.irstea.fr/cassiopee/nghyd/-/issues/560)) -* Courbe de remous (et bief): remonter une erreur quand le pas de discrétisation est supérieur la longueur du bief ([nghyd#565](https://gitlab.irstea.fr/cassiopee/nghyd/-/issues/565)) * Solveur: le paramètre recherché n'est pas conservé ([nghyd#555](https://gitlab.irstea.fr/cassiopee/nghyd/-/issues/555)) * PAB: Bugs de format du tableau NgPrime ([nghyd#562](https://gitlab.irstea.fr/cassiopee/nghyd/-/issues/562)) -* Section paramétrée: crash de l'appli sur variation de paramètre ([nghyd#561](https://gitlab.irstea.fr/cassiopee/nghyd/-/issues/561)) +* Section paramétrée: crash de l'appli sur variation de paramètre ([jalhyd#319](https://gitlab.irstea.fr/cassiopee/jalhyd/-/issues/319), [nghyd#561](https://gitlab.irstea.fr/cassiopee/nghyd/-/issues/561)) +* Module avec une section : le mode champs vide ne fonctionne pas ([jalhyd#327](https://gitlab.irstea.fr/cassiopee/jalhyd/-/issues/327), [nghyd#569](https://gitlab.irstea.fr/cassiopee/nghyd/-/issues/569)) +* Déplacement du paramètre calculé lors de la duplication d'un Nub ([jalhyd#322](https://gitlab.irstea.fr/cassiopee/jalhyd/-/issues/322), [nghyd#567](https://gitlab.irstea.fr/cassiopee/nghyd/-/issues/567)) +* Lois d'ouvrages : mauvaise gestion du paramètre calculé sur suppression d'ouvrage ([jalhyd#321](https://gitlab.irstea.fr/cassiopee/jalhyd/-/issues/321), [nghyd#566](https://gitlab.irstea.fr/cassiopee/nghyd/-/issues/566)) ### 4.15.1 - 2022-07-04 diff --git a/docs/en/calculators/pam/macrorugo.md b/docs/en/calculators/pam/macrorugo.md index 09b471f1bd41e84f13579299750cae2efebeb671..0efc4e042e8ef16fd8af6bb65d5c432848fa673d 100644 --- a/docs/en/calculators/pam/macrorugo.md +++ b/docs/en/calculators/pam/macrorugo.md @@ -25,3 +25,8 @@ It requires the following values to be entered: - The width of the blocks \(D\) facing the flow (m); - The useful height of the blocks \(k\) (m); - The drag coefficient of a single block (1 for round, 2 for square). + +<div style="position: relative"><a id="rugosite-de-fond" style="position: absolute; top: -60px;"></a></div> +# Bed roughness + + diff --git a/docs/en/calculators/pam/schema_rugosite_fond.png b/docs/en/calculators/pam/schema_rugosite_fond.png new file mode 100644 index 0000000000000000000000000000000000000000..57969041c6d9b2d45573cd0e5a1a0bbd78d9b46a Binary files /dev/null and b/docs/en/calculators/pam/schema_rugosite_fond.png differ diff --git a/docs/fr/calculators/pam/macrorugo.md b/docs/fr/calculators/pam/macrorugo.md index 646df7cff0f188f39ed5c5937258f71f7987acb7..83395baef05257c0711ed32229f849e1d8f967f9 100644 --- a/docs/fr/calculators/pam/macrorugo.md +++ b/docs/fr/calculators/pam/macrorugo.md @@ -26,3 +26,6 @@ Il nécessite d'entrer les valeurs suivantes : - La hauteur utile des blocs \(k\) (m) ; - Le coefficient de trainée d'un bloc (1 pour rond, 2 pour carré). +# Rugosité de fond + + diff --git a/docs/fr/calculators/pam/schema_rugosite_fond.png b/docs/fr/calculators/pam/schema_rugosite_fond.png new file mode 100644 index 0000000000000000000000000000000000000000..ee526a871fe372484dc25a1cd18463a0de1d5c97 Binary files /dev/null and b/docs/fr/calculators/pam/schema_rugosite_fond.png differ diff --git a/jalhyd_branch b/jalhyd_branch index 5327f921cccee39cb4e77fc0882bfd05e703507a..d64531f1305e091791eac674c3a36d86b9e17ddd 100644 --- a/jalhyd_branch +++ b/jalhyd_branch @@ -1 +1 @@ -327-module-avec-une-section-le-mode-champs-vide-ne-fonctionne-pas +devel diff --git a/src/app/calculators/macrorugo/config.json b/src/app/calculators/macrorugo/config.json index 6cbce4d4541d9cb123908d3026a6026c0a061954..6b9ee9a8f8c0303b91de90757d2ee8b6cc57c183 100644 --- a/src/app/calculators/macrorugo/config.json +++ b/src/app/calculators/macrorugo/config.json @@ -5,7 +5,10 @@ "fields": [ "ZF1", "L", - "Ks" + { + "id": "Ks", + "help": "pam/macrorugo.html#rugosite-de-fond" + } ] }, { @@ -41,4 +44,4 @@ "type": "options", "help": "pam/macrorugo.html" } -] \ No newline at end of file +] diff --git a/src/app/calculators/macrorugocompound/config.json b/src/app/calculators/macrorugocompound/config.json index 496a352629be9d42c5f98a37d5052df441743b16..8df7adca30b829076b1b3929bd2548686112fde9 100644 --- a/src/app/calculators/macrorugocompound/config.json +++ b/src/app/calculators/macrorugocompound/config.json @@ -15,7 +15,10 @@ "id": "If", "help": "hsl/pente.html" }, - "Ks", + { + "id": "Ks", + "help": "pam/macrorugo.html#rugosite-de-fond" + }, { "id": "C", "help": "pam/concentration.html" @@ -43,8 +46,14 @@ "type": "select", "property": "inclinedApron" }, - "ZRR", - "ZRL", + { + "id": "ZRR", + "help": "pam/macrorugo.html#rugosite-de-fond" + }, + { + "id": "ZRL", + "help": "pam/macrorugo.html#rugosite-de-fond" + }, "BR" ] }, @@ -53,7 +62,10 @@ "type": "fieldset_template", "calcType": "MacroRugo", "fields": [ - "ZF1", + { + "id": "ZF1", + "help": "pam/macrorugo.html#rugosite-de-fond" + }, "B" ] }, @@ -69,4 +81,4 @@ "selectIds": [ "select_passtype" ], "help": "pam/macrorugo_complexe.html" } -] \ No newline at end of file +] diff --git a/src/app/components/remous-results/remous-results.component.ts b/src/app/components/remous-results/remous-results.component.ts index 88a38d36647ccd4aee85a011001154ac7898e943..1f5ea4b720bfcb4bbf0ccbd85f42bde4dc582540 100644 --- a/src/app/components/remous-results/remous-results.component.ts +++ b/src/app/components/remous-results/remous-results.component.ts @@ -374,7 +374,7 @@ export class RemousResultsComponent extends ResultsComponentDirective implements } else { lineExtra.data = { label: this.extraParamLabel, - tension: 0, fill: false, spanGaps: true, borderColor: "#C17AF0", pointRadius: 4, showLine: "true" + tension: 0, fill: false, spanGaps: true, borderColor: "#C17AF0", pointBackgroundColor: "#C17AF0", pointRadius: 4, backgroundColor: "#C17AF0", showLine: "true" }; } } @@ -452,7 +452,7 @@ export class RemousResultsComponent extends ResultsComponentDirective implements tooltip: { callbacks: { label: function (tooltipItem) { - return fv(Number(tooltipItem.formattedValue)); + return fv(tooltipItem.dataset.data[tooltipItem.dataIndex].y); } } }, @@ -496,8 +496,10 @@ export class RemousResultsComponent extends ResultsComponentDirective implements }, ticks: { precision: ResultsComponentDirective.CHARTS_AXIS_PRECISION, - callback: function (value, index, values) { - return fv(Number(value)); + callback: (value, index, values) => { + // pour une raison mystérieuse, values ne contient pas les x fournis mais des valeurs générées, + // et donc value est inutilisable + return fv(gr2.data.datasets[0].data[index].x); } }, } @@ -510,7 +512,10 @@ export class RemousResultsComponent extends ResultsComponentDirective implements tooltip: { callbacks: { label: function (tooltipItem) { - return fv(Number(tooltipItem.formattedValue)); + return fv(tooltipItem.dataset.data[tooltipItem.dataIndex].y); + }, + title: () => { // pour empêcher le tooltip de contenir l'abscisse du point + return undefined; // pq a t-on besoin de ce callback dans ce graphe et pas dans l'autre ? mystère aussi... } } }, diff --git a/src/app/components/section-canvas/section-canvas.component.html b/src/app/components/section-canvas/section-canvas.component.html index da242b966d3d4eb18ed9873ad32e8779fec1ce6b..fc687f76cb6b9420bdf574549d9df16370711eee 100644 --- a/src/app/components/section-canvas/section-canvas.component.html +++ b/src/app/components/section-canvas/section-canvas.component.html @@ -1,2 +1,7 @@ -<canvas #canvas [attr.width]="width" [attr.height]="height"> -</canvas> +<div fxLayout="column" fxLayoutAlign="center center"> + <canvas #canvas [attr.width]="width" [attr.height]="height"> + </canvas> + <mat-checkbox [ngModel]="useRealAspectRatio" (ngModelChange)="setUseRealAspectRatio($event)"> + {{ uitextUseRealRatio }} + </mat-checkbox> +</div> \ No newline at end of file diff --git a/src/app/components/section-canvas/section-canvas.component.ts b/src/app/components/section-canvas/section-canvas.component.ts index 41364eac3afb02d9d0c18364d1494b1460973bdb..cc5f23320ca1bbd9e200af565867e1c26035e866 100644 --- a/src/app/components/section-canvas/section-canvas.component.ts +++ b/src/app/components/section-canvas/section-canvas.component.ts @@ -1,4 +1,5 @@ import { Component, ViewChild, Input, OnChanges, AfterViewInit, ElementRef } from "@angular/core"; +import { I18nService } from "app/services/internationalisation.service"; import { acSection, cSnTrapez, ParamsSectionTrapez, cSnRectang, ParamsSectionRectang, cSnCirc, @@ -26,32 +27,77 @@ export class SectionCanvasComponent extends ResultsComponentDirective implements "Y": { r: 50, g: 50, b: 50 } }; - /** marges gauche/droite pour le texte (pixels) */ - private _textMargin = 90; + /* + * notes : + * - l'origine des coordonnées en m est par ex pour la section rectangulaire le coin en bas (radier) à gauche de la section + * - canal : partie physique, sans les niveaux d'eau + * - section : canal + niveaux d'eau + * - rendu : section + texte + pointillés en haut + * - W : dimension horizontale, H : dimension verticale + * _ _m : valeur en mètres, _pix : valeur en pixels + */ + + /** largeur gauche/droite du texte (pixels) */ + private readonly _textWidth = 110; /** marge basse (pixels) */ - private _bottomMargin = 5; + private readonly _bottomMargin = 5; + + /** marge haute pour les pointillés (pixels) */ + private readonly _topDashMargin = 30; + + /** largeur des repères des niveaux sur le bord de la section (pixels) */ + private readonly _levelMark = 5; /** facteurs d'échelle (coordonnées en m <-> coordonnées en pixels) */ private _scaleX: number; private _scaleY: number; + /** origine (pixels) */ + private _origX: number; + private _origY: number; + private _section: acSection; private _result: Result; + /** + * paramètre de taille du canvas en entrée (pixels) + */ private _size: number; + /** + * taille horizontale de la section (m) + */ + private _Wsect_m: number; + + /** + * taille verticale de la section (m) + */ + private _Hsect_m: number; + + /** + * taille horizontale de la section (pixels) + */ + private _Wsect_pix: number; + + /** + * taille verticale de la section (pixels) + */ + private _Hsect_pix: number; + // tirants private _levels: Object[] = []; + constructor(private intlService: I18nService) { + super(); + } + public get width(): number { - // return this._calcCanvas.nativeElement.width; return this._size; } public get height(): number { - // return this._calcCanvas.nativeElement.height; return this._size; } @@ -75,8 +121,14 @@ export class SectionCanvasComponent extends ResultsComponentDirective implements this._size = s; } + // respect du rapport abscisses/ordonnées + private _useRealAspectRatio: boolean = false; + // redessine le canvas chaque fois qu'une entrée change public ngOnChanges() { + this.computeSectionWidth(); + this.computeSectionHeight(); + this.computeTransform(); setTimeout(() => { this.draw(); }, 10); @@ -84,18 +136,126 @@ export class SectionCanvasComponent extends ResultsComponentDirective implements public ngAfterViewInit() { // wait for the view to init before using the element this._context2d = this._calcCanvas.nativeElement.getContext("2d"); + } + + /** + * calcul des tailles horizontale et verticale de la zone de rendu (pixels) + * @returns true si le rendu touche le haut et le bas du canvas + */ + private computeRenderSize(): boolean { + let res: boolean; + if (this._useRealAspectRatio) { + // largeur en pixels pour la représentation de la section + const a = this.width - 2 * (this._textWidth + this._levelMark); + + // calcul des rapports largeur/hauteur pour le canvas et la section + const rlim = a / this.height; + const rsect = this._Wsect_m / this._Hsect_m; + + if (rsect <= rlim) { + this._Hsect_pix = this.height - this._bottomMargin - this._topDashMargin; + this._Wsect_pix = this._Hsect_pix * rsect; + res = true; + } + else { + this._Wsect_pix = a; + this._Hsect_pix = this._Wsect_pix / rsect; + res = false; + } + + } else { + this._Wsect_pix = this.width - 2 * (this._textWidth + this._levelMark) + this._Hsect_pix = this.height - this._bottomMargin - this._topDashMargin; + res = true; + } + + return res; + } + + /** + * calcul du coef pour passer de m -> pixels + * et de l'origine du rendu + */ + private computeTransform() { + const b = this.computeRenderSize(); + + // échelle m -> pix + this._scaleX = this._Wsect_pix / this._Wsect_m; + this._scaleY = this._Hsect_pix / this._Hsect_m; + // origine + this._origX = (this._size - this._Wsect_pix) / 2; + this._origY = b ? this._bottomMargin : (this._size - this._Hsect_pix) / 2; + } + + /** + * convertit une abscisse en m en pixels + */ + private Xm2pix(x: number) { + return this._origX + x * this._scaleX; + } + + /** + * convertit une ordonnée en m en pixels + */ + private Ym2pix(y: number) { + return this._size - this._origY - y * this._scaleY; + } + + public get useRealAspectRatio(): boolean { + return this._useRealAspectRatio; + } + + public setUseRealAspectRatio(b: boolean) { + this._useRealAspectRatio = b; + this.computeTransform(); this.draw(); } - public reset() { + private setLevels() { this._levels = []; - this.clear(); + + // traduction des symboles des variables calculées + const re = this._result.resultElement; + if (re !== undefined) { + for (const k in re.values) { + if (k !== re.vCalcSymbol) { + //const lbl = k.toUpperCase(); + const er = re.getValue(k); + // this._resultElement.addExtraResult(lbl, er); + + if (this.isSectionLevel(k)) { + this.addLevel(er, k + " = " + this.formattedValue(er), SectionCanvasComponent.labelColors[k]); + } + } + } + } + + // ajout du tirant d'eau saisi + const valY = this._result.sourceNub.getParameter("Y").singleValue; + this.addLevel(valY, "Y = " + this.formattedValue(valY), SectionCanvasComponent.labelColors["Y"]); + + this.sortLevels(); } - public addLevel(val: number, label: string, rgb: {}) { + private addLevel(val: number, label: string, rgb: {}) { this._levels.push({ val, label, rgb }); } + /** + * trie les tirants par niveau d'eau croissant + */ + private sortLevels() { + this._levels.sort((a, b) => { + if (a["val"] < b["val"]) { + return -1; + } + if (a["val"] > b["val"]) { + return 1; + } + return 0; + }); + } + // max des niveaux à représenter private getMaxLevel(): number { // max de la cote de berge et des niveaux (qui sont triés) @@ -111,35 +271,34 @@ export class SectionCanvasComponent extends ResultsComponentDirective implements return false; } + /** + * calcul des tailles horizontale et verticale de la section (m) + */ + private computeSectionWidth() { + if (this._section instanceof cSnTrapez) { + this.computeSectionWidthTrapez(); + } + else if (this._section instanceof cSnRectang) { + this.computeSectionWidthRect(); + } + else if (this._section instanceof cSnCirc) { + this.computeSectionWidthCirc(); + } + else if (this._section instanceof cSnPuiss) { + this.computeSectionWidthPara(); + } + else + throw new Error("SectionCanvasComponent.computeSectionWidth() : type de section non pris en charge"); + } + public draw() { - // console.log(">> redrawing at size", this._size); if (this._context2d && this._section) { - this.reset(); - - // traduction des symboles des variables calculées - const re = this._result.resultElement; - if (re !== undefined) { - for (const k in re.values) { - if (k !== re.vCalcSymbol) { - const lbl = k.toUpperCase(); - const er = re.getValue(k); - // this._resultElement.addExtraResult(lbl, er); - - if (this.isSectionLevel(k)) { - this.addLevel(er, k + " = " + this.formattedValue(er), SectionCanvasComponent.labelColors[k]); - } - } - } - } - - // ajout du tirant d'eau saisi - const valY = this._result.sourceNub.getParameter("Y").singleValue; - this.addLevel(valY, "Y = " + this.formattedValue(valY), SectionCanvasComponent.labelColors["Y"]); - - this.sortLevels(); - this.drawFrame(); - const maxWidth = this.drawSection(); - this.drawLevels(maxWidth); + this.clear(); + setTimeout(() => { // à cause du changement de taille du canvas dans updateCanvasSize() + this.drawFrame(); + this.drawSection(); + this.drawLevels(); + }, 10); } } @@ -149,32 +308,44 @@ export class SectionCanvasComponent extends ResultsComponentDirective implements * yb : cote de berge * maxHeight : valeur maxi des cotes */ - private drawTopDashLines(xmin: number, xmax: number, yb: number, maxHeight: number) { + private drawTopDashLines(xmin: number, xmax: number, yb: number) { this.setLineWidth(2); this.setLineDash([5]); this.setStrokeColor(128, 128, 128); - this.drawSectionLine(xmin, yb, xmin, maxHeight); - this.drawSectionLine(xmax, yb, xmax, maxHeight); + const x1 = this.Xm2pix(xmin); + const ybp = this.Ym2pix(yb); + this.drawLine(x1, ybp, x1, 0); + const x2 = this.Xm2pix(xmax); + this.drawLine(x2, ybp, x2, 0); } - private drawSectionTrapez(): number { + private computeSectionHeight() { + this.setLevels(); + + // hauteur totale de la section + this._Hsect_m = this.getMaxLevel(); + } + + private computeSectionWidthTrapez() { const sect: cSnTrapez = <cSnTrapez>this._section; const prms: ParamsSectionTrapez = <ParamsSectionTrapez>sect.prms; - // cote de berge - const yb: number = prms.YB.v; - // largeur de la partie pentue const lp: number = prms.Fruit.v * prms.YB.v; // largeur totale de la section - const maxWidth: number = lp * 2 + prms.LargeurFond.v; + this._Wsect_m = lp * 2 + prms.LargeurFond.v; + } - // hauteur totale de la section - let maxHeight: number = this.getMaxLevel(); - maxHeight *= 1.1; + private drawSectionTrapez() { + const sect: cSnTrapez = <cSnTrapez>this._section; + const prms: ParamsSectionTrapez = <ParamsSectionTrapez>sect.prms; + + // cote de berge + const yb: number = prms.YB.v; - this.computeScale(maxWidth, maxHeight); + // largeur de la partie pentue + const lp: number = prms.Fruit.v * prms.YB.v; // dessin de la section @@ -182,13 +353,19 @@ export class SectionCanvasComponent extends ResultsComponentDirective implements this.setLineWidth(5); this.drawSectionLine(0, yb, lp, 0); this.drawSectionLine(lp, 0, lp + prms.LargeurFond.v, 0); - this.drawSectionLine(lp + prms.LargeurFond.v, 0, maxWidth, yb); + this.drawSectionLine(lp + prms.LargeurFond.v, 0, this._Wsect_m, yb); // pointillés du haut - this.drawTopDashLines(0, maxWidth, yb, maxHeight); + this.drawTopDashLines(0, this._Wsect_m, yb); + } + + private computeSectionWidthRect() { + const sect: cSnRectang = <cSnRectang>this._section; + const prms: ParamsSectionRectang = <ParamsSectionRectang>sect.prms; - return maxWidth; + // largeur totale de la section + this._Wsect_m = prms.LargeurBerge.v; } private drawSectionRect() { @@ -198,28 +375,39 @@ export class SectionCanvasComponent extends ResultsComponentDirective implements // cote de berge const yb: number = prms.YB.v; - // largeur totale de la section - const maxWidth: number = prms.LargeurBerge.v; - - // hauteur totale de la section - let maxHeight: number = this.getMaxLevel(); - maxHeight *= 1.1; - - this.computeScale(maxWidth, maxHeight); // dessin de la section this.setStrokeColor(0, 0, 0); this.setLineWidth(5); this.drawSectionLine(0, yb, 0, 0); - this.drawSectionLine(0, 0, prms.LargeurBerge.v, 0); - this.drawSectionLine(prms.LargeurBerge.v, 0, maxWidth, yb); + this.drawSectionLine(0, 0, this._Wsect_m, 0); + this.drawSectionLine(this._Wsect_m, 0, this._Wsect_m, yb); // pointillés du haut - this.drawTopDashLines(0, maxWidth, yb, maxHeight); + this.drawTopDashLines(0, this._Wsect_m, yb); + } + + private computeSectionWidthCirc() { + const sect: cSnCirc = <cSnCirc>this._section; + const prms: ParamsSectionCirc = <ParamsSectionCirc>sect.prms; + + // cote de berge + const yb: number = prms.YB.v; - return maxWidth; + // diamètre, rayon + const D: number = prms.D.v; + const r: number = D / 2; + + // largeur au miroir + const B: Result = sect.CalcSection("B", yb); + if (!B.ok) { + throw B; + } + + // largeur totale de la section + this._Wsect_m = yb < r ? B.vCalc : D; } private drawSectionCirc() { @@ -240,21 +428,12 @@ export class SectionCanvasComponent extends ResultsComponentDirective implements throw B; } - // largeur totale de la section - const maxWidth: number = yb < r ? B.vCalc : D; - - // hauteur totale de la section - let maxHeight: number = this.getMaxLevel(); - maxHeight *= 1.1; - - this.computeScale(maxWidth, maxHeight); - // dessin de la section this.setStrokeColor(0, 0, 0); this.setLineWidth(5); - const wx: number = maxWidth / 2; + const wx: number = this._Wsect_m / 2; const alpha: Result = sect.CalcSection("Alpha", yb); if (!alpha.ok) { throw alpha; @@ -267,33 +446,33 @@ export class SectionCanvasComponent extends ResultsComponentDirective implements // pointillés du haut const w: number = yb > r ? (D - B.vCalc) / 2 : 0; - this.drawTopDashLines(w, maxWidth - w, yb, maxHeight); - - return maxWidth; + this.drawTopDashLines(w, this._Wsect_m - w, yb); } catch (e) { const res: Result = e as Result; this.drawText("error : " + res.log.toString(), 0, 0); } } - private drawSectionPara() { + private computeSectionWidthPara() { const sect: cSnPuiss = <cSnPuiss>this._section; const prms: ParamsSectionPuiss = <ParamsSectionPuiss>sect.prms; - // cote de berge - const yb: number = prms.YB.v; - // largeur au miroir const B: number = prms.LargeurBerge.v; // largeur totale de la section - const maxWidth: number = B; + this._Wsect_m = B; + } - // hauteur totale de la section - let maxHeight: number = this.getMaxLevel(); - maxHeight *= 1.1; + private drawSectionPara() { + const sect: cSnPuiss = <cSnPuiss>this._section; + const prms: ParamsSectionPuiss = <ParamsSectionPuiss>sect.prms; + + // cote de berge + const yb: number = prms.YB.v; - this.computeScale(maxWidth, maxHeight); + // largeur au miroir + const B: number = prms.LargeurBerge.v; // contour de la section @@ -312,17 +491,24 @@ export class SectionCanvasComponent extends ResultsComponentDirective implements this._context2d.stroke(); // pointillés du haut - this.drawTopDashLines(0, maxWidth, yb, maxHeight); - - return maxWidth; + this.drawTopDashLines(0, this._Wsect_m, yb); } private drawSectionEllipse(x: number, y: number, rX: number, rY: number, rot: number, start: number, end: number) { this.drawEllipse(this.Xm2pix(x), this.Ym2pix(y), rX * this._scaleX, rY * this._scaleY, rot, start, end); } + /** + * dessine un texte + * @param s texte à dessiner + * @param x position horizontale (m) + * @param y position verticale (m) + * @param align alignement "left" ou "right" cf. https://developer.mozilla.org/en-US/docs/Web/API/CanvasRenderingContext2D/textAlign + */ private drawText(s: string, x: number, y: number, align?: string) { - this.fillText(s, this.Xm2pix(x), this.Ym2pix(y), align); + // décalage du texte par rapport au bord gauche/droit de la section + const shiftX = align === undefined ? 0 : (align === "left" ? this._levelMark : -this._levelMark); + this.fillText(s, this.Xm2pix(x) + shiftX, this.Ym2pix(y), align); } private drawSectionLine(x1: number, y1: number, x2: number, y2: number) { @@ -333,86 +519,97 @@ export class SectionCanvasComponent extends ResultsComponentDirective implements * dessin de la section * @returns largeur de la section (en m) */ - private drawSection(): number { + private drawSection() { if (this._section instanceof cSnTrapez) { - return this.drawSectionTrapez(); + this.drawSectionTrapez(); } - if (this._section instanceof cSnRectang) { - return this.drawSectionRect(); + else if (this._section instanceof cSnRectang) { + this.drawSectionRect(); } - if (this._section instanceof cSnCirc) { - return this.drawSectionCirc(); + else if (this._section instanceof cSnCirc) { + this.drawSectionCirc(); } - if (this._section instanceof cSnPuiss) { - return this.drawSectionPara(); + else if (this._section instanceof cSnPuiss) { + this.drawSectionPara(); } - throw new Error("SectionCanvasComponent.drawSection() : type de section non pris en charge"); - } - - private computeScale(maxWidth: number, maxHeight: number) { - this._scaleX = (this._size - 2 * this._textMargin) / maxWidth; - this._scaleY = (this._size - this._bottomMargin) / maxHeight; + else + throw new Error("SectionCanvasComponent.drawSection() : type de section non pris en charge"); } /** - * convertit une abscisse en m en pixels + * dessin des niveaux en gérant le chevauchement + * @param levels liste des niveaux à tracer + * @param textHeight hauteur minimal entre le texte des niveaux (m) + * @param left true pour tracer le texte à gauche du niveau, false à droite */ - private Xm2pix(x: number) { - return this._textMargin + x * this._scaleX; - } + private drawLevelsWithoutOverlap(levels: any, textHeight: number, left: boolean) { + for (let i = levels.length - 1; i >= 0; i--) { + const l = levels[i]; - /** - * convertit une ordonnée en m en pixels - */ - private Ym2pix(y: number) { - return this._size - this._bottomMargin - y * this._scaleY; - } + // chevauchement avec le précédent ? + if (i < levels.length - 1) { + let yprec = levels[i + 1].y; - private sortLevels() { - this._levels.sort((a, b) => { - if (a["val"] < b["val"]) { - return -1; + const ycurr = l.y; + if (yprec - ycurr < textHeight) { + l.y = yprec - textHeight; + } } - if (a["val"] > b["val"]) { - return 1; + + // tracé du tirant courant + const col = l["rgb"]; + this.setStrokeColor(col["r"], col["g"], col["b"]); + this.drawSectionLine(0, l.val, this._Wsect_m, l.val); + this.setFillColor(col["r"], col["g"], col["b"]); + + if (left) { + this.drawText(l["label"], 0, l.y, "right"); + } else { + this.drawText(l["label"], this._Wsect_m, l.y, "left"); } - return 0; - }); + } } - private drawLevels(maxWidth: number) { - let left = true; - + private drawLevels() { this.resetLineDash(); this.setLineWidth(1); - this.setFont("12px sans- serif"); + this.setFont("12px sans-serif"); + + // hauteur des caractères + const tm: TextMetrics = this._context2d.measureText("Ag"); + const charHeightPix = tm.actualBoundingBoxAscent + tm.actualBoundingBoxDescent; + const charHeightMeter = charHeightPix / this._scaleY; + + // sépare les niveaux de gauche/droite + const leftLevels = []; + const rightLevels = []; + let left = true; for (const l of this._levels) { const y = l["val"]; - const col = l["rgb"]; - this.setStrokeColor(col["r"], col["g"], col["b"]); - this.drawSectionLine(0, y, maxWidth, y); - - this.setFillColor(col["r"], col["g"], col["b"]); + Object.assign(l, { "y": y }); // y = ordonnée de tracé if (left) { - this.drawText(l["label"], -0.1, y, "right"); + leftLevels.push(l); } else { - this.drawText(l["label"], maxWidth + 0.1, y, "left"); + rightLevels.push(l); } left = !left; } + + // dessin des textes + this.drawLevelsWithoutOverlap(leftLevels, charHeightMeter, true); + this.drawLevelsWithoutOverlap(rightLevels, charHeightMeter, false); } // contour du canvas private drawFrame() { - this.clear(); this.resetLineDash(); this.setStrokeColor(128, 128, 128); - this.drawRect(0, 0, this.width, this.height); + this.drawRect(0, 0, this._size, this._size); } public clear() { if (this._context2d) { - this._context2d.clearRect(0, 0, this.width, this.height); + this._context2d.clearRect(0, 0, this._size, this._size); } } @@ -426,10 +623,17 @@ export class SectionCanvasComponent extends ResultsComponentDirective implements this._context2d.fillStyle = col; } - public setFont(f: string) { + private setFont(f: string) { this._context2d.font = f; } + /** + * dessine un texte sur le canvas + * @param s texte à dessiner + * @param x position horizontale (pixels) + * @param y position verticale (pixels) + * @param align alignement "left" ou "right" cf. https://developer.mozilla.org/en-US/docs/Web/API/CanvasRenderingContext2D/textAlign + */ public fillText(s: string, x: number, y: number, align?: any) { if (align) { this._context2d.textAlign = align; @@ -475,4 +679,8 @@ export class SectionCanvasComponent extends ResultsComponentDirective implements this._context2d.ellipse(x, y, radiusX, radiusY, rotation, startAngle, endAngle); this._context2d.stroke(); } + + public get uitextUseRealRatio(): string { + return this.intlService.localizeText("INFO_SECTIONPARAMETREE_REAL_RATIO"); + } } diff --git a/src/locale/messages.en.json b/src/locale/messages.en.json index 47688d87d2ba2a837da73976e70fb790006c475f..4416adeefc9d8d8fd7f01f41a3cbef17676693aa 100755 --- a/src/locale/messages.en.json +++ b/src/locale/messages.en.json @@ -587,6 +587,7 @@ "INFO_SECTIONPARAMETREE_DESCRIPTION": "open-channel canal rectangular circular trapezoidal depth head normal critical conjugate corresponding subcritical supercritical Froude", "INFO_SECTIONPARAMETREE_TITRE_COURT": "Param. section", "INFO_SECTIONPARAMETREE_TITRE": "Parametric section", + "INFO_SECTIONPARAMETREE_REAL_RATIO": "Apply 1:1 scale", "INFO_SELECT_MULTIPLE_AND_OTHER": "other", "INFO_SELECT_MULTIPLE_AND_OTHERS": "others", "INFO_SETUP_ENABLE_HOTKEYS": "Enable keyboard shortcuts", diff --git a/src/locale/messages.fr.json b/src/locale/messages.fr.json index edbbcf847f55fd237f850dd13ff2d85ea39a485b..4161aea6c0d2697cff2267b91eeb73ed698299ed 100755 --- a/src/locale/messages.fr.json +++ b/src/locale/messages.fr.json @@ -588,6 +588,7 @@ "INFO_SECTIONPARAMETREE_DESCRIPTION": "hydraulique à surface libre canal chenal bief rectangulaire circulaire puissance trapézoïdale périmètre charge mouillée rugosité hauteur charge critique normal conjuguée correspondante fluvial torrentiel Froude", "INFO_SECTIONPARAMETREE_TITRE_COURT": "Sec. param.", "INFO_SECTIONPARAMETREE_TITRE": "Section paramétrée", + "INFO_SECTIONPARAMETREE_REAL_RATIO": "Respecter l'échelle", "INFO_SELECT_MULTIPLE_AND_OTHER": "autre", "INFO_SELECT_MULTIPLE_AND_OTHERS": "autres", "INFO_SETUP_ENABLE_HOTKEYS": "Activer les raccourcis clavier",