============================== Demo Nodal Tree ============================== In this demo, we will create a nodal tree using the ``Tree`` module in vSCAD. Creating a project ------------------- vSCAD uses a preset file structure to organize projects. After installing the package, you can create a new project by running the following command: .. code-block:: console $ python3 ~/path/to/vSCAD/src/vSCAD/io/new_project.py This will create a new project in the current directory. The project directory will contain: 1. `vscad.py`: The main script for generating the 3D model. 2. `image_data/`: A directory for storing the point and diameter data. 3. `scad/`: A directory for storing the OpenSCAD files. 4. `scad-stl/`: A directory for storing the generated 3D models generated from OpenSCAD In this demo, we will not use the ``image_data`` directory. Writing the Tree ------------------ Similar to the binary tree, we create a ``NodalTree`` object that contains ``branches``. However, we must provide a list of nodes, or poits, diameters, and a connectivity matrix that defines how the nodes are connected through branches. We will describe the function of each of these components below. - Connectivity Matrix: A matrix that defines how the nodes (points) are connected. The matrix is of the form `[[0, 1], [1, 2], [2, 3]]`, where each row defines a connection between two nodes. The row index of the connectivity matrix corresponds to the branch index. The elements of the matrix are are the node indices. For example, the first row `[0, 1]` defines a connection between nodes 0 and 1. - Points: A list of points that define the nodes of the tree. Each point is a tuple of the form `(x, y, z)`. - Diameters: A list of diameters for each branch. The diameter is a scalar value. Each of these arrays must be a numpy array before being passed to the ``NodalTree`` object. Here, we will create a simple nodal tree with 4 nodes and 3 branches. .. code-block:: python # Define the points, diameters, and connectivity of the tree points = np.array([[0.0, 0.0, 0.0], [0.0, 1.0, 0.0], [1.0, 2.0, 0.0], [-0.5, 2.0, 0.0]]) diameters = np.array([0.05, 0.05, 0.05]) connectivity = np.array([[0, 1], [1, 2], [1, 3]]) We now create a ``NodalTree`` object using the following code: .. code-block:: python nodal_tree = vs.Tree.NodalTree(points=points, diameters=diameters, connectivity=connectivity, num_points=5) We can specify the number of points in each branch by using the `num_points` argument. Writing the Vessels ---------------------- vSCAD uses the ``Vessel`` object as the main object for generating 3D models. In order to create a 3D model of the tree we just generated, we need to convert the tree object into a vessel object. ``Tree`` objects contain ``branch`` subobjects that contain the points and diameters of each branch. We can convert the ``branch`` subobjects into vessel objects using a for loop: .. code-block:: python branches = tree.draw_tree() # Create a vessel object for each branch vessel_object = [] for branch in branches: vessel_name = f'branch_{branches.index(branch)}' vessel = vs.Vessel(name=vessel_name) vessel.set_scale_factor(scale_factor=1.0) vessel.set_path(branch.points) vessel.set_diameters(branch.diameters) vessel.interpolate_paths(n=3) vessel.get_direction_vectors() vessel.get_euler_angles() vessel_object.append(vessel) Here, we've given the data within the ``branch`` object to a new ``vessel`` object. We give each ``vessel`` object a unique name and append it to a list. We then interpolate the paths, get the direction vectors, and get the Euler angles used in OpenSCAD. Writing the OpenSCAD file --------------------------- We can now write the OpenSCAD file. We will import the ``Vessel`` objects we created to the OpenSCAD file. We will also import lofting modules: .. code-block:: python scad_file = vs.SCADFile('vessel.scad') scad_file.modules.import_circle_at() scad_file.modules.import_loft_path() for object in vessel_object: scad_file.import_vessel(object) Next, we need to loft each vessel and merge their geometries: .. code-block:: python scad_file.modules.start_union() for object in vessel_object: scad_file.loft_path(object) Finally, we can write the OpenSCAD file: .. code-block:: python scad_file.modules.end_union() scad_file.write_scad_file('nodal_tree.scad') The full script ----------------------- .. code-block:: python import numpy as np import vSCAD as vs scad_file = 'demo_nodal_tree.scad' # Scad file written at scad/demo_nodal_tree.scad # ---- Draw a nodal tree with 4 levels of recursion ---- # # Define the points, diameters, and connectivity of the tree points = np.array([[0.0, 0.0, 0.0], [0.0, 1.0, 0.0], [1.0, 2.0, 0.0], [-0.5, 2.0, 0.0]]) # include end diameter of final branch diameters = np.array([0.05, 0.05, 0.05, 0.05]) connectivity = np.array([[0, 1], [1, 2], [1, 3]]) # ---- Create the NodeTree object ---- # tree = vs.NodalTree(connectivity, points, diameters, num_points=5) branches = tree.draw_tree() # Create a vessel object for each branch vessel_object = [] for branch in branches: vessel_name = f'branch_{branches.index(branch)}' vessel = vs.Vessel(name=vessel_name) vessel.set_scale_factor(scale_factor=1.0) vessel.set_path(branch.points) vessel.set_diameters(branch.diameters) vessel.interpolate_paths(n=3) vessel.get_direction_vectors() vessel.get_euler_angles() vessel_object.append(vessel) # ---- Prepare OpenSCAD file ---- # scad_file = vs.SCADFile('vessel.scad') # ---- Import OpenSCAD functions ---- # scad_file.modules.import_circle_at() scad_file.modules.import_loft_path() # ---- Import vessel data ---- # for object in vessel_object: scad_file.import_vessel(object) # ---- Write OpenSCAD code ---- # scad_file.modules.start_union() for object in vessel_object: scad_file.modules.function_loft_path(object) scad_file.modules.end_union() scad_file.write_stl('nodal_tree.stl')