From 2a140dae7e88abbe577c2106db8160f8ac7d2575 Mon Sep 17 00:00:00 2001 From: rinftech-github Date: Tue, 17 Mar 2020 22:16:58 +0200 Subject: [PATCH 001/130] Test Phong lighting support for plot_mesh. Add lighting_demo notebook. --- js/glsl/mesh-fragment-phong.glsl | 114 +++++++++ js/glsl/mesh-vertex-phong.glsl | 112 +++++++++ js/src/mesh.ts | 80 +++++- notebooks/lighting_demo.ipynb | 411 +++++++++++++++++++++++++++++++ 4 files changed, 708 insertions(+), 9 deletions(-) create mode 100644 js/glsl/mesh-fragment-phong.glsl create mode 100644 js/glsl/mesh-vertex-phong.glsl create mode 100644 notebooks/lighting_demo.ipynb diff --git a/js/glsl/mesh-fragment-phong.glsl b/js/glsl/mesh-fragment-phong.glsl new file mode 100644 index 00000000..38bc9f43 --- /dev/null +++ b/js/glsl/mesh-fragment-phong.glsl @@ -0,0 +1,114 @@ +#extension GL_OES_standard_derivatives : enable +#define PHONG + +uniform vec3 diffuse; +uniform vec3 emissive; +uniform vec3 specular; + +uniform float shininess; +uniform float opacity; + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +////////////////////////////////////////////////////////////////// +varying vec4 vertex_color; +varying vec3 vertex_position; +varying vec2 vertex_uv; + +#ifdef USE_TEXTURE + uniform sampler2D texture; + uniform sampler2D texture_previous; + uniform float animation_time_texture; +#endif + +void main() +{ + vec4 finalColor2 = vec4( 0.0, 0.0, 0.0, 1.0 ); + +#ifdef USE_RGB + finalColor2 = vec4( vertex_color.rgb, 1.0 ); +#else +#ifdef AS_LINE + finalColor2 = vec4( vertex_color.rgb, vertex_color.a ); +#else + + vec3 fdx_ = dFdx( vertex_position ); + vec3 fdy_ = dFdy( vertex_position ); + vec3 normal_position = normalize( cross( fdx_, fdy_ ) ); + float diffuse_ = dot( normal_position, vec3( 0.0, 0.0, 1.0 ) ); + +#ifdef USE_TEXTURE + vec4 sample = mix( texture2D( texture_previous, vertex_uv ), texture2D( texture, vertex_uv ), animation_time_texture ); + finalColor2 = vec4( clamp(diffuse_, 0.2, 1.) * sample.rgb, 1.0 ); +#else + finalColor2 = vec4( clamp(diffuse_, 0.2, 1.) * vertex_color.rgb, vertex_color.a ); +#endif // USE_TEXTURE +#endif // AS_LINE +#endif // USE_RGB + +////////////////////////////////////////////////////// + + #include + + vec4 diffuseColor = vec4( diffuse, opacity );//0.75 * finalColor2 + 0.25 * vec4( diffuse, opacity ); + ReflectedLight reflectedLight = ReflectedLight( vec3( 0.0 ), vec3( 0.0 ), vec3( 0.0 ), vec3( 0.0 ) ); + vec3 totalEmissiveRadiance = emissive; + + #include + #include + #include + #include + #include + #include + #include + #include + //normal = -normal; + #include + + // accumulation + #include + #include + #include + #include + + // modulation + #include + + vec3 outgoingLight = reflectedLight.directDiffuse + reflectedLight.indirectDiffuse + reflectedLight.directSpecular + reflectedLight.indirectSpecular + totalEmissiveRadiance; + + #include + + gl_FragColor = vec4( outgoingLight, diffuseColor.a ); + + #include + #include + #include + #include + #include + + //gl_FragColor = 0.75 * gl_FragColor + 0.25 * finalColor2; + //gl_FragColor = 0.25 * gl_FragColor + 0.75 * finalColor2; + +} \ No newline at end of file diff --git a/js/glsl/mesh-vertex-phong.glsl b/js/glsl/mesh-vertex-phong.glsl new file mode 100644 index 00000000..408bb690 --- /dev/null +++ b/js/glsl/mesh-vertex-phong.glsl @@ -0,0 +1,112 @@ +#extension GL_OES_standard_derivatives : enable +#define PHONG + + + // for animation, all between 0 and 1 +uniform float animation_time_x; +uniform float animation_time_y; +uniform float animation_time_z; +uniform float animation_time_u; +uniform float animation_time_v; +uniform float animation_time_color; + +uniform vec2 xlim; +uniform vec2 ylim; +uniform vec2 zlim; + +varying vec4 vertex_color; +varying vec3 vertex_position; + +attribute vec3 position_previous; + +#ifdef USE_TEXTURE + attribute float u; + attribute float v; + attribute float u_previous; + attribute float v_previous; + varying vec2 vertex_uv; +#endif + +attribute vec4 color_current; +attribute vec4 color_previous; + +//PHONG +//////////////////////////////////////////////////////////////////////////////// +varying vec3 vViewPosition; + +#ifndef FLAT_SHADED + + varying vec3 vNormal; + +#endif + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +//////////////////////////////////////////////////////////////////////////////// +void main() +{ + #include + #include + #include + + #include + #include + #include + #include + #include + +#ifndef FLAT_SHADED // Normal computed with derivatives when FLAT_SHADED + + vNormal = normalize( transformedNormal ); + +#endif + + #include + #include + #include + #include + #include + #include + #include + + vViewPosition = - mvPosition.xyz; + + #include + #include + #include + #include + + vec3 origin = vec3(xlim.x, ylim.x, zlim.x); + vec3 size_viewport = vec3(xlim.y, ylim.y, zlim.y) - origin; + + vec3 pos = (mix(position_previous, position, vec3(animation_time_x, animation_time_y, animation_time_z)) + - origin) / size_viewport - 0.5; + gl_Position = projectionMatrix * + modelViewMatrix * + vec4(pos,1.0); + vec3 positionEye = ( modelViewMatrix * vec4( pos, 1.0 ) ).xyz; + vertex_position = positionEye; + + //vViewPosition = vertex_position;//!!! + +#ifdef USE_TEXTURE + vertex_uv = vec2(mix(u_previous, u, animation_time_u), mix(v_previous, v, animation_time_v)); +#endif + +#ifdef USE_RGB + vertex_color = vec4(pos + vec3(0.5, 0.5, 0.5), 1.0); +#else + vertex_color = mix(color_previous, color_current, animation_time_color); +#endif + +} diff --git a/js/src/mesh.ts b/js/src/mesh.ts index 09fcb4af..16793fa2 100644 --- a/js/src/mesh.ts +++ b/js/src/mesh.ts @@ -40,7 +40,8 @@ class MeshView extends widgets.WidgetView { this._load_textures(); } - this.uniforms = { + this.uniforms = THREE.UniformsUtils.merge( [ + { xlim : { type: "2f", value: [0., 1.] }, ylim : { type: "2f", value: [0., 1.] }, zlim : { type: "2f", value: [0., 1.] }, @@ -54,13 +55,31 @@ class MeshView extends widgets.WidgetView { animation_time_texture : { type: "f", value: 1. }, texture: { type: "t", value: null }, texture_previous: { type: "t", value: null }, - }; + }, + THREE.UniformsLib[ "common" ], + THREE.UniformsLib[ "lights" ], + + { + emissive: { value: new THREE.Color( 0x000000 ) }, + specular: { value: new THREE.Color( 0x111111 ) }, + shininess: { value: 30 } + }, + + ] ); + + //this.uniforms.emissive.set = new THREE.Color(0,255,0); + const get_material = (name) => { if (this.model.get(name)) { return this.model.get(name).obj.clone(); } else { const mat = new THREE.ShaderMaterial(); mat.side = THREE.DoubleSide; + + mat.flatShading = false; + mat.transparent = true; + mat.needsUpdate = true; + return mat; } @@ -140,6 +159,31 @@ class MeshView extends widgets.WidgetView { add_to_scene() { this.meshes.forEach((mesh) => { this.renderer.scene_scatter.add(mesh); + + var globalIntensity = 1; + + /* + var amTest = new THREE.AmbientLight(0x000000, globalIntensity); + amTest.color = new THREE.Color(1,1,1); + amTest.intensity = 0.1; + this.renderer.scene_scatter.add(amTest); +*/ + + var dlTest = new THREE.DirectionalLight(0x000000, globalIntensity); + dlTest.castShadow = true; + dlTest.color = new THREE.Color("rgb(0, 255, 255)"); + dlTest.position.set(100, 100, 100).normalize(); + dlTest.lookAt(mesh.position); + this.renderer.scene_scatter.add(dlTest); + +/* + var dlTest2 = new THREE.DirectionalLight(0x000000, globalIntensity); + dlTest2.castShadow = true; + dlTest2.color = new THREE.Color("rgb(0, 0, 155)"); + dlTest2.position.set(0, 0, 100);//.normalize(); + dlTest2.lookAt(mesh.position); + this.renderer.scene_scatter.add(dlTest2); + */ }); } @@ -266,9 +310,13 @@ class MeshView extends widgets.WidgetView { if (this.model.get("line_material")) { this.line_material_rgb.copy(this.model.get("line_material").obj); } - this.material_rgb.defines = {USE_RGB: true}; + + //VERY IMPORTANT + this.material.defines = {USE_COLOR: true}; + // + this.material_rgb.defines = {USE_RGB: true, USE_COLOR: true}; this.line_material.defines = {AS_LINE: true}; - this.line_material_rgb.defines = {AS_LINE: true, USE_RGB: true}; + this.line_material_rgb.defines = {AS_LINE: true, USE_RGB: true, USE_COLOR: true}; this.material.extensions = {derivatives: true}; // locally and the visible with this object's visible trait this.material.visible = this.material.visible && this.model.get("visible"); @@ -276,13 +324,25 @@ class MeshView extends widgets.WidgetView { this.line_material.visible = this.line_material.visible && this.model.get("visible"); this.line_material_rgb.visible = this.line_material.visible && this.model.get("visible"); this.materials.forEach((material) => { - material.vertexShader = require("raw-loader!../glsl/mesh-vertex.glsl"); - material.fragmentShader = require("raw-loader!../glsl/mesh-fragment.glsl"); material.uniforms = this.uniforms; + material.vertexShader = require("raw-loader!../glsl/mesh-vertex-phong.glsl"); + material.fragmentShader = require("raw-loader!../glsl/mesh-fragment-phong.glsl"); material.depthWrite = true; material.transparant = true; material.depthTest = true; + //VERY IMPORTANT + material.lights = true; }); + + this.material.uniforms.diffuse.value = new THREE.Color(1, 1, 1);//BUG? keep hardcoded + this.material.uniforms.opacity.value = 1.0; + + this.material.uniforms.specular.value = new THREE.Color(0.5,0.5,0.5); + this.material.uniforms.shininess.value = 100; + + this.material.uniforms.emissive.value = new THREE.Color(0,0,0); + //this.material.uniforms.emissiveIntensity.value = 0.1; //TODO missing + const texture = this.model.get("texture"); if (texture && this.textures) { this.material.defines.USE_TEXTURE = true; @@ -396,7 +456,7 @@ class MeshView extends widgets.WidgetView { const geometry = new THREE.BufferGeometry(); geometry.addAttribute("position", new THREE.BufferAttribute(current.array_vec3.vertices, 3)); geometry.addAttribute("position_previous", new THREE.BufferAttribute(previous.array_vec3.vertices, 3)); - geometry.addAttribute("color", new THREE.BufferAttribute(current.array_vec4.color, 4)); + geometry.addAttribute("color_current", new THREE.BufferAttribute(current.array_vec4.color, 4)); geometry.addAttribute("color_previous", new THREE.BufferAttribute(previous.array_vec4.color, 4)); geometry.setIndex(new THREE.BufferAttribute(triangles, 1)); const texture = this.model.get("texture"); @@ -414,10 +474,12 @@ class MeshView extends widgets.WidgetView { geometry.addAttribute("u_previous", new THREE.BufferAttribute(u_previous, 1)); geometry.addAttribute("v_previous", new THREE.BufferAttribute(v_previous, 1)); } - + geometry.computeVertexNormals(); + //geometry.normalizeNormals(); this.surface_mesh = new THREE.Mesh(geometry, this.material); // BUG? because of our custom shader threejs thinks our object if out // of the frustum + this.surface_mesh.frustumCulled = false; this.surface_mesh.material_rgb = this.material_rgb; this.surface_mesh.material_normal = this.material; @@ -432,7 +494,7 @@ class MeshView extends widgets.WidgetView { geometry.addAttribute("position_previous", new THREE.BufferAttribute(previous.array_vec3.vertices, 3)); const color = new THREE.BufferAttribute(current.array_vec4.color, 4); color.normalized = true; - geometry.addAttribute("color", color); + geometry.addAttribute("color_current", color); const color_previous = new THREE.BufferAttribute(previous.array_vec4.color, 4); color_previous.normalized = true; geometry.addAttribute("color_previous", color_previous); diff --git a/notebooks/lighting_demo.ipynb b/notebooks/lighting_demo.ipynb new file mode 100644 index 00000000..7091699e --- /dev/null +++ b/notebooks/lighting_demo.ipynb @@ -0,0 +1,411 @@ +{ + "cells": [ + { + "cell_type": "code", + "execution_count": 1, + "metadata": {}, + "outputs": [ + { + "data": { + "application/vnd.jupyter.widget-view+json": { + "model_id": "928a20bfddc24b52b951cc78c1c52f04", + "version_major": 2, + "version_minor": 0 + }, + "text/plain": [ + "VBox(children=(Figure(camera=PerspectiveCamera(fov=45.0, position=(0.0, 0.0, 2.0), quaternion=(0.0, 0.0, 0.0, …" + ] + }, + "metadata": {}, + "output_type": "display_data" + }, + { + "data": { + "application/vnd.jupyter.widget-view+json": { + "model_id": "ee978d33fc7b4ffdb9f01cf7a1ce5d4a", + "version_major": 2, + "version_minor": 0 + }, + "text/plain": [ + "Mesh(line_material=ShaderMaterial(), material=ShaderMaterial(side='DoubleSide'), texture=None, triangles=array…" + ] + }, + "metadata": {}, + "output_type": "display_data" + } + ], + "source": [ + "\"\"\"Some examples for quick testing/demonstrations.\n", + "\n", + "All function accept `show` and `draw` arguments\n", + "\n", + " * If `draw` is `True` it will return the widgets (Scatter, Volume, Mesh)\n", + " * If `draw` is `False`, it will return the data\n", + " * if `show` is `False`, `ipv.show()` will not be called.\n", + "\"\"\"\n", + "#import sys\n", + "#sys.path.append(\"..\")\n", + "import warnings\n", + "import numpy as np\n", + "from numpy import cos, sin, pi\n", + "#import ipyvolume.pylab as p3\n", + "from ipyvolume import pylab as p3\n", + "\n", + "try:\n", + " import scipy.ndimage\n", + " import scipy.special\n", + "except:\n", + " pass # it's ok, it's not crucial\n", + "# __all__ = [\"example_ylm\"]\n", + "\n", + "\n", + "def xyz(shape=128, limits=[-3, 3], spherical=False, sparse=True, centers=False):\n", + " dim = 3\n", + " try:\n", + " shape[0]\n", + " except:\n", + " shape = [shape] * dim\n", + " try:\n", + " limits[0][0] # pylint: disable=unsubscriptable-object\n", + " except:\n", + " limits = [limits] * dim\n", + " if centers:\n", + " v = [\n", + " slice(vmin + (vmax - vmin) / float(N) / 2, vmax - (vmax - vmin) / float(N) / 4, (vmax - vmin) / float(N))\n", + " for (vmin, vmax), N in zip(limits, shape)\n", + " ]\n", + " else:\n", + " v = [\n", + " slice(vmin, vmax + (vmax - vmin) / float(N) / 2, (vmax - vmin) / float(N - 1))\n", + " for (vmin, vmax), N in zip(limits, shape)\n", + " ]\n", + " if sparse:\n", + " x, y, z = np.ogrid.__getitem__(v)\n", + " else:\n", + " x, y, z = np.mgrid.__getitem__(v)\n", + " if spherical:\n", + " r = np.linalg.norm([x, y, z])\n", + " theta = np.arctan2(y, x)\n", + " phi = np.arccos(z / r)\n", + " return x, y, z, r, theta, phi\n", + " else:\n", + " return x, y, z\n", + "\n", + "\n", + "def example_ylm(m=0, n=2, shape=128, limits=[-4, 4], draw=True, show=True, **kwargs):\n", + " \"\"\"Show a spherical harmonic.\"\"\"\n", + " \n", + "\n", + "\n", + " __, __, __, r, theta, phi = xyz(shape=shape, limits=limits, spherical=True)\n", + " radial = np.exp(-(r - 2) ** 2)\n", + " data = np.abs(scipy.special.sph_harm(m, n, theta, phi) ** 2) * radial # pylint: disable=no-member\n", + " if draw:\n", + " vol = p3.volshow(data=data, **kwargs)\n", + " if show:\n", + " p3.show()\n", + " return vol\n", + " else:\n", + " return data\n", + " # return ipyvolume.volshow(data=data.T, **kwargs)\n", + "\n", + "\n", + "def ball(rmax=3, rmin=0, shape=128, limits=[-4, 4], draw=True, show=True, **kwargs):\n", + " \"\"\"Show a ball.\"\"\"\n", + "\n", + " __, __, __, r, _theta, _phi = xyz(shape=shape, limits=limits, spherical=True)\n", + " data = r * 0\n", + " data[(r < rmax) & (r >= rmin)] = 0.5\n", + " if \"data_min\" not in kwargs:\n", + " kwargs[\"data_min\"] = 0\n", + " if \"data_max\" not in kwargs:\n", + " kwargs[\"data_max\"] = 1\n", + " data = data.T\n", + " if draw:\n", + " vol = p3.volshow(data=data, **kwargs)\n", + " if show:\n", + " p3.show()\n", + " return vol\n", + " else:\n", + " return data\n", + "\n", + "\n", + "# http://graphics.stanford.edu/data/voldata/\n", + "\n", + "\n", + "def klein_bottle(\n", + " draw=True,\n", + " show=True,\n", + " figure8=False,\n", + " endpoint=True,\n", + " uv=True,\n", + " wireframe=False,\n", + " texture=None,\n", + " both=False,\n", + " interval=1000,\n", + "):\n", + " \"\"\"Show one or two Klein bottles.\"\"\"\n", + "\n", + " # http://paulbourke.net/geometry/klein/\n", + " u = np.linspace(0, 2 * pi, num=40, endpoint=endpoint)\n", + " v = np.linspace(0, 2 * pi, num=40, endpoint=endpoint)\n", + " u, v = np.meshgrid(u, v)\n", + " if both:\n", + " x1, y1, z1, _u1, _v1 = klein_bottle(endpoint=endpoint, draw=False, show=False)\n", + " x2, y2, z2, _u2, _v2 = klein_bottle(endpoint=endpoint, draw=False, show=False, figure8=True)\n", + " x = [x1, x2]\n", + " y = [y1, y2]\n", + " z = [z1, z2]\n", + " else:\n", + " if figure8:\n", + " # u -= np.pi\n", + " # v -= np.pi\n", + " a = 2\n", + " s = 5\n", + " x = s * (a + cos(u / 2) * sin(v) - sin(u / 2) * sin(2 * v) / 2) * cos(u)\n", + " y = s * (a + cos(u / 2) * sin(v) - sin(u / 2) * sin(2 * v) / 2) * sin(u)\n", + " z = s * (sin(u / 2) * sin(v) + cos(u / 2) * sin(2 * v) / 2)\n", + " else:\n", + " r = 4 * (1 - cos(u) / 2)\n", + " x = 6 * cos(u) * (1 + sin(u)) + r * cos(u) * cos(v) * (u < pi) + r * cos(v + pi) * (u >= pi)\n", + " y = 16 * sin(u) + r * sin(u) * cos(v) * (u < pi)\n", + " z = r * sin(v)\n", + " if draw:\n", + " if texture:\n", + " uv = True\n", + " if uv:\n", + " mesh = p3.plot_mesh(\n", + " x,\n", + " y,\n", + " z,\n", + " wrapx=not endpoint,\n", + " wrapy=not endpoint,\n", + " u=u / (2 * np.pi),\n", + " v=v / (2 * np.pi),\n", + " wireframe=wireframe,\n", + " texture=texture,\n", + " )\n", + " else:\n", + " mesh = p3.plot_mesh(x, y, z, wrapx=not endpoint, wrapy=not endpoint, wireframe=wireframe, texture=texture)\n", + " if show:\n", + " if both:\n", + " p3.animation_control(mesh, interval=interval)\n", + " p3.squarelim()\n", + " p3.show()\n", + " return mesh\n", + " else:\n", + " return x, y, z, u, v\n", + "\n", + "\n", + "def brain(\n", + " draw=True, show=True, fiducial=True, flat=True, inflated=True, subject='S1', interval=1000, uv=True, color=None\n", + "):\n", + " \"\"\"Show a human brain model.\n", + "\n", + " Requirement:\n", + "\n", + " $ pip install https://github.com/gallantlab/pycortex\n", + " \"\"\"\n", + " import ipyvolume as ipv\n", + "\n", + " try:\n", + " import cortex\n", + " except:\n", + " warnings.warn(\"it seems pycortex is not installed, which is needed for this example\")\n", + " raise\n", + " xlist, ylist, zlist = [], [], []\n", + " polys_list = []\n", + "\n", + " def add(pts, polys):\n", + " xlist.append(pts[:, 0])\n", + " ylist.append(pts[:, 1])\n", + " zlist.append(pts[:, 2])\n", + " polys_list.append(polys)\n", + "\n", + " def n(x):\n", + " return (x - x.min()) / x.ptp()\n", + "\n", + " if fiducial or color is True:\n", + " pts, polys = cortex.db.get_surf('S1', 'fiducial', merge=True)\n", + " x, y, z = pts.T\n", + " r = n(x)\n", + " g = n(y)\n", + " b = n(z)\n", + " if color is True:\n", + " color = np.array([r, g, b]).T.copy()\n", + " else:\n", + " color = None\n", + " if fiducial:\n", + " add(pts, polys)\n", + " else:\n", + " if color is False:\n", + " color = None\n", + " if inflated:\n", + " add(*cortex.db.get_surf('S1', 'inflated', merge=True, nudge=True))\n", + " u = v = None\n", + " if flat or uv:\n", + " pts, polys = cortex.db.get_surf('S1', 'flat', merge=True, nudge=True)\n", + " x, y, z = pts.T\n", + " u = n(x)\n", + " v = n(y)\n", + " if flat:\n", + " add(pts, polys)\n", + "\n", + " polys_list.sort(key=lambda x: len(x))\n", + " polys = polys_list[0]\n", + " if draw:\n", + " if color is None:\n", + " mesh = ipv.plot_trisurf(xlist, ylist, zlist, polys, u=u, v=v)\n", + " else:\n", + " mesh = ipv.plot_trisurf(xlist, ylist, zlist, polys, color=color, u=u, v=v)\n", + " if show:\n", + " if len(x) > 1:\n", + " ipv.animation_control(mesh, interval=interval)\n", + " ipv.squarelim()\n", + " ipv.show()\n", + " return mesh\n", + " else:\n", + " return xlist, ylist, zlist, polys\n", + "\n", + "\n", + "def head(draw=True, show=True, max_shape=256):\n", + " \"\"\"Show a volumetric rendering of a human male head.\"\"\"\n", + " # inspired by http://graphicsrunner.blogspot.com/2009/01/volume-rendering-102-transfer-functions.html\n", + " import ipyvolume as ipv\n", + " from scipy.interpolate import interp1d\n", + "\n", + " # First part is a simpler version of setting up the transfer function. Interpolation with higher order\n", + " # splines does not work well, the original must do sth different\n", + " colors = [[0.91, 0.7, 0.61, 0.0], [0.91, 0.7, 0.61, 80.0], [1.0, 1.0, 0.85, 82.0], [1.0, 1.0, 0.85, 256]]\n", + " x = np.array([k[-1] for k in colors])\n", + " rgb = np.array([k[:3] for k in colors])\n", + " N = 256\n", + " xnew = np.linspace(0, 256, N)\n", + " tf_data = np.zeros((N, 4))\n", + " kind = 'linear'\n", + " for channel in range(3):\n", + " f = interp1d(x, rgb[:, channel], kind=kind)\n", + " ynew = f(xnew)\n", + " tf_data[:, channel] = ynew\n", + " alphas = [[0, 0], [0, 40], [0.2, 60], [0.05, 63], [0, 80], [0.9, 82], [1.0, 256]]\n", + " x = np.array([k[1] * 1.0 for k in alphas])\n", + " y = np.array([k[0] * 1.0 for k in alphas])\n", + " f = interp1d(x, y, kind=kind)\n", + " ynew = f(xnew)\n", + " tf_data[:, 3] = ynew\n", + " tf = ipv.TransferFunction(rgba=tf_data.astype(np.float32))\n", + "\n", + " head_data = ipv.datasets.head.fetch().data\n", + " if draw:\n", + " vol = ipv.volshow(head_data, tf=tf, max_shape=max_shape)\n", + " if show:\n", + " ipv.show()\n", + " return vol\n", + " else:\n", + " return head_data\n", + "\n", + "\n", + "def gaussian(N=1000, draw=True, show=True, seed=42, color=None, marker='sphere'):\n", + " \"\"\"Show N random gaussian distributed points using a scatter plot.\"\"\"\n", + " import ipyvolume as ipv\n", + "\n", + " rng = np.random.RandomState(seed) # pylint: disable=no-member\n", + " x, y, z = rng.normal(size=(3, N))\n", + "\n", + " if draw:\n", + " if color:\n", + " mesh = ipv.scatter(x, y, z, marker=marker, color=color)\n", + " else:\n", + " mesh = ipv.scatter(x, y, z, marker=marker)\n", + " if show:\n", + " # ipv.squarelim()\n", + " ipv.show()\n", + " return mesh\n", + " else:\n", + " return x, y, z\n", + "klein_bottle()\n", + "#gaussian()\n", + "#brain()\n", + "#head()" + ] + }, + { + "cell_type": "code", + "execution_count": 2, + "metadata": {}, + "outputs": [ + { + "data": { + "application/vnd.jupyter.widget-view+json": { + "model_id": "58c318d75ab74107921176aea915f3d7", + "version_major": 2, + "version_minor": 0 + }, + "text/plain": [ + "VBox(children=(Figure(camera=PerspectiveCamera(fov=45.0, position=(0.0, 0.0, 2.0), quaternion=(0.0, 0.0, 0.0, …" + ] + }, + "metadata": {}, + "output_type": "display_data" + }, + { + "data": { + "application/vnd.jupyter.widget-view+json": { + "model_id": "583aca4273ec466eb22be9d3ca15971b", + "version_major": 2, + "version_minor": 0 + }, + "text/plain": [ + "Mesh(line_material=ShaderMaterial(), material=ShaderMaterial(side='DoubleSide'), texture=None, triangles=array…" + ] + }, + "metadata": {}, + "output_type": "display_data" + } + ], + "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": { + "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.7.6" + } + }, + "nbformat": 4, + "nbformat_minor": 2 +} From 29d1d16e6869706466d6a8503c18950a44ac9b00 Mon Sep 17 00:00:00 2001 From: rinftech-github Date: Thu, 19 Mar 2020 19:06:02 +0200 Subject: [PATCH 002/130] Test shadowmap with spotlight. Fix shadow acne. --- js/src/mesh.ts | 99 +++++++++++++++++------ notebooks/lighting_demo.ipynb | 148 +++++++++++++++++++--------------- 2 files changed, 156 insertions(+), 91 deletions(-) diff --git a/js/src/mesh.ts b/js/src/mesh.ts index 16793fa2..8c547342 100644 --- a/js/src/mesh.ts +++ b/js/src/mesh.ts @@ -26,9 +26,12 @@ class MeshView extends widgets.WidgetView { texture_loader: any; textures: any; texture_video: any; + + lightCountTemp : any; render() { // console.log("created mesh view, parent is") // console.log(this.options.parent) + this.lightCountTemp = 0; this.renderer = this.options.parent; this.previous_values = {}; this.attributes_changed = {}; @@ -73,7 +76,7 @@ class MeshView extends widgets.WidgetView { if (this.model.get(name)) { return this.model.get(name).obj.clone(); } else { - const mat = new THREE.ShaderMaterial(); + const mat = new THREE.MeshPhongMaterial(); mat.side = THREE.DoubleSide; mat.flatShading = false; @@ -109,6 +112,68 @@ class MeshView extends widgets.WidgetView { this.create_mesh(); this.add_to_scene(); + + //LIGHTING +///////////////////// + var globalIntensity = 1; + + + if(this.lightCountTemp == 0) + { + this.lightCountTemp = 1; + + this.renderer.renderer.shadowMap.enabled = true; + this.renderer.renderer.shadowMap.type = THREE.PCFSoftShadowMap; + + var slTest = new THREE.SpotLight(0x000000, globalIntensity); + slTest.castShadow = true; + slTest.color = new THREE.Color("rgb(0, 255, 255)"); + slTest.position.set(20, 30, 20);//.normalize(); + slTest.angle = Math.PI/9; + slTest.penumbra = 0.25; + slTest.lookAt(new THREE.Vector3(0,-20,0)); + + + slTest.shadow = new THREE.SpotLightShadow(new THREE.PerspectiveCamera(100, 1, 0.5, 5000)); + //Set up shadow properties for the light + slTest.shadow.camera.position.set(slTest.position.x, slTest.position.y, slTest.position.z); + slTest.shadow.mapSize.width = 512; // default + slTest.shadow.mapSize.height = 512; // default + slTest.shadow.camera.near = 0.5; // default + slTest.shadow.camera.far = 500 // default + slTest.shadow.bias = -0.0005; //prevent shadow acne + + this.renderer.scene_scatter.add(slTest); + + + /* + var dlTest = new THREE.DirectionalLight(0x000000, globalIntensity); + dlTest.castShadow = true; + dlTest.color = new THREE.Color("rgb(0, 255, 255)"); + dlTest.position.set(100, 100, 100).normalize(); + dlTest.lookAt(new THREE.Vector3(0,0,0));// + this.renderer.scene_scatter.add(dlTest); + + + var dlTest2 = new THREE.DirectionalLight(0x000000, globalIntensity); + dlTest2.castShadow = true; + dlTest2.color = new THREE.Color("rgb(0, 0, 155)"); + dlTest2.position.set(0, 0, 100);//.normalize(); + dlTest2.lookAt(mesh.position); + this.renderer.scene_scatter.add(dlTest2); + */ + + /* + var amTest = new THREE.AmbientLight(0x000000, globalIntensity); + amTest.color = new THREE.Color(1,1,1); + amTest.intensity = 0.1; + this.renderer.scene_scatter.add(amTest); + */ + + } + + + this.model.on("change:color change:sequence_index change:x change:y change:z change:v change:u change:triangles change:lines", this.on_change, this); this.model.on("change:geo change:connected", this.update_, this); @@ -158,32 +223,10 @@ class MeshView extends widgets.WidgetView { add_to_scene() { this.meshes.forEach((mesh) => { + mesh.castShadow = true; + mesh.receiveShadow = true; this.renderer.scene_scatter.add(mesh); - var globalIntensity = 1; - - /* - var amTest = new THREE.AmbientLight(0x000000, globalIntensity); - amTest.color = new THREE.Color(1,1,1); - amTest.intensity = 0.1; - this.renderer.scene_scatter.add(amTest); -*/ - - var dlTest = new THREE.DirectionalLight(0x000000, globalIntensity); - dlTest.castShadow = true; - dlTest.color = new THREE.Color("rgb(0, 255, 255)"); - dlTest.position.set(100, 100, 100).normalize(); - dlTest.lookAt(mesh.position); - this.renderer.scene_scatter.add(dlTest); - -/* - var dlTest2 = new THREE.DirectionalLight(0x000000, globalIntensity); - dlTest2.castShadow = true; - dlTest2.color = new THREE.Color("rgb(0, 0, 155)"); - dlTest2.position.set(0, 0, 100);//.normalize(); - dlTest2.lookAt(mesh.position); - this.renderer.scene_scatter.add(dlTest2); - */ }); } @@ -475,7 +518,7 @@ class MeshView extends widgets.WidgetView { geometry.addAttribute("v_previous", new THREE.BufferAttribute(v_previous, 1)); } geometry.computeVertexNormals(); - //geometry.normalizeNormals(); + this.surface_mesh = new THREE.Mesh(geometry, this.material); // BUG? because of our custom shader threejs thinks our object if out // of the frustum @@ -483,6 +526,10 @@ class MeshView extends widgets.WidgetView { this.surface_mesh.frustumCulled = false; this.surface_mesh.material_rgb = this.material_rgb; this.surface_mesh.material_normal = this.material; + + this.surface_mesh.castShadow = true; + this.surface_mesh.receiveShadow = true; + this.meshes.push(this.surface_mesh); } diff --git a/notebooks/lighting_demo.ipynb b/notebooks/lighting_demo.ipynb index 7091699e..9ca6970f 100644 --- a/notebooks/lighting_demo.ipynb +++ b/notebooks/lighting_demo.ipynb @@ -8,7 +8,7 @@ { "data": { "application/vnd.jupyter.widget-view+json": { - "model_id": "928a20bfddc24b52b951cc78c1c52f04", + "model_id": "77ec401ca2724418ad2338f1f44839ff", "version_major": 2, "version_minor": 0 }, @@ -22,7 +22,7 @@ { "data": { "application/vnd.jupyter.widget-view+json": { - "model_id": "ee978d33fc7b4ffdb9f01cf7a1ce5d4a", + "model_id": "9a80e0c5e2f64a749515c118fbbaef5e", "version_major": 2, "version_minor": 0 }, @@ -133,68 +133,6 @@ "# http://graphics.stanford.edu/data/voldata/\n", "\n", "\n", - "def klein_bottle(\n", - " draw=True,\n", - " show=True,\n", - " figure8=False,\n", - " endpoint=True,\n", - " uv=True,\n", - " wireframe=False,\n", - " texture=None,\n", - " both=False,\n", - " interval=1000,\n", - "):\n", - " \"\"\"Show one or two Klein bottles.\"\"\"\n", - "\n", - " # http://paulbourke.net/geometry/klein/\n", - " u = np.linspace(0, 2 * pi, num=40, endpoint=endpoint)\n", - " v = np.linspace(0, 2 * pi, num=40, endpoint=endpoint)\n", - " u, v = np.meshgrid(u, v)\n", - " if both:\n", - " x1, y1, z1, _u1, _v1 = klein_bottle(endpoint=endpoint, draw=False, show=False)\n", - " x2, y2, z2, _u2, _v2 = klein_bottle(endpoint=endpoint, draw=False, show=False, figure8=True)\n", - " x = [x1, x2]\n", - " y = [y1, y2]\n", - " z = [z1, z2]\n", - " else:\n", - " if figure8:\n", - " # u -= np.pi\n", - " # v -= np.pi\n", - " a = 2\n", - " s = 5\n", - " x = s * (a + cos(u / 2) * sin(v) - sin(u / 2) * sin(2 * v) / 2) * cos(u)\n", - " y = s * (a + cos(u / 2) * sin(v) - sin(u / 2) * sin(2 * v) / 2) * sin(u)\n", - " z = s * (sin(u / 2) * sin(v) + cos(u / 2) * sin(2 * v) / 2)\n", - " else:\n", - " r = 4 * (1 - cos(u) / 2)\n", - " x = 6 * cos(u) * (1 + sin(u)) + r * cos(u) * cos(v) * (u < pi) + r * cos(v + pi) * (u >= pi)\n", - " y = 16 * sin(u) + r * sin(u) * cos(v) * (u < pi)\n", - " z = r * sin(v)\n", - " if draw:\n", - " if texture:\n", - " uv = True\n", - " if uv:\n", - " mesh = p3.plot_mesh(\n", - " x,\n", - " y,\n", - " z,\n", - " wrapx=not endpoint,\n", - " wrapy=not endpoint,\n", - " u=u / (2 * np.pi),\n", - " v=v / (2 * np.pi),\n", - " wireframe=wireframe,\n", - " texture=texture,\n", - " )\n", - " else:\n", - " mesh = p3.plot_mesh(x, y, z, wrapx=not endpoint, wrapy=not endpoint, wireframe=wireframe, texture=texture)\n", - " if show:\n", - " if both:\n", - " p3.animation_control(mesh, interval=interval)\n", - " p3.squarelim()\n", - " p3.show()\n", - " return mesh\n", - " else:\n", - " return x, y, z, u, v\n", "\n", "\n", "def brain(\n", @@ -323,7 +261,87 @@ " return mesh\n", " else:\n", " return x, y, z\n", + " \n", + "def klein_bottle(\n", + " draw=True,\n", + " show=True,\n", + " figure8=False,\n", + " endpoint=True,\n", + " uv=True,\n", + " wireframe=False,\n", + " texture=None,\n", + " both=False,\n", + " interval=1000,\n", + "):\n", + " \"\"\"Show one or two Klein bottles.\"\"\"\n", + "\n", + " # http://paulbourke.net/geometry/klein/\n", + " u = np.linspace(0, 2 * pi, num=50, endpoint=endpoint)\n", + " v = np.linspace(0, 2 * pi, num=50, endpoint=endpoint)\n", + " u, v = np.meshgrid(u, v)\n", + " if both:\n", + " x1, y1, z1, _u1, _v1 = klein_bottle(endpoint=endpoint, draw=False, show=False)\n", + " x2, y2, z2, _u2, _v2 = klein_bottle(endpoint=endpoint, draw=False, show=False, figure8=True)\n", + " x = [x1, x2]\n", + " y = [y1, y2]\n", + " z = [z1, z2]\n", + " else:\n", + " if figure8:\n", + " # u -= np.pi\n", + " # v -= np.pi\n", + " a = 2\n", + " s = 5\n", + " x = s * (a + cos(u / 2) * sin(v) - sin(u / 2) * sin(2 * v) / 2) * cos(u)\n", + " y = s * (a + cos(u / 2) * sin(v) - sin(u / 2) * sin(2 * v) / 2) * sin(u)\n", + " z = s * (sin(u / 2) * sin(v) + cos(u / 2) * sin(2 * v) / 2)\n", + " else:\n", + " r = 4 * (1 - cos(u) / 2)\n", + " x = 6 * cos(u) * (1 + sin(u)) + r * cos(u) * cos(v) * (u < pi) + r * cos(v + pi) * (u >= pi)\n", + " y = 16 * sin(u) + r * sin(u) * cos(v) * (u < pi)\n", + " z = r * sin(v)\n", + " if draw:\n", + " if texture:\n", + " uv = True\n", + " if uv:\n", + " mesh = p3.plot_mesh(\n", + " x,\n", + " y,\n", + " z,\n", + " wrapx=not endpoint,\n", + " wrapy=not endpoint,\n", + " u=u / (2 * np.pi),\n", + " v=v / (2 * np.pi),\n", + " wireframe=wireframe,\n", + " texture=texture,\n", + " )\n", + " else:\n", + " mesh = p3.plot_mesh(x, y, z, wrapx=not endpoint, wrapy=not endpoint, wireframe=wireframe, texture=texture)\n", + " if show:\n", + " if both:\n", + " p3.animation_control(mesh, interval=interval)\n", + " p3.squarelim()\n", + " drawSurface(separateFigure=False)\n", + " p3.show()\n", + " return mesh\n", + " else:\n", + " return x, y, z, u, v\n", + "\n", + "def drawSurface(separateFigure=True):\n", + " k = 20\n", + " h = -15\n", + " tx = np.array([k, -k, -k, k])\n", + " tz = np.array([k, k, -k, -k])\n", + " ty = np.array([h, h, h, h])\n", + " \n", + " tri = [(0, 1, 2), (0, 2, 3)]\n", + " p3.plot_trisurf(tx, ty, tz, triangles=tri, color='orange')\n", + " \n", + " if separateFigure:\n", + " p3.figure()\n", + " p3.show()\n", + " \n", "klein_bottle()\n", + "#drawSurface()\n", "#gaussian()\n", "#brain()\n", "#head()" @@ -403,7 +421,7 @@ "name": "python", "nbconvert_exporter": "python", "pygments_lexer": "ipython3", - "version": "3.7.6" + "version": "3.7.4" } }, "nbformat": 4, From 55d6b98f8adbbd4f61811a861ab28cfeabc271bf Mon Sep 17 00:00:00 2001 From: rinftech-github Date: Thu, 19 Mar 2020 20:50:14 +0200 Subject: [PATCH 003/130] Add Lambert lighting model shaders --- js/glsl/mesh-fragment-lambert.glsl | 126 +++++++++++++++++++++++++++++ js/glsl/mesh-vertex-lambert.glsl | 105 ++++++++++++++++++++++++ js/src/mesh.ts | 22 ++--- notebooks/lighting_demo.ipynb | 8 +- 4 files changed, 247 insertions(+), 14 deletions(-) create mode 100644 js/glsl/mesh-fragment-lambert.glsl create mode 100644 js/glsl/mesh-vertex-lambert.glsl diff --git a/js/glsl/mesh-fragment-lambert.glsl b/js/glsl/mesh-fragment-lambert.glsl new file mode 100644 index 00000000..81d5d85d --- /dev/null +++ b/js/glsl/mesh-fragment-lambert.glsl @@ -0,0 +1,126 @@ +#extension GL_OES_standard_derivatives : enable +#define LAMBERT + +uniform vec3 diffuse; +uniform vec3 emissive; +uniform float opacity; + +varying vec3 vLightFront; + +#ifdef DOUBLE_SIDED + + varying vec3 vLightBack; + +#endif + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +////////////////////////////////////////////////////////////////// +varying vec4 vertex_color; +varying vec3 vertex_position; +varying vec2 vertex_uv; + +#ifdef USE_TEXTURE + uniform sampler2D texture; + uniform sampler2D texture_previous; + uniform float animation_time_texture; +#endif + +void main() +{ + vec4 finalColor2 = vec4( 0.0, 0.0, 0.0, 1.0 ); + +#ifdef USE_RGB + finalColor2 = vec4( vertex_color.rgb, 1.0 ); +#else +#ifdef AS_LINE + finalColor2 = vec4( vertex_color.rgb, vertex_color.a ); +#else + + vec3 fdx_ = dFdx( vertex_position ); + vec3 fdy_ = dFdy( vertex_position ); + vec3 normal_position = normalize( cross( fdx_, fdy_ ) ); + float diffuse_ = dot( normal_position, vec3( 0.0, 0.0, 1.0 ) ); + +#ifdef USE_TEXTURE + vec4 sample = mix( texture2D( texture_previous, vertex_uv ), texture2D( texture, vertex_uv ), animation_time_texture ); + finalColor2 = vec4( clamp(diffuse_, 0.2, 1.) * sample.rgb, 1.0 ); +#else + finalColor2 = vec4( clamp(diffuse_, 0.2, 1.) * vertex_color.rgb, vertex_color.a ); +#endif // USE_TEXTURE +#endif // AS_LINE +#endif // USE_RGB + +////////////////////////////////////////////////////// + +#include + + vec4 diffuseColor = vec4( diffuse, opacity ); + ReflectedLight reflectedLight = ReflectedLight( vec3( 0.0 ), vec3( 0.0 ), vec3( 0.0 ), vec3( 0.0 ) ); + vec3 totalEmissiveRadiance = emissive; + + #include + #include + #include + #include + #include + #include + #include + + // accumulation + reflectedLight.indirectDiffuse = getAmbientLightIrradiance( ambientLightColor ); + + #include + + reflectedLight.indirectDiffuse *= BRDF_Diffuse_Lambert( diffuseColor.rgb ); + + #ifdef DOUBLE_SIDED + + reflectedLight.directDiffuse = ( gl_FrontFacing ) ? vLightFront : vLightBack; + + #else + + reflectedLight.directDiffuse = vLightFront; + + #endif + + reflectedLight.directDiffuse *= BRDF_Diffuse_Lambert( diffuseColor.rgb ) * getShadowMask(); + + // modulation + #include + + vec3 outgoingLight = reflectedLight.directDiffuse + reflectedLight.indirectDiffuse + totalEmissiveRadiance; + + #include + + gl_FragColor = vec4( outgoingLight, diffuseColor.a ); + + #include + #include + #include + #include + #include + + //gl_FragColor = 0.75 * gl_FragColor + 0.25 * finalColor2; + //gl_FragColor = 0.25 * gl_FragColor + 0.75 * finalColor2; + +} \ No newline at end of file diff --git a/js/glsl/mesh-vertex-lambert.glsl b/js/glsl/mesh-vertex-lambert.glsl new file mode 100644 index 00000000..82216d6b --- /dev/null +++ b/js/glsl/mesh-vertex-lambert.glsl @@ -0,0 +1,105 @@ +#extension GL_OES_standard_derivatives : enable +#define LAMBERT + + + // for animation, all between 0 and 1 +uniform float animation_time_x; +uniform float animation_time_y; +uniform float animation_time_z; +uniform float animation_time_u; +uniform float animation_time_v; +uniform float animation_time_color; + +uniform vec2 xlim; +uniform vec2 ylim; +uniform vec2 zlim; + +varying vec4 vertex_color; +varying vec3 vertex_position; + +attribute vec3 position_previous; + +#ifdef USE_TEXTURE + attribute float u; + attribute float v; + attribute float u_previous; + attribute float v_previous; + varying vec2 vertex_uv; +#endif + +attribute vec4 color_current; +attribute vec4 color_previous; + +//LAMBERT +//////////////////////////////////////////////////////////////////////////////// +varying vec3 vLightFront; + +#ifdef DOUBLE_SIDED + + varying vec3 vLightBack; + +#endif + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +//////////////////////////////////////////////////////////////////////////////// +void main() +{ + #include + #include + #include + + #include + #include + #include + #include + #include + + #include + #include + #include + #include + #include + #include + + #include + #include + #include + #include + #include + + vec3 origin = vec3(xlim.x, ylim.x, zlim.x); + vec3 size_viewport = vec3(xlim.y, ylim.y, zlim.y) - origin; + + vec3 pos = (mix(position_previous, position, vec3(animation_time_x, animation_time_y, animation_time_z)) + - origin) / size_viewport - 0.5; + gl_Position = projectionMatrix * + modelViewMatrix * + vec4(pos,1.0); + vec3 positionEye = ( modelViewMatrix * vec4( pos, 1.0 ) ).xyz; + vertex_position = positionEye; + + //vViewPosition = vertex_position;//!!! + +#ifdef USE_TEXTURE + vertex_uv = vec2(mix(u_previous, u, animation_time_u), mix(v_previous, v, animation_time_v)); +#endif + +#ifdef USE_RGB + vertex_color = vec4(pos + vec3(0.5, 0.5, 0.5), 1.0); +#else + vertex_color = mix(color_previous, color_current, animation_time_color); +#endif + +} diff --git a/js/src/mesh.ts b/js/src/mesh.ts index 8c547342..700d680b 100644 --- a/js/src/mesh.ts +++ b/js/src/mesh.ts @@ -127,7 +127,7 @@ class MeshView extends widgets.WidgetView { var slTest = new THREE.SpotLight(0x000000, globalIntensity); slTest.castShadow = true; - slTest.color = new THREE.Color("rgb(0, 255, 255)"); + slTest.color = new THREE.Color("rgb(100, 100, 100)"); slTest.position.set(20, 30, 20);//.normalize(); slTest.angle = Math.PI/9; slTest.penumbra = 0.25; @@ -141,20 +141,20 @@ class MeshView extends widgets.WidgetView { slTest.shadow.mapSize.height = 512; // default slTest.shadow.camera.near = 0.5; // default slTest.shadow.camera.far = 500 // default - slTest.shadow.bias = -0.0005; //prevent shadow acne + slTest.shadow.bias = -0.00005; // prevent shadow acne this.renderer.scene_scatter.add(slTest); - - /* + /* var dlTest = new THREE.DirectionalLight(0x000000, globalIntensity); dlTest.castShadow = true; dlTest.color = new THREE.Color("rgb(0, 255, 255)"); dlTest.position.set(100, 100, 100).normalize(); dlTest.lookAt(new THREE.Vector3(0,0,0));// this.renderer.scene_scatter.add(dlTest); - + */ + /* var dlTest2 = new THREE.DirectionalLight(0x000000, globalIntensity); dlTest2.castShadow = true; dlTest2.color = new THREE.Color("rgb(0, 0, 155)"); @@ -163,12 +163,14 @@ class MeshView extends widgets.WidgetView { this.renderer.scene_scatter.add(dlTest2); */ - /* + var amTest = new THREE.AmbientLight(0x000000, globalIntensity); - amTest.color = new THREE.Color(1,1,1); + amTest.color = new THREE.Color(1,0,0); amTest.intensity = 0.1; this.renderer.scene_scatter.add(amTest); - */ + + + } @@ -368,8 +370,8 @@ class MeshView extends widgets.WidgetView { this.line_material_rgb.visible = this.line_material.visible && this.model.get("visible"); this.materials.forEach((material) => { material.uniforms = this.uniforms; - material.vertexShader = require("raw-loader!../glsl/mesh-vertex-phong.glsl"); - material.fragmentShader = require("raw-loader!../glsl/mesh-fragment-phong.glsl"); + material.vertexShader = require("raw-loader!../glsl/mesh-vertex-lambert.glsl"); + material.fragmentShader = require("raw-loader!../glsl/mesh-fragment-lambert.glsl"); material.depthWrite = true; material.transparant = true; material.depthTest = true; diff --git a/notebooks/lighting_demo.ipynb b/notebooks/lighting_demo.ipynb index 9ca6970f..c79ff250 100644 --- a/notebooks/lighting_demo.ipynb +++ b/notebooks/lighting_demo.ipynb @@ -2,18 +2,18 @@ "cells": [ { "cell_type": "code", - "execution_count": 1, + "execution_count": 4, "metadata": {}, "outputs": [ { "data": { "application/vnd.jupyter.widget-view+json": { - "model_id": "77ec401ca2724418ad2338f1f44839ff", + "model_id": "9598bda6f9ee4eb28377396d2a5543a8", "version_major": 2, "version_minor": 0 }, "text/plain": [ - "VBox(children=(Figure(camera=PerspectiveCamera(fov=45.0, position=(0.0, 0.0, 2.0), quaternion=(0.0, 0.0, 0.0, …" + "VBox(children=(Figure(camera=PerspectiveCamera(aspect=0.8, fov=45.0, position=(1.1851781814701616, 0.210322184…" ] }, "metadata": {}, @@ -22,7 +22,7 @@ { "data": { "application/vnd.jupyter.widget-view+json": { - "model_id": "9a80e0c5e2f64a749515c118fbbaef5e", + "model_id": "f87c295e66234970b2275fac86eb3dd2", "version_major": 2, "version_minor": 0 }, From 88d019f1bfac42643d9eb7b1952daa9c7b380b9c Mon Sep 17 00:00:00 2001 From: rinftech-github Date: Fri, 20 Mar 2020 16:30:54 +0200 Subject: [PATCH 004/130] Add Physical lighting model shaders --- js/glsl/mesh-fragment-physical.glsl | 126 ++++++++++++++++++++++++++++ js/glsl/mesh-vertex-physical.glsl | 109 ++++++++++++++++++++++++ js/src/mesh.ts | 8 +- notebooks/lighting_demo.ipynb | 8 +- 4 files changed, 243 insertions(+), 8 deletions(-) create mode 100644 js/glsl/mesh-fragment-physical.glsl create mode 100644 js/glsl/mesh-vertex-physical.glsl diff --git a/js/glsl/mesh-fragment-physical.glsl b/js/glsl/mesh-fragment-physical.glsl new file mode 100644 index 00000000..0a05bd3d --- /dev/null +++ b/js/glsl/mesh-fragment-physical.glsl @@ -0,0 +1,126 @@ +#extension GL_OES_standard_derivatives : enable +#define PHYSICAL + +uniform vec3 diffuse; +uniform vec3 emissive; +uniform float roughness; +uniform float metalness; +uniform float opacity; + +#ifndef STANDARD + uniform float clearCoat; + uniform float clearCoatRoughness; +#endif + +varying vec3 vViewPosition; + +#ifndef FLAT_SHADED + + varying vec3 vNormal; + +#endif + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +////////////////////////////////////////////////////////////////// +varying vec4 vertex_color; +varying vec3 vertex_position; +varying vec2 vertex_uv; + +#ifdef USE_TEXTURE + uniform sampler2D texture; + uniform sampler2D texture_previous; + uniform float animation_time_texture; +#endif + +void main() +{ + vec4 finalColor2 = vec4( 0.0, 0.0, 0.0, 1.0 ); + +#ifdef USE_RGB + finalColor2 = vec4( vertex_color.rgb, 1.0 ); +#else +#ifdef AS_LINE + finalColor2 = vec4( vertex_color.rgb, vertex_color.a ); +#else + + vec3 fdx_ = dFdx( vertex_position ); + vec3 fdy_ = dFdy( vertex_position ); + vec3 normal_position = normalize( cross( fdx_, fdy_ ) ); + float diffuse_ = dot( normal_position, vec3( 0.0, 0.0, 1.0 ) ); + +#ifdef USE_TEXTURE + vec4 sample = mix( texture2D( texture_previous, vertex_uv ), texture2D( texture, vertex_uv ), animation_time_texture ); + finalColor2 = vec4( clamp(diffuse_, 0.2, 1.) * sample.rgb, 1.0 ); +#else + finalColor2 = vec4( clamp(diffuse_, 0.2, 1.) * vertex_color.rgb, vertex_color.a ); +#endif // USE_TEXTURE +#endif // AS_LINE +#endif // USE_RGB + +////////////////////////////////////////////////////// + + #include + + vec4 diffuseColor = vec4( diffuse, opacity ); + ReflectedLight reflectedLight = ReflectedLight( vec3( 0.0 ), vec3( 0.0 ), vec3( 0.0 ), vec3( 0.0 ) ); + vec3 totalEmissiveRadiance = emissive; + + #include + #include + #include + #include + #include + #include + #include + #include + #include + #include + + // accumulation + #include + #include + #include + #include + + // modulation + #include + + vec3 outgoingLight = reflectedLight.directDiffuse + reflectedLight.indirectDiffuse + reflectedLight.directSpecular + reflectedLight.indirectSpecular + totalEmissiveRadiance; + + gl_FragColor = vec4( outgoingLight, diffuseColor.a ); + + #include + #include + #include + #include + #include + + //gl_FragColor = 0.75 * gl_FragColor + 0.25 * finalColor2; + //gl_FragColor = 0.25 * gl_FragColor + 0.75 * finalColor2; + +} \ No newline at end of file diff --git a/js/glsl/mesh-vertex-physical.glsl b/js/glsl/mesh-vertex-physical.glsl new file mode 100644 index 00000000..8c6cd6d4 --- /dev/null +++ b/js/glsl/mesh-vertex-physical.glsl @@ -0,0 +1,109 @@ +#extension GL_OES_standard_derivatives : enable +#define PHYSICAL + + // for animation, all between 0 and 1 +uniform float animation_time_x; +uniform float animation_time_y; +uniform float animation_time_z; +uniform float animation_time_u; +uniform float animation_time_v; +uniform float animation_time_color; + +uniform vec2 xlim; +uniform vec2 ylim; +uniform vec2 zlim; + +varying vec4 vertex_color; +varying vec3 vertex_position; + +attribute vec3 position_previous; + +#ifdef USE_TEXTURE + attribute float u; + attribute float v; + attribute float u_previous; + attribute float v_previous; + varying vec2 vertex_uv; +#endif + +attribute vec4 color_current; +attribute vec4 color_previous; + +//LAMBERT +//////////////////////////////////////////////////////////////////////////////// +varying vec3 vViewPosition; + +#ifndef FLAT_SHADED + + varying vec3 vNormal; + +#endif + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +//////////////////////////////////////////////////////////////////////////////// +void main() +{ + #include + #include + #include + + #include + #include + #include + #include + #include + +#ifndef FLAT_SHADED // Normal computed with derivatives when FLAT_SHADED + + vNormal = normalize( transformedNormal ); + +#endif + + #include + #include + #include + #include + #include + #include + #include + + vViewPosition = - mvPosition.xyz; + + #include + #include + #include + + vec3 origin = vec3(xlim.x, ylim.x, zlim.x); + vec3 size_viewport = vec3(xlim.y, ylim.y, zlim.y) - origin; + + vec3 pos = (mix(position_previous, position, vec3(animation_time_x, animation_time_y, animation_time_z)) + - origin) / size_viewport - 0.5; + gl_Position = projectionMatrix * + modelViewMatrix * + vec4(pos,1.0); + vec3 positionEye = ( modelViewMatrix * vec4( pos, 1.0 ) ).xyz; + vertex_position = positionEye; + + //vViewPosition = vertex_position;//!!! + +#ifdef USE_TEXTURE + vertex_uv = vec2(mix(u_previous, u, animation_time_u), mix(v_previous, v, animation_time_v)); +#endif + +#ifdef USE_RGB + vertex_color = vec4(pos + vec3(0.5, 0.5, 0.5), 1.0); +#else + vertex_color = mix(color_previous, color_current, animation_time_color); +#endif + +} diff --git a/js/src/mesh.ts b/js/src/mesh.ts index 700d680b..32b6cc46 100644 --- a/js/src/mesh.ts +++ b/js/src/mesh.ts @@ -141,7 +141,7 @@ class MeshView extends widgets.WidgetView { slTest.shadow.mapSize.height = 512; // default slTest.shadow.camera.near = 0.5; // default slTest.shadow.camera.far = 500 // default - slTest.shadow.bias = -0.00005; // prevent shadow acne + slTest.shadow.bias = -0.0005; // prevent shadow acne this.renderer.scene_scatter.add(slTest); @@ -165,7 +165,7 @@ class MeshView extends widgets.WidgetView { var amTest = new THREE.AmbientLight(0x000000, globalIntensity); - amTest.color = new THREE.Color(1,0,0); + amTest.color = new THREE.Color(0,0,1); amTest.intensity = 0.1; this.renderer.scene_scatter.add(amTest); @@ -370,8 +370,8 @@ class MeshView extends widgets.WidgetView { this.line_material_rgb.visible = this.line_material.visible && this.model.get("visible"); this.materials.forEach((material) => { material.uniforms = this.uniforms; - material.vertexShader = require("raw-loader!../glsl/mesh-vertex-lambert.glsl"); - material.fragmentShader = require("raw-loader!../glsl/mesh-fragment-lambert.glsl"); + material.vertexShader = require("raw-loader!../glsl/mesh-vertex-physical.glsl"); + material.fragmentShader = require("raw-loader!../glsl/mesh-fragment-physical.glsl"); material.depthWrite = true; material.transparant = true; material.depthTest = true; diff --git a/notebooks/lighting_demo.ipynb b/notebooks/lighting_demo.ipynb index c79ff250..22383d16 100644 --- a/notebooks/lighting_demo.ipynb +++ b/notebooks/lighting_demo.ipynb @@ -2,18 +2,18 @@ "cells": [ { "cell_type": "code", - "execution_count": 4, + "execution_count": 1, "metadata": {}, "outputs": [ { "data": { "application/vnd.jupyter.widget-view+json": { - "model_id": "9598bda6f9ee4eb28377396d2a5543a8", + "model_id": "c870b981a06f4582963670de93ed9028", "version_major": 2, "version_minor": 0 }, "text/plain": [ - "VBox(children=(Figure(camera=PerspectiveCamera(aspect=0.8, fov=45.0, position=(1.1851781814701616, 0.210322184…" + "VBox(children=(Figure(camera=PerspectiveCamera(fov=45.0, position=(0.0, 0.0, 2.0), quaternion=(0.0, 0.0, 0.0, …" ] }, "metadata": {}, @@ -22,7 +22,7 @@ { "data": { "application/vnd.jupyter.widget-view+json": { - "model_id": "f87c295e66234970b2275fac86eb3dd2", + "model_id": "1a04d22b087749778202bc24d7bab157", "version_major": 2, "version_minor": 0 }, From 5918a74b9d0ca8480097aafc8f98b947bd358395 Mon Sep 17 00:00:00 2001 From: rinftech-github <61880865+rinftech-github@users.noreply.github.com> Date: Mon, 23 Mar 2020 18:59:28 +0200 Subject: [PATCH 005/130] Create widgets_test.ipynb Sliders and color picker widgets added --- notebooks/widgets_test.ipynb | 279 +++++++++++++++++++++++++++++++++++ 1 file changed, 279 insertions(+) create mode 100644 notebooks/widgets_test.ipynb diff --git a/notebooks/widgets_test.ipynb b/notebooks/widgets_test.ipynb new file mode 100644 index 00000000..c8757971 --- /dev/null +++ b/notebooks/widgets_test.ipynb @@ -0,0 +1,279 @@ +{ + "cells": [ + { + "cell_type": "code", + "execution_count": 154, + "metadata": { + "scrolled": true + }, + "outputs": [ + { + "data": { + "application/vnd.jupyter.widget-view+json": { + "model_id": "85d781df0a4248b79d42992e2d1738a3", + "version_major": 2, + "version_minor": 0 + }, + "text/plain": [ + "Box(children=(Box(children=(Label(value='Choose size:'), FloatSlider(value=5.0, max=10.0, readout_format='d', …" + ] + }, + "metadata": {}, + "output_type": "display_data" + } + ], + "source": [ + "from ipywidgets import Layout, Button, Box, FloatText, Textarea, Dropdown, Label, FloatSlider\n", + "\n", + "form_item_layout = Layout(\n", + " display='flex',\n", + " flex_flow='row',\n", + " justify_content='space-between'\n", + ")\n", + "\n", + "selected_size = FloatSlider(value=5, min=0, max=10, step=1, disabled=False,\n", + " continuous_update=True,orientation='horizontal', readout=True, readout_format='d')\n", + "select_color = ColorPicker(concise=False, value='red', disabled=False)\n", + "opacity = FloatSlider(value=0.5, min=0.0, max=10.0, step=0.1, disabled=False,\n", + " continuous_update=True,orientation='horizontal', readout=True, readout_format='d')\n", + "brightness = FloatSlider(value=0.5, min=0.0, max=10.0, step=0.1, disabled=False,\n", + " continuous_update=True,orientation='horizontal', readout=True, readout_format='d')\n", + "\n", + "form_items = [\n", + " Box([Label(value='Choose size:'), selected_size], layout=form_item_layout),\n", + " Box([Label(value='Pick a color'), select_color], layout=form_item_layout),\n", + " Box([Label(value='Choose opacity level:'), opacity], layout=form_item_layout),\n", + " Box([Label(value='Choose brightness level'), brightness], layout=form_item_layout)\n", + " ]\n", + "\n", + "form = Box(form_items, layout=Layout(\n", + " display='flex',\n", + " flex_flow='column',\n", + " border='solid 2px',\n", + " align_items='stretch',\n", + " width='50%'\n", + "))\n", + "form\n", + "\n", + "\n", + "\n", + "\n", + "\n", + " " + ] + }, + { + "cell_type": "code", + "execution_count": 128, + "metadata": {}, + "outputs": [ + { + "data": { + "application/vnd.jupyter.widget-view+json": { + "model_id": "589933449bba4f5b93b0d3f5037ebd1e", + "version_major": 2, + "version_minor": 0 + }, + "text/plain": [ + "interactive(children=(FloatSlider(value=5.0, description='Choose size for x:', max=10.0, readout_format='d', s…" + ] + }, + "metadata": {}, + "output_type": "display_data" + }, + { + "data": { + "application/vnd.jupyter.widget-view+json": { + "model_id": "14c8f57fc53e41e68b9a82e6da8c401d", + "version_major": 2, + "version_minor": 0 + }, + "text/plain": [ + "FloatSlider(value=5.0, description='Change size:', max=10.0, readout_format='d', step=1.0)" + ] + }, + "metadata": {}, + "output_type": "display_data" + }, + { + "data": { + "text/plain": [ + "5.0" + ] + }, + "metadata": {}, + "output_type": "display_data" + } + ], + "source": [ + "import ipywidgets as widgets\n", + "from ipywidgets import interact, interactive, fixed, interact_manual\n", + "\n", + "\n", + "#Changing size axes\n", + "\n", + "x_size = FloatSlider(value=5, min=0, max=10, step=1, description='Choose size for x:', disabled=False,\n", + " continuous_update=True,orientation='horizontal', readout=True, readout_format='d')\n", + "\n", + "y_size = FloatSlider(value=5, min=0, max=10, step=1, description='Choose size for y:', disabled=False,\n", + " continuous_update=True,orientation='horizontal', readout=True, readout_format='d')\n", + "\n", + "z_size = FloatSlider(value=5, min=0, max=10, step=1, description='Choose size for z:', disabled=False,\n", + " continuous_update=True,orientation='horizontal', readout=True, readout_format='d')\n", + "\n", + "def update_axes_range(*args):\n", + " #change size \n", + " #multiplier_x = 2.0\n", + " #multiplier_y = 3.0\n", + " #multiplier_z = 4.0\n", + " x_size.max = multiplier_x * y_size.value\n", + " z_size.max = multiplier_z * y_size.value\n", + "y_size.observe(update_axes_range, 'value')\n", + "\n", + "def printer(x, y, z):\n", + " print(x, y, z)\n", + " \n", + "widgets.interact(printer,x=x_size, y=y_size, z=z_size);\n", + "\n", + "\n", + "#Changing size \n", + "\n", + "selected_size = FloatSlider(value=5, min=0, max=10, step=1, description='Change size:', disabled=False,\n", + " continuous_update=True,orientation='horizontal', readout=True, readout_format='d')\n", + "\n", + "display(selected_size, selected_size.value)\n", + "\n", + "\n", + "\n", + "\n", + "\n", + "\n" + ] + }, + { + "cell_type": "code", + "execution_count": 139, + "metadata": {}, + "outputs": [ + { + "data": { + "application/vnd.jupyter.widget-view+json": { + "model_id": "650e7b9a51e1458ba0d0b9ede74f9aef", + "version_major": 2, + "version_minor": 0 + }, + "text/plain": [ + "interactive(children=(ColorPicker(value='red', description='Pick a color'), ColorPicker(value='green', descrip…" + ] + }, + "metadata": {}, + "output_type": "display_data" + }, + { + "data": { + "text/plain": [ + "" + ] + }, + "execution_count": 139, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "import ipywidgets as widgets\n", + "from ipywidgets import interact, interactive, fixed, interact_manual\n", + "\n", + "#select a color for plot \n", + "select_color1 = ColorPicker(concise=False, description='Pick a color', value='red', disabled=False)\n", + "select_color2 = ColorPicker(concise=False, description='Pick a color', value='green', disabled=False)\n", + "\n", + "def color_display(First_color, Second_color):\n", + " display(First_color, Second_color) \n", + " \n", + "widgets.interact(color_display(), First_color = select_color1, Second_color = select_color2)" + ] + }, + { + "cell_type": "code", + "execution_count": 153, + "metadata": {}, + "outputs": [ + { + "data": { + "application/vnd.jupyter.widget-view+json": { + "model_id": "ba6cf680a02240d8b04835534551dd0d", + "version_major": 2, + "version_minor": 0 + }, + "text/plain": [ + "FloatSlider(value=0.5, description='Choose opacity level:', max=10.0, readout_format='d')" + ] + }, + "metadata": {}, + "output_type": "display_data" + }, + { + "data": { + "application/vnd.jupyter.widget-view+json": { + "model_id": "936b7817b5a546a795ab188883503250", + "version_major": 2, + "version_minor": 0 + }, + "text/plain": [ + "FloatSlider(value=0.5, description='Choose brightness level:', max=10.0, readout_format='d')" + ] + }, + "metadata": {}, + "output_type": "display_data" + } + ], + "source": [ + "import ipywidgets as widgets\n", + "from ipywidgets import interact, interactive, fixed, interact_manual\n", + "\n", + "opacity = FloatSlider(value=0.5, min=0.0, max=10.0, step=0.1, description='Choose opacity level:', disabled=False,\n", + " continuous_update=True,orientation='horizontal', readout=True, readout_format='d')\n", + "brightness = FloatSlider(value=0.5, min=0.0, max=10.0, step=0.1, description='Choose brightness level:', disabled=False,\n", + " continuous_update=True,orientation='horizontal', readout=True, readout_format='d')\n", + "\n", + "display(opacity, brightness)" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [] + }, + { + "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.7.6" + } + }, + "nbformat": 4, + "nbformat_minor": 4 +} From 6cb99641e2474ab1cd5980cb364bb94e8747eff6 Mon Sep 17 00:00:00 2001 From: rinftech-github <61880865+rinftech-github@users.noreply.github.com> Date: Wed, 25 Mar 2020 17:41:24 +0200 Subject: [PATCH 006/130] Update widgets --- notebooks/widgets_test.ipynb | 116 ++++++++++++++++++++++------------- 1 file changed, 72 insertions(+), 44 deletions(-) diff --git a/notebooks/widgets_test.ipynb b/notebooks/widgets_test.ipynb index c8757971..5cbb328d 100644 --- a/notebooks/widgets_test.ipynb +++ b/notebooks/widgets_test.ipynb @@ -2,7 +2,7 @@ "cells": [ { "cell_type": "code", - "execution_count": 154, + "execution_count": 24, "metadata": { "scrolled": true }, @@ -10,12 +10,12 @@ { "data": { "application/vnd.jupyter.widget-view+json": { - "model_id": "85d781df0a4248b79d42992e2d1738a3", + "model_id": "e0e8ef90458e4ed88dee990d4d1073af", "version_major": 2, "version_minor": 0 }, "text/plain": [ - "Box(children=(Box(children=(Label(value='Choose size:'), FloatSlider(value=5.0, max=10.0, readout_format='d', …" + "Tab(children=(ColorPicker(value='green', description='Pick a color'), FloatSlider(value=50.0, description='Adj…" ] }, "metadata": {}, @@ -23,54 +23,35 @@ } ], "source": [ - "from ipywidgets import Layout, Button, Box, FloatText, Textarea, Dropdown, Label, FloatSlider\n", - "\n", - "form_item_layout = Layout(\n", - " display='flex',\n", - " flex_flow='row',\n", - " justify_content='space-between'\n", - ")\n", - "\n", - "selected_size = FloatSlider(value=5, min=0, max=10, step=1, disabled=False,\n", - " continuous_update=True,orientation='horizontal', readout=True, readout_format='d')\n", - "select_color = ColorPicker(concise=False, value='red', disabled=False)\n", - "opacity = FloatSlider(value=0.5, min=0.0, max=10.0, step=0.1, disabled=False,\n", - " continuous_update=True,orientation='horizontal', readout=True, readout_format='d')\n", - "brightness = FloatSlider(value=0.5, min=0.0, max=10.0, step=0.1, disabled=False,\n", - " continuous_update=True,orientation='horizontal', readout=True, readout_format='d')\n", - "\n", - "form_items = [\n", - " Box([Label(value='Choose size:'), selected_size], layout=form_item_layout),\n", - " Box([Label(value='Pick a color'), select_color], layout=form_item_layout),\n", - " Box([Label(value='Choose opacity level:'), opacity], layout=form_item_layout),\n", - " Box([Label(value='Choose brightness level'), brightness], layout=form_item_layout)\n", - " ]\n", - "\n", - "form = Box(form_items, layout=Layout(\n", - " display='flex',\n", - " flex_flow='column',\n", - " border='solid 2px',\n", - " align_items='stretch',\n", - " width='50%'\n", - "))\n", - "form\n", - "\n", + "from ipywidgets import Layout, Button, VBox, FloatText, Textarea, Dropdown, Label, FloatSlider, ColorPicker\n", + "from ipywidgets import widgets\n", "\n", + "select_size = FloatSlider(value=50, min=0, max=100, step=0.1, description='Adjust size:', disabled=False,\n", + " continuous_update=True,orientation='horizontal', readout=True, readout_format='.2f')\n", + "select_color = ColorPicker(concise=False, description='Pick a color', value='green', disabled=False)\n", + "opacity = FloatSlider(value=0.75, min=0.00, max=1.0, step=0.01, description='Adjust level:', disabled=False,\n", + " continuous_update=True,orientation='horizontal', readout=True, readout_format='.2f')\n", + "brightness = FloatSlider(value=0.25, min=0.00, max=1.0, step=0.01, description='Adjust level:', disabled=False,\n", + " continuous_update=True,orientation='horizontal', readout=True, readout_format='.2f')\n", "\n", - "\n", - "\n", - " " + "children = [select_color, select_size, brightness, opacity]\n", + "tab = widgets.Tab(children)\n", + "tab.set_title(0, 'Color')\n", + "tab.set_title(1, 'Size')\n", + "tab.set_title(2, 'Brightness')\n", + "tab.set_title(3, 'Opacity')\n", + "tab\n" ] }, { "cell_type": "code", - "execution_count": 128, + "execution_count": 10, "metadata": {}, "outputs": [ { "data": { "application/vnd.jupyter.widget-view+json": { - "model_id": "589933449bba4f5b93b0d3f5037ebd1e", + "model_id": "", "version_major": 2, "version_minor": 0 }, @@ -84,7 +65,7 @@ { "data": { "application/vnd.jupyter.widget-view+json": { - "model_id": "14c8f57fc53e41e68b9a82e6da8c401d", + "model_id": "", "version_major": 2, "version_minor": 0 }, @@ -242,10 +223,57 @@ }, { "cell_type": "code", - "execution_count": null, + "execution_count": 25, "metadata": {}, - "outputs": [], - "source": [] + "outputs": [ + { + "data": { + "application/vnd.jupyter.widget-view+json": { + "model_id": "9ae54036017c4dcdb9d1e46a656ed656", + "version_major": 2, + "version_minor": 0 + }, + "text/plain": [ + "Box(children=(Box(children=(Label(value='Choose size:'), FloatSlider(value=5.0, max=10.0, readout_format='d', …" + ] + }, + "metadata": {}, + "output_type": "display_data" + } + ], + "source": [ + "from ipywidgets import Layout, Button, Box, FloatText, Textarea, Dropdown, Label, FloatSlider\n", + "\n", + "form_item_layout = Layout(\n", + " display='flex',\n", + " flex_flow='row',\n", + " justify_content='space-between'\n", + ")\n", + "\n", + "selected_size = FloatSlider(value=5, min=0, max=10, step=1, disabled=False,\n", + " continuous_update=True,orientation='horizontal', readout=True, readout_format='d')\n", + "select_color = ColorPicker(concise=False, value='red', disabled=False)\n", + "opacity = FloatSlider(value=0.5, min=0.0, max=10.0, step=0.1, disabled=False,\n", + " continuous_update=True,orientation='horizontal', readout=True, readout_format='d')\n", + "brightness = FloatSlider(value=0.5, min=0.0, max=10.0, step=0.1, disabled=False,\n", + " continuous_update=True,orientation='horizontal', readout=True, readout_format='d')\n", + "\n", + "form_items = [\n", + " Box([Label(value='Choose size:'), selected_size], layout=form_item_layout),\n", + " Box([Label(value='Pick a color'), select_color], layout=form_item_layout),\n", + " Box([Label(value='Choose opacity level:'), opacity], layout=form_item_layout),\n", + " Box([Label(value='Choose brightness level'), brightness], layout=form_item_layout)\n", + " ]\n", + "\n", + "form = Box(form_items, layout=Layout(\n", + " display='flex',\n", + " flex_flow='column',\n", + " border='solid 2px',\n", + " align_items='stretch',\n", + " width='50%'\n", + "))\n", + "form" + ] }, { "cell_type": "code", From 5d739f2f5c8fb6b6794320547d84042d63a9c334 Mon Sep 17 00:00:00 2001 From: rinftech-github Date: Thu, 26 Mar 2020 20:21:05 +0200 Subject: [PATCH 007/130] Add Light widget classes. Add ambiental_light functionality --- ipyvolume/pylab.py | 33 ++++++++++++ ipyvolume/widgets.py | 20 ++++++- js/src/embed.ts | 1 + js/src/figure.ts | 44 ++++++++++++++++ js/src/index.ts | 1 + js/src/light.ts | 98 +++++++++++++++++++++++++++++++++++ js/src/mesh.ts | 12 +++-- notebooks/lighting_demo.ipynb | 7 ++- 8 files changed, 208 insertions(+), 8 deletions(-) create mode 100644 js/src/light.ts diff --git a/ipyvolume/pylab.py b/ipyvolume/pylab.py index af971ec6..7b65846b 100644 --- a/ipyvolume/pylab.py +++ b/ipyvolume/pylab.py @@ -40,6 +40,11 @@ 'style', 'plot_plane', 'selector_default', + 'ambient_light', + 'directional_light', + 'spot_light', + 'point_light', + 'hemisphere_light' ] import os @@ -1511,3 +1516,31 @@ def _make_triangles_lines(shape, wrapx=False, wrapy=False): lines[3::4, 0], lines[3::4, 1] = t1[1], t2[1] return triangles, lines + +def ambient_light(color=default_color, intensity = 1): + print("ADD AMBIENT LIGHT (from pylab) " + color) + + light = ipv.Light(color=color, intensity=intensity, light_type='AMBIENTAL') + fig = gcf() + fig.lights = fig.lights + [light] + + return light + + +def directional_light(color=default_color, intensity = 1): + print("ADD DIRECTIONAL LIGHT (from pylab) " + color) + + light = ipv.Light(color=color, intensity=intensity, light_type='DIRECTIONAL') + fig = gcf() + fig.lights = fig.lights + [light] + + return light + +def spot_light(): + return 0 + +def point_light(): + return 0 + +def hemisphere_light(): + return 0 \ No newline at end of file diff --git a/ipyvolume/widgets.py b/ipyvolume/widgets.py index 39f2e267..5b0ac75a 100644 --- a/ipyvolume/widgets.py +++ b/ipyvolume/widgets.py @@ -2,7 +2,7 @@ from __future__ import absolute_import -__all__ = ['Mesh', 'Scatter', 'Volume', 'Figure', 'quickquiver', 'quickscatter', 'quickvolshow'] +__all__ = ['Light', 'Mesh', 'Scatter', 'Volume', 'Figure', 'quickquiver', 'quickscatter', 'quickvolshow'] import logging import warnings @@ -217,7 +217,22 @@ def _update_data(self): self.data = np.array(data_view) self.extent = extent +@widgets.register +class Light(widgets.Widget): + """Widget class representing light addition to scene using three.js.""" + _view_name = Unicode('LightView').tag(sync=True) + _view_module = Unicode('ipyvolume').tag(sync=True) + _model_name = Unicode('LightModel').tag(sync=True) + _model_module = Unicode('ipyvolume').tag(sync=True) + _view_module_version = Unicode(semver_range_frontend).tag(sync=True) + _model_module_version = Unicode(semver_range_frontend).tag(sync=True) + + color = Array(default_value="red", allow_none=True).tag(sync=True, **color_serialization) + intensity = traitlets.CFloat(1).tag(sync=True) + light_type = traitlets.Enum(values=['AMBIENTAL', 'DIRECTIONAL', 'SPOT', 'POINT', 'HEMISPHERE'], default_value='AMBIENTAL').tag(sync=True) + + @widgets.register class Figure(ipywebrtc.MediaStream): """Widget class representing a volume (rendering) using three.js.""" @@ -240,6 +255,9 @@ class Figure(ipywebrtc.MediaStream): volumes = traitlets.List(traitlets.Instance(Volume), [], allow_none=False).tag( sync=True, **widgets.widget_serialization ) + lights = traitlets.List(traitlets.Instance(Light), [], allow_none=False).tag( + sync=True, **widgets.widget_serialization + ) animation = traitlets.Float(1000.0).tag(sync=True) animation_exponent = traitlets.Float(1.0).tag(sync=True) diff --git a/js/src/embed.ts b/js/src/embed.ts index 9966492b..1bfabada 100644 --- a/js/src/embed.ts +++ b/js/src/embed.ts @@ -10,6 +10,7 @@ export * from "./tf"; export * from "./scatter"; export * from "./volume"; export * from "./mesh"; +export * from "./light"; export * from "./utils"; export * from "./selectors"; export * from "./values"; diff --git a/js/src/figure.ts b/js/src/figure.ts index fda432aa..1fc1dd72 100644 --- a/js/src/figure.ts +++ b/js/src/figure.ts @@ -5,6 +5,7 @@ import * as Mustache from "mustache"; import * as screenfull from "screenfull"; import * as THREE from "three"; import * as THREEtext2d from "three-text2d"; +import { LightModel, LightView} from "./light"; import { MeshModel, MeshView} from "./mesh"; import { ScatterModel, ScatterView} from "./scatter"; import { copy_image_to_clipboard, download_image, select_text} from "./utils"; @@ -103,6 +104,7 @@ class FigureModel extends widgets.DOMWidgetModel { static serializers = {...widgets.DOMWidgetModel.serializers, scatters: { deserialize: widgets.unpack_models }, meshes: { deserialize: widgets.unpack_models }, + lights: { deserialize: widgets.unpack_models }, volumes: { deserialize: widgets.unpack_models }, camera: { deserialize: widgets.unpack_models }, scene: { deserialize: widgets.unpack_models }, @@ -130,6 +132,7 @@ class FigureModel extends widgets.DOMWidgetModel { displayscale: 1, scatters: null, meshes: null, + lights: null, volumes: null, show: "Volume", xlim: [0., 1.], @@ -208,6 +211,7 @@ class FigureView extends widgets.DOMWidgetView { mesh_views: { [key: string]: MeshView }; scatter_views: { [key: string]: ScatterView }; volume_views: { [key: string]: VolumeView }; + light_views: { [key: string]: LightView }; volume_back_target: THREE.WebGLRenderTarget; geometry_depth_target: THREE.WebGLRenderTarget; color_pass_target: THREE.WebGLRenderTarget; @@ -654,6 +658,7 @@ class FigureView extends widgets.DOMWidgetView { this.mesh_views = {}; this.scatter_views = {}; this.volume_views = {}; + this.light_views = {}; let render_width = width; const render_height = height; @@ -859,6 +864,8 @@ class FigureView extends widgets.DOMWidgetView { this.update_meshes(); this.model.on("change:volumes", this.update_volumes, this); this.update_volumes(); + this.model.on("change:lights", this.update_lights, this); + this.update_lights(); this.update_size(); @@ -1509,6 +1516,43 @@ class FigureView extends widgets.DOMWidgetView { } } + update_lights() { + const lights = this.model.get("lights") as LightModel[]; + if (lights.length !== 0) { // So now check if list has length 0 + const current_light_cids = []; + + lights.forEach((light_model) => { + current_light_cids.push(light_model.cid); + if (!(light_model.cid in this.light_views)) { + const options = { + parent: this, + }; + const light_view = new LightView({ + options, + model: light_model, + }); + light_view.render(); + this.light_views[light_model.cid] = light_view; + + } + }); + + // Remove old lights + for (const cid of Object.keys(this.light_views)) { + + const light_view = this.light_views[cid]; + if (current_light_cids.indexOf(cid) === -1) { + light_view.remove_from_scene(); + delete this.light_views[cid]; + } + + } + } else { + this.light_views = {}; + } + + } + transition(f, on_done, context) { const that = this; const exp = that.model.get("animation_exponent"); diff --git a/js/src/index.ts b/js/src/index.ts index f295b71a..8074b066 100644 --- a/js/src/index.ts +++ b/js/src/index.ts @@ -14,6 +14,7 @@ export * from "./tf"; export * from "./scatter"; export * from "./volume"; export * from "./mesh"; +export * from "./light"; export * from "./utils"; export * from "./selectors"; export * from "./values"; diff --git a/js/src/light.ts b/js/src/light.ts new file mode 100644 index 00000000..58d71e4c --- /dev/null +++ b/js/src/light.ts @@ -0,0 +1,98 @@ +import * as widgets from "@jupyter-widgets/base"; +import { isArray, isNumber } from "lodash"; +import * as THREE from "three"; +import { FigureView } from "./figure"; +import * as serialize from "./serialize.js"; +import { semver_range } from "./utils"; +import * as values from "./values.js"; +import { randomBates } from "d3"; + + +export +class LightView extends widgets.WidgetView { + + LIGHT_TYPES: any; + renderer: FigureView; + lights: any; + current_light: any; + color: any; + intensity: any; + light_type: any; + + render() { + + this.LIGHT_TYPES = { + AMBIENTAL: 'AMBIENTAL', + DIRECTIONAL: 'DIRECTIONAL', + SPOT: 'SPOT', + POINT: 'POINT', + HEMISPHERE: 'HEMISPHERE' + }; + this.renderer = this.options.parent; + + this.model.on("change:color", + this.on_change, this); + + console.log(this.LIGHT_TYPES); + this.create_light(); + this.add_to_scene(); + } + + on_change(attribute) { + for (const key of this.model.changedAttributes()) { + console.log("changed " +key); + } + } + + add_to_scene() { + this.lights.forEach((light) => { + this.renderer.scene_scatter.add(light); + }); + + } + + remove_from_scene() { + this.lights.forEach((light) => { + this.renderer.scene_scatter.remove(light); + }); + + } + + create_light() { + this.lights = []; + + this.color = this.model.get("color"); + this.intensity = this.model.get("intensity"); + this.light_type = this.model.get("light_type"); + + if(this.light_type === this.LIGHT_TYPES.AMBIENTAL){ + console.log("Create Ambient Light w color " + this.color + " intensity : " + this.intensity); + this.current_light = new THREE.AmbientLight(this.color, this.intensity); + this.lights.push(this.current_light); + } + + } +} + +export +class LightModel extends widgets.WidgetModel { + static serializers = { + ...widgets.WidgetModel.serializers, + color: serialize.color_or_json, + intensity: serialize.array_or_json, + }; + defaults() { + return { + ...super.defaults(), + _model_name : "LightModel", + _view_name : "LightView", + _model_module : "ipyvolume", + _view_module : "ipyvolume", + _model_module_version: semver_range, + _view_module_version: semver_range, + color: "red", + intensity: 1, + light_type: 'AMBIENTAL', + }; + } +} diff --git a/js/src/mesh.ts b/js/src/mesh.ts index 32b6cc46..0bd7d5eb 100644 --- a/js/src/mesh.ts +++ b/js/src/mesh.ts @@ -76,7 +76,7 @@ class MeshView extends widgets.WidgetView { if (this.model.get(name)) { return this.model.get(name).obj.clone(); } else { - const mat = new THREE.MeshPhongMaterial(); + const mat = new THREE.ShaderMaterial(); mat.side = THREE.DoubleSide; mat.flatShading = false; @@ -121,7 +121,7 @@ class MeshView extends widgets.WidgetView { if(this.lightCountTemp == 0) { this.lightCountTemp = 1; - + /* this.renderer.renderer.shadowMap.enabled = true; this.renderer.renderer.shadowMap.type = THREE.PCFSoftShadowMap; @@ -144,7 +144,9 @@ class MeshView extends widgets.WidgetView { slTest.shadow.bias = -0.0005; // prevent shadow acne this.renderer.scene_scatter.add(slTest); - + + */ + /* var dlTest = new THREE.DirectionalLight(0x000000, globalIntensity); dlTest.castShadow = true; @@ -163,12 +165,12 @@ class MeshView extends widgets.WidgetView { this.renderer.scene_scatter.add(dlTest2); */ - + /* var amTest = new THREE.AmbientLight(0x000000, globalIntensity); amTest.color = new THREE.Color(0,0,1); amTest.intensity = 0.1; this.renderer.scene_scatter.add(amTest); - + */ diff --git a/notebooks/lighting_demo.ipynb b/notebooks/lighting_demo.ipynb index 22383d16..48c91d89 100644 --- a/notebooks/lighting_demo.ipynb +++ b/notebooks/lighting_demo.ipynb @@ -22,7 +22,7 @@ { "data": { "application/vnd.jupyter.widget-view+json": { - "model_id": "1a04d22b087749778202bc24d7bab157", + "model_id": "0a6e787cafb8423d93da4fd1cfa05b40", "version_major": 2, "version_minor": 0 }, @@ -274,7 +274,7 @@ " interval=1000,\n", "):\n", " \"\"\"Show one or two Klein bottles.\"\"\"\n", - "\n", + " p3.clear()\n", " # http://paulbourke.net/geometry/klein/\n", " u = np.linspace(0, 2 * pi, num=50, endpoint=endpoint)\n", " v = np.linspace(0, 2 * pi, num=50, endpoint=endpoint)\n", @@ -320,7 +320,10 @@ " if both:\n", " p3.animation_control(mesh, interval=interval)\n", " p3.squarelim()\n", + " \n", " drawSurface(separateFigure=False)\n", + " p3.ambient_light(color='rgb(0,255,0)', intensity=0.5)\n", + " p3.ambient_light(color='blue', intensity=0.55)\n", " p3.show()\n", " return mesh\n", " else:\n", From 74f2eda520f48b404013b98bd158d07519d9c8ba Mon Sep 17 00:00:00 2001 From: rinftech-github Date: Thu, 26 Mar 2020 23:39:14 +0200 Subject: [PATCH 008/130] Add directional_light functionality --- ipyvolume/pylab.py | 15 ++++++++++---- ipyvolume/widgets.py | 10 ++++++++- js/src/light.ts | 38 ++++++++++++++++++++++++++++++++--- notebooks/lighting_demo.ipynb | 4 ++-- 4 files changed, 57 insertions(+), 10 deletions(-) diff --git a/ipyvolume/pylab.py b/ipyvolume/pylab.py index 7b65846b..5ebf1a57 100644 --- a/ipyvolume/pylab.py +++ b/ipyvolume/pylab.py @@ -1520,17 +1520,24 @@ def _make_triangles_lines(shape, wrapx=False, wrapy=False): def ambient_light(color=default_color, intensity = 1): print("ADD AMBIENT LIGHT (from pylab) " + color) - light = ipv.Light(color=color, intensity=intensity, light_type='AMBIENTAL') + light = ipv.Light(color=color, intensity=intensity, light_type='AMBIENT') fig = gcf() fig.lights = fig.lights + [light] return light -def directional_light(color=default_color, intensity = 1): - print("ADD DIRECTIONAL LIGHT (from pylab) " + color) +def directional_light(color=default_color, intensity = 1, position=[0, 1, 0], cast_shadow=False): + print("ADD DIRECTIONAL LIGHT (from pylab) " + color + " "+ str(position[0]) + str(position[1])+ str(position[2])) - light = ipv.Light(color=color, intensity=intensity, light_type='DIRECTIONAL') + light = ipv.Light( + color=color, + intensity=intensity, + cast_shadow=cast_shadow, + light_type='DIRECTIONAL', + position_x=position[0], + position_y=position[1], + position_z=position[2]) fig = gcf() fig.lights = fig.lights + [light] diff --git a/ipyvolume/widgets.py b/ipyvolume/widgets.py index 5b0ac75a..65f5ecde 100644 --- a/ipyvolume/widgets.py +++ b/ipyvolume/widgets.py @@ -230,7 +230,15 @@ class Light(widgets.Widget): color = Array(default_value="red", allow_none=True).tag(sync=True, **color_serialization) intensity = traitlets.CFloat(1).tag(sync=True) - light_type = traitlets.Enum(values=['AMBIENTAL', 'DIRECTIONAL', 'SPOT', 'POINT', 'HEMISPHERE'], default_value='AMBIENTAL').tag(sync=True) + light_type = traitlets.Enum(values=['AMBIENT', 'DIRECTIONAL', 'SPOT', 'POINT', 'HEMISPHERE'], default_value='AMBIENT').tag(sync=True) + + cast_shadow = traitlets.Bool(False).tag(sync=True) + # TODO - should use array serialization instead + #position = Array(default_value=(0, 1, 0), allow_none=True).tag(sync=True, **array_serialization) + position_x = traitlets.CFloat(0).tag(sync=True) + position_y = traitlets.CFloat(1).tag(sync=True) + position_z = traitlets.CFloat(0).tag(sync=True) + @widgets.register diff --git a/js/src/light.ts b/js/src/light.ts index 58d71e4c..7ead0d25 100644 --- a/js/src/light.ts +++ b/js/src/light.ts @@ -18,11 +18,14 @@ class LightView extends widgets.WidgetView { color: any; intensity: any; light_type: any; + cast_shadow: any; + position: any; + target: any; render() { this.LIGHT_TYPES = { - AMBIENTAL: 'AMBIENTAL', + AMBIENT: 'AMBIENT', DIRECTIONAL: 'DIRECTIONAL', SPOT: 'SPOT', POINT: 'POINT', @@ -65,11 +68,27 @@ class LightView extends widgets.WidgetView { this.intensity = this.model.get("intensity"); this.light_type = this.model.get("light_type"); - if(this.light_type === this.LIGHT_TYPES.AMBIENTAL){ + if(this.light_type === this.LIGHT_TYPES.AMBIENT){ console.log("Create Ambient Light w color " + this.color + " intensity : " + this.intensity); this.current_light = new THREE.AmbientLight(this.color, this.intensity); this.lights.push(this.current_light); } + else { + this.cast_shadow = this.model.get("cast_shadow"); + this.position = new THREE.Vector3(this.model.get("position_x"), this.model.get("position_y"), this.model.get("position_z")); + this.target = new THREE.Vector3(this.model.get("target_x"), this.model.get("target_y"), this.model.get("target_z")); + + if(this.light_type === this.LIGHT_TYPES.DIRECTIONAL){ + console.log("Create Directional Light w color " + this.color + " intensity : " + this.intensity + " position :"+ this.position + " cast_shadow: " + this.cast_shadow); + this.current_light = new THREE.DirectionalLight(this.color, this.intensity); + + this.current_light.position.set(this.position.x, this.position.y, this.position.z); + this.current_light.cast_shadow = this.cast_shadow; + this.lights.push(this.current_light); + + } + } + } } @@ -80,6 +99,12 @@ class LightModel extends widgets.WidgetModel { ...widgets.WidgetModel.serializers, color: serialize.color_or_json, intensity: serialize.array_or_json, + position_x: serialize.array_or_json, + position_y: serialize.array_or_json, + position_z: serialize.array_or_json, + target_x: serialize.array_or_json, + target_y: serialize.array_or_json, + target_z: serialize.array_or_json, }; defaults() { return { @@ -92,7 +117,14 @@ class LightModel extends widgets.WidgetModel { _view_module_version: semver_range, color: "red", intensity: 1, - light_type: 'AMBIENTAL', + light_type: 'AMBIENT', + cast_shadow: false, + position_x: 0, + position_y: 1, + position_z: 0, + target_x: 0, + target_y: 0, + target_z: 0, }; } } diff --git a/notebooks/lighting_demo.ipynb b/notebooks/lighting_demo.ipynb index 48c91d89..be75cf61 100644 --- a/notebooks/lighting_demo.ipynb +++ b/notebooks/lighting_demo.ipynb @@ -322,8 +322,8 @@ " p3.squarelim()\n", " \n", " drawSurface(separateFigure=False)\n", - " p3.ambient_light(color='rgb(0,255,0)', intensity=0.5)\n", - " p3.ambient_light(color='blue', intensity=0.55)\n", + " p3.ambient_light(color='rgb(255,0,0)', intensity=0.5)\n", + " p3.directional_light(color='blue', intensity=1.0, position=[0, 5, 10])\n", " p3.show()\n", " return mesh\n", " else:\n", From e30d1fa39acf0600d3b8e0ca9e44eee0ffc983ba Mon Sep 17 00:00:00 2001 From: rinftech-github Date: Fri, 27 Mar 2020 00:04:08 +0200 Subject: [PATCH 009/130] Add hemisphere_light functionality --- ipyvolume/pylab.py | 25 ++++++++++++++++--- ipyvolume/widgets.py | 2 ++ js/src/light.ts | 46 +++++++++++++++++++++++------------ notebooks/lighting_demo.ipynb | 18 ++++++++++---- 4 files changed, 68 insertions(+), 23 deletions(-) diff --git a/ipyvolume/pylab.py b/ipyvolume/pylab.py index 5ebf1a57..ef5bfcbe 100644 --- a/ipyvolume/pylab.py +++ b/ipyvolume/pylab.py @@ -1520,7 +1520,10 @@ def _make_triangles_lines(shape, wrapx=False, wrapy=False): def ambient_light(color=default_color, intensity = 1): print("ADD AMBIENT LIGHT (from pylab) " + color) - light = ipv.Light(color=color, intensity=intensity, light_type='AMBIENT') + light = ipv.Light( + color=color, + intensity=intensity, + light_type='AMBIENT') fig = gcf() fig.lights = fig.lights + [light] @@ -1528,6 +1531,7 @@ def ambient_light(color=default_color, intensity = 1): def directional_light(color=default_color, intensity = 1, position=[0, 1, 0], cast_shadow=False): + print("ADD DIRECTIONAL LIGHT (from pylab) " + color + " "+ str(position[0]) + str(position[1])+ str(position[2])) light = ipv.Light( @@ -1549,5 +1553,20 @@ def spot_light(): def point_light(): return 0 -def hemisphere_light(): - return 0 \ No newline at end of file +def hemisphere_light(color=default_color, color2=default_color_selected, intensity = 1, position=[0, 1, 0], cast_shadow=False): + + print("ADD HEMISPHERE LIGHT (from pylab) " + color + " "+ str(position[0]) + str(position[1])+ str(position[2])) + + light = ipv.Light( + color=color, + color2=color2, + intensity=intensity, + cast_shadow=cast_shadow, + light_type='HEMISPHERE', + position_x=position[0], + position_y=position[1], + position_z=position[2]) + fig = gcf() + fig.lights = fig.lights + [light] + + return light \ No newline at end of file diff --git a/ipyvolume/widgets.py b/ipyvolume/widgets.py index 65f5ecde..4e5aea2f 100644 --- a/ipyvolume/widgets.py +++ b/ipyvolume/widgets.py @@ -229,6 +229,8 @@ class Light(widgets.Widget): _model_module_version = Unicode(semver_range_frontend).tag(sync=True) color = Array(default_value="red", allow_none=True).tag(sync=True, **color_serialization) + color2 = Array(default_value="white", allow_none=True).tag(sync=True, **color_serialization) + intensity = traitlets.CFloat(1).tag(sync=True) light_type = traitlets.Enum(values=['AMBIENT', 'DIRECTIONAL', 'SPOT', 'POINT', 'HEMISPHERE'], default_value='AMBIENT').tag(sync=True) diff --git a/js/src/light.ts b/js/src/light.ts index 7ead0d25..0cbd074d 100644 --- a/js/src/light.ts +++ b/js/src/light.ts @@ -16,6 +16,7 @@ class LightView extends widgets.WidgetView { lights: any; current_light: any; color: any; + color2: any; intensity: any; light_type: any; cast_shadow: any; @@ -67,25 +68,38 @@ class LightView extends widgets.WidgetView { this.color = this.model.get("color"); this.intensity = this.model.get("intensity"); this.light_type = this.model.get("light_type"); - + //no shadow support if(this.light_type === this.LIGHT_TYPES.AMBIENT){ console.log("Create Ambient Light w color " + this.color + " intensity : " + this.intensity); this.current_light = new THREE.AmbientLight(this.color, this.intensity); this.lights.push(this.current_light); } - else { - this.cast_shadow = this.model.get("cast_shadow"); + else{ this.position = new THREE.Vector3(this.model.get("position_x"), this.model.get("position_y"), this.model.get("position_z")); - this.target = new THREE.Vector3(this.model.get("target_x"), this.model.get("target_y"), this.model.get("target_z")); + + //no shadow support + if(this.light_type === this.LIGHT_TYPES.HEMISPHERE) { + this.color2 = this.model.get("color2"); + console.log("Create Hemisphere Light w color " + this.color + " color2 " + this.color2 +" "+ " intensity : " + this.intensity); - if(this.light_type === this.LIGHT_TYPES.DIRECTIONAL){ - console.log("Create Directional Light w color " + this.color + " intensity : " + this.intensity + " position :"+ this.position + " cast_shadow: " + this.cast_shadow); - this.current_light = new THREE.DirectionalLight(this.color, this.intensity); - + this.current_light = new THREE.HemisphereLight(this.color, this.color2, this.intensity); this.current_light.position.set(this.position.x, this.position.y, this.position.z); - this.current_light.cast_shadow = this.cast_shadow; this.lights.push(this.current_light); + } + else { + //with shadow support + this.cast_shadow = this.model.get("cast_shadow"); + + //this.target = new THREE.Vector3(this.model.get("target_x"), this.model.get("target_y"), this.model.get("target_z")); + + if(this.light_type === this.LIGHT_TYPES.DIRECTIONAL){ + console.log("Create Directional Light w color " + this.color + " intensity : " + this.intensity + " position :"+ this.position + " cast_shadow: " + this.cast_shadow); + this.current_light = new THREE.DirectionalLight(this.color, this.intensity); + this.current_light.position.set(this.position.x, this.position.y, this.position.z); + this.current_light.cast_shadow = this.cast_shadow; + this.lights.push(this.current_light); + } } } @@ -98,13 +112,14 @@ class LightModel extends widgets.WidgetModel { static serializers = { ...widgets.WidgetModel.serializers, color: serialize.color_or_json, + color2: serialize.color_or_json, intensity: serialize.array_or_json, position_x: serialize.array_or_json, position_y: serialize.array_or_json, position_z: serialize.array_or_json, - target_x: serialize.array_or_json, - target_y: serialize.array_or_json, - target_z: serialize.array_or_json, + //target_x: serialize.array_or_json, + //target_y: serialize.array_or_json, + //target_z: serialize.array_or_json, }; defaults() { return { @@ -116,15 +131,16 @@ class LightModel extends widgets.WidgetModel { _model_module_version: semver_range, _view_module_version: semver_range, color: "red", + color2: "white", intensity: 1, light_type: 'AMBIENT', cast_shadow: false, position_x: 0, position_y: 1, position_z: 0, - target_x: 0, - target_y: 0, - target_z: 0, + //target_x: 0, + //target_y: 0, + //target_z: 0, }; } } diff --git a/notebooks/lighting_demo.ipynb b/notebooks/lighting_demo.ipynb index be75cf61..bfaeec47 100644 --- a/notebooks/lighting_demo.ipynb +++ b/notebooks/lighting_demo.ipynb @@ -2,13 +2,20 @@ "cells": [ { "cell_type": "code", - "execution_count": 1, + "execution_count": 2, "metadata": {}, "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "ADD HEMISPHERE LIGHT (from pylab) blue 010\n" + ] + }, { "data": { "application/vnd.jupyter.widget-view+json": { - "model_id": "c870b981a06f4582963670de93ed9028", + "model_id": "25f5efd56fb44c68b32ae2575041db9f", "version_major": 2, "version_minor": 0 }, @@ -22,7 +29,7 @@ { "data": { "application/vnd.jupyter.widget-view+json": { - "model_id": "0a6e787cafb8423d93da4fd1cfa05b40", + "model_id": "76fedfec1e6846ab88201a56350abafa", "version_major": 2, "version_minor": 0 }, @@ -322,8 +329,9 @@ " p3.squarelim()\n", " \n", " drawSurface(separateFigure=False)\n", - " p3.ambient_light(color='rgb(255,0,0)', intensity=0.5)\n", - " p3.directional_light(color='blue', intensity=1.0, position=[0, 5, 10])\n", + " #p3.ambient_light(color='rgb(255,0,0)', intensity=0.5)\n", + " #p3.directional_light(color='blue', intensity=1.0, position=[0, 5, 10])\n", + " p3.hemisphere_light(color='blue', color2='yellow', intensity=1.0, position=[1, 1, 0])\n", " p3.show()\n", " return mesh\n", " else:\n", From 0d5f58e13dec2e2d2a4157a6421caf7351d79ea1 Mon Sep 17 00:00:00 2001 From: rinftech-github Date: Fri, 27 Mar 2020 02:11:27 +0200 Subject: [PATCH 010/130] Add spot_light functionality. Add target to directional_light --- ipyvolume/pylab.py | 123 +++++++++++++++++++++++++++------- ipyvolume/widgets.py | 15 ++++- js/src/light.ts | 71 ++++++++++++++++---- notebooks/lighting_demo.ipynb | 23 +++++-- 4 files changed, 186 insertions(+), 46 deletions(-) diff --git a/ipyvolume/pylab.py b/ipyvolume/pylab.py index ef5bfcbe..a0ca5be6 100644 --- a/ipyvolume/pylab.py +++ b/ipyvolume/pylab.py @@ -80,7 +80,7 @@ import ipyvolume as ipv import ipyvolume.embed from ipyvolume import utils - +import math _last_figure = None @@ -1517,56 +1517,131 @@ def _make_triangles_lines(shape, wrapx=False, wrapy=False): return triangles, lines -def ambient_light(color=default_color, intensity = 1): - print("ADD AMBIENT LIGHT (from pylab) " + color) - +def ambient_light(color=default_color_selected, intensity = 1): + print("ADD AMBIENT LIGHT (from pylab) ") + print("color: " + str(color)) + print("intensity: " + str(intensity)) + light = ipv.Light( + light_type='AMBIENT', color=color, - intensity=intensity, - light_type='AMBIENT') + intensity=intensity) + fig = gcf() fig.lights = fig.lights + [light] return light +def hemisphere_light( + color=default_color_selected, + color2=default_color, + intensity = 1, + position=[0, 1, 0], + cast_shadow=False): -def directional_light(color=default_color, intensity = 1, position=[0, 1, 0], cast_shadow=False): + print("ADD HEMISPHERE LIGHT (from pylab) ") + print("color: " + str(color)) + print("color2: " + str(color2)) + print("intensity: " + str(intensity)) + print("position: " + str(position[0])+" "+str(position[1])+" "+str(position[2])) + print("cast_shadow: " + str(cast_shadow)) - print("ADD DIRECTIONAL LIGHT (from pylab) " + color + " "+ str(position[0]) + str(position[1])+ str(position[2])) - light = ipv.Light( + light_type='HEMISPHERE', color=color, + color2=color2, intensity=intensity, - cast_shadow=cast_shadow, - light_type='DIRECTIONAL', position_x=position[0], position_y=position[1], - position_z=position[2]) + position_z=position[2], + cast_shadow=cast_shadow) + fig = gcf() fig.lights = fig.lights + [light] return light -def spot_light(): - return 0 +def directional_light( + color=default_color_selected, + intensity = 1, + position=[0, 1, 0], + target=[0, 0, 0], + cast_shadow=False): -def point_light(): - return 0 + print("ADD DIRECTIONAL LIGHT (from pylab) ") + print("color: " + str(color)) + print("intensity: " + str(intensity)) + print("position: " + str(position[0])+" "+str(position[1])+" "+str(position[2])) + print("target: " + str(target[0])+" "+str(target[1])+" "+str(target[2])) + print("cast_shadow: " + str(cast_shadow)) -def hemisphere_light(color=default_color, color2=default_color_selected, intensity = 1, position=[0, 1, 0], cast_shadow=False): + light = ipv.Light( + light_type='DIRECTIONAL', + color=color, + intensity=intensity, + position_x=position[0], + position_y=position[1], + position_z=position[2], + target_x=target[0], + target_y=target[1], + target_z=target[2], + cast_shadow=cast_shadow) + + fig = gcf() + fig.lights = fig.lights + [light] + + return light + +def spot_light( + color=default_color_selected, + intensity = 1, + position=[0, 1, 0], + target=[0, 0, 0], + angle=math.pi/3, + distance=0, + decay=1, + penumbra=0, + cast_shadow=False): + + print("ADD SPOT LIGHT (from pylab) ") + print("color: " + str(color)) + print("intensity: " + str(intensity)) + print("position: " + str(position[0])+" "+str(position[1])+" "+str(position[2])) + print("target: " + str(target[0])+" "+str(target[1])+" "+str(target[2])) + print("angle: " + str(angle)) + print("distance: " + str(distance)) + print("decay: " + str(decay)) + print("penumbra: " + str(penumbra)) + print("cast_shadow: " + str(cast_shadow)) - print("ADD HEMISPHERE LIGHT (from pylab) " + color + " "+ str(position[0]) + str(position[1])+ str(position[2])) - light = ipv.Light( + light_type='SPOT', color=color, - color2=color2, intensity=intensity, - cast_shadow=cast_shadow, - light_type='HEMISPHERE', position_x=position[0], position_y=position[1], - position_z=position[2]) + position_z=position[2], + target_x=target[0], + target_y=target[1], + target_z=target[2], + angle=angle, + distance=distance, + decay=decay, + penumbra=penumbra, + cast_shadow=cast_shadow) + fig = gcf() fig.lights = fig.lights + [light] - return light \ No newline at end of file + return light + +def point_light(): + return 0 + + + + + + + + diff --git a/ipyvolume/widgets.py b/ipyvolume/widgets.py index 4e5aea2f..a987faad 100644 --- a/ipyvolume/widgets.py +++ b/ipyvolume/widgets.py @@ -28,7 +28,7 @@ ) from ipyvolume.transferfunction import TransferFunction from ipyvolume.utils import debounced, grid_slice, reduce_size - +import math _last_figure = None logger = logging.getLogger("ipyvolume") @@ -230,16 +230,25 @@ class Light(widgets.Widget): color = Array(default_value="red", allow_none=True).tag(sync=True, **color_serialization) color2 = Array(default_value="white", allow_none=True).tag(sync=True, **color_serialization) - + intensity = traitlets.CFloat(1).tag(sync=True) light_type = traitlets.Enum(values=['AMBIENT', 'DIRECTIONAL', 'SPOT', 'POINT', 'HEMISPHERE'], default_value='AMBIENT').tag(sync=True) cast_shadow = traitlets.Bool(False).tag(sync=True) - # TODO - should use array serialization instead + # TODO - should use array serialization instead ? #position = Array(default_value=(0, 1, 0), allow_none=True).tag(sync=True, **array_serialization) position_x = traitlets.CFloat(0).tag(sync=True) position_y = traitlets.CFloat(1).tag(sync=True) position_z = traitlets.CFloat(0).tag(sync=True) + + target_x = traitlets.CFloat(0).tag(sync=True) + target_y = traitlets.CFloat(1).tag(sync=True) + target_z = traitlets.CFloat(0).tag(sync=True) + + distance = traitlets.CFloat(0).tag(sync=True) + angle = traitlets.CFloat(math.pi/3).tag(sync=True) + decay = traitlets.CFloat(1).tag(sync=True) + penumbra = traitlets.CFloat(0).tag(sync=True) diff --git a/js/src/light.ts b/js/src/light.ts index 0cbd074d..aa1d8df9 100644 --- a/js/src/light.ts +++ b/js/src/light.ts @@ -11,17 +11,23 @@ import { randomBates } from "d3"; export class LightView extends widgets.WidgetView { - LIGHT_TYPES: any; renderer: FigureView; - lights: any; + lights: any; //TODO remove current_light: any; + LIGHT_TYPES: any; + light_type: any; + color: any; color2: any; intensity: any; - light_type: any; - cast_shadow: any; position: any; target: any; + angle: any; + distance: any; + decay: any; + penumbra: any; + cast_shadow: any; + render() { @@ -89,17 +95,48 @@ class LightView extends widgets.WidgetView { else { //with shadow support this.cast_shadow = this.model.get("cast_shadow"); + this.angle = this.model.get("angle"); + this.distance = this.model.get("distance"); + this.decay = this.model.get("decay"); + this.penumbra = this.model.get("penumbra"); + + // TODO - move below + this.target = new THREE.Object3D(); + this.target.position.set(this.model.get("target_x"), this.model.get("target_y"), this.model.get("target_z")); + this.target.updateMatrixWorld(); + this.renderer.scene_scatter.add(this.target); - //this.target = new THREE.Vector3(this.model.get("target_x"), this.model.get("target_y"), this.model.get("target_z")); if(this.light_type === this.LIGHT_TYPES.DIRECTIONAL){ console.log("Create Directional Light w color " + this.color + " intensity : " + this.intensity + " position :"+ this.position + " cast_shadow: " + this.cast_shadow); this.current_light = new THREE.DirectionalLight(this.color, this.intensity); this.current_light.position.set(this.position.x, this.position.y, this.position.z); + this.current_light.target = this.target; this.current_light.cast_shadow = this.cast_shadow; this.lights.push(this.current_light); } + else { + if(this.light_type === this.LIGHT_TYPES.SPOT) { + console.log("Create Spot Light w color " + this.color + " intensity : " + this.intensity + " position :"+ this.position + " cast_shadow: " + this.cast_shadow); + + this.current_light = new THREE.SpotLight(this.color, this.intensity); + + this.current_light.position.set(this.position.x, this.position.y, this.position.z); + + this.current_light.target = this.target; + + this.current_light.angle = this.angle; + this.current_light.distance = this.distance; + this.current_light.decay = this.decay; + this.current_light.penumbra = this.penumbra; + this.current_light.cast_shadow = this.cast_shadow; + + this.lights.push(this.current_light); + } + else if(this.light_type === this.LIGHT_TYPES.POINT) { //redundant if, easier to read + } + } } } @@ -117,9 +154,13 @@ class LightModel extends widgets.WidgetModel { position_x: serialize.array_or_json, position_y: serialize.array_or_json, position_z: serialize.array_or_json, - //target_x: serialize.array_or_json, - //target_y: serialize.array_or_json, - //target_z: serialize.array_or_json, + target_x: serialize.array_or_json, + target_y: serialize.array_or_json, + target_z: serialize.array_or_json, + angle: serialize.array_or_json, + distance: serialize.array_or_json, + decay: serialize.array_or_json, + penumbra: serialize.array_or_json, }; defaults() { return { @@ -130,17 +171,21 @@ class LightModel extends widgets.WidgetModel { _view_module : "ipyvolume", _model_module_version: semver_range, _view_module_version: semver_range, + light_type: 'AMBIENT', color: "red", color2: "white", intensity: 1, - light_type: 'AMBIENT', - cast_shadow: false, position_x: 0, position_y: 1, position_z: 0, - //target_x: 0, - //target_y: 0, - //target_z: 0, + target_x: 0, + target_y: 0, + target_z: 0, + angle: Math.PI/3, + distance: 0, + decay: 1, + penumbra: 0, + cast_shadow: false, }; } } diff --git a/notebooks/lighting_demo.ipynb b/notebooks/lighting_demo.ipynb index bfaeec47..3e91f9a9 100644 --- a/notebooks/lighting_demo.ipynb +++ b/notebooks/lighting_demo.ipynb @@ -2,20 +2,29 @@ "cells": [ { "cell_type": "code", - "execution_count": 2, + "execution_count": 13, "metadata": {}, "outputs": [ { "name": "stdout", "output_type": "stream", "text": [ - "ADD HEMISPHERE LIGHT (from pylab) blue 010\n" + "ADD SPOT LIGHT (from pylab) \n", + "color: white\n", + "intensity: 1.0\n", + "position: 20 20 20\n", + "target: -20 -20 -20\n", + "angle: 0.3490658503988659\n", + "distance: 0\n", + "decay: 1\n", + "penumbra: 0\n", + "cast_shadow: False\n" ] }, { "data": { "application/vnd.jupyter.widget-view+json": { - "model_id": "25f5efd56fb44c68b32ae2575041db9f", + "model_id": "a7f22ec1b8ec4fda8f0564369b1d007e", "version_major": 2, "version_minor": 0 }, @@ -29,7 +38,7 @@ { "data": { "application/vnd.jupyter.widget-view+json": { - "model_id": "76fedfec1e6846ab88201a56350abafa", + "model_id": "100b23ea7cfd4af5917398b9d78bbb46", "version_major": 2, "version_minor": 0 }, @@ -57,6 +66,7 @@ "from numpy import cos, sin, pi\n", "#import ipyvolume.pylab as p3\n", "from ipyvolume import pylab as p3\n", + "import math\n", "\n", "try:\n", " import scipy.ndimage\n", @@ -330,8 +340,9 @@ " \n", " drawSurface(separateFigure=False)\n", " #p3.ambient_light(color='rgb(255,0,0)', intensity=0.5)\n", - " #p3.directional_light(color='blue', intensity=1.0, position=[0, 5, 10])\n", - " p3.hemisphere_light(color='blue', color2='yellow', intensity=1.0, position=[1, 1, 0])\n", + " #p3.directional_light(color='blue', intensity=1.0, position=[20, 20, 20], target=[-20,0,20])\n", + " #p3.hemisphere_light(color='blue', color2='yellow', intensity=1.0, position=[1, 1, 0])\n", + " p3.spot_light(color='white', intensity=1.0, position=[20, 20, 20], angle=math.pi/9, target = [-20,-20,-20])\n", " p3.show()\n", " return mesh\n", " else:\n", From b76be11fdaa080761e06852e227c939eab32ab4c Mon Sep 17 00:00:00 2001 From: rinftech-github Date: Fri, 27 Mar 2020 02:40:25 +0200 Subject: [PATCH 011/130] Add point_light functionality --- ipyvolume/pylab.py | 33 ++++++++++++++- js/src/light.ts | 75 ++++++++++++++++++++--------------- notebooks/lighting_demo.ipynb | 20 +++++----- 3 files changed, 82 insertions(+), 46 deletions(-) diff --git a/ipyvolume/pylab.py b/ipyvolume/pylab.py index a0ca5be6..db0ea02b 100644 --- a/ipyvolume/pylab.py +++ b/ipyvolume/pylab.py @@ -1635,8 +1635,37 @@ def spot_light( return light -def point_light(): - return 0 +def point_light( + color=default_color_selected, + intensity = 1, + position=[0, 1, 0], + distance=0, + decay=1, + cast_shadow=False): + + print("ADD POINT LIGHT (from pylab) ") + print("color: " + str(color)) + print("intensity: " + str(intensity)) + print("position: " + str(position[0])+" "+str(position[1])+" "+str(position[2])) + print("distance: " + str(distance)) + print("decay: " + str(decay)) + print("cast_shadow: " + str(cast_shadow)) + + light = ipv.Light( + light_type='POINT', + color=color, + intensity=intensity, + position_x=position[0], + position_y=position[1], + position_z=position[2], + distance=distance, + decay=decay, + cast_shadow=cast_shadow) + + fig = gcf() + fig.lights = fig.lights + [light] + + return light diff --git a/js/src/light.ts b/js/src/light.ts index aa1d8df9..ef5a5d45 100644 --- a/js/src/light.ts +++ b/js/src/light.ts @@ -76,67 +76,76 @@ class LightView extends widgets.WidgetView { this.light_type = this.model.get("light_type"); //no shadow support if(this.light_type === this.LIGHT_TYPES.AMBIENT){ - console.log("Create Ambient Light w color " + this.color + " intensity : " + this.intensity); + console.log("Create Ambient Light"); this.current_light = new THREE.AmbientLight(this.color, this.intensity); this.lights.push(this.current_light); } else{ this.position = new THREE.Vector3(this.model.get("position_x"), this.model.get("position_y"), this.model.get("position_z")); - //no shadow support + // no shadow support if(this.light_type === this.LIGHT_TYPES.HEMISPHERE) { + console.log("Create Hemisphere Light "); + this.color2 = this.model.get("color2"); - console.log("Create Hemisphere Light w color " + this.color + " color2 " + this.color2 +" "+ " intensity : " + this.intensity); this.current_light = new THREE.HemisphereLight(this.color, this.color2, this.intensity); this.current_light.position.set(this.position.x, this.position.y, this.position.z); this.lights.push(this.current_light); } + // with shadow support else { - //with shadow support + this.cast_shadow = this.model.get("cast_shadow"); - this.angle = this.model.get("angle"); this.distance = this.model.get("distance"); this.decay = this.model.get("decay"); - this.penumbra = this.model.get("penumbra"); - - // TODO - move below - this.target = new THREE.Object3D(); - this.target.position.set(this.model.get("target_x"), this.model.get("target_y"), this.model.get("target_z")); - this.target.updateMatrixWorld(); - this.renderer.scene_scatter.add(this.target); - - - if(this.light_type === this.LIGHT_TYPES.DIRECTIONAL){ - console.log("Create Directional Light w color " + this.color + " intensity : " + this.intensity + " position :"+ this.position + " cast_shadow: " + this.cast_shadow); - this.current_light = new THREE.DirectionalLight(this.color, this.intensity); - + + if(this.light_type === this.LIGHT_TYPES.POINT) { + console.log("Create Point Light "); + this.current_light = new THREE.PointLight(this.color, this.intensity); + this.current_light.position.set(this.position.x, this.position.y, this.position.z); - this.current_light.target = this.target; - this.current_light.cast_shadow = this.cast_shadow; + this.current_light.distance = this.distance; + this.current_light.decay = this.decay; + this.lights.push(this.current_light); } else { - if(this.light_type === this.LIGHT_TYPES.SPOT) { - console.log("Create Spot Light w color " + this.color + " intensity : " + this.intensity + " position :"+ this.position + " cast_shadow: " + this.cast_shadow); - - this.current_light = new THREE.SpotLight(this.color, this.intensity); - + this.target = new THREE.Object3D(); + this.target.position.set(this.model.get("target_x"), this.model.get("target_y"), this.model.get("target_z")); + this.target.updateMatrixWorld(); + this.renderer.scene_scatter.add(this.target); + + if(this.light_type === this.LIGHT_TYPES.DIRECTIONAL){ + console.log("Create Directional Light "); + this.current_light = new THREE.DirectionalLight(this.color, this.intensity); + this.current_light.position.set(this.position.x, this.position.y, this.position.z); - this.current_light.target = this.target; - - this.current_light.angle = this.angle; - this.current_light.distance = this.distance; - this.current_light.decay = this.decay; - this.current_light.penumbra = this.penumbra; this.current_light.cast_shadow = this.cast_shadow; - this.lights.push(this.current_light); } - else if(this.light_type === this.LIGHT_TYPES.POINT) { //redundant if, easier to read + else if(this.light_type === this.LIGHT_TYPES.SPOT) { + console.log("Create Spot Light"); + + this.angle = this.model.get("angle"); + this.penumbra = this.model.get("penumbra"); + + this.current_light = new THREE.SpotLight(this.color, this.intensity); + + this.current_light.position.set(this.position.x, this.position.y, this.position.z); + this.current_light.target = this.target; + this.current_light.angle = this.angle; + this.current_light.distance = this.distance; + this.current_light.decay = this.decay; + this.current_light.penumbra = this.penumbra; + this.current_light.cast_shadow = this.cast_shadow; + + this.lights.push(this.current_light); } + } + } } diff --git a/notebooks/lighting_demo.ipynb b/notebooks/lighting_demo.ipynb index 3e91f9a9..69247d92 100644 --- a/notebooks/lighting_demo.ipynb +++ b/notebooks/lighting_demo.ipynb @@ -9,22 +9,19 @@ "name": "stdout", "output_type": "stream", "text": [ - "ADD SPOT LIGHT (from pylab) \n", - "color: white\n", + "ADD POINT LIGHT (from pylab) \n", + "color: orange\n", "intensity: 1.0\n", - "position: 20 20 20\n", - "target: -20 -20 -20\n", - "angle: 0.3490658503988659\n", - "distance: 0\n", - "decay: 1\n", - "penumbra: 0\n", + "position: 20 20 -20\n", + "distance: 125\n", + "decay: 2\n", "cast_shadow: False\n" ] }, { "data": { "application/vnd.jupyter.widget-view+json": { - "model_id": "a7f22ec1b8ec4fda8f0564369b1d007e", + "model_id": "383d1eba0adf40eb809819dc966c83bf", "version_major": 2, "version_minor": 0 }, @@ -38,7 +35,7 @@ { "data": { "application/vnd.jupyter.widget-view+json": { - "model_id": "100b23ea7cfd4af5917398b9d78bbb46", + "model_id": "73c40f915ff841b49e5e22a840a756ac", "version_major": 2, "version_minor": 0 }, @@ -342,7 +339,8 @@ " #p3.ambient_light(color='rgb(255,0,0)', intensity=0.5)\n", " #p3.directional_light(color='blue', intensity=1.0, position=[20, 20, 20], target=[-20,0,20])\n", " #p3.hemisphere_light(color='blue', color2='yellow', intensity=1.0, position=[1, 1, 0])\n", - " p3.spot_light(color='white', intensity=1.0, position=[20, 20, 20], angle=math.pi/9, target = [-20,-20,-20])\n", + " #p3.spot_light(color='white', intensity=1.0, position=[20, 20, 20], angle=math.pi/9, target = [-20,-20,-20])\n", + " p3.point_light(color='orange', intensity=1.0, position=[20, 20, -20], distance=125, decay=2)\n", " p3.show()\n", " return mesh\n", " else:\n", From 69480d39068eb351cba57a1da2f3ff6255354c3b Mon Sep 17 00:00:00 2001 From: rinftech-github <61880865+rinftech-github@users.noreply.github.com> Date: Fri, 27 Mar 2020 14:31:49 +0200 Subject: [PATCH 012/130] Updates - Range Widget, Dropdown Widgets added --- notebooks/widgets_test.ipynb | 27 ++++++++++++++++++++++----- 1 file changed, 22 insertions(+), 5 deletions(-) diff --git a/notebooks/widgets_test.ipynb b/notebooks/widgets_test.ipynb index 5cbb328d..20ba6c68 100644 --- a/notebooks/widgets_test.ipynb +++ b/notebooks/widgets_test.ipynb @@ -2,7 +2,7 @@ "cells": [ { "cell_type": "code", - "execution_count": 24, + "execution_count": 58, "metadata": { "scrolled": true }, @@ -10,7 +10,7 @@ { "data": { "application/vnd.jupyter.widget-view+json": { - "model_id": "e0e8ef90458e4ed88dee990d4d1073af", + "model_id": "9d57d7917ea24320bba59d6c60f87bd6", "version_major": 2, "version_minor": 0 }, @@ -23,7 +23,7 @@ } ], "source": [ - "from ipywidgets import Layout, Button, VBox, FloatText, Textarea, Dropdown, Label, FloatSlider, ColorPicker\n", + "from ipywidgets import FloatRangeSlider, Dropdown, FloatSlider, ColorPicker\n", "from ipywidgets import widgets\n", "\n", "select_size = FloatSlider(value=50, min=0, max=100, step=0.1, description='Adjust size:', disabled=False,\n", @@ -33,14 +33,31 @@ " continuous_update=True,orientation='horizontal', readout=True, readout_format='.2f')\n", "brightness = FloatSlider(value=0.25, min=0.00, max=1.0, step=0.01, description='Adjust level:', disabled=False,\n", " continuous_update=True,orientation='horizontal', readout=True, readout_format='.2f')\n", + "dropdown = Dropdown(options=['NORMAL', 'PHONG', 'PHYSICAL'],value='NORMAL',description='Select:',\n", + " disabled=False)\n", + "cast_shadow = Dropdown(options=[True, False], value=False, description='Select:', disabled=False)\n", + "range_widget = widgets.FloatRangeSlider( value=[2.25, 7.75], min=0.00, max=10.00, step=0.01, description='Range:',\n", + " disabled=False, continuous_update=False, orientation='horizontal', readout=True, readout_format='.2f')\n", "\n", - "children = [select_color, select_size, brightness, opacity]\n", + "#bool_valid = widgets.Valid(value=False, description='Valid!')\n", + "#toggle_click = widgets.ToggleButton(value=False, description='Click me', disabled=False, button_style= '', tooltip='Description', icon='check')\n", + "#check = widgets.Checkbox(value=False, description='Check me', disabled=False, indent=False )\n", + "\n", + "children = [select_color, select_size, brightness, opacity, dropdown, cast_shadow, range_widget]\n", + " #bool_valid, toggle_click, check ]\n", "tab = widgets.Tab(children)\n", "tab.set_title(0, 'Color')\n", "tab.set_title(1, 'Size')\n", "tab.set_title(2, 'Brightness')\n", "tab.set_title(3, 'Opacity')\n", - "tab\n" + "tab.set_title(4, 'Dropdown')\n", + "tab.set_title(5, 'Cast shadow')\n", + "tab.set_title(6, 'Range Widget')\n", + "#tab.set_title(7, 'Validate')\n", + "#tab.set_title(8, 'ToggleButton')\n", + "#tab.set_title(9, 'Check')\n", + "tab\n", + "\n" ] }, { From 9db5bcfac4d214e8adc88d9e8a43f2c13773590e Mon Sep 17 00:00:00 2001 From: rinftech-github Date: Fri, 27 Mar 2020 14:34:29 +0200 Subject: [PATCH 013/130] Add shadowmap support for spot_light --- ipyvolume/pylab.py | 26 +++++++++++++++++-- ipyvolume/widgets.py | 8 ++++++ js/src/light.ts | 47 ++++++++++++++++++++++++++++++++--- js/src/mesh.ts | 9 ++++--- notebooks/lighting_demo.ipynb | 23 +++++++++-------- 5 files changed, 94 insertions(+), 19 deletions(-) diff --git a/ipyvolume/pylab.py b/ipyvolume/pylab.py index db0ea02b..9e2aa9be 100644 --- a/ipyvolume/pylab.py +++ b/ipyvolume/pylab.py @@ -1601,7 +1601,14 @@ def spot_light( distance=0, decay=1, penumbra=0, - cast_shadow=False): + cast_shadow=False, + shadow_map_width=512, + shadow_map_height=512, + shadow_bias=-0.0005, + shadow_camera_near=0.5, + shadow_camera_far=500, + shadow_camera_perspective_fov=50, + shadow_camera_perspective_aspect=1): print("ADD SPOT LIGHT (from pylab) ") print("color: " + str(color)) @@ -1613,6 +1620,14 @@ def spot_light( print("decay: " + str(decay)) print("penumbra: " + str(penumbra)) print("cast_shadow: " + str(cast_shadow)) + if cast_shadow: + print("shadow_map_width: " + str(shadow_map_width)) + print("shadow_map_height: " + str(shadow_map_height)) + print("shadow_bias: " + str(shadow_bias)) + print("shadow_camera_near: " + str(shadow_camera_near)) + print("shadow_camera_far: " + str(shadow_camera_far)) + print("shadow_camera_perspective_fov: " + str(shadow_camera_perspective_fov)) + print("shadow_camera_perspective_aspect: " + str(shadow_camera_perspective_aspect)) light = ipv.Light( light_type='SPOT', @@ -1628,7 +1643,14 @@ def spot_light( distance=distance, decay=decay, penumbra=penumbra, - cast_shadow=cast_shadow) + cast_shadow=cast_shadow, + shadow_map_width=shadow_map_width, + shadow_map_height=shadow_map_height, + shadow_bias=shadow_bias, + shadow_camera_near=shadow_camera_near, + shadow_camera_far=shadow_camera_far, + shadow_camera_perspective_fov=shadow_camera_perspective_fov, + shadow_camera_perspective_aspect=shadow_camera_perspective_aspect) fig = gcf() fig.lights = fig.lights + [light] diff --git a/ipyvolume/widgets.py b/ipyvolume/widgets.py index a987faad..4d8e9961 100644 --- a/ipyvolume/widgets.py +++ b/ipyvolume/widgets.py @@ -249,6 +249,14 @@ class Light(widgets.Widget): angle = traitlets.CFloat(math.pi/3).tag(sync=True) decay = traitlets.CFloat(1).tag(sync=True) penumbra = traitlets.CFloat(0).tag(sync=True) + + shadow_map_width=traitlets.CFloat(512).tag(sync=True) + shadow_map_height=traitlets.CFloat(512).tag(sync=True) + shadow_bias=traitlets.CFloat(-0.0005).tag(sync=True) + shadow_camera_near=traitlets.CFloat(0.5).tag(sync=True) + shadow_camera_far=traitlets.CFloat(500).tag(sync=True) + shadow_camera_perspective_fov=traitlets.CFloat(50).tag(sync=True) + shadow_camera_perspective_aspect=traitlets.CFloat(1).tag(sync=True) diff --git a/js/src/light.ts b/js/src/light.ts index ef5a5d45..a799e934 100644 --- a/js/src/light.ts +++ b/js/src/light.ts @@ -27,7 +27,13 @@ class LightView extends widgets.WidgetView { decay: any; penumbra: any; cast_shadow: any; - + shadow_map_width: any; + shadow_map_height: any; + shadow_bias: any; + shadow_camera_near: any; + shadow_camera_far: any; + shadow_camera_perspective_fov: any; + shadow_camera_perspective_aspect: any; render() { @@ -131,6 +137,14 @@ class LightView extends widgets.WidgetView { this.angle = this.model.get("angle"); this.penumbra = this.model.get("penumbra"); + this.shadow_map_width = this.model.get("shadow_map_width"); + this.shadow_map_height = this.model.get("shadow_map_height"); + this.shadow_bias = this.model.get("shadow_bias"); + this.shadow_camera_near = this.model.get("shadow_camera_near"); + this.shadow_camera_far = this.model.get("shadow_camera_far"); + this.shadow_camera_perspective_fov = this.model.get("shadow_camera_perspective_fov"); + this.shadow_camera_perspective_aspect = this.model.get("shadow_camera_perspective_aspect"); + this.current_light = new THREE.SpotLight(this.color, this.intensity); this.current_light.position.set(this.position.x, this.position.y, this.position.z); @@ -139,9 +153,22 @@ class LightView extends widgets.WidgetView { this.current_light.distance = this.distance; this.current_light.decay = this.decay; this.current_light.penumbra = this.penumbra; - this.current_light.cast_shadow = this.cast_shadow; - + this.current_light.castShadow = this.cast_shadow; + + this.current_light.shadow = new THREE.SpotLightShadow(new THREE.PerspectiveCamera(100, 1, 0.5, 5000)); + + this.current_light.shadow.mapSize.width = this.shadow_map_width; + this.current_light.shadow.mapSize.height = this.shadow_map_height; + this.current_light.shadow.bias = this.shadow_bias; // prevent shadow acne + + this.current_light.shadow.camera.position.set(this.position.x, this.position.y, this.position.z); + this.current_light.shadow.camera.near = this.shadow_camera_near; + this.current_light.shadow.camera.far = this.shadow_camera_far; + this.current_light.shadow.camera.aspect = this.shadow_camera_perspective_aspect; + this.current_light.shadow.camera.fov = this.shadow_camera_perspective_fov; + this.lights.push(this.current_light); + } } @@ -170,6 +197,13 @@ class LightModel extends widgets.WidgetModel { distance: serialize.array_or_json, decay: serialize.array_or_json, penumbra: serialize.array_or_json, + shadow_map_width: serialize.array_or_json, + shadow_map_height: serialize.array_or_json, + shadow_bias: serialize.array_or_json, + shadow_camera_near: serialize.array_or_json, + shadow_camera_far: serialize.array_or_json, + shadow_camera_perspective_fov: serialize.array_or_json, + shadow_camera_perspective_aspect: serialize.array_or_json, }; defaults() { return { @@ -195,6 +229,13 @@ class LightModel extends widgets.WidgetModel { decay: 1, penumbra: 0, cast_shadow: false, + shadow_map_width: 512, + shadow_map_height: 512, + shadow_bias: -0.0005, + shadow_camera_near: 0.5, + shadow_camera_far: 500, + shadow_camera_perspective_fov: 50, + shadow_camera_perspective_aspect: 1, }; } } diff --git a/js/src/mesh.ts b/js/src/mesh.ts index 0bd7d5eb..38e66a8c 100644 --- a/js/src/mesh.ts +++ b/js/src/mesh.ts @@ -116,18 +116,19 @@ class MeshView extends widgets.WidgetView { //LIGHTING ///////////////////// var globalIntensity = 1; - + this.renderer.renderer.shadowMap.enabled = true; + this.renderer.renderer.shadowMap.type = THREE.PCFSoftShadowMap; if(this.lightCountTemp == 0) { this.lightCountTemp = 1; - /* + this.renderer.renderer.shadowMap.enabled = true; this.renderer.renderer.shadowMap.type = THREE.PCFSoftShadowMap; - + /* var slTest = new THREE.SpotLight(0x000000, globalIntensity); slTest.castShadow = true; - slTest.color = new THREE.Color("rgb(100, 100, 100)"); + slTest.color = new THREE.Color("rgb(100, 0, 100)"); slTest.position.set(20, 30, 20);//.normalize(); slTest.angle = Math.PI/9; slTest.penumbra = 0.25; diff --git a/notebooks/lighting_demo.ipynb b/notebooks/lighting_demo.ipynb index 69247d92..b9ad9236 100644 --- a/notebooks/lighting_demo.ipynb +++ b/notebooks/lighting_demo.ipynb @@ -2,26 +2,29 @@ "cells": [ { "cell_type": "code", - "execution_count": 13, + "execution_count": 15, "metadata": {}, "outputs": [ { "name": "stdout", "output_type": "stream", "text": [ - "ADD POINT LIGHT (from pylab) \n", - "color: orange\n", + "ADD SPOT LIGHT (from pylab) \n", + "color: yellow\n", "intensity: 1.0\n", - "position: 20 20 -20\n", - "distance: 125\n", - "decay: 2\n", + "position: 20 30 20\n", + "target: -20 -20 -20\n", + "angle: 0.3490658503988659\n", + "distance: 0\n", + "decay: 1\n", + "penumbra: 0\n", "cast_shadow: False\n" ] }, { "data": { "application/vnd.jupyter.widget-view+json": { - "model_id": "383d1eba0adf40eb809819dc966c83bf", + "model_id": "b9a7c35d903c46b4918c7d0f9bee9080", "version_major": 2, "version_minor": 0 }, @@ -35,7 +38,7 @@ { "data": { "application/vnd.jupyter.widget-view+json": { - "model_id": "73c40f915ff841b49e5e22a840a756ac", + "model_id": "06e595b43043437e9e58c11724992b80", "version_major": 2, "version_minor": 0 }, @@ -339,8 +342,8 @@ " #p3.ambient_light(color='rgb(255,0,0)', intensity=0.5)\n", " #p3.directional_light(color='blue', intensity=1.0, position=[20, 20, 20], target=[-20,0,20])\n", " #p3.hemisphere_light(color='blue', color2='yellow', intensity=1.0, position=[1, 1, 0])\n", - " #p3.spot_light(color='white', intensity=1.0, position=[20, 20, 20], angle=math.pi/9, target = [-20,-20,-20])\n", - " p3.point_light(color='orange', intensity=1.0, position=[20, 20, -20], distance=125, decay=2)\n", + " p3.spot_light(color='yellow', intensity=1.0, position=[20, 30, 20], angle=math.pi/9, target = [-20,-20,-20], cast_shadow=True, shadow_camera_far=5000)\n", + " #p3.point_light(color='orange', intensity=1.0, position=[20, 20, -20], distance=125, decay=2)\n", " p3.show()\n", " return mesh\n", " else:\n", From 700c735c047151ebaaf59de0e320d70963218202 Mon Sep 17 00:00:00 2001 From: rinftech-github <61880865+rinftech-github@users.noreply.github.com> Date: Fri, 27 Mar 2020 18:09:08 +0200 Subject: [PATCH 014/130] [Bugfixes] - opacity and brightness granularity and range was changed; - update_axes_range() function updated in order to fix the error caused by y_size changes; - color picker was fixed; --- notebooks/widgets_test.ipynb | 92 +++++++++++++++++++----------------- 1 file changed, 49 insertions(+), 43 deletions(-) diff --git a/notebooks/widgets_test.ipynb b/notebooks/widgets_test.ipynb index 20ba6c68..086d4ae2 100644 --- a/notebooks/widgets_test.ipynb +++ b/notebooks/widgets_test.ipynb @@ -62,18 +62,18 @@ }, { "cell_type": "code", - "execution_count": 10, + "execution_count": 71, "metadata": {}, "outputs": [ { "data": { "application/vnd.jupyter.widget-view+json": { - "model_id": "", + "model_id": "472ffb98618443d7b4de6dcbcc972e8c", "version_major": 2, "version_minor": 0 }, "text/plain": [ - "interactive(children=(FloatSlider(value=5.0, description='Choose size for x:', max=10.0, readout_format='d', s…" + "interactive(children=(FloatSlider(value=5.0, description='Choose size for x:', max=120.0, readout_format='d', …" ] }, "metadata": {}, @@ -82,7 +82,7 @@ { "data": { "application/vnd.jupyter.widget-view+json": { - "model_id": "", + "model_id": "6b08426e94ab476491cc4459b1010dc7", "version_major": 2, "version_minor": 0 }, @@ -108,25 +108,34 @@ "from ipywidgets import interact, interactive, fixed, interact_manual\n", "\n", "\n", - "#Changing size axes\n", + "#Change plot size adjusting the size on each axe.\n", "\n", - "x_size = FloatSlider(value=5, min=0, max=10, step=1, description='Choose size for x:', disabled=False,\n", + "x_size = FloatSlider(value=5, min=0, max=120, step=1, description='Choose size for x:', disabled=False,\n", " continuous_update=True,orientation='horizontal', readout=True, readout_format='d')\n", "\n", - "y_size = FloatSlider(value=5, min=0, max=10, step=1, description='Choose size for y:', disabled=False,\n", + "y_size = FloatSlider(value=5, min=0, max=200, step=1, description='Choose size for y:', disabled=False,\n", " continuous_update=True,orientation='horizontal', readout=True, readout_format='d')\n", "\n", - "z_size = FloatSlider(value=5, min=0, max=10, step=1, description='Choose size for z:', disabled=False,\n", + "z_size = FloatSlider(value=5, min=0, max=350, step=1, description='Choose size for z:', disabled=False,\n", " continuous_update=True,orientation='horizontal', readout=True, readout_format='d')\n", - "\n", + " \n", "def update_axes_range(*args):\n", - " #change size \n", - " #multiplier_x = 2.0\n", - " #multiplier_y = 3.0\n", - " #multiplier_z = 4.0\n", - " x_size.max = multiplier_x * y_size.value\n", - " z_size.max = multiplier_z * y_size.value\n", + " \n", + " multiplier_x = 0.5\n", + " y_size.max = multiplier_x * x_size.value\n", + " z_size.max = multiplier_x * x_size.value \n", + " \n", + " #multiplier_y = 0.5\n", + " #x_size.max = multiplier_y * y_size.value\n", + " #z_size.max = multiplier_y * y_size.value\n", + " \n", + " #multiplier_z = 0.5\n", + " #x_size.max = multiplier_z * z_size.value\n", + " #y_size.max = multiplier_z * z_size.value\n", + " \n", + "x_size.observe(update_axes_range, 'value') \n", "y_size.observe(update_axes_range, 'value')\n", + "z_size.observe(update_axes_range, 'value')\n", "\n", "def printer(x, y, z):\n", " print(x, y, z)\n", @@ -134,7 +143,7 @@ "widgets.interact(printer,x=x_size, y=y_size, z=z_size);\n", "\n", "\n", - "#Changing size \n", + "#Change size using only one param \n", "\n", "selected_size = FloatSlider(value=5, min=0, max=10, step=1, description='Change size:', disabled=False,\n", " continuous_update=True,orientation='horizontal', readout=True, readout_format='d')\n", @@ -142,26 +151,25 @@ "display(selected_size, selected_size.value)\n", "\n", "\n", - "\n", - "\n", - "\n", "\n" ] }, { "cell_type": "code", - "execution_count": 139, - "metadata": {}, + "execution_count": 72, + "metadata": { + "scrolled": false + }, "outputs": [ { "data": { "application/vnd.jupyter.widget-view+json": { - "model_id": "650e7b9a51e1458ba0d0b9ede74f9aef", + "model_id": "fdff0572e9bb4edc880df35a3ed61071", "version_major": 2, "version_minor": 0 }, "text/plain": [ - "interactive(children=(ColorPicker(value='red', description='Pick a color'), ColorPicker(value='green', descrip…" + "ColorPicker(value='red', description='Pick a color')" ] }, "metadata": {}, @@ -169,38 +177,37 @@ }, { "data": { + "application/vnd.jupyter.widget-view+json": { + "model_id": "d731a2cdeed04bb29d6e022574119884", + "version_major": 2, + "version_minor": 0 + }, "text/plain": [ - "" + "ColorPicker(value='green', description='Pick a color')" ] }, - "execution_count": 139, "metadata": {}, - "output_type": "execute_result" + "output_type": "display_data" } ], "source": [ "import ipywidgets as widgets\n", - "from ipywidgets import interact, interactive, fixed, interact_manual\n", "\n", "#select a color for plot \n", "select_color1 = ColorPicker(concise=False, description='Pick a color', value='red', disabled=False)\n", "select_color2 = ColorPicker(concise=False, description='Pick a color', value='green', disabled=False)\n", - "\n", - "def color_display(First_color, Second_color):\n", - " display(First_color, Second_color) \n", - " \n", - "widgets.interact(color_display(), First_color = select_color1, Second_color = select_color2)" + "display(select_color1, select_color2)" ] }, { "cell_type": "code", - "execution_count": 153, + "execution_count": 73, "metadata": {}, "outputs": [ { "data": { "application/vnd.jupyter.widget-view+json": { - "model_id": "ba6cf680a02240d8b04835534551dd0d", + "model_id": "76f8768be443482380e9e6153f67b1ca", "version_major": 2, "version_minor": 0 }, @@ -214,7 +221,7 @@ { "data": { "application/vnd.jupyter.widget-view+json": { - "model_id": "936b7817b5a546a795ab188883503250", + "model_id": "babb94a8149a415e9703816842b6cab6", "version_major": 2, "version_minor": 0 }, @@ -228,25 +235,24 @@ ], "source": [ "import ipywidgets as widgets\n", - "from ipywidgets import interact, interactive, fixed, interact_manual\n", - "\n", - "opacity = FloatSlider(value=0.5, min=0.0, max=10.0, step=0.1, description='Choose opacity level:', disabled=False,\n", - " continuous_update=True,orientation='horizontal', readout=True, readout_format='d')\n", - "brightness = FloatSlider(value=0.5, min=0.0, max=10.0, step=0.1, description='Choose brightness level:', disabled=False,\n", - " continuous_update=True,orientation='horizontal', readout=True, readout_format='d')\n", "\n", + "#set opacity and brightness \n", + "opacity = FloatSlider(value=0.75, min=0.00, max=1.0, step=0.01, description='Adjust level:', disabled=False,\n", + " continuous_update=True,orientation='horizontal', readout=True, readout_format='.2f')\n", + "brightness = FloatSlider(value=0.25, min=0.00, max=1.0, step=0.01, description='Adjust level:', disabled=False,\n", + " continuous_update=True,orientation='horizontal', readout=True, readout_format='.2f')\n", "display(opacity, brightness)" ] }, { "cell_type": "code", - "execution_count": 25, + "execution_count": 74, "metadata": {}, "outputs": [ { "data": { "application/vnd.jupyter.widget-view+json": { - "model_id": "9ae54036017c4dcdb9d1e46a656ed656", + "model_id": "8713cf5fffee468fabf8952cd7c8f742", "version_major": 2, "version_minor": 0 }, From 467609d417cd168d6fc4aa9e29c04f899ed4ea81 Mon Sep 17 00:00:00 2001 From: rinftech-github Date: Fri, 27 Mar 2020 22:33:51 +0200 Subject: [PATCH 015/130] Add shadowmap support for directional_light --- ipyvolume/pylab.py | 28 ++++++++++++------ ipyvolume/widgets.py | 4 +-- js/src/light.ts | 54 +++++++++++++++++++++++------------ js/src/mesh.ts | 28 +++++++++++++----- notebooks/lighting_demo.ipynb | 36 ++++++++++++----------- 5 files changed, 98 insertions(+), 52 deletions(-) diff --git a/ipyvolume/pylab.py b/ipyvolume/pylab.py index 9e2aa9be..b7260d7c 100644 --- a/ipyvolume/pylab.py +++ b/ipyvolume/pylab.py @@ -1566,7 +1566,12 @@ def directional_light( intensity = 1, position=[0, 1, 0], target=[0, 0, 0], - cast_shadow=False): + cast_shadow=False, + shadow_map_size=512, + shadow_bias=-0.0005, + shadow_camera_near=0.5, + shadow_camera_far=500, + shadow_camera_orthographic_size=5): print("ADD DIRECTIONAL LIGHT (from pylab) ") print("color: " + str(color)) @@ -1574,6 +1579,11 @@ def directional_light( print("position: " + str(position[0])+" "+str(position[1])+" "+str(position[2])) print("target: " + str(target[0])+" "+str(target[1])+" "+str(target[2])) print("cast_shadow: " + str(cast_shadow)) + if cast_shadow: + print("shadow_map_size: " + str(shadow_map_size)) + print("shadow_bias: " + str(shadow_bias)) + print("shadow_camera_near: " + str(shadow_camera_near)) + print("shadow_camera_far: " + str(shadow_camera_far)) light = ipv.Light( light_type='DIRECTIONAL', @@ -1585,7 +1595,12 @@ def directional_light( target_x=target[0], target_y=target[1], target_z=target[2], - cast_shadow=cast_shadow) + cast_shadow=cast_shadow, + shadow_map_size=shadow_map_size, + shadow_bias=shadow_bias, + shadow_camera_near=shadow_camera_near, + shadow_camera_far=shadow_camera_far, + shadow_camera_orthographic_size=shadow_camera_orthographic_size) fig = gcf() fig.lights = fig.lights + [light] @@ -1602,8 +1617,7 @@ def spot_light( decay=1, penumbra=0, cast_shadow=False, - shadow_map_width=512, - shadow_map_height=512, + shadow_map_size=512, shadow_bias=-0.0005, shadow_camera_near=0.5, shadow_camera_far=500, @@ -1621,8 +1635,7 @@ def spot_light( print("penumbra: " + str(penumbra)) print("cast_shadow: " + str(cast_shadow)) if cast_shadow: - print("shadow_map_width: " + str(shadow_map_width)) - print("shadow_map_height: " + str(shadow_map_height)) + print("shadow_map_size: " + str(shadow_map_size)) print("shadow_bias: " + str(shadow_bias)) print("shadow_camera_near: " + str(shadow_camera_near)) print("shadow_camera_far: " + str(shadow_camera_far)) @@ -1644,8 +1657,7 @@ def spot_light( decay=decay, penumbra=penumbra, cast_shadow=cast_shadow, - shadow_map_width=shadow_map_width, - shadow_map_height=shadow_map_height, + shadow_map_size=shadow_map_size, shadow_bias=shadow_bias, shadow_camera_near=shadow_camera_near, shadow_camera_far=shadow_camera_far, diff --git a/ipyvolume/widgets.py b/ipyvolume/widgets.py index 4d8e9961..11f306f5 100644 --- a/ipyvolume/widgets.py +++ b/ipyvolume/widgets.py @@ -250,13 +250,13 @@ class Light(widgets.Widget): decay = traitlets.CFloat(1).tag(sync=True) penumbra = traitlets.CFloat(0).tag(sync=True) - shadow_map_width=traitlets.CFloat(512).tag(sync=True) - shadow_map_height=traitlets.CFloat(512).tag(sync=True) + shadow_map_size=traitlets.CFloat(512).tag(sync=True) shadow_bias=traitlets.CFloat(-0.0005).tag(sync=True) shadow_camera_near=traitlets.CFloat(0.5).tag(sync=True) shadow_camera_far=traitlets.CFloat(500).tag(sync=True) shadow_camera_perspective_fov=traitlets.CFloat(50).tag(sync=True) shadow_camera_perspective_aspect=traitlets.CFloat(1).tag(sync=True) + shadow_camera_orthographic_size=traitlets.CFloat(5).tag(sync=True) diff --git a/js/src/light.ts b/js/src/light.ts index a799e934..00a5614a 100644 --- a/js/src/light.ts +++ b/js/src/light.ts @@ -27,13 +27,13 @@ class LightView extends widgets.WidgetView { decay: any; penumbra: any; cast_shadow: any; - shadow_map_width: any; - shadow_map_height: any; + shadow_map_size: any; shadow_bias: any; shadow_camera_near: any; shadow_camera_far: any; shadow_camera_perspective_fov: any; shadow_camera_perspective_aspect: any; + shadow_camera_orthographic_size: any; render() { @@ -122,13 +122,36 @@ class LightView extends widgets.WidgetView { this.target.updateMatrixWorld(); this.renderer.scene_scatter.add(this.target); - if(this.light_type === this.LIGHT_TYPES.DIRECTIONAL){ - console.log("Create Directional Light "); + this.shadow_map_size = this.model.get("shadow_map_size"); + this.shadow_bias = this.model.get("shadow_bias"); + this.shadow_camera_near = this.model.get("shadow_camera_near"); + this.shadow_camera_far = this.model.get("shadow_camera_far"); + + if(this.light_type === this.LIGHT_TYPES.DIRECTIONAL) { + console.log("Create Directional Light"); + + this.shadow_camera_orthographic_size = this.model.get("shadow_camera_orthographic_size"); + this.current_light = new THREE.DirectionalLight(this.color, this.intensity); - this.current_light.position.set(this.position.x, this.position.y, this.position.z); this.current_light.target = this.target; - this.current_light.cast_shadow = this.cast_shadow; + this.current_light.castShadow = this.cast_shadow; + + this.current_light.shadow = new THREE.DirectionalLightShadow(new THREE.OrthographicCamera(-5,5,-5,5,0.5,500)); + + this.current_light.shadow.camera.left = - this.shadow_camera_orthographic_size; + this.current_light.shadow.camera.right = this.shadow_camera_orthographic_size; + this.current_light.shadow.camera.top = this.shadow_camera_orthographic_size; + this.current_light.shadow.camera.bottom = - this.shadow_camera_orthographic_size; + + this.current_light.shadow.mapSize.width = this.shadow_map_size; + this.current_light.shadow.mapSize.height = this.shadow_map_size; + this.current_light.shadow.bias = this.shadow_bias; // prevent shadow acne + + this.current_light.shadow.camera.position.set(this.position.x, this.position.y, this.position.z); + this.current_light.shadow.camera.near = this.shadow_camera_near; + this.current_light.shadow.camera.far = this.shadow_camera_far; + this.lights.push(this.current_light); } else if(this.light_type === this.LIGHT_TYPES.SPOT) { @@ -137,11 +160,6 @@ class LightView extends widgets.WidgetView { this.angle = this.model.get("angle"); this.penumbra = this.model.get("penumbra"); - this.shadow_map_width = this.model.get("shadow_map_width"); - this.shadow_map_height = this.model.get("shadow_map_height"); - this.shadow_bias = this.model.get("shadow_bias"); - this.shadow_camera_near = this.model.get("shadow_camera_near"); - this.shadow_camera_far = this.model.get("shadow_camera_far"); this.shadow_camera_perspective_fov = this.model.get("shadow_camera_perspective_fov"); this.shadow_camera_perspective_aspect = this.model.get("shadow_camera_perspective_aspect"); @@ -155,10 +173,10 @@ class LightView extends widgets.WidgetView { this.current_light.penumbra = this.penumbra; this.current_light.castShadow = this.cast_shadow; - this.current_light.shadow = new THREE.SpotLightShadow(new THREE.PerspectiveCamera(100, 1, 0.5, 5000)); + this.current_light.shadow = new THREE.SpotLightShadow(new THREE.PerspectiveCamera()); - this.current_light.shadow.mapSize.width = this.shadow_map_width; - this.current_light.shadow.mapSize.height = this.shadow_map_height; + this.current_light.shadow.mapSize.width = this.shadow_map_size; + this.current_light.shadow.mapSize.height = this.shadow_map_size; this.current_light.shadow.bias = this.shadow_bias; // prevent shadow acne this.current_light.shadow.camera.position.set(this.position.x, this.position.y, this.position.z); @@ -197,13 +215,13 @@ class LightModel extends widgets.WidgetModel { distance: serialize.array_or_json, decay: serialize.array_or_json, penumbra: serialize.array_or_json, - shadow_map_width: serialize.array_or_json, - shadow_map_height: serialize.array_or_json, + shadow_map_size: serialize.array_or_json, shadow_bias: serialize.array_or_json, shadow_camera_near: serialize.array_or_json, shadow_camera_far: serialize.array_or_json, shadow_camera_perspective_fov: serialize.array_or_json, shadow_camera_perspective_aspect: serialize.array_or_json, + shadow_camera_orthographic_size: serialize.array_or_json, }; defaults() { return { @@ -229,13 +247,13 @@ class LightModel extends widgets.WidgetModel { decay: 1, penumbra: 0, cast_shadow: false, - shadow_map_width: 512, - shadow_map_height: 512, + shadow_map_size: 512, shadow_bias: -0.0005, shadow_camera_near: 0.5, shadow_camera_far: 500, shadow_camera_perspective_fov: 50, shadow_camera_perspective_aspect: 1, + shadow_camera_orthographic_size: 5 }; } } diff --git a/js/src/mesh.ts b/js/src/mesh.ts index 38e66a8c..5ea2a646 100644 --- a/js/src/mesh.ts +++ b/js/src/mesh.ts @@ -115,7 +115,7 @@ class MeshView extends widgets.WidgetView { //LIGHTING ///////////////////// - var globalIntensity = 1; + var globalIntensity = 0.5; this.renderer.renderer.shadowMap.enabled = true; this.renderer.renderer.shadowMap.type = THREE.PCFSoftShadowMap; @@ -149,14 +149,28 @@ class MeshView extends widgets.WidgetView { */ /* - var dlTest = new THREE.DirectionalLight(0x000000, globalIntensity); - dlTest.castShadow = true; - dlTest.color = new THREE.Color("rgb(0, 255, 255)"); - dlTest.position.set(100, 100, 100).normalize(); - dlTest.lookAt(new THREE.Vector3(0,0,0));// + var dlTest = new THREE.DirectionalLight("rgb(0, 255, 255)", globalIntensity); + dlTest.position.set(100, 100, 100); + dlTest.castShadow = true; + + dlTest.shadow = new THREE.DirectionalLightShadow(new THREE.OrthographicCamera(-5,5,-5,5,0.5,500)); + + var d = 100; + dlTest.shadow.camera.left = - d; + dlTest.shadow.camera.right = d; + dlTest.shadow.camera.top = d; + dlTest.shadow.camera.bottom = - d; + + dlTest.shadow.mapSize.width = 512;//this.shadow_map_width; + dlTest.shadow.mapSize.height = 512;//this.shadow_map_height; + dlTest.shadow.bias = -0.0005; // prevent shadow acne + + dlTest.shadow.camera.position.set(100,100,100);//(this.position.x, this.position.y, this.position.z); + dlTest.shadow.camera.near = 0.5;//this.shadow_camera_near; + dlTest.shadow.camera.far = 10000;// this.shadow_camera_far; this.renderer.scene_scatter.add(dlTest); - */ + */ /* var dlTest2 = new THREE.DirectionalLight(0x000000, globalIntensity); dlTest2.castShadow = true; diff --git a/notebooks/lighting_demo.ipynb b/notebooks/lighting_demo.ipynb index b9ad9236..ee47c999 100644 --- a/notebooks/lighting_demo.ipynb +++ b/notebooks/lighting_demo.ipynb @@ -2,29 +2,29 @@ "cells": [ { "cell_type": "code", - "execution_count": 15, + "execution_count": 10, "metadata": {}, "outputs": [ { "name": "stdout", "output_type": "stream", "text": [ - "ADD SPOT LIGHT (from pylab) \n", - "color: yellow\n", + "ADD DIRECTIONAL LIGHT (from pylab) \n", + "color: cyan\n", "intensity: 1.0\n", - "position: 20 30 20\n", - "target: -20 -20 -20\n", - "angle: 0.3490658503988659\n", - "distance: 0\n", - "decay: 1\n", - "penumbra: 0\n", - "cast_shadow: False\n" + "position: 100 100 100\n", + "target: 0 0 0\n", + "cast_shadow: True\n", + "shadow_map_size: 512\n", + "shadow_bias: -0.005\n", + "shadow_camera_near: 0.5\n", + "shadow_camera_far: 500\n" ] }, { "data": { "application/vnd.jupyter.widget-view+json": { - "model_id": "b9a7c35d903c46b4918c7d0f9bee9080", + "model_id": "8b9fe0a2374c4783ae57201e1b750c8f", "version_major": 2, "version_minor": 0 }, @@ -38,7 +38,7 @@ { "data": { "application/vnd.jupyter.widget-view+json": { - "model_id": "06e595b43043437e9e58c11724992b80", + "model_id": "9d0a8f983c4d4bed9bbe759632d50d98", "version_major": 2, "version_minor": 0 }, @@ -339,11 +339,13 @@ " p3.squarelim()\n", " \n", " drawSurface(separateFigure=False)\n", - " #p3.ambient_light(color='rgb(255,0,0)', intensity=0.5)\n", - " #p3.directional_light(color='blue', intensity=1.0, position=[20, 20, 20], target=[-20,0,20])\n", - " #p3.hemisphere_light(color='blue', color2='yellow', intensity=1.0, position=[1, 1, 0])\n", - " p3.spot_light(color='yellow', intensity=1.0, position=[20, 30, 20], angle=math.pi/9, target = [-20,-20,-20], cast_shadow=True, shadow_camera_far=5000)\n", - " #p3.point_light(color='orange', intensity=1.0, position=[20, 20, -20], distance=125, decay=2)\n", + " #p3.ambient_light(color='rgb(255,255,0)', intensity=1.5)\n", + " p3.directional_light(color='cyan', intensity=1.0, position=[100, 100, 100], target=[0,0,0], cast_shadow=True, shadow_bias= -0.005,shadow_camera_orthographic_size=100)\n", + " p3.directional_light(color='orange', intensity=1.0, position=[-100, 100, -100], target=[0,0,0], cast_shadow=True, shadow_bias= -0.005,shadow_camera_orthographic_size=100)\n", + " #p3.hemisphere_light(color='red', color2='yellow', intensity=0.5, position=[1, 0, 0])\n", + " #p3.point_light(color='orange', intensity=1.0, position=[20, 20, 20], distance=75, decay=0)\n", + " #p3.spot_light(color='yellow', intensity=1, position=[20, 30, 20], angle=math.pi/9, target = [-20,-20,-20], cast_shadow=True)\n", + " \n", " p3.show()\n", " return mesh\n", " else:\n", From d4c7d2dca2bd9433490a12e2d240fec16fadd0f5 Mon Sep 17 00:00:00 2001 From: rinftech-github Date: Sat, 28 Mar 2020 00:18:18 +0200 Subject: [PATCH 016/130] Add shadowmap support for point_light. Add shadow_radius --- ipyvolume/pylab.py | 28 +++++++++++++++++--- ipyvolume/widgets.py | 1 + js/src/light.ts | 48 ++++++++++++++++++++++------------- notebooks/lighting_demo.ipynb | 24 ++++++++++-------- 4 files changed, 69 insertions(+), 32 deletions(-) diff --git a/ipyvolume/pylab.py b/ipyvolume/pylab.py index b7260d7c..54e8fc0c 100644 --- a/ipyvolume/pylab.py +++ b/ipyvolume/pylab.py @@ -1569,6 +1569,7 @@ def directional_light( cast_shadow=False, shadow_map_size=512, shadow_bias=-0.0005, + shadow_radius=1, shadow_camera_near=0.5, shadow_camera_far=500, shadow_camera_orthographic_size=5): @@ -1582,6 +1583,7 @@ def directional_light( if cast_shadow: print("shadow_map_size: " + str(shadow_map_size)) print("shadow_bias: " + str(shadow_bias)) + print("shadow_radius: " + str(shadow_radius)) print("shadow_camera_near: " + str(shadow_camera_near)) print("shadow_camera_far: " + str(shadow_camera_far)) @@ -1598,6 +1600,7 @@ def directional_light( cast_shadow=cast_shadow, shadow_map_size=shadow_map_size, shadow_bias=shadow_bias, + shadow_radius=shadow_radius, shadow_camera_near=shadow_camera_near, shadow_camera_far=shadow_camera_far, shadow_camera_orthographic_size=shadow_camera_orthographic_size) @@ -1619,6 +1622,7 @@ def spot_light( cast_shadow=False, shadow_map_size=512, shadow_bias=-0.0005, + shadow_radius=1, shadow_camera_near=0.5, shadow_camera_far=500, shadow_camera_perspective_fov=50, @@ -1637,6 +1641,7 @@ def spot_light( if cast_shadow: print("shadow_map_size: " + str(shadow_map_size)) print("shadow_bias: " + str(shadow_bias)) + print("shadow_radius: " + str(shadow_radius)) print("shadow_camera_near: " + str(shadow_camera_near)) print("shadow_camera_far: " + str(shadow_camera_far)) print("shadow_camera_perspective_fov: " + str(shadow_camera_perspective_fov)) @@ -1659,6 +1664,7 @@ def spot_light( cast_shadow=cast_shadow, shadow_map_size=shadow_map_size, shadow_bias=shadow_bias, + shadow_radius=shadow_radius, shadow_camera_near=shadow_camera_near, shadow_camera_far=shadow_camera_far, shadow_camera_perspective_fov=shadow_camera_perspective_fov, @@ -1675,7 +1681,12 @@ def point_light( position=[0, 1, 0], distance=0, decay=1, - cast_shadow=False): + cast_shadow=False, + shadow_map_size=512, + shadow_bias=-0.0005, + shadow_radius=1, + shadow_camera_near=0.5, + shadow_camera_far=500): print("ADD POINT LIGHT (from pylab) ") print("color: " + str(color)) @@ -1684,7 +1695,13 @@ def point_light( print("distance: " + str(distance)) print("decay: " + str(decay)) print("cast_shadow: " + str(cast_shadow)) - + if cast_shadow: + print("shadow_map_size: " + str(shadow_map_size)) + print("shadow_bias: " + str(shadow_bias)) + print("shadow_radius: " + str(shadow_radius)) + print("shadow_camera_near: " + str(shadow_camera_near)) + print("shadow_camera_far: " + str(shadow_camera_far)) + light = ipv.Light( light_type='POINT', color=color, @@ -1694,7 +1711,12 @@ def point_light( position_z=position[2], distance=distance, decay=decay, - cast_shadow=cast_shadow) + cast_shadow=cast_shadow, + shadow_map_size=shadow_map_size, + shadow_bias=shadow_bias, + shadow_radius=shadow_radius, + shadow_camera_near=shadow_camera_near, + shadow_camera_far=shadow_camera_far) fig = gcf() fig.lights = fig.lights + [light] diff --git a/ipyvolume/widgets.py b/ipyvolume/widgets.py index 11f306f5..95bbe180 100644 --- a/ipyvolume/widgets.py +++ b/ipyvolume/widgets.py @@ -252,6 +252,7 @@ class Light(widgets.Widget): shadow_map_size=traitlets.CFloat(512).tag(sync=True) shadow_bias=traitlets.CFloat(-0.0005).tag(sync=True) + shadow_radius=traitlets.CFloat(1).tag(sync=True) shadow_camera_near=traitlets.CFloat(0.5).tag(sync=True) shadow_camera_far=traitlets.CFloat(500).tag(sync=True) shadow_camera_perspective_fov=traitlets.CFloat(50).tag(sync=True) diff --git a/js/src/light.ts b/js/src/light.ts index 00a5614a..7e2b126e 100644 --- a/js/src/light.ts +++ b/js/src/light.ts @@ -29,6 +29,7 @@ class LightView extends widgets.WidgetView { cast_shadow: any; shadow_map_size: any; shadow_bias: any; + shadow_radius: any; shadow_camera_near: any; shadow_camera_far: any; shadow_camera_perspective_fov: any; @@ -105,31 +106,42 @@ class LightView extends widgets.WidgetView { this.cast_shadow = this.model.get("cast_shadow"); this.distance = this.model.get("distance"); this.decay = this.model.get("decay"); + this.shadow_map_size = this.model.get("shadow_map_size"); + this.shadow_bias = this.model.get("shadow_bias"); + this.shadow_radius = this.model.get("shadow_radius"); + this.shadow_camera_near = this.model.get("shadow_camera_near"); + this.shadow_camera_far = this.model.get("shadow_camera_far"); if(this.light_type === this.LIGHT_TYPES.POINT) { - console.log("Create Point Light "); + console.log("Create Point Light"); this.current_light = new THREE.PointLight(this.color, this.intensity); this.current_light.position.set(this.position.x, this.position.y, this.position.z); this.current_light.distance = this.distance; this.current_light.decay = this.decay; + this.current_light.castShadow = this.cast_shadow; + this.current_light.shadow.mapSize.width = this.shadow_map_size; + this.current_light.shadow.mapSize.height = this.shadow_map_size; + this.current_light.shadow.bias = this.shadow_bias; // prevent shadow acne + this.current_light.shadow.radius = this.shadow_radius; + + this.current_light.shadow.camera.position.set(this.position.x, this.position.y, this.position.z); + this.current_light.shadow.camera.near = this.shadow_camera_near; + this.current_light.shadow.camera.far = this.shadow_camera_far; + this.lights.push(this.current_light); } else { + //TODO - move to a separate function this.target = new THREE.Object3D(); this.target.position.set(this.model.get("target_x"), this.model.get("target_y"), this.model.get("target_z")); this.target.updateMatrixWorld(); this.renderer.scene_scatter.add(this.target); - this.shadow_map_size = this.model.get("shadow_map_size"); - this.shadow_bias = this.model.get("shadow_bias"); - this.shadow_camera_near = this.model.get("shadow_camera_near"); - this.shadow_camera_far = this.model.get("shadow_camera_far"); - if(this.light_type === this.LIGHT_TYPES.DIRECTIONAL) { console.log("Create Directional Light"); - + this.shadow_camera_orthographic_size = this.model.get("shadow_camera_orthographic_size"); this.current_light = new THREE.DirectionalLight(this.color, this.intensity); @@ -137,21 +149,20 @@ class LightView extends widgets.WidgetView { this.current_light.target = this.target; this.current_light.castShadow = this.cast_shadow; - this.current_light.shadow = new THREE.DirectionalLightShadow(new THREE.OrthographicCamera(-5,5,-5,5,0.5,500)); - - this.current_light.shadow.camera.left = - this.shadow_camera_orthographic_size; - this.current_light.shadow.camera.right = this.shadow_camera_orthographic_size; - this.current_light.shadow.camera.top = this.shadow_camera_orthographic_size; - this.current_light.shadow.camera.bottom = - this.shadow_camera_orthographic_size; - this.current_light.shadow.mapSize.width = this.shadow_map_size; this.current_light.shadow.mapSize.height = this.shadow_map_size; this.current_light.shadow.bias = this.shadow_bias; // prevent shadow acne - + this.current_light.shadow.radius = this.shadow_radius; + this.current_light.shadow.camera.position.set(this.position.x, this.position.y, this.position.z); this.current_light.shadow.camera.near = this.shadow_camera_near; this.current_light.shadow.camera.far = this.shadow_camera_far; + this.current_light.shadow.camera.left = - this.shadow_camera_orthographic_size; + this.current_light.shadow.camera.right = this.shadow_camera_orthographic_size; + this.current_light.shadow.camera.top = this.shadow_camera_orthographic_size; + this.current_light.shadow.camera.bottom = - this.shadow_camera_orthographic_size; + this.lights.push(this.current_light); } else if(this.light_type === this.LIGHT_TYPES.SPOT) { @@ -173,12 +184,11 @@ class LightView extends widgets.WidgetView { this.current_light.penumbra = this.penumbra; this.current_light.castShadow = this.cast_shadow; - this.current_light.shadow = new THREE.SpotLightShadow(new THREE.PerspectiveCamera()); - this.current_light.shadow.mapSize.width = this.shadow_map_size; this.current_light.shadow.mapSize.height = this.shadow_map_size; this.current_light.shadow.bias = this.shadow_bias; // prevent shadow acne - + this.current_light.shadow.radius = this.shadow_radius; + this.current_light.shadow.camera.position.set(this.position.x, this.position.y, this.position.z); this.current_light.shadow.camera.near = this.shadow_camera_near; this.current_light.shadow.camera.far = this.shadow_camera_far; @@ -217,6 +227,7 @@ class LightModel extends widgets.WidgetModel { penumbra: serialize.array_or_json, shadow_map_size: serialize.array_or_json, shadow_bias: serialize.array_or_json, + shadow_radius: serialize.array_or_json, shadow_camera_near: serialize.array_or_json, shadow_camera_far: serialize.array_or_json, shadow_camera_perspective_fov: serialize.array_or_json, @@ -249,6 +260,7 @@ class LightModel extends widgets.WidgetModel { cast_shadow: false, shadow_map_size: 512, shadow_bias: -0.0005, + shadow_radius: 1, shadow_camera_near: 0.5, shadow_camera_far: 500, shadow_camera_perspective_fov: 50, diff --git a/notebooks/lighting_demo.ipynb b/notebooks/lighting_demo.ipynb index ee47c999..2d28c3b0 100644 --- a/notebooks/lighting_demo.ipynb +++ b/notebooks/lighting_demo.ipynb @@ -2,21 +2,23 @@ "cells": [ { "cell_type": "code", - "execution_count": 10, + "execution_count": 20, "metadata": {}, "outputs": [ { "name": "stdout", "output_type": "stream", "text": [ - "ADD DIRECTIONAL LIGHT (from pylab) \n", - "color: cyan\n", + "ADD POINT LIGHT (from pylab) \n", + "color: orange\n", "intensity: 1.0\n", - "position: 100 100 100\n", - "target: 0 0 0\n", + "position: 20 30 20\n", + "distance: 65\n", + "decay: 1\n", "cast_shadow: True\n", "shadow_map_size: 512\n", "shadow_bias: -0.005\n", + "shadow_radius: 3\n", "shadow_camera_near: 0.5\n", "shadow_camera_far: 500\n" ] @@ -24,7 +26,7 @@ { "data": { "application/vnd.jupyter.widget-view+json": { - "model_id": "8b9fe0a2374c4783ae57201e1b750c8f", + "model_id": "05e111446f0d4f22a12eafac36653632", "version_major": 2, "version_minor": 0 }, @@ -38,7 +40,7 @@ { "data": { "application/vnd.jupyter.widget-view+json": { - "model_id": "9d0a8f983c4d4bed9bbe759632d50d98", + "model_id": "dba17b1c7fd84b67bb981d496a1a7d29", "version_major": 2, "version_minor": 0 }, @@ -340,11 +342,11 @@ " \n", " drawSurface(separateFigure=False)\n", " #p3.ambient_light(color='rgb(255,255,0)', intensity=1.5)\n", - " p3.directional_light(color='cyan', intensity=1.0, position=[100, 100, 100], target=[0,0,0], cast_shadow=True, shadow_bias= -0.005,shadow_camera_orthographic_size=100)\n", - " p3.directional_light(color='orange', intensity=1.0, position=[-100, 100, -100], target=[0,0,0], cast_shadow=True, shadow_bias= -0.005,shadow_camera_orthographic_size=100)\n", + " #p3.directional_light(color='cyan', intensity=1.0, position=[100, 100, 100], target=[0,0,0], cast_shadow=True, shadow_bias= -0.005,shadow_camera_orthographic_size=100)\n", + " #p3.directional_light(color='orange', intensity=1.0, position=[-100, 100, -100], target=[0,0,0], cast_shadow=True, shadow_bias= -0.005,shadow_camera_orthographic_size=100)\n", " #p3.hemisphere_light(color='red', color2='yellow', intensity=0.5, position=[1, 0, 0])\n", - " #p3.point_light(color='orange', intensity=1.0, position=[20, 20, 20], distance=75, decay=0)\n", - " #p3.spot_light(color='yellow', intensity=1, position=[20, 30, 20], angle=math.pi/9, target = [-20,-20,-20], cast_shadow=True)\n", + " p3.point_light(color='orange', intensity=1.0, position=[20, 30, 20], distance=65, decay=0.5, cast_shadow=True,shadow_radius=3,shadow_bias= -0.005)\n", + " #p3.spot_light(color='yellow', intensity=1, position=[20, 30, 20], angle=math.pi/9, target = [-20,-20,-20], cast_shadow=True,shadow_radius=2)\n", " \n", " p3.show()\n", " return mesh\n", From 2f1478b4f0270bc158ef63c85fe4b2851a99b0fb Mon Sep 17 00:00:00 2001 From: rinftech-github Date: Sun, 29 Mar 2020 14:57:51 +0300 Subject: [PATCH 017/130] Simplify lighting_demo notebook. Replace color with light_color --- ipyvolume/pylab.py | 43 ++-- ipyvolume/widgets.py | 4 +- js/src/light.ts | 36 +-- notebooks/lighting_demo.ipynb | 413 ++++++++++------------------------ 4 files changed, 169 insertions(+), 327 deletions(-) diff --git a/ipyvolume/pylab.py b/ipyvolume/pylab.py index 54e8fc0c..6b790ebd 100644 --- a/ipyvolume/pylab.py +++ b/ipyvolume/pylab.py @@ -352,6 +352,7 @@ def plot_trisurf(x, y, z, triangles=None, lines=None, color=default_color, u=Non mesh = ipv.Mesh(x=x, y=y, z=z, triangles=triangles, lines=lines, color=color, u=u, v=v, texture=texture) _grow_limits(np.array(x).reshape(-1), np.array(y).reshape(-1), np.array(z).reshape(-1)) fig.meshes = fig.meshes + [mesh] + print(fig.meshes) return mesh @@ -1517,39 +1518,41 @@ def _make_triangles_lines(shape, wrapx=False, wrapy=False): return triangles, lines -def ambient_light(color=default_color_selected, intensity = 1): +def ambient_light( + light_color2=default_color_selected, + intensity = 1): print("ADD AMBIENT LIGHT (from pylab) ") - print("color: " + str(color)) + print("light_color2: " + str(light_color2)) print("intensity: " + str(intensity)) light = ipv.Light( light_type='AMBIENT', - color=color, + light_color2=light_color2, intensity=intensity) fig = gcf() fig.lights = fig.lights + [light] - + #print(fig.lights) return light def hemisphere_light( - color=default_color_selected, - color2=default_color, + light_color=default_color_selected, + light_color2=default_color, intensity = 1, position=[0, 1, 0], cast_shadow=False): print("ADD HEMISPHERE LIGHT (from pylab) ") - print("color: " + str(color)) - print("color2: " + str(color2)) + print("light_color: " + str(light_color)) + print("light_color2: " + str(light_color2)) print("intensity: " + str(intensity)) print("position: " + str(position[0])+" "+str(position[1])+" "+str(position[2])) print("cast_shadow: " + str(cast_shadow)) light = ipv.Light( light_type='HEMISPHERE', - color=color, - color2=color2, + light_color=light_color, + light_color2=light_color2, intensity=intensity, position_x=position[0], position_y=position[1], @@ -1562,7 +1565,7 @@ def hemisphere_light( return light def directional_light( - color=default_color_selected, + light_color=default_color_selected, intensity = 1, position=[0, 1, 0], target=[0, 0, 0], @@ -1575,7 +1578,7 @@ def directional_light( shadow_camera_orthographic_size=5): print("ADD DIRECTIONAL LIGHT (from pylab) ") - print("color: " + str(color)) + print("light_color: " + str(light_color)) print("intensity: " + str(intensity)) print("position: " + str(position[0])+" "+str(position[1])+" "+str(position[2])) print("target: " + str(target[0])+" "+str(target[1])+" "+str(target[2])) @@ -1589,7 +1592,7 @@ def directional_light( light = ipv.Light( light_type='DIRECTIONAL', - color=color, + light_color=light_color, intensity=intensity, position_x=position[0], position_y=position[1], @@ -1611,7 +1614,7 @@ def directional_light( return light def spot_light( - color=default_color_selected, + light_color=default_color_selected, intensity = 1, position=[0, 1, 0], target=[0, 0, 0], @@ -1629,7 +1632,7 @@ def spot_light( shadow_camera_perspective_aspect=1): print("ADD SPOT LIGHT (from pylab) ") - print("color: " + str(color)) + print("light_color: " + str(light_color)) print("intensity: " + str(intensity)) print("position: " + str(position[0])+" "+str(position[1])+" "+str(position[2])) print("target: " + str(target[0])+" "+str(target[1])+" "+str(target[2])) @@ -1649,7 +1652,7 @@ def spot_light( light = ipv.Light( light_type='SPOT', - color=color, + light_color=light_color, intensity=intensity, position_x=position[0], position_y=position[1], @@ -1676,7 +1679,7 @@ def spot_light( return light def point_light( - color=default_color_selected, + light_color=default_color_selected, intensity = 1, position=[0, 1, 0], distance=0, @@ -1689,7 +1692,7 @@ def point_light( shadow_camera_far=500): print("ADD POINT LIGHT (from pylab) ") - print("color: " + str(color)) + print("light_color: " + str(light_color)) print("intensity: " + str(intensity)) print("position: " + str(position[0])+" "+str(position[1])+" "+str(position[2])) print("distance: " + str(distance)) @@ -1701,10 +1704,10 @@ def point_light( print("shadow_radius: " + str(shadow_radius)) print("shadow_camera_near: " + str(shadow_camera_near)) print("shadow_camera_far: " + str(shadow_camera_far)) - + light = ipv.Light( light_type='POINT', - color=color, + light_color=light_color, intensity=intensity, position_x=position[0], position_y=position[1], diff --git a/ipyvolume/widgets.py b/ipyvolume/widgets.py index 95bbe180..c462adc8 100644 --- a/ipyvolume/widgets.py +++ b/ipyvolume/widgets.py @@ -228,8 +228,8 @@ class Light(widgets.Widget): _view_module_version = Unicode(semver_range_frontend).tag(sync=True) _model_module_version = Unicode(semver_range_frontend).tag(sync=True) - color = Array(default_value="red", allow_none=True).tag(sync=True, **color_serialization) - color2 = Array(default_value="white", allow_none=True).tag(sync=True, **color_serialization) + light_color = Array(default_value="red", allow_none=True).tag(sync=True, **color_serialization) + light_color2 = Array(default_value="white", allow_none=True).tag(sync=True, **color_serialization) intensity = traitlets.CFloat(1).tag(sync=True) light_type = traitlets.Enum(values=['AMBIENT', 'DIRECTIONAL', 'SPOT', 'POINT', 'HEMISPHERE'], default_value='AMBIENT').tag(sync=True) diff --git a/js/src/light.ts b/js/src/light.ts index 7e2b126e..cc54a0ad 100644 --- a/js/src/light.ts +++ b/js/src/light.ts @@ -17,8 +17,8 @@ class LightView extends widgets.WidgetView { LIGHT_TYPES: any; light_type: any; - color: any; - color2: any; + light_color: any; + light_color2: any; intensity: any; position: any; target: any; @@ -47,15 +47,16 @@ class LightView extends widgets.WidgetView { }; this.renderer = this.options.parent; - this.model.on("change:color", + this.model.on("change:light_color change:intensity", this.on_change, this); - - console.log(this.LIGHT_TYPES); + console.log(this.renderer.scene_scatter.children) + //console.log(this.LIGHT_TYPES); this.create_light(); this.add_to_scene(); } on_change(attribute) { + console.log("CHAKA LAKA " + attribute) for (const key of this.model.changedAttributes()) { console.log("changed " +key); } @@ -64,6 +65,7 @@ class LightView extends widgets.WidgetView { add_to_scene() { this.lights.forEach((light) => { this.renderer.scene_scatter.add(light); + console.log("1") }); } @@ -78,13 +80,13 @@ class LightView extends widgets.WidgetView { create_light() { this.lights = []; - this.color = this.model.get("color"); + this.light_color = this.model.get("light_color"); this.intensity = this.model.get("intensity"); this.light_type = this.model.get("light_type"); //no shadow support if(this.light_type === this.LIGHT_TYPES.AMBIENT){ - console.log("Create Ambient Light"); - this.current_light = new THREE.AmbientLight(this.color, this.intensity); + console.log("Create Ambient Light " + this.intensity); + this.current_light = new THREE.AmbientLight(this.light_color, this.intensity); this.lights.push(this.current_light); } else{ @@ -94,9 +96,9 @@ class LightView extends widgets.WidgetView { if(this.light_type === this.LIGHT_TYPES.HEMISPHERE) { console.log("Create Hemisphere Light "); - this.color2 = this.model.get("color2"); + this.light_color2 = this.model.get("light_color2"); - this.current_light = new THREE.HemisphereLight(this.color, this.color2, this.intensity); + this.current_light = new THREE.HemisphereLight(this.light_color, this.light_color2, this.intensity); this.current_light.position.set(this.position.x, this.position.y, this.position.z); this.lights.push(this.current_light); } @@ -114,7 +116,7 @@ class LightView extends widgets.WidgetView { if(this.light_type === this.LIGHT_TYPES.POINT) { console.log("Create Point Light"); - this.current_light = new THREE.PointLight(this.color, this.intensity); + this.current_light = new THREE.PointLight(this.light_color, this.intensity); this.current_light.position.set(this.position.x, this.position.y, this.position.z); this.current_light.distance = this.distance; @@ -144,7 +146,7 @@ class LightView extends widgets.WidgetView { this.shadow_camera_orthographic_size = this.model.get("shadow_camera_orthographic_size"); - this.current_light = new THREE.DirectionalLight(this.color, this.intensity); + this.current_light = new THREE.DirectionalLight(this.light_color, this.intensity); this.current_light.position.set(this.position.x, this.position.y, this.position.z); this.current_light.target = this.target; this.current_light.castShadow = this.cast_shadow; @@ -174,7 +176,7 @@ class LightView extends widgets.WidgetView { this.shadow_camera_perspective_fov = this.model.get("shadow_camera_perspective_fov"); this.shadow_camera_perspective_aspect = this.model.get("shadow_camera_perspective_aspect"); - this.current_light = new THREE.SpotLight(this.color, this.intensity); + this.current_light = new THREE.SpotLight(this.light_color, this.intensity); this.current_light.position.set(this.position.x, this.position.y, this.position.z); this.current_light.target = this.target; @@ -212,8 +214,8 @@ export class LightModel extends widgets.WidgetModel { static serializers = { ...widgets.WidgetModel.serializers, - color: serialize.color_or_json, - color2: serialize.color_or_json, + light_color: serialize.color_or_json, + light_color2: serialize.color_or_json, intensity: serialize.array_or_json, position_x: serialize.array_or_json, position_y: serialize.array_or_json, @@ -244,8 +246,8 @@ class LightModel extends widgets.WidgetModel { _model_module_version: semver_range, _view_module_version: semver_range, light_type: 'AMBIENT', - color: "red", - color2: "white", + light_color: "red", + light_color2: "white", intensity: 1, position_x: 0, position_y: 1, diff --git a/notebooks/lighting_demo.ipynb b/notebooks/lighting_demo.ipynb index 2d28c3b0..60f82c21 100644 --- a/notebooks/lighting_demo.ipynb +++ b/notebooks/lighting_demo.ipynb @@ -2,31 +2,31 @@ "cells": [ { "cell_type": "code", - "execution_count": 20, + "execution_count": 3, "metadata": {}, "outputs": [ { "name": "stdout", "output_type": "stream", "text": [ - "ADD POINT LIGHT (from pylab) \n", - "color: orange\n", - "intensity: 1.0\n", - "position: 20 30 20\n", - "distance: 65\n", - "decay: 1\n", - "cast_shadow: True\n", - "shadow_map_size: 512\n", - "shadow_bias: -0.005\n", - "shadow_radius: 3\n", - "shadow_camera_near: 0.5\n", - "shadow_camera_far: 500\n" + "[Mesh(line_material=ShaderMaterial(), material=ShaderMaterial(side='DoubleSide'), texture=None, triangles=array([[ 0, 50, 51],\n", + " [ 0, 51, 1],\n", + " [ 1, 51, 52],\n", + " ...,\n", + " [2447, 2498, 2448],\n", + " [2448, 2498, 2499],\n", + " [2448, 2499, 2449]], dtype=uint32), u=array([0. , 0.02040816, 0.04081633, ..., 0.95918367, 0.97959184,\n", + " 1. ]), v=array([0., 0., 0., ..., 1., 1., 1.]), x=array([8. , 8.71156903, 9.27378258, ..., 2.26620625, 3.17335636,\n", + " 4. ]), y=array([ 0.00000000e+00, 2.30388865e+00, 4.58237413e+00, ...,\n", + " -4.05847334e+00, -2.04603459e+00, -3.91886976e-15]), z=array([ 0.00000000e+00, 0.00000000e+00, 0.00000000e+00, ...,\n", + " -5.05879616e-16, -4.93880453e-16, -4.89858720e-16])), Mesh(color=array('orange', dtype='= rmin)] = 0.5\n", - " if \"data_min\" not in kwargs:\n", - " kwargs[\"data_min\"] = 0\n", - " if \"data_max\" not in kwargs:\n", - " kwargs[\"data_max\"] = 1\n", - " data = data.T\n", - " if draw:\n", - " vol = p3.volshow(data=data, **kwargs)\n", - " if show:\n", - " p3.show()\n", - " return vol\n", - " else:\n", - " return data\n", - "\n", - "\n", - "# http://graphics.stanford.edu/data/voldata/\n", - "\n", - "\n", - "\n", - "\n", - "def brain(\n", - " draw=True, show=True, fiducial=True, flat=True, inflated=True, subject='S1', interval=1000, uv=True, color=None\n", - "):\n", - " \"\"\"Show a human brain model.\n", - "\n", - " Requirement:\n", - "\n", - " $ pip install https://github.com/gallantlab/pycortex\n", - " \"\"\"\n", - " import ipyvolume as ipv\n", - "\n", - " try:\n", - " import cortex\n", - " except:\n", - " warnings.warn(\"it seems pycortex is not installed, which is needed for this example\")\n", - " raise\n", - " xlist, ylist, zlist = [], [], []\n", - " polys_list = []\n", - "\n", - " def add(pts, polys):\n", - " xlist.append(pts[:, 0])\n", - " ylist.append(pts[:, 1])\n", - " zlist.append(pts[:, 2])\n", - " polys_list.append(polys)\n", - "\n", - " def n(x):\n", - " return (x - x.min()) / x.ptp()\n", - "\n", - " if fiducial or color is True:\n", - " pts, polys = cortex.db.get_surf('S1', 'fiducial', merge=True)\n", - " x, y, z = pts.T\n", - " r = n(x)\n", - " g = n(y)\n", - " b = n(z)\n", - " if color is True:\n", - " color = np.array([r, g, b]).T.copy()\n", - " else:\n", - " color = None\n", - " if fiducial:\n", - " add(pts, polys)\n", - " else:\n", - " if color is False:\n", - " color = None\n", - " if inflated:\n", - " add(*cortex.db.get_surf('S1', 'inflated', merge=True, nudge=True))\n", - " u = v = None\n", - " if flat or uv:\n", - " pts, polys = cortex.db.get_surf('S1', 'flat', merge=True, nudge=True)\n", - " x, y, z = pts.T\n", - " u = n(x)\n", - " v = n(y)\n", - " if flat:\n", - " add(pts, polys)\n", - "\n", - " polys_list.sort(key=lambda x: len(x))\n", - " polys = polys_list[0]\n", - " if draw:\n", - " if color is None:\n", - " mesh = ipv.plot_trisurf(xlist, ylist, zlist, polys, u=u, v=v)\n", - " else:\n", - " mesh = ipv.plot_trisurf(xlist, ylist, zlist, polys, color=color, u=u, v=v)\n", - " if show:\n", - " if len(x) > 1:\n", - " ipv.animation_control(mesh, interval=interval)\n", - " ipv.squarelim()\n", - " ipv.show()\n", - " return mesh\n", - " else:\n", - " return xlist, ylist, zlist, polys\n", - "\n", - "\n", - "def head(draw=True, show=True, max_shape=256):\n", - " \"\"\"Show a volumetric rendering of a human male head.\"\"\"\n", - " # inspired by http://graphicsrunner.blogspot.com/2009/01/volume-rendering-102-transfer-functions.html\n", - " import ipyvolume as ipv\n", - " from scipy.interpolate import interp1d\n", - "\n", - " # First part is a simpler version of setting up the transfer function. Interpolation with higher order\n", - " # splines does not work well, the original must do sth different\n", - " colors = [[0.91, 0.7, 0.61, 0.0], [0.91, 0.7, 0.61, 80.0], [1.0, 1.0, 0.85, 82.0], [1.0, 1.0, 0.85, 256]]\n", - " x = np.array([k[-1] for k in colors])\n", - " rgb = np.array([k[:3] for k in colors])\n", - " N = 256\n", - " xnew = np.linspace(0, 256, N)\n", - " tf_data = np.zeros((N, 4))\n", - " kind = 'linear'\n", - " for channel in range(3):\n", - " f = interp1d(x, rgb[:, channel], kind=kind)\n", - " ynew = f(xnew)\n", - " tf_data[:, channel] = ynew\n", - " alphas = [[0, 0], [0, 40], [0.2, 60], [0.05, 63], [0, 80], [0.9, 82], [1.0, 256]]\n", - " x = np.array([k[1] * 1.0 for k in alphas])\n", - " y = np.array([k[0] * 1.0 for k in alphas])\n", - " f = interp1d(x, y, kind=kind)\n", - " ynew = f(xnew)\n", - " tf_data[:, 3] = ynew\n", - " tf = ipv.TransferFunction(rgba=tf_data.astype(np.float32))\n", - "\n", - " head_data = ipv.datasets.head.fetch().data\n", - " if draw:\n", - " vol = ipv.volshow(head_data, tf=tf, max_shape=max_shape)\n", - " if show:\n", - " ipv.show()\n", - " return vol\n", - " else:\n", - " return head_data\n", - "\n", - "\n", - "def gaussian(N=1000, draw=True, show=True, seed=42, color=None, marker='sphere'):\n", - " \"\"\"Show N random gaussian distributed points using a scatter plot.\"\"\"\n", - " import ipyvolume as ipv\n", - "\n", - " rng = np.random.RandomState(seed) # pylint: disable=no-member\n", - " x, y, z = rng.normal(size=(3, N))\n", - "\n", - " if draw:\n", - " if color:\n", - " mesh = ipv.scatter(x, y, z, marker=marker, color=color)\n", - " else:\n", - " mesh = ipv.scatter(x, y, z, marker=marker)\n", - " if show:\n", - " # ipv.squarelim()\n", - " ipv.show()\n", - " return mesh\n", - " else:\n", - " return x, y, z\n", - " \n", "def klein_bottle(\n", " draw=True,\n", " show=True,\n", @@ -292,37 +105,15 @@ " both=False,\n", " interval=1000,\n", "):\n", - " \"\"\"Show one or two Klein bottles.\"\"\"\n", - " p3.clear()\n", " # http://paulbourke.net/geometry/klein/\n", " u = np.linspace(0, 2 * pi, num=50, endpoint=endpoint)\n", " v = np.linspace(0, 2 * pi, num=50, endpoint=endpoint)\n", " u, v = np.meshgrid(u, v)\n", - " if both:\n", - " x1, y1, z1, _u1, _v1 = klein_bottle(endpoint=endpoint, draw=False, show=False)\n", - " x2, y2, z2, _u2, _v2 = klein_bottle(endpoint=endpoint, draw=False, show=False, figure8=True)\n", - " x = [x1, x2]\n", - " y = [y1, y2]\n", - " z = [z1, z2]\n", - " else:\n", - " if figure8:\n", - " # u -= np.pi\n", - " # v -= np.pi\n", - " a = 2\n", - " s = 5\n", - " x = s * (a + cos(u / 2) * sin(v) - sin(u / 2) * sin(2 * v) / 2) * cos(u)\n", - " y = s * (a + cos(u / 2) * sin(v) - sin(u / 2) * sin(2 * v) / 2) * sin(u)\n", - " z = s * (sin(u / 2) * sin(v) + cos(u / 2) * sin(2 * v) / 2)\n", - " else:\n", - " r = 4 * (1 - cos(u) / 2)\n", - " x = 6 * cos(u) * (1 + sin(u)) + r * cos(u) * cos(v) * (u < pi) + r * cos(v + pi) * (u >= pi)\n", - " y = 16 * sin(u) + r * sin(u) * cos(v) * (u < pi)\n", - " z = r * sin(v)\n", - " if draw:\n", - " if texture:\n", - " uv = True\n", - " if uv:\n", - " mesh = p3.plot_mesh(\n", + " r = 4 * (1 - cos(u) / 2)\n", + " x = 6 * cos(u) * (1 + sin(u)) + r * cos(u) * cos(v) * (u < pi) + r * cos(v + pi) * (u >= pi)\n", + " y = 16 * sin(u) + r * sin(u) * cos(v) * (u < pi)\n", + " z = r * sin(v)\n", + " mesh = p3.plot_mesh(\n", " x,\n", " y,\n", " z,\n", @@ -333,27 +124,9 @@ " wireframe=wireframe,\n", " texture=texture,\n", " )\n", - " else:\n", - " mesh = p3.plot_mesh(x, y, z, wrapx=not endpoint, wrapy=not endpoint, wireframe=wireframe, texture=texture)\n", - " if show:\n", - " if both:\n", - " p3.animation_control(mesh, interval=interval)\n", - " p3.squarelim()\n", - " \n", - " drawSurface(separateFigure=False)\n", - " #p3.ambient_light(color='rgb(255,255,0)', intensity=1.5)\n", - " #p3.directional_light(color='cyan', intensity=1.0, position=[100, 100, 100], target=[0,0,0], cast_shadow=True, shadow_bias= -0.005,shadow_camera_orthographic_size=100)\n", - " #p3.directional_light(color='orange', intensity=1.0, position=[-100, 100, -100], target=[0,0,0], cast_shadow=True, shadow_bias= -0.005,shadow_camera_orthographic_size=100)\n", - " #p3.hemisphere_light(color='red', color2='yellow', intensity=0.5, position=[1, 0, 0])\n", - " p3.point_light(color='orange', intensity=1.0, position=[20, 30, 20], distance=65, decay=0.5, cast_shadow=True,shadow_radius=3,shadow_bias= -0.005)\n", - " #p3.spot_light(color='yellow', intensity=1, position=[20, 30, 20], angle=math.pi/9, target = [-20,-20,-20], cast_shadow=True,shadow_radius=2)\n", - " \n", - " p3.show()\n", - " return mesh\n", - " else:\n", - " return x, y, z, u, v\n", "\n", - "def drawSurface(separateFigure=True):\n", + " return mesh\n", + "def worldplane(separate_figure=False):\n", " k = 20\n", " h = -15\n", " tx = np.array([k, -k, -k, k])\n", @@ -363,26 +136,49 @@ " tri = [(0, 1, 2), (0, 2, 3)]\n", " p3.plot_trisurf(tx, ty, tz, triangles=tri, color='orange')\n", " \n", - " if separateFigure:\n", + " if separate_figure:\n", " p3.figure()\n", " p3.show()\n", - " \n", + "def ambient_light_widget():\n", + " selected_size = FloatSlider(value=5, min=0, max=10, step=1, description='Intensity:', disabled=False,\n", + " continuous_update=True,orientation='horizontal', readout=True, readout_format='d')\n", + " selected_color = ColorPicker(concise=False, description='Ambient Color:', value='red', disabled=False)\n", + " display(selected_size,selected_color)\n", + " #widgets.interact(p3.ambient_light,light_color=selected_color,intensity=selected_size);\n", + " print(selected_size.value)\n", + " return selected_size\n", + "\n", + "def add_lights():\n", + " #ambient_light_widget()\n", + "\n", + " #p3.ambient_light(light_color=\"rgb(255,0,0)\", intensity=1.5)\n", + "\n", + " p3.directional_light(light_color='cyan', intensity=1.0, position=[100, 100, 100], target=[0,0,0], cast_shadow=True, shadow_bias= -0.005,shadow_camera_orthographic_size=100)\n", + " p3.directional_light(light_color='orange', intensity=1.0, position=[-100, 100, -100], target=[0,0,0], cast_shadow=True, shadow_bias= -0.005,shadow_camera_orthographic_size=100)\n", + " #p3.hemisphere_light(light_color='red', color2='yellow', intensity=0.5, position=[1, 0, 0])\n", + " #p3.point_light(light_color='orange', intensity=1.0, position=[20, 30, 20], distance=65, decay=0.5, cast_shadow=True,shadow_radius=3,shadow_bias= -0.005)\n", + " #p3.spot_light(light_color='yellow', intensity=1, position=[20, 30, 20], angle=math.pi/9, target = [-20,-20,-20], cast_shadow=True,shadow_radius=2)\n", + "\n", + "\n", + "\n", + "#very important to first call p3.clear()\n", + "p3.clear()\n", "klein_bottle()\n", - "#drawSurface()\n", - "#gaussian()\n", - "#brain()\n", - "#head()" + "worldplane()\n", + "\n", + "p3.show()\n", + "add_lights()\n" ] }, { "cell_type": "code", - "execution_count": 2, + "execution_count": 12, "metadata": {}, "outputs": [ { "data": { "application/vnd.jupyter.widget-view+json": { - "model_id": "58c318d75ab74107921176aea915f3d7", + "model_id": "ce286e36a3324e04a3346c22448b6505", "version_major": 2, "version_minor": 0 }, @@ -396,19 +192,60 @@ { "data": { "application/vnd.jupyter.widget-view+json": { - "model_id": "583aca4273ec466eb22be9d3ca15971b", + "model_id": "ba78c6160cc542dcaff205f33f2968b4", "version_major": 2, "version_minor": 0 }, "text/plain": [ - "Mesh(line_material=ShaderMaterial(), material=ShaderMaterial(side='DoubleSide'), texture=None, triangles=array…" + "Volume(data=array([[[ 1, 1, 1, ..., 1, 1, 1],\n", + " [ 1, 1, 1, ..., 1, 1, 1],\n", + " [ 1, 1, 1,…" ] }, "metadata": {}, "output_type": "display_data" } ], - "source": [] + "source": [ + "def head2(draw=True, show=True, max_shape=256):\n", + " \"\"\"Show a volumetric rendering of a human male head.\"\"\"\n", + " # inspired by http://graphicsrunner.blogspot.com/2009/01/volume-rendering-102-transfer-functions.html\n", + " import ipyvolume as ipv\n", + " from scipy.interpolate import interp1d\n", + "\n", + " # First part is a simpler version of setting up the transfer function. Interpolation with higher order\n", + " # splines does not work well, the original must do sth different\n", + " colors = [[0.91, 0.7, 0.61, 0.0], [0.91, 0.7, 0.61, 80.0], [1.0, 1.0, 0.85, 82.0], [1.0, 1.0, 0.85, 256]]\n", + " x = np.array([k[-1] for k in colors])\n", + " rgb = np.array([k[:3] for k in colors])\n", + " N = 256\n", + " xnew = np.linspace(0, 256, N)\n", + " tf_data = np.zeros((N, 4))\n", + " kind = 'linear'\n", + " for channel in range(3):\n", + " f = interp1d(x, rgb[:, channel], kind=kind)\n", + " ynew = f(xnew)\n", + " tf_data[:, channel] = ynew\n", + " alphas = [[0, 0], [0, 40], [0.2, 60], [0.05, 63], [0, 80], [0.9, 82], [1.0, 256]]\n", + " x = np.array([k[1] * 1.0 for k in alphas])\n", + " y = np.array([k[0] * 1.0 for k in alphas])\n", + " f = interp1d(x, y, kind=kind)\n", + " ynew = f(xnew)\n", + " tf_data[:, 3] = ynew\n", + " tf = ipv.TransferFunction(rgba=tf_data.astype(np.float32))\n", + "\n", + " head_data = ipv.datasets.head.fetch().data\n", + " if draw:\n", + " vol = ipv.volshow(head_data, tf=tf, max_shape=max_shape)\n", + " if show:\n", + " ipv.show()\n", + " return vol\n", + " else:\n", + " return head_data\n", + " \n", + "p3.clear()\n", + "head2()" + ] }, { "cell_type": "code", From fd5e785e23e1e6f33faba2c9c4895846ade53724 Mon Sep 17 00:00:00 2001 From: rinftech-github Date: Sun, 29 Mar 2020 18:02:09 +0300 Subject: [PATCH 018/130] Add mesh widget material params. Add shadowmap types. Mesh class cleanup --- ipyvolume/pylab.py | 21 ++++-- ipyvolume/widgets.py | 14 +++- js/glsl/mesh-fragment-phong.glsl | 3 +- js/glsl/mesh-fragment-physical.glsl | 3 +- js/src/light.ts | 29 +++++++- js/src/mesh.ts | 101 ++++------------------------ notebooks/lighting_demo.ipynb | 20 ++---- 7 files changed, 77 insertions(+), 114 deletions(-) diff --git a/ipyvolume/pylab.py b/ipyvolume/pylab.py index 6b790ebd..4509f1d5 100644 --- a/ipyvolume/pylab.py +++ b/ipyvolume/pylab.py @@ -1575,7 +1575,8 @@ def directional_light( shadow_radius=1, shadow_camera_near=0.5, shadow_camera_far=500, - shadow_camera_orthographic_size=5): + shadow_camera_orthographic_size=5, + shadow_map_type='PCF_SOFT'): print("ADD DIRECTIONAL LIGHT (from pylab) ") print("light_color: " + str(light_color)) @@ -1589,6 +1590,7 @@ def directional_light( print("shadow_radius: " + str(shadow_radius)) print("shadow_camera_near: " + str(shadow_camera_near)) print("shadow_camera_far: " + str(shadow_camera_far)) + print("shadow_map_type: " + str(shadow_map_type)) light = ipv.Light( light_type='DIRECTIONAL', @@ -1606,7 +1608,8 @@ def directional_light( shadow_radius=shadow_radius, shadow_camera_near=shadow_camera_near, shadow_camera_far=shadow_camera_far, - shadow_camera_orthographic_size=shadow_camera_orthographic_size) + shadow_camera_orthographic_size=shadow_camera_orthographic_size, + shadow_map_type=shadow_map_type) fig = gcf() fig.lights = fig.lights + [light] @@ -1629,7 +1632,8 @@ def spot_light( shadow_camera_near=0.5, shadow_camera_far=500, shadow_camera_perspective_fov=50, - shadow_camera_perspective_aspect=1): + shadow_camera_perspective_aspect=1, + shadow_map_type='PCF_SOFT'): print("ADD SPOT LIGHT (from pylab) ") print("light_color: " + str(light_color)) @@ -1649,6 +1653,7 @@ def spot_light( print("shadow_camera_far: " + str(shadow_camera_far)) print("shadow_camera_perspective_fov: " + str(shadow_camera_perspective_fov)) print("shadow_camera_perspective_aspect: " + str(shadow_camera_perspective_aspect)) + print("shadow_map_type: " + str(shadow_map_type)) light = ipv.Light( light_type='SPOT', @@ -1671,7 +1676,8 @@ def spot_light( shadow_camera_near=shadow_camera_near, shadow_camera_far=shadow_camera_far, shadow_camera_perspective_fov=shadow_camera_perspective_fov, - shadow_camera_perspective_aspect=shadow_camera_perspective_aspect) + shadow_camera_perspective_aspect=shadow_camera_perspective_aspect, + shadow_map_type=shadow_map_type) fig = gcf() fig.lights = fig.lights + [light] @@ -1689,7 +1695,8 @@ def point_light( shadow_bias=-0.0005, shadow_radius=1, shadow_camera_near=0.5, - shadow_camera_far=500): + shadow_camera_far=500, + shadow_map_type='PCF_SOFT'): print("ADD POINT LIGHT (from pylab) ") print("light_color: " + str(light_color)) @@ -1704,6 +1711,7 @@ def point_light( print("shadow_radius: " + str(shadow_radius)) print("shadow_camera_near: " + str(shadow_camera_near)) print("shadow_camera_far: " + str(shadow_camera_far)) + print("shadow_map_type: " + str(shadow_map_type)) light = ipv.Light( light_type='POINT', @@ -1719,7 +1727,8 @@ def point_light( shadow_bias=shadow_bias, shadow_radius=shadow_radius, shadow_camera_near=shadow_camera_near, - shadow_camera_far=shadow_camera_far) + shadow_camera_far=shadow_camera_far, + shadow_map_type=shadow_map_type) fig = gcf() fig.lights = fig.lights + [light] diff --git a/ipyvolume/widgets.py b/ipyvolume/widgets.py index c462adc8..7ec05ac1 100644 --- a/ipyvolume/widgets.py +++ b/ipyvolume/widgets.py @@ -80,6 +80,17 @@ def _default_material(self): def _default_line_material(self): return pythreejs.ShaderMaterial() + lighting_model = traitlets.Enum(values=['DEFAULT', 'LAMBERT', 'PHONG', 'PHYSICAL'], default_value='DEFAULT').tag(sync=True) + diffuse_color = Array(default_value="white", allow_none=True).tag(sync=True, **color_serialization) + opacity = traitlets.CFloat(1).tag(sync=True) + specular_color = Array(default_value="white", allow_none=True).tag(sync=True, **color_serialization) + shininess = traitlets.CFloat(1).tag(sync=True) + emissive_color = Array(default_value="black", allow_none=True).tag(sync=True, **color_serialization) + emissive_intensity = traitlets.CFloat(1).tag(sync=True) + roughness = traitlets.CFloat(0).tag(sync=True) + metalness = traitlets.CFloat(0).tag(sync=True) + cast_shadow = traitlets.CBool(default_value=False).tag(sync=True) + receive_shadow = traitlets.CBool(default_value=False).tag(sync=True) @widgets.register class Scatter(widgets.Widget): @@ -233,7 +244,8 @@ class Light(widgets.Widget): intensity = traitlets.CFloat(1).tag(sync=True) light_type = traitlets.Enum(values=['AMBIENT', 'DIRECTIONAL', 'SPOT', 'POINT', 'HEMISPHERE'], default_value='AMBIENT').tag(sync=True) - + shadow_map_type = traitlets.Enum(values=['BASIC', 'PCF', 'PCF_SOFT'], default_value='PCF_SOFT').tag(sync=True) + cast_shadow = traitlets.Bool(False).tag(sync=True) # TODO - should use array serialization instead ? #position = Array(default_value=(0, 1, 0), allow_none=True).tag(sync=True, **array_serialization) diff --git a/js/glsl/mesh-fragment-phong.glsl b/js/glsl/mesh-fragment-phong.glsl index 38bc9f43..9465376a 100644 --- a/js/glsl/mesh-fragment-phong.glsl +++ b/js/glsl/mesh-fragment-phong.glsl @@ -7,6 +7,7 @@ uniform vec3 specular; uniform float shininess; uniform float opacity; +uniform float emissiveIntensity; #include #include @@ -74,7 +75,7 @@ void main() vec4 diffuseColor = vec4( diffuse, opacity );//0.75 * finalColor2 + 0.25 * vec4( diffuse, opacity ); ReflectedLight reflectedLight = ReflectedLight( vec3( 0.0 ), vec3( 0.0 ), vec3( 0.0 ), vec3( 0.0 ) ); - vec3 totalEmissiveRadiance = emissive; + vec3 totalEmissiveRadiance = emissive * emissiveIntensity; #include #include diff --git a/js/glsl/mesh-fragment-physical.glsl b/js/glsl/mesh-fragment-physical.glsl index 0a05bd3d..44aa7652 100644 --- a/js/glsl/mesh-fragment-physical.glsl +++ b/js/glsl/mesh-fragment-physical.glsl @@ -6,6 +6,7 @@ uniform vec3 emissive; uniform float roughness; uniform float metalness; uniform float opacity; +uniform float emissiveIntensity; #ifndef STANDARD uniform float clearCoat; @@ -88,7 +89,7 @@ void main() vec4 diffuseColor = vec4( diffuse, opacity ); ReflectedLight reflectedLight = ReflectedLight( vec3( 0.0 ), vec3( 0.0 ), vec3( 0.0 ), vec3( 0.0 ) ); - vec3 totalEmissiveRadiance = emissive; + vec3 totalEmissiveRadiance = emissive * emissiveIntensity; #include #include diff --git a/js/src/light.ts b/js/src/light.ts index cc54a0ad..ef0f12a7 100644 --- a/js/src/light.ts +++ b/js/src/light.ts @@ -15,7 +15,9 @@ class LightView extends widgets.WidgetView { lights: any; //TODO remove current_light: any; LIGHT_TYPES: any; + SHADOW_MAP_TYPES: any; light_type: any; + shadow_map_type: any; light_color: any; light_color2: any; @@ -45,6 +47,13 @@ class LightView extends widgets.WidgetView { POINT: 'POINT', HEMISPHERE: 'HEMISPHERE' }; + + this.SHADOW_MAP_TYPES = { + BASIC: 'BASIC', + PCF: 'PCF', + PCF_SOFT: 'PCF_SOFT', + }; + this.renderer = this.options.parent; this.model.on("change:light_color change:intensity", @@ -83,6 +92,9 @@ class LightView extends widgets.WidgetView { this.light_color = this.model.get("light_color"); this.intensity = this.model.get("intensity"); this.light_type = this.model.get("light_type"); + this.cast_shadow = this.model.get("cast_shadow"); + this.renderer.renderer.shadowMap.enabled = this.cast_shadow; + //no shadow support if(this.light_type === this.LIGHT_TYPES.AMBIENT){ console.log("Create Ambient Light " + this.intensity); @@ -104,8 +116,6 @@ class LightView extends widgets.WidgetView { } // with shadow support else { - - this.cast_shadow = this.model.get("cast_shadow"); this.distance = this.model.get("distance"); this.decay = this.model.get("decay"); this.shadow_map_size = this.model.get("shadow_map_size"); @@ -114,6 +124,20 @@ class LightView extends widgets.WidgetView { this.shadow_camera_near = this.model.get("shadow_camera_near"); this.shadow_camera_far = this.model.get("shadow_camera_far"); + if(this.cast_shadow === true) { + this.shadow_map_type = this.model.get("shadow_map_type"); + + if(this.shadow_map_type === this.SHADOW_MAP_TYPES.BASIC) { + this.renderer.renderer.shadowMap.type = THREE.BasicShadowMap; + } + else if(this.shadow_map_type === this.SHADOW_MAP_TYPES.PCF) { + this.renderer.renderer.shadowMap.type = THREE.PCFShadowMap; + } + else if(this.shadow_map_type === this.SHADOW_MAP_TYPES.PCF_SOFT) { + this.renderer.renderer.shadowMap.type = THREE.PCFSoftShadowMap; + } + } + if(this.light_type === this.LIGHT_TYPES.POINT) { console.log("Create Point Light"); this.current_light = new THREE.PointLight(this.light_color, this.intensity); @@ -246,6 +270,7 @@ class LightModel extends widgets.WidgetModel { _model_module_version: semver_range, _view_module_version: semver_range, light_type: 'AMBIENT', + shadow_map_type: 'PCF_SOFT', light_color: "red", light_color2: "white", intensity: 1, diff --git a/js/src/mesh.ts b/js/src/mesh.ts index 5ea2a646..e2b0094c 100644 --- a/js/src/mesh.ts +++ b/js/src/mesh.ts @@ -27,11 +27,9 @@ class MeshView extends widgets.WidgetView { textures: any; texture_video: any; - lightCountTemp : any; render() { // console.log("created mesh view, parent is") // console.log(this.options.parent) - this.lightCountTemp = 0; this.renderer = this.options.parent; this.previous_values = {}; this.attributes_changed = {}; @@ -61,17 +59,17 @@ class MeshView extends widgets.WidgetView { }, THREE.UniformsLib[ "common" ], THREE.UniformsLib[ "lights" ], - { emissive: { value: new THREE.Color( 0x000000 ) }, + emissiveIntensity: { value: 1 }, specular: { value: new THREE.Color( 0x111111 ) }, - shininess: { value: 30 } + shininess: { value: 30 }, + roughness: { value: 0.5 }, + metalness: { value: 0.5 }, }, ] ); - //this.uniforms.emissive.set = new THREE.Color(0,255,0); - const get_material = (name) => { if (this.model.get(name)) { return this.model.get(name).obj.clone(); @@ -115,84 +113,10 @@ class MeshView extends widgets.WidgetView { //LIGHTING ///////////////////// - var globalIntensity = 0.5; - this.renderer.renderer.shadowMap.enabled = true; - this.renderer.renderer.shadowMap.type = THREE.PCFSoftShadowMap; - - if(this.lightCountTemp == 0) - { - this.lightCountTemp = 1; - - this.renderer.renderer.shadowMap.enabled = true; - this.renderer.renderer.shadowMap.type = THREE.PCFSoftShadowMap; - /* - var slTest = new THREE.SpotLight(0x000000, globalIntensity); - slTest.castShadow = true; - slTest.color = new THREE.Color("rgb(100, 0, 100)"); - slTest.position.set(20, 30, 20);//.normalize(); - slTest.angle = Math.PI/9; - slTest.penumbra = 0.25; - slTest.lookAt(new THREE.Vector3(0,-20,0)); - - - slTest.shadow = new THREE.SpotLightShadow(new THREE.PerspectiveCamera(100, 1, 0.5, 5000)); - //Set up shadow properties for the light - slTest.shadow.camera.position.set(slTest.position.x, slTest.position.y, slTest.position.z); - slTest.shadow.mapSize.width = 512; // default - slTest.shadow.mapSize.height = 512; // default - slTest.shadow.camera.near = 0.5; // default - slTest.shadow.camera.far = 500 // default - slTest.shadow.bias = -0.0005; // prevent shadow acne - - this.renderer.scene_scatter.add(slTest); - - */ - - /* - var dlTest = new THREE.DirectionalLight("rgb(0, 255, 255)", globalIntensity); - dlTest.position.set(100, 100, 100); - dlTest.castShadow = true; - - dlTest.shadow = new THREE.DirectionalLightShadow(new THREE.OrthographicCamera(-5,5,-5,5,0.5,500)); - - var d = 100; - dlTest.shadow.camera.left = - d; - dlTest.shadow.camera.right = d; - dlTest.shadow.camera.top = d; - dlTest.shadow.camera.bottom = - d; - - dlTest.shadow.mapSize.width = 512;//this.shadow_map_width; - dlTest.shadow.mapSize.height = 512;//this.shadow_map_height; - dlTest.shadow.bias = -0.0005; // prevent shadow acne - - dlTest.shadow.camera.position.set(100,100,100);//(this.position.x, this.position.y, this.position.z); - dlTest.shadow.camera.near = 0.5;//this.shadow_camera_near; - dlTest.shadow.camera.far = 10000;// this.shadow_camera_far; - this.renderer.scene_scatter.add(dlTest); - - */ - /* - var dlTest2 = new THREE.DirectionalLight(0x000000, globalIntensity); - dlTest2.castShadow = true; - dlTest2.color = new THREE.Color("rgb(0, 0, 155)"); - dlTest2.position.set(0, 0, 100);//.normalize(); - dlTest2.lookAt(mesh.position); - this.renderer.scene_scatter.add(dlTest2); - */ - - /* - var amTest = new THREE.AmbientLight(0x000000, globalIntensity); - amTest.color = new THREE.Color(0,0,1); - amTest.intensity = 0.1; - this.renderer.scene_scatter.add(amTest); - */ - - - - } + //this.renderer.renderer.shadowMap.enabled = true; + //this.renderer.renderer.shadowMap.type = THREE.PCFSoftShadowMap; +///////////////////// - - this.model.on("change:color change:sequence_index change:x change:y change:z change:v change:u change:triangles change:lines", this.on_change, this); this.model.on("change:geo change:connected", this.update_, this); @@ -397,13 +321,17 @@ class MeshView extends widgets.WidgetView { }); this.material.uniforms.diffuse.value = new THREE.Color(1, 1, 1);//BUG? keep hardcoded + this.material.uniforms.opacity.value = 1.0; - this.material.uniforms.specular.value = new THREE.Color(0.5,0.5,0.5); + this.material.uniforms.specular.value = new THREE.Color(1.0,1.0,1.0); this.material.uniforms.shininess.value = 100; this.material.uniforms.emissive.value = new THREE.Color(0,0,0); - //this.material.uniforms.emissiveIntensity.value = 0.1; //TODO missing + this.material.uniforms.emissiveIntensity.value = 1; + + this.material.uniforms.roughness.value = 0.0; + this.material.uniforms.metalness.value = 0.0; const texture = this.model.get("texture"); if (texture && this.textures) { @@ -546,9 +474,6 @@ class MeshView extends widgets.WidgetView { this.surface_mesh.material_rgb = this.material_rgb; this.surface_mesh.material_normal = this.material; - this.surface_mesh.castShadow = true; - this.surface_mesh.receiveShadow = true; - this.meshes.push(this.surface_mesh); } diff --git a/notebooks/lighting_demo.ipynb b/notebooks/lighting_demo.ipynb index 60f82c21..9b63d551 100644 --- a/notebooks/lighting_demo.ipynb +++ b/notebooks/lighting_demo.ipynb @@ -26,7 +26,7 @@ { "data": { "application/vnd.jupyter.widget-view+json": { - "model_id": "3ecdbec1b9404eaaab3259df498acabb", + "model_id": "f6dab6b0209e4b758b81e751ceb65ba1", "version_major": 2, "version_minor": 0 }, @@ -41,17 +41,6 @@ "name": "stdout", "output_type": "stream", "text": [ - "ADD DIRECTIONAL LIGHT (from pylab) \n", - "light_color: cyan\n", - "intensity: 1.0\n", - "position: 100 100 100\n", - "target: 0 0 0\n", - "cast_shadow: True\n", - "shadow_map_size: 512\n", - "shadow_bias: -0.005\n", - "shadow_radius: 1\n", - "shadow_camera_near: 0.5\n", - "shadow_camera_far: 500\n", "ADD DIRECTIONAL LIGHT (from pylab) \n", "light_color: orange\n", "intensity: 1.0\n", @@ -62,7 +51,8 @@ "shadow_bias: -0.005\n", "shadow_radius: 1\n", "shadow_camera_near: 0.5\n", - "shadow_camera_far: 500\n" + "shadow_camera_far: 500\n", + "shadow_map_type: PCF\n" ] } ], @@ -153,8 +143,8 @@ "\n", " #p3.ambient_light(light_color=\"rgb(255,0,0)\", intensity=1.5)\n", "\n", - " p3.directional_light(light_color='cyan', intensity=1.0, position=[100, 100, 100], target=[0,0,0], cast_shadow=True, shadow_bias= -0.005,shadow_camera_orthographic_size=100)\n", - " p3.directional_light(light_color='orange', intensity=1.0, position=[-100, 100, -100], target=[0,0,0], cast_shadow=True, shadow_bias= -0.005,shadow_camera_orthographic_size=100)\n", + " #p3.directional_light(light_color='blue', intensity=1.0, position=[100, 100, 100], target=[0,0,0], cast_shadow=True, shadow_bias= -0.005,shadow_camera_orthographic_size=100)\n", + " p3.directional_light(light_color='orange', intensity=1.0, position=[-100, 100, -100], target=[0,0,0], cast_shadow=True, shadow_bias= -0.005,shadow_camera_orthographic_size=100,shadow_map_type='PCF_SOFT')\n", " #p3.hemisphere_light(light_color='red', color2='yellow', intensity=0.5, position=[1, 0, 0])\n", " #p3.point_light(light_color='orange', intensity=1.0, position=[20, 30, 20], distance=65, decay=0.5, cast_shadow=True,shadow_radius=3,shadow_bias= -0.005)\n", " #p3.spot_light(light_color='yellow', intensity=1, position=[20, 30, 20], angle=math.pi/9, target = [-20,-20,-20], cast_shadow=True,shadow_radius=2)\n", From b057d00bfa2bd154bd439b047c9c0fe32112bdb3 Mon Sep 17 00:00:00 2001 From: rinftech-github Date: Sun, 29 Mar 2020 21:15:49 +0300 Subject: [PATCH 019/130] Propagate material params. Add plot_surface example --- ipyvolume/pylab.py | 110 ++++++++++++- ipyvolume/widgets.py | 24 +-- js/glsl/mesh-vertex.glsl | 6 +- js/src/light.ts | 2 - js/src/mesh.ts | 108 ++++++++++--- notebooks/lighting_demo.ipynb | 281 +++++++++++++++++++++++----------- 6 files changed, 393 insertions(+), 138 deletions(-) diff --git a/ipyvolume/pylab.py b/ipyvolume/pylab.py index 4509f1d5..db3ea8c3 100644 --- a/ipyvolume/pylab.py +++ b/ipyvolume/pylab.py @@ -314,7 +314,27 @@ def squarelim(): @_docsubst -def plot_trisurf(x, y, z, triangles=None, lines=None, color=default_color, u=None, v=None, texture=None): +def plot_trisurf( + x, + y, + z, + triangles=None, + lines=None, + color=default_color, + u=None, + v=None, + texture=None, + lighting_model='DEFAULT', + #diffuse_color='white', + opacity=1, + specular_color='white', + shininess=1, + emissive_color='black', + emissive_intensity=1, + roughness=0, + metalness=0, + cast_shadow=False, + receive_shadow=False): """Draw a polygon/triangle mesh defined by a coordinate and triangle indices. The following example plots a rectangle in the z==2 plane, consisting of 2 triangles: @@ -349,15 +369,53 @@ def plot_trisurf(x, y, z, triangles=None, lines=None, color=default_color, u=Non triangles = np.array(triangles).astype(dtype=np.uint32) if lines is not None: lines = np.array(lines).astype(dtype=np.uint32) - mesh = ipv.Mesh(x=x, y=y, z=z, triangles=triangles, lines=lines, color=color, u=u, v=v, texture=texture) + mesh = ipv.Mesh( + x=x, + y=y, + z=z, + triangles=triangles, + lines=lines, + color=color, + u=u, v=v, + texture=texture, + lighting_model=lighting_model, + #diffuse_color=diffuse_color, + opacity=opacity, + specular_color=specular_color, + shininess=shininess, + emissive_color=emissive_color, + emissive_intensity=emissive_intensity, + roughness=roughness, + metalness=metalness, + cast_shadow=cast_shadow, + receive_shadow=receive_shadow + ) _grow_limits(np.array(x).reshape(-1), np.array(y).reshape(-1), np.array(z).reshape(-1)) fig.meshes = fig.meshes + [mesh] - print(fig.meshes) + return mesh @_docsubst -def plot_surface(x, y, z, color=default_color, wrapx=False, wrapy=False): +def plot_surface( + x, + y, + z, + color=default_color, + wrapx=False, + wrapy=False, + lighting_model='DEFAULT', + #diffuse_color='white', + opacity=1, + specular_color='white', + shininess=1, + emissive_color='black', + emissive_intensity=1, + roughness=0, + metalness=0, + cast_shadow=False, + receive_shadow=False + ): """Draws a 2d surface in 3d, defined by the 2d ordered arrays x,y,z. :param x: {x2d} @@ -368,7 +426,25 @@ def plot_surface(x, y, z, color=default_color, wrapx=False, wrapy=False): :param bool wrapy: simular for the y coordinate :return: :any:`Mesh` """ - return plot_mesh(x, y, z, color=color, wrapx=wrapx, wrapy=wrapy, wireframe=False) + return plot_mesh( + x, + y, + z, + color=color, + wrapx=wrapx, + wrapy=wrapy, + wireframe=False, + lighting_model=lighting_model, + #diffuse_color=diffuse_color, + opacity=opacity, + specular_color=specular_color, + shininess=shininess, + emissive_color=emissive_color, + emissive_intensity=emissive_intensity, + roughness=roughness, + metalness=metalness, + cast_shadow=cast_shadow, + receive_shadow=receive_shadow) @_docsubst @@ -389,7 +465,18 @@ def plot_wireframe(x, y, z, color=default_color, wrapx=False, wrapy=False): def plot_mesh( - x, y, z, color=default_color, wireframe=True, surface=True, wrapx=False, wrapy=False, u=None, v=None, texture=None + x, y, z, color=default_color, wireframe=True, surface=True, wrapx=False, wrapy=False, u=None, v=None, texture=None, + lighting_model='DEFAULT', + #diffuse_color='white', + opacity=1, + specular_color='white', + shininess=1, + emissive_color='black', + emissive_intensity=1, + roughness=0, + metalness=0, + cast_shadow=False, + receive_shadow=False ): """Draws a 2d wireframe+surface in 3d: generalization of :any:`plot_wireframe` and :any:`plot_surface`. @@ -477,6 +564,17 @@ def reshape_color(ar): u=u, v=v, texture=texture, + lighting_model=lighting_model, + #diffuse_color=diffuse_color, + opacity=opacity, + specular_color=specular_color, + shininess=shininess, + emissive_color=emissive_color, + emissive_intensity=emissive_intensity, + roughness=roughness, + metalness=metalness, + cast_shadow=cast_shadow, + receive_shadow=receive_shadow ) fig.meshes = fig.meshes + [mesh] return mesh diff --git a/ipyvolume/widgets.py b/ipyvolume/widgets.py index 7ec05ac1..da7ba1d3 100644 --- a/ipyvolume/widgets.py +++ b/ipyvolume/widgets.py @@ -64,6 +64,18 @@ class Mesh(widgets.Widget): color = Array(default_value="red", allow_none=True).tag(sync=True, **color_serialization) visible = traitlets.CBool(default_value=True).tag(sync=True) + lighting_model = traitlets.Enum(values=['DEFAULT', 'LAMBERT', 'PHONG', 'PHYSICAL'], default_value='DEFAULT').tag(sync=True) + #diffuse_color = Array(default_value="white", allow_none=True).tag(sync=True, **color_serialization) + opacity = traitlets.CFloat(1).tag(sync=True) + specular_color = Array(default_value="white", allow_none=True).tag(sync=True, **color_serialization) + shininess = traitlets.CFloat(1).tag(sync=True) + emissive_color = Array(default_value="black", allow_none=True).tag(sync=True, **color_serialization) + emissive_intensity = traitlets.CFloat(1).tag(sync=True) + roughness = traitlets.CFloat(0).tag(sync=True) + metalness = traitlets.CFloat(0).tag(sync=True) + cast_shadow = traitlets.CBool(default_value=False).tag(sync=True) + receive_shadow = traitlets.CBool(default_value=False).tag(sync=True) + material = traitlets.Instance( pythreejs.ShaderMaterial, help='A :any:`pythreejs.ShaderMaterial` that is used for the mesh' ).tag(sync=True, **widgets.widget_serialization) @@ -80,18 +92,6 @@ def _default_material(self): def _default_line_material(self): return pythreejs.ShaderMaterial() - lighting_model = traitlets.Enum(values=['DEFAULT', 'LAMBERT', 'PHONG', 'PHYSICAL'], default_value='DEFAULT').tag(sync=True) - diffuse_color = Array(default_value="white", allow_none=True).tag(sync=True, **color_serialization) - opacity = traitlets.CFloat(1).tag(sync=True) - specular_color = Array(default_value="white", allow_none=True).tag(sync=True, **color_serialization) - shininess = traitlets.CFloat(1).tag(sync=True) - emissive_color = Array(default_value="black", allow_none=True).tag(sync=True, **color_serialization) - emissive_intensity = traitlets.CFloat(1).tag(sync=True) - roughness = traitlets.CFloat(0).tag(sync=True) - metalness = traitlets.CFloat(0).tag(sync=True) - cast_shadow = traitlets.CBool(default_value=False).tag(sync=True) - receive_shadow = traitlets.CBool(default_value=False).tag(sync=True) - @widgets.register class Scatter(widgets.Widget): _view_name = Unicode('ScatterView').tag(sync=True) diff --git a/js/glsl/mesh-vertex.glsl b/js/glsl/mesh-vertex.glsl index e2e674c5..f7f50e26 100644 --- a/js/glsl/mesh-vertex.glsl +++ b/js/glsl/mesh-vertex.glsl @@ -23,11 +23,9 @@ attribute vec3 position_previous; varying vec2 vertex_uv; #endif -attribute vec4 color; +attribute vec4 color_current; attribute vec4 color_previous; - - void main(void) { vec3 origin = vec3(xlim.x, ylim.x, zlim.x); vec3 size_viewport = vec3(xlim.y, ylim.y, zlim.y) - origin; @@ -46,6 +44,6 @@ void main(void) { #ifdef USE_RGB vertex_color = vec4(pos + vec3(0.5, 0.5, 0.5), 1.0); #else - vertex_color = mix(color_previous, color, animation_time_color); + vertex_color = mix(color_previous, color_current, animation_time_color); #endif } diff --git a/js/src/light.ts b/js/src/light.ts index ef0f12a7..ec082cba 100644 --- a/js/src/light.ts +++ b/js/src/light.ts @@ -58,8 +58,6 @@ class LightView extends widgets.WidgetView { this.model.on("change:light_color change:intensity", this.on_change, this); - console.log(this.renderer.scene_scatter.children) - //console.log(this.LIGHT_TYPES); this.create_light(); this.add_to_scene(); } diff --git a/js/src/mesh.ts b/js/src/mesh.ts index e2b0094c..2ba6d77b 100644 --- a/js/src/mesh.ts +++ b/js/src/mesh.ts @@ -27,9 +27,31 @@ class MeshView extends widgets.WidgetView { textures: any; texture_video: any; + LIGHTING_MODELS: any; + + lighting_model: any; + diffuse_color : any; + opacity : any; + specular_color : any; + shininess : any; + emissive_color : any; + emissive_intensity : any; + roughness : any; + metalness : any; + cast_shadow : any; + receive_shadow : any; + render() { // console.log("created mesh view, parent is") // console.log(this.options.parent) + + this.LIGHTING_MODELS = { + DEFAULT: 'DEFAULT', + LAMBERT: 'LAMBERT', + PHONG: 'PHONG', + PHYSICAL : 'PHYSICAL' + }; + this.renderer = this.options.parent; this.previous_values = {}; this.attributes_changed = {}; @@ -110,12 +132,6 @@ class MeshView extends widgets.WidgetView { this.create_mesh(); this.add_to_scene(); - - //LIGHTING -///////////////////// - //this.renderer.renderer.shadowMap.enabled = true; - //this.renderer.renderer.shadowMap.type = THREE.PCFSoftShadowMap; -///////////////////// this.model.on("change:color change:sequence_index change:x change:y change:z change:v change:u change:triangles change:lines", this.on_change, this); @@ -165,11 +181,13 @@ class MeshView extends widgets.WidgetView { } add_to_scene() { + this.cast_shadow = this.model.get("cast_shadow"); + this.receive_shadow = this.model.get("receive_shadow"); + this.meshes.forEach((mesh) => { - mesh.castShadow = true; - mesh.receiveShadow = true; + mesh.castShadow = this.cast_shadow; + mesh.receiveShadow = this.receive_shadow; this.renderer.scene_scatter.add(mesh); - }); } @@ -309,29 +327,50 @@ class MeshView extends widgets.WidgetView { this.material_rgb.visible = this.material.visible && this.model.get("visible"); this.line_material.visible = this.line_material.visible && this.model.get("visible"); this.line_material_rgb.visible = this.line_material.visible && this.model.get("visible"); + + this.lighting_model = this.model.get("lighting_model"); this.materials.forEach((material) => { material.uniforms = this.uniforms; - material.vertexShader = require("raw-loader!../glsl/mesh-vertex-physical.glsl"); - material.fragmentShader = require("raw-loader!../glsl/mesh-fragment-physical.glsl"); + if(this.lighting_model === this.LIGHTING_MODELS.DEFAULT) { + material.vertexShader = require("raw-loader!../glsl/mesh-vertex.glsl"); + material.fragmentShader = require("raw-loader!../glsl/mesh-fragment.glsl"); + } + else if(this.lighting_model === this.LIGHTING_MODELS.LAMBERT) { + material.vertexShader = require("raw-loader!../glsl/mesh-vertex-lambert.glsl"); + material.fragmentShader = require("raw-loader!../glsl/mesh-fragment-lambert.glsl"); + } + else if(this.lighting_model === this.LIGHTING_MODELS.PHONG) { + material.vertexShader = require("raw-loader!../glsl/mesh-vertex-phong.glsl"); + material.fragmentShader = require("raw-loader!../glsl/mesh-fragment-phong.glsl"); + } + else if(this.lighting_model === this.LIGHTING_MODELS.PHYSICAL) { + material.vertexShader = require("raw-loader!../glsl/mesh-vertex-physical.glsl"); + material.fragmentShader = require("raw-loader!../glsl/mesh-fragment-physical.glsl"); + } material.depthWrite = true; material.transparant = true; material.depthTest = true; - //VERY IMPORTANT + // very important material.lights = true; }); - this.material.uniforms.diffuse.value = new THREE.Color(1, 1, 1);//BUG? keep hardcoded - - this.material.uniforms.opacity.value = 1.0; - - this.material.uniforms.specular.value = new THREE.Color(1.0,1.0,1.0); - this.material.uniforms.shininess.value = 100; - - this.material.uniforms.emissive.value = new THREE.Color(0,0,0); - this.material.uniforms.emissiveIntensity.value = 1; - - this.material.uniforms.roughness.value = 0.0; - this.material.uniforms.metalness.value = 0.0; + this.diffuse_color = this.model.get("diffuse_color"); + this.opacity = this.model.get("opacity"); + this.specular_color = this.model.get("specular_color"); + this.shininess = this.model.get("shininess"); + this.emissive_color = this.model.get("emissive_color"); + this.emissive_intensity = this.model.get("emissive_intensity"); + this.roughness = this.model.get("roughness"); + this.metalness = this.model.get("metalness"); + + this.material.uniforms.diffuse.value = new THREE.Color(1, 1, 1);//this.diffuse_color//BUG? keep hardcoded + this.material.uniforms.opacity.value = this.opacity; + this.material.uniforms.specular.value = new THREE.Color(this.specular_color); + this.material.uniforms.shininess.value = this.shininess; + this.material.uniforms.emissive.value = new THREE.Color(this.emissive_color); + this.material.uniforms.emissiveIntensity.value = this.emissive_intensity; + this.material.uniforms.roughness.value = this.roughness; + this.material.uniforms.metalness.value = this.metalness; const texture = this.model.get("texture"); if (texture && this.textures) { @@ -536,6 +575,14 @@ class MeshModel extends widgets.WidgetModel { texture: serialize.texture, material: { deserialize: widgets.unpack_models }, line_material: { deserialize: widgets.unpack_models }, + diffuse_color : serialize.color_or_json, + opacity : serialize.array_or_json, + specular_color : serialize.color_or_json, + shininess : serialize.array_or_json, + emissive_color : serialize.color_or_json, + emissive_intensity : serialize.array_or_json, + roughness : serialize.array_or_json, + metalness : serialize.array_or_json, }; defaults() { return { @@ -545,13 +592,24 @@ class MeshModel extends widgets.WidgetModel { _model_module : "ipyvolume", _view_module : "ipyvolume", _model_module_version: semver_range, - _view_module_version: semver_range, + _view_module_version: semver_range, color: "red", sequence_index: 0, connected: false, visible: true, visible_lines: true, visible_faces: true, + lighting_model: "DEFAULT", + diffuse_color : "white", + opacity : 1, + specular_color : "white", + shininess : 1, + emissive_color : "black", + emissive_intensity : 1, + roughness : 0, + metalness : 0, + cast_shadow : false, + receive_shadow : false, }; } } diff --git a/notebooks/lighting_demo.ipynb b/notebooks/lighting_demo.ipynb index 9b63d551..0c23d2ef 100644 --- a/notebooks/lighting_demo.ipynb +++ b/notebooks/lighting_demo.ipynb @@ -2,60 +2,9 @@ "cells": [ { "cell_type": "code", - "execution_count": 3, + "execution_count": 115, "metadata": {}, - "outputs": [ - { - "name": "stdout", - "output_type": "stream", - "text": [ - "[Mesh(line_material=ShaderMaterial(), material=ShaderMaterial(side='DoubleSide'), texture=None, triangles=array([[ 0, 50, 51],\n", - " [ 0, 51, 1],\n", - " [ 1, 51, 52],\n", - " ...,\n", - " [2447, 2498, 2448],\n", - " [2448, 2498, 2499],\n", - " [2448, 2499, 2449]], dtype=uint32), u=array([0. , 0.02040816, 0.04081633, ..., 0.95918367, 0.97959184,\n", - " 1. ]), v=array([0., 0., 0., ..., 1., 1., 1.]), x=array([8. , 8.71156903, 9.27378258, ..., 2.26620625, 3.17335636,\n", - " 4. ]), y=array([ 0.00000000e+00, 2.30388865e+00, 4.58237413e+00, ...,\n", - " -4.05847334e+00, -2.04603459e+00, -3.91886976e-15]), z=array([ 0.00000000e+00, 0.00000000e+00, 0.00000000e+00, ...,\n", - " -5.05879616e-16, -4.93880453e-16, -4.89858720e-16])), Mesh(color=array('orange', dtype=' Date: Sun, 29 Mar 2020 22:30:50 +0300 Subject: [PATCH 020/130] Prepare lighting material uniforms for scatter rendering --- ipyvolume/pylab.py | 80 ++++++++++++++++++++++++++++++++- ipyvolume/widgets.py | 12 +++++ js/src/mesh.ts | 8 ++-- js/src/scatter.ts | 105 +++++++++++++++++++++++++++++++++++++++++-- 4 files changed, 195 insertions(+), 10 deletions(-) diff --git a/ipyvolume/pylab.py b/ipyvolume/pylab.py index db3ea8c3..c8266d2d 100644 --- a/ipyvolume/pylab.py +++ b/ipyvolume/pylab.py @@ -581,7 +581,23 @@ def reshape_color(ar): @_docsubst -def plot(x, y, z, color=default_color, **kwargs): +def plot( + x, + y, + z, + color=default_color, + lighting_model='DEFAULT', + #diffuse_color='white', + opacity=1, + specular_color='white', + shininess=1, + emissive_color='black', + emissive_intensity=1, + roughness=0, + metalness=0, + cast_shadow=False, + receive_shadow=False, + **kwargs): """Plot a line in 3d. :param x: {x} @@ -597,7 +613,23 @@ def plot(x, y, z, color=default_color, **kwargs): visible_lines=True, color_selected=None, size_selected=1, size=1, connected=True, visible_markers=False ) kwargs = dict(defaults, **kwargs) - s = ipv.Scatter(x=x, y=y, z=z, color=color, **kwargs) + s = ipv.Scatter( + x=x, + y=y, + z=z, + color=color, + lighting_model=lighting_model, + #diffuse_color=diffuse_color, + opacity=opacity, + specular_color=specular_color, + shininess=shininess, + emissive_color=emissive_color, + emissive_intensity=emissive_intensity, + roughness=roughness, + metalness=metalness, + cast_shadow=cast_shadow, + receive_shadow=receive_shadow, + **kwargs) s.material.visible = False fig.scatters = fig.scatters + [s] return s @@ -615,6 +647,17 @@ def scatter( marker="diamond", selection=None, grow_limits=True, + lighting_model='DEFAULT', + #diffuse_color='white', + opacity=1, + specular_color='white', + shininess=1, + emissive_color='black', + emissive_intensity=1, + roughness=0, + metalness=0, + cast_shadow=False, + receive_shadow=False, **kwargs ): """Plot many markers/symbols in 3d. @@ -645,6 +688,17 @@ def scatter( size_selected=size_selected, geo=marker, selection=selection, + lighting_model=lighting_model, + #diffuse_color=diffuse_color, + opacity=opacity, + specular_color=specular_color, + shininess=shininess, + emissive_color=emissive_color, + emissive_intensity=emissive_intensity, + roughness=roughness, + metalness=metalness, + cast_shadow=cast_shadow, + receive_shadow=receive_shadow, **kwargs ) fig.scatters = fig.scatters + [s] @@ -664,6 +718,17 @@ def quiver( color=default_color, color_selected=default_color_selected, marker="arrow", + lighting_model='DEFAULT', + #diffuse_color='white', + opacity=1, + specular_color='white', + shininess=1, + emissive_color='black', + emissive_intensity=1, + roughness=0, + metalness=0, + cast_shadow=False, + receive_shadow=False, **kwargs ): """Create a quiver plot, which is like a scatter plot but with arrows pointing in the direction given by u, v and w. @@ -698,6 +763,17 @@ def quiver( color_selected=color_selected, size_selected=size_selected, geo=marker, + lighting_model=lighting_model, + #diffuse_color=diffuse_color, + opacity=opacity, + specular_color=specular_color, + shininess=shininess, + emissive_color=emissive_color, + emissive_intensity=emissive_intensity, + roughness=roughness, + metalness=metalness, + cast_shadow=cast_shadow, + receive_shadow=receive_shadow, **kwargs ) fig.scatters = fig.scatters + [s] diff --git a/ipyvolume/widgets.py b/ipyvolume/widgets.py index da7ba1d3..a8ebe468 100644 --- a/ipyvolume/widgets.py +++ b/ipyvolume/widgets.py @@ -131,6 +131,18 @@ class Scatter(widgets.Widget): connected = traitlets.CBool(default_value=False).tag(sync=True) visible = traitlets.CBool(default_value=True).tag(sync=True) + lighting_model = traitlets.Enum(values=['DEFAULT', 'LAMBERT', 'PHONG', 'PHYSICAL'], default_value='DEFAULT').tag(sync=True) + #diffuse_color = Array(default_value="white", allow_none=True).tag(sync=True, **color_serialization) + opacity = traitlets.CFloat(1).tag(sync=True) + specular_color = Array(default_value="white", allow_none=True).tag(sync=True, **color_serialization) + shininess = traitlets.CFloat(1).tag(sync=True) + emissive_color = Array(default_value="black", allow_none=True).tag(sync=True, **color_serialization) + emissive_intensity = traitlets.CFloat(1).tag(sync=True) + roughness = traitlets.CFloat(0).tag(sync=True) + metalness = traitlets.CFloat(0).tag(sync=True) + cast_shadow = traitlets.CBool(default_value=False).tag(sync=True) + receive_shadow = traitlets.CBool(default_value=False).tag(sync=True) + texture = traitlets.Union( [ traitlets.Instance(ipywebrtc.MediaStream), diff --git a/js/src/mesh.ts b/js/src/mesh.ts index 2ba6d77b..5e3c1592 100644 --- a/js/src/mesh.ts +++ b/js/src/mesh.ts @@ -84,10 +84,10 @@ class MeshView extends widgets.WidgetView { { emissive: { value: new THREE.Color( 0x000000 ) }, emissiveIntensity: { value: 1 }, - specular: { value: new THREE.Color( 0x111111 ) }, - shininess: { value: 30 }, - roughness: { value: 0.5 }, - metalness: { value: 0.5 }, + specular: { value: new THREE.Color( 0xffffff ) }, + shininess: { value: 0 }, + roughness: { value: 0.0 }, + metalness: { value: 0.0 }, }, ] ); diff --git a/js/src/scatter.ts b/js/src/scatter.ts index c7af4edb..727f544f 100644 --- a/js/src/scatter.ts +++ b/js/src/scatter.ts @@ -26,7 +26,29 @@ class ScatterView extends widgets.WidgetView { texture_video: HTMLVideoElement; line_segments: any; mesh: any; + + LIGHTING_MODELS: any; + + lighting_model: any; + diffuse_color : any; + opacity : any; + specular_color : any; + shininess : any; + emissive_color : any; + emissive_intensity : any; + roughness : any; + metalness : any; + cast_shadow : any; + receive_shadow : any; + render() { + this.LIGHTING_MODELS = { + DEFAULT: 'DEFAULT', + LAMBERT: 'LAMBERT', + PHONG: 'PHONG', + PHYSICAL : 'PHYSICAL' + }; + this.renderer = this.options.parent; this.previous_values = {}; this.attributes_changed = {}; @@ -85,7 +107,8 @@ class ScatterView extends widgets.WidgetView { triangle_2d: geo_triangle_2d, }; - this.uniforms = { + this.uniforms = THREE.UniformsUtils.merge( [ + { xlim : { type: "2f", value: [0., 1.] }, ylim : { type: "2f", value: [0., 1.] }, zlim : { type: "2f", value: [0., 1.] }, @@ -99,7 +122,19 @@ class ScatterView extends widgets.WidgetView { animation_time_color : { type: "f", value: 1. }, texture: { type: "t", value: null }, texture_previous: { type: "t", value: null }, - }; + }, + THREE.UniformsLib[ "common" ], + THREE.UniformsLib[ "lights" ], + { + emissive: { value: new THREE.Color( 0x000000 ) }, + emissiveIntensity: { value: 1 }, + specular: { value: new THREE.Color( 0xffffff ) }, + shininess: { value: 0 }, + roughness: { value: 0.0 }, + metalness: { value: 0.0 }, + }, + + ] ); const get_material = (name) => { if (this.model.get(name)) { return this.model.get(name).obj.clone(); @@ -173,6 +208,11 @@ class ScatterView extends widgets.WidgetView { } } add_to_scene() { + this.cast_shadow = this.model.get("cast_shadow"); + this.receive_shadow = this.model.get("receive_shadow"); + this.mesh.castShadow = this.cast_shadow; + this.mesh.receiveShadow = this.receive_shadow; + this.renderer.scene_scatter.add(this.mesh); if (this.line_segments) { this.renderer.scene_scatter.add(this.line_segments); @@ -285,15 +325,53 @@ class ScatterView extends widgets.WidgetView { this.material_rgb.visible = this.material.visible && this.model.get("visible"); this.line_material.visible = this.line_material.visible && this.model.get("visible"); this.line_material_rgb.visible = this.line_material.visible && this.model.get("visible"); + + this.lighting_model = this.model.get("lighting_model"); this.materials.forEach((material) => { - material.vertexShader = require("raw-loader!../glsl/scatter-vertex.glsl"); - material.fragmentShader = require("raw-loader!../glsl/scatter-fragment.glsl"); + + if(this.lighting_model === this.LIGHTING_MODELS.DEFAULT) { + material.vertexShader = require("raw-loader!../glsl/scatter-vertex.glsl"); + material.fragmentShader = require("raw-loader!../glsl/scatter-fragment.glsl"); + } + else if(this.lighting_model === this.LIGHTING_MODELS.LAMBERT) { + material.vertexShader = require("raw-loader!../glsl/scatter-vertex-lambert.glsl"); + material.fragmentShader = require("raw-loader!../glsl/scatter-fragment-lambert.glsl"); + } + else if(this.lighting_model === this.LIGHTING_MODELS.PHONG) { + material.vertexShader = require("raw-loader!../glsl/scatter-vertex-phong.glsl"); + material.fragmentShader = require("raw-loader!../glsl/scatter-fragment-phong.glsl"); + } + else if(this.lighting_model === this.LIGHTING_MODELS.PHYSICAL) { + material.vertexShader = require("raw-loader!../glsl/scatter-vertex-physical.glsl"); + material.fragmentShader = require("raw-loader!../glsl/scatter-fragment-physical.glsl"); + } + //material.vertexShader = require("raw-loader!../glsl/scatter-vertex.glsl"); + //material.fragmentShader = require("raw-loader!../glsl/scatter-fragment.glsl"); material.uniforms = {...material.uniforms, ...this.uniforms}; material.depthWrite = true; material.transparant = true; material.depthTest = true; material.needsUpdate = true; }); + + this.diffuse_color = this.model.get("diffuse_color"); + this.opacity = this.model.get("opacity"); + this.specular_color = this.model.get("specular_color"); + this.shininess = this.model.get("shininess"); + this.emissive_color = this.model.get("emissive_color"); + this.emissive_intensity = this.model.get("emissive_intensity"); + this.roughness = this.model.get("roughness"); + this.metalness = this.model.get("metalness"); + + this.material.uniforms.diffuse.value = new THREE.Color(1, 1, 1);//this.diffuse_color//BUG? keep hardcoded + this.material.uniforms.opacity.value = this.opacity; + this.material.uniforms.specular.value = new THREE.Color(this.specular_color); + this.material.uniforms.shininess.value = this.shininess; + this.material.uniforms.emissive.value = new THREE.Color(this.emissive_color); + this.material.uniforms.emissiveIntensity.value = this.emissive_intensity; + this.material.uniforms.roughness.value = this.roughness; + this.material.uniforms.metalness.value = this.metalness; + const geo = this.model.get("geo"); const sprite = geo.endsWith("2d"); if (sprite) { @@ -444,6 +522,14 @@ class ScatterModel extends widgets.WidgetModel { texture: serialize.texture, material: { deserialize: widgets.unpack_models }, line_material: { deserialize: widgets.unpack_models }, + diffuse_color : serialize.color_or_json, + opacity : serialize.array_or_json, + specular_color : serialize.color_or_json, + shininess : serialize.array_or_json, + emissive_color : serialize.color_or_json, + emissive_intensity : serialize.array_or_json, + roughness : serialize.array_or_json, + metalness : serialize.array_or_json, }; defaults() { @@ -463,6 +549,17 @@ class ScatterModel extends widgets.WidgetModel { connected: false, visible: true, selected: null, + lighting_model: "DEFAULT", + diffuse_color : "white", + opacity : 1, + specular_color : "white", + shininess : 1, + emissive_color : "black", + emissive_intensity : 1, + roughness : 0, + metalness : 0, + cast_shadow : false, + receive_shadow : false, }; } } From 2fc6c644e89bd596c56b60254d910905cc24b9c2 Mon Sep 17 00:00:00 2001 From: rinftech-github Date: Mon, 30 Mar 2020 00:01:46 +0300 Subject: [PATCH 021/130] Add Scatter physical shaders --- js/glsl/mesh-vertex-physical.glsl | 2 +- js/glsl/scatter-fragment-physical.glsl | 129 +++++++++++++++++++++ js/glsl/scatter-vertex-physical.glsl | 151 +++++++++++++++++++++++++ js/glsl/scatter-vertex.glsl | 4 +- js/src/scatter.ts | 13 ++- notebooks/lighting_demo.ipynb | 36 +++--- 6 files changed, 310 insertions(+), 25 deletions(-) create mode 100644 js/glsl/scatter-fragment-physical.glsl create mode 100644 js/glsl/scatter-vertex-physical.glsl diff --git a/js/glsl/mesh-vertex-physical.glsl b/js/glsl/mesh-vertex-physical.glsl index 8c6cd6d4..ceeb9787 100644 --- a/js/glsl/mesh-vertex-physical.glsl +++ b/js/glsl/mesh-vertex-physical.glsl @@ -29,7 +29,6 @@ attribute vec3 position_previous; attribute vec4 color_current; attribute vec4 color_previous; -//LAMBERT //////////////////////////////////////////////////////////////////////////////// varying vec3 vViewPosition; @@ -83,6 +82,7 @@ void main() #include #include +//////////////////////////////////////////////////////////////////////////////// vec3 origin = vec3(xlim.x, ylim.x, zlim.x); vec3 size_viewport = vec3(xlim.y, ylim.y, zlim.y) - origin; diff --git a/js/glsl/scatter-fragment-physical.glsl b/js/glsl/scatter-fragment-physical.glsl new file mode 100644 index 00000000..6518f67b --- /dev/null +++ b/js/glsl/scatter-fragment-physical.glsl @@ -0,0 +1,129 @@ +#extension GL_OES_standard_derivatives : enable +#define PHYSICAL + +uniform vec3 diffuse; +uniform vec3 emissive; +uniform float roughness; +uniform float metalness; +uniform float opacity; +uniform float emissiveIntensity; + +#ifndef STANDARD + uniform float clearCoat; + uniform float clearCoatRoughness; +#endif + +varying vec3 vViewPosition; + +#ifndef FLAT_SHADED + + varying vec3 vNormal; + +#endif + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +////////////////////////////////////////////////////////////////// +//#include + +varying vec4 vertex_color; +varying vec3 vertex_position; +varying vec2 vertex_uv; + +#ifdef USE_TEXTURE + uniform sampler2D texture; + uniform sampler2D texture_previous; + uniform float animation_time_texture; +#endif + + +void main(void) { + + vec4 finalColor2 = vec4( 0.0, 0.0, 0.0, 1.0 ); +#ifdef USE_RGB + finalColor2 = vertex_color; +#else + #ifdef AS_LINE + finalColor2 = vertex_color; + #else + #ifdef USE_SPRITE + #ifdef USE_TEXTURE + finalColor2 = mix(texture2D(texture_previous, vertex_uv), texture2D(texture, vertex_uv), animation_time_texture); + #else + finalColor2 = vertex_color; + #endif + #else + vec3 fdx = dFdx( vertex_position ); + vec3 fdy = dFdy( vertex_position ); + vec3 normal = normalize( cross( fdx, fdy ) ); + float diffuse = dot( normal, vec3( 0.0, 0.0, 1.0 ) ); + + finalColor2 = vec4(clamp(diffuse, 0.2, 1.) * vertex_color.rgb, vertex_color.a); + #endif + #endif +#endif + #include + +////////////////////////////////////////////////////// + + #include + + vec4 diffuseColor = vec4( diffuse, opacity ); + ReflectedLight reflectedLight = ReflectedLight( vec3( 0.0 ), vec3( 0.0 ), vec3( 0.0 ), vec3( 0.0 ) ); + vec3 totalEmissiveRadiance = emissive * emissiveIntensity; + + #include + #include + #include + #include + #include + #include + #include + #include + #include + #include + + // accumulation + #include + #include + #include + #include + + // modulation + #include + + vec3 outgoingLight = reflectedLight.directDiffuse + reflectedLight.indirectDiffuse + reflectedLight.directSpecular + reflectedLight.indirectSpecular + totalEmissiveRadiance; + + gl_FragColor = vec4( outgoingLight, diffuseColor.a ); + + #include + #include + #include + #include + #include + +} diff --git a/js/glsl/scatter-vertex-physical.glsl b/js/glsl/scatter-vertex-physical.glsl new file mode 100644 index 00000000..604ec4ed --- /dev/null +++ b/js/glsl/scatter-vertex-physical.glsl @@ -0,0 +1,151 @@ +#extension GL_OES_standard_derivatives : enable +#define PHYSICAL + +#include + + // for animation, all between 0 and 1 +uniform float animation_time_x; +uniform float animation_time_y; +uniform float animation_time_z; +uniform float animation_time_vx; +uniform float animation_time_vy; +uniform float animation_time_vz; +uniform float animation_time_size; +uniform float animation_time_color; + +uniform vec2 xlim; +uniform vec2 ylim; +uniform vec2 zlim; + +varying vec4 vertex_color; +varying vec3 vertex_position; +varying vec2 vertex_uv; + +#ifdef AS_LINE +attribute vec3 position_previous; +#else +attribute float x; +attribute float x_previous; +attribute float y; +attribute float y_previous; +attribute float z; +attribute float z_previous; + +attribute vec3 v; +attribute vec3 v_previous; + + +attribute float size; +attribute float size_previous; +#endif + +attribute vec4 color_current; +attribute vec4 color_previous; + +//////////////////////////////////////////////////////////////////////////////// +varying vec3 vViewPosition; + +#ifndef FLAT_SHADED + + varying vec3 vNormal; + +#endif + +#include +#include +#include +#include +#include +//#include +#include +#include +#include +#include +#include +//////////////////////////////////////////////////////////////////////////////// + +void main(void) { + + #include + #include + #include + + #include + #include + #include + #include + #include + +#ifndef FLAT_SHADED // Normal computed with derivatives when FLAT_SHADED + + vNormal = normalize( transformedNormal ); + +#endif + + #include + #include + #include + #include + #include + #include + #include + + vViewPosition = - mvPosition.xyz; + + #include + #include + #include + +//////////////////////////////////////////////////////////////////////////////// + vec3 origin = vec3(xlim.x, ylim.x, zlim.x); + vec3 size_viewport = vec3(xlim.y, ylim.y, zlim.y) - origin; + vec3 animation_time = vec3(animation_time_x, animation_time_y, animation_time_z); + vec3 animation_time_v = vec3(animation_time_vx, animation_time_vy, animation_time_vz); + +#ifdef AS_LINE + vec3 model_pos = (mix(position_previous, position, animation_time) - origin) / size_viewport - 0.5; + vec4 view_pos = modelViewMatrix * vec4(model_pos, 1.0); +#else + vec3 vector = v; + vec3 vector_previous = v_previous; + vec3 position_offset = vec3(x, y, z); + vec3 position_offset_previous = vec3(x_previous, y_previous, z_previous); + + // assume the vector points to the y axis + vec3 vector_current = mix(normalize(vector_previous), normalize(vector), animation_time_v) + * mix(length(vector_previous), length(vector), (animation_time_vx+ animation_time_vy+ animation_time_vz)/3.); + vec3 y_axis = normalize(vector_current); + // we may have bad luck, and alight with 1 vector, so take two vectors, and we'll always find a non-zero vector + vec3 some_z_vector_a = vec3(0., 1., 1.); + vec3 some_z_vector_b = normalize(vec3(0., 2., 1.)); + vec3 x_axis = normalize(cross(y_axis, some_z_vector_a) + cross(y_axis, some_z_vector_b)); + vec3 z_axis = -normalize(cross(y_axis, x_axis)); // - to keep it right handed + //float vector_length = length(vector_current); + // the following matrix should point it to the direction of 'vector' + mat3 move_to_vector = mat3(x_axis, y_axis, z_axis); + + float s = mix(size_previous/100., size/100., animation_time_size); + vec3 model_pos = (mix(position_offset_previous, position_offset, animation_time) - origin) / size_viewport - 0.5; + //vec3 pos = (pos_object ) / size;// - 0.5; + #ifdef USE_SPRITE + // if we are a sprite, we add the position in view coordinates, and need to + vec4 view_pos = modelViewMatrix * vec4(model_pos, 1.0); + view_pos += vec4((position.xy)*(s*0.5),0,0); + #else + model_pos += move_to_vector * (position)*s; + vec4 view_pos = modelViewMatrix * vec4(model_pos, 1.0); + #endif +#endif + vec4 mvPosition = view_pos; + gl_Position = projectionMatrix * view_pos; + vec3 positionEye = ( modelViewMatrix * vec4( model_pos, 1.0 ) ).xyz; + vertex_position = positionEye; + vertex_uv = position.xy / 2. - 0.5; +#ifdef USE_RGB + vertex_color = vec4(model_pos + vec3(0.5, 0.5, 0.5), 1.0); +#else + vertex_color = mix(color_previous, color_current, animation_time_color); +#endif + + #include +} diff --git a/js/glsl/scatter-vertex.glsl b/js/glsl/scatter-vertex.glsl index 4dc262a7..395ef4ed 100644 --- a/js/glsl/scatter-vertex.glsl +++ b/js/glsl/scatter-vertex.glsl @@ -36,7 +36,7 @@ attribute float size; attribute float size_previous; #endif -attribute vec4 color; +attribute vec4 color_current; attribute vec4 color_previous; @@ -89,7 +89,7 @@ void main(void) { #ifdef USE_RGB vertex_color = vec4(model_pos + vec3(0.5, 0.5, 0.5), 1.0); #else - vertex_color = mix(color_previous, color, animation_time_color); + vertex_color = mix(color_previous, color_current, animation_time_color); #endif #include diff --git a/js/src/scatter.ts b/js/src/scatter.ts index 727f544f..344e73f7 100644 --- a/js/src/scatter.ts +++ b/js/src/scatter.ts @@ -42,6 +42,7 @@ class ScatterView extends widgets.WidgetView { receive_shadow : any; render() { + this.LIGHTING_MODELS = { DEFAULT: 'DEFAULT', LAMBERT: 'LAMBERT', @@ -147,6 +148,7 @@ class ScatterView extends widgets.WidgetView { this.line_material = get_material("line_material"); this.line_material_rgb = get_material("line_material"); this.materials = [this.material, this.material_rgb, this.line_material, this.line_material_rgb]; + this._update_materials(); if (this.model.get("material")) { this.model.get("material").on("change", () => { @@ -212,7 +214,7 @@ class ScatterView extends widgets.WidgetView { this.receive_shadow = this.model.get("receive_shadow"); this.mesh.castShadow = this.cast_shadow; this.mesh.receiveShadow = this.receive_shadow; - + this.renderer.scene_scatter.add(this.mesh); if (this.line_segments) { this.renderer.scene_scatter.add(this.line_segments); @@ -315,11 +317,13 @@ class ScatterView extends widgets.WidgetView { // not present on .copy.. bug? this.line_material_rgb.linewidth = this.line_material.linewidth = this.model.get("line_material").obj.linewidth; } + + this.material.defines = {USE_COLOR: true}; this.material.extensions = {derivatives: true}; - this.material_rgb.defines = {USE_RGB: true}; + this.material_rgb.defines = {USE_RGB: true, USE_COLOR: true}; this.material_rgb.extensions = {derivatives: true}; this.line_material.defines = {AS_LINE: true}; - this.line_material_rgb.defines = {USE_RGB: true, AS_LINE: true}; + this.line_material_rgb.defines = {USE_RGB: true, AS_LINE: true, USE_COLOR: true}; // locally and the visible with this object's visible trait this.material.visible = this.material.visible && this.model.get("visible"); this.material_rgb.visible = this.material.visible && this.model.get("visible"); @@ -327,6 +331,7 @@ class ScatterView extends widgets.WidgetView { this.line_material_rgb.visible = this.line_material.visible && this.model.get("visible"); this.lighting_model = this.model.get("lighting_model"); + console.log("------------------------- LIGHTING MODEL: " + this.lighting_model); this.materials.forEach((material) => { if(this.lighting_model === this.LIGHTING_MODELS.DEFAULT) { @@ -476,7 +481,7 @@ class ScatterView extends widgets.WidgetView { current.ensure_array(["color"]); previous.ensure_array(["color"]); - geometry.addAttribute("color", new THREE.BufferAttribute(current.array_vec4.color, 4)); + geometry.addAttribute("color_current", new THREE.BufferAttribute(current.array_vec4.color, 4)); geometry.addAttribute("color_previous", new THREE.BufferAttribute(previous.array_vec4.color, 4)); this.line_segments = new THREE.Line(geometry, this.line_material); diff --git a/notebooks/lighting_demo.ipynb b/notebooks/lighting_demo.ipynb index 0c23d2ef..5a1125b6 100644 --- a/notebooks/lighting_demo.ipynb +++ b/notebooks/lighting_demo.ipynb @@ -2,7 +2,7 @@ "cells": [ { "cell_type": "code", - "execution_count": 115, + "execution_count": 1, "metadata": {}, "outputs": [], "source": [ @@ -180,23 +180,9 @@ }, { "cell_type": "code", - "execution_count": 116, + "execution_count": 2, "metadata": {}, "outputs": [ - { - "data": { - "application/vnd.jupyter.widget-view+json": { - "model_id": "96d960f616f943fa995bc2066ecadf0c", - "version_major": 2, - "version_minor": 0 - }, - "text/plain": [ - "VBox(children=(Figure(camera=PerspectiveCamera(fov=45.0, position=(0.0, 0.0, 2.0), quaternion=(0.0, 0.0, 0.0, …" - ] - }, - "metadata": {}, - "output_type": "display_data" - }, { "name": "stdout", "output_type": "stream", @@ -232,6 +218,20 @@ "shadow_camera_perspective_aspect: 1\n", "shadow_map_type: PCF_SOFT\n" ] + }, + { + "data": { + "application/vnd.jupyter.widget-view+json": { + "model_id": "637138800a4a46ad840f08b17e40230c", + "version_major": 2, + "version_minor": 0 + }, + "text/plain": [ + "VBox(children=(Figure(camera=PerspectiveCamera(fov=45.0, position=(0.0, 0.0, 2.0), quaternion=(0.0, 0.0, 0.0, …" + ] + }, + "metadata": {}, + "output_type": "display_data" } ], "source": [ @@ -245,7 +245,7 @@ }, { "cell_type": "code", - "execution_count": 118, + "execution_count": 3, "metadata": {}, "outputs": [ { @@ -287,7 +287,7 @@ { "data": { "application/vnd.jupyter.widget-view+json": { - "model_id": "cfb7fb48d99a4e829941f4ed8d2990af", + "model_id": "33fc37fca37b4ecf83e230bbbdadb7de", "version_major": 2, "version_minor": 0 }, From 0eea214aeaefe7c0510cc663174304e0c377fb02 Mon Sep 17 00:00:00 2001 From: rinftech-github Date: Mon, 30 Mar 2020 19:21:25 +0300 Subject: [PATCH 022/130] Scatter lighting shaders work in progress - InstancedBufferGeometry shadowmap issue. Work in progress lighting demo notebook --- ipyvolume/pylab.py | 6 +- js/glsl/scatter-fragment-physical.glsl | 41 ++++++- js/glsl/scatter-vertex-physical.glsl | 7 +- js/src/light.ts | 6 +- js/src/scatter.ts | 67 +++++++++- notebooks/lighting_demo.ipynb | 162 ++++++++++++++++++------- 6 files changed, 227 insertions(+), 62 deletions(-) diff --git a/ipyvolume/pylab.py b/ipyvolume/pylab.py index c8266d2d..fe09ad6b 100644 --- a/ipyvolume/pylab.py +++ b/ipyvolume/pylab.py @@ -1693,15 +1693,15 @@ def _make_triangles_lines(shape, wrapx=False, wrapy=False): return triangles, lines def ambient_light( - light_color2=default_color_selected, + light_color=default_color_selected, intensity = 1): print("ADD AMBIENT LIGHT (from pylab) ") - print("light_color2: " + str(light_color2)) + print("light_color: " + str(light_color)) print("intensity: " + str(intensity)) light = ipv.Light( light_type='AMBIENT', - light_color2=light_color2, + light_color=light_color, intensity=intensity) fig = gcf() diff --git a/js/glsl/scatter-fragment-physical.glsl b/js/glsl/scatter-fragment-physical.glsl index 6518f67b..31264290 100644 --- a/js/glsl/scatter-fragment-physical.glsl +++ b/js/glsl/scatter-fragment-physical.glsl @@ -1,4 +1,5 @@ #extension GL_OES_standard_derivatives : enable +#define DEPTH_PACKING 3201 #define PHYSICAL uniform vec3 diffuse; @@ -62,7 +63,7 @@ varying vec2 vertex_uv; void main(void) { - +/* vec4 finalColor2 = vec4( 0.0, 0.0, 0.0, 1.0 ); #ifdef USE_RGB finalColor2 = vertex_color; @@ -87,23 +88,50 @@ void main(void) { #endif #endif #include - +*/ ////////////////////////////////////////////////////// #include - vec4 diffuseColor = vec4( diffuse, opacity ); + vec4 diffuseColor = vec4( 1.0, 1.0, 1.0, opacity ); ReflectedLight reflectedLight = ReflectedLight( vec3( 0.0 ), vec3( 0.0 ), vec3( 0.0 ), vec3( 0.0 ) ); vec3 totalEmissiveRadiance = emissive * emissiveIntensity; + #include #include - #include + //#include +//#ifdef USE_COLOR + +// diffuseColor.rgb *= vec3(1.0, 0.0, 0.0); + +//#endif #include #include #include #include #include + /* +#ifdef FLAT_SHADED + + // Workaround for Adreno/Nexus5 not able able to do dFdx( vViewPosition ) ... + + vec3 fdx = vec3( dFdx( vViewPosition.x ), dFdx( vViewPosition.y ), dFdx( vViewPosition.z ) ); + vec3 fdy = vec3( dFdy( vViewPosition.x ), dFdy( vViewPosition.y ), dFdy( vViewPosition.z ) ); + vec3 normal = normalize( cross( fdx, fdy ) ); + +#else + + vec3 normal = normalize( vNormal ); + + #ifdef DOUBLE_SIDED + + normal = normal * ( float( gl_FrontFacing ) * 2.0 - 1.0 ); + + #endif + +#endif +*/ #include #include @@ -126,4 +154,9 @@ void main(void) { #include #include +#ifdef FLAT_SHADED + gl_FragColor = vec4( 0.0,1.0,0.0, diffuseColor.a ); + +#endif + } diff --git a/js/glsl/scatter-vertex-physical.glsl b/js/glsl/scatter-vertex-physical.glsl index 604ec4ed..c14067ba 100644 --- a/js/glsl/scatter-vertex-physical.glsl +++ b/js/glsl/scatter-vertex-physical.glsl @@ -1,7 +1,8 @@ #extension GL_OES_standard_derivatives : enable +#define DEPTH_PACKING 3201 #define PHYSICAL -#include +//#include // for animation, all between 0 and 1 uniform float animation_time_x; @@ -56,7 +57,7 @@ varying vec3 vViewPosition; #include #include #include -//#include +#include #include #include #include @@ -136,7 +137,7 @@ void main(void) { vec4 view_pos = modelViewMatrix * vec4(model_pos, 1.0); #endif #endif - vec4 mvPosition = view_pos; + vec4 mvPosition2 = view_pos; gl_Position = projectionMatrix * view_pos; vec3 positionEye = ( modelViewMatrix * vec4( model_pos, 1.0 ) ).xyz; vertex_position = positionEye; diff --git a/js/src/light.ts b/js/src/light.ts index ec082cba..7c51c55a 100644 --- a/js/src/light.ts +++ b/js/src/light.ts @@ -63,7 +63,6 @@ class LightView extends widgets.WidgetView { } on_change(attribute) { - console.log("CHAKA LAKA " + attribute) for (const key of this.model.changedAttributes()) { console.log("changed " +key); } @@ -72,9 +71,8 @@ class LightView extends widgets.WidgetView { add_to_scene() { this.lights.forEach((light) => { this.renderer.scene_scatter.add(light); - console.log("1") }); - + console.log(this.renderer.scene_scatter); } remove_from_scene() { @@ -95,7 +93,7 @@ class LightView extends widgets.WidgetView { //no shadow support if(this.light_type === this.LIGHT_TYPES.AMBIENT){ - console.log("Create Ambient Light " + this.intensity); + console.log("Create Ambient Light "); this.current_light = new THREE.AmbientLight(this.light_color, this.intensity); this.lights.push(this.current_light); } diff --git a/js/src/scatter.ts b/js/src/scatter.ts index 344e73f7..89dc0ce8 100644 --- a/js/src/scatter.ts +++ b/js/src/scatter.ts @@ -212,12 +212,14 @@ class ScatterView extends widgets.WidgetView { add_to_scene() { this.cast_shadow = this.model.get("cast_shadow"); this.receive_shadow = this.model.get("receive_shadow"); - this.mesh.castShadow = this.cast_shadow; - this.mesh.receiveShadow = this.receive_shadow; + this.mesh.castShadow = true;//this.cast_shadow; + this.mesh.receiveShadow = true;//this.receive_shadow; this.renderer.scene_scatter.add(this.mesh); if (this.line_segments) { this.renderer.scene_scatter.add(this.line_segments); + this.line_segments.castShadow = true;//this.cast_shadow; + this.line_segments.receiveShadow = true;//this.receive_shadow; } } remove_from_scene() { @@ -333,11 +335,12 @@ class ScatterView extends widgets.WidgetView { this.lighting_model = this.model.get("lighting_model"); console.log("------------------------- LIGHTING MODEL: " + this.lighting_model); this.materials.forEach((material) => { - + if(this.lighting_model === this.LIGHTING_MODELS.DEFAULT) { material.vertexShader = require("raw-loader!../glsl/scatter-vertex.glsl"); material.fragmentShader = require("raw-loader!../glsl/scatter-fragment.glsl"); } + /* else if(this.lighting_model === this.LIGHTING_MODELS.LAMBERT) { material.vertexShader = require("raw-loader!../glsl/scatter-vertex-lambert.glsl"); material.fragmentShader = require("raw-loader!../glsl/scatter-fragment-lambert.glsl"); @@ -346,17 +349,51 @@ class ScatterView extends widgets.WidgetView { material.vertexShader = require("raw-loader!../glsl/scatter-vertex-phong.glsl"); material.fragmentShader = require("raw-loader!../glsl/scatter-fragment-phong.glsl"); } - else if(this.lighting_model === this.LIGHTING_MODELS.PHYSICAL) { + */ + else {//if(this.lighting_model === this.LIGHTING_MODELS.PHYSICAL) { material.vertexShader = require("raw-loader!../glsl/scatter-vertex-physical.glsl"); material.fragmentShader = require("raw-loader!../glsl/scatter-fragment-physical.glsl"); } + //material.vertexShader = require("raw-loader!../glsl/scatter-vertex.glsl"); //material.fragmentShader = require("raw-loader!../glsl/scatter-fragment.glsl"); material.uniforms = {...material.uniforms, ...this.uniforms}; + + material.lights = true; + + + this.diffuse_color = this.model.get("diffuse_color"); + this.opacity = this.model.get("opacity"); + this.specular_color = this.model.get("specular_color"); + this.shininess = this.model.get("shininess"); + this.emissive_color = this.model.get("emissive_color"); + this.emissive_intensity = this.model.get("emissive_intensity"); + this.roughness = this.model.get("roughness"); + this.metalness = this.model.get("metalness"); + + console.log(this.diffuse_color); + console.log(this.opacity); + console.log(this.specular_color); + console.log(this.shininess); + console.log(this.emissive_color); + console.log(this.emissive_intensity); + console.log(this.roughness); + console.log(this.metalness); + + material.uniforms.diffuse.value = new THREE.Color(1, 1, 1);//this.diffuse_color//BUG? keep hardcoded + material.uniforms.opacity.value = this.opacity; + material.uniforms.specular.value = new THREE.Color(this.specular_color); + material.uniforms.shininess.value = this.shininess; + material.uniforms.emissive.value = new THREE.Color(this.emissive_color); + material.uniforms.emissiveIntensity.value = this.emissive_intensity; + material.uniforms.roughness.value = this.roughness; + material.uniforms.metalness.value = this.metalness; + material.depthWrite = true; material.transparant = true; material.depthTest = true; material.needsUpdate = true; + }); this.diffuse_color = this.model.get("diffuse_color"); @@ -367,7 +404,17 @@ class ScatterView extends widgets.WidgetView { this.emissive_intensity = this.model.get("emissive_intensity"); this.roughness = this.model.get("roughness"); this.metalness = this.model.get("metalness"); - + this.renderer.renderer.shadowMap.enabled = true; +/* + console.log("this.diffuse_color:"+this.diffuse_color); + console.log("this.opacity:"+this.opacity); + console.log("this.specular_color:"+this.specular_color); + console.log("this.shininess:"+this.shininess); + console.log("this.emissive_color:"+this.emissive_color); + console.log("this.emissive_intensity:"+this.emissive_intensity); + console.log("this.roughness:"+this.roughness); + console.log("this.metalness:"+this.metalness); +*/ this.material.uniforms.diffuse.value = new THREE.Color(1, 1, 1);//this.diffuse_color//BUG? keep hardcoded this.material.uniforms.opacity.value = this.opacity; this.material.uniforms.specular.value = new THREE.Color(this.specular_color); @@ -389,6 +436,7 @@ class ScatterView extends widgets.WidgetView { this.material.defines.USE_TEXTURE = true; } } + this.material.lights = true; this.material.needsUpdate = true; this.material_rgb.needsUpdate = true; this.line_material.needsUpdate = true; @@ -402,11 +450,12 @@ class ScatterView extends widgets.WidgetView { } const sprite = geo.endsWith("2d"); const buffer_geo = new THREE.BufferGeometry().fromGeometry(this.geos[geo]); + buffer_geo.computeVertexNormals(); const instanced_geo = new THREE.InstancedBufferGeometry(); const vertices = (buffer_geo.attributes.position as any).clone(); instanced_geo.addAttribute("position", vertices); - + const sequence_index = this.model.get("sequence_index"); let sequence_index_previous = this.previous_values.sequence_index; if (typeof sequence_index_previous === "undefined") { @@ -418,6 +467,10 @@ class ScatterView extends widgets.WidgetView { const current = new values.Values(scalar_names, [], this.get_current.bind(this), sequence_index, vector4_names); const previous = new values.Values(scalar_names, [], this.get_previous.bind(this), sequence_index_previous, vector4_names); + // Workaround for shader issue - color redefine + current.ensure_array(["color"]); + instanced_geo.addAttribute("color_current", new THREE.BufferAttribute(current.array_vec4.color, 4)); + const length = Math.max(current.length, previous.length); if (length === 0) { console.error("no single member is an array, not supported (yet?)"); @@ -467,6 +520,7 @@ class ScatterView extends widgets.WidgetView { this.material.uniforms.texture_previous.value = this.textures[sequence_index_previous % this.textures.length]; } } + instanced_geo.computeVertexNormals(); this.mesh = new THREE.Mesh(instanced_geo, this.material); this.mesh.material_rgb = this.material_rgb; this.mesh.material_normal = this.material; @@ -483,6 +537,7 @@ class ScatterView extends widgets.WidgetView { previous.ensure_array(["color"]); geometry.addAttribute("color_current", new THREE.BufferAttribute(current.array_vec4.color, 4)); geometry.addAttribute("color_previous", new THREE.BufferAttribute(previous.array_vec4.color, 4)); + geometry.computeVertexNormals(); this.line_segments = new THREE.Line(geometry, this.line_material); this.line_segments.frustumCulled = false; diff --git a/notebooks/lighting_demo.ipynb b/notebooks/lighting_demo.ipynb index 5a1125b6..ff6ee843 100644 --- a/notebooks/lighting_demo.ipynb +++ b/notebooks/lighting_demo.ipynb @@ -39,8 +39,8 @@ "material_shininess=10\n", "material_emissive_color='black'\n", "material_emissive_intensity=1.0\n", - "material_roughness=0.3\n", - "material_metalness=0.3\n", + "material_roughness=0.1\n", + "material_metalness=0.1\n", "material_cast_shadow=True\n", "material_receive_shadow=True\n", " \n", @@ -145,34 +145,7 @@ " print(selected_size.value)\n", " return selected_size\n", "\n", - "def add_lights():\n", - " #ambient_light_widget()\n", - "\n", - " #p3.ambient_light(light_color=\"rgb(255,0,0)\", intensity=1.5)\n", "\n", - " p3.directional_light(light_color='green', intensity=1.0, position=[100, 100, 100], target=[0,0,0], cast_shadow=True, shadow_bias= -0.005,shadow_camera_orthographic_size=100)\n", - " #p3.directional_light(light_color='orange', intensity=1.0, position=[-100, 100, -100], target=[0,0,0], cast_shadow=True, shadow_bias= -0.005,shadow_camera_orthographic_size=100,shadow_map_type='PCF_SOFT')\n", - " #p3.hemisphere_light(light_color='red', color2='yellow', intensity=0.5, position=[1, 0, 0])\n", - " #p3.point_light(light_color='orange', intensity=1.0, position=[20, 30, 20], distance=65, decay=0.5, cast_shadow=True,shadow_radius=3,shadow_bias= -0.005)\n", - " p3.spot_light(\n", - " light_color='yellow', \n", - " intensity=1, \n", - " position=[20, 30, 20], \n", - " angle=math.pi/9, \n", - " target = [-20,-20,-20], \n", - " cast_shadow=True,\n", - " distance=0,\n", - " decay=10,\n", - " penumbra=0.5,\n", - " shadow_map_size=1024,\n", - " shadow_bias=-0.0005,\n", - " shadow_radius=5,\n", - " shadow_camera_near=0.5,\n", - " shadow_camera_far=500,\n", - " shadow_camera_perspective_fov=50,\n", - " shadow_camera_perspective_aspect=1,\n", - " shadow_map_type='PCF_SOFT')\n", - " return None\n", "\n", "\n", "\n" @@ -190,7 +163,7 @@ "ADD DIRECTIONAL LIGHT (from pylab) \n", "light_color: green\n", "intensity: 1.0\n", - "position: 100 100 100\n", + "position: -100 100 -100\n", "target: 0 0 0\n", "cast_shadow: True\n", "shadow_map_size: 512\n", @@ -222,7 +195,7 @@ { "data": { "application/vnd.jupyter.widget-view+json": { - "model_id": "637138800a4a46ad840f08b17e40230c", + "model_id": "29675463837e4086bc259f6ed9eb8879", "version_major": 2, "version_minor": 0 }, @@ -236,6 +209,34 @@ ], "source": [ "#very important to first call p3.clear()\n", + "def add_lights():\n", + " #ambient_light_widget()\n", + "\n", + " #p3.ambient_light(light_color=\"rgb(255,0,0)\", intensity=1.5)\n", + "\n", + " #p3.directional_light(light_color='green', intensity=1.0, position=[100, 100, 100], target=[0,0,0], cast_shadow=True, shadow_bias= -0.005,shadow_camera_orthographic_size=100)\n", + " p3.directional_light(light_color='green', intensity=1.0, position=[-100, 100, -100], target=[0,0,0], cast_shadow=True, shadow_bias= -0.005,shadow_camera_orthographic_size=100,shadow_map_type='PCF_SOFT')\n", + " #p3.hemisphere_light(light_color='red', color2='yellow', intensity=0.5, position=[1, 0, 0])\n", + " #p3.point_light(light_color='orange', intensity=1.0, position=[20, 30, 20], distance=65, decay=0.5, cast_shadow=True,shadow_radius=3,shadow_bias= -0.005)\n", + " p3.spot_light(\n", + " light_color='yellow', \n", + " intensity=1, \n", + " position=[20, 30, 20], \n", + " angle=math.pi/9, \n", + " target = [-20,-20,-20], \n", + " cast_shadow=True,\n", + " distance=0,\n", + " decay=10,\n", + " penumbra=0.5,\n", + " shadow_map_size=1024,\n", + " shadow_bias=-0.0005,\n", + " shadow_radius=5,\n", + " shadow_camera_near=0.5,\n", + " shadow_camera_far=500,\n", + " shadow_camera_perspective_fov=50,\n", + " shadow_camera_perspective_aspect=1,\n", + " shadow_map_type='PCF_SOFT')\n", + " return None\n", "p3.clear()\n", "klein_bottle()\n", "worldplane()\n", @@ -252,18 +253,67 @@ "name": "stdout", "output_type": "stream", "text": [ - "ADD DIRECTIONAL LIGHT (from pylab) \n", - "light_color: green\n", - "intensity: 1.0\n", - "position: 100 100 100\n", - "target: 0 0 0\n", + "ADD SPOT LIGHT (from pylab) \n", + "light_color: blue\n", + "intensity: 2\n", + "position: 20 30 20\n", + "target: -20 -20 -20\n", + "angle: 0.3490658503988659\n", + "distance: 0\n", + "decay: 1\n", + "penumbra: 1.0\n", "cast_shadow: True\n", - "shadow_map_size: 512\n", - "shadow_bias: -0.005\n", - "shadow_radius: 1\n", + "shadow_map_size: 1024\n", + "shadow_bias: -0.0005\n", + "shadow_radius: 3\n", "shadow_camera_near: 0.5\n", "shadow_camera_far: 500\n", - "shadow_map_type: PCF_SOFT\n", + "shadow_camera_perspective_fov: 50\n", + "shadow_camera_perspective_aspect: 1\n", + "shadow_map_type: PCF_SOFT\n" + ] + } + ], + "source": [ + "#p3.clear()\n", + "light_1 = p3.spot_light(\n", + " light_color='blue', \n", + " intensity=2, \n", + " position=[20, 30, 20], \n", + " angle=math.pi/9, \n", + " target = [-20,-20,-20], \n", + " cast_shadow=True,\n", + " distance=0,\n", + " decay=1,\n", + " penumbra=1.0,\n", + " shadow_map_size=1024,\n", + " shadow_bias=-0.0005,\n", + " shadow_radius=3,\n", + " shadow_camera_near=0.5,\n", + " shadow_camera_far=500,\n", + " shadow_camera_perspective_fov=50,\n", + " shadow_camera_perspective_aspect=1,\n", + " shadow_map_type='PCF_SOFT')" + ] + }, + { + "cell_type": "code", + "execution_count": 19, + "metadata": {}, + "outputs": [], + "source": [ + "light_1.light_color = 'red'" + ] + }, + { + "cell_type": "code", + "execution_count": 5, + "metadata": {}, + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ "ADD SPOT LIGHT (from pylab) \n", "light_color: yellow\n", "intensity: 1\n", @@ -287,7 +337,7 @@ { "data": { "application/vnd.jupyter.widget-view+json": { - "model_id": "33fc37fca37b4ecf83e230bbbdadb7de", + "model_id": "bae2fb2c60dd46b29de6ce16634928f1", "version_major": 2, "version_minor": 0 }, @@ -300,11 +350,39 @@ } ], "source": [ + "def add_lights2():\n", + " #ambient_light_widget()\n", + "\n", + " #p3.ambient_light(light_color=\"rgb(255,0,0)\", intensity=1.5)\n", + "\n", + " #p3.directional_light(light_color='green', intensity=1.0, position=[100, 100, 100], target=[0,0,0], cast_shadow=True, shadow_bias= -0.005,shadow_camera_orthographic_size=100)\n", + " #p3.directional_light(light_color='green', intensity=1.0, position=[-100, 100, -100], target=[0,0,0], cast_shadow=True, shadow_bias= -0.005,shadow_camera_orthographic_size=100,shadow_map_type='PCF_SOFT')\n", + " #p3.hemisphere_light(light_color='red', color2='yellow', intensity=0.5, position=[1, 0, 0])\n", + " #p3.point_light(light_color='orange', intensity=1.0, position=[20, 30, 20], distance=65, decay=0.5, cast_shadow=True,shadow_radius=3,shadow_bias= -0.005)\n", + " p3.spot_light(\n", + " light_color='yellow', \n", + " intensity=1, \n", + " position=[20, 30, 20], \n", + " angle=math.pi/9, \n", + " target = [-20,-20,-20], \n", + " cast_shadow=True,\n", + " distance=0,\n", + " decay=10,\n", + " penumbra=0.5,\n", + " shadow_map_size=1024,\n", + " shadow_bias=-0.0005,\n", + " shadow_radius=5,\n", + " shadow_camera_near=0.5,\n", + " shadow_camera_far=500,\n", + " shadow_camera_perspective_fov=50,\n", + " shadow_camera_perspective_aspect=1,\n", + " shadow_map_type='PCF_SOFT')\n", + " return None\n", "#very important to first call p3.clear()\n", "p3.clear()\n", "test_surface2()\n", "worldplane()\n", - "add_lights()\n", + "add_lights2()\n", "p3.show()" ] }, From db927eeec37dd6221f8a6fe93be8710482c4d893 Mon Sep 17 00:00:00 2001 From: rinftech-github Date: Tue, 31 Mar 2020 02:05:20 +0300 Subject: [PATCH 023/130] Finished notebook lighting demo for plot_mesh, plot_trisurf and plot_surface --- ipyvolume/pylab.py | 2 +- js/glsl/mesh-fragment-physical.glsl | 6 +- js/src/light.ts | 142 +++++-- notebooks/lighting_demo.ipynb | 630 +++++++++++++++++++++------- 4 files changed, 576 insertions(+), 204 deletions(-) diff --git a/ipyvolume/pylab.py b/ipyvolume/pylab.py index fe09ad6b..5ae21ef9 100644 --- a/ipyvolume/pylab.py +++ b/ipyvolume/pylab.py @@ -1749,7 +1749,7 @@ def directional_light( shadow_radius=1, shadow_camera_near=0.5, shadow_camera_far=500, - shadow_camera_orthographic_size=5, + shadow_camera_orthographic_size=100, shadow_map_type='PCF_SOFT'): print("ADD DIRECTIONAL LIGHT (from pylab) ") diff --git a/js/glsl/mesh-fragment-physical.glsl b/js/glsl/mesh-fragment-physical.glsl index 44aa7652..ce769134 100644 --- a/js/glsl/mesh-fragment-physical.glsl +++ b/js/glsl/mesh-fragment-physical.glsl @@ -86,14 +86,14 @@ void main() ////////////////////////////////////////////////////// #include - - vec4 diffuseColor = vec4( diffuse, opacity ); + //diffuse = vec3(1,1,1); + vec4 diffuseColor = vec4( vec3(1,1,1), opacity ); ReflectedLight reflectedLight = ReflectedLight( vec3( 0.0 ), vec3( 0.0 ), vec3( 0.0 ), vec3( 0.0 ) ); vec3 totalEmissiveRadiance = emissive * emissiveIntensity; #include #include - #include + //#include #include #include #include diff --git a/js/src/light.ts b/js/src/light.ts index 7c51c55a..d1d183ac 100644 --- a/js/src/light.ts +++ b/js/src/light.ts @@ -38,6 +38,10 @@ class LightView extends widgets.WidgetView { shadow_camera_perspective_aspect: any; shadow_camera_orthographic_size: any; + //change:light_type //? + /* + change:light_color change:light_color2 change:intensity change:shadow_map_type change:cast_shadow change:position_x change:position_y change:position_z change:target_x change:target_y change:target_z change:distance change:angle change:decay change:penumbra change:shadow_map_size change:shadow_bias change:shadow_radius change:shadow_camera_near change:shadow_camera_far change:shadow_camera_perspective_fov change:shadow_camera_perspective_aspect change:shadow_camera_orthographic_size + */ render() { this.LIGHT_TYPES = { @@ -56,16 +60,20 @@ class LightView extends widgets.WidgetView { this.renderer = this.options.parent; - this.model.on("change:light_color change:intensity", + this.model.on("change:light_color change:light_color2 change:intensity change:shadow_map_type change:cast_shadow change:position_x change:position_y change:position_z change:target_x change:target_y change:target_z change:distance change:angle change:decay change:penumbra change:shadow_map_size change:shadow_bias change:shadow_radius change:shadow_camera_near change:shadow_camera_far change:shadow_camera_perspective_fov change:shadow_camera_perspective_aspect change:shadow_camera_orthographic_size", this.on_change, this); - this.create_light(); + this.create_light(true); this.add_to_scene(); } on_change(attribute) { - for (const key of this.model.changedAttributes()) { - console.log("changed " +key); - } + + + this.cast_shadow = this.model.get("cast_shadow"); + this.renderer.renderer.shadowMap.enabled = this.cast_shadow; + console.log("CHANGE " + this.model.get("shadow_camera_orthographic_size")); + this.create_light(false); + } add_to_scene() { @@ -76,39 +84,56 @@ class LightView extends widgets.WidgetView { } remove_from_scene() { + this.renderer.scene_scatter.remove(this.target); this.lights.forEach((light) => { this.renderer.scene_scatter.remove(light); }); } - create_light() { + create_light(instantiate=true) { this.lights = []; this.light_color = this.model.get("light_color"); this.intensity = this.model.get("intensity"); - this.light_type = this.model.get("light_type"); + if(instantiate === true){ + this.light_type = this.model.get("light_type"); + } + this.cast_shadow = this.model.get("cast_shadow"); this.renderer.renderer.shadowMap.enabled = this.cast_shadow; //no shadow support if(this.light_type === this.LIGHT_TYPES.AMBIENT){ console.log("Create Ambient Light "); - this.current_light = new THREE.AmbientLight(this.light_color, this.intensity); - this.lights.push(this.current_light); + if(instantiate === true){ + this.current_light = new THREE.AmbientLight(this.light_color, this.intensity); + this.lights.push(this.current_light); + } + else{ + this.current_light.color.set(this.light_color); + this.current_light.intensity = this.intensity; + } } else{ this.position = new THREE.Vector3(this.model.get("position_x"), this.model.get("position_y"), this.model.get("position_z")); // no shadow support if(this.light_type === this.LIGHT_TYPES.HEMISPHERE) { - console.log("Create Hemisphere Light "); - this.light_color2 = this.model.get("light_color2"); - - this.current_light = new THREE.HemisphereLight(this.light_color, this.light_color2, this.intensity); + console.log("Create Hemisphere Light "); + if(instantiate === true){ + this.current_light = new THREE.HemisphereLight(this.light_color, this.light_color2, this.intensity); + } + else{ + this.current_light.color.set(this.light_color); + this.current_light.groundColor.set(this.light_color2); + this.current_light.intensity = this.intensity; + } this.current_light.position.set(this.position.x, this.position.y, this.position.z); - this.lights.push(this.current_light); + if(instantiate === true){ + this.lights.push(this.current_light); + } } // with shadow support else { @@ -136,8 +161,14 @@ class LightView extends widgets.WidgetView { if(this.light_type === this.LIGHT_TYPES.POINT) { console.log("Create Point Light"); - this.current_light = new THREE.PointLight(this.light_color, this.intensity); - + if(instantiate === true){ + this.current_light = new THREE.PointLight(this.light_color, this.intensity); + } + else { + this.current_light.color.set(this.light_color); + this.current_light.intensity = this.intensity; + } + this.current_light.position.set(this.position.x, this.position.y, this.position.z); this.current_light.distance = this.distance; this.current_light.decay = this.decay; @@ -151,22 +182,31 @@ class LightView extends widgets.WidgetView { this.current_light.shadow.camera.position.set(this.position.x, this.position.y, this.position.z); this.current_light.shadow.camera.near = this.shadow_camera_near; this.current_light.shadow.camera.far = this.shadow_camera_far; - - this.lights.push(this.current_light); + + if(instantiate === true){ + this.lights.push(this.current_light); + } } else { - //TODO - move to a separate function - this.target = new THREE.Object3D(); + if(instantiate === true) { + this.target = new THREE.Object3D(); + } this.target.position.set(this.model.get("target_x"), this.model.get("target_y"), this.model.get("target_z")); this.target.updateMatrixWorld(); - this.renderer.scene_scatter.add(this.target); - + if(instantiate === true) { + this.renderer.scene_scatter.add(this.target); + } if(this.light_type === this.LIGHT_TYPES.DIRECTIONAL) { console.log("Create Directional Light"); this.shadow_camera_orthographic_size = this.model.get("shadow_camera_orthographic_size"); - - this.current_light = new THREE.DirectionalLight(this.light_color, this.intensity); + if(instantiate === true) { + this.current_light = new THREE.DirectionalLight(this.light_color, this.intensity); + } + else { + this.current_light.color.set(this.light_color); + this.current_light.intensity = this.intensity; + } this.current_light.position.set(this.position.x, this.position.y, this.position.z); this.current_light.target = this.target; this.current_light.castShadow = this.cast_shadow; @@ -175,17 +215,28 @@ class LightView extends widgets.WidgetView { this.current_light.shadow.mapSize.height = this.shadow_map_size; this.current_light.shadow.bias = this.shadow_bias; // prevent shadow acne this.current_light.shadow.radius = this.shadow_radius; - + + this.current_light.shadow.camera = new THREE.OrthographicCamera( -this.shadow_camera_orthographic_size/2, + this.shadow_camera_orthographic_size/2, + this.shadow_camera_orthographic_size/2, + -this.shadow_camera_orthographic_size/2, + this.shadow_camera_near, + this.shadow_camera_far ); this.current_light.shadow.camera.position.set(this.position.x, this.position.y, this.position.z); + /* this.current_light.shadow.camera.near = this.shadow_camera_near; this.current_light.shadow.camera.far = this.shadow_camera_far; + this.current_light.shadow.camera.left = - this.shadow_camera_orthographic_size/2; + this.current_light.shadow.camera.right = this.shadow_camera_orthographic_size/2; + this.current_light.shadow.camera.top = this.shadow_camera_orthographic_size/2; + this.current_light.shadow.camera.bottom = - this.shadow_camera_orthographic_size/2; + this.current_light.shadow.camera.updateMatrixWorld(); + */ - this.current_light.shadow.camera.left = - this.shadow_camera_orthographic_size; - this.current_light.shadow.camera.right = this.shadow_camera_orthographic_size; - this.current_light.shadow.camera.top = this.shadow_camera_orthographic_size; - this.current_light.shadow.camera.bottom = - this.shadow_camera_orthographic_size; - - this.lights.push(this.current_light); + this.current_light.castShadow = this.cast_shadow; + if(instantiate === true) { + this.lights.push(this.current_light); + } } else if(this.light_type === this.LIGHT_TYPES.SPOT) { console.log("Create Spot Light"); @@ -195,9 +246,13 @@ class LightView extends widgets.WidgetView { this.shadow_camera_perspective_fov = this.model.get("shadow_camera_perspective_fov"); this.shadow_camera_perspective_aspect = this.model.get("shadow_camera_perspective_aspect"); - - this.current_light = new THREE.SpotLight(this.light_color, this.intensity); - + if(instantiate === true) { + this.current_light = new THREE.SpotLight(this.light_color, this.intensity); + } + else { + this.current_light.color.set(this.light_color); + this.current_light.intensity = this.intensity; + } this.current_light.position.set(this.position.x, this.position.y, this.position.z); this.current_light.target = this.target; this.current_light.angle = this.angle; @@ -211,13 +266,20 @@ class LightView extends widgets.WidgetView { this.current_light.shadow.bias = this.shadow_bias; // prevent shadow acne this.current_light.shadow.radius = this.shadow_radius; + this.current_light.shadow.camera = new THREE.PerspectiveCamera(this.shadow_camera_perspective_fov, + this.shadow_camera_perspective_aspect, + this.shadow_camera_near, + this.shadow_camera_far); + this.current_light.shadow.camera.position.set(this.position.x, this.position.y, this.position.z); - this.current_light.shadow.camera.near = this.shadow_camera_near; - this.current_light.shadow.camera.far = this.shadow_camera_far; - this.current_light.shadow.camera.aspect = this.shadow_camera_perspective_aspect; - this.current_light.shadow.camera.fov = this.shadow_camera_perspective_fov; - - this.lights.push(this.current_light); + //this.current_light.shadow.camera.near = this.shadow_camera_near; + //this.current_light.shadow.camera.far = this.shadow_camera_far; + //this.current_light.shadow.camera.aspect = this.shadow_camera_perspective_aspect; + //this.current_light.shadow.camera.fov = this.shadow_camera_perspective_fov; + + if(instantiate === true) { + this.lights.push(this.current_light); + } } diff --git a/notebooks/lighting_demo.ipynb b/notebooks/lighting_demo.ipynb index ff6ee843..17173c71 100644 --- a/notebooks/lighting_demo.ipynb +++ b/notebooks/lighting_demo.ipynb @@ -2,7 +2,7 @@ "cells": [ { "cell_type": "code", - "execution_count": 1, + "execution_count": 96, "metadata": {}, "outputs": [], "source": [ @@ -112,7 +112,7 @@ " cast_shadow=material_cast_shadow,\n", " receive_shadow=material_receive_shadow)\n", " \n", - "def test_surface2():\n", + "def test_surface():\n", " X = np.arange(-10, 10, 0.25*1)-10\n", " Y = np.arange(-10, 10, 0.25*1)\n", " X, Y = np.meshgrid(X, Y)\n", @@ -146,6 +146,16 @@ " return selected_size\n", "\n", "\n", + "def plot_all(kb=False, wp=True, ts=False):\n", + " p3.clear()\n", + " p3.figure()\n", + " if kb:\n", + " klein_bottle()\n", + " if wp:\n", + " worldplane()\n", + " if ts:\n", + " test_surface()\n", + " p3.show()\n", "\n", "\n", "\n" @@ -153,49 +163,70 @@ }, { "cell_type": "code", - "execution_count": 2, + "execution_count": 97, + "metadata": {}, + "outputs": [ + { + "data": { + "application/vnd.jupyter.widget-view+json": { + "model_id": "fd7b3e90b0dc4cadb1016415475c602c", + "version_major": 2, + "version_minor": 0 + }, + "text/plain": [ + "VBox(children=(Figure(camera=PerspectiveCamera(fov=45.0, position=(0.0, 0.0, 2.0), quaternion=(0.0, 0.0, 0.0, …" + ] + }, + "metadata": {}, + "output_type": "display_data" + } + ], + "source": [ + "#####################################################\n", + "#AMBIENT LIGHT\n", + "plot_all(kb=False, wp=True, ts=True)" + ] + }, + { + "cell_type": "code", + "execution_count": 98, "metadata": {}, "outputs": [ { "name": "stdout", "output_type": "stream", "text": [ - "ADD DIRECTIONAL LIGHT (from pylab) \n", - "light_color: green\n", - "intensity: 1.0\n", - "position: -100 100 -100\n", - "target: 0 0 0\n", - "cast_shadow: True\n", - "shadow_map_size: 512\n", - "shadow_bias: -0.005\n", - "shadow_radius: 1\n", - "shadow_camera_near: 0.5\n", - "shadow_camera_far: 500\n", - "shadow_map_type: PCF_SOFT\n", - "ADD SPOT LIGHT (from pylab) \n", - "light_color: yellow\n", - "intensity: 1\n", - "position: 20 30 20\n", - "target: -20 -20 -20\n", - "angle: 0.3490658503988659\n", - "distance: 0\n", - "decay: 10\n", - "penumbra: 0.5\n", - "cast_shadow: True\n", - "shadow_map_size: 1024\n", - "shadow_bias: -0.0005\n", - "shadow_radius: 5\n", - "shadow_camera_near: 0.5\n", - "shadow_camera_far: 500\n", - "shadow_camera_perspective_fov: 50\n", - "shadow_camera_perspective_aspect: 1\n", - "shadow_map_type: PCF_SOFT\n" + "ADD AMBIENT LIGHT (from pylab) \n", + "light_color: white\n", + "intensity: 1\n" ] - }, + } + ], + "source": [ + "#light_color - default white\n", + "#intensity - default 1\n", + "ambient1 = p3.ambient_light()" + ] + }, + { + "cell_type": "code", + "execution_count": 99, + "metadata": {}, + "outputs": [], + "source": [ + "ambient1.light_color='red'\n", + "ambient1.intensity=2.0" + ] + }, + { + "cell_type": "code", + "execution_count": 100, + "metadata": {}, + "outputs": [ { "data": { "application/vnd.jupyter.widget-view+json": { - "model_id": "29675463837e4086bc259f6ed9eb8879", + "model_id": "8be096c2afc4435680fd9732532b04a2", "version_major": 2, "version_minor": 0 }, @@ -208,136 +239,270 @@ } ], "source": [ - "#very important to first call p3.clear()\n", - "def add_lights():\n", - " #ambient_light_widget()\n", - "\n", - " #p3.ambient_light(light_color=\"rgb(255,0,0)\", intensity=1.5)\n", - "\n", - " #p3.directional_light(light_color='green', intensity=1.0, position=[100, 100, 100], target=[0,0,0], cast_shadow=True, shadow_bias= -0.005,shadow_camera_orthographic_size=100)\n", - " p3.directional_light(light_color='green', intensity=1.0, position=[-100, 100, -100], target=[0,0,0], cast_shadow=True, shadow_bias= -0.005,shadow_camera_orthographic_size=100,shadow_map_type='PCF_SOFT')\n", - " #p3.hemisphere_light(light_color='red', color2='yellow', intensity=0.5, position=[1, 0, 0])\n", - " #p3.point_light(light_color='orange', intensity=1.0, position=[20, 30, 20], distance=65, decay=0.5, cast_shadow=True,shadow_radius=3,shadow_bias= -0.005)\n", - " p3.spot_light(\n", - " light_color='yellow', \n", - " intensity=1, \n", - " position=[20, 30, 20], \n", - " angle=math.pi/9, \n", - " target = [-20,-20,-20], \n", - " cast_shadow=True,\n", - " distance=0,\n", - " decay=10,\n", - " penumbra=0.5,\n", - " shadow_map_size=1024,\n", - " shadow_bias=-0.0005,\n", - " shadow_radius=5,\n", - " shadow_camera_near=0.5,\n", - " shadow_camera_far=500,\n", - " shadow_camera_perspective_fov=50,\n", - " shadow_camera_perspective_aspect=1,\n", - " shadow_map_type='PCF_SOFT')\n", - " return None\n", - "p3.clear()\n", - "klein_bottle()\n", - "worldplane()\n", - "add_lights()\n", - "p3.show()" + "#####################################################\n", + "#HEMISPHERE LIGHT\n", + "#light_color - default white - upper light\n", + "#light_color2 - default white - bottom light\n", + "#intensity - default 1\n", + "#position_x, position_y, position_z - default [0, 1, 0] \n", + "plot_all(kb=False, wp=True, ts=True)" ] }, { "cell_type": "code", - "execution_count": 3, + "execution_count": 101, "metadata": {}, "outputs": [ { "name": "stdout", "output_type": "stream", "text": [ - "ADD SPOT LIGHT (from pylab) \n", - "light_color: blue\n", - "intensity: 2\n", - "position: 20 30 20\n", - "target: -20 -20 -20\n", - "angle: 0.3490658503988659\n", - "distance: 0\n", - "decay: 1\n", - "penumbra: 1.0\n", - "cast_shadow: True\n", - "shadow_map_size: 1024\n", - "shadow_bias: -0.0005\n", - "shadow_radius: 3\n", - "shadow_camera_near: 0.5\n", - "shadow_camera_far: 500\n", - "shadow_camera_perspective_fov: 50\n", - "shadow_camera_perspective_aspect: 1\n", - "shadow_map_type: PCF_SOFT\n" + "ADD HEMISPHERE LIGHT (from pylab) \n", + "light_color: white\n", + "light_color2: red\n", + "intensity: 1\n", + "position: 0 1 0\n", + "cast_shadow: False\n" ] } ], "source": [ - "#p3.clear()\n", - "light_1 = p3.spot_light(\n", - " light_color='blue', \n", - " intensity=2, \n", - " position=[20, 30, 20], \n", - " angle=math.pi/9, \n", - " target = [-20,-20,-20], \n", - " cast_shadow=True,\n", - " distance=0,\n", - " decay=1,\n", - " penumbra=1.0,\n", - " shadow_map_size=1024,\n", - " shadow_bias=-0.0005,\n", - " shadow_radius=3,\n", - " shadow_camera_near=0.5,\n", - " shadow_camera_far=500,\n", - " shadow_camera_perspective_fov=50,\n", - " shadow_camera_perspective_aspect=1,\n", - " shadow_map_type='PCF_SOFT')" + "hemisphere1 = p3.hemisphere_light()" ] }, { "cell_type": "code", - "execution_count": 19, + "execution_count": 102, "metadata": {}, "outputs": [], "source": [ - "light_1.light_color = 'red'" + "hemisphere1.light_color='orange'\n", + "hemisphere1.light_color2='blue'\n", + "hemisphere1.intensity=1.5\n", + "hemisphere1.position_x = 1\n", + "hemisphere1.position_y = 0\n", + "hemisphere1.position_z = 0" ] }, { "cell_type": "code", - "execution_count": 5, + "execution_count": 103, "metadata": {}, "outputs": [ + { + "data": { + "application/vnd.jupyter.widget-view+json": { + "model_id": "d169f5fefcb54ec59d05614c2003307e", + "version_major": 2, + "version_minor": 0 + }, + "text/plain": [ + "VBox(children=(Figure(camera=PerspectiveCamera(fov=45.0, position=(0.0, 0.0, 2.0), quaternion=(0.0, 0.0, 0.0, …" + ] + }, + "metadata": {}, + "output_type": "display_data" + }, { "name": "stdout", "output_type": "stream", "text": [ - "ADD SPOT LIGHT (from pylab) \n", - "light_color: yellow\n", + "ADD DIRECTIONAL LIGHT (from pylab) \n", + "light_color: white\n", "intensity: 1\n", - "position: 20 30 20\n", - "target: -20 -20 -20\n", - "angle: 0.3490658503988659\n", - "distance: 0\n", - "decay: 10\n", - "penumbra: 0.5\n", - "cast_shadow: True\n", - "shadow_map_size: 1024\n", - "shadow_bias: -0.0005\n", - "shadow_radius: 5\n", - "shadow_camera_near: 0.5\n", - "shadow_camera_far: 500\n", - "shadow_camera_perspective_fov: 50\n", - "shadow_camera_perspective_aspect: 1\n", - "shadow_map_type: PCF_SOFT\n" + "position: 0 1 0\n", + "target: 0 0 0\n", + "cast_shadow: False\n" ] + } + ], + "source": [ + "#####################################################\n", + "#DIRECTIONAL LIGHT\n", + "#light_color - default white\n", + "#intensity - default 1\n", + "#position_x, position_y, position_z - default [0, 1, 0] \n", + "#target_x, target_y, target_z - default [0, 0, 0]\n", + "#cast_shadow - default False\n", + "#shadow_map_size - default 512\n", + "#shadow_bias - default -0.0005\n", + "#shadow_radius - default 1\n", + "#shadow_camera_near - default 0.5\n", + "#shadow_camera_far - default 500\n", + "#shadow_camera_orthographic_size - default 100\n", + "#shadow_map_type - default 'PCF_SOFT' - 'BASIC', 'PCF', 'PCF_SOFT'\n", + "\n", + "plot_all(kb=True, wp=True, ts=False)\n", + "directional1=p3.directional_light()\n" + ] + }, + { + "cell_type": "code", + "execution_count": 104, + "metadata": {}, + "outputs": [], + "source": [ + "directional1.light_color='green'\n", + "directional1.intensity=1.0" + ] + }, + { + "cell_type": "code", + "execution_count": 105, + "metadata": {}, + "outputs": [], + "source": [ + "#light position\n", + "directional1.position_x=30\n", + "directional1.position_y=30\n", + "directional1.position_z=-30\n", + "#look at position\n", + "directional1.target_x=0\n", + "directional1.target_y=-20\n", + "directional1.target_z=-30" + ] + }, + { + "cell_type": "code", + "execution_count": 106, + "metadata": {}, + "outputs": [], + "source": [ + "#if some shadow settings do not update, due to a THREE.JS issue\n", + "#the settings should be updated when setting cast_shadow to False, Run cell, turn back to True and Run cell again\n", + "directional1.cast_shadow=True\n", + "directional1.shadow_map_size=512\n", + "directional1.shadow_map_type='PCF_SOFT'\n", + "#Setting this to values greater than 1 will blur the edges of the shadow.\n", + "#High values will cause unwanted banding effects in the shadows - a greater mapSize will allow for a higher value to be used here before these effects become visible.\n", + "#If shadow_map_type is set to PCF_SOFT, radius has no effect and it is recommended to increase softness by decreasing mapSize instead.\n", + "directional1.radius=10 \n", + "#solves shadow acne issues\n", + "directional1.shadow_bias=-0.005\n", + "directional1.shadow_camera_orthographic_size=100" + ] + }, + { + "cell_type": "code", + "execution_count": 107, + "metadata": {}, + "outputs": [ + { + "data": { + "application/vnd.jupyter.widget-view+json": { + "model_id": "1ffbb452174840ad92412ea13f82c228", + "version_major": 2, + "version_minor": 0 + }, + "text/plain": [ + "VBox(children=(Figure(camera=PerspectiveCamera(fov=45.0, position=(0.0, 0.0, 2.0), quaternion=(0.0, 0.0, 0.0, …" + ] + }, + "metadata": {}, + "output_type": "display_data" }, + { + "name": "stdout", + "output_type": "stream", + "text": [ + "ADD POINT LIGHT (from pylab) \n", + "light_color: white\n", + "intensity: 1\n", + "position: 0 1 0\n", + "distance: 0\n", + "decay: 1\n", + "cast_shadow: False\n" + ] + } + ], + "source": [ + "#####################################################\n", + "#POINT LIGHT\n", + "#light_color - default white\n", + "#intensity - default 1\n", + "#position_x, position_y, position_z - default [0, 1, 0]\n", + "#distance - default 0\n", + "#decay - default 1\n", + "#cast_shadow - default False\n", + "#shadow_map_size - default 512\n", + "#shadow_bias - default -0.0005\n", + "#shadow_radius - default 1\n", + "#shadow_camera_near - default 0.5\n", + "#shadow_camera_far - default 500\n", + "#shadow_map_type - default 'PCF_SOFT' - 'BASIC', 'PCF', 'PCF_SOFT'\n", + "plot_all(kb=True, wp=True, ts=False)\n", + "point1=p3.point_light()\n" + ] + }, + { + "cell_type": "code", + "execution_count": 108, + "metadata": {}, + "outputs": [], + "source": [ + "point1.position_x=20\n", + "point1.position_y=20\n", + "point1.position_z=20" + ] + }, + { + "cell_type": "code", + "execution_count": 109, + "metadata": {}, + "outputs": [], + "source": [ + "point1.distance = 100 # Maximum range of the light. Default is 0 (no limit)." + ] + }, + { + "cell_type": "code", + "execution_count": 110, + "metadata": {}, + "outputs": [], + "source": [ + "# The amount the light dims along the distance of the light. \n", + "# Default is 1. For physically correct lighting, set this to 2.\n", + "point1.decay = 0 " + ] + }, + { + "cell_type": "code", + "execution_count": 111, + "metadata": {}, + "outputs": [], + "source": [ + "point1.light_color='blue'\n", + "point1.intensity=1.5" + ] + }, + { + "cell_type": "code", + "execution_count": 112, + "metadata": {}, + "outputs": [], + "source": [ + "#if some shadow settings do not update, due to a THREE.JS issue\n", + "#the settings should be updated when setting cast_shadow to False, Run cell, turn back to True and Run cell again\n", + "point1.cast_shadow=True\n", + "point1.shadow_map_size=512\n", + "point1.shadow_map_type='PCF'\n", + "#Setting this to values greater than 1 will blur the edges of the shadow.\n", + "#High values will cause unwanted banding effects in the shadows - a greater mapSize will allow for a higher value to be used here before these effects become visible.\n", + "#If shadow_map_type is set to PCF_SOFT, radius has no effect and it is recommended to increase softness by decreasing mapSize instead.\n", + "point1.radius=100 \n", + "#solves shadow acne issues\n", + "point1.shadow_bias=-0.005" + ] + }, + { + "cell_type": "code", + "execution_count": 113, + "metadata": {}, + "outputs": [ { "data": { "application/vnd.jupyter.widget-view+json": { - "model_id": "bae2fb2c60dd46b29de6ce16634928f1", + "model_id": "25adb17047b648fabb2d1c8e49b4d29a", "version_major": 2, "version_minor": 0 }, @@ -347,45 +512,190 @@ }, "metadata": {}, "output_type": "display_data" + }, + { + "name": "stdout", + "output_type": "stream", + "text": [ + "ADD SPOT LIGHT (from pylab) \n", + "light_color: white\n", + "intensity: 1\n", + "position: 0 1 0\n", + "target: 0 0 0\n", + "angle: 1.0471975511965976\n", + "distance: 0\n", + "decay: 1\n", + "penumbra: 0\n", + "cast_shadow: False\n" + ] } ], "source": [ - "def add_lights2():\n", - " #ambient_light_widget()\n", + "#####################################################\n", + "#SPOT LIGHT\n", + "#light_color - default white\n", + "#intensity - default 1\n", + "#position_x, position_y, position_z - default [0, 1, 0]\n", + "#target_x, target_y, target_z - default [0, 0, 0]\n", + "#angle - default math.pi/3\n", + "#distance - default 0\n", + "#decay - default 1\n", + "#penumbra - default 0\n", + "#cast_shadow - default False\n", + "#shadow_map_size - default 512\n", + "#shadow_bias - default -0.0005\n", + "#shadow_radius - default 1\n", + "#shadow_camera_near - default 0.5\n", + "#shadow_camera_far - default 500\n", + "#shadow_camera_perspective_fov - default 50\n", + "#shadow_camera_perspective_aspect - default 1\n", + "#shadow_map_type - default 'PCF_SOFT' - 'BASIC', 'PCF', 'PCF_SOFT'\n", "\n", - " #p3.ambient_light(light_color=\"rgb(255,0,0)\", intensity=1.5)\n", + "plot_all(kb=True, wp=True, ts=False)\n", + "spot1=p3.spot_light()\n" + ] + }, + { + "cell_type": "code", + "execution_count": 114, + "metadata": {}, + "outputs": [], + "source": [ + "spot1.light_color='yellow' \n", + "spot1.intensity=1\n", + "spot1.position_x=20\n", + "spot1.position_y=30\n", + "spot1.position_z=20\n", "\n", - " #p3.directional_light(light_color='green', intensity=1.0, position=[100, 100, 100], target=[0,0,0], cast_shadow=True, shadow_bias= -0.005,shadow_camera_orthographic_size=100)\n", - " #p3.directional_light(light_color='green', intensity=1.0, position=[-100, 100, -100], target=[0,0,0], cast_shadow=True, shadow_bias= -0.005,shadow_camera_orthographic_size=100,shadow_map_type='PCF_SOFT')\n", - " #p3.hemisphere_light(light_color='red', color2='yellow', intensity=0.5, position=[1, 0, 0])\n", - " #p3.point_light(light_color='orange', intensity=1.0, position=[20, 30, 20], distance=65, decay=0.5, cast_shadow=True,shadow_radius=3,shadow_bias= -0.005)\n", - " p3.spot_light(\n", - " light_color='yellow', \n", - " intensity=1, \n", - " position=[20, 30, 20], \n", - " angle=math.pi/9, \n", - " target = [-20,-20,-20], \n", - " cast_shadow=True,\n", - " distance=0,\n", - " decay=10,\n", - " penumbra=0.5,\n", - " shadow_map_size=1024,\n", - " shadow_bias=-0.0005,\n", - " shadow_radius=5,\n", - " shadow_camera_near=0.5,\n", - " shadow_camera_far=500,\n", - " shadow_camera_perspective_fov=50,\n", - " shadow_camera_perspective_aspect=1,\n", - " shadow_map_type='PCF_SOFT')\n", - " return None\n", - "#very important to first call p3.clear()\n", - "p3.clear()\n", - "test_surface2()\n", - "worldplane()\n", - "add_lights2()\n", - "p3.show()" + "spot1.target_x=-20\n", + "spot1.target_y=-20\n", + "spot1.target_z=-20\n", + "\n", + "spot1.angle=math.pi/9" + ] + }, + { + "cell_type": "code", + "execution_count": 115, + "metadata": {}, + "outputs": [], + "source": [ + "#Default mode — When distance is zero, light does not attenuate. \n", + "#When distance is non-zero, light will attenuate linearly from maximum intensity at the \n", + "#light's position down to zero at this distance from the light.\n", + "\n", + "#Physically correct mode — When distance is zero, light will attenuate according to inverse-square law to infinite distance. \n", + "#When distance is non-zero, light will attenuate according to inverse-square law until near the distance cutoff, \n", + "#where it will then attenuate quickly and smoothly to 0. Inherently, cutoffs are not physically correct.\n", + "#Default is 0.0.\n", + "\n", + "spot1.distance=200" + ] + }, + { + "cell_type": "code", + "execution_count": 92, + "metadata": {}, + "outputs": [], + "source": [ + "#The amount the light dims along the distance of the light.\n", + "#In physically correct mode, decay = 2 leads to physically realistic light falloff. The default is 1.\n", + "#spot1.decay=0" + ] + }, + { + "cell_type": "code", + "execution_count": 117, + "metadata": {}, + "outputs": [], + "source": [ + "# Percent of the spotlight cone that is attenuated due to penumbra. \n", + "# Takes values between zero and 1. The default is 0.0.\n", + "spot1.penumbra=0.5" + ] + }, + { + "cell_type": "code", + "execution_count": 118, + "metadata": {}, + "outputs": [], + "source": [ + "#if some shadow settings do not update, due to a THREE.JS issue\n", + "#the settings should be updated when setting cast_shadow to False, Run cell, turn back to True and Run cell again\n", + "spot1.cast_shadow=True\n", + "spot1.shadow_map_size=1024\n", + "spot1.shadow_map_type='PCF_SOFT'\n", + "\n", + "spot1.shadow_camera_perspective_fov=50\n", + "#change the shadow camera aspect\n", + "shadow_camera_perspective_aspect=1\n", + "\n", + "#Setting this to values greater than 1 will blur the edges of the shadow.\n", + "#High values will cause unwanted banding effects in the shadows - a greater mapSize will allow for a higher value to be used here before these effects become visible.\n", + "#If shadow_map_type is set to PCF_SOFT, radius has no effect and it is recommended to increase softness by decreasing mapSize instead.\n", + "spot1.radius=5 \n", + "#solves shadow acne issues\n", + "spot1.shadow_bias=-0.0005" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [] + }, + { + "cell_type": "code", + "execution_count": 9, + "metadata": {}, + "outputs": [], + "source": [] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [ + "\n" ] }, + { + "cell_type": "code", + "execution_count": 26, + "metadata": {}, + "outputs": [], + "source": [] + }, + { + "cell_type": "code", + "execution_count": 25, + "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": [] + }, { "cell_type": "code", "execution_count": null, From 56657e7b579b96dd797dcc3db15b7c6c5d323dfa Mon Sep 17 00:00:00 2001 From: rinftech-github Date: Tue, 31 Mar 2020 13:14:28 +0300 Subject: [PATCH 024/130] Fix notebook figure autoupdate. Hardcode lambert and phong diffuse color to white --- js/glsl/mesh-fragment-lambert.glsl | 4 ++-- js/glsl/mesh-fragment-phong.glsl | 4 ++-- js/src/light.ts | 3 ++- 3 files changed, 6 insertions(+), 5 deletions(-) diff --git a/js/glsl/mesh-fragment-lambert.glsl b/js/glsl/mesh-fragment-lambert.glsl index 81d5d85d..fe466685 100644 --- a/js/glsl/mesh-fragment-lambert.glsl +++ b/js/glsl/mesh-fragment-lambert.glsl @@ -74,13 +74,13 @@ void main() #include - vec4 diffuseColor = vec4( diffuse, opacity ); + vec4 diffuseColor = vec4( vec3(1,1,1), opacity ); ReflectedLight reflectedLight = ReflectedLight( vec3( 0.0 ), vec3( 0.0 ), vec3( 0.0 ), vec3( 0.0 ) ); vec3 totalEmissiveRadiance = emissive; #include #include - #include + //#include #include #include #include diff --git a/js/glsl/mesh-fragment-phong.glsl b/js/glsl/mesh-fragment-phong.glsl index 9465376a..dfb24674 100644 --- a/js/glsl/mesh-fragment-phong.glsl +++ b/js/glsl/mesh-fragment-phong.glsl @@ -73,13 +73,13 @@ void main() #include - vec4 diffuseColor = vec4( diffuse, opacity );//0.75 * finalColor2 + 0.25 * vec4( diffuse, opacity ); + vec4 diffuseColor = vec4( vec3(1,1,1), opacity );//0.75 * finalColor2 + 0.25 * vec4( diffuse, opacity ); ReflectedLight reflectedLight = ReflectedLight( vec3( 0.0 ), vec3( 0.0 ), vec3( 0.0 ), vec3( 0.0 ) ); vec3 totalEmissiveRadiance = emissive * emissiveIntensity; #include #include - #include + //#include #include #include #include diff --git a/js/src/light.ts b/js/src/light.ts index d1d183ac..d1af565e 100644 --- a/js/src/light.ts +++ b/js/src/light.ts @@ -71,8 +71,9 @@ class LightView extends widgets.WidgetView { this.cast_shadow = this.model.get("cast_shadow"); this.renderer.renderer.shadowMap.enabled = this.cast_shadow; - console.log("CHANGE " + this.model.get("shadow_camera_orthographic_size")); + console.log("CHANGE "); this.create_light(false); + this.renderer.update(); } From b7da181061fbfe38b268088b54c1a6b7d7059e5c Mon Sep 17 00:00:00 2001 From: rinftech-github Date: Tue, 31 Mar 2020 14:06:27 +0300 Subject: [PATCH 025/130] Prepare notebook for plot_mesh material properties change --- js/src/mesh.ts | 6 ++ notebooks/lighting_demo.ipynb | 148 ++++++++++++++++++++++------------ 2 files changed, 101 insertions(+), 53 deletions(-) diff --git a/js/src/mesh.ts b/js/src/mesh.ts index 5e3c1592..af09adbc 100644 --- a/js/src/mesh.ts +++ b/js/src/mesh.ts @@ -138,6 +138,12 @@ class MeshView extends widgets.WidgetView { this.model.on("change:geo change:connected", this.update_, this); this.model.on("change:texture", this._load_textures, this); this.model.on("change:visible", this.update_visibility, this); + this.model.on("change:lighting_model change:opacity change:specular_color change:shininess change:emissive_color change:emissive_intensity change:roughness change:metalness change:cast_shadow change:receive_shadow", + this.update_lighting, this); + } + + public update_lighting() { + console.log("CHANGE MESH MATERIAL PARAMS"); } public update_visibility() { diff --git a/notebooks/lighting_demo.ipynb b/notebooks/lighting_demo.ipynb index 17173c71..8acc618d 100644 --- a/notebooks/lighting_demo.ipynb +++ b/notebooks/lighting_demo.ipynb @@ -2,7 +2,7 @@ "cells": [ { "cell_type": "code", - "execution_count": 96, + "execution_count": 19, "metadata": {}, "outputs": [], "source": [ @@ -31,29 +31,22 @@ " import scipy.special\n", "except:\n", " pass # it's ok, it's not crucial\n", - "# __all__ = [\"example_ylm\"]\n", "\n", - "material_lighting_model='PHYSICAL'\n", - "material_opacity=1\n", - "material_specular_color='white'\n", - "material_shininess=10\n", - "material_emissive_color='black'\n", - "material_emissive_intensity=1.0\n", - "material_roughness=0.1\n", - "material_metalness=0.1\n", - "material_cast_shadow=True\n", - "material_receive_shadow=True\n", - " \n", "def klein_bottle(\n", - " draw=True,\n", - " show=True,\n", - " figure8=False,\n", " endpoint=True,\n", - " uv=True,\n", " wireframe=False,\n", " texture=None,\n", - " both=False,\n", " interval=1000,\n", + " material_lighting_model='DEFAULT',\n", + " material_opacity=1,\n", + " material_specular_color='white',\n", + " material_shininess=1,\n", + " material_emissive_color='black',\n", + " material_emissive_intensity=1.0,\n", + " material_roughness=0.0,\n", + " material_metalness=0.0,\n", + " material_cast_shadow=False,\n", + " material_receive_shadow=False\n", "):\n", " # http://paulbourke.net/geometry/klein/\n", " u = np.linspace(0, 2 * pi, num=50, endpoint=endpoint)\n", @@ -87,7 +80,17 @@ " )\n", "\n", " return mesh\n", - "def worldplane():\n", + "def worldplane(material_lighting_model='DEFAULT',\n", + " material_opacity=1,\n", + " material_specular_color='white',\n", + " material_shininess=1,\n", + " material_emissive_color='black',\n", + " material_emissive_intensity=1.0,\n", + " material_roughness=0.0,\n", + " material_metalness=0.0,\n", + " material_cast_shadow=False,\n", + " material_receive_shadow=False,\n", + "):\n", " k = 20\n", " h = -15\n", " tx = np.array([k, -k, -k, k])\n", @@ -112,14 +115,24 @@ " cast_shadow=material_cast_shadow,\n", " receive_shadow=material_receive_shadow)\n", " \n", - "def test_surface():\n", + "def test_surface(material_lighting_model='DEFAULT',\n", + " material_opacity=1,\n", + " material_specular_color='white',\n", + " material_shininess=1,\n", + " material_emissive_color='black',\n", + " material_emissive_intensity=1.0,\n", + " material_roughness=0.0,\n", + " material_metalness=0.0,\n", + " material_cast_shadow=False,\n", + " material_receive_shadow=False\n", + "):\n", " X = np.arange(-10, 10, 0.25*1)-10\n", " Y = np.arange(-10, 10, 0.25*1)\n", " X, Y = np.meshgrid(X, Y)\n", " R = np.sqrt(X**2 + Y**2)\n", " Z = np.sin(R)\n", "\n", - " p3.plot_surface(\n", + " surf = p3.plot_surface(\n", " X+10, \n", " Z-10, \n", " Y+5, \n", @@ -134,6 +147,7 @@ " metalness=material_metalness,\n", " cast_shadow=material_cast_shadow,\n", " receive_shadow=material_receive_shadow,)\n", + " return surf\n", "\n", " \n", "def ambient_light_widget():\n", @@ -150,26 +164,54 @@ " p3.clear()\n", " p3.figure()\n", " if kb:\n", - " klein_bottle()\n", + " klein_bottle(material_lighting_model='PHYSICAL', \n", + " material_shininess=10, \n", + " material_roughness=0.1, \n", + " material_metalness=0.1,\n", + " material_cast_shadow=True,\n", + " material_receive_shadow=True)\n", " if wp:\n", - " worldplane()\n", + " worldplane(material_lighting_model='PHYSICAL', \n", + " material_shininess=10, \n", + " material_roughness=0.1, \n", + " material_metalness=0.1,\n", + " material_cast_shadow=True,\n", + " material_receive_shadow=True)\n", " if ts:\n", - " test_surface()\n", + " test_surface(material_lighting_model='PHYSICAL', \n", + " material_shininess=10, \n", + " material_roughness=0.1, \n", + " material_metalness=0.1,\n", + " material_cast_shadow=True,\n", + " material_receive_shadow=True)\n", " p3.show()\n", "\n", - "\n", - "\n" + "def plot_all_no_change(kb=False, wp=True, ts=False):\n", + " k=None\n", + " w=None\n", + " s=None\n", + " p3.clear()\n", + " p3.figure()\n", + " if kb:\n", + " k=klein_bottle()\n", + " if wp:\n", + " w=worldplane()\n", + " if ts:\n", + " s=test_surface()\n", + " p3.show()\n", + " \n", + " return [k,w,s] " ] }, { "cell_type": "code", - "execution_count": 97, + "execution_count": 20, "metadata": {}, "outputs": [ { "data": { "application/vnd.jupyter.widget-view+json": { - "model_id": "fd7b3e90b0dc4cadb1016415475c602c", + "model_id": "d840bbbf31a64c4faec636b9575b1470", "version_major": 2, "version_minor": 0 }, @@ -189,7 +231,7 @@ }, { "cell_type": "code", - "execution_count": 98, + "execution_count": 21, "metadata": {}, "outputs": [ { @@ -210,7 +252,7 @@ }, { "cell_type": "code", - "execution_count": 99, + "execution_count": 22, "metadata": {}, "outputs": [], "source": [ @@ -220,13 +262,13 @@ }, { "cell_type": "code", - "execution_count": 100, + "execution_count": 23, "metadata": {}, "outputs": [ { "data": { "application/vnd.jupyter.widget-view+json": { - "model_id": "8be096c2afc4435680fd9732532b04a2", + "model_id": "e996c9c989874bcf966fd3760f33195a", "version_major": 2, "version_minor": 0 }, @@ -250,7 +292,7 @@ }, { "cell_type": "code", - "execution_count": 101, + "execution_count": 24, "metadata": {}, "outputs": [ { @@ -272,7 +314,7 @@ }, { "cell_type": "code", - "execution_count": 102, + "execution_count": 25, "metadata": {}, "outputs": [], "source": [ @@ -286,13 +328,13 @@ }, { "cell_type": "code", - "execution_count": 103, + "execution_count": 26, "metadata": {}, "outputs": [ { "data": { "application/vnd.jupyter.widget-view+json": { - "model_id": "d169f5fefcb54ec59d05614c2003307e", + "model_id": "c3364f1119eb4d05869c472f13884f4f", "version_major": 2, "version_minor": 0 }, @@ -338,7 +380,7 @@ }, { "cell_type": "code", - "execution_count": 104, + "execution_count": 27, "metadata": {}, "outputs": [], "source": [ @@ -348,7 +390,7 @@ }, { "cell_type": "code", - "execution_count": 105, + "execution_count": 28, "metadata": {}, "outputs": [], "source": [ @@ -364,7 +406,7 @@ }, { "cell_type": "code", - "execution_count": 106, + "execution_count": 29, "metadata": {}, "outputs": [], "source": [ @@ -384,13 +426,13 @@ }, { "cell_type": "code", - "execution_count": 107, + "execution_count": 30, "metadata": {}, "outputs": [ { "data": { "application/vnd.jupyter.widget-view+json": { - "model_id": "1ffbb452174840ad92412ea13f82c228", + "model_id": "c5e4d42e88904659ae8eefa625c161b6", "version_major": 2, "version_minor": 0 }, @@ -436,7 +478,7 @@ }, { "cell_type": "code", - "execution_count": 108, + "execution_count": 31, "metadata": {}, "outputs": [], "source": [ @@ -447,7 +489,7 @@ }, { "cell_type": "code", - "execution_count": 109, + "execution_count": 32, "metadata": {}, "outputs": [], "source": [ @@ -456,7 +498,7 @@ }, { "cell_type": "code", - "execution_count": 110, + "execution_count": 33, "metadata": {}, "outputs": [], "source": [ @@ -467,7 +509,7 @@ }, { "cell_type": "code", - "execution_count": 111, + "execution_count": 34, "metadata": {}, "outputs": [], "source": [ @@ -477,7 +519,7 @@ }, { "cell_type": "code", - "execution_count": 112, + "execution_count": 35, "metadata": {}, "outputs": [], "source": [ @@ -496,13 +538,13 @@ }, { "cell_type": "code", - "execution_count": 113, + "execution_count": 36, "metadata": {}, "outputs": [ { "data": { "application/vnd.jupyter.widget-view+json": { - "model_id": "25adb17047b648fabb2d1c8e49b4d29a", + "model_id": "28d93ae73c6c494284bcd396fdd871e8", "version_major": 2, "version_minor": 0 }, @@ -557,7 +599,7 @@ }, { "cell_type": "code", - "execution_count": 114, + "execution_count": 37, "metadata": {}, "outputs": [], "source": [ @@ -576,7 +618,7 @@ }, { "cell_type": "code", - "execution_count": 115, + "execution_count": 38, "metadata": {}, "outputs": [], "source": [ @@ -594,7 +636,7 @@ }, { "cell_type": "code", - "execution_count": 92, + "execution_count": 39, "metadata": {}, "outputs": [], "source": [ @@ -605,7 +647,7 @@ }, { "cell_type": "code", - "execution_count": 117, + "execution_count": 40, "metadata": {}, "outputs": [], "source": [ @@ -616,7 +658,7 @@ }, { "cell_type": "code", - "execution_count": 118, + "execution_count": 41, "metadata": {}, "outputs": [], "source": [ From b77d204d35096848646a2cf306f8ee0b393edd07 Mon Sep 17 00:00:00 2001 From: rinftech-github Date: Tue, 31 Mar 2020 16:35:35 +0300 Subject: [PATCH 026/130] Switch lighting model automatically to PHYSICAL when adding a new light --- js/src/light.ts | 7 ++ js/src/mesh.ts | 11 +- notebooks/lighting_demo.ipynb | 226 +++++++++++++++++++++++++++------- 3 files changed, 196 insertions(+), 48 deletions(-) diff --git a/js/src/light.ts b/js/src/light.ts index d1af565e..ea62964c 100644 --- a/js/src/light.ts +++ b/js/src/light.ts @@ -93,6 +93,13 @@ class LightView extends widgets.WidgetView { } create_light(instantiate=true) { + + //force meshes light model update + for (let mesh_key in this.renderer.mesh_views) { + console.log(mesh_key, this.renderer.mesh_views[mesh_key]); + this.renderer.mesh_views[mesh_key].force_lighting_model(); + } + this.lights = []; this.light_color = this.model.get("light_color"); diff --git a/js/src/mesh.ts b/js/src/mesh.ts index af09adbc..fae307da 100644 --- a/js/src/mesh.ts +++ b/js/src/mesh.ts @@ -139,11 +139,16 @@ class MeshView extends widgets.WidgetView { this.model.on("change:texture", this._load_textures, this); this.model.on("change:visible", this.update_visibility, this); this.model.on("change:lighting_model change:opacity change:specular_color change:shininess change:emissive_color change:emissive_intensity change:roughness change:metalness change:cast_shadow change:receive_shadow", - this.update_lighting, this); + this.update_visibility, this); } - public update_lighting() { - console.log("CHANGE MESH MATERIAL PARAMS"); + public force_lighting_model() { + console.log("FORCE LIGHTING MODEL TO PHYSICAL"); + if(this.lighting_model === this.LIGHTING_MODELS.DEFAULT){ + this.model.set("lighting_model", this.LIGHTING_MODELS.PHYSICAL); + //this.lighting_model = this.LIGHTING_MODELS.PHYSICAL; + this.update_visibility(); + } } public update_visibility() { diff --git a/notebooks/lighting_demo.ipynb b/notebooks/lighting_demo.ipynb index 8acc618d..f5e82184 100644 --- a/notebooks/lighting_demo.ipynb +++ b/notebooks/lighting_demo.ipynb @@ -2,7 +2,7 @@ "cells": [ { "cell_type": "code", - "execution_count": 19, + "execution_count": 6, "metadata": {}, "outputs": [], "source": [ @@ -98,12 +98,12 @@ " ty = np.array([h, h, h, h])\n", " \n", " tri = [(0, 1, 2), (0, 2, 3)]\n", - " p3.plot_trisurf(\n", + " p = p3.plot_trisurf(\n", " tx, \n", " ty,\n", " tz, \n", " triangles=tri, \n", - " #color='blue', \n", + " color='red', \n", " lighting_model=material_lighting_model,\n", " opacity=material_opacity,\n", " specular_color=material_specular_color,\n", @@ -114,6 +114,7 @@ " metalness=material_metalness,\n", " cast_shadow=material_cast_shadow,\n", " receive_shadow=material_receive_shadow)\n", + " return p\n", " \n", "def test_surface(material_lighting_model='DEFAULT',\n", " material_opacity=1,\n", @@ -136,7 +137,7 @@ " X+10, \n", " Z-10, \n", " Y+5, \n", - " color=\"orange\",\n", + " color=\"red\",\n", " lighting_model=material_lighting_model,\n", " opacity=material_opacity,\n", " specular_color=material_specular_color,\n", @@ -149,17 +150,6 @@ " receive_shadow=material_receive_shadow,)\n", " return surf\n", "\n", - " \n", - "def ambient_light_widget():\n", - " selected_size = FloatSlider(value=5, min=0, max=10, step=1, description='Intensity:', disabled=False,\n", - " continuous_update=True,orientation='horizontal', readout=True, readout_format='d')\n", - " selected_color = ColorPicker(concise=False, description='Ambient Color:', value='red', disabled=False)\n", - " display(selected_size,selected_color)\n", - " #widgets.interact(p3.ambient_light,light_color=selected_color,intensity=selected_size);\n", - " print(selected_size.value)\n", - " return selected_size\n", - "\n", - "\n", "def plot_all(kb=False, wp=True, ts=False):\n", " p3.clear()\n", " p3.figure()\n", @@ -200,18 +190,164 @@ " s=test_surface()\n", " p3.show()\n", " \n", - " return [k,w,s] " + " return [k,w,s] \n", + "\n", + "def lights_dir_spot():\n", + " dir1=p3.directional_light(light_color='green', \n", + " intensity=1.0,\n", + " position=[30,30,30],\n", + " target=[0,20,30],\n", + " cast_shadow=True,\n", + " shadow_map_size=512,\n", + " shadow_map_type='PCF_SOFT',\n", + " shadow_bias=-0.005)\n", + " sp1=p3.spot_light(light_color='yellow',\n", + " intensity=1,\n", + " position=[20,30,20],\n", + " target=[-20,-20,-20],\n", + " angle=math.pi/9,\n", + " distance=200,\n", + " penumbra=0.5,\n", + " cast_shadow=True,\n", + " shadow_map_size=1024,\n", + " shadow_map_type='PCF_SOFT',\n", + " shadow_camera_perspective_fov=50,\n", + " shadow_camera_perspective_aspect=1,\n", + " shadow_bias=-0.0005)" ] }, { "cell_type": "code", - "execution_count": 20, + "execution_count": 7, "metadata": {}, "outputs": [ { "data": { "application/vnd.jupyter.widget-view+json": { - "model_id": "d840bbbf31a64c4faec636b9575b1470", + "model_id": "f22c5bd12008424e840a7ca45abe7861", + "version_major": 2, + "version_minor": 0 + }, + "text/plain": [ + "VBox(children=(Figure(camera=PerspectiveCamera(fov=45.0, position=(0.0, 0.0, 2.0), quaternion=(0.0, 0.0, 0.0, …" + ] + }, + "metadata": {}, + "output_type": "display_data" + } + ], + "source": [ + "[bottle, plane, surf] = plot_all_no_change(kb=False, wp=True, ts=True)" + ] + }, + { + "cell_type": "code", + "execution_count": 8, + "metadata": {}, + "outputs": [], + "source": [ + "#no need to change the lighting model manually\n", + "#whenever a light is added to the figure, the lighting model of the meshes will\n", + "#be updated to 'PHYSICAL'\n", + "#surf.lighting_model = 'PHONG'\n", + "#plane.lighting_model = 'PHONG'" + ] + }, + { + "cell_type": "code", + "execution_count": 9, + "metadata": {}, + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "ADD DIRECTIONAL LIGHT (from pylab) \n", + "light_color: green\n", + "intensity: 1.0\n", + "position: 30 30 30\n", + "target: 0 20 30\n", + "cast_shadow: True\n", + "shadow_map_size: 512\n", + "shadow_bias: -0.005\n", + "shadow_radius: 1\n", + "shadow_camera_near: 0.5\n", + "shadow_camera_far: 500\n", + "shadow_map_type: PCF_SOFT\n", + "ADD SPOT LIGHT (from pylab) \n", + "light_color: yellow\n", + "intensity: 1\n", + "position: 20 30 20\n", + "target: -20 -20 -20\n", + "angle: 0.3490658503988659\n", + "distance: 200\n", + "decay: 1\n", + "penumbra: 0.5\n", + "cast_shadow: True\n", + "shadow_map_size: 1024\n", + "shadow_bias: -0.0005\n", + "shadow_radius: 1\n", + "shadow_camera_near: 0.5\n", + "shadow_camera_far: 500\n", + "shadow_camera_perspective_fov: 50\n", + "shadow_camera_perspective_aspect: 1\n", + "shadow_map_type: PCF_SOFT\n" + ] + } + ], + "source": [ + "lights_dir_spot()" + ] + }, + { + "cell_type": "code", + "execution_count": 10, + "metadata": {}, + "outputs": [], + "source": [ + "surf.emissive_color='white'" + ] + }, + { + "cell_type": "code", + "execution_count": 11, + "metadata": {}, + "outputs": [], + "source": [ + "surf.emissive_intensity=0.2" + ] + }, + { + "cell_type": "code", + "execution_count": 119, + "metadata": {}, + "outputs": [], + "source": [ + "plane.emissive_color='blue'\n", + "surf.emissive_intensity=0.3;" + ] + }, + { + "cell_type": "code", + "execution_count": 61, + "metadata": {}, + "outputs": [], + "source": [ + "surf.roughness=0.7\n", + "plane.roughness=0.5\n", + "surf.metalness=200\n", + "plane.metalness=200" + ] + }, + { + "cell_type": "code", + "execution_count": 90, + "metadata": {}, + "outputs": [ + { + "data": { + "application/vnd.jupyter.widget-view+json": { + "model_id": "8fa07120df9a4daaaeebd6b7926b363b", "version_major": 2, "version_minor": 0 }, @@ -231,7 +367,7 @@ }, { "cell_type": "code", - "execution_count": 21, + "execution_count": 91, "metadata": {}, "outputs": [ { @@ -252,7 +388,7 @@ }, { "cell_type": "code", - "execution_count": 22, + "execution_count": 94, "metadata": {}, "outputs": [], "source": [ @@ -262,13 +398,13 @@ }, { "cell_type": "code", - "execution_count": 23, + "execution_count": 95, "metadata": {}, "outputs": [ { "data": { "application/vnd.jupyter.widget-view+json": { - "model_id": "e996c9c989874bcf966fd3760f33195a", + "model_id": "3b0151d05890413f96faaeabd2af9c5a", "version_major": 2, "version_minor": 0 }, @@ -292,7 +428,7 @@ }, { "cell_type": "code", - "execution_count": 24, + "execution_count": 96, "metadata": {}, "outputs": [ { @@ -314,27 +450,27 @@ }, { "cell_type": "code", - "execution_count": 25, + "execution_count": 98, "metadata": {}, "outputs": [], "source": [ "hemisphere1.light_color='orange'\n", "hemisphere1.light_color2='blue'\n", "hemisphere1.intensity=1.5\n", - "hemisphere1.position_x = 1\n", - "hemisphere1.position_y = 0\n", + "hemisphere1.position_x = 0\n", + "hemisphere1.position_y = 1\n", "hemisphere1.position_z = 0" ] }, { "cell_type": "code", - "execution_count": 26, + "execution_count": 99, "metadata": {}, "outputs": [ { "data": { "application/vnd.jupyter.widget-view+json": { - "model_id": "c3364f1119eb4d05869c472f13884f4f", + "model_id": "6db6520ad92a43048910f825ffee08d4", "version_major": 2, "version_minor": 0 }, @@ -380,7 +516,7 @@ }, { "cell_type": "code", - "execution_count": 27, + "execution_count": 100, "metadata": {}, "outputs": [], "source": [ @@ -390,7 +526,7 @@ }, { "cell_type": "code", - "execution_count": 28, + "execution_count": 101, "metadata": {}, "outputs": [], "source": [ @@ -406,7 +542,7 @@ }, { "cell_type": "code", - "execution_count": 29, + "execution_count": 102, "metadata": {}, "outputs": [], "source": [ @@ -426,13 +562,13 @@ }, { "cell_type": "code", - "execution_count": 30, + "execution_count": 103, "metadata": {}, "outputs": [ { "data": { "application/vnd.jupyter.widget-view+json": { - "model_id": "c5e4d42e88904659ae8eefa625c161b6", + "model_id": "d7dc2ad131fa4c72b8fdfa784fd52d0b", "version_major": 2, "version_minor": 0 }, @@ -478,7 +614,7 @@ }, { "cell_type": "code", - "execution_count": 31, + "execution_count": 104, "metadata": {}, "outputs": [], "source": [ @@ -489,7 +625,7 @@ }, { "cell_type": "code", - "execution_count": 32, + "execution_count": 105, "metadata": {}, "outputs": [], "source": [ @@ -498,7 +634,7 @@ }, { "cell_type": "code", - "execution_count": 33, + "execution_count": 106, "metadata": {}, "outputs": [], "source": [ @@ -509,7 +645,7 @@ }, { "cell_type": "code", - "execution_count": 34, + "execution_count": 107, "metadata": {}, "outputs": [], "source": [ @@ -519,7 +655,7 @@ }, { "cell_type": "code", - "execution_count": 35, + "execution_count": 108, "metadata": {}, "outputs": [], "source": [ @@ -538,13 +674,13 @@ }, { "cell_type": "code", - "execution_count": 36, + "execution_count": 109, "metadata": {}, "outputs": [ { "data": { "application/vnd.jupyter.widget-view+json": { - "model_id": "28d93ae73c6c494284bcd396fdd871e8", + "model_id": "7cd55adc22be4eb1963cae5b6b3f1d69", "version_major": 2, "version_minor": 0 }, @@ -599,7 +735,7 @@ }, { "cell_type": "code", - "execution_count": 37, + "execution_count": 110, "metadata": {}, "outputs": [], "source": [ @@ -618,7 +754,7 @@ }, { "cell_type": "code", - "execution_count": 38, + "execution_count": 111, "metadata": {}, "outputs": [], "source": [ @@ -636,7 +772,7 @@ }, { "cell_type": "code", - "execution_count": 39, + "execution_count": 82, "metadata": {}, "outputs": [], "source": [ @@ -647,7 +783,7 @@ }, { "cell_type": "code", - "execution_count": 40, + "execution_count": 112, "metadata": {}, "outputs": [], "source": [ @@ -658,7 +794,7 @@ }, { "cell_type": "code", - "execution_count": 41, + "execution_count": 113, "metadata": {}, "outputs": [], "source": [ From 66bc1de454b54b01d7859f8083ab353564886017 Mon Sep 17 00:00:00 2001 From: rinftech-github Date: Tue, 31 Mar 2020 19:17:30 +0300 Subject: [PATCH 027/130] Complete mesh and lighting demo notebook. Fix Lambert emissive intensity. Update mesh transparency and shadows --- js/glsl/mesh-fragment-lambert.glsl | 3 +- js/src/mesh.ts | 14 +- notebooks/lighting_demo.ipynb | 436 +++++++++++++---------------- 3 files changed, 206 insertions(+), 247 deletions(-) diff --git a/js/glsl/mesh-fragment-lambert.glsl b/js/glsl/mesh-fragment-lambert.glsl index fe466685..2c9a4ae8 100644 --- a/js/glsl/mesh-fragment-lambert.glsl +++ b/js/glsl/mesh-fragment-lambert.glsl @@ -3,6 +3,7 @@ uniform vec3 diffuse; uniform vec3 emissive; +uniform float emissiveIntensity; uniform float opacity; varying vec3 vLightFront; @@ -76,7 +77,7 @@ void main() vec4 diffuseColor = vec4( vec3(1,1,1), opacity ); ReflectedLight reflectedLight = ReflectedLight( vec3( 0.0 ), vec3( 0.0 ), vec3( 0.0 ), vec3( 0.0 ) ); - vec3 totalEmissiveRadiance = emissive; + vec3 totalEmissiveRadiance = emissive * emissiveIntensity; #include #include diff --git a/js/src/mesh.ts b/js/src/mesh.ts index fae307da..12ace889 100644 --- a/js/src/mesh.ts +++ b/js/src/mesh.ts @@ -202,6 +202,15 @@ class MeshView extends widgets.WidgetView { }); } + update_shadow() { + this.cast_shadow = this.model.get("cast_shadow"); + this.receive_shadow = this.model.get("receive_shadow"); + this.meshes.forEach((mesh) => { + mesh.castShadow = this.cast_shadow; + mesh.receiveShadow = this.receive_shadow; + }); + } + remove_from_scene() { this.meshes.forEach((mesh) => { this.renderer.scene_scatter.remove(mesh); @@ -359,7 +368,8 @@ class MeshView extends widgets.WidgetView { material.fragmentShader = require("raw-loader!../glsl/mesh-fragment-physical.glsl"); } material.depthWrite = true; - material.transparant = true; + material.transparant = true;//? + material.transparent = true; material.depthTest = true; // very important material.lights = true; @@ -383,6 +393,8 @@ class MeshView extends widgets.WidgetView { this.material.uniforms.roughness.value = this.roughness; this.material.uniforms.metalness.value = this.metalness; + this.update_shadow(); + const texture = this.model.get("texture"); if (texture && this.textures) { this.material.defines.USE_TEXTURE = true; diff --git a/notebooks/lighting_demo.ipynb b/notebooks/lighting_demo.ipynb index f5e82184..c8968bc9 100644 --- a/notebooks/lighting_demo.ipynb +++ b/notebooks/lighting_demo.ipynb @@ -2,24 +2,15 @@ "cells": [ { "cell_type": "code", - "execution_count": 6, + "execution_count": 24, "metadata": {}, "outputs": [], "source": [ - "\"\"\"Some examples for quick testing/demonstrations.\n", - "\n", - "All function accept `show` and `draw` arguments\n", - "\n", - " * If `draw` is `True` it will return the widgets (Scatter, Volume, Mesh)\n", - " * If `draw` is `False`, it will return the data\n", - " * if `show` is `False`, `ipv.show()` will not be called.\n", - "\"\"\"\n", "#import sys\n", "#sys.path.append(\"..\")\n", "import warnings\n", "import numpy as np\n", "from numpy import cos, sin, pi\n", - "#import ipyvolume.pylab as p3\n", "from ipyvolume import pylab as p3\n", "import math\n", "from ipywidgets import widgets\n", @@ -30,7 +21,7 @@ " import scipy.ndimage\n", " import scipy.special\n", "except:\n", - " pass # it's ok, it's not crucial\n", + " pass\n", "\n", "def klein_bottle(\n", " endpoint=True,\n", @@ -218,13 +209,13 @@ }, { "cell_type": "code", - "execution_count": 7, + "execution_count": 73, "metadata": {}, "outputs": [ { "data": { "application/vnd.jupyter.widget-view+json": { - "model_id": "f22c5bd12008424e840a7ca45abe7861", + "model_id": "1cb966ccf840442899a2678ec76e64b8", "version_major": 2, "version_minor": 0 }, @@ -237,25 +228,26 @@ } ], "source": [ + "# Plot a plane and a sinusoidal surface\n", "[bottle, plane, surf] = plot_all_no_change(kb=False, wp=True, ts=True)" ] }, { "cell_type": "code", - "execution_count": 8, + "execution_count": 74, "metadata": {}, "outputs": [], "source": [ - "#no need to change the lighting model manually\n", - "#whenever a light is added to the figure, the lighting model of the meshes will\n", - "#be updated to 'PHYSICAL'\n", - "#surf.lighting_model = 'PHONG'\n", - "#plane.lighting_model = 'PHONG'" + "# No need to change the lighting model manually\n", + "# whenever a light is added to the figure, the lighting model of the meshes will\n", + "# be updated to 'PHYSICAL'\n", + "#surf.lighting_model = 'PHYSICAL'\n", + "#plane.lighting_model = 'PHYSICAL'" ] }, { "cell_type": "code", - "execution_count": 9, + "execution_count": 75, "metadata": {}, "outputs": [ { @@ -296,21 +288,23 @@ } ], "source": [ + "# Add a directional light and a spot light\n", "lights_dir_spot()" ] }, { "cell_type": "code", - "execution_count": 10, + "execution_count": 76, "metadata": {}, "outputs": [], "source": [ + "# Emissive (light) color of the material, essentially a solid color unaffected by other lighting. Default is black.\n", "surf.emissive_color='white'" ] }, { "cell_type": "code", - "execution_count": 11, + "execution_count": 77, "metadata": {}, "outputs": [], "source": [ @@ -319,35 +313,91 @@ }, { "cell_type": "code", - "execution_count": 119, + "execution_count": 78, "metadata": {}, "outputs": [], "source": [ - "plane.emissive_color='blue'\n", - "surf.emissive_intensity=0.3;" + "plane.emissive_color='orange'\n", + "surf.emissive_intensity=0.1;" ] }, { "cell_type": "code", - "execution_count": 61, + "execution_count": 79, "metadata": {}, "outputs": [], "source": [ - "surf.roughness=0.7\n", - "plane.roughness=0.5\n", - "surf.metalness=200\n", - "plane.metalness=200" + "# roughness - How rough the material appears. 0.0 means a smooth mirror reflection, 1.0 means fully diffuse. \n", + "# Default is 1.0.\n", + "# metalness - How much the material is like a metal. Non-metallic materials such as wood or stone use 0.0, \n", + "# metallic use 1.0, with nothing (usually) in between. \n", + "# Default is 0.0. A value between 0.0 and 1.0 could be used for a rusty metal look.\n", + "# PHYISICAL ONLY\n", + "\n", + "surf.roughness=0.1\n", + "plane.roughness=0.1\n", + "surf.metalness=0.6\n", + "plane.metalness=0.6" ] }, { "cell_type": "code", - "execution_count": 90, + "execution_count": 80, + "metadata": {}, + "outputs": [], + "source": [ + "# 1.0 - completely opaque; 0.0 - completely transparent\n", + "surf.opacity = 0.1" + ] + }, + { + "cell_type": "code", + "execution_count": 81, + "metadata": {}, + "outputs": [], + "source": [ + "# specular_color - A specular highlight is the bright spot of light that appears on shiny objects when illuminated\n", + "# shininess - specular intensity\n", + "# PHONG ONLY\n", + "# Bui Tuong Phong, Illumination of Computer-Generated Images, Department of Computer Science, University of Utah, UTEC-CSs-73-129, July 1973.\n", + "# Bui Tuong Phong, \"Illumination for Computer Generated Pictures,\" Comm. ACM, Vol 18(6):311-317, June 1975.\n", + "surf.lighting_model = 'PHONG'\n", + "surf.specular_color='red' \n", + "surf.shininess=100 \n", + "surf.opacity = 0.9" + ] + }, + { + "cell_type": "code", + "execution_count": 82, + "metadata": {}, + "outputs": [], + "source": [ + "# Meshes can cast shadows and/or receive shadows\n", + "surf.cast_shadow=True\n", + "plane.receive_shadow=True" + ] + }, + { + "cell_type": "code", + "execution_count": 83, + "metadata": {}, + "outputs": [], + "source": [ + "# Lambertian reflectance is the property that defines an ideal \"matte\" or diffusely reflecting surface.\n", + "surf.lighting_model = 'LAMBERT'\n", + "surf.opacity = 0.5" + ] + }, + { + "cell_type": "code", + "execution_count": 84, "metadata": {}, "outputs": [ { "data": { "application/vnd.jupyter.widget-view+json": { - "model_id": "8fa07120df9a4daaaeebd6b7926b363b", + "model_id": "c7ded6d9656c461fbec1a3b60ecdf8f9", "version_major": 2, "version_minor": 0 }, @@ -361,13 +411,13 @@ ], "source": [ "#####################################################\n", - "#AMBIENT LIGHT\n", - "plot_all(kb=False, wp=True, ts=True)" + "# AMBIENT LIGHT\n", + "plot_all(kb=False, wp=True, ts=True)#lighting model set to 'PHYSICAL'" ] }, { "cell_type": "code", - "execution_count": 91, + "execution_count": 85, "metadata": {}, "outputs": [ { @@ -381,14 +431,14 @@ } ], "source": [ - "#light_color - default white\n", - "#intensity - default 1\n", + "# light_color - default white\n", + "# intensity - default 1\n", "ambient1 = p3.ambient_light()" ] }, { "cell_type": "code", - "execution_count": 94, + "execution_count": 86, "metadata": {}, "outputs": [], "source": [ @@ -398,13 +448,13 @@ }, { "cell_type": "code", - "execution_count": 95, + "execution_count": 87, "metadata": {}, "outputs": [ { "data": { "application/vnd.jupyter.widget-view+json": { - "model_id": "3b0151d05890413f96faaeabd2af9c5a", + "model_id": "4af52976a65f439f9bf8b519f1e17461", "version_major": 2, "version_minor": 0 }, @@ -418,17 +468,17 @@ ], "source": [ "#####################################################\n", - "#HEMISPHERE LIGHT\n", - "#light_color - default white - upper light\n", - "#light_color2 - default white - bottom light\n", - "#intensity - default 1\n", - "#position_x, position_y, position_z - default [0, 1, 0] \n", + "# HEMISPHERE LIGHT\n", + "# light_color - default white - upper light\n", + "# light_color2 - default white - bottom light\n", + "# intensity - default 1\n", + "# position_x, position_y, position_z - default [0, 1, 0] \n", "plot_all(kb=False, wp=True, ts=True)" ] }, { "cell_type": "code", - "execution_count": 96, + "execution_count": 88, "metadata": {}, "outputs": [ { @@ -450,27 +500,27 @@ }, { "cell_type": "code", - "execution_count": 98, + "execution_count": 89, "metadata": {}, "outputs": [], "source": [ "hemisphere1.light_color='orange'\n", "hemisphere1.light_color2='blue'\n", "hemisphere1.intensity=1.5\n", - "hemisphere1.position_x = 0\n", - "hemisphere1.position_y = 1\n", + "hemisphere1.position_x = 1\n", + "hemisphere1.position_y = 0\n", "hemisphere1.position_z = 0" ] }, { "cell_type": "code", - "execution_count": 99, + "execution_count": 90, "metadata": {}, "outputs": [ { "data": { "application/vnd.jupyter.widget-view+json": { - "model_id": "6db6520ad92a43048910f825ffee08d4", + "model_id": "d1dd51894c4e4a25a5afedd269046b27", "version_major": 2, "version_minor": 0 }, @@ -496,19 +546,19 @@ ], "source": [ "#####################################################\n", - "#DIRECTIONAL LIGHT\n", - "#light_color - default white\n", - "#intensity - default 1\n", - "#position_x, position_y, position_z - default [0, 1, 0] \n", - "#target_x, target_y, target_z - default [0, 0, 0]\n", - "#cast_shadow - default False\n", - "#shadow_map_size - default 512\n", - "#shadow_bias - default -0.0005\n", - "#shadow_radius - default 1\n", - "#shadow_camera_near - default 0.5\n", - "#shadow_camera_far - default 500\n", - "#shadow_camera_orthographic_size - default 100\n", - "#shadow_map_type - default 'PCF_SOFT' - 'BASIC', 'PCF', 'PCF_SOFT'\n", + "# DIRECTIONAL LIGHT\n", + "# light_color - default white\n", + "# intensity - default 1\n", + "# position_x, position_y, position_z - default [0, 1, 0] \n", + "# target_x, target_y, target_z - default [0, 0, 0]\n", + "# cast_shadow - default False\n", + "# shadow_map_size - default 512\n", + "# shadow_bias - default -0.0005\n", + "# shadow_radius - default 1\n", + "# shadow_camera_near - default 0.5\n", + "# shadow_camera_far - default 500\n", + "# shadow_camera_orthographic_size - default 100\n", + "# shadow_map_type - default 'PCF_SOFT' - 'BASIC', 'PCF', 'PCF_SOFT'\n", "\n", "plot_all(kb=True, wp=True, ts=False)\n", "directional1=p3.directional_light()\n" @@ -516,7 +566,7 @@ }, { "cell_type": "code", - "execution_count": 100, + "execution_count": 91, "metadata": {}, "outputs": [], "source": [ @@ -526,7 +576,7 @@ }, { "cell_type": "code", - "execution_count": 101, + "execution_count": 92, "metadata": {}, "outputs": [], "source": [ @@ -542,19 +592,19 @@ }, { "cell_type": "code", - "execution_count": 102, + "execution_count": 93, "metadata": {}, "outputs": [], "source": [ - "#if some shadow settings do not update, due to a THREE.JS issue\n", - "#the settings should be updated when setting cast_shadow to False, Run cell, turn back to True and Run cell again\n", + "# if some shadow settings do not update, due to a THREE.JS issue\n", + "# the settings should be updated when setting cast_shadow to False, Run cell, turn back to True and Run cell again\n", "directional1.cast_shadow=True\n", "directional1.shadow_map_size=512\n", "directional1.shadow_map_type='PCF_SOFT'\n", - "#Setting this to values greater than 1 will blur the edges of the shadow.\n", - "#High values will cause unwanted banding effects in the shadows - a greater mapSize will allow for a higher value to be used here before these effects become visible.\n", - "#If shadow_map_type is set to PCF_SOFT, radius has no effect and it is recommended to increase softness by decreasing mapSize instead.\n", - "directional1.radius=10 \n", + "# Setting this to values greater than 1 will blur the edges of the shadow.\n", + "# High values will cause unwanted banding effects in the shadows - a greater mapSize will allow for a higher value to be used here before these effects become visible.\n", + "# If shadow_map_type is set to PCF_SOFT, radius has no effect and it is recommended to increase softness by decreasing mapSize instead.\n", + "directional1.radius = 10 \n", "#solves shadow acne issues\n", "directional1.shadow_bias=-0.005\n", "directional1.shadow_camera_orthographic_size=100" @@ -562,13 +612,13 @@ }, { "cell_type": "code", - "execution_count": 103, + "execution_count": 94, "metadata": {}, "outputs": [ { "data": { "application/vnd.jupyter.widget-view+json": { - "model_id": "d7dc2ad131fa4c72b8fdfa784fd52d0b", + "model_id": "e34e7b02908d40ea936ad52b42070055", "version_major": 2, "version_minor": 0 }, @@ -595,26 +645,26 @@ ], "source": [ "#####################################################\n", - "#POINT LIGHT\n", - "#light_color - default white\n", - "#intensity - default 1\n", - "#position_x, position_y, position_z - default [0, 1, 0]\n", - "#distance - default 0\n", - "#decay - default 1\n", - "#cast_shadow - default False\n", - "#shadow_map_size - default 512\n", - "#shadow_bias - default -0.0005\n", - "#shadow_radius - default 1\n", - "#shadow_camera_near - default 0.5\n", - "#shadow_camera_far - default 500\n", - "#shadow_map_type - default 'PCF_SOFT' - 'BASIC', 'PCF', 'PCF_SOFT'\n", + "# POINT LIGHT\n", + "# light_color - default white\n", + "# intensity - default 1\n", + "# position_x, position_y, position_z - default [0, 1, 0]\n", + "# distance - default 0\n", + "# decay - default 1\n", + "# cast_shadow - default False\n", + "# shadow_map_size - default 512\n", + "# shadow_bias - default -0.0005\n", + "# shadow_radius - default 1\n", + "# shadow_camera_near - default 0.5\n", + "# shadow_camera_far - default 500\n", + "# shadow_map_type - default 'PCF_SOFT' - 'BASIC', 'PCF', 'PCF_SOFT'\n", "plot_all(kb=True, wp=True, ts=False)\n", "point1=p3.point_light()\n" ] }, { "cell_type": "code", - "execution_count": 104, + "execution_count": 95, "metadata": {}, "outputs": [], "source": [ @@ -625,7 +675,7 @@ }, { "cell_type": "code", - "execution_count": 105, + "execution_count": 96, "metadata": {}, "outputs": [], "source": [ @@ -634,7 +684,7 @@ }, { "cell_type": "code", - "execution_count": 106, + "execution_count": 97, "metadata": {}, "outputs": [], "source": [ @@ -645,7 +695,7 @@ }, { "cell_type": "code", - "execution_count": 107, + "execution_count": 98, "metadata": {}, "outputs": [], "source": [ @@ -655,32 +705,32 @@ }, { "cell_type": "code", - "execution_count": 108, + "execution_count": 99, "metadata": {}, "outputs": [], "source": [ - "#if some shadow settings do not update, due to a THREE.JS issue\n", - "#the settings should be updated when setting cast_shadow to False, Run cell, turn back to True and Run cell again\n", + "# if some shadow settings do not update, due to a THREE.JS issue\n", + "# the settings should be updated when setting cast_shadow to False, Run cell, turn back to True and Run cell again\n", "point1.cast_shadow=True\n", "point1.shadow_map_size=512\n", "point1.shadow_map_type='PCF'\n", - "#Setting this to values greater than 1 will blur the edges of the shadow.\n", - "#High values will cause unwanted banding effects in the shadows - a greater mapSize will allow for a higher value to be used here before these effects become visible.\n", - "#If shadow_map_type is set to PCF_SOFT, radius has no effect and it is recommended to increase softness by decreasing mapSize instead.\n", + "# Setting this to values greater than 1 will blur the edges of the shadow.\n", + "# High values will cause unwanted banding effects in the shadows - a greater mapSize will allow for a higher value to be used here before these effects become visible.\n", + "# If shadow_map_type is set to PCF_SOFT, radius has no effect and it is recommended to increase softness by decreasing mapSize instead.\n", "point1.radius=100 \n", - "#solves shadow acne issues\n", + "# solves shadow acne issues\n", "point1.shadow_bias=-0.005" ] }, { "cell_type": "code", - "execution_count": 109, + "execution_count": 100, "metadata": {}, "outputs": [ { "data": { "application/vnd.jupyter.widget-view+json": { - "model_id": "7cd55adc22be4eb1963cae5b6b3f1d69", + "model_id": "6cc92608c6a14aec977e61631dc248bb", "version_major": 2, "version_minor": 0 }, @@ -710,24 +760,24 @@ ], "source": [ "#####################################################\n", - "#SPOT LIGHT\n", - "#light_color - default white\n", - "#intensity - default 1\n", - "#position_x, position_y, position_z - default [0, 1, 0]\n", - "#target_x, target_y, target_z - default [0, 0, 0]\n", - "#angle - default math.pi/3\n", - "#distance - default 0\n", - "#decay - default 1\n", - "#penumbra - default 0\n", - "#cast_shadow - default False\n", - "#shadow_map_size - default 512\n", - "#shadow_bias - default -0.0005\n", - "#shadow_radius - default 1\n", - "#shadow_camera_near - default 0.5\n", - "#shadow_camera_far - default 500\n", - "#shadow_camera_perspective_fov - default 50\n", - "#shadow_camera_perspective_aspect - default 1\n", - "#shadow_map_type - default 'PCF_SOFT' - 'BASIC', 'PCF', 'PCF_SOFT'\n", + "# SPOT LIGHT\n", + "# light_color - default white\n", + "# intensity - default 1\n", + "# position_x, position_y, position_z - default [0, 1, 0]\n", + "# target_x, target_y, target_z - default [0, 0, 0]\n", + "# angle - default math.pi/3\n", + "# distance - default 0\n", + "# decay - default 1\n", + "# penumbra - default 0\n", + "# cast_shadow - default False\n", + "# shadow_map_size - default 512\n", + "# shadow_bias - default -0.0005\n", + "# shadow_radius - default 1\n", + "# shadow_camera_near - default 0.5\n", + "# shadow_camera_far - default 500\n", + "# shadow_camera_perspective_fov - default 50\n", + "# shadow_camera_perspective_aspect - default 1\n", + "# shadow_map_type - default 'PCF_SOFT' - 'BASIC', 'PCF', 'PCF_SOFT'\n", "\n", "plot_all(kb=True, wp=True, ts=False)\n", "spot1=p3.spot_light()\n" @@ -735,7 +785,7 @@ }, { "cell_type": "code", - "execution_count": 110, + "execution_count": 101, "metadata": {}, "outputs": [], "source": [ @@ -754,36 +804,36 @@ }, { "cell_type": "code", - "execution_count": 111, + "execution_count": 102, "metadata": {}, "outputs": [], "source": [ - "#Default mode — When distance is zero, light does not attenuate. \n", - "#When distance is non-zero, light will attenuate linearly from maximum intensity at the \n", - "#light's position down to zero at this distance from the light.\n", + "# Default mode — When distance is zero, light does not attenuate. \n", + "# When distance is non-zero, light will attenuate linearly from maximum intensity at the \n", + "# light's position down to zero at this distance from the light.\n", "\n", - "#Physically correct mode — When distance is zero, light will attenuate according to inverse-square law to infinite distance. \n", - "#When distance is non-zero, light will attenuate according to inverse-square law until near the distance cutoff, \n", - "#where it will then attenuate quickly and smoothly to 0. Inherently, cutoffs are not physically correct.\n", - "#Default is 0.0.\n", + "# Physically correct mode — When distance is zero, light will attenuate according to inverse-square law to infinite distance. \n", + "# When distance is non-zero, light will attenuate according to inverse-square law until near the distance cutoff, \n", + "# where it will then attenuate quickly and smoothly to 0. Inherently, cutoffs are not physically correct.\n", + "# Default is 0.0.\n", "\n", "spot1.distance=200" ] }, { "cell_type": "code", - "execution_count": 82, + "execution_count": 103, "metadata": {}, "outputs": [], "source": [ - "#The amount the light dims along the distance of the light.\n", - "#In physically correct mode, decay = 2 leads to physically realistic light falloff. The default is 1.\n", - "#spot1.decay=0" + "# The amount the light dims along the distance of the light.\n", + "# In physically correct mode, decay = 2 leads to physically realistic light falloff. The default is 1.\n", + "# spot1.decay=0" ] }, { "cell_type": "code", - "execution_count": 112, + "execution_count": 104, "metadata": {}, "outputs": [], "source": [ @@ -794,138 +844,34 @@ }, { "cell_type": "code", - "execution_count": 113, + "execution_count": 105, "metadata": {}, "outputs": [], "source": [ - "#if some shadow settings do not update, due to a THREE.JS issue\n", - "#the settings should be updated when setting cast_shadow to False, Run cell, turn back to True and Run cell again\n", + "# If some shadow settings do not update, due to a THREE.JS issue\n", + "# the settings should be updated when setting cast_shadow to False, Run cell, turn back to True and Run cell again\n", "spot1.cast_shadow=True\n", "spot1.shadow_map_size=1024\n", "spot1.shadow_map_type='PCF_SOFT'\n", "\n", "spot1.shadow_camera_perspective_fov=50\n", - "#change the shadow camera aspect\n", + "# change the shadow camera aspect\n", "shadow_camera_perspective_aspect=1\n", "\n", - "#Setting this to values greater than 1 will blur the edges of the shadow.\n", - "#High values will cause unwanted banding effects in the shadows - a greater mapSize will allow for a higher value to be used here before these effects become visible.\n", - "#If shadow_map_type is set to PCF_SOFT, radius has no effect and it is recommended to increase softness by decreasing mapSize instead.\n", + "# Setting this to values greater than 1 will blur the edges of the shadow.\n", + "# High values will cause unwanted banding effects in the shadows - a greater mapSize will allow for a higher value to be used here before these effects become visible.\n", + "# If shadow_map_type is set to PCF_SOFT, radius has no effect and it is recommended to increase softness by decreasing mapSize instead.\n", "spot1.radius=5 \n", - "#solves shadow acne issues\n", + "# solves shadow acne issues\n", "spot1.shadow_bias=-0.0005" ] }, - { - "cell_type": "code", - "execution_count": null, - "metadata": {}, - "outputs": [], - "source": [] - }, { "cell_type": "code", "execution_count": 9, "metadata": {}, "outputs": [], "source": [] - }, - { - "cell_type": "code", - "execution_count": null, - "metadata": {}, - "outputs": [], - "source": [ - "\n" - ] - }, - { - "cell_type": "code", - "execution_count": 26, - "metadata": {}, - "outputs": [], - "source": [] - }, - { - "cell_type": "code", - "execution_count": 25, - "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": [] - }, - { - "cell_type": "code", - "execution_count": null, - "metadata": {}, - "outputs": [], - "source": [ - "def head2(draw=True, show=True, max_shape=256):\n", - " \"\"\"Show a volumetric rendering of a human male head.\"\"\"\n", - " # inspired by http://graphicsrunner.blogspot.com/2009/01/volume-rendering-102-transfer-functions.html\n", - " import ipyvolume as ipv\n", - " from scipy.interpolate import interp1d\n", - "\n", - " # First part is a simpler version of setting up the transfer function. Interpolation with higher order\n", - " # splines does not work well, the original must do sth different\n", - " colors = [[0.91, 0.7, 0.61, 0.0], [0.91, 0.7, 0.61, 80.0], [1.0, 1.0, 0.85, 82.0], [1.0, 1.0, 0.85, 256]]\n", - " x = np.array([k[-1] for k in colors])\n", - " rgb = np.array([k[:3] for k in colors])\n", - " N = 256\n", - " xnew = np.linspace(0, 256, N)\n", - " tf_data = np.zeros((N, 4))\n", - " kind = 'linear'\n", - " for channel in range(3):\n", - " f = interp1d(x, rgb[:, channel], kind=kind)\n", - " ynew = f(xnew)\n", - " tf_data[:, channel] = ynew\n", - " alphas = [[0, 0], [0, 40], [0.2, 60], [0.05, 63], [0, 80], [0.9, 82], [1.0, 256]]\n", - " x = np.array([k[1] * 1.0 for k in alphas])\n", - " y = np.array([k[0] * 1.0 for k in alphas])\n", - " f = interp1d(x, y, kind=kind)\n", - " ynew = f(xnew)\n", - " tf_data[:, 3] = ynew\n", - " tf = ipv.TransferFunction(rgba=tf_data.astype(np.float32))\n", - "\n", - " head_data = ipv.datasets.head.fetch().data\n", - " if draw:\n", - " vol = ipv.volshow(head_data, tf=tf, max_shape=max_shape)\n", - " if show:\n", - " ipv.show()\n", - " return vol\n", - " else:\n", - " return head_data\n", - " \n", - "p3.clear()\n", - "head2()" - ] - }, - { - "cell_type": "code", - "execution_count": null, - "metadata": {}, - "outputs": [], - "source": [] } ], "metadata": { From 1b7171672bb50901e015a8db1817ea3f5a2def3a Mon Sep 17 00:00:00 2001 From: rinftech-github Date: Tue, 31 Mar 2020 19:36:54 +0300 Subject: [PATCH 028/130] Replace radius with shadow_radius in demo notebook --- notebooks/lighting_demo.ipynb | 52 +++++++++++++++++------------------ 1 file changed, 26 insertions(+), 26 deletions(-) diff --git a/notebooks/lighting_demo.ipynb b/notebooks/lighting_demo.ipynb index c8968bc9..60546c1b 100644 --- a/notebooks/lighting_demo.ipynb +++ b/notebooks/lighting_demo.ipynb @@ -2,7 +2,7 @@ "cells": [ { "cell_type": "code", - "execution_count": 24, + "execution_count": 2, "metadata": {}, "outputs": [], "source": [ @@ -514,13 +514,13 @@ }, { "cell_type": "code", - "execution_count": 90, + "execution_count": 3, "metadata": {}, "outputs": [ { "data": { "application/vnd.jupyter.widget-view+json": { - "model_id": "d1dd51894c4e4a25a5afedd269046b27", + "model_id": "ad00394ef521448aa04f2fcdd1dd2a64", "version_major": 2, "version_minor": 0 }, @@ -566,7 +566,7 @@ }, { "cell_type": "code", - "execution_count": 91, + "execution_count": 4, "metadata": {}, "outputs": [], "source": [ @@ -576,7 +576,7 @@ }, { "cell_type": "code", - "execution_count": 92, + "execution_count": 5, "metadata": {}, "outputs": [], "source": [ @@ -592,7 +592,7 @@ }, { "cell_type": "code", - "execution_count": 93, + "execution_count": 7, "metadata": {}, "outputs": [], "source": [ @@ -603,8 +603,8 @@ "directional1.shadow_map_type='PCF_SOFT'\n", "# Setting this to values greater than 1 will blur the edges of the shadow.\n", "# High values will cause unwanted banding effects in the shadows - a greater mapSize will allow for a higher value to be used here before these effects become visible.\n", - "# If shadow_map_type is set to PCF_SOFT, radius has no effect and it is recommended to increase softness by decreasing mapSize instead.\n", - "directional1.radius = 10 \n", + "# If shadow_map_type is set to PCF_SOFT, shadow_radius has no effect and it is recommended to increase softness by decreasing mapSize instead.\n", + "directional1.shadow_radius = 5 \n", "#solves shadow acne issues\n", "directional1.shadow_bias=-0.005\n", "directional1.shadow_camera_orthographic_size=100" @@ -612,13 +612,13 @@ }, { "cell_type": "code", - "execution_count": 94, + "execution_count": 8, "metadata": {}, "outputs": [ { "data": { "application/vnd.jupyter.widget-view+json": { - "model_id": "e34e7b02908d40ea936ad52b42070055", + "model_id": "18d42c49f2fe408eb5d2c4e068950494", "version_major": 2, "version_minor": 0 }, @@ -664,7 +664,7 @@ }, { "cell_type": "code", - "execution_count": 95, + "execution_count": 9, "metadata": {}, "outputs": [], "source": [ @@ -675,7 +675,7 @@ }, { "cell_type": "code", - "execution_count": 96, + "execution_count": 10, "metadata": {}, "outputs": [], "source": [ @@ -684,7 +684,7 @@ }, { "cell_type": "code", - "execution_count": 97, + "execution_count": 11, "metadata": {}, "outputs": [], "source": [ @@ -695,7 +695,7 @@ }, { "cell_type": "code", - "execution_count": 98, + "execution_count": 12, "metadata": {}, "outputs": [], "source": [ @@ -705,7 +705,7 @@ }, { "cell_type": "code", - "execution_count": 99, + "execution_count": 14, "metadata": {}, "outputs": [], "source": [ @@ -716,21 +716,21 @@ "point1.shadow_map_type='PCF'\n", "# Setting this to values greater than 1 will blur the edges of the shadow.\n", "# High values will cause unwanted banding effects in the shadows - a greater mapSize will allow for a higher value to be used here before these effects become visible.\n", - "# If shadow_map_type is set to PCF_SOFT, radius has no effect and it is recommended to increase softness by decreasing mapSize instead.\n", - "point1.radius=100 \n", + "# If shadow_map_type is set to PCF_SOFT, shadow_radius has no effect and it is recommended to increase softness by decreasing mapSize instead.\n", + "point1.shadow_radius=10 \n", "# solves shadow acne issues\n", "point1.shadow_bias=-0.005" ] }, { "cell_type": "code", - "execution_count": 100, + "execution_count": 15, "metadata": {}, "outputs": [ { "data": { "application/vnd.jupyter.widget-view+json": { - "model_id": "6cc92608c6a14aec977e61631dc248bb", + "model_id": "1f663c4da8f4484db3af07965d717808", "version_major": 2, "version_minor": 0 }, @@ -785,7 +785,7 @@ }, { "cell_type": "code", - "execution_count": 101, + "execution_count": 16, "metadata": {}, "outputs": [], "source": [ @@ -804,7 +804,7 @@ }, { "cell_type": "code", - "execution_count": 102, + "execution_count": 17, "metadata": {}, "outputs": [], "source": [ @@ -822,18 +822,18 @@ }, { "cell_type": "code", - "execution_count": 103, + "execution_count": 18, "metadata": {}, "outputs": [], "source": [ "# The amount the light dims along the distance of the light.\n", "# In physically correct mode, decay = 2 leads to physically realistic light falloff. The default is 1.\n", - "# spot1.decay=0" + "spot1.decay=0" ] }, { "cell_type": "code", - "execution_count": 104, + "execution_count": 19, "metadata": {}, "outputs": [], "source": [ @@ -844,7 +844,7 @@ }, { "cell_type": "code", - "execution_count": 105, + "execution_count": 20, "metadata": {}, "outputs": [], "source": [ @@ -861,7 +861,7 @@ "# Setting this to values greater than 1 will blur the edges of the shadow.\n", "# High values will cause unwanted banding effects in the shadows - a greater mapSize will allow for a higher value to be used here before these effects become visible.\n", "# If shadow_map_type is set to PCF_SOFT, radius has no effect and it is recommended to increase softness by decreasing mapSize instead.\n", - "spot1.radius=5 \n", + "spot1.shadow_radius=5 \n", "# solves shadow acne issues\n", "spot1.shadow_bias=-0.0005" ] From f2c6c38ec0f855c087040d0e6a322c3f448011fd Mon Sep 17 00:00:00 2001 From: rinftech-github Date: Tue, 31 Mar 2020 23:03:29 +0300 Subject: [PATCH 029/130] Switch scatter lighting model to PHYSICAL when adding a light. Scatter has DEFAULT and PHYSICAL lighting models --- ipyvolume/widgets.py | 2 +- js/src/light.ts | 8 +++--- js/src/mesh.ts | 1 - js/src/scatter.ts | 67 +++++++++++++++----------------------------- 4 files changed, 27 insertions(+), 51 deletions(-) diff --git a/ipyvolume/widgets.py b/ipyvolume/widgets.py index a8ebe468..cd83c9cb 100644 --- a/ipyvolume/widgets.py +++ b/ipyvolume/widgets.py @@ -131,7 +131,7 @@ class Scatter(widgets.Widget): connected = traitlets.CBool(default_value=False).tag(sync=True) visible = traitlets.CBool(default_value=True).tag(sync=True) - lighting_model = traitlets.Enum(values=['DEFAULT', 'LAMBERT', 'PHONG', 'PHYSICAL'], default_value='DEFAULT').tag(sync=True) + lighting_model = traitlets.Enum(values=['DEFAULT', 'PHYSICAL'], default_value='DEFAULT').tag(sync=True) #diffuse_color = Array(default_value="white", allow_none=True).tag(sync=True, **color_serialization) opacity = traitlets.CFloat(1).tag(sync=True) specular_color = Array(default_value="white", allow_none=True).tag(sync=True, **color_serialization) diff --git a/js/src/light.ts b/js/src/light.ts index ea62964c..0bd8b742 100644 --- a/js/src/light.ts +++ b/js/src/light.ts @@ -38,10 +38,6 @@ class LightView extends widgets.WidgetView { shadow_camera_perspective_aspect: any; shadow_camera_orthographic_size: any; - //change:light_type //? - /* - change:light_color change:light_color2 change:intensity change:shadow_map_type change:cast_shadow change:position_x change:position_y change:position_z change:target_x change:target_y change:target_z change:distance change:angle change:decay change:penumbra change:shadow_map_size change:shadow_bias change:shadow_radius change:shadow_camera_near change:shadow_camera_far change:shadow_camera_perspective_fov change:shadow_camera_perspective_aspect change:shadow_camera_orthographic_size - */ render() { this.LIGHT_TYPES = { @@ -99,6 +95,10 @@ class LightView extends widgets.WidgetView { console.log(mesh_key, this.renderer.mesh_views[mesh_key]); this.renderer.mesh_views[mesh_key].force_lighting_model(); } + for (let scatter_key in this.renderer.scatter_views) { + console.log(scatter_key, this.renderer.scatter_views[scatter_key]); + this.renderer.scatter_views[scatter_key].force_lighting_model(); + } this.lights = []; diff --git a/js/src/mesh.ts b/js/src/mesh.ts index 12ace889..085b6b79 100644 --- a/js/src/mesh.ts +++ b/js/src/mesh.ts @@ -146,7 +146,6 @@ class MeshView extends widgets.WidgetView { console.log("FORCE LIGHTING MODEL TO PHYSICAL"); if(this.lighting_model === this.LIGHTING_MODELS.DEFAULT){ this.model.set("lighting_model", this.LIGHTING_MODELS.PHYSICAL); - //this.lighting_model = this.LIGHTING_MODELS.PHYSICAL; this.update_visibility(); } } diff --git a/js/src/scatter.ts b/js/src/scatter.ts index 89dc0ce8..5b4ba530 100644 --- a/js/src/scatter.ts +++ b/js/src/scatter.ts @@ -45,8 +45,6 @@ class ScatterView extends widgets.WidgetView { this.LIGHTING_MODELS = { DEFAULT: 'DEFAULT', - LAMBERT: 'LAMBERT', - PHONG: 'PHONG', PHYSICAL : 'PHYSICAL' }; @@ -174,7 +172,11 @@ class ScatterView extends widgets.WidgetView { this._update_materials(); this.renderer.update(); }); + + this.model.on("change:lighting_model change:opacity change:specular_color change:shininess change:emissive_color change:emissive_intensity change:roughness change:metalness change:cast_shadow change:receive_shadow", + this.update_visibility, this); } + _load_textures() { const texture = this.model.get("texture"); if (texture.stream) { // instanceof media.MediaStreamModel) { @@ -200,6 +202,15 @@ class ScatterView extends widgets.WidgetView { ); } } + + public force_lighting_model() { + console.log("FORCE LIGHTING MODEL TO PHYSICAL"); + if(this.lighting_model === this.LIGHTING_MODELS.DEFAULT){ + this.model.set("lighting_model", this.LIGHTING_MODELS.PHYSICAL); + this.update_visibility(); + } + } + update_visibility() { this._update_materials(); this.renderer.update(); @@ -210,16 +221,18 @@ class ScatterView extends widgets.WidgetView { } } add_to_scene() { + + //currently, not shadow support because of InstancedBufferGeometry this.cast_shadow = this.model.get("cast_shadow"); this.receive_shadow = this.model.get("receive_shadow"); - this.mesh.castShadow = true;//this.cast_shadow; - this.mesh.receiveShadow = true;//this.receive_shadow; + this.mesh.castShadow = false;//this.cast_shadow; + this.mesh.receiveShadow = false;//this.receive_shadow; this.renderer.scene_scatter.add(this.mesh); if (this.line_segments) { this.renderer.scene_scatter.add(this.line_segments); - this.line_segments.castShadow = true;//this.cast_shadow; - this.line_segments.receiveShadow = true;//this.receive_shadow; + this.line_segments.castShadow = false;//this.cast_shadow; + this.line_segments.receiveShadow = false;//this.receive_shadow; } } remove_from_scene() { @@ -340,16 +353,7 @@ class ScatterView extends widgets.WidgetView { material.vertexShader = require("raw-loader!../glsl/scatter-vertex.glsl"); material.fragmentShader = require("raw-loader!../glsl/scatter-fragment.glsl"); } - /* - else if(this.lighting_model === this.LIGHTING_MODELS.LAMBERT) { - material.vertexShader = require("raw-loader!../glsl/scatter-vertex-lambert.glsl"); - material.fragmentShader = require("raw-loader!../glsl/scatter-fragment-lambert.glsl"); - } - else if(this.lighting_model === this.LIGHTING_MODELS.PHONG) { - material.vertexShader = require("raw-loader!../glsl/scatter-vertex-phong.glsl"); - material.fragmentShader = require("raw-loader!../glsl/scatter-fragment-phong.glsl"); - } - */ + else {//if(this.lighting_model === this.LIGHTING_MODELS.PHYSICAL) { material.vertexShader = require("raw-loader!../glsl/scatter-vertex-physical.glsl"); material.fragmentShader = require("raw-loader!../glsl/scatter-fragment-physical.glsl"); @@ -390,40 +394,13 @@ class ScatterView extends widgets.WidgetView { material.uniforms.metalness.value = this.metalness; material.depthWrite = true; - material.transparant = true; + material.transparant = true;//? + material.transparent = true; material.depthTest = true; material.needsUpdate = true; }); - this.diffuse_color = this.model.get("diffuse_color"); - this.opacity = this.model.get("opacity"); - this.specular_color = this.model.get("specular_color"); - this.shininess = this.model.get("shininess"); - this.emissive_color = this.model.get("emissive_color"); - this.emissive_intensity = this.model.get("emissive_intensity"); - this.roughness = this.model.get("roughness"); - this.metalness = this.model.get("metalness"); - this.renderer.renderer.shadowMap.enabled = true; -/* - console.log("this.diffuse_color:"+this.diffuse_color); - console.log("this.opacity:"+this.opacity); - console.log("this.specular_color:"+this.specular_color); - console.log("this.shininess:"+this.shininess); - console.log("this.emissive_color:"+this.emissive_color); - console.log("this.emissive_intensity:"+this.emissive_intensity); - console.log("this.roughness:"+this.roughness); - console.log("this.metalness:"+this.metalness); -*/ - this.material.uniforms.diffuse.value = new THREE.Color(1, 1, 1);//this.diffuse_color//BUG? keep hardcoded - this.material.uniforms.opacity.value = this.opacity; - this.material.uniforms.specular.value = new THREE.Color(this.specular_color); - this.material.uniforms.shininess.value = this.shininess; - this.material.uniforms.emissive.value = new THREE.Color(this.emissive_color); - this.material.uniforms.emissiveIntensity.value = this.emissive_intensity; - this.material.uniforms.roughness.value = this.roughness; - this.material.uniforms.metalness.value = this.metalness; - const geo = this.model.get("geo"); const sprite = geo.endsWith("2d"); if (sprite) { From c0bafd6f5491a234232fa2cd0cc2a79dc69ecee2 Mon Sep 17 00:00:00 2001 From: rinftech-github Date: Tue, 31 Mar 2020 23:41:04 +0300 Subject: [PATCH 030/130] Update lighting demo notebook --- notebooks/lighting_demo.ipynb | 170 +++++++++++++++++++++++++--------- notebooks/test.ipynb | 10 +- 2 files changed, 135 insertions(+), 45 deletions(-) diff --git a/notebooks/lighting_demo.ipynb b/notebooks/lighting_demo.ipynb index 60546c1b..54ab55fd 100644 --- a/notebooks/lighting_demo.ipynb +++ b/notebooks/lighting_demo.ipynb @@ -2,7 +2,7 @@ "cells": [ { "cell_type": "code", - "execution_count": 2, + "execution_count": 6, "metadata": {}, "outputs": [], "source": [ @@ -207,15 +207,22 @@ " shadow_bias=-0.0005)" ] }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "# Plot a plane and a sinusoidal surface" + ] + }, { "cell_type": "code", - "execution_count": 73, + "execution_count": 7, "metadata": {}, "outputs": [ { "data": { "application/vnd.jupyter.widget-view+json": { - "model_id": "1cb966ccf840442899a2678ec76e64b8", + "model_id": "ce06f1e3a03e46a5902ae64a87e9d5c9", "version_major": 2, "version_minor": 0 }, @@ -228,13 +235,12 @@ } ], "source": [ - "# Plot a plane and a sinusoidal surface\n", "[bottle, plane, surf] = plot_all_no_change(kb=False, wp=True, ts=True)" ] }, { "cell_type": "code", - "execution_count": 74, + "execution_count": 8, "metadata": {}, "outputs": [], "source": [ @@ -245,9 +251,16 @@ "#plane.lighting_model = 'PHYSICAL'" ] }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "# Add a directional light and a spot light" + ] + }, { "cell_type": "code", - "execution_count": 75, + "execution_count": 9, "metadata": {}, "outputs": [ { @@ -288,13 +301,19 @@ } ], "source": [ - "# Add a directional light and a spot light\n", "lights_dir_spot()" ] }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "# Set Emissive components" + ] + }, { "cell_type": "code", - "execution_count": 76, + "execution_count": 10, "metadata": {}, "outputs": [], "source": [ @@ -304,7 +323,7 @@ }, { "cell_type": "code", - "execution_count": 77, + "execution_count": 11, "metadata": {}, "outputs": [], "source": [ @@ -313,7 +332,7 @@ }, { "cell_type": "code", - "execution_count": 78, + "execution_count": 12, "metadata": {}, "outputs": [], "source": [ @@ -321,9 +340,16 @@ "surf.emissive_intensity=0.1;" ] }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "# Physical Lighting components" + ] + }, { "cell_type": "code", - "execution_count": 79, + "execution_count": 13, "metadata": {}, "outputs": [], "source": [ @@ -342,7 +368,7 @@ }, { "cell_type": "code", - "execution_count": 80, + "execution_count": 16, "metadata": {}, "outputs": [], "source": [ @@ -350,9 +376,16 @@ "surf.opacity = 0.1" ] }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "# Phong Lighting components" + ] + }, { "cell_type": "code", - "execution_count": 81, + "execution_count": 17, "metadata": {}, "outputs": [], "source": [ @@ -364,12 +397,19 @@ "surf.lighting_model = 'PHONG'\n", "surf.specular_color='red' \n", "surf.shininess=100 \n", - "surf.opacity = 0.9" + "surf.opacity = 0.9 #not Phong only" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "# Add shadows" ] }, { "cell_type": "code", - "execution_count": 82, + "execution_count": 18, "metadata": {}, "outputs": [], "source": [ @@ -378,9 +418,16 @@ "plane.receive_shadow=True" ] }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "# Lambert Lighting components" + ] + }, { "cell_type": "code", - "execution_count": 83, + "execution_count": 19, "metadata": {}, "outputs": [], "source": [ @@ -389,15 +436,22 @@ "surf.opacity = 0.5" ] }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "# Ambient Light" + ] + }, { "cell_type": "code", - "execution_count": 84, + "execution_count": 20, "metadata": {}, "outputs": [ { "data": { "application/vnd.jupyter.widget-view+json": { - "model_id": "c7ded6d9656c461fbec1a3b60ecdf8f9", + "model_id": "63932e5c16534f79953b79ccf1251554", "version_major": 2, "version_minor": 0 }, @@ -417,7 +471,7 @@ }, { "cell_type": "code", - "execution_count": 85, + "execution_count": 21, "metadata": {}, "outputs": [ { @@ -438,7 +492,7 @@ }, { "cell_type": "code", - "execution_count": 86, + "execution_count": 22, "metadata": {}, "outputs": [], "source": [ @@ -446,15 +500,22 @@ "ambient1.intensity=2.0" ] }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "# Hemisphere Light" + ] + }, { "cell_type": "code", - "execution_count": 87, + "execution_count": 23, "metadata": {}, "outputs": [ { "data": { "application/vnd.jupyter.widget-view+json": { - "model_id": "4af52976a65f439f9bf8b519f1e17461", + "model_id": "a05b70acf3894bc5bf615a9bb3ecf34c", "version_major": 2, "version_minor": 0 }, @@ -478,7 +539,7 @@ }, { "cell_type": "code", - "execution_count": 88, + "execution_count": 24, "metadata": {}, "outputs": [ { @@ -500,7 +561,7 @@ }, { "cell_type": "code", - "execution_count": 89, + "execution_count": 25, "metadata": {}, "outputs": [], "source": [ @@ -512,15 +573,22 @@ "hemisphere1.position_z = 0" ] }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "# Directional Light" + ] + }, { "cell_type": "code", - "execution_count": 3, + "execution_count": 26, "metadata": {}, "outputs": [ { "data": { "application/vnd.jupyter.widget-view+json": { - "model_id": "ad00394ef521448aa04f2fcdd1dd2a64", + "model_id": "cf8b66afc5b24cd18815d84528055097", "version_major": 2, "version_minor": 0 }, @@ -566,7 +634,7 @@ }, { "cell_type": "code", - "execution_count": 4, + "execution_count": 27, "metadata": {}, "outputs": [], "source": [ @@ -576,7 +644,7 @@ }, { "cell_type": "code", - "execution_count": 5, + "execution_count": 28, "metadata": {}, "outputs": [], "source": [ @@ -592,7 +660,7 @@ }, { "cell_type": "code", - "execution_count": 7, + "execution_count": 29, "metadata": {}, "outputs": [], "source": [ @@ -610,15 +678,22 @@ "directional1.shadow_camera_orthographic_size=100" ] }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "# Point Light" + ] + }, { "cell_type": "code", - "execution_count": 8, + "execution_count": 30, "metadata": {}, "outputs": [ { "data": { "application/vnd.jupyter.widget-view+json": { - "model_id": "18d42c49f2fe408eb5d2c4e068950494", + "model_id": "6909d1e5368148c29518e41d3187b783", "version_major": 2, "version_minor": 0 }, @@ -664,7 +739,7 @@ }, { "cell_type": "code", - "execution_count": 9, + "execution_count": 31, "metadata": {}, "outputs": [], "source": [ @@ -675,7 +750,7 @@ }, { "cell_type": "code", - "execution_count": 10, + "execution_count": 32, "metadata": {}, "outputs": [], "source": [ @@ -684,7 +759,7 @@ }, { "cell_type": "code", - "execution_count": 11, + "execution_count": 33, "metadata": {}, "outputs": [], "source": [ @@ -695,7 +770,7 @@ }, { "cell_type": "code", - "execution_count": 12, + "execution_count": 34, "metadata": {}, "outputs": [], "source": [ @@ -705,7 +780,7 @@ }, { "cell_type": "code", - "execution_count": 14, + "execution_count": 35, "metadata": {}, "outputs": [], "source": [ @@ -722,15 +797,22 @@ "point1.shadow_bias=-0.005" ] }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "# Spot Light" + ] + }, { "cell_type": "code", - "execution_count": 15, + "execution_count": 36, "metadata": {}, "outputs": [ { "data": { "application/vnd.jupyter.widget-view+json": { - "model_id": "1f663c4da8f4484db3af07965d717808", + "model_id": "9178f036b8a24ebf83e9d5d84c9da21b", "version_major": 2, "version_minor": 0 }, @@ -785,7 +867,7 @@ }, { "cell_type": "code", - "execution_count": 16, + "execution_count": 37, "metadata": {}, "outputs": [], "source": [ @@ -804,7 +886,7 @@ }, { "cell_type": "code", - "execution_count": 17, + "execution_count": 38, "metadata": {}, "outputs": [], "source": [ @@ -822,7 +904,7 @@ }, { "cell_type": "code", - "execution_count": 18, + "execution_count": 39, "metadata": {}, "outputs": [], "source": [ @@ -833,7 +915,7 @@ }, { "cell_type": "code", - "execution_count": 19, + "execution_count": 40, "metadata": {}, "outputs": [], "source": [ @@ -844,7 +926,7 @@ }, { "cell_type": "code", - "execution_count": 20, + "execution_count": 41, "metadata": {}, "outputs": [], "source": [ @@ -868,7 +950,7 @@ }, { "cell_type": "code", - "execution_count": 9, + "execution_count": null, "metadata": {}, "outputs": [], "source": [] diff --git a/notebooks/test.ipynb b/notebooks/test.ipynb index 7542ed4f..79dd1d09 100644 --- a/notebooks/test.ipynb +++ b/notebooks/test.ipynb @@ -899,6 +899,14 @@ "s.color_selected = \"brown\"" ] }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "# Lighting test\n", + "Lighting test" + ] + }, { "cell_type": "code", "execution_count": null, @@ -924,7 +932,7 @@ "name": "python", "nbconvert_exporter": "python", "pygments_lexer": "ipython3", - "version": "3.6.4" + "version": "3.7.4" } }, "nbformat": 4, From 1212c8a393b242976478f7ec9c235c2b0c55f7fe Mon Sep 17 00:00:00 2001 From: rinftech-github Date: Wed, 1 Apr 2020 01:30:03 +0300 Subject: [PATCH 031/130] Add unit tests. Remove cast_shadow from hemisphere_light --- ipyvolume/pylab.py | 7 +-- ipyvolume/test_all.py | 128 ++++++++++++++++++++++++++++++++++++++++++ 2 files changed, 130 insertions(+), 5 deletions(-) diff --git a/ipyvolume/pylab.py b/ipyvolume/pylab.py index 5ae21ef9..5f40e64e 100644 --- a/ipyvolume/pylab.py +++ b/ipyvolume/pylab.py @@ -1713,15 +1713,13 @@ def hemisphere_light( light_color=default_color_selected, light_color2=default_color, intensity = 1, - position=[0, 1, 0], - cast_shadow=False): + position=[0, 1, 0]): print("ADD HEMISPHERE LIGHT (from pylab) ") print("light_color: " + str(light_color)) print("light_color2: " + str(light_color2)) print("intensity: " + str(intensity)) print("position: " + str(position[0])+" "+str(position[1])+" "+str(position[2])) - print("cast_shadow: " + str(cast_shadow)) light = ipv.Light( light_type='HEMISPHERE', @@ -1730,8 +1728,7 @@ def hemisphere_light( intensity=intensity, position_x=position[0], position_y=position[1], - position_z=position[2], - cast_shadow=cast_shadow) + position_z=position[2]) fig = gcf() fig.lights = fig.lights + [light] diff --git a/ipyvolume/test_all.py b/ipyvolume/test_all.py index eb09e770..f5710aac 100644 --- a/ipyvolume/test_all.py +++ b/ipyvolume/test_all.py @@ -4,6 +4,7 @@ import shutil import json import contextlib +import math import numpy as np import pytest @@ -449,3 +450,130 @@ def test_datasets(): ipyvolume.datasets.aquariusA2.fetch() ipyvolume.datasets.hdz2000.fetch() ipyvolume.datasets.zeldovich.fetch() + + +def test_mesh_material(): + def test_material_components(mesh=None, is_scatter=False): + assert mesh.lighting_model == 'DEFAULT' + assert mesh.opacity == 1 + assert mesh.specular_color == 'white' + assert mesh.shininess == 1 + assert mesh.emissive_color == 'black' + assert mesh.emissive_intensity == 1 + assert mesh.roughness == 0 + assert mesh.metalness == 0 + if is_scatter == False: + assert mesh.cast_shadow == False + assert mesh.receive_shadow == False + + mesh.lighting_model = 'PHYSICAL' + mesh.opacity = 0 + mesh.specular_color = 'blue' + mesh.shininess = 10 + mesh.emissive_color = 'red' + mesh.emissive_intensity = 2 + mesh.roughness = 1 + mesh.metalness = 5 + if is_scatter == False: + mesh.cast_shadow = True + mesh.receive_shadow = True + + assert mesh.lighting_model == 'PHYSICAL' + assert mesh.opacity == 0 + assert mesh.specular_color == 'blue' + assert mesh.shininess == 10 + assert mesh.emissive_color == 'red' + assert mesh.emissive_intensity == 2 + assert mesh.roughness == 1 + assert mesh.metalness == 5 + if is_scatter == False: + assert mesh.cast_shadow == True + assert mesh.receive_shadow == True + + ipyvolume.figure() + mesh = ipyvolume.plot_mesh(0,0,0) + test_material_components(mesh) + + surf = ipyvolume.plot_surface(0,0,0) + test_material_components(surf) + + trisurf = ipyvolume.plot_trisurf(0,0,0) + test_material_components(trisurf) + + scatter = ipyvolume.plot_mesh(0,0,0) + test_material_components(scatter, True) + + +def test_light_components(): + ambient = ipyvolume.ambient_light() + assert ambient.light_type == 'AMBIENT' + assert ambient.light_color == 'white' + assert ambient.intensity == 1 + assert ambient.cast_shadow == False + + hemisphere = ipyvolume.hemisphere_light() + assert hemisphere.light_color == 'white' + assert hemisphere.light_color2 == 'red' + assert hemisphere.intensity == 1 + assert hemisphere.position_x == 0 + assert hemisphere.position_y == 1 + assert hemisphere.position_z == 0 + assert hemisphere.cast_shadow == False + + directional = ipyvolume.directional_light() + assert directional.light_color == 'white' + assert directional.intensity == 1 + assert directional.position_x == 0 + assert directional.position_y == 1 + assert directional.position_z == 0 + assert directional.target_x == 0 + assert directional.target_y == 0 + assert directional.target_z == 0 + assert directional.cast_shadow==False + assert directional.shadow_map_size==512 + assert directional.shadow_bias==-0.0005 + assert directional.shadow_radius==1 + assert directional.shadow_camera_near==0.5 + assert directional.shadow_camera_far==500 + assert directional.shadow_camera_orthographic_size==100 + assert directional.shadow_map_type=='PCF_SOFT' + + point = ipyvolume.point_light() + assert point.light_color == 'white' + assert point.intensity == 1 + assert point.position_x == 0 + assert point.position_y == 1 + assert point.position_z == 0 + assert point.angle==math.pi/3 + assert point.distance==0 + assert point.decay==1 + assert point.cast_shadow==False + assert point.shadow_map_size==512 + assert point.shadow_bias==-0.0005 + assert point.shadow_radius==1 + assert point.shadow_camera_near==0.5 + assert point.shadow_camera_far==500 + assert point.shadow_map_type=='PCF_SOFT' + + spot = ipyvolume.spot_light() + assert spot.light_color == 'white' + assert spot.intensity == 1 + assert spot.position_x == 0 + assert spot.position_y == 1 + assert spot.position_z == 0 + assert spot.target_x == 0 + assert spot.target_y == 0 + assert spot.target_z == 0 + assert spot.angle==math.pi/3 + assert spot.distance==0 + assert spot.decay==1 + assert spot.penumbra==0 + assert spot.cast_shadow==False + assert spot.shadow_map_size==512 + assert spot.shadow_bias==-0.0005 + assert spot.shadow_radius==1 + assert spot.shadow_camera_near==0.5 + assert spot.shadow_camera_far==500 + assert spot.shadow_camera_perspective_fov==50 + assert spot.shadow_camera_perspective_aspect==1 + assert spot.shadow_map_type=='PCF_SOFT' From e1fe83829c87d560ef7a7a5c7d043485c0db3649 Mon Sep 17 00:00:00 2001 From: rinftech-github Date: Wed, 1 Apr 2020 03:00:31 +0300 Subject: [PATCH 032/130] Add/update function description for plot_mesh, plot_surface, plot_trisurf, scatter and all light functions. pylab cleanup. Update lighting demo notebook --- ipyvolume/pylab.py | 243 +++++++++++++++++----------------- notebooks/lighting_demo.ipynb | 189 ++++++-------------------- 2 files changed, 164 insertions(+), 268 deletions(-) diff --git a/ipyvolume/pylab.py b/ipyvolume/pylab.py index 5f40e64e..3daae28d 100644 --- a/ipyvolume/pylab.py +++ b/ipyvolume/pylab.py @@ -325,7 +325,6 @@ def plot_trisurf( v=None, texture=None, lighting_model='DEFAULT', - #diffuse_color='white', opacity=1, specular_color='white', shininess=1, @@ -362,6 +361,16 @@ def plot_trisurf( :param u: {u} :param v: {v} :param texture: {texture} + :param lighting_model: The lighting model used to calculate the final color of the mesh. Can be 'DEFAULT', 'LAMBERT', 'PHONG', 'PHYSICAL'. implicit 'DEFAULT'. Will be automatically updated to 'PHYSICAL' if a light is added to figure + :param opacity: 0 - Mesh is fully transparent; 1 - Mesh is fully opaque + :param specular_color: {color} Color of the specular tint. Default 'white'. Only for 'PHONG' lighting model + :param shininess: Specular intensity. Default is 1 + :param emissive_color: {color} Emissive (light) color of the material, essentially a solid color unaffected by other lighting. Default is 'black' + :param emissive_intensity: Factor multiplied with emissive_color. Takes values between 0 and 1. Default is 1 + :param roughness: How rough the material appears. 0.0 means a smooth mirror reflection, 1.0 means fully diffuse. Default is 1. Only for 'PHYSICAL' lighting model + :param metalness: How much the material is like a metal. Non-metallic materials such as wood or stone use 0.0, metallic use 1.0, with nothing (usually) in between + :param cast_shadow: Property of a mesh to cast shadows. Default False. Works only with Directional, Point and Spot lights + :param receive_shadow: Property of a mesh to receive shadows. Default False. Works only with Directional, Point and Spot lights :return: :any:`Mesh` """ fig = gcf() @@ -379,7 +388,6 @@ def plot_trisurf( u=u, v=v, texture=texture, lighting_model=lighting_model, - #diffuse_color=diffuse_color, opacity=opacity, specular_color=specular_color, shininess=shininess, @@ -405,7 +413,6 @@ def plot_surface( wrapx=False, wrapy=False, lighting_model='DEFAULT', - #diffuse_color='white', opacity=1, specular_color='white', shininess=1, @@ -424,6 +431,16 @@ def plot_surface( :param color: {color2d} :param bool wrapx: when True, the x direction is assumed to wrap, and polygons are drawn between the end end begin points :param bool wrapy: simular for the y coordinate + :param lighting_model: The lighting model used to calculate the final color of the mesh. Can be 'DEFAULT', 'LAMBERT', 'PHONG', 'PHYSICAL'. implicit 'DEFAULT'. Will be automatically updated to 'PHYSICAL' if a light is added to figure + :param opacity: 0 - Mesh is fully transparent; 1 - Mesh is fully opaque + :param specular_color: {color} Color of the specular tint. Default 'white'. Only for 'PHONG' lighting model + :param shininess: Specular intensity. Default is 1 + :param emissive_color: {color} Emissive (light) color of the material, essentially a solid color unaffected by other lighting. Default is 'black' + :param emissive_intensity: Factor multiplied with emissive_color. Takes values between 0 and 1. Default is 1 + :param roughness: How rough the material appears. 0.0 means a smooth mirror reflection, 1.0 means fully diffuse. Default is 1. Only for 'PHYSICAL' lighting model + :param metalness: How much the material is like a metal. Non-metallic materials such as wood or stone use 0.0, metallic use 1.0, with nothing (usually) in between + :param cast_shadow: Property of a mesh to cast shadows. Default False. Works only with Directional, Point and Spot lights + :param receive_shadow: Property of a mesh to receive shadows. Default False. Works only with Directional, Point and Spot lights :return: :any:`Mesh` """ return plot_mesh( @@ -435,7 +452,6 @@ def plot_surface( wrapy=wrapy, wireframe=False, lighting_model=lighting_model, - #diffuse_color=diffuse_color, opacity=opacity, specular_color=specular_color, shininess=shininess, @@ -465,9 +481,18 @@ def plot_wireframe(x, y, z, color=default_color, wrapx=False, wrapy=False): def plot_mesh( - x, y, z, color=default_color, wireframe=True, surface=True, wrapx=False, wrapy=False, u=None, v=None, texture=None, + x, + y, + z, + color=default_color, + wireframe=True, + surface=True, + wrapx=False, + wrapy=False, + u=None, + v=None, + texture=None, lighting_model='DEFAULT', - #diffuse_color='white', opacity=1, specular_color='white', shininess=1, @@ -487,10 +512,20 @@ def plot_mesh( :param bool wireframe: draw lines between the vertices :param bool surface: draw faces/triangles between the vertices :param bool wrapx: when True, the x direction is assumed to wrap, and polygons are drawn between the begin and end points - :param boool wrapy: idem for y + :param bool wrapy: idem for y :param u: {u} :param v: {v} :param texture: {texture} + :param lighting_model: The lighting model used to calculate the final color of the mesh. Can be 'DEFAULT', 'LAMBERT', 'PHONG', 'PHYSICAL'. implicit 'DEFAULT'. Will be automatically updated to 'PHYSICAL' if a light is added to figure + :param opacity: 0 - Mesh is fully transparent; 1 - Mesh is fully opaque + :param specular_color: {color} Color of the specular tint. Default 'white'. Only for 'PHONG' lighting model + :param shininess: Specular intensity. Default is 1 + :param emissive_color: {color} Emissive (light) color of the material, essentially a solid color unaffected by other lighting. Default is 'black' + :param emissive_intensity: Factor multiplied with emissive_color. Takes values between 0 and 1. Default is 1 + :param roughness: How rough the material appears. 0.0 means a smooth mirror reflection, 1.0 means fully diffuse. Default is 1. Only for 'PHYSICAL' lighting model + :param metalness: How much the material is like a metal. Non-metallic materials such as wood or stone use 0.0, metallic use 1.0, with nothing (usually) in between + :param cast_shadow: Property of a mesh to cast shadows. Default False. Works only with Directional, Point and Spot lights + :param receive_shadow: Property of a mesh to receive shadows. Default False. Works only with Directional, Point and Spot lights :return: :any:`Mesh` """ fig = gcf() @@ -565,7 +600,6 @@ def reshape_color(ar): v=v, texture=texture, lighting_model=lighting_model, - #diffuse_color=diffuse_color, opacity=opacity, specular_color=specular_color, shininess=shininess, @@ -586,17 +620,6 @@ def plot( y, z, color=default_color, - lighting_model='DEFAULT', - #diffuse_color='white', - opacity=1, - specular_color='white', - shininess=1, - emissive_color='black', - emissive_intensity=1, - roughness=0, - metalness=0, - cast_shadow=False, - receive_shadow=False, **kwargs): """Plot a line in 3d. @@ -618,17 +641,6 @@ def plot( y=y, z=z, color=color, - lighting_model=lighting_model, - #diffuse_color=diffuse_color, - opacity=opacity, - specular_color=specular_color, - shininess=shininess, - emissive_color=emissive_color, - emissive_intensity=emissive_intensity, - roughness=roughness, - metalness=metalness, - cast_shadow=cast_shadow, - receive_shadow=receive_shadow, **kwargs) s.material.visible = False fig.scatters = fig.scatters + [s] @@ -648,16 +660,11 @@ def scatter( selection=None, grow_limits=True, lighting_model='DEFAULT', - #diffuse_color='white', opacity=1, - specular_color='white', - shininess=1, emissive_color='black', emissive_intensity=1, roughness=0, metalness=0, - cast_shadow=False, - receive_shadow=False, **kwargs ): """Plot many markers/symbols in 3d. @@ -672,6 +679,12 @@ def scatter( :param marker: {marker} :param selection: numpy array of shape (N,) or (S, N) with indices of x,y,z arrays of the selected markers, which can have a different size and color + :param lighting_model: The lighting model used to calculate the final color of the mesh. Can be 'DEFAULT', 'PHYSICAL'. implicit 'DEFAULT'. Will be automatically updated to 'PHYSICAL' if a light is added to figure + :param opacity: 0 - Mesh is fully transparent; 1 - Mesh is fully opaque + :param emissive_color: {color} Emissive (light) color of the material, essentially a solid color unaffected by other lighting. Default is 'black' + :param emissive_intensity: Factor multiplied with emissive_color. Takes values between 0 and 1. Default is 1 + :param roughness: How rough the material appears. 0.0 means a smooth mirror reflection, 1.0 means fully diffuse. Default is 1. Only for 'PHYSICAL' lighting model + :param metalness: How much the material is like a metal. Non-metallic materials such as wood or stone use 0.0, metallic use 1.0, with nothing (usually) in between :param kwargs: :return: :any:`Scatter` """ @@ -689,16 +702,11 @@ def scatter( geo=marker, selection=selection, lighting_model=lighting_model, - #diffuse_color=diffuse_color, opacity=opacity, - specular_color=specular_color, - shininess=shininess, emissive_color=emissive_color, emissive_intensity=emissive_intensity, roughness=roughness, metalness=metalness, - cast_shadow=cast_shadow, - receive_shadow=receive_shadow, **kwargs ) fig.scatters = fig.scatters + [s] @@ -718,17 +726,6 @@ def quiver( color=default_color, color_selected=default_color_selected, marker="arrow", - lighting_model='DEFAULT', - #diffuse_color='white', - opacity=1, - specular_color='white', - shininess=1, - emissive_color='black', - emissive_intensity=1, - roughness=0, - metalness=0, - cast_shadow=False, - receive_shadow=False, **kwargs ): """Create a quiver plot, which is like a scatter plot but with arrows pointing in the direction given by u, v and w. @@ -763,17 +760,6 @@ def quiver( color_selected=color_selected, size_selected=size_selected, geo=marker, - lighting_model=lighting_model, - #diffuse_color=diffuse_color, - opacity=opacity, - specular_color=specular_color, - shininess=shininess, - emissive_color=emissive_color, - emissive_intensity=emissive_intensity, - roughness=roughness, - metalness=metalness, - cast_shadow=cast_shadow, - receive_shadow=receive_shadow, **kwargs ) fig.scatters = fig.scatters + [s] @@ -1695,9 +1681,13 @@ def _make_triangles_lines(shape, wrapx=False, wrapy=False): def ambient_light( light_color=default_color_selected, intensity = 1): - print("ADD AMBIENT LIGHT (from pylab) ") - print("light_color: " + str(light_color)) - print("intensity: " + str(intensity)) + """Create a new Ambient Light + An Ambient Light source represents an omni-directional, fixed-intensity and fixed-color light source that affects all objects in the scene equally (is omni-present). + This light cannot be used to cast shadows. + :param light_color: {color} Color of the Ambient Light. Default 'white' + :param intensity: Factor used to increase or decrease the Ambient Light intensity. Default is 1 + :return: :any:`Light` + """ light = ipv.Light( light_type='AMBIENT', @@ -1706,7 +1696,7 @@ def ambient_light( fig = gcf() fig.lights = fig.lights + [light] - #print(fig.lights) + return light def hemisphere_light( @@ -1714,13 +1704,15 @@ def hemisphere_light( light_color2=default_color, intensity = 1, position=[0, 1, 0]): - - print("ADD HEMISPHERE LIGHT (from pylab) ") - print("light_color: " + str(light_color)) - print("light_color2: " + str(light_color2)) - print("intensity: " + str(intensity)) - print("position: " + str(position[0])+" "+str(position[1])+" "+str(position[2])) - + """Create a new Hemisphere Light + A light source positioned directly above the scene, with color fading from the sky color to the ground color. + This light cannot be used to cast shadows. + :param light_color: {color} Sky color. Default 'white' + :param light_color2: {color} Ground color. Default 'red' + :param intensity: Factor used to increase or decrease the Hemisphere Light intensity. Default is 1 + :param position: 3-element array (x y z) which describes the position of the Hemisphere Light. Default [0 1 0] + :return: :any:`Light` + """ light = ipv.Light( light_type='HEMISPHERE', light_color=light_color, @@ -1748,20 +1740,23 @@ def directional_light( shadow_camera_far=500, shadow_camera_orthographic_size=100, shadow_map_type='PCF_SOFT'): - - print("ADD DIRECTIONAL LIGHT (from pylab) ") - print("light_color: " + str(light_color)) - print("intensity: " + str(intensity)) - print("position: " + str(position[0])+" "+str(position[1])+" "+str(position[2])) - print("target: " + str(target[0])+" "+str(target[1])+" "+str(target[2])) - print("cast_shadow: " + str(cast_shadow)) - if cast_shadow: - print("shadow_map_size: " + str(shadow_map_size)) - print("shadow_bias: " + str(shadow_bias)) - print("shadow_radius: " + str(shadow_radius)) - print("shadow_camera_near: " + str(shadow_camera_near)) - print("shadow_camera_far: " + str(shadow_camera_far)) - print("shadow_map_type: " + str(shadow_map_type)) + """Create a new Directional Light + A Directional Light source illuminates all objects equally from a given direction. + This light can be used to cast shadows. + :param light_color: {color} Color of the Directional Light. Default 'white' + :param intensity: Factor used to increase or decrease the Directional Light intensity. Default is 1 + :param position: 3-element array (x y z) which describes the position of the Directional Light. Default [0 1 0] + :param target: 3-element array (x y z) which describes the target of the Directional Light. Default [0 0 0] + :param cast_shadow: Property of a Directional Light to cast shadows. Default False + :param shadow_map_size: Property of a Directional Light to cast shadows. Default False + :param shadow_bias: Factor used to reduce shadow acne. Default is -0.0005 + :param shadow_radius: Setting this to values greater than 1 will blur the edges of the shadow. Default is 1 + :param shadow_camera_near: Camera near factor. Default is 0.5 + :param shadow_camera_far: Camera far factor. Default is 500 + :param shadow_camera_orthographic_size: Size of the shadow orthographic camera. Directional Light only. Default is 100 + :param shadow_map_type: Shadow map type. Can be 'BASIC', 'PCF', 'PCF_SOFT'. Default is 'PCF_SOFT' + :return: :any:`Light` + """ light = ipv.Light( light_type='DIRECTIONAL', @@ -1805,26 +1800,28 @@ def spot_light( shadow_camera_perspective_fov=50, shadow_camera_perspective_aspect=1, shadow_map_type='PCF_SOFT'): - - print("ADD SPOT LIGHT (from pylab) ") - print("light_color: " + str(light_color)) - print("intensity: " + str(intensity)) - print("position: " + str(position[0])+" "+str(position[1])+" "+str(position[2])) - print("target: " + str(target[0])+" "+str(target[1])+" "+str(target[2])) - print("angle: " + str(angle)) - print("distance: " + str(distance)) - print("decay: " + str(decay)) - print("penumbra: " + str(penumbra)) - print("cast_shadow: " + str(cast_shadow)) - if cast_shadow: - print("shadow_map_size: " + str(shadow_map_size)) - print("shadow_bias: " + str(shadow_bias)) - print("shadow_radius: " + str(shadow_radius)) - print("shadow_camera_near: " + str(shadow_camera_near)) - print("shadow_camera_far: " + str(shadow_camera_far)) - print("shadow_camera_perspective_fov: " + str(shadow_camera_perspective_fov)) - print("shadow_camera_perspective_aspect: " + str(shadow_camera_perspective_aspect)) - print("shadow_map_type: " + str(shadow_map_type)) + """Create a new Spot Light + A Spot Light produces a directed cone of light. The light becomes more intense closer to the spotlight source and to the center of the light cone. + This light can be used to cast shadows. + :param light_color: {color} Color of the Spot Light. Default 'white' + :param intensity: Factor used to increase or decrease the Spot Light intensity. Default is 1 + :param position: 3-element array (x y z) which describes the position of the Spot Light. Default [0 1 0] + :param target: 3-element array (x y z) which describes the target of the Spot Light. Default [0 0 0] + :param angle: Spot Light angle. Default is Pi/3 + :param distance: When distance is non-zero, light will attenuate linearly from maximum intensity at the light's position down to zero at this distance from the light. + :param decay: The amount the light dims along the distance of the light. Default is 1. For physically correct lighting, set this to 2 + :param penumbra: Percent of the spotlight cone that is attenuated due to penumbra. Takes values between zero and 1. The default is 0.0. + :param cast_shadow: Property of a Spot Light to cast shadows. Default False + :param shadow_map_size: Property of a Spot Light to cast shadows. Default False + :param shadow_bias: Factor used to reduce shadow acne. Default is -0.0005 + :param shadow_radius: Setting this to values greater than 1 will blur the edges of the shadow. Default is 1 + :param shadow_camera_near: Camera near factor. Default is 0.5 + :param shadow_camera_far: Camera far factor. Default is 500 + :param shadow_camera_perspective_fov: Shadow perspective camera field of view angle. Default is 50. Spot Light only. + :param shadow_camera_perspective_aspect: Shadow perspective camera aspect ratio. Default is 1. Spot Light only. + :param shadow_map_type: Shadow map type. Can be 'BASIC', 'PCF', 'PCF_SOFT'. Default is 'PCF_SOFT' + :return: :any:`Light` + """ light = ipv.Light( light_type='SPOT', @@ -1868,21 +1865,23 @@ def point_light( shadow_camera_near=0.5, shadow_camera_far=500, shadow_map_type='PCF_SOFT'): - - print("ADD POINT LIGHT (from pylab) ") - print("light_color: " + str(light_color)) - print("intensity: " + str(intensity)) - print("position: " + str(position[0])+" "+str(position[1])+" "+str(position[2])) - print("distance: " + str(distance)) - print("decay: " + str(decay)) - print("cast_shadow: " + str(cast_shadow)) - if cast_shadow: - print("shadow_map_size: " + str(shadow_map_size)) - print("shadow_bias: " + str(shadow_bias)) - print("shadow_radius: " + str(shadow_radius)) - print("shadow_camera_near: " + str(shadow_camera_near)) - print("shadow_camera_far: " + str(shadow_camera_far)) - print("shadow_map_type: " + str(shadow_map_type)) + """Create a new Point Light + A Point Light originates from a single point and spreads outward in all directions. + This light can be used to cast shadows. + :param light_color: {color} Color of the Point Light. Default 'white' + :param intensity: Factor used to increase or decrease the Point Light intensity. Default is 1 + :param position: 3-element array (x y z) which describes the position of the Point Light. Default [0 1 0] + :param distance: Maximum range of the light. Default is 0 (no limit). + :param decay: The amount the light dims along the distance of the light. Default is 1. For physically correct lighting, set this to 2 + :param cast_shadow: Property of a Point Light to cast shadows. Default False + :param shadow_map_size: Property of a Point Light to cast shadows. Default False + :param shadow_bias: Factor used to reduce shadow acne. Default is -0.0005 + :param shadow_radius: Setting this to values greater than 1 will blur the edges of the shadow. Default is 1 + :param shadow_camera_near: Camera near factor. Default is 0.5 + :param shadow_camera_far: Camera far factor. Default is 500 + :param shadow_map_type: Shadow map type. Can be 'BASIC', 'PCF', 'PCF_SOFT'. Default is 'PCF_SOFT' + :return: :any:`Light` + """ light = ipv.Light( light_type='POINT', diff --git a/notebooks/lighting_demo.ipynb b/notebooks/lighting_demo.ipynb index 54ab55fd..59086cd5 100644 --- a/notebooks/lighting_demo.ipynb +++ b/notebooks/lighting_demo.ipynb @@ -2,7 +2,7 @@ "cells": [ { "cell_type": "code", - "execution_count": 6, + "execution_count": 1, "metadata": {}, "outputs": [], "source": [ @@ -216,13 +216,13 @@ }, { "cell_type": "code", - "execution_count": 7, + "execution_count": 2, "metadata": {}, "outputs": [ { "data": { "application/vnd.jupyter.widget-view+json": { - "model_id": "ce06f1e3a03e46a5902ae64a87e9d5c9", + "model_id": "ee958a7eb4de486dbfc1a4a13b0ef0af", "version_major": 2, "version_minor": 0 }, @@ -240,7 +240,7 @@ }, { "cell_type": "code", - "execution_count": 8, + "execution_count": 3, "metadata": {}, "outputs": [], "source": [ @@ -260,46 +260,9 @@ }, { "cell_type": "code", - "execution_count": 9, + "execution_count": 4, "metadata": {}, - "outputs": [ - { - "name": "stdout", - "output_type": "stream", - "text": [ - "ADD DIRECTIONAL LIGHT (from pylab) \n", - "light_color: green\n", - "intensity: 1.0\n", - "position: 30 30 30\n", - "target: 0 20 30\n", - "cast_shadow: True\n", - "shadow_map_size: 512\n", - "shadow_bias: -0.005\n", - "shadow_radius: 1\n", - "shadow_camera_near: 0.5\n", - "shadow_camera_far: 500\n", - "shadow_map_type: PCF_SOFT\n", - "ADD SPOT LIGHT (from pylab) \n", - "light_color: yellow\n", - "intensity: 1\n", - "position: 20 30 20\n", - "target: -20 -20 -20\n", - "angle: 0.3490658503988659\n", - "distance: 200\n", - "decay: 1\n", - "penumbra: 0.5\n", - "cast_shadow: True\n", - "shadow_map_size: 1024\n", - "shadow_bias: -0.0005\n", - "shadow_radius: 1\n", - "shadow_camera_near: 0.5\n", - "shadow_camera_far: 500\n", - "shadow_camera_perspective_fov: 50\n", - "shadow_camera_perspective_aspect: 1\n", - "shadow_map_type: PCF_SOFT\n" - ] - } - ], + "outputs": [], "source": [ "lights_dir_spot()" ] @@ -313,7 +276,7 @@ }, { "cell_type": "code", - "execution_count": 10, + "execution_count": 5, "metadata": {}, "outputs": [], "source": [ @@ -323,7 +286,7 @@ }, { "cell_type": "code", - "execution_count": 11, + "execution_count": 6, "metadata": {}, "outputs": [], "source": [ @@ -332,7 +295,7 @@ }, { "cell_type": "code", - "execution_count": 12, + "execution_count": 7, "metadata": {}, "outputs": [], "source": [ @@ -349,7 +312,7 @@ }, { "cell_type": "code", - "execution_count": 13, + "execution_count": 8, "metadata": {}, "outputs": [], "source": [ @@ -368,7 +331,7 @@ }, { "cell_type": "code", - "execution_count": 16, + "execution_count": 9, "metadata": {}, "outputs": [], "source": [ @@ -385,7 +348,7 @@ }, { "cell_type": "code", - "execution_count": 17, + "execution_count": 10, "metadata": {}, "outputs": [], "source": [ @@ -409,7 +372,7 @@ }, { "cell_type": "code", - "execution_count": 18, + "execution_count": 11, "metadata": {}, "outputs": [], "source": [ @@ -427,7 +390,7 @@ }, { "cell_type": "code", - "execution_count": 19, + "execution_count": 12, "metadata": {}, "outputs": [], "source": [ @@ -445,13 +408,13 @@ }, { "cell_type": "code", - "execution_count": 20, + "execution_count": 13, "metadata": {}, "outputs": [ { "data": { "application/vnd.jupyter.widget-view+json": { - "model_id": "63932e5c16534f79953b79ccf1251554", + "model_id": "49d92fcb8a5b4b2ba657ffbcf4c61575", "version_major": 2, "version_minor": 0 }, @@ -471,19 +434,9 @@ }, { "cell_type": "code", - "execution_count": 21, + "execution_count": 14, "metadata": {}, - "outputs": [ - { - "name": "stdout", - "output_type": "stream", - "text": [ - "ADD AMBIENT LIGHT (from pylab) \n", - "light_color: white\n", - "intensity: 1\n" - ] - } - ], + "outputs": [], "source": [ "# light_color - default white\n", "# intensity - default 1\n", @@ -492,7 +445,7 @@ }, { "cell_type": "code", - "execution_count": 22, + "execution_count": 15, "metadata": {}, "outputs": [], "source": [ @@ -509,13 +462,13 @@ }, { "cell_type": "code", - "execution_count": 23, + "execution_count": 16, "metadata": {}, "outputs": [ { "data": { "application/vnd.jupyter.widget-view+json": { - "model_id": "a05b70acf3894bc5bf615a9bb3ecf34c", + "model_id": "4ff0692662ca44b7bda6b6c6de6d677c", "version_major": 2, "version_minor": 0 }, @@ -539,29 +492,16 @@ }, { "cell_type": "code", - "execution_count": 24, + "execution_count": 17, "metadata": {}, - "outputs": [ - { - "name": "stdout", - "output_type": "stream", - "text": [ - "ADD HEMISPHERE LIGHT (from pylab) \n", - "light_color: white\n", - "light_color2: red\n", - "intensity: 1\n", - "position: 0 1 0\n", - "cast_shadow: False\n" - ] - } - ], + "outputs": [], "source": [ "hemisphere1 = p3.hemisphere_light()" ] }, { "cell_type": "code", - "execution_count": 25, + "execution_count": 18, "metadata": {}, "outputs": [], "source": [ @@ -582,13 +522,13 @@ }, { "cell_type": "code", - "execution_count": 26, + "execution_count": 19, "metadata": {}, "outputs": [ { "data": { "application/vnd.jupyter.widget-view+json": { - "model_id": "cf8b66afc5b24cd18815d84528055097", + "model_id": "c819fe6f700a4134bc1d20794d215cf4", "version_major": 2, "version_minor": 0 }, @@ -598,18 +538,6 @@ }, "metadata": {}, "output_type": "display_data" - }, - { - "name": "stdout", - "output_type": "stream", - "text": [ - "ADD DIRECTIONAL LIGHT (from pylab) \n", - "light_color: white\n", - "intensity: 1\n", - "position: 0 1 0\n", - "target: 0 0 0\n", - "cast_shadow: False\n" - ] } ], "source": [ @@ -634,7 +562,7 @@ }, { "cell_type": "code", - "execution_count": 27, + "execution_count": 20, "metadata": {}, "outputs": [], "source": [ @@ -644,7 +572,7 @@ }, { "cell_type": "code", - "execution_count": 28, + "execution_count": 21, "metadata": {}, "outputs": [], "source": [ @@ -660,7 +588,7 @@ }, { "cell_type": "code", - "execution_count": 29, + "execution_count": 22, "metadata": {}, "outputs": [], "source": [ @@ -687,13 +615,13 @@ }, { "cell_type": "code", - "execution_count": 30, + "execution_count": 23, "metadata": {}, "outputs": [ { "data": { "application/vnd.jupyter.widget-view+json": { - "model_id": "6909d1e5368148c29518e41d3187b783", + "model_id": "8704c06726ca4234b216c5f3b9323c01", "version_major": 2, "version_minor": 0 }, @@ -703,19 +631,6 @@ }, "metadata": {}, "output_type": "display_data" - }, - { - "name": "stdout", - "output_type": "stream", - "text": [ - "ADD POINT LIGHT (from pylab) \n", - "light_color: white\n", - "intensity: 1\n", - "position: 0 1 0\n", - "distance: 0\n", - "decay: 1\n", - "cast_shadow: False\n" - ] } ], "source": [ @@ -739,7 +654,7 @@ }, { "cell_type": "code", - "execution_count": 31, + "execution_count": 24, "metadata": {}, "outputs": [], "source": [ @@ -750,7 +665,7 @@ }, { "cell_type": "code", - "execution_count": 32, + "execution_count": 25, "metadata": {}, "outputs": [], "source": [ @@ -759,7 +674,7 @@ }, { "cell_type": "code", - "execution_count": 33, + "execution_count": 26, "metadata": {}, "outputs": [], "source": [ @@ -770,7 +685,7 @@ }, { "cell_type": "code", - "execution_count": 34, + "execution_count": 27, "metadata": {}, "outputs": [], "source": [ @@ -780,7 +695,7 @@ }, { "cell_type": "code", - "execution_count": 35, + "execution_count": 28, "metadata": {}, "outputs": [], "source": [ @@ -806,13 +721,13 @@ }, { "cell_type": "code", - "execution_count": 36, + "execution_count": 29, "metadata": {}, "outputs": [ { "data": { "application/vnd.jupyter.widget-view+json": { - "model_id": "9178f036b8a24ebf83e9d5d84c9da21b", + "model_id": "d3e12c32718641939e1b4cad33f9507b", "version_major": 2, "version_minor": 0 }, @@ -822,22 +737,6 @@ }, "metadata": {}, "output_type": "display_data" - }, - { - "name": "stdout", - "output_type": "stream", - "text": [ - "ADD SPOT LIGHT (from pylab) \n", - "light_color: white\n", - "intensity: 1\n", - "position: 0 1 0\n", - "target: 0 0 0\n", - "angle: 1.0471975511965976\n", - "distance: 0\n", - "decay: 1\n", - "penumbra: 0\n", - "cast_shadow: False\n" - ] } ], "source": [ @@ -867,7 +766,7 @@ }, { "cell_type": "code", - "execution_count": 37, + "execution_count": 30, "metadata": {}, "outputs": [], "source": [ @@ -886,25 +785,23 @@ }, { "cell_type": "code", - "execution_count": 38, + "execution_count": 31, "metadata": {}, "outputs": [], "source": [ "# Default mode — When distance is zero, light does not attenuate. \n", "# When distance is non-zero, light will attenuate linearly from maximum intensity at the \n", "# light's position down to zero at this distance from the light.\n", - "\n", "# Physically correct mode — When distance is zero, light will attenuate according to inverse-square law to infinite distance. \n", "# When distance is non-zero, light will attenuate according to inverse-square law until near the distance cutoff, \n", "# where it will then attenuate quickly and smoothly to 0. Inherently, cutoffs are not physically correct.\n", "# Default is 0.0.\n", - "\n", "spot1.distance=200" ] }, { "cell_type": "code", - "execution_count": 39, + "execution_count": 33, "metadata": {}, "outputs": [], "source": [ @@ -915,7 +812,7 @@ }, { "cell_type": "code", - "execution_count": 40, + "execution_count": 34, "metadata": {}, "outputs": [], "source": [ @@ -926,7 +823,7 @@ }, { "cell_type": "code", - "execution_count": 41, + "execution_count": 35, "metadata": {}, "outputs": [], "source": [ From d3a8979fc341ad150e36d5f412e5a350e26efa75 Mon Sep 17 00:00:00 2001 From: rinftech-github Date: Wed, 1 Apr 2020 03:33:34 +0300 Subject: [PATCH 033/130] Code cleanup --- ipyvolume/widgets.py | 5 +--- js/glsl/mesh-fragment-lambert.glsl | 3 --- js/glsl/mesh-fragment-phong.glsl | 5 +--- js/glsl/mesh-fragment-physical.glsl | 5 +--- js/glsl/mesh-vertex-lambert.glsl | 2 -- js/glsl/mesh-vertex-phong.glsl | 2 -- js/glsl/mesh-vertex-physical.glsl | 2 -- js/glsl/scatter-fragment-physical.glsl | 24 ------------------- js/src/light.ts | 29 +---------------------- js/src/mesh.ts | 11 ++++----- js/src/scatter.ts | 32 ++++++++------------------ 11 files changed, 18 insertions(+), 102 deletions(-) diff --git a/ipyvolume/widgets.py b/ipyvolume/widgets.py index cd83c9cb..364cf129 100644 --- a/ipyvolume/widgets.py +++ b/ipyvolume/widgets.py @@ -65,7 +65,6 @@ class Mesh(widgets.Widget): visible = traitlets.CBool(default_value=True).tag(sync=True) lighting_model = traitlets.Enum(values=['DEFAULT', 'LAMBERT', 'PHONG', 'PHYSICAL'], default_value='DEFAULT').tag(sync=True) - #diffuse_color = Array(default_value="white", allow_none=True).tag(sync=True, **color_serialization) opacity = traitlets.CFloat(1).tag(sync=True) specular_color = Array(default_value="white", allow_none=True).tag(sync=True, **color_serialization) shininess = traitlets.CFloat(1).tag(sync=True) @@ -132,7 +131,6 @@ class Scatter(widgets.Widget): visible = traitlets.CBool(default_value=True).tag(sync=True) lighting_model = traitlets.Enum(values=['DEFAULT', 'PHYSICAL'], default_value='DEFAULT').tag(sync=True) - #diffuse_color = Array(default_value="white", allow_none=True).tag(sync=True, **color_serialization) opacity = traitlets.CFloat(1).tag(sync=True) specular_color = Array(default_value="white", allow_none=True).tag(sync=True, **color_serialization) shininess = traitlets.CFloat(1).tag(sync=True) @@ -259,8 +257,7 @@ class Light(widgets.Widget): shadow_map_type = traitlets.Enum(values=['BASIC', 'PCF', 'PCF_SOFT'], default_value='PCF_SOFT').tag(sync=True) cast_shadow = traitlets.Bool(False).tag(sync=True) - # TODO - should use array serialization instead ? - #position = Array(default_value=(0, 1, 0), allow_none=True).tag(sync=True, **array_serialization) + position_x = traitlets.CFloat(0).tag(sync=True) position_y = traitlets.CFloat(1).tag(sync=True) position_z = traitlets.CFloat(0).tag(sync=True) diff --git a/js/glsl/mesh-fragment-lambert.glsl b/js/glsl/mesh-fragment-lambert.glsl index 2c9a4ae8..3d81b8f3 100644 --- a/js/glsl/mesh-fragment-lambert.glsl +++ b/js/glsl/mesh-fragment-lambert.glsl @@ -121,7 +121,4 @@ void main() #include #include - //gl_FragColor = 0.75 * gl_FragColor + 0.25 * finalColor2; - //gl_FragColor = 0.25 * gl_FragColor + 0.75 * finalColor2; - } \ No newline at end of file diff --git a/js/glsl/mesh-fragment-phong.glsl b/js/glsl/mesh-fragment-phong.glsl index dfb24674..149d0252 100644 --- a/js/glsl/mesh-fragment-phong.glsl +++ b/js/glsl/mesh-fragment-phong.glsl @@ -73,7 +73,7 @@ void main() #include - vec4 diffuseColor = vec4( vec3(1,1,1), opacity );//0.75 * finalColor2 + 0.25 * vec4( diffuse, opacity ); + vec4 diffuseColor = vec4( vec3(1,1,1), opacity ); ReflectedLight reflectedLight = ReflectedLight( vec3( 0.0 ), vec3( 0.0 ), vec3( 0.0 ), vec3( 0.0 ) ); vec3 totalEmissiveRadiance = emissive * emissiveIntensity; @@ -109,7 +109,4 @@ void main() #include #include - //gl_FragColor = 0.75 * gl_FragColor + 0.25 * finalColor2; - //gl_FragColor = 0.25 * gl_FragColor + 0.75 * finalColor2; - } \ No newline at end of file diff --git a/js/glsl/mesh-fragment-physical.glsl b/js/glsl/mesh-fragment-physical.glsl index ce769134..6a90f6a3 100644 --- a/js/glsl/mesh-fragment-physical.glsl +++ b/js/glsl/mesh-fragment-physical.glsl @@ -86,7 +86,7 @@ void main() ////////////////////////////////////////////////////// #include - //diffuse = vec3(1,1,1); + vec4 diffuseColor = vec4( vec3(1,1,1), opacity ); ReflectedLight reflectedLight = ReflectedLight( vec3( 0.0 ), vec3( 0.0 ), vec3( 0.0 ), vec3( 0.0 ) ); vec3 totalEmissiveRadiance = emissive * emissiveIntensity; @@ -121,7 +121,4 @@ void main() #include #include - //gl_FragColor = 0.75 * gl_FragColor + 0.25 * finalColor2; - //gl_FragColor = 0.25 * gl_FragColor + 0.75 * finalColor2; - } \ No newline at end of file diff --git a/js/glsl/mesh-vertex-lambert.glsl b/js/glsl/mesh-vertex-lambert.glsl index 82216d6b..a4e3057e 100644 --- a/js/glsl/mesh-vertex-lambert.glsl +++ b/js/glsl/mesh-vertex-lambert.glsl @@ -89,8 +89,6 @@ void main() vec4(pos,1.0); vec3 positionEye = ( modelViewMatrix * vec4( pos, 1.0 ) ).xyz; vertex_position = positionEye; - - //vViewPosition = vertex_position;//!!! #ifdef USE_TEXTURE vertex_uv = vec2(mix(u_previous, u, animation_time_u), mix(v_previous, v, animation_time_v)); diff --git a/js/glsl/mesh-vertex-phong.glsl b/js/glsl/mesh-vertex-phong.glsl index 408bb690..78067847 100644 --- a/js/glsl/mesh-vertex-phong.glsl +++ b/js/glsl/mesh-vertex-phong.glsl @@ -96,8 +96,6 @@ void main() vec4(pos,1.0); vec3 positionEye = ( modelViewMatrix * vec4( pos, 1.0 ) ).xyz; vertex_position = positionEye; - - //vViewPosition = vertex_position;//!!! #ifdef USE_TEXTURE vertex_uv = vec2(mix(u_previous, u, animation_time_u), mix(v_previous, v, animation_time_v)); diff --git a/js/glsl/mesh-vertex-physical.glsl b/js/glsl/mesh-vertex-physical.glsl index ceeb9787..bb14840d 100644 --- a/js/glsl/mesh-vertex-physical.glsl +++ b/js/glsl/mesh-vertex-physical.glsl @@ -93,8 +93,6 @@ void main() vec4(pos,1.0); vec3 positionEye = ( modelViewMatrix * vec4( pos, 1.0 ) ).xyz; vertex_position = positionEye; - - //vViewPosition = vertex_position;//!!! #ifdef USE_TEXTURE vertex_uv = vec2(mix(u_previous, u, animation_time_u), mix(v_previous, v, animation_time_v)); diff --git a/js/glsl/scatter-fragment-physical.glsl b/js/glsl/scatter-fragment-physical.glsl index 31264290..5ad18fc9 100644 --- a/js/glsl/scatter-fragment-physical.glsl +++ b/js/glsl/scatter-fragment-physical.glsl @@ -101,37 +101,13 @@ void main(void) { #include #include //#include -//#ifdef USE_COLOR -// diffuseColor.rgb *= vec3(1.0, 0.0, 0.0); - -//#endif #include #include #include #include #include - /* -#ifdef FLAT_SHADED - - // Workaround for Adreno/Nexus5 not able able to do dFdx( vViewPosition ) ... - - vec3 fdx = vec3( dFdx( vViewPosition.x ), dFdx( vViewPosition.y ), dFdx( vViewPosition.z ) ); - vec3 fdy = vec3( dFdy( vViewPosition.x ), dFdy( vViewPosition.y ), dFdy( vViewPosition.z ) ); - vec3 normal = normalize( cross( fdx, fdy ) ); - -#else - vec3 normal = normalize( vNormal ); - - #ifdef DOUBLE_SIDED - - normal = normal * ( float( gl_FrontFacing ) * 2.0 - 1.0 ); - - #endif - -#endif -*/ #include #include diff --git a/js/src/light.ts b/js/src/light.ts index 0bd8b742..ba28612e 100644 --- a/js/src/light.ts +++ b/js/src/light.ts @@ -12,7 +12,7 @@ export class LightView extends widgets.WidgetView { renderer: FigureView; - lights: any; //TODO remove + lights: any; current_light: any; LIGHT_TYPES: any; SHADOW_MAP_TYPES: any; @@ -63,21 +63,16 @@ class LightView extends widgets.WidgetView { } on_change(attribute) { - - this.cast_shadow = this.model.get("cast_shadow"); this.renderer.renderer.shadowMap.enabled = this.cast_shadow; - console.log("CHANGE "); this.create_light(false); this.renderer.update(); - } add_to_scene() { this.lights.forEach((light) => { this.renderer.scene_scatter.add(light); }); - console.log(this.renderer.scene_scatter); } remove_from_scene() { @@ -85,18 +80,14 @@ class LightView extends widgets.WidgetView { this.lights.forEach((light) => { this.renderer.scene_scatter.remove(light); }); - } create_light(instantiate=true) { - //force meshes light model update for (let mesh_key in this.renderer.mesh_views) { - console.log(mesh_key, this.renderer.mesh_views[mesh_key]); this.renderer.mesh_views[mesh_key].force_lighting_model(); } for (let scatter_key in this.renderer.scatter_views) { - console.log(scatter_key, this.renderer.scatter_views[scatter_key]); this.renderer.scatter_views[scatter_key].force_lighting_model(); } @@ -113,7 +104,6 @@ class LightView extends widgets.WidgetView { //no shadow support if(this.light_type === this.LIGHT_TYPES.AMBIENT){ - console.log("Create Ambient Light "); if(instantiate === true){ this.current_light = new THREE.AmbientLight(this.light_color, this.intensity); this.lights.push(this.current_light); @@ -129,7 +119,6 @@ class LightView extends widgets.WidgetView { // no shadow support if(this.light_type === this.LIGHT_TYPES.HEMISPHERE) { this.light_color2 = this.model.get("light_color2"); - console.log("Create Hemisphere Light "); if(instantiate === true){ this.current_light = new THREE.HemisphereLight(this.light_color, this.light_color2, this.intensity); } @@ -168,7 +157,6 @@ class LightView extends widgets.WidgetView { } if(this.light_type === this.LIGHT_TYPES.POINT) { - console.log("Create Point Light"); if(instantiate === true){ this.current_light = new THREE.PointLight(this.light_color, this.intensity); } @@ -205,7 +193,6 @@ class LightView extends widgets.WidgetView { this.renderer.scene_scatter.add(this.target); } if(this.light_type === this.LIGHT_TYPES.DIRECTIONAL) { - console.log("Create Directional Light"); this.shadow_camera_orthographic_size = this.model.get("shadow_camera_orthographic_size"); if(instantiate === true) { @@ -231,15 +218,6 @@ class LightView extends widgets.WidgetView { this.shadow_camera_near, this.shadow_camera_far ); this.current_light.shadow.camera.position.set(this.position.x, this.position.y, this.position.z); - /* - this.current_light.shadow.camera.near = this.shadow_camera_near; - this.current_light.shadow.camera.far = this.shadow_camera_far; - this.current_light.shadow.camera.left = - this.shadow_camera_orthographic_size/2; - this.current_light.shadow.camera.right = this.shadow_camera_orthographic_size/2; - this.current_light.shadow.camera.top = this.shadow_camera_orthographic_size/2; - this.current_light.shadow.camera.bottom = - this.shadow_camera_orthographic_size/2; - this.current_light.shadow.camera.updateMatrixWorld(); - */ this.current_light.castShadow = this.cast_shadow; if(instantiate === true) { @@ -247,7 +225,6 @@ class LightView extends widgets.WidgetView { } } else if(this.light_type === this.LIGHT_TYPES.SPOT) { - console.log("Create Spot Light"); this.angle = this.model.get("angle"); this.penumbra = this.model.get("penumbra"); @@ -280,10 +257,6 @@ class LightView extends widgets.WidgetView { this.shadow_camera_far); this.current_light.shadow.camera.position.set(this.position.x, this.position.y, this.position.z); - //this.current_light.shadow.camera.near = this.shadow_camera_near; - //this.current_light.shadow.camera.far = this.shadow_camera_far; - //this.current_light.shadow.camera.aspect = this.shadow_camera_perspective_aspect; - //this.current_light.shadow.camera.fov = this.shadow_camera_perspective_fov; if(instantiate === true) { this.lights.push(this.current_light); diff --git a/js/src/mesh.ts b/js/src/mesh.ts index 085b6b79..5a988a64 100644 --- a/js/src/mesh.ts +++ b/js/src/mesh.ts @@ -143,7 +143,6 @@ class MeshView extends widgets.WidgetView { } public force_lighting_model() { - console.log("FORCE LIGHTING MODEL TO PHYSICAL"); if(this.lighting_model === this.LIGHTING_MODELS.DEFAULT){ this.model.set("lighting_model", this.LIGHTING_MODELS.PHYSICAL); this.update_visibility(); @@ -334,13 +333,13 @@ class MeshView extends widgets.WidgetView { this.line_material_rgb.copy(this.model.get("line_material").obj); } - //VERY IMPORTANT + // update material defines in order to run correct shader code this.material.defines = {USE_COLOR: true}; - // this.material_rgb.defines = {USE_RGB: true, USE_COLOR: true}; this.line_material.defines = {AS_LINE: true}; this.line_material_rgb.defines = {AS_LINE: true, USE_RGB: true, USE_COLOR: true}; this.material.extensions = {derivatives: true}; + // locally and the visible with this object's visible trait this.material.visible = this.material.visible && this.model.get("visible"); this.material_rgb.visible = this.material.visible && this.model.get("visible"); @@ -367,10 +366,10 @@ class MeshView extends widgets.WidgetView { material.fragmentShader = require("raw-loader!../glsl/mesh-fragment-physical.glsl"); } material.depthWrite = true; - material.transparant = true;//? + material.transparant = true; material.transparent = true; material.depthTest = true; - // very important + // use lighting material.lights = true; }); @@ -383,7 +382,7 @@ class MeshView extends widgets.WidgetView { this.roughness = this.model.get("roughness"); this.metalness = this.model.get("metalness"); - this.material.uniforms.diffuse.value = new THREE.Color(1, 1, 1);//this.diffuse_color//BUG? keep hardcoded + this.material.uniforms.diffuse.value = new THREE.Color(1, 1, 1);// keep hardcoded this.material.uniforms.opacity.value = this.opacity; this.material.uniforms.specular.value = new THREE.Color(this.specular_color); this.material.uniforms.shininess.value = this.shininess; diff --git a/js/src/scatter.ts b/js/src/scatter.ts index 5b4ba530..803b96b1 100644 --- a/js/src/scatter.ts +++ b/js/src/scatter.ts @@ -204,7 +204,6 @@ class ScatterView extends widgets.WidgetView { } public force_lighting_model() { - console.log("FORCE LIGHTING MODEL TO PHYSICAL"); if(this.lighting_model === this.LIGHTING_MODELS.DEFAULT){ this.model.set("lighting_model", this.LIGHTING_MODELS.PHYSICAL); this.update_visibility(); @@ -222,17 +221,17 @@ class ScatterView extends widgets.WidgetView { } add_to_scene() { - //currently, not shadow support because of InstancedBufferGeometry + //currently, no shadow support because of InstancedBufferGeometry this.cast_shadow = this.model.get("cast_shadow"); this.receive_shadow = this.model.get("receive_shadow"); - this.mesh.castShadow = false;//this.cast_shadow; - this.mesh.receiveShadow = false;//this.receive_shadow; + this.mesh.castShadow = false; + this.mesh.receiveShadow = false; this.renderer.scene_scatter.add(this.mesh); if (this.line_segments) { this.renderer.scene_scatter.add(this.line_segments); - this.line_segments.castShadow = false;//this.cast_shadow; - this.line_segments.receiveShadow = false;//this.receive_shadow; + this.line_segments.castShadow = false; + this.line_segments.receiveShadow = false; } } remove_from_scene() { @@ -346,7 +345,7 @@ class ScatterView extends widgets.WidgetView { this.line_material_rgb.visible = this.line_material.visible && this.model.get("visible"); this.lighting_model = this.model.get("lighting_model"); - console.log("------------------------- LIGHTING MODEL: " + this.lighting_model); + this.materials.forEach((material) => { if(this.lighting_model === this.LIGHTING_MODELS.DEFAULT) { @@ -354,18 +353,14 @@ class ScatterView extends widgets.WidgetView { material.fragmentShader = require("raw-loader!../glsl/scatter-fragment.glsl"); } - else {//if(this.lighting_model === this.LIGHTING_MODELS.PHYSICAL) { + else {//if(this.lighting_model === this.LIGHTING_MODELS.PHYSICAL) material.vertexShader = require("raw-loader!../glsl/scatter-vertex-physical.glsl"); material.fragmentShader = require("raw-loader!../glsl/scatter-fragment-physical.glsl"); } - //material.vertexShader = require("raw-loader!../glsl/scatter-vertex.glsl"); - //material.fragmentShader = require("raw-loader!../glsl/scatter-fragment.glsl"); material.uniforms = {...material.uniforms, ...this.uniforms}; - material.lights = true; - this.diffuse_color = this.model.get("diffuse_color"); this.opacity = this.model.get("opacity"); this.specular_color = this.model.get("specular_color"); @@ -374,17 +369,8 @@ class ScatterView extends widgets.WidgetView { this.emissive_intensity = this.model.get("emissive_intensity"); this.roughness = this.model.get("roughness"); this.metalness = this.model.get("metalness"); - - console.log(this.diffuse_color); - console.log(this.opacity); - console.log(this.specular_color); - console.log(this.shininess); - console.log(this.emissive_color); - console.log(this.emissive_intensity); - console.log(this.roughness); - console.log(this.metalness); - material.uniforms.diffuse.value = new THREE.Color(1, 1, 1);//this.diffuse_color//BUG? keep hardcoded + material.uniforms.diffuse.value = new THREE.Color(1, 1, 1);// keep hardcoded material.uniforms.opacity.value = this.opacity; material.uniforms.specular.value = new THREE.Color(this.specular_color); material.uniforms.shininess.value = this.shininess; @@ -394,7 +380,7 @@ class ScatterView extends widgets.WidgetView { material.uniforms.metalness.value = this.metalness; material.depthWrite = true; - material.transparant = true;//? + material.transparant = true; material.transparent = true; material.depthTest = true; material.needsUpdate = true; From 4978259863b766d28b3bb4062d149cc2d2a4dd3a Mon Sep 17 00:00:00 2001 From: rinftech-github Date: Wed, 1 Apr 2020 12:14:06 +0300 Subject: [PATCH 034/130] Fix shadow_map_size description. Add scatter information about lighting/shader limitations. Remove widgets_test notebook --- ipyvolume/pylab.py | 11 +- js/src/scatter.ts | 2 + notebooks/widgets_test.ipynb | 330 ----------------------------------- 3 files changed, 8 insertions(+), 335 deletions(-) delete mode 100644 notebooks/widgets_test.ipynb diff --git a/ipyvolume/pylab.py b/ipyvolume/pylab.py index 3daae28d..13863165 100644 --- a/ipyvolume/pylab.py +++ b/ipyvolume/pylab.py @@ -668,7 +668,8 @@ def scatter( **kwargs ): """Plot many markers/symbols in 3d. - + Due to certain shader limitations, should not use with Spot Lights and Point Lights. + Does not support shadow mapping. :param x: {x} :param y: {y} :param z: {z} @@ -1748,7 +1749,7 @@ def directional_light( :param position: 3-element array (x y z) which describes the position of the Directional Light. Default [0 1 0] :param target: 3-element array (x y z) which describes the target of the Directional Light. Default [0 0 0] :param cast_shadow: Property of a Directional Light to cast shadows. Default False - :param shadow_map_size: Property of a Directional Light to cast shadows. Default False + :param shadow_map_size: Size of the projected shadow map. Default 512 (512x512) :param shadow_bias: Factor used to reduce shadow acne. Default is -0.0005 :param shadow_radius: Setting this to values greater than 1 will blur the edges of the shadow. Default is 1 :param shadow_camera_near: Camera near factor. Default is 0.5 @@ -1809,10 +1810,10 @@ def spot_light( :param target: 3-element array (x y z) which describes the target of the Spot Light. Default [0 0 0] :param angle: Spot Light angle. Default is Pi/3 :param distance: When distance is non-zero, light will attenuate linearly from maximum intensity at the light's position down to zero at this distance from the light. - :param decay: The amount the light dims along the distance of the light. Default is 1. For physically correct lighting, set this to 2 + :param decay: The amount the light dims along the distance of the light. In physically correct mode, decay = 2 leads to physically realistic light falloff. Default is 1. :param penumbra: Percent of the spotlight cone that is attenuated due to penumbra. Takes values between zero and 1. The default is 0.0. :param cast_shadow: Property of a Spot Light to cast shadows. Default False - :param shadow_map_size: Property of a Spot Light to cast shadows. Default False + :param shadow_map_size: Size of the projected shadow map. Default 512 (512x512) :param shadow_bias: Factor used to reduce shadow acne. Default is -0.0005 :param shadow_radius: Setting this to values greater than 1 will blur the edges of the shadow. Default is 1 :param shadow_camera_near: Camera near factor. Default is 0.5 @@ -1874,7 +1875,7 @@ def point_light( :param distance: Maximum range of the light. Default is 0 (no limit). :param decay: The amount the light dims along the distance of the light. Default is 1. For physically correct lighting, set this to 2 :param cast_shadow: Property of a Point Light to cast shadows. Default False - :param shadow_map_size: Property of a Point Light to cast shadows. Default False + :param shadow_map_size: Size of the projected shadow map. Default 512 (512x512) :param shadow_bias: Factor used to reduce shadow acne. Default is -0.0005 :param shadow_radius: Setting this to values greater than 1 will blur the edges of the shadow. Default is 1 :param shadow_camera_near: Camera near factor. Default is 0.5 diff --git a/js/src/scatter.ts b/js/src/scatter.ts index 803b96b1..1097c258 100644 --- a/js/src/scatter.ts +++ b/js/src/scatter.ts @@ -354,6 +354,8 @@ class ScatterView extends widgets.WidgetView { } else {//if(this.lighting_model === this.LIGHTING_MODELS.PHYSICAL) + //Does not support shadows because on three.js r97 the shadow mapping is not working correctly for InstancedBufferGeometry + //Should not use with Spot Lights and Point Lights because lighting color is the same for InstancedBufferGeometry instances material.vertexShader = require("raw-loader!../glsl/scatter-vertex-physical.glsl"); material.fragmentShader = require("raw-loader!../glsl/scatter-fragment-physical.glsl"); } diff --git a/notebooks/widgets_test.ipynb b/notebooks/widgets_test.ipynb deleted file mode 100644 index 086d4ae2..00000000 --- a/notebooks/widgets_test.ipynb +++ /dev/null @@ -1,330 +0,0 @@ -{ - "cells": [ - { - "cell_type": "code", - "execution_count": 58, - "metadata": { - "scrolled": true - }, - "outputs": [ - { - "data": { - "application/vnd.jupyter.widget-view+json": { - "model_id": "9d57d7917ea24320bba59d6c60f87bd6", - "version_major": 2, - "version_minor": 0 - }, - "text/plain": [ - "Tab(children=(ColorPicker(value='green', description='Pick a color'), FloatSlider(value=50.0, description='Adj…" - ] - }, - "metadata": {}, - "output_type": "display_data" - } - ], - "source": [ - "from ipywidgets import FloatRangeSlider, Dropdown, FloatSlider, ColorPicker\n", - "from ipywidgets import widgets\n", - "\n", - "select_size = FloatSlider(value=50, min=0, max=100, step=0.1, description='Adjust size:', disabled=False,\n", - " continuous_update=True,orientation='horizontal', readout=True, readout_format='.2f')\n", - "select_color = ColorPicker(concise=False, description='Pick a color', value='green', disabled=False)\n", - "opacity = FloatSlider(value=0.75, min=0.00, max=1.0, step=0.01, description='Adjust level:', disabled=False,\n", - " continuous_update=True,orientation='horizontal', readout=True, readout_format='.2f')\n", - "brightness = FloatSlider(value=0.25, min=0.00, max=1.0, step=0.01, description='Adjust level:', disabled=False,\n", - " continuous_update=True,orientation='horizontal', readout=True, readout_format='.2f')\n", - "dropdown = Dropdown(options=['NORMAL', 'PHONG', 'PHYSICAL'],value='NORMAL',description='Select:',\n", - " disabled=False)\n", - "cast_shadow = Dropdown(options=[True, False], value=False, description='Select:', disabled=False)\n", - "range_widget = widgets.FloatRangeSlider( value=[2.25, 7.75], min=0.00, max=10.00, step=0.01, description='Range:',\n", - " disabled=False, continuous_update=False, orientation='horizontal', readout=True, readout_format='.2f')\n", - "\n", - "#bool_valid = widgets.Valid(value=False, description='Valid!')\n", - "#toggle_click = widgets.ToggleButton(value=False, description='Click me', disabled=False, button_style= '', tooltip='Description', icon='check')\n", - "#check = widgets.Checkbox(value=False, description='Check me', disabled=False, indent=False )\n", - "\n", - "children = [select_color, select_size, brightness, opacity, dropdown, cast_shadow, range_widget]\n", - " #bool_valid, toggle_click, check ]\n", - "tab = widgets.Tab(children)\n", - "tab.set_title(0, 'Color')\n", - "tab.set_title(1, 'Size')\n", - "tab.set_title(2, 'Brightness')\n", - "tab.set_title(3, 'Opacity')\n", - "tab.set_title(4, 'Dropdown')\n", - "tab.set_title(5, 'Cast shadow')\n", - "tab.set_title(6, 'Range Widget')\n", - "#tab.set_title(7, 'Validate')\n", - "#tab.set_title(8, 'ToggleButton')\n", - "#tab.set_title(9, 'Check')\n", - "tab\n", - "\n" - ] - }, - { - "cell_type": "code", - "execution_count": 71, - "metadata": {}, - "outputs": [ - { - "data": { - "application/vnd.jupyter.widget-view+json": { - "model_id": "472ffb98618443d7b4de6dcbcc972e8c", - "version_major": 2, - "version_minor": 0 - }, - "text/plain": [ - "interactive(children=(FloatSlider(value=5.0, description='Choose size for x:', max=120.0, readout_format='d', …" - ] - }, - "metadata": {}, - "output_type": "display_data" - }, - { - "data": { - "application/vnd.jupyter.widget-view+json": { - "model_id": "6b08426e94ab476491cc4459b1010dc7", - "version_major": 2, - "version_minor": 0 - }, - "text/plain": [ - "FloatSlider(value=5.0, description='Change size:', max=10.0, readout_format='d', step=1.0)" - ] - }, - "metadata": {}, - "output_type": "display_data" - }, - { - "data": { - "text/plain": [ - "5.0" - ] - }, - "metadata": {}, - "output_type": "display_data" - } - ], - "source": [ - "import ipywidgets as widgets\n", - "from ipywidgets import interact, interactive, fixed, interact_manual\n", - "\n", - "\n", - "#Change plot size adjusting the size on each axe.\n", - "\n", - "x_size = FloatSlider(value=5, min=0, max=120, step=1, description='Choose size for x:', disabled=False,\n", - " continuous_update=True,orientation='horizontal', readout=True, readout_format='d')\n", - "\n", - "y_size = FloatSlider(value=5, min=0, max=200, step=1, description='Choose size for y:', disabled=False,\n", - " continuous_update=True,orientation='horizontal', readout=True, readout_format='d')\n", - "\n", - "z_size = FloatSlider(value=5, min=0, max=350, step=1, description='Choose size for z:', disabled=False,\n", - " continuous_update=True,orientation='horizontal', readout=True, readout_format='d')\n", - " \n", - "def update_axes_range(*args):\n", - " \n", - " multiplier_x = 0.5\n", - " y_size.max = multiplier_x * x_size.value\n", - " z_size.max = multiplier_x * x_size.value \n", - " \n", - " #multiplier_y = 0.5\n", - " #x_size.max = multiplier_y * y_size.value\n", - " #z_size.max = multiplier_y * y_size.value\n", - " \n", - " #multiplier_z = 0.5\n", - " #x_size.max = multiplier_z * z_size.value\n", - " #y_size.max = multiplier_z * z_size.value\n", - " \n", - "x_size.observe(update_axes_range, 'value') \n", - "y_size.observe(update_axes_range, 'value')\n", - "z_size.observe(update_axes_range, 'value')\n", - "\n", - "def printer(x, y, z):\n", - " print(x, y, z)\n", - " \n", - "widgets.interact(printer,x=x_size, y=y_size, z=z_size);\n", - "\n", - "\n", - "#Change size using only one param \n", - "\n", - "selected_size = FloatSlider(value=5, min=0, max=10, step=1, description='Change size:', disabled=False,\n", - " continuous_update=True,orientation='horizontal', readout=True, readout_format='d')\n", - "\n", - "display(selected_size, selected_size.value)\n", - "\n", - "\n", - "\n" - ] - }, - { - "cell_type": "code", - "execution_count": 72, - "metadata": { - "scrolled": false - }, - "outputs": [ - { - "data": { - "application/vnd.jupyter.widget-view+json": { - "model_id": "fdff0572e9bb4edc880df35a3ed61071", - "version_major": 2, - "version_minor": 0 - }, - "text/plain": [ - "ColorPicker(value='red', description='Pick a color')" - ] - }, - "metadata": {}, - "output_type": "display_data" - }, - { - "data": { - "application/vnd.jupyter.widget-view+json": { - "model_id": "d731a2cdeed04bb29d6e022574119884", - "version_major": 2, - "version_minor": 0 - }, - "text/plain": [ - "ColorPicker(value='green', description='Pick a color')" - ] - }, - "metadata": {}, - "output_type": "display_data" - } - ], - "source": [ - "import ipywidgets as widgets\n", - "\n", - "#select a color for plot \n", - "select_color1 = ColorPicker(concise=False, description='Pick a color', value='red', disabled=False)\n", - "select_color2 = ColorPicker(concise=False, description='Pick a color', value='green', disabled=False)\n", - "display(select_color1, select_color2)" - ] - }, - { - "cell_type": "code", - "execution_count": 73, - "metadata": {}, - "outputs": [ - { - "data": { - "application/vnd.jupyter.widget-view+json": { - "model_id": "76f8768be443482380e9e6153f67b1ca", - "version_major": 2, - "version_minor": 0 - }, - "text/plain": [ - "FloatSlider(value=0.5, description='Choose opacity level:', max=10.0, readout_format='d')" - ] - }, - "metadata": {}, - "output_type": "display_data" - }, - { - "data": { - "application/vnd.jupyter.widget-view+json": { - "model_id": "babb94a8149a415e9703816842b6cab6", - "version_major": 2, - "version_minor": 0 - }, - "text/plain": [ - "FloatSlider(value=0.5, description='Choose brightness level:', max=10.0, readout_format='d')" - ] - }, - "metadata": {}, - "output_type": "display_data" - } - ], - "source": [ - "import ipywidgets as widgets\n", - "\n", - "#set opacity and brightness \n", - "opacity = FloatSlider(value=0.75, min=0.00, max=1.0, step=0.01, description='Adjust level:', disabled=False,\n", - " continuous_update=True,orientation='horizontal', readout=True, readout_format='.2f')\n", - "brightness = FloatSlider(value=0.25, min=0.00, max=1.0, step=0.01, description='Adjust level:', disabled=False,\n", - " continuous_update=True,orientation='horizontal', readout=True, readout_format='.2f')\n", - "display(opacity, brightness)" - ] - }, - { - "cell_type": "code", - "execution_count": 74, - "metadata": {}, - "outputs": [ - { - "data": { - "application/vnd.jupyter.widget-view+json": { - "model_id": "8713cf5fffee468fabf8952cd7c8f742", - "version_major": 2, - "version_minor": 0 - }, - "text/plain": [ - "Box(children=(Box(children=(Label(value='Choose size:'), FloatSlider(value=5.0, max=10.0, readout_format='d', …" - ] - }, - "metadata": {}, - "output_type": "display_data" - } - ], - "source": [ - "from ipywidgets import Layout, Button, Box, FloatText, Textarea, Dropdown, Label, FloatSlider\n", - "\n", - "form_item_layout = Layout(\n", - " display='flex',\n", - " flex_flow='row',\n", - " justify_content='space-between'\n", - ")\n", - "\n", - "selected_size = FloatSlider(value=5, min=0, max=10, step=1, disabled=False,\n", - " continuous_update=True,orientation='horizontal', readout=True, readout_format='d')\n", - "select_color = ColorPicker(concise=False, value='red', disabled=False)\n", - "opacity = FloatSlider(value=0.5, min=0.0, max=10.0, step=0.1, disabled=False,\n", - " continuous_update=True,orientation='horizontal', readout=True, readout_format='d')\n", - "brightness = FloatSlider(value=0.5, min=0.0, max=10.0, step=0.1, disabled=False,\n", - " continuous_update=True,orientation='horizontal', readout=True, readout_format='d')\n", - "\n", - "form_items = [\n", - " Box([Label(value='Choose size:'), selected_size], layout=form_item_layout),\n", - " Box([Label(value='Pick a color'), select_color], layout=form_item_layout),\n", - " Box([Label(value='Choose opacity level:'), opacity], layout=form_item_layout),\n", - " Box([Label(value='Choose brightness level'), brightness], layout=form_item_layout)\n", - " ]\n", - "\n", - "form = Box(form_items, layout=Layout(\n", - " display='flex',\n", - " flex_flow='column',\n", - " border='solid 2px',\n", - " align_items='stretch',\n", - " width='50%'\n", - "))\n", - "form" - ] - }, - { - "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.7.6" - } - }, - "nbformat": 4, - "nbformat_minor": 4 -} From 2012bd9ea48d478bee8454106c1847cc6f54ad5d Mon Sep 17 00:00:00 2001 From: rinftech-github Date: Wed, 1 Apr 2020 13:02:21 +0300 Subject: [PATCH 035/130] Update unit tests --- ipyvolume/test_all.py | 160 ++++++++++++++++++++++++++++++++++++++++-- 1 file changed, 153 insertions(+), 7 deletions(-) diff --git a/ipyvolume/test_all.py b/ipyvolume/test_all.py index f5710aac..9e8ae5cf 100644 --- a/ipyvolume/test_all.py +++ b/ipyvolume/test_all.py @@ -490,17 +490,33 @@ def test_material_components(mesh=None, is_scatter=False): assert mesh.cast_shadow == True assert mesh.receive_shadow == True + x, y, z, u, v = ipyvolume.examples.klein_bottle(draw=False) + ipyvolume.figure() - mesh = ipyvolume.plot_mesh(0,0,0) + mesh = ipyvolume.plot_mesh( x, y, z) test_material_components(mesh) - surf = ipyvolume.plot_surface(0,0,0) + k = 20 + h = -15 + tx = np.array([k, -k, -k, k]) + tz = np.array([k, k, -k, -k]) + ty = np.array([h, h, h, h]) + + tri = [(0, 1, 2), (0, 2, 3)] + trisurf = ipyvolume.plot_trisurf(tx, ty, tz, triangles=tri) + test_material_components(trisurf) + + X = np.arange(-10, 10, 0.25*1)-10 + Y = np.arange(-10, 10, 0.25*1) + X, Y = np.meshgrid(X, Y) + R = np.sqrt(X**2 + Y**2) + Z = np.sin(R) + + surf = ipyvolume.plot_surface(X, Z, Y) test_material_components(surf) - trisurf = ipyvolume.plot_trisurf(0,0,0) - test_material_components(trisurf) - - scatter = ipyvolume.plot_mesh(0,0,0) + x, y, z = np.random.random((3, 10000)) + scatter = ipyvolume.scatter(x, y, z, size=1, marker="sphere") test_material_components(scatter, True) @@ -511,6 +527,12 @@ def test_light_components(): assert ambient.intensity == 1 assert ambient.cast_shadow == False + ambient.light_color = 'blue' + ambient.intensity = 2 + + ambient.light_color == 'blue' + assert ambient.intensity == 2 + # hemisphere = ipyvolume.hemisphere_light() assert hemisphere.light_color == 'white' assert hemisphere.light_color2 == 'red' @@ -520,6 +542,20 @@ def test_light_components(): assert hemisphere.position_z == 0 assert hemisphere.cast_shadow == False + hemisphere.light_color = 'orange' + hemisphere.light_color2 = 'green' + hemisphere.intensity = 0.5 + hemisphere.position_x = 100 + hemisphere.position_y = 100 + hemisphere.position_z = -100 + + assert hemisphere.light_color == 'orange' + assert hemisphere.light_color2 == 'green' + assert hemisphere.intensity == 0.5 + assert hemisphere.position_x == 100 + assert hemisphere.position_y == 100 + assert hemisphere.position_z == -100 + # directional = ipyvolume.directional_light() assert directional.light_color == 'white' assert directional.intensity == 1 @@ -537,7 +573,41 @@ def test_light_components(): assert directional.shadow_camera_far==500 assert directional.shadow_camera_orthographic_size==100 assert directional.shadow_map_type=='PCF_SOFT' - + + directional.light_color = 'black' + directional.intensity = 0 + directional.position_x = 50.5 + directional.position_y = 50.5 + directional.position_z = 50.5 + directional.target_x = 0.2 + directional.target_y = -0.2 + directional.target_z = 0.8 + directional.cast_shadow=True + directional.shadow_map_size=1024 + directional.shadow_bias=-0.0009 + directional.shadow_radius=6 + directional.shadow_camera_near=0.1 + directional.shadow_camera_far=5000 + directional.shadow_camera_orthographic_size=200 + directional.shadow_map_type='BASIC' + + assert directional.light_color == 'black' + assert directional.intensity == 0 + assert directional.position_x == 50.5 + assert directional.position_y == 50.5 + assert directional.position_z == 50.5 + assert directional.target_x == 0.2 + assert directional.target_y == -0.2 + assert directional.target_z == 0.8 + assert directional.cast_shadow==True + assert directional.shadow_map_size==1024 + assert directional.shadow_bias==-0.0009 + assert directional.shadow_radius==6 + assert directional.shadow_camera_near==0.1 + assert directional.shadow_camera_far==5000 + assert directional.shadow_camera_orthographic_size==200 + assert directional.shadow_map_type=='BASIC' + # point = ipyvolume.point_light() assert point.light_color == 'white' assert point.intensity == 1 @@ -555,6 +625,38 @@ def test_light_components(): assert point.shadow_camera_far==500 assert point.shadow_map_type=='PCF_SOFT' + point.light_color = 'grey' + point.intensity = 10 + point.position_x = 50.50 + point.position_y = -50.50 + point.position_z = 10.10 + point.angle=math.pi/6 + point.distance=70 + point.decay=10 + point.cast_shadow=True + point.shadow_map_size=256 + point.shadow_bias=0 + point.shadow_radius=10 + point.shadow_camera_near=2 + point.shadow_camera_far=10000 + point.shadow_map_type='PCF' + + assert point.light_color == 'grey' + assert point.intensity == 10 + assert point.position_x == 50.50 + assert point.position_y == -50.50 + assert point.position_z == 10.10 + assert point.angle==math.pi/6 + assert point.distance==70 + assert point.decay==10 + assert point.cast_shadow==True + assert point.shadow_map_size==256 + assert point.shadow_bias==0 + assert point.shadow_radius==10 + assert point.shadow_camera_near==2 + assert point.shadow_camera_far==10000 + assert point.shadow_map_type=='PCF' + # spot = ipyvolume.spot_light() assert spot.light_color == 'white' assert spot.intensity == 1 @@ -577,3 +679,47 @@ def test_light_components(): assert spot.shadow_camera_perspective_fov==50 assert spot.shadow_camera_perspective_aspect==1 assert spot.shadow_map_type=='PCF_SOFT' + + spot.light_color = 'red' + spot.intensity = 100.45 + spot.position_x = -5.1 + spot.position_y = -5.01 + spot.position_z = -5.001 + spot.target_x = 1.1 + spot.target_y = 1.001 + spot.target_z = 1.0001 + spot.angle=math.pi/20 + spot.distance=5.5 + spot.decay=6.7 + spot.penumbra=3.1 + spot.cast_shadow=True + spot.shadow_map_size=2001 + spot.shadow_bias=-0.000005 + spot.shadow_radius=6.6 + spot.shadow_camera_near=0.509 + spot.shadow_camera_far=500.03 + spot.shadow_camera_perspective_fov=50.56 + spot.shadow_camera_perspective_aspect=1 + spot.shadow_map_type='PCF' + + assert spot.light_color == 'red' + assert spot.intensity == 100.45 + assert spot.position_x == -5.1 + assert spot.position_y == -5.01 + assert spot.position_z == -5.001 + assert spot.target_x == 1.1 + assert spot.target_y == 1.001 + assert spot.target_z == 1.0001 + assert spot.angle==math.pi/20 + assert spot.distance==5.5 + assert spot.decay==6.7 + assert spot.penumbra==3.1 + assert spot.cast_shadow==True + assert spot.shadow_map_size==2001 + assert spot.shadow_bias==-0.000005 + assert spot.shadow_radius==6.6 + assert spot.shadow_camera_near==0.509 + assert spot.shadow_camera_far==500.03 + assert spot.shadow_camera_perspective_fov==50.56 + assert spot.shadow_camera_perspective_aspect==1 + assert spot.shadow_map_type=='PCF' From fd1c121829c868fa91ebb3a75b670a54a9a92bd6 Mon Sep 17 00:00:00 2001 From: Timo Friedrich Date: Thu, 2 Apr 2020 09:25:33 +0200 Subject: [PATCH 036/130] Updated lighting demos with animation example --- notebooks/lighting_demo.ipynb | 117 ++++++++++++++++++++++++++++------ notebooks/rotatelight.gif | Bin 0 -> 709035 bytes 2 files changed, 99 insertions(+), 18 deletions(-) create mode 100644 notebooks/rotatelight.gif diff --git a/notebooks/lighting_demo.ipynb b/notebooks/lighting_demo.ipynb index 59086cd5..1cd5b7fa 100644 --- a/notebooks/lighting_demo.ipynb +++ b/notebooks/lighting_demo.ipynb @@ -216,13 +216,13 @@ }, { "cell_type": "code", - "execution_count": 2, + "execution_count": 23, "metadata": {}, "outputs": [ { "data": { "application/vnd.jupyter.widget-view+json": { - "model_id": "ee958a7eb4de486dbfc1a4a13b0ef0af", + "model_id": "53876d4ecc6e4f38890459f619d6b8fe", "version_major": 2, "version_minor": 0 }, @@ -260,7 +260,7 @@ }, { "cell_type": "code", - "execution_count": 4, + "execution_count": 13, "metadata": {}, "outputs": [], "source": [ @@ -276,7 +276,7 @@ }, { "cell_type": "code", - "execution_count": 5, + "execution_count": 4, "metadata": {}, "outputs": [], "source": [ @@ -286,7 +286,7 @@ }, { "cell_type": "code", - "execution_count": 6, + "execution_count": 5, "metadata": {}, "outputs": [], "source": [ @@ -295,7 +295,7 @@ }, { "cell_type": "code", - "execution_count": 7, + "execution_count": 6, "metadata": {}, "outputs": [], "source": [ @@ -312,7 +312,7 @@ }, { "cell_type": "code", - "execution_count": 8, + "execution_count": 7, "metadata": {}, "outputs": [], "source": [ @@ -331,7 +331,7 @@ }, { "cell_type": "code", - "execution_count": 9, + "execution_count": 8, "metadata": {}, "outputs": [], "source": [ @@ -348,7 +348,7 @@ }, { "cell_type": "code", - "execution_count": 10, + "execution_count": 9, "metadata": {}, "outputs": [], "source": [ @@ -372,7 +372,7 @@ }, { "cell_type": "code", - "execution_count": 11, + "execution_count": 14, "metadata": {}, "outputs": [], "source": [ @@ -390,7 +390,7 @@ }, { "cell_type": "code", - "execution_count": 12, + "execution_count": 15, "metadata": {}, "outputs": [], "source": [ @@ -615,13 +615,13 @@ }, { "cell_type": "code", - "execution_count": 23, + "execution_count": 27, "metadata": {}, "outputs": [ { "data": { "application/vnd.jupyter.widget-view+json": { - "model_id": "8704c06726ca4234b216c5f3b9323c01", + "model_id": "94b122b532cd41529f9cf9d03bbf94fa", "version_major": 2, "version_minor": 0 }, @@ -654,7 +654,7 @@ }, { "cell_type": "code", - "execution_count": 24, + "execution_count": 28, "metadata": {}, "outputs": [], "source": [ @@ -695,7 +695,7 @@ }, { "cell_type": "code", - "execution_count": 28, + "execution_count": 29, "metadata": {}, "outputs": [], "source": [ @@ -845,12 +845,93 @@ "spot1.shadow_bias=-0.0005" ] }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "# Animated Light" + ] + }, { "cell_type": "code", - "execution_count": null, + "execution_count": 47, + "metadata": {}, + "outputs": [ + { + "data": { + "application/vnd.jupyter.widget-view+json": { + "model_id": "c993ddb4d5ac496ca269b14532c3aa0b", + "version_major": 2, + "version_minor": 0 + }, + "text/plain": [ + "VBox(children=(Figure(camera=PerspectiveCamera(fov=45.0, position=(0.0, 0.0, 2.0), quaternion=(0.0, 0.0, 0.0, …" + ] + }, + "metadata": {}, + "output_type": "display_data" + } + ], + "source": [ + "[b1, p1, s1] = plot_all_no_change(kb=True, wp=True, ts=False)\n", + "point1=p3.point_light()\n" + ] + }, + { + "cell_type": "code", + "execution_count": 48, "metadata": {}, "outputs": [], - "source": [] + "source": [ + "# Light and shadow Setup\n", + "point1.position_x=20\n", + "point1.position_y=10\n", + "point1.position_z=0\n", + "point1.cast_shadow = True\n", + "b1.cast_shadow = True\n", + "b1.receive_shadow = True\n", + "p1.receive_shadow = True\n", + "point1.shadow_map_type='PCF_SOFT'\n", + "point1.shadow_map_size=1024\n", + "# point1.shadow_radius=20 \n", + "point1.decay = 2" + ] + }, + { + "cell_type": "code", + "execution_count": 49, + "metadata": {}, + "outputs": [ + { + "data": { + "application/vnd.jupyter.widget-view+json": { + "model_id": "4a6e362235ab4017ac86e9b85dea240c", + "version_major": 2, + "version_minor": 0 + }, + "text/plain": [ + "Output()" + ] + }, + "metadata": {}, + "output_type": "display_data" + } + ], + "source": [ + "# Generating gif with animated light position\n", + "def set_pos(fig, i, fraction):\n", + " point1.position_x = np.sin(2*np.pi*fraction) * 20\n", + " point1.position_z = np.cos(2*np.pi*fraction) * 20\n", + " \n", + "# a = p3.movie('rotatelight.gif', set_pos, fps=20, frames=40, endpoint=False, cmd_template_gif)" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "" + ] } ], "metadata": { @@ -869,7 +950,7 @@ "name": "python", "nbconvert_exporter": "python", "pygments_lexer": "ipython3", - "version": "3.7.4" + "version": "3.6.10" } }, "nbformat": 4, diff --git a/notebooks/rotatelight.gif b/notebooks/rotatelight.gif new file mode 100644 index 0000000000000000000000000000000000000000..9011ef3e5fa7aee9c6f6a837082a3bf617eebdb6 GIT binary patch literal 709035 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 KS(x3H(C894rkt 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

  • Yi{p#$o8bE&AtZg{O71dhElg&JHtzx^e2z1`{} zy516_f^)6JR-7$;D=*443C0y{9}V0V)X3CL9yo4;W zVqZoiF9QG|>El4qDnsC{O7d9>Ew%^3al7@d7@9YB`Y9QxdG?dxPCumwpVo!sL33GS z1#)GCAmFCuJ-yD(C=aRY1vI=xuF~HlJaK z+?jAbia7371>?0j_R)kVWrWz<_8GXb9=^EkVf0sFGAXROr zcSn8Gh@A>vbN1Yj*}3)=MTrzg$!^9=;voyFY>VpiMtkN?2NNe4+!fP$!)OUy#f>u6MPn z9>3aHI!R>xG`gw!__#V~)8UrKQN5EO+(nAuxQof*@#ODxf<~QGz$75iGfPN@RE-V$#ZQY{~U+mZ0EsY z=_=+$5M&}I+-hGO^!gz&_zNCj*z|US5!uY-Z&vH-vYmVBF6|&TIhSiaBxU z1aWT2?pukX$exBkt~G+SoQFwC&Fc^p9)6J5J|^Mr&v~psy_OXV2vQ!@0@a(c<5pw= z;S&bp-yEB4m|$XP4LGD9q9v}quVa<=fd{j=TZ&0K1})i0Y@tmAP@0$#|9cc9_N zewc*YyNX3S_Hk}q#*n|spMa4^rBLRtr)AHNyWLE7fA^iF5M(hdB{kB%e}^bKpvn>Q z9G4f%t-lawz4U|FN9|$Ys|kj$?1uLW5dyOlMo#tgBDX-L?;8tq+9wSrX4|h1zvjO&VD~s2NIIToK|)+ z7hUy91#qDuJxZ35lp?)NMhpOp6#W5?8rDrow;$g8ywDJ@u2h2mU^Q(+v;>?~Q|raN zYn+i@Nc= zr%~OPM+lWG@qtBG#k(0x$u>P!ktz&Q)cBV_FK>Xvnq*CDy7$s>f2{ z4Z^GY%M4O5s`*fEQEfzWqe|u8_4g&t6jC4_vPoSI!NoJ^dU31 zJln!lVDDHhrPFa9FwlD{!rp^oj-<)#EXHf;F@>!ripEo};!W%Ag=JxS<0X4f)94X; z+B2V#-5FLn>jT3MXW8x6gNUGMn8EzvKR0D4!;jm7fep}JUGfF0nKUxRz?^QX;DsCgHnZi z1kDvZUbpVRm>3jm;nj&F@6i+yh;4qRl9^KbyA~ANxfRRgn~7_T?HY2X+ky?t>Tl(L z?<8DUq|>lAwE*kJe!yS}z3(=n>K_ah#J3t*#4!ctAW=Y#WC)JsGYz>?={P_)DSFj8 z*OBN{P%CU>#y?*6tBOb%1_AOM+_PEpW;oV%0Rc@iWO=<0QO-rtm^~7~+*1GHU8L3HakEuCR6*{d3b_;-aza z{>N&gPWoKqok#l6SDa(Nx6m~ZGGi|8rq4tR3SOf{p!K)8*HYHhcttz+g@6?pl-pn* zRlb9J)aHMrY;#-j_p#2-rp(NGKS%=FhCt2Uhccql2X>VBs7RHebPXOE@w*)?T|bZ# zV-A0uqIw5zZp8XN5|BziYXzT1+4FP|bXVLxPD3EMvn_YPQ1=_(E{Kd0XZvZy*pVr@)HLo5=J0z!D z{f7%H?m*x>62Qpd4w=*sO33I`qpu0+^SNt>kQm9SPn0k@0ESb7X~Hpb=sC0gqkf3b zH*ia^&vU)z%y<87F@XH-(JNxZ6XY+GNKX9H$ZS8Ev*+S_vOC0X`o%3(AF%pM#jkDNDdFE-w;vt?={!h_QfVA<}M` zz!@s!MBnrfOko=P` zTqKfBdmtjWMYE-)xRP638XN|4Lek95p3kv7BXdg`QdVxpc#T`RRhG?%C5 zs8(S(;)0RiK|#=eLLu_w!Zv|Eq#YqylIr`fq+*AD&Jn&_66)HVuBD!?(o*4fr{WQb zze(PZY-q@m0pLwbA!uZ7Adsqm2Mnz48NB|wFSq3PV&P8(t{_rrf;VKq9g+$SnIa2V zX^XdMnaTJ5kc|_J8w|YrTkxnSax!<++xL#)tK`p&-`VSklcl6(4Qy)P93O<3s)uO# zc;vczguxq9QbnjKtowh?GT-PjfCPD=-|UYE42n|ZGY%=5sPC_EdqQ=m>bJ0i0%RZD z7~gfV){3Y_D;IolNMaVs4Q@+EE2&{l`QZ#Z4=cYT?x_ESP`;`4mX4qWE2Q4L|E=>8 z8$n>~$Iz9z?~cRVo+rQDivqn4hu@fRdf z5XyngQYofhX%}}$OC4z#1%5Q`BwS12uGVfbf&MU>K1pK;S9rmT3ZWy05kvH5HImBVQyyknzjWYR$@;pIouAP!K5mg6(RE13sF9QS53JzomV(==s ze@6yxWSx$Sa=_)a;30MVB@2fDhQmNUXZVVB_|60(akb}~t`~fgO1mTi-)WKSH4Cw; zNYEYw75m*?gklRwiht7Olk%n^e@L>3guF+k*u3Tg@YBVzycWundEjjE+K%gr-tH?K1g64d%fJGj{?_s0)>Gou&HB_=!-l|Z`ukaz^8o;s_xg&z241wtS6nD~?^*IK6mO`F9}^Y#O*ObS5V+YOZI;sLB@nqMa_Ox4bJJmnkh zEo9jhfQC@+igH@M`kjo%yztzi%yeH#2Lnhb?k#`nZpSX~LdyQ8>q?j$s7xZ6Hl&!^ zEcgb3jtYK)61anws|okDOgAFEorIIVRk|$_Q^EnX z4ZjJhlD51VK8s?df-0h87qyJqkAPsZ9?BhLMKL7XPi;&#{^cOw?&LBjh3NJk`qG|zp9@Prj;UTaAaLWf?ibykMP0BI`iMB8pyl*G2NG5U@3D;+9jQa6 zB?R)2Ha!@44qaRIT-8CzI2nZqJxK(0E9ZHv@E*NucHLz??aK*2*ZXIoyW{Z(o+hE7 z^g_8-eGjO@B7J4}FCt_J`pOjarL3NMhcF zFrP*iY&_Xd%}M-599oK7-#TZ6;_NHN`p;@~aBC+=zXwbD@$E*#7~=M`$D?ynlVt|z zX&r<4$9bpwTWEv5?W~$TgX>$oM!0>ru&27WMEF1Y+P7P@y`3SW-TWnflS6B7U}le8 z6qnSX3cj`J@IW(lhp}0jF6m8{x4`yJwcGv}Sl$|c(e+HHN6hjYzJ5y2;Gcy%2xPnS z+t{9(Oq=9Q>)7S;b1%JRAx@=Qc5$rpvRRE=gg|(7Hvl=m; zO|88uN@MC-yLz3IxwW~xSCuhiDHRE{|0ix1FWv|%umrEaq{djnC7K%-=ld_+z~@=Z zOMXMTIwbw8{nwj!RJ&3G^hViThfwdTrCm)j<2NjnQITtper9YXZdCs@9ne{^Go6?h z;T4VZ8_#pKXJ>2jX^XGB?C(dQaK`2+NI>u6cr!F^I81I+9BF=D-_56NtH4QpO@EPZ zy0!I!)G;H{#oYX_y)5NQftxA@>|iBfWedO z1E68dnl>rZ0k%O*tDob`=~`Or!Kj4B8s#q5?(9r<+J8GOnYoec;Vao#DtBkQ-n~KG zcu05Z!Q0%5E~T5wSStF!HJ$FvRY1MsNnop|^T@T%>c}Ar(K1u4_ao(8Y;8b}{&8|Y zm3`=6p6oOZ!EGZ0g_RXpE1zSKcicjbc9>&ifUs5c@<^o5A5mFQFV0H{eA_|2#<4UJ zKj%pQ1I#h6yTy+-?SjxVtk(9iwc~R4^WjmQq9-Z80%58@J-50#udi!x>UNowG8G0Y zEqXdyV>XtYnkLU_a$=WlRsnR-PH8JHF=d85v**`OKvHx&IB3tTVV$?o3*npCrp_~3 zBK_)THD8MBN`G0)%oFp`^^Df*?cM9-r)$JImpwQ87*6?PwVM+nZCF*yP>zc(g_~`E z6CmczBZWQ>riL}-EwzVL=+iGX3j;T(+oc;#PF2eXx4Rd}J9y$-?N%EPXis`UP2&|w zct;~cjGHKI`K3}y3>1*T$l)CWwZ0M79fsmPWvki@vKj20IHTi3wS+;KxuW#)!z8}8 z5V^$sv93VtBmeEg2Ba3UQN<+vTn~M?z}2{w_eb4hJAM%M;>a$*3PZcQco{Ndt%+krnrjFd7-bilrNWc zI`t)@Uiq(#n9sE4ck@7z9JjZ~d?A_EXLya*_+QY832HBFzAsRk8YNPRD6at}&v+;5 z`TxBY64T~;b7vb`pzOU-yS-%Yc?gBD;~PPsG%(4DG6$P0**OZ+{!A{+e=TDNGrfff zJz{?vd=F8S%mO{hWc>OUnZQDfp$P2=!KlRcO>30ZOXK{6M&bCUGxCqlTpY`Y4;ThJP%DnK}D@mDYu5|LPj(!NMI5}{z| z|1%d2hBK7O0r7+a{zqCu1CU7gYU2(2#gWO>N*zwh#b8HrSseD*UP{6cuvmpD)m}}k&AgMc_K8MjKT-G zZ&q4UFJww(-~7~iwpp#!H*7^L2vlgG`3*3ZeyYLe@w6Lm+fJoWY|Jh6J0bh{X?;AM z%N{_e|KWB~7vc-mjYl(eXiDpDc>23ZL09+?ZlUP2=V!_K@_Dr#=az1K?bh9 z+XDh6QQS|dfofln0lN1<~m1@xNQ_GP_452rJs~qth|B@Ulfk2A9H1%_$;>JI_`SPvkz`@4mO{+s> z!5h62G#Q4}>jpU<_WKS-4hq+4Nxae(WgU58$~AW7!WKMC&p3S|HTRs!=eKgt)n;hQGIYm}OZfNqDCU2SXPbKW7v8Eoi!VLk^!bK^S*I zm3EL$uv>jCMcIPKrqa}*^EkaWzSE+1thbXY3D6q1Lzi*HxXaHRkJ3?R_33EYt;b0B zw;X2n`69!(?!zJD`GoJX_CKnoOQu)HN}a*9fAM9{gLsYM0l+}UdETrs@Rs^# zy8%~U+1&>Tm|yQa%mHuRdobc!zt9&G0;o%(|67)zMBrP<77~z>4w@~Bhrn#$ z*xY>unv(Qq>AiE6bNI|?e({UYC77|NK+m{0@IV3@22s*jT$8I8ELz1{z%mYqkGTL9*Mbeb9nLu@Aro&oYYXr6Tsv=8Kt{70J6b&S>|u(Z z`UB~0lGe6d+ABmegXzPbLqifwsWRt4L&fX^W)#%n0)@xK z-sw(d5evlJmGni65&wCgH}&(J2+T&ww(6-=ld^)Pq*A^1MFdwb%v!FKQ#ET)fgJ)9 zHTTA-P4{EDsIfvQUki7jLD|;&R?mj|qyr?WoCmC;A*9i0?pmyOaIZFks@Cl8(yEPC zS226u%b$hEBxxB|-PX+^`;DL9b-ldKT zWPvHJNVpfMW7QaU>ue3btoEFK(XmE4>ps`D_g>8~uA%elVMQwjqGVyECdB8?REw4-c8Dp)ii=Ya8=$>n-k~NRfdV2Bb9rUXAS=ur{_FNyrJ859?!P>4} zXis&-S7z9$R0Ziw_sv6HFs*EmfP`mYoxK)rf|VEMY*p9?Ll|pJymgI{aNdU`C0vqI z{%O@_Y83*eG4Bwy`2^R|0r4@4{W9qbadZ|8GNSR;`%wsLJuhRe0ySPy-}?&mnM31N z%sElcn><4svo2+iInQ#If_pr3wq(wOIdZFUxg3;D3^HcPP%ZTIVUij#BX(ALIa32O zG&AcCHu{aQK_*!3qN}yOTUQo>)vqnT1>LLEdu=rGyJmeA-2XxfSc!NZcQ|}blV0N$z|Q!N=w<>x_4T^X zp-iRLM09xG9^PY%9{k$3v2vxF!z2Zq`1&^giobTw1rfQmweMUzdiri|+&KMH^OKV9 z<5n2`<{ItiEQ`Q}=CR+Gk`99xJ$}aqr^ZdzXRnPra&}j0&s^uw>s{M?*Zzv{A7^Zd zmU8F*ZAB@#p_YQ+ZoS4PARKyWlkkrv>@>1`{q3_RgcD^5otMB6zrXPUW4|?o7R~3Y?<1>0syfGC5 zC;_BnlIUF!dC!p^WDCTj>pmN)jB(+!Tw4b>+uuU?o2z*WKY6A@{v3b)`j4O=&;pHP z_Xo#r0Ix*&g0FwpH0~NA3EECb`ZKV4DUSgh1)+y>nY+)cxj2A;L(`U7?Vy*FrIow6 zE!>xHz8f@fRy+XaCydt;&bTdd^f+jBCc^Yjgq2|kH)r5K-9Iw~h=esnM4>~PY*q9h zu@GGlWJg6C1f3N8HRMZN#22BW8Exw9!v943PsxRFAw}6ChnTNKSk1(w{KTZ)#3W0^ zq=3WXkV1Hv{rMP(T+_nbnBvnU#PV=u+MCP|woIktp_24HNdFq@Ovp3=Vd8iFp#*}{ zs>1Xo!c4JDym=f%mqb5H`O@D|Q$&Io=j*1@*E5wHP0Y(%tM{E$`_IRR9=n-zM z&;b=q=B$0Qlhj*_wZT3~q_48Mttqp`Ff<0f_zhKT)S)=1$`6JS$tqDw?h z;DF{#9DMM$D%oH6bQRi9<03z##ni+sOU8gdhWvA+vzBG--ok62WNNQOvJhp{dnSzd zrKIEvS1K~1Ng*iTq;1Ve7i8)bX(z?Vs1cW&I3K8of?0?~#w17tJ!=FBwufdh>3-B?x?r-(dDbumD@gs}KMX)gGfb%wzv+WvxkG-1I=kuPoayNv zx#N=QjOpoAGwI+Kv7@QT`OxTvAM#85GtsQ_`~xX`N+I`3Gy6UmhMOI5{4wiK9SHwg zds^fx59pWo)8w=u=g8$!?1sM+2m;(DM*L<^V--%+W+%mzrsb5*LKVOnl>%}UW)P=L z?IdF76=DFPboBCz|7P!=lXdfGe(UxM%2XnhMV}J{ZwQ42Tg5=f;Z%4I#d~s6lEABE-;mj5LGDc z7lj#@#VD5@Ni>87G{gkd#O;^CI1~`GL%9=;gHLuF8sNDl`6tR|+o>x_@L!GcR zv1wOajpaGQqyQPX3C*~LL5(R31*e@^J3vUGNJ%-&*+8HIG>CDYOn=o>Udu4YQEfG?1UW`pn8a8YK>}w@}ay^7mE6jYo z)O@qdeX~qn$DD8(;%mu2S1uD$DhDVWR-h4ri-okajR^-%ZiN`1XTC|OWV$$ZG!X8q1$G~AZLvlc4H77LcP zl09VW_12cXZmNBn1sbeOziy{}%<@to{5t6kAh0|Hr`T`h{Vc7A7VoVfRJ;RXXjqb1 zU2idUeUwsdu66q+b$jY-cNWTUn$U1kU+3ZXW(ks(XxQQrP`ajM(WfOd>TS~&X^ZK4 zUEz4OS$s)!Qk~DYd zdF!Io%;dS7O7XMaBc<`|$o4p;A*O~ghJo>nxG|3Z{tT8Op222h+~T2|Le0!3)6dBH znmy%(bvgH)wcq;uQ9JzShHOwqKzZ#QLXgoeR%D4%G_65&th7<0R|*_UgD_YinDx>P zoN}tU_{mo~v2?TqHzdbjgg*Jc&kS#lX9>+p z!q&Mg^rU9Sh%{g!-uG>f*O4i=q(I9Q8V9l#2loD3n!9EYX(qi&$jfJ;d-fX>#=IKN zQ{anRuc3+?Q)mbikU#-Qfex7wkl3QAzTpkWo*=e`kw%07vQ>~^M9`>S2-8_8$kM3p z5U^ps?Q zU7z6j2Lhl1Ti>)U3+r&+DZX@EUnnsI?rQ-$b=q&#+#mV~6WbyL*~(qZrp)7_+~XqD z;iAIeBG}(1%;1a<+3b$cIKq67dL%p)_NM^JrPZueyu1E)g;=`G?7!ZfKQlYjpc7h+ z^Le4Tw6<|VXOb1S40SLn080t34m}AGHLHbqFDk6hDg?)@OrgC7x5Tnund#GvO;Daq zb@;{f>ebB2)zs)kSol7u*8j|G)Y$GUGMec!X7r3P)gs|qfz04i`VXO<{{rQS1=#9Q zr{GoBsx|z5G9s&Lc&zn;Nz%5ExVY&W3+I)i#D@5tHPhSQ7o^pQ)T>+Zi%Y??t3Ow> z8WEmncDFY-GtiDV4mQ=ZH^zBaPlQ@8l#zT!l$Hwi%?A1{pxJ){2MKBR?SGD_IIU?E z&YcX-yFC&_y)lV+Qs8jKu{TYyqqL90(wlBHwpq`e+;2u4uA-=HA`P@|_3rA>} z^AJ3^A%y}e7K|5T$2J0>;|FX<%yL&WOJ~0JM=WaRJ0fSnhzlG?XFTRxBV;qd7rkK0 z$e=fvR|#13>OB%idt`Te8>$CIs1=a-167(W)v0}MxMfUFB5eew&VJf~FccPdZ$=w> zJ^*ld=koQ@eIspdH;O@=4#ClHP-w6izG7~wa1jD>!0L0wxqZBueKZ|=6jXdMmVCi- zbQVguz>u&iaj>cr)Y2q@<#>w_{hJz%z-237`*E_&e5{c)bGr#L1Pi~UFmwU#+B<`u zX!g+TK=(LZ16j_Zjm_YJ!p?}1B57uDt_mxwqPVGiRv~(t^Cs?(ljg=I&@Yw#7dWqv z0_9%(TrcKhZ{~eoW|kgUo;FrEPgPWIyeE38jFI9Uq=3k<=(bF!{M$9E1M(BY7Wsp( zRX2}UN1y>bHF2C5%Cq-eT0mj;TxWJiQGF{96QH8q-_wZ)fMT^fPe*Rc!|f$B<8&ct zXozx>nBbM5`4Y4D7CZOmjq2P){n>=`8O!BieCzW7I(<0a?652qf-oW7Q*udvM@2f9 z@xP>)Gjjx;18}4n_8SBFkL`)*f3VKbHd{nkB!X#II({uhl(Q92B=Y$y?V%-^LTz(2SG#FO{Z| z+YN;+l}7!xYsY?1PddRXXMY!^TlAp>tU_koqP6U$#{3WAXCV>R1^_@tl&-A(AFuOA z>taZ27etjKgkJsTIn%}6wNqf;W4xwEBiCnBeLzcJK=WF_is2W?@MRq)(i40I?uS)k zL02d|4i*2YQm$yke^e?tumGWvfJU0&7S7L4VJTp@WVb1WcjwRU^f1?U0`>sx!$ z?-WsAt*O?sHf^-mrC;6Bak1T3alU8T?eTeiP`@h(hDHVGj5j0=!Jtyg)I{IqdCRjZCa^V#PKyTzWR;_fE7*}gs5Kk3ya zx%D25z+fiY?zr2V2zgzt-Qsi$N7N5#(aJ>`VZH%6`)PFTi@j42EZCYEc^iUO~jF~YH7G+C% z3zA98@IRgb01`i`bz^e6Q7x=e!L$S1%4AYRmbKm!IW| zcNO+^cJSywVGK;!C1~ zKzDK52UxD^<*3LD395G<|Rj5XxF z#MM+cYE-8Qn&OqX4uQ4jICG-VvroST3U*Di`tCkB^PJk5*9pPsqtRgOER45eId3t_ z%Af8ob||IW-gLTa2<+ubAHOr@-+xHo2E6X-g8u#qC$r)VahP}}*@BRT=h}9gV?<=# zk5NQq%T-G}hrmz++?R*pK|`8ElGkxt+ilabL`!JFV%(lkbI%a9 z7$Aunf)Lb5Ahl0wNTIGh>bw88%>uiDIp6QVpiTKQ?4APHdM~Q!P1QUytm->SC2c&+ zKMz=GP^obYR<4P~L{OFO&dd((pLsi@5r_{-D0mY5ChWYV5j`|QU>A>D5kJ# z2U@jv!6ijX*g>NwEySwVp#x?jN9l@5r?fSk{iBNWt@Ofvea+Ye0IPr4vq_dLLm>ns z#+uKa#Z3%7+DaE_^?GqfJB}K|;j5gr`GD;cBE0pLol$|@Li?Cn0 zI+J$3W9o+VCZomVmg4F%9sEupvft&B*j!6&a!k*Y%_Yfaii5ZODzE>oQ)LFS+Z>i` z-qoZjVDcvC27xD140%*|ug-(Qtt)r+8Y1QtA5ejA1e9;XIP(H4kf31`;Ktpk|u1P&fIwgDY3J*kTA-y0N6i);bq`i=UM|yj16CW&Wj+asyZj z&P}HRXLP4s^BYV@rSs*}-IEju9Bt?NO~P3@2w`qom1QosmBLTuwAC_7!4gBMusatT z-52V3`*S}{aT6uTD1~cs{8ZB=#(2JXGT`dz47nT&^(LCB?-Zc zi^Gx(N1IrpO4Wcd^y)E>dI;~KI-IMAA(WezCB%8vS1Hv74NPZ6HC`hPM*d91)>~F{ zWzN5QIsb@;E1TH>IS|xVyC@+G!`lO|+0jdQ{wpoh$eancgNmcl!2MF?NQFCO1+C;3 z77jBbw+tS!dL6x^EPU4=jgG0wXMI@5%@omD z>aH<}M*rmYK@ZS!*c<`s6Z&xdq{V}wjK0l)$(1NcutslX&%G7Gr0Mdc=bvZOA8`gF zQ&BfkqykyBxM6rkik0qi+GQ-IGM!O*Bw0!n zMoMm5)b)6GKY?;w1!~u56fT^6S=+K{ilYq(su!zx1h0K7Gjv2DcotrF$cpLlr(2NE zxuq}8bc3?bp%g>owr^U>(0b1019&^;OU6Q_Yj13VN;+p5ElJTbr9Nqzl9avS)$2Z& z#${H8IykEY2D;nsFPfVL|JA60>4>Q{wXgH|m-<>ys9Tt#6^VMl+Paz1>%H5>+Pm9I zC7fF;h;D2hf!x{$uI*D3e(bCmw|AM|NDlIPPM-i16g=UXYkAtjgnnC^eaX*F=FJFu z-nf^*?+W0N>qT-JlnxR9F$D~-jZN1rXo{+4iQ{xGw93<;q&?CfvefDU8NTh#;wu)pG@Q< zt~18#g{bGd&br4=Oz9YnMCI@iSithYp#;yQ>FvvVAL#k0OyIIJ=kv>#*6Jn+!|i<8 zBuGn9V6g{tZ*4hion%}|86{*`d^0rt~7WOTxM=h-RGm|4Bx<^WFG$@huh^H z!7soiQergLq}U5$$lF*~^+MW4rp(Rr#-@ea`XxX(WSz(0oX_zCxT2i%Dtr?ypMoZU9^w_}GGd9&b4ITU%-5cy!gEp8L#CVw{xwR%4X;E$_Hp;(iY!AEL% zAWWLeA*Eux3VERo1+5!rO`?vayL^o+61AU@v=2*%??0e{55Ck~68n*)KR!S*v9mYX zYCt?%^tDrX)tgV)l}ijFCHdI%{gE7jcR*T}X*#o6067jmo0zk&vmm{XDVPsh)HZ5> z%SJOU65{#sebF#X*;tJ-i7Plq1*D@KSuMi&lXjSrQF@RmhjccV6@lt=$bz z-HU&>Vlzj@j=qn-7fvTP{)HGw4Qf5$Yub^%+2^$gomePBo-{?-EtaD~l6uq6EKKRX zQtPQe6_Q9^8&=J6U&BBrEZ)w1e8;Dp)mq9#rxGEA(V#d`BGfd77}-gX2$zG4mph(L z6F!Tb%s~5*X4X-uf7waS__3mWVazmpgp#G>2O#cMPj_$Be(YDm-~<&u;Z7eDIw*^%=c@ktlf`R4lP{#Q2C0rXu{6(`m%D=@s7(r$rHQI-Ul|B`I5Ly9Iq5}kLDD*`6*h^#hw=vw4^(k8mZ}y8Feb$b0~L(tSbe5 z6hB%QE?UWh9}2CQtmgTxEa&8h#Nc+PREwg zoYc}-JDVg(mpZ$UYw_rwy)T~GPZYNn2Gn8&g#$lF3KDlKyUuClAFT7CXEM)!%uUDw zss4uJ)7g_%AvIrPfVdA!Hd=p3ZZmz#Pw2wGV z%xHKOMxB$^?$fi&_B4Y_D`igU(XIJ+3kL^F(w(b^{4L#&j%J0S&^4gIcQ5P|(tK1D z`;SqNdM_8%|9Tt$EorL2Nv_1qv@ToJ{B|k1+ax5jOg`*qTR1ZlTux;3D5_8LtOc*E zKWJ;h==F*6>~>2>b*$^cDk9_0ym&5*;%k=BvW4KXN^R69CpD`{vJx3LU3`>V4$jsY zWjAVz2Fh|b1S@n7Oy@qX=CU@?UNC|9dD&nfb4u2~l=qYrMEVX?=0E+dp}{R1lQm#0 zRy44v)hB7D{HGHKuF4lz6OJkZ)9Can$q8*vWjJXnYZatY|Q(@(pe% zcd<*y6p9uFsw{(6v26;}TPsQj(%2r~RyTGo z_H_^7X~ezPr>j%@z1n@=XAhLMb@q$JcGyuedr^WE9Chi-ofW>6)u&{Q5*T1{dp_2Z@R;Xu5))6EZgAAd0*vAyQqE)bHt{CG^+4gG6_q<4d3nwNw_gLrdv)LD>Oc2JtVY#Af*0j-?<<=02e)@*49I=L`s(L>Og z4nx&$o9!Y`?e?(cQsvq!^l2kf`D<8V0wELj49!_s>`gKv%S??+26<_m9&9m1QZVF0 z*%qAUeH*;}f64R_tJe_{#*sFazWxm05y|64XANLXD8IIy7SU0;)K;4`rZ_JpWTZ)NHsJp&`A>n_Q(I-jjMjl^#g}P^=3YBax^_Z^ zQH>5b3Gci*D^;{0Wj!2yll9k3K^MMG9j5!Qp7I9$tO?`8-}{%OFXX1`tWB2HDTKoE z&NQY&*8DUXlc<|A@a)Dx;Svy)Amy<3^-6HTX^=Xa;hmUwE$zdkEB^e(@jl|@Ev)AW z=L{R8Yc*W@X$L;>&Pi3>%>@n@dHVli?;aW}3A8N$pV;Xb9ox2T+qT`Y?R0G0wr$(! z*jDHC&7HsLo86yKqpI3_t#5-?#lYAs^Wzx{Z~w%kf5$(i&|y4EbQa;Y4=6kwYuJP1 z*SelC*c^2Jt$fcx(M~(+TpYOV6ymaLJWwqx9P~ z%~~l#bI^i+!2?(6?o8_g>RNuzuO<$Pr=iV8h1{~I$s|R@lEN!$aAs;l?WbI`CuRLy zOS?key-VXxr}vx7!wYDtynM4TJ!%w7S40PV@-rF*CZeTg4INE4cqGx1eFaqEsSX)|jtCI$HrtgAAVW_iI8Ij8-6YPh}C&roDl-@R8ISt9JtUudU zrgo$ilh$cTIP6^~{Y5QP&C5$d+DSd4OToT1fodFsii+dW8QagqLiNKa6fn&hNCy1_ zTORT&b`3>inipdz1bR%9byv&tpT!Iec32ujdyQ^+Y{#)9E-UmnrQ~tWrzlO%)E|S@ zoPMNPGlezALAD06bi>mjz0JbLyOa8Vb>33$;sNk8J^*7}8dJ3#CjGt>@4!d!dd zikVkS-m&hWQJ@%I9n<1QW}@%3RyOR$X|m}8v1AF9L^LpAOoW(cqiayBD+~yClTt}JN5Rug@fNdZ7MMMfa&ru*vuURJgN2i=jMzp-;G+EFM)<&j zukT)uCUf=qv~1TB>%ApWir&TsCuNMM!l1*0;Ccse?{=Ud}tZ_nm z=OT^^&`@<()+tiq_Dr``q`H;-?-^rlhIJkNv#fiqNk)Om1zHTW+38PrmDx zJ`SHEE_ScNwyJIxp4StRHdF4uLu1_^zPRzZO8a^I4|_TD8Egi`#Ya;47lWq=d23e& zxf7#q$`&i$YB|mvI4N`6*%=B2gCa+vzRDG}yC9&p5@AV|&33z6` zITT2x5=rD{i`txz1=1J{KmmXxbGa-Ir<3K5BnyQ+fj~$IZd3~DqS}N);Z9epYR%eg z@ftOn)#ZwIYon2tl*?899!Fnv8e3ZiBZ~zTm9A8*#@6xuLAx+p`yF%lTk&sm@h6~n zwNML{7AuR*_S;;fm%PjG4~@qW&*ps3=8j5bGGA?XA{mb-Q!12|VxUkeC{rp^@5yw# zOy`QHb|dSeS}vEbIBD(dqS34~7>c5kd8yVi8jh=SyL_?J?Qp&qk6*R@(C-TZgTm+K zWD*+LgPcRaFL|-saH{dGjoWbAm#=vegl};+rJ6-F`^9TN0EVap$Yb{Fmnrz*w zT)eKVZoHVO+7BU_i)Y3SI^W&QY|`)W;5OJFln<;5J_5f#XM~o6EBV<&~?v`7kITaC*X~iNDc}o z3M;gox?M9+n;)axUPoH}z)6!bHQ&*GchT7S<8zXphq;5Lx@&k4XHIpD*n3IKeZKq&XE{8$pwImj^9(~WJW$`R2A>?Maw&Sj6{7Pn!!>n9Mif+0nv=`v*Donl9 znXn;KaKu>G{Z!bh%``QQ!Gx;7yPz7{i-1`eHQZ9J>tfT4N~&{!pk=Ce>=vmn;!s4b zw2R@(Se^D2>pIy?*@C7*cO8W>-2?b+J{ZD1!I-h%Fl`W1)7tSc(b)Th{`0#a?x&=_ z19781xQmYGx-rhPySWsVUafKv$J64Fyht^M>R=Gio{3HE@5>igCRb34I)BN{ zmIJ;Fu#5`qc4UpZMmrA0ysj@*m5yeFJe?j9Sn#E6)KZ7wrm7rH?`G_HTFb+y!D8)o zn^g|fd0x?y$sres@76dTYCGxNGqHfwG&HB9+p@i&H%OmGmUz}RE+k0$-(9|+#LAr@M{3osO9?MScO=oWB zt&JWKmk>hSJppJiZy(yY`6d>T8v9#$CU&ZwJ$~=&^Dj5Gn9*C3KM+NAw+M|F!xCdQ z;dC%A_My->gapbM^;)kngEJ%20jhvVI_XFK6D4(FenMEEs~DB|r4u|65zV$IFRY+4)=70_@= zKQ7Y9DOAPAQs&Hm9UxKhJ(YyQIM{`o9%G-jNq({|5+w|u@ExCtl3Cf&M0#Vtv2&5} zL)zLNXX~b&1$aiVxgdkq%>~0 zbc#y%Kr?5U92yzTf-8C0EB@Yx%@=p-T*%%pE&6g@?CqCI4ZN{8G-Wx}L*vF%$!#pC zz%`+Ndom+&e}(#qM%V_+5K9ehvi#WR)DFT*1I#ZEPq$&(W3*})YieW_<#yeh+dh{S9egfzb+cu6TU?TrXUacNP1Th7 z=tsy$e33DHQ8PSYp;Y$Qn4AmyZD*%r zHY_=nP+6MV?z?mf3%q?(ssqw!jW!@*fJ7_Pf#iqfh}p~~PcYu}H+%f}BwCQy(Y=?p zYoWKgwi#&Ct%rnf@=u0y%OlHy!N6!n98tgDC1(u|aqs>L)!xT&6-*ydZ|!d;=2GD1 zf>~AU9WQ2}Ub~QQ`$`}jdcF-6!)W)vBd?2ty|8_!9Gcd*P7au+WA1}C+O@~@%cX(# zXTmk{NO2$PLUV-k<;s>{W$$#-y2Bu9r)sX$46nGdzc-{cA?t$c|1c(!s!Li*fiNa&Lb&`rVR$$m3ijwi*~{}XXxbXcUM(#s zpW`|#Y?`nOvM+=ft_%#(gfA&HnKcBUpGHuJJ@Y8iEUbW!fLg9?NqZf5Ju=~82Y~O z?!T(DcB<(;7`LL~0oxectw7~eJ~#Ww_`iGHN-g1Ew99s5SzI^-tdT=>lVKLvhoo(u zHh`NFX1|WlIckEk{_-kZFOKh3J%ZAVL+;Z`byu1(wnx>-`(sP{-y~C-^;;xg*AD8O z%NjU#!J+Vz=g}-IA%+XE0X^3eWr~2GHy>MR_RhGsDRqXS&!b-+FF|ONIY%i%Uuic}s~3zMx5x;*fQ&m?sRsEs zXa>qXs7cQYfj;~H6Nidq{wEIo|A<5K-T=U*ysEw5x^ePNGc<01AQ&>~vR&3G25Vg_ z^!8SNue18o?Et)b!T=ORUx;HnvTFVv6bb9};?SWv(!d}|v7wVRR+-KK6k6Hc;23#m z^>rtdDWmWNevxeA5Qd;1qeWVGyzt&OJD~Cg(2FALTkeu0yKy z?hxcmmz&U2*g$;lEdo{G;FSz-0~bbBQ(VI3GzR|XqVbj=50>c}KjrQ4<|o^^?4BbS zx{{-O_dSq!uc`-HmezZT^Gy%}T_0%=^4Uduuc{w6YnydLv-t<)PfJUy?)QnkEFhdC z6mkgWLD53k-|8ZRhr5=BAL+7?T2r`K!xZ6!5Q+h(IyfUCsRA9y@$mn=7r}@uX}YOm zJhhUxP*MSX@GnNexYcT4{F+?gsWrcp?hGJ7grLVt>NO#*(&5-JL(f_Y5PQD#Okf0j9t;O1 z$sUA~lYmU4<4Mgt=xo)P{pZtfJxqL?88zP$Wzyq(99>y9GNhOkGRIO@mt4hs`mZT= z8phyRg@N;UDZaLci>OwM52);f@s`~d} zn%dgL;=gmH-^gyE^y`)IegiGi`BzwanAA`;)XFFgnpMmnAFF-pQm!dHCP?h3q1DF9 zxee_j`jHFpecY{*Q0wW4Q>|Qw=om<2(^qr>1u^EXzeMxcfGE4d87+&W3RPoQ!?KYD z@K%yV4Gks8S`Fo5cfSvsJHEb+e~|6{RLLMT+FF6zd2!tfjLIpyS{I?_L3q(ssAAh- z{7uS8-3!pJ#dSy*nC6|Ip{r)Ut9M*K^T%~FgX)&o#X0bYW#G}PDz|0vumZ}*9>`I# z`Ypx1UZ-}&>5cOhsrM)te(qCbr}SppQZsSN#M*}h_c`f`QWy)xz^l#urb6GOtT5E( zYyX{XYbPEqu)R;r4hkr57mJkh;SFZr&!DNP#)XwtKyPUs`jg)f5T&WF0uaO;exr-` zmR*J=y@w1za}K0JTUfOx!Tm!x77#_L2zYvFLFOL^Rv;NJ$1u<4gp!GdiCrAqhj>oo zvI;Y=!2+;Z*k`PJ&&Op01b+=`#mDYD)28Aex|M^?fmU0Xdk<&<6is?mc6X#yvXw+k z5_)6vEsW9snz_}3EuNrOm}^xT0&Goiz?oL#M?0C&ZS2dOX6tD%=n$+~KbB=m4a_;Z zw(S7)!*VElE&glC}Umd>*u*K`~tfa|Wu7G^DC&_Wk9dVx_t zwyJCS4w`Gwv+d5FXM5pFk!y+wR8v0K{RErgL50W9&I=sJHTv3ZXaL^z7dYNw14g#D zzwB9KV0=M&2{4|XqBaP_^++9xF_Lq zKbwN_0T=tenJ(wv2jcmACFr*oR6qszA5}x=;k{2{q>;$!*Dhk4 zns@R1hBelWZ#xdnO9KL*1L(Os;ZR=hi~-=#zS0;*3P>P`!$>!3HVlIPhY--mDJHgN z76jpbL22yef_}NLo)!`T5aWmk`|3Z9VbN6FOD)XeF(Gb~oX{Rr-(Q{n_4|K;a0y@r z5Jddqmiz%A4B~+#^O^@}2L}-)@=1XQzPtvZpE=1>c(Yk@;6?;MO>{O!LOiHs;kJXoWwDs1o6+1O{nuVI2V#1SS~@1`W6qL|n`l0uaL3AJLPc zXiFt4h8Fw2zN$_d2Fj%e0+o(n!z}!`B#~U8zY!$_iboRt06>R|7ooE+6(E9+G7t>~ z4qXxv^DN}A5r-XC05~O5+G|8nH~>2cjJ#he{fwxH#7pk5#003|q3MUHUXoa>*5!1tl47Tuyu=Ze`!gL3fjIxvnznVvYa%7eP z0Dk_7$vD$HW{)i=vo4?vE^a5V5`o-$^TaxfcK%jYkm7m*03n8;oaT&VgKmSE0@v-) z=Hnr%5fOYM@oj0|Lz+mVz7hys`&qGRPBoi2i*t7d2mT7dC_^ZG$=q*8!!Ti?5 zwh_kmC71^^+@%CU5{thDFuz(wJ*ljRrXG|r+fqeoV25{^I35VXYn!C0o)CF@y82MT zn6zn6BBfYG5?Hx_sh-Z7ZOR4tKLq+wMQ z*`Zk8fk13#=6TN;%@|6nn1pqN#P4GYp2h^sIc=I+^y>QSFy6`?IYdjA_LMO>=L~13 z!6=wH=U6^rWJUJ|j9d=|@n+ex!$0Nxf9c~~NO zc<`NVH>4-7um~5rR1HttYw+LQ1CP#p5G@4D4~k*VRO|v1GKGd zfe*%oTEWrUWWP)$O1RTv`5p`T&QmF@LRF!i56;xF-idgm%cLwzxArq*GfO30qWB{H zTDU?TI^#7p$}>NTai}89K2*=iq6#huf*%VbFpFBHDuN~ve~+_yoL4H$7omFSUz)qI zVX!4kmL`3WfOD4jZ&s4m$FZJ9nx^UFQ+-_(4hl5iQj@ zJUWsQx>Tenlsg$4|8X~<@nj`_#5$H&B8SHz51KE*+GzrQ$&okqsyC{CgXD+RKborZ%Bli8gHySK)TapD-Nh?j>vd^N4B%R!N$S-w zTbwvsz}FjJsGAWQTX8z-h(~k`O7;5BET6TCUEZ47&l78?3n;yExXDcxEgB!S;(y6h zz-2Tnv^D(QXm|C3+LACP;puSZY1{e8acOVFmif&f6LRuVydqU#LeW_#-X3bxKCRuF zG1b^N+2;ER@@4|gk`@w3WAawoWUU=fxE6b$9jYzWseeZF;0`vJm6-^nQie%2$YtZ) z^~|@hAa;yMufgfzhVAaon?qcHEb+RAal@T^XJ3|AJHk^PRQtD(%nohO7Uf+cp#4 z(1qjmYrO;cFud&D;VD$ceN#vIx!;Z|;Gnas+q?Vjl?W<7=t)EKxn-bdUYmiJlgHDW zE8GuEH|D7l@sIVuTzL>@eVSdq1nqPtI=oJ|O5z^(5UT8Nyl$kqk|7zv)b@}Bf%cvR zFz77oLA8isHJM%w8#65#h&*v=Hz_h)6nT|kzwAqKxfJRcdPz0spvJ#?sn+TQC!va>WQ^fm!7ML_wz<5 zcw)-G!?fwq$#*KODYN-t(DviOK3B7}U6rVqsfO8o?QU}!p>j`4b2;5r;N9{2x`jnP zAjEg``GM>rxT$2aSWDf} zj*lyDh_wj;sTeli`OKd@w$V0Hmas^v3nwDbLhgR|>s81*;TK$5&eKDYFDf~tf>J%% zRhkuOS|ccQbJUVO3*t+vdMiKW!nw|3rKsnLeFyv7a#^IHLD*KQ=ma5bkfc7c=daS` zZJVXV#&lm;sZ85`)vW9wEy_vHOK{G}OA*D0t~I|;puCTJ_SE~QyX&)#S|kZ4TCef- ztRBlOUO}&|Ew4YJ7SYXg73+|+oS64vi~6u`2Gnf$Td(D7jfT_BwnFRwW7p3tmMPZT z+>wmo)vL>$A$+Crp<9XT{n|QV*&6g+qm>{L2ewBtdul^saB>VUqD(Y4Zm$%{T86_~ zcqK@UhwN-1cqR07MZ5=B%1x&3noPMRw>V$VxY&HySVrh(5neH~?{m!dSoLCKj zEZ)N_k-Vqd`-k8s>9Yp^;=S&!ZqKTaDn>UC(Rr+Z5RRnAKKs3OV{url}4mF>`Dki$@w0o8=aut-Jt-Sc2$ldP%+ktLX_XQczJfqqkqi`6G}8d@YML}SjT!; zonBLje%BRo3G}HfZZ`mr-bMIVT!6e7N5H9zCMjl#G4SLxN&ggy^_bV=kRD9->d67F z{7khO+Q0I&2Hz#r)xJ#rJY}}KFxPmv^-%KlvSzQc(4JWcLyc%4Y!R7J7U=+j}u@Z&vH)bboZcs;_tpao(1Cd;`3O zV7s^eoYhu&>TD}4Or*Vrevjh|Xb-3O5BfIcy4)$^fwkLf* zE0;8Kjq&5YhLl@FTzeEk@R=W|HdLa5zITuRPvXTy@F-W6y6?fsRf&s?W0Z}b+Cilb zO%ICT5tBh2;>WH~uOdIgBgY?^fBZ6>1drq^dUVA~qiRnA{!hrkoPR~NMo6At@vouL z{6{j8OfFY6L)lCc9GTgN@%|7pj!-7o5X1yFMP8N18;Gk}e=1Zc7EAn; zu6nLqDwo3_y6JTMUvVgvcfS8P0*%=h%TpKv4m)2U99GQx$&gmV)}$x&ll?-s)9re9 z#OD4&zt>lW*isS192T3iH|$xswOXjadhMs{R;!)vr1PWxXtvg)d6*xcKd{y7JI8{| z1h*#o9RxXQv8VZ#0oZ?4Z)Dr;jplR_TkiI#fYu6jUd9e7IX2Q?e6-k}G&yj^f9Za> z?5xxn%w6hyz9batA@U=nMqx5S;L(KRad6vhE|m58$Lj%oJ_tJY*66u8Wiwbuyty2> z+P6&{I$!oh?lAN)Oce({@Iw;#|B^q|l>kfKK&;4@Oi0CpZ}bkbm>>`G?%f z=*9e1k!AK~LX|Q^Bx79=!GsW(lOV8TQSR;bmXgP|-$tS^R@8D3<^KX_ofn$WO;!|F z5KBoHAuxLulQP(WtB2;Nybis~jUZ++hTUcs5{SlClTrH|QG*Ql+ z`#4!kUfZlfL;b|8>N=WBV8FhenU;231Pq>4w!Nd0%B}ZlQWmwV4O4@Yyk$CGLj1$9 zETi<}yyEqw%ORx>a=W1V_GnJJz7XpNJo9G45h5SJ!>E!2g64WLiYxiVqHP3?O(`B5 zqTN2^p1jAQr>s}wcv(fG2YXFJA;$;+>R{=3qn(*%k#$81hA>J1^^HMI@>@w;yWeE+ zuBCvGr!vhvz3wGk(qmiq)oGY}Uf&)A-ERA!_BJ-G{*AqSTK~CF&i(N(GiC$_3VY+1Uf{*V3X{q!V{BK7R5w7w#z)?o+C02HW~*0ILq7&zl-2xc%=c@Hw{^EMHs z&d8cnryI@NlN5{#n1_02D@0Ca1XULosp8T@q_C94%l$*%Q-o0O3emm$f(G3?%{3p6 z1Wt@pvM>aN*nn+axj#kESpbGh2-;McdPAFvwHKKX^2kF(HqMyHVMi9FCxZ*GMm{vR zfMB&+w7Hv_)^l~O7$g*ukL*o3Mzrb__iNY$e<6AJ7oC&1e7B;8d=og(H^B)oFG2v# zBp~&@Fpfu!3?(lWXxV^)p90&YV{Rxf%4o0xSIq!fIUD7Gy^r%uhGcsvmsUmm{j=JZ zj4ge5%={f$`k^rWrm&x952WAep@@Kt61V`P=qf>zjDslm>veFs;sT=-fc*?z&H?tT zE*8d=5Lu@~PNTOmtJ24eb0|aFX%#v9*cF)$&rF7$jfpY%TwyJSC3WOo`A5HQ2r21r z7~>#tPE?W@Bd^hE#h);l7OJb0q8r@c4~$t(+vKz}RPs^k%Y~RdNrBb5OK=02T9*R^4#@y=G541hpchblLq(=6XyKRR_#els#CH zxG$M;~(KDS8t)WN-K1zpVDtBUf>A02^XeWO( z6@Bv5q?K?{7)M`+QWjEZIagmKaH06+xk54LLbc-O$^h(3U0}+b8d0*Ev%g)f+Ol{#J*u;l6M$bm2}YDFdLgi(J+;q7uHy17CJl)soCgB zjQTb^IwO76Y}ckEU(2ghw}hF=iPyv+f4UviIy z^US2Kir@)`0nGAKs@Og-oA@nrcZoc>$4aTJQN8;@uQGIQD;5UR6Gx2PIWZ;s4+%-e zxP*PCkwTKR&%Fuw2@@9fMLJy&=sfbbWbP9u!yY)( zCuYwH_tB=rq2Ct#0`C)Ywe_?NM+aDX8^0P0)htLZLs1Fb`^@lIGPY2UREW++%k#l6 zJSbIpa+}Ki_-<{=aR2Zhap8K4yKyM`S`#;MuT-qMRAj)P33aW_pFASAJdLf^<-Sct zk%qv8O37kihOmo8<(8NPT7?ZVWo_nAiVg6b!hddTy6-ksYSdh-mec3t2 zcP%US!WCRQ+vos6PsoUO^40+JJV(SF>yvy{#lhgTG7Od*$?HV;L>E>oEV1D*Ul?CL+MS``r85 zv)6MYVd1qzr|(qGA$Gx7#BE~3ZcHT(0#FqdIqHvbHs)Acr~J8?I*WK`;hwo)$5!nX zn*ArL56FGzF2T2_*7s3R75rRx`*}X3=ai1XeShn_)YTOfTrIF$Oe9MGrI{HinN=pE zquoly*|$Fj_NxWj|1(kU-(B|>-b?Ko&&xys;&m7P=jI;&mBFohi9+{E=;`S#o2pnKj!}+ULwPVEMJ#d#9NzwFh#l~q)%sxuSJ=l1q9Hr&piRgj%r=u!_qrx2(OqQqL#=$gwjH! z(jNKQ1*h5%71Iw@-G8?!@YK87)DMZjipe&}uy=#U|p>>*`VF?Lche0VX%ke=eLA$v#RQot~xD1lHRrpRCF2CHC(uCjeA zQ3+F~TuXR{=@97{A+9M=%3z-G%i*=p0dCzP6;qL4hT$B=;%@EY*306y-J>e9BkX_q z)l~WTLO3;JpaoPTw7^C8E79wqK}AGy8S;tIz}Tk;+tOggo7N+xrbEQ`nFxS5%q+Bm zosT>Us$!13Vs_P|p{nCyuH)g`;`V=f?ODYv!Nquo1^eMRT&nRZ6vwo{Nwu*9fp;JP zN+w21A+crwrX|{xCSD^}2*opSX+VfcaZ?)@$6^uxD$Sr}_K=;aWJCy5D6VwG@@OHe zbm;IXSM?B{?ue7?h~p(Z`QPz)G$!F@+$372dLvv^(yGSE!F1c&hU5v^gpf=hMnav@ z2-H3{Qem6y@#16|k9?V5l#`v!Q(etd#iG(h)sm{JBCFBT6|&G3Rg**D0SVP9834d? zeepgF-FFock<3uZo@}zop$a~RN+xqZW&kM#dW;IR2l|%koRiF zH92A*d{);RMu=H{OcvKGsk>p2xSq5(Icav<7F-wr7EnAO2R{?kERq*9Y7MSXpgQ+7 zJP*GzkDw;k@g(lFJiz-j6W%|E5=$ie$ajXgM23`3nRI5p8*Q#QWRAlXC`~flRdE%AGNLRshy*|? zADR#wXB85pBtl!Jpp8*NB?jlCtP9{B3Jg&s%AVr{Ukr1d>G`8b77E3yr|hM<$a}rS z3!zF*ryz8!$P>D#l-v)AmhC#cC@v?#mn_Kl!E-9Ypbe&8{mzE$~ z)s%U zY#L-eYJw~CFEQg$vJpI#dB|ky#v^&|--E5nNHL&{)Yn7jl(A?)i8|MvY&I@0719woP?(X;Y>+pgDkobtF=mN7IWAy}+tP28=l1`uEh8}u<4 z^j=j6AR~IuAh3W?<7)?FW@N}IYByh2x38I3^VNjER&j38LezQfmm}KBg5Fz~E`G6IrUser6YKKEws^5%usd&F9Hb=- zAb8iK)0eWd zRin36v$NB(Q>v!%OKy&EM>W(ETon+hS-&`#y!|&D%*?BBk*KpGtB3t-7q8+NZ zHTS%i4gtoL0>(rZ%mV;_bs`WJ1j7{sWAy-G)dZy#4F2H}NcIMxeG5SI2>P*aIQ||0 zi3I|vC-`jGhQSU*jsu`#fv{qMkT3?~Bp9N;=`vk|smzx(6oaG8Qd58e0vfYxH%q43 zDiSXzkv2!z33ukQ`N3G#LSleZ_OUZUZhD1mYi}z%ub~EgBZqH0#`L|1yDi3V&w9KF z`X2KJ10a!MZ+rj5w%9RLGDA%)zLqUYjxr!L1K5ZTs+3YUm*wb|CRkM_T}iR&=0QiM zQ$!n7^Xw;Td6vITNH75(HKh<_#2#g& z99i@nS;Vbco*eN5kK|*Ogl1(AN&Y0W%!`hWIgn3@xAIEjM>w-b$a(|~Z~*}2fZ4_r zDLndyx1SI5y5Qu&5I{E_zDxGYPf-E{14{BaRd$0X2$R>)V{P$o^6ZSp?^pxhgp}_n z^b9PtKPbc!Jnq4OLtMv{f6J_Ng9}pA!q40?_T(qqc~b7Q4TdRpiqiBSbFff(<4^M` zcnb;kXlMMLXu0VB@w289khM~eYhZKTAXv~ zS|5^E&E9Md2#>xT*n9k#bG-EZz-Nd4>&B}m7on@x8|d4b*Jcs(V4;GS{^=86Y9_IM zHt}Ic9jzw6M@qGfJ)gp-dTyUKKD}T#-lNZJ@pqg5{bM zXJ%jocm=w@1U@e$>@c;r%5Z{;YU0oE-+gIu_slzm?&!3d(XL*so!q{{L+#+o zZ-rQAo}zzq!fuKZZf~6EGT3^lH80a1QW?M`K%Cjyy~Q= z=9;WW`I+aYC_E%ToFKafMRXoz7}2;WvS90nck-d!rw3$HXE;Rb)z$ehCtQ zGu6;zT=|U8V0&r1!Xg$i|M*(wNff z1)7RC#=AoCdOHxPwm4WO8`!nSd$#{`rLyo0wfszN`&_NVbG;f0*L=>Ib3a^twLErD z&UHhAdK+>-Pi4K&V1K)~aWgWF;ZZ(V&ph5&F32sM*V25`@_Z=_BIGQf<-&cbzc03p!Nq|>3x{C9&`K)q69>Vf zGTZ*SDl9UQ%c|ty4JyRQ|vZNxNS+1Zk@`7Am zqujOg;(ST1I$t<=H_EN*Y@t}fjcH|N%BrrsCqNFu936|PdbrhI4OkkJzE(ez%w_|b zZ_8q3wIppr6Z=!Qed|zp%e?KT)!yql3~p`4VXyzs;BOjR&797`2+Ss%t8_eGj|38x zIN3Sop6SfTg`@1b$J5z7iGL(^A1>$qC66WYGn*~etK|lLaj}~%SL^L+k2eVU9C!QQ z;?V1t4(DJXG%BL$nU505*$+JR;@ybC;%FQ$^S{uGMPt8-$MRgQfMg$OEOsYX9Z9F3 zxk7nn%KN)X6l9JIdaJ7Ek^*ZJ4u4j3um@7 zEZy3+%c7jcj@z11_`ZY8M4;O+73(ktH*;6{fW@yr z{H?L;sW<3qE6WyHu_PzJ#e=5&T+?B${Lz73maVm*b(#Z}m_n#y#&otH9I(q)Uf_S9 zVrdlZd}bLQU%9m2ouH;|75RRWMHQXZ^Hyh7a51_Ctms%^ryi)FrnPpg@BFxnZVyZR_k2`98>30xiMh|IWI+X>`9Ko>+WXhbe0xUe|qM~04 zo@rBa*Ou+QX)H5 z19h%q39>xLb`ADhWWx+`N47p#3_@6C?N$__Zs~c`?qo2h@=D!^rUT-b41llEXU7g4 z=Q0kOmwk0@+WcG6t$`79zZ6AT&~vZrkKzSB?2e2k@9teOZP{jo7Fii6MKFwT9qWfbvDT%&0xfCb zO^6Xm((T`V?Xk450@1X@bmLm)c~G&q+dRyXnqBQht%Lpj)lq6$QLjzw8U;sy$%Ty= z;k?_J9W;7OSeS8X`OuWGnBH2xI0BT>7fVK4sV-&kYlu*3>XKXIWfN;)x--gAoxy0L zoYa7s<3cPjgvc3Yy0$$f*>%#F3QI{$tU=bAF#ho}p3FCd7Z+#Sm-u*VTLwyAz!M% zHB{a;dYGU%%5vD&euCJ(~D#d>hp!nXOUFvBP3oy_oeX$ zUCjx^Zu_lN8Ql)ON&Z~t4B`C=LwNLuU-*+zA-(ntk-4kCS`w+|dkdRPYKnLfHgLLaJg325V_o+V-Yk9whelTH=WQ%b&QT}tZ532M#I8JE;o1BX7IT#aXN(uFW?j`3 z%f&ZmH8F_$Or@PxOGZ*CD_Ri^LiP)?NK5q8p@?{p(LIAxT`KRyhI~fJ^T$~wszvAU zGBBKnsT^5xYDwCCga+CzQ%CjGxliz!;T87s=zuuIM;L#87Jj*g|C4J1@dcN)W;3lS zF3vU96#f@iCwDw3Qb0bJ2f30BqqBbJCL+{vX&&oZrXPH2>&Ruyn4JkqqT(3cV_8+~ zq&+7(njj<)HqW6();!V17AuIhD>a&y02 z4WyrdYwj(&+B?b1s;(9*EwP9vHeGAeJ+N#s0|<~8QKquHr|r8 zdyR>)6xw%Dr*fz;eNM^rK7j>&O_i2kVknVuy~$x|a_Wjk%-?PbJE%tG>jr@@=j~rt zpDww*}|!K>V; zX@J^=TiTpkR7`qGM1?xEe8I;`@(qOk)Bft;XVp6;J;eeXCBvK?5G^MsnMW;s<{hLd zwsclU#5yN@V!9dEop?5?OmJeOqR`cUF{>Q0%YaA&5YK!rIU=IOMaSvX5nr59C#k)0 zaD#(HdgM4b`dvjmz3KkVfDRm!1?}Un-^_GLrd(@JAV$+{`_p8Zp;CmFo(2PSI+nWo z#WkY?Xo|yVg>;pBG#$!qzFEEv$T&_12O#QG0Yoi-RaPNX5OxW3)}<&aw}HnI(X~Bu zEOZ;9=$o!|QC*DE$eXxZa`{CadYVRtsO&gszWbgV#w}__pAa&VoO<7FDPNEy1)^Mq znR{7Tf zlL`4n;`&(<^B5SjiM}n!e%ShOHz;H;%J5OyS%fC7j(CU<*jj&nvbJvb*x~<&x^sxG zq=DM?iS3SU+qP}nw$ZU|qhs5)ZQJZ59p`+1{yV;-JG+CL)>>7A!n>dSyyxP}MS6r= z_>h*)$6=?r|CJCB4*jo0DOT)|+s15By@(-y(Q70#{n-Hbn?$-Q+c&f1_oDJ&V-pIB zV_|xAp{*^}u2Gyj9b$RX8U09P6H{b!Ly6s7&R~oycTKb)H0?>C$u7H`3eBkssw7GN z0m;js{f#AP{RB7xSwUgAz_0{mafnCsb(O&!Li9o$69u)XU2PG~6l@&iABJ3hGb}5= zFOan4?;#gYQPo4KO*A%xKtg-v<^c-9gS#X+fW}Abx2K04h9&7nd4aXHlBfLvXQqc>ze=#*s z4IOayo!n2=-%JUm&j?3LP64xW9GTNFuw8EXZndY#CIy#)79Q%GoG#0#ReKPy$X@>@ z3>?b$?2|l?QZb@S;)G4?1Bl7zw6Q=_Tg40I|7*C6jjE-`|41%0VB7w^0 z45~6QD9%xw0kvRo{kBDd`Qk@*nI_;gN<9TLbb ztneXLjvG(bys1v}jJ4D(I-uj!q>l?v%8UFA6jU_(QMS8t<0T-d{&OR=PPdTyn~Onj zmGx&IqsaQXLYWk3C7^sU^^js@Zhq`PAMR1w4Km?)Rf!mFB`TdR{Ba=9f&CkHOkpV( zzskgIh{|HbYRxJ-PQ#}3?7}{*{MnAU3WA!yE*l86E`|MP*qxS(5MYH^hEx!2qV|2c zXh!ib?FjFWe!7mz4ZO4MS1%9L2h}4fk{pcrDJ+g(5`d} z?71JtNwr9-yoxoRi$(XFKWp#^1V#@hUyw6pNoG-{eCj@0KEQ5U6CfhSN&1taE_;e2HABLaKnVC4#s;h_Ow| zPd29$Ew%(Ul1zV$bL6CUlg&f&U7D@_lD_@1Tyb|C7-a`|R2F)Xn@SH0ez$!-Ja<0B zRUnaB3*SRVs6(+>M&a11lYpG(U^q6gVXQjvIY8^_v;LiDMt(;q`6KFXk!8a#ib$Q0s1*S47J*U_j)mx-RU`LFFX+-miLTcU%8?$(D9 z4%f*!Gka{b0lxo||Bo&_gGC@Q#(6r@7b^CA$<3FIleUTmu{RUaso6i4*)<^UENI0# z2PB9lGu$~oDF#8}(pmYH*0_x9S-3NtwZ7xsB0%BCtyE=>SgvM#=6cBprlM+@Pts#o zoL+`VP~AZAn`NZaiE!C4z`ckJ62t#uf86Fmncr5Jet#MC1Zli{g+=S~F}o0?s77HK zkH-jK1{>eA*7Ss#>e?AdMgBkvirBRD@PKb09?Wb?Lob|1lYe#z&+dW)=e!|&e@0aZ zMfw+LaZBP|O@Zdni|opb`-EZKi2JVQ+hUJEikN_J{n>So=m~%SVxr(Ja;c@77cl1_Jz66b|f-1UoDc(6?r;}Ekd+}lMERZz&yBksjN1+4uP@J zN&iD_(8K}iO4{fq+x?7@Sn7?(diCSwG59%$3 z=o;KV(qp3U9s~0wip6x@L$D`H$3=*!Vl~wN z?OJN;@sh0Z6iJ%l1l3xwO38_7$#C6NGqw{l?v*<2;lEvAPI`84!QK1eIpN{$_%W{O zg0IC%EeFY-`Z(!K+J1g=4^=Ju&6SnY&z*}IA(yeOc*6O2a0cPCJ0b!$g@iaK!hKgSz*%+PRyiJ*RG(#3XT$;w@m9XoBQY!!ZwmhtI0| z$|>SQF8ws=*v0(4w!^U}){_s9xfzeX+Q5)KpYU<1PYD}z0O87qGQ2fP_Ny1H#ixKB z`Ga>>uy>QHyR(S*rw=vHa(BMpx7Br&<;aMV$MA_GHEY{)`VV`b^6-r4cg{BOi+ca9 z$H0y4lP?0;w($DQ3~|0l%72N$oSZ|oRo=T*@85}e`{2#I!(-6WOa?$rSva>{H~yT$ zY$w8g7amGjh7vf#g(?}ZmNt2}C%LUK;jx_W&Gm9il&;Auv zV=x|&fN3MN{-hjp4;sNY$?r2=kK98JWqb0L>05=*h=)k`j+ooM$MBjMmKWQv=xhV`LvBwvsiXm|jV(FlVMSsU;tGL4%`MS;Us zA(Kk^i9A(1RZ3gZXZ6}rIZ`Ott$%X;m3O28h5t1Uq4T6#DHls7QK)yNTC0|ef;_`t zMbpq!Y3h$gr?FSB1?fPbe$lKI*6CWHm90V~)rvZq15QfY*elksx?WwNpmr802{{P^LnWf2}7pm@Vmw^l}IQTOvkI_B%kQ> zw;cM#$7QuL*6w`1)|=CQT$Dtvy1KQ>ayDbi?y1o2M6bJ8d}z}X$-^$yQ}a}{^U33M zlOsa-iXoWq=lygr{MiGWOY1yk(rckGm({FuhPbJbE9K(@9J{RKuBv8ORj@(exP#_Z z9E8BOQ)+znFuAX)ZFBj#wM@B=tgTosbs5njF?B6*z~btz>xOOH?tV3q`9VzMSmm(- zq8;iE*qCNHqJ{~YiJG}oUD=7Rmly?}E1o9XR`O<7`%#R+9OWtMO1Am7Lb8&Dp|_)J z<>Am^JX8_@0n6hAiXvJ=*Dqb)rY!L4Sk*ah!e2C8hb|*_bSHf+2}NfDu~IGPu_qN` zE9qa6@|5}|%Zrl4;#CrP4S$&CUm3oZBnAJHqOq|`@}<^+{3U4+;v$t-Usdz{!817y$sdDMk75 zqiJ=EsjTp%;D~GNIG3uu5c1LPsI2kFmur03zn8^i6tB{-W z?&Et}J!a71AEhPja1KF0-JNg7F596W(!Sq)jWw><^=O=ge0=$IV{mz;@^@q+0uaB0 z10VnZ0Nu|4NCbHX0YC!*|H0r%H53kpK%>#|j+$&+cVTCLVu5L}jzpt1nCWOd9#15d&6n$JI-SmBwcVTP{OLz16pbN}?`pYRu28Ku znC)u4UT-j+&X@0QyWQ?^z22McZol6j2!SJ1=;`=-Jdr|cIM>tpbiVNaof|_9JJ3(N zEEb0YREE?@BGg=qqAklf(sdL@FPOdeG9#~zy(2l_m%$3Tpq5z+wJ=0Is?a9Dh3w2M z+Gj(%7#{{lrzDa_;?gBkLwloa(LMUoApHN`jX~E8H3DvDH-y7}+eWExtJlJ6Ps;#+ z;rh7oI*4bx>p+roa|qZpX{qagB4ZL3*=x&k5Pov_k!C>gCD!Mn|FdNZ!LV`y9c0Ak ztLw#yeE=P%Z|eLi>aA;E4}tOZ z4TdbG1TuDjfX>i1fpbwfj3gyDIEs@A|#c6>njN|^if9hf4p z`X2h#9MUL}SZzu82)^rxKm1G0NjV&EEyJ;|A7I7z40o*>1cBwqxB32cBa=B|a==Mf z;@}tfMhGZZuMg#p3*!x2YM8+Ll{xuHqcM=3yRc9Nb9is70*xuRbfrtaji@Jl&?@tCv`hibSvFW%pCpoom8@sAiylV&}xHaruh z6x~G-l3__AIiqzoya0les?r{1ev_m0QujUpR|hXC1^cEotas;eyOQRkv|9R)@HM$S7>0wE)G~A0*b& z?NNebtqDFZM%Hv*AgWB0EhVh1#hekD%W{Dx<&%+@ioEEx`hziMZo}oW z<}@E%FEcB`%1ElK;w%+Gt2Gj^F@ZY;`2QB76o^`|%PemsXef1{Vl6;R8h-n%xv?l`=ERT@!C$Lb>7@yofa9TZm9zvt&g5|B+R1wR zY=X?ryY*e~{sIhy@zNb-D`f-G@WLQlmyL}PLi6ZoJhoz&U@P2l67z{RJ!O$VjZg|J z#Px2~=munNil)KL*}Y@pMm6N2g5=1Z5#DBrF_Mu14P{cSZi&q4D5Z;OXAXv22p&cl zR?Jv+K!^8PN>q&0xCzpqb&1WOk}6H+9V*n7=wb1S$np)Rdq?FLuy>dCKCxa=V&P1~ z_AyYvLZO7Up}I^QKZg4k?L$yb5Q1CgnlY(U1GuWyFmYc{Vga2+m^|~G2_O*XaHHWI zE4pu-gJI;yKNzLB!$=10()X`)FUA;t%1Oq(tA|R06|fNJf;^&izH>z(+yjY1>OKMY zk^zh5X?C}^&?g584h$pzYlgl`GBsNW2^caq|Bt0ao#ON=A!;LXR|6f_9Wk+kuW;*D z#A^~Q|BQr8CQ1Pdqo@lY!NRhG07xAk@#tf%vchGbjbof>M5bj2MIf@^H*)T!eJZ-u zk`k5qTO7|Z>R^jDUx?7*bj|BF`&SinB4J;7!DeUC(d+`vU3Vuz#+ zUU_mJM^5ecg)__dzVlkcRH*`zk5b;fW_k6(9HhxmK+m6Eb*D=7))p7qo75Oq7PgoG zL*zc!KTZ)H{r2(*vv7R(dF5TJp9bUuF00TjDP|OVxgWzB6+!W%`=T`v-JU= z#iqcd+p_KKr9)__J#IxLe?TC?Fp|G%MB7}vOfu?>WBrrpPt`_lprvKY>6jtit@t_T zNz)lDa=CpnhxLY$XU3TK2eRLDq0doSfI15+8LJvss$rLq4&nu&rMd%-7+YKhw^+G{ z{F%p^1o4K4_r9j5QoGC3g7?Qm@Oef+{26kl2_sI6QxXI;+CK}8w4m=npSjV1MozbX z5>hr~rXL#KT~P4y>+tRAE-+k3^jZN-8-bc<+E-E;&vFn1tth<&Lzjd ze5+3FMAUp0)-*sWe7C~A-6Ie@$Ai`0f(1>&vR=T^ganQ?$hJ*I%2Ptn+=FH@J%H*< zN>?LXBdxz*!$MOjZJjRs7o1$&r~;1Hy*=MZKL!wwA;`;&_>iF^i+}XtV;13=Mv+)S zx~D;AXLc_VQiUrX@8)V{LZ)CB{uJfGhvl{^7s3A(B4LXlJt^s*xI-Zsu@!5PA{8=S zgbDFR20gtJgDgkufCukCtJ}T)c9OJpp76zi3_Iow8={KMHzF0P_D$(4PREz%W~)X2l~T`X$MYMsLtvmypN+y#m{jQ%b43>p9zi3r8StOx)A zQ!>O=9DFeJ-$z4?+I%EXmAXwm7RUsYR?J$hf+a5*h#(YOj% zO>^^*%>&c(1-UY#HwrB;vy)fDW!^F|rea>DaJT`v|K zDzLL(OeJD7<2p+fDPWxKu(M>diugWC{}YNmJRmVLB|~ap7C*M;TB&eaElIjc5;zYk zCrwsz_veU)jmI|Ut}2y-@q524_8qr9n2epnO5l!?w4W+*_DS%OPJcs7zR%KCuPWbz z&Z5yW&j|Na&<+@+s<@!6`A?d3c0#PI0&3!_Yy~-!5jGM^sC;)Nq0y>3)>_LsqL|vM z0F*O6tuparGcXxo@TfwDJCh zi}+OGGN&&xY~CYxj;m+A@^&rsyvq{er8?j12G%S6B{Fg8cK<+y_Z+3*0p`w zXUQmRuaRtbZVX{fyS!Y9b~v0MfV)xl&GEpu)-62mZL;=EGu~xVb6AJuPi2MFRr4`h z+sbu{*tJ^LF_-N`ldNy>OLnqzb!~x7!0uLK2}T{UNs>YJfC>3;3S8nMQ<1og%m`k2 zLiQqzn(9}duKcPfSZb0}teQ(Wl0VqpH=hce;{ynG12{$wv)Ybd?G^y7da9^F%$P=l z=wU`JceUbnznGFNSuc^FUm%sZI5&xA$y9r)95AiMm#8jR+=0Q!Rs!9sblHYX+qz^U zd1xE`WE;LN+_8i==Qgb2vh7NsvSb!J?f__PvqMdv{&kp`nQN<&x`!Uw<8-#9Z--1y zbI_Bw&_98nXbZ{>1)}k&~@De zy%L#~{0~b<+jFxuibPDks4v}~V7UpL>K?+LR|Lo=gK5cxaJU1aD$))L>ADK+uF+4WY zbd!y?v8^e(1H^PqEzLj?q}gdyebSld(Po}f@ygJQFaKQFiDO*9ua8s&>m|P2iWnV! z#9P=%b&WEa^5k3*y!N<$SyNCc7nYk5-R$Lj&jqc|NuKJysc=8t2tD3^QS|&BReb&x z%ffrDy%gLzvv$i+&=Vcr0bADXH;HQ6_$2&m0Y(qP1_Px!X5C z5FB>B%B4mlJDIfXHQdsTip=_+ndVMk3eV+UnJmcJA~J*~cGN;$ZOBUxwFdjm87v;8 z67Asc>^%RNQs8yVr|(!7M|`RMYTm_0=<$-}C+KLc1ktME)a^jLxKL9ucb#-{gTykX z)GmSiunVvIfK@zf<#2+0+!i~YOl+AdsNsA;s_phBxf#yhweJm4_&l( zHY*>W+Dm|#x8r+|IJphY<1{X6pUPj)M8$dP8%P`2Rm{Dr zvc4Pe4T}ACGE=ky$q<)>2r6vlMt5ghN&TSE#MPhF{M^a?V=Kn1)sk(s=OCkMk3Eml zxshEuW7pv4AwRD$(60lv_(JP+!Vyg)f!!M~O{;Os;FiNdG_m0`M|0H`@uFHOCn_6m z8oM{qcy(80uoD{|+sj*ZTl45%E`dPF zx)bJB6A;FS=uc6#cEsrbx^po5hkhPptslP1`UIhkn|xj(Ywo1>5ztuOB=va+g8jtA z+w=Hs|F&72|JAp#e8iK`>5^zzF#y-AMvGJkHW#Jpu(vV;-yY{{z3sV z>*=IaY(`6|)b9m*B7uG?+Bfg zP_e}QJeFg!8>whkcw9z4~Z&$IK_Fv4sYeqA3)^W?)Ep%L2Y~;$F(wkV-T)*k6)2^0 zKaK1z=(On!1_ROLwvcf+Z4SGDRMGMHT|S@Z=gZHC=fXbkQ>^4r*n^;OH0A)lLzQr( zA~^`o00>--8?7eu+4Q(I~0=5#a~7crWbTLRVYbh?yQ8B;c!%i9P74dq6qya5d}f9-+$8U#h) zftv+tflw$hn9v_VXxQg;`cK%$YkJ9iSF6 zK=kM9_KHc0I3gEGjPx|pR&d!1go)Cs4sLwl)`IDXAP18JVC3X<8a^9?OFvWjTqaGXJAfFfaX&wQbrqi=_QMQE0N$>*;H+vml-4Gc*Rw+q~Gp!yGhLa*XuAx%8zYy^^YHh&QN@|rGy;2QqeqRL*j9~IA1T9Mb_vP zme{h=@T6%Ufp})OXDP7sr2uMrdzLx$D~Pwx ziVb)pLv((vyJ}W6=d%`>`@XqBp1To1iefIo7h|)b)}G%ndWrNu{*Rx*k7C%Nx-CY9 zrD5poi!J%{hb(sqWr5*UT5$>1I+Pi@_txz;>Nuf`zG%YZ{eLfhrxip71IIzx<2QT% zhy_4EXs-u<+!*4e{U1gNmi*Ou+c(3lfA}r@U`Gzhu`+Wxf6eo#gUuY$hzpHfQt^9E z7{Nlc4xShpyF$TjRzq`j;fqUzapGaXIX6f4n%xUEFOP%UuVU5ofJ8`=B_leEwpQ8K zQ#~}N^;Q7F;Yb#vaaE2`*URz%sOG^)RK~jlZsIH!R52Z!Ch+|iL198R&Au$<$6vnk*Uf_1O8(7L^VkjD44(F<|7a0P$KeVVMq;C zcbaJ8KUKI^xs#lEZnLOa3#rz*=bKue3{R+y9s8se0|-X)U)e0-U0}ikFw_+PV^iT4 z?rbu}St){M|0O_PLha47Qiv>$7FuCaKd!a%2Ma=(TQZ}X#p}6;8na+|aZcsyU@=HsB;6Hfpw#_N8yXA^&u`8WlI%(np ztL4h_)4a7L5=sLzRZ62YYGgWWWRXqP`rkBK!!)Q#S8ipl-ci&C*=sg+?qo(}XJ2J+ zK-7-0GzNnrTe@4Eb812BUFT{OOb;Z7vUpK@k(-9ei@lJl}&X5WjF~{y79W@-K#BoPk`n%Japnjf@Qvw?kTW8=H zT(PnA&%~3SIFkvrHf9(kST*u;o8I{$$9eah6N2oH=tDN=3?vY2hUhGmY9VH=%D~AR zJ`9G}5a3ngUDM9xEVBc(=MBbW^U3W~_?|Z9ra)Q>ldjD2x=`jQnjZfI`JHEr`7BjX zOXRD@U8(EpND+ys6c+o)BKRV}6&SE(;L2Yyo^P&{OSm_Hu-zDUZ^e#~fUCfK>k~X} zjA$Z0<^}QJs-taht+#ow#QNvX2Um2C#9HjG0j{`1yw?-&*jw#E3*CXHpA?%45-Zr=KhqCQEi44mN+dwy<+yjtCF|wF3Hq)4*=>j9M|_R7oK0i*+nHckpQJM zfwrYBIZ=IK7{ZDy~1D}c~$}sBDi1t{?m@6e867pQ6cfcYK5DcYQIzO zeV()aI`26osi(Ga|2wE(e;3woZtV0bQLsK9OmZbwYw~%oiSL~z75lE6I0wT{@ z_h0<(dN(tHX2W&8j{{77JEMw^r_;I@;`MyboWDOoBwad*FIKjaMmyXi2dpD=c+2u`dBP3~}B)-Z3&(BvPL^b@Fi+30bnb{#q=;ri?~iKs-9o!16I1zrP%kO5W|M%7hc2s6YLT_X&m~A4MF5*UMB9v{yQIE(O zz7hohs8LE_55(g?2;mh9#(aaOUjZoH^igM$n1~|i7mjyN^Zs)kc()ytN*$J~5!Yyx z2ya^h$e-N8%aU>lcFY_B(X?M@oG(-8NtB4Nue9jkOJ^VzF0=pBxY;{ zcRpo4U8SjVMdeDwvumWMZ)Q$*rp06;lcb?roUeJ2`59<@WImcO=1 zK)?UuX;vsqbTW2+YGo2_RRzOeZPQsz8e+|;bcL`15Ui|I~hg|5cbbit|3+>h;8mfZJjA^ zg)n+uYEjE8ZQXNqBequa=kn&L5dGP6*wf@;qBH<744*2!6 zHXGXZVz04dnYoH?`qTg6Et|w?#)qvlfX0@JZ5{LP zm+Zz5d~Xb*>kZXvR8VKC&cSqkm)lDeX@sQpzGv7Jj4m46znmkqJ zUPTYm2P>u1&Gtte8=~g`36s+j`yDFnAT~!Uz9K@RBQd*eswYOEfyM|!6S*ovj zsr5*{U!lHlG&>)ov{QefZ{!yk0U{93H`~BJH8ncXAYbyn^ z1(0|=P5uBZP|1FiyH+naPYKpPzAw4@N6OQIFdbQHjMlrS&#!T(uU|2G0LyShVTD-4B%ikH1>-4qQeHyr9^^Q6-{4r$!&Dj3#9ATIngiB0 z;FuWFgeU)CP+i~m*2va*Uo*T!V5dwQqrwSIyc;AW8eRp)e9AyVSCtuECJ|f&=p+h8 zaE#h&%pktI>c3uodiIAXS_RU47}?yW?o27b)W!mq>+TR%;s}9ax$vPpn&@2hQ3?S7 z*!o39gk^B9Ex-0W_x(LBj*QS@6eVY5o!(|Cl(@$Mf%YGn3;83;$wA+n_- z+e&MN@LsPy_oLhTG+zg*$_b;iLmKgMIBHd!5Ex=OvNd@HJtA zD)&8`7&d%;M1)izW2ynT$O4#fnpSqrWh=q5{L76cqJ5`@{R+qJwZH?o;KLopRgZ=h zx5ZUtz}EId`I7{dA_QQFZk7|D<_O>V%|kx+%cks?gUA6Gx7RRLuXO92S&z6WY1x5Q zoozSA)x7cp?)cwphugwA+ek!5_7`X<5PrA_s>g{3ynmX1egP+sS~YmnHc|eQ6<7#i z4-|Vlab)pO0E`}5ZeDJLU3b=9LsHt~&i`c@cfQ83>8rz6%!m3hcovQbk&IU?fCf@g z%vzC~shB^qOUab}`m4WZPW>Bfy3S0m)mpq)Ezsj&;{`ih;R0vemPPSFtJ7IV@QL~S zt}nn|b28M>nWhNag}c2R3;@8jR`uLbCS^MOvVTm6aTW-4|4|EZ7e3PTuV$T1ic)Tg zd1_jOTQD&`6mz)BYG!H?dzflsLCoLbClp$6nuko_{!)p={pB86)j`Y?R0X~IqA{BR zp)%p@zMkvem#Vus>ORY{n+y5yWN(UHetje1v`q101>}B7DoT}BjP}NS0kw4Es*C;2 zMIk~3-5L61<4C!)Ry;CigH*11CuV&AJjZ$9DwQgyS3ZBOen4omwfXIC`P&Ug^TB)m z)nxuPN%AH6H!kbA+g55c<`r;TyB zvk$;`Fvp{5-D(FoL0Npt_Zt*xJlLjGm1oux$kj7CGq>tiuOlD-3uOJ1>HM>g)6Xji zy=wf&WaXJU{xL+D1@34+hM5qW`TiRK0ivogQ9$lmrjS?Vmsod1tu#9`?>D`6?mGUc zU%tNLe%9le)~j%vQ1D!{{y|gZIrNHnUyqtv?lz$5u~YC8b`TOW02*>oHv$0i@N`q% z2`B7%G$815iwBtG8!w5gl^!g0LqU5yZNSiR-5(5uJY%qwlsg`ZMto4H)L1F+_eWuK zT8z9o{=lXr{NcY~9!tjJX&4RItWg7HlRU*#-Z!LCXEOf-JltG9Q7#mUM4-~!JX0-| z$YijH`}~{_3XRVTtLW&5S>m*^!oDP)k-rrh+XG#$_IvHrI# zx=c};$F0v{fBV6Hv(aP;CQsxZ99~6PH?}bK4~nhXY`IMT`o*iG-SM2I{QB!|w=Vz$ zuFl|_X=o%Gnn%ou7I=N>mEpxt}5Q(Q}ZZ!)t%I^t9i7x5UaOv*;Kfyo{ z$h1Iv3@xEmm~L|j1Z{+T@m%K(ymbv17xq^?{Uv!QEca034E)F^C(a|!Sm7}h$+V&t z0&Dv8auV@^QLKU(lAy;$d|+JWOR^Q{$D64wC33PfUEPQaS#skFY7BuOKzaqFP2Q;x zYJo{=Ee#Qs@=!;Qq&zpw)wqJ#z#P&dPndwDY&ViZz~jgauae5rmpyy@C@-Z`ax1{& zar0bK5tgR{F|_aq_D>W>=S7k%&xaL#>&2g#l9VeGSQ-p27$f>wFPqY|6*KOgbYul7 z<4m`xEMhk^6;3S?h+6C;4+j;pBu|`P&f>c4cWu-9EAO@A`hL)=t-{HvF$kgxyeqmt z^AtSx$uc^)l_i@xHq?x}K5+0IhoGu69iy4H4lJW3y6}L>j-1%?2cIo?w&!3U@(_#5 zPofM|pUNE?%9dY%HBegOQR5E3{RRc zka6}0*SdUbd}BD$mNSp`^a}TpOJ@(}(a~^vgE(B*8|MU~YJ@*Li_{xWI)c#P7@A;$$70&~gnc;uX_m?pEUvVh-A21o6;o z@lKr=>`l*n{AI7R(aOfna?hEMErG3t$nDl{g!UchWqr;mk7~P;J$l1WmKi43YS~%N zb2)~m)2bZc-Jvj}LCYe1d{zCDojewWYqdp%u@R)9=D&#MHf@r+&LQr`ORui@uFslY z$G?q@%NC&oc^Cb^Z_9QFUT;srE0gnXJUxmv9$qARjz;Y)*gw|}rcU|X_>rt0b@Y1g z!q^T|!wmoXNM5FqYzTISmS0a&j}$xUL=Tqqe~_NMxMJ&HZgmK^ddmD3(FhlJBS90` zDF6yB!n7kSdi?iP0Dy^XvXrKEjCD~f#;U08CsuUg(j|C;h3YMkb~JeYlfTF@Q4ei> zFT2y6TW#Pzq@_WX6J>&h>5lJn7Zj4>n-I>Mb;iCkTwL-YWTgey5LVC*Z%#Uiq;F{%1 zdahwbyWnpb-y^d-`kX1dc#$0bTV)$OPzYKEnH6o6?u>}FQP3PR>9>R*F2J3#UE+no zF*5q3RgXfvUmrCk#5Ny;7J5o4_^;LQCDBXU2wMl&tgmVGoT1L;6i0wmSZj1Ko}+db zPaJ~Q*j4GZM2IM%yf9*5Vkj9^(t_y+a@ORHF;W)Hh~*Zdr&O#ii1#@!c#~anS~kgS z6;=nWMUzwB4cU=U)Zo}5t+go|qFho6je7Ef3v}@o?e3d|wgZAmYb=VAv?pp{n-s86 z?BA)7@lzlQ;15ci(1j{Wv{D#qbCm`=1$i>H)VFl%Z{e>363kBC6ni#h$Po*UC{5`B ze_1E;)e`CbG zrp8P7f{^4EMwIy?^PQ?01YWzoFg zACKOfS(^OjD)l;?rpO2}d;ij?=v{lYeuWy0Uzi&M$BP(xvU4W$PSc|dQ!#@@E)Qv; z#p?`tDlXI5TF%QZ-QDn-3UGm`2}y-V@{Ak4d~4=GC7_8Teyjpbd+D35S!c13T}#p}&Oj%$XD!vT zru`y!>A7@lbO^j#Xq9ctJijIfw05Jbq^6M478V+6$;0e$r1G#sy>qRLhDaxRliK)p zd`&fz?_94vwi5Q-=@`f7KkH+Ky-)qWYiH^Zb2eUU8A&H~b0e3^s3j8P#HMXS^jz{jFol0D9a}A%~{kqk? z{)^2&i$8#Hx{JFwfLiQAnp_U3{GUjQ5Ky>G+y;<0h~SKdWBLT+*4b(Nzfwn2>9!0-oUP5;{^%IspdbZKeiH6!`9OPY-27NT@sCK za2rAhgFOgJ1>TS7=b#07<%|`#%uVC*bZ1gfq*I2mUm;xDpI=@@92%w;BYOx-d-I!l z3#)$yW;F+i^lx#An|P9%E)IyN))=sjD8Fke&PknsfhSl69EnVbU)raY$>|LkZ*B$~ zky-CKs9S}EIR3v}c{CYFv7_!MISh{Qst!2+>JfAu+&~#{BO5fH{P2G<`i>=&@?Mcb zDj4}VK%*q>=ARefH9T_D(*dMo8ffzcWt~OhpBv-CFJi!AV|zC2i1x{3x*v-9kWu?X z?un=)25bEJz_;Etx>a70#3!I1E21acIus$ok|3Dl*WtHWBr(WN-AILqN<4juJ`U7< zk$~a<3S+H9BTzmJcR$GJL}l>)ErtxDFvu(HOFY~IBSM<{aXYDb^WWyTLMBC~+oZjm zNS)U_i-;zQ`iO=M?tcRHWrBOK%8U&&Q78+mZ=_(5?`Br`>b?J3s(10C#!kGZe`s!y zG*;JmKtraA)Q2N^xWE6EaeIzT-?p|(e~KZYZ2CIewton5LAHvgRni(Q%V~IGysb}a zV&JUP&Vuv;X11dB=Y%AEayClD2Ij9`k#UeEaU-QS_KDOv0hh*4)ukW%0u*XVN5+qa z+B6o@qEkhQB`XTKkeMs1l_h#C0NLZ-o{@ ztAyVEMYe0W5(cX(;1&a!X{ew^GLXbFh$a{1ui^5bh8@-qFzfEJ_ZPwscILt|m=hc9Ql*eV&?~;8AYvk}*7jpLAR4 zpD^~>N=iF-iX>{yCPI-4j>kc>gJOcg5M_3Rlu_)5!B2UH{dE>&v5+Z<8|Raar);e6 zRhITby_vWjGN)?2B#YxS=k-Wa?I3TcVfK5-I3@htx69<<;m@vo^wfLVeeIDBkCJnr z)#@U7*Mz#cL(T~lIY=9wx_s(5j*_~1Si?;>`nMdf# z6jIuy!)W<{;FKf@3x3}D9s|Ns8ZA=6d|VM@?V=?Y$eD7Vyb9!Fg``s$vTG45+j-U+ zZxBR{3Oy+nC}|(Bifos*PEzjhq%ewB=g+=ngNylN(DbT9VgRRQpg~q!CCz#!g}b0_ zg+A%~j+(BHZGr)#D81cw+sznc?MT_729G)0uHm3ICh#vY2TSc?)xI@rP}r-`D~!Lo96s>LA;!ejnEK>1gt7g_8cAJdpjP?$Z(;Ntw7vXS> zJXRiAbX4?O^+#0ihYiK(;Oh0#?WDy{aOt&{LHzpbbiecuowCw2s5|IAvsRyo)1i83 zNBITNmp;-EPQ+;e@@ah|#g4x-z0L~*g@Q7lNAiS+IT%614H;wKY2CQUFH8+1$dF5S z+276#6-F(kL#QJUHVtU54auOabKYc04H}*zMhv{IXY|bbihE8Zstn}&xe~Jytm+%| z!!_{Yvd7|730y1<4c!!A=oqMC6dNfC;Gge2VwVSWQxWUv4qFejK8e71WhQ<;$ zafG2hzEX3%nM zNuiK};K!$<>E4+W`WZ<8#uFxUug{(AVR3IoO;a8Q*`X7Q!DdhtA&R0?PPoOSx)cb_ z@sTn+c8Av-7UL8xu>!=%KuoMN3`G?Pb~|!$)`41m>{p*@Ld$$gS9x8`t6=$&=bWZ( z3|+>A4ee}jkIp*I7LWi<6m7dp#z53ZmS$niQGe8|{ayDm#vW#U)5zubX=b8qSYv6* zk~~-%DUp|T!LNS6|2-dW*2F)~hQ3Y0?Vn10_5*l%s#R+HudpFtbnWH!Kv<3zfXmJx zH~+jbEh=V6?y1WZGWh}B{8HQKM(8Rz{j|MQ|AlXlQH;G;RIS~q@#8a{yo4`j z*XPUA?LE5I$er|Oc^9P0>cZ?7-f$MaN7kkKD=IZ;R|IVZ#W2 zS0~v?_X>yh2fm)Jb2=GbvHgkuZ#m2SXt{njGPYkDj_zc2H2p=KGrTYgy=~vWGtp}F z${2~tM`cWk5t463gNncS-&LHDPAK(VDf4K8;BOg4;Xs~O`5cp%4Bj+dy=2sTY%3v zll-kp_O9+Oj}KZ6ocAfQyvQEztViA--+GyS?<&!-$C<{;x2+{@`&rITg&o;0Z zCM%WJ`)+|(iiMIv=PoC|y*o54n1M{pq+t4EzCW8X(9~Pd!43w`r z`SamtCwIHwI(BN9pwMoA?JhCxsjZ*+Ixo08=-#;W6^HaqC=V+N(ViBCKtxc<`b#iAY6`@_bsml}Qlw%^HekdG`Jlt(E$?5;w7(kbX@eFq7 zwUnA<)~Z|qmQTpbWh={41zQOu2A@myN{xD*)hRhm%?88%q*Mh?2Howp-8Or^bO-%z z9|ZiJD^FvwqaYD*tDX#R*R%OT91Re9nZiUe&C#(p&(rB_?jW7Q7N*-prMg1-7<@t1 zdPC#roJKYFjr~rS_w?u|K?i7{4<;HS27j=KZeaidEyjT-&dle!42Ev_nyRR;8nvfD z(3G}iQJO8DwnoJ?p5v{D3_);fDl5M4^LUFvsOx!|uKwR&GUh=7=NV=QOnHWz3dag`t@+ezg_~myhUIdT;HMT)S*Uv| zXHh=fE>v|{frlezHafV4jg{|VMzy&x3jS`s0zfn?CaTWIPKRz7ttiOYef%i*pI*)t zqpbPQb9pP)o0SdIM34ew33NS%%LISO7KRLB^3Qr1lzCOfHErwISY=GjXP5PkiP`yf zqbD7d}Ddu}=OVG0PWlq!^RwfHGXGxl&M&S_Wnut2(yMQiIoui;Mj>Y?^;kOceR@*6fuRl)K%qU7m z72{Dsu1@5*T4I}?BFj!y>BHCCv7Oz0 zB5`x;a+%?IseWRd6;M%;3L&U=xXlEYfz5Md`gI z91O)QarULg^ZgY#owTcW{@PuHZ)R=S|DX~=zz7K!T3osBq${Mas?%B}PY3KODTB2x zuwQ92O8*nN^d)K`1xUdRp++(5Sg9IL6#Q;Mn!J^vugQCJ|)tX_*1C^6OaJy z@hCP=K^4*4wHcex?sj_S;?*^rJNsK(PyJ{Qx?eW$IwE7`_{L6s+A=QLwy;N<%+tG1 zCC^32zlvNn+`HB!FI-7UCG#c^o4`3KIiH_EAM4@gP$31X*)68FT2p3=H;B$u!he=)^u*~sG|S`ss3z{q>)I0m^w-8@~1X=?fh z`mA??nvJ(G@4V>XWrSesrFP<|3LUz80g0_;Qr};}hEwJEmg*?&#Vk!z<5j0iLSvNJ zU|Aus{j56lL($=hmX&?=`q9!1*x&c{`TlhEXTHDx-#-8}^?slyn9+U^EM>)h zFe0&ZfjScHyCPK??6E8*I2ef%6~4sz!EX|q_W}vhs0~z#f8Sx+q5ldh4#@rm2CGG~ zuWjf@l8h$7(Sq+j#D@!l$Yp{!us;edxw|mOg|I;1oz8!daK+Y=lGK_>d<-%P@xt(- zhY>#(J23wj)fYyR>eA>NTI|Ld{0@wUEE+oX=2>D?jFtvCTg=v-yij}RrMZ!t4uJ5! zRpm(-AA098Co@{tr*$-1_c5^%qv)UDN(YhWrf%Ur7nPN}0YuS;L3pr!@rHiv2!PlS zJ%ipI7?Pb6I5KMaPnKQ;;rk#2K{LChF%bBI@GxTpkb3f%ap3`mYNen_ntpj(c$};z z@L`5{b;l6`i;s|Xp5TN39-08y0FEe*nwz)?NyPv@A@Qb*?>&S;qx#KuQ#oz-RX+Q}ASxyg)=M2)@fM)@9KTr(SnqaQ;5+Y=; z>#8(4@^5_0vMm!N_WM2r8gTm7^A3Uw@E^1wq_Ph_a3-#27TWRtFZ!sQpx?=K04n5z zVGG*V!*;V=Ba{qZy*Iv3L#M}wP_ZH>T2?aJZ&KIc`9>(6=mtm(&392kKoDv5#k?nU z$mYK#%05!O`@k1o?af~Ukkr5+KUFe($hY=ZEWm=>8y(3!=Q&e+?<|y3M=ylK$?k1q(p2342^@m`LW@312U zhHb{rI1D&Yh+HaNOZ=-Anqy)J>ZJh|880z<2YzeG$w`8BXAm_rVia#tiy@w&7FrM$ z0uMi9ICQZw8cX%?{MkzkPvkm2Fy{dMNg5h)>>(k-ZVeoG-bKQ$g5so?_=9}12I}51 zQAApdU{4xO4bwC^=X!NoBT!1N?Z7`t7>WrzP1a&_+Qiazk51PrN^hYltqM_xG4g(j z-eU;6AJU?>=bT%AY%rrSFgzz^lMb${z*f%d1eNU|O7${2tL@Qj?pd;*8?iBEzzX9# z=TjWF;#<5-5ocQM{H3|OWH$^l4d(eEfm7NLCHPZARpnDL0u&WN8EcVX1E*b3l~J;M z?j9m~R~mC*Z7IxsELk$p9ON@{k-8vM-bMxhl9n7&Ged%}0*^%KbpuZj&3Muq zZw;f9FJ{enm$NnzEJEZ+e|T9VSffukr0M27k~F9y(4+Y2YzK}ftqC=O8O4eSV+;mE zH~rH$u88G66^+;FETNqTKNrJa5?>JN8ANg|cHO-PC~=F;pv$`2;7t*yLkYArU^FU0 z%XdnsY=6mx!|mYjrJM(k4GFusd=bBbOG5(y;FvvLGzKt5>+uh(^~SMbTreYIQ8e`? z|EXTX5fNw}_{GEilLH-Kg~7rCCYtbb1Yp~ppp`el$yx@uVYI8w`AV#jEUHHdk@XP1 z-Nk!&6U-3myL(N*Jvjc*Eb5`1wUp=Iw20jmyFe2!Kn1M_lpF>TZZ_5ChUgnYEFlFY zjtHDm2RLAMA)MExd)Hu>%@qvtdr}!p2@Gx8Mfhq%ZJLsMqA4AA7K#M*gI(4YezWoSdr7;r!*`|&u48k+zariW%` zE(`%UV`c>fV5!jSB{2>>bBwwd?o?&bD%u3=IzEy7x5F^*ng6}U=BJ*5V35&C0Py;q zB^G0*tGx^(FUdmTW4-dgxLk%*To^*3JBVo^67Rdy7)T)!iC9dA-6>7Q&YxrDO_)hq zwL|w>=Vcrax4KjZzWdX!V&`D7K3LeUnEEJCi8#3nqk!AJaJB}?sR9D<==73BkZTJI zL<5>tw2I<<{qR*m1cy1_Co+WDrsqmT^kuhf#6dfikHK3FcaY@hdN8(<%U*0~qxwez zug?;QhZzLL5_o+fgFr}P$>y*ZTf9BNjp|zwj>_JoP-thA;k`HK4+FSVj@SG*o9D4Y zfC&=(a$JmR--fus{fE!#5GM4tytHc3I4FN9A1FH9rscs_u!!UY)FLM6f3px0R0?TC z_t1oNr5du`4+3YfmJPM@rI<0de{4ElRQ-L z1aJjs51dObD?;(&4%@r+`Fj(X$C-GfXNi_eaxg~?Ugwo-6Ps~$j}slz!sXT8SbKdY zJLfJw>hM_>ZM)`MeopKVT+<6id`@$%;S95z+_Z73B5Ft8%h!9VzM{sKI!J3U&9p61?y~Nj8c3z;k!cX^f6~yJj zsOeR4E$Gptp7Jz!*Mnbmy4$6FW{+bgp zl(R85uw28xwB5$|4SA{^_;24oJIrpLidJY;b|1t0(Jf%ToH`AH|MQePE8TRNLm7Mx zR$vV-P|9K?J<#aP)ubG#1Rg{t3P-lUE?yphwXEBi9<%@m`a%n^RyXN_2~*Ra0OCC z7|Wc)7mo)Eqyw*TLJmtw%Y->mgJ^$Zvu!|!3*s_iRw!qb`}(XqjjVZs3Shh%0gj{l z?hAqb&*47IuoQnuobZcakWeA*mT7o4!hla<{M2qj5g|(HzJH$da^9TAECPo$t;XIW zoFja$xWe#@P@>C7MH;~H*n={fJbcCc@HP+wU?VbAJk{BG=Z>s-Ekg6(Jd4^xL&^~! z!=29(EGo-Uw#=gyAIXzQz*&cVa=e)LT0;sF5N1iD{v)L=P>u3x@%>73_=)Sx;puvO z7B%JM%%ByE$Q_+RjY9K8ZqfkIzGuWU2V-gGdRbSL2#XuRr4bJCtvy!PD{}}`3J&1% z*MmXC{v99w7W|)9^vgb@kO~BulK8!5Oh>4jp_U>1Xu@AoTD1}o`>b2|Vk-2fTWG0d zeBDCSqL+W;d7=ug$;-FaqM|E@$~kJ4K$f1+HfCRpOY`Ryj9`KLn9>+iGV#DNuox#c zKEhv+D$-Rt(kz`-4glDePDo^xK@M><8&GMZbOHtW{p94B#nc6Rm%XBdv$KN7UyG9W z^8bKMQ;movD?-^{0E9b%x2dE3U1yFTPiZSaboxjkgau27`5xl-tUC9+^>F2r0SfTM z3U$V-NN0d6N5V9KBL#Y_j0MHPS>LZhn_xScX#F%WyC{N~>@gn?4W#Wzu z`#}EJl=aiZmspHLyFseC5^*y%069}UT0FV(Tyu6KQx+r*ELjg-4RmId&I++6dvpSa z0YKDDTC}xmzK~}JLglt&ROZ$3 zaFi_P*x!2;VdXk&$B2YvC$0M3N900rJ8y5gzip(3zZnxp#%8Od{8#`qIu(#%(|EKL z_+o18LKdb}Qm1*xKvPKGzx$$>C$|mcmRkB4X~tdx!kVqJP%4nKB8tAue`38`&^>!F z;pelP@LX#ZYvRT8M8V6RN5#TsfZ&P_KTwdl!|s@WN@sZ>do#;#7078KGH1o1Z!rfy z2V`Mn=i&Hg8o3s=7t3!HCcGC4^W>G&*qhsweUnF#4LII%<%qv7%6hHrf4FkqYK1~h zg!f%Ujd8^aU6v|yhmrsFx6-b#;`Ot^*COB!CooEgEq21TN&ab7j^7obLmQiMS&q07 zNKOkE&Rmg8UR?Osy4c5dNt>cgm2(v?@GbJYxmZrHmCN@Q5rkKAcoAr%gYsEg{hv!2 zuNb$2jcut-HXu;YIV&cGLmxH0=D8z+XFb>AwB`f0b`iQFh}TV8n}Y6=gMn6KSLY{F z2v;$L@f5cmEJZv|r@LH7xn2}7i#M9G$ZIW&m$pkebU6(^GGNdq8y~BD*4?2mJ-0rq zPGGXGg*FdC7o}pMA?GAP7Q#AzO%x;!YDCLQvk8sS_Q=J$sLJC37IY>CZXzXe=ag!D z81aUk88vxBRc&tNI8+9G;Fn_XCIj4Zwa#Mj+mbs63TFpO6R9JoaH!g(TYA{Se|ogo zS5kHg`zq;>3H(g+x(7!ZB{oXidurw zX&GM45xE``PdPbqCh}x;YmqR}l_oZxk7~4yetMwa-_X`9uMO@D#?DB|rW~t~mapfY zVJw=*5Op;7^z6*y`HYSw+@7-RhDtuvVc58^OKr5Po*IH;Sz3{f$xcoicWykNY5dqL z_(->_M9=9;Rqo8m=(eA0>eAJeo)VU(UI5js##xy-6Zas5ar6^e zF=}<63#ZnVHA@+{HhXt?u6BQ=WEU!b4`PQiCSuu%Z_?Q(3#}aGBc=*5eGN1l$9jq2 zS$KNCXNRC&10P)ugI?CrXW+tg1}m_|WI?pdY*?vM@|Ro(xK`abr?W$btB{`0AGpD} zwb78s_KED@tTQ?Gl_sEKtX^FH(gt$fC5c2r-#Y)zh_~&0(r#ywk0Gg=uqi%{i_h^o zKutz==Vw%=;-}@tN2kEf0Gw8rVWdw2e}>3*zw~vNYKWU5wiKgWH?wW`^k|#!aT{J$ zK5_QY*8Ow~yuHCpE|ITMYwm*|o zYJtOwmNAQxF02(k-5TD-6u^c1vN}VUnX`RY;oXG|t9e-26%2&{X$CFX+WI?$VD`?K zjnBoNneP>>c`>;jBnH%%hVfeMpFHmtE9$b#C2<365iVPf+(gbF+WiHcr2@4JjzoQi zb|Z8|WLT~WrEJP6BvQ{mY3E}y!-0kTX%Rp?FZJrgJ>4ZSzMQI={#Yb3yckZ+ea+fh8;o^Try&B7K`!TFT`D+UE@v48`VxR6R@CPyM|jwkOXsvLlc zFS>hkyRHD_^+aG#tsc3AKE%e8dZM* z-yKoV3TI(s6M=95qR30Ize8?*O`LPTxk1ry576Z)4*OE9^GZtGjC3Sf_|5n(^t?-7ZS)$j%ThP@F&I|ivmRbPJ zH%!WaGr%{+h~gni>O-U&-ku$1}l z*^EL5{!vX~4j|&!B7$l_K_KUR{7cOT>5nQpNLW7NXLk()=H{2lZ~CAs+JqjetG+8g z^zG9x%Ew1=A7!Ge*Nb`hNW2^N&AlxSB`#(S&owVQzqkKwWQS| zKT%P|++Uw7ivfQDLGbD|TAf~CbkGHO?Dks&L44)6x%}RL=|RiCmG^|=h`@mVPko7p zA&GE0FonYtNa5`Z#Zx_%&*HG(8_85X`;{l)2ZF>`tB^{Phz;j=G7xeBJ8;$)Tm8a)@u-A(MurfLrjx6UcQE83hVAm8Z zCCFv;ZQBnf$Nast2X5vZ2P4t321GVnefWdEzW(9J_Kl|n2R4=4OvFS` z)I7@5E%wkV)X4L)$Zdu|3>w+<>G2o;5K~1=byFL{%U4m|o?P!DS7m&EXcQPd5 z9&vVM2}8(c>QZ@|DS1fcnyTh+=;TQ1?pHN)tsX$QqPG1pr<$yr-sen_`2vGIf<9cA zT|?WFp>DAVKM)Xn!Pv|=dp$AW#$G>=WHxla)C8qw&TI$E*lE)|Tlc1x+Z6OSc(@)- zr~RaDJI{{d0-#48CQD0J)kRxf%Z`;)^GitLd`_7!C!$XEpjllL$I0j^?pF^Kw^2|f z)LStWrR@~!j8aKeGqJ=ngtSR)E%RpUOtr=2Gyi`JZM*nkm~8X!xE*H6mJHk_rRQ|b zJC)dGPi$Pju~dFF!-DUdC}#rCh_-n_{^J_S;<)SxVv3Ty){Lsq7d(1gPM0%~#tsd! z@-l3(CM_F4QLN8aV6UojFDJMPEEm>sSP&R za}>y|9Fxa2Y&9zio4K{@x}C>u{RjZ1@OF5EB$#GOyVYQks6EX9t=T)oGd~Z-w<(8} zEx$rkvG{Sr-vW&j$-Kf{Qzd;lHr;=6a5+@nkA zH#nOaP%)7;iUh&2nFSO1GHyUhCOlB9NLra)5VMCV__tuD+QlJq4?~*FWhIdC_RL1G zt1tpABC)&(PwHKDID<>{Cc)bT@#k5iLRLYxRsoa2SK=LFr-`V56jyk)!*rg%-Y0bE zWayy379tH$bp|hhbw@~QXkrZ(k81+W73?+P-vajzy>tkm7_;)9qm*{Rbc7x9#}Jey zgHeudgm5^3AS8Jd1v#yutgX=Ao?cXd)kSu0mKHC;UkX^w2na%~PR?_N0zY^DAoC(o znmIKjlbq&!8c+syu5p4IMk6V}wjq>>eknyJd)Ji{!11o!6umvDqZI=c7=%k>AUj3U zyQ1}jkU0pk9MWRa0t7?EyXO$SA)|@L!Xu&s-jvJ+`CAX(109DDJ#=uStbL;$Q`+GB z^E0&r|FJ;y$;-$#byIPT5vRvM~x$*C`Ojh7DG~e9M&|dG@Sp zDD?($R#87Dr3j2xDO&z!Mzb48Oqc|3q&`sNbyqOh-QS0KT^*}wOI5Cf#85p%Rn4q| z&V?|C)d`aX&5BVj)dsi|e8Ovlm`Y%oIw%)Yrzb!SaJC5rn{UaZJ+>k$B&YYe*;%AQ zY__|LGQcF#vcpFvl^Cctm)j#+lH*9nsH-c98`0aCPvfrk&4OFlL&Ma!LekEywMSIi zAM4$>idJ!ykbl{`KzyHc6ug|7WC$yTZ})_Cy(_*E+o?`g|JMJi>km?L-SzO=Z5kKV zzQmohO}gqu)_O0Oj3cm+Ty~>xy^lSS$!GK>=mGTD_fNpczn*U#1rZ(|Ajzx@IkR&u z%GCx3eIHyaC-r%cJp`NP75tYd{<_}r@L^^{L=VTJ^|vSD++g2YT`yp31j6;* zzI(+NMP0Ig4kFn3gFUS^>4$01tc-Z+|J)V*b-RXHyq}y~vyOSo;nQD-hSKiY;iv!? zb@xfVmekf|tZ=<%=DQ7@{+)*-=)#w(hc{}~u|)jKCW4D^2lIF5=y$9xrs3e$u*2H` z9PwjtGVqwn5F0POG$th-@sQs0W5hTwvT;D|lq?ciR)yi(_U^w8=(o5n*1GG;k&;JFAl_&oWpniIG;u0OX0dl zx;wUxyvuBhD{XSWGzWOhR^j4aLX&rH)Bk*~|LgOfV=^O!MEY+j4jnS`3`B5>X$ZW; z`|~Qc4b!&y`aXWqcR&5d@6G_m_YAJfckfN`nb8Ir@|O^BwYu+SkvN6PwBCNoQva&m zaP9Q7?_=++4gPV9ojed#P@f(53WWCVMG|wEqbG3aARTMvWfu} zQ6!p8sDm5=1CeK|S>Vldu-{b#K2!u@1{XhPaKkEsI%Oz)VlX!X#6*TF?2?@4$Mdsa zHGGO(^etL&x48dmT9DFih)RB#CT7UN3j28@p}k`0#k%KNR1l0+xZ70doM8Cfb&#z@ z_{%neGE|h)ZCFfv1W9=W3G6pg<}jdo_&KYXU9OJ_mRHwHaC=w)n|>HKMTCG}Tnvct z95^gtHu5_#S`8RM8zT-D%{wAP%ZkfuFu+F+6oz{qQOptHB$SY#mr$K4Pz6R9{~R+$ z?A6d~2novai>xc8gKoa$oz3opJ?<429~Rk>Fu0a5nkg_L9CN*wbeh31Rl^5J|1e8w zkio=EodEA?E_izy{*^NT?9`{0j7Si1Kkg5Llqz8vCl-~0!@$Xst70N4 zwhSHj+7bB7mZChC;QDV=?OM|BzcGF=Dd(J6XT>Qy@sLyXkh2SzGmAfGm(*aiS!O^%BJyN%@DD>V=jb6DM^L(fa(XGU z_o%tqADABJRmr>Qu=XbMyFb44DH0# zM<(_VW#%Dg21w@?fiQ#Dc#+JCgDmq8YV*(RG(j?RY}w-Fu*j4X;8Fyf7r+Ue6$$0z zvFsV?xD~nmXPHIz*+(Guqxk%b{QNWhe4h6F<8{)J`0U$GQ^*b$Avcaua;siLSVf6E zM~&=t&vec4^giOu0ioicwGx*50=D}iPS~PaBpMtC4Sa9&$#K1iHu&m(i&XMlM#&8R z%GfTV6yQW2gJNMxZ63>f5jR%ZJ0pVH9i##ihK3)jL;%$Y9SDYt8_PKX9&?3nxSycXfXRaLCtex%+qm{U!lJVUfo#jb?V)rxGWML|uSrRh2_s zC3Pv!nc#>}4K5^5cB{DiqMY=B2>+;=CmKq2qTkz1^t-NOGBfRusdWEy7E?e8#(s%! zciDnr)iP|&Ds|23Yxx*fP1sKzjt^{Rk$hH0P2L7)1vZ{8noZXUO7(pq(+->LYfS!R zst!}Zj6%^oOqpMO)tX}U%`5%|w2`c`s@b)#2@G30=~%m+UW4Kb6w$8K)Jcz+BrYq; z_=U%&2?%2k8xo_J__s2HF7YknEcm!*ey!(&X(cgi`g3e1r)uC;E``iCxD;Y? zMB*{|B!7oqG{K_iK{dPf4){Q>7y^no`fGWKYkuZi zRjE1^tr5KfxyzfnNhj-xu}Qat zgf+ zAA=mVP92xfY3F)@(0K4?V-ki^h2Atr*sl(eDsk~xthIRhH7~6BuGZ~t3GKDmJuBs1 zur`CHP6G(&oojRbG>TO~&E3#|Xe8Qvx%zqLKlLRFb7i4klit<6ZCnDTzCC7*V~I3l zTuw7F=$%T6om>5UB()=Wb$CDoH70NRcy9j9dtyBa_D>r_p2E<~8ktXfO>{|vPN-vw zTGfD26VYEr(rw1#2FH49C%EAU(s9S-H=0&o=)=9o1p4p_j2M-j$PKG;wO*(s7gQ=~ z$?^%1?qS2%BUAtWo1w{Dr(IhvH8*DUH`CiYiOD#4g*A1PJkvkLV5r-x=-rZC8#Ljx zP}n;*038%%VNtq&x!OJd09`jNqE=-Oe9}+hvgc77)wQpzp*D zI#Kli7B$i;w+|t2@4}_+#L!^V>Lix!_T0}mPVXbI?4Huq7(D9)EgBzbY2bUQheNDj zGUj9!ol%DtTfe*1IsoZR)Z$!x?ING^tS#<5-@-Q1;wsnwoEdvxbj-@;C4$|+CfXOd zM4pZ7*6jDg8tkEE14(x+vXw-Op(oT;zxYB(p0#$_NwFX4fo4xuc~y<5LMlgzO@( zQQw+2${X;QuW6@kK@dAr$3LFO!9}D+emOV@|Gov}JA9)v`CySH&`-^F3M4AH)83h~ z@Y}@wHUb0B+$15Sme;SGE(^J;p>@{h6sG!V4qdlTm{Je4^>FPPw_-x60!$8Z(j`so z5QjJOpA>6vk{ViRccG`-YqQpBcXk>~&w%ns z5l!cF59iY|2bQ{Md6JlbdpgN+_u>&OeYgwOe=l=Z3EYxu)iNOpAVXEbwMmncQz$Hb zEbJZr^aH8@^e>H`sVG+zD4EX_iRE>4DsO^HW7=qy-WP7NZL@ zsSOd#&i+hCB7!-3PG#3VGR>h72 z4m-*#6RdJQ$m`F4a|2+z#aQl6X|I5>6V-HETc#LH95?&?bG$wzm8%Fz;js7t)@w@K z9HG7tWlsi^f?ubIo@Z#plfk4MfA{i`-Yb6BLn{6v`Vh=@ELYfV;dIxWceJB=S5JSO znKO@-1k#YoHq{TWL0%Z(6it7)6KN;Ubp&_Tc)s=-Cbam1N)3G=pf(TUzI9Otk$>@L zb9Z8jz~BF1<1Bz^Zu!3GCQlqVu=$r&THB}=cWAfLLdY{P`fJ@1yG(-r1xh)mI(p|z z`=>cRxLNpR!1^jVajpgU^?UrdkhZAE_&G}g4mC)QItP>=H9Rgoq+R<=rf@LmyLR|- zS^4j&is`>o%ZHP6^vv&sC!5gka&Z|YDsL9@1Pm-LhtvE0H^8$|Y`wA#jziOOFos5PCg7DA{q#F+<;&6F_pSd3&WRhrqcUnC;3|R%wbZI_$^RsSa; zz6=Y68Exgz1;<4a1cmQapfe%@2!i^-ex&C*FF>{Hwn8L{#=WbTo+yRO0i7vBsZf?A zo6-7H)%$ER6eIdlW^`FiX%IRac(-V^q6n%VO;-!73?f@0ie11aP>03${+T4x#-YhJ z!-H4ox}YwdD~@9@o?#LjhNmAd4x=<41Ny8ttle*}-qxDgp2f+Roa)sL>1;TS?PhogLW-Ob=6Z zOw2D^2=XlNhu;#+=sR6?P3s}=Ij9cxkyp!2RmbWie9Wcdq$$4e=S4G9q_u(1d2NS5 zd2aZnj?!Ko9F<@3VkT|J{?w~_C&LdJN&535+v%CBHUy@gr$?2$xwq5Lr~WTm2-#{r2m=dY2r*X!(oa4Og~$|9#w(g0$E2$yD*Tme zN12*snFfwF4DE`mT*aG(QLi^!mF=+vJGK3*k#vnC#B8&y)wZ5pURT$i^FlDh&;8*D zz?NiaoT>#kcwL#A<4qAlkh7U4pDs=2qQ|UBtFrT7vWc5r%dQx{#|+*Nhx%LIzm_Zq z@SoSyUxK?F+p+E4-A{gZe?Fo0xfk2e`)xzhpmWRA$9W?+CmPYUTC3b?Y!3>FqZrMT z*^HTd3m>$6+oV{V8*6~X;&|~(>3tXlMSeHQyPu~T?c2LG{tr{ye{O#Z4S>}L3{PVL zptyy@uoifuW%rL;*pniR6~vc|S4KIhM6e{F?3h)O-^6iv6TPc`{dnHL)S?n~5ef9G zpeDstz~y@@ZSd&m&c1b{9R3f?-s!*Q!hg_V>>(m>-jmHrU~F!su_U%OJ79?rbYf(e z>hX~)Qz=mOtu&khphYtl(Os-YuReylD0IXBOg?3)$5_f1Jm_-__l%^O{=2X>d?(B4 zx4>f79aWASm5V^m9Ap1YP3w{}j6On^if$1G6Uh`=fbK3TLdCe`wiR8}E{Z#89;_ss zBEE#qNTZ;sw-lY__5*L4yE${WHjKH)5Yewp$p4EjwVHlwprEiim<8EbO$YbsnYuGx z+FovfuBcfni3fw`!6&cUh%BFaK_V_bb4D$jEuW_VBLf_>%&UT?ghtuM!Np@sKK(yC zY*~)*WrM!z0|FGw7U9ZdrAERTKHrGK)Bj=b9J}japmn=r+idKlvD4VLZQD+EjK;QY z+qP}nXp{Y(-hXk=80R;vFAL9_(>lT>@Mg}8iA%}~%r(TIoAxa-Q|fmvLku+IJ_XjF z>~LO8P!Wo_xEPD~tgVAecFVLC!t2?Pcj8j|;AnkwZu-q|RX6(G$`37+HnY<6!# z)uG+-d_&e$VeYb3lPHSB=N7mQoAx-{~PQrK5jnh2>_|Gd!T=6QAgG zpDUy5tw@Iq|C3oM0@r8=Ev=x+^wG++e@@=Rr0o6T6b+P4YpUuYm1eW$CsLs){nc`+ zEuf}&k;s|QOHuNg=2e!B*erhiX}M)ly=O5t)@DA*=;yty6#3>+w8~I}%V*73QG{j* zB2;$(y-;sXLf15<-(DU0wX%;Z!;v9}@1Ssr%_I9yxR8RQj50tNU~0?gM1GxnRj+2g z)M@tmy4Mr)f<-GdaQ8jtIvV|!(JIwgDRO--F}X}{KdECG7{GOUpyN}~YRCjhbzh2-N=dOIK zeZ*zd;Zt0j(eKQ*>G@bwc1~)}5#yt#mySLd#k-0}(zqZIH-_jRN+hqfNai$^v}5nh8doiGPqvhC<+i2ilVYs3c~A=xT`3py%-V%J z`v=BM;*Zaa)oR@2F5_OGu2mZzBkmzN=ZeB{DDbZNI~56LQwnM*5WUCdc_v0~Q7GuN z4&`{XV8m0Wy~2T~biB1Te_z^VcW#$}JYo~+l<|+b=4#4z;eTK6Ye9UZ1aOwSBF(6e zzD`Zty}Z^JOBkCmW^E{fkV`laYYpd0hBaBG8!3eTcK>}3f%5o`(|3sz_$XSc{Y1O^q^-{>pk6)sJN|O^ z?q*Z{du|d=v35s>_f%()UEw0mppRpwXf?T?F<}IdgVsu%0lL8ODox`bF#MV{+;0^} z9SNpj5BZ)Y0e$Bkpc(v!m0!`jkwv<%Oq*XNkcU&7bz+REX1yYzPfXjnEh$byI7wiU znKDfpeX&b0JVE#aZrkH?8%75=MF77<8UAnQ$W3hl9+MtyX&9_u@b}~a;u6XI=ib4k z;RD?Mi2Zi7cFxgdT4k=zsp%!(q*0h7Vzb|4y;c~xT5okAbE~X+lq8;K z2r;X#ezweQQ;HXUBIst$mvutFx6gO>LpF`F!djPj$gGmlV9Z_*m*WSXw1KlRW|#IJ z7bSW3;b}gS`H@&Ld!Cx{gOa7N!sYDpKa@HQXpy>(SiMNh<|D;4%LLo|lsBZ*Jlgyr zj*@7OT;PmM2iypSmAEQ%p@U~%)xNyC5Uqye;2X2#Ue9n!`hd-cn4)b5S(!96F4ZzF zpUPrg^-M)V=9J0X%=zrxne1FMoDwu!c#wEXbado)N9JF9`Xi~#SRmh7d|z$Ryjy*h z_0OiO^{RK4exfo?lJ+4Mt9ir-buq~K&=H%UncX5FcwX zzL`Jrd6a{-Fs-=D8#Tia`Qhj>Q644A)ViFCzI>Ot@npO;6^YGO>_l+6zRs#XbZISE z)|>s0LduMAjrHEfTw-~%2A+9F4m=4aga8-X^gHT|aBG>W68<@wU>Rx@Xp*N((<*I@ zP9&U(!wtMkVmfDtjlC5lMZl9{k3CbqdKZ)M@3ve*~~C-h?BTzQjNY!qvismPfi z4K-mjB@q)*<`LOZuJuoF4N!-DmApx^HL{7`TGRmLC6^@Mi zR!O>OE|#8EXFVxZmH4!5Dpn#6t0HBV$!pu9B4&(-XX0)U;=HBCcW06}SEG3q^-ULi z;D$?ar9qs#hq-uux7a`CO3FZ_<$ERB25KfyX5UWwtsxlQq}F*F7m#&3Lmt-_9AZYq zTfQolm*$hjL7`pPAjVw>N|hk<7}oO!cq_M~!^XQqOR|c|aOHBcwY9P`76q(94Xpf8 zJMuY(BZd-9Y4VG2_!M|qZRwJ2i$iU1;OLtDqD4($Rf%iTDL?XstPj;sGDH9GBUJRe z)m~a>j*8P(yMILnM%xelD&o~HsQbPwTshkkwrdna76)IHKxMpOt*U1_51b-x%(@$o z-jXdd{{io?3Wk%za)CBwj=!qd`i2w)hOBgC-DYqx<(-ak;}( z{|ydn!+rlOQvdQ+Te?-;u6&#AtoLkw+YD}Y7fFcN$v`G^k;p_g6rPeNVr)nyKm4oG z9c!WH%z_FOaQ7Wjr|8UJ@pNmEXD+Zs{>!4OFW$m~w_5eo%%%+iB8hI2=i&9V^ib}h z&E(>K&+bCMPDSU)y|Z@5k`FtuAOQr z0}FUfJd0Ca)X(E96v*Nn17!0}_f)sn$|bp?2aUox_~|)p7K6b?s%30-W=oB6c_G(4p=c>M+fxOYOBbmg82pFVmgHG|SsDM~P_2AoeM$=l#N?eqg;988I zs!TO1ho?!YGQG|$IL(TS&Wt_`ZJ&;hcvkL5rjvb{$L>`(#17yv3=F?*6t@~|&q~>v zmdN3^$p4;JlOY-@u!=1u@%6Lu|9EDwC}S7M+S`U3)K&{ASQeM5C%!U=eVg89hv7o&g+mqW+Cc^ z*@_l~#Zwyp-O8<}Pl@Z5A;3?OjvwyYRNPJz9$(lL#bn17$T_v?oiA-Xbx{Z<*=v%YiK<2H47VH+QUP5|<|8F9b6igHPWx*X!!V#d@c z|4qBr;FO+^mu_!j1PBQ=+qsYy%VN9>OERA%5#8Hx7I-<2=vc&~xX-*M=3{CrNYw_qpTGI}~NPN~hXh?>w)g%O;;} zPu$x*LwADs+@lTiTs~(l#~G5Y%tNQSEzsnAMWwnd~fNkiFB+izyOUwho-1pTn(%1Qiau0rUl z@QJ@QtA5{@J~~AzZx7SKw!VMLJ?Cw6?P6Un&IIxHCEWn-Xav!D)_ksVCJ~e9jVj{x zsw0-22+Ud>4MgrK3mDkzOnuwR!x{4&enT|q#Mi)wcIwi#>ScdqzPAa*&;j3Ontzv{ zjdr_cmW#-K_8$AT+49v(p40qyzY@a!##&uyNTdf2;4EIu;p{Hnw&oe-4VC1Xg_arB zo!wU zYsI~*{1bWOe~%9RC=2U|19d*@3btGXtm@>f)8uy35Lw&(0z2i<`GSr6pdSU-%>@Yj z4mf}RZs1O0;BbbjY{Ga7H*?>0b#CCjFZ1-t&6h=N1JXOh9G`vhnJcS!AwK@`8U%Ms)j{tIZOStPNI z`}64)_UY$8P&dR6hs=&L`LP6d2@k@}a;2$xwq$I9Tp#!A^=gQOyO*2IWt;W3yzYb^ zyG?ZK^}0wLa>-pX>2OGfjs~NlaQ(Bu%#AXT^BBLsPJHJ?4g?k34XT{dAQYaPO?PS6 zU#8ks8oS7zjhSL8@>eHYMJ~WE@JX%%6&1MtFTJCB?*HONk(*&YU?^ES3{Qd)=mC#XcqYX-m-+SAL3TVcN3r02*T%cnLdimZk|>v#TWA zmdIcyNR4BzdtUBbezxztfuoh6%Bxg1{JA4knEL*xZW0F&VJMANxP2`xh@!pLECk9C zEYJMVcAf2sxH54a-s0b>E~4~|ndjmQZbs?~D<{?IU)v*nD?@XD#3u$NM)CfHxl6r{<)El)Jw=NXNgc0k z!94oqK;rK@`&kY)oa3n1groOc#w`@OALSn|bWwB9j*%sc z?T2B$VUHpEcYpcWn(Ju(gB_5>9J_Qsnnt@_vvO>u^@uQvKYgWgbE@GZSuNjFw{y(I z_1nRT??q!S1==Q!VVcIvuD7H5n8#nOjd7KgnwWUn z4P#J)pi~A|fSUAQZrtHr>01kK41<2BiN;mwlVHLoq)#(T*8i~NLs{9$E)YL3Z|#5k z@_enmQ&9iPr&ZLWXTRHwjP?>^B#w7eGc^gT8ylz*E{UMYnHZRQM|(Dor(oVWBIuOD zViktxgm*gv=g}DJahs5*S2K+~NfzfuRvzU=IVw{s z6&bKQgr8L>jiY<^#Od1%`*RR)UPNUyhP%4B?&@Etn%LE)Go^&<^fNUG@PGJ{|KIQ> z6#uL6B`af)jCMo#;@|cI(|8yQL2y{pJ!rFFKN|*ym>vpTCHN8So}tKRltvbC zu!x)>kmPzqkQJGc8kQ9mgI*9tos3(vwGeg&ponS)L{|;@Fg=EJOpU;i#JzGkMQy45 zz@TZL0iz%SS9&$B5wCtC&;2(LBQvxV3{n_A5XHu8h{%nE8$0g$FZT&Kje^9fs{xbK z3H?F>BzBTDRHD675y<0eibKgmqs{xdk+0yE^l$bWtF=lXoY z@aDGXPqr^6QF(|5tqNh#-;bCLaH@uWvIa#I!Y)I=`D-)W$-foq2647V+du+O0!*}1Q{MxOws!uA+y^rpFC%EMMGBlgCw=?lV6bh={?Oe7 z0}#f*0BUrD2v3WWf}kD_tosKfLr*X&+?OW$Ce{ctrm>sj$8dTH_o)3QqNC;rI39>3 z%HX3RW?Lr%D|w)gkKIIEO6<4|b}bmKfdx^bhP3ZQsO_N8(b z2FAWhz%iC`?P}esJ9vdJQ7Rwlf_}7v9E6X9`2y_LZZIL2eo>4Gsxn^vs^Hj0&<54~ z+^2B1{6W6wb_twAIs}0KFwX+0a{kuZ;Pa-4;_uGYEq86ob0t3L# zi1V_V@r2pWKoQ#;k_*wwpZlZQ=ydB6skVdfmns5NPD1eS005;E5rThZf!I9*$B$f$ zD_gLTo#z^8)DN;t^3_*8Fz%I>hJc2qbG zbT9({bOn48MiAc&4RA|S;xp+NDwv7MOB0#st9_n zkT_E`7{KY}wYea!WW^!~N(0!Z&Oi3lhm zCx+}?74gNISqu+OOc%4W@_!pF@3Jq5_rJwD^-*W7C~OMUg0v7mjMjM(!P^1|@$n<* z^>ZMYhwM^PK2`3C1~k}vRa?Ii<7#gv1=o2t+>lWu!YLID^-l#!Z~;(Jz(d_h{Zn$j zEMz52!6S99R&-9}5_sY@Qu|ibMfLx4(nR*<#_q;q;g-tz{ZJLBjl_6m_e%0lNXH>$ zZATz4nhw!Hhd#d63-OyvF6zfQ8Sc!21lJN+kRN8}01eX`Xw8LLJCdq(a0Pg2W3LBE zrsLkUR?A3=KX|y~fHSkO0L?RDXF>iWvnPt&0r#!}wt1*i1A$~cHsAO7=9D@)=ux2kFo(p|-JOFPb} zQFubXu8pW5um^+wO5HSb0f!pH#xt2t(wJV8X+~pQ2O3!BySx9Bo_wZ=*eUZBs?DLO zrh=dQ-g)`@#vz~)LM-)i%uJhm zO7lj7H-z|H{swvSZYi4}W$F~3I$#Z|jazUq!8>GXYOa0$%@)G7sz>T~bZ{{;_y zRVU8=?sYUU=EnzF?$?X{d;b%51dS{fbjfiBgnQCmQ^-B3T6?Yec#hgOuGlV!wH(vLVgH<(evSbjHT^*zz8F?02Jh~W4HW+xK4~Y8 z0XGMJDd^a6_!R&!!-S1y+2t>UFi6_T)-mCqp~IZvBIpSx*lEMP>m6nO8ATePf>jtw zixCi5p+;&Ip!_U3?rI8`VarAhw~6JQ+veCpZKv4bs%%B}*Z=@FfCGZu#>46SHbUdZ z5JNU-%|t^k&pkTIEVsj*IW;{8IRo1yk&87yi0h*?Pk!v%p02X8t0TX;jJ$~6&7!w?I`ze>v;Z#{YtHA<1& zMxV>Ua2*Wk0G{>VAKnEgr?){^YBIqlt^KoJgS#!cweI43z|}?c)I-RfR8-3bi!!Z? za0iR}r&u^vZ1j`UgIG9Es%fW3xN#>?o+dEWGtJwQ?30g3(qUg@q26NW79AfN1^))^NU?vYr;axpH}1q|t;@@%XmtL66IUvng)wn5s=1ZT+r z0jopGu2%ySP{(+ZEX8A_9_ zB^6;MbI2wmts>)VBHdTs?E^bAADNxRq4R~YfE1dkzhHCJpV%N1yk^Q}lR1*&%Yhf# z2=499gr3>2)DC4H1y5x8YzJ}YgNUh!I)p zB9RWqeAS-vTJDCeuUQS6DS$uL;B-+eS~hu?#e(QSbMg|owbD23#1ge=XAaFXuW-LK zeGN}{5a`u96E1^SrZlx9stBCjtQ00?z`@{80e_SOmPhnC4V?d2`f_^G%0VGtdWAD@ zQSMQN$%yRaXrk0|@{D9zhewVzP4N^@wj^xw6&%waKRZ>gK&WC5f24V zYv=ZK1^j{q=7La-IzF9JfG(wZ8_sb?H{ecqOApC$V>mNJFtqBhjI0?c1)>^o$4&Br)dP2 zhzc8P(vEDLPJvJmgMFoKW+McOX-91C@9fc4U8 z3zX$FwKB&XujM1~*6>Z?_*3~Grrg-3QMRwjFwnb?V%LJ@Z?fqNpjwjq3nCGMX# zD!zifRd=jz-&fq6sAFY^6WK~_@zRY{;B;G$hPy3nQ~+(4D0Z$mXKz13=RRlz%CkYd z4ndBDqEMW>UizYzc+6Vjv&V$fL}jb>_dlRAt$I-a^u9ii^iW)LvmafQ%U`8kLWEJ~9l-_@;=7wd3NkIkDNvd1^d z#v9g@NoJRWO`UGEKp?d$-oD)OWRpWTVN5iZ4#Gr?v6U;(;rC$A z*%goVV?MzGw9G0lY#CJr)S>y#^q0E z?P|_cQ}pbNmOrGx$RvNV%(aZ>1N0WbR9lK))OqsqYSxYnC^H#xWEU%Dx5qm?w$U7e z|3P>B>F)xf-4tv3PL}p+$CUC&iTP`uqg_VzPF(V3!*U$(G+s#@{)!>eafsZYR{uOG#DG5H@X9H zXpuO0U6hu|>}Am*y3yOc%6A%6uiG|u!hO`e4+h+orAi%$vcS5*2Tets*ET$?ZI7d( z`)}H&rgUfE<4R>$HYPSDXQ2$_*C&DFjeLb^Z{;{3pIKXYfJNn^JHlKv8HaSxd_lVrox+9&9nWS20RqSh6z$+MdZuG$NRU6h19r{EhpEOO$)s-l zYp}FPX5!K76oqad|4ty9cnp?Ww5)ge&eC1UyEQoODEMv-@`BU(Sx`K0 z+sicaXL;n8^%~ufx+=p8XzNTcx)B$^Z$@%XNPH~((g;==fNrmiYImmB>S)fhbB}v* zFHo*yeW5tOl|wA;&ZPdd>bE4ns4=b{hToNy*;n>=wp@hs`uQkI!C+xc>vz~q0%A2{ zh9h7Bcw%s9(KZNKaXBr(9sz0ZnilY;I~KQ5(g|CD*|(P?J@TCgA&v??bjMPter`2A zU3WELG1Jo-d9PG;-I?y&?L&tV3KH&8{go)T)MTLEbn3^9UyTa`P`No*?b}PSLg|U1 zqeOv<%)50L0G8G+EA#CVQ9W17`%`E4k?vm)SKVnI{G$2KZf@2?Fe>Edd=P7WJgTif zOQb(!+ZDyb5aE^G)@$P;6B55X7dad`QxTC z-L1de0YX=)H^3LJjdT<_9H&Grq5aikBT3?p@w&DIKYdiaKQ>cSB>S%RpF*Dd+*pEB zQddPBc|%=-pNH_h9Z{cgpg-5r2_Ihh@7VwCESmo{t1|>VoY+m|TrqCk*#AZW-Y(yN zvO1{p^yXu)!vY$jLlmE(^b08V!PtU0YcL+R^YW?u`jd0NhU|_2!tr|D9sopQu?(fd zA!u?JqfZ>=Be7^KMuU+IB7uPahcA&zqLjDL%TPI$0YGB2cLWs2d$je_D*P(Fr z_>Hr*pDpHc1@b|;gj*!R&=^cJC48Y{ySYj};mXhN@kYe}8h74UIt|8(!4&1yG9*@> zMS6mzpbza&mw(!TWJcmY<}=7{`ZEKS$>oX#j^Wt)X1cVh<-O&+|HfXQDFiAnpf|B0 z$nVeedVid+4>0PQ`|($>$T}f zS~byj_jJVASAc_Ajfn4Hvj3*5?!sFtfRmd5>m%uhWSg05QoE$=w5XzoA#PcYsowyi zBg#Ogi-7C~@oGn0CgAjGj*waYgJfSwo{*yzT=}Eh+}itx3>B3}f5yIj&uWEg8j3dq zj7^y9v_&-*Ry`81WU}Q5wVHy68k3)eh!`YJk-410iaMDU#p+s@ z)gZgVq23*e0VviPiKCUaEv1tMX98Tu8Ptz*P8ee73=EThlBbOT{CQclAu{9CBr1!D zo|f?FW;hMJ8F$`DM60W>nQ~+GSNQ}|1Z1X$(M=O(!d*H{48m|6>h>0;_}DnQH}qa+ z`BEtD=7K42!&s~DWB9N}uT13@NYsLD4(BT0OI(}%4xUs8{(R4yK(Kg)>(ujRcKQPq_`t%5v;WGvzo2cW9IM)oMeYl(~;Kx57VOVeb_Z zeD-lQ$R6e#|FQWD)LZ+i+dY}3tTmCXoORzvZD{aZ6FRA+baU+ighso@i=V8Czr*$H z+lSRS9xNZZ+Zr8cOjzM~&n-c6+_U!P0FZDIBLNFV@2+zyOFXH%8z9s*WS1vWcF7 z!jl|H!@XoME8jou$pwSJSA_lKN3f9ZS)*|Z=R?w~mL+U8d1)rcsPD53)LnwsxTp`D3dyu6C9*PK+ zLF25UO0kTP0>HULcQzLyaHotBe?x?6jT}WtmMEecZ{)q-Sa3-Ji(FH@2B#jZxmpKI zu!}oJS>zuBF)jc|c?titry*RO6Hqv-71%%L9Ch@LMDP~5C3(x7<2(sv2^2L(NsZ9r zENe`E7A(aW94safESdItd;obxpyGf)Gm2stDH#un#C|d%2(&dRX>?ZHyjU4#F}zrr zv?a21$keL=)^P<1%A{goDNT&9jOrSCDsamf9eQ(0X-p+;G=q{k45U3Rsxp+(KBMw! z%K5MAD21Ru zss%V$H0-QXh#}~x8jo@nF{~_0VG`7#`~xBkmb9|gPtRFcNHh{$@JMk>DRe~kdy>Lg zO2Bw_3`)j1*a+0A)V7t<*voT4^D5;krLZ!^T6p zTe`?qs}zzSjc$I*qZOmQmq8YPsywoetr8ol))?*Y+7hQmYplAxi}p6X77=Ara;>d7 zU+{X*88*}HBpBur+{S>3T8AQ~g9#2n&I*}k>n^t=*UEO>sY8`(eQveJ^_2Ry%6r>t zYn_N15AS>*W{0f%m6dF@)=E5ACrsaz&nf~kR6CBGqZ5;#Af0w?d`jQ@D6_+JbprI$ zLUTWnovI|?&Jp%T&!Dhl2v&~?^!CK)fm1^`Hh3iHBP6BCGq+BKH8vZ`wOW8(DumT< zS^pkeqY8qvHVQ3g53BrqRFMkCEF9yQ!o?gCx|G4+KE_zdZF5@SS0r8YwP9%)$G|}x zo%Oix0VS9ApkhFykR7Hu4+4--%^|~z3Uz5~D}B1-k~O3AJcQI~upfZ!*V2_8)0fGV@XNmIrePPw4z$Np*i0E`jkG{12D%n?%or4Hje7H7|Z+Up)+7Ep%%E5 zG2B}7=yNk$O0?#5utf{#`$+@1HCGMNcBeuuW=@~dcp%&CfYERY!MiczUgnufo^v;A z$wk(6-QM0m<8Z_7fMtr-+4z1B~9uJ}1xQKRQW>83I71xE#(b z)o{tk2#Uu`WF}MJnWOv9;_BWe*oS9jfT{`<`P-ggn=zxPK# z-+Qm17c@i8gK@wShgyt5uaaHp{>}VO}Wkeb<=#k>NnDi~PGLMPWGtF>!ZSzcb_a|)gC2jXFT=zb*2|q#z zQMnCs*$p9h58*_MlqDlzIC0}TN7Iuv#P~K7jc~<74D}+2utD~qzVHL|M7dic{FHDY~E_l)X{i0j1;e+-C`EQz^?M$vc2S&;q~xEX%D8bUY`&VB9(?B?J3E2P0b zASNcFRWc&gGpg4juJbbHcq5@dGJ5_oN{1kEZ3Bp4iyVZ-C0Z8Mbb^w|?uR-alq%-e zd+z+g;2k`cw6zoG2Gxv8VM;ntOoXscXq&~myo|cG)L{vw4FG=6p=BrnB{e62ma%2brd>eNhE=t=jiAf~Uh)U+_c zKqjmUVN*v*nf{9#^R#pNvUMF#?7t$uZex1G{d~LQca2W19 zmtdgugF;-YM2LZfxgfO4?XHPfH5SAk&f0(dKgGO1#n2RRT3S!WNo{GKP;%k0~CsgJ(@JP8kD{g zOWce|%j&)Bs9vhtF@uI7k%sI)eP(Z@49C)fo(iR0=a-&_a5p}r2~m;pY@MIH2FSin zVtK+3LDi2?(SK*0jZpaDWs$>P713YO^%a)fU5hML3-w!qLwv9mFU4D+hfFy)vj8p) z54>YAB(rA;Q(JwAJ6b{uDDihmtaefRbU^2uj|fTAerK%nOG(~;8Btv`UP{w-R@p(!;R! z$sUu3wAIA17UVaL^_5*Ey@dnJK;VwUoQ?m_4r4<}U5Exg8xp7qHNx$?`IsVC#J?L>97F9;AT>^?gGS?Mz%Y zx8x1??cI!HYAZN z50K~!b%fe6iqm$Fi=WP_yUC8o%jCGvA?ox z0Mb>z=M%r9h6v%$zacn`v>#@KY^(c(8N>z=2D5s#J5#1+`PXMq4O@|vbt!ZxDf-(w zB4458BEiVnd9eJwLk9V=`!m)G0f%<08vdqA04BwHBWZ+R((tPC2o^{=d1W^rYOPsZQ-Q-U z7`Pry(?iI)Snx=64bZ-wSQm-U2Ae$~|4)tH!Q3pf^H!xC696>W%HnK@Z?@n7EZ z-*~KzbTQZ4QlFyWk0fwFV7fD5L(B3-((WnUSC2yCAPHsuPyp?&c|=C$6AWLw>$k^> zZ?-B5&Y30$9Tt=xrj8tOr$E4^MHaJ}(J-Q?1{GmvVLk!ov4A`a$6b0utraH@EOMPLg(uTjjf z5Dd8DImKM>ajC6YT8#b733Bc zSwJe1VDU`Q7hGo~N{glHpTG8Du`bmloo9f5+PMnVRR<+qJaW;ns$NO4{dS^VFdGT= z%Yui}#>tN{$_i4A8I~M=Z`%E`<_7!tHmD40Q+zK9DF8gSMMwsEBYl+55zFX5HsRY> zi+J1N!|jhJ6r zCKkG7Gb?0q*M1ULPyxnIrSl5@dUAv(1ZTD&`5iq-@MBf98`6RS4pTWFH&%)^zTFXi zkGaKq^J_`M^`c)!QMt>zP=-EpcHMbu3uUlXAQ+_t$WZRmg|(fPPQdCW%5}mep;S|L znqdBkx#bu4kiA|1^j^W8@+;89W!3V@+=iFYJbHB|s4P-2A@W2h8cxHZ(>+R=$KazD z4#!JCxyIq=_mC^JOn1!Y(5@g4^fr5(TJX;FA*B4Xg(i~yA?56KG~gmIGE0Hm;UnG= ziovSu8(Y1)Ya{d`XSl8Vd7`ga0=&jv(ck2yOpS}h3Ch~WdVf6-7nDN{*9Ubiu!ZzG=;h9g5ErAf24Q?(Mk{-nJ{yu%evoecdvtkTE1!~%qf!rf1p zW^>urHkZDqtdjvOy&o-$emP_Led;fmHbrsIWt#vwy02!w+BLKkl4|AXn^#Xch6!{Cv{NOjo zvptq#-dK;CnhQvDgvVmU9%OfI`TH$&M%=7dFM0FDmO39n2|}6~N*cY?Ad3>PQSSCs z%gWZ=Ud+21{!!iFpe<{n#;_x=-~ngccBlj>f&BW`7>>p%Y3*WwYFAB*)+EMY^X z^i=9VT^*)bHJHVNg7OsO+T!ME{};kZ!g3n+*5PH>VE=ZkbmrZ(bZ5?CGUg-n{8U$j zMqh*kLZkyiq`^l-2|{E6qLwS6MiA}3(*lErT*!q&%H~svj1I^}R|4MnpGx%)K~{=p z`ODM&-1^>NO0hP(b6tTUPiQHv{uxvC`IyqT{vOyq@vn;e?Y`bW)%_40u(uK<#KA{nQ_n9ob_pL%U@O10Z-vhK>B8~en+Vxj`?dI z%}yMG>%rS_Xh;)TPRy^O{ZA1|=*{EEUIUJ5gDJM^_dpw><+AgKH`g9QvfyHpS`(id8i$Evkt+7%j7f+^FZF0E9r;tq& zvRtmS|J$a3A*zohpQT2-e4v;wQ))wK%M8%&_CWgU&Y<0Fy{QsttIwq0Njpj#@5UO>=iJE+_KbIXyix@DHZm^9oC~ zB=!NtUS8~(rA~4g`onBWCRmCqoG1n6bG#@8L-Kx_NmVg&Q>LoVosguVzHwrP$$)tt zIw|3;lWI({;FxHc3rH5R+7y1I>Cx3?5E<;Bu4;M(Z%S>L_}9H}_{2Y6nEGW{r{N`L z?`oDL!V`Iyq$tT;nb|dId()N)ItNG7=UCts0>w#BDV^kkY0tt5eLD0stU@@jYbX`o z&-9#T#m_w$`*6wg4sX5py!}PS@(O~UthCL4&6R75L~>5kSS?`V9h9shTNRXM+H^Al z6bp`M%ku*8>6E3^t!y&zB~q)LEdspGT$pwQ{c3y{!6_4jIC=jUpl1?&b}<1;FCyos}?o;a1XaGjONX?zu?< zB5kTY-=8_{dp_%R-+Mgd1HNRO4e_JczBhviKW7+*VQ9Y}gya>zT5Pd&V_8;PjGb~(X_E`tFdj{ww=aYv2C-lZ8x@U8>^@LIb)w!=L_sH_E)%H&3n%O z@4D=p%))V8Jqx8oY^fJxTEng+)nv9+&@?T6ag7rCBf6)dYo}j#n249)We|3BJ$Fqt zX))HU4%g5|yN~=diQ#(~Z_SIxmT`@Pr?s`*lcJ^RT8PEXOI)Op>+zK<{doi8J=Sx$ zVjjy*0$!gCqL1!PShB8IyI9w*Jy$RF+f{AS&s^n12i);$ZXaz?+0F*M^HKS}{grQ< zlf?Ar1(2rFA1hrU<$G>F6pq$A8jahaprNO=i8hpFSN_BhuU{TMJmwQM|$(lOp z@U53*r1+faeSEAk+>6!hz+Fb5d>|EuiuFgJRlEl;7deuO4A@}l2g|`6g4C9`(Y;7N z7Gy6-h(vNVwqAke@}y|)2GZH;;9tyy^Xpzk8GKDFc-mqz4L30HK zZxZ8?hKmqtOQGfe+{OP^K04`SXR^DVojP&E#mIG}iaE5ahIYn>ssv)zw=GQ5-d1mH z>#CJ^P0BF_ReQz@%2*#3uEG&%Be!zO_ zffg#dy_dm)S2^{rZ>S_HGqZ6qJ)h9P&+Ly3c@I*$WEp4Wi)1!5o7$QMnb(Do%lOCj z7tjo1x8t>-326qDP~ZNP;iEuxku2UmZXs)MiK(&Oz%>sqT&2+{xCXMY5>D%CjQ`4D z%Jd&E{;lp=^yY>Z_PkMwBa?xw3T!Oo`jp_ZT8NPEYba`vwIHyvn$#g+PD6<%`YxlU za0RELHtefGS^!Z+n=6)aQY1reWHGx|q8PfwJN5ZmN}$kk0t&Sv!x5vkp5roCr98QW zl;8u(^PV$Dz;}`J3P1_DurvD%wG;8j9dsYFp=FR(QeULQwLYfYCQ=?QeN<{;<+~^P z4ZbH`$8yg}QaAs`FfmiH5{4*8OD$5$Iagg6mxm&#FFu$*QiqWeq6}kCc+)l2N|5UD z-)>U3RWlHBu2exZ%5ZDlPRB z?{l+YVRC%W;B~z>JtaKaWM&?~Q#<0KetK$hm-IrIQb_h{A6+x=0_vHTutbYZN5cA%0pGPX{ zExO0CDS3CsI#VF4o|zGPjXED%cocYXobe}j*P1HIaiML*UXGR|+Mw{252KA(&C2sn zOUVKcHE44;&ilj4)h?U^5g;-x?jd{^x2HO)yhq2YwYj{^Dn8LpMOje0OH2o?K%kQ6 zxU4-3SA}~x)pwkstCEJ41D&NemejyZ9_kY^8)Bxnm!OLIUFXNhs_-llopp<(k{iogm2`SUoK%e*%jXug}akMC0y8m$+)l;5I$@<~|K4x?R~ zr5jamQUYT~tUln;&EESJ{!Gw=$vM8et69r7{GQ>XmrZyJDMA?R*sSBaNv?IY(cQ?L zJ!gL`fU`M@y>DqHWj9tQ7OkzJz!jOKkY{RiUIU2u^l{qbVxY)wU96tF@%z!TA+1-< z*|#?R1P{-f#=j6`v;ozTABN*LwZ#NOp@Hxm%f!*`)E~7 zr(aGBid!)MWtxR%e}&)s*65){*!o3s_#^C_L$7o5ND%|B>mPG)4^7T7k!^eTllQHw z;hKqG-8u=SKV5Kr*i6%~%#*%8s-`JGDnB@Lj!ooo6YX=g$~6`b2oI(f8+oXPrDO=4 zWx8*%O1u@s0kBfOco&*0+~WdM7aQ;1M?}B$Cov_Jb#Z*ssOe1^)^i_br_)bHrmE2c zb8r8B$vtroo6FDI$6HALXl%~UJYvq7A=ccxWXsPnk=Tpf&W|n1GUwGLt()QH#5fB9 zpOd?b5~1b7E6Rnpz9(wKV9fN=N}aDjkr@6z<{zi7A5Os#C|KnOJzrJ4v+`lH5!)}I zmsrg9G2$J!B*KmfvRHDptGRBcwIU;_b*Y^vIw(N3>e>|6Wb#bon{F9c&PE7fT+D2F zO6DaLwk{O(Uc14^c%~d+;1e{LcLPQUvf8>m%L~U|zl%)&tG6bxmX&Ii`?#70Hlynl zn&FI%CIhu#W@WIRj2<|*s1>jiTamKeo%^nvi0PCS{W^RVnBRW3y$ArHND9Mo2*b_i zeQo94)*B+=?9`5L*oEpvUa14#Q|bG4NBb5Wud*Bv=fN?Qp}q_=GS*0>@=39cX4~a- z%9pa1q_Y#Wh5Bj=>q3Z=7$ARP-Z#Eoy<_TYl{Zk@J3{aF!W=r}Y6lH)6C-Frav zEU;314W%9M_xz6dwHYCz%;~L|lH8XsBeHM!gE0osE$Af2viiP6q{@3@LWNAq#$N@y z2T+7}N0|u1O9;;YvU~VQk(TEHiK<}88pX^?DDv^&xeU)>_e$UJ0xNQ3wTNo^w21i^ z=BC%8WcL`QbI|=lnC1z|Fp9{9^;s}7Dg5w{z=#}j(gDV55cQh8XG9HNQuy*XYxz?f zXT;{r^Dc7vKS}mxBlF6KBj>R9qZETlT1X`ba-}TyKED-V;*gG!ldhy}vSb?FpQ+o;-Um(@Yv*2nkMLyw%ui#ra9A+L$Q z)kc~r@BZvT;#!W*9*YdJE7l7$X9_BY^UJ>B(#JYBxWhJ3%iZhhUs+Ai1ICbO@GrA- zQ*`vDZ`4dWn`QPD#JJ?=zAomt5*3&$lUXRE<&c?K(va(eM68wxRSAmtj04=_EqKS`mwUBiK3dn!PMwsIp>6n`n)h-crK=E);lIkz& zd+&Xv6!T%=V7DAgw~*D1mp-s7_Ne^{!GZ$d>GZ~wY)~IMp(df0u8D*lXs_cF<;I~6 zz*&OM%OboFC9aQd3Jk4lcdd=f_;E0d^!05!$kgxV>gXv#u$P)=LoG#g4aHhGAm0_3@bM>>YVwrs;x7B<+5^2V%HF~Eze7xY5b)rtwBcy;@bfvJ0N~E zbHMunfLq-YzPRVxaMis|wKUafaz*9gLMXRJzgnpR8fEyzR5tWQDRPw+_gQ^uegF$; zpmSjgg}MjGWbIqNr?ph`{Gk^-j6ax44R8sfrEDFN+EsQpQ_M6AP}QDkQ-)?EMR1WD zyVnV#RyWp|WXNPM$2D+xc9fMuO3uQ<=|S)h%5W3;>wiRR?CM)~VmsUX`C@rzlSZ$j=;8u)26{7Memrx~E~*874*Anf*W z&9=Vk)V&=Rw56fGMHgJ{Dp(~Yi^1CPW>Eos@zp#sh2e8?nKvj6qDOU8cwRd_6Ectolk`Y zPO;K)K`}zq2&AtZAWI7^yAU@B#vq#yyA0v4)N;02;4qDMc6b3X_}^c30QXqTDUaGu z|6-R8cO0_ys0g+&RtaaetBy7AlR2+lZ`fUY88ecii`@L%&47|Dr2-gO#4l@O&iRpR zy9ny`maEnx5!MHc)+_ZOZA^skkA?MrF9>IW+;2hPi~*&kurm68zd0WDpCU3CJS5QD zA=h}05U)<{tye5MDl%4TGM4fgZn8)qU)&-;I2x>L!~QM@AUJ{1?%a=4dD_b&z+7GI zV}=}``WaP)tuA<*8w8luW4JJ33RJ5)U%~cX*ff~lXV&v@rCu;l9bnj*=9HyNF;B2H ztTEaBcnYfOUdPvPmh@z>zP>{l&0+;Ib7NC^kkq}q!vo6C(R!H}`+qxKtvuQvj4_av z!fmRp){|c_-NF+F0MC=bYx!9&&Ki;$2a3_65U6o*L0&C)-?M;_5DU+rw5>DFe#_3} z4~{$P7DI^)ls+uyVGWJiGn0Dq)SRg}7aIt9g$ES4{`3!!Jl2hJ4Q#y+2Zk)=Ajdp- z#~BLkf4r*BPK%n>JWc-Mq4a*PmWB2Y6RVAl>MRSd0;}|)Gl~bkG#b^6`#xo=Q#3*S zLv!8n(rGt8V|ombWYY1Fqd}hLQJD2n>Xm64$aLn7Uueb3Mw7XRY$(u4{J3^%kGcY} zu=!o2mOD;AtnEPxh24htL?Gn%PVDti(AL{P8h@4j@}tX0L;8M$^>+le>7!E<@mlXp zHl>w&dK(s*#!^ENGa!=&$v8&aUq%jvL(T&m&EuF2`DDr`%^;P&?J zOtqqOoDrl7fmxr?um;}5OH9Q5p{#cjqwybV05Y7S^4ECTREKhI^yZxqF4kz~)v|yf}k%I;z2JRs;*pykd zS|UXP=0Agw+eUiWbzeCAoID~=UfXkqrfMGpQi7rw+8;`#nVWEC~3V6+DY4D{y6U8cU-%_zkx3O>*$( zJ>h^068(O{?k(t6Z&v?C7F*_(slw6;q>+Z^LIl+pLeb=uu^X3G%IUn=%Zz1wv=`&D zcb2@mf!8;5oHigc6PW!>{q+`xn_z#k;V9Nooab%xeTNU3yEN_Ewzu>F&+A(m)5r{k zvB%Nq<6FfIH+k!Z=0hH#3f(`(*kM>yCmG0@I@q;0^8B zg_gUh;zpI$zLAHgZO0>7%12tc^GzE&$DhR8!{wVCXG~@ecu`PoFi*FSqm*ZT(}Mxl zhZQBoJDdUC1Np7E!c*%CPq&CqlR;y$617T}TgOec?4S4gJg0Qbe{T%-jHLb3h3*LOfmk|gbFO^sPVO~QE9`NRD?Tz^^M!uCQFKL@kJ{l9X zp8PukjHM|2JpzhOn|$nLs$VONd?hH1gh>U?{(6Hj!&u=pV563tonIR+zka`b+B8z7 zwibswPhZFgbW?&anub*O*9FyrAr(dhz;d&-=cutJN$Y4BnH zHj-rbJL&zk(5VB2=rmOCanJNHy6%1`2c0+iX_9hkL3kPE;GK1*c~SO6+WB1*{tcF} z>S^`$f%*%0(cZ550NVo%!$AJ8y8j=h?vt?te6>oWaX7%GNe#tA%rq%lGK?ZzhT(LB z*t^fta$4Bi5_5eREK&1nm^IPzgViGQt^J(Ij!l8yt6D|)a5&n<5j5gw>q}tEF>p zz%%_YYs&MAkWwfbkMfpGYYk8ph8vk_?ee>|YQ4Fbg<;F{yK1dNprpp{AqJwVlBg>h zjQS~@w(oVB)U;}i|37s6As; zw-d1~h;30+3Jr zJmGz=OGtXZ>K17~4WU=yy|(Hgw0VTwh0S4f0jmO*_0Rcsoztv{l2bJfpe=69I-ynP zi!ah0C5c%uL~%d0K|DWLjYX%3i#IwAFQ~e-kHzlk10i11bs(cV$DFL zcmVVT`epC~eBf(WJ`BMdLg)p&5k%Ze2}i%9KDl8(N@*Q5$M{(o=z^yIz!s*95L`)@ z7>^i1!*r0;UM3=9VE~^Al7ceU28z2{7$wV6%=+jSM)c4aGb2R=KmHb_xqKfhOAYX^ zoDpcPOIi;fFudHp7l4vz(x?uAU~*=P^=?wb{?iu43+)_BR~LA5-A4>QTnli3=sU-X zC`fH~3Ulr&3V?lSfkkBu2HfTlz{8br8O6kj>>9msoHe)F#e(B%5)t|{K~e&CW1AHv z!k$YTweIVJV-YiHxnN|pDDGjz9fL5J0Rgf@g*Xtzs*CS!LW1#$DU`v7;COxQkD;?M z+=9nHnhc@&=)eKBhG7gIUZ~0R0Z=ofNeB>Rjdu3-5cQy`J?8L$5E{>ZMElo45*Dxk zV^vauIa8hC-AG8*&jP8(&D9OO+lfaw=7c@jQjAnDm_YLZbU?re*KLAgXzfLgwCQ00 zN6WNAC2ADYSo!zTT_~pNJ>&pOQA*u<1c0qc&Lvw#+j@Dhm@;#rl8qH}0hWq3Nk1G2 zvYfzq9Zq#o7z^+dMp#C>#i?Ksc68y;e^VU*6U~rH16hU~7C+^*u8QnU6+*^h1k`*C z!UiZx@WDD)SppGCDqMq663**V_a92I#3;~8QJ1nnLbVwHD1{`Yn6AX+D!nart%~+A zwb*83o}#|f@g==-u_#J}E&P1(}P2&s%1d;Kb&h)7L9PX>Xr`7_+LVnaip zC8|fv%!m|hsMZeSA&a9f(72!yiV5(D?2TGZmWdms2OEsNv>V2~JGoNE_cK8}eqZTs zqZ-QA-UA!b!hZ{1u?-x&;RjGGFbXWjI>Kalv8Gm}yjc#ni6Lo=9)oiZUIo9pxd!)_ zO@s@#hT_kaAsBfk9zgsd^nzV1@+=bw1xCh%`T(nbn0>&?rXy?-+H>dvp9MH9d;Hqb zrjfN}WKpDB6=;uvRoNH#dMGR~+`nwJ_2FHWYgeS?n=~u&Qpdba(0! zrSk*4Xn0-SZ0Xh>ff#W>_#<~Dy21%tXMsSWdC^7BArdLt*p#-jSUv&(aFD6q^o|W) zT5d{Ct6aVd<8CiPX0i#r+5|^9=%eO$ZjjQ}LCPTcg(~D?WhY_h^)fN?lR*giZBoJB zXLTk{j**;d(ShkkYxiXP2p@#EYx{7q7cwBR zJfpaYzw}d{n^R}{CHyA5W@ku)+L?;uh$gH+$^rx2dg~f zmk04&w2a-?CGuZhxi?=8P?~5&fcVz>&dSSmxuP?rP@Bj>-VC+dr*hVr_0=8U=W#)& zz9r`4Zjhu=i*LTDDCm!3xUb>PNy^x@P4Ck{lDB>BJu`?f3`K}o{_Efe)(g{L0yMxp2|;r6wR zpjqbT%b`c+=Fh|w_^fRCEniX_5WKrW-9{mT-C`8=?EXgKwl@;o=;0xj>TyzMO!bPQ zQU|a%43Su+$tnu*b~M0V4GJG|9Xm9@Z1p`QH%urYfi(93oajrm7?Lf4A)_LguFBJ4 zf+3NKS;I1d3OTuR)csgAEYz^PG(25As1W}`Bk`=b(1oF>evh!=@U#-=^Kgv#0JGd` zGKg8R!wT0=qzt;&@EFGUHEQmo@2SNA&PMkVDMJN(zjFjy0uP!k|Aso1gax>)I3;8K zO7jdt3}YR7iKHX*{fp@bZ1Zdi@r@yenbd%d9|P7ZdJU@wRUL;eTj)q8xk{b#P%Qq7 z1Tg9n@zXdq7*Ekwf{DyP)%v61;n^0d!>RKssGO(>o4p#z;u*UxX2}xajg@MNaT1|4 z>X$?rc|&RC)fRWh6f3PB8=MwGmTWb*N^K? z24Q_%EZk}(eRQahZ-$YZ%!t9x3|U|mS{jp!G%YPs)aA%6vDiSL{lGmX&3Vo8*kPQIqvoLF(>?J+Yiz6b6e zcbIyMD~SnK(volrys9GSSkww*5;znVcwsX0pFZneiQcqS=8sVTvGMG1!I3%dQNbg^A8 z4X!LmACQo&gC4TV*dEV+IfHC`^VB)c*3>k9J5{ke&-EM++M^Wigp#y^$?|+xC2faR z9xp?sPWv0CfwY=ilbK0iRm~<{b)gC5tSBmaw`-AdQ}YNHK2y<6FTr?Ayr2?RsVg?9 zq%u27<}S@vk<99}&K*uK^~X_8m-vPCj&fn`JZ#N-h9!%Wu2Am0*T!D3zFBPpFsQ~hl^){funp1fyBC%Lnw-=~FM%)((6595-9>F`6 zQ>xHVF!5;3_aifuxYF8gT zrB=96d>%E7I@L_OkQ=}zcWFTcgp!|@=~b^qb3khVAgcJSFm0*DjEbh|p;jEyay2#H zPD8(UIX)(;v=*yfva;x-t+Ogl$fntTgZjLvnewn_6t2akwCrIO z_G$&lrxxkwQ}CXpRjAA@5BMGSqcf_tsaxFQ9?MuEtMCQ}u#lO8SlwaxRw?_?;cU}Y ztQJ#MR)&LUpNCyTfEa6IQ*m~YGhGBFfzXK}-m0MG^a9<(N8M8c3lR2-1R%7fUE~j%Lup0>VTZ= z6;o;B=9M6%?i`QweX|OfqU%_yv47EYJGk!d)|KeQ8_CkWH!&V!$HQwC9GvXk+Y=Slei{>RpTsyj5^ zFKDW!dKPu3rK{rYSH>W>;4r!a#G^rf(r~6%K8CX{^&G_gAbvs#_)cwD&RblJE>+gj z#?Yh$L*jqTKa@m#k@mG$`h7|%!c&cR$hN;4Hoqf6va^?p#Z@S zVXJyE?F5Jo8h;bW)6F3(d>N(^qkZZYF4)Mxm_#(VkY#O@3!G`Xd=Aeqjo-eFwAIr& zYp~OhnSZ$wPWt+?t`<1*$MdcxTsQJJIOdhD)=5tD2-2ccWS4_|SNb7zt$*c3S65xY z0PcO`qAJ2AE;n)pctz?~`$5?Ts_sihokbO%@*1$ovU)y29cn>zH2w!`{W%8DkzEa+ z5r!|RN(heHbk()aKkM|indHov>Z%=oPIitqI@RRTU6k(8(_kLVJ<@N4tDD_eI=#31 zlpdS$Ma;3<{;Cn?79iRZuF;aO8cnCbhOW}i`sj)deD^z5AWN8C-m&dna zoVhaZPiPM4x#$?QLF!1uzzq{8{f6;NU3KpX&mM4#?tMD>Ma3Zlqe)AEV?fgAmRNRe zEjV+~_ubY~cl*<0avB)p>jAgdUGgy(x)m^0(x1qUGg#*R@W>}kyr;*~`(Ca2eOz~%ZT#L;3y|yRG?}Sw`NV?Tl|83+rw4IDYYs_8JuLm)USX z1Ky{5ht2`ouxBe}i_vXu>N<`L{Tq1#X>bc2XFiEM=h$`+MfZoz@7zlN<^kO447sI^ zJM<^x#Gs#BZ!5Sg-2-~#MX_0MA1t7eH{}^Nts`ocgY;p%=j_`_*mH}L@{TvTU5mQM zc}49LE29nP`zlEd6ae6sI1>}FN`qVn{xJQB;a~It^<=*@wrlP(V|HnIvuRs=wHLYs zVCt=XxiuGfO|;tXLLN@Yd36*x)_?XPH@vnq&=mRpW}pw1{JRR9bE1^!HTLi8*Jiu# z(3K(-U@GIG-s615@7?Ney>aw*?M~Bev_<;wx8UCwTa%s+mNNMl*VCByO$x7B$IdLt zsR|${z$$(Coh~gd>cjZohl1(AKY2^7^l*prcTFgt7$w(|fUgtE_sUnF$J|dHls-Pc zojh|8BwoRy!`Ef+>Ez4`;P$+z1ssj)4q$Y+BNzmQ^dEIE9EOC?AAz6ap59D6$Sp4kmEnn5J;~7)+9%ZowA52taI|>dQF1sVFCA3)RZ+%YO;(q z;wo~iDQ;Qv4%1f)ir*mu z<^58AD`7>5HLkLKsHU!Irv2j3voG8-*K=y~O2f52QHx5n`F)kRqVxh4x^Zi{KrBf@ zZP)y-Gf=K|*$vbDRnKXtfOEg-vYBJiR${5;yCU0j8uyQcS(nWqFtg<~&83)BBTo%K ztnF~Z)St)ERLkfFwlDbTKXQ27?IAFrLHa4miF0B<_d++^R`kLR%(b&!W)irpBdBUu zkE5KHJxy{xjxyNSZBf&91&#PtHbazRJ@!1a?BCDA^HRC5gURY&7AomyI1c{3OKrEA zBSCGPkoqF)JZ$`9pKdpt=bq&>T9C?PU1`;$NtmFO;#J;1VrBg=HXPEn<+KprQM=h6w4VXmew~X6h2}ZrGrZjb3 zw-GIW?VR%(e?A{~{k)uob-O$~)hgx@t&1IYn14)n;1i6;8D&Iu`B=p~C|JIQezP6|kP{Dsx7^x?pW`31aEeSX&4x)k3BuSaM(I70xQ5vfth2#( zw<0n7uYBx&lV}sANUe*Nv)%u`1(8TxJsYsUp|@@5$65%%c+gMwa_T#K$ zf@MP5qSG<4PqXAkdm#oRiLHe*SB}n>2!)&Wxf1^1cesGQ$?(v7PH??qsor#LJ?yHXLdGGFX*ye>*l zR80CW+-zO4AuJ^QsgOikZBm7;9YRX3h{?+2)z^G1x?LINR>oX(Pf`)?7L`OwrYhjq zqGZ%OIGdT|;BEz53EdN{on`=}DynPNos0TUy)c!fgoW&$>>{+4wecwH$8sq-8va9W zjgEbjIL>=qqQWzfLX~kdX+UHD4_UAUO{PDt9OO2*Tcf-tH#O=+1Y#OthK#!w7DbQ2K4jnqE?l5?MOKSjkLh=B}tSr zFKB2`Jr+PiBdPV>hlKb`II>$9=oR=a)d8bCD`=SMLnIu`K$R}jgPL6!SC=~L7-&(a z#HE<(EIT~bj6DVG^uZJ)XFU<#Z4`u8qJb~Q2xgc4L%OwT04ym2^lHg-CV1L`ZnZIC z<&i6RW8vuIoqY<9`CVZK5s`YMkht%uXxSQz?Z|Z2P8Xn$o&?A+v(B~=%0{!2o~HFo z4JWo`26|7MjRnjek^z)@36H=@`_tz4+d>`ul^TI?|^ zj5%BrLVZqEIXnCQh&NY4K-hRsyic>X58^)pB82HkR*<2gI#F%R8o_yDzjd2*gyL8h zfiktnDkxYgzkmHpQ4JY_(sI`>|FX8}T%)kj4908v3b#j@RYabF{7u<4X^^$iAvwjw z`>RWpsBCTaC2XuKXUjU;29Nq9*S&WOFEBHpAJN8fu+JgpBG^H8?{|sg;9J)LevOI&8)taOdWT&uRH|DY+>_XwESql#QO|{^nsDsgBDD(I)*mf9k0* z>E%I;aQCK@tGiG(oE>w6r_ndNM{=G%SV=+eafRG_p2ZGSL-g6=8lOFbH|b}TBY?t!l|SOVe}a~Cs%!5E0>A>q|DZw<#b;ia<}ov{L&#? z;JHJ;Yg;>z+sz*$cy3H%L(mOEZ~HTZ&?(X0*Sckw3gf@|WBE+pDoS4I@4xya@~-f# ztJmZ2W@}F#j~A@Qzitf;=3OeH2t7O7KKKKQcsd80*Ol6wCMs}wT8$1^es@+Q)3PmY z{A`$n9TMU7%<5fS=mqWyMBfV7d>~$R0>PTP+P@0~eRP9O5cSv;J^u4Ws;vb$yi5oz z=aXC~8-gbY@a(wtM8&jI4H2l>5#KI3|D?lTRUvFN@@^+Vl;QRU*@Pe(g9P7c_(?l{ zE0K09SG9>92qNL}1N96A$aU(v5g>>>4vYSZ6g^7w-gEU6iEnw|)K-Tu__C7g4TU-QJkOH$4J*m3L$K10vls8LRiS+WZWhM%H|R|Wd7Q(ea#wH zi{fBW<<-y`^krq$hU^3$Fe(be8cs#KNAu1|My5!P5TEk%Zg#g%a`3$dCmxQbCXhAm z6WMf$M@%xj7FGZLE-Ws>BB?qSV#q7H&?kAxkR3&dt2!FgCK`>`59J7)xkZfK?3q9q zPD1z@xWK{3gda}KujbmP-b5cr$KgIG^cP;lc_@NeR1!sZNUHc}^!#t$b=XLv30b;G zBt_xMReqjL_;v(=@~+@P;heK~wgZ=5SBMGa3wlY>mU_0qLE3tU&k<&o=G;U2rhnDb zx|0UBld`@D@{oOmsC{v^BkhB{T#D(%LipJYK&#%>mM2L)QDt>78hq@4j$9rn+wq_?RqfTFlByh}G-a?$Xr(pV8fF>z78YSDCnGO7{ecm(ch5mHnQGC^>?7^b%$g&a_0 zsl3YWH0rgym2kX#W@d94kV_zN)^YGfPf$c?pk}Z?6{ISW>fO+?E&7OE=xB^-;Bem@ zaR6Ln!Q#Vr<#TY3)2=@#Vm-LFoHT0cQfzA8F;ymw%8^mwH^dQQuVsGZtMu%ecE3j3 z(&0-s`SAl*2iKNVquXVt8*GL&Kby)6*MCRKE_;=%xbX5R#0(2|FJqZ$VcTgL`NFq7 zsy}9r4pgjBH!ddY;sedbpE{XpM3oizWU_Im7}+K5;@^BjU<_iJVfw z!30dAN=Bn=Miwxy=;erWnBikDsz%P&H^WyNi>-fS-$-YZ@ja?*URv2ioM>Z*P5JsS zEZi2O(~jQ{ZV4~#gVaS2#zC9`(z+|n+LH0_;_jW~{#aKPh!Sj>SE!ZM#oS_QuGw;v znH+c134VnLijVfUgw#CHB@50+E!WuSAf^oll}0=8+8RAacIYIG!gA*Rdq zWbk^i6N$JE7N%6H79mCi%ZUXmSf{)1w(F;Y9@PG$>Z$-LTHlbt&?MK)^p8yh1JU2CFa-UCWaL(m zNlaHt^koNX2k3Q0IZQl|yW)_=e@$Bay8idOQ=9}kIe@jiR7BNnIn$53S6z#?;fj<@!dBP(}u8|+#bs&_saqhVR2YjafD6XDusQAZJD^0KRgu9BvKkWZ9 zA1=7scn4ioS&vZF}ST|VeNHl07(PrN%%8{DN~JwZB} zT~W7<62E;?alA7>K}o!1U$A>rzp=Z&dg`&&M!VM9w+-aC@7}f{?EgN$ZQ7@=pIEQG z0CoI|*UJ7Rv5I!UEPryMdx>&>4HP(yb!19uhMXUi2=`PL?}fWS3cu$KXMyB&Acv4Ok;}bBhwAC6$Pvg!mIg-t0m#9Twd&$3u7qdlP1*@ z!Oa!n=xs->O=O&P#SF5Ye*Ek+Duuuc4hCL9#x5xL{x1e668uqUhx5?LdsqiIeHy_p ziTV&vFBQmF|C>Sk1#q(DU@TI%?0?0ynT}j}E^3(GueGm#!k(N_?x?8kPWVa7{ZC&- z^;U4a1}Sa_dhKQ1aDF?WSo?VWOBN7=bt8KWN_K{tAf#{kUN5+XI#O?{LRRG;&$z{D0S5^Jq=?TV4aiGye@M(ziC36Lzz@(r2?Q6T@ zHGT8^och6C=6OJH+By1h!H{ad_@(2yQnETU^0d7?wxINjyCF_x_@W4kt#K(HJebSQLXIIGa8;?$5_$U?+ruO_qAPZ zGTUje`%t&n>+*cMt^sk@8TJPw5nj)%8kz({p~R-i=K3B@pyF#EjNW^l)?Y*8;8yPBTkI@#uNcUg>FB$59WADBEd_IBi*blJ7-b*HK7gMQ`Em2{U5V zEbX7QCr;J$1PXBw2qk2M>wBY!C1;4@EWKt}f?m(s4t~8?JQBtWwTSoI<|DSL+O-gC z8U`VPYalo(ShH^i8*6dxX-&|dY=%0CS!n3bYkA~Egji{;W>C>AYkE?1tLqEz%cbU; zozMMDh}n!dCXM^`{+4Jh0MA8Lz^w#;FBC&gKeUrQUM>zo5MIkyM-iq{4;eR$R1dY+ z@+{EB73VZ}Gc#`0i=@bH!>Wp_f29yf=*K$n&d}meiuFJ5u&Mn(f4A|Ez{#Mp6Q55_ zPK3h2S}Ibzqp(UwQs*iOS+buvOSN%>%PF6al&Y*C%gr$Npy`0auCAvs)z5aHe^>n7 zHNnG}M*-&JU}H}vO;;a9vv%5`tb{HDDn<~eEh!mP%Bm>UZh$&>(f6Z1Ze4zJJCCR+ z?VvF^0j4oTA$?O_t9HHSVw|9Yp`q%be<{AHE4`%sC(S`ohJDlOM_Fn~^44d`s5l;0 zgWA>~OhcP~YewmW$8Plz<%JdXm5~Ke*Bzvc#rB;{BUvn~oy$InlV+rIk#$xJwzj<- z8^@M?o!>sIllcA7AJs>hv$LJiwIiCsw$|ByRuCm(*Dm5#Y1NKu`0H+a>0v&)t)_gm zH*}lr;DWDP`7pRljS!5VsyI=;(rv+LtnWH{>)50YN{D3d4dHpiUhiR1%ClG5apzti zIHacK9SuKlb4X=ku1iwpcz{4&r@;4*mOvN|a*y-GZ8@v$bOMdXR7nej${|;FBnmf$ zhfrptjfASZN<9CU4C594BbL1-I|DhS5^$Hlq` z8(A#8IE!A-k-c~j#swkBe*B1&#B;tK=t3!_F@_moR5V1ypry5_Tce{b`VgWkPz>paXb zGnhEgyumnf{{^3;1~kq*+m?q(T0zPb=0s&mM@nG<3k2gwafO^B2KQR+TR@_7L%qyU zk2vMC?!Kn;xm1PDqWbxe5f^`ffN40(CM;TOl@P5|Ko;zUgV|G;$U6?n!YValW{nnO z8>K%jxgxkQJZC5(YSsi??YNeMbzkMi*-if`7Hk4WbRxGm{yfr5X^kTmJy|B}DqBYa9?>#`jroA4 z5f)_qIHk4PH)(Z$NHk^62g=?OoQYcIOQ8}dp{F4}?TOk29kc!H`PVbmJKhH18AFbh zD96SEHD}9;{IpG&8|*4ry*?z(UftSOYtw|Yis#2~S>w)AW2iILLUu9NR(51%v`k=a_QT;kDW`)7fWds#w~jjbRg8$vwZ93ov+(3qRBs29^QC7FBVWU=DYQK?ac_cpOI+V=iDL~9W zLVIpdPF^EUW`e)@NUXX=Dv-|O%EW@g$GJ5&Xl;guu_%CH%fII*0aNKNab{nfufZuS zFW;s#Ur(~bZ^_uFB@-GUz9vpHX*)%!|5gTSGa+rvXED?mLJ#Dl0{3BH_R)59cdtnZ zdVr5~`@PN!@I40onH#x<1JB*a4xvOy(c53Vago6W3LOeiI%sn*@~FT<)I73ral`SN zLU6}E0eBZ9YwwcRI2S7??AX|mNc-@5>gJZXV0iUt+bC2mPdgP?hq8#A`2fsO^ifJi zsqpzam#?Nz(kHFEkDPNhlp{+9O+&A=^7eM zpEn%mEy7pHFH%`xUoRiWqlM4;hEE89OT`+&B#}UfIM#bfDnjNuED+CZsVC>8 zC#L|BElrbOIGZjTt2F1%6DCviGs#RF>zW3U(BYXnQKeo$nb75_A72Oa?@$P^88Ti_ z2sUPVR^)MCs5Y-_>=zbLqm1#+oRu7l_m`G2{G5LIi^Q2fo;NU?y z9}Y}GWwI@WK#CBppaL#RGG4EC3l6dvj$@=R=!YV}g+u8T`8bnmd+{-2Y6rGu1`$J8$dz+%>SbtOkz^q3{qwmoCgj%6f>_L2&5kv@AYmfk6dI_zESv$>(Z;-@FU8!vh zX+k#BZ_XA#V6Rc;ZcxTKVPY9T>Uo##q>yH%VWN#ds^XH1uv6&6Sz4JJ&pKj353dQe zec~rz71VuTv4gAFY7ED`b^%T7j1RobH3}pL1%jDNA&JUu`DlVK4D3V*B+fc% z5g2pvl}Rng<&hWrN!@!skjk_MVIkxd8ITh(HzkHo#exBp%|Vuc*Tr~jN@-@Gk97qN z2tU|G%Z>Ov>G#4xtVTAFjsa~#_X2---c5q2@;#h`VhB23?*+osLTyP?acKR3wQ z%3mR$FfY&UCku_!%=Vedb}-Csk+-p^Fs0VBjmMA;rz`ELtf~yntR<6lsEmiFDU_h| z9yi`lqwDQ6hy;_H(!~H9FMJe+O9m>z?oV{PpZbrF5+-W0pCiVXQS^aXtAITE*8XXr zLxm|sbYnP!YDcKR&0!`awE$V6m4><(^pTf*c3swqAhhj4jk)Veg~L!~`%P6r;$`(k zKGzLRYUAbJxbf)_LrVjuQ0B#%nRRieku5btufQ41j1A}_yC58dAwdUlz!dheWw-H8(~2O~JG6y@%urbv+=EcFLY>h2L|tQ2UE^~)wP0s_ZS@pI z@h$mqiT<>Om1afeYNgdmH3ojfBi+E43LdP=-4-S@R}ZwYEPM92Q%Nt-+@!!3$~32-h36cN!G3T8t^d z1)VpiR5ca58VuzM84&Zv!oZ_bX2HnB?JafP zv(N7>@SuW)ji5k<fqo5NTi8upj#e+Z6m z2UaJ+b|}cQ&*Us0;GWRcgKy!?Yx6yYuX#(a(Ughu=lfW55Uwn+tKVWr%F(&bjo_>Q z1<+)}7)4apCpFh2cGaH+)X%S6^t5Oj0snB@l>foMN(D{B{9{4$t@r0+2mn?iEYT5AGpEsoc=LkU>2WR5QX{ zo_(HYqK88#S$ik~K2;Q+Wp-NyE?Bm}?Z#8(Vl=Ts+LWE9b&c5C*9n4pBe6u{UWIz- z7}vapYpxLHmz!VNc0uRXOT@o=5n~fWZs15zzavV|G1uS!ld1oUgQ=SEW2Q*4uDb^j zGgs_(=jS)aebB6a&&#FGt|+$M-DE)j_$JzLipxx|)4YOFUN87EngAiTpoa^BCGci) zAidA6&zZ@{nzub4f`Eq^CXg9krx6EwcsLLlsXh>OqoAGGiyz5aDRe2=2ou~Vgvm7t z*iV6g(LEEfC`<_Cs5Y)cB89*_+Cu~rk|&}VwsX8SC`IP62SW1%29tG91+z^R1Y)!W zLQ4gH0Ys7#*d-MPX-xr$Qpql0K0W+0Mqem|f{=p&05;44tcx>pVYpLaidg4p{g6T^ zV8#e1-##M1DwHm7e>h~#&I^izj5x(&gbEl4Z7UaywNL;H2!zBvqAeklUm1KJ{wAd4 zyN4(FXG}^$7%5c`3g8BT>|y1S@^WAd4}^tdyF&^`a-u{qOZ-A1g^}i?B}M}dA#_X<8ifE5qWG*2f!h1V_|HEi%~==;Njx({ zNcGqeO%ex5@P*-3*B}9F;7C)RVQD6p@R^^pTN!xVfg>tUq;S%aBG_Z3vm#26n6+on zfM5F){&!8Y?of6t~zq5 z1b}m9dnYAn=2T?Qj8jolh*(QNAR$Z3nRrxK4{0P>Bp?`3&62L+`ikB+GSiJO}9R}q?b zGBL0{q6=HVflOKImDKsP< zly0j~QEeZ<79R#nXx~yb;~22IsXWY1{Lq=RHd(GUQL zFO@kM1JlP|Ii*W)V+E-FGGp>G{Gk%I8-%5@sm}jSR(inc@GbTh3A|9;(RP3z z=zF#V(aTax*v`ypU!Y&ZtkwQAgOQ5ubI_;NTD}T5>caXXQDH2yUXI8hVz<&@XJ)w> zUY~`&>&?jLWVI2K`FAY0Y!rHZ9NEQiuBxM)KIEY15Ii*fL>C4;e2foZVjur$+L0VpK?AQA0+dKwm zq$a-C6t~G8iFn~**2WEorjS|SWn+=Bz0Kob_x;j)zHsBS~ z1;I%5ltO?#r+E}kD6FwV18wKrzXp#zA-Y%01nY+ozwi4deT=`1F~S3!Kw^>Z+yq+t zDLoD=lgjc3#-@5L+u+JPh}xLRn;fE3{XTLf0D$;ft_FK52+Jw=!K7QSOp~dh2ggQ* zmuGD=Im4~JD$i`a^6(do5X2X@Uud9%DTGLI4s<~uBMe==UB%PJ-gCO0jsyaHkJSgk zryfs??(Z%^b!@8MvJ<(L+cR5S9$WUY2sn%SQ;k#+N3uo$yg&TYt-&UkY=%!n@z^C= z0>2VS_N znTBPAzC4Xz`*EJ`&gP_l2yx$G@mRF4cZKiBokqYQ)7B9Bbc=> zFI=v_SM|GTtdXRwYtj5Lxoo##RX=G_L0YbfXD6{WdM67bR&M_{4S#)3y**AJ5WoqA z)+4$N3UyoSGvkXhZ6@53r$-=9EccTefGFzco;u%K8(8JjwE zm}3O2kNv5Q6OHGHcyJ&V$`Pr{jJbDh@(vc^bonF-Ok=XYmoQJ+5F=&akhg zDCDd1PG@0%U__YLoG@5q!d9&!BV-VGKAX;-eQ1$^$H+%};n`Z~4Wk@a>h^f;l21V2 z>o#9b9Bc29Kx^DEH%W)D?y;xlk*;bCwynyza2}l*zGbDMc7KABsXdi5TrtBHGycXk zMbbCA@wS?Y=B#RIwdl)O`OB}tIj?&ChKs!Q`k4u*x_2ht{+GQ&-M0ZYWc^ryUCQ+X z+ZvMBS_#)L2o7+ZmN-2eRlXo9P3qQm7G|mG#n$d&HWrE~MYewe4kSKD-WQ4_5{aQr zgpyqH%e9Iq4T~tp@^MVJOwsT$f{A2;_U5GXWvQFtNmq zOEfZ80`3u(QR!WmG z$CHW6oeA2~?llufSklq7(&g9bz41gYsFLDKeQ_;S^eo~aUqU4^U0o|u>)Wl#>F8xB zztn_?$yi2|ibXH}bpuqSsYJw8!yy6w1ZHSCdGNZ`sPJ&jIUJX}ScZDo@P?#lhllVc zR^cM{<79t1jmz^+kFnxvZO@)LHPvtr?CUVsw$7v%_kjjS<>XjvJkrv;NX4sICoFWx z3VAs9Q{@B(Cebf@=}XIfKSQqfGRR-2ll=;11(TZ#7q^C$xj5!{wU$*?9`^^{dn7XU zF^Uf64?C|Kqd+)h>{Ghw8W2AsH$%E0DAFf7vmj|f)F(3mpb^MA7IXvaj%5?F&Rg(Y z5`spXFE^f3fXCh|ngyWNnR`z8H3qD7PwI;dVX#3$wTf-7h&8Z+U@OQzl+JS*PGD=d zFMNjm){z7VOl^`#fy;7`h%aA@$gtL{!i@Z`m68=%T42p} z0ADnG=VwIgPYqMBx?Ws+j8Mg4jP;ZxdI8EkhyuT4mXrmnwnrElBSc-i01u$U{1HmV zy-WXLMSHOef0QT?I60o;rBd@{;Av+7>cgYfGg)l%sRCV)vjXNc)7)A;u+M>aG{MCi z(dpDBua?>60{~i<;&^Q>(789pM0puprPI2+3U0|hT6x+A9Y04_m5qmWRMyg{5s6gl zJuE=7!nJfGq|=99VxlzT)tG205~~f!WNim&gRn7SDO8zi{8Hm*4Z$@R>!MTK?d{l` z86i@U#trwM;Xz}K(#*d_Qit>eiC@Ax(_`A*VAc|ZL0P_Qp$02n8#YlIHuXr{<%9*C zclQ=;Z80y{sYw|o@iJr(w%P$U1-aVb>?6yHO1|2U2ZMf?RP8=T=BOgYjK6e37K~QT6g^jc6S{==%J1Rzt+g|&m;=&pPe8@8Tp51}pqN)u!_uAn20a6{n_SKw#5R zHUMD!lf$-Y8nfw^Jl^6B10aX9fK1m5y=>f%q<=v85v5T(58J>vZVy2R-%OA`;92mH z=~uMS(DPlPZzZ2A+l%*2eH2WP(n(qYlC7Xy%TeTNJ~a)Mw1PV( ze-)`f_C&Y!MYpVr7xC%XQCG?QU52N}Xlv7{@4V#d>g*i(DkPg;-V0x)h4&MX=>Oo^ zg{Rw&#GA~k+6QrD`dVqTIb2?aWj%8euc8%vw+<4rqQpkyna#E`3m`qfoo%TZgbMQ+oK;I63;4FGRA|CEY_s~E3l zq^J48JC-8bnd%WE6E3W2M)#WK^((cTk)B{~%-)V|z)xQwC+pmMs?K8U=Cw>@V=8xH zysuh|%cd6oJptfUlF*v5{M(Nk_tQ z^iK@ZNrk{Oq(P$8^>U`X5n}gpw4Sh$g5Fg`W3HShY9ngA+%p!*pPpiGe4S#JW7GQ- zzrjRlPRTByMy^zUWV~PAaU(Au~#S;!tiUYn!xcC-){NjXC6@P~>JoRz5R|{X$ z%=p7o(P9FD`Y{WLYZ;0Sv{(C_BQ7x5mbMH)kme3FxcNT(>Q~hd8MPRddg?`nT@>in zg~;{z41}h{&Wd}FHc{(NoKCP3O)cVs9J-5>%cJAD!zN1o`21EC^~-{g0R7(nKeKZ^ ze#NOp*4tC`p(7jmX2(0^Hm|~kWguy_s6Zy!vwsm^Sc;O5BpSl4nBFW8roH06v(tar z3MfTRygJV^;dGECS|=hBiZg>K>Q0XQSOMd&9>8R`xUjU_vpYk()?Y#?004I9ZRys_ z^x(5u@B77LJlEBut*CADQ3-2F@6GnyO_-dRa!~m?zw4i}wBFU;&cx+?rk!lroi*%m z0861-+?>^hlgG#UO71EgG~i~`S0={L7dt|3YJ;4q=L~MI%QNW?wxX7t?Kko+hk6Rv0rm)3!UxG#iQkYbA8wq%vYPVjfWCCqKQ*U@Cq(wnDQS1Ik^$;otv?G)J`L z{YY^5RR+|b3qKRF!WF-TeWOL==^VtHBgDMM6>xa)J`@WBIG5@cl~!z7V3k+c@Aa_h z$^4?y(>iC*ke7R`lUH#eHtllqnY0;$C5oupJ}S@;e1`+nZS1W+>uV~U>|6+|z8CI!Ye3?QWPN>?8AyQA%)12xhQeiwf}mlRM90p# zRM$mM{JB+33t%sQNfIXTD0P(if__SCvKHvs3HU{PJ$6&zsCVr}3-KLlb~JwL2w4T7 z)GN^^F*E>}le;7}aI|u};Sw_O9cApUy%t092h^)>4nU3=A#H#!51BOI;>9npp#J3{ zjR*d==@tXjLmV_hAPC-m{e8*hYU8K@iSQ3;w*K~L8Tc-m=Z!(*Hze_;+@f^NlL-j$QGd!SH_7=zf*u=&A|8MtU#!@19H0RtPm{ zEf9I1Ul5z+US{^83Hw2d|8Ji0geLLiBOz^Po|1b?sdh1^oLv(_4B7s@86u>zk5s6wvML$zn?*9FQX}*a^D_I4WF&*Ub0D0IM!eC6j7QL z-=HvF<56D{W^0!Q0sQq3+KSH}2`{yEe;a0BS`6PV3f}W)N!r1v2t~bbrG~G``LF$s z&$Wi{(Yr54fl=d%Z>;146N;~Xp;oD0W5ARlT_q7mBpl2J%~dU> z`t3gh)!4`3wsSTcpe!x1DSsA*d#*9Gvg^(u3RlSInh@nmYQVV}Z&_h-lb$!?QMU`B}3*g)U{9 zP>*L4nr-($tk%_Gd)_+xhH#mfjpcQCW!MqNA>17sPKRi2+~XyB~?CrT{2*=SVbvkFx6o zmf~gPIafiOb%I{6#bsh&rxl$QhVzuO4Z<*)KNgwG zV5ku&Q3#3W>|cXm9a@zkydC;_TNokEP*+kT#H2qYKN3r-IyZ9fvXmsruA3|ru4Sjp zEYQiu=u%FRq6cQ`{g8-Nk@;FN6d~jDcL-Ki7&g~N7RUI3t$&ofD~VA}ZhSuNVWvc_ z6ggO{X`1Y(&5RVvn@!yK_uDDeS;03kPIWbS^Oof>%K6WEzp0s=^3K#?lAM}eMYCoK z60Y&8mP0ZF2hVY;Y8{HYZlm-+snDlcjF;3WHFb4&&P@%feXbSe4l$>;gaP`5eo2by zc}+t+BZvj#ZKOEfU-4oXf~#U!EqweRB~Lm(RX=Ey>|r&MHXXW|(DeRz+{*|j`MIXb zX;N#nX(HMME~RND#%MMd9;kV&^F1ym^s2}o<0#pExn|!B)W*&*%4}fA)zEIsd6doV zH&d&QoCk|197N9^^}*FEt>X@M9W?(|?bYgfp0Rpvc{2Q|Z;*DJ*(OnqF8xbDYelr> z^=5Je`+EIUk1Ee=wQ%|Yy*M^~(^>$Oq_;BImw~A+hLNA6VjkFp-R_hQy})NtiwtRW z8wE%dj?Y5oYVOMMJn`1sB@I_QDDXUfye6VDf}p6^E8KKIzEr=cvv?d{RI`4o_W1++ zI{JDX-0Y!OFyiYtR*H@wtY!SC^R8Ute#xQ!UOzvd$7h~wixgIdFyujgIe+qduU|ft z--NYo@9WbRs$`DmAkIIczoBdd{Rp_+Hgets&L5fA1z~vs?a(}MBA$dH2-j&Nf%hic zi(sjf658V^!y{kq{i%qaN4gtQY(lNKElo7`aUoRr#(b>|&)V7RZ$5>W`A)b>kTg;Q zZXjAx|3r5Ze8>@PYSHxyP)j4w7}?@Wlr|bGoLHvy=R7$@2!ms_{9Ra4MKMQ&RgtS7hRDUWU|-V#+t`~RzZE74Qn5a zosqa!+$fEE%D$`BSs~S}yv;%O_A+@pJvmB41d{M!!Ag6}_q<_vDm#L+BF#~m$}kAR zGhs4ph$^|j;{?2gIhDU`Y<@wvW=oa&ct0b*lh%HVLye-0p_Z;!umC}278H+^fY&CQ zs{f!CJ!GBpN#KLRkoXpy7nYtVdBhLmJ=GeH(>&JcO>W?zW9m)0wM`%}-yzV7D9vdA z9pX>|Uv0vtR(FdY`&cn<499?Ui4Eo~3 zLjG7>C$ zK18#H`2w{rN=`m67pvg>*5{nxOnak=oT7Z< zObvEnRMGG>!VtQxt=rvaYE%~*HrF0aOyP~@N`1jNr-A*0?)A8&glfmkMKqiDYK>Zx zYQ4GDo%Ya<>edKGfDoSXPlP`Lr-IE|UVK<7$|Y}^Ef}MqVB(C(0W9x zIr%|Bl&iVon|~$h*XI?$F0hCaNT+tXwXb%b=s`8mWcTpnqDd?8i$AC_xK2**kbHF{ zOesq#YkgSVM9Xl&?@U$EZ={m`WzbyAWo2e{eEz*H$D7~mC)iuZL(zxOID`K;kSF_#Umja5 z4&&Bbd~5g50sx%BRm{O!6_ZF2A<4t7Twc{Cwp(y0VfXryV2?N${&b(x39^Ws| z%n)ghOs0p#IQ5X*nDm}@B&@#9K}=!5OwV##14o}Vs=SBJ=WJ{BD0JZO@P%Q#WV=wi ztus2-KnU!+w#5Bn;jnb<%=HbI|{$ z%qBBZjl6SFBvK)79+M^@);O z3~%R#^s*wzGx*f#Dax+P6RhdY{Wx08e<-kJC#am)7x5w@5O$Mr?{_Dg`~WT02T6k3(fp5D=cv(`dFZuq^enA< zki2z1Le8tUuJp2z-TN-qzN;Yw)$@XbPwi8J8l5KiGZ_~EnEolyquTv@LG@o5_C?gv zI{9)XPEr)}8@>RtXvu9MwR02mU2%JZZw-sf_dmwH8lCBUywQgQe6ROH04Y8grygHX z2*E{e7*|M*dj?jtFDCHqsv@Q2>pjAdSj{R7D&F6r_4>pbyITgk^NO2p3A*yXboP?< zC|O4%#|n15^-)6s&{F|t1pEvXyf%|y$XTg)t*UyW?5|129A5ZXtI0%rZ{1<8KS(J_ zZYc&?*fLq5lAcu&P|OHvN(M_^17-|_Fke0reipkRA^HqHl>)Y4;K~&Ob z@&(h`wM}BF@IiMd197n1I<72Als8t8??|jPWI#2Wjh~YLpb?VcES75?Q|vHxK%@W^ z#Q)n_bXaC+BV|Y&s)v(t0IGWEX)wKaOblg1OaXJ`?W?fmq96fJKP7H0=8s{))EJx} zbdYY;_bD+j)fM;sAi=!!t-6fiz$iXg$z6JknAsM9OGC4v04Cj*hs*-p?ghQF_GsCevC zQUDijmJ=Q-Wvtd&`qmyD+DWKXPka{Cx`$+mi80{HeB+w+>$bByC{808zkaYk7*l}I zX8b&Q_#=_uWvNfD1S+UKWcqMC&rRg*Yd^9TpW0VmGclPNtC7nuLRP8iUvzkdgJo>} zc+Z1#L)_>PQ_Bi`fm*5E%~{e}p(vVYksN;`MEayDT%?-qL=*{yF_6Tutq|ju#-9I* z;-n^6F=7lhvXqMrIQR~NYnz2?#D;pe#6x4d4^*W}Su%pGLZzdijgTZJ9)zO&db$~c z1Vm-p&ZgScWbz6oLv)4{j`))FIC>sh3HZd8)uw;<_YFM^CvC+P9V+B)$?vH3dlumYi_Y1cantfzc=olJMW5hP?ko{#RUbZFsrbqw$z1`qS5okjD?@E%gTKbHZ zk77bT2mme3GWSJ%?nqS$#!AV`Y3@O8_EByk2QYmM$Mcu6Z$YjCnP_~GLxsc^gJo!j zDtDSvF1dtMDfllqBxnrs^T7Ji^HBD4Ox?a%e6iqAt_1?4d{laj;~DoV=8>E9Q) z+*N7hMIiiV79&-HX=Q`zA%o7KaOCG1Yr4$5V1%HTn1<)S#ZAKT&jjkC3_z+B!KnbV z3sG)Gc#$W$-50;6Ez&UY&@#=1wlDr1Tl{$z+Tf>*a;F+M2`P-M#!R&&!m0N3FohU} zcod7n2Hn(nz$#MG&*6?xeyeU6w+KLvwYgHs}%{LHCthbmi( zvv5hln1ah!Y>?H|&DEKQ=cj+D!TY1eAK{V^Ej9!zE(p<+T@Q$VU+u|QGwDXvSsZ;X zj8@@P_xoTn1+4ocYX@Af+Cpmu`OgtPYOs7vbrL3rG%ic{FKKM`NPS>>E=xvtYm;%V z<_^bTb|Wx8YW+fhGy-TFvFIcoua;h~CQoamF0T+?Yrua@!TW1xgb6R>O-J~De#sBq z*sjXQX_+1q7YVJz#jO-R(r60l6pQPWhHd)W>!_D*mbojz63u)&maF_gBL0Z2f?gPi zR;>NlXgC(hn_mCr#$PKg6+@i)4UGn#1Gnnd0~$kRn~Q0fw2f2}dpGT%8-Upoy0?+BT# zfw8tq=XJCZ^(?YbuWPEF3yt@kAa(opS!Rp@xVr|lhxciFySYYBL?*&%yD2sE_{&De zMB6iw>hGM}RW<5}Er{o72ZAsvU%2Y=ZcJJLhoUg1`hCW?OuM)Ihr|kpQ~Wy8@wx|0 z26kbF8sa;1{_0c?BMa`rPRqOePh&4*I)bh0_vz6ELj1Tk5D~Cd66UUgymbC;-#AY}C$ElHFoZ4S? z*DP2K%ydxg2T(hxlW$5o0HdJ?9zo2WXRNf8A2~Q!cJ`qdZdujdi}(sJBO>=OF=vmQxED zYmJd)djzZ0`aruG3l@d*U=-6(6ia8~(}&!>h?C=|ZZmQL?S~}|hnBNgN-af$19v`?;Eh2XI(6!ag}sHfF6m+AjIo}CDG69#rq(+0=^`7& zwt?7oj!En7QA2C`tl&mhjl;$l)4WvzrNaX&nc0k{ytqr@96Wbp5eyT^qHqL*aw?gp zSG{7B#cYT8bX<3$GMi>P0^C3jU*-diDoWUzgZGZkl-tqHkxVWMp+U5e_VXtv#}Y>i z0@nmH``gf0D^L0|Hy2#MYj@yb*YB3XTVNJ{#7p|vyB}Dn{gqja_?2$Oq_PFciat^bwuQ{=~#5OSu zFvR_K5XgUEEZ}4+VM4I#>;;sCx1?Y;K*fWDsY$ZWO3aqbZSlv^p*X> z&{)I``&h@CApwBGPWdt$MJz9J!>4kWhJ8y-a@*T#LuY7Duj%~nMpG8s$*xqF+3+eF zbSnkxn*XE%o&I2Z=1lTijZ_gH1`xduW5g8lu@LD3z>@$s11 zULe6-Jlugc;jXIIJsfDwU}>7|v1@?pbPaxTH~Qyb!lm|Qb-tq8&3u;{#p{VN@yR;< zNj$?N_}NX#^PW2$fGrEDf9pXja;F|~!LNB9^Uc#W{V~1gLJ#{PQ(=P>+n`S1p>x}1 z{!U1~asLjYl?`U~_64`6dpY&G(oJ0c5HxcsyNEme`}OB*KbPebvVj|L zGuiWSUGQDlUx!D*J9o$D!Ow3j!7sS}Wlzv|vdx2QjGfCU59T)KbUj~+?zL>kwd>!X zU=Nq>UEiLFBPsCPDzSHRn*ZkSEnLNi)LA2D&-eYOkG*wJPTl)BjTiP<69yU?jBrsvyF0x%_i51E}-EPm9?E@HqiBXyx6d<`rX$D zh0aWQ^{0L)96LyR>ddQnBtA)01slGx#60br?7-CzHQKYeBFU7I^XBq}VmSiVv5QyE ztIznV7qmJ-wPwBEaF)C;ux7KhZ+oJ~_MQ2zyWt&dkMh@h&j1!)_4n``pUDVT%eR@a zHCD;E6b4N$YlCC&dlp{|rfV?&`@*#=v3zA&%hmhq^Ot)2iQb?8&atr_yEDVEod%&{ z19eA;+O}M#q-p02gW;+5-4To)l zB})5A;=#No=ArfwC&10XFZg3XM_cGs>=38j8}6qVMyZ)W07_LvqDqy zXsIGE!d$65Sz42)DNFXs4vW~b^Ad_2XOU%!TsMTZLro}@sDdOL^wm9G)Rp6sR6o24 z95)RVE`5k6%iEoWE@FErsN-Ip6cm(bz0j~5apN#G;vp+{ME;9HkCgDg+vzxQaN3~5IN0Qve3|bxN`)I@^sTjc z&NjK*kQtVVCLOQYj__b<+mKd)sH#+dvJ1kr1qfT*?gWd~+>&vwr?tjpaSOSuC4?V; zz}YJ^sZ0ZExt;a)TSW+a3Dw~(1l6$MaO{xg8Fkx&OP}h?+U{3AT%fkjsIkeA3Z=E@ z#D(lZ>4CEh%QPp+mB|2ac4t$?4p>uns2!TOHpp)RA+Y1Da+9)aob}YfHS(4IciIwN z$^NqTCINZ+h)F18DW143`#KcCyW$L-bt0u}Y8YXsJ`%X~X#tNa=f|UmXJ*Gty>LtG6IHgd*}F9U+YF={LX-6u z_dCpDi4?1fEr^oK$%i{1ZKd5w@YaA@0DFjXET}VvTL%@6JbZcsj={r$J{@Fx6S(bb z#$!RKm@CFe&pbRXE)s+s(}blaVN@2usUArCU(;44zc;WYcJ9YjHLhiA*(+N|Qx+h9 zK#nKIDMtjG8vR8uZ5c#rNEz8G_#@IGrkckV>a(3jEzCBjHI72JNCaT7XEBhEQWYUGev`s31HVhXTQUEa@Ydg~o<<;ZeaJuADIxdL6x{R3 zWQ)y_UH?i_Q(Z<$&C1~$=SDkL)XHGUJV!cDHsu!GF<;mt364)IhEhpEP)X$anozNI z2u}tau(^N%gv9R1qE1Y^ExRf0x#4Sh^36cOgco6bq$x#JYAg)h>%b8Nsif)1fVl4C zy%^62+Lzhir8%WK8ZHoEqevIK(e&B$%enI(lp-7ybCd~+wf&c8T2z2F(=>N?aMQmeI7@|7aEUEJ7aO~AZMfg=jx~`5*4a@q1N?ICwkmHn( zDdDGWG-{=gt-p%lTM4x$?jr_WQp)@64r9@C&Kynz)LCs;TciV{t}N^bqH(^QV*Cm?w+}IW zPuZK0fJD!$_l>dDQ!(Hf#Pgs*I5jD_I*{nT(oEYSq`?^nGKl zi*33}c_@bMI75%O(}a;m6Ig$`yE;9sT0el{x9Pv8o>i&>X=!dzpFY|HnfF@ z1#VLwe_09-L(a|iaK{ldLWtcH%twH^`*4^}o?Tj}9y#v)&cAPPvj0kx0JBt4KFM67 zv@W`FsdU+U+a0>KFi($gk69xD@cwok{p;bXru$(b`OGtjFj^&DY_oi(qWY5@#Dmyy zd9#XuHoWuB^Vy$Prg_NT1t@Y%81p*?i@r9sv>xh9mRq2Bu5qFMzD%mfxIs7lC8cxZ zJ)0!2+PU`q%mPPU5v=4^qPn@e1Oz)Rs0*CfQ~at;MzMF5@6?RQB(U;&&0(m~2BJ5y zRCzvea9eiX`k204#Y=uy#BYdfY3p9DMfqD~uD|cYc0bhyz8zrDzp<`-*us!>spr3T zzwX%t3jO@G-tnF=Ue|p@J^CX zWQh9KWSaOS@w@G~DdA14ZtHyKiC_Bu?^2%o!Tz)T&Tf~4H#a&JbCl5c5I|yiNSCl(4ly^LZG@ z1!BLZn-KkE!__-}R~a1v!T|BpgWsbRJV%P9U>kJn{5{?uS?i9c@ZjY<$E+B9H5n&>O(A5uxBF9)-L>02TbI$#(pF$J5*!DCDAxViBJ z*)o+20h7lL#|$c947F4^&47wDFJniuJ(-2gWc^d7Y3x1eS5>aOeawDjSs5hyi4v7y6``A5p&LW? zfRdXR(zcHRm*K*jDl7*|$sTeA3j(Q|5*OA*MZRUUliZza z{co=a2(93f*J(=ao8#=iV)LxUn+@Al+l2}@TI$heir^!uFp}9=5|Yv*(hZ9HGb%$3 z&=1bi>+KQ?AG3I}5=-wL3_9wieWIQ5W6mNYyh^hK%2M-I>J6h4EEH;mladpx9AZc^ zz!o_~@a7B;HKz25myFJgeCR-@Oqtfe}YE)%ga!5tvfd`cN4wF;)Tdi*d) z0Yh5+o2GW=rx1}SW4bFm{V;X}Qv1eZ+j^A8F3^M^fb0x(1F$ut?r2n$&y2xkVbQ=q z75s>av)C-EV|kN*`{med8#*T^{b8hm7t~8hq??qAN%$2>0akGvv|DjBs22tf6Ik1i z#Aycy)IdORJRiS%ukYx+79 z`Z$>#ZI27pWLheKkgE)BNfar2g-w&kW%k{WiV#C2q;1j8f3SCs&2>QS8s14`TWxGR zY1r7-j%{0wo9sA^ZQHhOJ85h?>F%4EGv^1Kne*}euztg{?zNulzK(6R=?fIhIGB<| z?9Vm5D}u~PF72!dA?fqU&Vuu7D)lOYy>~2F={vdTqZP6%oULnPPEt@(y5lYCo-F*+ z@q1w^d&lV;aTrjxE&Z4()sihNc&Jsjat1-RxziP`%cWr2GG}XsU|w1&zvYqcicyqu z#bElrbqk)O(mXBt1YQ%PAk)ENhR$)KDUAy)y3;W-0)P~m1>3Q95W}qCR$6T*2sV{9!10Fb^y0Y1z30Suo95DW1D0e}Mo{=t8fs?F{D1&2weJ6fAJ5Q;|r z87AxUha&OVtv5&O3PygDh(>&qt}h%*piwQ?9jh;zNMSJ<&y;Q`p33;{a<)0vP%@Jv z^b3Jdrm=LcKq`SwZ@jT=p+u=bCQGKNe5pdS!Fp@Fsbb|bz()|uHdn4SST2<7O*B_+ zv^egMXUVoyZ*_RypKVRF)a>*Gd_g3VYpwk=5Qa&wKiOKhKN3qWn=RK?e>joKZnHhv z)^I$ND;h~8-`;q-P^wy?Kh@rJzEW#4kuBfRe7Vu;a=ty)(Q>`h`wNj+p|kaNe>8#K zV7jyI{&@DkW=Kyq=QqvNU_(BLt zXJ)NwPCi(GYgPg1+dPDQk^8i+X_LjVi6aYmnsxmmF9KfUKDKj$v46hg9ze-;6rcPh z*~yL;M$PoNMannb2HEcBCO^Kb4-G(DcPFB*=b;3EwJp{twZ%u)2 z#4sV=u8jd5z^-r*KDd`-)gJtMqo4CP;3D!`;N>=Q;v->wg}Hj`>_=%&>wwPAzv85( zYjn$iKak&uKSG7<+^YQ{+jSu*ZXoccCLyZ$a(AfU09fCQkmT9sM%pcU0dsZ`C@%nb zV?Y4?Ngn3TDB6lEh#xryIRu*|5YasV3UEV;3GT;(%d33~BubnK2GaLJQp7@_=NiIF zwu%q{NWwJ@MZ@4&*HPvHJr#^~;WW7rXz8&b948QfQZ&%_+EnpCdE}sf;prG@rJ}54 zq==@i17rZSNQ=>5(RK$ZTg9P+`O>+Nben(hZfQcrl^_8(kFsACj~1UMcM3ndX+*hN{X3PDn+5&hv;=6O78wJX(OeY z(y^!k<6YY;V$F~$_W?lQf`_7zg7|F*p8diu{3k*eu>18Y^+@z834*|JE-S*Nm+O4> z7x_0AB&*ghB*X9#njd>P5aJfRX@?!GhN?~gyka;!ZzzW#mt;_^7`2iSqLHCm?iYa~ zM3hja6!?ZD-l#s5Gp`v_nZ>wVA#tV%2!PXva)A;e5MV=BM@beBb-#IvUKlXR@foZ! zr-EtUq}2-gIg0;`r!zwoe*YFU2R+1aDlj$+s$dc+Dz2qri||&%-;>i>p`c&JS|7}m zNh+sa`a-kG}YZM zRD#@OfzufaV`kiho|rr95#prlRA@r1$sukk5UKz8G-76+bb;a>uucyEqIeiG`aN#U zw?V4qdOHfiepZB+AuWB#`w@mF_6TB0E zwPMO*`QC&7zfFn4F^80It;c%`R*0;b)f`I??7uL{Q7GLjl_l^ZY*&AoI#Y zQb^dry~D_=q{))43ZD4o_F~4mJ};&Nc<#;G-Q*ELywHc}>~bqh7^il$OTfIyV;3U$ zB~z7glTUT;m=O#;{#O4JIH8oJE zf!D2n?l|8*XjTvDEpeS-B4(Qtv-&hJ9G|avY*+a46*=HL4YT_zb(IUDs)YkX1l``O zU~PY`Xb1p(wk#0A!AR&Sx}}c(><4tYW_}4HKzv7cfv2AVkCwKQJ_$#5DAY1_$Lc8G z<-6h`zEZhEdbpf%YeA;kp)Gq49c%AE1BL>SUSLefX~eqjAf}r<)`KkN4)o7Gtjom% z;>_I`VE`X)$WjuhJeu;=cMw@rUe)%_2M> z&%fwi^t(&kQ!RoKR`e2<9c4-bL{~^Hq*T>b=`_NmB5^F#+X|kVZgn6Ax8#=ztXo^`s=2$`Z%TVMjVh0IJ0=`Be1gJ2v*vpdTmZZiAMW9*%ao za(YMZj>ibG6d=S>-56j9g`rKcl+d?Omn2WcU5>DH>d@-L@WwD(mf^5c6@XO&XmUPO zWHcyo)I~~Q@QT@%nGbSUz=wL|_=}tA zQB=F6^+HKRa%rHhgu|T$!l&)o9Z%<$h7JYo`vab@O{-rir&~X+(ejB4R=*@F5NRyM z!2?MDHX`&jC6>Wb@kBE^15akBD0+0*bDM?^pC#mvCjBgpVj`TwGZa|2s#Pxl%^wLC zK=mAc;~AfI2jT4*D~~G|@npVutOQf?dqdn|kTRayB6Q3>u9}RSfR?G`*q&h(3hC94 z!z!9JOag`4V?)Y=AD@<)THu7ny*pR@o8a;;x-sk$wrq4$=ead?Ur zlD<;2mTS$w$&XzCyMZ~W&UH)CoFF=sT~i@1KZ8P%XBu^6iRb|fi zrg(@S(w}d3{&WYx6&UAK&omDzctEcs4=vIVTF}^c%cg$zd~jPF8K-fX2UIVPkI++4 zI$TyJ9-?MTbWK}o2_(Dum_5)kkY^=`fChHpId%?T?a4jLJv^&AK1J0^el#t)XC$^C zFBC8vSnBAERpt|!YF?rOh_T9~ZI!3lwGn)ZJFtod+_-OZrJPyCqshd=yk`60Dt5y; zNT$VXNQfJ>=H#yCkbD0Wz{|CGb>&f)yXAB^~PAvJ5I}%(=7a!oCQeWn-6tQYW3FDNcd^$!&sb2T}k!UK4~;7dj*cF?hDVM%`0;(&m{kWQ?3j81DH z04*L-rbr4@+6<($S}*6cFq0!F!+-G$f(C$Aym-T-%IWB!Yd?dxeH^WpU2B9%tnr3f zWd=aqd0C;ukL88jFO3sVOtcE8NwyYzhz&u|9}6|TSDc$TSxI~H}K z@uv$4z{3A|o$gw)SJ|!Y^2NG_uic&L8Jvs|kv;N~NU=eS=om!(lexqq@I@*bFza$| zty!H`K6OlYr(M^!5Xn=XEMZlxnwXfeQUhoU2=}J@a?T59{#&;^q!b?T3?C^ZndQ-5 z(TTtd2TFd@u7-sLJW(1E*_2}NHG0c5Oa4@?^$zR>YFYC+fMVf=&uYSGo5kw^4P`85 z;^}u~-uKXeG+4M#FHVbhc^`i5Z8;CTS%#5o4e}8*K*)mSCBMe}7Z|#wRHez8o}VvS zwK%ygqobAdw5CoHB|ef4VBN@NmSIcd_T1QB-D)lST9=96G0EF{UtR>t2;p~w1~^s^ zG$&S5c_6$r^RCkQNwjIICgp;yjR8V#`mJdYuK_f)-=S_T~4iQnT*2mk5UD-06wn%Z{ji*$y*$QHkbMLpb>^hvTs5SQMMSbkB zy4xiYrq)i%XKQTu5Vc5doL_#h8BGEll?SKZB5$2|40OA#Y}bn(IS8JT0Dz=)5iMl-5UrUO>A9AoqaXdNqj?AucdnGE!Cl~$ju_{;<~Lg z5oVdoevha1u>6IqhV;~%-@jh?&#is|%d~Kki&>Y!0l>T?5qcXPeX|JfxKdpY7bI2fBE?2`S{Ssu&kKy)zifB3n<0Sf)H$wE?k>_1eJ+DT@fYUEx%Va2;c|ZXoTdMKyB`b>A-f zGv{Oe<(zS8fZjNPUJ=5n4V@}>fKsRBA!HX@UeR9#0NQ*l z-C{Rb?W10+jZ97}I}Xs`vJUP(Hy# zIzoQL+8tJ=eRhR?ZlZ%zArz6X?8S};-C^QQDoPPVFf`_L{hvEvXl2>-f zmi!#M`c={YPNQ z7zFL^!&X%Yx$Ogbq61^5Blno2h%1z+?UN`4lH6Hqo9x3v>K=bZHSO}F5k=*-PE(St}$>N2a9M9A@c(w@sp7OVS>aHo#Hja^~w-`HTZ&wfU|6cO$A+SVV zlMIfcy6@Y&uizB#gsxwyuU`X+-|}a^6&ZYF3B7=RKQQ@U+g_d7fM=e5J2>aev2c7j+zcw>Urmi&0$%Y2Z$mPPggrtDVEO?wA0R0E>_s9 zj2}qnBy4ll>uQ%hR=CjgxEmiAjnvp(4}{(z;vYrJ?+iNZ+RZk|WVRKtPgfhA#|R@i2LJq`gThgnqKXcZzk~>-8@F3gW#)dd zZa)okro_=P9;+5X%yI_AiWi@Waa6U_U3Wf=V#qc%>f0zbzgp{*c6Yq|Vx<5W7dny8 zgwap80#Icc>eBrNrG>Nb5*cW6!^#!<5mQVt0uTVKh3GQncd?CV^5iy6_0uwOj4x=$ zaoz7%6?Jkk<{S+Y()NSOl7zp%#tI#w$boBFPYMTI*eDWg>lH7=b_Zg`I}vNf=f$9G z#2yt)XS!bYzPJd64-Z~8b1aD6&;MQ4ZJ7G@g(FIGFx6&ydL$xKVTK~nL)QfZlZ~43(|!PhuSSpGk#Rez0l5NG(_n$#VtgIxAP+Ip3)ZG+EqwI{3*v*>=+ZO!kv!+|JBz z#%GNgUN?4s$84?hRx1lRa$$rAAKbvsHs&+$t_c6T_`4NA%<{{=7A?9%#jU+O2T)Jz zC+;@$K60|k8{MNN+52FOR)O!tGA=v1acjXEw})EHB2*P}`skRa2*#ehoif2Dw3MZ2 zj1zRypy-{{z2@Aa`{ARq7vi2aQ#ixYrMMBNwU=|pEcn=VTHs9sQUK>vSz+i*_ogl< z%-vHRp8BA}0Z2(g9vx{$xVTxyC*dmAAY|a$1^b}SzMk@>pb0DsVqVg5 zH7L)0sCd@XWY31Xr|pX%IMdQ=%tiV`nVNe8B)*Bvy5q42t7GwGm!(;VMc9z15Ij~( zjfuz5Y(yWN&Mr!3FymLQPnPv5(jDuW&@?N3>?|y3wjQ zO@5ZSS+QaJCJ+sKudyv@jYG~F!U}j6(+z2M6sI}Gwex*z?-n6rY(o!sU^tFoN|Oxi zM;ELOmybBeBQ|Ao%|azCLNu=ZMVPCS#1m||`_da~&7|V1kx!sS;)P@Hy+G1cZYpE* zMMS?^O<$*i!d9U@xe=};k1NDd;U5hI)?jbejaa`pvR8*gW?gznJ;^mj5mSX-ct#C6 zkqqflpFvq-W_#k!K46^)s|n=P(^lr-%B#T)Mux`lrA7-P7=}&0M)9VxW~jm!YcH;_ z)-YN07CQn@>3h`{B|}xRG_^MccTZGMla=v#V`}uoF4bvH2Ea+}LgLQm!U~z$NI(M@ zTZiWzRThrPGtRm(;Z5VUCW_P&PQH&QH>JXbRB6xm6O_ahF-I*;n7EzyLy9FsS^Tx) zFNz%?+zxEmJ1Lc9)C88>HAGu_S*s~}EA1IQCdfW~X1EQ?;VGPU`Ke1kcfM^1ja=gB4QBD}3$zLq~9G+J`%|%NfxzlW;d9 zf;&;O^&xPS0#T)KTCvs7#kH*XrrkBGUmBBu%b^5Z?!abfFtrUZV=s+Nxf#Y>H%L5! z2wdk`M1c&33wGOVfoAB1z)G$Z+p?OQEG9+X32+EKltl@h{nAKGK&HADYJgd?I!=F; z>)!UY6Oa1ILqipqDtxdzbhSDutrHMD^B#zxi`M{5J&sGrG(}AiWdLQExfJP|V5EdK z&iNVu|M4u~SVT`yN!(Z#G33wdvX4XyxD?VZo4}_6p-u_!*x6-4JoNZ&&f;gzc14TY zT#p_*0=0g!Kg~o%9|u_l-SVYPf$!Va$%K7%;uKvr5z?Oh*x9ybD;*k9WNiyX;2Yih zG6}(j_L&QBrhLSUvjO|r#^NM*eKX`E*YLJEjsAu;yh$79Uljn9SRt5iXr?(}Ud$-1 zQBt2nJvCnPpWVdT?v_?jJK_+`8~LA>=gb`kvOi^`5aXD=gqV-d%D zcg*%b-#ktNbTCHB7~5>FJ{+}{{P@taEmzLrT&C*F<9n=-&pM-WCZ1H3ze#wuFKdQr zv816{aX8@}5s>W2K_D@L{|;Cc_-h3kXmu+1>}et7wywWrXeyTUn%OvbPWdaZll-oA zb7oa;iKyUG`-|^_^q-^hlwWZpQ2+7-h7DoUCs>WF@5;UBeeSl~mH!x=ko~8D! z9&GV@+}gIVl$@3t5Z^9MNXb5Q+o2|wH+Y|~5|6Jwf&Uc!$DK(prB(-YDaS)lhsRmq(^tX$pFQmf zJ*~!qF?j+dqA4{CKN#{@ha!2kzY4Eb@SwkQb<+!F%5tIbBY@5`VH$;D836E7+!3%cz8ORA+ph^hv_5cg@BmMUsL-@C9M8$O zhK@d#N;LQhVG@%TJQ6_?+aJ#b9UTk3FMJ}eWCF!HOzrJIXcpP&Tt)aR`)$%G`3rtr zi!v3wf#ifjm?p#U(FepkW48I2$KDvR!a&lUAY!$Fy@B3>a}M%vV!|JJN&_N%PrpV6RP zu)JWjXjS%w*h)DjvV;$gg%?VZsEtILj&LmwMn?^FnhZUpjd(wNewvKUn~bYR^;tI# zqePC0aYSrZj5$t9XKs$UUPwm{j&f1-lU;m2yl>6#EMc&fQ8tnBhZ1*Tl`T!|@n+BP zm6i(q_7Y=~hHsJ(v;r@JtDg+F5nzrsQb}2|H^dL>mB}&arj!Sb)ogB<8 za>$v8c8_Bz-8K#@F34Y-N<(cFn|u! z6y6x7GO;GBA7lnh|iMEzh=7ZhF^Wki;V z!BoB1W!M)}y~6-WhNyA7sZ!{vyjQ9oeN&D&kTG}|qS;bi8WaNvOp=-^*B_|V@h<`7 z1qYGTJTe=i$yK{=CMA}t4A5Y7nwBKZRQ0mdzDKK~Wi80sDg0$ncD-0Mz!Fg^nnu!< z2Tt{hb}6tjE(t;_aeweDhb(D!RcRG1g|-J70Dz8N>QadM|D|fjgL%KzL5Fs zPALI-4MAiXc~^})A5j%?!5`qRW#u6u@hMdMOAQr~K;0MUU(mo{}9HG(BI8Pj}QgKLdc~=Yh}ej` z=TSK4-I~;Rr=;b4=Odal`I`n8vDQyFRpYUTa|V~q<(2mlMdUg`(W6aHqHBp+*$AW1 z0K>IP_0`m&CEcVoJ%N=B=sDLZEuJpLY+scs291-=@hIQvd`7tfGwn9Bb+=BbfGYIB zBJH&?=x(dE9s$i{`PE;C>yk-Y&H^jxlsZ&ZikSymM~gFj$l3~e1G0(0yrIp>CEYyn zDGm;;+o1I>bpqPlJaEO|jRnfhl*9GZEG@mFHL|};tI*4aLu!rDqsDqV^1jpH#f6Ec z%@%dhNTls?-}RY8RCZ*Qxhy<_dguj#txEYV$1Uw;#uYu^tpvOuzWTk;n|)?_PIg=8;wz}W3l~+2ms@L8 z@;aM@a@CWn*_oTr63Zrp22fnfCzGp|EZe5eJI_-wtcLnW&;dtw+ucL+gATp9~fBP8-WRws+02q6LLo_8lofH0P|!?bYmQn`J|IUi zT1?W%DBd1OGX4H!P?xl2czDQDZ7kny0$D7Tg(SxhHWhf%QeiSv6*N;*Ku}-K=sMkn z_BMMH*WGw6o6x2O;+sg=9kO40>P+FjK9vcuTZ}=MJAe)DO?3tm=BT5{~ttMD% z#CL(qjy{1P2@R7zy)EaaL(q_!4AF5AlVv#Pv7E#)m7W5KN!|=(kvgVi!G$n>lC2IA zu!6q_yxI^~Fwr8?)dsEoZPNeFHLpu`vf8vSW2QUorh8-xOaULJ4x9D_pM)i!gj1RF zNRbrOp@=S90*_99OBhpV>`CIR1XSyzbZsZNTkC=LC=bo(noZN54pSs7jm+j}hpg~# z|2!XN=g(zCRU=t5?fWcSNHAI3;he_V?{T_q1%eHnVyq7H^O&~CK!?BeO-|iRTBxk36?97_PW84AgB2{x>`$kQKUaZmz`e^> z<#l4^HeIDLg_zUne$N1?v?Z|QJ| za>E$U9(smxKbr_W7m6zma)b=0Bl9K69%agf_ znYAv;#^$meM9Lym%I#Og{ceaLRsh}%Ab7;s0hD&rmyJY%YqO$k_qudq&9#?oXS=Fl z(yXti-MvKBa*3H@njYdz`|QFnd^C21`+bq@yFkR9G%4cp=Q9}DH&GEzTCI6jI>J}{3=jI?K8(B3cM z6954KD1YFvAh3kiuelS#aMO-sS6Qtx}N0of(G`_a z+)cfISpR);#Za^k;M(-T1!Z16?*ej*90Q2Zu!2fngP31zfiIaU!9=T7yvlewD@0@v zck|7juhkEnsO}yP7oOB-=-eI-`1xPw}P#J|)4LR_!1P zyN7*xL56Bxhq+(ufG==6jLf+>BxT7G4ynH=-kf_fz#KJ5QPlggKyPx;Gx^B%S@Hb` z&S~k4K{B@wXdO=U<*k<3v##g$*UA$WNex5o2g&WQkJ zxiDw$27X5~KNqO2jcYub{oYP-ZygUkE?~p_Az?8XEN@7J0RS{Gd*f*AKxibDN@Z*5 zTs|rSxAVoxC%G6)Cg~1kc2_8^N+Vxpx^`D2`-vdVK?yA^$*X;LJ97*AzWTh(g=RWUx<>NMJWc$k&eGT-d>hhjcYC>e`IVt-|KnC`@M z9i!G!$l*_8n>bH}n7o+uvQW$x8G||YkXixG_a&g_vM0gCl8Sa-b0(>tSn#fCT@RF9rpOAF3<~$E> zuai7aQUYvkWy<`py>J!rA>+szr^o!LC*Q-bzj;5&#TY9T*Ih9IB%Tr%Af6Qk4Z2A2 zLHwGL$Clfs5e{V@u?A0eQeku|ku`O3>R!ra2!1ipbY7A;8m?`Osw4}8o5V1(P?)+b zS}$%aYmP9@)NHZsEk0L%T`IBJGt;{9a?;S9OOEWsKa>$gR#9B*=Rad%=qn?&^U4^y9`A-O zbZ5`b=gJnfGs?@o5E%5p?pP{tfL!@{4*JADGCb97$1$T7$z`lfMIFlW2M%|KP(RrEs)EQY`*-FSYX?-*e_G~CrX`z&#}pqO7L>ceU)Pb+ zcex5rrXy9s(E&5qCe*$}QBO)Y4`zj6(S5_r+YLu#Rcg(qCksE*?|h07|JP2Yt}!*i zu_gC=^I^e7qN;w89+-6|4I%qf;s&R>{J65acY(hqbm?Q`MZzTGG-TVs_b^Aa()Bv8 za7=hWn5NXveY&i0{+#v2B7>qIUgroSA#G0PTq#t-kIOru!fvlXv?hh)xGyy7`WQh% z!249~e(P?j-1sZlrNtL=-D0mk^sk+lSMng8`^J4@*{8iLe&e3beJ zXRzPD_s;B!oWaLn{T{6MTsB5OfhRPx=Bp$Suwy;zg(IZqjx|;_Th_q1P#pdsx;p6k z{Of&na>-IvwHsSPcFw~yqoeqkQ`CDbz)n&~{4hp2iooEDNh!mguo8vPr-fuaW#Az0 z$^qXsyZ2(H{RcWapG2q9g}nw%9Z$7rG~(2yBWxoiBqI(>A}Nl5TX*0h8U;7j7Suqg zD1yGDnYpb#8u5Yf$m;#1YLWC$jbV8NLH8*^i zv)@^VK5*+RePXN5w6bjnL&{V8$cRxqW+EpY5n8NtxG9}#+66rYeirSNIKfz4MFd&} z%1ZfwSwjx)-VuhCWOLRf22B<`Z{E7^R$YK8$dM?{snoulJ&qE;VV=#=*G z95{ZPV{v-8ZN7-^A#;}ZNVb5tFHEK}O@q^P=fFcEYwuM1RTrM3>ApRW?!x>lK^ap| zno4oKo0bA)5)?t8T${vWo*HE#Uz(Pxx^{GuuKd=0V^U9U`Xw7cciJgsjh!5 z-#~^EzHQ#E+D-mQ?|yxKir_LI=jE%mVxz{jP!YUB70N_qw71F4)kqcb#(59ol*OA zYP@qkcGIc+C9V9deQm1&^f_3GUSw%Jj&dfN$>$MrZIrhJc6Er!t|mio(!4nlJkhvI ztDMHaTR-2>zh%`6xiLp6FIAcr%Jb zkj&_G+NRU{ouydn&HlQ3jXZB%i4AhTe%<6`IZVFUyh9}u2E|uo>Sv*w9WUX zF-vy_#6@0cjp#O4+WlluXJcj`#1IAlwkOEuR*5@z z)*kU|zREG6J5%;g%@-xwiu3(CwX2^vrP+b=ce`>%E2D6^rMuZ_b`kyXq-`O)+7{H8 zsTYRxzK8`C5VmByXGyTS^m1^LK`mxwyTHeMKb0cVnVT7T^K+)>$;A(Xamg8zV68+$ zs~uCz@QCN@5UMptC#A`y_eur`^L9>*Ys=2z_;~>Wwkg#~$F@@ivh~(=C-!ISnb&sN zAAv0zSV|732%s+0TQS`5`<9Kv{p)@n5x2QXyUw?KzFv^r%Uv>ZPtn)1KaXGSb`{th z(p9_qU&hKGlsv3!KR1eyu6wYZp&yvG;Dz@Pmf|4^37mxcL0>*#Vul z^t5ccF(ZDA6f(7WY=z&bWv>-Wan&tgZidrOblu)1Ar8B2Usk#NogskXnQhZ?N5pyS z(73!{N8Neili#{6#$8*woW6|m@E!q-)STP!*`}7Ore&;D>BG;o1XHIcmhgJrB9bczk{OLKK@Gx}3JjzHgiGy?R~Y z<2Ua`+*%FX$$PBv@7o62yl?gRpCXc_Mi~0uRju%)Jf;F53B>X?cHC0dF0&zvx5lGa z8V`uU;v{3E;#+0eMo~NevUYlWzty_o0ctVRLh#n%iO9ftgeRPaRI52q)w{x*UyXD# zZ(oUDUv(}xiP?N}LkV;J)7I^IRQXA@$4f~NQAn!^B?C2gZz&B%+Vo#pNppQkE~7L3 z;nNMh#1|hbePjL%#ay>8A?+vGc;~`2)3)5D`nTyn--?Nlw8j2yZ6S5*r@m%V2$7Q9 zt-M8N#?E1V5Mof8=8-WOW6>tv}<-sp?XEZ1TqPnB^S}(eRH>hznPwH>IA7paNtiSJSZ zzf5jOR7crO=KL^F z{{_tctH!gYJ}V0@hNkllW@%*8DXo1fm)svQEQfHi~vKY-bE z1#|n~?RREbi2aS;URvx)2WYsALdPCt;y0Ea^hge;UhGgW)?xhX4yQgsi-)>?m|&l| zQ3P2-HgTFCNd4pX&6O;WEI%mj2k{m+XQwbHavMbW6)W#%;GhL=C&V^v9tJ3>wFxPKmT|fLW&gNTF@Bqc^V>I?0gzT{;0j%z)fM{-au0OiF`EH))?fGtRj{NxV zPTvGc?w>Vkp;KoEY(B1+N-Exet#K>*KL63^g?`=*jSN6JxVIC6Rvfn#LO9K_0{{qD z(S}l}Vme>$Ex45+-~cQl1O({ONVM{fw*~1C1Ob6qCjfvI$zTN1_tMV37=Hv)LwHj| zC^|p@YL?WnkjmkAaziw0xEBKiTb5v&FbF`rqc{YuXKZ4gq7c3sDPkH4h!2p93GSpB zfeXZ8`BKOzq{J?SAY9u|$`Xj^y!!>9q#RYkSZJ8=Wp*BiCJ?1i2AbNf;G#dsPCSdoo*?lzD z4;dw#`tfFHT5GR0R?XKqJA8 z*Vb@C3K4oGN{OjQB1L5zh@`9wMJWdnU^0ZqB*VidtuhZ|R477v4G5FCC`}!i zG+`S$2l)PPI~ zWtt&5-IR)xVYJ;Ilu{K~qo{u7jo6vk@&)`gj#KHTB$a=f4~b=k6nH$|is=~n=mSwh zQZCl{3Zqi_3*MPB@5}Rg{fPxGrZmJc>F`Q4*dgM4NxzMV6#KI(t!S(f- z$_M4N-EYzeY{HPjbo6zFq04^wLh%|~G?=AI(|oqrSuTuJTxYCessRDN?+|t#ir-iq zjSb_OVj(zq=;YZNNmEISD)>R3p)j#a=IiCX(6lzZ|DZV=Sj55ohHXNi-{Va=D~=;* z(E)+9dG6X6npKKv!}AEO90h?f;UFG|K7F| zeB#u6^sobG4Ha`;5{2QE_V^|jo;&(7hq!Bja?G#4 zqc9}9v0{x&fVXzmsuEorcr;!Gs?o++@RqfdGWvvN)Br#h5(;3Sre`i4Fj#pA4DQR& z%{OoA3;^8F^L#^!hXlAueQt=$$}xP&bZ@n4vh}lzofXDdF2FysrnbC5vfcdx5iA7( zp!)Q^ks881%BNuHO)N)Yc)>AA)O*DtAfVjmkKK*xfxkpG4I7i@x(nG?Ik8r(IXNdI zPBPYjXzLEds7%^z^x4BxACDL!&ujdV%2T#@S1bKAP+5=ISzVNoAX9n)Y=#X)k!}N6 z)HSV4*k>tvaytKx?%Ncg0Y&iNprIOt7c!cYAb1HaHnZv>BdXa`8Tln=#v$gTg)Z=I z_+wBwgvzMtlhKh0nVR&QC)$%Q;}&i#g)&wA1~iAU?i#$w+joiB1J~A_xgw_sq5J2P z^SoDGdV!2$SsI0!Fn+aFdKm_Km`x2ig!fcnBWbI6nIN>gY>yg=<5O2`?w!$)qv|>N z%T_^d%)vtO=#IRN#1*CP?Fahr(X#fx@8AC5$2dvNj7I*b7}#Uju$jGWGW$plh)?P) z5BYszBXxeC1PBvffJb_y>yRmDlwe9SODKUT^Tssx`p^@WF=O z#^Pq&V}rO~aOXAdy3#yDze*ob?5pjs$JZF%Ip;DUFvguj#yA~??8X2NXb!sYBjLCA z-7svZFY@wQidLAq7im9y`B(^B8XgfP+L=|jswQMIcO!R;sMCD>Zu&#SxNqSpIJvXi zEyvr6>~)2pxgY>@2YGmJOG8VnyWRm@b>chYgXsCh$q9d}3%GH)pH8hF^@g^;?BJP0 z>^bvn3vj(p?wQQ^=Q5OFN3U)3p|1n7xuw>Orh@6Ruyb!T?E@lFH2F0d18zbb$AI3E zTnba67M!?Fl8>SbK$BG#?GGS5Jk%%U`B)o83I`KXw~?|I1Kt* zWI+?EkjnzBNLDrXpj)F31aH9lcNA*DQ|i-<>Mpv&#=-!k#7V1#Jmzi!O;6}{_JPdY z(wM_xN!*%QOKxNu;m5~;iXKj=tKom04ZJM9*`A&3XvF)tOpK}Ze!!u3#XE_@88I$7 z6+F4ewt53@bZaQW_$+*(f%FU>A|k+GyAg*@+;DHls3aOg-W0!z)u?J?ko%~|I53cV z6$B9S3kXACY>m7!rFWzG#XjmZA?f9Fq6LNquu{Q~d4?lNh4mcLUBX7Ic_`<;h$p6m zEP4Khqp?aUjR>Kk8^AR6!;MI6`8mC!OYEu^7J|_B-En)B4qc07cM(8H67}J3H1s08 zRQTI0Ia2W@HkAwZPnj55bct?rQL-(I;?3OJ~S9pV7&fr#_@^X^z|)11C(LOq zJeY?aEb0oRiGYLmBPvR+TnQG$Pb(!$TxCzz<@!y1>Z4s23>x|6yc$!z=gEE={_WW# z_0_!*8c>n)`{F1bd}3F%=U?|4fgCHc<>6gGquK)n(1mkc#|w39k}nP7F}x4I*EH1$ zwLmKj>IVnw6OP6FpiZk3P2#eOwOMsoqYZxzi>!Fc?4}Kw=gGpD6(>wdk%Sd?8p*8A zb@8W4O(pXtr^yhP$}XUh2MBqRwPfhyr_#a3XD)DWS|s6&+FW9X`^09OEyo(*+mt}^ z;9`1VTKyvC&T|6FC*Gt8z-4`1&t1?;XT{A^1^^y`c`;*gSS5M+Wa*^R=@8>#hQl%} z>!JE7(a7)`mtJ&Wo^&;6|Kc_qKx~p9o-I{-jyP}Xw{{h)Ft=GXFF-&#@lbN$$$!{8 z$L6}ib^-6$PJ@PR*toH68;xz-w(T@cW81cN>}1Ebo!yf;^L{x0;Cy-i!85bwnOXN* z_jO%K>}x+FSeQ|I3pUN6wWHF0KcHbQ2a1k+FiCO}S!LLyyAr?vqB6|`W-_1M3$|8i zIJq-vX#&79h=5>oD<6OZO}!OT6wNhDF^;KL+oLI&xULnlm6!|m3lxNC6k178s^s5P*pkxk$q8S}LfS0=+YzOi*>Rqc=4KTo zPCja1%djjHy2{h*PQBqz?ecMRDd(9he&qlZ4dW&Mge@mGaeHr%m2KVs$z;!uGxI-* z@Y`IYY0WAxf6ae&t4a;G`_^KvzvglhMKe6kd25n^cK`_>b)E|g)QKoV@kAMVs@i^) zQxlW=XO)Y(>>Al_xb9J2>cv5=q6j%E3dr|f{$XbO)yL9=wwtqx9x58IsqS#t+L!w8 zj2PGwTf5S%p+@9Mlfx{n{HXF-!(HHeke*7tB|Koi&HF&Q#(B}Dd#>RP(O=NIq|hvi zgTr^ofcDI)?zYhlKx&0knh_G-@Pd!roK%BAP+jWM#O{$s+)-6r>zSR!$}ymf3=QaT zN!59>IF4|ZZfX|Ak}{2gaENYtD{4Z=tvjcz|Fy?IwAgYW9&7PZ=re4{tKIr0o#TMy z5k#Rf4QE5))tKO2C*s~@8(NhM>xw_-h;b*Oos# zoj?Mq^NI4Z?5bww(2q0zf7&@$-r2W2J@*ym=d`h|;#Tj`UA%vNB5Cy+#9EPkYB~vi zC%<-Ycl6ovWl?De9p4s03t zVI#d)YR&%MkHTA$sue5>JIJfk3ELEbraH)B)1FpViDWZK7&BP+EGA3WUrTFpMKdJ6 zF;ID0qY*Qth0H#_JfuR`5HdcjcRoD4=wY#8CZMKRLOblr+i$%@3!2FWyS~>;(&>Yk zQ2hx<{a=69%g}}DjP6~JZgLGT{Gtm(9*fsO-BUzGq#5g+2gLb|Wf6{_Dx!i$$Lfv8 z@+-&w%8sATjB6tECJ>I7SB_Urj#uf7WBc&b(@p&KndsgazeJxj*6ginm}s<_=&eNU z@0`44njEa)?$DX&@tK&=0bY`#t-P~T*8=MYCc1cmeU+2_8x0!m%nSQKfe4m^? zeV?9r2acYj!U89kV`h+Zr=JzkK6kF31xZVt;wICx$!h^Dielf33gw!aAlxqyoLYc<(ok8?n(63tf zmb>f;T<`%cTo@t-_%8ABExy~#S%a2xv6i)Ufd_>12y*j=vCF=EtNth}fw4=LM6>F0 zNH(!6npKMozO(y;^N&6YM7r~A^s8QcOWs|}`w)nMcoAvzE4;a@YF(>#x?I0{)+@?vZ1Y-frCm|B`YwXGv}idKDSlzVz5zls@uJn4t7*o{5N zft_h^^~8Vi+p|9GNyj+*L+)d0qul%@V=|=zJ zcW0A_jgN*+Kypv~`%1n03c!Ez5w|+&yD8wiLfLcF(S7C;3_)BJa_D#Ylm4z)?zq3} zd^>iFbNUh^{`igP(L?}SU<)gy_EBj1CU)wW1nmT0{^&#R43u}IK=RN*eEH)a8^v~A z1>G_e$<{CZwNvo5eD#8vJvyuWo&n>|1pgIh;v-kxqm6!n-M`0}?icmAJ!k!=((5bf z-1U-wf9)0$fDhxD<5gnKTl41u?6F=gUeXvZ z9#Nkh8J||-4hXvU(7$8( zWvDl$)z7_K*h$ymLI0P`nwPPwm!!OVUcXJ!?$s^g2k76g00_bVlg0yJ7T92j6a$F_ z59rEi!<9hdu9xiXQ25GuJf0MCOUdE&o8w;3n!CaHDwLD{EY@>_8Y)LXe_rFqK~I*W z>CB(nOJ|w?0n9?%u?7D(jTcHIRY5MT^FwJo1;J*e?sID3_HI_8QnX(EjO9r)Y_t}i zX9=fj*z0MO>U_WuPN>oKhgcHv=R~LN>cl$#3?bC2Hz7xBi;!{-j~|X9^Lk;MO%#auBR=4}P%Gvzhc@eVWl^Yac4f;y5b1a z7(gA~505Z8T@HqW5%@fQyjt%bH{uzJfs*2JdC|BWZijf7jmL9E3V;1hFga#d-+hOd z%GftFSrB_t!$i<&wlPg=Tf~X!;%IAMcuuIYJ(j1ldF-2*d-UH3-+RKSyY zHiV}v$PYqdo=YGbA=-JHl$Z@7FYA6o=AA30O5dNBBL5SE$@X1lLb;We=OQH`z?X!&Ml38}M;My&%P$G2vhQrJu9J zWnV0Jox&&YDQra!H7Uz#;@U?`q|!JWoi)-B&$m4&J8T+s8NP-7PGn z>Q|%MQZTbsLerUUS?;AQ4VotHAP5F z8==0O&GI#&px3n&v0bWnlfeR+Z|{xjY}TcWV*R0}Sk2b2ch0yPH2Fy}13+l32^Lpe z!l^YB@S|~zlQt;;mq*I*-72|6otp{!HY$F(g^wx4-oukaP&B_~(d7*>7iYBmR4mQs zN$^hdzl}{(6wjx4Od8{nto0DTOvmfKZ$0I~O{HRD7tA;rOeqwHq6Qbfa5N17 zD<^fsSgcB8HG^7#gKYXz$;|J1%JSzFJZT~CY9|F%NvzR6UgOqOj262~me?$lTg*}; zdr|U>0*On%d$Ha^hm4Acl(A@6rT$Iz%=p7{NixehDoXS`fYQh+1dxEp5OMAdQoaNIZZi#!W!>i{^%NEF-{Qm;E83? z*hJ~lV`j71rMa_rtVKeLW9N;nmp`q-_qi|50!+e=u>`+nx!p>*Q&08FUL@heFgmDm z7;J>nLG$A%J>Fup9wOe_V{z`C(tirQDBiW9S@R)9Wz7x=dG$NzmODN~Yq?z!r7oB& za{#E7MhddqL&8bvin`T$^Ix^VpC+Rg_90@4@I$6)NdYjoOHY%5ZjqM3DU2bFpVnIB}dkAB*W2A@m~&mnEESf_-tWdbRUF=Jh4)8ZVb-#m;pVQZ*+B2sGv zHS8`ndE?+U?@OPO*ikYHuN5ZM)ifLfEs-Vd9=-SKg#9qrB@n{$)3(GF`_ zT-G&lCTTXKM1Cfw^!-MCMU>+AsB zjf3PpYH!>r3;W==xz6sNgA+vOk-<47Cw`~(nYy#_SRLJAhg5Be*_PovI!oJ^2ozx8 zZCcO9sTr*qR1OA;xsC5(XS3(Lx9*;Ibjk$(Kd$7bQ%%nb0+6`9t4T1>6js1zmaDmu z)+;H@AN~8mZ_MXKnCXUUZ_pWwIcJX}>e1I8?)@Bbo*}aMM=pWv4U?9iW4I)bq4pF% zVg*A5jdqU9(ZHMc14$w~YK(^iRGWt!Ej)W;47Y_(osY>+Tyvg&FNx;FOB8T#^8|9P z#Q+J?b{^l`k>r?a7X1g^BknUa61yyKo(-e)uHr=5tp)+@F{d8B=L=o$D+!-&PMoU& z%ebdy?W}rJ_4L~V5`1$e0sKdQ@agBWYzc?Y9alQvOu;3#tk^kmCsbn^;J zn>|uPCT&9zURT~HPQ=;0o85!L8}WA6`)b|&>fGJG$9=pm09NUgp=I>N>c!cw9p3A@ z#Hw*}?sJ6V|Kh_3pCY)cg2*o!MSmPA?m&o2-Y-uLizX_JPT2Q8--9K~`>%s*MY}V{ zS!i+|+(tv#0h`>csnWNN#6Nh)L9B`xaE3s094X%`_)ONnPa8(&B0~P&`=%m{?$eFQ z%F9&AwKKr~@2+XZnknf`gr1y7V6u~4g>?mr<8)q#$t4WI1(8n@k>wLDN@zz;iDF~z z`oyf>c!W9cMJXqHFdJE^-rFDMg|^oRvW@%5jE6*7L?67x7z+7$O$NoWMT#{;3N3>3 z03nFToNR-lLGy!h8-peu1LXIj?@&X>>+NQRLxlUhQ!JvQ=L0LzO;(-a{2}7Y);*ye zB3M2G<$zFsYiLRGhAnt;o4-Nq$YBO}!|trZ9Ow@V#mgOzjOw{!NY!M!qi}! zc1v$D%Rw%_%5L*X37+%z68Q2I{C=01Mz@qvBMXTrpAin;VR_kM^2QM_av^haG2fVB z_#F0R7AgPp9u?8iROqpHXHhy&$%vd<)`ZclN=diQQNw*v(%N+Vw-Dl)jg5^#1A{T+ zl!GI!bmIv=6PXB{hliicjz5CMcgtC2;V_^3Ev0Pr+ z{K640xk}=PF0% z2`A?Zr{B&+2L=^BSY^P4Q$zRD5ErB9$V&C9;wI2jPzOIxQEsP!8$ej@OeM1pirjtSEQ}!mG{VpbsaSt@XR=`;V_d@&5jztDtS-(RQR2vQb!l|rD>jfv6nYD$8S|- zcvKngLzaPFRX9}PP;>$5V|+meC@$8I(xo7kx~PRRVPdkt15|BxQj+H*$hpDAfmp() zpArmJ?D3gqjGfNTQRr7yWj+wqOqGajRMm)G)NE2+Vx5287li^YrWi`?Wd07o}%%G!J;ynNU>JE@sW>WxaG&ZFN<}FIHY<<#Z@CD^@0h z7qR*kH3vvF6ku@ZLNy;m<(26dg+J!p#TJot)saD(aYYKuU$hcne)X}gCX21wtF7`H z2KC`BXM1XexoRq+EPXz=u0}1PD$SfO%^0;M#8{2rM8l@^YxP9y?u06KVrwo^t3|oU zL>b8?e2MyrYlHAO2DUSFLu(H@%SK%*Jr8RlK+QZ|wR!S&GotzE^HmX3ohaV5CNaif zSM5-y#E`0uwxZBPJO@^gcz#VXS!)$1(I&p z-GBiI&*d3p{~KR>DJuj_)F2&YnNUsFr5RT z3-iX{3aE4a0V*3>_ZQNjS6jyJo}K*yf)CwxGc_;Og=O`NWp7!1E0BdQap_b87l?L<{$>1_YNzjNn~lzgZnm zESBLoOrgELZC$@Me2!);PCsKVH%08?J#M`np{6~VseQpALqYLOB60qf)L@LdxDTSw z+iENZYp5?}qyVF~V{1eOtNXA&_qEC7$FS-CyRiZTnNYJdc`Yi&&wZ55FRG}>7o;(4 zsN3vr#^tHAoj_$ShAp^UQ|fB1N)*+*3vKO9Q@zUr-Gc?pnP~POrpmCpM)C(>5bhP9 zkr~dm0>yS`)7p0GenjZ50GMxENo{#XcUtGz#C}P317ex&+ff&jF-9bo3;UsKw~px{ zF1eeDhu-c^>>*FtQN@sjRkOLb`m`Oa1u^8_-3DY3ECxCPpEIQ?GrtL^mW8YQ;b!RZ zm8-c#w>Cfhwq}~S*3!xJ!*TpDwf<&q4PKDJ_mcvCixkQ|m8m!u%3UtU(|(6zw>-IY!cBXqY_>{*?1b&Q$AjTwIAUcXDb*h{yQfNv{FaDfTS!-9rM zA*?6pCkdz<-KlJW=U4Jpx;xv~;>N0OmfT}Dz%a|Os&#d@E1=lLYj^HjSmL*BNbu&> zXPcz28c>N3;{5zc^J&uka=mek$&=+I&G z!NL6n0%Hqok5O~^!R~mDbA2xFsK|7EacPI2e$zCZzNvov-gM4oXmNk&D4u3b{&>z( z0`tmn+r;M-`V7IR86qBW$Ek74uw&2Y80cP*POXuQ>UeYEcT+GGO{ZZEOM<~@Zbf*c zuQug)k8{Z!{wp11qn|U*_GF}pdJqhNAlD3$e{$i1d*HshqPGlihlD_}jAn&!fs{D& zs=GKrzxU2`G`h4EdAhxkKN=r8H!-_UCw3;La+2xUCi@pie|>K(@u-OF$`D9}Fq43? zbGOD0gIi)djOqeSWiBjWK5k~ITXv(Zac{htF7EE4&+)XW=4@E9)khg3PZi>(5A7lG z$OXoIpqQrSA4UHq=f&u)#k{D`k{@`<=zH@n_~FG>#e!u0p7PcM=fe^yow7<@fvcZ;P9@(95Z!=O)8FOv{ZOhevSFO?tSebdP8@ z+WGC6XWq0&Z2a9HZY*TSxMo3Di;~-4hBy5BSQ`4TLL?qzc@K6}_5}Kt6UA?=aUN9y zT2WU#x#>?e|6IN;P{(s175wu^G)GE`cN5gV(fIe&5`WVwaIYeMGH!o;W9M1nZVm2Q z_wJVXwXy2icjd`|M?*%9RWI>gpN{ky;iJ~Su@ZQ(mUw~IaPW~jwcmOVQMebKigtnf zh=26<@ORHWdXdM~sEhxysDY&Q%?l9lxUBEG8t(I>`Q+Z*<9DI=daiI?tpBpXaX$_VuL`Nu_EmF6 zt1-30TCf2$`LV)o3+s+b$!@h~m*UKEz7iP_M&DWhY2%+0GOEe_qi8O?&io=ld{6QU0 zfgd74(??_|9ozRCL(6Z|tjy9a{~yY@+W}CSMr+oHGwNWa&YHqkt;n<2Z^vnUA7MI4zg=Jo|k>|cKvCqXXOgxf-%SLEzUFO}6HS5)5Cqs3E&56+B#etLZ{LpBK| zP+u>tT7BiwA8q%GJ}K(?zHvP+RWo@h!my0PvT!}6a|et<)Ua(@{aQwEr1KB?IvbQE z+o+5{>&rf2!$p(~9l#37wxJ@?L0nNccX~QSVC_pjDoIJPrED~EVmod@kY_hx-2=_C zU@t-6u%Y@^$^uG-Th%kvH9osrzJ(!bHNOAi$9Xc!3~Jn^4V)QWuN)y_8@idYb)3RQ z8`j&zGn?9}%H?RT$aZ8H+dnQMu(#SR`@Imbj@iNh9=ofN$;m#yQ-%~Ae&{|uJutug zaGdUY(MvfBuJGl3JEU*qJ{7#D*BX@5PK}km1rnM%skvpB0SsvJZoi0Y6i8Dz2M_9!wH`c@5+gdSHqQI?XAF zu@&MAqH(9%QrcD4*>-VJ4$7p8{lU zsU|I}nIxXS2lPNUpy$$Y@+8ePj(9)N?v^EEkKF7#?vFa>;aD%es0~^&eA;#TMvmxu zMYfY4N|z=Lj5;$^;x7oY4#huX4^+vR@wxb1nN@zV7aD;HeI}L zWrKN*IWwR6gTAU{Dw3a^=VcCXHz$KBvt52Dw?M0;%`}$1o*#)zMT$H1Pv?|`7$}h7 z4oIZA#33CJdwtJZs4F)s`ynK3V|Kcb<;7CYF;tETkw0(Dj2V@%kvBxVR-T&FRKsjk zrf*TM-c+sjmvG4Pj4)Et$xqb}3mZ%E>Z^L)Yg4^bow=;Z*QCDchceQe1HI6{i$1ulbFrV zov6jF=9`j-;56%jiY=9@dvdVl3Y~Li$3`Nndzbu0iFY!dqM;Y7-+@h~WC>i&)n!Y6 zWuFb)TvYmDIc~4VDpMfJx!?x-ZkPQ~wS_kPobjB75q2|E%!D+?>P%}dJJ=yS*V^E5 zZDEi@?V1UfcBY)0GZkaPJ|>wLH!W*<*fRHr7hH~UznSXa=mcx;9q&V^J>|o%_L>8b>uLITsOFcZy&&$htfQ5L1r}?y(#koV#|@4__;baF5q#FXmiGlO zb5_O7sSo`pG4;5O;b61t(w#Mn@x1ospd{8&h+9b`0&m4%!qyL=0T?`m%KH zUC^+Qu6#H|>A!X=qF$#Wcsm)_;P&1=;fzZ;U6+bU2Y0`o)^W{HpcIE@7 z!Ey(RF}sJs{g;Z7`7ago|3<~!@i<7sJbBkjA<%X*Nult(Gf85=@qP;0w0&;6@o_sH z#fdoHZ^XVu^WL^oP{s6)*%jL`PoT8v{G9wbv2hK{N^5dA$wk)aJP32odpwPSWXm`! zM({y4hvA>kI?wxr>^dYQ!{<83s-NpP^-Cj$WkS~;#cc&6+JSlXOARmcnv(vc+YpY= zyW2Y2wC>}u8;6c7806X0>9B>u!t`_7@1KmvfEkI8$Bq+2UhQriE?w(RqPMK)!N;Zp z*Woiai1X+}UY_w7Gg!*&@b_=Onj^CEshVT7cDve>`mJcFi>8tZ?!dNz3b*sFigmB+ z-A4fL&5j?R!6tHLoy&L+(sMI3HTsT37*wi{&oU+e0EO*-9>ga^WG~^1(A%+u>2ocN1_)7-q+5O4xJH=h_zHO%9z@Ip0r1KXc^-C8$c90;g4zHeItW5D zekv^T1H}LBtJStjp;^Hs2)-H!LeqwPqA=UDuv~cUaF|s9A&yl zjbbMsAVP!ys1kn0m^#yM!FMcp@^x|Ujr}MOwebA>xA6g;>Cmu08iY|X5-DwhNxt?; zm){X}76a0zvB2Dyxq;1x&rXtZCXHN&D)D1vwJ z&^#rmJ%kb`Vuq;jpV0$RShAend+DnppyjG}Py-U60Y*16c5W&j3NBjMBbp;R%n(S1 zU`{GhuQuS5oTT_v;SvXrdH|%RVAz8wCI3U2yscLv=6BK{k?n_Jt>Vd~-9k#t%S;8n z^W=CxLJ}-;^uhp~iUM$=IW&uvSm+^E?&&!h*Q*;WK3#bIshSO#DG)y=sTm-wL;vI{HMA&##Uauf zCCC-YbpWjNi|C|cA<9^_WB2`BTnGR~iv~a>-?k2scIC#Unmv3z4N>44)OTq1-%c}7 zWU;i!PNezhrz+o^Bbz$Fg>(@Q+0{gmy&>DxJ25fmA+$a_DErL4ntgOjCVlWdNBa)8P=Gf;M-ElJLrN6JTtN|lx2<80Ol&bwKpS{p zAXtUneaXO=H3+1ELWJ-AHkNVFcSI?hrBY~YS~UTUGy##z#Tv>k&>NuXgEu%-?r6d@ ztc~&drL@hYCNvT;;==0lS0A4fns<>2SlTt9$;U~N)(7}x<`#X|6b6nqtJKG+po{pI zTOANideEG;+S@&oFF@N*Cv=Kq;gIG)bksS!WQ04wvs=kCqD^Zdj@;!~{g>!uV>8ss zH9m9Y{3m(esQZ_RS&lTbLRxK*v_4CRcOkTUSKZON-x?4ixj5OiQ~eRvz1CfjjAFk7 z3B}5DA~wvmr(i|n*Is_RM(!=X(h9=B>)QOF)1OTTWP>Ky#dbwS_OAqqCoAo{%iw7C zQlzE*VCj{$A5+cNO|22Kcu#oS0Cy;TdK({%jx=W8zWl!%#y)y@fM`Y6tOFDQ{a79Y z@~OIRe`{+QvY*?)OSZmv%1f_x@A?;F7qrV2J*=di8~}9)IT*7lCNeo*v%c}B);r4H zQJQ;vths@ZVbu290D(zYQ118(+hQ7{PVdPQxf>&%gJL26QS<3-(tOyOR*7p>@6ahj zWVf{jA{&0A*? zqD&(=!D`%Zkqxv^L|*P{dunDt!lA8+F06?RqtgPa%QML}+L^&+(=xaYRIgZFmy@(E!bpx?2T^O;j6 zUi1+-osRZ@g*j}Vy~}wARrt3!P2NQMYY*6HUv~|)Sf_{kos9T`j(lEWp%l|?Gywo> zLDTQcwxCqs@fOd4uwQU6o(p1@W6*%sV%=sclm2`Ud9}caWjDEHKOr?MB2LXxbr*?H z&loqFA0zI6()|BDxESO3zT5?PC1@3u7=A!w=&*Au!-#Yh1&#~{*Gp)+rdzt3qp(oZ zl#!`zN=Ve;x&a*I_FL4-ae`G=eyB;Z0TgX4ha4_o06~kP;kY3d9KT>Z{LjLF5hOS! zN| zAB}BW$vT)qD8)jJsUscIg5Scz&-NTLP2JnVHL40+?y#6UQ$wh(U3W^tV#BB0NV&3)iQzdb}6wQ}jJ8|j7H z1;7B5B~;ryeB$t=CPrg$4}51db+(@sSio3f_|yRODK(xv(mGLOlSlYi$~q{>n(?>rig4QDfCqi0|N7Re(=Y3f{@MXGlAaDZiX3&YxQ0KTISSAxGW z3(QcgA4Nv`u|ETRkYa={6-U}1_-I`!Lx7;1sG@mzar)gCyCaWO;IVTqf^YF-ylq5= z(JIYsLx%s02jJRkB@6~BG+MzzCyk4$$TDkiKch)Y%km^RoXTN2gD)d8E@mucK+Bp& z+|Lv@1j!|{2O(!hL=6BYHLqR%b%hrotnN@+;Jorf>w9Bv^+DJQS+sB%C8d6 z`*=xzQVSPY&OLh!kr4aC&BdPTqHmv`SMidXQmgeg_S;TH3LXF;Ycs~@@=|Ll_(oH} zb5}4Wk@Oy>2C#6WF`>m-D@cX@D{q#lV<|k0@9^@%OBcl4~wHApV$VjaB3T z>rN+Wu=XVcHl0n9B{H!&aM?6xBMh;*-q+|=IQu2pP{WKB5mD2Nosl(C_}+C0LH|^> zMC-Ibj>-%h+Z#;#sN_e(p$KgVXQ{?F9QxP2)L&Y%F2jDiMJXL7z>tPjQB@ItG`Gdm zBGZb^?yI50FAJSgG5D`#K+6Pfypj~SGB?E1>F4C%+}^e2#{4URfih%lnp%fnJr7Yuem6?sHk?OarXIl1fekh56IUfUjoD zR%$!aRqiypUNo_5YsE>Y&WwU`M>N#}e=C-w1iDYFd!?%t!fS@hS(nB#lPHCbLIP^yVY-nDi#5M@RD=(OcYS+(D5>oBxyrV#20*K?lL>i}nk z6<&(u1mOC&sQMoj_5Q1T^xE}6tp`?68_U6pv>7MuQezWda?&x{F>G_A$JE7e-sofhl-{DHz z)gRq`n%(uW-gC2qNG{utJ=xv%)`jcS8$i}<)6wVV-BXv{T_DrR{@#m5M?$0Ak0sj& z$=h41-P;||i=fla4&@~6$@O)_55K-GZLdX>#2 za^hg34q?s4q_@tPiB11Sc1JUBuUaRP^#+HQODp4f#~-9lNt>=g!m$Y-^zS-T9T12$ zl~XnElWCRTQ**}5a{2{jN858a`YGTx-uoj7M~W*4-FzldeMZJ}y7hRcdJPfJC#R-- z#;$Y5=6w3^WrwU{fE_XHFXt1xZ^Jc6Q!zfBadcBj$a9B2X`l@y~Py)@_?{N#03v-#J*m&QaNgM(@dnx;go-IYiK8 z3i1Rh5wJ-I_>eQvevZ_iFwYn|M1V4xN-%ZEGh^zz0BbuktTPky{x$b{=~G6Cd@*3v)GHv{n`bk-I>~;qsQy|xyTQ8>sof?lw-nAd$>Fo~y53hbiJi!Yoh-Mi z(6x>XoXJ9&zx7!Z;2ruyzxG#-eR&a9t7F=JV>()PX^3v-JGg40)TUFC@biTSy9EV1 za|Qv#x0;{7u1LS^oV)5q1oYztu4gX>cP`>qu@R(3SkE*D@HPKE-ykNMEFkCtVWd7xg_w9`HL0;ZzY`q?1gu93hQF;8wAz*&pDIY5H9#y(5X7{UX_MKcpk@yG<*<(g8~%LW&>)!zKtJZ@cqi z0abn(-EQ%Oe)b-D&D(av(AhS24OVjE@LsMd5Y!=Fz2-%{09CzTP(A7@e+rE{>C13n zt=+^8am@7dI7IK5G4G&_Z%+|8cagK&dvVM?HO701R6vh>70aq7YM3exE7x%@g1Yk6 zcgr#F6k5Jpf#IUuuZ3W;#pP^5_3c#R+i?j0StEFf^IiO3JDHV?C&_;Q=!)3A5B z4cw*y9%Ov1cl-4Z(w}f#>`97hX;-Lhpf%>jks$#`$whC7Nx0Mx|+Ut`_>HoJFBZk>-dLo z0TqV2JCd8n*y~5ff7d~A+rfSZ2YzR*aZha3H|MBB|0cTp6%LnUfVWeu1O~EX_AtNW zQ6p=fsd^rOcFn|gFXLCk3H~oM;H|@7(=SoKpMU#5LpNX*>Aw~$Ae1nkZOcD3q3*To z-nZvHsQ}O1a`!G#&s%v%Q>Iv7TZ}J0hcc4)sQ5b@)Q<72!rxf zbOaS79EO6Q6pV*K$mOsN*8NX*i4F1^PkCQ5>EBl=?jKSI(o!ip?USNE4rMblZPw0H zRLrRq^F1D+5yux4|A@%_;f|6nQ`RWWY7aW4NmDJWtK4jldRS7g?y$Rn?3hT`)b9>~ z5X@G;_|JB}_2@FBOQS1x|G=|5Z*82|Dm7FpZ2}D98Cf7tK?i-3;WeX$BtTP#f zB2kd{(QFV7MS%MY4u*fdvWWU3lZS(V`Czp~sle_9{EXSB|Fqs5%q+v2$`uGkgwb2D zm@AUYxWV2bR@7~oL$s^){LcrnAY4!dKr%#6^g@l9b`fRNGPWubYaR;`M{l)5gDb;uozA22dFC0Xwi=8Pts5As|#3 zyZ&v8I0*zZYuAsxpc-R#lgF#pjNVbx)P9HR<3jI)Re58UO~&!8orE{F9uv+mF~Z_N z_0}FlNGruLFTf3x@Mw7!@&F42=mN#U%E9c&I2Qe)PaV0r!I@4Sj!I z>uMC2PNeXgd?qv*#Z)oIMtQO#mw9qpwR!<*q`Mj>so%Db>4)9Iz_e6an2-o>1LNxBm&hsTL@T zEh=v)D-3yLJ6B1$>bj|=a+oAaP-bqn;nf24MG{F@glfs_+XJc2XFm9_d&#H6?9R-h_u&%*_di``NGaROG-~39? zv!~r8&1uO{*(Q{Nak!10+2phAinz>bE!9g7JM4Sv{-#4fvp=IU`bHjR$>jT9avjz| z$=I7I%ob-JL3lrUIz~@Wa2x++gZ;QH{8JZpzjTj!XTPXn-UaCEf~pz$#zfpQF(d}V zV>1WcsC<|Um7M9`Oa|7ttENx$w(Afh$-SSG38(a0P*g~8J*zod-zRiGuM6KF19j@x z;M#jP283U^jn>UFZt*&7)p$5x_b|R%9Hvr&UXGu(ev_YS4Cn!1IGMd%;WVhPXOF<$ zxcoO=u|Ai{8J8>WZMTMFzAbmlH-oO_Q<+GX6Ii<7tPufBW~i?QMr#1I)_!-OskdfF z;4F3qFD&eZ8;sTO`Y9@*xqu4Wq2S*W!?^rTvsV@p(1!52a9zhge|k}dz_JvM#>BnX z=+X@u;r!d@h6)#vC4oGQ;hs9Uf5S@M7_D3BM~qA~ zcik%Sk;)VmfLutL>WFM=Fc?K5WI|`^Nd&!p%)`|B`@Rc)~+myX&EKN`LJEeX`>l4-BcLLn)L{p`{RCAO3e35x+)AiYF?+DIm>FU#K}ZRZOEEkjuH$37DCJVIYg#N zMAMQLw-`50a0E9rrWAjen8L(YPVzfL!FO))H&_7c*(S7R(O=C>fPFFSWlc zd{vwzFMVN<>VI?|thI|GRma&~tKT}$O&Pz3`&S~8=fK#}Q4j!E%3UjJE>5YHD%Sco z6aNTNRIw1qEOf6qw~|v>>e#Om8*?Lx&68PqD#HQ zqwpx4T-ETUOj*WC`LbHMFrsGtE=5S{G87 zk(*4ruG-N0iwsF1ZUb9nr6>VSvRMgorfSl^}A+eajQIC!a2g_mPi9|y*|ljTR~s5-gxOM zK2v%b?DdS-FZd(yA_^yAY;~@?-6r8L-NBtaR+sT1i(|!`xd=&16V~o=LM~pbKcc%ylmjkZMc{B&=wbJip+##fkh(czn}P402!L zd0^z_@G=EEyG(!sSn@Z@(fRbDk#NidHr;uwFPb5jQ-Ewffjm~LV4{~+C0(}Y`}A`n z7xz_Z9_G|JPit{L&98N;Dq^sn=rr2-7%i$lTCg?~#a`QX-g%o^udR;E;fc)+GTsiAulUu)KCn)9P@qjMt$t&9PJnkpg zy0OoZXCL@JO0_Oe4&<2j$NN?qoZQUkv(2hZd6z5ZeIujyG>_qZPcGI}sE6utO0!vO zKG`wCBmFqJms z;(;gY*z&+Dimitd?zXx?{F)MVN*oTy%?o$V`QXr1MZ=BQ-t%X#o6wRI{JnkCu=Day z*Zp^aa^{{Z|2oWh3JLul3hu62*Pdx1zL~rpbWE_oM_$`lSvNvCKhb!Vs$)bPbHP)za87AHYGEPV_TGm%Zoobd19IQym>@lLA0K8v z4}su8p5Q(W5}S5k%vU~k7Bcyes0sj*lxHl5b~mLZ{|~YbYGGl7w@y^(9(=NXV(2#b zJDx2mVP*n>dk20pPhkyDJc<5XqGI9W=6;>@zS}=NROiA!goQ+q2Y#>j@!oN&26P*6 z_lY16LZlDOG4T7=ieUGKVULKM3=7L>^K&7K;@%0vdGZ^Q4cXWC$~lUVw)dIlH<+ap zO_dFvo%mt*3|h{MoNN!)9ES8-TTiMW6?8m3i?9#QpYL4UGDIFb8ecVKg0w zoc)Gel{2v1fbK@<9`*e?@j}++LZ?rIvi(A&{e6Mbyn*9=aV!%5pg-4}BxK8hW)Z`u zDdJ)K_!P~eSUrR8^TSx$Jywo_*a5xXo+Cd9B$6zL^Jpd2wI#jo2C8lQQ@ArA>PNa1 z&?~Bk%ZJ4y`}?h+M?Ld}^o03xwMS!VCGssLlco7`0?6xk$*Om^>W zM7kYjL3*;oT;^~c>nK0$D19Q`5qQ!r9bPVd79ey!KOQ+Q`}J9J3UlhDTW)e*PPbo< zqkXc1cQ;D)M*?`S9R-&Wwj3af-7@@n@VI-zvRn>@Tz+qUvbtGz6Iu>(V(Nuf zq4Ex~RCxM(`XHJ1G+!(L&S@Gxa-uY5`oVLmtzEP-X6m_K8h9bG5z#ksE{%R6xv@UI zK0XMgGqasHigS;_p$`uG602}jABLYHzZySTzCKFSF!^(0U;|naV?vstLYTrzky1sT zDiCSkb`Dl0irQGN)OYeYe*!Ub3TZ)sh;@$lUWs==9$96EC2|nELWHd#_6KW}h*ub_ zmGXVWG>u5?!LTA-gFN!VEPaAp#odhT#5B*lV#K)u3%N43m!fb75Q!L_+G_q+f)Y)^ z++p`DSH)cUw4@fyJb!__^uYpQWYlAT+|L48Y?1{<)&*7(6^gI1#u>BhPReQ-Gxz!P zG#M(I4Rc%@3WOQbJO$FS6AStcGFk&lzdz+LPGUlI49Z^N#;ibgo+uM1H4HZu$v}Dh zgb7ZkDNF=aDR6}hQh_Y@#ZpB2BqI58I<2ZhhRD3al9LFc31O^=ld7rlyja4FgwB+B z#f&Doy5@kC&xAtMg6b3I$|{R`lkl=2&qAO~cpd{{aNv@OmCAvwN=kxKM8OPZs7mC5 zn&*XD#{9}3<0t{1l^fwDM-??PfbWi(`mU`;h?iQ3SJ;8JmgS9b3r#(xz z(dV@^U_7s)A~#qu+Z?%Z6sUWDzP6zw;~1z&hK2|@4&9;)yCBrrVO`H*&^1xfUUXVj z4OF^ynpbn$JhsxFDA29G*cgJ2s>O-|U2N`f3}$FN1|n`m zZpCHjrL{w3Pj7B*>*g}7T`cI$Vr|XcZ>bn-Et2akSuNe^=%i$50z|j=k~d*=XL1Kf z5qu#f^+~b;I+q38TN&Ha4Lf+9`t>@9D5`7DSo*(McS5lYQ0etaVRsq>^{@&zVH?4j zIL-*Ph+*)F`j;p=1VG?ShkgM$@SC$bF?e^^RkHLk6=fIHo3Q0=}nQ?{C z({aEJjG~^@lLHwd!5-|eZp^~$D}wdsWpw&=_EdDYRh@Cr^&u0)5fXj>(%zu;RP1Lx zp-ID{;}z}@fDS%S$?L8f$Z93t|29-z!!$8bbvDsO_ltLpL>OVEorOXLpFjhK4^dZ4 zbU=r)AQJ-6z8$^x)xF`IOc*`(fdiX%lFqKP^^pq*Lp|pxemYmA+tjPkICJ}$6J)BJ zsBa5<{Dz{7h<&6_aTGv*^!cMp85TDr$lS@VUpE#&;fu!HaM_YOToC%elKK=tX z^GP(wo!rNxw;%-T^6FX;ikiop93@*DhIRtOCQsZ%Lp2&rfNpV~gGW`9huRdUG7m;H zz#~5jb3g3kzZBX#tY*DlW+hSPr6%DxSAW$=Fqm@85Mr@}PEUal7S-72T8kz%L}xmk zzw1>k2PjT<-Vf2t&jxf&T7t~57o$*Z6UHacM5q6hSwr(Pkb!Aza|bhn*(Jz_8cJy=D5-)d%Cc!i(HYONd7hgpn=m&r4{ZYd9Z2p;kX+ z+>3o@3>5YY6bQ>_YwH)#Rj6=W~g?WeeOgiz0ILuG}MGzg?2GW3zFHcD>~gyMkRhS6945N(trZLcrI%yobHx z`?x{Ix!aV#5aPRIlCx6eJ7+b$Q?a>W555r(zF~_gKr+WbmaxWOx|{L7B>1q;G6C_U z-bWVNpyJ#pP94a~->aB9&D+_-1Uvi)zGjUfr^!Z1)yGHy`2Lx?l^%-WU9x)&x|F@N z*T-?}6}=Fwy42EjG*)vy<+xIlyMoOk*914+QFY$2&fIBVI-soB{k*iZM|}|d_V6+p zYGnH8Z*wo^b4wIME?yWJtQV)Xb-SubLsSr&&qcz)CuP~%jU*4*nxJq5YY|+QN!)4 z_p?XQm35cB_K@oVn^oG;Q|XZ#!xXgt$8&|6P?W86&J@L0{ZMh)m&C4z;G(NS(-*6$ z`;;Zur%Jc7t^1N_n;13YN;r2D7yH~P3VwbJB#AgG*_XPZTlcOzD?ZPr=oh3VSCk=l z`6b(|A&+)ccPqTVUEf~2;Q*`}Zs0Zk*Sbf~emPI%7L+1A&q1Hpy~URiQ?JvM>&_&T z@o%paStyfo?o)agjg;?wNzZeS@AF%c3#RXjAJ!WGeAPp}R<=BA#a?=foYvvIJeQn# zE}iGp-sW(?JKw;&Vq_fpq2{vQiP`Tp?Cyv)?>)g_P19HDBdBo4(RK$SP(JL=kI{XH zKDA5#Xw7f_Z!1RnAF&i3dve|St;2)C}Cqps+Uno?? ziqk6t!4OPJlL>HG!mui{(`Ctz{Z$ zepduDCsbIzvVqnUz6GzJzdo9Fc<}hc61zX0bQR}RW+E=!FCN2IMHNuhG=k1on;iH? zUiU7q9Q}PLc+-pR=A>on^oZ269Tt^xl=!3<>>l4<9``yUG`M{^V%f88kDp;y-Fyj; zGL^~^V08>M4Tio2VreEXx)8!N=g0`r3F_NsVi~80c$|>WDSaMW?6l&EEWf1G9Y4WNfqJQ+ z=67pBW;(29acFL4lm1`X6iy2z>q<`A8f%Fc3%Nh~8pEXHY&3r|xgn>Eq7MRpS(Fqu zQs&zGvTA;!n3d!w`NM~I<#p47Qg})3s%s@l#-y$kO(NL$70u75XNqN{DIRl3W1wVf za2@m*=S3#Z>`^crscTIE zk5l%wsE#q2NO)whv^X(k!wt!;(cVvR?X^2EX#DQ5T@>+fzFTrqTeq?KHGA$jbi5jI zyQQr4a@{&Ji+Hyviu8wl@0Fe~8`{`GyKcx!YLa~c_G5!B(^EIYv;mWyw{>q5;;CmJ zMv4CUz*DZnd8PKt=IxP9k~I5COK>>5>rQOX%YM&mM(Y{3PzCc^ox^0oLgaQOnCotU z7o>Gnj*9Afe+;SbwOTzERdkc&PVatq;E(@cvQSjX`>Md)_C8^%clmy}?a(=Vdw5nw zeTioQEB;#B)ai#{tCH|BX8hNr0ldYk*z{g^UEaGtbBQ3=QWC(1-C)7KD(U^i9Oj zg+qmJej>jL;_%FkCoPVPgsJ*VP`8U#*2IJL#nvlvO@E8C`TLDPO+?f`Qduf^VOmwj za02Gs)7|18Jo*_YQ-@sKTk$-?L?lYSx15*7VP2A2n{W+ngV*zATJF=_nph@5LlOY4 zgkUM-KWmo!7lvbzT|)U#lKR;1LeEsZtNSj0-}cDpB*bZI(S4KKvvF8|h%KbZ#y1ia zxRf)DC>1KkB)0GV^g#%rt7Y^}UEhbmbsIAtJt6ba-uvO_DnbHTn_T6XcLgmzHo#by z#=N`7?1C^({Es>LU->p`c<99G!fYBSB9~d=S=y~oF`+k}0+9bhS|4^Ua@3Pj5)$&i zc8dRU`2TNk_-URuLO;aXna1co^HGRVjk`{ebG)WgZ5Bdb|Lhtf9-woN8s4`D`$PRm3WH;ahS5F z<#q%q1#CUajRyXNEl8^bg_H(Ixt^7E0X>?(4fA?5Hzw%-Fr}K(Ul+es^NW7s_HP~w z#8=xe!Saw94?e&3002~ZZi%;SbQ|97MMQmmNPuKEy@aDwHS!SmFE*j2MvGcXx zHy~?j9+ZWSH7QRg{dz_zN-$g-6DeL0LIwdzUd^Z@Vre}26)^nk7<|nb6;Na;V5)s$S}b z0f53_c^WaX0PWFAsNS6o{8#a9#)bx2XL=0C6Nf_D3$qaE?Ou04Oh253Z>>3Nd^8V@ zFJc<*6y^MNaC`_164O2mjB8Hb_~{Xw*5MEmi(=^Z{9nSceZ&+<034a?7Hx@D5Ke)XlhL~SSl3~p}HXXfyQA2kBg=n76y%m8{9?wEu$j{kb++-eZhYrmp@lmQXeW) z2KG%JHKCXq6H#PD2H?J;g|EI;X%tPUv*WjDGNs-{xXUXs24zc@ZerK4h~$2MmK1p`kD)fTW*m^JB8fsI|d{cI-)z!xIbh!Kj*uFnzu19UV#{!O+wPX<7P8u1}#J z%eUo+KAJ;fFI<47WZ54bpi8p>h}(w1gJudRcL&HV0uyGgUUmV~9QIY+()|P;z@o1H zC0l(k=0b(`yTfh5p95<{2!2>L;U+H*(gn6$v2qf z1HA3-mS-V9(Ks(YQ3j5W^4(u^j?)pyjL}1*X0+xgX27s}=VZL95*R#+S%x~x#!@n_c5RJPaFD&Jp+aTr|uf^9Ep!C&Vx_?`emnZ9P>+yR z=OKU5592c=CxKMQBWz%KZ}92mKRCh(hy??b+{#ZM;>&x6Be6aJ}ZaanvPnC<|xT7#}cn9uhk{qeDehF>z*y= zpIIk&Ye(LFZ_O_=n<>Gko!<3B)>?%{QE6`X5QNYGrfd&Qd2{R_#|b#kBOHBFV=V2i{Yz}$&dvq9Dq~VcOwusXUL;)(C5dXFFCwu7)(IBlmExIWkchq(7=Fu z*l$XcJa1HTJhKQ5;dD zND?y!FZmv`YY>Zi3VwBymxp=AwgL~a2C2Im0k(fzxOnP4S|(5`ONjq9jT1!tY;`TT zX+A&`C7H$h{;@R+O7N|+P@HB)mGAnMhtiYAwn-E+{lBO+^8`@8{(d|P-BtUYryem#6*x?xEzk zO(OpdQw;P)T@(u>=c`74bt~#OnG|QKIu7>g2Vmn`91lkrlg465#7Yo9nK)6QHHoGYG+EQw;HZ_3_UCctnhDhKwqCsX+Y{`26s1WE|nAC5h1YA9dj;tcD z;(nIIZ2XD|DmD%GjsKMrH#?-md1TL?693UG@bt?7gAz_YG{Gz0`(I;3{X>F6NsPNl zg13semn$o%I3WYho76l}UxVeYNKLR=+0iZWL)`4XFv*I8SwKRPP+7mSDamOhfK$~s zHaOYSP2#T59u_m19fLoa%-~ow<*!@BT(izEmjs2qD2>3NKX9oDxcufSW(6zJLKyL+ zSRn(ZsW~Mv-neP0ZmA58wgKjyw5m2CS>y}l z4K>Q|OFUAGtwxKh zS4-UcavC)A3aCpI@$%zd(3&`jYP!oxU&_K(3o8+eeMdj{W1$mmK`xa$SFXc;il}FSSXOb2A)a8bp<^6c< z>(oUJh?NRqXriYDEvx0If2!UsDzU>W+ro+)vWpT&i=bW$`w+`q@?c;`b8amPO-su$ zXlepA!TGr_dHF8{m1szn40VT^NFS*+rK2_HFEutVMR09woGP`5f66yss{gH4-&+*Z za@B~Z6-+DCDgLRMNv-2ttAWHXuR^T9#A7#6t`!ci)-e+8o1GQ88MS z+S=gTiY#!(`d1m|E~dpZtvWrt)(5{-^0mJAEQj=^tu3~7ysedWt<^5PP9?nNRI)+? z+*TZ~)nd&2+swD6^R=x`vvr88^Aone9+U#f+v3VefF<6#UE{gJny&y;1@FV6nA%Cl(kO2HY$76 zeS-aUTy3sn4QAiPc(A)sBU-wU8q>_inx_?+^Lvwdi5Okkk(P%UFa9j3V zoVAB@HQ@Yda9pe5PV0(n?RpDm^K58i_w0kw?As3Sycz9f4r@PZ>%~B9gj{daee0(f z2Y!w=N|Bfd8z5%;4qA+N@<#Mn6Lej3^;c_kaiw)truLK4vMD3L{H7h$x9kNom-lX` z4eW500r7@^q__AWqk4}6qsE7$(lJd4FwN2jdB*$r%3B3!`|%@c@Y?FiH7i^!t98y; zgGPoT+J`ss2Vf$G{XB<{@rPNJx3NDFBNMNKcI>7%pl!@6$+Ea#)k;{ywC{cLYTUJ+x>>A+p?NnyqjE(56^+VMoo z5lqh>PCghPh=`qyy35E0t^0MK@@zK)rp$6p1 zCfdn1+Q}n^84NPm%6)8S*d(-L#zL7$eHu9_`Et|BNsSkk@X8Wd=d=eGIo^rcZhtgglFP z-t1u6rTSi$oZ8SJ)uM%=K!yy{iah7VJ%=nahKMp?7WpZ*o13LwG$5Q8l$pmw0b0eBV- z#urmFR*c>!65bbWc_!_>mYigkwry8kGM2=0=DjYK8t3QnIwlD(CP7GDA#_7wgw>1g zP1-1NPs>`8Xsc)yV+M4K=@Zk#UhC&?=eU?Wljm#4`Pq^AbFqv>MZREA` zp)-cEv9&F|KKse~TP-bjY_Dc)tOJ1SXge%38x)_AJ7Cl6eWS8tzUH%{;5mYoK7vco zk1xG&?7rYcJKD@&4eJrXMyc>5Gb2d1J|MHbjXW|*IKc)Q&gNNXk=eI_ynAqwXEV5GYsP98*On#TN{7_-EQX-+d1jtv z_Ru@FSUdNui6(724q1ryO`}#tc=yF*_t7%)u7CU#^i z275g>`Py64+Beyy`eVnI#nWd=U!zlprLT+*O=XYFcsFe6HY|ybEWJ9Hm@y zZJeV-)gur}K6^V$cY}ZOlrQRZkM@Wcyl01U>JYiHq_bMUb0*$-)HsRKT6s3_i!#rT z0`HIE4>}95-U*^V_l!K2<~dN-J-8@uE=QVD%2-yDn#x5!Ow){Ve2@_#-bbR}-=DlN zrw8#_Ukee=??+uA>mJoqZZvqWUPOV2YF|iw(O9T<m~83JLk-eQ`sBRZ#(Lr3o_m}SD<@pzDs@7hly=#67=IQ z0uPb$5AnnZ^dB36z;mW=2h|-j+?Jcez<$oITMF-8@{EQgaN)!8@5kGx8gEc!)&&Cn zn~2Uvb>>}|&rvPtm6Y$f5ezy?=(@j&dQOo;N%MK1&2}V$cma|iL^8Y>%e|OpUA}g{ z;BP(*OwO=pjWp1mRHcnmbuM%Nspw6OOIr{em)m!xe;WOH{fhbqumh%dJ3#lK2&*&f z2>8Jf@_G(mWDAF3wdm{zp%{u<67o3bWBP$08A zO8i0%Wu9!_3nNu%$xqfoY%M7+wWc|1AC={*K}Pq7hF|Ps6eD!umiB|BZw5ePBPz?( zLO==EcFltGB6uMp$zq1XIhkS_7WpAV1uXK-(F)*6#w&0Uht?y7l2}-jibU`dbbR9+~Z0m{WaneX?bcJ7mGtzN|A|_V1eW-q64wGMG+A*fExIQD` zi}ug8fiC@|ElAy8dnIE5yq89 zvaulJ|hO1tPpX_)An zWa3b!vG=>hHaeNisiJxj?fU0k4su=gufF>3msL38t;fJseLW5B;)yw3Kr2$TEtdi- zpD8qnE-5QHl*ykb?IdVy*7-s3+|?UvzBZ+A462#3;5~1PcIXB3JoF@X@Utyzx8e23 zX2EK>grR?Q*wKiXq#DVSZ~ePjP5gNIqr4KBbUQf}mE=Ah4)Kn2;S*YNmu@?`ght0( zt>t_>CH7G@zz~zJz5bEd%(@Y1^VS_5g7Y|M7o9|K5U-*;a*&=xt#T5i0+&wo0IsHN zowkzm_<2nvGs`Z3C~M1z!+>-1z)lZzM)Up_kahj5m=A0&qfDuw+uGX5lxRL_%s`X2 zg5BH=Yk`Wx){i7j3g3hHFdbaoMl2b_E{qyPCh*;3>*Aq^`0=?yTZ4J0MfQdm3$fZQ znOXmEJz-#Sh-$zhA|$o^x~xn2F1 zb0i4WYZQSP8OkrZ5R&Q)17)`HgnlIT?KlxJZyslhM*I zf~ZWAWBAN1%I`P9-UCyApN8sODyM}mof_46{Dr+e*dY%s@VP)v5?*i+nhZj0WZ^Av{WMW#78t!~ydFw+HslX{;=F|}DywdSvEs?M)Rht}js+(#v*Bct-m~~D_2`ng9 zN7sRI=))KW)Tx`UQfLWn0o9i@b$j^qMU;c}v&BR#Wl*e+Q3KcGymCIKC z%cub5XX#A+KajLptCjOaQD6o|Q@r;2E_O7R?u`X~_{?kBcxRh-FHp5QSLni^C{6uT zvniHs&f5EaapCTtOv?Fug`S<}=1gXjV4{qPy~pTyXc!2mjK$KCd3E%~xk|V09gd1@ zCkgpvsZW^M>PWIo|Bke|8#tkDu5`Wy?V{F0Vp7eGq|paH5>0BrZIB8y$H(An7e*qa z-Utr#q@C(f@7TPr43k6Fp9MX1!-?JyC$((%bl!GMMqYhIA6tE175U2|P$pHn{ZH2N zsZ&VwHcI)&GA`cpfKI^i;r_S^bgTNPGm!Cq^;JbEsAlj>CPR`#xgpUe+4#rTTRu($ zRbU^}M6{|LciOW^6=FtKG_QU$e@UhfcvYH}5xr@gkX zaqvNJc)Qyz2&)_!*{NoQ+mae7tp%Z+Db4gv7Y_W0TU?4o9eDY7PhiU^_%7F}@sHJM znCMeX3!Ag>fX*;=c8-sYZgx)W`~=rv%K*>D5xhAUF)CiW1_AbX|VAFkFtB2@@cfhhHuuJ(-& zZ;DFFumc5rdq2sn$k%RJJiBTxT>Cj- zdowWZjinmhS{t|Bq(jFUM`rl8hpa0z9vFN@k`Qo2Xp_`G={d$C*RXHw)29gaa`R>C zb%je#FMNyO&JVTy_|u`peE53z4Mt@0XXVRAR_itE6xU)Py_d-mxO&mYYr?6@ViU=I z+83^KN$BUBAV!vMAzvr!(1+XMW5;uAm)&p{suzMO--D+f*Pb1!&z1+#)oxPwHa=f9 zbJwj<*PTBZ(bukfD82*3&IJd)$-Jh4xz;Cq&dWIdMGyX?HGVi`J_rIne3xPM~zV@)~dDTo=MPLJo~T% z2DbKur0#lTlz9P;-IDpj*wDhn$Q`Q6efrXUDwar)WFnd0g$OwZ5ETbRkq5*A27e-p zs_hFxK=){!4`{vdYnO{!K?_?%2&-E58AywGa1BCA_ZvqL66l9JTZ*c93X5y^nOpXW zLl03Pcfs5Zco2vQybZX6(?#t)hkmmRI*@TIDU02nKws*MH<}>@6NtOUi<#Gp3GNSt z--+oX5B&`2ca0El@*NhWYR&HYTv{?;zjdgqxIuW(4z(CQq7oSL1bgynB#S{ zadm-g|H^zrfd@l_-75$%Z@TJdQzkpO~;0DS)$W(BD;Tl=v=xpVEkBmbe9FGdw-&qVEpW5!f*ySuns&^o){oNy5yhO zRi2zM8qHUpYSbP%&X+wEmc6T&-B@oqCzISdpA>Y1#6_6erj@%vo}53AS`d~y5T1q> znTBGOyJeY}>YwVKpL*X%=f9G}C6Psz8Q&q0hajAURFQ&JncgFq0int6kRhE%OrNjI zHss0^+^zX!@R)7f_8;6XNIaiimHK% zk}}<5S2a-0YQfl8#V%5%I%HNNaPbpDuUs7WR)P~9)7Cn!`7tGco-U|%eDPyh9-0EPxCmRB!@4MZ7ksMW+MnPx2b zz^rH-s5@S$#qg-BtKbKzte|uEDBnY!W~+xAtAwMd*#<5c$}D;OZ5YY*$HdS8=Cd=kR{7&qYc`%BCELt8r>BueO6kHXSWhQ^xCdVj+5 zsm@j>mM$nz`%>gvzzGR_a@ZA*F7FD)N1)Cj>lQ?!?nI<+6`_7}!4{0k3KREt{wES1 zd35l_CwQ-O(-6ixvId~o2D0hjZs@}5Zo_ka&qnkAW)IXGiMuM;MwH#W*3oT`+*#Vt zM{V30&Co*^-4L!I#(hd+Ry4p0LeyK?LUcB`hS5Jbh^x#19(aLwD0Md}5)H;w^a&ex zTMlGz{d_5(k|+VYy5@+-woxRiu}o~c%&_|H?^_hkzdHgq*iAN1;CE;i4Tus6`Kf@} zzrKGtPAG21;M>$A#WpN4Ghi9iN8;VP*4YyQZxIykp<64qU;DRQBT)Uy!vh!=UKw)l z7zu)mNEhR{8TS<^j72NAslE4=H*0EWQZpf9QJ{Wfj_hm5qAOtUGXRa5>GuTb4|GM1 zRbWjT!uA7JerJ*ZarwV%6dE&Z?rUQ2l7Nl)7mcxbH?jZDFNri~{4CZ;4&CRmlG9-XE(Cdxa@DPGnAcB0?W7|_3P9QmP}aXlY778?8W z9{aPI$5$HjtC}TlnuG#P!ssJVr&BaS@mqdmF`ph|T$@J2n(N}891@%Tvj)b=p16pb znW%<^1f3KPni7_ndl|%qFCH7w#~v`4MgxyC=}lu{Ph%)ft5r`ebrB1#Es7G&yRsAJ zLQk7DFqUdgb$%R`9iFsS{;7ujli7*EiJP2*{YTULq%P>Bh%Ndm^t4Ym?neAOJbd!t zphXKWdYkuoJLuGJ^DG;LbvXSe2HU(2`+SPx9NatY-(;SaZg7_BO-98J)GLR_@I$Xv7-4!RiCwV41Pk6K5q zPg>y=9DGf*{x%k^wqDAI?`6@(3*7@@`uZq`Tw zT2dVPqa6mE{pUm#F(o>J&-H5*G42r97Vu&7Gl-1i7#Hm$lo0j$8z1ls>;_wtS??AMY#@#D?UtSd4&XvVEe?}letMS*qMZ#GN)zJsx&UDC0 zAlRAx@!n#{fhWlhBACo@=-3V1gr%);t1fpd4d;Ze;p~7K=4ATljQW5Syls*D=(Tvg zlk61O>|$j6dmjoFm-1a{S>?OD3=87+x4W3 z>L5hy|Hu9;E%7;8>?x)TOP%sL;O8@|i*bUUTjCHxR2M~a307LBTPoj!H-c$F^iu%k zqiFF{R?Ge zI!3i?7?pb;8+t>qPisfDnW{41$$H=Qc?+j_+fegfnA%1B?d!SXiSl8668*lz4({N4 zKUnfMj(e(hO7Y%ZO1Yv3pKpQhQH9tdGf=xzCLS*(zxgbGSbZ#dtR#79 zN_am!emua1_&~xTLhFCNAP0QfDJ+-KB1wb*q>3!{A-f{s=s)eX_iOS*qws~iX;)^* zdg5VJ{CoTec6J+^z#nr{G!oclQhTG6%2<9DbfV7%gH&!h$s0?Bg_|&Z(7Ay;{)nhSaoe!_>Y}p)hJu?3B4e|y`yAE z%LxLOD%9zR(|_Fym+lvH!QQVfpa!FA%kc zQCzjnzXO(@)pqQd@!a-3S-&{u38KX>YlZVZBaoA8#H#u%3l9j5sqo+c%+Z9YL{a;i2L#iN5# ze&?N_uu&BxfU!&kNE3(geSX(A7f17prcz`lrn+0mCBFoGgR)oWI4s1_d_F1mPE@BZ zZ8_Cc%8}X)K8kvS;G)f{mwZ0WZ=-HImnr2P>aNZp!l$mO62v2~9Jjzm`!BToUugON z7ijsDSJw66v0K*QlWtsw%hGXPwUFzuU9(0-c3JVJ9WCy*o2X~au%Bv^`3WsM0NsyKqoS&g3&X?RPuILB z-J1)#D(U?k9;+S4YT{)0VI+-y$&a*nTmTRRu6DSl&0bv|;92yqUj;^Pao8XM?efC- zQ$jwE-bQKwepGpA1oA1bM;tb2A^;jRwk45V=RUx_g9`ybMFKmG#7Ok9h-Q=x36K|u z0^bC;d{{;T(7yH{0r+9gjiY@t*Yp<901#igef@Uvzd_4UW>;_!Ly=`e0IIL@;jCjE zVaxF#d9oqV!+KmPxOdLUo>-uN`37Q^h0>A+PJkdjpKZ_9_B=xo* z&K-ZSNCe4*d@(ncD!-o`CKO48YriWD{2wU-2;@w03VLS+C%t$)2wrhWO2L^3rE~+t zf<->sQ?wwEe+W*TIt#u@cNCYWKBbZ4;No?SnUiDQS4mfH$%t%tU{3Dc{F4BeyI<^{ z10*yPRi6nVlNmf92fxD>j_7$3ljy4-(ZMeXR*IWY^%y9UoP0Pq=pGrf^u0Owt09F^ z2%%0ILD)iQ=4CG?Q}}aZV9hkFL_8Yc-xUBw-ei~>RQpfRNFN{j`Y&=Q1cYujMMIPZ zGh_f`nzA?)F+mIoUJM|)x%?3I54;~uZk#kcSp5s}saUMG=B+ufx(L4HN)Xaf2?4OZ zPd!v3m5=V5^xcdEfOCQfjrP}@=Cp`gt3`?;2n`@hY)pxa2w~_nt7_g(fuUmC6EiCC z1qk4mmZ(<%m7oJ?IJ%${|3&K-c%=Q#z?grZkGO z(aR$!6VJgDpdl6SS{C2tmqL9>5u$2G!)&@MZSNjc2!_3j{(^EsNVP-yuV$f8F;D-e z$nort5~qrR)Y31nL%oSi4&iv^A(SJy%nm{J(BcE7oa6!_U7b^Ee?{;hRflI^j8#z0 z+|~4%*TfKsocu%>&Ip%{G%nD~{4pHCzA6hivsM(Uc~yaQJT}Grt3-=f6@>F<5^Hs= zs@X|fakSs0(X~BLl4|I*z8icbL6P(OA-lu~JH#ew?UFi_|-<;hjY-^-ZvcZ z!&u+~6x;Wm%#B6XbdDRPtHfXcDDK+0pyGw!x2=4C1 z-5t`+`y=+*cf3Z%TBn)gn)gJ+LxCZQCq^@|hBxOhVp%=c=v1yY!3u&w%)K#7jU;sb zqokxqh%DK+;!v-p!pghyr_JlNrPeco^gG6sfHkA4DY`g?xdb=}T!WS?&6IAQ`aq4M zx?MrF9kGUYOVaOz2&1GM{TI~z4YfeKRx58J)?2mfy|lG zTwZk3_+SL@Oq`n7;slPEJ&S)0F(0fxbwncENNH_(SguhJ7RLmUD6>C++Kq-#v;#DB z*Pcb+q>tKXtNvAZePQUhMX=_Os0?=L0g1ocn`jPu)@$&cE|^Nr=!}uyF^vn8BX>$= zTKI}))wJuew+B5ys4`$qa`_h-^saZ?=`wTG_pBX^T65?yMiWWz{xqNzqe;&kkHqo^ zPI4_^*flTGncvq*#!MoZS`8MA?kE5xEbEW`^w=yLCe<%qbC>t2)^=N#7b9N{q1tO_ zOZ$>wtrrCr-v8|r%@$vYFS}d*@WZjgo4ilMHp1SL5rK<(?)<{q%=Sa}u2>y=MMV$!G9yUgLN;?X?(RCjxb@qS#$U z{U^@8c?X^ZKE55&77a^$8keb1W_26J7rYE4PFP*W@x6BosL>LZ`k~g~@wgWqwg51gVMx z8w3UaE~~(+R^RdFKu{TPXOgm7+P7AuUl_}QXYPU0OHKo6eu&mSW=)pP+SY6b5?qVC zR^N?!7VY2ZB)A}f-{*p1_hCaztor#v2Hm-BZv%*uf7P`GGZgXJCBqhmIE%=JiYV}{ zAnWd>X&Wg7(dm8V&@*9C_;c3g1#^V(eL0jD@`pN1&8Gc7hwlc^BnQ{>Kgh|-p6Sxw z)?q(9j2s{SXj_LsFBsI{__=Mlb#!IK10i9^;6H!bxxQK(#47}b9fcwhM>sEs*&jqC zKKX2Zi3}Kk9%&9Ikt|2lZ@MYiiKN=L%fa~ z7l?iLh=)IpNiK>*5yZh_ib>E+z}JtDy^Y6;NEivn81KcnO^@G0Nu*&)jFL{E62xUe zrH5Q4j(>wDGU^LpDkdP!Bw_|8{ku-&WJ;RhO!C)E!henvioki+OMaHb04QRJFeNcW z2*4f3$$7?WJ;!UJQY(!ls~jiO=nFuX6Vz=|&?Vy$B$G*PKJ`jq*i@t#tR#t{Cc6d4 zYmuf3t|a^vO#T^>e3+1Gmyu$4oGea?86+KtpqTD%lM-r^ZZVP&uAdZXlYViH-UEY? zoRI>Ez;HlKG-68kX-cs2oZ2jmW62$oS7QQ5-eRAt}8~ zKhvElAzOe=lEmR6m_MEewk++jU z*;bGK-hn=dnqOs;oBEt+(2?8tob5ITzuA!!_nh@`oQq3VfVG;xb)54|ihYNJO<_RU zc9jPe%!L!mw>?ffUdf(b;S=0OAY3i%*3Wq$En)+tZ$#w1R1}bp7Er!Wd|EB~#9RnN zR-CAq;d7i;$CTuhQD`L%4}i%f5-MUxD|u8bQmxF>LCY1&EV=H*I;*E>xGFGR%@b!X zgk)sP7~~&EqHD-~ldh3=B( z@9P(acP9Hs6guC!h&q=Lp_O3@RU~;8xENFrXXcT3m3U?rd5;#H)t4BJmK?!Q1bCqb z3YCYD6-o#dTNz|TzGTv%rcbW$p(Vo$j8>$-6eqkC^OBWi+m;AamI;wn7Oa*PWmXq= zk~eIl1-w)TGM9^^6@C%Qm#r*k7%jK`Ri%nky;)h6`BL*2P@QK`Jt|ar6kmH{P}#DB zHi=d<6Iof=Ss8j#iC|lJf|})2!56BHAmmj<&RpxpT;Eq&Kygx$XIp&;sKfx)9yw#- zcw^P?)DpbbPBGWbt`^mJWy1i9A0ulFD@u)5M5%e?{(03SE7k8ZSMLF!)x);ch0K*B zkyvN>wTJb!I4oE=uPjCdp9x=UiM&5PzmD9n)H1RhX*IcFB*jV-Ru^o~R#k#RUQfDO zp?^|=QdKe>SqqCys2Ry9KYC8H ztt{3<))jDik76*LAX{;oYg_f zQu^Jtd>^&SIy0l8BiG_r2pd8Z2YGuSu*EyFlZ&P4PgX6jVH0jvtsr^Bk1SxUVe1E( zwH5BLx9dnFZ@w`A1)~*H05ikAa??l~RV#$w=j4h2?Hq<(`Owue_NcD>lg@I(c61gX zZ&W+ZYkhlFdnXIldM~C$R`;0_K|&RN={K};>Yg6E4zbgw3$(gHV8@+79qmhlaz^Qt zw}kg2oaA=PMpRdYa2NV`mrrExkr0r_uIVpJ>w$OoX;k;oSob+P!KF8TDGb`RQjZ-u z$U&*W5xq2stnjB{_97tP<~S3MBC8}zLJFZz;H_5>G*Ap^+VO7ZSL$wy>O0KpCI|JM zRbj5@V=7l;ie2|{)AWB9>8Hg2(XF>KMOVHF*H{?TP}yZntbvrqa&6s0Y2)Fy#|CVp z2gKHg@XqQbSz86Y`$($0Ppi5qvb$B*!SlV~d1qFr(tm@RBK^0o!275kaq{L2I}lkE zNXoWu$sixFmSjK5BSs{3Le`ymHqf*-g8Md-|2E|0)BY{1UD~HFCJX511NO`Y`>YSI zN?`iF4GY2!vcjSTu?}ic^!zdE=#K7pWNEG(t%Ou%e(@^(=2Ltk*l3vKi$>5|EYg=F z(l^TrTwEUze;X+AX(BS}E*o!`&F&-19`-hxY@`_TV;!5v!Q?j^V+tI*iXPJvfrPG) z(Pu+mUR$Hb2S;A}6Icfr3`QW91Jm|ENs5W$^#K%zX+qXvq1Oo+d+uIri4|0(3;FI;0x z(TPGp_EQ&LQ_oc$(!dU|NE`cE14LvlNkr8CH&B{w37M^1igNhZ`*ft?d`H%3Wp+k8K`+MH2Yw2fK`!9#l%9^Dzst%{Z9we4aFBS-opH>O6a@BljbXoD+{a z$hMNtxngWQOK&{&FxDDwwD=6I9?U(5u)lS{__V3mtj>n4)=#WPMUDF9jQTiq`-!eq z*@IhZHr+&)Lt|!0H`cV?*P0^oGb1oQ)r|zs5`+M(;CHt@p7tHg%S# zllFe6!efWx{6^f(W#6@nT_5w^{boF9UbB*wGj$DUwqYx#e66HAS%gURq7v-m`JSry zfrGm@@kD1LCO7v$;Qq;@dZWLfjWvpxzb4&h_n2AVplRvd$-OxAY?8W*(BuF7*6lYB zI*m>c{kAP)I;H^|R>m8~%5C{N8VP7$+9xIU_n8zNER-{s`(J<0{ z1lI2s{SL6>&J8I~U~<=!L}wi@&b5_0EMf-xs;8=nr59XiFv^M8o zYS&`L#(3)X4OF*`HuvGbUYR@Qf2m7O)-QztaM9DC`Z(Rm#~modov(DCn>g%#5gEMo zzJ5?@5Ek3WIWK~G9`5Pq*2W{1Eb{?1&xl0NvaxTy&z`^eKfGQH7x%2`#_p5(-j1># z3;jDwjm>rY^hyQ1_pHQNhao-RZt3DRm+HDubr8eZv;zdS}3p(CIi( z1GZN#mQvIYf%Jdza5@z?TA0Ah)|6GFP9xG=y77-Q#NGBI8dj>$}r8D4@C+8IJpzJu~5qw(jLMX)!LuPRPlvEqeqh^ zX5m$^Wjxeqq{DC|6OLUp9_&|J9Q=RxaxsplN;n+MduqBn=YjoCsDzr2ewIum2sy0s z*lJa3H~gIFT&U7*{NW!Vn&rl3cmrHF0Hx^G8sDEEzTA%yUv2e+TIsHRl_74he+#?-5BVW)izG_aXz??*2)nnHqJa{Qk>N@5bxIY{G+bI8yiR4OCFn1!wptem#5RF zg^T<=%AmDkaX+pd1Gt{x&oEb?6s(A9P_4EeXa7q1eD z>OzKPRqS%Yp69$%qCoV;uM!``Md3SLC4@$L$6&Eb7RBXZMV2zhY1Q_NsPY`gs!%7T`%G-h>wZ9003Elt0yV5G}=|`;dj~pO@siF(RrJnx7U#*7V+Ad9H zfb1$_xY1*fCW#pv`jwI(4mrw=s+$zu26HzDRj{zyzq&q@)uN#3;gOYOv>x}TvlOO_ zfE?GRrBx;R7Q&!=)m%$W5MlPVc94RY2yTM#Q4%Gx@Kc`zW`z?0&CZsxN5B7i0lTuM z8G4Jy;Er~+y3bJ{$(Vp5U>1pGS92FzJx%D7s%(t&-5x!g03dG#66;=^l{>D%d5JRS zD+{Fbf+2OI=cr$%pyuA zk?V$-%U!3)%WdBI7&+^sWdhEqli!vF#Pshhik3ZX_G11;jaYqxP1GJNdF+1eY)-q+t> zq_cUR_v>BL^NwDPzV)uLQ`5kUvNb*w4&vBBY z;#gx*!TTK0GpouYN|x%zQ8VNVMS)k~*yiMcEVfI+rMS?4lQWt4J)e-WXuHQoVPBWP z@5Vm@d*=G?bMEAmv@2#vK!#9c^h7hma~5fh-AxedXLg4GbZW7x_4=S?$Uz~T#3Q5b zWx7X?hA*po*81M12|h^Xd!D0}kv`d{iJ=}uM`z4NGO9LYf<1e1s7q(=mo|N^3ngdA zI>jaC-I~!hZ7vUg@GuESdPHj1nF->A_6tpN7cwks)B2nlHgIKzfSD{vJ#(8?^M3HR zj`KOKvF^=Y>y+<$mZx3y(n|tPX_aD(vtree4DRzxUF}4fwh*h{s-wtGOmnrk?@~(a z>X^Z~CEF4ZN2_eL&h&?C)j7JIWmItlI#NMxiRd{AI*%E3b9H$RqxCmCzHu<0TVYa- zwH$iu%HaXZ1%b|r8kGC#+hS=e(4A6g(-zqFDCbl#kUbp|^f6$yylo#oxBZ>xZ3f@v zmUi4_==x7^?Z|~cX6f86GLJYiNUN?*e_+$R%z*j_kw6uy$(61y#b4}U7BD`QgM^*; zUwVJ%kUS4H&daQ=FvoaFwJ#U(=%96bde1Be3hVUU1rUe3#|)*&F9UPFWj<4YtY?XE)P1e$#@#~?|#f)0)@ErI4uU? zAIA9muABDcL#Dbf4BEQBK4QMj&8?rQFf`XEEjnZ>EgyXEezw)yddUVt&(Y2OZgV(1 zkL2?T-z;D62%Ua)3w6ETiu1m`zUS|HQ_vtv_Us8`LT>BtxqC<9Ss!gKiQ#;_Zf z0NOjdE&1t9l3H1UhL-+&Q71;U6vl610%cyV@m_!Q0Zfq|a;>t5k@)Iy~aX)`{KhpJpUa{=IA|WLVY0B|I3K1|Y zfo$Lvfu@3l7VXfeH&{C)m!i3UKX~A4F<6mD#+nvPJ})Sr;~%_I{I{MZugQ!E}ZqV9HU* zn>P#p;-N-4!gE}gWBEPoE&~o5m;X`S?+PCFZC3bpC+CEq;7lTe*=1j4gXEZ|#P|p(|8#3C6Q5ByJ_>DVO zIS#101GLEiir0tR$04m}@-c72EeMcOp7`#)(Wq(h^9YRTaKP(KIdjrEnk;>IB7``j`Oyp;a|*Jpa<_8{_0h~OS|Tm8l7w3Sb@JII-Bm}~Vb=V|yjF>hR*SYym$p&NqLu5Sa=^R=;u0^skmWv#=oPlp z&i~M|`NX2DtM2zu3|-76=NN7AgO*x+U3Z zGT;5wU-!Dd?^FGJmmTkyM#A+x2p4Nsbe-7rf@9{rDAhwZh(^qonQWHh&6eXkmVMat zqg?e!g6s-a>4s*sLF~{&T2E}%xclBaPMjxrD5H| zYFUb5C)--l4}DBoySb^}_!&gil+>btRsNct|ZHq~Mr zpRJKV3vVBoIIC?un?ta@5w|dC)c?bH#K9Q4y|&h3Jls`)%Sg=?WYoW4#E5FLpklm5 zY0@`Hc6z^Y!D|BPBXX$tpvb1HW5x(UL44`OxE>S(Pn(-Hn|8RS6NHprms0$ zE{UtR4n~K*xm4TeB-W;V`^Fc(rVvS!(|Z#Hw{J+BCUJq=s1K&uh}#$@hPkr&xbb9w zpIeATW{3wHJ1XB^_BUrSHyJ#)&e@`G7Pc|4K3)eS=0O1X2~$P-VW`dU9nGkqW{7+k zB(Y|sMBkDF1LR=I)HSz{y8!D&j>mAblUGk9!R; zXXe{wr2osp{+AVr9J!r@q>>i9mdq~WyB!k-!_Y?$oJ5rVJEz-UVXWOf#qYc(yI<&& zs62LQ%}fC|0YYvT@(;W8u@-%h@BigmC{gXwx0*}oeCLx(oD!G5L7zV4qN(%qPJ*2R(f=77=t9yQD7 z{LD(pTp<%nOFv8bSj*mJi*HB=dRR$39+{>FA zpTaybGXQE8lR)H~i|vYq0mE!HG?E@HXKk-zy>b=6_On2?ki?mao-V^WN-Q_>!rEoh zI@Ki6+Amoy{4jp;_%p6$+!f3>gR-RB^qkM7d{22ri&g>bN1F6U{@uqx59slmHX&3t z2@3Gxw$|@6pG2+8{sr37byyc;oo1EV1f|kc-A<^p?K6^sgALQ-@d)0dRI^<-v;&6Z9l&gdNNRI&H zf4QB?=;&s>`*85q?R0!`d9Q;_h<636%MelNlyq@`-e!g9RQ%cI3QHXCA=ddZ_G;VD z!M#67w$Q47$O*mf5Ps>h`@tDIZ^EDCny}JED!n{W^_pNSFKN;qL;i}`be)9Z=UrFS z1JV^aHq?1Lqa1(g6QTUI>50n~l_M6%4U79e46zHF=}$r-(ufxuf+^cJzoUGl6IL%W zmf;)T;q5P`H?(2SaHB57{hz7iZMf@h^Ct`6EpNrPRyvk%9+0ST8Q>#dY^r0OM9L2P zEor%Re~E|P1&pD?690aL{^Vr1-4XkBK6xVSR3IOBr}kAAByl5g$h5F{(WY7^2t~VMSl}*+T1Q+0VCQNcVck52PIT1F7Y>)Awzb=SEYm;}8EKAwQ5H zyGXotD(t~c;bH6y`_N40i=Pkv4eC{OM?R4#N3 z2)(=OcvKYEN4Jtqu%gw@e^A z8Rh<*9rmoK;VFQ97whz#?f#s|@g;-fUc>*E-{~@+*emDIhKuVZPu>%^$J5Hm>o>y- zxkeB`+^e$Ai=^kJbjuR~FSwT9`_b~H9{aU{Kc86Owf3ubi|MNuc2>E+_rKIvkZE&S z92MbqaP7a>k~S|#Y?S{~od2gd|4(uL|4^K1|5KcK#=U5ySXw_X%!E0m2)f{7TKzjs zIfE~eDW|qp`tkdGZrv}-9uQer{}a8BhA(eL;tp>ReN6vX_JG0<)1~%!5M~SmLwA=C zecAtVDF(v=5$^`2tbKJuU0wS$^|JJ-v8xUdmX&G(#k#AW4F-n$TmKtbzr=g|1T0Q+ zKFqUwCyX&p5O|Fmd3RqDImkH(>p1^WSs)mPnKQK5E^lem9iCWfM1|INi5FVOf7Aq2SY@y$i_{D=OQ+Sg22fXO%I7wHIIz3OrV2(y+9sWbtJt|O88tmFxNH`dUaQD32}U+_JCxq7 zsg%z*Q_XN&UF$B{ht%PCg_asNNv;q3V-lQwc9T4I9|i#|El5HF#+2wlECvR^o(b_2}z(bI{)9I@9jZDL|9O zYzU-0TgGb(KvgdfN@dO7SUA6GpwtV*3Bf_cnbQM*!P-Qu_Y9`sq!|=9`7f$Oc#nMi zTML(JW1@q|$PM&6ViRRevaDv^CM9!=bHgaUeO`9X)x>K4{TRJZWxXBpVT5uo>%9d_g& zsNT&AWi_pJ8%gTv2#&Ywc-lmm<2V1~Y#<=~4Tb=Lw#U`HN*6W|O z{C%>yV05F|{+N24WR>PHX(I>ci3;~6hwCVEoZ&)Dk6~!Xx!9xmb5xu3)AE_lCFd8< zE|x?7kbPT=K#}+jDh8KW^4yz*!?!*TMAt!I)f@E8r6SR^$B`Q(Gix%^HkPrq7JT&k zk|v+FrW&uMbVsLjCtpYUrOt)J(jD)}_fxd6QxuSxTYKNjnM0hw`pjmlX_M&5+hXs= zE<4a?qxv#YBNGDK!}tMKt~|Kjrg6pD2N@`@W5q>wzw~(YYeKJmdwlkPlzEQ#)ZRwd zoet4&c}`{3-jyO#p74uzE|i)4sznB!>SMpISZzM!<%ynuDSO=zu6?YNr?~PpZQnsQ zbsqYfa~+r0w$HugIO*hj#~X$nsI~Rdd6|710mXg~HWGhGwu3zK`U{3fi{I=Ji$8Eo zy+&sHTc7JH+)u^Doi6#ma}*)*)?dGdk8eT$Is3yt`Jos2FFOak%j0ax2fzy8pzHbn z8xHvL8S_;)5QWhPi_!nKEC7Bv0N%qNZkc|TGw8)C02$&xm=<_R97ORHh-2-GgA#Zz zLH$K9n9e%jH7p1+9K>mjL){)oeiZayIQG3n$USE;v=3ukEtpm>XgNMu2*OW#6o4M? z3xg8EArNw}8zOHVBA@>0BQa}k9n9|$L=54lUJlS+4*Xa4N3uLrrajaI1zTPZ`%3MT z8Oa~zCyDS>i17A^unhl`gA(S*7!n2vjTDG1(Tgng zh%8s28pesPB8eE0h%j#tu{a88SoYyK3Ve`{3L^iyWkq(dQNDq(Ei|hJ0L$(}q)El$<9|rU&ZWt!E z5)!#55OEkDJL(bS#tnDQ7ag!dkU>yhYhnNAv*PM|`KrB;j{WQ_Qp z9_`y6YY{9uXB|?woXn{h1Jq04IgaWMPgrk{BelUg`i5n4oH91^KePFdBwAWAdR#3! z#Uq){CJp z8oxu5h+q?K9}$0Yo(Ee=~*vf`TM-tT$wS%q?xOX1q4Ew zgkyq`EZI^@PPaj>j?h#EMm?go#zaG z+w7r?l=F-vBb$QV?V?J>yj#*@N}*!Qa7<+yFR8%dpOwWx&cYd{9O!dWBWZq%U?Qy| zX0nX~v2zhLq%wE4J;pS$NC1#IB3NpoUs`TcdK*z{ds1L$`{y{F!1)FLS7x!i7kaKx zA?-}occWE*Z0Zf5m4k%{vNG{&)#@Ob)%lgxfX+Onm(2R(Vl}e-wd1mb<-(OpVRSXP z$km#&l^O%Zg4W6e(}=2F<_fEqqAst1)6r4~+k7_WKa&5|@tM`#dsUBgR_8Mpx_Xs) z2tjkqJ#(hX;%Av-=gBgL^-7c(jVQ0`Nl_awJL@|YGWb8jBW;})YW>TYZ@rSE-5EmCmR-Qo z`9fYX`&vE+ELimJRIF+SRCTFB+SocPk)FG3IwFUa@OPE)O4a&Gzjb%owI5Zr zUt6G=W%OQ!8i1Y#@1{GQ=2@lmLh{~yKpW>X0CC>Z zKQZCsQ{d%)>m!avQ?ze6jA{wBYdl^9=B;*9i+~2mJ7|?#=Q2}MPa~Pm`sI{5@0ofz zeLCS;i}`#iR-y)|(Cd6p2lm=?MsV_*p5r*w!IWlb^7eh++2FP+AoS3?-CzxPdfLJw zTp0&U=LD8nDt4sUMR)G@n~QWV7!Gk6^|}K~Z9$y^ptb?D7I~W*36bdEtWA<5=^r{U zzkbwfeUzvg?1upf&1#`jg5;1DMz7T=XO_*pB=}SoE&w7JZSszUhK_`KU6sbuqlY|z z4R)YT)vDe)bRYxk@GpwkPoy>fm{LDvVAb0Mp%2*iZIr~Qu0wdNCA!WCIQcsgqEZRz zT8F?@6*dTKr$slw8I4nT4UJ`YdX6<$lTSECw{F_RV~!7QdxL%)CwxE70|wxEDC2I4 z%+#<>D){s%8FiDfLXLeJHLDvhqRPN59o(mo6g$)RZ0Kx8b|>=Nc+OdE?pddR-E`sj zAPOZ|D!Q<&x~$>3JIs@jYX(&c6D@~xW?N)(*S?!9dQ$&%CInP>K3;5wK2-|Hx)mwu zESfDJ8z)~|IP)5yM4zaAZ59#v!|O1|iyl#_H@bq+B$5N6Im?`3!oxkskusi_b(r^N zo!m9*0~k%Jg8J06>*UcuKQUV)-WnH4rwo*P;B6&HqlS>rrjlwJ*kdYc$S2x6dPPC0 z4R#Yg2E!Qdi^XWIVLs?)vv}?~sP4W?UJf$~>%#$*kic=^3Co}wC8*T65OZTphb=lb z2gB<7%89{>iNgv%#v=Vnv-x|i#e0LLZ%->UW6_p!6@6kl`gnR?vG8SOqx_tarEfK( zceN&GNoHbHK4z2nd?}!72DCQQ`UdQXfoQ%L)1Hs59xq!It=o-n!B8%ywRge4H_ZA@ zV_~c=QIyOItuAG3nCs`vl7e2zDk+2Tc71npBsTZXSG_1F33KNCH#U#F>mrnvqtF+^ zNM|pVE3dN#Z ze(3M`_qywIIRajC{i&+zQK;CahRJ4~%1nI>INotGK;$pIFysbvHm!S%wYKcLA)?#& z{G!bRhuHX3fjI=@Z3drhS9UP^<``zqvi#;@2isOoWO0OHozpxnaPm~uZ>JmUOkHeW zdvesvekR88tZ4!~I@#BL-uK1~lAk<`e2-%Z5U2~Up>1C&rMjLQFMN1C7oR*qi#aSc zs>JM0$GMmb>uOZ&ZV!%zwgZ(U$!SopyKioxrzCzyHNKnAwI7+_(S~TClEdVOyX+pD zJQqIIpvZr}&e$KlGTmXMYc88vV$9Nct{(E4qyJ0oGw2s?(WIyAs+oWff2VxKm|9xM2*wm6<9 zPFSJyI)I#{c|AOu{!JESIRp*TO2^Cl*1lmI@iA9fWdqtcHn^Ez0bgfv+)D7&o1Guw@*qT~w zRysq;i5hEm2b7t$wCcxQPf851IPo`pZJXb?c2aUy0Bcd7?;IpGB#Fow5OTnAI(qn zVVUZ`HD{@0OLfkRl8}D0l`QEbp3`NR8%(!myNxRH=lZY!1a<Aq#Z|(@m#az~-QDQXBDa%Yq8kQhy;-@2XbqjXg8mLDb5EY^o{3fD z-l!E#3$VsA&chj5;KG@H$1H)vO;(}ju=&N=woboZ!;khK1-o*G=T9xRl&oL z#WgrkI}m+}$asFPmc$03dFToO_@6ul{#wKECx838MjFh|Tm=&czskkJ7)Rp<7=&k= zzZ5ILsTDKVH2;*)Yzbj2Ni_mojd8jrPBW;D-(i+-2PkO{#BwDBsG9mCE>;nT(YeWq z!0Se$A&QchwS8jA_0UQ|1U7Rb-6P#O#&DB5g^~`1)f)LJQYITQU;WV{`gaQ|EMSagJPM3#7u_I?50=qqg9)?fV+BMTJn(pxrUcZ zaVm9OOZ;7k2a~X{9_~I*{VVLis~T?c(&?G$wBmNPD^a=d9`plM+`2x3Ri;eDDKkA zS^K7o_EV_HyAZyX7#^@V9*ZVzy=iJf<2=>)X9oT1wjZw!P_ob4Y0U{6*Sc?5I&)s&+=b#9XRdk@q{J(oMP7rAu1R79tF47gC4Yw@BI--qpYxU~c4TlrF(tCTQ4n>pyT z%d-EG^)OY=hFUYq-7nXBK)kbWn7;_ZYsCIc(fl@?-S`RFWQyk-zHxxYFGBSkELU}{ zk(279PFy}~T$v;D&x9i>VaIhgkuJ1zlLCqq^c(@gAzHFS?|%)Y!M$oI?mhJ5FmP1 zTvFTGU@__vi+Pt>mb=oG_#{cIhl7Lk3X!~4oXM{z4HO^*j8Q3^d036_6n>5r;gxa> zxSA=3#Z8wu{GlX>|GanCe%~;$;gVO%iux#~5gPZqFjK2Cv7<4gHnqUHfIp>#V+NHr zSG8I6RO<_NsF3mtK}yl|ZLz5QC{t;woXRvtJm6p7tp9H*@5Om)Ny9H<&VT2tnu?E9 zUCSp&U|I?hWLRKv6V;1MkYcBVOB0hjdcCD8VrjVX1MG9cUQF)kNdmAOTmwln~`|FE5*zS2A7-4r70p}EjYvo+Z2|um792l3E#3o zTk@4wD%3Wo5^R}+jB&a^t%}VP9L4i;dJ(Fn_CvOFmo(reVPas+l(C&p5xVh7>x?Z$ zNwuT+>c%CsHGJWgjLAux;BVnlFSQ_@H{o0N3txM8(!_-?!>dm5EWY>yf>RlQ?Ay%+ zVFxlFyN-I(H@w4%K8Ds>Y<|>X4~=Cp0$g*a_pwh<71xtmnpZN<7&vX5N4@$&7MJ*U zStR?81m@%k4u-=dbI;YofX4#gIEIroxhUn9$2nhsXUjNTK!K_ z({{*WQ|9Jv*zkdkNpK1NiWG0CQqD=m*<%qv*3H$nkwvj~W5FQ145SP*r~WJP4_~em9R(Fsmwahy(hUUbj7)M#lrh@(=-2m78oxOA z`7P%Z=UsR=j#m@ZH)yB9oWsGhLRNC&=AM_t;$)(<-Ou&V=3emEc+(uP{?k$;#^26S z*YLR(PPMHX8RUrWN%EKD-`60f&0`Dxmt9^{R|x6yNg}XwQq?#qpke(AjWDNoXO?%I zyXG`w&SzU-_^MaBLn}Ux0%ZKk@iB({8A`sP)6^P2Fjw_EUCC!j1)1A;I3_zAKRbLU z^wlye#<|eB>-0rRGqn5SXX7JfpMt!1e1?QozqkF~aKSG~Q|!~PF<+)%J-Ns;O;8%p1K#|`*@!va4|YxZ^iK5 z#IB-R2{C$ZiX>JCNYN1@ReO6e<|Q6>C9Z7@@h;R zQ1$VwlQmg_{jNu-IgN`csgqQii&dIJL|Q))rfvXOz}?Ru-4!~8=-3*&FpsEa(QBP`dS63JhGMqJ%%BDGWK$45q+iz zLtdzII>|Y?)2Iw0Lr&mk3$39?GkDu%KAUJ!ljb1~qhU7-DTnnToq2BO;$fBDW|xpW zJZrF_7lQUMYmg4RjT!On3+zGBB1h?SsR$SBN z*=O#Zy?5{I?(FQ{S2r^!$;_F_d2=R{bMpCrf8G$_j3SsXuLg`?2aYIyQuO3&uYaXv zGKpHqp#%ggR`=uACS?FbM}go7Fh|}{@aS;m5F(>eNvIXMc3CNURBr4K3}BGsA*579 z4Qru*PKoBE?<>xrsmvNElf?Uv&nPWw(oM`LEd|52?MIiF6<3qS>bwQl8^(|syEi!E z3%`zS9jlxzD<9yEpJ$F`yrt)ejEDh`T`$)i&!`a7qw;vDOhl_J2dhkRjFXJyuQp&` zJteGJj32!oyY-GCW`V6Vj6Z3rKF^HL|4POXqC4S%A{mZD7$=BzC-8P)&(W!?U+FHl zzj1g{VP~o0{1`{>P_1>JKpc->ZmYg|Y0bJ!V4EvynpC4XUW3vngBmnV zjIT)?I3)_2(Ak~_y$zB8q)L8#(VEj*#4}7CGpVc^FNE{v&$w(yB=N$Gy0qp8rb2a6 z`Z*8mxr3Q+A>WVDKB~>)d~cl92Ti-0e%Fcsq;yZ~Tx-5AQa50lLo=P#Iw@{~fB!B| zD)0W?BxH`RXtp9QC~e{U4{0q+n3jDb!<>+o!%49v>AY3QoHeNy3Jvnl9W8r9Qth_6 zpAOo;lIK01XMdy5InSzK1Lu7ZFP%JxIYP>L>Bf299F;}Wy0ZUS#UAs$)Ft_4&;U8o`oWO#xOuWC{iz;NiU5P ze)}5rB{!Rq%nMhM|=$D=7|NX0zsWRQ+O+lUtYopPxqS+B!Te@L0Cjnkl4dg<#SGA7phPbSN- zo#nAC{qU^i@GPZ~<9q~@4%)9nr19@7vrMEA{1pgj`L%5T8}WN=os~qnGy5S;e$?CJ=S_s1~pls!OWrdS;o@R9eV7TiP2uK7;Wg$mj^)=ni5uZHgN58!3%p?Jjxk9)I<+acz^$Xo(_q>K9!|`r5zewKH9` z%Z|0n4kQ0b)G0L+4jmS;(uA{s^~8vEOj=UpCKD7c6I7Kou)GO+70Jw@(Mi!d@f%a5 z)!YZz`fHhubo3Nj)pfG5fRIC^6HXe+VpGCF(<%7c)P^aZ*2bgQx)3CQ>}LH<3oB05 z2BVA_)$Yc#3&M;JVfuE}#6%Z`ftf~J5G}LLgv!^1Nz3d#a~5gLCJ*g$cVQxZrx`nj zX^1T{yXzL?oT=cPDGT!!9)a0G>84coCSRx7s38kx_!d=)IW6<%^T3+0%(j%PCdtN@ zn2LF|=ayWG1&_+M!l!EK6auk9Gg;Sdj=?g5lx$XY%^x}-=c~c?I?GeVT5kWoo*A*atWHJIh4E ze+U#?m^WE=^KI3(pjz%)D6g7}>jYGf{7`n?{qf04{OzutmX&VDPR)X)){W)w&^;>z z8^^-7C1UQ!=g+}^jP_hU|3G>h@t<4xz?X8 zty3&*Vn-vH;tq0{Z7f}PsuTR3L-&&BY_bs6m~T2epU2=llf zmhRe$hnx1-S*48~R507cdLPzf1cOd(#Xp$!A0jz6+17C#6-ph|SJ{<*w$4oOSB|l3 zO|h$&IciX`Yq#7j8MDj8*{{7h8W`M-bvhh+W8dRy3(>0Tw6yP{T^X9Qho#tmsoAft zJ9dN`*>|rVPhkYlsM}9!IZSamz&=+_Xq`aEj^}*siT<)J#5hb^I((T3TI_TX9y^&V zK3Y9J8LA4*@Z4Y5I=#FY-t;|Y(P3FOI$d@=-JA>R(_%cxc08nYTzylry?T01P`i0} zGCt_wF4dBO?tIk~vFxVgDMeE7h_!}IauM_yiDK0ZEve*RCNJ_!g22nq@c2?+@c3yX+| zh>D7eiHUvw{Q1k5FA@?Gl9G~AQc_>PewCJ%mXVQ>m6er~larU1S5Qz;R8&+}R#s6_ z`S$Ibs;a7*nwo}&hNhua&mHWbMx}@^7Hcx3JMAf3yX@1ii?X&N=iygOUug2%FD|uDk>@~D?uPoRaI4W zb#+ZmO>J#$U0q#$eSJeiLt|rOQ&UrOb8|~eOKWRuTU%Rudpj5m?&#>~?Ck97>gw+9 z?&*11+Sl9H*Vo_QKQJ&bI5;>oG&DRs41qvKMn<4e=*zMb7z{QxHa0#!J~1&dIXO8s zH8njwJu@>iJ3Bi!H#a{&zp$_Xhr<^a7nhcnmY0`TR#sM5SJ&3o*4NiJHa0dlH@CL7 zwzs!;c6N4mclY-8_V@P>4h{|v508$Hj*pK|PEJlwPtVTI&d<;P{{4G#adCNhd3AMl zeSLj%b8~xpdv|wte}Dh*@bLKf`1JJj{QQhSApZUP_a7(u|J?%%fwYHBEmxD<6NL4O z&0x4DuP=<~okFHuZT>(MIj`;VaBaa*>|3cwYWccCNFs|yxdEiE2%7rAd?Zu8z8IDv z=zO>gsV^DN5f8$lQD`WgERaiNGaP9sn=Vl;P{>kfET65=skdDjX{?y9HtCC`QEaM& z*IQ4Q8$z2vOU+JOBUy^gRV(f8mxn9R=IXUBe^gvrrIwnFzVKJvo_(Uc1%N*808iT&XBp<+g@{=~9gfBUoGG(R__LG+Vj7>13(J`Dhi^-h8&!9fV7# z0&e-cHJr$9JO*yP+?yy+{Qu6D3Vvfu$6{1?hl2c8m61|#M|U4toJA&p4iaOW9D)J{BQ2_tOVOE21QB7wgzJ_(Xb0_ zWMo*h6GS-W<6^~ef;YqICR<*U{qfcA9d=K!Z)GR~XJFbMPCerMpl(=@NNh6#Bnx z>HiE{%KIO-w8BR`6g4^X5zcf@z_*C#S$UM`zns58AA0wa1a)$r0AEIzpzG=ckl&1Y#nY~1Ist(0o#eYSq}r^`jCzR-hJAGav%lD*m$<7msz(;s?7_b4aO zSufA+i<+7Bh{q!SkG00%LS+4!`l1V6rY3+Omm5^RESBnJLt!e#RHpsm&b zWJ#xKLWg~RjOBHA$>HuKlVS-rM|@-s_o-OCo&a_Qi%3p9Dinq8Ks-}qWF2uh=Jb9e zYIwKnmtG0%4}S)^rY7yE#k&*sk&qZw(EtkeRA>YfGK@&#(ZB8A5CyfMp&cWmBdt%; z?*WIe?)~HOKBQp!@Iyd6NEmr|Oc8=*Um8KRXbZ2H#&obdj*`2v@je7`Sh31;qa>i> zu?%qS%tOC;a-zzFZ+`HQ0jWElpnPy5hNK@TYUG;)W^kln&kp*p?>Hs*lT0ihTZfBr zB&KaF?;?`!xg5&teH}#iZtR)HS|#3QpCFX>MEd5G)rOr_PnWG0zOD~@C#dHR_4j-^ zAyu{doQRG~ll_?{k{`=tD)*{#k3_l#El#;1U0*~TV^k0pq~2i50Hc*Llon~BK|-E4 zu&K~%>nHisP}um8#V+KJnwG6lVkIR`;lsw3n{a}JCoVziG@_o~i;uztMAxu7)RdugQR^Qz5f)Bld=aKj8 zka;t?-12%+H1uJsmxEWp7;zEH@E}Bg;!w*9@F$^d1qI8&@ zi4+c;o`I(zxsk*2J^SLM7CtiB9fHFSIb%^6CmFriLC!!;e<|*FV|B@j{m(8eWMcoh zFpvwU!cVH@ZMIt*xsFQXc*P|M`~+!0?35+Sv*nlPr{b9(_HKqa`s<;o?OR25uH#Wg z1Ap<$sFAq2v!HM$%Wm)`KgZHVs39?!G;7v{+mD`T4KFg)$y*9ZlL>bn3HPpT0Cp^T z-M$9qNd*%4)~t+Vtf6X?_ug@0F^4>wUi=oXMPlL!9@K27Ix~<*>KI0TxfS6fe@;B2nV_=Uvsx(I)|L5678D ztmEom6*rM{u{NLazO|FFo32}BCl_3Ux9Hq0P4)Ore$87Os9iTZ2!KoI&}g< zMGq8=g@r6TU4OS28XIGZEEI4FMwvhDTMe}>dY#^Mw$o6f1%|F;o?X|3(fnd=7F@0E zbk4E!Ir9A{BGfH$|BtVLBIrGM9^KEqXUF?5%Y6IlJ;D8OkoiSPiTBQ5=leM;-%Do! z@0C~8mlXw1hq~{@rr&XU%mxKfE#LY0#7%n6cOi~KOT1Ra={yI&7#%sT`Kbx338D0KfCnydha96;$h()`~pK2 zN<$HL*6u1;UXFLv9p~XrEMe+$A>`I!LZv}N0=}+&NWLvDMP%U}vf&}afgJmR_T+A* zg6^r!)Nyy=ee01`azUEFfHk)e9bm|?o8bgms1IhOg-$4;TqLJl0Aw$q3>Y>LhBr`) zJGy||J`owu5~3y;)v(|rbmxas>fzDni#zOP<{o@b<{c^*`C2eKg4}fw=mA;qe8i+~ zcETMcj~&N~sum1NkPCb9giXM(Apv*SEx`MIUld)RzuIxV;o%ZiegiCS8<_r73w{(W zRA+ZF?MSf~Sb#~vh!NtrZu{|e2eHoeN$5h6&hmbLAi-*_ zVF$ypz}q;1g;X-~FtxjIC3>J_4~}73dN4fM1Upt(C?%5OrQVvFWF3;~9u+8%mTVKZ z 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