Refined YAML Data Model for the Gallery Layout Project

As the next step in the project, we want to unify the ideas different groups proposed into one shared data model that everyone can use. For now, that data model will be stored in YAML files. The goal is to make the project more consistent, easier to extend, and easier to evaluate fairly across different layout algorithms.

The strongest ideas from the group work suggest that we should separate the project into three major kinds of information:

  1. The physical gallery wall and layout-generation rules
  2. The scoring model and evaluation rules
  3. The artwork catalog and curator annotations

This leads to a clean three-file structure:

  • gallery.yaml
  • scoring.yaml
  • show.yaml

This is a much better approach than putting everything into one file, because each file has a clear purpose.

gallery.yaml should describe the physical wall, the usable space, and the rules for placing artworks.
scoring.yaml should define what the curator values, including both hard constraints and weighted preferences.
show.yaml should define the artworks themselves, including both factual fields and curator-supplied descriptive tags.

A key improvement over the earlier version is that the evaluator should be driven by data rather than hard-coded assumptions. Instead of scattering “magic numbers” throughout the code, we can place the scoring logic into structured YAML records. That makes it much easier to revise the evaluator as the project evolves.

Suggested Overall File Structure

  1. gallery.yaml

Purpose:
This file describes the physical display environment and the rules the generator must follow.

It should include:

  • gallery and wall identification
  • wall dimensions
  • coordinate system
  • spacing rules
  • whether overlap is allowed
  • whether vertical offsets are allowed
  • whether locked artworks must stay fixed
  • output options such as how many candidate layouts to generate
  1. scoring.yaml

Purpose:
This file describes the scoring philosophy of the curator.

It should include:

  • hard constraints that must be satisfied
  • weighted criteria that are scored
  • preferred target values
  • tolerances
  • importance weights
  • pairwise lookup tables such as theme similarity
  • final score aggregation rules
  1. show.yaml

Purpose:
This file describes the artworks being considered for the wall.

It should include:

  • identification fields
  • artist
  • title
  • year
  • period
  • dimensions
  • image/source references
  • theme tags
  • visual intensity
  • focal importance
  • whether the piece is eligible, required, or locked

A Recommended Design Decision

One especially important improvement is to move from a single theme field to a list of theme tags.

Instead of this:

theme: “portrait”

we should allow this:

theme_tags: [“portrait”, “figure”, “child_portrait”]

This is better because many artworks naturally fit more than one category. A single label is often too restrictive and makes thematic scoring brittle and overly subjective.

Another important design choice is that every scoring criterion should have the same internal structure. For each criterion, we should define:

  • label
  • what it evaluates
  • metric type
  • preferred value
  • tolerance
  • importance
  • algorithm method
  • data dependencies

That makes the evaluator more systematic and much easier to extend.

Detailed Proposed YAML Structures

Proposed structure for gallery.yaml

schema_version: string
gallery:
id: string
name: string
description: string
coordinate_system:
x_origin: "left_edge"
y_origin: "floor_line"
units: "ft"
walls:
- id: string
room: string
label: string
width_ft: float
height_ft: float
usable_x_min_ft: float
usable_x_max_ft: float
usable_y_min_ft: float
usable_y_max_ft: float
centerline_ft: float
light_level: float
viewing_distance_ft: float
notes: stringgenerator:
active_wall_id: string
artwork_count:
min: integer
max: integer
spacing:
ideal_gap_ft: float
min_gap_ft: float
max_gap_ft: float
placement_rules:
no_overlap: boolean
stay_within_wall: boolean
enforce_order_left_to_right: boolean
allow_stacking: boolean
allow_vertical_offset: boolean
snap_to_grid_ft: float
lock_rules:
respect_locked_positions: boolean
respect_required_artworks: boolean
output:
candidate_layouts: integer
keep_top_n: integer
export_html: boolean
export_csv: boolean
show_score_breakdown: boolean

Proposed structure for scoring.yaml

