Vous aimez les transformations et les géométries en mouvement… vous êtes bien tombé
En addition du mode périodique, nous nous concentrons sur l'utilisation de l'API DynamoCore pour des mises à jour périodiques des variables, un processus different que le stockage traditionnel en mémoire ou dans des fichiers.
- Utilisation de l'API DynamoCore
L'API DynamoCore est utilisé ici pour manipuler les valeurs au sein des nœuds Dynamo en temps réel.
La méthode UpdateValue
de la classe ModelBase
permet de mettre à jour les
variables à chaque exécution du script, offrant une alternative aux méthodes
telles que System.AppDomain.CurrentDomain.SetData
/ GetData
et la
sérialisation via le module pickle.
ModelBase.UpdateValue(UpdateValueParams updateValueParams)
Dynamo.Graph.ModelBase est la classe de base pour tous les objets avec lesquels l'utilisateur peut interagir dans Dynamo.
Utilisation de SetData / GetData entre 2 nœuds Python sans transfert de données via les fils (wires) |
- Exemple 1 : Un zoom au travers de la fractale Mandelbrot
Code (IronPython)
import clr
import System
from System.Runtime.InteropServices import Marshal
clr.AddReference("System.Core")
clr.ImportExtensions(System.Linq)
clr.AddReference('DynamoRevitDS')
import Dynamo
#reference dynamo core to update node values
clr.AddReference('DynamoCore')
from Dynamo.Graph import UpdateValueParams
#access to the current Dynamo instance and workspace
dynamoRevit = Dynamo.Applications.DynamoRevit()
engine = dynamoRevit.RevitDynamoModel.EngineController
currentWorkspace = dynamoRevit.RevitDynamoModel.CurrentWorkspace
model = dynamoRevit.RevitDynamoModel
clr.AddReference('System.Drawing')
from System.Drawing import Color, Bitmap, Imaging, Rectangle, Image
import colorsys
import math
#import time
px, py = -0.7746806106269039, -0.1374168856037867 #Tante Renate
R = 3
max_iteration = 170 #2500
w, h = 400, 400 #1024,1024
mfactor = 0.5
def Mandelbrot2(*args):
"""
get Mandelbrot fractal
"""
x, y, max_iteration, minx, maxx, miny, maxy = args
zx = 0
zy = 0
RX1, RX2, RY1, RY2 = px-R/2.0, px+R/2.0, py-R/2.0, py+R/2.0
cx = (x-minx)/float(maxx-minx)*(RX2-RX1)+RX1
cy = (y-miny)/float(maxy-miny)*(RY2-RY1)+RY1
i=0
while zx**2 + zy**2 <= 4 and i < max_iteration:
temp = zx**2 - zy**2
zy = 2*zx*zy + cy
zx = temp + cx
i += 1
return x, y, i
def image_by_lockBits(data_array):
global w
global h
global max_iteration
global mfactor
bmpOut = Bitmap(w, h, Imaging.PixelFormat.Format24bppRgb)
newData = bmpOut.LockBits(
Rectangle(0, 0, w, h),
Imaging.ImageLockMode.WriteOnly, bmpOut.PixelFormat)
newBytesPerPixel = int(Image.GetPixelFormatSize(bmpOut.PixelFormat) / 8)
for x, y, c in data_array:
v = c**mfactor / float(max_iteration**mfactor)
hv = 0.67-v
if hv < 0:
hv += 1
r, g, b = colorsys.hsv_to_rgb(hv,1,1-(v-0.1)**2/0.9**2)
r = min(255,round(r*255))
g = min(255,round(g*255))
b = min(255,round(b*255))
newPixelIndex = y * newData.Stride + x * newBytesPerPixel
Marshal.WriteByte(newData.Scan0, newPixelIndex, r)
Marshal.WriteByte(newData.Scan0, newPixelIndex + 1, g)
Marshal.WriteByte(newData.Scan0, newPixelIndex + 2, b)
bmpOut.UnlockBits(newData)
#
return bmpOut
def image_by_setPixel(data_array):
global w
global h
global max_iteration
global mfactor
#
bmpOut = Bitmap(w, h)
# color pixels
for x, y, c in data_array:
v = c**mfactor/float(max_iteration**mfactor)
hv = 0.67-v
if hv < 0:
hv+=1
r,g,b = colorsys.hsv_to_rgb(hv,1,1-(v-0.1)**2/0.9**2)
r = min(255,round(r*255))
g = min(255,round(g*255))
b = min(255,round(b*255))
bmpOut.SetPixel(x, y, Color.FromArgb(r, g, b))
#
return bmpOut
def gen_Mandelbrot_image(sequence, useSetPixel=False):
global w
global h
byteArr = []
ziplst = []
for x in range(w):
for y in range(h):
ziplst.append([x, y, max_iteration, 0, w-1, 0, h-1])
# apply multiprocessing parrlalelisme
threadResult = ziplst.AsParallel()\
.WithDegreeOfParallelism(System.Environment.ProcessorCount)\
.WithExecutionMode(System.Linq.ParallelExecutionMode.ForceParallelism)\
.Select(lambda e : Mandelbrot2(*e))
#
if useSetPixel:
return image_by_setPixel(threadResult)
else:
return image_by_lockBits(threadResult)
nodeK = next((i for i in currentWorkspace.Nodes if i.Name.Equals("K_R")), None)
if nodeK is not None:
# get values
k = int(nodeK.GetValue(0,engine).Data)
R = float(nodeK.GetValue(1,engine).Data)
f = 0.80
RZF = 1/1000000000000
useSetPixel = IN[1]
if k < 40 :
mfactor = 0.5 + (1/float(1000000000000))**0.1/float(R**0.1)
imgM = gen_Mandelbrot_image(k, useSetPixel)
R *= f
k+=1
params = UpdateValueParams("Code", "{0};\n{1};".format(k, R))
nodeK.UpdateValue(params)
OUT = imgM
Note:
-
ici est utilisé IronPython pour profiter du multiprocessing avec la
méthode Linq
AsParallel()
- l'image est construite à partir d'une des 2 méthodes :
-
SetPixel
-
LockBits/UnLockBits
il sera préférable d'utiliser la méthode LockBits si vous souhaitez générer une plus grande image
Résultats (avec DynanoBim 3.0.4 / Revit 2025)
- Exemple 2 : Visualisation d'un tesseract (hypercube 4D) en
mouvement
En géométrie, le tesseract, appelé aussi 4-hypercube ou encore 8-cellules ou octachore, est l'analogue quadridimensionnel du cube (tri-dimensionnel), où le mouvement le long de la quatrième dimension est souvent une représentation pour des transformations liées du cube à travers le temps.
Grace au mode periodique, la représentation du tesseract montre comment les transformations dans la quatrième dimension peuvent être visualisées dans notre espace tridimensionnel.
Code (IronPython ou CPython3/PythonNet)
import clr
import System
clr.AddReference('ProtoGeometry')
from Autodesk.DesignScript.Geometry import *
import math
clr.AddReference('DSCoreNodes')
from DSCore import Color as DSColor
clr.AddReference('GeometryColor')
from Modifiers import GeometryColor
clr.AddReference('DynamoRevitDS')
import Dynamo
clr.AddReference('DynamoCore')
from Dynamo.Graph import UpdateValueParams
#access to the current Dynamo instance and workspace
dynamoRevit = Dynamo.Applications.DynamoRevit()
engine = dynamoRevit.RevitDynamoModel.EngineController
currentWorkspace = dynamoRevit.RevitDynamoModel.CurrentWorkspace
model = dynamoRevit.RevitDynamoModel
net_clr_runtime_name = ".Net Runtime : " + str(System.Environment.Version)
# Define the function for matrix multiplication (matrix by vector)
def matrix_multiply(mat, vec):
return [
sum(mat[i][j] * vec[j] for j in range(len(vec)))
for i in range(len(mat))
]
# Project 4D coordinates to 3D
def project_4d_to_3d(point, scale, center):
w = 1.0 / (4 - point[3]) # Perspective projection
return [scale * w * point[i] + center[i] for i in range(3)]
# Define a function to create lines between points
def create_lines(points, connections_colors):
lines = []
for start, end, ds_color in connections_colors:
line = Line.ByStartPointEndPoint(points[start], points[end])
objColor = GeometryColor.ByGeometryColor(line, ds_color)
lines.append(objColor)
return lines
# Define the tesseract's vertices in 4D
points_4d = [
[-1, -1, -1, 1], [1, -1, -1, 1], [1, 1, -1, 1], [-1, 1, -1, 1],
[-1, -1, 1, 1], [1, -1, 1, 1], [1, 1, 1, 1], [-1, 1, 1, 1],
[-1, -1, -1, -1], [1, -1, -1, -1], [1, 1, -1, -1], [-1, 1, -1, -1],
[-1, -1, 1, -1], [1, -1, 1, -1], [1, 1, 1, -1], [-1, 1, 1, -1]
]
# Define rotation angles and setup rotation matrices for 4D rotation
#find the specific node
nodeK = next((i for i in currentWorkspace.Nodes if i.Name =="angle"), None)
if nodeK is not None:
# get values
d = nodeK.GetValue(0,engine)
angle = float(nodeK.GetValue(0,engine).Data)
cos_a, sin_a = math.cos(angle), math.sin(angle)
rotation_xy = [[cos_a, -sin_a, 0, 0], [sin_a, cos_a, 0, 0], [0, 0, 1, 0], [0, 0, 0, 1]]
rotation_zw = [[1, 0, 0, 0], [0, 1, 0, 0], [0, 0, cos_a, -sin_a], [0, 0, sin_a, cos_a]]
# Apply rotations
points_4d_rotated = [matrix_multiply(rotation_xy, p) for p in points_4d]
points_4d_rotated = [matrix_multiply(rotation_zw, p) for p in points_4d_rotated]
# Project points to 3D
scale = 200 # Scale factor for visualization
center = [400, 400, 0] # Center of the tesseract projection
points_3d = [Point.ByCoordinates(*project_4d_to_3d(p, scale, center)) for p in points_4d_rotated]
# Define connections based on the tesseract's edges
ds_colorA = DSColor.ByARGB(255, 255,0,0)
ds_colorB = DSColor.ByARGB(255, 46, 134, 193)
ds_colorC = DSColor.ByARGB(255, 0,0,0)
connections_colors = [
# first cube
(0, 1, ds_colorA), (1, 2, ds_colorA), (2, 3, ds_colorA), (3, 0, ds_colorA),
(4, 5, ds_colorA), (5, 6, ds_colorA), (6, 7, ds_colorA), (7, 4, ds_colorA),
(0, 4, ds_colorA), (1, 5, ds_colorA), (2, 6, ds_colorA), (3, 7, ds_colorA),
# second cube
(8, 9, ds_colorB), (9, 10, ds_colorB), (10, 11, ds_colorB), (11, 8, ds_colorB),
(12, 13, ds_colorB), (13, 14, ds_colorB), (14, 15, ds_colorB), (15, 12, ds_colorB),
(8, 12, ds_colorB), (9, 13, ds_colorB), (10, 14, ds_colorB), (11, 15, ds_colorB),
# sommets
(0, 8, ds_colorC), (1, 9, ds_colorC), (2, 10, ds_colorC), (3, 11, ds_colorC),
(4, 12, ds_colorC), (5, 13, ds_colorC), (6, 14, ds_colorC), (7, 15, ds_colorC)
]
# Create lines between connected points
lines = create_lines(points_3d, connections_colors)
# Output the lines to Dynamo to visualize the tesseract
OUT = net_clr_runtime_name, lines, points_3d
params = UpdateValueParams("Code", "{0};".format(angle + 0.01))
nodeK.UpdateValue(params)
Résultats (avec DynanoBim 3.0.4 / Revit 2025)
articles (épisodes) sur le mode Periodique
lien des scripts Dynamo (Github)
Cet article n'augmentera peut-être pas votre productivité BIM mais...
'La connaissance est un trésor, mais la pratique est la clé pour y accéder.' — Thomas Fuller"
0 commentaires:
Enregistrer un commentaire