diff --git a/src/app/app.module.ts b/src/app/app.module.ts
index c7a5dadedf66eb17d904e4ad4abb9b848655685b..9c8da9ef4ba33e073c2426f0966c5b0d71f2dadd 100644
--- a/src/app/app.module.ts
+++ b/src/app/app.module.ts
@@ -16,7 +16,11 @@ import { ApplicationSetupService } from "./services/app-setup/app-setup.service"
 import { AppComponent } from './app.component';
 import { NgParamInputComponent } from './components/ngparam-input/ngparam-input.component';
 import { FieldSetComponent } from './components/field-set/field-set.component';
-import { ParamFieldLineComponent, NgParamMinComponent, NgParamMaxComponent, NgParamStepComponent } from './components/param-field-line/param-field-line.component';
+import { ParamFieldLineComponent } from './components/param-field-line/param-field-line.component';
+import { NgParamMinComponent } from './components/param-values/ngparam-min.component';
+import { NgParamMaxComponent } from './components/param-values/ngparam-max.component';
+import { NgParamStepComponent } from './components/param-values/ngparam-step.component';
+import { ParamValuesComponent, ValueListComponent } from './components/param-values/param-values.component';
 import { SelectFieldLineComponent } from './components/select-field-line/select-field-line.component';
 import { CheckFieldLineComponent } from './components/check-field-line/check-field-line.component';
 // import { AlertDialog } from './components/alert-dialog/alert-dialog.component';
@@ -28,6 +32,7 @@ import { GenericCalculatorComponent } from './components/generic-calculator/calc
 import { CalcCanvasComponent } from './components/canvas/canvas.component';
 import { SectionCanvasComponent } from './components/section-canvas/section-canvas.component';
 import { RemousResultsComponent } from './components/remous-results/remous-results.component';
+import { ResultsGraphComponent, GraphTypeSelectComponent } from './components/results-graph/results-graph.component';
 import { LogComponent } from './components/log/log.component';
 import { CalculatorListComponent } from './components/calculator-list/calculator-list.component';
 import { ApplicationSetupComponent } from './components/app-setup/app-setup.component';