schema_version: string
scoring:
profile_id: string
name: string
description: string normalization:
raw_metric_scale: "0_to_100"
final_score_scale: "0_to_1" hard_constraints:
constraint_name:
enabled: boolean
value: float
penalty_mode: "reject"
penalty_value: float criteria:
criterion_id:
label: string
evaluates: "selection"
metric_type: "target"
raw_value_range: [0, 100]
preferred_value: float
tolerance: float
importance: float
scoring_curve:
in_tolerance_floor: float
out_of_tolerance_decay: float
min_score: float
algorithm:
method: string
params: {}
data_dependencies: [string]
notes: string pairwise_tables:
theme_similarity:
default_similarity: float
pairs:
"portrait|figure_study": float
"landscape|cityscape": float aggregation:
method: "weighted_average"
use_only_nonfailed_criteria: false
include_bonus_terms: true
include_penalty_terms: true adaptive_feedback:
enabled: boolean
allow_weight_updates: boolean
allow_threshold_updates: boolean
learning_rate: float
max_weight_change_per_round: float

Proposed structure for show.yaml

schema_version: string
show:
id: string
title: string
subtitle: string
curator: string
source_museum: string
wall_id: string
notes: string controlled_vocabularies:
theme_tags: [string]
periods: [string]
media: [string] artworks:
- id: string
title: string
artist: string
year: integer
period: string
medium: string
width_ft: float
height_ft: float
orientation: string
image_url: string
source_url: string primary_theme: string
theme_tags: [string]
visual_intensity: float
focal_weight: float
palette_tags: [string]
mood_tags: [string] eligible: boolean
required: boolean
locked: boolean
locked_position:
x_ft: float|null
y_ft: float|null notes: string

Example File 1: gallery.yaml

# gallery.yaml
# Physical setup and generator rules for one gallery wall.schema_version: "2.0"gallery:
id: "vienna_main_gallery"
name: "Vienna Art Wall"
description: "Primary teaching wall used for layout experiments in the gallery visualizer." coordinate_system:
x_origin: "left_edge"
y_origin: "floor_line"
units: "ft" walls:
- id: "W1"
room: "Main Gallery"
label: "North Wall"
width_ft: 10.0
height_ft: 4.5
usable_x_min_ft: 0.0
usable_x_max_ft: 10.0
usable_y_min_ft: 0.0
usable_y_max_ft: 4.5
centerline_ft: 5.0
light_level: 0.60
viewing_distance_ft: 8.0
notes: "Even front lighting; standard viewer approach is centered."generator:
active_wall_id: "W1" artwork_count:
min: 4
max: 10 spacing:
ideal_gap_ft: 0.55
min_gap_ft: 0.30
max_gap_ft: 0.95 placement_rules:
no_overlap: true
stay_within_wall: true
enforce_order_left_to_right: true
allow_stacking: false
allow_vertical_offset: false
snap_to_grid_ft: 0.01 lock_rules:
respect_locked_positions: true
respect_required_artworks: true output:
candidate_layouts: 5000
keep_top_n: 50
export_html: true
export_csv: true
show_score_breakdown: true

Example File 2: scoring.yaml

