Developer Reference
Essential Commands
# Load package for development
devtools::load_all()
# Install all dependencies
devtools::install_deps(dependencies = TRUE)
# Check package (like R CMD check)
devtools::check()
# Run all tests
devtools::test()
# Run specific test file
devtools::test_active_file('tests/testthat/test-plot-customization.R')
# Install package locally
devtools::install()
# Build source tarball
devtools::build()
# Generate function documentation from roxygen2 comments
devtools::document()
# Build README.md from README.Rmd
devtools::build_readme()
# Build all vignettes
devtools::build_vignettes()
# Build a specific vignette
devtools::load_all(); rmarkdown::render('vignettes/loss_functions.Rmd')
# Build package website
pkgdown::build_site()
# Preview website locally
pkgdown::preview_site()
Structure
vistool/
├── DESCRIPTION # Package metadata, dependencies
├── NAMESPACE # Exports (auto-generated by roxygen2)
├── README.Rmd # Source for GitHub README
├── NEWS.md # Changelog
├── CONTRIBUTING.md # This file
├── R/ # R source code
│ ├── as_visualizer.r # main user interface
│ ├── LossFunction.R # Loss functions
│ ├── Objective.R # Objective function classes
│ ├── Optimizer.R # Optimization algorithms
│ ├── Visualizer*.R # Visualization classes
│ └── zzz.R # Package imports
├── man/ # Documentation (auto-generated)
├── man-roxygen/ # Roxygen2 templates
├── tests/testthat/ # Test files
├── vignettes/ # Long-form documentation
└── .github/workflows/ # CI/CD
Core Architecture (Visualization)
Visualizer
├── VisualizerModel # Handles both 1D and 2D
├── VisualizerObj # Handles both 1D and 2D
├── VisualizerLossFuns
└── VisualizerSurface # plotly-based
├── VisualizerSurfaceModel
└── VisualizerSurfaceObj
-
Visualizer
: Base visualization class-
VisualizerLossFuns
: Plotting loss functions with ggplot2 -
VisualizerModel
: Plotting model predictions with ggplot2 -
VisualizerObj
: Plotting objective functions with ggplot2 -
VisualizerSurface*
: Interactive surface plotting with plotly for 2D functions only
-
-
as_visualizer()
: Smart constructor that selects appropriate visualizer
API Design Principles
Constructor vs. Styling Separation
Constructors (as_visualizer()
, VisualizerModel$new()
, etc.) accept only: - Domain objects: tasks, learners, objectives, loss functions - Computational parameters: limits, padding, n_points, grids
Styling is handled separately via: - Instance theme: vis$set_theme(vistool_theme(...))
- Plot theme: vis$plot(theme = list(...), ...)
- Layer styling: vis$add_points(color = "red", size = 3)
Example Workflow
# 1. Create visualizer (computational setup only)
vis <- as_visualizer(task, learner = learner, n_points = 200, padding = 0.1)
# 2. Set styling preferences
vis$set_theme(vistool_theme(palette = "plasma", text_size = 14))
# 3. Add layers with auto colors
vis$add_training_data(color = "auto") # Uses theme palette
vis$add_boundary(color = "red") # Explicit override
# 4. Plot with optional theme override
p <- vis$plot(theme = list(alpha = 0.7))
# 5. Save (uses cached plot for efficiency)
vis$save("plot.png")
Theme System
vistool uses a flexible theme system that separates “what to plot” from “how to plot”:
Theme Hierarchy (Precedence)
-
Layer-specific style (highest):
add_points(color = "red")
-
Plot theme override:
plot(theme = list(palette = "plasma"))
-
Instance theme:
vis$set_theme(vistool_theme(...))
-
Package default (lowest): Set via
options(vistool.theme = ...)
Theme Usage
# Set instance theme
vis$set_theme(vistool_theme(palette = "plasma", text_size = 14, alpha = 0.7))
# Override theme for single plot
vis$plot(theme = list(text_size = 12, theme = "bw"))
# Layer-specific styling (highest precedence)
vis$add_points(data, color = "red", size = 3)
Deferred Rendering
All visualizer classes in vistool follow a standardized plot()
method structure that leverages the deferred rendering architecture. This ensures consistent behavior, proper color resolution, and maintainable code.
Standard Pattern
1. Visualizer.R
The base Visualizer
class provides: - Layer storage: private$store_layer(type, spec)
- Layer retrieval: private$get_layers_by_type(type)
- Theme management: Effective theme resolution in plot()
method - Color resolution: Auto colors resolved using effective theme palette
CAREFUL: Its plot()
method needs to be called by all child classes (VisualizerSurface
, VisualizerModel
, …) to resolve effective theme and prepare rendering.
2. Child classes
These extend the plot()
method from the base Visualizer
class and add methods specific for that type (e.g., add_contours()
for all VisualizerSurface
classes, add_optimization_trace
for Visualizer*Obj
), they inherit from Visualizer
or VisualizerSurface
.
VisualizerLossFuns
, VisualizerModel
, VisualizerObj
, VisualizerSurfaceModel
, VisualizerSurfaceObj
should all follow this pattern:
plot = function(...) {
# 1. ALWAYS call parent first to resolve effective theme and setup
p <- super$plot(...) # or just super$plot(...) for plotly classes
# 2. Render class-specific layers using dedicated private methods
private$render_layer_type_1()
private$render_layer_type_2()
# ... etc
# 3. Resolve auto colors using effective theme
self$resolve_layer_colors()
# 4. Cache and return the plot object (for ggplot2 classes only)
private$.last_plot = p # Cache for save() method
return(p) # omit for plotly classes that modify private$.plot
}
Implementation Examples
ggplot2-based Classes (1D, 2D)
plot = function(...) {
# Call parent to resolve effective theme and prepare rendering
p <- super$plot(...)
# render layers in the order they were added
if (!is.null(private$.layers_to_add)) {
for (layer in private$.layers_to_add) {
if (layer$type == "optimization_trace") {
p = private$render_optimization_trace_layer(p, layer$spec)
} else if (layer$type == "layer_type_2") {
# render layer_type_2
}
# ... handle other layer types
}
}
# Resolve auto colors using effective theme
self$resolve_layer_colors()
# Cache and return plot
private$.last_plot = p
return(p)
}
private = list(
render_optimization_trace_layers = function(plot_obj) {
trace_layers <- private$get_layers_by_type("optimization_trace")
if (length(trace_layers) == 0) {
return(plot_obj)
}
for (trace_spec in trace_layers) {
plot_obj <- private$render_optimization_trace_layer(plot_obj, trace_spec)
}
return(plot_obj)
}
)
plotly-based Classes (Surface)
# Good Example: VisualizerSurfaceObj
plot = function(...) {
# Call parent to resolve effective theme
super$plot(...)
# Render stored layers (modifies private$.plot directly)
private$render_optimization_trace_layers()
private$render_taylor_layers()
private$render_hessian_layers()
# Resolve auto colors using effective theme
self$resolve_layer_colors()
# Cache and return plot
private$.last_plot = private$.plot
return(private$.plot)
}
private = list(
render_optimization_trace_layers = function() {
trace_layers <- private$get_layers_by_type("optimization_trace")
# ... process layers, modify private$.plot directly
# Colors are resolved using private$.effective_theme$palette
}
)
Development Workflow
2. Make Changes
Edit files in R/
, add roxygen2 documentation:
#' @title Brief description
#' @description Detailed description
#' @param x Parameter description
#' @return Return value description
#' @examples
#' # Example code
#' @export
my_function = function(x) {
# Implementation
}
For reusable documentation blocks, consider using templates from man-roxygen/
directory.
3. Update Documentation
devtools::document() # Generate man/ files
devtools::build_readme() # Update README.md
4. Add Tests
Test all public functions using testthat
:
test_that("my feature works", {
result = my_function(input)
expect_equal(result, expected)
})
5. Check Package
devtools::test() # Run tests
devtools::check() # Full package check
You can also check the rendered website:
pkgdown::build_site()
Guidelines
Testing
- Each R file should have corresponding test file
- Use descriptive test names:
test_that("objective evaluation works", ...)
- Test both success and failure cases
- Use appropriate
expect_*()
functions
# Skip tests requiring Python dependencies on CI
skip_on_ci()
# Skip if optional package not available
skip_if_not_installed("plotly")