# AOI analysis workflow

# Overview

This vignette shows a practical AOI workflow for Gazepoint GP3 / Gazepoint Analysis exports using `gp3tools`.

The workflow covers:

1. creating and validating a master table;
2. checking AOI geometry and coding;
3. creating AOI-entry and AOI-window summaries;
4. fitting AOI-window GLMMs;
5. extracting fixation, AOI, and transition features;
6. fitting AOI time-course GAMMs;
7. running model diagnostics and reporting checks.

The vignette chunks are not evaluated during package checks, so the file can serve as a readable workflow template.

The first section uses built-in example data; the optional import chunk shows how to replace those objects with data from a private Gazepoint export folder.

When adapting the code, replace the placeholder paths, AOI labels, time windows, and column names with those used in your own study.

# 1. Load example data or import your own exports

For a quick read-through, start with the lightweight synthetic example data included in the package.

These objects make the later workflow easier to follow because they already use the column names expected by the examples.

    library(gp3tools)

    data("gazepoint_example_master")
    data("gazepoint_example_fixations")
    data("gazepoint_example_aoi_geometry")
    data("gazepoint_example_aoi_windows")

    master <- gazepoint_example_master
    all_fix <- gazepoint_example_fixations
    aoi_geometry <- gazepoint_example_aoi_geometry
    aoi_windows <- gazepoint_example_aoi_windows

For a real study, replace the example objects above by importing a Gazepoint export folder.

This optional chunk creates `all_gaze` and `all_fix`, then rebuilds `master` from the imported gaze rows:

    export_dir <- "C:/Users/YourName/Desktop/gp3_test_exports"
    output_dir <- "C:/Users/YourName/Desktop/gp3_outputs"

    results <- run_gazepoint_workflow(
      export_dir = export_dir,
      output_dir = output_dir,
      prefix = "study1",
      save_plots = TRUE,
      create_report = TRUE
    )

    all_gaze <- results$all_gaze
    all_fix <- results$all_fix
    master <- create_gazepoint_master(all_gaze)

# 2. Validate the master table

    master_audit <- audit_gazepoint_master(master)

    validation <- validate_gazepoint_master(master)

    master_audit$overview
    validation$summary
    validation$checks

Use the validation output before continuing to AOI coding, AOI-window summaries, and model fitting. If you imported real Gazepoint exports, this is the point where you should confirm that the expected participant, trial, time, gaze, fixation, and AOI-related columns were created correctly.

# 3. AOI geometry and coding checks

The example data already includes an `aoi_geometry` object.

For a real study, create or import a geometry table with one row per AOI and stimulus.

A minimal geometry template looks like this:

    aoi_geometry_template <- tibble::tibble(
      media_id = c("stim1", "stim1"),
      aoi = c("logo", "product"),
      x_min = c(0.10, 0.50),
      y_min = c(0.10, 0.50),
      x_max = c(0.30, 0.70),
      y_max = c(0.30, 0.70)
    )

    aoi_geometry_template

Audit AOI geometry:

    aoi_geometry_audit <- audit_gazepoint_aoi_geometry(
      aoi_geometry,
      aoi_col = "aoi",
      stimulus_col = "media_id",
      x_min_col = "x_min",
      y_min_col = "y_min",
      x_max_col = "x_max",
      y_max_col = "y_max",
      screen_x_range = c(0, 1),
      screen_y_range = c(0, 1)
    )

    aoi_geometry_audit$overview
    aoi_geometry_audit$flagged_aois

Audit AOI overlap:

    aoi_overlap_audit <- audit_gazepoint_aoi_overlap(
      aoi_geometry,
      aoi_col = "aoi",
      stimulus_col = "media_id",
      x_min_col = "x_min",
      y_min_col = "y_min",
      x_max_col = "x_max",
      y_max_col = "y_max",
      min_overlap_area = 0,
      min_overlap_prop = 0
    )

    aoi_overlap_audit$overview
    aoi_overlap_audit$flagged_overlaps