# scoring.yaml
# Curatorial scoring model.schema_version: "2.0"scoring:
profile_id: "vienna_balanced_curatorial_v1"
name: "Balanced Curatorial Profile"
description: "A target-based scoring profile that rewards balanced, coherent, moderately varied walls." normalization:
raw_metric_scale: "0_to_100"
final_score_scale: "0_to_1" hard_constraints:
min_artworks:
enabled: true
value: 4
penalty_mode: "reject"
penalty_value: 1.0 max_artworks:
enabled: true
value: 10
penalty_mode: "reject"
penalty_value: 1.0 no_overlap:
enabled: true
penalty_mode: "reject"
penalty_value: 1.0 stay_within_wall:
enabled: true
penalty_mode: "reject"
penalty_value: 1.0 min_gap_ft:
enabled: true
value: 0.30
penalty_mode: "reject"
penalty_value: 1.0 max_gap_ft:
enabled: true
value: 0.95
penalty_mode: "reject"
penalty_value: 1.0 respect_locked_positions:
enabled: true
penalty_mode: "reject"
penalty_value: 1.0 criteria: thematic_cohesion:
label: "Thematic Cohesion"
evaluates: "arrangement"
metric_type: "target"
raw_value_range: [0, 100]
preferred_value: 65
tolerance: 20
importance: 0.80
scoring_curve:
in_tolerance_floor: 0.70
out_of_tolerance_decay: 0.02
min_score: 0.00
algorithm:
method: "adjacent_theme_similarity_average"
params:
default_similarity: 0.10
use_primary_theme_first: true
allow_theme_tag_fallback: true
data_dependencies:
- "artworks.primary_theme"
- "artworks.theme_tags"
- "layout.x_ft"
- "pairwise_tables.theme_similarity"
notes: "Moderate cohesion is preferred, not total sameness." artist_variety:
label: "Artist Variety"
evaluates: "selection"
metric_type: "target"
raw_value_range: [0, 100]
preferred_value: 75
tolerance: 15
importance: 0.60
scoring_curve:
in_tolerance_floor: 0.70
out_of_tolerance_decay: 0.02
min_score: 0.00
algorithm:
method: "distinct_artist_ratio"
params:
adjacency_penalty_same_artist: 5
data_dependencies:
- "artworks.artist"
- "layout.x_ft"
notes: "Rewards variety without requiring maximum diversity." spacing_regularity:
label: "Spacing Regularity"
evaluates: "arrangement"
metric_type: "target"
raw_value_range: [0, 100]
preferred_value: 70
tolerance: 12
importance: 0.70
scoring_curve:
in_tolerance_floor: 0.70
out_of_tolerance_decay: 0.025
min_score: 0.00
algorithm:
method: "gap_variance_vs_ideal"
params:
ideal_gap_ft: 0.55
min_gap_ft: 0.30
max_gap_ft: 0.95
out_of_range_gap_penalty: 25
data_dependencies:
- "layout.x_ft"
- "artworks.width_ft"
- "gallery.generator.spacing"
notes: "Regular but not rigid spacing is preferred." visual_balance:
label: "Visual Balance"
evaluates: "both"
metric_type: "target"
raw_value_range: [0, 100]
preferred_value: 80
tolerance: 15
importance: 0.70
scoring_curve:
in_tolerance_floor: 0.70
out_of_tolerance_decay: 0.02
min_score: 0.00
algorithm:
method: "left_right_visual_mass_balance"
params:
mass_formula: "area_times_intensity"
intensity_floor: 0.50
data_dependencies:
- "layout.x_ft"
- "artworks.width_ft"
- "artworks.height_ft"
- "artworks.visual_intensity"
- "gallery.wall.centerline_ft"
notes: "Uses visual mass rather than physical width alone." wall_utilization:
label: "Wall Utilization"
evaluates: "both"
metric_type: "target"
raw_value_range: [0, 100]
preferred_value: 75
tolerance: 15
importance: 0.70
scoring_curve:
in_tolerance_floor: 0.70
out_of_tolerance_decay: 0.02
min_score: 0.00
algorithm:
method: "occupied_width_ratio"
params:
count_internal_gaps: true
count_outer_margins: false
data_dependencies:
- "artworks.width_ft"
- "layout.x_ft"
- "gallery.wall.width_ft"
notes: "The wall should feel used, but not crowded." period_adjacency:
label: "Period Adjacency"
evaluates: "arrangement"
metric_type: "target"
raw_value_range: [0, 100]
preferred_value: 55
tolerance: 25
importance: 0.50
scoring_curve:
in_tolerance_floor: 0.70
out_of_tolerance_decay: 0.02
min_score: 0.00
algorithm:
method: "adjacent_period_similarity_average"
params:
year_bins:
- { max_diff: 15, score: 1.00 }
- { max_diff: 40, score: 0.80 }
- { max_diff: 80, score: 0.55 }
- { max_diff: 150, score: 0.25 }
- { max_diff: 10000, score: 0.05 }
same_period_bonus: 0.10
max_pair_score: 1.00
data_dependencies:
- "artworks.year"
- "artworks.period"
- "layout.x_ft"
notes: "Moderate historical flow is preferred." pairwise_tables:
theme_similarity:
default_similarity: 0.10
pairs:
"portrait|self_portrait": 0.95
"portrait|child_portrait": 0.92
"portrait|figure": 0.80
"portrait|figure_study": 0.90
"figure|abstract_figure": 0.55
"landscape|cityscape": 0.70
"landscape|nature_study": 0.78
"animal|nature_study": 0.60
"religious_study|figure_study": 0.58
"fantasy|abstract_figure": 0.50
"portrait|landscape": 0.20
"animal|portrait": 0.18
"cityscape|religious_study": 0.12 aggregation:
method: "weighted_average"
use_only_nonfailed_criteria: false
include_bonus_terms: false
include_penalty_terms: true adaptive_feedback:
enabled: false
allow_weight_updates: true
allow_threshold_updates: false
learning_rate: 0.15
max_weight_change_per_round: 0.10

