Make conversions use formulas
This commit is contained in:
parent
cc1a2998e0
commit
3e0aafcf02
2 changed files with 15 additions and 282 deletions
|
@ -1,11 +1,10 @@
|
||||||
import type { OptionsFunc, Replace } from "features/feature";
|
import type { OptionsFunc, Replace } from "features/feature";
|
||||||
import { setDefault } from "features/feature";
|
import { setDefault } from "features/feature";
|
||||||
import type { Resource } from "features/resources/resource";
|
import type { Resource } from "features/resources/resource";
|
||||||
|
import { InvertibleFormula } from "game/formulas/types";
|
||||||
import type { BaseLayer } from "game/layers";
|
import type { BaseLayer } from "game/layers";
|
||||||
import type { Modifier } from "game/modifiers";
|
|
||||||
import type { DecimalSource } from "util/bignum";
|
import type { DecimalSource } from "util/bignum";
|
||||||
import Decimal from "util/bignum";
|
import Decimal from "util/bignum";
|
||||||
import type { WithRequired } from "util/common";
|
|
||||||
import type { Computable, GetComputableTypeWithDefault, ProcessedComputable } from "util/computed";
|
import type { Computable, GetComputableTypeWithDefault, ProcessedComputable } from "util/computed";
|
||||||
import { convertComputable, processComputable } from "util/computed";
|
import { convertComputable, processComputable } from "util/computed";
|
||||||
import { createLazyProxy } from "util/proxies";
|
import { createLazyProxy } from "util/proxies";
|
||||||
|
@ -15,9 +14,10 @@ import { computed, unref } from "vue";
|
||||||
/** An object that configures a {@link Conversion}. */
|
/** An object that configures a {@link Conversion}. */
|
||||||
export interface ConversionOptions {
|
export interface ConversionOptions {
|
||||||
/**
|
/**
|
||||||
* The scaling function that is used to determine the rate of conversion from one {@link features/resources/resource.Resource} to the other.
|
* The formula used to determine how much {@link gainResource} should be earned by this converting.
|
||||||
|
* When evaluating, the variable will always be overidden to the amount of {@link baseResource}.
|
||||||
*/
|
*/
|
||||||
scaling: ScalingFunction;
|
formula: InvertibleFormula;
|
||||||
/**
|
/**
|
||||||
* How much of the output resource the conversion can currently convert for.
|
* How much of the output resource the conversion can currently convert for.
|
||||||
* Typically this will be set for you in a conversion constructor.
|
* Typically this will be set for you in a conversion constructor.
|
||||||
|
@ -53,10 +53,6 @@ export interface ConversionOptions {
|
||||||
* Defaults to true.
|
* Defaults to true.
|
||||||
*/
|
*/
|
||||||
buyMax?: Computable<boolean>;
|
buyMax?: Computable<boolean>;
|
||||||
/**
|
|
||||||
* Whether or not to round up the cost to generate a given amount of the output resource.
|
|
||||||
*/
|
|
||||||
roundUpCost?: Computable<boolean>;
|
|
||||||
/**
|
/**
|
||||||
* The function that performs the actual conversion from {@link baseResource} to {@link gainResource}.
|
* The function that performs the actual conversion from {@link baseResource} to {@link gainResource}.
|
||||||
* Typically this will be set for you in a conversion constructor.
|
* Typically this will be set for you in a conversion constructor.
|
||||||
|
@ -73,20 +69,6 @@ export interface ConversionOptions {
|
||||||
* This will not be called whenever using currentGain without calling convert (e.g. passive generation)
|
* This will not be called whenever using currentGain without calling convert (e.g. passive generation)
|
||||||
*/
|
*/
|
||||||
onConvert?: (amountGained: DecimalSource) => void;
|
onConvert?: (amountGained: DecimalSource) => void;
|
||||||
/**
|
|
||||||
* An additional modifier that will be applied to the gain amounts.
|
|
||||||
* Must be reversible in order to correctly calculate {@link nextAt}.
|
|
||||||
* @see {@link game/modifiers.createSequentialModifier} if you want to apply multiple modifiers.
|
|
||||||
*/
|
|
||||||
gainModifier?: WithRequired<Modifier, "revert">;
|
|
||||||
/**
|
|
||||||
* A modifier that will be applied to the cost amounts.
|
|
||||||
* That is to say, this modifier will be applied to the amount of baseResource before going into the scaling function.
|
|
||||||
* A cost modifier of x0.5 would give gain amounts equal to the player having half the baseResource they actually have.
|
|
||||||
* Must be reversible in order to correctly calculate {@link nextAt}.
|
|
||||||
* @see {@link game/modifiers.createSequentialModifier} if you want to apply multiple modifiers.
|
|
||||||
*/
|
|
||||||
costModifier?: WithRequired<Modifier, "revert">;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@ -109,7 +91,6 @@ export type Conversion<T extends ConversionOptions> = Replace<
|
||||||
nextAt: GetComputableTypeWithDefault<T["nextAt"], Ref<DecimalSource>>;
|
nextAt: GetComputableTypeWithDefault<T["nextAt"], Ref<DecimalSource>>;
|
||||||
buyMax: GetComputableTypeWithDefault<T["buyMax"], true>;
|
buyMax: GetComputableTypeWithDefault<T["buyMax"], true>;
|
||||||
spend: undefined extends T["spend"] ? (amountGained: DecimalSource) => void : T["spend"];
|
spend: undefined extends T["spend"] ? (amountGained: DecimalSource) => void : T["spend"];
|
||||||
roundUpCost: GetComputableTypeWithDefault<T["roundUpCost"], true>;
|
|
||||||
}
|
}
|
||||||
>;
|
>;
|
||||||
|
|
||||||
|
@ -123,7 +104,6 @@ export type GenericConversion = Replace<
|
||||||
nextAt: ProcessedComputable<DecimalSource>;
|
nextAt: ProcessedComputable<DecimalSource>;
|
||||||
buyMax: ProcessedComputable<boolean>;
|
buyMax: ProcessedComputable<boolean>;
|
||||||
spend: (amountGained: DecimalSource) => void;
|
spend: (amountGained: DecimalSource) => void;
|
||||||
roundUpCost: ProcessedComputable<boolean>;
|
|
||||||
}
|
}
|
||||||
>;
|
>;
|
||||||
|
|
||||||
|
@ -142,11 +122,7 @@ export function createConversion<T extends ConversionOptions>(
|
||||||
|
|
||||||
if (conversion.currentGain == null) {
|
if (conversion.currentGain == null) {
|
||||||
conversion.currentGain = computed(() => {
|
conversion.currentGain = computed(() => {
|
||||||
let gain = conversion.gainModifier
|
let gain = conversion.formula.evaluate(conversion.baseResource.value);
|
||||||
? conversion.gainModifier.apply(
|
|
||||||
conversion.scaling.currentGain(conversion as GenericConversion)
|
|
||||||
)
|
|
||||||
: conversion.scaling.currentGain(conversion as GenericConversion);
|
|
||||||
gain = Decimal.floor(gain).max(0);
|
gain = Decimal.floor(gain).max(0);
|
||||||
|
|
||||||
if (unref(conversion.buyMax) === false) {
|
if (unref(conversion.buyMax) === false) {
|
||||||
|
@ -160,17 +136,16 @@ export function createConversion<T extends ConversionOptions>(
|
||||||
}
|
}
|
||||||
if (conversion.currentAt == null) {
|
if (conversion.currentAt == null) {
|
||||||
conversion.currentAt = computed(() => {
|
conversion.currentAt = computed(() => {
|
||||||
let current = conversion.scaling.currentAt(conversion as GenericConversion);
|
return conversion.formula.invert(
|
||||||
if (unref((conversion as GenericConversion).roundUpCost))
|
Decimal.floor(unref((conversion as GenericConversion).currentGain))
|
||||||
current = Decimal.ceil(current);
|
);
|
||||||
return current;
|
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
if (conversion.nextAt == null) {
|
if (conversion.nextAt == null) {
|
||||||
conversion.nextAt = computed(() => {
|
conversion.nextAt = computed(() => {
|
||||||
let next = conversion.scaling.nextAt(conversion as GenericConversion);
|
return conversion.formula.invert(
|
||||||
if (unref((conversion as GenericConversion).roundUpCost)) next = Decimal.ceil(next);
|
Decimal.floor(unref((conversion as GenericConversion).currentGain)).add(1)
|
||||||
return next;
|
);
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -198,177 +173,11 @@ export function createConversion<T extends ConversionOptions>(
|
||||||
processComputable(conversion as T, "nextAt");
|
processComputable(conversion as T, "nextAt");
|
||||||
processComputable(conversion as T, "buyMax");
|
processComputable(conversion as T, "buyMax");
|
||||||
setDefault(conversion, "buyMax", true);
|
setDefault(conversion, "buyMax", true);
|
||||||
processComputable(conversion as T, "roundUpCost");
|
|
||||||
setDefault(conversion, "roundUpCost", true);
|
|
||||||
|
|
||||||
return conversion as unknown as Conversion<T>;
|
return conversion as unknown as Conversion<T>;
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
|
||||||
* A collection of functions that allow a conversion to scale the amount of resources gained based on the input resource.
|
|
||||||
* This typically shouldn't be created directly. Instead use one of the scaling function constructors.
|
|
||||||
* @see {@link createLinearScaling}.
|
|
||||||
* @see {@link createPolynomialScaling}.
|
|
||||||
*/
|
|
||||||
export interface ScalingFunction {
|
|
||||||
/**
|
|
||||||
* Calculates the amount of the output resource a conversion should be able to currently produce.
|
|
||||||
* This should be based off of `conversion.baseResource.value`.
|
|
||||||
* The conversion is responsible for applying the gainModifier, so this function should be un-modified.
|
|
||||||
* It does not need to be clamped or rounded.
|
|
||||||
*/
|
|
||||||
currentGain: (conversion: GenericConversion) => DecimalSource;
|
|
||||||
/**
|
|
||||||
* Calculates the amount of the input resource that is required for the current value of `conversion.currentGain`.
|
|
||||||
* Note that `conversion.currentGain` has been modified by `conversion.gainModifier`, so you will need to revert that as appropriate.
|
|
||||||
* The conversion is responsible for rounding up the amount as appropriate.
|
|
||||||
* The returned value should not be below 0.
|
|
||||||
*/
|
|
||||||
currentAt: (conversion: GenericConversion) => DecimalSource;
|
|
||||||
/**
|
|
||||||
* Calculates the amount of the input resource that would be required for the current value of `conversion.currentGain` to increase.
|
|
||||||
* Note that `conversion.currentGain` has been modified by `conversion.gainModifier`, so you will need to revert that as appropriate.
|
|
||||||
* The conversion is responsible for rounding up the amount as appropriate.
|
|
||||||
* The returned value should not be below 0.
|
|
||||||
*/
|
|
||||||
nextAt: (conversion: GenericConversion) => DecimalSource;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Creates a scaling function based off the formula `(baseResource - base) * coefficient`.
|
|
||||||
* If the baseResource value is less than base then the currentGain will be 0.
|
|
||||||
* @param base The base variable in the scaling formula.
|
|
||||||
* @param coefficient The coefficient variable in the scaling formula.
|
|
||||||
* @example
|
|
||||||
* A scaling function created via `createLinearScaling(10, 0.5)` would produce the following values:
|
|
||||||
* | Base Resource | Current Gain |
|
|
||||||
* | ------------- | ------------ |
|
|
||||||
* | 10 | 1 |
|
|
||||||
* | 12 | 2 |
|
|
||||||
* | 20 | 6 |
|
|
||||||
*/
|
|
||||||
export function createLinearScaling(
|
|
||||||
base: Computable<DecimalSource>,
|
|
||||||
coefficient: Computable<DecimalSource>
|
|
||||||
): ScalingFunction {
|
|
||||||
const processedBase = convertComputable(base);
|
|
||||||
const processedCoefficient = convertComputable(coefficient);
|
|
||||||
return {
|
|
||||||
currentGain(conversion) {
|
|
||||||
let baseAmount: DecimalSource = unref(conversion.baseResource.value);
|
|
||||||
if (conversion.costModifier) {
|
|
||||||
baseAmount = conversion.costModifier.apply(baseAmount);
|
|
||||||
}
|
|
||||||
if (Decimal.lt(baseAmount, unref(processedBase))) {
|
|
||||||
return 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
return Decimal.sub(baseAmount, unref(processedBase))
|
|
||||||
.sub(1)
|
|
||||||
.times(unref(processedCoefficient))
|
|
||||||
.add(1);
|
|
||||||
},
|
|
||||||
currentAt(conversion) {
|
|
||||||
let current: DecimalSource = unref(conversion.currentGain);
|
|
||||||
if (conversion.gainModifier) {
|
|
||||||
current = conversion.gainModifier.revert(current);
|
|
||||||
}
|
|
||||||
current = Decimal.max(0, current)
|
|
||||||
.sub(1)
|
|
||||||
.div(unref(processedCoefficient))
|
|
||||||
.add(unref(processedBase));
|
|
||||||
if (conversion.costModifier) {
|
|
||||||
current = conversion.costModifier.revert(current);
|
|
||||||
}
|
|
||||||
return current;
|
|
||||||
},
|
|
||||||
nextAt(conversion) {
|
|
||||||
let next: DecimalSource = Decimal.add(unref(conversion.currentGain), 1).floor();
|
|
||||||
if (conversion.gainModifier) {
|
|
||||||
next = conversion.gainModifier.revert(next);
|
|
||||||
}
|
|
||||||
next = Decimal.max(0, next)
|
|
||||||
.sub(1)
|
|
||||||
.div(unref(processedCoefficient))
|
|
||||||
.add(unref(processedBase))
|
|
||||||
.max(unref(processedBase));
|
|
||||||
if (conversion.costModifier) {
|
|
||||||
next = conversion.costModifier.revert(next);
|
|
||||||
}
|
|
||||||
return next;
|
|
||||||
}
|
|
||||||
};
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Creates a scaling function based off the formula `(baseResource / base) ^ exponent`.
|
|
||||||
* If the baseResource value is less than base then the currentGain will be 0.
|
|
||||||
* @param base The base variable in the scaling formula.
|
|
||||||
* @param exponent The exponent variable in the scaling formula.
|
|
||||||
* @example
|
|
||||||
* A scaling function created via `createPolynomialScaling(10, 0.5)` would produce the following values:
|
|
||||||
* | Base Resource | Current Gain |
|
|
||||||
* | ------------- | ------------ |
|
|
||||||
* | 10 | 1 |
|
|
||||||
* | 40 | 2 |
|
|
||||||
* | 250 | 5 |
|
|
||||||
*/
|
|
||||||
export function createPolynomialScaling(
|
|
||||||
base: Computable<DecimalSource>,
|
|
||||||
exponent: Computable<DecimalSource>
|
|
||||||
): ScalingFunction {
|
|
||||||
const processedBase = convertComputable(base);
|
|
||||||
const processedExponent = convertComputable(exponent);
|
|
||||||
return {
|
|
||||||
currentGain(conversion) {
|
|
||||||
let baseAmount: DecimalSource = unref(conversion.baseResource.value);
|
|
||||||
if (conversion.costModifier) {
|
|
||||||
baseAmount = conversion.costModifier.apply(baseAmount);
|
|
||||||
}
|
|
||||||
if (Decimal.lt(baseAmount, unref(processedBase))) {
|
|
||||||
return 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
const gain = Decimal.div(baseAmount, unref(processedBase)).pow(
|
|
||||||
unref(processedExponent)
|
|
||||||
);
|
|
||||||
|
|
||||||
if (gain.isNan()) {
|
|
||||||
return new Decimal(0);
|
|
||||||
}
|
|
||||||
return gain;
|
|
||||||
},
|
|
||||||
currentAt(conversion) {
|
|
||||||
let current: DecimalSource = unref(conversion.currentGain);
|
|
||||||
if (conversion.gainModifier) {
|
|
||||||
current = conversion.gainModifier.revert(current);
|
|
||||||
}
|
|
||||||
current = Decimal.max(0, current)
|
|
||||||
.root(unref(processedExponent))
|
|
||||||
.times(unref(processedBase));
|
|
||||||
if (conversion.costModifier) {
|
|
||||||
current = conversion.costModifier.revert(current);
|
|
||||||
}
|
|
||||||
return current;
|
|
||||||
},
|
|
||||||
nextAt(conversion) {
|
|
||||||
let next: DecimalSource = Decimal.add(unref(conversion.currentGain), 1).floor();
|
|
||||||
if (conversion.gainModifier) {
|
|
||||||
next = conversion.gainModifier.revert(next);
|
|
||||||
}
|
|
||||||
next = Decimal.max(0, next)
|
|
||||||
.root(unref(processedExponent))
|
|
||||||
.times(unref(processedBase))
|
|
||||||
.max(unref(processedBase));
|
|
||||||
if (conversion.costModifier) {
|
|
||||||
next = conversion.costModifier.revert(next);
|
|
||||||
}
|
|
||||||
return next;
|
|
||||||
}
|
|
||||||
};
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Creates a conversion that simply adds to the gainResource amount upon converting.
|
* Creates a conversion that simply adds to the gainResource amount upon converting.
|
||||||
* This is similar to the behavior of "normal" layers in The Modding Tree.
|
* This is similar to the behavior of "normal" layers in The Modding Tree.
|
||||||
|
@ -396,13 +205,8 @@ export function createIndependentConversion<S extends ConversionOptions>(
|
||||||
|
|
||||||
if (conversion.currentGain == null) {
|
if (conversion.currentGain == null) {
|
||||||
conversion.currentGain = computed(() => {
|
conversion.currentGain = computed(() => {
|
||||||
let gain = conversion.gainModifier
|
let gain = conversion.formula.evaluate(conversion.baseResource.value);
|
||||||
? conversion.gainModifier.apply(
|
|
||||||
conversion.scaling.currentGain(conversion as GenericConversion)
|
|
||||||
)
|
|
||||||
: conversion.scaling.currentGain(conversion as GenericConversion);
|
|
||||||
gain = Decimal.floor(gain).max(conversion.gainResource.value);
|
gain = Decimal.floor(gain).max(conversion.gainResource.value);
|
||||||
|
|
||||||
if (unref(conversion.buyMax) === false) {
|
if (unref(conversion.buyMax) === false) {
|
||||||
gain = gain.min(Decimal.add(conversion.gainResource.value, 1));
|
gain = gain.min(Decimal.add(conversion.gainResource.value, 1));
|
||||||
}
|
}
|
||||||
|
@ -412,7 +216,7 @@ export function createIndependentConversion<S extends ConversionOptions>(
|
||||||
if (conversion.actualGain == null) {
|
if (conversion.actualGain == null) {
|
||||||
conversion.actualGain = computed(() => {
|
conversion.actualGain = computed(() => {
|
||||||
let gain = Decimal.sub(
|
let gain = Decimal.sub(
|
||||||
Decimal.floor(conversion.scaling.currentGain(conversion as GenericConversion)),
|
conversion.formula.evaluate(conversion.baseResource.value),
|
||||||
conversion.gainResource.value
|
conversion.gainResource.value
|
||||||
).max(0);
|
).max(0);
|
||||||
|
|
||||||
|
@ -424,11 +228,7 @@ export function createIndependentConversion<S extends ConversionOptions>(
|
||||||
}
|
}
|
||||||
setDefault(conversion, "convert", function () {
|
setDefault(conversion, "convert", function () {
|
||||||
const amountGained = unref((conversion as GenericConversion).actualGain);
|
const amountGained = unref((conversion as GenericConversion).actualGain);
|
||||||
conversion.gainResource.value = conversion.gainModifier
|
conversion.gainResource.value = unref((conversion as GenericConversion).currentGain);
|
||||||
? conversion.gainModifier.apply(
|
|
||||||
unref((conversion as GenericConversion).currentGain)
|
|
||||||
)
|
|
||||||
: unref((conversion as GenericConversion).currentGain);
|
|
||||||
(conversion as GenericConversion).spend(amountGained);
|
(conversion as GenericConversion).spend(amountGained);
|
||||||
conversion.onConvert?.(amountGained);
|
conversion.onConvert?.(amountGained);
|
||||||
});
|
});
|
||||||
|
@ -466,71 +266,3 @@ export function setupPassiveGeneration(
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
|
||||||
* Given a value, this function finds the amount above a certain value and raises it to a power.
|
|
||||||
* If the power is <1, this will effectively make the value scale slower after the cap.
|
|
||||||
* @param value The raw value.
|
|
||||||
* @param cap The value after which the softcap should be applied.
|
|
||||||
* @param power The power to raise value above the cap to.
|
|
||||||
* @example
|
|
||||||
* A softcap added via `addSoftcap(scaling, 100, 0.5)` would produce the following values:
|
|
||||||
* | Raw Value | Softcapped Value |
|
|
||||||
* | --------- | ---------------- |
|
|
||||||
* | 1 | 1 |
|
|
||||||
* | 100 | 100 |
|
|
||||||
* | 125 | 105 |
|
|
||||||
* | 200 | 110 |
|
|
||||||
*/
|
|
||||||
export function softcap(
|
|
||||||
value: DecimalSource,
|
|
||||||
cap: DecimalSource,
|
|
||||||
power: DecimalSource = 0.5
|
|
||||||
): DecimalSource {
|
|
||||||
if (Decimal.lte(value, cap)) {
|
|
||||||
return value;
|
|
||||||
} else {
|
|
||||||
return Decimal.pow(value, power).times(Decimal.pow(cap, Decimal.sub(1, power)));
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Creates a scaling function based off an existing scaling function, with a softcap applied to it.
|
|
||||||
* The softcap will take any value above a certain value and raise it to a power.
|
|
||||||
* If the power is <1, this will effectively make the value scale slower after the cap.
|
|
||||||
* @param scaling The raw scaling function.
|
|
||||||
* @param cap The value after which the softcap should be applied.
|
|
||||||
* @param power The power to raise value about the cap to.
|
|
||||||
* @see {@link softcap}.
|
|
||||||
*/
|
|
||||||
export function addSoftcap(
|
|
||||||
scaling: ScalingFunction,
|
|
||||||
cap: ProcessedComputable<DecimalSource>,
|
|
||||||
power: ProcessedComputable<DecimalSource> = 0.5
|
|
||||||
): ScalingFunction {
|
|
||||||
return {
|
|
||||||
...scaling,
|
|
||||||
currentAt: conversion =>
|
|
||||||
softcap(scaling.currentAt(conversion), unref(cap), Decimal.recip(unref(power))),
|
|
||||||
nextAt: conversion =>
|
|
||||||
softcap(scaling.nextAt(conversion), unref(cap), Decimal.recip(unref(power))),
|
|
||||||
currentGain: conversion =>
|
|
||||||
softcap(scaling.currentGain(conversion), unref(cap), unref(power))
|
|
||||||
};
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Creates a scaling function off an existing function, with a hardcap applied to it.
|
|
||||||
* The harcap will ensure that the currentGain will stop at a given cap.
|
|
||||||
* @param scaling The raw scaling function.
|
|
||||||
* @param cap The maximum value the scaling function can output.
|
|
||||||
*/
|
|
||||||
export function addHardcap(
|
|
||||||
scaling: ScalingFunction,
|
|
||||||
cap: ProcessedComputable<DecimalSource>
|
|
||||||
): ScalingFunction {
|
|
||||||
return {
|
|
||||||
...scaling,
|
|
||||||
currentGain: conversion => Decimal.min(scaling.currentGain(conversion), unref(cap))
|
|
||||||
};
|
|
||||||
}
|
|
||||||
|
|
|
@ -315,6 +315,7 @@ export default class Formula<T extends [FormulaSource] | FormulaSource[]> {
|
||||||
return new Formula({ variable: value }) as InvertibleFormula;
|
return new Formula({ variable: value }) as InvertibleFormula;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// TODO add integration support to step-wise functions
|
||||||
/**
|
/**
|
||||||
* Creates a step-wise formula. After {@ref start} the formula will have an additional modifier.
|
* Creates a step-wise formula. After {@ref start} the formula will have an additional modifier.
|
||||||
* This function assumes the incoming {@ref value} will be continuous and monotonically increasing.
|
* This function assumes the incoming {@ref value} will be continuous and monotonically increasing.
|
||||||
|
|
Loading…
Reference in a new issue