This document defines a declarative configuration protocol for building arbitrary OriginPro figures through the originplot skill.
The protocol is designed so an agent or script can translate one config into:
clean data -> Origin objects -> GraphPage/GraphLayer/Plot construction -> export -> OPJU archive
The protocol answers two goals:
- Make arbitrary-figure generation structured and reproducible.
- Decouple figure intent from Python implementation details.
FigureSpec
|- RuntimeSpec
|- DataSpec[]
|- PageSpec
|- LayerSpec[]
|- PlotSpec[]
|- AnnotationSpec[]
|- StyleSpec
|- ExportSpec
FigureSpecdefines one complete figure.LayerSpecdefines one panel / coordinate system.PlotSpecdefines one plot primitive inside a layer.AnnotationSpecdefines legend, panel tags, notes, arrows, colorbars, and labels.StyleSpecdefines global or overridable style.ExportSpecdefines artifacts and QA expectations.
figure:
id: fig1
title: Optional human-readable name
runtime:
show_origin: true
new_project: true
save_project: true
windows_python: auto
project_path: output/opju/fig1.opju
log_path: output/logs/fig1.log
data:
- id: ds_main
source: data/processed/panel_a.csv
format: csv
object: worksheet
roles:
x: time
y: response
group: method
err_low: lower
err_high: upper
preprocess:
dropna: true
sort_by: [time, method]
normalize: null
page:
id: page_main
layout: single
size_mm: [180, 130]
margins_mm: [10, 10, 8, 8]
panel_spacing_mm: [6, 6]
layers:
- id: panel_a
page: page_main
position_mode: grid
grid_cell: [0, 0]
grid_span: [1, 1]
title: null
panel_tag: "(a)"
data_ref: ds_main
x:
title: Time (s)
scale: linear
limits: auto
y:
title: Response (a.u.)
scale: linear
limits: auto
frame:
top: true
right: true
left: true
bottom: true
plots:
- id: plot_a1
layer: panel_a
type: line
data_ref: ds_main
map:
x: time
y: response
group: method
group_style:
mode: color
palette_keys: [blue_main, green_3, red_strong]
uncertainty:
type: band
low: lower
high: upper
annotations:
- id: legend_main
type: legend
layer: panel_a
location: upper_right
frame: false
style:
theme: nature
font_family: Arial
font_size_pt: 8
line_width_pt: 2.0
semantic_palette: nature
template: null
export:
dir_figures: output/figures
dir_data: output/data
dir_opju: output/opju
png:
enabled: true
width_px: 2400
pdf:
enabled: true
svg:
enabled: false
save_clean_data: true
qa:
require_opju: true
require_vector: true
require_nonempty_outputs: truePurpose: control execution environment.
Fields:
| Field | Type | Meaning |
|---|---|---|
show_origin |
bool | Whether Origin GUI is visible |
new_project |
bool | Start a fresh project |
save_project |
bool | Save OPJU at the end |
windows_python |
str | auto, path, or bridge mode |
project_path |
str | Output OPJU path |
log_path |
str | Log file path |
template_dir |
str | Optional template search path |
Purpose: define one logical data source.
Required fields:
| Field | Meaning |
|---|---|
id |
Internal reference name |
source |
File path |
format |
csv/dat/txt/xlsx/npy/npz |
object |
worksheet / matrix / xyz |
roles |
Semantic role mapping |
Optional fields:
preprocess.dropnapreprocess.sort_bypreprocess.renamepreprocess.filterpreprocess.normalizepreprocess.pivotmatrix_map.x_rangematrix_map.y_range
Purpose: define overall figure canvas.
Fields:
| Field | Meaning |
|---|---|
layout |
single, grid, custom |
size_mm |
Figure width/height |
margins_mm |
Outer margins |
panel_spacing_mm |
Horizontal / vertical spacing |
shared_legend |
Optional global legend |
shared_colorbar |
Optional global colorbar |
Purpose: define one panel / coordinate system.
Fields:
| Field | Meaning |
|---|---|
id |
Layer id |
page |
Page reference |
position_mode |
grid or absolute |
grid_cell |
Row/column in grid |
grid_span |
Span in rows/columns |
position_abs |
Absolute [left, top, width, height] if custom |
title |
Optional panel title |
panel_tag |
(a) etc. |
data_ref |
Default dataset |
x / y |
Axis specs |
frame |
Border visibility |
share_with |
Optional shared-axis reference |
Axis spec example:
x:
title: Time (s)
scale: linear
limits: auto
padding_fraction: 0.08
ticks:
major_count: 5
minor_count: 0Purpose: define one plot primitive.
Required fields:
| Field | Meaning |
|---|---|
id |
Plot id |
layer |
Layer reference |
type |
line/scatter/bar/heatmap/contour/area/... |
data_ref |
Dataset reference |
map |
Channel mapping |
Common type values:
line
scatter
bar
grouped_bar
errorbar
heatmap
contour
bubble
area
box
violin
surface
Common channel mappings:
map:
x: time
y: response
group: method
color: condition
size: magnitude
error: sem
z: value
text: labelOptional style fields:
style.colorstyle.line_width_ptstyle.symbolstyle.symbol_size_ptstyle.fillstyle.colormapstyle.opacity
Optional structural fields:
group_styleuncertaintybaselinesmoothingsort_points
Purpose: define non-plot visual elements.
Types:
legend
panel_tag
text
arrow
callout
colorbar
reference_line
reference_band
Example:
annotations:
- id: panel_a_tag
type: panel_tag
layer: panel_a
text: "(a)"
position: upper_left_outside
- id: ref_y0
type: reference_line
layer: panel_a
orientation: horizontal
value: 0
style:
color: neutral_mid
line_style: dashedPurpose: define global visual policy.
Fields:
| Field | Meaning |
|---|---|
theme |
nature, prl, custom, minimal |
template |
Optional Origin template |
font_family |
Arial / Helvetica / ... |
font_size_pt |
Base size |
line_width_pt |
Default line width |
semantic_palette |
Palette name |
color_order |
Optional explicit order |
heatmap_diverging |
Default diverging palette |
legend_frame |
bool |
show_grid |
bool |
Purpose: define artifacts and QA.
Fields:
| Field | Meaning |
|---|---|
dir_figures |
Figure export dir |
dir_data |
Clean data archive dir |
dir_opju |
OPJU dir |
png.enabled |
Export PNG |
png.width_px |
PNG width |
pdf.enabled |
Export PDF |
svg.enabled |
Export SVG |
tiff.enabled |
Export TIFF |
save_clean_data |
Archive normalized data |
qa.* |
Validation requirements |
For theme: nature:
font_family: Arial
font_size_pt: 8
line_width_pt: 2.0
legend_frame: false
show_grid: false
semantic_palette: naturex.limits: auto
y.limits: auto
x.padding_fraction: 0.08
y.padding_fraction: 0.08
frame.top: true
frame.right: true
frame.left: true
frame.bottom: truepng.enabled: true
png.width_px: 2400
pdf.enabled: true
save_clean_data: true
qa.require_opju: true
qa.require_nonempty_outputs: truefigure:
id: line_demo
runtime:
show_origin: true
new_project: true
save_project: true
project_path: output/opju/line_demo.opju
data:
- id: ds_line
source: data/processed/line.csv
format: csv
object: worksheet
roles:
x: time
y: signal
group: method
preprocess:
dropna: true
sort_by: [time, method]
page:
id: page_main
layout: single
size_mm: [160, 120]
margins_mm: [10, 10, 8, 8]
layers:
- id: panel_a
page: page_main
position_mode: grid
grid_cell: [0, 0]
panel_tag: "(a)"
data_ref: ds_line
x: {title: Time (s), scale: linear, limits: auto}
y: {title: Signal (a.u.), scale: linear, limits: auto}
frame: {top: true, right: true, left: true, bottom: true}
plots:
- id: line_main
layer: panel_a
type: line
data_ref: ds_line
map:
x: time
y: signal
group: method
group_style:
mode: color
palette_keys: [blue_main, green_3, red_strong]
annotations:
- id: legend_a
type: legend
layer: panel_a
location: upper_right
frame: false
style:
theme: nature
template: line_basic.otpu
export:
dir_figures: output/figures
dir_data: output/data
dir_opju: output/opju
png: {enabled: true, width_px: 2400}
pdf: {enabled: true}
save_clean_data: true
qa: {require_opju: true, require_vector: true, require_nonempty_outputs: true}figure:
id: nature_2x2
runtime:
show_origin: true
new_project: true
save_project: true
project_path: output/opju/nature_2x2.opju
data:
- id: ds_line
source: data/processed/panel_a_line_trends.csv
format: csv
object: worksheet
roles: {x: time, y: response, group: method, err_low: lower, err_high: upper}
- id: ds_bar
source: data/processed/panel_b_grouped_bar_wide.csv
format: csv
object: worksheet
roles: {x: group, y: value, group: method, error: sem}
- id: ds_scatter
source: data/processed/panel_c_scatter_correlation.csv
format: csv
object: worksheet
roles: {x: x, y: y, group: group, size: size}
- id: ds_heatmap
source: data/processed/panel_d_heatmap_matrix.csv
format: csv
object: matrix
roles: {z: value}
matrix_map:
x_range: [-1.0, 1.0]
y_range: [-1.0, 1.0]
page:
id: page_main
layout: grid
size_mm: [180, 160]
margins_mm: [10, 10, 8, 8]
panel_spacing_mm: [6, 6]
layers:
- id: panel_a
page: page_main
position_mode: grid
grid_cell: [0, 0]
panel_tag: "(a)"
data_ref: ds_line
x: {title: Time (a.u.), scale: linear, limits: auto}
y: {title: Response (a.u.), scale: linear, limits: auto}
frame: {top: true, right: true, left: true, bottom: true}
- id: panel_b
page: page_main
position_mode: grid
grid_cell: [0, 1]
panel_tag: "(b)"
data_ref: ds_bar
x: {title: Group, scale: categorical, limits: auto}
y: {title: Score, scale: linear, limits: auto}
frame: {top: true, right: true, left: true, bottom: true}
- id: panel_c
page: page_main
position_mode: grid
grid_cell: [1, 0]
panel_tag: "(c)"
data_ref: ds_scatter
x: {title: X, scale: linear, limits: auto}
y: {title: Y, scale: linear, limits: auto}
frame: {top: true, right: true, left: true, bottom: true}
- id: panel_d
page: page_main
position_mode: grid
grid_cell: [1, 1]
panel_tag: "(d)"
data_ref: ds_heatmap
x: {title: kx, scale: linear, limits: [-1.0, 1.0]}
y: {title: ky, scale: linear, limits: [-1.0, 1.0]}
frame: {top: true, right: true, left: true, bottom: true}
plots:
- id: p1
layer: panel_a
type: line
data_ref: ds_line
map: {x: time, y: response, group: method}
uncertainty: {type: band, low: lower, high: upper}
group_style: {mode: color, palette_keys: [blue_main, green_3, red_strong]}
- id: p2
layer: panel_b
type: grouped_bar
data_ref: ds_bar
map: {x: group, y: value, group: method, error: sem}
group_style: {mode: fill, palette_keys: [blue_main, green_3, red_strong]}
- id: p3
layer: panel_c
type: scatter
data_ref: ds_scatter
map: {x: x, y: y, group: group, size: size}
group_style: {mode: color, palette_keys: [blue_main, teal, violet]}
- id: p4
layer: panel_d
type: heatmap
data_ref: ds_heatmap
map: {z: value}
style: {colormap: diverging_zero_center}
annotations:
- {id: lg_a, type: legend, layer: panel_a, location: upper_right, frame: false}
- {id: lg_b, type: legend, layer: panel_b, location: upper_right, frame: false}
- {id: cb_d, type: colorbar, layer: panel_d, side: right, title: Signed value}
style:
theme: nature
template: nature_multpanel_2x2.otpu
export:
dir_figures: output/figures
dir_data: output/data
dir_opju: output/opju
png: {enabled: true, width_px: 2600}
pdf: {enabled: true}
svg: {enabled: true}
save_clean_data: true
qa: {require_opju: true, require_vector: true, require_nonempty_outputs: true}Required mapping:
map:
x: <field>
y: <field>Optional:
group: <field>
error: <field>
err_low: <field>
err_high: <field>Required:
x, yOptional:
group, size, color, textRequired:
x, y, groupOptional:
errorRequired for matrix-backed data:
z: valueEither matrix-backed or worksheet-XYZ-backed.
A compliant figurespec_executor.py should do the following:
- Parse YAML.
- Start Origin session from
RuntimeSpec. - Load and normalize all
DataSpecsources. - Create Origin worksheets/matrices.
- Build one
GraphPage. - Build all
GraphLayers from layout spec. - Create plots in each layer.
- Apply axis settings.
- Apply annotations.
- Apply global and local style.
- Export outputs.
- Save OPJU.
- Run QA.
Before success is reported, verify:
1. OPJU exists.
2. At least one raster export exists.
3. If required, at least one vector export exists.
4. Output files are non-empty.
5. Panel labels exist when specified.
6. No panel is blank.
7. Clean data were archived when requested.
- Prefer
FigureSpecfor reusable pipelines and agent execution. - Prefer direct Python for tiny one-off plots.
- Keep style in Origin templates when publication quality matters.
- Use
FigureSpecto describe figure intent, not every low-level Origin quirk.