25 mai 2024

[Dynamo += Python] Trouver la courbe médiane entre 2 courbes





Comment trouver la courbe moyenne entre deux courbes, qui ne sont pas forcément parallèles. 

#DynamoBIM #Revit #Python  #Geometry #AutodeskExpertElite #AutodeskCommunity 

if this article is not in your language, use the Google Translate widget ⬈ (bottom of page for Mobile version )



Trouver la courbe moyenne entre deux courbes qui ne sont pas parallèles peut sembler complexe. Cependant, en utilisant Dynamo et Scipy, nous pouvons accomplir cette tâche en plusieurs étapes :


  • Étape 1 : Préparation des Courbes

Nous nous assurons que les courbes sont projetées sur un plan commun pour simplifier les calculs.


  • Étape 2 : Calcul des Points Moyens
Nous utilisons uniform_filter1d du module Python Scipy pour lisser les points obtenus à partir des courbes initiales et créer une courbe moyenne approximative.

Note:
Depuis Revit 2024 (Dynamo 2.18+), le package python Scipy est intégré à Dynamo.


  • Étape 3 : Création de la courbe moyenne finale

Ensuite, nous recherchons le long des perpendiculaires de points équidistants sur les courbes en utilisant des lignes comme des rayons d'intersection. La direction de ces lignes est calculée à partir de la courbe précédemment obtenue avec Scipy.


Code


import sys
import clr
clr.AddReference('ProtoGeometry')
from Autodesk.DesignScript.Geometry import *
import numpy as np
from scipy.interpolate import interp1d
from scipy.ndimage import uniform_filter1d

curveA, curveB = IN[0]
plane = Plane.ByOriginNormal(Point.ByCoordinates(0, 0 , 0), Vector.ByCoordinates(0,0,1))
# ensure curves are Z zero
curveA = curveA.PullOntoPlane(plane)
curveB = curveB.PullOntoPlane(plane)
ptCurveA = [curveA.PointAtParameter(n) for n in np.linspace(0, 1, num=500)]
ptCurveB = [curveB.PointAtParameter(n) for n in np.linspace(0, 1, num=500)]
# merge, flatten points and keeping order
pts  = [pt for pairPts in zip(ptCurveA,ptCurveB) for pt in pairPts]

data = [[int(p.X), int(p.Y)] for p in pts]
x, y = [i for i in zip(*data)]
# compute the arithmetic average with scipy.ndimage.uniform_filter1d
y_smooth = uniform_filter1d(y,size=5, mode='wrap')
x_smooth = uniform_filter1d(x,size=5, mode='wrap')

rays = []
mid_Pts = []
ray_length = 6000 # example in millimeter
# compute an approximative average curve 
pts_uniform = [Point.ByCoordinates(x, y , 0) for x, y in zip(x_smooth, y_smooth)]
pts_uniform_iter = iter(pts_uniform[:])
dsPoints = [Point.ByCoordinates((pta.X + ptb.X) / 2, (pta.Y + ptb.Y) / 2, 0) for pta, ptb in zip(pts_uniform_iter, pts_uniform_iter)]
# remove extra points
dsPoints = dsPoints[1:-1]
aprox_average_curve = PolyCurve.ByPoints(dsPoints, False)
# with this curve compute an new average curve by normal curve intersection (ray) and compute middle points 
for n in np.linspace(0, 1, num=100):
    normal = aprox_average_curve.NormalAtParameter(n)
    pt = aprox_average_curve.PointAtParameter(n)
    ray_line = Line.ByStartPointDirectionLength(pt, normal, ray_length) 
    ray_line = ray_line.Translate(normal.Reverse(), ray_length / 2)
    if ray_line.DoesIntersect(curveA) and ray_line.DoesIntersect(curveB):
        ptInterA = ray_line.Intersect(curveA)[0]
        ptInterB = ray_line.Intersect(curveB)[0]
        midpt = Point.ByCoordinates((ptInterA.X + ptInterB.X) / 2, (ptInterA.Y + ptInterB.Y) / 2, 0)
        rays.append(ray_line)
        mid_Pts.append(midpt)

OUT = rays, PolyCurve.ByPoints(mid_Pts, False)
          



Vidéo Démonstration 







"Il n'y a pas de lignes droites ou d'angles aigus dans la nature. Par conséquent, les bâtiments ne doivent pas en avoir non plus." - Antoni Gaudí


0 commentaires:

Enregistrer un commentaire