PaintCode Power User: Expressions
This post is a part of series PaintCode Power User.
Probably the most complex feature of PaintCode is Expression Variables. It dives deep into programming world, far from visual design, but offers infinite possibilities. Don’t panic, simple conditions or arithmetics are trivial enough even for non-programmers and can be a powerful tool in your toolbox.
Before discussing them in-depth, here are some examples so you have an idea of what is an Expression:
-
Thickness changing based on highlight:
isHighlighted ? 3 : 2
. -
Distance of thumb on slider:
mouseLocation.x + 20 + padding
. -
Position of marker on circle:
makePoint(100 * sin(angle), 100 * cos(angle))
.
Not that bad, is it? You may want to read our post about Text Fields, which support some simple expressions. Here I will discuss more advanced features.
Using References
Most important aspect of expressions is that you can use other Variables within them. All of the above examples take advantage of it. This allows you to have Variables that are set manually, like isHighlighted
or angle and other Variables that calculate the result based on them.
- You can use other Variables, but also Colors, Gradients, Shadows and Images. More about these later.
-
References to these Library items need to be adjusted so they don't contain spaces, special characters, and each word begins with capital letter. You don't need to rename your Library Items, their names in Expressions are adjusted automatically. This form is called camelCase 🐫 . Here are few examples:
-
Thickness →
thickness
-
Is highlighted →
isHighlighted
-
Mouse Location →
mouseLocation
-
angle →
angle
, no change -
Výška →
vyska
, diacritics are removed -
DP3#>F →
dP3F
, special characters are removed
-
Thickness →
- Order of Variables in the list has no effect on how expressions evaluate. You can refer to a Variable defined below or above.
- It is possible to rename a Variable and all references to it will be updated automatically.
Other Library Items
In previous post about Variables, I made a list of types that Variables can have, like Number or Text. However, expression-based Variables can evaluate to some additional types, but these types should be already familiar to you:
- Color
- Gradient
- Shadow
- Image
Yes, an expression can return any other Library item. This is useful for example with conditioned style. Imagine you have two colors Tint Color and Lighter Color as its derivation. We want to change fill of a shape when our Variable Is highlighted is set. We achieve this by creating a new Variable that contains this expression: isHighlighted ? lighterColor : tintColor
. After we connect this Variable to the Shape’s fill, we can toggle Is Highlighted On and Off to see how the color changes in canvas.
As if that was not enough, we also implemented functions to create these types using custom values:
-
makeColor(red, green, blue, alpha)
– This will create color, takes 4 values in range from 0 to 1. -
makeGradient(color1, color2)
– This will create a gradient with 2 colors. It’s not currently possible to create more complex gradients using Expression. -
makeShadow(color, x, y, blur)
– This will create a shadow of given color, X and Y offset, and blur radius. - Sorry, no function to create an image. Why would you do that?
Language Tips
For full description on the language, read our documentation on this topic. It’s not very complicated, the language is a mostly a subset of both JavaScript and C.
Numbers
- Even if Variables have different editors for Number, Fraction, and Angle types, in Expressions they are all equal.
-
Basic math operators
+ - * / %
follow standard precedence rules. Use parentheses if needed. -
Hey, programmers! All numbers are floats, which means that
7 / 2 = 3.5
. -
Decimal separator needs to be
.
and not,
as in some languages. -
Operator
%
calculates remainder after division, for example10 % 3 = 1
and11 % 3 = 2
and12 % 3 = 0
. This works also for fractional numbers and can be used to wrap progress into a defined range of values. For example, expressionangle % 360
will always return a number between 0 and 360, even ifangle
is 967 (in which case is evaluates to 247). -
The only available numeric constant is
PI
, in caps. -
Three rounding functions are available:
round() ceil() floor()
and a function that returns the fractional part isfrac()
. -
To clamp values use
min()
andmax()
and their combination. For examplemax(0, angle)
will never evaluate to negative values andmin(max(0, angle), 180)
will never exceed 180. -
Distance between points can calculated using Pythagorean formula:
sqrt(pow(A.x - B.x, 2) + pow(A.y - B.y, 2))
, whereA
andB
are variables of type Point. -
Point on circle can be calculated using
makePoint(radius * sin(angle), radius * cos(angle))
whereradius
andangle
are existing variables (you can of course use constant radius, too). -
To rotate a shape, for example an arrow, so that it is directed to a specific point, use
atan2(B.y - A.y, B.x - A.y)
for rotation, whereA
is position of the shape andB
is desired direction point.
Text
- In programming, very common term for text is string.
-
Enclose anything inside single quotes
'
or double quotes"
and it becomes a string value. -
Strings can be appended using
+
operator, for example"Paint" + "Code"
. No other arithmetic operator is allowed. -
To obtain length of a string, append
.length
to it, for example"hello".length
evaluates to 5. -
Numbers can be converted to text using
stringFromNumber()
function, for example"Remaining " + stringFromNumber(seconds) + " seconds."
However, it’s not possible to customize the number format.
Booleans
- In programming, boolean is a name for type of variable that can hold only two distinct values: 0/1, On/Off, Yes/No, True/False. Booleans are used for conditions.
-
Constants for positive values are
true YES yes
. They are all equal. -
Constants for negative values are
false NO no
. They are all equal. -
To invert boolean value, use
!
operator, for example!isHighlighted
evaluates to true only ifisHighlighted
is false. -
Operators
< > <= >=
compare two numbers and evaluates to a boolean value. -
Operators
== !=
compare two values for equality or inequality. They require both operands to be the same type: numbers, booleans, strings, and so on. -
To make a decision based on boolean use
b ? t : f
construct, whereb
is a boolean value andt f
are some values (of the same type). Ifb
is true thent
is used. Ifb
is false thenf
is used. -
To combine multiple boolean values, use
&& ||
operators. They are called And and Or. For&&
to be true, both values must be true. For||
to be true, at least one value must be true.
Geometry
-
Function
makePoint(x, y)
creates a value of Point type that has X and Y components, which can be accessed using.x
and.y
suffix. -
Function
makeSize(width, height)
creates a value of Size type that has Width and Height components, which can be accessed using.width .height
suffixes. -
Function
makeRect(x, y, width, height)
creates a value of Rectangle type that has X, Y, Width, and Height components, which can be accessed using.x .y .width .height
suffixes. -
Rectangle also has an accessor
.origin
that obtains X and Y as Point and an accessor.size
that obtains Width and Height as Size.
Quite a lot of information to absorb, I know. Keep in mind that we consider Expressions as really advanced feature of PaintCode. It’s totally possible to build a design without touching any of these things.
And remember, you can use numeric operators and functions in all text fields of Inspector and they will evaluate immediately. For details, see our older post about Text Fields.
To learn more about Expressions, read our Expressions documentation or watch a video about Expressions. We also have a video with Advanced Expressions.