Skip to content

Scalar syntax

Snark edited this page Nov 18, 2023 · 5 revisions

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:

The following sections describe the syntax for each of these types of scalar.

Special modifier: The - operator

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 supporting IScalar. If a controller on the part has set its controllerName 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.
  • this keyword: Any controller that implements IScalar can use the reserved word this in any scalar context within its ColorSource syntax. The value of this, 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):

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.