Scalar syntax - KSPSnark/IndicatorLights GitHub Wiki
What's a "scalar"?
In IndicatorLights, a "scalar" is a type of object that exposes a single floating-point numeric value, which may change dynamically at run time. This can be used to control the behavior of color sources on the ship.
IndicatorLights provides a variety of scalar syntax options to allow achieving various complex effects. This page describes the syntax of scalar parameters and provides some examples.
Please see the IndicatorLights syntax overview for a high-level summary of the syntax language, a discussion of the various other types of syntax available besides scalars, and debugging tips.
Note that scalars, like all IndicatorLights syntax, can be nested to form complex expressions. For example:
average(scale(input1, 3), -offset(input2, 1), range(input3, 0, 1))
Where's the code?
See Scalars.cs for the source code in IndicatorLights that handles the parsing of scalars.
Types of scalar
The following types of scalar syntax are supported:
- Static values (e.g. "3")
- Reference (e.g. an identifier for some scalar module on the part)
- Parameterized (e.g. "take the value of <some scalar module> and multiply by 2")
The following sections describe the syntax for each of these types of scalar.
-
operator
Special modifier: The You can use a minus sign as a prefix on any legitimate scalar syntax. This is a convenient syntactic shorthand meaning "take the negative value of".
Thus, for example, if "foo" evaluates as a legitimate scalar reference, then "-foo" would be legal syntax meaning "the negative of whatever foo's scalar output value is."
Static values
Statics (such as "3"), like scalars, are one of the four data types in IndicatorLights. Since statics are a special case of scalars, this means that anywhere you can use a scalar expression, it's also possible to use a static expression.
Please see the static syntax page for details and examples.
Reference
This is a simple but powerful feature that enables using the output of scalar modules to control a color.
A "scalar module" is one that supports the IScalar
interface. An example of this would be ModuleResourceLevelIndicator, whose scalar value is a number in the range [0,1] indicating the fraction of resource available (0 = empty, 1 = full). Please see the controller reference for documentation on individual controller types, including which ones support IScalar
.
The reference syntax is very simple.
- By controller name: The ModuleEmissiveController base class (and therefore all its subclasses) has a
controllerName
property. (See Setting up a ModuleEmissiveController for details.) You can use that name as a scalar reference, if the module is one of the types supportingIScalar
. If a controller on the part has set itscontrollerName
to "foo", for example, then you can simply use the string "foo" as a scalar value, which means "go find the controller whose name is 'foo' and use its current output scalar value". - By class: If a controller doesn't set its
controllerName
property (i.e. the value is empty or null), then you can refer to it by its class name, e.g "ModuleResourceLevelIndicator" or "ModuleReactionWheelIndicator" or whatever. - By field & module name: You can specify any arbitrary numeric field of any PartModule with syntax like this:
fieldname@modulename
.- Example:
driveOutput@ModuleWheelMotor
- This syntax variant is useful in that it allows you to use any numeric field of any PartModule (not just IndicatorLights modules): e.g. stock modules, or even modules from other mods.
- However, it does have some important limitations to be aware of. It only works on public fields that are annotated as "KSPField". Also, for non-IndicatorLights module, there's no way to distinguish individual modules by name-- only by module type. So if you target
someField@ModuleFoo
, it will just use the first ModuleFoo that it finds; there's no way to pick "which one" if there happens to be more than one ModuleFoo on the part. - See example file ArbitraryFieldInputs.cfg for how to use this syntax.
- Example:
this
keyword: Any controller that implementsIScalar
can use the reserved wordthis
in any scalar context within its ColorSource syntax. The value ofthis
, as a scalar, will be evaluated as "the current scalar value of this controller".
Note that you can only use the class name for a controller if it doesn't set a controllerName
. Essentially, what using the class name does is, "find the first module on the part of this type that doesn't have a controllerName
set, and use the output value of that."
Parameterized
This is the most complex (but most flexible and powerful) type of scalar syntax. It allows specifying some complex behaviors by using parameters.
A parameterized scalar has the form:
functionName(param1,param2,param3,...)
The number and type of parameters will depend on what the function is.
The following functions are currently supported (more may be added in the future):
- Linear transforms:
scale
,offset
- Range constraints:
range
,gt
,lt
- Aggregation of multiple inputs:
maximum
,minimum
,average
- Data type conversion:
scalar
scale
This does a linear transform of a scalar: multiplying it by a constant, and optionally adding another constant.
scale(input, multiplier)
scale(input, multiplier, offset)
Parameters:
- input: A scalar value to transform.
- multiplier: A static value by which to multiply the input.
- offset: A static value to add to the input, after multiplying. Defaults to zero in the 2-parameter form of this this scalar expression.
Example: scale(foo, 2, 1)
means "take the value of foo, multiply by 2, and then add 1".
offset
This does a linear transform of a scalar by adding a constant to it.
offset(input, amount)
Parameters:
- input: A scalar value to transform.
- amount: A static value to add to the input.
Example: offset(foo, -1)
means "take the value of foo and subtract 1".
range
This constrains an input scalar value to fall between the specified maximum and minimum values.
range(input, minimum, maximum)
Parameters:
- input: A scalar value to transform.
- minimum: A static value representing the lowest allowable output. If the input is lower than the minimum, use the minimum.
- maximum: A static value representing the highest allowable output. If the input is higher than the maximum, use the maximum.
Example: range(foo, 0, 1)
means "take the value of foo, but if it's lower than 0, use 0; and if it's higher than 1, use 1".
gt
"gt" for "greater than". This constrains an input scalar value to be at least equal to the specified amount.
gt(input, minimum)
Parameters:
- input: A scalar value to transform.
- minimum: A static value representing the lowest allowable output. If the input is lower than the minimum, use the minimum.
Example: gt(foo, 0)
means "take the value of foo, or 0, whichever is higher".
lt
"lt" for "less than". This constrains an input scalar value to be at most equal to the specified amount.
lt(input, maximum)
Parameters:
- input: A scalar value to transform.
- maximum: A static value representing the highest allowable output. If the input is higher than the maximum, use the maximum.
Example: lt(foo, 1)
means "take the value of foo, or 1, whichever is lower".
maximum
Combines two or more input scalar values into a single scalar result, taking the maximum of the inputs.
maximum(input1, input2, ...)
Parameters:
- Each input parameter is a scalar expression.
- The output value of this expression will equal the maximum value of any of the inputs.
Example: maximum(a, b, c)
means "take the value a, b, or c, whichever is highest".
minimum
Combines two or more input scalar values into a single scalar result, taking the minimum of the inputs.
minimum(input1, input2, ...)
Parameters:
- Each input parameter is a scalar expression.
- The output value of this expression will equal the minimum value of any of the inputs.
Example: minimum(a, b, c)
means "take the value a, b, or c, whichever is lowest".
average
Combines two or more input scalar values into a single scalar result, taking the average of the inputs.
minimum(input1, input2, ...)
Parameters:
- Each input parameter is a scalar expression.
- The output value of this expression will equal the average of all the inputs.
Example: average(a, b, c)
means "take the average of a's value, b's value, and c's value".
scalar
Converts a toggle expression into a scalar one.
scalar(toggle)
Parameters:
- toggle: A toggle expression to convert to a scalar.
- The output value of this expression will be 1 when the toggle is true, 0 when the toggle is false.
Example: scalar(ModuleToggleLED)
will be 1 when the light is turned on, 0 when the light is turned off.