Validate observed AOI labels against geometry-derived labels:

    aoi_coding_audit <- audit_gazepoint_aoi_coding_matrix(
      gaze_data = master,
      aoi_geometry = aoi_geometry,
      observed_aoi_col = "aoi_current",
      gaze_x_col = "x",
      gaze_y_col = "y",
      gaze_stimulus_col = "MEDIA_ID",
      sample_id_cols = c("subject", "MEDIA_ID", "trial_global"),
      geometry_aoi_col = "aoi",
      geometry_stimulus_col = "media_id",
      x_min_col = "x_min",
      y_min_col = "y_min",
      x_max_col = "x_max",
      y_max_col = "y_max"
    )

    aoi_coding_audit$overview
    aoi_coding_audit$coding_matrix

Create a visual AOI verification plot:

    aoi_verification_plot <- plot_gazepoint_aoi_verification(
      aoi_geometry = aoi_geometry,
      gaze_data = master,
      geometry_aoi_col = "aoi",
      geometry_stimulus_col = "media_id",
      x_min_col = "x_min",
      y_min_col = "y_min",
      x_max_col = "x_max",
      y_max_col = "y_max",
      gaze_x_col = "x",
      gaze_y_col = "y",
      gaze_stimulus_col = "MEDIA_ID"
    )

    aoi_verification_plot

# 4. AOI entries and AOI-window summaries

The examples assume that each gaze sample has already been assigned to an AOI-state column called `aoi_current`. For your own data, check that the AOI labels used in `target_aoi_values` and `distractor_aoi_values` match your coding scheme.

Create ordered AOI-entry episodes:

    aoi_entries <- summarise_gazepoint_aoi_entries(
      master,
      time_col = "time",
      aoi_col = "aoi_current",
      group_cols = c("subject", "MEDIA_ID", "trial_global")
    )

    dplyr::glimpse(aoi_entries)

Create AOI-window summaries:

    aoi_windows <- summarise_gazepoint_aoi_windows(
      master,
      windows = c(0, 500, 1000, 2000, 5000, 10000),
      time_col = "time",
      aoi_col = "aoi_current",
      subject_col = "subject",
      condition_col = "condition",
      group_cols = c("subject", "MEDIA_ID", "trial_global"),
      target_aoi_values = "AOI 2",
      distractor_aoi_values = c("AOI 0", "AOI 1")
    )

    dplyr::glimpse(aoi_windows)

Inspect target-looking proportions:

    dplyr::select(
      aoi_windows,
      subject,
      condition,
      MEDIA_ID,
      trial_global,
      window_label,
      n_window_samples,
      n_target_samples,
      n_distractor_samples,
      n_valid_denominator_samples,
      target_sample_prop_valid,
      aoi_window_status
    )

# 5. AOI-window GLMM

Use the AOI-window GLMM for predefined confirmatory windows. Before fitting the model, check the AOI-window denominators so that sparse or imbalanced windows are visible.

Audit denominator adequacy:

    aoi_window_denominator_audit <- audit_gazepoint_aoi_window_denominators(
      aoi_windows,
      min_denominator_samples = 5,
      min_valid_denominator_prop = 0.70,
      max_denominator_cv = 0.25,
      max_condition_ratio = 2
    )

    aoi_window_denominator_audit$overview
    aoi_window_denominator_audit$window_summary
    aoi_window_denominator_audit$condition_window_summary

Prepare binomial success/failure data:

    aoi_glmm_data <- prepare_gazepoint_aoi_glmm_data(
      aoi_windows,
      success_col = "n_target_samples",
      denominator = "valid",
      subject_col = "subject",
      condition_col = "condition",
      window_col = "window_label",
      window_start_col = "window_start_ms",
      window_end_col = "window_end_ms",
      min_denominator_samples = 5,
      outcome_label = "target"
    )

    dplyr::count(
      aoi_glmm_data,
      aoi_glmm_condition,
      aoi_glmm_window,
      aoi_glmm_status
    )

Fit the main AOI-window GLMM:

    aoi_glmm_fit <- fit_gazepoint_aoi_window_glmm(
      aoi_glmm_data,
      random_window_slopes = FALSE
    )

    aoi_glmm_fit$model_status
    aoi_glmm_fit$formula
    aoi_glmm_fit$comparison
    aoi_glmm_fit$fixed_effects

Transform AOI proportions into empirical logits for sensitivity models:

    aoi_emp_logit <- transform_gazepoint_aoi_empirical_logit(
      aoi_glmm_data,
      numerator_col = "aoi_glmm_success",
      denominator_col = "aoi_glmm_denominator",
      correction = 0.5
    )

    attr(aoi_emp_logit, "gp3_empirical_logit_overview")

