9 août 2024

[Dynamo += Python] HelixToolKit




Découverte de la librairie HelixToolKit avec Dynamo

#DynamoBIM #Revit #Python  #HelixToolkit #WPF #AutodeskExpertElite #AutodeskCommunity 

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


  • Introduction
HelixToolkit est une bibliothèque 3D pour .NET, principalement utilisée avec WPF (Windows Presentation Foundation). Elle permet de créer des interfaces utilisateur avec des composants 3D de manière relativement simple. 
      • Il peut être utilisé comme une alternative à DirectContext3D (Revit API)
      • Dynamo l'utilise, ce qui la rend accessible dans le System.AppDomain courant
    Les codes ci-après sont valables avec le moteur IronPython3 et Dynamo 3.x.
    Ils seront mis à jour ultérieurement avec le CPython3 quand PythonNet3 sera implémenté
    🤞🤞🤞 

    • Exemple 1 : Visualisation d'une géométrie



    Pour afficher une géométrie 3D, la classe MeshGeometry3D est utilisée. Celle-ci est construite à partir d'une liste de triangles définie par des positions et des indices.

    À partir d'un solide en entrée, plusieurs méthodes de tesselation peuvent être employées pour générer la géométrie.

    -
    
    import clr
    import sys
    import System
    clr.AddReference("System.Numerics")
    
    clr.AddReference('HelixToolkit')
    clr.AddReference('SharpDX.Mathematics')
    clr.AddReference('HelixToolkit.Core.Wpf')
    clr.AddReference('HelixToolkit.SharpDX.Core')
    clr.AddReference('HelixToolkit.SharpDX.Core.Wpf')
    from HelixToolkit import *
    from HelixToolkit.Wpf.SharpDX import *
    from HelixToolkit.SharpDX.Core import *
    from SharpDX import *
    import SharpDX
    #
    clr.AddReference('ProtoGeometry')
    from Autodesk.DesignScript.Geometry import *
    import Autodesk.DesignScript.Geometry as DS
    
    
    clr.AddReference("System.Core")
    clr.ImportExtensions(System.Linq)
    clr.AddReference("IronPython.Wpf")
    clr.AddReference('System.Core')
    clr.AddReference('System.Xml')
    clr.AddReference('PresentationCore')
    clr.AddReference('PresentationFramework')
    clr.AddReferenceByPartialName("WindowsBase")
    
        
    from System.IO import StringReader
    from System.Windows.Markup import XamlReader, XamlWriter
    from System.Windows import Window, Application
    
    try:
        import wpf
        import time
    except ImportError:
        wpf = None
    
    
    class MeshViewer(Window):
        LAYOUT = '''
        <Window xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation" 
            xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
            xmlns:hx="http://helix-toolkit.org/wpf"
            Title="..."
            Height="700"
            Width="600">
            <Grid>
            <Viewbox Grid.Row="0">
                <hx:HelixViewport3D 
                    Height="400" 
                    Width="400" 
                    VerticalAlignment="Bottom" 
                    ZoomExtentsWhenLoaded="True"  
                    CameraRotationMode="Trackball" 
                    IsViewCubeEdgeClicksEnabled="True" 
                    ZoomAroundMouseDownPoint="True" 
                     Margin="5,35,5,2">
                    <hx:DefaultLights/>
                    <ModelVisual3D>
                        <ModelVisual3D.Content>
                            <GeometryModel3D x:Name="model3D">
                                <GeometryModel3D.Geometry>
                                    <MeshGeometry3D x:Name="meshMain">
                                    </MeshGeometry3D>
                                </GeometryModel3D.Geometry>
                                 <GeometryModel3D.Material>
                                 <DiffuseMaterial x:Name="matDiffuseMain">
                                        <DiffuseMaterial.Brush>
                                            <SolidColorBrush Color="Gray"/>
                                        </DiffuseMaterial.Brush>
                                    </DiffuseMaterial>
                            </GeometryModel3D.Material>
                            </GeometryModel3D>
                        </ModelVisual3D.Content>
                    </ModelVisual3D>
                    <hx:GridLinesVisual3D Width="8" Length="8" MinorDistance="1" MajorDistance="1" Thickness="0.01"/>
                </hx:HelixViewport3D >
            </Viewbox>
            </Grid>
        </Window>
        '''
        
        def __init__(self, mesh):
            self.ui = wpf.LoadComponent(self, StringReader(MeshViewer.LAYOUT))
            self.mesh = mesh
            self.Title = "3D Viewer"
            indices = System.Windows.Media.Int32Collection([System.Int32(i) for i in mesh.VertexIndicesByTri()])
            positions = System.Windows.Media.Media3D.Point3DCollection([System.Windows.Media.Media3D.Point3D(p.X, p.Y, p.Z) for p in mesh.Vertices()])
            self.meshMain.TriangleIndices = indices
            self.meshMain.Positions = positions
            
    if wpf is not None:
        input_mesh = IN[0]
        mesh_viewer = MeshViewer(input_mesh)
        mesh_viewer.Show()
    
      --
    • Exemple 2 : Visualisation de l'avancement d'un process 3D





    Pour afficher des points, cette fois-ci, nous utilisons la classe PointsVisual3D.

    Cet exemple montre comment visualiser l'avancement d'un processus 3D en générant des points successifs. Chaque point est ajouté à une scène 3D dans une fenêtre WPF via HelixViewport3D.

    La classe HelixPointVisu encapsule la création et l'affichage des points 3D Point3DCollection<PointsVisual3D> avec des propriétés telles que la taille et la couleur.
    -
    
    import clr
    import sys
    import System
    from System.Threading import Thread, ThreadStart, ApartmentState
    
    clr.AddReference('HelixToolkit')
    clr.AddReference('SharpDX.Mathematics')
    clr.AddReference('HelixToolkit.Core.Wpf')
    clr.AddReference('HelixToolkit.SharpDX.Core')
    clr.AddReference('HelixToolkit.SharpDX.Core.Wpf')
    from HelixToolkit import *
    from HelixToolkit.Wpf import *
    from HelixToolkit.Wpf.SharpDX import *
    from HelixToolkit.SharpDX.Core import *
    from SharpDX import *
    import SharpDX
    #
    clr.AddReference('ProtoGeometry')
    from Autodesk.DesignScript.Geometry import *
    import Autodesk.DesignScript.Geometry as DS
    
    clr.AddReference("System.Core")
    clr.ImportExtensions(System.Linq)
    clr.AddReference("IronPython.Wpf")
    clr.AddReference('System.Core')
    clr.AddReference('System.Xml')
    clr.AddReference('PresentationCore')
    clr.AddReference('PresentationFramework')
    clr.AddReferenceByPartialName("WindowsBase")
    
        
    from System.IO import StringReader
    from System.Windows.Markup import XamlReader, XamlWriter
    from System.Windows import Window, Application
    
    try:
        import wpf
        import time
    except ImportError:
            wpf = None
    
    import itertools
    import random
    
    class CreateProgressWindow(Window):
        LAYOUT = '''
        <Window xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation" 
            xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
            xmlns:hx="http://helix-toolkit.org/wpf"
            xmlns:hxs="http://helix-toolkit.org/wpf/SharpDX"
            Title="..."
            Height="700"
            Width="600">
            <Grid>
                <ProgressBar
                    Grid.Column="0"
                    Grid.Row="0"
                    HorizontalAlignment="Center"
                    VerticalAlignment="Top"
                    Margin="5,30,5,0"
                    Width="400"
                    Height="30"
                    x:Name="pbar" />
            <TextBlock HorizontalAlignment="Center" VerticalAlignment="Top" Margin="0,35,0,0">
                <TextBlock.Text>
                <MultiBinding StringFormat="{}Process:{0}/{1}">
                    <Binding Path="Value" ElementName="pbar" />
                    <Binding Path="Maximum" ElementName="pbar" />
                </MultiBinding>
                </TextBlock.Text>
            </TextBlock>
            <Viewbox Grid.Row="0">
                <hx:HelixViewport3D 
                    x:Name="HViewPort"
                    Height="400" 
                    Width="400" 
                    VerticalAlignment="Bottom" 
                    ZoomExtentsWhenLoaded="True"  
                    CameraRotationMode="Trackball" 
                    IsViewCubeEdgeClicksEnabled="True" 
                    ZoomAroundMouseDownPoint="True" 
                     Margin="5,35,5,2">
                    <!-- <hx:PointsVisual3D Color="Black" Size="2"/> Points="{Binding dataList}"/>-->
                </hx:HelixViewport3D>
            </Viewbox>
            </Grid>
        </Window>
        '''
        
        def __init__(self, max, title = ""):
            self.ui = wpf.LoadComponent(self, StringReader(CreateProgressWindow.LAYOUT))
            self.pbar.Maximum = max
            self.Title = title
            self.new_value = 0
            self.lst = []
            
    
        def _dispatch_updater(self):
            # ask WPF dispatcher for gui update
            self.pbar.Dispatcher.Invoke(System.Action(self._update_pbar),
                                        System.Windows.Threading.DispatcherPriority.Background)
    
    
        def _update_pbar(self):
            self.pbar.Value = self.new_value
            if self.pbar.Value == self.pbar.Maximum:
                self.Close()
        
        def update_progress(self, lst_new_geopts):
            self.new_value += 1
            curent_idx = len(self.lst) - 1
            for objpt in lst_new_geopts:
                curent_idx += 1
                self.lst.append(objpt)
                self.HViewPort.Children.Add(objpt.visuPoint);
            self._dispatch_updater()
        
        
    class HelixPointVisu():
        """
        Class for visualizing a point in a 3D space using HelixToolkit.
        
        Args:
            x (float): X coordinate of the point.
            y (float): Y coordinate of the point.
            z (float): Z coordinate of the point.
        """
        def __init__(self, x, y, z):
            pt = System.Windows.Media.Media3D.Point3D(x, y, z)
            if z > 20:
                color = System.Windows.Media.Colors.Red
            else:
                color = System.Windows.Media.Colors.Blue
            self.visuPoint = PointsVisual3D()
            self.visuPoint.Size = 3 
            self.visuPoint.Points = System.Windows.Media.Media3D.Point3DCollection([pt])
            self.visuPoint.Color = color
    
    
    def decoThread(func):
        """
        Decorator to run a function in a separate thread if the current application is DynamoSandbox.
        If not, runs the function normally.
        
        Args:
            func: Function to be wrapped.
    
        Returns:
            None
        """
        def wrapper():
            currentAppName = System.AppDomain.CurrentDomain.FriendlyName
            if currentAppName == "DynamoSandbox":
                thread = Thread(ThreadStart(func))
                thread.SetApartmentState(ApartmentState.STA)
                thread.Start()
                thread.Join()
            else:
                func()
            return None     
        return wrapper
        
    @decoThread
    def appThread():
        """
        Main function to run the application thread.
        Creates points and visualizes them using HelixToolkit.
        """
        lst_uv = list(range(800))
        pbar = CreateProgressWindow(len(lst_uv), "Points Processing (Qte : {})".format(len(lst_uv)))
        pbar.Show()
        for idx, i in enumerate(lst_uv):
            time.sleep(0.01)
            if pbar is not None:
                x, y, z = random.randint(-10,30), random.randint(-10,20), random.randint(-10,40)
                objpt = HelixPointVisu(x, y, z)
                lst_new_geopts = [objpt]
                pbar.update_progress(lst_new_geopts)
        
    appThread()
    
    Note Dynamo 2.x:

    Avec le framework .Net 4.x (Dynamo 2.x), il faut utiliser une version un peu différente. Les classes HelixToolKits à utiliser sont differentes suivant si l'on est avec .Net Core ou avec .Net Framework 4.x

            
    import clr
    import sys
    import System
    
    clr.AddReference('HelixToolkit')
    clr.AddReference('SharpDX.Mathematics')
    clr.AddReference('HelixToolkit.Wpf.SharpDx')
    from HelixToolkit import *
    from HelixToolkit.Wpf.SharpDX import *
    from HelixToolkit.Wpf.SharpDX.Core import *
    from SharpDX import *
    import SharpDX
    #
    clr.AddReference('ProtoGeometry')
    from Autodesk.DesignScript.Geometry import *
    import Autodesk.DesignScript.Geometry as DS
    
    #import Revit API
    clr.AddReference('RevitAPI')
    import Autodesk
    from Autodesk.Revit.DB import *
    import Autodesk.Revit.DB as DB
    
    #import net library
    from System.Collections.Concurrent import ConcurrentQueue
    from System import Array
    from System.Collections.Generic import List, IList, Dictionary
    
    clr.AddReference('RevitNodes')
    import Revit
    clr.ImportExtensions(Revit.GeometryConversion)
    
    clr.AddReference('DSCoreNodes')
    import DSCore
    
    #import transactionManager and DocumentManager (RevitServices is specific to Dynamo)
    clr.AddReference('RevitServices')
    import RevitServices
    from RevitServices.Persistence import DocumentManager
    from RevitServices.Transactions import TransactionManager
    
    doc = DocumentManager.Instance.CurrentDBDocument
    
    pf_path = System.Environment.GetFolderPath(System.Environment.SpecialFolder.ProgramFilesX86)
    
    sys.path.append(pf_path + '\\IronPython 2.7\\Lib')
    sys.path.append(pf_path + '\\IronPython 2.7\\DLLs')
    clr.AddReference("System.Core")
    clr.ImportExtensions(System.Linq)
    clr.AddReference("IronPython.Wpf")
    clr.AddReference('System.Core')
    clr.AddReference('System.Xml')
    clr.AddReference('PresentationCore')
    clr.AddReference('PresentationFramework')
    clr.AddReferenceByPartialName("WindowsBase")
    
    	
    from System.IO import StringReader
    from System.Windows.Markup import XamlReader, XamlWriter
    from System.Windows import Window, Application
    
    
    try:
    	import wpf
    	import time
    except ImportError:
    	wpf = None
    
    import itertools
    import random
    	
    class CreateProgressWindow(Window):
    	LAYOUT = '''
    	<window height="700" title="..." width="600" xmlns:hx="http://helix-toolkit.org/wpf/SharpDX" xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml" xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation">
    		<grid>
    			<progressbar grid.column="0" grid.row="0" height="30" horizontalalignment="Center" margin="5,30,5,0" verticalalignment="Top" width="400" x:name="pbar">
    		<textblock horizontalalignment="Center" margin="0,35,0,0" verticalalignment="Top">
    			<textblock .text="">
    			<multibinding stringformat="{}Process:{0}/{1}">
    				<binding elementname="pbar" path="Value">
    				<binding elementname="pbar" path="Maximum">
    			</binding></binding></multibinding>
    			</textblock>
    		</textblock>
    		<viewbox grid.row="0">
    			<hx:viewport3dx backgroundcolor="#FF88AACD" camerarotationmode="Trackball" height="400" isviewcubeedgeclicksenabled="True" margin="5,35,5,2" verticalalignment="Bottom" width="400" zoomaroundmousedownpoint="True" zoomextentswhenloaded="True">
    				<hx:viewport3dx .effectsmanager="">
    					<hx:defaulteffectsmanager>
    				</hx:defaulteffectsmanager></hx:viewport3dx>
    				<hx:viewport3dx .camera="">
    					<hx:perspectivecamera lookdirection="0,0,-10" position="0,0,0" updirection="0,1,0">
    				</hx:perspectivecamera></hx:viewport3dx>
    				<hx:ambientlight3d>
    				<hx:pointgeometrymodel3d color="{x:Static Colors.White}" size="2 2" x:name="points">
    			</hx:pointgeometrymodel3d></hx:ambientlight3d></hx:viewport3dx>
    		</viewbox>
    		</progressbar></grid>
    	</window>
    	'''
    	
    	def __init__(self, max, title = ""):
    		self.ui = wpf.LoadComponent(self, StringReader(CreateProgressWindow.LAYOUT))
    		self.pbar.Maximum = max
    		self.Title = title
    		self.new_value = 0
    		self.vc = Vector3Collection()
    		self.id = IntCollection()
    		self.colors = Color4Collection()
    
    		
    
    	def _dispatch_updater(self):
    		# ask WPF dispatcher for gui update
    		self.pbar.Dispatcher.Invoke(System.Action(self._update_pbar),
    									System.Windows.Threading.DispatcherPriority.Background)
    
    
    	def _update_pbar(self):
    		self.pbar.Value = self.new_value
    		if self.pbar.Value == self.pbar.Maximum:
    			self.Close()
    	
    	def update_progress(self, lst_new_geopts):
    		self.new_value += 1
    		curent_idx = self.id.Count - 1
    		for objpt in lst_new_geopts:
    			curent_idx += 1
    			self.vc.Add(objpt.vector)
    			self.colors.Add(objpt.color)
    			self.id.Add(curent_idx)
    
    		self.Geometry = PointGeometry3D()
    		self.Geometry.Positions = self.vc
    		self.Geometry.Indices = self.id
    		self.Geometry.Colors = self.colors
    		self.points.Geometry = self.Geometry
    			
    		self._dispatch_updater()
    
    lst_uv = list(range(300))
    if wpf is not None:
    	pbar = CreateProgressWindow(len(lst_uv), "Points Processing (Qte : {})".format(len(lst_uv)))
    	pbar.Show()
    else:
    	pbar = None
    	
    class HelixPoint():
    	def __init__(self, x, y, z, r, g, b):
    		self.vector = SharpDX.Vector3(x, y, z)
    		self.color = SharpDX.Color4(float(r), float(g), float(b), 200.0)
    
    rgb_values = [[255, 0, 0], [0, 255, 0], [0, 0, 255]]
    for idx, i in enumerate(lst_uv):
    	time.sleep(0.01)
    	if pbar is not None:
    		x, y, z = random.randint(-10,30), random.randint(-10,20), random.randint(-10,10)
    		r, g, b = random.choice(rgb_values)
    		objpt = HelixPoint(x, y, z, r, g, b)
    		lst_new_geopts = [objpt]
    		pbar.update_progress(lst_new_geopts)        
    
    -

    Voici un exemple de visualisation dans Dynamo de l'avancement d'un "scan 3D" (via ReferenceIntersector + la bibliothèque helixtoolkit wpf ) dans le but d'obtenir l'enveloppe d'un bâtiment (ici une maquette liée).






    • Ressources




    0 commentaires:

    Enregistrer un commentaire