{ "cells": [ { "cell_type": "markdown", "metadata": {}, "source": [ "# Interactive Design Tool for Asymptotic Grid-Shells " ] }, { "cell_type": "code", "execution_count": 1, "metadata": {}, "outputs": [], "source": [ "import igl\n", "import pyvista as pv\n", "import numpy as np\n", "\n", "import sys\n", "sys.path.append('../src')\n", "\n", "import importlib, fabrication_helper, tracer_tool\n", "importlib.reload(fabrication_helper)\n", "importlib.reload(tracer_tool)\n", "from tracer_tool import AsymptoticTracer\n", "from fabrication_helper import FabricationHelper" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "### Load Mesh\n", "Import your own mesh as an .obj.\n", "
Note that the filename should not contain the file extension because it will be used as the basis for saving further data. " ] }, { "cell_type": "code", "execution_count": 2, "metadata": {}, "outputs": [], "source": [ "filename = \"../data/final\"" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "### Initialize Interface\n", "Don't modify this part. All parameters can be tuned with the interface." ] }, { "cell_type": "code", "execution_count": null, "metadata": {}, "outputs": [ { "name": "stdout", "output_type": "stream", "text": [ "Hit Boundary\n", "Duplicate Point\n", "Hit Boundary\n", "Hit Boundary\n", "Hit Boundary\n", "Hit Boundary\n", "Hit Boundary\n", "Hit Boundary\n", "Duplicate Point\n", "Hit Boundary\n", "Hit Boundary\n", "Hit Boundary\n", "Hit Boundary\n", "Hit Boundary\n", "Hit Boundary\n", "Hit Boundary\n", "Hit Boundary\n", "Hit Boundary\n", "Hit Boundary\n", "Hit Boundary\n", "Duplicate Point\n", "Hit Boundary\n", "Hit Boundary\n", "Hit Boundary\n", "Hit Boundary\n", "Hit Boundary\n", "Hit Boundary\n", "Hit Boundary\n", "Hit Boundary\n", "Hit Boundary\n", "Hit Boundary\n" ] } ], "source": [ "# Fabrication parameters in cm\n", "strips_scale = 3\n", "strips_width = 2\n", "strips_spacing = 0.2\n", "strips_thickness = 0.3\n", "board_width = 60\n", "board_length = 100\n", "# Creation parameters\n", "sampling_distance = 1.0\n", "num_neighbors = 4\n", "iter_sampling = False\n", "# Visualisation\n", "strips_actor = []\n", "points_actor = []\n", "labels_actor = []\n", "samples_actor = {}\n", "pathA_actor = {}\n", "pathA_indexes = {}\n", "pathB_actor = {}\n", "pathB_indexes = {}\n", "intersections = np.empty((0,3), float)\n", "visual_scaling = 0.2\n", "\n", "# Initialise tracer\n", "mesh = pv.read(filename + \".obj\")\n", "v,f = igl.read_triangle_mesh(filename + \".obj\")\n", "tracer = AsymptoticTracer(filename + \".obj\")\n", "helper = FabricationHelper(strips_width, strips_thickness, strips_spacing, strips_scale)\n", "\n", "plot = pv.Plotter(notebook=0)\n", "\n", "def add_pathA(pid):\n", " points, samples = tracer.generate_asymptotic_path(pid, True, num_neighbors, sampling_distance) \n", "\n", " if len(points)> 1:\n", " pathA_actor[pid] = plot.add_mesh(pv.Spline(points, 400), color='white', line_width=10, pickable=False)\n", " pathA_indexes[pid] = tracer.num_pathsA()-1\n", " tracer.samples_indexes[0].append(pid)\n", " return samples\n", "\n", "def add_pathB(pid):\n", " points, samples = tracer.generate_asymptotic_path(pid, False, num_neighbors, sampling_distance) \n", " if len(points)> 1:\n", " pathB_actor[pid] = plot.add_mesh(pv.Spline(points, 400), color='yellow', line_width=10, pickable=False)\n", " pathB_indexes[pid] = tracer.num_pathsB()-1\n", " tracer.samples_indexes[1].append(pid) \n", " return samples\n", "\n", "def remove_pathA(pid):\n", " plot.remove_actor(pathA_actor[pid])\n", " tracer.delete_path(pathA_indexes[pid], True)\n", " del pathA_actor[pid]\n", " #Update indexes\n", " update_indexes(pid, True)\n", "\n", "def remove_pathB(pid):\n", " plot.remove_actor(pathB_actor[pid])\n", " tracer.delete_path(pathB_indexes[pid], False)\n", " del pathB_actor[pid]\n", " #Update indexes\n", " update_indexes(pid, False)\n", "\n", "def add_or_delete_sample_point(pid):\n", " orig = v[pid]\n", "\n", " if pid not in pathB_actor.keys() and pid not in pathA_actor.keys():\n", " if pid in samples_actor.keys():\n", " plot.remove_actor(samples_actor[pid])\n", " del samples_actor[pid]\n", " clean_intersections()\n", " else:\n", " if pid not in samples_actor.keys():\n", " color = 'blue'\n", " if tracer.flagA and not tracer.flagB:\n", " color = 'white'\n", " elif tracer.flagB and not tracer.flagA:\n", " color = 'yellow'\n", "\n", " samples_actor[pid] = plot.add_points(np.array(orig), color=color, render_points_as_spheres=True, point_size=20.0, pickable=False)\n", " clean_intersections()\n", " else:\n", " plot.remove_actor(samples_actor[pid])\n", " color = 'blue'\n", " if pid not in pathB_actor.keys() and pid in pathA_actor.keys():\n", " color = 'white'\n", " elif pid in pathB_actor.keys() and pid not in pathA_actor.keys():\n", " color = 'yellow'\n", " samples_actor[pid] = plot.add_points(np.array(orig), color=color, render_points_as_spheres=True, point_size=20.0, pickable=False)\n", " clean_intersections()\n", "\n", "def update_indexes(path_index, first_principal_direction):\n", " if first_principal_direction:\n", " if path_index in pathA_indexes.keys():\n", " del pathA_indexes[path_index]\n", " idx = 0\n", " for key in pathA_indexes:\n", " pathA_indexes[key] = idx\n", " idx+=1\n", " else:\n", " if path_index in pathB_indexes.keys():\n", " del pathB_indexes[path_index]\n", " idx = 0\n", " for key in pathB_indexes:\n", " pathB_indexes[key] = idx\n", " idx+=1\n", "\n", "def callback_first_family(value):\n", " tracer.flagA = value\n", "\n", "def callback_second_family(value):\n", " tracer.flagB = value\n", "\n", "def clean_intersections():\n", " if len(points_actor)>0:\n", " callback_remove_labels()\n", " plot.remove_actor(points_actor)\n", " points_actor.clear()\n", " labels_actor.clear()\n", " tracer.flag_intersections = False\n", "\n", "def callback_intersection():\n", " clean_intersections() \n", " global intersections\n", " intersections = np.empty((0,3), float)\n", " intersections = np.append(intersections, tracer.generate_intersection_network(), axis=0)\n", " if len(tracer.intersection_points)>0:\n", " points_actor.append(plot.add_points(tracer.intersection_points, color='red',point_size=13.0, pickable=False)) \n", "\n", "def callback_flatten():\n", " if not tracer.flag_intersections:\n", " callback_intersection()\n", " \n", " helper.generate_flatten_network(tracer)\n", " \n", " strips_num = helper.strips_numA if helper.strips_numA > helper.strips_numB else helper.strips_numB\n", " plot.remove_actor(strips_actor)\n", " strips_actor.clear()\n", " if strips_num:\n", " for i in range(strips_num):\n", " if i0 and labelsA:\n", " labels = np.core.defchararray.add('A', np.arange(len(tracer.paths_indexes[0])).astype(str))\n", " indexes = [idx[:1][0] for idx in tracer.paths_indexes[0]]\n", " labels_actor.append(plot.add_point_labels(tracer.paths[0][indexes], labels, font_size=22, always_visible=True, show_points=False))\n", "\n", " indexes = np.unique(np.array([item for sublist in tracer.intersections[0] for item in sublist[:,2]], int).flatten())\n", " labels = np.core.defchararray.add('c', indexes.astype(str))\n", " labels_actor.append(plot.add_point_labels(tracer.intersection_points[indexes], labels, bold=False, font_size=18, always_visible=True, show_points=False))\n", "\n", " if len(tracer.paths_indexes[1])>0 and labelsB:\n", " labels = np.core.defchararray.add('B', np.arange(len(tracer.paths_indexes[1])).astype(str))\n", " indexes = [idx[:1][0] for idx in tracer.paths_indexes[1]]\n", " labels_actor.append(plot.add_point_labels(tracer.paths[1][indexes], labels, font_size=22, always_visible=True, show_points=False))\n", "\n", " indexes = np.unique(np.array([item for sublist in tracer.intersections[1] for item in sublist[:,2]], int).flatten())\n", " labels = np.core.defchararray.add('c', indexes.astype(str))\n", " labels_actor.append(plot.add_point_labels(tracer.intersection_points[indexes], labels, bold=False, font_size=18, always_visible=True, show_points=False))\n", "\n", "def callback_save_network():\n", " file = open(filename + \"_rhino.txt\", 'w')\n", " for i in range(2):\n", " label = \"A\"\n", " if i==1:\n", " label =\"B\"\n", "\n", " # positions\n", " for j in range(len(tracer.paths_indexes[i])):\n", " path = tracer.paths_indexes[i][j]\n", " for idx in path:\n", " pt = tracer.paths[i][idx]\n", " file.write(label + str(j)+ \"_\" +str(pt[0]) + \",\" + str(pt[1]) + \",\" +str(pt[2]) + \"\\n\")\n", "\n", " # Intersections \n", " for i in range( len(tracer.intersection_points) ):\n", " pt = tracer.intersection_points[i]\n", " file.write(\"C\" + str(i) + \"_\" +str(pt[0]) + \",\" + str(pt[1]) + \",\" +str(pt[2]) + \"\\n\")\n", " file.close()\n", "\n", "def callback_sampling_distance(value):\n", " global sampling_distance\n", " sampling_distance = value\n", "\n", "def callback_iterative_sampling(value):\n", " global iter_sampling\n", " iter_sampling = value\n", "\n", "plot.add_mesh(mesh, show_edges=True)\n", "plot.add_axes()\n", "msg = \"Press for saving indexes, for loading indexes or to save the curve network model.\\n\"\n", "msg += \"Press for computing intersections, for generating the flatten strips and for saving the laser-cutting file.\\n\"\n", "msg += \"Press for hiding labels, for showing all labels, for showing A labels or for showing B labels.\\n\"\n", "plot.add_text(msg, position='lower_right', font_size=12, color=None, font=None, shadow=False, name=None, viewport=False)\n", "plot.add_checkbox_button_widget(callback_first_family, value=tracer.flagA, position=(10, 200.0), size=40, border_size=1, color_on='white', color_off='grey', background_color='red')\n", "plot.add_checkbox_button_widget(callback_second_family, value=tracer.flagB, position=(10, 300.0), size=40, border_size=1, color_on='yellow', color_off='grey', background_color='red')\n", "plot.add_checkbox_button_widget(callback_iterative_sampling, value=iter_sampling, position=(10, 400.0), size=40, border_size=1, color_on='green', color_off='grey', background_color='red')\n", "plot.add_text(\"First Family\", position=(80.0, 200.0), font_size=12, color=None, font=None, shadow=False, name=None, viewport=False)\n", "plot.add_text(\"Second Family\", position=(80, 300.0), font_size=12, color=None, font=None, shadow=False, name=None, viewport=False)\n", "plot.add_text(\"Iterative sampling\", position=(80, 400.0), font_size=12, color=None, font=None, shadow=False, name=None, viewport=False)\n", "plot.add_key_event('i', callback_intersection)\n", "plot.add_key_event('j', callback_flatten)\n", "plot.add_key_event('k', callback_save_indexes)\n", "plot.add_key_event('l', callback_load_indexes)\n", "plot.add_key_event('h', callback_save_svg)\n", "plot.add_key_event('m', callback_remove_labels)\n", "plot.add_key_event('n', callback_add_all_labels)\n", "plot.add_key_event('u', callback_add_labelsA)\n", "plot.add_key_event('y', callback_add_labelsB)\n", "plot.add_key_event('o', callback_save_network)\n", "plot.enable_point_picking(callback=callback_picking, show_message=True, color='pink', point_size=10, use_mesh=True, show_point=True)\n", "plot.add_slider_widget(callback_width, [0.1, 5.0], value=strips_width, title=\"Strip Width (cm)\", pointa=(.83, .15), pointb=(.98, .15), title_height=0.02, fmt=\"%0.1f\", style='modern')\n", "plot.add_slider_widget(callback_thickness, [0.1, 1], value=strips_thickness, title=\"Strip Thickness (cm)\", pointa=(.67, .15), pointb=(.82, .15), title_height=0.02, fmt=\"%0.2f\", style='modern')\n", "plot.add_slider_widget(callback_length, [1, 10], value=strips_scale, title=\"Scale Strip Length\", pointa=(.51, .15), pointb=(.66, .15), title_height=0.02, fmt=\"%0.1f\", style='modern')\n", "plot.add_slider_widget(callback_spacing, [0., 0.5], value=strips_spacing, title=\"Strip spacing\", pointa=(.51, .88), pointb=(.66, .88), title_height=0.02, fmt=\"%0.1f\", style='modern')\n", "plot.add_slider_widget(callback_board_width, [10, 100], value=board_width, title=\"Board width (cm)\", pointa=(.67, .88), pointb=(.82, .88), title_height=0.02, fmt=\"%0.0f\", style='modern')\n", "plot.add_slider_widget(callback_board_length, [10, 100], value=board_length, title=\"Board length (cm)\", pointa=(.83, .88), pointb=(.98, .88), title_height=0.02, fmt=\"%0.0f\", style='modern')\n", "plot.add_slider_widget(callback_sampling_distance, [0.1, 2.0], value=sampling_distance, title=\"Sampling distance\", pointa=(.005, .88), pointb=(.16, .88), title_height=0.02, fmt=\"%0.1f\", style='modern')\n", "plot.show(\"Asymptotic GridShell\")\n" ] }, { "cell_type": "code", "execution_count": null, "metadata": {}, "outputs": [], "source": [] }, { "cell_type": "code", "execution_count": null, "metadata": {}, "outputs": [], "source": [] }, { "cell_type": "code", "execution_count": null, "metadata": {}, "outputs": [], "source": [] }, { "cell_type": "code", "execution_count": null, "metadata": {}, "outputs": [], "source": [] } ], "metadata": { "interpreter": { "hash": "294c9a689e437aa1430c5ffd3595515046a5cee981399d48a2291c079c814bc8" }, "kernelspec": { "display_name": "Python 3 (ipykernel)", "language": "python", "name": "python3" }, "language_info": { "codemirror_mode": { "name": "ipython", "version": 3 }, "file_extension": ".py", "mimetype": "text/x-python", "name": "python", "nbconvert_exporter": "python", "pygments_lexer": "ipython3", "version": "3.9.7" } }, "nbformat": 4, "nbformat_minor": 4 }