1
$\begingroup$

Since Python's modulo operator % isn't available in Simple Expressions:

Blender can evaluate a useful subset of Python driver expressions directly, which significantly improves performance, especially on multi-core systems.

Since Python drivers are considerably slower how would one port a Python formula using % modulo operator, to use fmod function (or something else?) instead?

Below a testing script to compare % operator with fmod function:

from math import fmod


def fakemod(a, n):
    return fmod(a, n)


row = "| {:>7} " * 5 + "|"
header = row.replace('>','^').replace('<','^')
line = header.replace(' ','-').replace('^','-^').replace('|','+').format(*'-'*5)

print(line)
print(header.format(" ", "     ", "fakemod", "     ", "fakemod"))
print(header.format("i", "i % 4", "(i, 4)", "i % -4", "(i, -4)"))
print(line)
equal = lambda a,b: '==' if a == b else '!='
for i in range(-10, 10):
    a, b, c, d = i % 4, fakemod(i, 4), i % -4, fakemod(i, -4)
    print(row.format(i, a, f"{equal(a,b)} {b}", c, f"{equal(c,d)} {d}"))
print(line)

I'm looking for a way to remove the inconsistencies between % and fakemod. For readability I mark the inconsistencies with != below:

+---------+---------+---------+---------+---------+
|         |         | fakemod |         | fakemod |
|    i    |  i % 4  | (i, 4)  | i % -4  | (i, -4) |
+---------+---------+---------+---------+---------+
|     -10 |       2 | != -2.0 |      -2 | == -2.0 |
|      -9 |       3 | != -1.0 |      -1 | == -1.0 |
|      -8 |       0 | == -0.0 |       0 | == -0.0 |
|      -7 |       1 | != -3.0 |      -3 | == -3.0 |
|      -6 |       2 | != -2.0 |      -2 | == -2.0 |
|      -5 |       3 | != -1.0 |      -1 | == -1.0 |
|      -4 |       0 | == -0.0 |       0 | == -0.0 |
|      -3 |       1 | != -3.0 |      -3 | == -3.0 |
|      -2 |       2 | != -2.0 |      -2 | == -2.0 |
|      -1 |       3 | != -1.0 |      -1 | == -1.0 |
|       0 |       0 |  == 0.0 |       0 |  == 0.0 |
|       1 |       1 |  == 1.0 |      -3 |  != 1.0 |
|       2 |       2 |  == 2.0 |      -2 |  != 2.0 |
|       3 |       3 |  == 3.0 |      -1 |  != 3.0 |
|       4 |       0 |  == 0.0 |       0 |  == 0.0 |
|       5 |       1 |  == 1.0 |      -3 |  != 1.0 |
|       6 |       2 |  == 2.0 |      -2 |  != 2.0 |
|       7 |       3 |  == 3.0 |      -1 |  != 3.0 |
|       8 |       0 |  == 0.0 |       0 |  == 0.0 |
|       9 |       1 |  == 1.0 |      -3 |  != 1.0 |
+---------+---------+---------+---------+---------+
$\endgroup$
2
  • $\begingroup$ I think an example workflow to place this feature in the context of Blender would be nice, currently I feel like only the people who would know how to solve this problem may understand the question :) $\endgroup$
    – Gorgious
    Oct 27, 2022 at 6:44
  • $\begingroup$ @Gorgious Well I lost some time figuring this out, unable to google the existing solution… I also got it wrong in the past because I tested lazily. $\endgroup$ Oct 27, 2022 at 10:29

2 Answers 2

3
$\begingroup$
def fakemod(a, n):
    return fmod(fmod(a, n)+n, n)

Result:

