var jsPsychWebgazerValidate = (function (jspsych) { 'use strict'; const info = { name: "webgazer-validate", parameters: { /** Array of points in [x,y] coordinates */ validation_points: { type: jspsych.ParameterType.INT, default: [ [10, 10], [10, 50], [10, 90], [50, 10], [50, 50], [50, 90], [90, 10], [90, 50], [90, 90], ], array: true, }, /** * Are the validation_points specified as percentages of screen width and height, or the distance in pixels from the center of the screen? * Options are 'percent' and 'center-offset-pixels' */ validation_point_coordinates: { type: jspsych.ParameterType.SELECT, default: "percent", options: ["percent", "center-offset-pixels"], }, /** Tolerance around validation point in pixels */ roi_radius: { type: jspsych.ParameterType.INT, default: 200, }, /** Whether or not to randomize the order of validation points */ randomize_validation_order: { type: jspsych.ParameterType.BOOL, default: false, }, /** Delay before validating after showing a point */ time_to_saccade: { type: jspsych.ParameterType.INT, default: 1000, }, /** Length of time to show each point */ validation_duration: { type: jspsych.ParameterType.INT, default: 2000, }, /** Validation point size in pixels */ point_size: { type: jspsych.ParameterType.INT, default: 20, }, /** If true, then validation data will be shown on the screen after validation is complete */ show_validation_data: { type: jspsych.ParameterType.BOOL, default: false, }, }, }; /** * **webgazer-validate** * * jsPsych plugin for measuring the accuracy and precision of eye gaze predictions. * Intended for use with the Webgazer eye-tracking extension, after the webcam has been initialized with the * `webgazer-init-camera` plugin and calibrated with the `webgazer-calibrate` plugin. * * @author Josh de Leeuw * @see {@link https://www.jspsych.org/plugins/jspsych-webgazer-validate/ webgazer-validate plugin} and * {@link https://www.jspsych.org/overview/eye-tracking/ eye-tracking overview} documentation on jspsych.org */ class WebgazerValidatePlugin { constructor(jsPsych) { this.jsPsych = jsPsych; } trial(display_element, trial) { var trial_data = {}; trial_data.raw_gaze = []; trial_data.percent_in_roi = []; trial_data.average_offset = []; trial_data.validation_points = null; var html = `
`; display_element.innerHTML = html; var wg_container = display_element.querySelector("#webgazer-validate-container"); var points_completed = -1; var val_points = null; var start = performance.now(); // function to end trial when it is time const end_trial = () => { this.jsPsych.extensions.webgazer.stopSampleInterval(); // kill any remaining setTimeout handlers this.jsPsych.pluginAPI.clearAllTimeouts(); // clear the display display_element.innerHTML = ""; // move on to the next trial this.jsPsych.finishTrial(trial_data); }; const validation_display = (pt) => { var pt_html = drawValidationPoint(pt[0], pt[1]); wg_container.innerHTML = pt_html; var pt_dom = wg_container.querySelector(".validation-point"); var br = pt_dom.getBoundingClientRect(); var x = br.left + br.width / 2; var y = br.top + br.height / 2; var pt_start_val = performance.now() + trial.time_to_saccade; var pt_finish = pt_start_val + trial.validation_duration; var pt_data = []; var cancelGazeUpdate = this.jsPsych.extensions["webgazer"].onGazeUpdate((prediction) => { if (performance.now() > pt_start_val) { pt_data.push({ x: prediction.x, y: prediction.y, dx: prediction.x - x, dy: prediction.y - y, t: Math.round(prediction.t - start), }); } }); requestAnimationFrame(function watch_dot() { if (performance.now() < pt_finish) { requestAnimationFrame(watch_dot); } else { trial_data.raw_gaze.push(pt_data); cancelGazeUpdate(); next_validation_point(); } }); }; const next_validation_point = () => { points_completed++; if (points_completed == val_points.length) { validation_done(); } else { var pt = val_points[points_completed]; validation_display(pt); } }; const validate = () => { if (trial.randomize_validation_order) { val_points = this.jsPsych.randomization.shuffle(trial.validation_points); } else { val_points = trial.validation_points; } trial_data.validation_points = val_points; points_completed = -1; //jsPsych.extensions['webgazer'].resume(); this.jsPsych.extensions.webgazer.startSampleInterval(); //jsPsych.extensions.webgazer.showPredictions(); next_validation_point(); }; const show_validation_data = () => { var html = ""; for (var i = 0; i < trial.validation_points.length; i++) { html += drawValidationPoint(trial.validation_points[i][0], trial.validation_points[i][1]); html += drawCircle(trial.validation_points[i][0], trial.validation_points[i][1], 0, 0, trial.roi_radius); for (var j = 0; j < trial_data.raw_gaze[i].length; j++) { html += drawRawDataPoint(trial.validation_points[i][0], trial.validation_points[i][1], trial_data.raw_gaze[i][j].dx, trial_data.raw_gaze[i][j].dy); } } html += '