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.
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:
Is highlighted →
Mouse Location →
angle, no change
vyska, diacritics are removed
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:
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?
- 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
,as in some languages.
%calculates remainder after division, for example
10 % 3 = 1and
11 % 3 = 2and
12 % 3 = 0. This works also for fractional numbers and can be used to wrap progress into a defined range of values. For example, expression
angle % 360will always return a number between 0 and 360, even if
angleis 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 is
To clamp values use
max()and their combination. For example
max(0, angle)will never evaluate to negative values and
min(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)), where
Bare variables of type Point.
Point on circle can be calculated using
makePoint(radius * sin(angle), radius * cos(angle))where
angleare 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, where
Ais position of the shape and
Bis desired direction point.
- 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
.lengthto it, for example
"hello".lengthevaluates 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.
- 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
!isHighlightedevaluates to true only if
< > <= >=compare two numbers and evaluates to a boolean value.
== !=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 : fconstruct, where
bis a boolean value and
t fare some values (of the same type). If
bis true then
tis used. If
bis false then
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.
makePoint(x, y)creates a value of Point type that has X and Y components, which can be accessed using
makeSize(width, height)creates a value of Size type that has Width and Height components, which can be accessed using
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 .heightsuffixes.
Rectangle also has an accessor
.originthat obtains X and Y as Point and an accessor
.sizethat 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.