2022-04-07 18:46:57 +02:00

510 lines
21 KiB
Plaintext

{
"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",
"<br>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 i<helper.strips_numA:\n",
" points = helper.paths_flatten[0][i] * visual_scaling\n",
" strips_actor.append(plot.add_lines(lines_from_points(points), color='white', width=3))\n",
" if i<helper.strips_numB:\n",
" points = helper.paths_flatten[1][i] * visual_scaling\n",
" strips_actor.append(plot.add_lines(lines_from_points(points), color='yellow', width=3))\n",
" # Board boundary\n",
" points = np.array([[0.,0.,0.],[board_length*visual_scaling,0.,0.],[board_length*visual_scaling, board_width*visual_scaling,0.],[0., board_width*visual_scaling,0],[0.,0.,0.]])\n",
" strips_actor.append(plot.add_lines(lines_from_points(points), color='red', width=3))\n",
" \n",
"def callback_save_indexes():\n",
" file = open(filename + \"_indexes.txt\", 'w')\n",
" for i in range(len(tracer.samples_indexes)):\n",
" for idx in tracer.samples_indexes[i]:\n",
" if i==0: \n",
" file.write(\"A\"+str(idx))\n",
" elif i==1:\n",
" file.write(\"B\"+str(idx))\n",
" file.write('\\n')\n",
" file.close()\n",
"\n",
"def callback_load_indexes():\n",
" indexes = np.loadtxt(filename + \"_indexes.txt\", dtype=str)\n",
" old_flagA = tracer.flagA\n",
" old_flagB = tracer.flagB\n",
"\n",
" for data in indexes:\n",
" pid = int(data[1:])\n",
"\n",
" tracer.flagA = True if data[0] == \"A\" else False\n",
" tracer.flagB = True if data[0] == \"B\" else False\n",
"\n",
" callback_picking(mesh,pid)\n",
"\n",
" tracer.flagA = old_flagA\n",
" tracer.flagB = old_flagB\n",
"\n",
"def lines_from_points(points):\n",
" cells = np.empty((len(points)-1, 2), dtype=np.int_)\n",
" cells[:,0] = np.arange(0, len(points)-1, dtype=np.int_)\n",
" cells[:,1] = np.arange(1, len(points), dtype=np.int_)\n",
" cells = cells.flatten()\n",
" return np.array([points[i] for i in cells])\n",
"\n",
"def callback_picking(mesh, pid, iterative_sampling=None):\n",
"\n",
" if(iterative_sampling==None):\n",
" iterative_sampling = iter_sampling\n",
"\n",
" old_flagA = tracer.flagA\n",
" old_flagB = tracer.flagB\n",
"\n",
" # Generate first family of asymptotic curves\n",
" if tracer.flagA:\n",
" if pid in pathA_actor.keys():\n",
" remove_pathA(pid)\n",
" else:\n",
" samples = add_pathA(pid)\n",
"\n",
" if iterative_sampling:\n",
" for pt in samples:\n",
" idx = tracer.mesh.kd_tree.query(pt)[1]\n",
" tracer.flagA = False\n",
" tracer.flagB = True\n",
" callback_picking(mesh, idx, iterative_sampling=False)\n",
"\n",
" tracer.flagA = old_flagA\n",
" tracer.flagB = old_flagB\n",
"\n",
" # Generate second family of asymptotic curves\n",
" if tracer.flagB:\n",
" if pid in pathB_actor.keys():\n",
" remove_pathB(pid)\n",
" else:\n",
" samples = add_pathB(pid)\n",
"\n",
" if iterative_sampling:\n",
" for pt in samples:\n",
" idx = tracer.mesh.kd_tree.query(pt)[1]\n",
" tracer.flagA = True\n",
" tracer.flagB = False\n",
" callback_picking(mesh, idx, iterative_sampling=False)\n",
"\n",
" tracer.flagA = old_flagA\n",
" tracer.flagB = old_flagB\n",
" \n",
" add_or_delete_sample_point(pid)\n",
" \n",
"def callback_save_svg():\n",
" cutting_color = \"red\"\n",
" engraving_color = \"black\"\n",
" font_size = 0.4\n",
" if helper.flag_flatten==False:\n",
" helper.generate_flatten_network()\n",
" helper.generate_svg_file(filename + \"_cutting.svg\", font_size, cutting_color, engraving_color, board_length, board_width)\n",
"\n",
"def callback_width(value):\n",
" helper.strips_width = value\n",
" callback_flatten()\n",
"\n",
"def callback_board_width(value):\n",
" global board_width\n",
" board_width = value\n",
" callback_flatten()\n",
"\n",
"def callback_thickness(value):\n",
" helper.strips_thickness = value\n",
" callback_flatten()\n",
"\n",
"def callback_length(value):\n",
" helper.scale_length = value\n",
" callback_flatten()\n",
"\n",
"def callback_spacing(value):\n",
" helper.strips_spacing = value\n",
" callback_flatten()\n",
"\n",
"def callback_board_length(value):\n",
" global board_length\n",
" board_length = value\n",
" callback_flatten()\n",
"\n",
"def callback_remove_labels():\n",
" plot.remove_actor(labels_actor)\n",
" labels_actor.clear()\n",
"\n",
"def callback_add_all_labels():\n",
" callback_add_labels(True, True)\n",
"\n",
"def callback_add_labelsA():\n",
" callback_add_labels(True, False)\n",
"\n",
"def callback_add_labelsB():\n",
" callback_add_labels(False, True)\n",
" \n",
"def callback_add_labels(labelsA, labelsB):\n",
" callback_remove_labels()\n",
"\n",
" if len(tracer.paths_indexes[0])>0 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 <K> for saving indexes, <L> for loading indexes or <O> to save the curve network model.\\n\"\n",
"msg += \"Press <I> for computing intersections, <J> for generating the flatten strips and <H> for saving the laser-cutting file.\\n\"\n",
"msg += \"Press <M> for hiding labels, <N> for showing all labels, <U> for showing A labels or <Y> 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
}