Random snowflake generator in Python for BricsCAD V26.
Illustrates PyRx, Python wrappers around BRX , Db.OverrulableEntity, an entity designed for overruling, Using Gi.DrawableOverrule to overrule the graphics of an object
import traceback
import math
import random
from pyrx import Ap, Db, Ed, Ge, Gi
from typing import Tuple
class OrDrawOverrule(Gi.DrawableOverrule):
def __init__(self) -> None:
Gi.DrawableOverrule.__init__(self)
# override
def isApplicable(self, subject) -> bool:
return True
# override
def worldDraw(self, subject, wd) -> bool:
try:
flag = self.baseWorldDraw(subject, wd)
ore = Db.OverrulableEntity.cast(subject)
geo: Gi.Geometry = wd.geometry()
mat = Ge.Matrix3d.translation(ore.position() - Ge.Point3d.kOrigin)
geo.pushModelTransform(mat)
pnts = ore.points()
for idx in range(1, len(pnts)):
geo.polyline([pnts[idx - 1], pnts[idx]])
geo.popModelTransform()
except Exception as err:
traceback.print_exception(err)
finally:
return flag
overrulableEntityDraw = None
def generate_single_3d_point(
x_min: float, x_max: float, y_min: float, y_max: float, z_min: float, z_max: float
) -> Tuple[float, float, float]:
"""
Generates a single random 3D point (x, y, z) within the defined cuboid limits.
Args:
x_min, x_max: Minimum and maximum bounds for the X-coordinate.
y_min, y_max: Minimum and maximum bounds for the Y-coordinate.
z_min, z_max: Minimum and maximum bounds for the Z-coordinate.
Returns:
A tuple representing the single point (x, y, z).
"""
# Generate random coordinates for each axis
x = random.uniform(x_min, x_max)
y = random.uniform(y_min, y_max)
z = random.uniform(z_min, z_max)
return (x, y, z)
def generate_snowflake_edges(iterations: int, side_length: float = 100.0) -> list:
"""
Generates the edges of a random-variant Koch snowflake fractal.
Args:
iterations: The number of recursive steps (fractal depth).
Higher value means more complex edges.
side_length: The length of the initial hexagon's sides.
Returns:
A list of tuples, where each tuple represents an edge ((x1, y1), (x2, y2)).
"""
# --- 1. Initial Edges (Setup for 6-fold symmetry) ---
# A snowflake starts with a hexagon shape (6 edges).
# We calculate the vertices of a hexagon centered at (0, 0).
edges = []
# Start angle: -pi/2 (to have a flat side at the top/bottom)
start_angle = -math.pi / 2.0
# Calculate initial six vertices
for i in range(6):
angle = start_angle + i * (math.pi / 3.0) # 60 degrees * i
# Calculate the coordinates of the start point of the current edge
# We start the hexagon at the 6th point and iterate back for simplicity
x1 = side_length * math.cos(angle - math.pi / 3.0)
y1 = side_length * math.sin(angle - math.pi / 3.0)
# Calculate the coordinates of the end point of the current edge
x2 = side_length * math.cos(angle)
y2 = side_length * math.sin(angle)
# Add the initial edge: ((x_start, y_start), (x_end, y_end))
edges.append(((x1, y1), (x2, y2)))
# --- 2. Fractal Iteration ---
# The Koch iteration breaks one line into four smaller lines.
# We introduce a random variation to make it look unique.
#
for _ in range(iterations):
new_edges = []
for (x1, y1), (x2, y2) in edges:
# 1. Calculate the vector of the current edge (v)
vx = x2 - x1
vy = y2 - y1
# 2. Calculate the intermediate points for the 4 new segments
# Point A (1/3 of the way along the segment)
ax = x1 + vx / 3.0
ay = y1 + vy / 3.0
# Point B (2/3 of the way along the segment)
bx = x1 + 2.0 * vx / 3.0
by = y1 + 2.0 * vy / 3.0
# Point C (The tip of the new triangle)
# This is rotated 60 degrees (pi/3 radians) from the vector at point A.
# Rotation matrix: (vx', vy') = (vx*cos(a) - vy*sin(a), vx*sin(a) + vy*cos(a))
# We use cos(60) = 0.5 and sin(60) = sqrt(3)/2
cos_60 = 0.5
sin_60 = math.sqrt(3) / 2.0
# Rotate the middle segment (1/3 of the full vector) by 60 degrees (outward)
tip_vx = (vx / 3.0) * cos_60 - (vy / 3.0) * sin_60
tip_vy = (vx / 3.0) * sin_60 + (vy / 3.0) * cos_60
# C is A plus the rotated vector
cx = ax + tip_vx
cy = ay + tip_vy
# --- Random Variation (The "Snowflake" part) ---
# Add a small, random deviation to the tip point C to break the perfect fractal
# and give each snowflake a unique appearance.
RANDOM_FACTOR = 0.05 # Adjust this value for more or less randomness
cx += random.uniform(-RANDOM_FACTOR * side_length, RANDOM_FACTOR * side_length)
cy += random.uniform(-RANDOM_FACTOR * side_length, RANDOM_FACTOR * side_length)
# 3. Create the 4 new edges
new_edges.append(((x1, y1), (ax, ay))) # 1st segment
new_edges.append(((ax, ay), (cx, cy))) # 2nd segment (to the tip)
new_edges.append(((cx, cy), (bx, by))) # 3rd segment (from the tip)
new_edges.append(((bx, by), (x2, y2))) # 4th segment
edges = new_edges
return edges
def gen_snowFlakes(num: int):
snowFlakes = []
for i in range(num):
pnts = []
ent = Db.OverrulableEntity()
snowflake_edges = generate_snowflake_edges(iterations=4, side_length=150)
for s, e in snowflake_edges:
pnts.append(Ge.Point3d(s[0], s[1], 0.0))
pnts.append(Ge.Point3d(e[0], e[1], 0.0))
ent.setPoints(pnts)
x, y, z = generate_single_3d_point(
x_min=0.0, x_max=5000.0, y_min=0.0, y_max=5000.0, z_min=0.0, z_max=5000.0
)
ent.setPosition(Ge.Point3d(x, y, z))
snowFlakes.append(ent)
return snowFlakes
def OnDblClk(ent: Db.OverrulableEntity, pnt: Ge.Point3d):
print("\nOMG it's a flake {},{}".format(ent.isA().name(), pnt))
@Ap.Command()
def startOverrule():
global overrulableEntityDraw
if overrulableEntityDraw is not None:
return
overrulableEntityDraw = OrDrawOverrule()
overrulableEntityDraw.addOverrule(Db.OverrulableEntity.desc(), overrulableEntityDraw)
overrulableEntityDraw.setIsOverruling(True)
# register double click
Db.OverrulableEntity.registerOnDoubleClick(OnDblClk)
@Ap.Command()
def letitsnow() -> None:
try:
db = Db.curDb()
flakes = gen_snowFlakes(100)
db.addToModelspace(flakes)
except Exception as err:
traceback.print_exception(err)
1
Comments
-
Yes nearly Christmas, but here in AUS its 39c today. Similar task a bit of fun. Save Santa sleigh .png to a support directory.
1 -
At the end the tree is exploded, is it as expected? I wanted to make a printscreen of the beautiful tree )
0 -
I hit undo a few times
1 -
Credit should go to I think Kent Cooper who wrote the original code for the tree, I just added the blinking and rotate, decided that Santa should crash into tree.
Who knows what we will get this year. A Santa sleigh sliding down a random snow flake slope maybe.
0