Run model-family sensitivity checks:

    aoi_sensitivity <- fit_gazepoint_aoi_model_sensitivity(
      aoi_glmm_data,
      model_types = c(
        "binomial_glmm",
        "empirical_logit_lmm",
        "proportion_lmm",
        "quasibinomial_glm"
      ),
      include_condition = TRUE,
      include_window = TRUE,
      include_interaction = TRUE,
      random_intercept = TRUE
    )

    aoi_sensitivity$comparison
    aoi_sensitivity$fixed_effects

# 6. AOI, fixation, and transition features

The next helpers create descriptive trial-level and sequence-level features. You do not need every feature for every study; select the summaries that match your research question.

Create transition-ready AOI sequences:

    aoi_sequences <- prepare_gazepoint_aoi_sequences(
      master,
      time_col = "time",
      aoi_col = "aoi_current",
      group_cols = c("subject", "MEDIA_ID", "trial_global")
    )

    dplyr::glimpse(aoi_sequences)

Summarise AOI transitions:

    aoi_transition_summary <- summarise_gazepoint_aoi_transitions(
      master,
      time_col = "time",
      aoi_col = "aoi_current",
      group_cols = c("subject", "MEDIA_ID", "trial_global"),
      target_aoi_values = "AOI 2",
      distractor_aoi_values = c("AOI 0", "AOI 1")
    )

    aoi_transition_summary

Create transition matrices:

    aoi_transition_matrix <- compute_gazepoint_aoi_transition_matrix(
      master,
      time_col = "time",
      aoi_col = "aoi_current",
      group_cols = c("subject", "MEDIA_ID", "trial_global"),
      target_aoi_values = "AOI 2",
      distractor_aoi_values = c("AOI 0", "AOI 1")
    )

    aoi_transition_matrix$count_matrix
    aoi_transition_matrix$probability_matrix
    aoi_transition_matrix$long_table

Create time-varying transition matrices:

    time_varying_transition_matrix <- compute_gazepoint_time_varying_transition_matrix(
      master,
      time_col = "time",
      state_col = "aoi_current",
      group_cols = c("subject", "MEDIA_ID", "trial_global"),
      windows = c(0, 500, 1000, 2000, 5000)
    )

    time_varying_transition_matrix$overview
    time_varying_transition_matrix$transition_summary

Create trial-level AOI features:

    aoi_trial_features <- summarise_gazepoint_aoi_trial_features(
      master,
      time_col = "time",
      aoi_col = "aoi_current",
      group_cols = c("subject", "MEDIA_ID", "trial_global"),
      target_aoi_values = "AOI 2",
      distractor_aoi_values = c("AOI 0", "AOI 1")
    )

    aoi_trial_features

Create trial-level fixation features from Gazepoint fixation exports:

    fixation_trial_features <- summarise_gazepoint_fixation_trials(
      all_fix,
      target_aoi_values = "AOI 2",
      distractor_aoi_values = c("AOI 0", "AOI 1")
    )

    fixation_trial_features

Create fixation-, saccade-, or AOI-contingent aligned data:

    fixation_aligned <- prepare_gazepoint_fixation_aligned_data(
      master,
      time_col = "time",
      participant_col = "subject",
      trial_col = "trial_global",
      aoi_col = "aoi_current",
      target_aoi = "AOI 2",
      fixation_col = "is_fixation",
      alignment_event = "first_fixation_to_target",
      baseline_window = c(-200, 0),
      analysis_window = c(0, 800)
    )

    fixation_aligned$overview
    fixation_aligned$event_table
    fixation_aligned$trial_summary

# 7. Advanced sequence-model preparation

These preparation helpers are optional. Use them when you plan Markov-chain, semi-Markov, or HMM-style sequence analyses; otherwise, this section can be skipped.

Prepare dependency-free sequence-model objects:

    markov_obj <- create_gazepoint_markovchain_object(
      aoi_sequences,
      state_col = "aoi"
    )

    semimarkov_data <- prepare_gazepoint_semimarkov_data(
      aoi_sequences,
      state_col = "aoi",
      time_col = "time"
    )

    hmm_data <- prepare_gazepoint_hmm_data(
      aoi_sequences,
      state_col = "aoi",
      observation_cols = c("x", "y", "pupil")
    )

    markov_obj$overview
    semimarkov_data$overview
    hmm_data$overview

