From 8933e858b0ab1893fff1e6c48df0a18488703674 Mon Sep 17 00:00:00 2001
From: "mathias.chouet" <mathias.chouet@irstea.fr>
Date: Thu, 12 Sep 2019 15:35:31 +0200
Subject: [PATCH 1/7] #287 added prototype for Jet module

---
 README.md                               |  5 +++--
 src/app/calculators/jet/jet.config.json | 12 ++++++++++++
 src/app/calculators/jet/jet.en.json     | 21 +++++++++++++++++++++
 src/app/calculators/jet/jet.fr.json     | 21 +++++++++++++++++++++
 src/app/config.json                     | 10 +++++++++-
 src/app/services/formulaire.service.ts  |  2 ++
 src/locale/messages.en.json             |  4 ++++
 src/locale/messages.fr.json             |  4 ++++
 8 files changed, 76 insertions(+), 3 deletions(-)
 create mode 100644 src/app/calculators/jet/jet.config.json
 create mode 100644 src/app/calculators/jet/jet.en.json
 create mode 100644 src/app/calculators/jet/jet.fr.json

diff --git a/README.md b/README.md
index 319797424..4b93c0343 100644
--- a/README.md
+++ b/README.md
@@ -267,8 +267,9 @@ Custom Material SVG Icons will only show up when the application is deployed on
 		On peut soit composer la classe concrète directement avec ces classes, soient dériver ces dernières et composer avec.
 
 3. _src/locale/messages.&lt;langue&gt;.json_ :
-	Ajouter un champ pour le titre du module de calcul. Par exemple :
-		 _"INFO_MACALC_TITRE": "Ma calculette"_
+	Ajouter deux champs pour le titre et le titre court du module de calcul. Par exemple :
+		 _"INFO_MACALC_TITRE": "Ma calculette"_,
+		 _"INFO_MACALC_TITRE_COURT": "Ma calc."_
 
 4. Dans le constructeur de _FormulaireService_, ajouter une entrée dans `this.calculatorPaths` pour fournir le préfixe des fichiers de configuration/internationalisation.
 
diff --git a/src/app/calculators/jet/jet.config.json b/src/app/calculators/jet/jet.config.json
new file mode 100644
index 000000000..857c2018e
--- /dev/null
+++ b/src/app/calculators/jet/jet.config.json
@@ -0,0 +1,12 @@
+[
+    {
+        "id": "fs_jet",
+        "type": "fieldset",
+        "fields": [ "V0", "S", "D", "H" ]
+    },
+    {
+        "type": "options",
+        "__idCal": "D",
+        "help": "devalaison/jet"
+    }
+]
\ No newline at end of file
diff --git a/src/app/calculators/jet/jet.en.json b/src/app/calculators/jet/jet.en.json
new file mode 100644
index 000000000..67fc3ad49
--- /dev/null
+++ b/src/app/calculators/jet/jet.en.json
@@ -0,0 +1,21 @@
+{
+    "fs_jet": "Jet and impact parameters",
+
+    "V0": "Initial speed",
+    "S": "Initial slope",
+    "D": "Impact abscissa",
+    "H": "Fall height",
+    "t": "Flight time",
+    "Vx": "Horizontal speed at impact",
+    "Vz": "Vertical speed at impact",
+    "Vt": "Impact speed at impact",
+
+    "UNIT_V0": "m/s",
+    "UNIT_S": "m/m",
+    "UNIT_D": "m",
+    "UNIT_H": "m",
+    "UNIT_T": "s",
+    "UNIT_VX": "m/s",
+    "UNIT_VZ": "m/s",
+    "UNIT_VT": "m/s"
+}
\ No newline at end of file
diff --git a/src/app/calculators/jet/jet.fr.json b/src/app/calculators/jet/jet.fr.json
new file mode 100644
index 000000000..af50a3b85
--- /dev/null
+++ b/src/app/calculators/jet/jet.fr.json
@@ -0,0 +1,21 @@
+{
+    "fs_jet": "Paramètres du jet et de l'impact",
+
+    "V0": "Vitesse initiale",
+    "S": "Pente initiale",
+    "D": "Abscisse de l'impact",
+    "H": "Hauteur de chute",
+    "t": "Temps de vol",
+    "Vx": "Vitesse horizontale à l'impact",
+    "Vz": "Vitesse verticale à l'impact",
+    "Vt": "Vitesse d'impact à l'impact",
+
+    "UNIT_V0": "m/s",
+    "UNIT_S": "m/m",
+    "UNIT_D": "m",
+    "UNIT_H": "m",
+    "UNIT_T": "s",
+    "UNIT_VX": "m/s",
+    "UNIT_VZ": "m/s",
+    "UNIT_VT": "m/s"
+}
\ No newline at end of file
diff --git a/src/app/config.json b/src/app/config.json
index c2ec2a974..41449c8e1 100644
--- a/src/app/config.json
+++ b/src/app/config.json
@@ -14,7 +14,7 @@
                 "path": "passe-bassin.jpg",
                 "credits": "S. Richard / AFB"
             },
-            "calculators": [ 12, 13, 6, 5, 10, 15, 9 ]
+            "calculators": [ 12, 13, 6, 5, 10, 15 ]
         },
         {
             "name": "PASSE_NATURELLE",
@@ -24,6 +24,14 @@
             },
             "calculators": [ 11, 17 ]
         },