Example File 3: show.yaml

# show.yaml
# Artwork catalog and evaluator-ready annotations.schema_version: "2.0"show:
id: "vienna_exhibition_01"
title: "Vienna Exhibition"
subtitle: "Mixed historical and modern works for wall-layout experiments"
curator: "Applied Algorithms class"
source_museum: "Albertina and related reference works"
wall_id: "W1"
notes: "Initial teaching dataset for evaluator experiments." controlled_vocabularies:
theme_tags:
- "animal"
- "nature_study"
- "religious_study"
- "figure_study"
- "figure"
- "portrait"
- "self_portrait"
- "child_portrait"
- "landscape"
- "cityscape"
- "fantasy"
- "abstract_figure" periods:
- "Northern Renaissance"
- "High Renaissance"
- "Impressionism"
- "Neo-Impressionism"
- "Post-Impressionism"
- "Symbolism"
- "Expressionism"
- "Modernism"
- "Avant-Garde" media:
- "drawing"
- "painting"
- "watercolor"
- "mixed_media" artworks: - id: "A1"
title: "Hare"
artist: "Albrecht Dürer"
year: 1502
period: "Northern Renaissance"
medium: "watercolor"
width_ft: 1.7
height_ft: 2.1
orientation: "portrait"
image_url: "https://commons.wikimedia.org/wiki/Special:FilePath/Albrecht_D%C3%BCrer_-_Hare%2C_1502_-_Google_Art_Project.jpg"
source_url: "https://www.albertina.at/en/press/general-information/masterworks-of-the-albertina/"
primary_theme: "animal"
theme_tags: ["animal", "nature_study"]
visual_intensity: 0.40
focal_weight: 0.35
palette_tags: ["earth", "brown", "natural"]
mood_tags: ["observational", "quiet", "precise"]
eligible: true
required: false
locked: false
locked_position:
x_ft: null
y_ft: null
notes: "Can also be treated as a nature study." - id: "A2"
title: "The Great Piece of Turf"
artist: "Albrecht Dürer"
year: 1503
period: "Northern Renaissance"
medium: "watercolor"
width_ft: 1.4
height_ft: 1.8
orientation: "portrait"
image_url: "https://upload.wikimedia.org/wikipedia/commons/f/f6/Albrecht_D%C3%BCrer_-_The_Large_Piece_of_Turf%2C_1503_-_Google_Art_Project.jpg"
source_url: "https://www.albertina.at/en/press/general-information/masterworks-of-the-albertina/"
primary_theme: "nature_study"
theme_tags: ["nature_study", "landscape"]
visual_intensity: 0.35
focal_weight: 0.25
palette_tags: ["green", "earth", "natural"]
mood_tags: ["observational", "quiet"]
eligible: true
required: false
locked: false
locked_position:
x_ft: null
y_ft: null
notes: "Bridge piece between natural studies and landscapes." - id: "A3"
title: "Praying Hands"
artist: "Albrecht Dürer"
year: 1508
period: "Northern Renaissance"
medium: "drawing"
width_ft: 1.2
height_ft: 1.9
orientation: "portrait"
image_url: "https://commons.wikimedia.org/wiki/Special:FilePath/Albrecht_D%C3%BCrer_-_Praying_Hands%2C_1508_-_Google_Art_Project.jpg"
source_url: "https://www.albertina.at/en/press/general-information/masterworks-of-the-albertina/"
primary_theme: "religious_study"
theme_tags: ["religious_study", "figure_study"]
visual_intensity: 0.45
focal_weight: 0.30
palette_tags: ["neutral", "brown"]
mood_tags: ["devotional", "quiet", "focused"]
eligible: true
required: false
locked: false
locked_position:
x_ft: null
y_ft: null
notes: "Can pair moderately with figure-study works." - id: "A4"
title: "Half-Length Figure of an Apostle"
artist: "Leonardo da Vinci"
year: 1493
period: "High Renaissance"
medium: "drawing"
width_ft: 1.6
height_ft: 2.2
orientation: "portrait"
image_url: "https://commons.wikimedia.org/wiki/Special:FilePath/Leonardo_da_Vinci_-_Half-Length_Figure_of_an_Apostle%2C_1493-1495_-_Google_Art_Project.jpg"
source_url: "https://www.albertina.at/en/press/general-information/masterworks-of-the-albertina/"
primary_theme: "figure_study"
theme_tags: ["figure_study", "religious_study"]
visual_intensity: 0.50
focal_weight: 0.40
palette_tags: ["neutral", "brown"]
mood_tags: ["reflective", "classical"]
eligible: true
required: false
locked: false
locked_position:
x_ft: null
y_ft: null
notes: "Historical bridge work." - id: "A5"
title: "The Water Lily Pond"
artist: "Claude Monet"
year: 1917
period: "Impressionism"
medium: "painting"
width_ft: 2.8
height_ft: 1.6
orientation: "landscape"
image_url: ""
source_url: "https://www.albertina.at/en/press/general-information/masterworks-of-the-albertina/"
primary_theme: "landscape"
theme_tags: ["landscape", "nature_study"]
visual_intensity: 0.55
focal_weight: 0.55
palette_tags: ["green", "blue", "light"]
mood_tags: ["calm", "immersive"]
eligible: true
required: false
locked: false
locked_position:
x_ft: null
y_ft: null
notes: "Possible horizontal anchor work." - id: "A6"
title: "Portrait of a Lady with Cape and Hat"
artist: "Gustav Klimt"
year: 1897
period: "Symbolism"
medium: "painting"
width_ft: 1.8
height_ft: 2.4
orientation: "portrait"
image_url: ""
source_url: "https://www.albertina.at/en/press/general-information/masterworks-of-the-albertina/"
primary_theme: "portrait"
theme_tags: ["portrait", "figure"]
visual_intensity: 0.75
focal_weight: 0.70
palette_tags: ["dark", "gold", "ornate"]
mood_tags: ["formal", "intense"]
eligible: true
required: false
locked: false
locked_position:
x_ft: null
y_ft: null
notes: "Visually heavy relative to size." - id: "A7"
title: "The Sin"
artist: "Edvard Munch"
year: 1902
period: "Expressionism"
medium: "painting"
width_ft: 1.5
height_ft: 2.2
orientation: "portrait"
image_url: ""
source_url: "https://www.albertina.at/en/press/general-information/masterworks-of-the-albertina/"
primary_theme: "portrait"
theme_tags: ["portrait", "figure"]
visual_intensity: 0.82
focal_weight: 0.68
palette_tags: ["dark", "red", "green"]
mood_tags: ["psychological", "dramatic"]
eligible: true
required: false
locked: false
locked_position:
x_ft: null
y_ft: null
notes: "Heavy visual emphasis piece." - id: "A8"
title: "Self-portrait in Orange Cloak"
artist: "Egon Schiele"
year: 1913
period: "Expressionism"
medium: "painting"
width_ft: 1.6
height_ft: 2.3
orientation: "portrait"
image_url: ""
source_url: "https://www.albertina.at/en/press/en-exhibitions/monet-to-picasso/"
primary_theme: "self_portrait"
theme_tags: ["self_portrait", "portrait", "figure"]
visual_intensity: 0.78
focal_weight: 0.72
palette_tags: ["orange", "dark", "warm"]
mood_tags: ["tense", "expressive"]
eligible: true
required: false
locked: false
locked_position:
x_ft: null
y_ft: null
notes: "Should score strongly in portrait-oriented walls."

Final Recommendation

For the class, I recommend that we standardize on this division of responsibilities:

  • gallery.yaml controls wall physics and generation rules
  • show.yaml controls artwork facts and artwork annotations
  • scoring.yaml controls what counts as a good layout

That separation will make the system much easier to code, test, revise, and compare across groups.

The most important conceptual improvement is this: the evaluator should no longer depend mainly on hard-coded values inside the program. Instead, it should read a structured curatorial model from YAML. That will let us experiment with different scoring philosophies without constantly rewriting the code.

Leave a Comment

Your email address will not be published. Required fields are marked *

Scroll to Top