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 */