@@ -62,6 +67,7 @@ const appRoutes: Routes = [
     NgParamInputComponent,
     FieldSetComponent,
     ParamFieldLineComponent, NgParamMinComponent, NgParamMaxComponent, NgParamStepComponent,
+    ParamValuesComponent, ValueListComponent,
     SelectFieldLineComponent, CheckFieldLineComponent,
     LogComponent,
     CalculatorListComponent,
@@ -70,6 +76,7 @@ const appRoutes: Routes = [
     GenericCalculatorComponent,
     // AlertDialog,
     CalculatorResultsComponent, FixedVarResultsComponent, SectionResultsComponent, RemousResultsComponent,
+    ResultsGraphComponent, GraphTypeSelectComponent,
     CalcCanvasComponent, SectionCanvasComponent
   ],
   // entryComponents: [AlertDialog],
diff --git a/src/app/components/fixedvar-results/fixedvar-results.component.html b/src/app/components/fixedvar-results/fixedvar-results.component.html
index d8a32c6a488c42daea15b7e8a8d5cb0fee64cd83..f3ddd2076cd24f2b97897e85f6a1cc896de15b42 100644
--- a/src/app/components/fixedvar-results/fixedvar-results.component.html
+++ b/src/app/components/fixedvar-results/fixedvar-results.component.html
@@ -1,13 +1,7 @@
 <!-- journal -->
 <log></log>
 
-<!-- graphe -->
-<div class="row" *ngIf="showVarResults">
-    <div class="col">
-        <chart [type]="graph_type" [data]="graph_data" [options]="graph_options">
-        </chart>
-    </div>
-</div>
+<results-graph *ngIf="showVarResults"></results-graph>
 
 <!-- 
     classe conditionnelle :
diff --git a/src/app/components/fixedvar-results/fixedvar-results.component.ts b/src/app/components/fixedvar-results/fixedvar-results.component.ts
index 802f668258a13a75d18f1b80a2c98ae8cf5583a0..27b225369857614cc456b08f8cd98921776383e6 100644
--- a/src/app/components/fixedvar-results/fixedvar-results.component.ts
+++ b/src/app/components/fixedvar-results/fixedvar-results.component.ts
@@ -1,10 +1,10 @@
-import { Component, ViewChild } from "@angular/core";
+import { Component, ViewChild, DoCheck } from "@angular/core";
 
 import { InternationalisationService } from "../../services/internationalisation/internationalisation.service";
 import { ApplicationSetupService } from '../../services/app-setup/app-setup.service';
 import { LogComponent } from '../../components/log/log.component';
-import { cLog } from "jalhyd";
 import { FixedVarResults } from "../../results/fixed-var-results";
+import { ResultsGraphComponent } from "../results-graph/results-graph.component";
 
 @Component({
     selector: "fixedvar-results",
@@ -29,7 +29,7 @@ import { FixedVarResults } from "../../results/fixed-var-results";
     `
     ]
 })
-export class FixedVarResultsComponent {
+export class FixedVarResultsComponent implements DoCheck {
     /**
      * résultats non mis en forme
      */
@@ -45,25 +45,10 @@ export class FixedVarResultsComponent {
      */
     private _varResults: Object[];
 
-    /*
-    * config du graphe
-    */
-    private graph_type = "line";
-    private graph_data = {};
-    private graph_options = {
-        responsive: true,
-        maintainAspectRatio: true,
-        animation: {
-            duration: 0
-        },
-        legend: {
-            display: false
-        },
-        title: {
-            display: true,
-            text: ""
-        }
-    };
+    /**
+     * true si le graphe des résultats doit être remis à jour
+     */
+    private _updateResultsGraph: boolean;
 
     /**
      * composant journal
@@ -71,6 +56,9 @@ export class FixedVarResultsComponent {
     @ViewChild(LogComponent)
     private logComponent: LogComponent;
 
+    @ViewChild(ResultsGraphComponent)
+    private resultsGraphComponent: ResultsGraphComponent;
+
     constructor(
         private intlService: InternationalisationService,
         private appSetupService: ApplicationSetupService
@@ -78,32 +66,44 @@ export class FixedVarResultsComponent {
 
     public set results(r: FixedVarResults) {
         this._results = r;
-        this.updateView();
+        this._updateResultsGraph = true;
     }
 
     public updateView() {
         this._fixedResults = [];
         this._varResults = [];
         this.logComponent.log = undefined;
-        if (this._results != undefined)
-            this.generateResults();
+        this.generateResults();
+        this._updateResultsGraph = true;
+    }
+
+    public ngDoCheck() {
+        if (this._updateResultsGraph) {
+            if (this.resultsGraphComponent != undefined) {
+                this.resultsGraphComponent.results = this._results;
+                this.resultsGraphComponent.updateView();
+                this._updateResultsGraph = false;
+            }
+        }
     }
 
     private generateResults() {
-        const nDigits = this.appSetupService.displayDigits;
-        const prec = this.appSetupService.displayPrecision;
+        if (this._results != undefined) {
+            const nDigits = this.appSetupService.displayDigits;
+            const prec = this.appSetupService.displayPrecision;
 
-        for (let r of this._results.fixedResults) {
-            let v = r["value"];
-            this._fixedResults.push({ "label": r["label"], "value": v.toFixed(nDigits) });
-        }
+            for (let r of this._results.fixedResults) {
+                let v = r["value"];
+                this._fixedResults.push({ "label": r["label"], "value": v.toFixed(nDigits) });
+            }
 
-        for (let r of this._results.varResults)
-            this._varResults.push({ "param": r["param"].toFixed(nDigits), "result": r["result"].toFixed(nDigits) });
+            for (let r of this._results.varResults)
+                this._varResults.push({ "param": r["param"].toFixed(nDigits), "result": r["result"].toFixed(nDigits) });
 
-        this.logComponent.log = this._results.log;
-        this.generateGraph();
+            this.logComponent.log = this._results.log;
+        }
     }
+
     /**
      * affichage de la table des résultats fixés
      */
@@ -126,81 +126,6 @@ export class FixedVarResultsComponent {
         return this.intlService.localizeText("INFO_CALCULATOR_VALEURS");
     }
 
-    // public addFixedResult(p: NgParameter, v: number, fixedPrec: number, displaySymbol: boolean) {
-    //     this._fixedResults.push({ "label": this.paramLabel(p, displaySymbol), "value": v.toFixed(fixedPrec) });
-    // }
-
-    // public addVarResult(paramVal: number, resVal: number, fixedPrec: number) {
-    //     this._varResults.push({ "param": paramVal.toFixed(fixedPrec), "result": resVal.toFixed(fixedPrec) });
-    //     this._varGraph.push(resVal);
-    // }
-
-    // public addLogMessages(l: cLog) {
-    //     this.logComponent.addLogEntries(l);
-    // }
-
-    private generateGraph() {
-        let labs = [];
-        let dat = [];
-        for (let i in this._results.varResults) {
-            let r = this._results.varResults[i];
-            labs.push(r["param"]);
-            dat.push(this._results.varGraph[i]);
-        }
-
-        this.graph_options.title.text = this._results.graphTitle;
-
-        this.graph_data = {
-            labels: labs,
-            datasets: [
-                {
-                    label: "",
-                    data: dat
-                }
-            ]
-        };
-    }
-
-    // public reset(fixed: boolean) {
-    //     this._results.reset();
-    //     this.logComponent.reset();
-    // }
-
-    // public setVariableParamHeader(h: string) {
-    //     this._variableParamHeader = h;
-    // }
-
-    // public setVariableParamHeaderFromParameter(p: NgParameter, displaySymbol: boolean) {
-    //     this._variableParamHeader = this.paramLabel(p, displaySymbol);
-    // }
-
-    // public setVariableResultHeader(h: string) {
-    //     this._variableResultHeader = h;
-    // }
-
-    // public setVariableResultHeaderFromParameter(p: NgParameter) {
-    //     this._variableResultHeader = this.paramLabel(p, true);
-    // }
-
-    // public setGraphTitle(t: string) {
-    //     this.graph_options.title.text = t;
-    // }
-
-    // private paramLabel(p: NgParameter, displaySymbol: boolean): string {
-    //     let res = "";
-    //     if (displaySymbol)
-    //         res += p.symbol;
-    //     if (p.label != undefined && p.label != "")
-    //         if (p.symbol != p.label || !displaySymbol) {
-    //             if (res.length > 0)
-    //                 res += ":";
-    //             res += p.label;
-    //         }
-    //     if (p.unit != undefined && p.unit != "")
-    //         res += " (" + p.unit + ")";
-    //     return res;
-    // }
-
     private getFixedResultClass(i: number) {
         if (this._results.isFixed && i == this._results.fixedResults.length - 1)
             return "font-weight-bold";
diff --git a/src/app/components/generic-select/generic-select.component.html b/src/app/components/generic-select/generic-select.component.html
new file mode 100644
index 0000000000000000000000000000000000000000..3506521b3b2d33bb79c0bacdcf7f6abf4d8b506c
--- /dev/null
+++ b/src/app/components/generic-select/generic-select.component.html
@@ -0,0 +1,8 @@
+<div class="btn-group" dropdown (click)="onSelect($event)">
+    <button dropdownToggle class="btn btn-primary dropdown-toggle waves-light my-1" type="button" mdbRippleRadius>
+        {{currentLabel}}
+    </button>
+    <div class="dropdown-menu">
+        <a class="dropdown-item" *ngFor="let e of entries" [value]=e>{{entryLabel(e)}}</a>
+    </div>
+</div>
\ No newline at end of file
diff --git a/src/app/components/generic-select/generic-select.component.ts b/src/app/components/generic-select/generic-select.component.ts
new file mode 100644
index 0000000000000000000000000000000000000000..7dd5f113a48068b58506e731d1eb3308120994ea
--- /dev/null
+++ b/src/app/components/generic-select/generic-select.component.ts
@@ -0,0 +1,57 @@
+import { Component, Input, Output, EventEmitter } from "@angular/core";
+
+import { SelectField, } from "../../formulaire/select-field";
+import { SelectEntry } from "../../formulaire/select-entry";
+import { FormulaireService } from "../../services/formulaire/formulaire.service";
+
+/*
+  exemple de template :
+
+<div class="btn-group" dropdown (click)="onSelect($event)">
+    <button dropdownToggle class="btn btn-primary dropdown-toggle waves-light my-1" type="button" mdbRippleRadius>
+        {{currentLabel}}
+    </button>
+    <div class="dropdown-menu">
+        <a class="dropdown-item" *ngFor="let e of entries" [value]=e>{{entryLabel(e)}}</a>
+    </div>
+</div>
+*/
+
+export abstract class GenericSelectComponent {
+    // valeur actuellement sélectionnée
+    // la valeur repère une entrée de la liste (cf. [value] dans le template)
+    private _selectedValue: any;
+
+    /**
+     * selected value event
+     */
+    @Output()
+    private selectChange = new EventEmitter<string>();
+
+    public set selectedValue(v: any) {
+        this._selectedValue = v;
+    }
+
+    private get currentLabel(): string {
+        for (let e of this.entries)
+            if (e == this._selectedValue)
+                return this.entryLabel(e);
+
+        return "<no selection>";
+    }
+
+    private onSelect(event: any) {
+        this._selectedValue = event.target.value;
+        this.selectChange.emit(this._selectedValue);
+    }
+
+    /**
+     * liste des objets sélectionnables
+     */
+    protected abstract get entries(): any[];
+
+    /**
+     * calcule l'étiquette d'une entrée (ce qui est affiché dans la liste déroulante)
+     */
+    protected abstract entryLabel(entry: any): string;
+}
diff --git a/src/app/components/param-field-line/param-field-line.component.html b/src/app/components/param-field-line/param-field-line.component.html
index a7b0ad78818daa5faab33c99e05319b205699b7f..e99500c62ff0d4c964e41384055462f4d9a8d098 100644
--- a/src/app/components/param-field-line/param-field-line.component.html
+++ b/src/app/components/param-field-line/param-field-line.component.html
@@ -30,14 +30,4 @@
     </div>
 </div>
 
-<div *ngIf="isRadioVarChecked" class="row">
-    <div class="col-12 col-sm-4">
-        <ngparam-min [title]="uitextValeurMini" (onChange)="onMinChanged($event)"></ngparam-min>
-    </div>
-    <div class="col-12 col-sm-4">
-        <ngparam-max [title]="uitextValeurMaxi" (onChange)="onMaxChanged($event)"></ngparam-max>
-    </div>
-    <div class="col-12 col-sm-4">
-        <ngparam-step [title]="uitextPasVariation" (onChange)="onStepChanged($event)"></ngparam-step>
-    </div>
-</div>
\ No newline at end of file
+<param-values *ngIf="isRadioVarChecked" [param]="_param"></param-values>
\ No newline at end of file
diff --git a/src/app/components/param-field-line/param-field-line.component.ts b/src/app/components/param-field-line/param-field-line.component.ts
index eaea2232eef793b431eacb7766c297652193e696..6e7a402ee82849529eb75bbed4ac269306e6730e 100644
--- a/src/app/components/param-field-line/param-field-line.component.ts
+++ b/src/app/components/param-field-line/param-field-line.component.ts
@@ -1,229 +1,9 @@
-import { Component, ViewChild, Input, Output, DoCheck, EventEmitter } from "@angular/core";
-
-import { NumericalString, ParamDomainValue, Pair } from "jalhyd";
+import { Component, ViewChild, Input, Output, EventEmitter } from "@angular/core";
 
 import { InternationalisationService } from "../../services/internationalisation/internationalisation.service";
 import { NgParameter, ParamRadioConfig } from "../../formulaire/ngparam";
 import { GenericInputComponent } from "../generic-input/generic-input.component";
 
-@Component({
-    selector: "ngparam-min",
-    templateUrl: "../generic-input/generic-input.component.html"
-})
-export class NgParamMinComponent extends GenericInputComponent {
-    /**
-     * valeur actuelle du minimum
-     */0.49999999995
-    private _currentValue: number;
-
-    /**
-     * reférence (valeur mini pour le minimum)
-     */
-    private _refValue: Pair;
-
-    @Output()
-    private onChange = new EventEmitter<string>();
-
-    public isInit: boolean;
-
-    constructor(private intlService: InternationalisationService) {
-        super();
-    }
-
-    public set refValue(v: Pair) {
-        this._refValue = v;
-    }
-
-    protected getModelValue(): any {
-        return this._currentValue;
-    }
-
-    protected setModelValue(v: any) {
-        this._currentValue = v;
-        this.onChange.emit("value");
-    }
-
-    protected validateModelValue(v: any): { isValid: boolean, message: string } {
-        let msg = undefined;
-        let valid = false;
-
-        if (!this._refValue.intervalHasValue(v))
-            msg = "La valeur n'est pas dans " + this._refValue.toString();
-        else
-            valid = true;
-
-        return { isValid: valid, message: msg };
-    }
-
-    protected modelToUI(v: any): string {
-        if (typeof (v) == "number")
-            return String(v);
-        return undefined;
-    }
-
-    protected validateUIValue(ui: string): { isValid: boolean, message: string } {
-        let valid: boolean = false;
-        let msg: string = undefined;
-
-        let v: NumericalString = new NumericalString(ui);
-        if (!v.isNumerical)
-            msg = "Veuillez entrer une valeur numérique";
-        else
-            valid = true;
-
-        return { isValid: valid, message: msg };
-    }
-
-    protected uiToModel(ui: string): any {
-        return +ui;
-    }
-}
-
-@Component({
-    selector: "ngparam-max",
-    templateUrl: "../generic-input/generic-input.component.html"
-})
-export class NgParamMaxComponent extends GenericInputComponent {
-    /**
-       * valeur actuelle du maximum
-       */
-    private _currentValue: number;
-
-    /**
-     * reférence (valeur maxi pour le maximum)
-     */
-    private _refValue: Pair;
-
-    @Output()
-    private onChange = new EventEmitter<string>();
-
-    constructor(private intlService: InternationalisationService) {
-        super();
-    }
-
-    public set refValue(v: Pair) {
-        this._refValue = v;
-    }
-
-    protected getModelValue(): any {
-        return this._currentValue;
-    }
-
-    protected setModelValue(v: any) {
-        this._currentValue = v;
-        this.onChange.emit("value");
-    }
-
-    protected validateModelValue(v: any): { isValid: boolean, message: string } {
-        let msg = undefined;
-        let valid = false;
-
-        if (!this._refValue.intervalHasValue(v))
-            msg = "La valeur n'est pas dans " + this._refValue.toString();
-        else
-            valid = true;
-
-        return { isValid: valid, message: msg };
-    }
-
-    protected modelToUI(v: any): string {
-        if (typeof (v) == "number")
-            return String(v);
-        return undefined;
-    }
-
-    protected validateUIValue(ui: string): { isValid: boolean, message: string } {
-        let valid: boolean = false;
-        let msg: string = undefined;
-
-        let v: NumericalString = new NumericalString(ui);
-        if (!v.isNumerical)
-            msg = "Veuillez entrer une valeur numérique";
-        else
-            valid = true;
-
-        return { isValid: valid, message: msg };
-    }
-
-    protected uiToModel(ui: string) {
-        return +ui;
-    }
-}
-
-@Component({
-    selector: "ngparam-step",
-    templateUrl: "../generic-input/generic-input.component.html"
-})
-export class NgParamStepComponent extends GenericInputComponent {
-    /**
-     * valeur actuelle du pas
-     */
-    private _currentValue: number;
-
-    /**
-     * reférence (valeur maxi pour le pas)
-     */
-    private _refValue: Pair;
-
-    @Output()
-    private onChange = new EventEmitter<string>();
-
-    constructor(private intlService: InternationalisationService) {
-        super();
-    }
-
-    public set refValue(v: Pair) {
-        this._refValue = v;
-    }
-
-    protected getModelValue(): any {
-        return this._currentValue;
-    }
-
-    protected setModelValue(v: any) {
-        this._currentValue = v;
-        this.onChange.emit("value");
-    }
-
-    protected validateModelValue(v: any): { isValid: boolean, message: string } {
-        let msg = undefined;
-        let valid = false;
-
-        if (!this._refValue.intervalHasValue(v))
-            msg = "La valeur n'est pas dans " + this._refValue.toString();
-        else {
-            valid = v > 0;
-            if (!valid)
-                msg = "La valeur ne peut pas être <= 0";
-        }
-
-        return { isValid: valid, message: msg };
-    }
-
-    protected modelToUI(v: any): string {
-        if (typeof (v) == "number")
-            return String(v);
-        return "<invalid>";
-    }
-
-    protected validateUIValue(ui: string): { isValid: boolean, message: string } {
-        let valid: boolean = false;
-        let msg: string = undefined;
-
-        let v: NumericalString = new NumericalString(ui);
-        if (!v.isNumerical)
-            msg = "Veuillez entrer une valeur numérique";
-        else
-            valid = true;
-
-        return { isValid: valid, message: msg };
-    }
-
-    protected uiToModel(ui: string) {
-        return +ui;
-    }
-}
-
 @Component({
     selector: "param-field-line",
     templateUrl: "./param-field-line.component.html",
@@ -244,142 +24,13 @@ export class NgParamStepComponent extends GenericInputComponent {
         }`
     ]
 })