These helpers prepare sequence and transition data for inspection, reporting, export, or specialist modelling packages. They do not directly fit Markov-chain, semi-Markov, or hidden Markov models.

# 8. AOI time-course GAMM

Prepare AOI-GAMM data from an AOI-state column:

    aoi_gamm_data <- prepare_gazepoint_aoi_gamm_data(
      master,
      aoi_col = "aoi_current",
      target_aoi_values = "AOI 2",
      subject_col = "subject",
      condition_col = "condition",
      time_col = "time",
      trial_col = "trial_global",
      time_window = c(0, 2000),
      bin_size_ms = 50,
      denominator = "valid",
      min_denominator_samples = 1,
      outcome_label = "target_aoi"
    )

    dplyr::count(
      aoi_gamm_data,
      .gp3_aoi_gamm_condition,
      .gp3_aoi_gamm_condition_status,
      .gp3_aoi_gamm_status
    )

Fit the AOI time-course GAMM:

    aoi_gamm_fit <- fit_gazepoint_aoi_gamm(
      aoi_gamm_data,
      include_condition = TRUE,
      condition_smooths = TRUE,
      random_subject = TRUE,
      random_subject_time = FALSE,
      time_k = 10
    )

    aoi_gamm_fit$model_status
    aoi_gamm_fit$condition_status
    aoi_gamm_fit$formula_text
    aoi_gamm_fit$diagnostics
    aoi_gamm_fit$smooth_table

Plot observed and fitted AOI trajectories:

    aoi_gamm_plot <- plot_gazepoint_aoi_gamm(
      aoi_gamm_fit,
      n_time_points = 100,
      include_observed = TRUE,
      include_fitted = TRUE,
      show_ci = TRUE
    )

    aoi_gamm_plot

AOI time-course GAMMs are complementary to AOI-window GLMMs. Use AOI-window GLMMs for predefined confirmatory windows and AOI-GAMMs when the research question concerns smooth target-looking trajectories.

# 9. Diagnostics and reporting

Finish by checking the fitted model and collecting reporting-ready summaries. These outputs are intended to support manuscript reporting rather than replace statistical interpretation.

Run model diagnostics:

    aoi_diagnostics <- diagnose_gazepoint_glmm(
      aoi_glmm_fit,
      model_name = "aoi_window_glmm",
      use_dharma = FALSE
    )

    aoi_diagnostics$overview
    aoi_diagnostics$convergence
    aoi_diagnostics$singularity
    aoi_diagnostics$overdispersion

Create model-summary tables:

    aoi_model_summary <- tidy_gazepoint_model_summary(
      aoi_glmm_fit,
      model_name = "aoi_window_glmm",
      exponentiate = TRUE,
      use_dharma = FALSE
    )

    aoi_model_summary$overview
    aoi_model_summary$fixed_effects

Estimate marginal means when `emmeans` is installed:

    aoi_emmeans <- summarise_gazepoint_emmeans(
      aoi_glmm_fit,
      specs = "aoi_glmm_condition",
      by = "aoi_glmm_window",
      model_name = "aoi_window_glmm",
      type = "response"
    )

    aoi_emmeans$overview
    aoi_emmeans$emmeans
    aoi_emmeans$contrasts

Export manuscript-ready tables:

    model_table_files <- export_gazepoint_model_tables(
      model_summary = aoi_model_summary,
      emmeans_summary = aoi_emmeans,
      output_dir = output_dir,
      prefix = "aoi_window_glmm"
    )

    model_table_files

Create a reporting checklist:

    reporting <- create_gazepoint_reporting_checklist(
      data = master,
      objects = list(
        validation = validation,
        aoi_geometry_audit = aoi_geometry_audit,
        aoi_denominator_audit = aoi_window_denominator_audit,
        aoi_model = aoi_glmm_fit,
        diagnostics = aoi_diagnostics
      ),
      analysis_type = "aoi",
      study_title = "Gazepoint AOI study"
    )

    reporting$overview
    reporting$checklist