+        {
+            "name": "DEVALAISON",
+            "image": {
+                "path": "surface-libre.jpg",
+                "credits": "PENSER À CHANGER CETTE IMAGE"
+            },
+            "calculators": [ 18, 3, 9 ]
+        },
         {
             "name": "HYDRAULIQUE_A_SURFACE_LIBRE",
             "image": {
diff --git a/src/app/services/formulaire.service.ts b/src/app/services/formulaire.service.ts
index d880cb7d3..5bfbb27c0 100644
--- a/src/app/services/formulaire.service.ts
+++ b/src/app/services/formulaire.service.ts
@@ -77,6 +77,7 @@ export class FormulaireService extends Observable {
         this.calculatorPaths[CalculatorType.MacroRugo] = "macrorugo";
         this.calculatorPaths[CalculatorType.Pab] = "pab";
         this.calculatorPaths[CalculatorType.MacroRugoCompound] = "macrorugo-compound";
+        this.calculatorPaths[CalculatorType.Jet] = "jet";
     }
 
     private get _intlService(): I18nService {
@@ -326,6 +327,7 @@ export class FormulaireService extends Observable {
      * @param calculatorName nom du module, à afficher dans l'interface
      */
     public createFormulaire(ct: CalculatorType, nub?: Nub, calculatorName?: string): Promise<FormulaireDefinition> {
+        console.log(">> Create form !!", ct);
         // Crée un formulaire du bon type
         const f: FormulaireDefinition = this.newFormulaire(ct);
         this._formulaires.push(f);
diff --git a/src/locale/messages.en.json b/src/locale/messages.en.json
index 51c6f4652..57ac348e1 100644
--- a/src/locale/messages.en.json
+++ b/src/locale/messages.en.json
@@ -146,6 +146,8 @@
     "INFO_FIELDSET_MOVE_DOWN": "Move down",
     "INFO_FIELDSET_MOVE_LEFT": "Move left",
     "INFO_FIELDSET_MOVE_RIGHT": "Move right",
+    "INFO_JET_TITRE_COURT": "Jet",
+    "INFO_JET_TITRE": "Jet impact",
     "INFO_WALL_ADDED": "1 wall added",
     "INFO_WALL_ADDED_N_TIMES": "%s walls added",
     "INFO_WALL_COPIED": "Wall #%s copied",
@@ -408,6 +410,8 @@
     "INFO_SNACKBAR_RESULTS_INVALIDATED": "Results invalidated for",
     "INFO_SNACKBAR_SETTINGS_SAVED": "Settings saved on this device",
     "INFO_THEME_CREDITS": "Credit",
+    "INFO_THEME_DEVALAISON_TITRE": "Downstream migration",
+    "INFO_THEME_DEVALAISON_DESCRIPTION": "",
     "INFO_THEME_HYDRAULIQUE_A_SURFACE_LIBRE_DESCRIPTION": "Calculation modules for flows in channels and rivers",
     "INFO_THEME_HYDRAULIQUE_A_SURFACE_LIBRE_TITRE": "Open-channel flow",
     "INFO_THEME_HYDRAULIQUE_EN_CHARGE_DESCRIPTION": "Modules for calculating head losses in pressure pipes",
diff --git a/src/locale/messages.fr.json b/src/locale/messages.fr.json
index 217824715..69ae2bef5 100644
--- a/src/locale/messages.fr.json
+++ b/src/locale/messages.fr.json
@@ -146,6 +146,8 @@
     "INFO_FIELDSET_MOVE_DOWN": "Déplacer vers le bas",
     "INFO_FIELDSET_MOVE_LEFT": "Déplacer vers la gauche",
     "INFO_FIELDSET_MOVE_RIGHT": "Déplacer vers la droite",
+    "INFO_JET_TITRE_COURT": "Jet",
+    "INFO_JET_TITRE": "Impact de jet",
     "INFO_WALL_ADDED": "1 cloison ajoutée",
     "INFO_WALL_ADDED_N_TIMES": "%s cloisons ajoutées",
     "INFO_WALL_COPIED": "Cloison n°%s copiée",
@@ -407,6 +409,8 @@
     "INFO_SNACKBAR_RESULTS_INVALIDATED": "Résultats invalidés pour",
     "INFO_SNACKBAR_SETTINGS_SAVED": "Paramètres enregistrés sur cet appareil",
     "INFO_THEME_CREDITS": "Crédit",
+    "INFO_THEME_DEVALAISON_TITRE": "Dévalaison",
+    "INFO_THEME_DEVALAISON_DESCRIPTION": "PENSER À METTRE UNE DESCRIPTION ICI",
     "INFO_THEME_HYDRAULIQUE_A_SURFACE_LIBRE_DESCRIPTION": "Modules de calcul pour les écoulements en canaux et rivières",
     "INFO_THEME_HYDRAULIQUE_A_SURFACE_LIBRE_TITRE": "Hydraulique à surface libre",
     "INFO_THEME_HYDRAULIQUE_EN_CHARGE_DESCRIPTION": "Modules de calcul de perte de charge dans les conduites sous pression",
-- 
GitLab


From 14dd419344331024a66490ea18897dd7adb39ea4 Mon Sep 17 00:00:00 2001
From: "mathias.chouet" <mathias.chouet@irstea.fr>
Date: Thu, 12 Sep 2019 15:36:02 +0200
Subject: [PATCH 2/7] Update jalhyd_branch

---
 jalhyd_branch | 2 +-
 1 file changed, 1 insertion(+), 1 deletion(-)

diff --git a/jalhyd_branch b/jalhyd_branch
index 1f7391f92..924865eab 100644
--- a/jalhyd_branch
+++ b/jalhyd_branch
@@ -1 +1 @@
-master
+112-ajout-du-module-devalaison-trajectoire-des-poissons
-- 
GitLab


From ce7ed5b796c87a6cd8ee8272281fab9154145649 Mon Sep 17 00:00:00 2001
From: "mathias.chouet" <mathias.chouet@irstea.fr>
Date: Thu, 12 Sep 2019 16:51:06 +0200
Subject: [PATCH 3/7] [WIP] #287 jet type results

---
 src/app/app.module.ts                         |  2 ++
 .../calculator-results.component.html         |  1 +
 .../calculator-results.component.ts           | 30 +++++++++++++++++--
 .../calculator.component.ts                   |  9 ++++++
 .../jet-results/jet-results.component.html    | 18 +++++++++++
 .../jet-results/jet-results.component.scss    |  4 +++
 .../jet-results/jet-results.component.ts      | 14 +++++++++
 .../form-iterator/deep-node-iterator.ts       | 10 -------
 src/app/results/multidimension-results.ts     |  2 +-
 9 files changed, 76 insertions(+), 14 deletions(-)
 create mode 100644 src/app/components/jet-results/jet-results.component.html
 create mode 100644 src/app/components/jet-results/jet-results.component.scss
 create mode 100644 src/app/components/jet-results/jet-results.component.ts
 delete mode 100644 src/app/formulaire/form-iterator/deep-node-iterator.ts

diff --git a/src/app/app.module.ts b/src/app/app.module.ts
index 88491f109..794becbda 100644
--- a/src/app/app.module.ts
+++ b/src/app/app.module.ts
@@ -90,6 +90,7 @@ import { QuicknavComponent } from "./components/quicknav/quicknav.component";
 import { ModulesDiagramComponent } from "./components/modules-diagram/modules-diagram.component";
 import { MacrorugoCompoundResultsTableComponent } from "./components/macrorugo-compound-results/macrorugo-compound-results-table.component";
 import { MacrorugoCompoundResultsComponent } from "./components/macrorugo-compound-results/macrorugo-compound-results.component";
+import { JetResultsComponent } from "./components/jet-results/jet-results.component";
 
 import { DialogConfirmEmptySessionComponent } from "./components/dialog-confirm-empty-session/dialog-confirm-empty-session.component";
 import { DialogConfirmCloseCalcComponent } from "./components/dialog-confirm-close-calc/dialog-confirm-close-calc.component";
@@ -193,6 +194,7 @@ const appRoutes: Routes = [
     JalhydModelValidationMinDirective,
     JalhydModelValidationMaxDirective,
     JalhydModelValidationStepDirective,
+    JetResultsComponent,
     LogComponent,
     LogEntryComponent,
     ModulesDiagramComponent,
diff --git a/src/app/components/calculator-results/calculator-results.component.html b/src/app/components/calculator-results/calculator-results.component.html
index 93748fe04..e15f90423 100644
--- a/src/app/components/calculator-results/calculator-results.component.html
+++ b/src/app/components/calculator-results/calculator-results.component.html
@@ -3,5 +3,6 @@
     <remous-results></remous-results>
     <pab-results></pab-results>
     <macrorugo-compound-results></macrorugo-compound-results>
+    <jet-results></jet-results>
     <fixedvar-results></fixedvar-results>
 </div>
diff --git a/src/app/components/calculator-results/calculator-results.component.ts b/src/app/components/calculator-results/calculator-results.component.ts
index 9dd4576c5..dcc7e97fb 100644
--- a/src/app/components/calculator-results/calculator-results.component.ts
+++ b/src/app/components/calculator-results/calculator-results.component.ts
@@ -1,4 +1,4 @@
-import { Component, ViewChild, Output, EventEmitter, AfterViewChecked } from "@angular/core";
+import { Component, ViewChild, Output, EventEmitter, AfterViewChecked, Inject, forwardRef } from "@angular/core";
 
 import { FixedVarResultsComponent } from "../../components/fixedvar-results/fixedvar-results.component";
 import { SectionResultsComponent } from "../../components/section-results/section-results.component";
@@ -6,12 +6,15 @@ import { RemousResultsComponent } from "../../components/remous-results/remous-r
 import { PabResultsComponent } from "../../components/pab-results/pab-results.component";
 import { MacrorugoCompoundResultsComponent } from "../macrorugo-compound-results/macrorugo-compound-results.component";
 import { FormulaireDefinition } from "../../formulaire/definition/form-definition";
+import { JetResultsComponent } from "../jet-results/jet-results.component";
+import { GenericCalculatorComponent } from "../generic-calculator/calculator.component";
 
 @Component({
     selector: "calc-results",
     templateUrl: "./calculator-results.component.html",
 })
 export class CalculatorResultsComponent implements AfterViewChecked {
+
     private _formulaire: FormulaireDefinition;
 
     /**
@@ -42,7 +45,13 @@ export class CalculatorResultsComponent implements AfterViewChecked {
      * composant d'affichage des résultats des passes à macrorugosités complexes
      */
     @ViewChild(MacrorugoCompoundResultsComponent, { static: true })
-    private mrcResultsComponent: PabResultsComponent;
+    private mrcResultsComponent: MacrorugoCompoundResultsComponent;
+
+    /**
+     * composant d'affichage des résultats des impacts de jet
+     */
+    @ViewChild(JetResultsComponent, { static: true })
+    private jetResultsComponent: JetResultsComponent;
 
     /**
      * événement émis à la fin du dessin de la vue
@@ -50,6 +59,10 @@ export class CalculatorResultsComponent implements AfterViewChecked {
     @Output()
     private afterViewChecked = new EventEmitter();
 
+    public constructor(
+        @Inject(forwardRef(() => GenericCalculatorComponent)) private calculatorComponent: GenericCalculatorComponent
+    ) { }
+
     public set formulaire(f: FormulaireDefinition) {
         this._formulaire = f;
         if (this._formulaire === undefined) {
@@ -58,12 +71,18 @@ export class CalculatorResultsComponent implements AfterViewChecked {
             this.remousResultsComponent.results = undefined;
             this.pabResultsComponent.results = undefined;
             this.mrcResultsComponent.results = undefined;
+            this.jetResultsComponent.results = undefined;
         } else {
-            this.fixedVarResultsComponent.results = f.results;
             this.sectionResultsComponent.results = f.results;
             this.remousResultsComponent.results = f.results;
             this.pabResultsComponent.results = f.results;
             this.mrcResultsComponent.results = f.results;
+            // FixedVar and Jet are mutually incompatible (the 2nd extend the 1st)
+            if (this.isJet) {
+                this.jetResultsComponent.results = f.results;
+            } else {
+                this.fixedVarResultsComponent.results = f.results;
+            }
         }
     }
 
@@ -73,9 +92,14 @@ export class CalculatorResultsComponent implements AfterViewChecked {
         this.remousResultsComponent.updateView();
         this.pabResultsComponent.updateView();
         this.mrcResultsComponent.updateView();
+        this.jetResultsComponent.updateView();
     }
 
     public ngAfterViewChecked() {
         this.afterViewChecked.emit();
     }
+
+    public get isJet() {
+        return this.calculatorComponent.isJet;
+    }
 }
diff --git a/src/app/components/generic-calculator/calculator.component.ts b/src/app/components/generic-calculator/calculator.component.ts
index c68de58db..8ad2d4194 100644
--- a/src/app/components/generic-calculator/calculator.component.ts
+++ b/src/app/components/generic-calculator/calculator.component.ts
@@ -500,6 +500,15 @@ export class GenericCalculatorComponent implements OnInit, DoCheck, AfterViewChe
         );
     }
 
+    // true if current Nub is Jet
+    public get isJet() {
+        return (
+            this._formulaire
+            && this._formulaire.currentNub
+            && this._formulaire.currentNub.calcType === CalculatorType.Jet
+        );
+    }
+
     // for "generate PAB" button
     public get isPABCloisons() {
         return (
diff --git a/src/app/components/jet-results/jet-results.component.html b/src/app/components/jet-results/jet-results.component.html
new file mode 100644
index 000000000..ca214f920
--- /dev/null
+++ b/src/app/components/jet-results/jet-results.component.html
@@ -0,0 +1,18 @@
+<div class="container">
+    <!-- journal -->
+    <log></log>
+
+    <results-graph *ngIf="showVarResults"></results-graph>
+
+    <trajectory-graph></trajectory-graph>
+
+    <div>
+        <!-- table des résultats fixés -->
+        <fixed-results [results]=fixedResults></fixed-results>
+
+        <!-- table des résultats variés -->
+        <div *ngIf="showVarResults">
+            <var-results [results]=varResults></var-results>
+        </div>
+    </div>
+</div>
diff --git a/src/app/components/jet-results/jet-results.component.scss b/src/app/components/jet-results/jet-results.component.scss
new file mode 100644
index 000000000..6f5eb10d5
--- /dev/null
+++ b/src/app/components/jet-results/jet-results.component.scss
@@ -0,0 +1,4 @@
+results-graph {
+    margin-left: 1em;
+    margin-right: 1em;
+}
diff --git a/src/app/components/jet-results/jet-results.component.ts b/src/app/components/jet-results/jet-results.component.ts
new file mode 100644
index 000000000..4bbf4a2d0
--- /dev/null
+++ b/src/app/components/jet-results/jet-results.component.ts
@@ -0,0 +1,14 @@
+import { Component } from "@angular/core";
+
+import { FixedVarResultsComponent } from "../fixedvar-results/fixedvar-results.component";
+
+@Component({
+    selector: "jet-results",
+    templateUrl: "./jet-results.component.html",
+    styleUrls: [
+        "./jet-results.component.scss"
+    ]
+})
+export class JetResultsComponent extends FixedVarResultsComponent {
+
+}
diff --git a/src/app/formulaire/form-iterator/deep-node-iterator.ts b/src/app/formulaire/form-iterator/deep-node-iterator.ts
deleted file mode 100644
index cca6046a1..000000000
--- a/src/app/formulaire/form-iterator/deep-node-iterator.ts
+++ /dev/null
@@ -1,10 +0,0 @@
-import { AbstractFormulaireNodeIterator } from "./abstract-node-iterator";
-import { FormulaireNode } from "../formulaire-node";
-
-export class DeepFormulaireNodeIterator extends AbstractFormulaireNodeIterator<FormulaireNode> implements IterableIterator<FormulaireNode> {
-    // interface IterableIterator
-
-    [Symbol.iterator](): IterableIterator<FormulaireNode> {
-        return this;
-    }
-}
diff --git a/src/app/results/multidimension-results.ts b/src/app/results/multidimension-results.ts
index 5475c6c60..b0ecbca0e 100644
--- a/src/app/results/multidimension-results.ts
+++ b/src/app/results/multidimension-results.ts
@@ -2,7 +2,7 @@ import { CalculatedParamResults } from "./param-calc-results";
 import { NgParameter } from "../formulaire/ngparam";
 
 
-export class MultiDimensionResults extends CalculatedParamResults/*  implements PlottableData */ {
+export class MultiDimensionResults extends CalculatedParamResults {
 
     /** paramètres variés */
     public variatedParameters: NgParameter[];
-- 
GitLab


From d469b73d0a1b65985d5f7b9458657a71a5ac9320 Mon Sep 17 00:00:00 2001
From: "mathias.chouet" <mathias.chouet@irstea.fr>
Date: Thu, 12 Sep 2019 16:51:06 +0200
Subject: [PATCH 4/7] Fix #287 add jet type results

---
 src/app/app.module.ts                         |   2 +
 .../fixedvar-results.component.ts             |   8 +-
 .../jet-results/jet-results.component.html    |   2 +-
 .../jet-results/jet-results.component.ts      |  45 ++-
 .../jet-trajectory-graph.component.html       |  22 ++
 .../jet-trajectory-graph.component.scss       |  34 +++
 .../jet-trajectory-graph.component.ts         | 268 ++++++++++++++++++
 src/app/services/formulaire.service.ts        |   1 -
 src/locale/messages.en.json                   |   3 +
 src/locale/messages.fr.json                   |   3 +
 10 files changed, 381 insertions(+), 7 deletions(-)
 create mode 100644 src/app/components/jet-trajectory-graph/jet-trajectory-graph.component.html
 create mode 100644 src/app/components/jet-trajectory-graph/jet-trajectory-graph.component.scss
 create mode 100644 src/app/components/jet-trajectory-graph/jet-trajectory-graph.component.ts

diff --git a/src/app/app.module.ts b/src/app/app.module.ts
index 794becbda..7b1ed1fce 100644
--- a/src/app/app.module.ts
+++ b/src/app/app.module.ts
@@ -91,6 +91,7 @@ import { ModulesDiagramComponent } from "./components/modules-diagram/modules-di
 import { MacrorugoCompoundResultsTableComponent } from "./components/macrorugo-compound-results/macrorugo-compound-results-table.component";
 import { MacrorugoCompoundResultsComponent } from "./components/macrorugo-compound-results/macrorugo-compound-results.component";
 import { JetResultsComponent } from "./components/jet-results/jet-results.component";
+import { JetTrajectoryGraphComponent } from "./components/jet-trajectory-graph/jet-trajectory-graph.component";
 
 import { DialogConfirmEmptySessionComponent } from "./components/dialog-confirm-empty-session/dialog-confirm-empty-session.component";
 import { DialogConfirmCloseCalcComponent } from "./components/dialog-confirm-close-calc/dialog-confirm-close-calc.component";
@@ -195,6 +196,7 @@ const appRoutes: Routes = [
     JalhydModelValidationMaxDirective,
     JalhydModelValidationStepDirective,
     JetResultsComponent,
+    JetTrajectoryGraphComponent,
     LogComponent,
     LogEntryComponent,
     ModulesDiagramComponent,
diff --git a/src/app/components/fixedvar-results/fixedvar-results.component.ts b/src/app/components/fixedvar-results/fixedvar-results.component.ts
index 394450131..211c44c80 100644
--- a/src/app/components/fixedvar-results/fixedvar-results.component.ts
+++ b/src/app/components/fixedvar-results/fixedvar-results.component.ts
@@ -22,13 +22,13 @@ export class FixedVarResultsComponent extends ResultsComponent implements DoChec
     /**
      * résultats non mis en forme
      */
-    private _fixedResults: FixedResults;
-    private _varResults: VarResults;
+    protected _fixedResults: FixedResults;
+    protected _varResults: VarResults;
 
     /**
      * true si les résultats doiventt être remis à jour
      */
-    private _doUpdate = false;
+    protected _doUpdate = false;
 
     @ViewChild(FixedResultsComponent, { static: false })
     private fixedResultsComponent: FixedResultsComponent;
@@ -116,7 +116,7 @@ export class FixedVarResultsComponent extends ResultsComponent implements DoChec
      * met à jour l'affichage des résultats
      * @returns true si les résultats ont pu être mis à jour
      */
-    private updateResults() {
+    protected updateResults() {
         const fixedUpdated = this._fixedResults !== undefined && this.fixedResultsComponent !== undefined;
         if (fixedUpdated) {
             this.fixedResultsComponent.results = this._fixedResults;
diff --git a/src/app/components/jet-results/jet-results.component.html b/src/app/components/jet-results/jet-results.component.html
index ca214f920..5b726da97 100644
--- a/src/app/components/jet-results/jet-results.component.html
+++ b/src/app/components/jet-results/jet-results.component.html
@@ -4,7 +4,7 @@
 
     <results-graph *ngIf="showVarResults"></results-graph>
 
-    <trajectory-graph></trajectory-graph>
+    <jet-trajectory-graph *ngIf="hasResults"></jet-trajectory-graph>
 
     <div>
         <!-- table des résultats fixés -->
diff --git a/src/app/components/jet-results/jet-results.component.ts b/src/app/components/jet-results/jet-results.component.ts
index 4bbf4a2d0..8703b78e7 100644
--- a/src/app/components/jet-results/jet-results.component.ts
+++ b/src/app/components/jet-results/jet-results.component.ts
@@ -1,6 +1,7 @@
-import { Component } from "@angular/core";
+import { Component, ViewChild } from "@angular/core";
 
 import { FixedVarResultsComponent } from "../fixedvar-results/fixedvar-results.component";
+import { JetTrajectoryGraphComponent } from "../jet-trajectory-graph/jet-trajectory-graph.component";
 
 @Component({
     selector: "jet-results",
@@ -11,4 +12,46 @@ import { FixedVarResultsComponent } from "../fixedvar-results/fixedvar-results.c
 })
 export class JetResultsComponent extends FixedVarResultsComponent {
 
+    /** graphique de trajectoire */
+    @ViewChild(JetTrajectoryGraphComponent, { static: false })
+    private jetTrajectoryGraphComponent: JetTrajectoryGraphComponent;
+
+    public get hasResults(): boolean {
+        return (
+            (this._fixedResults !== undefined && this._fixedResults.hasResults)
+            ||
+            (this._varResults !== undefined && this._varResults.hasResults)
+        );
+    }
+
+    public updateView() {
+        if (this.jetTrajectoryGraphComponent) {
+            this.jetTrajectoryGraphComponent.results = undefined;
+        }
+        super.updateView();
+    }
+
+    /**
+     * met à jour l'affichage des résultats
+     * @returns true si les résultats ont pu être mis à jour
+     */
+    protected updateResults() {
+        const superUpdated = super.updateResults();
+
+        let trajectoryGraphUpdated: boolean;
+        trajectoryGraphUpdated = this.jetTrajectoryGraphComponent !== undefined;
+
+        if (trajectoryGraphUpdated) {
+            // draw chart whether params are variating or not,
+            // hence different Results object for each case
+            if (this._varResults && this._varResults.hasResults) {
+                this.jetTrajectoryGraphComponent.results = this._varResults;
+            } else {
+                this.jetTrajectoryGraphComponent.results = this._fixedResults;
+            }
+            this.jetTrajectoryGraphComponent.updateView();
+        }
+
+        return superUpdated && trajectoryGraphUpdated;
+    }
 }
diff --git a/src/app/components/jet-trajectory-graph/jet-trajectory-graph.component.html b/src/app/components/jet-trajectory-graph/jet-trajectory-graph.component.html
new file mode 100644
index 000000000..6ef01d504
--- /dev/null
+++ b/src/app/components/jet-trajectory-graph/jet-trajectory-graph.component.html
@@ -0,0 +1,22 @@
+<div class="graph-results-container" #graphProfile fxLayout="row wrap" fxLayoutAlign="center center">
+    <div fxFlex="1 1 100%">
+        <div class="graph-profile-buttons">
+            <button mat-icon-button (click)="resetZoom()" [disabled]="! zoomWasChanged" [title]="uitextResetZoomTitle">
+                <mat-icon color="primary">replay</mat-icon>
+            </button>
+            <button mat-icon-button (click)="exportAsImage(graphProfile)" [title]="uitextExportImageTitle">
+                <mat-icon color="primary">image</mat-icon>
+            </button>
+            <button mat-icon-button *ngIf="! isFullscreen" (click)="setFullscreen(graphProfile)" [title]="uitextEnterFSTitle">
+                <mat-icon color="primary" class="scaled12">fullscreen</mat-icon>
+            </button>
+            <button mat-icon-button *ngIf="isFullscreen" (click)="exitFullscreen()" [title]="uitextExitFSTitle">
+                <mat-icon color="primary" class="scaled12">fullscreen_exit</mat-icon>
+            </button>
+        </div>
+
+        <div *ngIf="! displayChart" class="fake-chart"></div><!-- trick to avoid blinking effect due to forceRebuild -->
+        <chart *ngIf="displayChart" type="scatter" [data]="graph_data" [options]="graph_options" #graphChart>
+        </chart>
+    </div>
+</div>
\ No newline at end of file
diff --git a/src/app/components/jet-trajectory-graph/jet-trajectory-graph.component.scss b/src/app/components/jet-trajectory-graph/jet-trajectory-graph.component.scss
new file mode 100644
index 000000000..19266623b
--- /dev/null
+++ b/src/app/components/jet-trajectory-graph/jet-trajectory-graph.component.scss
@@ -0,0 +1,34 @@
+.graph-results-container{
+    display: block;
+    background-color: white;
+}
+
+.graph-profile-buttons {
+    padding-right: 10px;
+    padding-top: 4px;
+    margin-bottom: -30px;
+    text-align: right;
+    background-color: white;
+
+    button {
+        margin-left: 3px;
+        width: auto;
+
+        mat-icon {
+            &.scaled12 {
+                transform: scale(1.2);
+            }
+        }
+
+        &:disabled {
+            mat-icon {
+                color: #bfbfbf;
+            }
+        }
+    }
+}
+
+.fake-chart {
+    width: 100%;
+    padding-top: 50%;
+}
diff --git a/src/app/components/jet-trajectory-graph/jet-trajectory-graph.component.ts b/src/app/components/jet-trajectory-graph/jet-trajectory-graph.component.ts
new file mode 100644
index 000000000..50a014d99
--- /dev/null
+++ b/src/app/components/jet-trajectory-graph/jet-trajectory-graph.component.ts
@@ -0,0 +1,268 @@
+import { Component, ViewChild, ChangeDetectorRef } from "@angular/core";
+
+import { ChartComponent } from "angular2-chartjs";
+
+import { I18nService } from "../../services/internationalisation.service";
+import { ResultsComponent } from "../fixedvar-results/results.component";
+import { IYSeries } from "../../results/y-series";
+import { FixedResults } from "../../results/fixed-results";
+import { VarResults } from "../../results/var-results";
+import { fv } from "../../util";
+
+import { Jet } from "jalhyd";
+
+@Component({
+    selector: "jet-trajectory-graph",
+    templateUrl: "./jet-trajectory-graph.component.html",
+    styleUrls: [
+        "./jet-trajectory-graph.component.scss"
+    ]
+})
+export class JetTrajectoryGraphComponent extends ResultsComponent {
+
+    @ViewChild(ChartComponent, { static: false })
+    private chartComponent;
+
+    private _results: FixedResults | VarResults;
+
+    private _zoomWasChanged = false;
+
+    private _varValuesLists: any = {};
+
+    /** used to briefly destroy/rebuild the chart component, to refresh axis labels (@see bug #137) */
+    public displayChart = true;
+
+    /*
+     * config du graphe
+     */
+    public graph_data: { datasets: any[] };
+    public graph_options: any = {
+        responsive: true,
+        maintainAspectRatio: true,
+        aspectRatio: 1.5,
+        animation: {
+            duration: 0
+        },
+        legend: {
+            display: true,
+            position: "bottom",
+            reverse: false
+        },
+        title: {
+            display: true,
+            text: this.intlService.localizeText("INFO_JET_TITRE_TRAJECTOIRE")
+        },
+        elements: {
+            line: {
+                tension: 0
+            }
+        }
+    };
+
+    public constructor(
+        private intlService: I18nService,
+        private cd: ChangeDetectorRef
+    ) {
+        super();
+        // do not move following block out of constructor or scale labels won't be rendered
+        this.graph_options["scales"] = {
+            xAxes: [{
+                type: "linear",
+                position: "bottom",
+                ticks: {
+                    precision: ResultsComponent.CHARTS_AXIS_PRECISION
+                },
+                scaleLabel: {
+                    display: true,
+                    labelString: this.intlService.localizeText("INFO_LIB_ABSCISSE")
+                }
+            }],
+            yAxes: [{
+                type: "linear",
+                position: "left",
+                ticks: {
+                    precision: ResultsComponent.CHARTS_AXIS_PRECISION
+                },
+                scaleLabel: {
+                    display: true,
+                    labelString: this.intlService.localizeText("INFO_LIB_ALTITUDE")
+                }
+            }]
+        };
+        // enable zoom and pan (using "chartjs-plugin-zoom" package)
+        const that = this;
+        this.graph_options["plugins"] = {
+            zoom: {
+                pan: {
+                    enabled: false, // conflicts with drag zoom
+                    mode: "xy",
+                },
+                zoom: {
+                    enabled: true,
+                    drag: { // conflicts with pan; set to false to enable mouse wheel zoom,
+                        borderColor: "rgba(225,225,225,0.3)",
+                        borderWidth: 1,
+                        backgroundColor: "rgba(0,0,0,0.25)"
+                    },
+                    mode: "xy",
+                    // percentage of zoom on a wheel event
+                    // speed: 0.1,
+                    onZoomComplete: function(t: any) { return function() { t.zoomComplete(); }; }(that)
+                }
+            }
+        };
+        // format numbers in tooltips
+        this.graph_options.tooltips = {
+            displayColors: false,
+            callbacks: {
+                label: (tooltipItem, data) => {
+                    return "(" + fv(Number(tooltipItem.xLabel)) + ", " + fv(Number(tooltipItem.yLabel)) + ")";
+                }
+            }
+        };
+    }
+
+    /** forces Angular to rebuild the chart @see bug #137 */
+    private forceRebuild() {
+        this.displayChart = false;
+        const that = this;
+        setTimeout(() => { // trick
+            that.displayChart = true;
+        }, 10);
+    }
+
+    public set results(r: FixedResults | VarResults) {
+        this.forceRebuild(); // used for (de)activating legend in generateScatterGraph()
+        this._results = r;
+
+        if (this._results) {
+            const nub = this._results.result.sourceNub as Jet;
+            const length = nub.variatingLength();
+            // extract variable values list for legend
+            if (nub.resultHasMultipleValues()) {
+                for (const p of nub.parameterIterator) {
+                    if (p.hasMultipleValues) {
+                        this._varValuesLists[p.symbol] = [];
+                        if (nub.calculatedParam === p) { // calculated
+                            for (let i = 0; i < length; i++) {
+                                this._varValuesLists[p.symbol].push(nub.result.resultElements[i].vCalc);
+                            }
+                        } else { // variating
+                            const iter = p.getExtendedValuesIterator(length);
+                            while (iter.hasNext) {
+                                const nv = iter.next();
+                                this._varValuesLists[p.symbol].push(nv.value);
+                            }
+                        }
+                    }
+                }
+            }
+        }
+    }
+
+    public zoomComplete() {
+        this._zoomWasChanged = true;
+        this.cd.detectChanges();
+    }
+
+    public get zoomWasChanged(): boolean {
+        return this._zoomWasChanged;
+    }
+
+    public updateView() {
+        this.generateScatterGraph();
+    }
+
+    /**
+     * génère les données d'un graphe de type "scatter"
+     */
+    private generateScatterGraph() {
+        const ySeries = this.getYSeries();
+
+        // hide legend when there is only 1 series
+        this.graph_options.legend.display = (ySeries.length > 1);
+
+        this.graph_data = {
+            datasets: []
+        };
+
+        // build Y data series
+        for (const ys of ySeries) {
+            if (ys.data.length > 0) {
+                // push series config
+                this.graph_data.datasets.push({
+                    label: ys.label,
+                    data: ys.data,
+                    borderColor: ys.color, // couleur de la ligne
+                    backgroundColor: "rgba(0,0,0,0)",  // couleur de remplissage sous la courbe : transparent
+                    showLine: "true"
+                });
+            }
+        }
+    }
+
+    public exportAsImage(element: HTMLDivElement) {
+        const canvas: HTMLCanvasElement = element.querySelector("canvas");
+        canvas.toBlob((blob) => {
+            saveAs(blob, "chart.png");
+        }); // defaults to image/png
+    }
+
+    public resetZoom() {
+        this.chartComponent.chart.resetZoom();
+        this._zoomWasChanged = false;
+    }
+
+    public get uitextResetZoomTitle() {
+        return this.intlService.localizeText("INFO_GRAPH_BUTTON_TITLE_RESET_ZOOM");
+    }
+
+    public get uitextExportImageTitle() {
+        return this.intlService.localizeText("INFO_GRAPH_BUTTON_TITLE_EXPORT_IMAGE");
+    }
+
+    public get uitextEnterFSTitle() {
+        return this.intlService.localizeText("INFO_GRAPH_BUTTON_TITLE_ENTER_FS");
+    }
+
+    public get uitextExitFSTitle() {
+        return this.intlService.localizeText("INFO_GRAPH_BUTTON_TITLE_EXIT_FS");
+    }
+
+    private getYSeries(): IYSeries[] {
+        const ret: IYSeries[] = [];
+        const palette = ResultsComponent.distinctColors;
+        const nub = (this._results.result.sourceNub as Jet);
+        const trajectories = nub.generateTrajectories();
+
+        for (let i = 0; i < trajectories.length; i++) {
+            const traj = trajectories[i];
+            ret.push({
+                label: trajectories.length === 0 ? "" /* legend is hidden */ : this.getLegendForSeries(i),
+                color: palette[i % palette.length],
+                // map to IYSeries format
+                data: traj.map((t) => {
+                    return {
+                        x: t[0],
+                        y: t[1]
+                    };
+                })
+            });
+        }
+        console.log("Y series", ret.length, ret);
+        return ret;
+    }
+
+    /**
+     * Returns a label showing the boundary conditions values for
+     * the given iteration
+     * @param n index of the variating parameter(s) iteration
+     */
+    private getLegendForSeries(n: number): string {
+        return Object.keys(this._varValuesLists).map((symbol) => {
+            const values = this._varValuesLists[symbol];
+            const val = fv(values[n]);
+            return `${symbol} = ${val}`;
+        }).join(", ");
+    }
+}
diff --git a/src/app/services/formulaire.service.ts b/src/app/services/formulaire.service.ts
index 5bfbb27c0..9dc427094 100644
--- a/src/app/services/formulaire.service.ts
+++ b/src/app/services/formulaire.service.ts
@@ -327,7 +327,6 @@ export class FormulaireService extends Observable {
      * @param calculatorName nom du module, à afficher dans l'interface
      */
     public createFormulaire(ct: CalculatorType, nub?: Nub, calculatorName?: string): Promise<FormulaireDefinition> {
-        console.log(">> Create form !!", ct);
         // Crée un formulaire du bon type
         const f: FormulaireDefinition = this.newFormulaire(ct);
         this._formulaires.push(f);
diff --git a/src/locale/messages.en.json b/src/locale/messages.en.json
index 57ac348e1..94cc1aa88 100644
--- a/src/locale/messages.en.json
+++ b/src/locale/messages.en.json
@@ -156,8 +156,11 @@
     "INFO_WALL_REMOVED": "Wall #%s removed",
     "INFO_WALLS_AND_DEVICES_REMOVED": "%s wall(s) and %s device(s) removed",
     "INFO_WALLS_REMOVED": "%s wall(s) removed",
+    "INFO_JET_TITRE_TRAJECTOIRE": "Trajectory",
     "INFO_LECHAPTCALMON_TITRE_COURT": "Lechapt-C.",
     "INFO_LECHAPTCALMON_TITRE": "Lechapt-Calmon",
+    "INFO_LIB_ABSCISSE": "Abscissa (m)",
+    "INFO_LIB_ALTITUDE": "Altitude (m)",
     "INFO_LIB_LENGTHS": "Every length",
     "INFO_LIB_WIDTHS": "Every width",
     "INFO_LIB_SLOPES": "Every slope",
diff --git a/src/locale/messages.fr.json b/src/locale/messages.fr.json
index 69ae2bef5..685070474 100644
--- a/src/locale/messages.fr.json
+++ b/src/locale/messages.fr.json
@@ -158,6 +158,9 @@
     "INFO_WALLS_REMOVED": "%s cloison(s) supprimée(s)",
     "INFO_LECHAPTCALMON_TITRE_COURT": "Lechapt-C.",
     "INFO_LECHAPTCALMON_TITRE": "Lechapt-Calmon",
+    "INFO_JET_TITRE_TRAJECTOIRE": "Trajectoire",
+    "INFO_LIB_ABSCISSE": "Abscisse (m)",
+    "INFO_LIB_ALTITUDE": "Altitude (m)",
     "INFO_LIB_LENGTHS": "Toutes les longueurs",
     "INFO_LIB_WIDTHS": "Toutes les largeurs",
     "INFO_LIB_SLOPES": "Toutes les pentes",
-- 
GitLab


From 66d31e50eddae3959720f9ff4ae4f40877b25cce Mon Sep 17 00:00:00 2001
From: "mathias.chouet" <mathias.chouet@irstea.fr>
Date: Fri, 13 Sep 2019 17:32:18 +0200
Subject: [PATCH 5/7] Updated e2e tests

---
 e2e/session/session-bad-syntax.json         | 2 +-
 e2e/session/session-empty-modules-list.json | 2 +-
 e2e/session/session-missing-info.json       | 2 +-
 3 files changed, 3 insertions(+), 3 deletions(-)

diff --git a/e2e/session/session-bad-syntax.json b/e2e/session/session-bad-syntax.json
index c2de57a8d..bd9de9b5f 100644
--- a/e2e/session/session-bad-syntax.json
+++ b/e2e/session/session-bad-syntax.json
@@ -1,7 +1,7 @@
 {
     "header": {
         "source": "jalhyd",
-        "format_version": "1.1",
+        "format_version": "1.2",
         "created": "2019-08-21T13:20:54.284Z"
     },
     "session": [
diff --git a/e2e/session/session-empty-modules-list.json b/e2e/session/session-empty-modules-list.json
index 3a5bbefdd..9fe699c78 100644
--- a/e2e/session/session-empty-modules-list.json
+++ b/e2e/session/session-empty-modules-list.json
@@ -1,7 +1,7 @@
 {
     "header": {
         "source": "jalhyd",
-        "format_version": "1.1",
+        "format_version": "1.2",
         "created": "2019-08-21T13:20:54.284Z"
     },
     "session": [
diff --git a/e2e/session/session-missing-info.json b/e2e/session/session-missing-info.json
index fa6003940..dadfcf9c2 100644
--- a/e2e/session/session-missing-info.json
+++ b/e2e/session/session-missing-info.json
@@ -1,7 +1,7 @@
 {
     "header": {
         "source": "jalhyd",
-        "format_version": "1.1",
+        "format_version": "1.2",
         "created": "2019-08-21T13:20:54.284Z"
     },
     "session": [
-- 
GitLab


From 6a98656b27813f82f5a0a51ff47488408c2f630f Mon Sep 17 00:00:00 2001
From: "mathias.chouet" <mathias.chouet@irstea.fr>
Date: Fri, 13 Sep 2019 17:35:33 +0200
Subject: [PATCH 6/7] Removed debug message

---
 .../jet-trajectory-graph/jet-trajectory-graph.component.ts       | 1 -
 1 file changed, 1 deletion(-)

diff --git a/src/app/components/jet-trajectory-graph/jet-trajectory-graph.component.ts b/src/app/components/jet-trajectory-graph/jet-trajectory-graph.component.ts
index 50a014d99..079e3fcc8 100644
--- a/src/app/components/jet-trajectory-graph/jet-trajectory-graph.component.ts
+++ b/src/app/components/jet-trajectory-graph/jet-trajectory-graph.component.ts
@@ -249,7 +249,6 @@ export class JetTrajectoryGraphComponent extends ResultsComponent {
                 })
             });
         }
-        console.log("Y series", ret.length, ret);
         return ret;
     }
 
-- 
GitLab


From dfe41e84df1680a9acc3c1c76aab38aa54ca3f6e Mon Sep 17 00:00:00 2001
From: "mathias.chouet" <mathias.chouet@irstea.fr>
Date: Mon, 16 Sep 2019 10:48:44 +0200
Subject: [PATCH 7/7] Updated translation

---
 src/app/calculators/jet/jet.en.json | 2 +-
 src/app/calculators/jet/jet.fr.json | 2 +-
 2 files changed, 2 insertions(+), 2 deletions(-)

diff --git a/src/app/calculators/jet/jet.en.json b/src/app/calculators/jet/jet.en.json
index 67fc3ad49..4b7b42f43 100644
--- a/src/app/calculators/jet/jet.en.json
+++ b/src/app/calculators/jet/jet.en.json
@@ -8,7 +8,7 @@
     "t": "Flight time",
     "Vx": "Horizontal speed at impact",
     "Vz": "Vertical speed at impact",
-    "Vt": "Impact speed at impact",
+    "Vt": "Speed at impact",
 
     "UNIT_V0": "m/s",
     "UNIT_S": "m/m",
diff --git a/src/app/calculators/jet/jet.fr.json b/src/app/calculators/jet/jet.fr.json
index af50a3b85..022826344 100644
--- a/src/app/calculators/jet/jet.fr.json
+++ b/src/app/calculators/jet/jet.fr.json
@@ -8,7 +8,7 @@
     "t": "Temps de vol",
     "Vx": "Vitesse horizontale à l'impact",
     "Vz": "Vitesse verticale à l'impact",
-    "Vt": "Vitesse d'impact à l'impact",
+    "Vt": "Vitesse à l'impact",
 
     "UNIT_V0": "m/s",
     "UNIT_S": "m/m",
-- 
GitLab