-export class ParamFieldLineComponent implements DoCheck {
+export class ParamFieldLineComponent {
     @Input("param")
     private _param: NgParameter;
 
-    /**
-     * composant de saisie du minimum
-     */
-    @ViewChild(NgParamMinComponent)
-    private _minComponent: NgParamMinComponent;
-
-    /**
-     * composant de saisie du maximum
-     */
-    @ViewChild(NgParamMaxComponent)
-    private _maxComponent: NgParamMaxComponent;
-
-    /**
-     * composant de saisie du pas de variation
-     */
-    @ViewChild(NgParamStepComponent)
-    private _stepComponent: NgParamStepComponent;
-
     constructor(private intlService: InternationalisationService) {
     }
 
-    private getDefaultMin(): number {
-        switch (this._param.domain.domain) {
-            case ParamDomainValue.ANY:
-            case ParamDomainValue.NOT_NULL:
-                return -10;
-
-            default:
-                return this._param.domain.minValue;
-        }
-    }
-
-    private getDefaultMax(): number {
-        switch (this._param.domain.domain) {
-            case ParamDomainValue.INTERVAL:
-                return this._param.domain.maxValue;
-
-            default:
-                return 10;
-        }
-    }
-
-    private initMinMaxStep() {
-        // valeur pour min (celle déjà définie ou celle déduite du domaine de définition)
-        let min: number = this._param.minValue;
-        let ok = min != undefined
-        if (ok) {
-            try {
-                // on la vérifie
-                this._param.checkValue(min);
-                ok = true;
-            }
-            catch (e) {
-                ok = false;
-            }
-        }
-        if (!ok)
-            min = this.getDefaultMin();
-
-        // valeur pour max (celle déjà définie ou celle déduite du domaine de définition)
-        let max: number = this._param.maxValue;
-        ok = max != undefined
-        if (ok) {
-            try {
-                // on la vérifie
-                this._param.checkValue(max);
-                ok = true;
-            }
-            catch (e) {
-                ok = false;
-            }
-        }
-        if (!ok)
-            min = this.getDefaultMax();
-
-        this._minComponent.refValue = new Pair(this._param.domain.minValue, max);
-        this._minComponent.model = min;
-
-        this._maxComponent.refValue = new Pair(min, this._param.domain.maxValue);
-        this._maxComponent.model = max;
-
-        this.updateStepComponentRef();
-
-        let step = this._param.stepValue;
-        if (step == undefined)
-            step = (max - min) / 20;
-        if (min == -Infinity || max == Infinity)
-            step = 10;
-        this._stepComponent.model = step;
-    }
-
-    private onMinChanged(val: string) {
-        this.updateStepComponentRef();
-
-        this._maxComponent.refValue = new Pair(this._minComponent.model, this._param.domain.maxValue);
-        this._maxComponent.validateModel();
-
-        if (this._minComponent.validateModel())
-            this._param.minValue = this._minComponent.model;
-    }
-
-    private onMaxChanged(val: string) {
-        this.updateStepComponentRef();
-
-        this._minComponent.refValue = new Pair(this._param.domain.minValue, this._maxComponent.model);
-        this._minComponent.validateModel();
-
-        if (this._maxComponent.validateModel())
-            this._param.maxValue = this._maxComponent.model;
-    }
-
-    private onStepChanged(val: string) {
-        if (this._stepComponent.validateModel())
-            this._param.stepValue = this._stepComponent.model;
-    }
-
-    public ngDoCheck() {
-        // initialisation des champs min/max/step à l'apparition de ces contrôles
-        if (this._minComponent != undefined && !this._minComponent.isInit) {
-            this._minComponent.isInit = true;
-            this.initMinMaxStep();
-        }
-    }
-
-    /**
-     * met à jour la valeur de référence du composant gérant le pas de variation
-     */
-    private updateStepComponentRef() {
-        this._stepComponent.refValue = new Pair(1e-9, this._maxComponent.model - this._minComponent.model);
-        this._stepComponent.validateModel();
-    }
-
     private get title(): string {
         let t = "";
         if (this._param.label != undefined)
@@ -397,18 +48,6 @@ export class ParamFieldLineComponent implements DoCheck {
         return this.intlService.localizeText("INFO_PARAMFIELD_PARAMVARIER");
     }
 
-    private get uitextValeurMini() {
-        return this.intlService.localizeText("INFO_PARAMFIELD_VALEURMINI");
-    }
-
-    private get uitextValeurMaxi() {
-        return this.intlService.localizeText("INFO_PARAMFIELD_VALEURMAXI");
-    }
-
-    private get uitextPasVariation() {
-        return this.intlService.localizeText("INFO_PARAMFIELD_PASVARIATION");
-    }
-
     private get uitextParamCalculer() {
         return this.intlService.localizeText("INFO_PARAMFIELD_PARAMCALCULER");
     }
diff --git a/src/app/components/param-values/ngparam-max.component.ts b/src/app/components/param-values/ngparam-max.component.ts
new file mode 100644
index 0000000000000000000000000000000000000000..1db61f59ae15491bf3c1bb8e3a584faea158337b
--- /dev/null
+++ b/src/app/components/param-values/ngparam-max.component.ts
@@ -0,0 +1,77 @@
+import { Component, Output, EventEmitter } from "@angular/core";
+
+import { NumericalString, Pair } from "jalhyd";
+
+import { GenericInputComponent } from "../generic-input/generic-input.component";
+import { InternationalisationService } from "../../services/internationalisation/internationalisation.service";
+
+@Component({
+    selector: "ngparam-max",
+    templateUrl: "../generic-input/generic-input.component.html"
+})
+export class NgParamMaxComponent extends GenericInputComponent {
+    /**
+       * valeur actuelle du maximum
+       */
+    private _currentValue: number;
+
+    /**
+     * reférence (valeur maxi pour le maximum)
+     */
+    private _refValue: Pair;
+
+    @Output()
+    private onChange = new EventEmitter<string>();
+
+    constructor(private intlService: InternationalisationService) {
+        super();
+    }
+
+    public set refValue(v: Pair) {
+        this._refValue = v;
+    }
+
+    protected getModelValue(): any {
+        return this._currentValue;
+    }
+
+    protected setModelValue(v: any) {
+        this._currentValue = v;
+        this.onChange.emit("value");
+    }
+
+    protected validateModelValue(v: any): { isValid: boolean, message: string } {
+        let msg = undefined;
+        let valid = false;
+
+        if (!this._refValue.intervalHasValue(v))
+            msg = "La valeur n'est pas dans " + this._refValue.toString();
+        else
+            valid = true;
+
+        return { isValid: valid, message: msg };
+    }
+
+    protected modelToUI(v: any): string {
+        if (typeof (v) == "number")
+            return String(v);
+        return undefined;
+    }
+
+    protected validateUIValue(ui: string): { isValid: boolean, message: string } {
+        let valid: boolean = false;
+        let msg: string = undefined;
+
+        let v: NumericalString = new NumericalString(ui);
+        if (!v.isNumerical)
+            msg = "Veuillez entrer une valeur numérique";
+        else
+            valid = true;
+
+        return { isValid: valid, message: msg };
+    }
+
+    protected uiToModel(ui: string) {
+        return +ui;
+    }
+}
diff --git a/src/app/components/param-values/ngparam-min.component.ts b/src/app/components/param-values/ngparam-min.component.ts
new file mode 100644
index 0000000000000000000000000000000000000000..ca6749eb40445573a869cabbd8d4d8df10a53b38
--- /dev/null
+++ b/src/app/components/param-values/ngparam-min.component.ts
@@ -0,0 +1,79 @@
+import { Component, Output, EventEmitter } from "@angular/core";
+
+import { NumericalString, Pair } from "jalhyd";
+
+import { GenericInputComponent } from "../generic-input/generic-input.component";
+import { InternationalisationService } from "../../services/internationalisation/internationalisation.service";
+
+@Component({
+    selector: "ngparam-min",
+    templateUrl: "../generic-input/generic-input.component.html"
+})
+export class NgParamMinComponent extends GenericInputComponent {
+    /**
+     * valeur actuelle du minimum
+     */0.49999999995
+    private _currentValue: number;
+
+    /**
+     * reférence (valeur mini pour le minimum)
+     */
+    private _refValue: Pair;
+
+    @Output()
+    private onChange = new EventEmitter<string>();
+
+    public isInit: boolean;
+
+    constructor(private intlService: InternationalisationService) {
+        super();
+    }
+
+    public set refValue(v: Pair) {
+        this._refValue = v;
+    }
+
+    protected getModelValue(): any {
+        return this._currentValue;
+    }
+
+    protected setModelValue(v: any) {
+        this._currentValue = v;
+        this.onChange.emit("value");
+    }
+
+    protected validateModelValue(v: any): { isValid: boolean, message: string } {
+        let msg = undefined;
+        let valid = false;
+
+        if (!this._refValue.intervalHasValue(v))
+            msg = "La valeur n'est pas dans " + this._refValue.toString();
+        else
+            valid = true;
+
+        return { isValid: valid, message: msg };
+    }
+
+    protected modelToUI(v: any): string {
+        if (typeof (v) == "number")
+            return String(v);
+        return undefined;
+    }
+
+    protected validateUIValue(ui: string): { isValid: boolean, message: string } {
+        let valid: boolean = false;
+        let msg: string = undefined;
+
+        let v: NumericalString = new NumericalString(ui);
+        if (!v.isNumerical)
+            msg = "Veuillez entrer une valeur numérique";
+        else
+            valid = true;
+
+        return { isValid: valid, message: msg };
+    }
+
+    protected uiToModel(ui: string): any {
+        return +ui;
+    }
+}
diff --git a/src/app/components/param-values/ngparam-step.component.ts b/src/app/components/param-values/ngparam-step.component.ts
new file mode 100644
index 0000000000000000000000000000000000000000..e34225e948d9ad9318441ed8655faac4b6d18ca0
--- /dev/null
+++ b/src/app/components/param-values/ngparam-step.component.ts
@@ -0,0 +1,80 @@
+import { Component, Output, EventEmitter } from "@angular/core";
+
+import { NumericalString, Pair } from "jalhyd";
+
+import { GenericInputComponent } from "../generic-input/generic-input.component";
+import { InternationalisationService } from "../../services/internationalisation/internationalisation.service";
+
+@Component({
+    selector: "ngparam-step",
+    templateUrl: "../generic-input/generic-input.component.html"
+})
+export class NgParamStepComponent extends GenericInputComponent {
+    /**
+     * valeur actuelle du pas
+     */
+    private _currentValue: number;
+
+    /**
+     * reférence (valeur maxi pour le pas)
+     */
+    private _refValue: Pair;
+
+    @Output()
+    private onChange = new EventEmitter<string>();
+
+    constructor(private intlService: InternationalisationService) {
+        super();
+    }
+
+    public set refValue(v: Pair) {
+        this._refValue = v;
+    }
+
+    protected getModelValue(): any {
+        return this._currentValue;
+    }
+
+    protected setModelValue(v: any) {
+        this._currentValue = v;
+        this.onChange.emit("value");
+    }
+
+    protected validateModelValue(v: any): { isValid: boolean, message: string } {
+        let msg = undefined;
+        let valid = false;
+
+        if (!this._refValue.intervalHasValue(v))
+            msg = "La valeur n'est pas dans " + this._refValue.toString();
+        else {
+            valid = v > 0;
+            if (!valid)
+                msg = "La valeur ne peut pas être <= 0";
+        }
+
+        return { isValid: valid, message: msg };
+    }
+
+    protected modelToUI(v: any): string {
+        if (typeof (v) == "number")
+            return String(v);
+        return "<invalid>";
+    }
+
+    protected validateUIValue(ui: string): { isValid: boolean, message: string } {
+        let valid: boolean = false;
+        let msg: string = undefined;
+
+        let v: NumericalString = new NumericalString(ui);
+        if (!v.isNumerical)
+            msg = "Veuillez entrer une valeur numérique";
+        else
+            valid = true;
+
+        return { isValid: valid, message: msg };
+    }
+
+    protected uiToModel(ui: string) {
+        return +ui;
+    }
+}
diff --git a/src/app/components/param-values/param-values.component.html b/src/app/components/param-values/param-values.component.html
new file mode 100644
index 0000000000000000000000000000000000000000..e3adf9e933181dac3f7099bb05dc17f308cbf109
--- /dev/null
+++ b/src/app/components/param-values/param-values.component.html
@@ -0,0 +1,24 @@
+<div class="row">
+    <div class="btn-group col-12 col-sm-3" dropdown (click)="onSelectValueMode($event)">
+        <button dropdownToggle class="btn btn-primary dropdown-toggle waves-light my-1" type="button" mdbRippleRadius>
+            {{currentLabel}}
+        </button>
+        <div class="dropdown-menu">
+            <a class="dropdown-item" *ngFor="let e of _valueModes" [value]=e.value>{{e.label}}</a>
+        </div>
+    </div>
+
+    <div *ngIf="!isList" class="col-12 col-sm-3">
+        <ngparam-min [title]="uitextValeurMini" (onChange)="onMinChanged($event)"></ngparam-min>
+    </div>
+    <div *ngIf="!isList" class="col-12 col-sm-3">
+        <ngparam-max [title]="uitextValeurMaxi" (onChange)="onMaxChanged($event)"></ngparam-max>
+    </div>
+    <div *ngIf="!isList" class="col-12 col-sm-3">
+        <ngparam-step [title]="uitextPasVariation" (onChange)="onStepChanged($event)"></ngparam-step>
+    </div>
+
+    <div *ngIf="isList" class="col-12 col-sm-6">
+        <value-list title="valeurs séparées par ';'" (onChange)="onListChanged($event)"></value-list>
+    </div>
+</div>
\ No newline at end of file
diff --git a/src/app/components/param-values/param-values.component.ts b/src/app/components/param-values/param-values.component.ts
new file mode 100644
index 0000000000000000000000000000000000000000..e0f63c158b5bc8e902f25d1782a74fe6ae4d936b
--- /dev/null
+++ b/src/app/components/param-values/param-values.component.ts
@@ -0,0 +1,286 @@
+import { Component, Input, Output, EventEmitter, ViewChild, DoCheck } from "@angular/core";
+
+import { ParamDomainValue, Pair } from "jalhyd";
+
+import { InternationalisationService } from "../../services/internationalisation/internationalisation.service";
+import { NgParameter, ParamValueMode } from "../../formulaire/ngparam";
+import { GenericInputComponent } from "../generic-input/generic-input.component";
+import { NgParamMinComponent } from "./ngparam-min.component";
+import { NgParamMaxComponent } from "./ngparam-max.component";
+import { NgParamStepComponent } from "./ngparam-step.component";
+
+
+@Component({
+    selector: "value-list",
+    templateUrl: "../generic-input/generic-input.component.html"
+})
+export class ValueListComponent extends GenericInputComponent {
+    @Output()
+    private onChange = new EventEmitter<string>();
+
+    public _list: number[];
+
+    public isInit;
+
+    constructor(private intlService: InternationalisationService) {
+        super();
+        this._list = [];
+    }
+
+    protected getModelValue(): any {
+        return this._list;
+    }
+
+    protected setModelValue(l: any) {
+        if (typeof (l) == "number") {
+            this._list = [];
+            this._list.push(l);
+        }
+        else
+            this._list = l;
+        this.onChange.emit("value");
+    }
+
+    protected validateModelValue(v: any): { isValid: boolean, message: string } {
+        let msg = undefined;
+        let valid = false;
+
+        if (v instanceof Array) {
+            valid = true;
+            for (let e of v) {
+                valid = valid && (typeof (e) == "number")
+                if (!valid) {
+                    msg = "La valeur n'est pas une liste de nombres"
+                    break;
+                }
+            }
+        }
+
+        return { isValid: valid, message: msg };
+    }
+
+    protected modelToUI(v: any): string {
+        let res = "";
+        for (let e of v) {
+            if (res != "")
+                res += ";";
+            res += String(e);
+        }
+        return res;
+    }
+
+    protected validateUIValue(ui: string): { isValid: boolean, message: string } {
+        let valid: boolean = false;
+        let msg: string = undefined;
+
+        let tmp: string[] = ui.split(";");
+        let res = true;
+        for (let v of tmp) {
+            let isnum = v != "" && (+v == +v);
+            res = res && isnum;
+            if (!res)
+                break;
+        }
+
+        if (!res)
+            msg = "Veuillez entrer une liste de nombres";
+        else
+            valid = true;
+
+        return { isValid: valid, message: msg };
+    }
+
+    protected uiToModel(ui: string) {
+        let tmp: string[] = ui.split(";");
+        let res = [];
+        for (let v of tmp)
+            res.push(+v);
+        return res;
+    }
+}
+
+@Component({
+    selector: "param-values",
+    templateUrl: "./param-values.component.html",
+    styles: [
+        `.btn-on {
+            color:#505050;
+            border: 3px solid #505050;
+            background-color:white;
+            text-transform: uppercase;
+            font-size: 0.8em;
+        }`,
+        `.btn-off {
+            color:white;
+            border: 3px solid #505050;
+            background-color:#505050;
+            text-transform: uppercase;
+            font-size: 0.8em;
+        }`
+    ]
+})
+export class ParamValuesComponent implements DoCheck {
+    @Input("param")
+    private _param: NgParameter;
+
+    /**
+     * true si liste de valeur, false si min/max/pas
+     */
+    private _list: boolean;
+
+    private _valueModes = [];
+
+    /**
+     * composant de saisie du minimum
+     */
+    @ViewChild(NgParamMinComponent)
+    private _minComponent: NgParamMinComponent;
+
+    /**
+     * composant de saisie du maximum
+     */
+    @ViewChild(NgParamMaxComponent)
+    private _maxComponent: NgParamMaxComponent;
+
+    /**
+     * composant de saisie du pas de variation
+     */
+    @ViewChild(NgParamStepComponent)
+    private _stepComponent: NgParamStepComponent;
+
+    /**
+     * composant de saisie d'une liste de valeurs
+     */
+    @ViewChild(ValueListComponent)
+    private _listComponent: ValueListComponent;
+
+    constructor(private intlService: InternationalisationService) {
+        this._valueModes.push({ "value": ParamValueMode.MINMAX, "label": "Min/max" });
+        this._valueModes.push({ "value": ParamValueMode.LISTE, "label": "Liste" });
+    }
+   
+    private initMinMaxStep() {
+        // valeur pour min (celle déjà définie ou celle déduite du domaine de définition)
+        let min: number = this._param.minValue;
+        let ok = min != undefined
+        if (ok) {
+            try {
+                // on la vérifie
+                this._param.checkValue(min);
+                ok = true;
+            }
+            catch (e) {
+                ok = false;
+            }
+        }
+        if (!ok)
+            min = this._param.getValue() / 2;
+
+        // valeur pour max (celle déjà définie ou celle déduite du domaine de définition)
+        let max: number = this._param.maxValue;
+        ok = max != undefined
+        if (ok) {
+            try {
+                // on la vérifie
+                this._param.checkValue(max);
+                ok = true;
+            }
+            catch (e) {
+                ok = false;
+            }
+        }
+        if (!ok)
+            max = this._param.getValue() * 2;
+
+        this._minComponent.refValue = new Pair(this._param.domain.minValue, max);
+        this._minComponent.model = min;
+
+        this._maxComponent.refValue = new Pair(min, this._param.domain.maxValue);
+        this._maxComponent.model = max;
+
+        this.updateStepComponentRef();
+
+        this._stepComponent.model = (this._maxComponent.model - this._minComponent.model) / 20;
+    }
+
+    private onMinChanged(val: string) {
+        this.updateStepComponentRef();
+
+        this._maxComponent.refValue = new Pair(this._minComponent.model, this._param.domain.maxValue);
+        this._maxComponent.validateModel();
+
+        if (this._minComponent.validateModel())
+            this._param.minValue = this._minComponent.model;
+    }
+
+    private onMaxChanged(val: string) {
+        this.updateStepComponentRef();
+
+        this._minComponent.refValue = new Pair(this._param.domain.minValue, this._maxComponent.model);
+        this._minComponent.validateModel();
+
+        if (this._maxComponent.validateModel())
+            this._param.maxValue = this._maxComponent.model;
+    }
+
+    private onStepChanged(val: string) {
+        if (this._stepComponent.validateModel())
+            this._param.stepValue = this._stepComponent.model;
+    }
+
+    private onListChanged(val: string) {
+        if (this._listComponent.validateModel())
+            this._param.valueList = this._listComponent.model;
+    }
+
+    public ngDoCheck() {
+        // initialisation des champs min/max/step à l'apparition de ces contrôles
+        if (this._minComponent != undefined && !this._minComponent.isInit) {
+            this._minComponent.isInit = true;
+            this.initMinMaxStep();
+        }
+
+        if (this._listComponent != undefined && !this._listComponent.isInit) {
+            this._listComponent.isInit = true;
+
+            this._listComponent.model = [];
+            if (this._param.isDefined)
+                this._listComponent.model.push(this._param.getValue());
+        }
+    }
+
+    /**
+     * met à jour la valeur de référence du composant gérant le pas de variation
+     */
+    private updateStepComponentRef() {
+        this._stepComponent.refValue = new Pair(1e-9, this._maxComponent.model - this._minComponent.model);
+        this._stepComponent.validateModel();
+    }
+
+    private get uitextValeurMini() {
+        return this.intlService.localizeText("INFO_PARAMFIELD_VALEURMINI");
+    }
+
+    private get uitextValeurMaxi() {
+        return this.intlService.localizeText("INFO_PARAMFIELD_VALEURMAXI");
+    }
+
+    private get uitextPasVariation() {
+        return this.intlService.localizeText("INFO_PARAMFIELD_PASVARIATION");
+    }
+
+    private get isList(): boolean {
+        return this._param.valueMode == ParamValueMode.LISTE;
+    }
+
+    /**
+     * valeur courante affichée dans le select
+     */
+    private get currentLabel(): string {
+        return ParamValueMode[this._param.valueMode];
+    }
+
+    private onSelectValueMode(event: any) {
+        this._param.valueMode = event.target.value;
+    }
+}
diff --git a/src/app/components/results-graph/results-graph.component.html b/src/app/components/results-graph/results-graph.component.html
new file mode 100644
index 0000000000000000000000000000000000000000..31648552404f6b7a98bded70096955b20f0b9ac0
--- /dev/null
+++ b/src/app/components/results-graph/results-graph.component.html
@@ -0,0 +1,12 @@
+<div class="row">
+    <div class="col-12">
+        <chart [type]="graph_type" [data]="graph_data" [options]="graph_options">
+        </chart>
+    </div>
+</div>
+
+<div class="row">
+    <div class="col-4 mx-auto">
+        <graph-type (selectChange)="onGraphTypeSelectChange($event)"></graph-type>
+    </div>
+</div>
\ No newline at end of file
diff --git a/src/app/components/results-graph/results-graph.component.ts b/src/app/components/results-graph/results-graph.component.ts
new file mode 100644
index 0000000000000000000000000000000000000000..83b55ff9897869cd215e8deadf8688d4a2b6c258
--- /dev/null
+++ b/src/app/components/results-graph/results-graph.component.ts
@@ -0,0 +1,178 @@
+import { Component, AfterViewInit, EventEmitter, Output, ViewChild } from '@angular/core';
+
+import { FixedVarResults, GraphType } from "../../results/fixed-var-results";
+import { GenericSelectComponent } from '../generic-select/generic-select.component';
+
+@Component({
+    selector: "graph-type",
+    templateUrl: "../generic-select/generic-select.component.html"
+})
+export class GraphTypeSelectComponent extends GenericSelectComponent {
+    private _entries: GraphType[] = [GraphType.Histogram, GraphType.Scatter];
+    private _entriesLabels: string[] = ["Histogramme", "XY"];
+
+    protected get entries(): any[] {
+        return this._entries;
+    }
+
+    protected entryLabel(entry: any): string {
+        // return entry;
+        const i = this._entries.indexOf(entry);
+        return this._entriesLabels[i];
+    }
+}
+
+@Component({
+    selector: 'results-graph',
+    templateUrl: './results-graph.component.html'
+})
+export class ResultsGraphComponent { //implements AfterViewInit {
+    private _results: FixedVarResults;
+
+    private _currentGraphType: GraphType;
+
+    @ViewChild(GraphTypeSelectComponent)
+    private _graphTypeComponent: GraphTypeSelectComponent;
+
+    /*
+     * config du graphe
+     */
+    private graph_type: string;
+    private graph_data = {};
+    private graph_options = {
+        responsive: true,
+        maintainAspectRatio: true,
+        animation: {
+            duration: 0
+        },
+        legend: {
+            display: false
+        },
+        title: {
+            display: true,
+            text: ""
+        }
+    };
+
+    public set results(r: FixedVarResults) {
+        this._results = r;
+        this._currentGraphType = r.graphType;
+        this._graphTypeComponent.selectedValue = this._currentGraphType;
+    }
+
+    public updateView() {
+        switch (this._currentGraphType) {
+            case GraphType.Histogram:
+                this.graph_type = "bar";
+                this.generateBarGraph();
+                break;
+
+            case GraphType.HistoLine:
+                this.graph_type = "line";
+                this.generateLineGraph();
+                break;
+
+            default:
+                this.graph_type = "scatter";
+                this.generateScatterGraph();
+                break;
+        }
+    }
+
+    private onGraphTypeSelectChange(event) {
+        this._currentGraphType = event;
+        this.updateView();
+    }
+
+    /**
+     * génère les données d'un graphe de type "line"
+     */
+    private generateLineGraph() {
+        let labs = [];
+        let dat = [];
+        for (let i in this._results.varResults) {
+            let r = this._results.varResults[i];
+            labs.push(r["param"]);
+            dat.push(this._results.varGraph[i]);
+        }
+
+        this.graph_options.title.text = this._results.graphTitle;
+
+        this.graph_data = {
+            labels: labs,
+            datasets: [
+                {
+                    label: "",
+                    data: dat
+                }
+            ]
+        };
+    }
+
+    /**
+     * génère les données d'un graphe de type "bar"
+     */
+    private generateBarGraph() {
+        let labs = [];
+        let dat = [];
+        for (let i in this._results.varResults) {
+            let r = this._results.varResults[i];
+            labs.push(r["param"]);
+            dat.push(this._results.varGraph[i]);
+        }
+
+        this.graph_options.title.text = this._results.graphTitle;
+        this.graph_options["scales"] = {
+            xAxes: [{
+                gridLines: {
+                    offsetGridLines: true
+                }
+            }]
+        };
+
+        this.graph_data = {
+            labels: labs,
+            datasets: [
+                {
+                    label: "",
+                    data: dat
+                }
+            ]
+        };
+    }
+
+    /**
+     * génère les données d'un graphe de type "scatter"
+     */
+    private generateScatterGraph() {
+        let dat = [];
+        for (let i in this._results.varResults) {
+            let r = this._results.varResults[i];
+            dat.push({ x: r["param"], y: this._results.varGraph[i] });
+        }
+
+        this.graph_options.title.text = this._results.graphTitle;
+        this.graph_options["scales"] = {
+            xAxes: [{
+                type: 'linear',
+                position: 'bottom'
+            }],
+            yAxes: [{
+                type: 'linear',
+                position: 'left'
+            }]
+        };
+
+        this.graph_data = {
+            datasets: [
+                {
+                    label: "",
+                    data: dat,
+                    borderColor: "#808080", // couleur de la ligne
+                    backgroundColor: "rgba(0,0,0,0)",  // couleur de remplissage sous la courbe : transparent
+                    showLine: "true"
+                }
+            ]
+        };
+    }
+}
diff --git a/src/app/formulaire/formulaire-definition.ts b/src/app/formulaire/formulaire-definition.ts
index f32886e4a9e218f3cf68170684819d0c6cd6187c..23615dcf41abd49f57a27b7121f7e6d3fd01d10a 100644
--- a/src/app/formulaire/formulaire-definition.ts
+++ b/src/app/formulaire/formulaire-definition.ts
@@ -7,7 +7,7 @@ import { ParamService } from "../services/param/param.service";
 import { InternationalisationService } from "../services/internationalisation/internationalisation.service";
 import { ApplicationSetupService } from "../services/app-setup/app-setup.service";
 import { Field } from "./field";
-import { NgParameter, ParamRadioConfig } from "./ngparam";
+import { NgParameter, ParamRadioConfig, ParamValueMode } from "./ngparam";
 import { InputField } from "./input-field";
 import { CheckField } from "./check-field";
 import { SelectField } from "./select-field";
@@ -19,7 +19,7 @@ import { FormulaireElement } from "./formulaire-element";
 import { ValueDependency } from "./value-dependency";
 import { ValueDependencyCondition } from "./value-dependency-condition";
 import { ExistenceDependency } from "./existence-dependency";
-import { FixedVarResults } from "../results/fixed-var-results";
+import { FixedVarResults, GraphType } from "../results/fixed-var-results";
 import { SectionResults } from "../results/section-results";
 import { RemousResults } from "../results/remous-results";
 import { StringMap } from "../stringmap";
@@ -1118,20 +1118,43 @@ export class FormulaireDefinition extends Observable {
             this._fixVarResults.setVariableParamHeaderFromParameter(varParam, !rg);
             this._fixVarResults.setVariableResultHeaderFromParameter(computedParam);
 
-            let min: number = +varParam.minValue;
-            let max: number = +varParam.maxValue;
-            let step: number = +varParam.stepValue;
+            switch (varParam.valueMode) {
+                case ParamValueMode.MINMAX:
+                    this._fixVarResults.graphType = GraphType.Scatter;
 
-            for (let val = min; val <= max; val += step) {
-                prms[varParam.symbol].v = val;
+                    let min: number = +varParam.minValue;
+                    let max: number = +varParam.maxValue;
+                    let step: number = +varParam.stepValue;
 
-                let res: Result = this.runNubCalc(nub, computedParam, computePrec);
-                if (res.ok) {
-                    this._fixVarResults.addVarResult(val, res.vCalc);
-                }
-                else {
-                    this._fixVarResults.addLogMessages(res.log);
-                }
+                    for (let val = min; val <= max; val += step) {
+                        prms[varParam.symbol].v = val;
+
+                        let res: Result = this.runNubCalc(nub, computedParam, computePrec);
+                        if (res.ok) {
+                            this._fixVarResults.addVarResult(val, res.vCalc);
+                        }
+                        else {
+                            this._fixVarResults.addLogMessages(res.log);
+                        }
+                    }
+                    break;
+
+                case ParamValueMode.LISTE:
+                    this._fixVarResults.graphType = GraphType.Histogram;
+
+                    for (let val of varParam.valueList) {
+                        prms[varParam.symbol].v = val;
+
+                        let res: Result = this.runNubCalc(nub, computedParam, computePrec);
+                        if (res.ok) {
+                            this._fixVarResults.addVarResult(val, res.vCalc);
+                        }
+                        else {
+                            this._fixVarResults.addLogMessages(res.log);
+                        }
+                    }
+
+                    break;
             }
 
             this._fixVarResults.graphTitle = computedParam.symbol + " = f( " + varParam.symbol + " )";
diff --git a/src/app/formulaire/ngparam.ts b/src/app/formulaire/ngparam.ts
index 2db893c26360d1c2a8cf5ac0656f21071ac0a2c1..9d94db8e4395f696f59e929e9106b7fd29edc46e 100644
--- a/src/app/formulaire/ngparam.ts
+++ b/src/app/formulaire/ngparam.ts
@@ -24,6 +24,21 @@ export enum ParamRadioConfig {
 };
 
 
+/**
+ * mode de génération des valeurs d'entrée lors d'un calcul avec plusieurs valeurs
+ */
+export enum ParamValueMode {
+    /**
+     * min, max, pas
+     */
+    MINMAX,
+
+    /**
+     * liste de valeurs discrètes
+     */
+    LISTE
+}
+
 /**
  * classe englobante de ParamDefinition (champs supplémentaires pour l'affichage, radio boutons, ...)
  */
@@ -35,35 +50,11 @@ export class NgParameter extends InputField {
     public minValue: number; // valeur min dans le cas ParamRadioConfig.VAR
     public maxValue: number; // valeur max dans le cas ParamRadioConfig.VAR
     public stepValue: number; // pas de progression dans le cas ParamRadioConfig.VAR
+    public valueList: number[];
+    public valueMode: ParamValueMode = ParamValueMode.MINMAX;
 
     constructor(private _paramDef: ParamDefinition, formId: number) {
         super(_paramDef.computeNodeType, _paramDef.symbol, formId);
-        switch (this._paramDef.getDomain().domain) {
-            case ParamDomainValue.ANY:
-                this.minValue = -10;
-                this.maxValue = 10;
-                this.stepValue = 0.5;
-                break;
-
-            case ParamDomainValue.POS:
-            case ParamDomainValue.NOT_NULL:
-                this.minValue = 0.01;
-                this.maxValue = 10;
-                this.stepValue = 0.5;
-                break;
-
-            case ParamDomainValue.INTERVAL:
-                this.minValue = this._paramDef.getDomain().minValue;
-                this.maxValue = this._paramDef.getDomain().maxValue;
-                this.stepValue = (this.maxValue - this.minValue) / 10;
-                break;
-
-            case ParamDomainValue.POS_NULL:
-                this.minValue = 0;
-                this.maxValue = 10;
-                this.stepValue = 0.5;
-                break;
-        }
     }
 
     get symbol(): string {
@@ -74,10 +65,6 @@ export class NgParameter extends InputField {
         return this._paramDef.getDomain();
     }
 
-    // get alias(): string {
-    //     return this._paramDef.symbolAlias;
-    // }
-
     public getValue() {
         return this._paramDef.v;
     }
diff --git a/src/app/results/fixed-var-results.ts b/src/app/results/fixed-var-results.ts
index 33b49bb07985460136114bf4455a5f2ecc5bcff1..97d16be54d613650eb983d9ccb41279b5a629303 100644
--- a/src/app/results/fixed-var-results.ts
+++ b/src/app/results/fixed-var-results.ts
@@ -3,6 +3,26 @@ import { cLog } from "jalhyd";
 import { NgParameter } from "../formulaire/ngparam";
 import { CalculatorResults } from "./calculator-results";
 
+/**
+ * type de graphe
+ */
+export enum GraphType {
+    /**
+     * histogramme
+     */
+    Histogram,
+
+    /**
+     * points indépendants reliés par une courbe
+     */
+    HistoLine,
+
+    /**
+     * graphe XY classique
+     */
+    Scatter,
+}
+
 export class FixedVarResults extends CalculatorResults {
     /**
      * résultats fixed (true) ou variés (false)
@@ -34,6 +54,11 @@ export class FixedVarResults extends CalculatorResults {
      */
     private _graphTitle: string;
 
+    /**
+     * type de graphe
+     */
+    public graphType: GraphType = GraphType.Scatter;
+
     /**
      * tableau de valeurs du graphe des résultats variés
      */