var device = "mobile" ; // Multiply the pixels by this amount so that the images are not grainy. A factor of 2 // is good for laptop/desktop devices, but four may be necessary for tablets/phones. var drawRatio = 2 ; var doLogo = false ; var use_ws = false ; var use_ajax = true ; // Physical constants. var SoL = 3e8 ; // Speed of light. var CoE = 1.6e-19 ; // Charge of electron. var pi = Math.PI ; var shifts_per_game = -2 ; var collisions_per_shift = 60 ; // Control over the speed of the game. var collision_delay_min = 800 ; var collision_delay_max = 1000 ; var collision_delay = collision_delay_max ; var collision_breath = 250 ; var nCharsPerName = 32 ; var player_names = ['Peter Higgs' , 'Brian Cox' , 'Rolf Heuer' , 'Fabiola Gionatti', 'Emmy Noether' , 'Niels Bohr' , 'Marie Curie' , 'Carlo Rubbia' , 'Lisa Randall' , 'Maria Goeppert-Meyer' ] ; // Colours. var text_color = 'black' ; var eventDisplay_fillColor = '#000000' ; var collision_matched_color = 'rgba(0,255,0,0.8)' ; var collision_notMatched_color = 'rgba(255,0,0,0.8)' ; // probabilities for each kind of collision. var probability_Higgs = 0.1 ; // Used for the histograms var mass_min = 100 ; var mass_max = 150 ; var histogram_nBins = 25 ; var histogram_drawStyle = 'rect' ; histogram_drawStyle = 'pe' ; var histogram_xAxisLAbelFrequency = 5 ; var time_animate_histogram = 5000 ; //var time_animate_histogram = 500 ; // Used for the heartbeat (eg for drawing animations). var delay = 50 ; var delay_animate_histogram = 50 ; var delay_enable_click = 500 ; var delay_drawStep = 50 ; // Dimensions of the detector and canvas // "S" refers to the detector. // Sr is the maximal width of the detector in real life (eg cavern walls). // SR is the maximal width of the detector on the canvas. // cw and ch and canvas width and height. var Sr = 10 ; var SR = 375*drawRatio ; var cw = 2*SR ; var ch = 2*SR ; // Tweaks for the event display. var particleLineWidthFactor = 0.0025 ; var particleHeadSizeFactor = 0.02 ; var particleLineWidth = particleLineWidthFactor*cw ; var particleHeadSize = particleHeadSizeFactor*cw ; // Number of slices in R and phi for the cells. var NR = 50 ; var NPhi = 250 ; var cellSizeR = Sr/NR ; var cellSizePhi = 2*pi/NPhi ; // Functions used to map real coordinates to canvas coordinates. var xMax = Sr ; var yMax = Sr ; // Settings for the jets: // How much spread the jet is allowed to have per track var B1_0 = 0.10 ; var B2_0 = 0.08 ; // Colours for the particles: var track_color = 'rgb(200,200,200)' ; function team_object(title, color, box_x, box_y, box_w, box_h){ this.title = title ; this.color = color ; this.nSigma = 0 ; this.apply_style = function(){ // Just change some colours to make everything "branded". // This should probably be changed to something cooler later on. // Also the border widths should be stored in settings.js. document.body.style.background = this.color ; Get('div_gameWrapper').style.border = '9px solid ' + this.color ; Get('div_teamname' ).style.backgroundColor = this.color ; Get('div_header' ).style.border = '1px solid ' + this.color ; Get('div_footer' ).color = this.color ; if(canvas){ canvas.style.borderTop = '9px solid ' + this.color ; canvas.style.borderBottom = '9px solid ' + this.color ; } } this.box = new experiment_box(box_x, box_y, box_w, box_h) ; this.draw_experiment_box = function(context, image_name){ this.box.draw(context, this.title, this.color, image_name) ; } } function experiment_box(x, y, w, h){ this.x = x ; this.y = y ; this.w = w ; this.h = h ; this.draw = function(context, name, color, image_name){ // This just write some text and an image. At the moment it draws things to the // canvas, but let's not completely discount the idea of using the HTML DOM- it could // be cheaper. context.save() ; context.lineWidth = 0.005*cw ; context.font = 0.07*ch+'px arial' ; context.fillStyle = color ; context.fillRect(x, y, w, h) ; context.strokeRect(x, y, w, h) ; // Hard coded values! These should be changed. context.fillStyle = 'white' context.fillText('Team', x+0.5*w, y+0.075*ch) ; context.fillText(name , x+0.5*w, y+0.15*ch) ; var img = Get(image_name) ; context.drawImage(img, x+0.01*cw, y+h-0.23*ch, drawRatio*img.width, drawRatio*img.height) ; context.restore() ; } this.contains = function(x, y){ x *= drawRatio ; y *= drawRatio ; return (x>=this.x && x<=this.x+this.w && y>=this.y && y<=this.y+this.h) ; } } var teams = [] ; teams['neutral'] = new team_object('CERN' , 'rgb( 0, 0, 0)', -1, -1, 0, 0) ; teams['ATLAS' ] = new team_object('ATLAS', 'rgb(236,103, 29)', 0.05*cw, 0.5*ch, 0.35*cw, 0.4*ch) ; teams['CMS' ] = new team_object('CMS' , 'rgb( 17,133,193)', 0.60*cw, 0.5*ch, 0.35*cw, 0.4*ch) ; // A series of trivial functions to save some typing. The most common mathematcal // functions are aliased here to save typing. function random_color(lower, rnd){ var r = Math.floor(lower + rnd.random()*(255-lower)) ; var g = Math.floor(lower + rnd.random()*(255-lower)) ; var b = Math.floor(lower + rnd.random()*(255-lower)) ; var color = 'rgb('+r+','+g+','+b+')' ; return color ; } function psuedorandom_number_generator(){ this.seed = 1 ; this.a = 1255214 ; this.b = 247256 ; this.set_seed = function(seed){ //if(isNaN(seed)) seed = floor(1e9*random()) ; this.seed = floor(seed) ; } this.random = function(){ this.seed = this.seed*this.a%this.b ; return this.seed/this.b ; } } function min(a,b){ return Math.min(a,b) ; } function max(a,b){ return Math.max(a,b) ; } function abs(x){ return Math. abs(x) ; } function abs(x){ return Math. abs(x) ; } function sqrt(x){ return Math.sqrt(x) ; } function acos(x){ return Math.acos(x) ; } function cos(x){ return Math. cos(x) ; } function sin(x){ return Math. sin(x) ; } function log(x){ return Math. log(x) ; } function exp(x){ return Math. exp(x) ; } function floor(x){ return Math.floor(x) ; } function atan2(y,x){ return Math.atan2(y,x) ; } function pow(x,n){ return Math.pow(x,n) ; } function random(){ return Math.random() ; } // Non standard function that Aidan really needs to check. function random_gaussian(sigma){ return sqrt(-2*sigma*log(random())) ; } // Get and Create HTML elements. These can be removed if they cause confusion. This is // merely my own preference. function Get(id){ return document.getElementById(id) ; } function Create(type){ return document.createElement(type) ; } // The normal AJAX stuff. We can replace this with jQuery if we like. function GetXmlHttpObject(){ if(window.XMLHttpRequest){ // code for IE7+, Firefox, Chrome, Opera, Safari return new XMLHttpRequest() ; } if(window.ActiveXObject){ // code for IE6, IE5 return new ActiveXObject("Microsoft.XMLHTTP") ; } return null ; } function ajax_object(){ this.xmlhttp = GetXmlHttpObject() ; this.send = function(message){ if(message.indexOf('sid=')==-1) message = message + '&sid=' + random() ; this.xmlhttp.open('GET', message, true) ; this.xmlhttp.send(null) ; } } function getParameterByName(name){ // Taken from http://stackoverflow.com/questions/901115/how-can-i-get-query-string-values-in-javascript var match = RegExp('[?&]' + name + '=([^&]*)').exec(window.location.search) ; return match && decodeURIComponent(match[1].replace(/\+/g, ' ')) ; } // Converting real coordinates to canvas coordinates. function X_from_x(x){ return SR + SR*x/xMax ; } function Y_from_y(y){ return SR + SR*y/yMax ; } var touchEvent = false ; function eventDisplayClickMouseDown(evt){ if(touchEvent){ touchEvent = false ; return ; } else{ eventDisplayClick(evt) ; } } function eventDisplayClickTouchStart(evt){ touchEvent = true ; eventDisplayClick(evt) ; } function eventDisplayClick(evt){ // This function detects a click and repsonds appropriately. // Keep careful track of the game state- there may be a bug where the final click of a // shift doesn't trigger an event! if(game.can_click==false) return ; if(game.state=='game_start'){ pick_team(evt) ; } else if(game.state=='shift_start'){ Get('span_shiftNumber').innerHTML = game.shift_counter ; set_footer_toplogy() ; // Start the collision thread to deliver new events collision_thread() ; game.state = 'playing' ; // Make sure we are not paused. Explicitly setting paused=false might be simpler. if(game.paused) game.toggle_pause() ; // Reset everything game.reset_statistics() ; return ; } else if(game.state=='shift_end'){ game.start_shift() ; game.state = 'shift_start' ; } else if(game.paused){ // Wake the game up. Is this counterintuitive for the player? Best ask them! game.toggle_pause() ; return ; } else if(game.state=='playing'){ // Play the game. game.current_shift.trigger.fire() ; } } // Controls to pause the game. function keyDown(evt){ var keyDownID = window.event ? event.keyCode : (evt.keyCode != 0 ? evt.keyCode : evt.which) ; switch(keyDownID){ case 80: // p game.toggle_pause() ; case 32: // Space break ; } } var music = [] ; var current_music = null ; var current_music_index = null ; var music_playing = false ; function create_music_playlist(){ music.push(Get('music_intuit' )) ; music.push(Get('music_Klockworx')) ; current_music_index = 0 ; current_music = music[current_music_index] ; Get('span_current_song').innerHTML = 'Track ' + (current_music_index+1) ; Get('canvas_music_pause').style.display = 'none' ; } function add_music_eventListeners(){ Get('canvas_music_previous' ).addEventListener('click', music_previous ) ; Get('canvas_music_play' ).addEventListener('click', music_play ) ; Get('canvas_music_pause' ).addEventListener('click', music_play ) ; Get('canvas_music_stop' ).addEventListener('click', music_stop ) ; Get('canvas_music_next' ).addEventListener('click', music_next ) ; Get('canvas_music_sound_off' ).addEventListener('click', sounds_mute_off) ; Get('canvas_music_sound_on' ).addEventListener('click', sounds_mute_on ) ; } function draw_music_canvases(){ var types = ['play','pause','stop','previous','next','sound_on','sound_off'] ; for(var i=0 ; iparticle_settings[particle.particle_type].rCutoff) break ; } if(r1) this.response = 1 ; // The colours are arranged in a simple linear gradient. // This may be changed if it turns out a different gradient looks more "natural". var fr = Math.floor(this.fillRgb0[0]+this.response*(this.fillRgb1[0])) ; var fg = Math.floor(this.fillRgb0[1]+this.response*(this.fillRgb1[1])) ; var fb = Math.floor(this.fillRgb0[2]+this.response*(this.fillRgb1[2])) ; // Okay, enough rambling, let's just draw this thing. //context.fillStyle = 'rgb(' + fr + ',' + fg + ',' + fb + ')' ; var xA = 0.5*(this.X1+this.X2) ; var yA = 0.5*(this.Y1+this.Y2) ; var xB = 0.5*(this.X3+this.X4) ; var yB = 0.5*(this.Y3+this.Y4) ; var dX = xA-xB ; var dY = yA-yB ; var cX = 0.5*(xA+xB) ; var cY = 0.5*(yA+yB) ; var xC = cX - dX ; var yC = cY - dY ; var xD = cX + dX ; var yD = cY + dY ; var gradient = context.createLinearGradient(xC,yC,xD,yD) ; gradient.addColorStop(0,'white') ; gradient.addColorStop(0.5,'rgb(' + fr + ',' + fg + ',' + fb + ')') ; gradient.addColorStop(1,'white') ; context.fillStyle = gradient ; context.fill() ; context.restore() ; } this.touch = function(particle_types){ this.is_touched = true ; for(var i=0 ; ithis.phi2) ; context.beginPath() ; context.moveTo(X1, Y1) ; context.arc(CX, CY, R1, this.phi1, this.phi2, direction) ; context.lineTo(X4, Y4) ; context.arc(CX, CY, R2, this.phi2, this.phi1, !direction) ; context.lineTo(X1, Y1) ; if(this.is_touched) context.fill() ; context.stroke() ; context.restore() ; } this.update_segments = function(){ if(this.is_touched){ for(var i=0 ; icollision_parameters.jet_track_pt_threshold) ; for(var i=0 ; iSr*1.2){ break ; } else if(r>particle_settings[this.particle_type].rCutoff){ break ; } } if(rSr*1.2){ break ; } else if(r>particle_settings[this.particle_type].rCutoff){ break ; } } if(r2*pi) phi -= 2*pi ; var u = floor( r/cellSizeR ) ; var v = floor(phi/cellSizePhi) ; if(uparticle_settings[this.particle_type].rCutoff){ break ; } } else if(r>0.3*Sr){ break ; } context.lineTo(X_from_x(xy[0]),Y_from_y(xy[1])) ; } if(doLogo) this.lineWidth *= 2 ; // Set line styles here. Draw the first line. context.lineWidth = 2*this.lineWidth ; context.strokeStyle = 'rgb(255,255,255)' ; context.stroke() ; // Now draw the second line, following the same path. context.lineWidth = this.lineWidth ; context.strokeStyle = this.color ; context.shadowBlur = 5 ; context.shadowColor = this.color ; context.stroke() ; context.restore() ; } } // Simple function to clear the canvas. function clear_canvas(context){ context.fillStyle = eventDisplay_fillColor ; context.fillRect(0,0,cw,ch) ; } // This function draws a detector with no activity in it. This can then be used to speed // up drawing of the detector with activity in it. function make_eventDisplay_base(){ //return ; var canvas_base = Get('canvas_eventDisplay_hidden') ; var context_base = canvas_base.getContext('2d') ; //context_base.drawImage(Get('img_CMSPhoto'), 0, 0) ; context_base.fillStyle = 'rgba(255,255,255,0.25)' ; //context_base.fillRect(0,0,cw,ch) ; detector.draw(context_base) ; } function draw_eventDisplay_base_old(context){ context.drawImage(Get('canvas_eventDisplay_hidden'),0,0) ; } function draw_eventDisplay_base(context){ context.drawImage(Get('img_detectorBase'),0,0,cw,ch) ; } function draw_eventDisplay(collision, context){ // Clear the canvas so we start with a clean black background. clear_canvas(context) ; // Draw the base image. draw_eventDisplay_base(context) ; // For debugging purposes, we can draw a mesh of cells. if(game.underlay_cells) draw_cells(context) ; // Draw the active segments over the top. detector.draw_active_segments(context) ; // Draw the tracks next. So far these are only decorative. collision.draw_tracks(context, -1) ; // Now the jets. These are collections of tracks of the same colour. // decorative so far. collision.draw_jets(context, -1) ; // Draw the main particles last, as we need the player to see these very clearly. if(game.difficulty!='pro'){ collision.draw_main_particles(context, -1) ; } //collision.draw_interaction_point(context) ; // For debugging purposes, we can draw a mesh of cells again, this time on top of the // detector components and particles. if(game.overlay_cells) draw_cells(context) ; } function draw_eventDisplay_step_proxy(){ if(game.kill_drawStep){ var time_left = collision_delay*(100-game.draw_step)/100 ; window.setTimeout(collision_breather, time_left) ; return ; } var collision = game.current_shift.current_collision ; if(game.draw_step==0){ var canvas_hidden = Get('canvas_eventDisplay_hidden_activity') ; var context_hidden = canvas_hidden.getContext('2d') ; draw_eventDisplay_step0(collision, context_hidden) ; } else{ draw_eventDisplay_step(collision, context, game.draw_step*game.draw_step_scale) ; } if(game.paused==false){ var nSteps = collision_delay/delay_drawStep ; var dStep = 100/nSteps ; game.draw_step += dStep ; game.draw_step = floor(game.draw_step) ; } if(game.draw_step<=100){ window.setTimeout(draw_eventDisplay_step_proxy, delay_drawStep) ; } else{ window.setTimeout(collision_breather, delay_drawStep) ; } } function draw_eventDisplay_step0(collision, context){ // Clear the canvas so we start with a clean black background. clear_canvas(context) ; // Draw the base image. draw_eventDisplay_base(context) ; // For debugging purposes, we can draw a mesh of cells. if(game.underlay_cells) detector.draw_cells(context) ; // Draw the active segments over the top. detector.draw_active_segments(context) ; // Draw the tracks next. So far these are only decorative. collision.draw_tracks(context, -1) ; // Now the jets. These are collections of tracks of the same colour. // decorative so far. collision.draw_jets(context, -1) ; } function draw_eventDisplay_step(collision, context, step){ context.drawImage(Get('canvas_eventDisplay_hidden_activity'),0,0) ; // For debugging purposes, we can draw a mesh of cells. //if(game.underlay_cells) draw_cells(context) ; // Draw the active segments over the top. //detector.draw_active_segments(context) ; // Draw the main particles last, as we need the player to see these very clearly. if(game.difficulty!='pro'){ collision.draw_main_particles(context, step) ; } // For debugging purposes, we can draw a mesh of cells again, this time on top of the // detector components and particles. //if(game.overlay_cells) draw_cells(context) ; } function draw_game_start_screen(){ // Function to draw the first screen of the game. This is screen is only seen once, // but we call this function many times to animate the spokes. context.save() ; clear_canvas(context) ; spokes.draw(context) ; // Make some nice text boxes. This was written last minute, so it should probably be // tided up and many variables moved to settings.js. context.strokeStyle = '#ffffff' ; context.lineWidth = 2 ; context.fillStyle = text_color ; context.textAlign = 'center' ; context.font = 0.08*ch+'px arial' ; context.fillText('Welcome to the' , 0.5*cw, 0.15*ch) ; context.fillText('Higgs Trigger Game!', 0.5*cw, 0.27*ch) ; context.lineWidth = 1 ; context.font = 0.05*ch+'px arial' ; context.fillText('Choose your team:', 0.5*cw, 0.40*ch) ; context.font = 0.1*ch+'px arial' ; context.fillText('vs', 0.5*cw, 0.7*ch) ; // Draw some text boxes. teams['ATLAS'].draw_experiment_box(context, 'img_ATLAS') ; teams['CMS' ].draw_experiment_box(context, 'img_CMS' ) ; context.restore() ; } function draw_particle_head(context, X, Y, size, color, text, shape){ context.save() ; //var rho = 5 ; var rho = drawRatio*size ; context.beginPath() ; if(abs(shape)<=2){ context.arc(X, Y, rho, 0, 2*pi, true) ; } else if(abs(shape)>=3){ var extra = (shape>0) ? 0 : 0.5 ; shape = abs(shape) ; for(var i=0 ; i.recursively_decay() ; function particle_decay_object(probability, topology){ this.topology = topology ; this.probability = probability ; this.cumulative_probability = 0 ; this.normalised_probability = 0 ; this.normalised = false ; } // This class contains a list of decay modes for a given particle. function particle_decay_collection(name){ // Name of the particle. this.name = name ; // List of decay modes, each containing a probability and a topology. this.decay_modes = [] ; // Complete and unique list of final state topologies after all downstream decays. this.final_state_topologies = [] ; this.add_decay_mode = function(probability, topology){ // This just pushes back a new decay mode. this.decay_modes.push(new particle_decay_object(probability, topology)) ; this.normalise_probabilities() ; } this.normalise_probabilities = function(){ // This adds up all the probabilities of the next level down. // First add all probabilities. var cumulative_probability = 0 ; for(var i=0 ; iself.mass_max) return ; var index = Math.floor((self.nBins+0)*(mass-self.mass_min)/(self.mass_max-self.mass_min)) ; self.bins[index]++ ; self.binsInOrder.push(index) ; } self.add_Higgs = function(){ self.nHiggs++ ; } self.max_height = function(){ // Find the heigh of the highest bin so we can dynamically change the y-axis etc. var result = 0 ; for(var i=0 ; iresult) result = 1*self.bins[i] ; } return result ; } self.nEvents = function(){ // Sum up all the events stored. It might be cheaper to increment a counter as we // fill the histogram. var total = 0 ; for(var i=0 ; i=10 ) di = 2 ; if(mh>=20 ) di = 10 ; if(mh>=100 ) di = 10 ; if(mh>=200 ) di = 25 ; if(mh>=1000) di = 100 ; if(mh>=2000) di = 250 ; for(var i=0 ; i<=mh ; i+=di){ var x = m*w ; var y = h-h*m-ph*i/(1+mh) ; c.moveTo(x-5,y) ; c.lineTo(x+5,y) ; c.fillText(i,0.5*m*w,y) ; } c.stroke() ; c.restore() ; } self.animate_accumulate = function(){ // Yuck, this needs to be tidied up. // Basically add events one by one. // Save a few characters. var c = self.context ; if(self.binsInOrder_index>=self.binsInOrder.length){ // If we're on the final event, update the sigmas. This should be done elsewhere. // But I was under a lot of time pressure. var w = self.canvas.width ; var h = self.canvas.height ; c.font = 0.1*w + 'px arial' ; c.fillStyle = self.color ; if(self.binsInOrder_index==self.binsInOrder.length && self.nSigma<1){ self.nSigma = 3.6 + random()*0.6 ; } if(results_mode=='sigma'){ //c.fillText(self.nSigma.toPrecision(2)+' \u03C3!', 0.7*w, 0.12*h) ; } else{ var percent = 100*Math.exp(-self.nSigma*self.nSigma) ; //c.fillText(percent.toPrecision(2)+'%!', 0.4*w, 0.12*h) ; } if(experiments['ATLAS'].nSigma<1){ Get('button_analyse_CMS_show').style.display = '' ; experiments['ATLAS'].nSigma = self.nSigma ; } else if(experiments['CMS' ].nSigma<1){ Get('button_continue_postShow').style.display = '' ; //Get('button_combine').style.display = '' ; experiments['CMS' ].nSigma = self.nSigma ; } window.setTimeout(spy.animate_fit, delay_animate_histogram) ; return ; } // Update the binsInOrder and redraw the histogram. self.binsInOrder_array[self.binsInOrder[self.binsInOrder_index]]++ ; self.draw(self.binsInOrder_array) ; self.binsInOrder_index++ ; // Send the signal for the next draw call. window.setTimeout(spy.animate_histogram, delay_animate_histogram) ; //self.draw_pistons(self.binsInOrder_index) ; self.draw_gears(self.binsInOrder_index) ; } self.animate_fit_ghost = function(){ // Pretend to perform a fit. // Save a few characters. var s = self.fit_animation_step ; var c = self.context ; var s0 = 0 ; var s1 = 100 ; if(s>s1) return ; var id = 'canvas_histogram_' + self.name + '_hidden' ; var canvas_tmp = Get(id) ; var context_tmp = canvas_tmp.getContext('2d') ; // A handful of useful variables. var w = self.canvas.width ; var h = self.canvas.height ; var m = self.margin ; var pw = w*(1-2*m) ; var ph = h*(1-2*m) ; var mh = self.max_height()+2*sqrt(self.max_height()) ; var envelope = exp(-(s%100)*0.05)*sin(2*pi*s/50.0) ; var bkg = 0 ; c.drawImage(canvas_tmp, 0 ,0) ; if(s==s0){ context_tmp.drawImage(self.canvas, 0 ,0) ; // Find the average bin height for the background. var total = 0 ; var n = 0 ; var max_height = 0 ; var ghost_height = 0 ; for(var i=0 ; imax_height){ max_height = self.bins[i] ; self.max_index = i ; } // Exclude the peak region (cheating, I know). if(self.name=='ATLAS' && i>=10) continue ; if(self.name=='CMS' && i<=15) continue ; if(self.bins[i]>ghost_height){ ghost_height = self.bins[i] ; self.ghost_mass = i ; } total += self.bins[i] ; n++ ; } self.average_bin_content = total/n ; } if(s>=s0 && s<=s1){ var x1 = w*m ; var x2 = w*(1-m) ; bkg = 0*self.average_bin_content ; var y0 = h - h*m - ph*bkg/(1+mh) ; var m0 = self.mass_min + (self.mass_max-self.mass_min)*(self.ghost_mass+0.5)/self.bins.length ; var x0 = m*w + pw*(m0-self.mass_min)/(self.mass_max-self.mass_min) + pw*0.25*envelope ; c.beginPath() ; for(var i=0 ; i<1000 ; ++i){ var x = m*w + pw*i/1000.0 ; var gauss = exp(-(x-x0)*(x-x0)/250.0)*(0.75*self.max_height()) ; var y = h - h*m - ph*(0.5*gauss+bkg)/(1.0+mh) ; if(i%10==0){ c.moveTo(x,y) ; } else if(i%10<5){ c.lineTo(x,y) ; } } c.closePath() ; c.stroke() ; } if(s==s1){ var m0 = self.mass_min + (self.mass_max-self.mass_min)*(self.ghost_mass+0.5)/self.bins.length ; var x0 = m*w + pw*(1+envelope)*(m0-self.mass_min)/(self.mass_max-self.mass_min) ; var gauss = (0.75*self.max_height()-bkg) ; var y = h - 1.5*h*m - ph*(0.5*gauss+bkg)/(1.0+mh) ; self.draw_marks(c, x0, y) ; } self.draw_gears(s) ; self.fit_animation_step++ ; // Send the signal for the next draw call. if(s>=s0 && ss1) return ; var id = 'canvas_histogram_' + self.name + '_hidden' ; var canvas_tmp = Get(id) ; var context_tmp = canvas_tmp.getContext('2d') ; if(s==s0){ context_tmp.drawImage(self.canvas, 0 ,0) ; } else{ // A handful of useful variables. var w = self.canvas.width ; var h = self.canvas.height ; var m = self.margin ; var pw = w*(1-2*m) ; var ph = h*(1-2*m) ; var mh = self.max_height()+2*sqrt(self.max_height()) ; var envelope = exp(-(s%100)*0.05)*sin(2*pi*s/50.0) ; c.drawImage(canvas_tmp, 0 ,0) ; if(s<=s1){ c.lineWidth = 1 ; var x1 = w*m ; var x2 = w*(1-m) ; // Find the final resting point of the line. var y0 = h - h*m - ph*self.average_bin_content/(1+mh) ; // But "wobble" a bit on the way there to make it look like we're thinking. var y = y0 + h*m*envelope ; // Draw the line. c.beginPath() ; c.moveTo(x1, y) ; c.lineTo(x2, y) ; c.closePath() ; c.stroke() ; } } self.draw_gears(s) ; self.fit_animation_step++ ; // Send the signal for the next draw call. if(s300) return ; var id = 'canvas_histogram_' + self.name + '_hidden' ; var canvas_tmp = Get(id) ; var context_tmp = canvas_tmp.getContext('2d') ; // A handful of useful variables. var w = self.canvas.width ; var h = self.canvas.height ; var m = self.margin ; var pw = w*(1-2*m) ; var ph = h*(1-2*m) ; var mh = self.max_height()+2*sqrt(self.max_height()) ; var envelope = exp(-(s%100)*0.05)*sin(2*pi*s/50.0) ; c.drawImage(canvas_tmp, 0 ,0) ; if(s>=200 && s<=300){ var x1 = w*m ; var x2 = w*(1-m) ; var bkg = self.average_bin_content ; var y0 = h - h*m - ph*bkg/(1+mh) ; // Just draw the freaking line this time. c.beginPath() ; c.moveTo(x1, y0) ; c.lineTo(x2, y0) ; c.closePath() ; c.stroke() ; var m0 = self.mass_min + (self.mass_max-self.mass_min)*(self.max_index+0.5)/self.bins.length ; var x0 = m*w + pw*(1+envelope)*(m0-self.mass_min)/(self.mass_max-self.mass_min) ; c.beginPath() ; for(var i=0 ; i<1000 ; ++i){ var x = m*w + pw*i/1000.0 ; var gauss = exp(-(x-x0)*(x-x0)/250.0)*(0.75*self.max_height()-bkg) ; var y = h - h*m - ph*(gauss+bkg)/(1.0+mh) ; if(i==0){ c.moveTo(x,y) ; } else{ c.lineTo(x,y) ; } } c.closePath() ; c.stroke() ; } self.draw_gears(s) ; self.fit_animation_step++ ; // Send the signal for the next draw call. if(s>=200 && s<300){ window.setTimeout(self.animate_fit_peak, delay_animate_histogram) ; } else{ Get('button_analyse_both_peak').style.display = 'none' ; Get('button_continue_postPeak').style.display = '' ; } } self.animate_fit_signal = function(){ // Pretend to perform a fit. // Save a few characters. var s = self.fit_animation_step ; var c = self.context ; var id = 'canvas_histogram_' + self.name + '_hidden' ; var canvas_tmp = Get(id) ; var context_tmp = canvas_tmp.getContext('2d') ; // A handful of useful variables. var w = self.canvas.width ; var h = self.canvas.height ; var m = self.margin ; var pw = w*(1-2*m) ; var ph = h*(1-2*m) ; var mh = self.max_height()+2*sqrt(self.max_height()) ; var envelope = exp(-(s%100)*0.05)*sin(2*pi*s/50.0) ; c.drawImage(canvas_tmp, 0 ,0) ; if(s>=300 && s<=400){ var x1 = w*m ; var x2 = w*(1-m) ; var bkg = self.average_bin_content ; var y0 = h - h*m - ph*bkg/(1+mh) ; // Just draw the freaking line this time. c.beginPath() ; c.moveTo(x1, y0) ; c.lineTo(x2, y0) ; c.closePath() ; c.stroke() ; var m0 = self.mass_min + (self.mass_max-self.mass_min)*(self.max_index+0.5)/self.bins.length ; var x0 = m*w + pw*(m0-self.mass_min)/(self.mass_max-self.mass_min) ; c.beginPath() ; for(var i=0 ; i<1000 ; ++i){ var x = m*w + pw*i/1000.0 ; var gauss = exp(-(x-x0)*(x-x0)/250.0)*(self.max_height()-bkg)*(1+envelope) ; var y = h - h*m - ph*(gauss+bkg)/(1.0+mh) ; if(i==0){ c.moveTo(x,y) ; } else{ c.lineTo(x,y) ; } } c.closePath() ; c.stroke() ; } self.draw_gears(s) ; self.fit_animation_step++ ; // Send the signal for the next draw call. if(s>=300 && s<400){ window.setTimeout(self.animate_fit_signal, delay_animate_histogram) ; } else{ if(results_mode=='sigma'){ c.fillText(self.nSigma.toPrecision(2)+' \u03C3!', 0.7*w, 0.12*h) ; } else{ var percent = 100*Math.exp(-self.nSigma*self.nSigma) ; c.fillText(percent.toPrecision(2)+'%!', 0.4*w, 0.12*h) ; } if(spy.current_team_name=='ATLAS'){ Get('button_analyse_CMS_signal').style.display = '' ; } else{ Get('button_continue_postSignal').style.display = '' ; } } } self.draw_gears = function(s){ var c = self.context ; c.save() ; c.fillStyle = self.color ; c.strokeStyle = self.color ; var gears = [] ; var w = self.canvas.width ; var h = self.canvas.height ; gears.push(new gear_object(0.24*w,0.30*h,0.040*w,0.010*w,9,-1)) ; gears.push(new gear_object(0.31*w,0.26*h,0.024*w,0.005*w,8, 1)) ; for(var i=0 ; i