+---------+---------+---------+---------+---------+
|         |         | fakemod |         | fakemod |
|    i    |  i % 4  | (i, 4)  | i % -4  | (i, -4) |
+---------+---------+---------+---------+---------+
|     -10 |       2 |  == 2.0 |      -2 | == -2.0 |
|      -9 |       3 |  == 3.0 |      -1 | == -1.0 |
|      -8 |       0 |  == 0.0 |       0 | == -0.0 |
|      -7 |       1 |  == 1.0 |      -3 | == -3.0 |
|      -6 |       2 |  == 2.0 |      -2 | == -2.0 |
|      -5 |       3 |  == 3.0 |      -1 | == -1.0 |
|      -4 |       0 |  == 0.0 |       0 | == -0.0 |
|      -3 |       1 |  == 1.0 |      -3 | == -3.0 |
|      -2 |       2 |  == 2.0 |      -2 | == -2.0 |
|      -1 |       3 |  == 3.0 |      -1 | == -1.0 |
|       0 |       0 |  == 0.0 |       0 | == -0.0 |
|       1 |       1 |  == 1.0 |      -3 | == -3.0 |
|       2 |       2 |  == 2.0 |      -2 | == -2.0 |
|       3 |       3 |  == 3.0 |      -1 | == -1.0 |
|       4 |       0 |  == 0.0 |       0 | == -0.0 |
|       5 |       1 |  == 1.0 |      -3 | == -3.0 |
|       6 |       2 |  == 2.0 |      -2 | == -2.0 |
|       7 |       3 |  == 3.0 |      -1 | == -1.0 |
|       8 |       0 |  == 0.0 |       0 | == -0.0 |
|       9 |       1 |  == 1.0 |      -3 | == -3.0 |
+---------+---------+---------+---------+---------+
$\endgroup$
5
  • $\begingroup$ Back in the day.. not needed any more, in nodes, with Wrap. But.. how come I got % to work in the expression in my comment here? $\endgroup$
    – Robin Betts
    Oct 27, 2022 at 7:42
  • 1
    $\begingroup$ Oh Rich Sedman came up with exactly the same solution, nice! I tried googling "implement Python's modulo using fmod" etc. but never found something concise (and drivers really need conciseness). As for why you have no problem using % - drivers work in 3 modes: Blender first tries to tokenize an expression for its own stack machine, if it succeeds, you have a simple expression. % operator is not there, so using it will make Blender fail to tokenize and use a slow Python expression. $\endgroup$ Oct 27, 2022 at 10:23
  • $\begingroup$ @RobinBetts BTW when Python is used, first the names and opcodes are checked against a whitelist, if the check fails, you're asked for a permission to run a potentially dangerous script. Here's an interesting case showcasing that. I actually wrote this Q&A to relief some bloat from that answer. $\endgroup$ Oct 27, 2022 at 10:26
  • $\begingroup$ And only now I realized I misrepresented Chris'es solution there because I just represented % as modulo, whereas shading modulo uses fmod $\endgroup$ Oct 27, 2022 at 10:43
  • 1
    $\begingroup$ MVB: Wow! That is such a complete response! (Like your answers, of late).. Thanks! :) $\endgroup$
    – Robin Betts
    Oct 27, 2022 at 11:32
0
$\begingroup$

I know that the topic is closed, but I want to add something for future readers, cause I found a simple solution to use modulos in a Blender scripted expression. So maybe that can help ?

Replace **i%4** by **i-i//4**

For exemple, for i = 406 (406%4 = **2**) :

406-406//4 = **2**

406-(101x4) = **2**

406-404 = **2**
$\endgroup$
2
  • 1
    $\begingroup$ Welcome to BSE! Unfortunately, as simple as this answer is, it seems incorrect: let's assume $i = 4$, then we expect $4 \% 4 = 0$, whereas your solution 4 - 4 // 4 will evaluate to 4 - 1 which will evaluate to 3. You have a unit test prepared in the question, just replace fakemod function's body with return a-a//n and you will see the result: i.imgur.com/XljD9mj.png Also, topics are never closed (from old age) on Stack Exchange, feel free to add an answer, suggest an edit, comment etc. after decades. $\endgroup$ Nov 19, 2023 at 10:39
  • $\begingroup$ Hello ! Thanks for your reply ! I hadn't noticed this error because in my use this case couldn't occur. the behavior changed if a=n. So it seemed perfect to me. Thank you for your help ! $\endgroup$ Nov 30, 2023 at 21:04

You must log in to answer this question.

Not the answer you're looking for? Browse other questions tagged .