From 2f8e8051ef53be1e07c91e45087afee2d008a7dd Mon Sep 17 00:00:00 2001 From: "mathias.chouet" <mathias.chouet@irstea.fr> Date: Thu, 23 May 2019 15:53:28 +0200 Subject: [PATCH] work on #62adapted CalcSerie() to multiple varying parameters removed valueMode constraint on max. 1 variated parameter added jasmine tests --- spec/param/param_modes.spec.ts | 8 +- spec/param/param_multivar.spec.ts | 296 ++++++++++++++++++++++++++++++ src/nub.ts | 209 ++++++++++++--------- src/param/param-definition.ts | 136 +++++--------- src/param/param-values.ts | 20 ++ src/section/section_parametree.ts | 65 +------ src/structure/structure.ts | 4 +- 7 files changed, 486 insertions(+), 252 deletions(-) create mode 100644 spec/param/param_multivar.spec.ts diff --git a/spec/param/param_modes.spec.ts b/spec/param/param_modes.spec.ts index b01100c4..bf3ecb6b 100644 --- a/spec/param/param_modes.spec.ts +++ b/spec/param/param_modes.spec.ts @@ -141,17 +141,12 @@ function createEnv() { function checkConsistency(nubToCheck: Nub) { expect(nubToCheck.calculatedParam).toBeDefined(); let calcCount = 0; - let varCount = 0; for (const p of nubToCheck.parameterIterator) { if (p.valueMode === ParamValueMode.CALCUL) { calcCount++; } - if (p.valueMode === ParamValueMode.MINMAX || p.valueMode === ParamValueMode.LISTE) { - varCount++; - } } expect(calcCount).toBe(1); - expect(varCount).toBeLessThanOrEqual(1); nubToCheck.CalcSerie(); expect(nubToCheck.result).toBeDefined(); @@ -232,8 +227,7 @@ describe("cohérence des modes de paramètres : ", () => { testModesPermutations(nub2); }); - // @TODO reenable after fixing jalhyd#74 - xit("ouvrages en parallèle : Cloisons", () => { + it("ouvrages en parallèle : Cloisons", () => { createEnv(); testModesPermutations(nub3); }); diff --git a/spec/param/param_multivar.spec.ts b/spec/param/param_multivar.spec.ts new file mode 100644 index 00000000..f10a1038 --- /dev/null +++ b/spec/param/param_multivar.spec.ts @@ -0,0 +1,296 @@ +import { CreateStructure, ExtensionStrategy, LoiDebit, ParallelStructure, ParallelStructureParams, + ParamValueMode, Structure} from "../../src/index"; +import { RectangularStructureParams } from "../../src/structure/structure_cem88d"; + +let prms1: ParallelStructureParams; +let nub1: ParallelStructure; +let struct1: Structure; +let prmsStruct: RectangularStructureParams; + +function createSingleNubEnv() { + prms1 = new ParallelStructureParams(0.5, 102, 101.5); + nub1 = new ParallelStructure(prms1); + struct1 = CreateStructure(LoiDebit.Cunge80, nub1); + prmsStruct = struct1.prms as RectangularStructureParams; + nub1.addChild(struct1); +} + +function createLinkedNubEnv() { + // TODO +} + +/** + * Generates a random-length list of numbers close + * @param refVal reference value + * @param minSize minimum size of the list to be returned + * @param maxSize maximum size of the list to be returned + * @param maxDev maximum deviation, so that generated numbers + * value v verifies (refVal - maxDev) < v < (refVal + maxDev) + * @param sort if true, list will be ordered + */ +function randomList( + refVal: number, minSize: number = 1, maxSize: number = 20, maxDev: number = 3, sort: boolean = true +): number[] { + let list: number[] = []; + const size = minSize + Math.floor(Math.random() * ((maxSize - minSize) + 1)); // between minSize and maxSize + for (let i = 0; i < size; i++) { + let dev = (Math.random() * (2 * maxDev)) - (maxDev + 1); // between -maxDev and maxDev + dev = Number(Math.round(Number(dev + "e3")) + "e-3"); // trick to properly round to 3 decimals + list.push(refVal + dev); + } + if (sort) { + list = list.sort((a, b) => a - b); + } + return list; +} + +describe("multiple variated parameters - ", () => { + + describe("on the same Nub, with REPEAT_LAST strategy - ", () => { + + it("test 1 : minmax < list", () => { + createSingleNubEnv(); + prms1.Z1.setValues(100, 102, 0.5); // 5 values + prms1.Z1.extensionStrategy = ExtensionStrategy.REPEAT_LAST; + prmsStruct.L.setValues([ 1.89, 1.91, 1.99, 2, 1.7, 2.1, 2.18, 2.23, 2.6 ]); // 9 values + prmsStruct.L.extensionStrategy = ExtensionStrategy.REPEAT_LAST; + // check that both parameters are varying + expect(prms1.Z1.valueMode).toBe(ParamValueMode.MINMAX); + expect(prmsStruct.L.valueMode).toBe(ParamValueMode.LISTE); + // check that calculation works + const res = nub1.CalcSerie(); + expect(res).toBeDefined(); + expect(res.resultElements.length).toBe(9); + }); + + it("test 2 : minmax < minmax", () => { + createSingleNubEnv(); + prms1.Z1.setValues(100, 102, 0.5); // 5 values + prms1.Z1.extensionStrategy = ExtensionStrategy.REPEAT_LAST; + prmsStruct.L.setValues(1.89, 2.1, 0.03); // 8 values + prmsStruct.L.extensionStrategy = ExtensionStrategy.REPEAT_LAST; + // check that both parameters are varying + expect(prms1.Z1.valueMode).toBe(ParamValueMode.MINMAX); + expect(prmsStruct.L.valueMode).toBe(ParamValueMode.MINMAX); + // check that calculation works + const res = nub1.CalcSerie(); + expect(res).toBeDefined(); + expect(res.resultElements.length).toBe(8); + }); + + it("test 3 : list < minmax", () => { + createSingleNubEnv(); + prms1.Z1.setValues(100, 102, 0.1); // 21 values + prms1.Z1.extensionStrategy = ExtensionStrategy.REPEAT_LAST; + prmsStruct.L.setValues([ 1.89, 1.91, 1.99, 2, 1.7, 2.1, 2.18, 2.23, 2.6 ]); // 9 values + prmsStruct.L.extensionStrategy = ExtensionStrategy.REPEAT_LAST; + // check that both parameters are varying + expect(prms1.Z1.valueMode).toBe(ParamValueMode.MINMAX); + expect(prmsStruct.L.valueMode).toBe(ParamValueMode.LISTE); + // check that calculation works + const res = nub1.CalcSerie(); + expect(res).toBeDefined(); + expect(res.resultElements.length).toBe(21); + }); + + it("test 4 : list < list", () => { + createSingleNubEnv(); + prms1.Z1.setValues([ 100, 101, 102 ]); // 3 values + prms1.Z1.extensionStrategy = ExtensionStrategy.REPEAT_LAST; + prmsStruct.L.setValues([ 1.89, 1.91, 1.99, 2, 1.7, 2.1, 2.18, 2.23, 2.6 ]); // 9 values + prmsStruct.L.extensionStrategy = ExtensionStrategy.REPEAT_LAST; + // check that both parameters are varying + expect(prms1.Z1.valueMode).toBe(ParamValueMode.LISTE); + expect(prmsStruct.L.valueMode).toBe(ParamValueMode.LISTE); + // check that calculation works + const res = nub1.CalcSerie(); + expect(res).toBeDefined(); + expect(res.resultElements.length).toBe(9); + }); + + it("test 5 : list = list", () => { + createSingleNubEnv(); + prms1.Z1.setValues([ 100, 100.3, 100.7, 101, 101.3, 101.7, 102, 102.3, 115 ]); // 9 values + prms1.Z1.extensionStrategy = ExtensionStrategy.REPEAT_LAST; + prmsStruct.L.setValues([ 1.89, 1.91, 1.99, 2, 1.7, 2.1, 2.18, 2.23, 2.6 ]); // 9 values + prmsStruct.L.extensionStrategy = ExtensionStrategy.REPEAT_LAST; + // check that both parameters are varying + expect(prms1.Z1.valueMode).toBe(ParamValueMode.LISTE); + expect(prmsStruct.L.valueMode).toBe(ParamValueMode.LISTE); + // check that calculation works + const res = nub1.CalcSerie(); + expect(res).toBeDefined(); + expect(res.resultElements.length).toBe(9); + }); + + it("test 6 : list = minmax", () => { + createSingleNubEnv(); + prms1.Z1.setValues(100, 102, 0.5); // 5 values + prms1.Z1.extensionStrategy = ExtensionStrategy.REPEAT_LAST; + prmsStruct.L.setValues(1.89, 2.15, 0.06); // 5 values + prmsStruct.L.extensionStrategy = ExtensionStrategy.REPEAT_LAST; + // check that both parameters are varying + expect(prms1.Z1.valueMode).toBe(ParamValueMode.MINMAX); + expect(prmsStruct.L.valueMode).toBe(ParamValueMode.MINMAX); + // check that calculation works + const res = nub1.CalcSerie(); + expect(res).toBeDefined(); + expect(res.resultElements.length).toBe(5); + }); + + it("test 7 : all parameters varying with random lengths", () => { + createSingleNubEnv(); + let longest = 0; + let sparedParam = null; + // set all parameters to LIST mode with random length (except the first that has to stay in CALC mode) + for (const p of nub1.parameterIterator) { + if (p.visible) { + if (! sparedParam) { + sparedParam = p; + } else { + const rl = randomList(p.singleValue); + longest = Math.max(longest, rl.length); + p.setValues(rl); + p.extensionStrategy = ExtensionStrategy.REPEAT_LAST; + } + } + } + // check that all parameters are varying + for (const p of nub1.parameterIterator) { + if (p.visible && p !== sparedParam) { + expect(p.hasMultipleValues).toBe(true); + } + } + // check that calculation works and length of result is length of the longest values list + const res = nub1.CalcSerie(); + expect(res).toBeDefined(); + expect(res.resultElements.length).toBe(longest); + }); + }); + + describe("on the same Nub, with RECYCLE strategy - ", () => { + + it("test 1 : minmax < list", () => { + createSingleNubEnv(); + prms1.Z1.setValues(100, 102, 0.5); // 5 values + prms1.Z1.extensionStrategy = ExtensionStrategy.RECYCLE; + prmsStruct.L.setValues([ 1.89, 1.91, 1.99, 2, 1.7, 2.1, 2.18, 2.23, 2.6]); // 9 values + prmsStruct.L.extensionStrategy = ExtensionStrategy.RECYCLE; + // check that both parameters are varying + expect(prms1.Z1.valueMode).toBe(ParamValueMode.MINMAX); + expect(prmsStruct.L.valueMode).toBe(ParamValueMode.LISTE); + // check that calculation works + const res = nub1.CalcSerie(); + expect(res).toBeDefined(); + expect(res.resultElements.length).toBe(9); + }); + + it("test 2 : minmax < minmax", () => { + createSingleNubEnv(); + prms1.Z1.setValues(100, 102, 0.5); // 5 values + prms1.Z1.extensionStrategy = ExtensionStrategy.RECYCLE; + prmsStruct.L.setValues(1.89, 2.1, 0.03); // 8 values + prmsStruct.L.extensionStrategy = ExtensionStrategy.RECYCLE; + // check that both parameters are varying + expect(prms1.Z1.valueMode).toBe(ParamValueMode.MINMAX); + expect(prmsStruct.L.valueMode).toBe(ParamValueMode.MINMAX); + // check that calculation works + const res = nub1.CalcSerie(); + expect(res).toBeDefined(); + expect(res.resultElements.length).toBe(8); + }); + + it("test 3 : list < minmax", () => { + createSingleNubEnv(); + prms1.Z1.setValues(100, 102, 0.1); // 21 values + prms1.Z1.extensionStrategy = ExtensionStrategy.RECYCLE; + prmsStruct.L.setValues([ 1.89, 1.91, 1.99, 2, 1.7, 2.1, 2.18, 2.23, 2.6 ]); // 9 values + prmsStruct.L.extensionStrategy = ExtensionStrategy.RECYCLE; + // check that both parameters are varying + expect(prms1.Z1.valueMode).toBe(ParamValueMode.MINMAX); + expect(prmsStruct.L.valueMode).toBe(ParamValueMode.LISTE); + // check that calculation works + const res = nub1.CalcSerie(); + expect(res).toBeDefined(); + expect(res.resultElements.length).toBe(21); + }); + + it("test 4 : list < list", () => { + createSingleNubEnv(); + prms1.Z1.setValues([ 100, 101, 102 ]); // 3 values + prms1.Z1.extensionStrategy = ExtensionStrategy.RECYCLE; + prmsStruct.L.setValues([ 1.89, 1.91, 1.99, 2, 1.7, 2.1, 2.18, 2.23, 2.6 ]); // 9 values + prmsStruct.L.extensionStrategy = ExtensionStrategy.RECYCLE; + // check that both parameters are varying + expect(prms1.Z1.valueMode).toBe(ParamValueMode.LISTE); + expect(prmsStruct.L.valueMode).toBe(ParamValueMode.LISTE); + // check that calculation works + const res = nub1.CalcSerie(); + expect(res).toBeDefined(); + expect(res.resultElements.length).toBe(9); + }); + + it("test 5 : list = list", () => { + createSingleNubEnv(); + prms1.Z1.setValues([ 100, 100.3, 100.7, 101, 101.3, 101.7, 102, 102.3, 115 ]); // 9 values + prms1.Z1.extensionStrategy = ExtensionStrategy.RECYCLE; + prmsStruct.L.setValues([ 1.89, 1.91, 1.99, 2, 1.7, 2.1, 2.18, 2.23, 2.6 ]); // 9 values + prmsStruct.L.extensionStrategy = ExtensionStrategy.RECYCLE; + // check that both parameters are varying + expect(prms1.Z1.valueMode).toBe(ParamValueMode.LISTE); + expect(prmsStruct.L.valueMode).toBe(ParamValueMode.LISTE); + // check that calculation works + const res = nub1.CalcSerie(); + expect(res).toBeDefined(); + expect(res.resultElements.length).toBe(9); + }); + + it("test 6 : list = minmax", () => { + createSingleNubEnv(); + prms1.Z1.setValues(100, 102, 0.5); // 5 values + prms1.Z1.extensionStrategy = ExtensionStrategy.RECYCLE; + prmsStruct.L.setValues(1.89, 2.15, 0.06); // 5 values + prmsStruct.L.extensionStrategy = ExtensionStrategy.RECYCLE; + // check that both parameters are varying + expect(prms1.Z1.valueMode).toBe(ParamValueMode.MINMAX); + expect(prmsStruct.L.valueMode).toBe(ParamValueMode.MINMAX); + // check that calculation works + const res = nub1.CalcSerie(); + expect(res).toBeDefined(); + expect(res.resultElements.length).toBe(5); + }); + + it("test 7 : all parameters varying with random lengths", () => { + createSingleNubEnv(); + let longest = 0; + let sparedParam = null; + // set all parameters to LIST mode with random length (except the first that has to stay in CALC mode) + for (const p of nub1.parameterIterator) { + if (p.visible) { + if (! sparedParam) { + sparedParam = p; + } else { + const rl = randomList(p.singleValue); + longest = Math.max(longest, rl.length); + p.setValues(rl); + p.extensionStrategy = ExtensionStrategy.RECYCLE; + } + } + } + // check that all parameters are varying + for (const p of nub1.parameterIterator) { + if (p.visible && p !== sparedParam) { + expect(p.hasMultipleValues).toBe(true); + } + } + // check that calculation works and length of result is length of the longest values list + const res = nub1.CalcSerie(); + expect(res).toBeDefined(); + expect(res.resultElements.length).toBe(longest); + }); + }); + + describe("on different linked Nubs - ", () => { + // TODO + }); +}); diff --git a/src/nub.ts b/src/nub.ts index b0877e6d..22aa2f40 100644 --- a/src/nub.ts +++ b/src/nub.ts @@ -3,7 +3,7 @@ import { Dichotomie } from "./dichotomie"; import { acSection, IParamDefinitionIterator, Pab, ParamDefinition, ParamsEquation, ParamsEquationArrayIterator, Session, Structure } from "./index"; import { LinkedValue } from "./linked-value"; -import { ParamCalculability } from "./param/param-definition"; +import { ExtensionStrategy, ParamCalculability } from "./param/param-definition"; import { ParamValueMode } from "./param/param-value-mode"; import { ParamValues } from "./param/param-values"; import { Props } from "./props"; @@ -146,8 +146,22 @@ export abstract class Nub extends ComputeNode implements IObservable { } /** - * Resets the calculated parameter to the default one if it is in SINGLE - * mode, or else to the first calculable parameter other than requirer. + * Finds the previous calculated parameter and sets its mode to SINGLE + */ + public unsetCalculatedParam(except: ParamDefinition) { + for (const p of this.parameterIterator) { + if (p !== except) { + p.setValueMode(ParamValueMode.SINGLE, false); + } + } + } + + /** + * Tries to reset the calculated parameter, successively, to : + * - the default one if it is in SINGLE mode + * - the first SINGLE calculable parameter other than requirer + * - the first MINMAX/LISTE calculable parameter other than requirer + * * If no default calculated parameter is defined, does nothing. */ public resetDefaultCalculatedParam(requirer?: ParamDefinition) { @@ -155,15 +169,24 @@ export abstract class Nub extends ComputeNode implements IObservable { // if default calculated param is not eligible to CALC mode if ( requirer === this._defaultCalculatedParam - || this._defaultCalculatedParam.valueMode !== ParamValueMode.SINGLE + || ! [ + ParamValueMode.SINGLE, + ParamValueMode.MINMAX, + ParamValueMode.LISTE + ].includes(this._defaultCalculatedParam.valueMode + ) ) { // first SINGLE calculable parameter if any - const newCalculatedParam = this.findFirstSingleParameter(requirer); + let newCalculatedParam = this.findFirstSingleParameter(requirer); + if (! newCalculatedParam) { + // first MIMAX/LISTE calculable parameter if any + newCalculatedParam = this.findFirstVariableParameter(requirer); + } if (newCalculatedParam) { this.calculatedParam = newCalculatedParam; } else { // @TODO throws when a new linkable nub is added, and all parameters are already linked ! - throw Error("resetDefaultCalculatedParam : could not find any SINGLE parameter"); + throw Error("resetDefaultCalculatedParam : could not find any SINGLE/MINMAX/LISTE parameter"); } } else { // default one @@ -195,42 +218,23 @@ export abstract class Nub extends ComputeNode implements IObservable { } /** - * Resets all parameters to SINGLE mode, except sourceParam (the one that asked - * for the change) and parameters in LINK mode. - * If exceptMode is defined, parameters in this mode won't be affected either. - * - * Used to ensure that - * - exactly one parameter is in CALCUL mode - * - at most one parameter variates (MIMAX / LISTE mode) + * Returns the first visible calculable parameter other than "Pr", + * and other than otherThan, that is set to MINMAX or LISTE mode + * (there might be none) */ - public ensureParametersConsistency(sourceParam: ParamDefinition, exceptMode: ParamValueMode|ParamValueMode[] = []) { - if (! Array.isArray(exceptMode)) { - exceptMode = [ exceptMode ]; - } - // console.log(`nub ${this.uid} (${this.constructor.name})`); - // console.log(`(calculated parameter: ${this.calculatedParam.symbol})`); + public findFirstVariableParameter(otherThan?: ParamDefinition) { for (const p of this.parameterIterator) { if ( - p !== sourceParam - && ! exceptMode.includes(p.valueMode) - && ( - // don't touch linked parameters - p.valueMode !== ParamValueMode.LINK - || ( // unless they are linked to multiple values… - p.isReferenceDefined() - && p.hasMultipleValues - // …and we're not skipping multiple values - && ! exceptMode.includes(ParamValueMode.MINMAX) - && ! exceptMode.includes(ParamValueMode.LISTE) - ) - ) - && p.valueMode !== ParamValueMode.SINGLE + [ ParamCalculability.EQUATION, ParamCalculability.DICHO ].includes(p.calculability) + && p.symbol !== "Pr" + && p.visible + && p !== otherThan + && [ ParamValueMode.SINGLE, ParamValueMode.MINMAX, ParamValueMode.LISTE ].includes(p.valueMode) ) { - // console.log(">> resetting", p.symbol); - // "false" prevents infinite loop when managing CALC params - p.setValueMode(ParamValueMode.SINGLE, false); + return p; } } + return undefined; } /** @@ -304,42 +308,37 @@ export abstract class Nub extends ComputeNode implements IObservable { * @param sDonnee éventuel symbole / paire symbole-uid du paramètre à calculer */ public CalcSerie(rInit?: number, sDonnee?: any): Result { - // instance de ParamValues utilisée pour le paramètre varié (qui peut être un paramètre référencé (importé)) - let variatedParam: ParamDefinition; - let variatedValues: ParamValues; + // variated parameters caracteristics + const variated: Array<{ param: ParamDefinition, values: ParamValues, extendedValues: number[] }> = []; + // prepare calculation this.progress = 0; this.triggerChainCalculation(); - this.copySingleValuesToSandboxValues(); // check which values are variating, if any for (const p of this.parameterIterator) { - switch (p.valueMode) { - case ParamValueMode.SINGLE: - case ParamValueMode.CALCUL: - break; - - case ParamValueMode.LISTE: - case ParamValueMode.MINMAX: - variatedParam = this.setVariatedValues(p, variatedParam); - break; - - case ParamValueMode.LINK: - if ( - p.isReferenceDefined() - && p.referencedValue.hasMultipleValues() - ) { - variatedParam = this.setVariatedValues(p, variatedParam); - } - break; - - default: - // tslint:disable-next-line:max-line-length - throw new Error(`CalcSerie() : valeur de ParamValueMode ${ParamValueMode[p.valueMode]} non prise en charge`); + if ( + p.valueMode === ParamValueMode.LISTE + || p.valueMode === ParamValueMode.MINMAX + || ( + p.valueMode === ParamValueMode.LINK + && p.isReferenceDefined() + && p.referencedValue.hasMultipleValues() + ) + ) { + variated.push({ + param: p, + // extract variated values from variated Parameters + // (in LINK mode, proxies to target data) + values: p.paramValues, + // values list by default, will be extended later + extendedValues: p.paramValues.getInferredValuesList() + }); } } + // find calculated parameter let computedSymbol: any; if (sDonnee) { computedSymbol = sDonnee; @@ -354,31 +353,74 @@ export abstract class Nub extends ComputeNode implements IObservable { rInit = this.calculatedParam.v; } - if (variatedParam === undefined) { - this._result = this.Calc(computedSymbol, rInit); // résultat dans this._result - // update progress to 100% + if (variated.length === 0) { // no parameter is varying + this._result = this.doCalc(computedSymbol, rInit); this.progress = 100; - } else { - // extract variated values from variated Parameter - // (in LINK mode, proxies to target data) - variatedValues = variatedParam.paramValues; + } else { // at least one parameter is varying + // find longest series + let longest = 0; + for (let i = 0; i < variated.length; i++) { + if (variated[i].values.valuesIterator.count() > variated[longest].values.valuesIterator.count()) { + longest = i; + } + } + + // for all variating parameters other than the longest one, + // complete the series depending on the strategy (recycle / extend) + const size = variated[longest].values.valuesIterator.count(); + for (let i = 0; i < variated.length; i++) { + if (i !== longest) { + const vp = variated[i].param; + const vxv = variated[i].extendedValues; + // if sizes differ + if (vxv.length < size) { + const newValues = [...vxv]; // array copy + // extend values + switch (vp.extensionStrategy) { + case ExtensionStrategy.REPEAT_LAST: + while (newValues.length < size) { + newValues.push(vxv[vxv.length - 1]); + } + break; + + case ExtensionStrategy.RECYCLE: + let j = 0; + while (newValues.length < size) { + newValues.push(vxv[j]); + j ++; + if (j === vxv.length) { + j = 0; + } + } + break; + } + // store extended values + variated[i].extendedValues = newValues; + } + } + } // grant the remaining percentage of the progressbar to local calculation // (should be 50% if any chain calculation occurred, 100% otherwise) let progressStep; const remainingProgress = 100 - this.progress; - const nbValues = variatedValues.valuesIterator.count(); + const nbValues = variated[longest].extendedValues.length; progressStep = remainingProgress / nbValues; const res = new Result(undefined, this); - variatedValues.initValuesIterator(false); - while (variatedValues.hasNext) { - const currentIteratorValue = variatedValues.next(); - variatedParam.v = currentIteratorValue.value; // copy to local sandbox value + // iterate over longest series @TODO iterate over extendedValues directly ? + variated[longest].values.initValuesIterator(false); + let index = 0; + while (variated[longest].values.hasNext) { + variated[longest].values.next(); // move on + // get next value for all variating parameters + for (const v of variated) { + v.param.v = v.extendedValues[index]; // copy to local sandbox value + } - this.Calc(computedSymbol, rInit); // résultat dans this._result + this.doCalc(computedSymbol, rInit); // résultat dans this._result if (this._result.ok) { res.addResultElement(this._result.resultElement); res.addLog(this._result.log); @@ -387,6 +429,7 @@ export abstract class Nub extends ComputeNode implements IObservable { res.globalLog.addLog(this._result.globalLog); // update progress this.progress += progressStep; + index ++; } this._result = res; // round progress to 100% @@ -1098,6 +1141,10 @@ export abstract class Nub extends ComputeNode implements IObservable { this._observable.notifyObservers(data, sender); } + protected doCalc(computedSymbol?: any, rInit?: number) { + return this.Calc(computedSymbol, rInit); + } + /** * Returns values of parameters and the result of the calculation for the calculated parameter * @param sVarCalc Symbol of the calculated param @@ -1147,18 +1194,6 @@ export abstract class Nub extends ComputeNode implements IObservable { return Nub.prototype.Calc.call(this, sVarCalc, rInit); } - /** - * Sets the variated values and warns if there were already some - */ - protected setVariatedValues(newValues: ParamDefinition, oldValues: ParamDefinition): ParamDefinition { - if (oldValues === undefined) { - return newValues; - } else { - // tslint:disable-next-line:max-line-length - throw new Error(`CalcSerie() : Paramètres à varier redondant : ${newValues.name}`); - } - } - /** * Used by GUI to update results display */ diff --git a/src/param/param-definition.ts b/src/param/param-definition.ts index 263440ea..ee48deda 100644 --- a/src/param/param-definition.ts +++ b/src/param/param-definition.ts @@ -43,6 +43,19 @@ export enum ParamFamily { // SPEEDS // vitesses, seulement des résultats } +/** + * Strategy to apply when multiple parameters are variating, and + * values series have to be extended for sizes to match + */ +export enum ExtensionStrategy { + REPEAT_LAST, // repeat last value as many times as needed + RECYCLE // repeat the whole series from the beginning + // autres propositions : + // PAD_LEFT // pad with zeroes at the beginning ? + // PAD_RIGHT // pad with zeroes at the end ? + // INTERPOLATE // insert regular steps between first and last value +} + /** * Paramètre avec symbole, famille, domaine de définition, calculabilité, * pointant éventuellement vers un autre paramètre / résultat @@ -55,6 +68,9 @@ export class ParamDefinition implements INamedIterableValues, IObservable { /** sandbox value used during calculation */ public v: number; + /** extension strategy, when multiple parameters vary */ + public extensionStrategy: ExtensionStrategy; + /** mode de génération des valeurs : min/max, liste, ... */ private _valueMode: ParamValueMode; @@ -90,7 +106,7 @@ export class ParamDefinition implements INamedIterableValues, IObservable { this._observable = new Observable(); this._paramValues = new ParamValues(); - // set single value and copy it to currentValue + // set single value and copy it to sandbox value this._paramValues.singleValue = val; this.v = val; @@ -98,6 +114,7 @@ export class ParamDefinition implements INamedIterableValues, IObservable { this._family = family; this.visible = visible; this.valueMode = ParamValueMode.SINGLE; + this.extensionStrategy = ExtensionStrategy.REPEAT_LAST; if (d instanceof ParamDomain) { this._domain = d; @@ -203,8 +220,8 @@ export class ParamDefinition implements INamedIterableValues, IObservable { } /** - * Sets the value mode and asks the Nub to ensure all parameters requirements are met - * @see Nub::ensureParametersConsistency() + * Sets the value mode and asks the Nub to ensure there is only one parameter + * in CALC mode * * If propagateToCalculatedParam is true, any param that goes from CALC mode * to any other mode will trigger a reset of the default calculated param at @@ -221,88 +238,19 @@ export class ParamDefinition implements INamedIterableValues, IObservable { return; } - switch (oldMode) { - case ParamValueMode.SINGLE: // ancien état - switch (newMode) { - case ParamValueMode.MINMAX: // nouvel état - case ParamValueMode.LISTE: - this.ensureParametersConsistency(this, ParamValueMode.CALCUL); - break; - - case ParamValueMode.CALCUL: // nouvel état - this.ensureParametersConsistency(this, [ ParamValueMode.MINMAX, ParamValueMode.LISTE ]); - break; - - case ParamValueMode.LINK: // nouvel état - if ( - this.isReferenceDefined() - && this.hasMultipleValues - ) { - this.ensureParametersConsistency(this); // reset existing variable param - } - break; - } - break; - - case ParamValueMode.LISTE: // ancien état - case ParamValueMode.MINMAX: - switch (newMode) { - case ParamValueMode.CALCUL: // nouvel état - this.ensureParametersConsistency(this); - break; - - case ParamValueMode.LINK: // nouvel état - if ( - this.isReferenceDefined() - && this.hasMultipleValues - ) { - this.ensureParametersConsistency(this); // reset existing variable param - } - break; - } - break; - - case ParamValueMode.LINK: // ancien état - switch (newMode) { - case ParamValueMode.MINMAX: // nouvel état - case ParamValueMode.LISTE: - this.ensureParametersConsistency(this, ParamValueMode.CALCUL); - break; - - case ParamValueMode.CALCUL: // nouvel état - this.ensureParametersConsistency(this, [ ParamValueMode.MINMAX, ParamValueMode.LISTE ]); - break; - } - break; - - case ParamValueMode.CALCUL: // ancien état - switch (newMode) { - case ParamValueMode.SINGLE: // nouvel état - break; - - case ParamValueMode.MINMAX: // nouvel état - case ParamValueMode.LISTE: - this.ensureParametersConsistency(this); - break; - - case ParamValueMode.LINK: // nouvel état - if ( - this.isReferenceDefined() - && this.hasMultipleValues - ) { - this.ensureParametersConsistency(this); // reset existing variable param - } - break; + if (oldMode === ParamValueMode.CALCUL) { + if (propagateToCalculatedParam) { + // Set default calculated parameter, only if previous CALC param was + // manually set to something else than CALC + if (this.parentComputeNode && this.parentComputeNode instanceof Nub) { + this.parentComputeNode.resetDefaultCalculatedParam(this); } - - if (propagateToCalculatedParam) { - // Set default calculated parameter, only if previous CALC param was - // manually set to something else than CALC - if (this.parentComputeNode && this.parentComputeNode instanceof Nub) { - this.parentComputeNode.resetDefaultCalculatedParam(this); - } - } - + } + } else if (newMode === ParamValueMode.CALCUL) { + // set old CALC param to SINGLE mode + if (this.parentComputeNode && this.parentComputeNode instanceof Nub) { + this.parentComputeNode.unsetCalculatedParam(this); + } } // set new mode @@ -500,6 +448,15 @@ export class ParamDefinition implements INamedIterableValues, IObservable { return this.paramValues.singleValue; } + /** + * Returns values as a number list, for LISTE and MINMAX modes; + * in MINMAX mode, infers the list from min/max/step values + */ + public getInferredValuesList() { + this.checkValueMode([ParamValueMode.LISTE, ParamValueMode.MINMAX, ParamValueMode.LINK]); + return this.paramValues.getInferredValuesList(); + } + /** * Magic method to define current values into Paramvalues set; defines the mode * accordingly by detecting values nature @@ -1076,17 +1033,6 @@ export class ParamDefinition implements INamedIterableValues, IObservable { return res; } - /** - * Asks parent Nub if any to ensure parameters consistency - */ - protected ensureParametersConsistency(sourceParam: ParamDefinition, - exceptMode: ParamValueMode|ParamValueMode[] = [] - ) { - if (this.parentComputeNode && this.parentComputeNode instanceof Nub) { - this.parentComputeNode.ensureParametersConsistency(sourceParam, exceptMode); - } - } - /** * notification envoyée après la modification de la valeur du paramètre */ diff --git a/src/param/param-values.ts b/src/param/param-values.ts index 9d850c7d..d4919b3f 100644 --- a/src/param/param-values.ts +++ b/src/param/param-values.ts @@ -104,6 +104,26 @@ export class ParamValues implements IterableValues { } } + /** + * Returns values as a number list, for LISTE and MINMAX modes; + * in MINMAX mode, infers the list from min/max/step values + */ + public getInferredValuesList() { + if (this.valueMode === ParamValueMode.LISTE) { + return this.valueList; + } + if (this.valueMode === ParamValueMode.MINMAX) { + const values = [ this.min ]; + let latestVal = this.min + this.step; + while (latestVal <= this.max) { + values.push(latestVal); + latestVal += this.step; + } + return values; + } + throw new Error("ParamValues.getInferredValuesList() : incorrect value mode" + ParamValueMode[this.valueMode]); + } + // -- iterator-related methods /** diff --git a/src/section/section_parametree.ts b/src/section/section_parametree.ts index d09e8708..a943d060 100644 --- a/src/section/section_parametree.ts +++ b/src/section/section_parametree.ts @@ -69,67 +69,6 @@ export class SectionParametree extends SectionNub { } } - public CalcSerie(rInit?: number, sDonnee?: string): Result { - - this.triggerChainCalculation(); - - let variatedParam: ParamDefinition; - let variatedValues: ParamValues; - - for (const p of this.parameterIterator) { - // checks which values are variating, if any - switch (p.valueMode) { - case ParamValueMode.SINGLE: - case ParamValueMode.CALCUL: - break; - - case ParamValueMode.LISTE: - case ParamValueMode.MINMAX: - variatedParam = this.setVariatedValues(p, variatedParam); - break; - - case ParamValueMode.LINK: - if ( - p.isReferenceDefined() - && p.referencedValue.hasMultipleValues() - ) { - variatedParam = this.setVariatedValues(p, variatedParam); - } - break; - - default: - // tslint:disable-next-line:max-line-length - throw new Error(`CalcSerie() : valeur de ParamValueMode ${ParamValueMode[p.valueMode]} non prise en charge`); - } - } - - if (variatedParam === undefined) { - this.Calc(); // résultat dans this._result - } else { - // extract variated values from variated Parameter - // (in LINK mode, proxies to target data) - variatedValues = variatedParam.paramValues; - - const res = new Result(undefined, this); - variatedValues.initValuesIterator(false); - while (variatedValues.hasNext) { - variatedValues.next(); - - this.Calc(); // résultat dans this._result - if (this._result.ok) { - res.addResultElement(this._result.resultElement); - res.addLog(this._result.log); - } - res.globalLog.addLog(this._result.globalLog); - } - this._result = res; - - } - - this.notifyResultUpdated(); - return this._result; - } - /** * Aucune variable à calculer plus que les autres, on stocke toutes les * valeurs des variables à calcul dans les résultats complémentaires @@ -198,6 +137,10 @@ export class SectionParametree extends SectionNub { return this._result; } + protected doCalc(computedSymbol?: any, rInit?: number) { + return this.Calc(); + } + // tslint:disable-next-line:no-empty protected setParametersCalculability(): void {} diff --git a/src/structure/structure.ts b/src/structure/structure.ts index 2547d78f..68eb9930 100644 --- a/src/structure/structure.ts +++ b/src/structure/structure.ts @@ -106,9 +106,9 @@ export abstract class Structure extends Nub { * Forwards to parent, that has vsibility over * all the parameters, including the Structure ones */ - public ensureParametersConsistency(sourceParam: ParamDefinition, exceptMode: ParamValueMode|ParamValueMode[] = []) { + public unsetCalculatedParam(except: ParamDefinition) { if (this.parent) { - this.parent.ensureParametersConsistency(sourceParam, exceptMode); + this.parent.unsetCalculatedParam(except); } } -- GitLab