mirror of
https://github.com/smyalygames/FiniteVolumeGPU.git
synced 2025-05-18 06:24:13 +02:00
3084 lines
236 KiB
Plaintext
3084 lines
236 KiB
Plaintext
{
|
|
"cells": [
|
|
{
|
|
"cell_type": "code",
|
|
"execution_count": 1,
|
|
"metadata": {},
|
|
"outputs": [],
|
|
"source": [
|
|
"#Lets have matplotlib \"inline\"\n",
|
|
"%matplotlib notebook\n",
|
|
"\n",
|
|
"#Import packages we need\n",
|
|
"import numpy as np\n",
|
|
"import os\n",
|
|
"import subprocess\n",
|
|
"from matplotlib import animation, rc\n",
|
|
"from matplotlib import pyplot as plt"
|
|
]
|
|
},
|
|
{
|
|
"cell_type": "code",
|
|
"execution_count": 2,
|
|
"metadata": {},
|
|
"outputs": [],
|
|
"source": [
|
|
"#Misc plotting setup\n",
|
|
"def setBwStyles(ax):\n",
|
|
" from cycler import cycler\n",
|
|
"\n",
|
|
" ax.set_prop_cycle( cycler('marker', ['.', 'x', 4, '+', '*', '1', 5]) +\n",
|
|
" cycler('linestyle', ['-.', '--', ':', '-.', '--', ':', '-.']) +\n",
|
|
" #cycler('markersize', [5, 5, 5, 5, 5, 5]) +\n",
|
|
" cycler('color', ['k', 'k', 'k', 'k', 'k', 'k', 'k']) ) \n",
|
|
"\n",
|
|
"#Set large figure sizes\n",
|
|
"#plt.rcParams['figure.figsize'] = [12, 8]\n",
|
|
"plt.rcParams['figure.dpi'] = 100\n",
|
|
"plt.rcParams['animation.html'] = 'html5'\n",
|
|
"#plt.rcParams['legend.markerscale'] = 1.0\n",
|
|
"#plt.rcParams['lines.markersize'] = 6\n",
|
|
"plt.rcParams['lines.markeredgewidth'] = 1.5\n",
|
|
"#plt.rcParams['savefig.dpi'] = 400"
|
|
]
|
|
},
|
|
{
|
|
"cell_type": "code",
|
|
"execution_count": 3,
|
|
"metadata": {},
|
|
"outputs": [
|
|
{
|
|
"data": {
|
|
"text/plain": [
|
|
"'\\nref_x, ref_h, _ = gen_reference(512)\\nplt.plot(ref_x, ref_h, \\'-\\', label=\\'Reference\\')\\nplt.title(\"Reference data\")\\nplt.legend()\\nplt.show()\\n'"
|
|
]
|
|
},
|
|
"execution_count": 3,
|
|
"metadata": {},
|
|
"output_type": "execute_result"
|
|
}
|
|
],
|
|
"source": [
|
|
"def gen_reference(nx):\n",
|
|
" csv_filename = 'shock1d_ref_nx=' + str(nx) + '.csv'\n",
|
|
"\n",
|
|
" #If we do not have the data, generate it \n",
|
|
" if (not os.path.isfile(csv_filename)):\n",
|
|
" print(\"Generating new reference!\")\n",
|
|
" swashes_path = r'C:\\Users\\anbro\\Documents\\programs\\SWASHES-1.03.00_win\\bin\\swashes_win.exe'\n",
|
|
"\n",
|
|
" swashes_args = [\\\n",
|
|
" '1', # 1D problems \\\n",
|
|
" '3', # Dam breaks \\\n",
|
|
" '1', # Domain 1 \\\n",
|
|
" '1', # Wet domain no friction\n",
|
|
" str(nx) #Number of cells X\n",
|
|
" ]\n",
|
|
"\n",
|
|
" with open(csv_filename, 'w') as csv_file:\n",
|
|
" p = subprocess.check_call([swashes_path] + swashes_args, stdout=csv_file)\n",
|
|
"\n",
|
|
" reference = np.genfromtxt(csv_filename, comments='#', delimiter='\\t', skip_header=0, usecols=(0, 1, 2))\n",
|
|
" x, h, u = reference[:, 0], reference[:, 1], reference[:, 2]\n",
|
|
" return x, h, h*u\n",
|
|
"\n",
|
|
"\"\"\"\n",
|
|
"ref_x, ref_h, _ = gen_reference(512)\n",
|
|
"plt.plot(ref_x, ref_h, '-', label='Reference')\n",
|
|
"plt.title(\"Reference data\")\n",
|
|
"plt.legend()\n",
|
|
"plt.show()\n",
|
|
"\"\"\""
|
|
]
|
|
},
|
|
{
|
|
"cell_type": "code",
|
|
"execution_count": 4,
|
|
"metadata": {},
|
|
"outputs": [
|
|
{
|
|
"data": {
|
|
"text/plain": [
|
|
"'\\nr = np.linspace(-1.0, 2.0, 10)\\nc = -15*np.ones_like(r)\\n\\nx = r\\ny = list(map(WAF_minmod, r, c))\\n\\nplt.figure()\\nplt.plot(x, y)\\nplt.title(\"WAF minmod limiter\")\\nplt.show()\\n'"
|
|
]
|
|
},
|
|
"execution_count": 4,
|
|
"metadata": {},
|
|
"output_type": "execute_result"
|
|
}
|
|
],
|
|
"source": [
|
|
"def WAF_minmod(r_, c_):\n",
|
|
" return 1.0 - (1.0 - abs(c_)) * max(0.0, min(1.0, r_));\n",
|
|
"\n",
|
|
"\"\"\"\n",
|
|
"r = np.linspace(-1.0, 2.0, 10)\n",
|
|
"c = -15*np.ones_like(r)\n",
|
|
"\n",
|
|
"x = r\n",
|
|
"y = list(map(WAF_minmod, r, c))\n",
|
|
"\n",
|
|
"plt.figure()\n",
|
|
"plt.plot(x, y)\n",
|
|
"plt.title(\"WAF minmod limiter\")\n",
|
|
"plt.show()\n",
|
|
"\"\"\""
|
|
]
|
|
},
|
|
{
|
|
"cell_type": "code",
|
|
"execution_count": 5,
|
|
"metadata": {},
|
|
"outputs": [
|
|
{
|
|
"data": {
|
|
"text/plain": [
|
|
"'\\nref_x, ref_h, ref_hu = gen_reference(512)\\nF = np.array(list(map(F_func_helper, ref_h, ref_hu)))\\n\\nfig, ax1 = plt.subplots()\\nplt.ylim([0, 0.01])\\nax2 = ax1.twinx()\\n\\nax1.plot(ref_x, ref_h, \\'-\\', label=\\'Reference\\')\\n\\nax2.plot(ref_x, F[:,0], \\':\\', label=\\'F_0\\')\\nax2.plot(ref_x, F[:,1], \\'.-\\', label=\\'F_1\\')\\nax2.plot(ref_x, F[:,2], \\'--\\', label=\\'F_2\\')\\n\\nplt.title(\"Flux function for shallow water\")\\nplt.legend()\\n'"
|
|
]
|
|
},
|
|
"execution_count": 5,
|
|
"metadata": {},
|
|
"output_type": "execute_result"
|
|
}
|
|
],
|
|
"source": [
|
|
"def F_func(Q, g):\n",
|
|
" F = np.zeros(3)\n",
|
|
"\n",
|
|
" F[0] = Q[1]; #hu\n",
|
|
" F[1] = Q[1]*Q[1] / Q[0] + 0.5*g*Q[0]*Q[0]; #hu*hu/h + 0.5f*g*h*h;\n",
|
|
" F[2] = Q[1]*Q[2] / Q[0]; #hu*hv/h;\n",
|
|
"\n",
|
|
" return F;\n",
|
|
"\n",
|
|
"def F_func_helper(h, hu):\n",
|
|
" return F_func(np.array([h, hu, 0.0]), 9.81)\n",
|
|
"\n",
|
|
"\"\"\"\n",
|
|
"ref_x, ref_h, ref_hu = gen_reference(512)\n",
|
|
"F = np.array(list(map(F_func_helper, ref_h, ref_hu)))\n",
|
|
"\n",
|
|
"fig, ax1 = plt.subplots()\n",
|
|
"plt.ylim([0, 0.01])\n",
|
|
"ax2 = ax1.twinx()\n",
|
|
"\n",
|
|
"ax1.plot(ref_x, ref_h, '-', label='Reference')\n",
|
|
"\n",
|
|
"ax2.plot(ref_x, F[:,0], ':', label='F_0')\n",
|
|
"ax2.plot(ref_x, F[:,1], '.-', label='F_1')\n",
|
|
"ax2.plot(ref_x, F[:,2], '--', label='F_2')\n",
|
|
"\n",
|
|
"plt.title(\"Flux function for shallow water\")\n",
|
|
"plt.legend()\n",
|
|
"\"\"\""
|
|
]
|
|
},
|
|
{
|
|
"cell_type": "code",
|
|
"execution_count": 6,
|
|
"metadata": {
|
|
"scrolled": false
|
|
},
|
|
"outputs": [
|
|
{
|
|
"data": {
|
|
"text/plain": [
|
|
"'\\nref_x, ref_h, ref_hu = gen_reference(512)\\nF = WAF_1D_flux_helper(ref_h, ref_hu, 9.81, 0.1, 0.01)\\n\\nfig, ax1 = plt.subplots()\\nplt.ylim([0, 0.01])\\nax2 = ax1.twinx()\\n\\nax1.plot(ref_x, ref_h, \\'--\\', label=\\'Reference\\')\\n\\nfor i in range(F.shape[1]):\\n ax2.plot(ref_x[2:-1], F[:,i], label=\\'F_\\' + str(i))\\n\\nplt.title(\"WAF flux for shallow water\")\\nplt.legend()\\n'"
|
|
]
|
|
},
|
|
"execution_count": 6,
|
|
"metadata": {},
|
|
"output_type": "execute_result"
|
|
}
|
|
],
|
|
"source": [
|
|
"def WAF_1D_flux(Q_l2, Q_l1, Q_r1, Q_r2, g_, dx_, dt_): \n",
|
|
" h_l = Q_l1[0];\n",
|
|
" h_r = Q_r1[0];\n",
|
|
" \n",
|
|
" h_l2 = Q_l2[0];\n",
|
|
" h_r2 = Q_r2[0];\n",
|
|
" \n",
|
|
" # Calculate velocities\n",
|
|
" u_l = Q_l1[1] / h_l;\n",
|
|
" u_r = Q_r1[1] / h_r;\n",
|
|
" \n",
|
|
" v_l = Q_l1[2] / h_l;\n",
|
|
" v_r = Q_r1[2] / h_r;\n",
|
|
" \n",
|
|
" v_l2 = Q_l2[2] / h_l2;\n",
|
|
" v_r2 = Q_r2[2] / h_r2;\n",
|
|
" \n",
|
|
" # Estimate the potential wave speeds\n",
|
|
" c_l = np.sqrt(g_*h_l);\n",
|
|
" c_r = np.sqrt(g_*h_r);\n",
|
|
" \n",
|
|
" # Compute h in the \"star region\", h^dagger\n",
|
|
" h_dag = 0.5 * (h_l+h_r) - 0.25 * (u_r-u_l)*(h_l+h_r)/(c_l+c_r);\n",
|
|
" \n",
|
|
" q_l_tmp = np.sqrt(0.5 * ( (h_dag+h_l)*h_dag / (h_l*h_l) ) );\n",
|
|
" q_r_tmp = np.sqrt(0.5 * ( (h_dag+h_r)*h_dag / (h_r*h_r) ) );\n",
|
|
" \n",
|
|
" q_l = q_l_tmp if (h_dag > h_l) else 1.0;\n",
|
|
" q_r = q_r_tmp if (h_dag > h_r) else 1.0;\n",
|
|
" \n",
|
|
" # Compute wave speed estimates\n",
|
|
" S_l = u_l - c_l*q_l; #FIXME: Correct wave speed estimate?\n",
|
|
" S_r = u_r + c_r*q_r;\n",
|
|
" S_star = ( S_l*h_r*(u_r - S_r) - S_r*h_l*(u_l - S_l) ) / ( h_r*(u_r - S_r) - h_l*(u_l - S_l) );\n",
|
|
" \n",
|
|
" Q_star_l = h_l * (S_l - u_l) / (S_l - S_star) * np.array([1.0, S_star, v_l]);\n",
|
|
" Q_star_r = h_r * (S_r - u_r) / (S_r - S_star) * np.array([1.0, S_star, v_r]);\n",
|
|
" \n",
|
|
" # Estimate the fluxes in the four regions\n",
|
|
" F_1 = F_func(Q_l1, g_);\n",
|
|
" F_4 = F_func(Q_r1, g_);\n",
|
|
" \n",
|
|
" F_2 = F_1 + S_l*(Q_star_l - Q_l1);\n",
|
|
" F_3 = F_4 + S_r*(Q_star_r - Q_r1);\n",
|
|
" \n",
|
|
" # Compute the courant numbers for the waves\n",
|
|
" c_1 = S_l * dt_ / dx_;\n",
|
|
" c_2 = S_star * dt_ / dx_;\n",
|
|
" c_3 = S_r * dt_ / dx_;\n",
|
|
" \n",
|
|
" # Compute the \"upwind change\" vectors for the i-3/2 and i+3/2 interfaces\n",
|
|
" \n",
|
|
" \"\"\"\n",
|
|
" rh_denom = float(h_r) - float(h_l)\n",
|
|
" \n",
|
|
" rh_m = 2.0\n",
|
|
" rh_p = 2.0\n",
|
|
" \n",
|
|
" if (abs(rh_denom) > 0.0):\n",
|
|
" rh_m = (float(h_l) - float(h_l2)) / rh_denom\n",
|
|
" rh_p = (float(h_r2) - float(h_r)) / rh_denom\n",
|
|
" else:\n",
|
|
" rh_m = rh_m * np.sign(float(h_l) - float(h_l2))\n",
|
|
" rh_p = rh_p * np.sign(float(h_r2) - float(h_r))\n",
|
|
" \n",
|
|
" rv_denom = float(v_r) - float(v_l)\n",
|
|
" \n",
|
|
" rv_m = 10**10\n",
|
|
" rv_p = 10**10\n",
|
|
" \n",
|
|
" if (abs(rv_denom) > 0.0):\n",
|
|
" rv_m = (float(v_l) - float(v_l2)) / rv_denom\n",
|
|
" rv_p = (float(v_r2) - float(v_r)) / rv_denom\n",
|
|
" else:\n",
|
|
" rv_m = rv_m * np.sign(float(v_l) - float(v_l2))\n",
|
|
" rv_p = rv_p * np.sign(float(v_r2) - float(v_r))\n",
|
|
" \"\"\"\n",
|
|
" \n",
|
|
" \n",
|
|
" \n",
|
|
" \"\"\"\n",
|
|
" rh_m = (h_l - h_l2) / (h_r - h_l) if (h_r != h_l) else 10**10*np.sign(h_l-h_l2);\n",
|
|
" rh_p = (h_r2 - h_r) / (h_r - h_l) if (h_r != h_l) else 10**10*np.sign(h_r2-h_r);\n",
|
|
" \n",
|
|
" rv_m = (v_l - v_l2) / (v_r - v_l) if (v_r != v_l) else 10**10*np.sign(v_l-v_l2);\n",
|
|
" rv_p = (v_r2 - v_r) / (v_r - v_l) if (v_r != v_l) else 10**10*np.sign(v_r2-v_r);\n",
|
|
" \"\"\"\n",
|
|
" \n",
|
|
" \n",
|
|
" eps = 1e-10\n",
|
|
" orig_denom = h_r - h_l\n",
|
|
" denom = orig_denom\n",
|
|
" if (h_r == h_l):\n",
|
|
" if (np.abs(h_l - h_l2) > eps):\n",
|
|
" rh_m = 10.0*np.sign(h_l - h_l2)\n",
|
|
" else:\n",
|
|
" rh_m = 0.0\n",
|
|
" if (np.abs(h_r2 - h_r) > eps):\n",
|
|
" rh_p = 10.0*np.sign(h_r2 - h_r)\n",
|
|
" else:\n",
|
|
" rh_p = 0.0\n",
|
|
" elif (np.abs(h_r - h_l) < eps):\n",
|
|
" # np.maximum(np.minimum(x*x/(2*eps)+0.5*eps, eps), x)\n",
|
|
" denom = (np.sign(orig_denom)*max(min(orig_denom*orig_denom/(2*eps)+0.5*eps, eps), abs(orig_denom)))\n",
|
|
" rh_m = (h_l - h_l2) / denom\n",
|
|
" rh_p = (h_r2 - h_r) / denom\n",
|
|
" else:\n",
|
|
" rh_m = (h_l - h_l2) / (h_r - h_l)\n",
|
|
" rh_p = (h_r2 - h_r) / (h_r - h_l)\n",
|
|
" \n",
|
|
" \n",
|
|
" \n",
|
|
" rv_m = -10**10 #(v_l - v_l2) / (v_r - v_l) if (v_r != v_l) else 10**10*np.sign(v_l-v_l2);\n",
|
|
" rv_p = -10**10 #(v_r2 - v_r) / (v_r - v_l) if (v_r != v_l) else 10**10*np.sign(v_r2-v_r);\n",
|
|
" \n",
|
|
" \"\"\"\n",
|
|
" rh_m = (h_l - h_l2) / (h_r - h_l);\n",
|
|
" rh_p = (h_r2 - h_r) / (h_r - h_l); \n",
|
|
" \n",
|
|
" rv_m = (v_l - v_l2) / (v_r - v_l);\n",
|
|
" rv_p = (v_r2 - v_r) / (v_r - v_l);\n",
|
|
" \"\"\"\n",
|
|
" \n",
|
|
" # Compute the r parameters for the flux limiter\n",
|
|
" rh_1 = rh_m if (c_1 > 0.0) else rh_p; \n",
|
|
" rv_1 = rv_m if (c_1 > 0.0) else rv_p; \n",
|
|
" \n",
|
|
" rh_2 = rh_m if (c_2 > 0.0) else rh_p; \n",
|
|
" rv_2 = rv_m if (c_2 > 0.0) else rv_p; \n",
|
|
" \n",
|
|
" rh_3 = rh_m if (c_3 > 0.0) else rh_p;\n",
|
|
" rv_3 = rv_m if (c_3 > 0.0) else rv_p;\n",
|
|
" \n",
|
|
" # Compute the limiter\n",
|
|
" # We use h for the nonlinear waves, and v for the middle shear wave \n",
|
|
" A_1 = np.sign(c_1) * WAF_minmod(rh_1, c_1);\n",
|
|
" A_2 = np.sign(c_2) * WAF_minmod(rv_2, c_2); #Middle shear wave \n",
|
|
" A_3 = np.sign(c_3) * WAF_minmod(rh_3, c_3); \n",
|
|
" \n",
|
|
" # Average the fluxes\n",
|
|
" flux = 0.5*( F_1 + F_4 ) \\\n",
|
|
" - 0.5*( A_1 * (F_2 - F_1) \\\n",
|
|
" + A_2 * (F_3 - F_2) \\\n",
|
|
" + A_3 * (F_4 - F_3) );\n",
|
|
" \n",
|
|
" flux_nolimit = 0.5*( F_1 + F_4 ) \\\n",
|
|
" - 0.5*( c_1 * (F_2 - F_1) \\\n",
|
|
" + c_2 * (F_3 - F_2) \\\n",
|
|
" + c_3 * (F_4 - F_3) );\n",
|
|
" \n",
|
|
" flux_hll = np.array([0.0, 0.0, 0.0])\n",
|
|
" if (S_l >= 0.0):\n",
|
|
" flux_hll = F_1;\n",
|
|
" elif (S_r <= 0.0):\n",
|
|
" flux_hll = F_4;\n",
|
|
" elif (S_l <= 0.0 and 0.0 <=S_star):\n",
|
|
" flux_hll = F_1 + S_l*(Q_star_l - Q_l1);\n",
|
|
" elif (S_star <= 0.0 and 0.0 <=S_r):\n",
|
|
" flux_hll = F_4 + S_r*(Q_star_r - Q_r1);\n",
|
|
"\n",
|
|
" return flux\n",
|
|
"\n",
|
|
" #return [rh_1, rv_2, rh_3];\n",
|
|
" #return [A_1, A_2, A_3]\n",
|
|
" #return [A_1 + A_2 + A_3]\n",
|
|
" #return [c_1 + c_2 + c_3]\n",
|
|
"\n",
|
|
" #return [A_1 + A_2 + A_3, \\\n",
|
|
" #c_1 + c_2 + c_3]\n",
|
|
" #return [A_1, A_2, A_3, c_1, c_2, c_3]\n",
|
|
" \n",
|
|
" #return [flux, flux_nolimit]\n",
|
|
" \n",
|
|
" #return [rh_1, A_1, c_1]\n",
|
|
" #return [A_1, A_2, A_3]\n",
|
|
" #return [(h_r2 - h_r)/(h_r - h_l), rh_p]\n",
|
|
" #return [denom, orig_denom]\n",
|
|
" \n",
|
|
" #i = 2\n",
|
|
" #return [F_1[i], F_2[i], F_3[i], F_4[i]]\n",
|
|
"\n",
|
|
"\n",
|
|
"def WAF_1D_flux_helper(h, hu, g, dx, dt):\n",
|
|
" \n",
|
|
" hv = np.zeros_like(h)\n",
|
|
" Q = np.vstack([h, hu, hv]).T\n",
|
|
" \n",
|
|
" Q_l2 = Q[0:-3,:]\n",
|
|
" Q_l1 = Q[1:-2,:]\n",
|
|
" Q_r1 = Q[2:-1,:]\n",
|
|
" Q_r2 = Q[3:,:]\n",
|
|
" \n",
|
|
" g = np.ones(Q_l2.shape[0])*g\n",
|
|
" dx = np.ones(Q_l2.shape[0])*dx\n",
|
|
" dt = np.ones(Q_l2.shape[0])*dt\n",
|
|
" \n",
|
|
" return np.array(list(map(WAF_1D_flux, Q_l2, Q_l1, Q_r1, Q_r2, g, dx, dt)))\n",
|
|
"\n",
|
|
"\"\"\"\n",
|
|
"ref_x, ref_h, ref_hu = gen_reference(512)\n",
|
|
"F = WAF_1D_flux_helper(ref_h, ref_hu, 9.81, 0.1, 0.01)\n",
|
|
"\n",
|
|
"fig, ax1 = plt.subplots()\n",
|
|
"plt.ylim([0, 0.01])\n",
|
|
"ax2 = ax1.twinx()\n",
|
|
"\n",
|
|
"ax1.plot(ref_x, ref_h, '--', label='Reference')\n",
|
|
"\n",
|
|
"for i in range(F.shape[1]):\n",
|
|
" ax2.plot(ref_x[2:-1], F[:,i], label='F_' + str(i))\n",
|
|
"\n",
|
|
"plt.title(\"WAF flux for shallow water\")\n",
|
|
"plt.legend()\n",
|
|
"\"\"\""
|
|
]
|
|
},
|
|
{
|
|
"cell_type": "code",
|
|
"execution_count": 7,
|
|
"metadata": {
|
|
"scrolled": false
|
|
},
|
|
"outputs": [
|
|
{
|
|
"data": {
|
|
"text/plain": [
|
|
"'\\nfig = plt.figure()\\nplt.ylim([0, 0.01])\\n\\nref_x, ref_h, ref_hu = gen_reference(128)\\nplt.plot(ref_x, ref_h, \\'-\\')\\n\\nfor i in range(50):\\n h, hu = WAF_step(ref_h, ref_hu, 9.81, 0.1, 0.1)\\nplt.plot(ref_x, h, \\'--\\')\\n\\nplt.title(\"WAF for shallow water\")\\nplt.legend()\\n'"
|
|
]
|
|
},
|
|
"execution_count": 7,
|
|
"metadata": {},
|
|
"output_type": "execute_result"
|
|
}
|
|
],
|
|
"source": [
|
|
"def WAF_step(h, hu, g, dx, dt):\n",
|
|
" F = WAF_1D_flux_helper(h, hu, g, dx, dt)\n",
|
|
" \n",
|
|
" h[2:-2] = h[2:-2] + (F[0:-1,0] - F[1:,0]) * dt / dx;\n",
|
|
" hu[2:-2] = hu[2:-2] + (F[0:-1,1] - F[1:,1]) * dt / dx;\n",
|
|
" \n",
|
|
" return [h, hu]\n",
|
|
" \n",
|
|
"\"\"\"\n",
|
|
"fig = plt.figure()\n",
|
|
"plt.ylim([0, 0.01])\n",
|
|
"\n",
|
|
"ref_x, ref_h, ref_hu = gen_reference(128)\n",
|
|
"plt.plot(ref_x, ref_h, '-')\n",
|
|
"\n",
|
|
"for i in range(50):\n",
|
|
" h, hu = WAF_step(ref_h, ref_hu, 9.81, 0.1, 0.1)\n",
|
|
"plt.plot(ref_x, h, '--')\n",
|
|
"\n",
|
|
"plt.title(\"WAF for shallow water\")\n",
|
|
"plt.legend()\n",
|
|
"\"\"\""
|
|
]
|
|
},
|
|
{
|
|
"cell_type": "code",
|
|
"execution_count": 8,
|
|
"metadata": {},
|
|
"outputs": [],
|
|
"source": [
|
|
"def gen_test_data(nx, ny, g, num_ghost_cells):\n",
|
|
" width = 100.0\n",
|
|
" height = 100.0\n",
|
|
" dx = width / float(nx)\n",
|
|
" dy = height / float(ny)\n",
|
|
"\n",
|
|
" x_center = dx*nx/2.0\n",
|
|
" y_center = dy*ny/2.0\n",
|
|
" \n",
|
|
" h = np.zeros((ny+2*num_ghost_cells, nx+2*num_ghost_cells)); \n",
|
|
" hu = np.zeros((ny+2*num_ghost_cells, nx+2*num_ghost_cells));\n",
|
|
" hv = np.zeros((ny+2*num_ghost_cells, nx+2*num_ghost_cells));\n",
|
|
"\n",
|
|
" #Create a gaussian \"dam break\" that will not form shocks\n",
|
|
" size = width / 5.0\n",
|
|
" dt = 10**10\n",
|
|
" for j in range(-num_ghost_cells, ny+num_ghost_cells):\n",
|
|
" for i in range(-num_ghost_cells, nx+num_ghost_cells):\n",
|
|
" x = dx*(i+0.5) - x_center\n",
|
|
" y = dy*(j+0.5) - y_center\n",
|
|
" \n",
|
|
" h[j+num_ghost_cells, i+num_ghost_cells] = 0.5 + 0.1*np.exp(-(x**2/size))\n",
|
|
" hu[j+num_ghost_cells, i+num_ghost_cells] = 0.1*np.exp(-(x**2/size))\n",
|
|
" \n",
|
|
" max_h_estimate = 0.6\n",
|
|
" max_u_estimate = 0.1*2.0\n",
|
|
" dt = min(dx, dy) / (max_u_estimate + np.sqrt(g*max_h_estimate))\n",
|
|
" \n",
|
|
" return h, hu, hv, dx, dy, dt"
|
|
]
|
|
},
|
|
{
|
|
"cell_type": "code",
|
|
"execution_count": 9,
|
|
"metadata": {},
|
|
"outputs": [
|
|
{
|
|
"name": "stdout",
|
|
"output_type": "stream",
|
|
"text": [
|
|
"Step 1/56\n",
|
|
"Step 2/56\n",
|
|
"Step 3/56\n",
|
|
"Step 4/56\n",
|
|
"Step 5/56\n",
|
|
"Step 6/56\n",
|
|
"Step 7/56\n",
|
|
"Step 8/56\n",
|
|
"Step 9/56\n",
|
|
"Step 10/56\n",
|
|
"Step 11/56\n",
|
|
"Step 12/56\n",
|
|
"Step 13/56\n",
|
|
"Step 14/56\n",
|
|
"Step 15/56\n",
|
|
"Step 16/56\n",
|
|
"Step 17/56\n",
|
|
"Step 18/56\n",
|
|
"Step 19/56\n",
|
|
"Step 20/56\n",
|
|
"Step 21/56\n",
|
|
"Step 22/56\n",
|
|
"Step 23/56\n",
|
|
"Step 24/56\n",
|
|
"Step 25/56\n",
|
|
"Step 26/56\n",
|
|
"Step 27/56\n",
|
|
"Step 28/56\n",
|
|
"Step 29/56\n",
|
|
"Step 30/56\n",
|
|
"Step 31/56\n",
|
|
"Step 32/56\n",
|
|
"Step 33/56\n",
|
|
"Step 34/56\n",
|
|
"Step 35/56\n",
|
|
"Step 36/56\n",
|
|
"Step 37/56\n",
|
|
"Step 38/56\n",
|
|
"Step 39/56\n",
|
|
"Step 40/56\n",
|
|
"Step 41/56\n",
|
|
"Step 42/56\n",
|
|
"Step 43/56\n",
|
|
"Step 44/56\n",
|
|
"Step 45/56\n",
|
|
"Step 46/56\n",
|
|
"Step 47/56\n",
|
|
"Step 48/56\n",
|
|
"Step 49/56\n",
|
|
"Step 50/56\n",
|
|
"Step 51/56\n",
|
|
"Step 52/56\n",
|
|
"Step 53/56\n",
|
|
"Step 54/56\n",
|
|
"Step 55/56\n",
|
|
"Step 56/56\n"
|
|
]
|
|
}
|
|
],
|
|
"source": [
|
|
"nx=1025\n",
|
|
"g = 9.81\n",
|
|
"h0, hu0, hv0, dx, dy, dt = gen_test_data(nx, 1, 9.81, 0)\n",
|
|
"\n",
|
|
"h = h0.ravel()\n",
|
|
"hu = hu0.ravel()\n",
|
|
"x = np.linspace(0.0, nx*dx, nx)\n",
|
|
"\n",
|
|
"dt = dt*0.95\n",
|
|
"\n",
|
|
"timesteps = int(2.0/dt)\n",
|
|
"for i in range(timesteps):\n",
|
|
" print(\"Step \" + str(i+1) + \"/\" + str(timesteps))\n",
|
|
" WAF_step(h, hu, g, dx, dt) "
|
|
]
|
|
},
|
|
{
|
|
"cell_type": "code",
|
|
"execution_count": 10,
|
|
"metadata": {
|
|
"scrolled": false
|
|
},
|
|
"outputs": [
|
|
{
|
|
"data": {
|
|
"application/javascript": [
|
|
"/* Put everything inside the global mpl namespace */\n",
|
|
"window.mpl = {};\n",
|
|
"\n",
|
|
"\n",
|
|
"mpl.get_websocket_type = function() {\n",
|
|
" if (typeof(WebSocket) !== 'undefined') {\n",
|
|
" return WebSocket;\n",
|
|
" } else if (typeof(MozWebSocket) !== 'undefined') {\n",
|
|
" return MozWebSocket;\n",
|
|
" } else {\n",
|
|
" alert('Your browser does not have WebSocket support.' +\n",
|
|
" 'Please try Chrome, Safari or Firefox ≥ 6. ' +\n",
|
|
" 'Firefox 4 and 5 are also supported but you ' +\n",
|
|
" 'have to enable WebSockets in about:config.');\n",
|
|
" };\n",
|
|
"}\n",
|
|
"\n",
|
|
"mpl.figure = function(figure_id, websocket, ondownload, parent_element) {\n",
|
|
" this.id = figure_id;\n",
|
|
"\n",
|
|
" this.ws = websocket;\n",
|
|
"\n",
|
|
" this.supports_binary = (this.ws.binaryType != undefined);\n",
|
|
"\n",
|
|
" if (!this.supports_binary) {\n",
|
|
" var warnings = document.getElementById(\"mpl-warnings\");\n",
|
|
" if (warnings) {\n",
|
|
" warnings.style.display = 'block';\n",
|
|
" warnings.textContent = (\n",
|
|
" \"This browser does not support binary websocket messages. \" +\n",
|
|
" \"Performance may be slow.\");\n",
|
|
" }\n",
|
|
" }\n",
|
|
"\n",
|
|
" this.imageObj = new Image();\n",
|
|
"\n",
|
|
" this.context = undefined;\n",
|
|
" this.message = undefined;\n",
|
|
" this.canvas = undefined;\n",
|
|
" this.rubberband_canvas = undefined;\n",
|
|
" this.rubberband_context = undefined;\n",
|
|
" this.format_dropdown = undefined;\n",
|
|
"\n",
|
|
" this.image_mode = 'full';\n",
|
|
"\n",
|
|
" this.root = $('<div/>');\n",
|
|
" this._root_extra_style(this.root)\n",
|
|
" this.root.attr('style', 'display: inline-block');\n",
|
|
"\n",
|
|
" $(parent_element).append(this.root);\n",
|
|
"\n",
|
|
" this._init_header(this);\n",
|
|
" this._init_canvas(this);\n",
|
|
" this._init_toolbar(this);\n",
|
|
"\n",
|
|
" var fig = this;\n",
|
|
"\n",
|
|
" this.waiting = false;\n",
|
|
"\n",
|
|
" this.ws.onopen = function () {\n",
|
|
" fig.send_message(\"supports_binary\", {value: fig.supports_binary});\n",
|
|
" fig.send_message(\"send_image_mode\", {});\n",
|
|
" if (mpl.ratio != 1) {\n",
|
|
" fig.send_message(\"set_dpi_ratio\", {'dpi_ratio': mpl.ratio});\n",
|
|
" }\n",
|
|
" fig.send_message(\"refresh\", {});\n",
|
|
" }\n",
|
|
"\n",
|
|
" this.imageObj.onload = function() {\n",
|
|
" if (fig.image_mode == 'full') {\n",
|
|
" // Full images could contain transparency (where diff images\n",
|
|
" // almost always do), so we need to clear the canvas so that\n",
|
|
" // there is no ghosting.\n",
|
|
" fig.context.clearRect(0, 0, fig.canvas.width, fig.canvas.height);\n",
|
|
" }\n",
|
|
" fig.context.drawImage(fig.imageObj, 0, 0);\n",
|
|
" };\n",
|
|
"\n",
|
|
" this.imageObj.onunload = function() {\n",
|
|
" fig.ws.close();\n",
|
|
" }\n",
|
|
"\n",
|
|
" this.ws.onmessage = this._make_on_message_function(this);\n",
|
|
"\n",
|
|
" this.ondownload = ondownload;\n",
|
|
"}\n",
|
|
"\n",
|
|
"mpl.figure.prototype._init_header = function() {\n",
|
|
" var titlebar = $(\n",
|
|
" '<div class=\"ui-dialog-titlebar ui-widget-header ui-corner-all ' +\n",
|
|
" 'ui-helper-clearfix\"/>');\n",
|
|
" var titletext = $(\n",
|
|
" '<div class=\"ui-dialog-title\" style=\"width: 100%; ' +\n",
|
|
" 'text-align: center; padding: 3px;\"/>');\n",
|
|
" titlebar.append(titletext)\n",
|
|
" this.root.append(titlebar);\n",
|
|
" this.header = titletext[0];\n",
|
|
"}\n",
|
|
"\n",
|
|
"\n",
|
|
"\n",
|
|
"mpl.figure.prototype._canvas_extra_style = function(canvas_div) {\n",
|
|
"\n",
|
|
"}\n",
|
|
"\n",
|
|
"\n",
|
|
"mpl.figure.prototype._root_extra_style = function(canvas_div) {\n",
|
|
"\n",
|
|
"}\n",
|
|
"\n",
|
|
"mpl.figure.prototype._init_canvas = function() {\n",
|
|
" var fig = this;\n",
|
|
"\n",
|
|
" var canvas_div = $('<div/>');\n",
|
|
"\n",
|
|
" canvas_div.attr('style', 'position: relative; clear: both; outline: 0');\n",
|
|
"\n",
|
|
" function canvas_keyboard_event(event) {\n",
|
|
" return fig.key_event(event, event['data']);\n",
|
|
" }\n",
|
|
"\n",
|
|
" canvas_div.keydown('key_press', canvas_keyboard_event);\n",
|
|
" canvas_div.keyup('key_release', canvas_keyboard_event);\n",
|
|
" this.canvas_div = canvas_div\n",
|
|
" this._canvas_extra_style(canvas_div)\n",
|
|
" this.root.append(canvas_div);\n",
|
|
"\n",
|
|
" var canvas = $('<canvas/>');\n",
|
|
" canvas.addClass('mpl-canvas');\n",
|
|
" canvas.attr('style', \"left: 0; top: 0; z-index: 0; outline: 0\")\n",
|
|
"\n",
|
|
" this.canvas = canvas[0];\n",
|
|
" this.context = canvas[0].getContext(\"2d\");\n",
|
|
"\n",
|
|
" var backingStore = this.context.backingStorePixelRatio ||\n",
|
|
"\tthis.context.webkitBackingStorePixelRatio ||\n",
|
|
"\tthis.context.mozBackingStorePixelRatio ||\n",
|
|
"\tthis.context.msBackingStorePixelRatio ||\n",
|
|
"\tthis.context.oBackingStorePixelRatio ||\n",
|
|
"\tthis.context.backingStorePixelRatio || 1;\n",
|
|
"\n",
|
|
" mpl.ratio = (window.devicePixelRatio || 1) / backingStore;\n",
|
|
"\n",
|
|
" var rubberband = $('<canvas/>');\n",
|
|
" rubberband.attr('style', \"position: absolute; left: 0; top: 0; z-index: 1;\")\n",
|
|
"\n",
|
|
" var pass_mouse_events = true;\n",
|
|
"\n",
|
|
" canvas_div.resizable({\n",
|
|
" start: function(event, ui) {\n",
|
|
" pass_mouse_events = false;\n",
|
|
" },\n",
|
|
" resize: function(event, ui) {\n",
|
|
" fig.request_resize(ui.size.width, ui.size.height);\n",
|
|
" },\n",
|
|
" stop: function(event, ui) {\n",
|
|
" pass_mouse_events = true;\n",
|
|
" fig.request_resize(ui.size.width, ui.size.height);\n",
|
|
" },\n",
|
|
" });\n",
|
|
"\n",
|
|
" function mouse_event_fn(event) {\n",
|
|
" if (pass_mouse_events)\n",
|
|
" return fig.mouse_event(event, event['data']);\n",
|
|
" }\n",
|
|
"\n",
|
|
" rubberband.mousedown('button_press', mouse_event_fn);\n",
|
|
" rubberband.mouseup('button_release', mouse_event_fn);\n",
|
|
" // Throttle sequential mouse events to 1 every 20ms.\n",
|
|
" rubberband.mousemove('motion_notify', mouse_event_fn);\n",
|
|
"\n",
|
|
" rubberband.mouseenter('figure_enter', mouse_event_fn);\n",
|
|
" rubberband.mouseleave('figure_leave', mouse_event_fn);\n",
|
|
"\n",
|
|
" canvas_div.on(\"wheel\", function (event) {\n",
|
|
" event = event.originalEvent;\n",
|
|
" event['data'] = 'scroll'\n",
|
|
" if (event.deltaY < 0) {\n",
|
|
" event.step = 1;\n",
|
|
" } else {\n",
|
|
" event.step = -1;\n",
|
|
" }\n",
|
|
" mouse_event_fn(event);\n",
|
|
" });\n",
|
|
"\n",
|
|
" canvas_div.append(canvas);\n",
|
|
" canvas_div.append(rubberband);\n",
|
|
"\n",
|
|
" this.rubberband = rubberband;\n",
|
|
" this.rubberband_canvas = rubberband[0];\n",
|
|
" this.rubberband_context = rubberband[0].getContext(\"2d\");\n",
|
|
" this.rubberband_context.strokeStyle = \"#000000\";\n",
|
|
"\n",
|
|
" this._resize_canvas = function(width, height) {\n",
|
|
" // Keep the size of the canvas, canvas container, and rubber band\n",
|
|
" // canvas in synch.\n",
|
|
" canvas_div.css('width', width)\n",
|
|
" canvas_div.css('height', height)\n",
|
|
"\n",
|
|
" canvas.attr('width', width * mpl.ratio);\n",
|
|
" canvas.attr('height', height * mpl.ratio);\n",
|
|
" canvas.attr('style', 'width: ' + width + 'px; height: ' + height + 'px;');\n",
|
|
"\n",
|
|
" rubberband.attr('width', width);\n",
|
|
" rubberband.attr('height', height);\n",
|
|
" }\n",
|
|
"\n",
|
|
" // Set the figure to an initial 600x600px, this will subsequently be updated\n",
|
|
" // upon first draw.\n",
|
|
" this._resize_canvas(600, 600);\n",
|
|
"\n",
|
|
" // Disable right mouse context menu.\n",
|
|
" $(this.rubberband_canvas).bind(\"contextmenu\",function(e){\n",
|
|
" return false;\n",
|
|
" });\n",
|
|
"\n",
|
|
" function set_focus () {\n",
|
|
" canvas.focus();\n",
|
|
" canvas_div.focus();\n",
|
|
" }\n",
|
|
"\n",
|
|
" window.setTimeout(set_focus, 100);\n",
|
|
"}\n",
|
|
"\n",
|
|
"mpl.figure.prototype._init_toolbar = function() {\n",
|
|
" var fig = this;\n",
|
|
"\n",
|
|
" var nav_element = $('<div/>')\n",
|
|
" nav_element.attr('style', 'width: 100%');\n",
|
|
" this.root.append(nav_element);\n",
|
|
"\n",
|
|
" // Define a callback function for later on.\n",
|
|
" function toolbar_event(event) {\n",
|
|
" return fig.toolbar_button_onclick(event['data']);\n",
|
|
" }\n",
|
|
" function toolbar_mouse_event(event) {\n",
|
|
" return fig.toolbar_button_onmouseover(event['data']);\n",
|
|
" }\n",
|
|
"\n",
|
|
" for(var toolbar_ind in mpl.toolbar_items) {\n",
|
|
" var name = mpl.toolbar_items[toolbar_ind][0];\n",
|
|
" var tooltip = mpl.toolbar_items[toolbar_ind][1];\n",
|
|
" var image = mpl.toolbar_items[toolbar_ind][2];\n",
|
|
" var method_name = mpl.toolbar_items[toolbar_ind][3];\n",
|
|
"\n",
|
|
" if (!name) {\n",
|
|
" // put a spacer in here.\n",
|
|
" continue;\n",
|
|
" }\n",
|
|
" var button = $('<button/>');\n",
|
|
" button.addClass('ui-button ui-widget ui-state-default ui-corner-all ' +\n",
|
|
" 'ui-button-icon-only');\n",
|
|
" button.attr('role', 'button');\n",
|
|
" button.attr('aria-disabled', 'false');\n",
|
|
" button.click(method_name, toolbar_event);\n",
|
|
" button.mouseover(tooltip, toolbar_mouse_event);\n",
|
|
"\n",
|
|
" var icon_img = $('<span/>');\n",
|
|
" icon_img.addClass('ui-button-icon-primary ui-icon');\n",
|
|
" icon_img.addClass(image);\n",
|
|
" icon_img.addClass('ui-corner-all');\n",
|
|
"\n",
|
|
" var tooltip_span = $('<span/>');\n",
|
|
" tooltip_span.addClass('ui-button-text');\n",
|
|
" tooltip_span.html(tooltip);\n",
|
|
"\n",
|
|
" button.append(icon_img);\n",
|
|
" button.append(tooltip_span);\n",
|
|
"\n",
|
|
" nav_element.append(button);\n",
|
|
" }\n",
|
|
"\n",
|
|
" var fmt_picker_span = $('<span/>');\n",
|
|
"\n",
|
|
" var fmt_picker = $('<select/>');\n",
|
|
" fmt_picker.addClass('mpl-toolbar-option ui-widget ui-widget-content');\n",
|
|
" fmt_picker_span.append(fmt_picker);\n",
|
|
" nav_element.append(fmt_picker_span);\n",
|
|
" this.format_dropdown = fmt_picker[0];\n",
|
|
"\n",
|
|
" for (var ind in mpl.extensions) {\n",
|
|
" var fmt = mpl.extensions[ind];\n",
|
|
" var option = $(\n",
|
|
" '<option/>', {selected: fmt === mpl.default_extension}).html(fmt);\n",
|
|
" fmt_picker.append(option)\n",
|
|
" }\n",
|
|
"\n",
|
|
" // Add hover states to the ui-buttons\n",
|
|
" $( \".ui-button\" ).hover(\n",
|
|
" function() { $(this).addClass(\"ui-state-hover\");},\n",
|
|
" function() { $(this).removeClass(\"ui-state-hover\");}\n",
|
|
" );\n",
|
|
"\n",
|
|
" var status_bar = $('<span class=\"mpl-message\"/>');\n",
|
|
" nav_element.append(status_bar);\n",
|
|
" this.message = status_bar[0];\n",
|
|
"}\n",
|
|
"\n",
|
|
"mpl.figure.prototype.request_resize = function(x_pixels, y_pixels) {\n",
|
|
" // Request matplotlib to resize the figure. Matplotlib will then trigger a resize in the client,\n",
|
|
" // which will in turn request a refresh of the image.\n",
|
|
" this.send_message('resize', {'width': x_pixels, 'height': y_pixels});\n",
|
|
"}\n",
|
|
"\n",
|
|
"mpl.figure.prototype.send_message = function(type, properties) {\n",
|
|
" properties['type'] = type;\n",
|
|
" properties['figure_id'] = this.id;\n",
|
|
" this.ws.send(JSON.stringify(properties));\n",
|
|
"}\n",
|
|
"\n",
|
|
"mpl.figure.prototype.send_draw_message = function() {\n",
|
|
" if (!this.waiting) {\n",
|
|
" this.waiting = true;\n",
|
|
" this.ws.send(JSON.stringify({type: \"draw\", figure_id: this.id}));\n",
|
|
" }\n",
|
|
"}\n",
|
|
"\n",
|
|
"\n",
|
|
"mpl.figure.prototype.handle_save = function(fig, msg) {\n",
|
|
" var format_dropdown = fig.format_dropdown;\n",
|
|
" var format = format_dropdown.options[format_dropdown.selectedIndex].value;\n",
|
|
" fig.ondownload(fig, format);\n",
|
|
"}\n",
|
|
"\n",
|
|
"\n",
|
|
"mpl.figure.prototype.handle_resize = function(fig, msg) {\n",
|
|
" var size = msg['size'];\n",
|
|
" if (size[0] != fig.canvas.width || size[1] != fig.canvas.height) {\n",
|
|
" fig._resize_canvas(size[0], size[1]);\n",
|
|
" fig.send_message(\"refresh\", {});\n",
|
|
" };\n",
|
|
"}\n",
|
|
"\n",
|
|
"mpl.figure.prototype.handle_rubberband = function(fig, msg) {\n",
|
|
" var x0 = msg['x0'] / mpl.ratio;\n",
|
|
" var y0 = (fig.canvas.height - msg['y0']) / mpl.ratio;\n",
|
|
" var x1 = msg['x1'] / mpl.ratio;\n",
|
|
" var y1 = (fig.canvas.height - msg['y1']) / mpl.ratio;\n",
|
|
" x0 = Math.floor(x0) + 0.5;\n",
|
|
" y0 = Math.floor(y0) + 0.5;\n",
|
|
" x1 = Math.floor(x1) + 0.5;\n",
|
|
" y1 = Math.floor(y1) + 0.5;\n",
|
|
" var min_x = Math.min(x0, x1);\n",
|
|
" var min_y = Math.min(y0, y1);\n",
|
|
" var width = Math.abs(x1 - x0);\n",
|
|
" var height = Math.abs(y1 - y0);\n",
|
|
"\n",
|
|
" fig.rubberband_context.clearRect(\n",
|
|
" 0, 0, fig.canvas.width, fig.canvas.height);\n",
|
|
"\n",
|
|
" fig.rubberband_context.strokeRect(min_x, min_y, width, height);\n",
|
|
"}\n",
|
|
"\n",
|
|
"mpl.figure.prototype.handle_figure_label = function(fig, msg) {\n",
|
|
" // Updates the figure title.\n",
|
|
" fig.header.textContent = msg['label'];\n",
|
|
"}\n",
|
|
"\n",
|
|
"mpl.figure.prototype.handle_cursor = function(fig, msg) {\n",
|
|
" var cursor = msg['cursor'];\n",
|
|
" switch(cursor)\n",
|
|
" {\n",
|
|
" case 0:\n",
|
|
" cursor = 'pointer';\n",
|
|
" break;\n",
|
|
" case 1:\n",
|
|
" cursor = 'default';\n",
|
|
" break;\n",
|
|
" case 2:\n",
|
|
" cursor = 'crosshair';\n",
|
|
" break;\n",
|
|
" case 3:\n",
|
|
" cursor = 'move';\n",
|
|
" break;\n",
|
|
" }\n",
|
|
" fig.rubberband_canvas.style.cursor = cursor;\n",
|
|
"}\n",
|
|
"\n",
|
|
"mpl.figure.prototype.handle_message = function(fig, msg) {\n",
|
|
" fig.message.textContent = msg['message'];\n",
|
|
"}\n",
|
|
"\n",
|
|
"mpl.figure.prototype.handle_draw = function(fig, msg) {\n",
|
|
" // Request the server to send over a new figure.\n",
|
|
" fig.send_draw_message();\n",
|
|
"}\n",
|
|
"\n",
|
|
"mpl.figure.prototype.handle_image_mode = function(fig, msg) {\n",
|
|
" fig.image_mode = msg['mode'];\n",
|
|
"}\n",
|
|
"\n",
|
|
"mpl.figure.prototype.updated_canvas_event = function() {\n",
|
|
" // Called whenever the canvas gets updated.\n",
|
|
" this.send_message(\"ack\", {});\n",
|
|
"}\n",
|
|
"\n",
|
|
"// A function to construct a web socket function for onmessage handling.\n",
|
|
"// Called in the figure constructor.\n",
|
|
"mpl.figure.prototype._make_on_message_function = function(fig) {\n",
|
|
" return function socket_on_message(evt) {\n",
|
|
" if (evt.data instanceof Blob) {\n",
|
|
" /* FIXME: We get \"Resource interpreted as Image but\n",
|
|
" * transferred with MIME type text/plain:\" errors on\n",
|
|
" * Chrome. But how to set the MIME type? It doesn't seem\n",
|
|
" * to be part of the websocket stream */\n",
|
|
" evt.data.type = \"image/png\";\n",
|
|
"\n",
|
|
" /* Free the memory for the previous frames */\n",
|
|
" if (fig.imageObj.src) {\n",
|
|
" (window.URL || window.webkitURL).revokeObjectURL(\n",
|
|
" fig.imageObj.src);\n",
|
|
" }\n",
|
|
"\n",
|
|
" fig.imageObj.src = (window.URL || window.webkitURL).createObjectURL(\n",
|
|
" evt.data);\n",
|
|
" fig.updated_canvas_event();\n",
|
|
" fig.waiting = false;\n",
|
|
" return;\n",
|
|
" }\n",
|
|
" else if (typeof evt.data === 'string' && evt.data.slice(0, 21) == \"data:image/png;base64\") {\n",
|
|
" fig.imageObj.src = evt.data;\n",
|
|
" fig.updated_canvas_event();\n",
|
|
" fig.waiting = false;\n",
|
|
" return;\n",
|
|
" }\n",
|
|
"\n",
|
|
" var msg = JSON.parse(evt.data);\n",
|
|
" var msg_type = msg['type'];\n",
|
|
"\n",
|
|
" // Call the \"handle_{type}\" callback, which takes\n",
|
|
" // the figure and JSON message as its only arguments.\n",
|
|
" try {\n",
|
|
" var callback = fig[\"handle_\" + msg_type];\n",
|
|
" } catch (e) {\n",
|
|
" console.log(\"No handler for the '\" + msg_type + \"' message type: \", msg);\n",
|
|
" return;\n",
|
|
" }\n",
|
|
"\n",
|
|
" if (callback) {\n",
|
|
" try {\n",
|
|
" // console.log(\"Handling '\" + msg_type + \"' message: \", msg);\n",
|
|
" callback(fig, msg);\n",
|
|
" } catch (e) {\n",
|
|
" console.log(\"Exception inside the 'handler_\" + msg_type + \"' callback:\", e, e.stack, msg);\n",
|
|
" }\n",
|
|
" }\n",
|
|
" };\n",
|
|
"}\n",
|
|
"\n",
|
|
"// from http://stackoverflow.com/questions/1114465/getting-mouse-location-in-canvas\n",
|
|
"mpl.findpos = function(e) {\n",
|
|
" //this section is from http://www.quirksmode.org/js/events_properties.html\n",
|
|
" var targ;\n",
|
|
" if (!e)\n",
|
|
" e = window.event;\n",
|
|
" if (e.target)\n",
|
|
" targ = e.target;\n",
|
|
" else if (e.srcElement)\n",
|
|
" targ = e.srcElement;\n",
|
|
" if (targ.nodeType == 3) // defeat Safari bug\n",
|
|
" targ = targ.parentNode;\n",
|
|
"\n",
|
|
" // jQuery normalizes the pageX and pageY\n",
|
|
" // pageX,Y are the mouse positions relative to the document\n",
|
|
" // offset() returns the position of the element relative to the document\n",
|
|
" var x = e.pageX - $(targ).offset().left;\n",
|
|
" var y = e.pageY - $(targ).offset().top;\n",
|
|
"\n",
|
|
" return {\"x\": x, \"y\": y};\n",
|
|
"};\n",
|
|
"\n",
|
|
"/*\n",
|
|
" * return a copy of an object with only non-object keys\n",
|
|
" * we need this to avoid circular references\n",
|
|
" * http://stackoverflow.com/a/24161582/3208463\n",
|
|
" */\n",
|
|
"function simpleKeys (original) {\n",
|
|
" return Object.keys(original).reduce(function (obj, key) {\n",
|
|
" if (typeof original[key] !== 'object')\n",
|
|
" obj[key] = original[key]\n",
|
|
" return obj;\n",
|
|
" }, {});\n",
|
|
"}\n",
|
|
"\n",
|
|
"mpl.figure.prototype.mouse_event = function(event, name) {\n",
|
|
" var canvas_pos = mpl.findpos(event)\n",
|
|
"\n",
|
|
" if (name === 'button_press')\n",
|
|
" {\n",
|
|
" this.canvas.focus();\n",
|
|
" this.canvas_div.focus();\n",
|
|
" }\n",
|
|
"\n",
|
|
" var x = canvas_pos.x * mpl.ratio;\n",
|
|
" var y = canvas_pos.y * mpl.ratio;\n",
|
|
"\n",
|
|
" this.send_message(name, {x: x, y: y, button: event.button,\n",
|
|
" step: event.step,\n",
|
|
" guiEvent: simpleKeys(event)});\n",
|
|
"\n",
|
|
" /* This prevents the web browser from automatically changing to\n",
|
|
" * the text insertion cursor when the button is pressed. We want\n",
|
|
" * to control all of the cursor setting manually through the\n",
|
|
" * 'cursor' event from matplotlib */\n",
|
|
" event.preventDefault();\n",
|
|
" return false;\n",
|
|
"}\n",
|
|
"\n",
|
|
"mpl.figure.prototype._key_event_extra = function(event, name) {\n",
|
|
" // Handle any extra behaviour associated with a key event\n",
|
|
"}\n",
|
|
"\n",
|
|
"mpl.figure.prototype.key_event = function(event, name) {\n",
|
|
"\n",
|
|
" // Prevent repeat events\n",
|
|
" if (name == 'key_press')\n",
|
|
" {\n",
|
|
" if (event.which === this._key)\n",
|
|
" return;\n",
|
|
" else\n",
|
|
" this._key = event.which;\n",
|
|
" }\n",
|
|
" if (name == 'key_release')\n",
|
|
" this._key = null;\n",
|
|
"\n",
|
|
" var value = '';\n",
|
|
" if (event.ctrlKey && event.which != 17)\n",
|
|
" value += \"ctrl+\";\n",
|
|
" if (event.altKey && event.which != 18)\n",
|
|
" value += \"alt+\";\n",
|
|
" if (event.shiftKey && event.which != 16)\n",
|
|
" value += \"shift+\";\n",
|
|
"\n",
|
|
" value += 'k';\n",
|
|
" value += event.which.toString();\n",
|
|
"\n",
|
|
" this._key_event_extra(event, name);\n",
|
|
"\n",
|
|
" this.send_message(name, {key: value,\n",
|
|
" guiEvent: simpleKeys(event)});\n",
|
|
" return false;\n",
|
|
"}\n",
|
|
"\n",
|
|
"mpl.figure.prototype.toolbar_button_onclick = function(name) {\n",
|
|
" if (name == 'download') {\n",
|
|
" this.handle_save(this, null);\n",
|
|
" } else {\n",
|
|
" this.send_message(\"toolbar_button\", {name: name});\n",
|
|
" }\n",
|
|
"};\n",
|
|
"\n",
|
|
"mpl.figure.prototype.toolbar_button_onmouseover = function(tooltip) {\n",
|
|
" this.message.textContent = tooltip;\n",
|
|
"};\n",
|
|
"mpl.toolbar_items = [[\"Home\", \"Reset original view\", \"fa fa-home icon-home\", \"home\"], [\"Back\", \"Back to previous view\", \"fa fa-arrow-left icon-arrow-left\", \"back\"], [\"Forward\", \"Forward to next view\", \"fa fa-arrow-right icon-arrow-right\", \"forward\"], [\"\", \"\", \"\", \"\"], [\"Pan\", \"Pan axes with left mouse, zoom with right\", \"fa fa-arrows icon-move\", \"pan\"], [\"Zoom\", \"Zoom to rectangle\", \"fa fa-square-o icon-check-empty\", \"zoom\"], [\"\", \"\", \"\", \"\"], [\"Download\", \"Download plot\", \"fa fa-floppy-o icon-save\", \"download\"]];\n",
|
|
"\n",
|
|
"mpl.extensions = [\"eps\", \"jpeg\", \"pdf\", \"png\", \"ps\", \"raw\", \"svg\", \"tif\"];\n",
|
|
"\n",
|
|
"mpl.default_extension = \"png\";var comm_websocket_adapter = function(comm) {\n",
|
|
" // Create a \"websocket\"-like object which calls the given IPython comm\n",
|
|
" // object with the appropriate methods. Currently this is a non binary\n",
|
|
" // socket, so there is still some room for performance tuning.\n",
|
|
" var ws = {};\n",
|
|
"\n",
|
|
" ws.close = function() {\n",
|
|
" comm.close()\n",
|
|
" };\n",
|
|
" ws.send = function(m) {\n",
|
|
" //console.log('sending', m);\n",
|
|
" comm.send(m);\n",
|
|
" };\n",
|
|
" // Register the callback with on_msg.\n",
|
|
" comm.on_msg(function(msg) {\n",
|
|
" //console.log('receiving', msg['content']['data'], msg);\n",
|
|
" // Pass the mpl event to the overridden (by mpl) onmessage function.\n",
|
|
" ws.onmessage(msg['content']['data'])\n",
|
|
" });\n",
|
|
" return ws;\n",
|
|
"}\n",
|
|
"\n",
|
|
"mpl.mpl_figure_comm = function(comm, msg) {\n",
|
|
" // This is the function which gets called when the mpl process\n",
|
|
" // starts-up an IPython Comm through the \"matplotlib\" channel.\n",
|
|
"\n",
|
|
" var id = msg.content.data.id;\n",
|
|
" // Get hold of the div created by the display call when the Comm\n",
|
|
" // socket was opened in Python.\n",
|
|
" var element = $(\"#\" + id);\n",
|
|
" var ws_proxy = comm_websocket_adapter(comm)\n",
|
|
"\n",
|
|
" function ondownload(figure, format) {\n",
|
|
" window.open(figure.imageObj.src);\n",
|
|
" }\n",
|
|
"\n",
|
|
" var fig = new mpl.figure(id, ws_proxy,\n",
|
|
" ondownload,\n",
|
|
" element.get(0));\n",
|
|
"\n",
|
|
" // Call onopen now - mpl needs it, as it is assuming we've passed it a real\n",
|
|
" // web socket which is closed, not our websocket->open comm proxy.\n",
|
|
" ws_proxy.onopen();\n",
|
|
"\n",
|
|
" fig.parent_element = element.get(0);\n",
|
|
" fig.cell_info = mpl.find_output_cell(\"<div id='\" + id + \"'></div>\");\n",
|
|
" if (!fig.cell_info) {\n",
|
|
" console.error(\"Failed to find cell for figure\", id, fig);\n",
|
|
" return;\n",
|
|
" }\n",
|
|
"\n",
|
|
" var output_index = fig.cell_info[2]\n",
|
|
" var cell = fig.cell_info[0];\n",
|
|
"\n",
|
|
"};\n",
|
|
"\n",
|
|
"mpl.figure.prototype.handle_close = function(fig, msg) {\n",
|
|
" var width = fig.canvas.width/mpl.ratio\n",
|
|
" fig.root.unbind('remove')\n",
|
|
"\n",
|
|
" // Update the output cell to use the data from the current canvas.\n",
|
|
" fig.push_to_output();\n",
|
|
" var dataURL = fig.canvas.toDataURL();\n",
|
|
" // Re-enable the keyboard manager in IPython - without this line, in FF,\n",
|
|
" // the notebook keyboard shortcuts fail.\n",
|
|
" IPython.keyboard_manager.enable()\n",
|
|
" $(fig.parent_element).html('<img src=\"' + dataURL + '\" width=\"' + width + '\">');\n",
|
|
" fig.close_ws(fig, msg);\n",
|
|
"}\n",
|
|
"\n",
|
|
"mpl.figure.prototype.close_ws = function(fig, msg){\n",
|
|
" fig.send_message('closing', msg);\n",
|
|
" // fig.ws.close()\n",
|
|
"}\n",
|
|
"\n",
|
|
"mpl.figure.prototype.push_to_output = function(remove_interactive) {\n",
|
|
" // Turn the data on the canvas into data in the output cell.\n",
|
|
" var width = this.canvas.width/mpl.ratio\n",
|
|
" var dataURL = this.canvas.toDataURL();\n",
|
|
" this.cell_info[1]['text/html'] = '<img src=\"' + dataURL + '\" width=\"' + width + '\">';\n",
|
|
"}\n",
|
|
"\n",
|
|
"mpl.figure.prototype.updated_canvas_event = function() {\n",
|
|
" // Tell IPython that the notebook contents must change.\n",
|
|
" IPython.notebook.set_dirty(true);\n",
|
|
" this.send_message(\"ack\", {});\n",
|
|
" var fig = this;\n",
|
|
" // Wait a second, then push the new image to the DOM so\n",
|
|
" // that it is saved nicely (might be nice to debounce this).\n",
|
|
" setTimeout(function () { fig.push_to_output() }, 1000);\n",
|
|
"}\n",
|
|
"\n",
|
|
"mpl.figure.prototype._init_toolbar = function() {\n",
|
|
" var fig = this;\n",
|
|
"\n",
|
|
" var nav_element = $('<div/>')\n",
|
|
" nav_element.attr('style', 'width: 100%');\n",
|
|
" this.root.append(nav_element);\n",
|
|
"\n",
|
|
" // Define a callback function for later on.\n",
|
|
" function toolbar_event(event) {\n",
|
|
" return fig.toolbar_button_onclick(event['data']);\n",
|
|
" }\n",
|
|
" function toolbar_mouse_event(event) {\n",
|
|
" return fig.toolbar_button_onmouseover(event['data']);\n",
|
|
" }\n",
|
|
"\n",
|
|
" for(var toolbar_ind in mpl.toolbar_items){\n",
|
|
" var name = mpl.toolbar_items[toolbar_ind][0];\n",
|
|
" var tooltip = mpl.toolbar_items[toolbar_ind][1];\n",
|
|
" var image = mpl.toolbar_items[toolbar_ind][2];\n",
|
|
" var method_name = mpl.toolbar_items[toolbar_ind][3];\n",
|
|
"\n",
|
|
" if (!name) { continue; };\n",
|
|
"\n",
|
|
" var button = $('<button class=\"btn btn-default\" href=\"#\" title=\"' + name + '\"><i class=\"fa ' + image + ' fa-lg\"></i></button>');\n",
|
|
" button.click(method_name, toolbar_event);\n",
|
|
" button.mouseover(tooltip, toolbar_mouse_event);\n",
|
|
" nav_element.append(button);\n",
|
|
" }\n",
|
|
"\n",
|
|
" // Add the status bar.\n",
|
|
" var status_bar = $('<span class=\"mpl-message\" style=\"text-align:right; float: right;\"/>');\n",
|
|
" nav_element.append(status_bar);\n",
|
|
" this.message = status_bar[0];\n",
|
|
"\n",
|
|
" // Add the close button to the window.\n",
|
|
" var buttongrp = $('<div class=\"btn-group inline pull-right\"></div>');\n",
|
|
" var button = $('<button class=\"btn btn-mini btn-primary\" href=\"#\" title=\"Stop Interaction\"><i class=\"fa fa-power-off icon-remove icon-large\"></i></button>');\n",
|
|
" button.click(function (evt) { fig.handle_close(fig, {}); } );\n",
|
|
" button.mouseover('Stop Interaction', toolbar_mouse_event);\n",
|
|
" buttongrp.append(button);\n",
|
|
" var titlebar = this.root.find($('.ui-dialog-titlebar'));\n",
|
|
" titlebar.prepend(buttongrp);\n",
|
|
"}\n",
|
|
"\n",
|
|
"mpl.figure.prototype._root_extra_style = function(el){\n",
|
|
" var fig = this\n",
|
|
" el.on(\"remove\", function(){\n",
|
|
"\tfig.close_ws(fig, {});\n",
|
|
" });\n",
|
|
"}\n",
|
|
"\n",
|
|
"mpl.figure.prototype._canvas_extra_style = function(el){\n",
|
|
" // this is important to make the div 'focusable\n",
|
|
" el.attr('tabindex', 0)\n",
|
|
" // reach out to IPython and tell the keyboard manager to turn it's self\n",
|
|
" // off when our div gets focus\n",
|
|
"\n",
|
|
" // location in version 3\n",
|
|
" if (IPython.notebook.keyboard_manager) {\n",
|
|
" IPython.notebook.keyboard_manager.register_events(el);\n",
|
|
" }\n",
|
|
" else {\n",
|
|
" // location in version 2\n",
|
|
" IPython.keyboard_manager.register_events(el);\n",
|
|
" }\n",
|
|
"\n",
|
|
"}\n",
|
|
"\n",
|
|
"mpl.figure.prototype._key_event_extra = function(event, name) {\n",
|
|
" var manager = IPython.notebook.keyboard_manager;\n",
|
|
" if (!manager)\n",
|
|
" manager = IPython.keyboard_manager;\n",
|
|
"\n",
|
|
" // Check for shift+enter\n",
|
|
" if (event.shiftKey && event.which == 13) {\n",
|
|
" this.canvas_div.blur();\n",
|
|
" event.shiftKey = false;\n",
|
|
" // Send a \"J\" for go to next cell\n",
|
|
" event.which = 74;\n",
|
|
" event.keyCode = 74;\n",
|
|
" manager.command_mode();\n",
|
|
" manager.handle_keydown(event);\n",
|
|
" }\n",
|
|
"}\n",
|
|
"\n",
|
|
"mpl.figure.prototype.handle_save = function(fig, msg) {\n",
|
|
" fig.ondownload(fig, null);\n",
|
|
"}\n",
|
|
"\n",
|
|
"\n",
|
|
"mpl.find_output_cell = function(html_output) {\n",
|
|
" // Return the cell and output element which can be found *uniquely* in the notebook.\n",
|
|
" // Note - this is a bit hacky, but it is done because the \"notebook_saving.Notebook\"\n",
|
|
" // IPython event is triggered only after the cells have been serialised, which for\n",
|
|
" // our purposes (turning an active figure into a static one), is too late.\n",
|
|
" var cells = IPython.notebook.get_cells();\n",
|
|
" var ncells = cells.length;\n",
|
|
" for (var i=0; i<ncells; i++) {\n",
|
|
" var cell = cells[i];\n",
|
|
" if (cell.cell_type === 'code'){\n",
|
|
" for (var j=0; j<cell.output_area.outputs.length; j++) {\n",
|
|
" var data = cell.output_area.outputs[j];\n",
|
|
" if (data.data) {\n",
|
|
" // IPython >= 3 moved mimebundle to data attribute of output\n",
|
|
" data = data.data;\n",
|
|
" }\n",
|
|
" if (data['text/html'] == html_output) {\n",
|
|
" return [cell, data, j];\n",
|
|
" }\n",
|
|
" }\n",
|
|
" }\n",
|
|
" }\n",
|
|
"}\n",
|
|
"\n",
|
|
"// Register the function which deals with the matplotlib target/channel.\n",
|
|
"// The kernel may be null if the page has been refreshed.\n",
|
|
"if (IPython.notebook.kernel != null) {\n",
|
|
" IPython.notebook.kernel.comm_manager.register_target('matplotlib', mpl.mpl_figure_comm);\n",
|
|
"}\n"
|
|
],
|
|
"text/plain": [
|
|
"<IPython.core.display.Javascript object>"
|
|
]
|
|
},
|
|
"metadata": {},
|
|
"output_type": "display_data"
|
|
},
|
|
{
|
|
"data": {
|
|
"text/html": [
|
|
"<img src=\"data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAoAAAAHgCAYAAAA10dzkAAAgAElEQVR4XuydCbiN5Rr37yaVJolSxkiiOKEQJU2oToaGUzohpVGFSiIpydAgkhMiJVGiURkqhTKk0CgqjuGjOdHR4BTf9X++711n2Xste631rHGv33Nd+1J7v887/N577+e/7ucedtuxY8cOY0AAAhCAAAQgAAEI5A2B3RCAefOueVAIQAACEIAABCDgCCAAMQQIQAACEIAABCCQZwQQgHn2wnlcCEAAAhCAAAQggADEBiAAAQhAAAIQgECeEUAA5tkL53EhAAEIQAACEIAAAhAbgAAEIAABCEAAAnlGAAGYZy+cx4UABCAAAQhAAAIIQGwAAhCAAAQgAAEI5BkBBGCevXAeFwIQgAAEIAABCCAAsQEIQAACEIAABCCQZwQQgHn2wnlcCEAAAhCAAAQggADEBiAAAQhAAAIQgECeEUAA5tkL53EhAAEIQAACEIAAAhAbgAAEIAABCEAAAnlGAAGYZy+cx4UABCAAAQhAAAIIQGwAAhCAAAQgAAEI5BkBBGCevXAeFwIQgAAEIAABCCAAsQEIQAACEIAABCCQZwQQgHn2wnlcCEAAAhCAAAQggADEBiAAAQhAAAIQgECeEUAA5tkL53EhAAEIQAACEIAAAhAbgAAEIAABCEAAAnlGAAGYZy+cx4UABCAAAQhAAAIIQGwAAhCAAAQgAAEI5BkBBGCevXAeFwIQgAAEIAABCCAAsQEIQAACEIAABCCQZwQQgHn2wnlcCEAAAhCAAAQggADEBiAAAQhAAAIQgECeEUAA5tkL53EhAAEIQAACEIAAAhAbgAAEIAABCEAAAnlGAAGYZy+cx4UABCAAAQhAAAIIQGwAAhCAAAQgAAEI5BkBBGCevXAeFwIQgAAEIAABCCAAsQEIQAACEIAABCCQZwQQgHn2wnlcCEAAAhCAAAQggADEBiAAAQhAAAIQgECeEUAA5tkL53EhAAEIQAACEIAAAhAbgAAEIAABCEAAAnlGAAGYZy+cx4UABCAAAQhAAAIIQGwAAhCAAAQgAAEI5BkBBGCevXAeFwIQgAAEIAABCCAAsQEIQAACEIAABCCQZwQQgHn2wnlcCEAAAhCAAAQggADEBiAAAQhAAAIQgECeEUAA5tkL53EhAAEIQAACEIAAAhAbgAAEIAABCEAAAnlGAAGYZy+cx4UABCAAAQhAAAIIQGwAAhCAAAQgAAEI5BkBBGCevXAeFwIQgAAEIAABCCAAsQEIQAACEIAABCCQZwQQgHn2wnlcCEAAAhCAAAQggADEBiAAAQhAAAIQgECeEUAA5tkL53EhAAEIQAACEIAAAhAbgAAEIAABCEAAAnlGAAGYZy+cx4UABCAAAQhAAAIIQGwAAhCAAAQgAAEI5BkBBKDHC//zzz9t2bJldthhh9nuu+/ucSamQgACEIAABCCQLgLbt2+3b7/91urWrWt77rlnui6bVddBAHq8jvfff98aNGjgcQamQgACEIAABCCQKQKLFy+2E088MVOXz+h1EYAe+NetW2eVK1c2GdDhhx/ucSamQgACEIAABCCQLgJff/21c+CsXbvWKlWqlK7LZtV1EIAer+P//J//YxUrVrT169dbhQoVPM7EVAhAAAIQgAAE0kWA9dsMAehhbRiQBzymQgACEIAABDJEgPUbAehlehiQFz4mQwACEIAABDJCgPUbAehleBiQFz4mQwACEIAABDJCgPUbAehleBiQFz4mQwACEIAABDJCgPUbAehleBiQFz4mQwACEIAABDJCgPUbAehleBiQFz4mQwACEIAABDJCgPUbAehleBiQFz4mQwACEIAABDJCgPUbAehleBiQFz4mQwACEIAABDJCgPUbAehleBiQFz4mQwACEIAABDJCgPUbAehleBiQFz4mQwACEIAABDJCgPU7TQLw0UcftQceeMDUe+/YY4+1YcOG2SmnnBLxpT/55JPWqVOnQj/77bffbJ999nHfHzlypPtas2aN+3+ds2/fvnb22WeH5j322GM2adIkW7p0qf3yyy+2adMmK1WqVMRr/vHHH9awYUP76KOPbNmyZXb88cfHZJAYUEyYOAgCEIAABCCQVQRYv9MgACdPnmzt27c3icAmTZrY6NGjbezYsbZ8+fKIDZglALt27WorV67cyVjKlSsX+v9p06bZHnvsYUcddZT73vjx453AlHiTGNSQyPz999/df/fq1WuXAlDX+/LLL23GjBkIwKz6FeVmIAABCEAAAskngABMgwCUZ61evXrOYxeMmjVrWps2bWzQoEGF3qoEYLdu3eznn3+O642XLl3aicArr7xyp3lz5syx0047LaoAlOi7+eab7fnnn3fiEQ9gXNg5GAIQgAAEIJBz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554r9Xf9eskPP/yQx0fv37Rkbnq3devW2dNPP20bNmywqlWrWrt27eyTTz6xGjVqpPywN99806ZMmWL63cjWrVu7fujcubN/4/K4ZkW5FXq87du3z5o1a5a0pz/44AO7//77k/5Nv8qp7+ns2bPt2LFjduONN9r06dNd3EWxJMabr9uQIUNs3rx5pYhkt379+iiyuSc+JJ4XNJY/8cQT9umnn5p+OaVFixbuNX369CnXoJDGt0gGQ8CDIgEMCJit6vqd4b59+7pf9ujatWupBPCqq66y559//sxHK4HRL4GkKjohf/TRR/bZZ5+deUmVKlWsbt262Wp+zt43lZuSvzvuuMM9Kufuu++2atWq2RdffOH+P9WjcxYtWmQDBw40DZK33HKLzZo1y95++233k3tR+/HwinQr9Hj7559/7Oeffy71HVBS9+qrr7qLrvPPPz/p9+OVV16xF1980d555x3Td3zSpEm2cuVK+/rrryP3E4/J4s3XTQmgftJr7ty5Z1z1/b7wwgtzNg5l64NTfU+7d+/ufglq2rRpdvHFF9v7779vEyZMsM2bN7ufOktWCml8y1Z/hP19SQDzsAd///13u+6661zioZOAZqriZwDj/51J83VCXrx4sW3fvj2Tl4f2NeW53XTTTaZB8oUXXsj4+DSLoH6YMWPGmTqtWrWye+65x12FR6VUtBvxVjYydBJWLM2ZMydp2Gj279JLL7VHH33Uxo0b516j599ppl6J4ciRI6MSblZevCUeZDo3vV4J4C+//OLGuCiX8tx0UaFxShessXLRRRe5i47hw4cnZSmU8S3KMRH02EgAgwpmof7gwYPd1evrr7/ulnwTE8CdO3eaThg6Odx5553uSq9WrVopW6ITspYxNUuo2S598V966SVr3rx5Flqfu7dM5fbTTz85q6lTp9qCBQvs22+/tZYtW7rZlk6dOiVt8MmTJ61mzZr24YcfWu/evc+8RsvxSqQTl+Fzd9TBP7ki3dSaQo+3xB7ZsmWLXX/99bZmzRq7+eabk3bYd999Z5dffrlt3bq11IxNr169rE6dOmWWOIP3eu7eobzxLb5VmbjFEkAlf5r1k1WXLl3cd7tevXq5O8gsfHJ5blrdKCoqsvnz5zsDbTV44IEH3CqH4iqxFNL4loWuiMxbkgDmWVdqL59m/TR1f95555VJAN966y23v6h+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\" width=\"640\">"
|
|
],
|
|
"text/plain": [
|
|
"<IPython.core.display.HTML object>"
|
|
]
|
|
},
|
|
"metadata": {},
|
|
"output_type": "display_data"
|
|
},
|
|
{
|
|
"data": {
|
|
"text/plain": [
|
|
"\"\\nfig = plt.figure(figsize=(8, 16))\\n\\nplt.subplot(311)\\nsetBwStyles(fig.gca())\\nplt.plot(x, h, marker=' ', label='h_' + str(nx))\\nplt.legend()\\n\\nplt.subplot(312)\\nsetBwStyles(fig.gca())\\nplt.plot(x, h, marker=' ', label='h_' + str(nx))\\nplt.xlim([43, 49])\\nplt.ylim([0.5305, 0.5315])\\nplt.legend()\\n\\nplt.subplot(313)\\nsetBwStyles(fig.gca())\\nplt.plot(x, h, marker=' ', label='h_' + str(nx)) \\nplt.xlim([53, 56.5])\\nplt.ylim([0.566, 0.57])\\nplt.legend()\\n\""
|
|
]
|
|
},
|
|
"execution_count": 10,
|
|
"metadata": {},
|
|
"output_type": "execute_result"
|
|
}
|
|
],
|
|
"source": [
|
|
"fig, ax1 = plt.subplots()\n",
|
|
"setBwStyles(fig.gca())\n",
|
|
"ax1.plot(x, h, marker='.', label='h_' + str(nx))\n",
|
|
"ax2 = ax1.twinx()\n",
|
|
"ax2.plot(x, hu, marker='+', label='h_' + str(nx))\n",
|
|
"\n",
|
|
"\n",
|
|
"\"\"\"\n",
|
|
"fig = plt.figure(figsize=(8, 16))\n",
|
|
"\n",
|
|
"plt.subplot(311)\n",
|
|
"setBwStyles(fig.gca())\n",
|
|
"plt.plot(x, h, marker=' ', label='h_' + str(nx))\n",
|
|
"plt.legend()\n",
|
|
"\n",
|
|
"plt.subplot(312)\n",
|
|
"setBwStyles(fig.gca())\n",
|
|
"plt.plot(x, h, marker=' ', label='h_' + str(nx))\n",
|
|
"plt.xlim([43, 49])\n",
|
|
"plt.ylim([0.5305, 0.5315])\n",
|
|
"plt.legend()\n",
|
|
"\n",
|
|
"plt.subplot(313)\n",
|
|
"setBwStyles(fig.gca())\n",
|
|
"plt.plot(x, h, marker=' ', label='h_' + str(nx)) \n",
|
|
"plt.xlim([53, 56.5])\n",
|
|
"plt.ylim([0.566, 0.57])\n",
|
|
"plt.legend()\n",
|
|
"\"\"\""
|
|
]
|
|
},
|
|
{
|
|
"cell_type": "code",
|
|
"execution_count": 11,
|
|
"metadata": {
|
|
"scrolled": false
|
|
},
|
|
"outputs": [
|
|
{
|
|
"data": {
|
|
"application/javascript": [
|
|
"/* Put everything inside the global mpl namespace */\n",
|
|
"window.mpl = {};\n",
|
|
"\n",
|
|
"\n",
|
|
"mpl.get_websocket_type = function() {\n",
|
|
" if (typeof(WebSocket) !== 'undefined') {\n",
|
|
" return WebSocket;\n",
|
|
" } else if (typeof(MozWebSocket) !== 'undefined') {\n",
|
|
" return MozWebSocket;\n",
|
|
" } else {\n",
|
|
" alert('Your browser does not have WebSocket support.' +\n",
|
|
" 'Please try Chrome, Safari or Firefox ≥ 6. ' +\n",
|
|
" 'Firefox 4 and 5 are also supported but you ' +\n",
|
|
" 'have to enable WebSockets in about:config.');\n",
|
|
" };\n",
|
|
"}\n",
|
|
"\n",
|
|
"mpl.figure = function(figure_id, websocket, ondownload, parent_element) {\n",
|
|
" this.id = figure_id;\n",
|
|
"\n",
|
|
" this.ws = websocket;\n",
|
|
"\n",
|
|
" this.supports_binary = (this.ws.binaryType != undefined);\n",
|
|
"\n",
|
|
" if (!this.supports_binary) {\n",
|
|
" var warnings = document.getElementById(\"mpl-warnings\");\n",
|
|
" if (warnings) {\n",
|
|
" warnings.style.display = 'block';\n",
|
|
" warnings.textContent = (\n",
|
|
" \"This browser does not support binary websocket messages. \" +\n",
|
|
" \"Performance may be slow.\");\n",
|
|
" }\n",
|
|
" }\n",
|
|
"\n",
|
|
" this.imageObj = new Image();\n",
|
|
"\n",
|
|
" this.context = undefined;\n",
|
|
" this.message = undefined;\n",
|
|
" this.canvas = undefined;\n",
|
|
" this.rubberband_canvas = undefined;\n",
|
|
" this.rubberband_context = undefined;\n",
|
|
" this.format_dropdown = undefined;\n",
|
|
"\n",
|
|
" this.image_mode = 'full';\n",
|
|
"\n",
|
|
" this.root = $('<div/>');\n",
|
|
" this._root_extra_style(this.root)\n",
|
|
" this.root.attr('style', 'display: inline-block');\n",
|
|
"\n",
|
|
" $(parent_element).append(this.root);\n",
|
|
"\n",
|
|
" this._init_header(this);\n",
|
|
" this._init_canvas(this);\n",
|
|
" this._init_toolbar(this);\n",
|
|
"\n",
|
|
" var fig = this;\n",
|
|
"\n",
|
|
" this.waiting = false;\n",
|
|
"\n",
|
|
" this.ws.onopen = function () {\n",
|
|
" fig.send_message(\"supports_binary\", {value: fig.supports_binary});\n",
|
|
" fig.send_message(\"send_image_mode\", {});\n",
|
|
" if (mpl.ratio != 1) {\n",
|
|
" fig.send_message(\"set_dpi_ratio\", {'dpi_ratio': mpl.ratio});\n",
|
|
" }\n",
|
|
" fig.send_message(\"refresh\", {});\n",
|
|
" }\n",
|
|
"\n",
|
|
" this.imageObj.onload = function() {\n",
|
|
" if (fig.image_mode == 'full') {\n",
|
|
" // Full images could contain transparency (where diff images\n",
|
|
" // almost always do), so we need to clear the canvas so that\n",
|
|
" // there is no ghosting.\n",
|
|
" fig.context.clearRect(0, 0, fig.canvas.width, fig.canvas.height);\n",
|
|
" }\n",
|
|
" fig.context.drawImage(fig.imageObj, 0, 0);\n",
|
|
" };\n",
|
|
"\n",
|
|
" this.imageObj.onunload = function() {\n",
|
|
" fig.ws.close();\n",
|
|
" }\n",
|
|
"\n",
|
|
" this.ws.onmessage = this._make_on_message_function(this);\n",
|
|
"\n",
|
|
" this.ondownload = ondownload;\n",
|
|
"}\n",
|
|
"\n",
|
|
"mpl.figure.prototype._init_header = function() {\n",
|
|
" var titlebar = $(\n",
|
|
" '<div class=\"ui-dialog-titlebar ui-widget-header ui-corner-all ' +\n",
|
|
" 'ui-helper-clearfix\"/>');\n",
|
|
" var titletext = $(\n",
|
|
" '<div class=\"ui-dialog-title\" style=\"width: 100%; ' +\n",
|
|
" 'text-align: center; padding: 3px;\"/>');\n",
|
|
" titlebar.append(titletext)\n",
|
|
" this.root.append(titlebar);\n",
|
|
" this.header = titletext[0];\n",
|
|
"}\n",
|
|
"\n",
|
|
"\n",
|
|
"\n",
|
|
"mpl.figure.prototype._canvas_extra_style = function(canvas_div) {\n",
|
|
"\n",
|
|
"}\n",
|
|
"\n",
|
|
"\n",
|
|
"mpl.figure.prototype._root_extra_style = function(canvas_div) {\n",
|
|
"\n",
|
|
"}\n",
|
|
"\n",
|
|
"mpl.figure.prototype._init_canvas = function() {\n",
|
|
" var fig = this;\n",
|
|
"\n",
|
|
" var canvas_div = $('<div/>');\n",
|
|
"\n",
|
|
" canvas_div.attr('style', 'position: relative; clear: both; outline: 0');\n",
|
|
"\n",
|
|
" function canvas_keyboard_event(event) {\n",
|
|
" return fig.key_event(event, event['data']);\n",
|
|
" }\n",
|
|
"\n",
|
|
" canvas_div.keydown('key_press', canvas_keyboard_event);\n",
|
|
" canvas_div.keyup('key_release', canvas_keyboard_event);\n",
|
|
" this.canvas_div = canvas_div\n",
|
|
" this._canvas_extra_style(canvas_div)\n",
|
|
" this.root.append(canvas_div);\n",
|
|
"\n",
|
|
" var canvas = $('<canvas/>');\n",
|
|
" canvas.addClass('mpl-canvas');\n",
|
|
" canvas.attr('style', \"left: 0; top: 0; z-index: 0; outline: 0\")\n",
|
|
"\n",
|
|
" this.canvas = canvas[0];\n",
|
|
" this.context = canvas[0].getContext(\"2d\");\n",
|
|
"\n",
|
|
" var backingStore = this.context.backingStorePixelRatio ||\n",
|
|
"\tthis.context.webkitBackingStorePixelRatio ||\n",
|
|
"\tthis.context.mozBackingStorePixelRatio ||\n",
|
|
"\tthis.context.msBackingStorePixelRatio ||\n",
|
|
"\tthis.context.oBackingStorePixelRatio ||\n",
|
|
"\tthis.context.backingStorePixelRatio || 1;\n",
|
|
"\n",
|
|
" mpl.ratio = (window.devicePixelRatio || 1) / backingStore;\n",
|
|
"\n",
|
|
" var rubberband = $('<canvas/>');\n",
|
|
" rubberband.attr('style', \"position: absolute; left: 0; top: 0; z-index: 1;\")\n",
|
|
"\n",
|
|
" var pass_mouse_events = true;\n",
|
|
"\n",
|
|
" canvas_div.resizable({\n",
|
|
" start: function(event, ui) {\n",
|
|
" pass_mouse_events = false;\n",
|
|
" },\n",
|
|
" resize: function(event, ui) {\n",
|
|
" fig.request_resize(ui.size.width, ui.size.height);\n",
|
|
" },\n",
|
|
" stop: function(event, ui) {\n",
|
|
" pass_mouse_events = true;\n",
|
|
" fig.request_resize(ui.size.width, ui.size.height);\n",
|
|
" },\n",
|
|
" });\n",
|
|
"\n",
|
|
" function mouse_event_fn(event) {\n",
|
|
" if (pass_mouse_events)\n",
|
|
" return fig.mouse_event(event, event['data']);\n",
|
|
" }\n",
|
|
"\n",
|
|
" rubberband.mousedown('button_press', mouse_event_fn);\n",
|
|
" rubberband.mouseup('button_release', mouse_event_fn);\n",
|
|
" // Throttle sequential mouse events to 1 every 20ms.\n",
|
|
" rubberband.mousemove('motion_notify', mouse_event_fn);\n",
|
|
"\n",
|
|
" rubberband.mouseenter('figure_enter', mouse_event_fn);\n",
|
|
" rubberband.mouseleave('figure_leave', mouse_event_fn);\n",
|
|
"\n",
|
|
" canvas_div.on(\"wheel\", function (event) {\n",
|
|
" event = event.originalEvent;\n",
|
|
" event['data'] = 'scroll'\n",
|
|
" if (event.deltaY < 0) {\n",
|
|
" event.step = 1;\n",
|
|
" } else {\n",
|
|
" event.step = -1;\n",
|
|
" }\n",
|
|
" mouse_event_fn(event);\n",
|
|
" });\n",
|
|
"\n",
|
|
" canvas_div.append(canvas);\n",
|
|
" canvas_div.append(rubberband);\n",
|
|
"\n",
|
|
" this.rubberband = rubberband;\n",
|
|
" this.rubberband_canvas = rubberband[0];\n",
|
|
" this.rubberband_context = rubberband[0].getContext(\"2d\");\n",
|
|
" this.rubberband_context.strokeStyle = \"#000000\";\n",
|
|
"\n",
|
|
" this._resize_canvas = function(width, height) {\n",
|
|
" // Keep the size of the canvas, canvas container, and rubber band\n",
|
|
" // canvas in synch.\n",
|
|
" canvas_div.css('width', width)\n",
|
|
" canvas_div.css('height', height)\n",
|
|
"\n",
|
|
" canvas.attr('width', width * mpl.ratio);\n",
|
|
" canvas.attr('height', height * mpl.ratio);\n",
|
|
" canvas.attr('style', 'width: ' + width + 'px; height: ' + height + 'px;');\n",
|
|
"\n",
|
|
" rubberband.attr('width', width);\n",
|
|
" rubberband.attr('height', height);\n",
|
|
" }\n",
|
|
"\n",
|
|
" // Set the figure to an initial 600x600px, this will subsequently be updated\n",
|
|
" // upon first draw.\n",
|
|
" this._resize_canvas(600, 600);\n",
|
|
"\n",
|
|
" // Disable right mouse context menu.\n",
|
|
" $(this.rubberband_canvas).bind(\"contextmenu\",function(e){\n",
|
|
" return false;\n",
|
|
" });\n",
|
|
"\n",
|
|
" function set_focus () {\n",
|
|
" canvas.focus();\n",
|
|
" canvas_div.focus();\n",
|
|
" }\n",
|
|
"\n",
|
|
" window.setTimeout(set_focus, 100);\n",
|
|
"}\n",
|
|
"\n",
|
|
"mpl.figure.prototype._init_toolbar = function() {\n",
|
|
" var fig = this;\n",
|
|
"\n",
|
|
" var nav_element = $('<div/>')\n",
|
|
" nav_element.attr('style', 'width: 100%');\n",
|
|
" this.root.append(nav_element);\n",
|
|
"\n",
|
|
" // Define a callback function for later on.\n",
|
|
" function toolbar_event(event) {\n",
|
|
" return fig.toolbar_button_onclick(event['data']);\n",
|
|
" }\n",
|
|
" function toolbar_mouse_event(event) {\n",
|
|
" return fig.toolbar_button_onmouseover(event['data']);\n",
|
|
" }\n",
|
|
"\n",
|
|
" for(var toolbar_ind in mpl.toolbar_items) {\n",
|
|
" var name = mpl.toolbar_items[toolbar_ind][0];\n",
|
|
" var tooltip = mpl.toolbar_items[toolbar_ind][1];\n",
|
|
" var image = mpl.toolbar_items[toolbar_ind][2];\n",
|
|
" var method_name = mpl.toolbar_items[toolbar_ind][3];\n",
|
|
"\n",
|
|
" if (!name) {\n",
|
|
" // put a spacer in here.\n",
|
|
" continue;\n",
|
|
" }\n",
|
|
" var button = $('<button/>');\n",
|
|
" button.addClass('ui-button ui-widget ui-state-default ui-corner-all ' +\n",
|
|
" 'ui-button-icon-only');\n",
|
|
" button.attr('role', 'button');\n",
|
|
" button.attr('aria-disabled', 'false');\n",
|
|
" button.click(method_name, toolbar_event);\n",
|
|
" button.mouseover(tooltip, toolbar_mouse_event);\n",
|
|
"\n",
|
|
" var icon_img = $('<span/>');\n",
|
|
" icon_img.addClass('ui-button-icon-primary ui-icon');\n",
|
|
" icon_img.addClass(image);\n",
|
|
" icon_img.addClass('ui-corner-all');\n",
|
|
"\n",
|
|
" var tooltip_span = $('<span/>');\n",
|
|
" tooltip_span.addClass('ui-button-text');\n",
|
|
" tooltip_span.html(tooltip);\n",
|
|
"\n",
|
|
" button.append(icon_img);\n",
|
|
" button.append(tooltip_span);\n",
|
|
"\n",
|
|
" nav_element.append(button);\n",
|
|
" }\n",
|
|
"\n",
|
|
" var fmt_picker_span = $('<span/>');\n",
|
|
"\n",
|
|
" var fmt_picker = $('<select/>');\n",
|
|
" fmt_picker.addClass('mpl-toolbar-option ui-widget ui-widget-content');\n",
|
|
" fmt_picker_span.append(fmt_picker);\n",
|
|
" nav_element.append(fmt_picker_span);\n",
|
|
" this.format_dropdown = fmt_picker[0];\n",
|
|
"\n",
|
|
" for (var ind in mpl.extensions) {\n",
|
|
" var fmt = mpl.extensions[ind];\n",
|
|
" var option = $(\n",
|
|
" '<option/>', {selected: fmt === mpl.default_extension}).html(fmt);\n",
|
|
" fmt_picker.append(option)\n",
|
|
" }\n",
|
|
"\n",
|
|
" // Add hover states to the ui-buttons\n",
|
|
" $( \".ui-button\" ).hover(\n",
|
|
" function() { $(this).addClass(\"ui-state-hover\");},\n",
|
|
" function() { $(this).removeClass(\"ui-state-hover\");}\n",
|
|
" );\n",
|
|
"\n",
|
|
" var status_bar = $('<span class=\"mpl-message\"/>');\n",
|
|
" nav_element.append(status_bar);\n",
|
|
" this.message = status_bar[0];\n",
|
|
"}\n",
|
|
"\n",
|
|
"mpl.figure.prototype.request_resize = function(x_pixels, y_pixels) {\n",
|
|
" // Request matplotlib to resize the figure. Matplotlib will then trigger a resize in the client,\n",
|
|
" // which will in turn request a refresh of the image.\n",
|
|
" this.send_message('resize', {'width': x_pixels, 'height': y_pixels});\n",
|
|
"}\n",
|
|
"\n",
|
|
"mpl.figure.prototype.send_message = function(type, properties) {\n",
|
|
" properties['type'] = type;\n",
|
|
" properties['figure_id'] = this.id;\n",
|
|
" this.ws.send(JSON.stringify(properties));\n",
|
|
"}\n",
|
|
"\n",
|
|
"mpl.figure.prototype.send_draw_message = function() {\n",
|
|
" if (!this.waiting) {\n",
|
|
" this.waiting = true;\n",
|
|
" this.ws.send(JSON.stringify({type: \"draw\", figure_id: this.id}));\n",
|
|
" }\n",
|
|
"}\n",
|
|
"\n",
|
|
"\n",
|
|
"mpl.figure.prototype.handle_save = function(fig, msg) {\n",
|
|
" var format_dropdown = fig.format_dropdown;\n",
|
|
" var format = format_dropdown.options[format_dropdown.selectedIndex].value;\n",
|
|
" fig.ondownload(fig, format);\n",
|
|
"}\n",
|
|
"\n",
|
|
"\n",
|
|
"mpl.figure.prototype.handle_resize = function(fig, msg) {\n",
|
|
" var size = msg['size'];\n",
|
|
" if (size[0] != fig.canvas.width || size[1] != fig.canvas.height) {\n",
|
|
" fig._resize_canvas(size[0], size[1]);\n",
|
|
" fig.send_message(\"refresh\", {});\n",
|
|
" };\n",
|
|
"}\n",
|
|
"\n",
|
|
"mpl.figure.prototype.handle_rubberband = function(fig, msg) {\n",
|
|
" var x0 = msg['x0'] / mpl.ratio;\n",
|
|
" var y0 = (fig.canvas.height - msg['y0']) / mpl.ratio;\n",
|
|
" var x1 = msg['x1'] / mpl.ratio;\n",
|
|
" var y1 = (fig.canvas.height - msg['y1']) / mpl.ratio;\n",
|
|
" x0 = Math.floor(x0) + 0.5;\n",
|
|
" y0 = Math.floor(y0) + 0.5;\n",
|
|
" x1 = Math.floor(x1) + 0.5;\n",
|
|
" y1 = Math.floor(y1) + 0.5;\n",
|
|
" var min_x = Math.min(x0, x1);\n",
|
|
" var min_y = Math.min(y0, y1);\n",
|
|
" var width = Math.abs(x1 - x0);\n",
|
|
" var height = Math.abs(y1 - y0);\n",
|
|
"\n",
|
|
" fig.rubberband_context.clearRect(\n",
|
|
" 0, 0, fig.canvas.width, fig.canvas.height);\n",
|
|
"\n",
|
|
" fig.rubberband_context.strokeRect(min_x, min_y, width, height);\n",
|
|
"}\n",
|
|
"\n",
|
|
"mpl.figure.prototype.handle_figure_label = function(fig, msg) {\n",
|
|
" // Updates the figure title.\n",
|
|
" fig.header.textContent = msg['label'];\n",
|
|
"}\n",
|
|
"\n",
|
|
"mpl.figure.prototype.handle_cursor = function(fig, msg) {\n",
|
|
" var cursor = msg['cursor'];\n",
|
|
" switch(cursor)\n",
|
|
" {\n",
|
|
" case 0:\n",
|
|
" cursor = 'pointer';\n",
|
|
" break;\n",
|
|
" case 1:\n",
|
|
" cursor = 'default';\n",
|
|
" break;\n",
|
|
" case 2:\n",
|
|
" cursor = 'crosshair';\n",
|
|
" break;\n",
|
|
" case 3:\n",
|
|
" cursor = 'move';\n",
|
|
" break;\n",
|
|
" }\n",
|
|
" fig.rubberband_canvas.style.cursor = cursor;\n",
|
|
"}\n",
|
|
"\n",
|
|
"mpl.figure.prototype.handle_message = function(fig, msg) {\n",
|
|
" fig.message.textContent = msg['message'];\n",
|
|
"}\n",
|
|
"\n",
|
|
"mpl.figure.prototype.handle_draw = function(fig, msg) {\n",
|
|
" // Request the server to send over a new figure.\n",
|
|
" fig.send_draw_message();\n",
|
|
"}\n",
|
|
"\n",
|
|
"mpl.figure.prototype.handle_image_mode = function(fig, msg) {\n",
|
|
" fig.image_mode = msg['mode'];\n",
|
|
"}\n",
|
|
"\n",
|
|
"mpl.figure.prototype.updated_canvas_event = function() {\n",
|
|
" // Called whenever the canvas gets updated.\n",
|
|
" this.send_message(\"ack\", {});\n",
|
|
"}\n",
|
|
"\n",
|
|
"// A function to construct a web socket function for onmessage handling.\n",
|
|
"// Called in the figure constructor.\n",
|
|
"mpl.figure.prototype._make_on_message_function = function(fig) {\n",
|
|
" return function socket_on_message(evt) {\n",
|
|
" if (evt.data instanceof Blob) {\n",
|
|
" /* FIXME: We get \"Resource interpreted as Image but\n",
|
|
" * transferred with MIME type text/plain:\" errors on\n",
|
|
" * Chrome. But how to set the MIME type? It doesn't seem\n",
|
|
" * to be part of the websocket stream */\n",
|
|
" evt.data.type = \"image/png\";\n",
|
|
"\n",
|
|
" /* Free the memory for the previous frames */\n",
|
|
" if (fig.imageObj.src) {\n",
|
|
" (window.URL || window.webkitURL).revokeObjectURL(\n",
|
|
" fig.imageObj.src);\n",
|
|
" }\n",
|
|
"\n",
|
|
" fig.imageObj.src = (window.URL || window.webkitURL).createObjectURL(\n",
|
|
" evt.data);\n",
|
|
" fig.updated_canvas_event();\n",
|
|
" fig.waiting = false;\n",
|
|
" return;\n",
|
|
" }\n",
|
|
" else if (typeof evt.data === 'string' && evt.data.slice(0, 21) == \"data:image/png;base64\") {\n",
|
|
" fig.imageObj.src = evt.data;\n",
|
|
" fig.updated_canvas_event();\n",
|
|
" fig.waiting = false;\n",
|
|
" return;\n",
|
|
" }\n",
|
|
"\n",
|
|
" var msg = JSON.parse(evt.data);\n",
|
|
" var msg_type = msg['type'];\n",
|
|
"\n",
|
|
" // Call the \"handle_{type}\" callback, which takes\n",
|
|
" // the figure and JSON message as its only arguments.\n",
|
|
" try {\n",
|
|
" var callback = fig[\"handle_\" + msg_type];\n",
|
|
" } catch (e) {\n",
|
|
" console.log(\"No handler for the '\" + msg_type + \"' message type: \", msg);\n",
|
|
" return;\n",
|
|
" }\n",
|
|
"\n",
|
|
" if (callback) {\n",
|
|
" try {\n",
|
|
" // console.log(\"Handling '\" + msg_type + \"' message: \", msg);\n",
|
|
" callback(fig, msg);\n",
|
|
" } catch (e) {\n",
|
|
" console.log(\"Exception inside the 'handler_\" + msg_type + \"' callback:\", e, e.stack, msg);\n",
|
|
" }\n",
|
|
" }\n",
|
|
" };\n",
|
|
"}\n",
|
|
"\n",
|
|
"// from http://stackoverflow.com/questions/1114465/getting-mouse-location-in-canvas\n",
|
|
"mpl.findpos = function(e) {\n",
|
|
" //this section is from http://www.quirksmode.org/js/events_properties.html\n",
|
|
" var targ;\n",
|
|
" if (!e)\n",
|
|
" e = window.event;\n",
|
|
" if (e.target)\n",
|
|
" targ = e.target;\n",
|
|
" else if (e.srcElement)\n",
|
|
" targ = e.srcElement;\n",
|
|
" if (targ.nodeType == 3) // defeat Safari bug\n",
|
|
" targ = targ.parentNode;\n",
|
|
"\n",
|
|
" // jQuery normalizes the pageX and pageY\n",
|
|
" // pageX,Y are the mouse positions relative to the document\n",
|
|
" // offset() returns the position of the element relative to the document\n",
|
|
" var x = e.pageX - $(targ).offset().left;\n",
|
|
" var y = e.pageY - $(targ).offset().top;\n",
|
|
"\n",
|
|
" return {\"x\": x, \"y\": y};\n",
|
|
"};\n",
|
|
"\n",
|
|
"/*\n",
|
|
" * return a copy of an object with only non-object keys\n",
|
|
" * we need this to avoid circular references\n",
|
|
" * http://stackoverflow.com/a/24161582/3208463\n",
|
|
" */\n",
|
|
"function simpleKeys (original) {\n",
|
|
" return Object.keys(original).reduce(function (obj, key) {\n",
|
|
" if (typeof original[key] !== 'object')\n",
|
|
" obj[key] = original[key]\n",
|
|
" return obj;\n",
|
|
" }, {});\n",
|
|
"}\n",
|
|
"\n",
|
|
"mpl.figure.prototype.mouse_event = function(event, name) {\n",
|
|
" var canvas_pos = mpl.findpos(event)\n",
|
|
"\n",
|
|
" if (name === 'button_press')\n",
|
|
" {\n",
|
|
" this.canvas.focus();\n",
|
|
" this.canvas_div.focus();\n",
|
|
" }\n",
|
|
"\n",
|
|
" var x = canvas_pos.x * mpl.ratio;\n",
|
|
" var y = canvas_pos.y * mpl.ratio;\n",
|
|
"\n",
|
|
" this.send_message(name, {x: x, y: y, button: event.button,\n",
|
|
" step: event.step,\n",
|
|
" guiEvent: simpleKeys(event)});\n",
|
|
"\n",
|
|
" /* This prevents the web browser from automatically changing to\n",
|
|
" * the text insertion cursor when the button is pressed. We want\n",
|
|
" * to control all of the cursor setting manually through the\n",
|
|
" * 'cursor' event from matplotlib */\n",
|
|
" event.preventDefault();\n",
|
|
" return false;\n",
|
|
"}\n",
|
|
"\n",
|
|
"mpl.figure.prototype._key_event_extra = function(event, name) {\n",
|
|
" // Handle any extra behaviour associated with a key event\n",
|
|
"}\n",
|
|
"\n",
|
|
"mpl.figure.prototype.key_event = function(event, name) {\n",
|
|
"\n",
|
|
" // Prevent repeat events\n",
|
|
" if (name == 'key_press')\n",
|
|
" {\n",
|
|
" if (event.which === this._key)\n",
|
|
" return;\n",
|
|
" else\n",
|
|
" this._key = event.which;\n",
|
|
" }\n",
|
|
" if (name == 'key_release')\n",
|
|
" this._key = null;\n",
|
|
"\n",
|
|
" var value = '';\n",
|
|
" if (event.ctrlKey && event.which != 17)\n",
|
|
" value += \"ctrl+\";\n",
|
|
" if (event.altKey && event.which != 18)\n",
|
|
" value += \"alt+\";\n",
|
|
" if (event.shiftKey && event.which != 16)\n",
|
|
" value += \"shift+\";\n",
|
|
"\n",
|
|
" value += 'k';\n",
|
|
" value += event.which.toString();\n",
|
|
"\n",
|
|
" this._key_event_extra(event, name);\n",
|
|
"\n",
|
|
" this.send_message(name, {key: value,\n",
|
|
" guiEvent: simpleKeys(event)});\n",
|
|
" return false;\n",
|
|
"}\n",
|
|
"\n",
|
|
"mpl.figure.prototype.toolbar_button_onclick = function(name) {\n",
|
|
" if (name == 'download') {\n",
|
|
" this.handle_save(this, null);\n",
|
|
" } else {\n",
|
|
" this.send_message(\"toolbar_button\", {name: name});\n",
|
|
" }\n",
|
|
"};\n",
|
|
"\n",
|
|
"mpl.figure.prototype.toolbar_button_onmouseover = function(tooltip) {\n",
|
|
" this.message.textContent = tooltip;\n",
|
|
"};\n",
|
|
"mpl.toolbar_items = [[\"Home\", \"Reset original view\", \"fa fa-home icon-home\", \"home\"], [\"Back\", \"Back to previous view\", \"fa fa-arrow-left icon-arrow-left\", \"back\"], [\"Forward\", \"Forward to next view\", \"fa fa-arrow-right icon-arrow-right\", \"forward\"], [\"\", \"\", \"\", \"\"], [\"Pan\", \"Pan axes with left mouse, zoom with right\", \"fa fa-arrows icon-move\", \"pan\"], [\"Zoom\", \"Zoom to rectangle\", \"fa fa-square-o icon-check-empty\", \"zoom\"], [\"\", \"\", \"\", \"\"], [\"Download\", \"Download plot\", \"fa fa-floppy-o icon-save\", \"download\"]];\n",
|
|
"\n",
|
|
"mpl.extensions = [\"eps\", \"jpeg\", \"pdf\", \"png\", \"ps\", \"raw\", \"svg\", \"tif\"];\n",
|
|
"\n",
|
|
"mpl.default_extension = \"png\";var comm_websocket_adapter = function(comm) {\n",
|
|
" // Create a \"websocket\"-like object which calls the given IPython comm\n",
|
|
" // object with the appropriate methods. Currently this is a non binary\n",
|
|
" // socket, so there is still some room for performance tuning.\n",
|
|
" var ws = {};\n",
|
|
"\n",
|
|
" ws.close = function() {\n",
|
|
" comm.close()\n",
|
|
" };\n",
|
|
" ws.send = function(m) {\n",
|
|
" //console.log('sending', m);\n",
|
|
" comm.send(m);\n",
|
|
" };\n",
|
|
" // Register the callback with on_msg.\n",
|
|
" comm.on_msg(function(msg) {\n",
|
|
" //console.log('receiving', msg['content']['data'], msg);\n",
|
|
" // Pass the mpl event to the overridden (by mpl) onmessage function.\n",
|
|
" ws.onmessage(msg['content']['data'])\n",
|
|
" });\n",
|
|
" return ws;\n",
|
|
"}\n",
|
|
"\n",
|
|
"mpl.mpl_figure_comm = function(comm, msg) {\n",
|
|
" // This is the function which gets called when the mpl process\n",
|
|
" // starts-up an IPython Comm through the \"matplotlib\" channel.\n",
|
|
"\n",
|
|
" var id = msg.content.data.id;\n",
|
|
" // Get hold of the div created by the display call when the Comm\n",
|
|
" // socket was opened in Python.\n",
|
|
" var element = $(\"#\" + id);\n",
|
|
" var ws_proxy = comm_websocket_adapter(comm)\n",
|
|
"\n",
|
|
" function ondownload(figure, format) {\n",
|
|
" window.open(figure.imageObj.src);\n",
|
|
" }\n",
|
|
"\n",
|
|
" var fig = new mpl.figure(id, ws_proxy,\n",
|
|
" ondownload,\n",
|
|
" element.get(0));\n",
|
|
"\n",
|
|
" // Call onopen now - mpl needs it, as it is assuming we've passed it a real\n",
|
|
" // web socket which is closed, not our websocket->open comm proxy.\n",
|
|
" ws_proxy.onopen();\n",
|
|
"\n",
|
|
" fig.parent_element = element.get(0);\n",
|
|
" fig.cell_info = mpl.find_output_cell(\"<div id='\" + id + \"'></div>\");\n",
|
|
" if (!fig.cell_info) {\n",
|
|
" console.error(\"Failed to find cell for figure\", id, fig);\n",
|
|
" return;\n",
|
|
" }\n",
|
|
"\n",
|
|
" var output_index = fig.cell_info[2]\n",
|
|
" var cell = fig.cell_info[0];\n",
|
|
"\n",
|
|
"};\n",
|
|
"\n",
|
|
"mpl.figure.prototype.handle_close = function(fig, msg) {\n",
|
|
" var width = fig.canvas.width/mpl.ratio\n",
|
|
" fig.root.unbind('remove')\n",
|
|
"\n",
|
|
" // Update the output cell to use the data from the current canvas.\n",
|
|
" fig.push_to_output();\n",
|
|
" var dataURL = fig.canvas.toDataURL();\n",
|
|
" // Re-enable the keyboard manager in IPython - without this line, in FF,\n",
|
|
" // the notebook keyboard shortcuts fail.\n",
|
|
" IPython.keyboard_manager.enable()\n",
|
|
" $(fig.parent_element).html('<img src=\"' + dataURL + '\" width=\"' + width + '\">');\n",
|
|
" fig.close_ws(fig, msg);\n",
|
|
"}\n",
|
|
"\n",
|
|
"mpl.figure.prototype.close_ws = function(fig, msg){\n",
|
|
" fig.send_message('closing', msg);\n",
|
|
" // fig.ws.close()\n",
|
|
"}\n",
|
|
"\n",
|
|
"mpl.figure.prototype.push_to_output = function(remove_interactive) {\n",
|
|
" // Turn the data on the canvas into data in the output cell.\n",
|
|
" var width = this.canvas.width/mpl.ratio\n",
|
|
" var dataURL = this.canvas.toDataURL();\n",
|
|
" this.cell_info[1]['text/html'] = '<img src=\"' + dataURL + '\" width=\"' + width + '\">';\n",
|
|
"}\n",
|
|
"\n",
|
|
"mpl.figure.prototype.updated_canvas_event = function() {\n",
|
|
" // Tell IPython that the notebook contents must change.\n",
|
|
" IPython.notebook.set_dirty(true);\n",
|
|
" this.send_message(\"ack\", {});\n",
|
|
" var fig = this;\n",
|
|
" // Wait a second, then push the new image to the DOM so\n",
|
|
" // that it is saved nicely (might be nice to debounce this).\n",
|
|
" setTimeout(function () { fig.push_to_output() }, 1000);\n",
|
|
"}\n",
|
|
"\n",
|
|
"mpl.figure.prototype._init_toolbar = function() {\n",
|
|
" var fig = this;\n",
|
|
"\n",
|
|
" var nav_element = $('<div/>')\n",
|
|
" nav_element.attr('style', 'width: 100%');\n",
|
|
" this.root.append(nav_element);\n",
|
|
"\n",
|
|
" // Define a callback function for later on.\n",
|
|
" function toolbar_event(event) {\n",
|
|
" return fig.toolbar_button_onclick(event['data']);\n",
|
|
" }\n",
|
|
" function toolbar_mouse_event(event) {\n",
|
|
" return fig.toolbar_button_onmouseover(event['data']);\n",
|
|
" }\n",
|
|
"\n",
|
|
" for(var toolbar_ind in mpl.toolbar_items){\n",
|
|
" var name = mpl.toolbar_items[toolbar_ind][0];\n",
|
|
" var tooltip = mpl.toolbar_items[toolbar_ind][1];\n",
|
|
" var image = mpl.toolbar_items[toolbar_ind][2];\n",
|
|
" var method_name = mpl.toolbar_items[toolbar_ind][3];\n",
|
|
"\n",
|
|
" if (!name) { continue; };\n",
|
|
"\n",
|
|
" var button = $('<button class=\"btn btn-default\" href=\"#\" title=\"' + name + '\"><i class=\"fa ' + image + ' fa-lg\"></i></button>');\n",
|
|
" button.click(method_name, toolbar_event);\n",
|
|
" button.mouseover(tooltip, toolbar_mouse_event);\n",
|
|
" nav_element.append(button);\n",
|
|
" }\n",
|
|
"\n",
|
|
" // Add the status bar.\n",
|
|
" var status_bar = $('<span class=\"mpl-message\" style=\"text-align:right; float: right;\"/>');\n",
|
|
" nav_element.append(status_bar);\n",
|
|
" this.message = status_bar[0];\n",
|
|
"\n",
|
|
" // Add the close button to the window.\n",
|
|
" var buttongrp = $('<div class=\"btn-group inline pull-right\"></div>');\n",
|
|
" var button = $('<button class=\"btn btn-mini btn-primary\" href=\"#\" title=\"Stop Interaction\"><i class=\"fa fa-power-off icon-remove icon-large\"></i></button>');\n",
|
|
" button.click(function (evt) { fig.handle_close(fig, {}); } );\n",
|
|
" button.mouseover('Stop Interaction', toolbar_mouse_event);\n",
|
|
" buttongrp.append(button);\n",
|
|
" var titlebar = this.root.find($('.ui-dialog-titlebar'));\n",
|
|
" titlebar.prepend(buttongrp);\n",
|
|
"}\n",
|
|
"\n",
|
|
"mpl.figure.prototype._root_extra_style = function(el){\n",
|
|
" var fig = this\n",
|
|
" el.on(\"remove\", function(){\n",
|
|
"\tfig.close_ws(fig, {});\n",
|
|
" });\n",
|
|
"}\n",
|
|
"\n",
|
|
"mpl.figure.prototype._canvas_extra_style = function(el){\n",
|
|
" // this is important to make the div 'focusable\n",
|
|
" el.attr('tabindex', 0)\n",
|
|
" // reach out to IPython and tell the keyboard manager to turn it's self\n",
|
|
" // off when our div gets focus\n",
|
|
"\n",
|
|
" // location in version 3\n",
|
|
" if (IPython.notebook.keyboard_manager) {\n",
|
|
" IPython.notebook.keyboard_manager.register_events(el);\n",
|
|
" }\n",
|
|
" else {\n",
|
|
" // location in version 2\n",
|
|
" IPython.keyboard_manager.register_events(el);\n",
|
|
" }\n",
|
|
"\n",
|
|
"}\n",
|
|
"\n",
|
|
"mpl.figure.prototype._key_event_extra = function(event, name) {\n",
|
|
" var manager = IPython.notebook.keyboard_manager;\n",
|
|
" if (!manager)\n",
|
|
" manager = IPython.keyboard_manager;\n",
|
|
"\n",
|
|
" // Check for shift+enter\n",
|
|
" if (event.shiftKey && event.which == 13) {\n",
|
|
" this.canvas_div.blur();\n",
|
|
" event.shiftKey = false;\n",
|
|
" // Send a \"J\" for go to next cell\n",
|
|
" event.which = 74;\n",
|
|
" event.keyCode = 74;\n",
|
|
" manager.command_mode();\n",
|
|
" manager.handle_keydown(event);\n",
|
|
" }\n",
|
|
"}\n",
|
|
"\n",
|
|
"mpl.figure.prototype.handle_save = function(fig, msg) {\n",
|
|
" fig.ondownload(fig, null);\n",
|
|
"}\n",
|
|
"\n",
|
|
"\n",
|
|
"mpl.find_output_cell = function(html_output) {\n",
|
|
" // Return the cell and output element which can be found *uniquely* in the notebook.\n",
|
|
" // Note - this is a bit hacky, but it is done because the \"notebook_saving.Notebook\"\n",
|
|
" // IPython event is triggered only after the cells have been serialised, which for\n",
|
|
" // our purposes (turning an active figure into a static one), is too late.\n",
|
|
" var cells = IPython.notebook.get_cells();\n",
|
|
" var ncells = cells.length;\n",
|
|
" for (var i=0; i<ncells; i++) {\n",
|
|
" var cell = cells[i];\n",
|
|
" if (cell.cell_type === 'code'){\n",
|
|
" for (var j=0; j<cell.output_area.outputs.length; j++) {\n",
|
|
" var data = cell.output_area.outputs[j];\n",
|
|
" if (data.data) {\n",
|
|
" // IPython >= 3 moved mimebundle to data attribute of output\n",
|
|
" data = data.data;\n",
|
|
" }\n",
|
|
" if (data['text/html'] == html_output) {\n",
|
|
" return [cell, data, j];\n",
|
|
" }\n",
|
|
" }\n",
|
|
" }\n",
|
|
" }\n",
|
|
"}\n",
|
|
"\n",
|
|
"// Register the function which deals with the matplotlib target/channel.\n",
|
|
"// The kernel may be null if the page has been refreshed.\n",
|
|
"if (IPython.notebook.kernel != null) {\n",
|
|
" IPython.notebook.kernel.comm_manager.register_target('matplotlib', mpl.mpl_figure_comm);\n",
|
|
"}\n"
|
|
],
|
|
"text/plain": [
|
|
"<IPython.core.display.Javascript object>"
|
|
]
|
|
},
|
|
"metadata": {},
|
|
"output_type": "display_data"
|
|
},
|
|
{
|
|
"data": {
|
|
"text/html": [
|
|
"<img src=\"data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAoAAAAHgCAYAAAA10dzkAAAgAElEQVR4Xu3dB3gUdf7H8U/oBAgBBAQMoBQl4CmgVEE4u+dJuxPUA0FFLH8Fy+lZDw4BBU/w9OgnTRAuSrFxeJwCSlNRLPTeQQQSKSGQ8n9+EzdSEjK7v2xmy3uehwcl852dec139/fJtI3JysrKEhMCCCCAAAIIIIBA1AjEEACjZl+zoQgggAACCCCAgCNAAKQREEAAAQQQQACBKBMgAEbZDmdzEUAAAQQQQAABAiA9gAACCCCAAAIIRJkAATDKdjibiwACCCCAAAIIEADpAQQQQAABBBBAIMoECIBRtsPZXAQQQAABBBBAgABIDyCAAAIIIIAAAlEmQACMsh3O5iKAAAIIIIAAAgRAegABBBBAAAEEEIgyAQJglO1wNhcBBBBAAAEEECAA0gMIIIAAAggggECUCRAAo2yHs7kIIIAAAggggAABkB5AAAEEEEAAAQSiTIAAGGU7nM1FAAEEEEAAAQQIgPQAAggggAACCCAQZQIEwCjb4WwuAggggAACCCBAAKQHEEAAAQQQQACBKBMgAEbZDmdzEUAAAQQQQAABAiA9gAACCCCAAAIIRJkAATDKdjibiwACCCCAAAIIEADpAQQQQAABBBBAIMoECIBRtsPZXAQQQAABBBBAgABIDyCAAAIIIIAAAlEmQACMsh3O5iKAAAIIIIAAAgRAegABBBBAAAEEEIgyAQJglO1wNhcBBBBAAAEEECAA0gMIIIAAAggggECUCRAAo2yHs7kIIIAAAggggAABkB5AAAEEEEAAAQSiTIAAGGU7nM1FAAEEEEAAAQQIgPQAAggggAACCCAQZQIEwCjb4WwuAggggAACCCBAAKQHEEAAAQQQQACBKBMgAEbZDmdzEUAAAQQQQAABAiA9gAACCCCAAAIIRJkAATDKdjibiwACCCCAAAIIEADpAQQQQAABBBBAIMoECIBRtsPZXAQQQAABBBBAgABIDyCAAAIIIIAAAlEmQACMsh3O5iKAAAIIIIAAAgRAegABBBBAAAEEEIgyAQJglO1wNhcBBBBAAAEEECAA0gMIIIAAAggggECUCRAAo2yHs7kIIIAAAggggAABkB5AAAEEEEAAAQSiTIAAGGU7nM1FAAEEEEAAAQQIgPQAAggggAACCCAQZQIEwCjb4WwuAggggAACCCBAAKQHEEAAAQQQQACBKBMgAEbZDmdzEUAAAQQQQAABAiA9gAACCCCAAAIIRJkAATDKdjibiwACCCCAAAIIEADpAQQQQAABBBBAIMoECIBRtsPZXAQQQAABBBBAgABIDyCAAAIIIIAAAlEmQAC02OGZmZnavXu3ypUrp5iYGIslUYoAAggggAAChSWQlZWlw4cPq3r16ipSpEhhvWxIvQ4B0GJ37Ny5UwkJCRZLoBQBBBBAAAEEvBLYsWOHLrjgAq9e3tPXJQBa8KekpCg+Pl6mgeLi4iyWRCkCCCCAAAIIFJbAzz//7BzASU5OVvny5QvrZUPqdQiAFrvDNJBpHBMECYAWkJQigAACCCBQiAKM3xIB0KLhaCALPEoRQAABBBDwSIDxmwBo1Xo0kBUfxQgggAACCHgiwPhNALRqPBrIio9iBBBAAAEEPBFg/CYAWjUeDWTFRzECCCCAgIWAeZRJenq6MjIyLJYSmaVFixZVsWLF8nxEG+M3AdCq82kgKz6KEUAAAQQCFDhx4oT27NmjY8eOBbiEyC+LjY1VtWrVVKJEibM2lvGbAGj1DqCBrPgoRgABBBAIQMB8CcGGDRtkjnJVrlzZCTh8GcGvkObIqAnI+/fvd46O1qtX76yHPTN+EwADeOv9WkIDWfFRjAACCCAQgMDx48e1ZcsW1apVS+YoF1PuAubo6LZt23ThhReqVKlSp83E+E0AtHrf0EBWfBQjgAACCAQg4AuAuQWbABYXsSXncmL8JgBaNT4NZMVHMQIIIIBAAAIEQHdoBMBzO/EgaHd9lOtcBEALPEoRQAABBAISIAC6YyMAEgDddUoAcxEAA0CjBAEEEEDASiCcA2DPnj01adKks7bf3NRSt27dc7qMHDlSw4YNc+5+btiwoUaMGKE2bdrkWUMAJABavdHOVUwADBotC0YAAQQQyEMg3APgvn37NGHChNO2ztzNbO5qzmuaMWOGunfvLhMCW7durTFjxmj8+PFavXq1atasmWsZAZAAGLQPEQJg0GhZMAKRK5CaLH2fJO37QSp7vnT5HVKFWpG7vWxZgQuEewBMTk7W7Nmz/XJp3ry5mjRpolGjRuXUNWjQQB07dtSQIUMIgH5pZs/MNYABoPlKCIAWeJQiEI0C25dJST2lw3t+3frisVLHkVLDTtEowjYHIJBbADTPvks96c03gpQuXtT1cwjNKWB/A6B5pp953E1SUpI6dfr1fdK3b1+tXLlSCxcuJAAG0EcEwADQCIAWaJQiEK0CO76UJneQTh6VKl4kJXaUti2WdiyXihST/jRTuujqaNVhu/0QyC0AHjuRrsQX5vmxlIKbdfXfblBsiWKuFmgC4FtvvXXac/luuukmJ9zlNe3evVs1atTQ4sWL1apVq5zZBg8e7FxPuG7dOgKgK/3TZyIABoBGALRAoxSBaBQ4vE8a01Y6sle6qL3UbZpUIlbKzJBm9ck+JVyumvTQcqlU+WgUYpv9EAj3ALhr167TTuWWKVPG+cq2/ALgkiVL1LJly5zZBg0apClTpmjt2rUEQD/6xzcrATAANAKgBRqlCESbQFaWNP1Oad2HUuUG0r3zpZJlf1U4mSqNaiUd3Cy1eFC6MffrmaKNje3NW4BTwNk2nAK2e5cQAC38uAbQAo9SBKJFYNWs7Ov+zGnePoukqg3P3vIN86WpXaSiJaW+30pxeR8NiRY2ttO/ABguXoFcA2i2zdwE0rRpU+cuYN+UmJioDh06cBNIgDufABggnCkjAFrgUYpANAikn5BebyqlbJfaPin99tnct9ocJXzzhuzrAVs9Il0/MBp02MYABaLxLmDfY2BGjx7tnAYeO3asxo0bp1WrVjnfiZzbxGNgzt1gBMAA34AEQAs4ShGIFoEv/yV9+Fj24176rpSKl857y9d+KE2/Q4qtJD22RipWMlqU2E4/BaIxABoic/Rv6NChzoOgGzVqpOHDh6tt27Z56hEACYB+vrXcz84RQPdWzIlA1AmYo3+vXSYd3i3dNExqft+5CTLSpRGXZs/f5V/SpX+IOjI22J1AOAdAd1tYMHMRAAmABdNJuSyFABg0WhaMQPgLrHxbmn1/9t295ro+N0f0Ph0sLXxZqt1G6vlB+BuwBUERIAC6YyUAEgDddUoAcxEAA0CjBIFoEDDX9I1uI+37Xrrmr1Kbx9xtdfL27KOAipEeXyeVq+qujrmiSiDSAuD27dtlbujIazrX172da8cTAAmAQftgIAAGjZYFIxDeApsXZD/02XzLx2OrpdIV3G/PuGukXV9JN78iNevtvo45o0Yg0gJgenq6tm7dmuf+q127tooVc/eg6VMXQgAkAAbtQ4EAGDRaFoxAeAtM/aO04WOp2X3SzcP825bF/5D++zyngf1Ti6q5Iy0ABmvnEQAJgMHqLR4DEzRZFoxAGAv8tEF644rs07gPr5Aq1fFvYw5tk177jRRTRHp8vVS2sn/1zB3xAgRAd7uYAEgAdNcpAczFEcAA0ChBINIF5j0rLX1Dqn+TdMf0wLbWXD+49zup0xjpsm6BLYOqiBUgALrbtQRAAqC7TglgLgJgAGiUIBDJAulp0t8vkVIPSnf8W6p/Q2BbO3+A9Pmr0qW3SV3GBbYMqiJWgADobtcSAAmA7jolgLkIgAGgUYJAJAt8/4707j1SXA2p3/dSkaKBbe3WxdLEm7MfCv3ERqlIkcCWQ1VEChAA3e1WAiAB0F2nBDAXATAANEoQiGSBibdIWz+Trv6L1P7pwLc046T08oXSicNS70+lGk0CXxaVESdAAHS3SwmABEB3nRLAXATAANAoQSBSBQ5skl5vkn3zRt/vpPgEuy2dfqe09gOp/XPS1X+2WxbVESVAAHS3OwmABEB3nRLAXATAANAoQSBSBf77grT4NaneDdKd/7bfyq8mSB/0kxJaSPfMs18eS4gYgXAOgD179tSkSZPO2hcbNmxQ3bp189xHixYt0rBhw7RixQrnu4BnzZqljh07nnOfEgAJgEF70xMAg0bLghEILwHzvb+vNpCO/SR1myZd8jv79fd9K0hMUekv26SS5eyXyRIiQiDcA+C+ffs0YcKE0/ZF5cqVVbRo3tfMzp07V4sXL1aTJk3UpUsXAmABdHJMVpb5ziKmQAQIgIGoUYNABAqsmiUl9ZTKni89ukoq6v+3FuSqMuI3UvI26c53pXrXRiAcmxSIQLgHwOTkZM2ePTuQTXdqYmJiCIAB6/1aSAC0QCQAWuBRikAkCUzuKG3+VGrzhHTN8wW3ZbMfkla+JbXuJ103oOCWy5LCWiDXAGiO5Zw85s12ma88jIlx9drmFDAB0BVV0GciAFoQEwAt8ChFIFIEDm6R/nF59jd/9F0pVahdcFu2cpo0+wHpgiule+cX3HJZUlgL5BoATxyVBlf3Zrue2S2VKOPqtU0AfOutt1SqVKmc+W+66SYlJSW5qucIoGumfGcMiwDo78WfCxYsUPv27c/a+DVr1uiSSy7J+fd3331Xzz//vDZt2qQ6depo0KBB6tSpU75ovhkIgK6pmBGByBX439+kz/4u1fmt1H1WwW5nztfCmesAt0slyxbs8llaWAqEewDctWuXRo0alWNfpkwZVatWzfW+4BSwa6pzzhgWAdDfiz99AXDdunWKi4vLATj1ItOlS5eqTZs2GjhwoBP6zB1FL7zwgj7//HM1b97clS4B0BUTMyEQuQLmeX2vJkpHf5Rumywldij4bR1xqWRuCPnTu1JdrgMseODwWyKngLkGsCC6NiwC4Kkb6ib5+wLgoUOHFB8fn6tT165dZQKcCZe+6cYbb1SFChX09ttvu7IlALpiYiYEIldg1Wwp6S6pbNVfbv4oXvDbOusB6dtp0lWPStf2L/jls8SwE+AmEAJgQTRtRAfA2rVry7xREhMT9dxzz512WrhmzZp69NFHnT++afjw4RoxYoS2bduWq21aWprMH99kAmBCQoJSUlJOO9JYEDuGZSCAQBgITO4gbV4gtXlcuuaF4KzwN1OlOQ9KFzST7v1vcF6DpYaVQDQGwCNHjmjjxo3OfmrcuLFeffVVZ0yvWLGizHie28RzAM/d1hEZAM2pX3PdYNOmTZ3ANmXKFI0ePVrmyGDbtm0dkRIlSmjixIm64447coSmTZumXr16nRbyTuXr37+/Bgw4+048AmBYfXaysggUjIDvmz+cmz++lSrUKpjlnrkU33WARYpJT5nnAXIdYHCgw2ep0RgA87q2/6677nLGcgKg//0bkQEwN4bf//73zrOD3nvvvZwAaJ5Gfvvtt+fMPnXqVN1zzz3OUcPcJo4A+t9gVCAQsQK+b/4w1+WZ6/OCOQ1vJKXsyL7JxNxswhTVAuEcAAtzx3EE8NzaURMAzR2+5tZzcyewmQI5BXwmJdcAFuZbmddCIIQETv3mj65TpQa3BHflZvaRvpsutf2z9NvngvtaLD3kBQiA7nYRAZAA6Aj84Q9/0MGDB/XJJ584/29uAjl8+LA++uijHCHzLCJz0wg3gbh7czEXAlEr8MO70jt3S+WqSf1+KLhv/sgLdMUk6f1HpJqtpLt/vXEtav2jfMMjLQBu377duVY/r2n16tV5Xud3rlYgAEZAAMzv4s+nn35a5rlCkydPdrbW3MhhbgBp2LChTpw44Rz5e+mll2Se+9e5c2dnniVLljjXA5ojgx06dNCcOXOcG0V4DEyUf7Ky+Qi4EZjwO2nb51LbJ6XfPuumwm4e3/WGRUtIf9khFf/1Ibp2C6Y6HAUiLQCmp6dr69atee4KM54XK+b/1ysSACMgAOZ38ad5srhpHjOfmYYOHaqxY8c6obB06dJOEDQh8eabbz5N45133nFC3+bNm3MeBO0LiG4+FDgF7EaJeRCIMIHdK6WxV0vmpoy+30nlawR/A83XfP39YunIPqnnh1Ltq4L/mrxCyApEWgAMFjQBMAICYLCaw3a5BEBbQeoRCEOBmfdJ382QLv2j1GV84W1AUi9p1Uyp/bPS1U8W3uvySiEnQAB0t0sIgARAd50SwFwEwADQKEEgnAV+3i2Zb+bITJd6fyrVaFJ4W/PFOOmjJ6QLr5buyn6aAVN0ChAA3e13AiAB0F2nBDAXATAANEoQCGeB+QOkz1+VarWWev16A1mhbNKPa6SRLaRipbO/F7hYiUJ5WV4k9AQIgO72CQGQAOiuUwKYiwAYABolCISrwPGU7KN/5u/CePTLmU6ZmdKwOlLqQeme/0oJzcJVkvW2FCAAugMkABIA3XVKAHMRAANAowSBcBVY8LK0YLB03sXSg0ulIkULf0um3ymt/SD7O4HNdwMzRaUAAdDdbicAEgDddUoAcxEAA0CjBIFwFEg9JI24TEpLkf7wptSoizdbsXSkNO9pqd710p1J3qwDr+q5AAHQ3S4gABIA3XVKAHMRAANAowSBcBT4ZJC0aKhUuYH0wBKpSBFvtsL3CJqScdJTW705CunNlvOqpwiEcwA0j20zX8N65rRhwwbVrVs3z/08ZMgQzZw5U2vXrnUe79aqVSu9/PLLuvjii/OsIQASAIP2wUEADBotC0YgdASOHpBeu0w6cVi6bbKU2MG7dcvMkF6uLaX9LN23UKp+uXfrwit7JhDuAXDfvn2aMGHCaX6VK1dW0aJ5X1Zx4403qlu3brryyitlHhz97LPP6vvvv5f5lpAyZcrkui8IgATAoL1JCYBBo2XBCISOwHuPSF9Pks6/VLpvkXdH/3wiU/8obfhYumGw1PKh0HFiTQpNINwDYHJysmbPnm3ltX//flWpUkULFy50vtUrt4kASAC0arJzFRMAg0bLghEIDYFdX0vjfispS+r1H6lWS+/X6/MR0vy/SpfcInWb6v36sAaFLpBbsMnKylJqemqhr4t5wdLFSismJsbVa5tTwAURADdu3Kh69eo5RwEbNWpEAHSlf/pMMVmma5gCEiAABsRGEQLhIXDyuDS2nbR/jXTpbVKXcaGx3ju+lP51rVS6gvTnzd4fkQwNlahai9wC4LGTx9R8WnNPHJbfsVyxxWNdvbYJgG+99ZZKlfr1+6xvuukmJSW5v6nJxJYOHTro0KFD+uyzz/J8XY4AcgTQVVMGMhMBMBA1ahAIE4F5z0pL35DKVJYeXCaVOS80VjzjpPRSTenkMemBpVLVxNBYL9ai0ATCPQDu2rVLo0aNyvEy1/BVq1bNtd9DDz2kDz/8UJ9//rkuuOACAqBrOY4ABkh1dhkBsMAoWRACoSXw/TvSu/dkr1O3adIlvwut9ZvcQdq8QLr5FalZ79BaN9Ym6ALRfAr44Ycfdq4fXLRokS688MJzWnMEkCOAQXszEgCDRsuCEfBOwJxinXSLlH5cat1Xuu5v3q1LXq+8aJj0yYtSg99LXd8KvfVjjYIqEI03gZjTvib8zZo1SwsWLHCu/8tvIgASAPPrkYB/TgAMmI5CBEJTYPUcaWYfyVxMbx62fPv00HzW3s4V0vjfSiXLS09ulooWC01P1iooAtEYAB988EFNmzZNc+bMOe3Zf+XLl3eeC5jbRAAkAAblDWgWSgAMGi0LRsCdgLmHbcdyaf08ad8P0pF9UkwRqVx16by6UrXLpQuukOJrnnt56WnS4tekTwdlz1f3OumPE6SS5dytR2HPZZ4H6Hwv8CHp7o+lmt5c/F/Ym83rZQtEYwDM6y5j8zxBc2MJAdD/dwd3AftvllNBALTAoxQBW4GN86X5A6S93+W/pEr1pJotsv9Uu0wqFZ8dFH/eJW36VPrmLSlle/Zymt8vXT8o9I+qJfWUVs2Srv6L1P7p/A2YI2IEwjkAFuZO4AjgubUJgBbdSAC0wKMUgUAFzONZPnxcWvnLtW/m8RPmJo1araS4GlJWppS8I/vxLeY5fubIYGZ6/q8WW0m6tr/UpEf+84bCHF9Plt57WLqgmXTvf0NhjViHQhIgALqDJgASAN11SgBzEQADQKMEARuBI/ul6XdIO7/IPoJnjta1/bMUWzHvpZrTpNuWSDu+kLZ+Lu39XspIy54/9jypemPp4huzn/VXKs5m7Qq3NmWnNLxhtoO5DtA8F5ApKgQiLQBu375diYl5P87IfN1bzZr5XMaRy54nABIAg/aBQAAMGi0LRuBsgaM/SRNuln5al30K13wv70VX+y9lrhv0HREsUkxy+Q0G/r9QIVS80Szb4w8TpEadC+EFeYlQEIi0AGi+23fr1q150tauXVvFivl/oxMBkAAYtPcrATBotCwYgdMFjqdIE2/Jvt7PnObtMUc6L//HQEQ848fPSUteD61vKol4dO83MNICYLBECYAEwGD1FncBB02WBSNwioA5YjfjT9LaD7JP2d79H8Kfj2f7MunNG355HMwmqWhxWicKBAiA7nYyAZAA6K5TApiLI4ABoFGCgL8CX/5L+vAxqUhx6Z55Uo2m/i4hcuc3j4P5+8XS0f1S99lSnfaRu61sWY6AL9iYU6N5PQMPLik1NdU5tWy+MeTU7x42NozfEncBW7xLaCALPEoRcCOwb7U0rn32t3LcMFhq+ZCbquiax9wJbO4IvrK39LtXomvbo3RrMzIytH79elWpUkWVKlWKUoX8N/vAgQP68ccfVb9+fRUtWvS0AsZvAmD+HXSOOWggKz6KETi3wMlUaWz77Me51L1WuiNJKlIEtTMFzEOwp92W/fDrR1dhFCUdsmfPHiUnJzshMDY2Vnk9KDlKOE7bTPO1cceOHXPCX3x8vKpVq3YWA+M3AdDqvUEDWfFRjMC5Bcyz/r4cL5WpIj2wWCpbBbHcBMxzEc23gpw4It09L/th10wRL2BCzt69e50QyJS7gAl/559/fq7hmPGbAGj1vqGBrPgoRiBvgTUfSDPuzP75n2ZKda9B61wCsx6Qvp0mNe0p/f41rKJIwJwOPnnyZBRtsbtNLV68+FmnfU+tZPwmALrrpDzmooGs+ChGIHcB84DjUa2l48lSq0ek6wcilZ/A5oXS5FulUuWlx9dLxUvlV8HPEYhqAcZvAqDVG4AGsuKjGIGzBcxdrZNulbZ9nv0NHXd/LBUrgVR+ApmZ0ohG2d9tbB6Qndghvwp+jkBUCzB+EwCt3gA0kBUfxQicLbBwmPTpi1KJslKfRVKlOii5FfjvX6XFI6T6N0l3THdbxXwIRKUA4zcB0KrxaSArPooROF3APNTYfNVbVobUaYx0WTeE/BHYv076Z7Ps7wbu+50Un+BPNfMiEFUCjN8EQKuGp4Gs+ChG4FeB1GRp9FVSyg7pN12lzmPRCURg0u+lLYuk1v2k6wYEsgRqEIgKAcZvAqBVo9NAVnwUI5AtYL7qLamntHq2VOHC7FO/peLQCURg7YfS9Duk0hWkR1dLJWIDWQo1CES8AOM3AdCqyWkgKz6KEcgWWD5WmvtnqUix7Js+LuCr3gJuDXMTzT8aS8nbpBuGSC0fDHhRFCIQyQKM3wRAq/6mgaz4KEZA2rlCevMGKfMkgaWg+uGrCdIH/aSyVaW+30rFSxfUklkOAhEjwPhNALRqZhrIio/iaBc4dlAac7WUsl1qcGv240tiYqJdxX77009IrzfJvp7y+kFSq/+zXyZLQCDCBBi/CYBWLU0DWfFRHM0CJqRM7ZJ9w0LFi6T7FmQ/xJipYAS+niy997BUsrz0yNdSmfMKZrksBYEIEWD8JgBatTINZMVHcbQKmJs+ZpuvLns7+3l/5vtrz28UrRrB2W5zLeDYdtLe76TG3aUObwTndVgqAmEqwPhNALRqXRrIio/iaBX430Dps1ekmKLSHf+W6l0brRLB3W7zXEVzfaWZ/vSuVBfn4IKz9HASYPwOkwC4aNEiDRs2TCtWrNCePXs0a9YsdezYMc9emzlzpkaNGqWVK1cqLS1NDRs2VP/+/XXDDb98GErO/w8YcPpzsqpWraq9e/e67mEayDUVMyKQLfDpEGnhS9n/fcsI6YpeyARTYO5T0vLRUtnzpfs/k8pWCearsWwEwkaA8TtMAuDcuXO1ePFiNWnSRF26dMk3APbr10/Vq1dX+/btFR8frwkTJuiVV17R8uXL1bhxY6dBTQB85513NH/+/JyGLVq0qCpXruy6gWkg11TMGO0C5rtq578gLXk9W+L6F6VWD0e7SvC3/8QxaezV0k/rpYQW0l3vScVKBv91eQUEQlyA8TtMAuCpfRQTE5NvAMyt78xRwK5du+qFF17ICYCzZ892jhIGOtFAgcpRF1UCx3/OvuZv7Qe/hD/uTC3U/f/TBmncNVJaitTg99IfJkhFixfqKvBiCISaAON3lATAzMxM1a5dW08++aT+7/+yH4lgjgCa08rly5dXyZIl1bx5cw0ePFgXXXSR6z6lgVxTMWO0Cpjn/L17t3Roq1S0hNRxlHTpH6JVw7vt3vSJNK2rlHFCqn+T1GWcVLKcd+vDKyPgsQDjd5QEQBP0XnrpJa1Zs0ZVqmRfA2NOKx87dkz169fXvn379OKLL2rt2rVatWqVKlWqlGtrmusJzR/fZBooISFBKSkpiovjq6s8fj/z8qEkkHpI+nSw9OV4KStTKp8gdfmXVLN5KK1ldK3L+o+lGXdmh8DKDaQ/TpSqXBJdBmwtAr8IEACjIAC+/fbbuvfeezVnzhxde23ed8EdPXpUderUcY4SPvbYY7m+SXK7ccTMSADkMwWBXwQO75OWj5K+/JeU9nP2Pzb6g/S7V7K/n5bJW4EdX0oz/iQd2SsVKZ59HWbrvlLpeG/Xi1dHoJAFCIARHgBnzJihXr16KSkpSb/73e/yba/rrrtOdevWde4gzm3iCGC+hMwQjQIm9G1eIM9GUqMAACAASURBVP3wjrTxf1JWRrZClUTpxiHSRe2iUSV0t/nwXun9ftL6udnraJ7F2PhP2UH9giv4NpbQ3XOsWQEKEAAjOACaI3933323zN/nemSMr59MuDNHAO+7776cG0Xy6zUaKD8hfh4xAubhzeaI3pEfpYNbpP1rpf3rpJ1fSj+tO30zL2gmXdUv+1qzIkUihiCiNsTsz7UfZp+m/3HVr5tWrrpUs4WU0Ew6r75UqY4Ud4FUtFhEbT4bgwDjd5gEwCNHjmjjxo1Ox5rHuLz66qvOI14qVqyomjVr6umnn9auXbs0efJkZx4T+nr06KHXXntNnTt3zun00qVLOzd9mOmJJ57Q73//e6f+xx9/dK4BXLhwob7//nvVqlXL1bsjaA1kLtje9Gku65CV+3qZD3N/pnPO7+9rhPv8Bs6rbchrpwV7fc6xzZnpknl0yMmjv/x9TEo7Ih3dL6Wn5rHCMdL5l0r1b5R+c5t0Xj1/upF5vRQwnwXmqO1306V1c6UTR3JZm5jsU8SxlaTSFaUSsVKxUtmPk/H9XcQExJhfjh6av4uc8t/m+51/+ZnzXc+++bzccF47rAQuai/VaV+gqxy08btA1zK4C4vJyvI3PQR3hXJb+oIFC5zAd+Z01113aeLEierZs6e2bt0qM5+Z2rVr54S5vOY3/96tWzeZB0z/9NNPzrP/WrRooYEDByoxMdH1BgatgRa8JC0Y4no9mBGBQhMoGSeVv0CqfHH2jQRVG0q1WkmxFQttFXihIAmcTJV2LJfMdYK7v5EObJQObcm+aYQJAS8F2j0ttftLga5B0MbvAl3L4C4sLAJgcAkCX3rQGsj8Rm6OAuY2Ob9B5/qDgpnfLCXYrxFqyw9om/Pqmzz2j2fb7Gyc+94wX89mjvAUL/PL36WzrxErU1kqWzX735iiR8B8p/DRn6TUg9KxA9Kxg1L68V/+pEnp5k+qZOZzjiVknfJ35un/Zu4Gz/l59BCypQUgUOe3Ut1rCmBBvy4iaON3ga5lcBdGALTwpYEs8ChFAAEEEEDAIwHG7zC5BtCj/sj3ZWmgfImYAQEEEEAAgZATYPwmAFo1JQ1kxUcxAggggAACnggwfhMArRqPBrLioxgBBBBAAAFPBBi/CYBWjUcDWfFRjAACCCCAgCcCjN8EQKvGo4Gs+ChGAAEEEEDAEwHGbwKgVePRQFZ8FCOAAAIIIOCJAOM3AdCq8WggKz6KEUAAAQQQ8ESA8ZsAaNV4NJAVH8UIIIAAAgh4IsD4TQC0ajwayIqPYgQQQAABBDwRYPwmAFo1Hg1kxUcxAggggAACnggwfhMArRqPBrLioxgBBBBAAAFPBBi/CYBWjUcDWfFRjAACCCCAgCcCjN8EQKvGo4Gs+ChGAAEEEEDAEwHGbwKgVePRQFZ8FCOAAAIIIOCJAOM3AdCq8WggKz6KEUAAAQQQ8ESA8ZsAaNV4NJAVH8UIIIAAAgh4IsD4TQC0ajwayIqPYgQQQAABBDwRYPwmAFo1Hg1kxUcxAggggAACnggwfhMArRqPBrLioxgBBBBAAAFPBBi/CYBWjUcDWfFRjAACCCCAgCcCjN8EQKvGo4Gs+ChGAAEEEEDAEwHGbwKgVePRQFZ8FCOAAAIIIOCJAOM3AdCq8WggKz6KEUAAAQQQ8ESA8ZsAaNV4NJAVH8UIIIAAAgh4IsD4TQC0ajwayIqPYgQQQAABBDwRYPwmAFo1Hg1kxUcxAggggAACnggwfhMArRqPBrLioxgBBBBAAAFPBBi/CYBWjUcDWfFRjAACCCCAgCcCjN8EQKvGo4Gs+ChGAAEEEEDAEwHGbwKgVePRQFZ8FCOAAAIIIOCJAOM3AdCq8WggKz6KEUAAAQQQ8ESA8ZsAaNV4NJAVH8UIIIAAAgh4IsD4TQC0ajwayIqPYgQQQAABBDwRYPwmAFo1Hg1kxUcxAggggAACnggwfhMArRqPBrLioxgBBBBAAAFPBBi/CYBWjUcDWfFRjAACCCCAgCcCjN8EQKvGo4Gs+ChGAAEEEEDAEwHGbwKgVePRQFZ8FCOAAAIIIOCJAON3mATARYsWadiwYVqxYoX27NmjWbNmqWPHjudsmoULF+qxxx7TqlWrVL16dT355JO6//77T6sZOXKks1yzzIYNG2rEiBFq06aN62akgVxTMSMCCCCAAAIhI8D4HSYBcO7cuVq8eLGaNGmiLl265BsAt2zZokaNGql3797q06ePU/vggw/q7bffdurNNGPGDHXv3l0mBLZu3VpjxozR+PHjtXr1atWsWdNVk9JArpiYCQEEEEAAgZASYPwOkwB4atfExMTkGwCfeuopvffee1qzZk1OqTn69+2332rp0qXOvzVv3twJlKNGjcqZp0GDBs6RxSFDhrhqVBrIFRMzIYAAAgggEFICjN8RGgDbtm2rxo0b67XXXstpOHPa+LbbbtOxY8eUlZWl2NhYJSUlqVOnTjnz9O3bVytXrpQ5fZzblJaWJvPHN5kGSkhIUEpKiuLi4kKquVkZBBBAAAEEEMhdgAAYoQGwfv366tmzp5555pmcPb9kyRLnVO/u3budAFijRg3n1HCrVq1y5hk8eLAmTZqkdevW5dox/fv314ABA876GQGQjxgEEEAAAQTCR4AAGMEBsFevXnr66adzutGEvauuusq54SMzM9MJgCYUtmzZMmeeQYMGacqUKVq7di1HAMPnfcyaIoAAAggg4JcAATBCA2CwTgGf2V00kF/vN2ZGAAEEEEAgJAQYvyM0AJqbQN5//33njl7f9MADDzjX9516E0jTpk2du4B9U2Jiojp06MBNICHx9mQlEEAAAQQQCI4AATBMAuCRI0e0ceNGpwvMzR2vvvqq2rdvr4oVKzqPbDGnenft2qXJkyc78/geA2MeAWMeBWNCn7kLOLfHwIwePdo5DTx27FiNGzfOeW5grVq1XHUcDeSKiZkQQAABBBAIKQHG7zAJgAsWLHAC35nTXXfdpYkTJzo3fGzdulVmPt9k7uR99NFHcx4EbY4K5vYg6KFDhzrXBZrnBg4fPlzm9LHbiQZyK8V8CCCAAAIIhI4A43eYBMDQaZnT14QGCtU9w3ohgAACCCCQtwDjNwHQ6v1BA1nxUYwAAggggIAnAozfBECrxqOBrPgoRgABBBBAwBMBxm8CoFXj0UBWfBQjgAACCCDgiQDjNwHQqvFoICs+ihFAAAEEEPBEgPGbAGjVeDSQFR/FCCCAAAIIeCLA+E0AtGo8GsiKj2IEEEAAAQQ8EWD8JgBaNR4NZMVHMQIIIIAAAp4IMH4TAK0ajway4qMYAQQQQAABTwQYvwmAVo1HA1nxUYwAAggggIAnAozfBECrxqOBrPgoRgABBBBAwBMBxm8CoFXj0UBWfBQjgAACCCDgiQDjNwHQqvFoICs+ihFAAAEEEPBEgPGbAGjVeDSQFR/FCCCAAAIIeCLA+E0AtGo8GsiKj2IEEEAAAQQ8EWD8JgBaNR4NZMVHMQIIIIAAAp4IMH4TAK0ajway4qMYAQQQQAABTwQYvwmAVo1HA1nxUYwAAggggIAnAozfBECrxqOBrPgoRgABBBBAwBMBxm8CoFXj0UBWfBQjgAACCCDgiQDjNwHQqvFoICs+ihFAAAEEEPBEgPGbAGjVeDSQFR/FCCCAAAIIeCLA+E0AtGo8GsiKj2IEEEAAAQQ8EWD8JgBaNR4NZMVHMQIIIIAAAp4IMH4TAK0ajway4qMYAQQQQAABTwQYvwmAVo1HA1nxUYwAAggggIAnAozfBECrxqOBrPgoRgABBBBAwBMBxm8CoFXj0UBWfBQjgAACCCDgiQDjNwHQqvFoICs+ihFAAAEEEPBEgPGbAGjVeDSQFR/FCCCAAAIIeCLA+E0AtGo8GsiKj2IEEEAAAQQ8EWD8JgBaNR4NZMVHMQIIIIAAAp4IMH4TAK0ajway4qMYAQQQQAABTwQYvwmAVo1HA1nxUYwAAggggIAnAozfBECrxqOBrPgoRgABBBBAwBMBxm8CoFXj0UBWfBQjgAACCCDgiQDjNwHQqvFoICs+ihFAAAEEEPBEgPGbAGjVeDSQFR/FCCCAAAIIeCLA+E0AtGo8GsiKj2IEEEAAAQQ8EWD8DqMAOHLkSA0bNkx79uxRw4YNNWLECLVp0ybXxmnXrp0WLlx41s9uvvlmffjhh86/9+zZU5MmTTptnubNm2vZsmWum5EGck3FjAgggAACCISMAON3mATAGTNmqHv37jIhsHXr1hozZozGjx+v1atXq2bNmmc11MGDB3XixImcfz9w4IAuu+wyp8YEP18A3LdvnyZMmJAzX4kSJVSxYkXXDUoDuaZiRgQQQAABBEJGgPE7TAKgOTLXpEkTjRo1Kqd5GjRooI4dO2rIkCH5NpQ5WvjCCy84Rw/LlCmTEwCTk5M1e/bsfOvzmoEGCpiOQgQQQAABBDwTYPwOgwBojuTFxsYqKSlJnTp1ymmWvn37auXKlbme6j2zoy699FK1bNlSY8eOzfmRORJowp856hcfH6+rr75agwYNUpUqVfJsyLS0NJk/vsk0UEJCglJSUhQXF+dZI/PCCCCAAAIIIOBegAAYBgFw9+7dqlGjhhYvXqxWrVrl7N3Bgwc71/CtW7funHv8iy++kDmCuHz5cjVr1ixnXnNauWzZsqpVq5a2bNmi559/Xunp6VqxYoVKliyZ6zL79++vAQMGnPUzAqD7Nx1zIoAAAggg4LUAATCMAuCSJUuco3i+yRytmzJlitauXXvOPurTp49M7ffff3/O+czpYRMGp0+frs6dO+c6L0cAvX7L8voIIIAAAgjYCxAAwyAA2pwCPnbsmKpVq6a//e1vMqeM85vq1aune++9V0899VR+szo/p4FcMTETAggggAACISXA+B0GAdB0jDmF27RpU+cuYN+UmJioDh06nPMmkIkTJ+r+++/Xrl27VKlSpXM2n7lT2JxqNtcJ9ujRw1Wj0kCumJgJAQQQQACBkBJg/A6TAOh7DMzo0aNzbuYYN26cVq1a5Zy2NYHNhLcz7wg2zwk0/25O6546HTlyROZ6vi5dujhHCLdu3apnnnlG27dv15o1a1SuXDlXjUoDuWJiJgQQQAABBEJKgPE7TAKg6Rpz9G/o0KHOo1waNWqk4cOHq23btk5DmQc/165dW+aIn29av369Lr74Yn388ce67rrrTmu81NRU5xEy33zzjcyjYEwIbN++vQYOHOjc1et2ooHcSjEfAggggAACoSPA+B1GATB02ubXNaGBQnGvsE4IIIAAAgicW4DxmwBo9R6hgaz4KEYAAQQQQMATAcZvAqBV49FAVnwUI4AAAggg4IkA4zcB0KrxaCArPooRQAABBBDwRIDxmwBo1Xg0kBUfxQgggAACCHgiwPhNALRqPBrIio9iBBBAAAEEPBFg/CYAWjUeDWTFRzECCCCAAAKeCDB+EwCtGo8GsuKjGAEEEEAAAU8EGL8JgFaNRwNZ8VGMAAIIIICAJwKM3wRAq8ajgaz4KEYAAQQQQMATAcZvAqBV49FAVnwUI4AAAggg4IkA4zcB0KrxaCArPooRQAABBBDwRIDxmwBo1Xg0kBUfxQgggAACCHgiwPhNALRqPBrIio9iBBBAAAEEPBFg/CYAWjUeDWTFRzECCCCAAAKeCDB+EwCtGo8GsuKjGAEEEEAAAU8EGL8JgFaNRwNZ8VGMAAIIIICAJwKM3wRAq8ajgaz4KEYAAQQQQMATAcZvAqBV49FAVnwUI4AAAggg4IkA4zcB0KrxaCArPooRQAABBBDwRIDxmwBo1Xg0kBUfxQgggAACCHgiwPhNALRqPBrIio9iBBBAAAEEPBFg/CYAWjUeDWTFRzECCCCAAAKeCDB+EwCtGo8GsuKjGAEEEEAAAU8EGL8JgFaNRwNZ8VGMAAIIIICAJwKM3wRAq8ajgaz4KEYAAQQQQMATAcZvAqBV49FAVnwUI4AAAggg4IkA4zcB0KrxaCArPooRQAABBBDwRIDxmwBo1Xg0kBUfxQgggAACCHgiwPhNALRqPBrIio9iBBBAAAEEPBFg/CYAWjUeDWTFRzECCCCAAAKeCDB+EwCtGo8GsuKjGAEEEEAAAU8EGL8JgFaNRwNZ8VGMAAIIIICAJwKM3wRAq8ajgaz4KEYAAQQQQMATAcZvAqBV49FAVnwUI4AAAggg4IkA4zcB0KrxaCArPooRQAABBBDwRIDxmwBo1Xg0kBUfxQgggAACCHgiwPhNALRqPBrIio9iBBBAAAEEPBFg/CYAWjUeDWTFRzECCCCAAAKeCDB+h1EAHDlypIYNG6Y9e/aoYcOGGjFihNq0aZNr40ycOFG9evU662epqakqVapUzr/7s8zcXogG8uR9y4sigAACCCBgJcD4HSYBcMaMGerevbtMYGvdurXGjBmj8ePHa/Xq1apZs+ZZTWACYN++fbVu3brTfnb++efn/L+/yyQAWr3XKEYAAQQQQCBkBAiAYRIAmzdvriZNmmjUqFE5zdOgQQN17NhRQ4YMyTUA9uvXT8nJyXk2m7/LJACGzPuWFUEAAQQQQMBKgAAYBgHwxIkTio2NVVJSkjp16pSzw80RvpUrV2rhwoW5BsB7771XNWrUUEZGhi6//HINHDhQjRs3duYNZJkEQKv3GsUIIIAAAgiEjAABMAwC4O7du50gt3jxYrVq1SqneQYPHqxJkyaddZrXzLBs2TJt3LhRl156qcxOfu211/TRRx/p22+/Vb169RTIMs1y09LSnD++ySw7ISFBKSkpiouLC5nGZkUQQAABBBBAIG8BAmAYBcAlS5aoZcuWOXtz0KBBmjJlitauXZtvj2dmZjqnkNu2bat//OMfOQHQ32X2799fAwYMOOv1CID57gJmQAABBBBAIGQECIBhEAAL6nRt7969tXPnTs2dOzfgU8AcAQyZ9y4rggACCCCAQMACBMAwCIBm75obNpo2bercBeybEhMT1aFDh1xvAjmzI7KystSsWTPnlPCbb77p/Nh2mWYZNFDA7z0KEUAAAQQQ8EyA8TtMAqDvkS2jR492TgOPHTtW48aN06pVq1SrVi316NHDuU7Qd0ewOU3bokUL53o/s5PNaV9zuthcR2iCoJnyW6abrqSB3CgxDwIIIIAAAqElwPgdJgHQtI05+jd06FDnQdCNGjXS8OHDnWv6zNSuXTvVrl1b5vl/Znr00Uc1c+ZM7d27V+XLl3fu/jXX7516DWF+y3TTqjSQGyXmQQABBBBAILQEGL/DKACGVutkrw0NFIp7hXVCAAEEEEDg3AKM3wRAq/cIDWTFRzECCCCAAAKeCDB+EwCtGo8GsuKjGAEEEEAAAU8EGL8JgFaNRwNZ8VGMAAIIIICAJwKM3wRAq8ajgaz4KEYAAQQQQMATAcZvAqBV49FAVnwUI4AAAggg4IkA4zcB0KrxaCArPooRQAABBBDwRIDxmwBo1Xg0kBUfxQgggAACCHgiwPhNALRqPBrIio9iBBBAAAEEPBFg/CYAWjUeDWTFRzECCCCAAAKeCDB+EwCtGo8GsuKjGAEEEEAAAU8EGL8JgFaNRwNZ8VGMAAIIIICAJwKM3wRAq8ajgaz4KEYAAQQQQMATAcZvAqBV49FAVnwUI4AAAggg4IkA4zcB0KrxaCArPooRQAABBBDwRIDxmwBo1Xg0kBUfxQgggAACCHgiwPhNALRqPBrIio9iBBBAAAEEPBFg/CYAWjUeDWTFRzECCCCAAAKeCDB+EwCtGo8GsuKjGAEEEEAAAU8EGL8JgFaNRwNZ8VGMAAIIIICAJwKM3wRAq8ajgaz4KEYAAQQQQMATAcZvAqBV49FAVnwUI4AAAggg4IkA4zcB0KrxaCArPooRQAABBBDwRIDxmwBo1Xg0kBUfxQgggAACCHgiwPhNALRqPBrIio9iBBBAAAEEPBFg/CYAWjUeDWTFRzECCCCAAAKeCDB+EwCtGo8GsuKjGAEEEEAAAU8EGL8JgFaNRwNZ8VGMAAIIIICAJwKM3wRAq8ajgaz4KEYAAQQQQMATAcZvAqBV49FAVnwUI4AAAggg4IkA4zcB0KrxaCArPooRQAABBBDwRIDxmwBo1Xg0kBUfxQgggAACCHgiwPhNALRqPBrIio9iBBBAAAEEPBFg/CYAWjUeDWTFRzECCCCAAAKeCDB+EwCtGo8GsuKjGAEEEEAAAU8EGL8JgFaNRwNZ8VGMAAIIIICAJwKM3wRAq8ajgaz4KEYAAQQQQMATAcZvAqBV49FAVnwUI4AAAggg4IkA4zcB0KrxaCArPooRQAABBBDwRIDxmwBo1Xg0kBUfxQgggAACCHgiwPgdRgFw5MiRGjZsmPbs2aOGDRtqxIgRatOmTa6NM27cOE2ePFk//PCD8/OmTZtq8ODBatasWc78PXv21KRJk06rb968uZYtW+a6GWkg11TMiAACCCCAQMgIMH6HSQCcMWOGunfvLhMCW7durTFjxmj8+PFavXq1ataseVZD3Xnnnc58rVq1UqlSpTR06FDNnDlTq1atUo0aNZz5TQDct2+fJkyYkFNfokQJVaxY0XWD0kCuqZgRAQQQQACBkBFg/A6TAGiOzDVp0kSjRo3KaZ4GDRqoY8eOGjJkSL4NlZGRoQoVKuiNN95Qjx49cgJgcnKyZs+enW99XjPQQAHTUYgAAggggIBnAozfYRAAT5w4odjYWCUlJalTp045zdK3b1+tXLlSCxcuzLeBDh8+rCpVqjjLuOWWW3ICoAl/5qhffHy8rr76ag0aNMiZL68pLS1N5o9vMg2UkJCglJQUxcXF5bsezIAAAggggAAC3gsQAMMgAO7evds5bbt48WLnlK5vMtf0mWv41q1bl28nPfTQQ5o3b55zTaA5JWwmc1q5bNmyqlWrlrZs2aLnn39e6enpWrFihUqWLJnrMvv3768BAwac9TMCYL67gBkQQAABBBAIGQECYBgFwCVLlqhly5Y5zWOO1k2ZMkVr1649Z0OZ6/9eeuklLViwQL/5zW/ynNfcXGLC4PTp09W5c+dc5+MIYMi8d1kRBBBAAAEEAhYgAIZBALQ5BfzKK6/oxRdf1Pz583XFFVfk2yj16tXTvffeq6eeeirfec0MNJArJmZCAAEEEEAgpAQYv8MgAJqOMTeBmEe5mLuAfVNiYqI6dOiQ500g5pExJvyZU78tWrTIt/EOHDjgnGoeO3Zszo0i+RXRQPkJ8XMEEEAAAQRCT4DxO0wCoO8xMKNHj3ZOA5uQZp71Zx7rYk7bmjt7TXjz3RFsTvuaa/qmTZvmPA7GN5lr/syfI0eOyFzP16VLF1WrVk1bt27VM888o+3bt2vNmjUqV66cq26lgVwxMRMCCCCAAAIhJcD4HSYB0HSNOfpngp25Vq9Ro0YaPny42rZt6zRUu3btVLt2bU2cONH5f/Pf27ZtO6vZ/vrXvzrBLzU11XmEzDfffCPzKBgTAtu3b6+BAwc6d/W6nWggt1LMhwACCCCAQOgIMH6HUQAMnbb5dU1ooFDcK6wTAggggAAC5xZg/CYAWr1HaCArPooRQAABBBDwRIDxmwBo1Xg0kBUfxQgggAACCHgiwPhNALRqPBrIio9iBBBAAAEEPBFg/CYAWjUeDWTFRzECCCCAAAKeCDB+EwCtGo8GsuKjGAEEEEAAAU8EGL8JgFaNRwNZ8VGMAAIIIICAJwKM3wRAq8ajgaz4KEYAAQQQQMATAcZvAqBV49FAVnwUI4AAAggg4IkA4zcB0KrxaCArPooRiEqBQ0dPaPGmn7TrUKqKxMToospl1LrueSpVvGhUerDRCHghwPhNALTqOxrIio9iBKJKYP2+w3pt/gbN/WGPMrNO3/RypYqpZ6vaeqh9XYJgVHUFG+uVAOM3AdCq92ggKz6KEYgKgZMZmRq1YJP+8b8NSv8l+V1yfjk1qBYn87NvtidrV3KqY3HheWU0/q4rVKdy2aiwYSMR8EqA8ZsAaNV7NJAVH8UIRLzAz8dP6r7JX2nZ5oPOtl7boKqeuKG+Ljk/LmfbMzOz9J9Ve/W391dr78/HFVeqmCbf01yXJ8RHvA8biIBXAozfBECr3qOBrPgoRiCiBfYfTlOPN7/Qmj0/q2zJYhrUqZFuvay6YmJict3un46kqffkr5wjgvGxxZXUp6XqVS0X0UZsHAJeCTB+EwCteo8GsuKjGIGIFUhJPaluY5c54e+8siU1sdeValSjfL7beyQtXXeOX65vdySrZsVYvf/wVSpfuni+dcyAAAL+CTB+EwD965gz5qaBrPgoRiAiBVJPZKjHm8v15dZDTvhLur+lc22f28ncJXzL65871wVen1hVY7o3zfOoodtlMh8CCJwuwPhNALR6T9BAVnwUIxBxAllZWXpw6tea+8NemTt7Z9zXUonVf73ez+0GmyOAfxy9VCcyMjXg1oa6q1Vtt6XMhwACLgQYvwmALtok71loICs+ihGIOIF/frpRw+atU/GiMZp6bws1u7BiwNs4cfEW9X9/tWJLFNW8fm2VUDE24GVRiAACHAE8swdissyvrEwBCRAAA2KjCIGIFFiw7kf1mvilzCfqkM6X6vZmNa2209wd3G3cMn2x5aCuqnueptzTjFPBVqIUI/CrAOM3RwCt3g80kBUfxQhEjMCOg8ec6/bMzR8m+JkAWBDTlp+O6sYRi5SWnqmhXX6j265MKIjFsgwEol6A8ZsAaPUmoIGs+ChGICIE0jMy1XXsMq3Ydsh5dt+MPi1UsljBfa3bmIWbNGTuWudu4P89frVzYwkTAgjYCTB+EwCtOogGsuKjGIGIEDBf7zZ8/nqVK1lMH/VtU+DX6pmAeesbi7V6z8/q3KSGXr3t8ohwYyMQ8FKA8ZsAaNV/NJAVH8UIhL3A19sPOXfrZmRm6bVul6vD5TWCsk0rdySr08jFzvWF03o3V6s65wXldVgoAtEiwPhNALTqdRrIio9i9I6W0gAAGO5JREFUBMJawDy0+ebXPtP2g8fU8fLqGtGtcVC35/nZP2jKsm266LwymtuvTYGeZg7qirNwBEJQgPGbAGjVljSQFR/FCIS1wJ+TvlXSip2qEV/aCWRxpYL7jR3mBpNrX10o8xVzj15bX32vrRfWfqw8Al4KMH4TAK36jway4qMYgbAV+GTtPt098SuZr/U1D3u2ed6fPwjvfbtbj7z9jUoUK+I8G9Cfbxjx53WYF4FIF2D8JgBa9TgNZMVHMQJhKZBy7KSuH7FQ+35OU+82F+rZ3yUW2naYx7b2ePMLfbbhJ7WuW0lv3dOcZwMWmj4vFEkCjN8EQKt+poGs+ChGICwFnkj6Vu+s2Olci2fu+i1VvOAe+eIGZNuBo7p+ePazAUd0vVwdGwfnxhM368I8CISrAOM3AdCqd2kgKz6KEQg7gVNP/b5zf0s1rRX4V73ZbPwbn2zQKx+v13llS+h/j7VT+djgXn9os67UIhCKAozfBECrvqSBrPgoRiCsBMxNGNcP9+bU75lQJ9IzdfM/PtPGH48U6DePhNUOYWURsBBg/CYAWrSPRANZ8VGMQFgJeH3q90ysZZsPqNvYZc4/T7+vhVpcVCmsPFlZBLwUYPwmAFr1Hw1kxUcxAmEj8OnaH9Vr4pfOXb9JfVrqitrenPo9E+ypd77TjK92qGpcSX30SBtV4mviwqanWFFvBRi/CYBWHUgDWfFRjEBYCJhTvzcMX6S9Px/XvVddqOduKby7fvMDOpqWrlvf+Fyb9h9Vu4sr6193XamiRWLyK+PnCES9AOM3AdDqTUADWfFRjEBYCDz275Wa+fUu55l75ihb6RKFe9dvfkhr9/6sDm8sdu4KLuzH0uS3bvwcgVAVYPwmAFr1Jg1kxUcxAiEv8PGqvbpvygqZg2pJHt71mx/UnJW71Hf6Sme2gR0bqXuLWvmV8HMEolqA8ZsAaPUGoIGs+ChGIKQFDh494dz1+9ORE+pz9UV6+qYGIb2+r/9vg/7+3/XOOg7pfKlzdzATAgjkLsD4TQC0em/QQFZ8FCMQ0gIPTf1aH36/R/WqlNX7D19V6A989hfHfEvIgPdXa+KSrU7pMzdfot5tLuKbQvyFZP6oEGD8JgBaNToNZMVHMQIhK+D7zl1zQ8XsB1vr0gvKh+y6nrpiJgQO/miNxn22xfnnzo1r6MVOjRRbolhYrD8riUBhCTB+EwCteo0GsuKjGIGQFNhx8JjzkOXDx9P1yDX19Nh19UNyPfNaKRMCJy3ZqoEfrlFGZpZqVox1Tgm3rnteWG0HK4tAMAUYv8MsAI4cOVLDhg3Tnj171LBhQ40YMUJt2rTJs0feffddPf/889q0aZPq1KmjQYMGqVOnTjnzO6dMBgzQ2LFjdejQITVv3lz//Oc/nWW7mWggN0rMg0D4CJzMyFTXMUv19fZkNa4Zr3/3aaniRYuEzwacsqZLNx2QeXj1ruRU51/NY2JMoG2cEM9p4bDco6x0QQowfodRAJwxY4a6d+8uEwJbt26tMWPGaPz48Vq9erVq1jz7YuelS5c64XDgwIFO6Js1a5ZeeOEFff75507QM9PLL7/shMKJEyeqfv36evHFF7Vo0SKtW7dO5cqVy7fXaKB8iZgBgbASGDZvrf756SaVK1lMH/Vto4SKsWG1/meu7JG0dA37z1q9tXy7czTQTJecX05/vCJB11xSRbXPKxPW28fKIxCoAON3GAVAE9qaNGmiUaNG5ezvBg0aqGPHjhoyZMhZPdC1a1fnq9rmzp2b87Mbb7xRFSpU0Ntvvy1z9K969erq16+fnnrqKWeetLQ0Va1a1QmGffr0ybevgtVAZt1S07N/a2dCAIHCEVi++YDunvSVsrKkV2+7TDc2Or9wXrgQXmX7gWMatXCT5v6wV+Z7hH1TQoXSalSjvBKrx6lO5TKqHl9a1eJLOwGYCYFQEShdrHSBH7UO1vgdKmZu1iMmy6SNEJ9OnDih2NhYJSUlnXYKt2/fvlq5cqUWLlx41haYo4KPPvqo88c3DR8+3DltvG3bNm3evNk5Lfz111+rcePGOfN06NBB8fHxmjRp0lnLNAHR/PFNpoESEhKUkpKiuLi4AlP875rteuyL3xXY8lgQAggggAAC4SrwarMPdV2Dgn2sEQEwTI4A7t69WzVq1NDixYvVqlWrnB4ePHiwE9TMKdszpxIlSjindu+4446cH02bNk29evVyQtySJUucU8m7du1yjgT6pvvuu88JiPPmzTtrmf3793euGTxzKugAOOzj7zR5z53h+l5lvRFAAAEEECgwgR7VpurP1/+mwJZnFkQADLMAaEJby5Ytc5rAXL83ZcoUrV27NtcAaMLh7bffnvOzqVOn6p577tHx48dzAqAJl9WqVcuZp3fv3tqxY4f+85//nLXMwjoC+Nn6/Vq4cddZr5+lPA7WnuMYbl4/yuvAb17Hg891mDjvmtyrAjnmnOf65vGR4O86mcX4u+15b0feWnm+Rp671n/DvPd5Xp+fefdVWrq5HCFDqSfSlXoy85e/M/TzsZM6nJZxzg/kulXKqGmtCs7NBy3rnKcSIXozxfD56zVu0RaVKFZE/+7TQvWr5n/9b4GORCG2sGMnMnTo6AmlHD+pw+ZP6kmlpmfqZHqWTmRkKiMjUyczsv87PcN8KmX3j+ntnE7K+vXTKvvff5nnlG0N5HMgxKhYnUISuLpuDbWpX7lAX40AGCYBMFROAZ/ZfTRQgb4fWViYCRw/maH9h9O0/0iazDVmm/cf0ab9R7Vmz8/a/NPR07ambMliuqZBFXW7sqZaXFSxwK/nCZTu31/u0JPvfueUm+v+Oje5INBFUYcAAmEkwPgdJgHQ9JS5CaRp06bOXcC+KTExUeaavbxuAjl8+LA++uijnPlvuukm5/q+U28CMdcIPvnkk848JmhWqVLF85tAwug9xKoikKvAgSNp+nLrIS3Z9JM+XrVPe38+njOfudmgZ+sLddsVF6hksaKeCS7e+JPuevMLpWdm6ZHf1tVj11/s2brwwgggULgCBMAwCoC+x8CMHj3aOQ1snt03btw4rVq1SrVq1VKPHj2c6wR9YdCcLm7btq3zmBcTEufMmaPnnnvurMfAmPknTJigevXqyVxTuGDBAh4DU7jvQ14twgUyM7O0cmeykr7aqTkrd8mcYjTT+XGldP/VF+n25jULPQhu/PGwOo1c4jzs+dbLquu1bpeHzFHJCG8HNg+BkBAgAIZRADQdY47+DR061HkQdKNGjWTu6jUhz0zt2rVT7dq1nRs/fNM777zjhD7fHb8mDHbu3Dnn574HQZtnCp76IGizbDcTDeRGiXkQ+FXAXFP2zoqdGrNwc85RwVqVYvXMzQ10fWLVQglhu5NT1XXsUu04mOpcozj13uYh/z2/9BACCBSsAON3mAXAgt399kujgewNWUJ0CqSlZ+jfX+3U6//boB8PZz9aqXXdSnr+lkRdcn7BPVLpTF3zNW93jl+u7QePyQTPmQ+0UqWyJaNzJ7DVCESxAOM3AdCq/WkgKz6KEdDRtHSNXLBR4z7b4jyguEiM9KcWtZzv342PLVGgQubmFHPNnwmc5vtx376vhWrEly7Q12BhCCAQHgKM3wRAq06lgaz4KEYgR8AcmRsyd40++n6v828VYovr8esv1u3NaqqoSYWW08er9urxf3+rw2npurhqOU26u5nOL1/KcqmUI4BAuAowfhMArXqXBrLioxiBswTMXcMD3lutdfsOOz9rUC1OA25tqGYXVgxIyzyq5u8fr3OOMJqp+YUVNbbHFSpfunhAy6MIAQQiQ4DxmwBo1ck0kBUfxQjkKpCekampy7c7we3n4+nOPOYGkQfb19XlCfGu1MwNXos2mDC5KueZhHe3vlBP33yJiofoA6ldbRgzIYBAgQgwfhMArRqJBrLioxiBcwqYZwn+/b/r9fYX23O+peXK2hWchzWbh0pXKXf2KdyUYyc1b/Vep+ab7cnO8quUK6kXOzbS9Q3PRxwBBBBwBBi/CYBWbwUayIqPYgRcCZhn9o1asNl5hqB5aLNvMjdymD9xpYsp9USGth08pi0/Hc0JiyWLFXFuKHnkmnqc8nUlzUwIRI8A4zcB0KrbaSArPooR8EtgT0qq5qzcrfe/3a3Ve37O87ubzU0et/ymmrpemaAqcdzo4RcyMyMQJQKM3wRAq1angaz4KEYgYIGU1JP6YVeK9qYcl3m4dGzJYs6p3kY1yus8nusXsCuFCESLAOM3AdCq12kgKz6KEUAAAQQQ8ESA8ZsAaNV4NJAVH8UIIIAAAgh4IsD4TQC0ajwayIqPYgQQQAABBDwRYPwmAFo1Hg1kxUcxAggggAACnggwfhMArRqPBrLioxgBBBBAAAFPBBi/CYBWjUcDWfFRjAACCCCAgCcCjN8EQKvGo4Gs+ChGAAEEEEDAEwHGbwKgVePRQFZ8FCOAAAIIIOCJAOM3AdCq8WggKz6KEUAAAQQQ8ESA8ZsAaNV4NJAVH8UIIIAAAgh4IsD4TQC0ajwayIqPYgQQQAABBDwRYPwmAFo1Hg1kxUcxAggggAACnggwfhMArRqPBrLioxgBBBBAAAFPBBi/CYBWjUcDWfFRjAACCCCAgCcCjN8EQKvGS0lJUXx8vHbs2KG4uDirZVGMAAIIIIAAAoUjYAJgQkKCkpOTVb58+cJ50RB7lZisrKysEFunsFmdnTt3Og3EhAACCCCAAALhJ2AO4FxwwQXht+IFsMYEQAvEzMxM7d69W+XKlVNMTIzFkk4v9f1mwpHFAiPNdUE4B9f31KVjXTjWOONcOAKF9yrB6mlz7Ovw4cOqXr26ihQpUngbFEKvRAAMoZ3hWxWuTSicnYJz4TibV8G6cKxxxrlwBArvVejp4FkTAINnG/CSafiA6fwqxNkvLquZsbbic12Ms2sqqxlxtuLzqxhrv7j8mpkA6BdX4cxMw+NcOAKF9yr0dOFY44xz4QgU3qvQ08GzJgAGzzbgJaelpWnIkCF6+umnVbJkyYCXQ+G5BXAuvA7BunCscca5cAQK71Xo6eBZEwCDZ8uSEUAAAQQQQACBkBQgAIbkbmGlEEAAAQQQQACB4AkQAINny5IRQAABBBBAAIGQFCAAhuRuYaUQQAABBBBAAIHgCRAAg2fLkhFAAAEEEEAAgZAUIACG2G4ZOXKkhg0bpj179qhhw4YaMWKE2rRpE2JrGT6rY+6mnjlzptauXavSpUurVatWevnll3XxxRfnbIS5y+yJJ57Q22+/rdTUVF1zzTUy+yFavx6oIPaucX/mmWfUt29fp4fNhHNByGYvY9euXXrqqac0d+5cp2fr16+vf/3rX2ratKnzc/MtBwMGDNDYsWN16NAhNW/eXP/85z+dzxQm9wLp6enq37+/pk6dqr1796patWrq2bOnnnvuuZxvj8DavadvzkWLFjnj3IoVK5yxbtasWerYsWPOgtyYmr5+5JFH9N577zl1t956q15//XXFx8f7v0JRWkEADKEdP2PGDHXv3t0JH61bt9aYMWM0fvx4rV69WjVr1gyhNQ2fVbnxxhvVrVs3XXnllTIf5s8++6y+//57x7RMmTLOhjzwwAN6//33NXHiRFWqVEmPP/64Dh486Hw4FS1aNHw2NkTW9Msvv9Rtt92muLg4tW/fPicA4lwwO8gMfI0bN3ZsjWmVKlW0adMm1a5dW3Xq1HFexPySM2jQIKenTTh88cUXZQbddevWOV9dyeROwBgOHz5ckyZNcsLzV199pV69ejme5pcbrN05njmX+cVl8eLFatKkibp06XJWAHTTvzfddJN27tzp/JJjpvvuu895D5jPciZ3AgRAd06FMpf5Ld28IUaNGpXzeg0aNHB+MzJHVJjsBfbv3+8MmAsXLlTbtm2VkpKiypUra8qUKeratavzAub7nRMSEvTRRx/phhtusH/RKFrCkSNHnB42v8SYQfLyyy93AiDOBdcEf/nLX5zB87PPPst1oeboifl+0379+jlHCc1kjr5WrVrVCYZ9+vQpuJWJ8CXdcsstjps5uuqbTGCJjY11PjOwtm+AmJiY0wKgG9M1a9YoMTFRy5Ytc45um8n8d8uWLZ2zPaee4bFfw8hdAgEwRPbtiRMnnA+VpKQkderUKWetzG+ZK1eudAILk73Axo0bVa9ePecoYKNGjfTJJ584p3zNEb8KFSrkvMBll13mBG9zGo3JvcBdd92lihUrOkdN2rVrlxMAcXZvmN+cZuAzv5iYox/mc6FGjRp68MEH1bt3b6d08+bNzpHAr7/+2jlS6Js6dOjgnB4zR7OY3Am89NJLGj16tD7++GPnSOq3336r66+/3vml5vbbb8faHeM55zozALrp3zfffFOPPfaYkpOTT1u26W/z2WOO0jLlL0AAzN+oUOYwR53MB7n5zd5cp+abBg8e7Hxgm1M3THYC5jdLMwiaU2i+oyfTpk1zPizMEZJTJ/Mhf+GFFzqn4ZncCUyfPt056mdOk5UqVeq0AIizO0M3cxlbM5kB8I9//KO++OIL52if6dUePXpoyZIlziUk5jpBcyTQN5lTZNu2bdO8efPcvAzz/HItpbmW1Rw5NZeDZGRkOKfWzbc0mQlr+zY5MwC6MTXjorm8Yf369aetgAnp5vPct3/s1y6yl0AADJH96wuApvnNYWzfZD5szKkGc1ibyU7goYce0ocffqjPP/885waPvILJdddd5xxFMb/9M+UvsGPHDl1xxRXOkRJz9NRMpx4BxDl/Q7dzlChRwrE2nxW+yVwMb669XLp0aU4oMZ8p5qYF32SOEJr99J///MftS0X9fOaXmj//+c/ODQvmGkBzNsaE7VdffVXmaLcvrGAdeKvkFQDPZZrXgRFzdueee+6RuUyCKX8BAmD+RoUyB6eAg8v88MMPa/bs2c6F8ObInm/i1GTBuBtbc+nCqTfNmKMl5sO9SJEizlGna6+9llPtBcBdq1YtmV9QzA1ivslcN2yOvpqjfm5OoRXAakTFIsy1wCZMmF8efZNxfuutt5xfyrG2bwNOAdsbBroEAmCgckGoMxezmsc4mAvofZO53sectuQmkMDAzWlfE/7MYwYWLFjgXP936uS7OcF8oJs7V81kHktgHgHDTSDuzQ8fPuycXjx1MqdiLrnkEudGBDOQmpttcHZvmtecd9xxh3Mk79SbQB599FEtX77cOSLlu4je/NuTTz7pLMb8gmlufuImEP/8zVMBTOAzd1v7JvNZPGHCBOf0I9b+eeY2d143gZyrf303gZieb9asmbNY898tWrTgJhA/dgkB0A+sYM/qewyMOe1oTgOb29vHjRunVatWyfzWz+S/gLk43px+nDNnzml3hpUvX955LqCZzIf7Bx984FxTYm5gMM8EPHDgAI+B8Z/7tIpTTwHjbIl5Srk51WuuEzY3KJlfWsw1gOb0rvm8uPPOO505TdDzBRXzS485ZWZ+AeIxMP7tB/PMv/nz5zvXV5pTwN98843zuJG7777bMcbaP0/f3OZpAeaGPDOZG5XMKXXzWCPz+Wseeeamf81jYMxpYt912ma/mHGSx8C43ycEQPdWhTKnOfo3dOhQ5yiUuUvV3NFkHlfCFJiA+e0yt8n8Bm8+3M10/Phx5zofExRPfRC0OWrFFLjAmQEQ58Atz6w0v7CYC903bNjgXNJgbgjx3QVs5vU9SNcMjqc+CNp8pjC5FzBHtp9//nnnDMKPP/7o3FRj7v594YUXZK7FxNq95alzml9GTOA7czLXVZpfxN30r3lyw5kPgn7jjTd4ELQfu4QA6AcWsyKAAAIIIIAAApEgQACMhL3INiCAAAIIIIAAAn4IEAD9wGJWBBBAAAEEEEAgEgQIgJGwF9kGBBBAAAEEEEDADwECoB9YzIoAAggggAACCESCAAEwEvYi24AAAggggAACCPghQAD0A4tZEUAAAQQQQACBSBAgAEbCXmQbEEAAAQQQQAABPwQIgH5gMSsCCCCAAAIIIBAJAgTASNiLbAMCCCCAAAIIIOCHAAHQDyxmRQABBBBAAAEEIkGAABgJe5FtQAABBBBAAAEE/BAgAPqBxawIIIAAAggggEAkCBAAI2Evsg0IIIAAAggggIAfAgRAP7CYFQEEEEAAAQQQiAQBAmAk7EW2AQEEEEAAAQQQ8EOAAOgHFrMigAACCCCAAAKRIEAAjIS9yDYggAACCCCAAAJ+CBAA/cBiVgQQQAABBBBAIBIECICRsBfZBgQQQAABBBBAwA8BAqAfWMyKAAIIIIAAAghEggABMBL2ItuAAAIIIIAAAgj4IUAA9AOLWRFAAAEEEEAAgUgQIABGwl5kGxBAAAEEEEAAAT8ECIB+YDErAggggAACCCAQCQIEwEjYi2wDAggggAACCCDghwAB0A8sZkUAAQQQQAABBCJBgAAYCXuRbUAAAQQQQAABBPwQIAD6gcWsCCCAAAIIIIBAJAgQACNhL7INCCCAAAIIIICAHwIEQD+wmBUBBBBAAAEEEIgEAQJgJOxFtgEBBBBAAAEEEPBD4P8BI1o9T9paaiEAAAAASUVORK5CYII=\" width=\"640\">"
|
|
],
|
|
"text/plain": [
|
|
"<IPython.core.display.HTML object>"
|
|
]
|
|
},
|
|
"metadata": {},
|
|
"output_type": "display_data"
|
|
},
|
|
{
|
|
"data": {
|
|
"text/plain": [
|
|
"<matplotlib.legend.Legend at 0x41611619e8>"
|
|
]
|
|
},
|
|
"execution_count": 11,
|
|
"metadata": {},
|
|
"output_type": "execute_result"
|
|
}
|
|
],
|
|
"source": [
|
|
"F = WAF_1D_flux_helper(h, hu, g, dx, dt)\n",
|
|
"plt.figure()\n",
|
|
"for i in range(F.shape[1]):\n",
|
|
" plt.plot(x[2:-1], F[:,i], marker=' ', label='F_' + str(i))\n",
|
|
"#plt.ylim(-10, 10)\n",
|
|
"plt.legend()"
|
|
]
|
|
},
|
|
{
|
|
"cell_type": "code",
|
|
"execution_count": 12,
|
|
"metadata": {},
|
|
"outputs": [
|
|
{
|
|
"data": {
|
|
"application/javascript": [
|
|
"/* Put everything inside the global mpl namespace */\n",
|
|
"window.mpl = {};\n",
|
|
"\n",
|
|
"\n",
|
|
"mpl.get_websocket_type = function() {\n",
|
|
" if (typeof(WebSocket) !== 'undefined') {\n",
|
|
" return WebSocket;\n",
|
|
" } else if (typeof(MozWebSocket) !== 'undefined') {\n",
|
|
" return MozWebSocket;\n",
|
|
" } else {\n",
|
|
" alert('Your browser does not have WebSocket support.' +\n",
|
|
" 'Please try Chrome, Safari or Firefox ≥ 6. ' +\n",
|
|
" 'Firefox 4 and 5 are also supported but you ' +\n",
|
|
" 'have to enable WebSockets in about:config.');\n",
|
|
" };\n",
|
|
"}\n",
|
|
"\n",
|
|
"mpl.figure = function(figure_id, websocket, ondownload, parent_element) {\n",
|
|
" this.id = figure_id;\n",
|
|
"\n",
|
|
" this.ws = websocket;\n",
|
|
"\n",
|
|
" this.supports_binary = (this.ws.binaryType != undefined);\n",
|
|
"\n",
|
|
" if (!this.supports_binary) {\n",
|
|
" var warnings = document.getElementById(\"mpl-warnings\");\n",
|
|
" if (warnings) {\n",
|
|
" warnings.style.display = 'block';\n",
|
|
" warnings.textContent = (\n",
|
|
" \"This browser does not support binary websocket messages. \" +\n",
|
|
" \"Performance may be slow.\");\n",
|
|
" }\n",
|
|
" }\n",
|
|
"\n",
|
|
" this.imageObj = new Image();\n",
|
|
"\n",
|
|
" this.context = undefined;\n",
|
|
" this.message = undefined;\n",
|
|
" this.canvas = undefined;\n",
|
|
" this.rubberband_canvas = undefined;\n",
|
|
" this.rubberband_context = undefined;\n",
|
|
" this.format_dropdown = undefined;\n",
|
|
"\n",
|
|
" this.image_mode = 'full';\n",
|
|
"\n",
|
|
" this.root = $('<div/>');\n",
|
|
" this._root_extra_style(this.root)\n",
|
|
" this.root.attr('style', 'display: inline-block');\n",
|
|
"\n",
|
|
" $(parent_element).append(this.root);\n",
|
|
"\n",
|
|
" this._init_header(this);\n",
|
|
" this._init_canvas(this);\n",
|
|
" this._init_toolbar(this);\n",
|
|
"\n",
|
|
" var fig = this;\n",
|
|
"\n",
|
|
" this.waiting = false;\n",
|
|
"\n",
|
|
" this.ws.onopen = function () {\n",
|
|
" fig.send_message(\"supports_binary\", {value: fig.supports_binary});\n",
|
|
" fig.send_message(\"send_image_mode\", {});\n",
|
|
" if (mpl.ratio != 1) {\n",
|
|
" fig.send_message(\"set_dpi_ratio\", {'dpi_ratio': mpl.ratio});\n",
|
|
" }\n",
|
|
" fig.send_message(\"refresh\", {});\n",
|
|
" }\n",
|
|
"\n",
|
|
" this.imageObj.onload = function() {\n",
|
|
" if (fig.image_mode == 'full') {\n",
|
|
" // Full images could contain transparency (where diff images\n",
|
|
" // almost always do), so we need to clear the canvas so that\n",
|
|
" // there is no ghosting.\n",
|
|
" fig.context.clearRect(0, 0, fig.canvas.width, fig.canvas.height);\n",
|
|
" }\n",
|
|
" fig.context.drawImage(fig.imageObj, 0, 0);\n",
|
|
" };\n",
|
|
"\n",
|
|
" this.imageObj.onunload = function() {\n",
|
|
" fig.ws.close();\n",
|
|
" }\n",
|
|
"\n",
|
|
" this.ws.onmessage = this._make_on_message_function(this);\n",
|
|
"\n",
|
|
" this.ondownload = ondownload;\n",
|
|
"}\n",
|
|
"\n",
|
|
"mpl.figure.prototype._init_header = function() {\n",
|
|
" var titlebar = $(\n",
|
|
" '<div class=\"ui-dialog-titlebar ui-widget-header ui-corner-all ' +\n",
|
|
" 'ui-helper-clearfix\"/>');\n",
|
|
" var titletext = $(\n",
|
|
" '<div class=\"ui-dialog-title\" style=\"width: 100%; ' +\n",
|
|
" 'text-align: center; padding: 3px;\"/>');\n",
|
|
" titlebar.append(titletext)\n",
|
|
" this.root.append(titlebar);\n",
|
|
" this.header = titletext[0];\n",
|
|
"}\n",
|
|
"\n",
|
|
"\n",
|
|
"\n",
|
|
"mpl.figure.prototype._canvas_extra_style = function(canvas_div) {\n",
|
|
"\n",
|
|
"}\n",
|
|
"\n",
|
|
"\n",
|
|
"mpl.figure.prototype._root_extra_style = function(canvas_div) {\n",
|
|
"\n",
|
|
"}\n",
|
|
"\n",
|
|
"mpl.figure.prototype._init_canvas = function() {\n",
|
|
" var fig = this;\n",
|
|
"\n",
|
|
" var canvas_div = $('<div/>');\n",
|
|
"\n",
|
|
" canvas_div.attr('style', 'position: relative; clear: both; outline: 0');\n",
|
|
"\n",
|
|
" function canvas_keyboard_event(event) {\n",
|
|
" return fig.key_event(event, event['data']);\n",
|
|
" }\n",
|
|
"\n",
|
|
" canvas_div.keydown('key_press', canvas_keyboard_event);\n",
|
|
" canvas_div.keyup('key_release', canvas_keyboard_event);\n",
|
|
" this.canvas_div = canvas_div\n",
|
|
" this._canvas_extra_style(canvas_div)\n",
|
|
" this.root.append(canvas_div);\n",
|
|
"\n",
|
|
" var canvas = $('<canvas/>');\n",
|
|
" canvas.addClass('mpl-canvas');\n",
|
|
" canvas.attr('style', \"left: 0; top: 0; z-index: 0; outline: 0\")\n",
|
|
"\n",
|
|
" this.canvas = canvas[0];\n",
|
|
" this.context = canvas[0].getContext(\"2d\");\n",
|
|
"\n",
|
|
" var backingStore = this.context.backingStorePixelRatio ||\n",
|
|
"\tthis.context.webkitBackingStorePixelRatio ||\n",
|
|
"\tthis.context.mozBackingStorePixelRatio ||\n",
|
|
"\tthis.context.msBackingStorePixelRatio ||\n",
|
|
"\tthis.context.oBackingStorePixelRatio ||\n",
|
|
"\tthis.context.backingStorePixelRatio || 1;\n",
|
|
"\n",
|
|
" mpl.ratio = (window.devicePixelRatio || 1) / backingStore;\n",
|
|
"\n",
|
|
" var rubberband = $('<canvas/>');\n",
|
|
" rubberband.attr('style', \"position: absolute; left: 0; top: 0; z-index: 1;\")\n",
|
|
"\n",
|
|
" var pass_mouse_events = true;\n",
|
|
"\n",
|
|
" canvas_div.resizable({\n",
|
|
" start: function(event, ui) {\n",
|
|
" pass_mouse_events = false;\n",
|
|
" },\n",
|
|
" resize: function(event, ui) {\n",
|
|
" fig.request_resize(ui.size.width, ui.size.height);\n",
|
|
" },\n",
|
|
" stop: function(event, ui) {\n",
|
|
" pass_mouse_events = true;\n",
|
|
" fig.request_resize(ui.size.width, ui.size.height);\n",
|
|
" },\n",
|
|
" });\n",
|
|
"\n",
|
|
" function mouse_event_fn(event) {\n",
|
|
" if (pass_mouse_events)\n",
|
|
" return fig.mouse_event(event, event['data']);\n",
|
|
" }\n",
|
|
"\n",
|
|
" rubberband.mousedown('button_press', mouse_event_fn);\n",
|
|
" rubberband.mouseup('button_release', mouse_event_fn);\n",
|
|
" // Throttle sequential mouse events to 1 every 20ms.\n",
|
|
" rubberband.mousemove('motion_notify', mouse_event_fn);\n",
|
|
"\n",
|
|
" rubberband.mouseenter('figure_enter', mouse_event_fn);\n",
|
|
" rubberband.mouseleave('figure_leave', mouse_event_fn);\n",
|
|
"\n",
|
|
" canvas_div.on(\"wheel\", function (event) {\n",
|
|
" event = event.originalEvent;\n",
|
|
" event['data'] = 'scroll'\n",
|
|
" if (event.deltaY < 0) {\n",
|
|
" event.step = 1;\n",
|
|
" } else {\n",
|
|
" event.step = -1;\n",
|
|
" }\n",
|
|
" mouse_event_fn(event);\n",
|
|
" });\n",
|
|
"\n",
|
|
" canvas_div.append(canvas);\n",
|
|
" canvas_div.append(rubberband);\n",
|
|
"\n",
|
|
" this.rubberband = rubberband;\n",
|
|
" this.rubberband_canvas = rubberband[0];\n",
|
|
" this.rubberband_context = rubberband[0].getContext(\"2d\");\n",
|
|
" this.rubberband_context.strokeStyle = \"#000000\";\n",
|
|
"\n",
|
|
" this._resize_canvas = function(width, height) {\n",
|
|
" // Keep the size of the canvas, canvas container, and rubber band\n",
|
|
" // canvas in synch.\n",
|
|
" canvas_div.css('width', width)\n",
|
|
" canvas_div.css('height', height)\n",
|
|
"\n",
|
|
" canvas.attr('width', width * mpl.ratio);\n",
|
|
" canvas.attr('height', height * mpl.ratio);\n",
|
|
" canvas.attr('style', 'width: ' + width + 'px; height: ' + height + 'px;');\n",
|
|
"\n",
|
|
" rubberband.attr('width', width);\n",
|
|
" rubberband.attr('height', height);\n",
|
|
" }\n",
|
|
"\n",
|
|
" // Set the figure to an initial 600x600px, this will subsequently be updated\n",
|
|
" // upon first draw.\n",
|
|
" this._resize_canvas(600, 600);\n",
|
|
"\n",
|
|
" // Disable right mouse context menu.\n",
|
|
" $(this.rubberband_canvas).bind(\"contextmenu\",function(e){\n",
|
|
" return false;\n",
|
|
" });\n",
|
|
"\n",
|
|
" function set_focus () {\n",
|
|
" canvas.focus();\n",
|
|
" canvas_div.focus();\n",
|
|
" }\n",
|
|
"\n",
|
|
" window.setTimeout(set_focus, 100);\n",
|
|
"}\n",
|
|
"\n",
|
|
"mpl.figure.prototype._init_toolbar = function() {\n",
|
|
" var fig = this;\n",
|
|
"\n",
|
|
" var nav_element = $('<div/>')\n",
|
|
" nav_element.attr('style', 'width: 100%');\n",
|
|
" this.root.append(nav_element);\n",
|
|
"\n",
|
|
" // Define a callback function for later on.\n",
|
|
" function toolbar_event(event) {\n",
|
|
" return fig.toolbar_button_onclick(event['data']);\n",
|
|
" }\n",
|
|
" function toolbar_mouse_event(event) {\n",
|
|
" return fig.toolbar_button_onmouseover(event['data']);\n",
|
|
" }\n",
|
|
"\n",
|
|
" for(var toolbar_ind in mpl.toolbar_items) {\n",
|
|
" var name = mpl.toolbar_items[toolbar_ind][0];\n",
|
|
" var tooltip = mpl.toolbar_items[toolbar_ind][1];\n",
|
|
" var image = mpl.toolbar_items[toolbar_ind][2];\n",
|
|
" var method_name = mpl.toolbar_items[toolbar_ind][3];\n",
|
|
"\n",
|
|
" if (!name) {\n",
|
|
" // put a spacer in here.\n",
|
|
" continue;\n",
|
|
" }\n",
|
|
" var button = $('<button/>');\n",
|
|
" button.addClass('ui-button ui-widget ui-state-default ui-corner-all ' +\n",
|
|
" 'ui-button-icon-only');\n",
|
|
" button.attr('role', 'button');\n",
|
|
" button.attr('aria-disabled', 'false');\n",
|
|
" button.click(method_name, toolbar_event);\n",
|
|
" button.mouseover(tooltip, toolbar_mouse_event);\n",
|
|
"\n",
|
|
" var icon_img = $('<span/>');\n",
|
|
" icon_img.addClass('ui-button-icon-primary ui-icon');\n",
|
|
" icon_img.addClass(image);\n",
|
|
" icon_img.addClass('ui-corner-all');\n",
|
|
"\n",
|
|
" var tooltip_span = $('<span/>');\n",
|
|
" tooltip_span.addClass('ui-button-text');\n",
|
|
" tooltip_span.html(tooltip);\n",
|
|
"\n",
|
|
" button.append(icon_img);\n",
|
|
" button.append(tooltip_span);\n",
|
|
"\n",
|
|
" nav_element.append(button);\n",
|
|
" }\n",
|
|
"\n",
|
|
" var fmt_picker_span = $('<span/>');\n",
|
|
"\n",
|
|
" var fmt_picker = $('<select/>');\n",
|
|
" fmt_picker.addClass('mpl-toolbar-option ui-widget ui-widget-content');\n",
|
|
" fmt_picker_span.append(fmt_picker);\n",
|
|
" nav_element.append(fmt_picker_span);\n",
|
|
" this.format_dropdown = fmt_picker[0];\n",
|
|
"\n",
|
|
" for (var ind in mpl.extensions) {\n",
|
|
" var fmt = mpl.extensions[ind];\n",
|
|
" var option = $(\n",
|
|
" '<option/>', {selected: fmt === mpl.default_extension}).html(fmt);\n",
|
|
" fmt_picker.append(option)\n",
|
|
" }\n",
|
|
"\n",
|
|
" // Add hover states to the ui-buttons\n",
|
|
" $( \".ui-button\" ).hover(\n",
|
|
" function() { $(this).addClass(\"ui-state-hover\");},\n",
|
|
" function() { $(this).removeClass(\"ui-state-hover\");}\n",
|
|
" );\n",
|
|
"\n",
|
|
" var status_bar = $('<span class=\"mpl-message\"/>');\n",
|
|
" nav_element.append(status_bar);\n",
|
|
" this.message = status_bar[0];\n",
|
|
"}\n",
|
|
"\n",
|
|
"mpl.figure.prototype.request_resize = function(x_pixels, y_pixels) {\n",
|
|
" // Request matplotlib to resize the figure. Matplotlib will then trigger a resize in the client,\n",
|
|
" // which will in turn request a refresh of the image.\n",
|
|
" this.send_message('resize', {'width': x_pixels, 'height': y_pixels});\n",
|
|
"}\n",
|
|
"\n",
|
|
"mpl.figure.prototype.send_message = function(type, properties) {\n",
|
|
" properties['type'] = type;\n",
|
|
" properties['figure_id'] = this.id;\n",
|
|
" this.ws.send(JSON.stringify(properties));\n",
|
|
"}\n",
|
|
"\n",
|
|
"mpl.figure.prototype.send_draw_message = function() {\n",
|
|
" if (!this.waiting) {\n",
|
|
" this.waiting = true;\n",
|
|
" this.ws.send(JSON.stringify({type: \"draw\", figure_id: this.id}));\n",
|
|
" }\n",
|
|
"}\n",
|
|
"\n",
|
|
"\n",
|
|
"mpl.figure.prototype.handle_save = function(fig, msg) {\n",
|
|
" var format_dropdown = fig.format_dropdown;\n",
|
|
" var format = format_dropdown.options[format_dropdown.selectedIndex].value;\n",
|
|
" fig.ondownload(fig, format);\n",
|
|
"}\n",
|
|
"\n",
|
|
"\n",
|
|
"mpl.figure.prototype.handle_resize = function(fig, msg) {\n",
|
|
" var size = msg['size'];\n",
|
|
" if (size[0] != fig.canvas.width || size[1] != fig.canvas.height) {\n",
|
|
" fig._resize_canvas(size[0], size[1]);\n",
|
|
" fig.send_message(\"refresh\", {});\n",
|
|
" };\n",
|
|
"}\n",
|
|
"\n",
|
|
"mpl.figure.prototype.handle_rubberband = function(fig, msg) {\n",
|
|
" var x0 = msg['x0'] / mpl.ratio;\n",
|
|
" var y0 = (fig.canvas.height - msg['y0']) / mpl.ratio;\n",
|
|
" var x1 = msg['x1'] / mpl.ratio;\n",
|
|
" var y1 = (fig.canvas.height - msg['y1']) / mpl.ratio;\n",
|
|
" x0 = Math.floor(x0) + 0.5;\n",
|
|
" y0 = Math.floor(y0) + 0.5;\n",
|
|
" x1 = Math.floor(x1) + 0.5;\n",
|
|
" y1 = Math.floor(y1) + 0.5;\n",
|
|
" var min_x = Math.min(x0, x1);\n",
|
|
" var min_y = Math.min(y0, y1);\n",
|
|
" var width = Math.abs(x1 - x0);\n",
|
|
" var height = Math.abs(y1 - y0);\n",
|
|
"\n",
|
|
" fig.rubberband_context.clearRect(\n",
|
|
" 0, 0, fig.canvas.width, fig.canvas.height);\n",
|
|
"\n",
|
|
" fig.rubberband_context.strokeRect(min_x, min_y, width, height);\n",
|
|
"}\n",
|
|
"\n",
|
|
"mpl.figure.prototype.handle_figure_label = function(fig, msg) {\n",
|
|
" // Updates the figure title.\n",
|
|
" fig.header.textContent = msg['label'];\n",
|
|
"}\n",
|
|
"\n",
|
|
"mpl.figure.prototype.handle_cursor = function(fig, msg) {\n",
|
|
" var cursor = msg['cursor'];\n",
|
|
" switch(cursor)\n",
|
|
" {\n",
|
|
" case 0:\n",
|
|
" cursor = 'pointer';\n",
|
|
" break;\n",
|
|
" case 1:\n",
|
|
" cursor = 'default';\n",
|
|
" break;\n",
|
|
" case 2:\n",
|
|
" cursor = 'crosshair';\n",
|
|
" break;\n",
|
|
" case 3:\n",
|
|
" cursor = 'move';\n",
|
|
" break;\n",
|
|
" }\n",
|
|
" fig.rubberband_canvas.style.cursor = cursor;\n",
|
|
"}\n",
|
|
"\n",
|
|
"mpl.figure.prototype.handle_message = function(fig, msg) {\n",
|
|
" fig.message.textContent = msg['message'];\n",
|
|
"}\n",
|
|
"\n",
|
|
"mpl.figure.prototype.handle_draw = function(fig, msg) {\n",
|
|
" // Request the server to send over a new figure.\n",
|
|
" fig.send_draw_message();\n",
|
|
"}\n",
|
|
"\n",
|
|
"mpl.figure.prototype.handle_image_mode = function(fig, msg) {\n",
|
|
" fig.image_mode = msg['mode'];\n",
|
|
"}\n",
|
|
"\n",
|
|
"mpl.figure.prototype.updated_canvas_event = function() {\n",
|
|
" // Called whenever the canvas gets updated.\n",
|
|
" this.send_message(\"ack\", {});\n",
|
|
"}\n",
|
|
"\n",
|
|
"// A function to construct a web socket function for onmessage handling.\n",
|
|
"// Called in the figure constructor.\n",
|
|
"mpl.figure.prototype._make_on_message_function = function(fig) {\n",
|
|
" return function socket_on_message(evt) {\n",
|
|
" if (evt.data instanceof Blob) {\n",
|
|
" /* FIXME: We get \"Resource interpreted as Image but\n",
|
|
" * transferred with MIME type text/plain:\" errors on\n",
|
|
" * Chrome. But how to set the MIME type? It doesn't seem\n",
|
|
" * to be part of the websocket stream */\n",
|
|
" evt.data.type = \"image/png\";\n",
|
|
"\n",
|
|
" /* Free the memory for the previous frames */\n",
|
|
" if (fig.imageObj.src) {\n",
|
|
" (window.URL || window.webkitURL).revokeObjectURL(\n",
|
|
" fig.imageObj.src);\n",
|
|
" }\n",
|
|
"\n",
|
|
" fig.imageObj.src = (window.URL || window.webkitURL).createObjectURL(\n",
|
|
" evt.data);\n",
|
|
" fig.updated_canvas_event();\n",
|
|
" fig.waiting = false;\n",
|
|
" return;\n",
|
|
" }\n",
|
|
" else if (typeof evt.data === 'string' && evt.data.slice(0, 21) == \"data:image/png;base64\") {\n",
|
|
" fig.imageObj.src = evt.data;\n",
|
|
" fig.updated_canvas_event();\n",
|
|
" fig.waiting = false;\n",
|
|
" return;\n",
|
|
" }\n",
|
|
"\n",
|
|
" var msg = JSON.parse(evt.data);\n",
|
|
" var msg_type = msg['type'];\n",
|
|
"\n",
|
|
" // Call the \"handle_{type}\" callback, which takes\n",
|
|
" // the figure and JSON message as its only arguments.\n",
|
|
" try {\n",
|
|
" var callback = fig[\"handle_\" + msg_type];\n",
|
|
" } catch (e) {\n",
|
|
" console.log(\"No handler for the '\" + msg_type + \"' message type: \", msg);\n",
|
|
" return;\n",
|
|
" }\n",
|
|
"\n",
|
|
" if (callback) {\n",
|
|
" try {\n",
|
|
" // console.log(\"Handling '\" + msg_type + \"' message: \", msg);\n",
|
|
" callback(fig, msg);\n",
|
|
" } catch (e) {\n",
|
|
" console.log(\"Exception inside the 'handler_\" + msg_type + \"' callback:\", e, e.stack, msg);\n",
|
|
" }\n",
|
|
" }\n",
|
|
" };\n",
|
|
"}\n",
|
|
"\n",
|
|
"// from http://stackoverflow.com/questions/1114465/getting-mouse-location-in-canvas\n",
|
|
"mpl.findpos = function(e) {\n",
|
|
" //this section is from http://www.quirksmode.org/js/events_properties.html\n",
|
|
" var targ;\n",
|
|
" if (!e)\n",
|
|
" e = window.event;\n",
|
|
" if (e.target)\n",
|
|
" targ = e.target;\n",
|
|
" else if (e.srcElement)\n",
|
|
" targ = e.srcElement;\n",
|
|
" if (targ.nodeType == 3) // defeat Safari bug\n",
|
|
" targ = targ.parentNode;\n",
|
|
"\n",
|
|
" // jQuery normalizes the pageX and pageY\n",
|
|
" // pageX,Y are the mouse positions relative to the document\n",
|
|
" // offset() returns the position of the element relative to the document\n",
|
|
" var x = e.pageX - $(targ).offset().left;\n",
|
|
" var y = e.pageY - $(targ).offset().top;\n",
|
|
"\n",
|
|
" return {\"x\": x, \"y\": y};\n",
|
|
"};\n",
|
|
"\n",
|
|
"/*\n",
|
|
" * return a copy of an object with only non-object keys\n",
|
|
" * we need this to avoid circular references\n",
|
|
" * http://stackoverflow.com/a/24161582/3208463\n",
|
|
" */\n",
|
|
"function simpleKeys (original) {\n",
|
|
" return Object.keys(original).reduce(function (obj, key) {\n",
|
|
" if (typeof original[key] !== 'object')\n",
|
|
" obj[key] = original[key]\n",
|
|
" return obj;\n",
|
|
" }, {});\n",
|
|
"}\n",
|
|
"\n",
|
|
"mpl.figure.prototype.mouse_event = function(event, name) {\n",
|
|
" var canvas_pos = mpl.findpos(event)\n",
|
|
"\n",
|
|
" if (name === 'button_press')\n",
|
|
" {\n",
|
|
" this.canvas.focus();\n",
|
|
" this.canvas_div.focus();\n",
|
|
" }\n",
|
|
"\n",
|
|
" var x = canvas_pos.x * mpl.ratio;\n",
|
|
" var y = canvas_pos.y * mpl.ratio;\n",
|
|
"\n",
|
|
" this.send_message(name, {x: x, y: y, button: event.button,\n",
|
|
" step: event.step,\n",
|
|
" guiEvent: simpleKeys(event)});\n",
|
|
"\n",
|
|
" /* This prevents the web browser from automatically changing to\n",
|
|
" * the text insertion cursor when the button is pressed. We want\n",
|
|
" * to control all of the cursor setting manually through the\n",
|
|
" * 'cursor' event from matplotlib */\n",
|
|
" event.preventDefault();\n",
|
|
" return false;\n",
|
|
"}\n",
|
|
"\n",
|
|
"mpl.figure.prototype._key_event_extra = function(event, name) {\n",
|
|
" // Handle any extra behaviour associated with a key event\n",
|
|
"}\n",
|
|
"\n",
|
|
"mpl.figure.prototype.key_event = function(event, name) {\n",
|
|
"\n",
|
|
" // Prevent repeat events\n",
|
|
" if (name == 'key_press')\n",
|
|
" {\n",
|
|
" if (event.which === this._key)\n",
|
|
" return;\n",
|
|
" else\n",
|
|
" this._key = event.which;\n",
|
|
" }\n",
|
|
" if (name == 'key_release')\n",
|
|
" this._key = null;\n",
|
|
"\n",
|
|
" var value = '';\n",
|
|
" if (event.ctrlKey && event.which != 17)\n",
|
|
" value += \"ctrl+\";\n",
|
|
" if (event.altKey && event.which != 18)\n",
|
|
" value += \"alt+\";\n",
|
|
" if (event.shiftKey && event.which != 16)\n",
|
|
" value += \"shift+\";\n",
|
|
"\n",
|
|
" value += 'k';\n",
|
|
" value += event.which.toString();\n",
|
|
"\n",
|
|
" this._key_event_extra(event, name);\n",
|
|
"\n",
|
|
" this.send_message(name, {key: value,\n",
|
|
" guiEvent: simpleKeys(event)});\n",
|
|
" return false;\n",
|
|
"}\n",
|
|
"\n",
|
|
"mpl.figure.prototype.toolbar_button_onclick = function(name) {\n",
|
|
" if (name == 'download') {\n",
|
|
" this.handle_save(this, null);\n",
|
|
" } else {\n",
|
|
" this.send_message(\"toolbar_button\", {name: name});\n",
|
|
" }\n",
|
|
"};\n",
|
|
"\n",
|
|
"mpl.figure.prototype.toolbar_button_onmouseover = function(tooltip) {\n",
|
|
" this.message.textContent = tooltip;\n",
|
|
"};\n",
|
|
"mpl.toolbar_items = [[\"Home\", \"Reset original view\", \"fa fa-home icon-home\", \"home\"], [\"Back\", \"Back to previous view\", \"fa fa-arrow-left icon-arrow-left\", \"back\"], [\"Forward\", \"Forward to next view\", \"fa fa-arrow-right icon-arrow-right\", \"forward\"], [\"\", \"\", \"\", \"\"], [\"Pan\", \"Pan axes with left mouse, zoom with right\", \"fa fa-arrows icon-move\", \"pan\"], [\"Zoom\", \"Zoom to rectangle\", \"fa fa-square-o icon-check-empty\", \"zoom\"], [\"\", \"\", \"\", \"\"], [\"Download\", \"Download plot\", \"fa fa-floppy-o icon-save\", \"download\"]];\n",
|
|
"\n",
|
|
"mpl.extensions = [\"eps\", \"jpeg\", \"pdf\", \"png\", \"ps\", \"raw\", \"svg\", \"tif\"];\n",
|
|
"\n",
|
|
"mpl.default_extension = \"png\";var comm_websocket_adapter = function(comm) {\n",
|
|
" // Create a \"websocket\"-like object which calls the given IPython comm\n",
|
|
" // object with the appropriate methods. Currently this is a non binary\n",
|
|
" // socket, so there is still some room for performance tuning.\n",
|
|
" var ws = {};\n",
|
|
"\n",
|
|
" ws.close = function() {\n",
|
|
" comm.close()\n",
|
|
" };\n",
|
|
" ws.send = function(m) {\n",
|
|
" //console.log('sending', m);\n",
|
|
" comm.send(m);\n",
|
|
" };\n",
|
|
" // Register the callback with on_msg.\n",
|
|
" comm.on_msg(function(msg) {\n",
|
|
" //console.log('receiving', msg['content']['data'], msg);\n",
|
|
" // Pass the mpl event to the overridden (by mpl) onmessage function.\n",
|
|
" ws.onmessage(msg['content']['data'])\n",
|
|
" });\n",
|
|
" return ws;\n",
|
|
"}\n",
|
|
"\n",
|
|
"mpl.mpl_figure_comm = function(comm, msg) {\n",
|
|
" // This is the function which gets called when the mpl process\n",
|
|
" // starts-up an IPython Comm through the \"matplotlib\" channel.\n",
|
|
"\n",
|
|
" var id = msg.content.data.id;\n",
|
|
" // Get hold of the div created by the display call when the Comm\n",
|
|
" // socket was opened in Python.\n",
|
|
" var element = $(\"#\" + id);\n",
|
|
" var ws_proxy = comm_websocket_adapter(comm)\n",
|
|
"\n",
|
|
" function ondownload(figure, format) {\n",
|
|
" window.open(figure.imageObj.src);\n",
|
|
" }\n",
|
|
"\n",
|
|
" var fig = new mpl.figure(id, ws_proxy,\n",
|
|
" ondownload,\n",
|
|
" element.get(0));\n",
|
|
"\n",
|
|
" // Call onopen now - mpl needs it, as it is assuming we've passed it a real\n",
|
|
" // web socket which is closed, not our websocket->open comm proxy.\n",
|
|
" ws_proxy.onopen();\n",
|
|
"\n",
|
|
" fig.parent_element = element.get(0);\n",
|
|
" fig.cell_info = mpl.find_output_cell(\"<div id='\" + id + \"'></div>\");\n",
|
|
" if (!fig.cell_info) {\n",
|
|
" console.error(\"Failed to find cell for figure\", id, fig);\n",
|
|
" return;\n",
|
|
" }\n",
|
|
"\n",
|
|
" var output_index = fig.cell_info[2]\n",
|
|
" var cell = fig.cell_info[0];\n",
|
|
"\n",
|
|
"};\n",
|
|
"\n",
|
|
"mpl.figure.prototype.handle_close = function(fig, msg) {\n",
|
|
" var width = fig.canvas.width/mpl.ratio\n",
|
|
" fig.root.unbind('remove')\n",
|
|
"\n",
|
|
" // Update the output cell to use the data from the current canvas.\n",
|
|
" fig.push_to_output();\n",
|
|
" var dataURL = fig.canvas.toDataURL();\n",
|
|
" // Re-enable the keyboard manager in IPython - without this line, in FF,\n",
|
|
" // the notebook keyboard shortcuts fail.\n",
|
|
" IPython.keyboard_manager.enable()\n",
|
|
" $(fig.parent_element).html('<img src=\"' + dataURL + '\" width=\"' + width + '\">');\n",
|
|
" fig.close_ws(fig, msg);\n",
|
|
"}\n",
|
|
"\n",
|
|
"mpl.figure.prototype.close_ws = function(fig, msg){\n",
|
|
" fig.send_message('closing', msg);\n",
|
|
" // fig.ws.close()\n",
|
|
"}\n",
|
|
"\n",
|
|
"mpl.figure.prototype.push_to_output = function(remove_interactive) {\n",
|
|
" // Turn the data on the canvas into data in the output cell.\n",
|
|
" var width = this.canvas.width/mpl.ratio\n",
|
|
" var dataURL = this.canvas.toDataURL();\n",
|
|
" this.cell_info[1]['text/html'] = '<img src=\"' + dataURL + '\" width=\"' + width + '\">';\n",
|
|
"}\n",
|
|
"\n",
|
|
"mpl.figure.prototype.updated_canvas_event = function() {\n",
|
|
" // Tell IPython that the notebook contents must change.\n",
|
|
" IPython.notebook.set_dirty(true);\n",
|
|
" this.send_message(\"ack\", {});\n",
|
|
" var fig = this;\n",
|
|
" // Wait a second, then push the new image to the DOM so\n",
|
|
" // that it is saved nicely (might be nice to debounce this).\n",
|
|
" setTimeout(function () { fig.push_to_output() }, 1000);\n",
|
|
"}\n",
|
|
"\n",
|
|
"mpl.figure.prototype._init_toolbar = function() {\n",
|
|
" var fig = this;\n",
|
|
"\n",
|
|
" var nav_element = $('<div/>')\n",
|
|
" nav_element.attr('style', 'width: 100%');\n",
|
|
" this.root.append(nav_element);\n",
|
|
"\n",
|
|
" // Define a callback function for later on.\n",
|
|
" function toolbar_event(event) {\n",
|
|
" return fig.toolbar_button_onclick(event['data']);\n",
|
|
" }\n",
|
|
" function toolbar_mouse_event(event) {\n",
|
|
" return fig.toolbar_button_onmouseover(event['data']);\n",
|
|
" }\n",
|
|
"\n",
|
|
" for(var toolbar_ind in mpl.toolbar_items){\n",
|
|
" var name = mpl.toolbar_items[toolbar_ind][0];\n",
|
|
" var tooltip = mpl.toolbar_items[toolbar_ind][1];\n",
|
|
" var image = mpl.toolbar_items[toolbar_ind][2];\n",
|
|
" var method_name = mpl.toolbar_items[toolbar_ind][3];\n",
|
|
"\n",
|
|
" if (!name) { continue; };\n",
|
|
"\n",
|
|
" var button = $('<button class=\"btn btn-default\" href=\"#\" title=\"' + name + '\"><i class=\"fa ' + image + ' fa-lg\"></i></button>');\n",
|
|
" button.click(method_name, toolbar_event);\n",
|
|
" button.mouseover(tooltip, toolbar_mouse_event);\n",
|
|
" nav_element.append(button);\n",
|
|
" }\n",
|
|
"\n",
|
|
" // Add the status bar.\n",
|
|
" var status_bar = $('<span class=\"mpl-message\" style=\"text-align:right; float: right;\"/>');\n",
|
|
" nav_element.append(status_bar);\n",
|
|
" this.message = status_bar[0];\n",
|
|
"\n",
|
|
" // Add the close button to the window.\n",
|
|
" var buttongrp = $('<div class=\"btn-group inline pull-right\"></div>');\n",
|
|
" var button = $('<button class=\"btn btn-mini btn-primary\" href=\"#\" title=\"Stop Interaction\"><i class=\"fa fa-power-off icon-remove icon-large\"></i></button>');\n",
|
|
" button.click(function (evt) { fig.handle_close(fig, {}); } );\n",
|
|
" button.mouseover('Stop Interaction', toolbar_mouse_event);\n",
|
|
" buttongrp.append(button);\n",
|
|
" var titlebar = this.root.find($('.ui-dialog-titlebar'));\n",
|
|
" titlebar.prepend(buttongrp);\n",
|
|
"}\n",
|
|
"\n",
|
|
"mpl.figure.prototype._root_extra_style = function(el){\n",
|
|
" var fig = this\n",
|
|
" el.on(\"remove\", function(){\n",
|
|
"\tfig.close_ws(fig, {});\n",
|
|
" });\n",
|
|
"}\n",
|
|
"\n",
|
|
"mpl.figure.prototype._canvas_extra_style = function(el){\n",
|
|
" // this is important to make the div 'focusable\n",
|
|
" el.attr('tabindex', 0)\n",
|
|
" // reach out to IPython and tell the keyboard manager to turn it's self\n",
|
|
" // off when our div gets focus\n",
|
|
"\n",
|
|
" // location in version 3\n",
|
|
" if (IPython.notebook.keyboard_manager) {\n",
|
|
" IPython.notebook.keyboard_manager.register_events(el);\n",
|
|
" }\n",
|
|
" else {\n",
|
|
" // location in version 2\n",
|
|
" IPython.keyboard_manager.register_events(el);\n",
|
|
" }\n",
|
|
"\n",
|
|
"}\n",
|
|
"\n",
|
|
"mpl.figure.prototype._key_event_extra = function(event, name) {\n",
|
|
" var manager = IPython.notebook.keyboard_manager;\n",
|
|
" if (!manager)\n",
|
|
" manager = IPython.keyboard_manager;\n",
|
|
"\n",
|
|
" // Check for shift+enter\n",
|
|
" if (event.shiftKey && event.which == 13) {\n",
|
|
" this.canvas_div.blur();\n",
|
|
" event.shiftKey = false;\n",
|
|
" // Send a \"J\" for go to next cell\n",
|
|
" event.which = 74;\n",
|
|
" event.keyCode = 74;\n",
|
|
" manager.command_mode();\n",
|
|
" manager.handle_keydown(event);\n",
|
|
" }\n",
|
|
"}\n",
|
|
"\n",
|
|
"mpl.figure.prototype.handle_save = function(fig, msg) {\n",
|
|
" fig.ondownload(fig, null);\n",
|
|
"}\n",
|
|
"\n",
|
|
"\n",
|
|
"mpl.find_output_cell = function(html_output) {\n",
|
|
" // Return the cell and output element which can be found *uniquely* in the notebook.\n",
|
|
" // Note - this is a bit hacky, but it is done because the \"notebook_saving.Notebook\"\n",
|
|
" // IPython event is triggered only after the cells have been serialised, which for\n",
|
|
" // our purposes (turning an active figure into a static one), is too late.\n",
|
|
" var cells = IPython.notebook.get_cells();\n",
|
|
" var ncells = cells.length;\n",
|
|
" for (var i=0; i<ncells; i++) {\n",
|
|
" var cell = cells[i];\n",
|
|
" if (cell.cell_type === 'code'){\n",
|
|
" for (var j=0; j<cell.output_area.outputs.length; j++) {\n",
|
|
" var data = cell.output_area.outputs[j];\n",
|
|
" if (data.data) {\n",
|
|
" // IPython >= 3 moved mimebundle to data attribute of output\n",
|
|
" data = data.data;\n",
|
|
" }\n",
|
|
" if (data['text/html'] == html_output) {\n",
|
|
" return [cell, data, j];\n",
|
|
" }\n",
|
|
" }\n",
|
|
" }\n",
|
|
" }\n",
|
|
"}\n",
|
|
"\n",
|
|
"// Register the function which deals with the matplotlib target/channel.\n",
|
|
"// The kernel may be null if the page has been refreshed.\n",
|
|
"if (IPython.notebook.kernel != null) {\n",
|
|
" IPython.notebook.kernel.comm_manager.register_target('matplotlib', mpl.mpl_figure_comm);\n",
|
|
"}\n"
|
|
],
|
|
"text/plain": [
|
|
"<IPython.core.display.Javascript object>"
|
|
]
|
|
},
|
|
"metadata": {},
|
|
"output_type": "display_data"
|
|
},
|
|
{
|
|
"data": {
|
|
"text/html": [
|
|
"<img src=\"data:image/png;base64,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\" width=\"640\">"
|
|
],
|
|
"text/plain": [
|
|
"<IPython.core.display.HTML object>"
|
|
]
|
|
},
|
|
"metadata": {},
|
|
"output_type": "display_data"
|
|
},
|
|
{
|
|
"data": {
|
|
"text/plain": [
|
|
"<matplotlib.legend.Legend at 0x416117ed30>"
|
|
]
|
|
},
|
|
"execution_count": 12,
|
|
"metadata": {},
|
|
"output_type": "execute_result"
|
|
}
|
|
],
|
|
"source": [
|
|
"F = WAF_1D_flux_helper(h, hu, g, dx, dt)\n",
|
|
"plt.figure()\n",
|
|
"for i in range(1):\n",
|
|
" plt.plot(x[2:-1], F[:,i]-F[:,i+1], marker=' ', label='F_' + str(i))\n",
|
|
"#plt.ylim(-10, 10)\n",
|
|
"plt.legend()"
|
|
]
|
|
},
|
|
{
|
|
"cell_type": "code",
|
|
"execution_count": null,
|
|
"metadata": {},
|
|
"outputs": [],
|
|
"source": []
|
|
}
|
|
],
|
|
"metadata": {
|
|
"kernelspec": {
|
|
"display_name": "Python 3",
|
|
"language": "python",
|
|
"name": "python3"
|
|
},
|
|
"language_info": {
|
|
"codemirror_mode": {
|
|
"name": "ipython",
|
|
"version": 3
|
|
},
|
|
"file_extension": ".py",
|
|
"mimetype": "text/x-python",
|
|
"name": "python",
|
|
"nbconvert_exporter": "python",
|
|
"pygments_lexer": "ipython3",
|
|
"version": "3.6.5"
|
|
}
|
|
},
|
|
"nbformat": 4,
|
|
"nbformat_minor": 2
|
|
}
|