_mxutils_random¶
Demonstrates deterministic random values with mxutils.Random.
_mxutils_random.py¶
"""Demonstrates deterministic random values with mxutils.Random.
Run this example in the Cinema 4D Script Manager. Re-running it prints the same
values because the random generator is initialized with a fixed seed.
Topics:
* mxutils.Random
* Deterministic random values
* Hash-based random values
"""
__author__ = "Ferdinand Hoppe"
__copyright__ = "Copyright (C) 2026 MAXON Computer GmbH"
__license__ = "Apache-2.0 License"
__version__ = "2027.0"
import c4d
import maxon
import mxutils
import random
def main() -> None:
"""Generates sequential and hash-based pseudo-random values.
"""
# Create a random object. We could also use the global random object mxutils.g_random.
rand: mxutils.Random = mxutils.Random(seed=42)
# Deterministic random values
# Create a list of 100 random points in the interval [0, 1000] using RandomVector. This is
# based on the random module from the Python standard library because the function name starts
# with 'Random', and will produce different values each time the code is run.
points: list[c4d.Vector] = [rand.RandomVector(c4d.Vector(1000)) for _ in range(100)]
# Associate each point with a random float value using RandomFloat. The tuples will be different
# each time the code is run unless we reset the seed before each call (which is a very expensive
# operation).
pairs1: list[tuple[c4d.Vector, float]] = [(p, rand.RandomFloat()) for p in points]
print(f"First ten random vector-float pairs: {pairs1[:10]} ...")
# Generate point, random float pairs using a hash function, here hashing the point index. The tuples
# will be the same each time the code is run unless the point order/indices in #points change.
pairs2: list[tuple[c4d.Vector, float]] = [(p, rand.HashNumberToNumber(i))
for i, p in enumerate(points)]
print(f"First ten hash index-based vector-float pairs: {pairs2[:10]} ...")
# Will change, because the points have now a different order/index.
print("Shuffling input.")
random.shuffle(points)
pairs3: list[tuple[c4d.Vector, float]] = [(p, rand.HashNumberToNumber(i))
for i, p in enumerate(points)]
print(f"First ten hash index-based vector-float pairs after shuffling: {pairs3[:10]} ...")
# And finally generate the tuples using the point values itself. A given point will always be
# associated with the same random float value, no matter the count and order of points in #obj.
pairs4: list[tuple[c4d.Vector, float]] = [(p, rand.HashVectorToNumber(p)) for p in points]
random.shuffle(points)
pairs5: list[tuple[c4d.Vector, float]] = [(p, rand.HashVectorToNumber(p)) for p in points]
# The outputs would still be in a different order because we shuffled the data. But each point
# will always be associated with the same random float value, no matter the count and order of
# points in #points. So, when we sort our results by value, the outputs will be identical.
pairs4.sort(key=lambda pair: pair[1])
pairs5.sort(key=lambda pair: pair[1])
print(f"First ten hash vector-based vector-float pairs after shuffling: {pairs5[:10]} ...")
print(f"Are the hash vector-based pairs identical after shuffling? {pairs4 == pairs5}")
## Specialized hash functions (most of these also have a Random* equivalent)
# Hash 42 to a pseudo-random RGBA color in the intervals hue(0, 90), saturation(0.5, 1.0),
# value(0.5, 1.0), and alpha(0.5, 1.0) using the global random instance and the c4d.Vector4d type.
rgba: c4d.Vector4d = rand.HashNumberToColor(
value=42, hueInterval=(0, 90), saturationInterval=(0.5, 1.0), valueInterval=(0.5, 1.0),
alphaInterval=(0.5, 1.0), dtype=c4d.Vector4d)
print(f"Hash number to color: 42 -> {rgba}")
deg45: float = c4d.utils.DegToRad(45.0) # 45° in radians
# Hash 42 into a matrix whose offset lies in the interval [[0, 0, 0], [100, 100, 100]],
# but has no rotation and a scale of 1.
m: c4d.Matrix = rand.HashNumberToMatrix(42, positionInterval=(c4d.Vector(0), c4d.Vector(100)))
print(f"Hash number to matrix: 42 -> {m}")
# Hash 42 into a matrix with a rotation in the interval [[-45, -45, -45], [45, 45, 45]] degrees.
m: c4d.Matrix = rand.HashNumberToMatrix(42, rotationInterval=(c4d.Vector(-deg45), c4d.Vector(deg45)))
print(f"Hash number to matrix with rotation: 42 -> {m}")
# Hash 42 into a transform that has an offset in the interval [0, 0, 0], [0, 0, 100],
# a rotation in the interval [[-45, 0, 0], [45, 0, 0]] degrees, and a scale in the interval
# [1, 1, 0.5], [1 1, 2.0].
m: c4d.Matrix = rand.HashNumberToMatrix(
value=42,
positionInterval=(c4d.Vector(0), c4d.Vector(0, 0, 100)),
rotationInterval=(c4d.Vector(-deg45, 0, 0), c4d.Vector(deg45, 0, 0)),
scaleInterval=(c4d.Vector(1, 1, 0.5), c4d.Vector(1, 1, 2.0))
)
print(f"Hash number to matrix with position, rotation, and scale: 42 -> {m}")
# There are many more specialized hash functions available, check the docs for details.
if __name__ == "__main__":
main()