1

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 |
+---------+---------+---------+---------+---------+
Markus von Broady
  • 36,563
  • 3
  • 30
  • 99
  • 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 :) – Gorgious Oct 27 '22 at 06:44
  • @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. – Markus von Broady Oct 27 '22 at 10:29

2 Answers2

3
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 |
+---------+---------+---------+---------+---------+
Markus von Broady
  • 36,563
  • 3
  • 30
  • 99
  • 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? – Robin Betts Oct 27 '22 at 07:42
  • 1
    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. – Markus von Broady Oct 27 '22 at 10:23
  • @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. – Markus von Broady Oct 27 '22 at 10:26
  • And only now I realized I misrepresented Chris'es solution there because I just represented % as modulo, whereas shading modulo uses fmod – Markus von Broady Oct 27 '22 at 10:43
  • 1
    MVB: Wow! That is such a complete response! (Like your answers, of late).. Thanks! :) – Robin Betts Oct 27 '22 at 11:32
0

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

Duarte Farrajota Ramos
  • 59,425
  • 39
  • 130
  • 187
  • 1
    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: https://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. – Markus von Broady Nov 19 '23 at 10:39
  • 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 ! – Yannick Deharo Nov 30 '23 at 21:04