{ "cells": [ { "cell_type": "markdown", "metadata": {}, "source": [ "# Thick line geometry" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "Three.js has some example code for thick lines via an instance-based geometry. Since WebGL does not guarantee support for line thickness greater than 1 for GL lines, pytheejs includes these objects by default." ] }, { "cell_type": "code", "execution_count": 1, "metadata": {}, "outputs": [], "source": [ "from pythreejs import *\n", "from IPython.display import display\n", "from ipywidgets import VBox, HBox, Checkbox, jslink\n", "import numpy as np" ] }, { "cell_type": "code", "execution_count": 2, "metadata": {}, "outputs": [], "source": [ "# Reduce repo churn for examples with embedded state:\n", "from pythreejs._example_helper import use_example_model_ids\n", "use_example_model_ids()" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "First, let's set up a normal GL line for comparison. Depending on your OS/browser combination, this might not respect the `linewidth` argument. E.g. most browsers on Windows does not support linewidth greater than 1, due to lack of support in the ANGLE library that most browsers rely on." ] }, { "cell_type": "code", "execution_count": 3, "metadata": {}, "outputs": [ { "data": { "application/vnd.jupyter.widget-view+json": { "model_id": "pythree_example_model_006", "version_major": 2, "version_minor": 0 }, "text/plain": [ "Preview(child=LineSegments(geometry=BufferGeometry(attributes={'position': BufferAttribute(array=array([[0., 0…" ] }, "metadata": {}, "output_type": "display_data" } ], "source": [ "g1 = BufferGeometry(\n", " attributes={\n", " 'position': BufferAttribute(np.array([\n", " [0, 0, 0], [1, 1, 1],\n", " [2, 2, 2], [4, 4, 4]\n", " ], dtype=np.float32), normalized=False),\n", " 'color': BufferAttribute(np.array([\n", " [1, 0, 0], [1, 0, 0],\n", " [0, 1, 0], [0, 0, 1]\n", " ], dtype=np.float32), normalized=False),\n", " },\n", ")\n", "m1 = LineBasicMaterial(vertexColors='VertexColors', linewidth=10)\n", "line1 = LineSegments(g1, m1)\n", "line1" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "Next, we'll set up two variants of the instance geometry based lines. One with a single color, and one with vertex colors." ] }, { "cell_type": "code", "execution_count": 4, "metadata": {}, "outputs": [ { "data": { "application/vnd.jupyter.widget-view+json": { "model_id": "pythree_example_model_012", "version_major": 2, "version_minor": 0 }, "text/plain": [ "Preview(child=LineSegments2(geometry=LineSegmentsGeometry(positions=array([[[0., 0., 0.],\n", " [1., 1., 1.]…" ] }, "metadata": {}, "output_type": "display_data" } ], "source": [ "g2 = LineSegmentsGeometry(\n", " positions=[\n", " [[0, 0, 0], [1, 1, 1]],\n", " [[2, 2, 2], [4, 4, 4]]\n", " ],\n", ")\n", "m2 = LineMaterial(linewidth=10, color='cyan')\n", "line2 = LineSegments2(g2, m2)\n", "line2" ] }, { "cell_type": "code", "execution_count": 5, "metadata": {}, "outputs": [ { "data": { "application/vnd.jupyter.widget-view+json": { "model_id": "pythree_example_model_018", "version_major": 2, "version_minor": 0 }, "text/plain": [ "Preview(child=LineSegments2(geometry=LineSegmentsGeometry(colors=array([[[1., 0., 0.],\n", " [1., 0., 0.]],\n", "…" ] }, "metadata": {}, "output_type": "display_data" } ], "source": [ "g3 = LineSegmentsGeometry(\n", " positions=[\n", " [[0, 0, 0], [1, 1, 1]],\n", " [[2, 2, 2], [4, 4, 4]]\n", " ],\n", " colors=[\n", " [[1, 0, 0], [1, 0, 0]],\n", " [[0, 1, 0], [0, 0, 1]]\n", " ],\n", ")\n", "m3 = LineMaterial(linewidth=10, vertexColors='VertexColors')\n", "line3 = LineSegments2(g3, m3)\n", "line3" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "Finally, let's set up a simple scene and renderer, and add some checkboxes so we can toggle the visibility of the different lines." ] }, { "cell_type": "code", "execution_count": 6, "metadata": {}, "outputs": [], "source": [ "view_width = 600\n", "view_height = 400\n", "camera = PerspectiveCamera(position=[10, 0, 0], aspect=view_width/view_height)\n", "key_light = DirectionalLight(position=[0, 10, 10])\n", "ambient_light = AmbientLight()" ] }, { "cell_type": "code", "execution_count": 7, "metadata": {}, "outputs": [], "source": [ "scene = Scene(children=[line1, line2, line3, camera, key_light, ambient_light])\n", "controller = OrbitControls(controlling=camera, screenSpacePanning=False)\n", "renderer = Renderer(camera=camera, scene=scene, controls=[controller],\n", " width=view_width, height=view_height)" ] }, { "cell_type": "code", "execution_count": 8, "metadata": {}, "outputs": [ { "data": { "application/vnd.jupyter.widget-view+json": { "model_id": "pythree_example_model_046", "version_major": 2, "version_minor": 0 }, "text/plain": [ "VBox(children=(Renderer(camera=PerspectiveCamera(aspect=1.5, position=(10.0, 0.0, 0.0), projectionMatrix=(1.42…" ] }, "metadata": {}, "output_type": "display_data" } ], "source": [ "chks = [\n", " Checkbox(True, description='GL line'),\n", " Checkbox(True, description='Fat line (single color)'),\n", " Checkbox(True, description='Fat line (vertex colors)'),\n", "]\n", "jslink((chks[0], 'value'), (line1, 'visible'))\n", "jslink((chks[1], 'value'), (line2, 'visible'))\n", "jslink((chks[2], 'value'), (line3, 'visible'))\n", "VBox([renderer, HBox(chks)])" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "For reference, the code below shows how you would recreate the line geometry and material from the kernel. The only significant difference is that you need to declare the render view resolution on material creation, while the included `LineMaterial` automatically sets this." ] }, { "cell_type": "code", "execution_count": 9, "metadata": {}, "outputs": [], "source": [ "# The line segment points and colors.\n", "# Each array of six is one instance/segment [x1, y1, z1, x2, y2, z2]\n", "posInstBuffer = InstancedInterleavedBuffer( np.array([\n", " [0, 0, 0, 1, 1, 1],\n", " [2, 2, 2, 4, 4, 4]\n", "], dtype=np.float32))\n", "colInstBuffer = InstancedInterleavedBuffer( np.array([\n", " [1, 0, 0, 1, 0, 0],\n", " [0, 1, 0, 0, 0, 1]\n", "], dtype=np.float32))\n", "\n", "# This uses InstancedBufferGeometry, so that the geometry is reused for each line segment\n", "lineGeo = InstancedBufferGeometry(attributes={\n", " # Helper line geometry (2x4 grid), that is instanced\n", " 'position': BufferAttribute(np.array([\n", " [ 1, 2, 0], [1, 2, 0],\n", " [-1, 1, 0], [1, 1, 0],\n", " [-1, 0, 0], [1, 0, 0],\n", " [-1, -1, 0], [1, -1, 0]\n", " ], dtype=np.float32)),\n", " 'uv': BufferAttribute(np.array([\n", " [-1, 2], [1, 2],\n", " [-1, 1], [1, 1],\n", " [-1, -1], [1, -1],\n", " [-1, -2], [1, -2]\n", " ], dtype=np.float32)),\n", " 'index': BufferAttribute(np.array([\n", " 0, 2, 1,\n", " 2, 3, 1,\n", " 2, 4, 3,\n", " 4, 5, 3,\n", " 4, 6, 5,\n", " 6, 7, 5\n", " ], dtype=np.uint8)),\n", " # The line segments are split into start/end for each instance:\n", " 'instanceStart': InterleavedBufferAttribute(posInstBuffer, 3, 0),\n", " 'instanceEnd': InterleavedBufferAttribute(posInstBuffer, 3, 3),\n", " 'instanceColorStart': InterleavedBufferAttribute(colInstBuffer, 3, 0),\n", " 'instanceColorEnd': InterleavedBufferAttribute(colInstBuffer, 3, 3),\n", "})" ] }, { "cell_type": "code", "execution_count": 10, "metadata": {}, "outputs": [], "source": [ "# The line material shader:\n", "lineMat = ShaderMaterial(\n", " vertexShader='''\n", "#include \n", "#include \n", "#include \n", "#include \n", "#include \n", "\n", "uniform float linewidth;\n", "uniform vec2 resolution;\n", "\n", "attribute vec3 instanceStart;\n", "attribute vec3 instanceEnd;\n", "\n", "attribute vec3 instanceColorStart;\n", "attribute vec3 instanceColorEnd;\n", "\n", "varying vec2 vUv;\n", "\n", "void trimSegment( const in vec4 start, inout vec4 end ) {\n", "\n", " // trim end segment so it terminates between the camera plane and the near plane\n", "\n", " // conservative estimate of the near plane\n", " float a = projectionMatrix[ 2 ][ 2 ]; // 3nd entry in 3th column\n", " float b = projectionMatrix[ 3 ][ 2 ]; // 3nd entry in 4th column\n", " float nearEstimate = - 0.5 * b / a;\n", "\n", " float alpha = ( nearEstimate - start.z ) / ( end.z - start.z );\n", "\n", " end.xyz = mix( start.xyz, end.xyz, alpha );\n", "\n", "}\n", "\n", "void main() {\n", "\n", " #ifdef USE_COLOR\n", "\n", " vColor.xyz = ( position.y < 0.5 ) ? instanceColorStart : instanceColorEnd;\n", "\n", " #endif\n", " \n", " float aspect = resolution.x / resolution.y;\n", "\n", " vUv = uv;\n", " \n", " // camera space\n", " vec4 start = modelViewMatrix * vec4( instanceStart, 1.0 );\n", " vec4 end = modelViewMatrix * vec4( instanceEnd, 1.0 );\n", "\n", " // special case for perspective projection, and segments that terminate either in, or behind, the camera plane\n", " // clearly the gpu firmware has a way of addressing this issue when projecting into ndc space\n", " // but we need to perform ndc-space calculations in the shader, so we must address this issue directly\n", " // perhaps there is a more elegant solution -- WestLangley\n", "\n", " bool perspective = ( projectionMatrix[ 2 ][ 3 ] == - 1.0 ); // 4th entry in the 3rd column\n", "\n", " if ( perspective ) {\n", "\n", " if ( start.z < 0.0 && end.z >= 0.0 ) {\n", "\n", " trimSegment( start, end );\n", "\n", " } else if ( end.z < 0.0 && start.z >= 0.0 ) {\n", "\n", " trimSegment( end, start );\n", "\n", " }\n", "\n", " }\n", "\n", " // clip space\n", " vec4 clipStart = projectionMatrix * start;\n", " vec4 clipEnd = projectionMatrix * end;\n", "\n", " // ndc space\n", " vec2 ndcStart = clipStart.xy / clipStart.w;\n", " vec2 ndcEnd = clipEnd.xy / clipEnd.w;\n", "\n", " // direction\n", " vec2 dir = ndcEnd - ndcStart;\n", "\n", " // account for clip-space aspect ratio\n", " dir.x *= aspect;\n", " dir = normalize( dir );\n", "\n", " // perpendicular to dir\n", " vec2 offset = vec2( dir.y, - dir.x );\n", "\n", " // undo aspect ratio adjustment\n", " dir.x /= aspect;\n", " offset.x /= aspect;\n", "\n", " // sign flip\n", " if ( position.x < 0.0 ) offset *= - 1.0;\n", "\n", " // endcaps\n", " if ( position.y < 0.0 ) {\n", "\n", " offset += - dir;\n", "\n", " } else if ( position.y > 1.0 ) {\n", "\n", " offset += dir;\n", "\n", " }\n", "\n", " // adjust for linewidth\n", " offset *= linewidth;\n", " \n", " // adjust for clip-space to screen-space conversion // maybe resolution should be based on viewport ...\n", " offset /= resolution.y;\n", "\n", " // select end\n", " vec4 clip = ( position.y < 0.5 ) ? clipStart : clipEnd;\n", "\n", " // back to clip space\n", " offset *= clip.w;\n", "\n", " clip.xy += offset;\n", "\n", " gl_Position = clip;\n", " \n", " vec4 mvPosition = ( position.y < 0.5 ) ? start : end; // this is an approximation\n", "\n", " #include \n", " #include \n", " #include \n", "}\n", "''',\n", " fragmentShader='''\n", "uniform vec3 diffuse;\n", "uniform float opacity;\n", "\n", "varying float vLineDistance;\n", "\n", "#include \n", "#include \n", "#include \n", "#include \n", "#include \n", "\n", "varying vec2 vUv;\n", "\n", "void main() {\n", "\n", " #include \n", "\n", "\n", " if ( abs( vUv.y ) > 1.0 ) {\n", "\n", " float a = vUv.x;\n", " float b = ( vUv.y > 0.0 ) ? vUv.y - 1.0 : vUv.y + 1.0;\n", " float len2 = a * a + b * b;\n", "\n", " if ( len2 > 1.0 ) discard;\n", "\n", " }\n", "\n", " vec4 diffuseColor = vec4( diffuse, opacity );\n", "\n", " #include \n", " #include \n", "\n", " gl_FragColor = vec4( diffuseColor.rgb, diffuseColor.a );\n", "\n", " #include \n", " #include \n", " #include \n", " #include \n", "\n", "}\n", "''',\n", " vertexColors='VertexColors',\n", " uniforms=dict(\n", " linewidth={'value': 10.0},\n", " resolution={'value': (100., 100.)},\n", " **UniformsLib['common']\n", " )\n", ")" ] }, { "cell_type": "code", "execution_count": 11, "metadata": {}, "outputs": [ { "data": { "application/vnd.jupyter.widget-view+json": { "model_id": "pythree_example_model_060", "version_major": 2, "version_minor": 0 }, "text/plain": [ "Preview(child=Mesh(geometry=InstancedBufferGeometry(attributes={'position': BufferAttribute(array=array([[ 1.,…" ] }, "metadata": {}, "output_type": "display_data" } ], "source": [ "Mesh(lineGeo, lineMat)" ] }, { "cell_type": "code", "execution_count": null, "metadata": {}, "outputs": [], "source": [] } ], "metadata": { "kernelspec": { "display_name": "Python 3", "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.6.6" }, "widgets": { "application/vnd.jupyter.widget-state+json": { "state": { "2108202c-c417-468e-a67f-4c8422eb49e2": { "model_module": "jupyter-threejs", "model_module_version": "2.0.0", "model_name": "OrthographicCameraModel", "state": { "bottom": -5, "far": 500, "left": -5, "near": 0.5, "projectionMatrix": [ 0.2, 0, 0, 0, 0, 0.2, 0, 0, 0, 0, -0.004004004004004004, 0, 0, 0, -1.002002002002002, 1 ], "right": 5, "top": 5 } }, "a0da8954-9821-487a-8da1-409e8679c9cc": { "model_module": "jupyter-threejs", "model_module_version": "2.0.0", "model_name": "DirectionalLightShadowModel", "state": { "camera": "IPY_MODEL_2108202c-c417-468e-a67f-4c8422eb49e2" } }, "dedd8574-6920-4f16-bdee-ae7fad1749b1": { "model_module": "jupyter-threejs", "model_module_version": "2.0.0", "model_name": "Object3DModel", "state": {} }, "pythree_example_model_001": { "buffers": [ { "data": "AAAAAAAAAAAAAAAAAACAPwAAgD8AAIA/AAAAQAAAAEAAAABAAACAQAAAgEAAAIBA", "encoding": "base64", "path": [ "array", "buffer" ] } ], "model_module": "jupyter-threejs", "model_module_version": "^2.0.0", "model_name": "BufferAttributeModel", "state": { "_model_module_version": "^2.0.0", "_view_module": null, "_view_module_version": "", "array": { "dtype": "float32", "shape": [ 4, 3 ] }, "version": 2 } }, "pythree_example_model_002": { "buffers": [ { "data": "AACAPwAAAAAAAAAAAACAPwAAAAAAAAAAAAAAAAAAgD8AAAAAAAAAAAAAAAAAAIA/", "encoding": "base64", "path": [ "array", "buffer" ] } ], "model_module": "jupyter-threejs", "model_module_version": "^2.0.0", "model_name": "BufferAttributeModel", "state": { "_model_module_version": "^2.0.0", "_view_module": null, "_view_module_version": "", "array": { "dtype": "float32", "shape": [ 4, 3 ] }, "version": 2 } }, "pythree_example_model_003": { "model_module": "jupyter-threejs", "model_module_version": "^2.0.0", "model_name": "BufferGeometryModel", "state": { "_model_module_version": "^2.0.0", "_view_module": null, "_view_module_version": "", "attributes": { "color": "IPY_MODEL_pythree_example_model_002", "position": "IPY_MODEL_pythree_example_model_001" } } }, "pythree_example_model_004": { "model_module": "jupyter-threejs", "model_module_version": "^2.0.0", "model_name": "LineBasicMaterialModel", "state": { "_model_module_version": "^2.0.0", "_view_module": null, "_view_module_version": "", "linewidth": 10, "vertexColors": "VertexColors" } }, "pythree_example_model_005": { "model_module": "jupyter-threejs", "model_module_version": "^2.0.0", "model_name": "LineSegmentsModel", "state": { "_model_module_version": "^2.0.0", "_view_module": null, "_view_module_version": "", "geometry": "IPY_MODEL_pythree_example_model_003", "material": "IPY_MODEL_pythree_example_model_004" } }, "pythree_example_model_006": { "model_module": "jupyter-threejs", "model_module_version": "^2.0.0", "model_name": "PreviewModel", "state": { "_alpha": false, "_model_module_version": "^2.0.0", "_view_module_version": "^2.0.0", "child": "IPY_MODEL_pythree_example_model_005", "layout": "IPY_MODEL_pythree_example_model_007", "shadowMap": "IPY_MODEL_pythree_example_model_008" } }, "pythree_example_model_007": { "model_module": "@jupyter-widgets/base", "model_module_version": "1.1.0", "model_name": "LayoutModel", "state": {} }, "pythree_example_model_008": { "model_module": "jupyter-threejs", "model_module_version": "^2.0.0", "model_name": "WebGLShadowMapModel", "state": { "_model_module_version": "^2.0.0", "_view_module": null, "_view_module_version": "" } }, "pythree_example_model_009": { "buffers": [ { "data": "AAAAAAAAAAAAAAAAAACAPwAAgD8AAIA/AAAAQAAAAEAAAABAAACAQAAAgEAAAIBA", "encoding": "base64", "path": [ "positions", "buffer" ] } ], "model_module": "jupyter-threejs", "model_module_version": "^2.0.0", "model_name": "LineSegmentsGeometryModel", "state": { "_model_module_version": "^2.0.0", "_view_module": null, "_view_module_version": "", "positions": { "dtype": "float32", "shape": [ 2, 2, 3 ] } } }, "pythree_example_model_010": { "model_module": "jupyter-threejs", "model_module_version": "^2.0.0", "model_name": "LineMaterialModel", "state": { "_model_module_version": "^2.0.0", "_view_module": null, "_view_module_version": "", "color": "cyan", "linewidth": 10 } }, "pythree_example_model_011": { "model_module": "jupyter-threejs", "model_module_version": "^2.0.0", "model_name": "LineSegments2Model", "state": { "_model_module_version": "^2.0.0", "_view_module": null, "_view_module_version": "", "geometry": "IPY_MODEL_pythree_example_model_009", "material": "IPY_MODEL_pythree_example_model_010", "morphTargetInfluences": [] } }, "pythree_example_model_012": { "model_module": "jupyter-threejs", "model_module_version": "^2.0.0", "model_name": "PreviewModel", "state": { "_alpha": false, "_model_module_version": "^2.0.0", "_view_module_version": "^2.0.0", "child": "IPY_MODEL_pythree_example_model_011", "layout": "IPY_MODEL_pythree_example_model_013", "shadowMap": "IPY_MODEL_pythree_example_model_014" } }, "pythree_example_model_013": { "model_module": "@jupyter-widgets/base", "model_module_version": "1.1.0", "model_name": "LayoutModel", "state": {} }, "pythree_example_model_014": { "model_module": "jupyter-threejs", "model_module_version": "^2.0.0", "model_name": "WebGLShadowMapModel", "state": { "_model_module_version": "^2.0.0", "_view_module": null, "_view_module_version": "" } }, "pythree_example_model_015": { "buffers": [ { "data": "AACAPwAAAAAAAAAAAACAPwAAAAAAAAAAAAAAAAAAgD8AAAAAAAAAAAAAAAAAAIA/", "encoding": "base64", "path": [ "colors", "buffer" ] }, { "data": "AAAAAAAAAAAAAAAAAACAPwAAgD8AAIA/AAAAQAAAAEAAAABAAACAQAAAgEAAAIBA", "encoding": "base64", "path": [ "positions", "buffer" ] } ], "model_module": "jupyter-threejs", "model_module_version": "^2.0.0", "model_name": "LineSegmentsGeometryModel", "state": { "_model_module_version": "^2.0.0", "_view_module": null, "_view_module_version": "", "colors": { "dtype": "float32", "shape": [ 2, 2, 3 ] }, "positions": { "dtype": "float32", "shape": [ 2, 2, 3 ] } } }, "pythree_example_model_016": { "model_module": "jupyter-threejs", "model_module_version": "^2.0.0", "model_name": "LineMaterialModel", "state": { "_model_module_version": "^2.0.0", "_view_module": null, "_view_module_version": "", "linewidth": 10, "vertexColors": "VertexColors" } }, "pythree_example_model_017": { "model_module": "jupyter-threejs", "model_module_version": "^2.0.0", "model_name": "LineSegments2Model", "state": { "_model_module_version": "^2.0.0", "_view_module": null, "_view_module_version": "", "geometry": "IPY_MODEL_pythree_example_model_015", "material": "IPY_MODEL_pythree_example_model_016", "morphTargetInfluences": [] } }, "pythree_example_model_018": { "model_module": "jupyter-threejs", "model_module_version": "^2.0.0", "model_name": "PreviewModel", "state": { "_alpha": false, "_model_module_version": "^2.0.0", "_view_module_version": "^2.0.0", "child": "IPY_MODEL_pythree_example_model_017", "layout": "IPY_MODEL_pythree_example_model_019", "shadowMap": "IPY_MODEL_pythree_example_model_020" } }, "pythree_example_model_019": { "model_module": "@jupyter-widgets/base", "model_module_version": "1.1.0", "model_name": "LayoutModel", "state": {} }, "pythree_example_model_020": { "model_module": "jupyter-threejs", "model_module_version": "^2.0.0", "model_name": "WebGLShadowMapModel", "state": { "_model_module_version": "^2.0.0", "_view_module": null, "_view_module_version": "" } }, "pythree_example_model_021": { "model_module": "jupyter-threejs", "model_module_version": "^2.0.0", "model_name": "PerspectiveCameraModel", "state": { "_model_module_version": "^2.0.0", "_view_module": null, "_view_module_version": "", "aspect": 1.5, "matrix": [ 1, 0, 0, 0, 0, 1, 0, 0, 0, 0, 1, 0, 10, 0, 0, 1 ], "position": [ 10, 0, 0 ], "projectionMatrix": [ 1.4296712803397058, 0, 0, 0, 0, 2.1445069205095586, 0, 0, 0, 0, -1.00010000500025, -1, 0, 0, -0.200010000500025, 0 ] } }, "pythree_example_model_022": { "model_module": "jupyter-threejs", "model_module_version": "^2.0.0", "model_name": "DirectionalLightModel", "state": { "_model_module_version": "^2.0.0", "_view_module": null, "_view_module_version": "", "matrixWorldNeedsUpdate": true, "position": [ 0, 10, 10 ], "shadow": "IPY_MODEL_a0da8954-9821-487a-8da1-409e8679c9cc", "target": "IPY_MODEL_dedd8574-6920-4f16-bdee-ae7fad1749b1" } }, "pythree_example_model_023": { "model_module": "jupyter-threejs", "model_module_version": "^2.0.0", "model_name": "AmbientLightModel", "state": { "_model_module_version": "^2.0.0", "_view_module": null, "_view_module_version": "" } }, "pythree_example_model_027": { "model_module": "jupyter-threejs", "model_module_version": "^2.0.0", "model_name": "SceneModel", "state": { "_model_module_version": "^2.0.0", "_view_module": null, "_view_module_version": "", "children": [ "IPY_MODEL_pythree_example_model_005", "IPY_MODEL_pythree_example_model_011", "IPY_MODEL_pythree_example_model_017", "IPY_MODEL_pythree_example_model_021", "IPY_MODEL_pythree_example_model_022", "IPY_MODEL_pythree_example_model_023" ] } }, "pythree_example_model_028": { "model_module": "jupyter-threejs", "model_module_version": "^2.0.0", "model_name": "OrbitControlsModel", "state": { "_model_module_version": "^2.0.0", "_view_module": null, "_view_module_version": "", "controlling": "IPY_MODEL_pythree_example_model_021", "maxAzimuthAngle": "inf", "maxDistance": "inf", "maxZoom": "inf", "minAzimuthAngle": "-inf", "screenSpacePanning": false } }, "pythree_example_model_029": { "model_module": "jupyter-threejs", "model_module_version": "^2.0.0", "model_name": "RendererModel", "state": { "_height": 400, "_model_module_version": "^2.0.0", "_view_module_version": "^2.0.0", "_width": 600, "camera": "IPY_MODEL_pythree_example_model_021", "controls": [ "IPY_MODEL_pythree_example_model_028" ], "layout": "IPY_MODEL_pythree_example_model_030", "scene": "IPY_MODEL_pythree_example_model_027", "shadowMap": "IPY_MODEL_pythree_example_model_031" } }, "pythree_example_model_030": { "model_module": "@jupyter-widgets/base", "model_module_version": "1.1.0", "model_name": "LayoutModel", "state": {} }, "pythree_example_model_031": { "model_module": "jupyter-threejs", "model_module_version": "^2.0.0", "model_name": "WebGLShadowMapModel", "state": { "_model_module_version": "^2.0.0", "_view_module": null, "_view_module_version": "" } }, "pythree_example_model_032": { "model_module": "@jupyter-widgets/controls", "model_module_version": "1.3.0", "model_name": "CheckboxModel", "state": { "description": "GL line", "disabled": false, "layout": "IPY_MODEL_pythree_example_model_033", "style": "IPY_MODEL_pythree_example_model_034", "value": true } }, "pythree_example_model_033": { "model_module": "@jupyter-widgets/base", "model_module_version": "1.1.0", "model_name": "LayoutModel", "state": {} }, "pythree_example_model_034": { "model_module": "@jupyter-widgets/controls", "model_module_version": "1.3.0", "model_name": "DescriptionStyleModel", "state": { "description_width": "" } }, "pythree_example_model_035": { "model_module": "@jupyter-widgets/controls", "model_module_version": "1.3.0", "model_name": "CheckboxModel", "state": { "description": "Fat line (single color)", "disabled": false, "layout": "IPY_MODEL_pythree_example_model_036", "style": "IPY_MODEL_pythree_example_model_037", "value": true } }, "pythree_example_model_036": { "model_module": "@jupyter-widgets/base", "model_module_version": "1.1.0", "model_name": "LayoutModel", "state": {} }, "pythree_example_model_037": { "model_module": "@jupyter-widgets/controls", "model_module_version": "1.3.0", "model_name": "DescriptionStyleModel", "state": { "description_width": "" } }, "pythree_example_model_038": { "model_module": "@jupyter-widgets/controls", "model_module_version": "1.3.0", "model_name": "CheckboxModel", "state": { "description": "Fat line (vertex colors)", "disabled": false, "layout": "IPY_MODEL_pythree_example_model_039", "style": "IPY_MODEL_pythree_example_model_040", "value": true } }, "pythree_example_model_039": { "model_module": "@jupyter-widgets/base", "model_module_version": "1.1.0", "model_name": "LayoutModel", "state": {} }, "pythree_example_model_040": { "model_module": "@jupyter-widgets/controls", "model_module_version": "1.3.0", "model_name": "DescriptionStyleModel", "state": { "description_width": "" } }, "pythree_example_model_041": { "model_module": "@jupyter-widgets/controls", "model_module_version": "1.3.0", "model_name": "LinkModel", "state": { "source": [ "IPY_MODEL_pythree_example_model_032", "value" ], "target": [ "IPY_MODEL_pythree_example_model_005", "visible" ] } }, "pythree_example_model_042": { "model_module": "@jupyter-widgets/controls", "model_module_version": "1.3.0", "model_name": "LinkModel", "state": { "source": [ "IPY_MODEL_pythree_example_model_035", "value" ], "target": [ "IPY_MODEL_pythree_example_model_011", "visible" ] } }, "pythree_example_model_043": { "model_module": "@jupyter-widgets/controls", "model_module_version": "1.3.0", "model_name": "LinkModel", "state": { "source": [ "IPY_MODEL_pythree_example_model_038", "value" ], "target": [ "IPY_MODEL_pythree_example_model_017", "visible" ] } }, "pythree_example_model_044": { "model_module": "@jupyter-widgets/controls", "model_module_version": "1.3.0", "model_name": "HBoxModel", "state": { "children": [ "IPY_MODEL_pythree_example_model_032", "IPY_MODEL_pythree_example_model_035", "IPY_MODEL_pythree_example_model_038" ], "layout": "IPY_MODEL_pythree_example_model_045" } }, "pythree_example_model_045": { "model_module": "@jupyter-widgets/base", "model_module_version": "1.1.0", "model_name": "LayoutModel", "state": {} }, "pythree_example_model_046": { "model_module": "@jupyter-widgets/controls", "model_module_version": "1.3.0", "model_name": "VBoxModel", "state": { "children": [ "IPY_MODEL_pythree_example_model_029", "IPY_MODEL_pythree_example_model_044" ], "layout": "IPY_MODEL_pythree_example_model_047" } }, "pythree_example_model_047": { "model_module": "@jupyter-widgets/base", "model_module_version": "1.1.0", "model_name": "LayoutModel", "state": {} }, "pythree_example_model_048": { "buffers": [ { "data": "AAAAAAAAAAAAAAAAAACAPwAAgD8AAIA/AAAAQAAAAEAAAABAAACAQAAAgEAAAIBA", "encoding": "base64", "path": [ "array", "buffer" ] } ], "model_module": "jupyter-threejs", "model_module_version": "^2.0.0", "model_name": "InstancedInterleavedBufferModel", "state": { "_model_module_version": "^2.0.0", "_view_module": null, "_view_module_version": "", "array": { "dtype": "float32", "shape": [ 2, 6 ] }, "version": 2 } }, "pythree_example_model_049": { "buffers": [ { "data": "AACAPwAAAAAAAAAAAACAPwAAAAAAAAAAAAAAAAAAgD8AAAAAAAAAAAAAAAAAAIA/", "encoding": "base64", "path": [ "array", "buffer" ] } ], "model_module": "jupyter-threejs", "model_module_version": "^2.0.0", "model_name": "InstancedInterleavedBufferModel", "state": { "_model_module_version": "^2.0.0", "_view_module": null, "_view_module_version": "", "array": { "dtype": "float32", "shape": [ 2, 6 ] }, "version": 2 } }, "pythree_example_model_050": { "buffers": [ { "data": "AACAPwAAAEAAAAAAAACAPwAAAEAAAAAAAACAvwAAgD8AAAAAAACAPwAAgD8AAAAAAACAvwAAAAAAAAAAAACAPwAAAAAAAAAAAACAvwAAgL8AAAAAAACAPwAAgL8AAAAA", "encoding": "base64", "path": [ "array", "buffer" ] } ], "model_module": "jupyter-threejs", "model_module_version": "^2.0.0", "model_name": "BufferAttributeModel", "state": { "_model_module_version": "^2.0.0", "_view_module": null, "_view_module_version": "", "array": { "dtype": "float32", "shape": [ 8, 3 ] }, "normalized": true, "version": 2 } }, "pythree_example_model_051": { "buffers": [ { "data": "AACAvwAAAEAAAIA/AAAAQAAAgL8AAIA/AACAPwAAgD8AAIC/AACAvwAAgD8AAIC/AACAvwAAAMAAAIA/AAAAwA==", "encoding": "base64", "path": [ "array", "buffer" ] } ], "model_module": "jupyter-threejs", "model_module_version": "^2.0.0", "model_name": "BufferAttributeModel", "state": { "_model_module_version": "^2.0.0", "_view_module": null, "_view_module_version": "", "array": { "dtype": "float32", "shape": [ 8, 2 ] }, "normalized": true, "version": 2 } }, "pythree_example_model_052": { "buffers": [ { "data": "AAIBAgMBAgQDBAUDBAYFBgcF", "encoding": "base64", "path": [ "array", "buffer" ] } ], "model_module": "jupyter-threejs", "model_module_version": "^2.0.0", "model_name": "BufferAttributeModel", "state": { "_model_module_version": "^2.0.0", "_view_module": null, "_view_module_version": "", "array": { "dtype": "uint8", "shape": [ 18 ] }, "normalized": true, "version": 2 } }, "pythree_example_model_053": { "model_module": "jupyter-threejs", "model_module_version": "^2.0.0", "model_name": "InterleavedBufferAttributeModel", "state": { "_model_module_version": "^2.0.0", "_view_module": null, "_view_module_version": "", "data": "IPY_MODEL_pythree_example_model_048", "itemSize": 3 } }, "pythree_example_model_054": { "model_module": "jupyter-threejs", "model_module_version": "^2.0.0", "model_name": "InterleavedBufferAttributeModel", "state": { "_model_module_version": "^2.0.0", "_view_module": null, "_view_module_version": "", "data": "IPY_MODEL_pythree_example_model_048", "itemSize": 3, "offset": 3 } }, "pythree_example_model_055": { "model_module": "jupyter-threejs", "model_module_version": "^2.0.0", "model_name": "InterleavedBufferAttributeModel", "state": { "_model_module_version": "^2.0.0", "_view_module": null, "_view_module_version": "", "data": "IPY_MODEL_pythree_example_model_049", "itemSize": 3 } }, "pythree_example_model_056": { "model_module": "jupyter-threejs", "model_module_version": "^2.0.0", "model_name": "InterleavedBufferAttributeModel", "state": { "_model_module_version": "^2.0.0", "_view_module": null, "_view_module_version": "", "data": "IPY_MODEL_pythree_example_model_049", "itemSize": 3, "offset": 3 } }, "pythree_example_model_057": { "model_module": "jupyter-threejs", "model_module_version": "^2.0.0", "model_name": "InstancedBufferGeometryModel", "state": { "_model_module_version": "^2.0.0", "_view_module": null, "_view_module_version": "", "attributes": { "index": "IPY_MODEL_pythree_example_model_052", "instanceColorEnd": "IPY_MODEL_pythree_example_model_056", "instanceColorStart": "IPY_MODEL_pythree_example_model_055", "instanceEnd": "IPY_MODEL_pythree_example_model_054", "instanceStart": "IPY_MODEL_pythree_example_model_053", "position": "IPY_MODEL_pythree_example_model_050", "uv": "IPY_MODEL_pythree_example_model_051" } } }, "pythree_example_model_058": { "model_module": "jupyter-threejs", "model_module_version": "^2.0.0", "model_name": "ShaderMaterialModel", "state": { "_model_module_version": "^2.0.0", "_view_module": null, "_view_module_version": "", "fragmentShader": "\nuniform vec3 diffuse;\nuniform float opacity;\n\nvarying float vLineDistance;\n\n#include \n#include \n#include \n#include \n#include \n\nvarying vec2 vUv;\n\nvoid main() {\n\n #include \n\n\n if ( abs( vUv.y ) > 1.0 ) {\n\n float a = vUv.x;\n float b = ( vUv.y > 0.0 ) ? vUv.y - 1.0 : vUv.y + 1.0;\n float len2 = a * a + b * b;\n\n if ( len2 > 1.0 ) discard;\n\n }\n\n vec4 diffuseColor = vec4( diffuse, opacity );\n\n #include \n #include \n\n gl_FragColor = vec4( diffuseColor.rgb, diffuseColor.a );\n\n #include \n #include \n #include \n #include \n\n}\n", "uniforms": { "alphaMap": { "value": null }, "diffuse": { "type": "c", "value": "#eeeeee" }, "linewidth": { "value": 10 }, "map": { "value": null }, "opacity": { "value": 1 }, "resolution": { "type": "v2", "value": [ 100, 100 ] }, "uvTransform": { "type": "m3", "value": [ 1, 0, 0, 0, 1, 0, 0, 0, 1 ] } }, "vertexColors": "VertexColors", "vertexShader": "\n#include \n#include \n#include \n#include \n#include \n\nuniform float linewidth;\nuniform vec2 resolution;\n\nattribute vec3 instanceStart;\nattribute vec3 instanceEnd;\n\nattribute vec3 instanceColorStart;\nattribute vec3 instanceColorEnd;\n\nvarying vec2 vUv;\n\nvoid trimSegment( const in vec4 start, inout vec4 end ) {\n\n // trim end segment so it terminates between the camera plane and the near plane\n\n // conservative estimate of the near plane\n float a = projectionMatrix[ 2 ][ 2 ]; // 3nd entry in 3th column\n float b = projectionMatrix[ 3 ][ 2 ]; // 3nd entry in 4th column\n float nearEstimate = - 0.5 * b / a;\n\n float alpha = ( nearEstimate - start.z ) / ( end.z - start.z );\n\n end.xyz = mix( start.xyz, end.xyz, alpha );\n\n}\n\nvoid main() {\n\n #ifdef USE_COLOR\n\n vColor.xyz = ( position.y < 0.5 ) ? instanceColorStart : instanceColorEnd;\n\n #endif\n \n float aspect = resolution.x / resolution.y;\n\n vUv = uv;\n \n // camera space\n vec4 start = modelViewMatrix * vec4( instanceStart, 1.0 );\n vec4 end = modelViewMatrix * vec4( instanceEnd, 1.0 );\n\n // special case for perspective projection, and segments that terminate either in, or behind, the camera plane\n // clearly the gpu firmware has a way of addressing this issue when projecting into ndc space\n // but we need to perform ndc-space calculations in the shader, so we must address this issue directly\n // perhaps there is a more elegant solution -- WestLangley\n\n bool perspective = ( projectionMatrix[ 2 ][ 3 ] == - 1.0 ); // 4th entry in the 3rd column\n\n if ( perspective ) {\n\n if ( start.z < 0.0 && end.z >= 0.0 ) {\n\n trimSegment( start, end );\n\n } else if ( end.z < 0.0 && start.z >= 0.0 ) {\n\n trimSegment( end, start );\n\n }\n\n }\n\n // clip space\n vec4 clipStart = projectionMatrix * start;\n vec4 clipEnd = projectionMatrix * end;\n\n // ndc space\n vec2 ndcStart = clipStart.xy / clipStart.w;\n vec2 ndcEnd = clipEnd.xy / clipEnd.w;\n\n // direction\n vec2 dir = ndcEnd - ndcStart;\n\n // account for clip-space aspect ratio\n dir.x *= aspect;\n dir = normalize( dir );\n\n // perpendicular to dir\n vec2 offset = vec2( dir.y, - dir.x );\n\n // undo aspect ratio adjustment\n dir.x /= aspect;\n offset.x /= aspect;\n\n // sign flip\n if ( position.x < 0.0 ) offset *= - 1.0;\n\n // endcaps\n if ( position.y < 0.0 ) {\n\n offset += - dir;\n\n } else if ( position.y > 1.0 ) {\n\n offset += dir;\n\n }\n\n // adjust for linewidth\n offset *= linewidth;\n \n // adjust for clip-space to screen-space conversion // maybe resolution should be based on viewport ...\n offset /= resolution.y;\n\n // select end\n vec4 clip = ( position.y < 0.5 ) ? clipStart : clipEnd;\n\n // back to clip space\n offset *= clip.w;\n\n clip.xy += offset;\n\n gl_Position = clip;\n \n vec4 mvPosition = ( position.y < 0.5 ) ? start : end; // this is an approximation\n\n #include \n #include \n #include \n}\n" } }, "pythree_example_model_059": { "model_module": "jupyter-threejs", "model_module_version": "^2.0.0", "model_name": "MeshModel", "state": { "_model_module_version": "^2.0.0", "_view_module": null, "_view_module_version": "", "geometry": "IPY_MODEL_pythree_example_model_057", "material": "IPY_MODEL_pythree_example_model_058", "morphTargetInfluences": [] } }, "pythree_example_model_060": { "model_module": "jupyter-threejs", "model_module_version": "^2.0.0", "model_name": "PreviewModel", "state": { "_alpha": false, "_model_module_version": "^2.0.0", "_view_module_version": "^2.0.0", "child": "IPY_MODEL_pythree_example_model_059", "layout": "IPY_MODEL_pythree_example_model_061", "shadowMap": "IPY_MODEL_pythree_example_model_062" } }, "pythree_example_model_061": { "model_module": "@jupyter-widgets/base", "model_module_version": "1.1.0", "model_name": "LayoutModel", "state": {} }, "pythree_example_model_062": { "model_module": "jupyter-threejs", "model_module_version": "^2.0.0", "model_name": "WebGLShadowMapModel", "state": { "_model_module_version": "^2.0.0", "_view_module": null, "_view_module_version": "" } } }, "version_major": 2, "version_minor": 0 } } }, "nbformat": 4, "nbformat_minor": 2 }