{"id":4496,"date":"2024-02-25T17:23:24","date_gmt":"2024-02-25T21:23:24","guid":{"rendered":"https:\/\/faculty.fiu.edu\/~theobald\/?page_id=4496"},"modified":"2024-02-25T18:03:09","modified_gmt":"2024-02-25T22:03:09","slug":"motion-detection","status":"publish","type":"page","link":"https:\/\/faculty.fiu.edu\/~theobald\/fun\/neurobiology-fun\/motion-detection\/","title":{"rendered":"Motion detection"},"content":{"rendered":"\t\t<div data-elementor-type=\"wp-page\" data-elementor-id=\"4496\" class=\"elementor elementor-4496\">\n\t\t\t\t\t\t\t\t\t<section class=\"elementor-section elementor-top-section elementor-element elementor-element-7374e5e elementor-section-boxed elementor-section-height-default elementor-section-height-default\" data-id=\"7374e5e\" data-element_type=\"section\">\n\t\t\t\t\t\t<div class=\"elementor-container elementor-column-gap-default\">\n\t\t\t\t\t<div class=\"elementor-column elementor-col-100 elementor-top-column elementor-element elementor-element-f29aee7\" data-id=\"f29aee7\" data-element_type=\"column\">\n\t\t\t<div class=\"elementor-widget-wrap elementor-element-populated\">\n\t\t\t\t\t\t\t\t<div class=\"elementor-element elementor-element-1c451fd elementor-widget elementor-widget-image\" data-id=\"1c451fd\" data-element_type=\"widget\" data-widget_type=\"image.default\">\n\t\t\t\t<div class=\"elementor-widget-container\">\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t<img loading=\"lazy\" decoding=\"async\" width=\"1024\" height=\"711\" src=\"https:\/\/faculty.fiu.edu\/~theobald\/wp-content\/uploads\/bee_motion-1024x711.png\" class=\"attachment-large size-large wp-image-4502\" alt=\"\" srcset=\"https:\/\/faculty.fiu.edu\/~theobald\/wp-content\/uploads\/bee_motion-1024x711.png 1024w, https:\/\/faculty.fiu.edu\/~theobald\/wp-content\/uploads\/bee_motion-300x208.png 300w, https:\/\/faculty.fiu.edu\/~theobald\/wp-content\/uploads\/bee_motion-768x534.png 768w, https:\/\/faculty.fiu.edu\/~theobald\/wp-content\/uploads\/bee_motion.png 1104w\" sizes=\"auto, (max-width: 1024px) 100vw, 1024px\" \/>\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t<\/div>\n\t\t\t\t<\/div>\n\t\t\t\t<div class=\"elementor-element elementor-element-385bd69 elementor-widget elementor-widget-text-editor\" data-id=\"385bd69\" data-element_type=\"widget\" data-widget_type=\"text-editor.default\">\n\t\t\t\t<div class=\"elementor-widget-container\">\n\t\t\t\t\t\t\t<p>In sensory neurobiology, the components necessary to detect image motion are: 1. at least two light detectors, viewing different regions in space (this is realized by interommatidial angle), 2. a temporal difference (this is the delay of the signals that cross over), and 3. a nonlinear interaction (this is the multiplication of the direct and delayed signals). This is a full correlator, two half correlators mirroring each other, and when the parameters are all correct, it registers positive for one direction and negative for the other.<\/p><p>Basically, when an image is moving, first one detector will pick it up, then a second one will pick it up, but at a later time. To decide if these detectors correlate, we just multiply the output of second with the delayed output of the first. If the delay brings them into close agreement, the bright parts (positive) will align and multiply together, yielding positive outputs, and the dim parts (negative) will align and multiply together, also yielding positive outputs. If the delay brings the signals farther out of phase, dim and bright (positive and negative) elements align and multiply together, yielding negative outputs.<\/p>\t\t\t\t\t\t<\/div>\n\t\t\t\t<\/div>\n\t\t\t\t\t<\/div>\n\t\t<\/div>\n\t\t\t\t\t\t\t<\/div>\n\t\t<\/section>\n\t\t\t\t<section class=\"elementor-section elementor-top-section elementor-element elementor-element-8c115a4 elementor-section-boxed elementor-section-height-default elementor-section-height-default\" data-id=\"8c115a4\" data-element_type=\"section\">\n\t\t\t\t\t\t<div class=\"elementor-container elementor-column-gap-default\">\n\t\t\t\t\t<div class=\"elementor-column elementor-col-100 elementor-top-column elementor-element elementor-element-833f316\" data-id=\"833f316\" data-element_type=\"column\">\n\t\t\t<div class=\"elementor-widget-wrap\">\n\t\t\t\t\t\t\t\t\t<\/div>\n\t\t<\/div>\n\t\t\t\t\t\t\t<\/div>\n\t\t<\/section>\n\t\t\t\t<section class=\"elementor-section elementor-top-section elementor-element elementor-element-ac9f449 elementor-section-boxed elementor-section-height-default elementor-section-height-default\" data-id=\"ac9f449\" data-element_type=\"section\">\n\t\t\t\t\t\t<div class=\"elementor-container elementor-column-gap-default\">\n\t\t\t\t\t<div class=\"elementor-column elementor-col-100 elementor-top-column elementor-element elementor-element-f04d892\" data-id=\"f04d892\" data-element_type=\"column\">\n\t\t\t<div class=\"elementor-widget-wrap elementor-element-populated\">\n\t\t\t\t\t\t\t\t<div class=\"elementor-element elementor-element-75e12a5 elementor-widget elementor-widget-html\" data-id=\"75e12a5\" data-element_type=\"widget\" data-widget_type=\"html.default\">\n\t\t\t\t<div class=\"elementor-widget-container\">\n\t\t\t<!DOCTYPE HTML>\n<html>\n  <meta charset=\"utf-8\" \/>\n  <style>\n    .nacolor {\n\tcolor: #909000;\n\t     }\n    .kcolor {\n\tcolor: #8e60e6;\n\t     }\n    .clcolor {\n\tcolor: #169000;\n\t     }\n  <\/style>\n\n\n  \n  <body>\n    <h2>Motion detection (Hassenstein Reichardt correlation)\n    <\/h2>\n\n\n    <!-- table of facet sliders -->\n    <table style=\"width:800px\">\n      <tr>\n\t<th style=\"width:20%\">num facets<\/th>\n\t<td style=\"width:20%\"><input type=\"range\" min=\"2\" max=\"20\" value=\"8\" step=\"1\" class=\"fslider\" name=\"fslider\" id=\"nfacets_slider\"><\/td>\n\t<td style=\"width:15%\"><label id=\"nfacets_slider_display\">8<\/label><br><\/td>\n\n\t<th style=\"width:20%\">spatial freq<\/th>\n\t<td style=\"width:20%\"><input type=\"range\" min=\".1\" max=\"4\" value=\"2\" step=\".1\" class=\"fslider\" name=\"fslider\" id=\"sf_slider\"><\/td>\n\t<td style=\"width:15%\"><label id=\"sf_slider_display\">2<\/label><br><\/td>\n      <\/tr>\n\n      <tr>\n\t<th style=\"width:20%\">io angle<\/th>\n\t<td style=\"width:20%\"><input type=\"range\" min=\"2\" max=\"9\" value=\"6\" step=\".5\" class=\"fslider\" name=\"fslider\" id=\"ioang_slider\"><\/td>\n\t<td style=\"width:15%\"><label id=\"ioang_slider_display\">6<\/label><br><\/td>\n\n\t<th style=\"width:20%\">temporal freq<\/th>\n\t<td style=\"width:20%\"><input type=\"range\" min=\"-3\" max=\"3\" value=\".2\" step=\".1\" class=\"fslider\" name=\"fslider\" id=\"tf_slider\"><\/td>\n\t<td style=\"width:15%\"><label id=\"tf_slider_display\">0<\/label><br><\/td>\n      <\/tr>\n\n      <tr>\n\t<th style=\"width:20%\">delay<\/th>\n\t<td style=\"width:20%\"><input type=\"range\" min=\"0\" max=\"99\" value=\"50\" step=\"1\" class=\"fslider\" name=\"fslider\" id=\"delay_slider\"><\/td>\n\t<td style=\"width:15%\"><label id=\"delay_slider_display\">50<\/label><br><\/td>\n\n      \t<th style=\"width:20%\">contrast<\/th>\n\t<td style=\"width:20%\"><input type=\"range\" min=\"0\" max=\"1\" value=\".5\" step=\".05\" class=\"fslider\" name=\"fslider\" id=\"contrast_slider\"><\/td>\n\t<td style=\"width:15%\"><label id=\"contrast_slider_display\">0.5<\/label><br><\/td>\n      <\/tr>\n    \n      <!-- <tr> -->\n      <!-- \t<th style=\"width:20%\">r rhabdome<\/th> -->\n      <!-- \t<td style=\"width:20%\"><input type=\"range\" min=\"0\" max=\"480\" value=\"100\" step=\".1\" class=\"fslider\" name=\"fslider\" id=\"rrhab_slider\"><\/td> -->\n      <!-- \t<td style=\"width:15%\"><label id=\"rrhab_slider_display\">100<\/label><br><\/td> -->\n      <!-- <\/tr> -->\n\n    \n\n    <!-- plot -->\n    <div>\n      <span id=\"v1_plot\"> \n\t<!-- <canvas id=\"canvas_r\" width=\"960\" height=\"480\"><\/canvas> -->\n\t<canvas id=\"canvas_r\" width=\"960\" height=\"960\"><\/canvas>\n      <\/span>\n    <\/div>\n\n\n\n    <!-- javascript -->\n    <script type=\"text\/javascript\">\n      'use strict';\n\n\n      function interp(x, x0, x1, y0, y1) {\n\t  return y0 + (x - x0)*((y1-y0)\/(x1-x0));\n      }\n\n\n      \/\/ colormaps roughly interpolated from matplotlib\n      function coolwarm_cm (x) { \/\/x = 0 -- 1\n\t  var r = Math.min(Math.max(Math.round(-8.8*x**3 + 36.7*x**2 + 15.3*x + 176.0), 0), 255);\n\t  var g = Math.min(Math.max(Math.round(9.6*x**3 + 2.7*x**2 + -63.8*x + 219.6), 0), 255);\n\t  var b = Math.min(Math.max(Math.round(3.5*x**3 + -16.0*x**2 + 2.0*x + 247.0), 0), 255);\n\t  return \"rgb(\"+r+\",\"+g+\",\"+b+\")\";\n      }\n\n      \/\/ function cm_plasma(x) { \/\/x = 0--1\n      \/\/ \t  var r = Math.min(Math.max(Math.round(-104.1*x**3 + -135.0*x**2 + 462.6*x + 19.3), 0), 255);\n      \/\/ \t  var g = Math.min(Math.max(Math.round(-254.9*x**3 + 617.4*x**2 + -127.7*x + 7.0), 0), 255);\n      \/\/ \t  var b = Math.min(Math.max(Math.round(582.1*x**3 + -1023.3*x**2 + 344.7*x + 133.4), 0), 255);\n      \/\/ \t  return \"rgb(\"+r+\",\"+g+\",\"+b+\")\";\n      \/\/ }\n\n      \n      \/\/ function cm_viridis(x) { \/\/x = 0--1\n      \/\/ \t  var r = Math.min(Math.max(Math.round(733.3*x**3 + -542.5*x**2 + 11.2*x + 74.5), 0), 255);\n      \/\/ \t  var g = Math.min(Math.max(Math.round(-44.7*x**3 + -41.0*x**2 + 314.5*x + 4.3), 0), 255);\n      \/\/ \t  var b = Math.min(Math.max(Math.round(44.5*x**3 + -435.7*x**2 + 312.5*x + 87.4), 0), 255);\n      \/\/ \t  return \"rgb(\"+r+\",\"+g+\",\"+b+\")\";\n      \/\/ }\n\n      function cm_cividis(x) { \/\/x = 0--1\n\t  var r = Math.min(Math.max(Math.round(148.3*x**3 + -237.2*x**2 + 361.6*x + -14.4), 0), 255);\n\t  var g = Math.min(Math.max(Math.round(37.1*x**3 + -19.0*x**2 + 178.2*x + 33.8), 0), 255);\n\t  var b = Math.min(Math.max(Math.round(-228.0*x**3 + 168.1*x**2 + 14.2*x + 98.1), 0), 255);\n\t  return \"rgb(\"+r+\",\"+g+\",\"+b+\")\";\n      }\n\n\n      \/\/ soapbubble colors for complex magnitudes and angles \n      function cm_soapbubble(x, y, alpha=1.0) { \/\/x = 0 -- 1, y= -pi -- pi\n\t  var r = Math.min(Math.max(Math.round(-57.4*Math.sin(y + 4.5) +x*265.1 + -20.3), 0), 255);\n\t  var g = Math.min(Math.max(Math.round(20.6*Math.sin(y + -1.5) +x*253.5 + -14.3), 0), 255);\n\t  var b = Math.min(Math.max(Math.round(57.1*Math.sin(y + 3.2) +x*234.6 + -4.3), 0), 255);\n\t  return \"rgba(\"+r+\",\"+g+\",\"+b+\",\"+alpha+\")\";\n      }\n      \n\n\n      class CircularBuffer {\n\t  constructor(size=100, fillValue=0.0) {\n              this.size = size;\n              this.buffer = new Array(size).fill(fillValue);\n              this.head = 0; \/\/ Points to the current start of the buffer\n\t  }\n\n\t  \/\/ Adds a value to the end of the buffer, overwriting the oldest value if necessary\n\t  add(value) {\n              this.buffer[this.head] = value;\n              this.head = (this.head + 1) % this.size; \/\/ Move head to the next position, wrapping around if necessary\n\t  }\n\n\t  \/\/ Gets a value from the buffer at the index relative to the most recently added value\n\t  get(relativeIndex) {\n              if (relativeIndex >= this.size) {\n\t\t  throw new Error(\"Index out of bounds\");\n              }\n              \/\/ Calculate the actual index in the buffer, accounting for wrapping\n              let index = (this.head - 1 - relativeIndex + this.size) % this.size;\n              return this.buffer[index];\n\t  }\n\n\t  \/\/ Optional: method to view the current state of the buffer (for debugging)\n\t  view() {\n              return this.buffer.slice(this.head).concat(this.buffer.slice(0, this.head));\n\t  }\n      }\n\n\n      \n      \n      class Oscilloscope {\n\t  constructor (context, left, right, bottom, top, title, bg,\n\t\t       vmin=-1.7, vmax=1.5) {\n\t      this.left = left;\n\t      this.right = right;\n\t      this.bottom = bottom;\n\t      this.top = top;\n\t      this.ctx = context;\n\t      this.bg = bg;\n\t      this.ctx.fillStyle = bg;\n\t      this.ctx.fillRect(this.left,this.bottom,this.right-this.left,this.top-this.bottom);\n\t      this.tmin = 0;\n\t      this.tmax = 10;\n\t      this.vmin = vmin;\n\t      this.vmax = vmax;\n\t      this.speed = .01\n\t      this.t = 0;\n\t      this.v = 0;\n\t      this.t_old;\n\t      this.v_old;\n\t      this.x;\n\t      this.y;\n\t      this.noise = 0;\n\t      this.dt = .02;\n\n\t      this.title = title;\n\n\t      this.vobjs = [];  \/\/ objects that can return voltages\n\t      this.args = [];   \/\/ arguments to those objects\n\t      this.volts = [];  \/\/ the current voltages returned\n\t      this.cols = [];   \/\/ color to plot \n\t      this.lws = [];    \/\/ linewidths to plot \n\t      this.dashes = []; \/\/ dashes to plot \n\t      this.v_olds = []; \/\/ voltages in the previous time step\n\t  }\n\t  \n\n\t  draw_line(t0, v0, t1, v1, color, dash, lw, x0off=0, x1off=0, y0off=0, y1off=0) {\n\t      var x0 = Math.round(interp(t0, this.tmin, this.tmax, this.left, this.right)) + x0off;\n\t      var x1 = Math.round(interp(t1, this.tmin, this.tmax, this.left, this.right)) + x1off;\n\t      var y0 = Math.round(interp(v0, this.vmin, this.vmax, this.bottom, this.top)) + y0off;\n\t      var y1 = Math.round(interp(v1, this.vmin, this.vmax, this.bottom, this.top)) + y1off;\n\t      this.ctx.lineWidth = lw;\n\t      this.ctx.setLineDash(dash);\n\t      this.ctx.strokeStyle = color;\n\t      this.ctx.beginPath();\n\t      this.ctx.moveTo(x0, y0);\n\t      this.ctx.lineTo(x1, y1);\n\t      this.ctx.fillStyle = color;\n\t      this.ctx.stroke();\n\t  }\n\t  \n\t  write_label(posx, posy, symbol, aftersym, centerx, centery) {\n\t      this.ctx.font = \"12px Ariel\";\n\t      if (centery==1) this.ctx.textBaseline = \"middle\";\n\t      if (centery==0) this.ctx.textBaseline = \"bottom\";\n\t      if (centerx==1) this.ctx.textAlign = \"center\";\n\t      if (centerx==0) this.ctx.textAlign = \"right\";\n\t      this.ctx.fillText(symbol + aftersym, posx, posy);\n\t  }\n\n\t  draw_h_line(pos, start, stop, color, dash, symbol, aftersym, centerx, centery) {\n\t      this.draw_line(this.tmin, pos, this.tmax, pos, color, dash, 0.25);\n\t  }\n\t  \n\t  draw_v_line(pos, start, stop, color, dash, symbol, aftersym, centerx, centery) {\n\t      this.draw_line(pos, this.vmin, pos, this.vmax, color, dash, 0.25);\n\t  }\n\t  \n\t  draw_lines(color) {\n\t      var i, first, dash, stepsize;\n\t      stepsize = Math.pow(10, Math.floor(Math.log10(this.tmax-this.tmin)-.3));\n\t      first = Math.ceil(this.tmin\/stepsize)*stepsize;\n\t      for (i=first; i<this.tmax; i+=stepsize) {\n\t\t  this.draw_v_line(i, 0.05, 1, color, [1,4], \"\", \"\", 0, 0);\n\t      }\n\t      stepsize = Math.pow(10, Math.floor(Math.log10(this.vmax-this.vmin)-.3));\n\t      first = Math.ceil(this.vmin\/stepsize)*stepsize;\n\t      for (i=first; i<this.vmax; i+=stepsize) {\n\t       \t  if (i==0) dash=[1,1];\n\t       \t  if (i!=0) dash=[1,4];\n\t\t  this.draw_h_line(i, 0.05, 1, color, dash, \"\", \"\", 0, 0);\n\t      }\n\t  }\n\t  \n\n\t  updates(dt=.02) {\n\t      this.t_old = this.t;\n\t      this.t = this.t + dt;\n\t      if (this.t>=this.tmax) this.t=this.tmin;\n\t      \/\/ if t is earlier than t_old, move the t_old to avoid drawing a big backwards line\n\t      if (this.t<this.t_old) this.t_old = this.t;\n\t      \n\t      \/\/ erase ahead with a line\n\t      this.draw_line(this.t, this.vmin, this.t, this.vmax, this.bg, [0], 5, 3, 3);\n\n\t      \/\/ for each line\n\t      for (var i=0; i<this.volts.length; i++) {\n\t\t  this.v_olds[i] = this.volts[i];\n\t\t  this.volts[i] = this.vobjs[i].get_v(...this.args[i]);\n\n\t\t  \/\/ draw the line\n\t\t  this.draw_line(this.t_old, this.v_olds[i], this.t, this.volts[i], this.cols[i], this.dashes[i], this.lws[i]);\n\t      }\n\n\t      \/\/ plot elements\n\t      this.draw_lines(\"gray\");\n\t      this.draw_title();\n\t  }\n\n\t  draw_title() {\n\t      this.ctx.font = \"18px Arial\";\n\t      this.ctx.textAlign = 'center'; \/\/ Align text horizontally in the middle\n\t      this.ctx.textBaseline = 'top'; \/\/ Align text vertically in the middle\n\t      this.ctx.fillText(this.title, (this.right+this.left)\/2, this.top); \n\t  }\n\n\t  vsource(object, arg) {\n\t      this.vobj_arg = arg;\n\t      this.vobj = object;\n\t  }\n\n\t  add_v(object, arg, color, width=2, dash=[]) {\n\t      this.vobjs.push(object);\n\t      this.volts.push(0.0);\n\t      this.args.push(arg);\n\t      this.cols.push(color);\n\t      this.lws.push(width);\n\t      this.dashes.push(dash);\n\t      this.v_olds.push(0.0);\n\t  }\n\t  \n      }\n\n\n      \n      \n      class CompoundEye {\n\t  constructor (context, left, right, bottom, top) {\n\t      this.ctx = context\n\t      \n\t      this.left = left;\n\t      this.right = right;\n\t      this.top = top;\n\t      this.bottom = bottom;\n\t      this.midcolor = coolwarm_cm(.5);\n\n\t      this.ang = 0.0;\n\n\t      this.r_angs = [];\n\n\t      \/\/ eye parameters\n\t      this.nfacets_slider=document.getElementById(\"nfacets_slider\");\n\t      this.nfacets_display=document.getElementById(\"nfacets_slider_display\");\n\t      this.nfacets=8;\n\t      this.ioang_slider=document.getElementById(\"ioang_slider\");\n\t      this.ioang_display=document.getElementById(\"ioang_slider_display\");\n\t      this.ioang=5;\n\t      this.delay_slider=document.getElementById(\"delay_slider\");\n\t      this.delay_display=document.getElementById(\"delay_slider_display\");\n\t      this.delay=50;\n\t      \/\/ this.rrhab_slider=document.getElementById(\"rrhab_slider\");\n\t      \/\/ this.rrhab_display=document.getElementById(\"rrhab_slider_display\");\n\n\t      this.rcornea=240\n\t      this.rrhab = 100\n\n\t      this.v_vals = [];\n\t      for (var i=0; i<20; i++) {\n\t\t  this.v_vals[i] = new CircularBuffer();\n\t      }\n\n\t      \/\/ sin parameters\n\t      this.sf_slider=document.getElementById(\"sf_slider\");\n\t      this.sf_display=document.getElementById(\"sf_slider_display\");\n\t      this.sf = 2;\n\t      this.tf_slider=document.getElementById(\"tf_slider\");\n\t      this.tf_display=document.getElementById(\"tf_slider_display\");\n\t      this.tf = 0;\n\t      this.contrast_slider=document.getElementById(\"contrast_slider\");\n\t      this.contrast_display=document.getElementById(\"contrast_slider_display\");\n\t      this.contrast = 0.5;\n\n\t      this.tot_ang = 1.00*Math.PI;\n\t      this.pix_per_rad = 100;\n\t      this.tot_pix = this.tot_ang*this.pix_per_rad;\n\t      this.stim = [];\n\t      \n\t      \/\/ eye and stim center\n\t      this.cx = Math.round((this.right-this.left)\/2);\n\t      \/\/ this.cy = this.top-this.rcornea;\n\t      this.cy = this.top;\n\n\t      this.update_sliders();\n\n\t  }\n\n\t  draw_sin() {\n\t      var val = .5;\n\t      \n\t      this.width=100;\n\t      this.bot=this.rcornea+100;\n\t      let time = Date.now();\n\t      \n\t      for (var i=0; i<(this.tot_pix); i++) {\n\t      \t  this.ang = i*this.tot_ang\/this.tot_pix;\n\t\n\t\t  val = this.contrast * Math.sin(2*Math.PI * (this.tf*time\/1000 + this.sf*this.ang));\n\t\t  this.stim[i] = val;\n\t\t  \n\t\t  let grayValue = Math.round(((val + 1) \/ 2) * 255); \/\/ Example: Arbitrary shade of gray\n\t\t  this.ctx.strokeStyle = `rgb(${grayValue}, ${grayValue}, ${grayValue})`;\n\t\t  this.ctx.setLineDash([]);\n\t\t  \n\t\t  this.ctx.beginPath();\n\t\t  this.ctx.lineWidth = 5;\n\t      \t  this.ctx.moveTo(this.cx+this.bot*Math.cos(this.ang), this.cy-this.bot*Math.sin(this.ang));\n\t      \t  this.ctx.lineTo(this.cx+(this.bot + this.width)*Math.cos(this.ang), this.cy-(this.bot + this.width)*Math.sin(this.ang))\n\t   \t  this.ctx.stroke();\n\t   \n\t      }\n\t      \n\t  }\n\t \n\t  draw_eye() {\n\t      \/\/ fill the background\n\t      this.ctx.fillStyle = coolwarm_cm(.1);\n\t      this.ctx.fillRect(this.left,this.bottom,this.right-this.left,this.top-this.bottom);\n\t      this.ctx.fillStyle = coolwarm_cm(.2);\n\t      this.ctx.fillRect(this.left+380,this.top, 200,380);\n\t      \n\t      \/\/ draw the facets\n\t      this.ctx.lineWidth = 2;\n\n\t      for (var i=0; i<this.nfacets; i++) {\n\t\t  \/\/ and the photoreceptors\n\t\t  this.ctx.beginPath();\n\t\t  this.ctx.lineWidth = 15;\n\t\t  this.ctx.setLineDash([3,3]);\n\t\t  \n\t\t  this.ang = this.r_angs[i];\n\t\t  this.ctx.strokeStyle = cm_cividis((this.stim[Math.round(this.ang\/Math.PI*this.tot_pix)] + 1)\/2);\n\n\t\t  this.ctx.moveTo(this.cx+this.rrhab*Math.cos(this.ang), this.cy-this.rrhab*Math.sin(this.ang));\n\t\t  this.ctx.lineTo(this.cx+0.7*this.rcornea*Math.cos(this.ang), this.cy-0.7*this.rcornea*Math.sin(this.ang));\n\t\t  this.ctx.stroke();\n\n\t\t  \/\/ and the facets\n\t\t  this.ctx.beginPath();\n\t\t  this.ctx.strokeStyle = \"black\";\n\t\t  this.ctx.lineWidth = 2;\n\t\t  this.ctx.setLineDash([]);\n\t\t  \n\t\t  \/\/ left screening\n\t\t  this.ang = this.r_angs[i] - this.ioang\/2;\n\t\t  this.ctx.moveTo(this.cx+this.rrhab*Math.cos(this.ang), this.cy-this.rrhab*Math.sin(this.ang));\n\t\t  this.ctx.lineTo(this.cx+this.rcornea*Math.cos(this.ang), this.cy-this.rcornea*Math.sin(this.ang));\n\t\t  \n\t\t  \/\/ right_screening\n\t\t  this.ang = this.r_angs[i] + this.ioang\/2;\n\t\t  this.ctx.moveTo(this.cx+this.rrhab*Math.cos(this.ang), this.cy-this.rrhab*Math.sin(this.ang));\n\t\t  this.ctx.lineTo(this.cx+this.rcornea*Math.cos(this.ang), this.cy-this.rcornea*Math.sin(this.ang));\n\t\t  this.ctx.stroke();\n\t      }\n\n\t      \/\/ draw the schematic model\n\t      this.ctx.beginPath();\n\t      this.ctx.strokeStyle = \"black\";\n\t      this.ctx.lineWidth = 2;\n\t      \/\/ in 1\n\t      this.ang = this.r_angs[this.facet1_ind];\n\t      this.ctx.moveTo(this.cx+this.rrhab*Math.cos(this.ang), this.cy-this.rrhab*Math.sin(this.ang));\n\t      this.ctx.lineTo(this.cx+this.rrhab*Math.cos(this.ang), this.cy + 50);\n\t      this.ctx.lineTo(this.cx+this.rrhab*Math.cos(this.ang) + 50, this.cy + 50);\n\t      this.ctx.lineTo(this.cx+this.rrhab*Math.cos(this.ang) + 50, this.cy + 320);\n\t      this.ang = this.r_angs[this.facet2_ind];\n\t      this.ctx.lineTo(this.cx+this.rrhab*Math.cos(this.ang) - 50, this.cy + 50);\n\t      \/\/ in 2\n\t      this.ang = this.r_angs[this.facet2_ind];\n\t      this.ctx.moveTo(this.cx+this.rrhab*Math.cos(this.ang), this.cy-this.rrhab*Math.sin(this.ang));\n\t      this.ctx.lineTo(this.cx+this.rrhab*Math.cos(this.ang), this.cy + 50);\n\t      this.ctx.lineTo(this.cx+this.rrhab*Math.cos(this.ang) - 50, this.cy + 50);\n\t      this.ctx.lineTo(this.cx+this.rrhab*Math.cos(this.ang) - 50, this.cy + 320);\n\t      this.ang = this.r_angs[this.facet1_ind];\n\t      this.ctx.lineTo(this.cx+this.rrhab*Math.cos(this.ang) + 50, this.cy + 50);\n\n\t      \/\/ bottom line for subtraction\n\t      this.ctx.moveTo(this.cx, this.cy+320);\n\t      this.ctx.lineTo(this.cx-50, this.cy+320);\n\t      this.ctx.moveTo(this.cx, this.cy+320);\n\t      this.ctx.lineTo(this.cx+50, this.cy+320);\n\t      this.ctx.moveTo(this.cx, this.cy+320);\n\t      this.ctx.lineTo(this.cx, this.cy+375);\n\t      \n\t      this.ctx.stroke();\n\n\t      this.draw_circle_text(this.cx-55, this.cy+50, 15, \"L\", cm_soapbubble(.6, -2, 1.0));\n\t      this.draw_circle_text(this.cx+55, this.cy+50, 15, \"R\", cm_soapbubble(.6, 0, 1.0));\n\n\t      this.draw_circle_text(this.cx, this.cy+180, 15, '\\u03B4', \"rgb(112,112,112)\");\n\n\t      this.draw_circle_text(this.cx, this.cy+330, 15, '-', \"rgb(112,112,112)\");\n\n\t      this.draw_circle_text(this.cx-55, this.cy+320, 20, \"LR\\u03B4\", cm_soapbubble(.6, -3, 1.0));\n\t      this.draw_circle_text(this.cx+55, this.cy+320, 20, \"RL\\u03B4\", cm_soapbubble(.6, 1.0, 1.0));\n\n\t  }\n\n\n\t  update_sliders() {\n\t      this.nfacets = parseFloat(this.nfacets_slider.value);\n\t      this.nfacets_display.innerHTML=this.nfacets;\n\n\t      this.facet1_ind = Math.floor(this.nfacets\/2 -1);\n\t      this.facet2_ind = this.facet1_ind+1;\n\n\t      this.ioang = parseFloat(this.ioang_slider.value)*Math.PI\/180;\n\t      this.ioang_display.innerHTML=this.ioang_slider.value;\n\n\t      this.offset_ang = 1*Math.PI\/2 - 1*this.nfacets*this.ioang\/2\/\/*Math.PI\/180\n\n\t      for (var i=0; i<this.nfacets; i++)\n\t\t  this.r_angs[i] = (i + 0.5)*this.ioang + this.offset_ang;\n\t      \n\t      this.delay = parseFloat(this.delay_slider.value);\n\t      this.delay_display.innerHTML=this.delay;\n\n\t      this.sf = parseFloat(this.sf_slider.value);\n\t      this.sf_display.innerHTML=this.sf;\n\n\t      this.tf = parseFloat(this.tf_slider.value);\n\t      this.tf_display.innerHTML=this.tf;\n\n\t      this.contrast = parseFloat(this.contrast_slider.value);\n\t      this.contrast_display.innerHTML=this.contrast;\n\n\t  }\n\n\n\t  draw_circle_text(x, y, radius, char, textColor) {\n\t      \/\/ Draw gray circle\n\t      this.ctx.beginPath();\n\t      this.ctx.arc(x, y, radius, 0, Math.PI * 2);\n\t      this.ctx.fillStyle = \"rgb(212,212,212)\";\n\t      this.ctx.fill();\n\n\t      \/\/ Draw character in the middle of the circle\n\t      this.ctx.font = '20px Arial'; \/\/ Adjust font size and family as needed\n\t      this.ctx.fillStyle = textColor; \/\/ Set text color\n\t      this.ctx.textAlign = 'center'; \/\/ Align text horizontally in the middle\n\t      this.ctx.textBaseline = 'middle'; \/\/ Align text vertically in the middle\n\t      this.ctx.fillText(char, x, y);\n}\n\n\t  get_v(r_ind, t=0) {\n\t      if (r_ind==-1) {\n\t\t  r_ind = this.facet1_ind;\n\t      }\n\t      else if (r_ind==-2) {\n\t\t  r_ind = this.facet2_ind;\n\t      }\n\t      else if (r_ind>= this.nfacets) {\n\t\t  r_ind = this.nfacets - 1;\n\t      }\n\t      \n\t      if (t==0) {\n\t\t  return this.v_vals[r_ind].get(0);\n\t      }\n\t      else if (t==-1) {\n\t\t  return this.v_vals[r_ind].get(this.delay);\n\t      }\n\t      else if (t==-2) {\n\t\t  return this.v_vals[r_ind].get(0) * this.v_vals[r_ind + 1].get(this.delay);\n\t      }\n\t      else if (t==-3) {\n\t\t  return this.v_vals[r_ind].get(this.delay) * this.v_vals[r_ind + 1].get(0);\n\t      }\n\t      else if (t==-4) {\n\t\t  return this.v_vals[r_ind].get(this.delay) * this.v_vals[r_ind + 1].get(0) - this.v_vals[r_ind].get(0) * this.v_vals[r_ind + 1].get(this.delay);\n\t      }\n\t  }\n\t  \n\t  update(dt=.02) {\n\t      this.draw_eye();\n\t      this.draw_sin();\n\t      this.update_sliders();\n\n\t      for (var i=0; i<this.nfacets; i++) {\n\t\t  this.v_vals[i].add( this.stim[Math.round(this.r_angs[i]\/Math.PI*this.tot_pix)] );\n\t      }\n\n\t  }\n      }\n\n\n      const plotl1 = 10;\n      const plotl2 = 590;\n      const plotw = 350;\n      const plott1 = 600;\n      const plott2 = 720;\n      const plott3 = 840;\n      const plott4 = 960;\n      const ploth = 100;\n\n      var canvas = document.getElementById(\"canvas_r\");\n      var ctx = canvas.getContext('2d');\n      ctx.fillStyle = coolwarm_cm(.1);\n      ctx.fillRect(0,0,canvas.width, canvas.height);\n\n      var eye = new CompoundEye(ctx, 0, 960, 0, 480);\n\n      var scopes = [];\n\n      \n      var l_scope = new Oscilloscope(ctx, plotl1, plotl1+plotw, plott1, plott1-ploth, \"Left rhabdome\", \"rgb(212,212,212)\");\n      \/\/ left rhab\n      l_scope.add_v(eye, [-2], cm_soapbubble(.6, -2, 1.0), 3);\n      \/\/ right rhab low contrast\n      l_scope.add_v(eye, [-1], cm_soapbubble(.6, 0, 0.3));\n      scopes.push(l_scope);\n      \n      var r_scope = new Oscilloscope(ctx, plotl2, plotl2+plotw, plott1, plott1-ploth, \"Right rhabdome\", \"rgb(212,212,212)\");\n      \/\/ right rhab\n      r_scope.add_v(eye, [-1], cm_soapbubble(.6, 0, 1.0), 3);\n      \/\/ left rhab low contrast\n      r_scope.add_v(eye, [-2], cm_soapbubble(.6, -2, 0.3));\n      scopes.push(r_scope);\n\n      var rd_scope = new Oscilloscope(ctx, plotl1, plotl1+plotw, plott2, plott2-ploth, \"Right delay\", \"rgb(212,212,212)\");\n      \/\/ right delay\n      rd_scope.add_v(eye, [-1, -1], cm_soapbubble(.6, 0, 0.7), 3, [1,10]);\n      \/\/ right low contrast\n      \/\/ rd_scope.add_v(eye, [-2], cm_soapbubble(.6, -2, 0.3));\n      \/\/ left low contrast\n      rd_scope.add_v(eye, [-1], cm_soapbubble(.6, 0, 0.3));\n      scopes.push(rd_scope);\n\n      var ld_scope = new Oscilloscope(ctx, plotl2, plotl2+plotw, plott2, plott2-ploth, \"Left delay\", \"rgb(212,212,212)\");\n      \/\/ left delay\n      ld_scope.add_v(eye, [-2, -1], cm_soapbubble(.6, -2, 0.7), 3, [1,10]);\n      \/\/ right low contrast\n      ld_scope.add_v(eye, [-2], cm_soapbubble(.6, -2, 0.3));\n      \/\/ left low contrast\n      \/\/ ld_scope.add_v(eye, [-1], cm_soapbubble(.6, 0, 0.3));\n      scopes.push(ld_scope);\n\n      var lrd_scope = new Oscilloscope(ctx, plotl1, plotl1+plotw, plott3, plott3-ploth, \"Left x Right delay\", \"rgb(212,212,212)\")\n      \/\/ left x right delay\n      lrd_scope.add_v(eye, [-1, -3], cm_soapbubble(.6, -3, 1.0), 4);\n      \/\/ left rhab low contrast\n      lrd_scope.add_v(eye, [-2], cm_soapbubble(.6, -2, 0.3));\n      \/\/ right delay low contrast\n      lrd_scope.add_v(eye, [-1, -1], cm_soapbubble(.6, 0, 0.3), 3, [1,4]);\n      scopes.push(lrd_scope);\n      \n      var rld_scope = new Oscilloscope(ctx, plotl2, plotl2+plotw, plott3, plott3-ploth, \"Right x Left delay\", \"rgb(212,212,212)\")\n      \/\/ right x left delay\n      rld_scope.add_v(eye, [-1, -2], cm_soapbubble(.6, 1, 1.0), 4);\n      \/\/ right low contrast\n      rld_scope.add_v(eye, [-1], cm_soapbubble(.6, 0, 0.3));\n      \/\/ left delay low contrast\n      rld_scope.add_v(eye, [-2, -1], cm_soapbubble(.6, -2, 0.3), 3, [1,4]);\n      scopes.push(rld_scope);\n      \n      \n      var hrc_scope = new Oscilloscope(ctx, (plotl2+plotl1)\/2, (plotl2+plotl1)\/2+plotw, plott4, plott4-ploth, \"Left x Right delay - Right x Left delay\", \"rgb(212,212,212)\")\n      \/\/ diff\n      hrc_scope.add_v(eye, [-1, -4], \"rgb(50,50,50)\", 4);\n      hrc_scope.add_v(eye, [-1, -2], cm_soapbubble(.6, 1, 0.3), 3);\n      hrc_scope.add_v(eye, [-1, -3], cm_soapbubble(.6, -3, 0.3), 3);\n      scopes.push(hrc_scope);\n      \n      \n      \n      function update() {\n\t  eye.update();\n\t  \n\t  for (let i=0; i<scopes.length ; i++) {\n\t      scopes[i].updates();\n\t  }\n      }\n      \n      setInterval(update, 20);\n\n    \n\n    <\/script>\n  <\/body>\n<\/html>\n\t\t<\/div>\n\t\t\t\t<\/div>\n\t\t\t\t\t<\/div>\n\t\t<\/div>\n\t\t\t\t\t\t\t<\/div>\n\t\t<\/section>\n\t\t\t\t\t\t\t<\/div>\n\t\t","protected":false},"excerpt":{"rendered":"<p>In sensory neurobiology, the components necessary to detect image motion are: 1. at least two light detectors, viewing different regions in space (this is realized by interommatidial angle), 2. a temporal difference (this is the delay of the signals that cross over), and 3. a nonlinear interaction (this is the multiplication of the direct and [&hellip;]<\/p>\n","protected":false},"author":3,"featured_media":0,"parent":4438,"menu_order":0,"comment_status":"closed","ping_status":"closed","template":"","meta":{"footnotes":""},"class_list":["post-4496","page","type-page","status-publish","hentry","entry"],"_links":{"self":[{"href":"https:\/\/faculty.fiu.edu\/~theobald\/wp-json\/wp\/v2\/pages\/4496","targetHints":{"allow":["GET"]}}],"collection":[{"href":"https:\/\/faculty.fiu.edu\/~theobald\/wp-json\/wp\/v2\/pages"}],"about":[{"href":"https:\/\/faculty.fiu.edu\/~theobald\/wp-json\/wp\/v2\/types\/page"}],"author":[{"embeddable":true,"href":"https:\/\/faculty.fiu.edu\/~theobald\/wp-json\/wp\/v2\/users\/3"}],"replies":[{"embeddable":true,"href":"https:\/\/faculty.fiu.edu\/~theobald\/wp-json\/wp\/v2\/comments?post=4496"}],"version-history":[{"count":14,"href":"https:\/\/faculty.fiu.edu\/~theobald\/wp-json\/wp\/v2\/pages\/4496\/revisions"}],"predecessor-version":[{"id":4520,"href":"https:\/\/faculty.fiu.edu\/~theobald\/wp-json\/wp\/v2\/pages\/4496\/revisions\/4520"}],"up":[{"embeddable":true,"href":"https:\/\/faculty.fiu.edu\/~theobald\/wp-json\/wp\/v2\/pages\/4438"}],"wp:attachment":[{"href":"https:\/\/faculty.fiu.edu\/~theobald\/wp-json\/wp\/v2\/media?parent=4496"}],"curies":[{"name":"wp","href":"https:\/\/api.w.org\/{rel}","templated":true}]}}