heatmap-builder 0.1.0 → 0.2.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- checksums.yaml +4 -4
- data/.github/workflows/ci.yml +1 -1
- data/.gitignore +3 -0
- data/CHANGELOG.md +33 -2
- data/Gemfile.lock +9 -1
- data/README.md +340 -37
- data/Rakefile +15 -0
- data/bin/generate_examples +234 -0
- data/examples/calendar_blue_ocean.svg +1 -0
- data/examples/calendar_default.svg +1 -0
- data/examples/calendar_github_style.svg +1 -3
- data/examples/calendar_purple_vibes.svg +1 -0
- data/examples/calendar_red_to_green.svg +1 -0
- data/examples/calendar_rounded_corners.svg +1 -0
- data/examples/calendar_rounded_corners_max_radius.svg +1 -0
- data/examples/calendar_sunday_start.svg +1 -3
- data/examples/calendar_warm_sunset.svg +1 -0
- data/examples/calendar_with_outside_cells.svg +1 -3
- data/examples/large_cells.svg +1 -3
- data/examples/linear_blue_ocean.svg +1 -0
- data/examples/linear_github_green.svg +1 -0
- data/examples/linear_neon_gradient.svg +1 -0
- data/examples/linear_purple_vibes.svg +1 -0
- data/examples/linear_red_to_green.svg +1 -0
- data/examples/linear_rounded_corners.svg +1 -0
- data/examples/linear_rounded_corners_max_radius.svg +1 -0
- data/examples/linear_warm_sunset.svg +1 -0
- data/examples/weekly_progress.svg +1 -3
- data/heatmap-builder.gemspec +1 -0
- data/lib/heatmap-builder.rb +47 -3
- data/lib/heatmap_builder/builder.rb +100 -0
- data/lib/heatmap_builder/calendar_heatmap_builder.rb +177 -162
- data/lib/heatmap_builder/color_helpers.rb +170 -0
- data/lib/heatmap_builder/linear_heatmap_builder.rb +49 -103
- data/lib/heatmap_builder/svg_helpers.rb +75 -0
- data/lib/heatmap_builder/value_conversion.rb +64 -0
- data/lib/heatmap_builder/version.rb +1 -1
- metadata +36 -4
- data/examples/generate_samples.rb +0 -114
- data/mise.toml +0 -2
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA256:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: cf666db73abc3356f19fea3869f4a6bb4af21c28a2036f2926c811250636c904
|
4
|
+
data.tar.gz: 6ba3349f270af6ef2216d7501888187aa39e2c11d8b874d084e861cf3f41ef8b
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: 65f06b77aeeda0e45054bbfe402b9d344bf2996a7a5423ca17ab005f79a2aaced93a656eb830294d021b46c2c7ad3c01338553d7d8ece9259d7208ace6ece1b9
|
7
|
+
data.tar.gz: 5fdb4ea1d6c8ff1f7d4966cadd13f6cbeb06916754bdd1e68e553b58632cbc6d49a154f7629f8fc1bb127513dc848831ae87c6d26503f94c29510d8772ee0e08
|
data/.github/workflows/ci.yml
CHANGED
data/.gitignore
CHANGED
data/CHANGELOG.md
CHANGED
@@ -7,10 +7,40 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
|
|
7
7
|
|
8
8
|
## [Unreleased]
|
9
9
|
|
10
|
+
## [0.2.0] - 2025-10-02
|
11
|
+
|
12
|
+
### Added
|
13
|
+
- Support for raw numeric values with automatic score calculation using linear distribution
|
14
|
+
- `values:` parameter as alternative to `scores:` for both linear and calendar heatmaps
|
15
|
+
- `value_min:` and `value_max:` options for explicit boundary control
|
16
|
+
- `value_to_score:` option for custom value-to-score conversion functions
|
17
|
+
- Automatic boundary detection from input data when not explicitly specified
|
18
|
+
- Dynamic color palette generation using OKLCH color space interpolation
|
19
|
+
- Rounded corners support for heatmap cells via `corner_radius` option
|
20
|
+
- Test coverage reporting with SimpleCov
|
21
|
+
- Snapshot testing for visual regression testing
|
22
|
+
- YARD documentation for public API methods
|
23
|
+
- Logarithmic scale example for custom scoring logic
|
24
|
+
- Detailed options documentation in README
|
25
|
+
|
26
|
+
### Changed
|
27
|
+
- Primary API now uses keyword arguments (`scores:`, `values:`, etc.) for clarity and flexibility
|
28
|
+
- Refactored common validation logic into base Builder class
|
29
|
+
- Improved test suite with Minitest specs syntax
|
30
|
+
- Enhanced README with better organization and forward references
|
31
|
+
- Renamed `num_scores` parameter to `max_score` for better clarity in custom scoring functions
|
32
|
+
- Automatic corner radius clamping to valid range
|
33
|
+
|
34
|
+
### Deprecated
|
35
|
+
- `HeatmapBuilder.generate(scores, options)` - use `HeatmapBuilder.build_linear(scores: scores, **options)` instead
|
36
|
+
- `HeatmapBuilder.generate_calendar(scores, options)` - use `HeatmapBuilder.build_calendar(scores: scores, **options)` instead
|
37
|
+
- Old API still works with deprecation warnings for backward compatibility
|
38
|
+
|
10
39
|
## [0.1.0] - 2025-09-19
|
11
40
|
|
41
|
+
Initial release with core heatmap visualization capabilities.
|
42
|
+
|
12
43
|
### Added
|
13
|
-
- Initial release of HeatmapBuilder gem
|
14
44
|
- Linear heatmap generation with `HeatmapBuilder.generate()`
|
15
45
|
- Calendar heatmap generation with `HeatmapBuilder.generate_calendar()`
|
16
46
|
- GitHub-style color schemes and styling
|
@@ -18,5 +48,6 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
|
|
18
48
|
- Support for custom start of week (Monday/Sunday)
|
19
49
|
- SVG output format for perfect scaling
|
20
50
|
|
21
|
-
[Unreleased]: https://github.com/dreikanter/heatmap-builder/compare/v0.
|
51
|
+
[Unreleased]: https://github.com/dreikanter/heatmap-builder/compare/v0.2.0...HEAD
|
52
|
+
[0.2.0]: https://github.com/dreikanter/heatmap-builder/compare/v0.1.0...v0.2.0
|
22
53
|
[0.1.0]: https://github.com/dreikanter/heatmap-builder/releases/tag/v0.1.0
|
data/Gemfile.lock
CHANGED
@@ -1,12 +1,13 @@
|
|
1
1
|
PATH
|
2
2
|
remote: .
|
3
3
|
specs:
|
4
|
-
heatmap-builder (0.
|
4
|
+
heatmap-builder (0.2.0)
|
5
5
|
|
6
6
|
GEM
|
7
7
|
remote: https://rubygems.org/
|
8
8
|
specs:
|
9
9
|
ast (2.4.3)
|
10
|
+
docile (1.4.1)
|
10
11
|
json (2.14.1)
|
11
12
|
language_server-protocol (3.17.0.5)
|
12
13
|
lint_roller (1.1.0)
|
@@ -39,6 +40,12 @@ GEM
|
|
39
40
|
rubocop (>= 1.75.0, < 2.0)
|
40
41
|
rubocop-ast (>= 1.38.0, < 2.0)
|
41
42
|
ruby-progressbar (1.13.0)
|
43
|
+
simplecov (0.22.0)
|
44
|
+
docile (~> 1.1)
|
45
|
+
simplecov-html (~> 0.11)
|
46
|
+
simplecov_json_formatter (~> 0.1)
|
47
|
+
simplecov-html (0.13.2)
|
48
|
+
simplecov_json_formatter (0.1.4)
|
42
49
|
standard (1.51.1)
|
43
50
|
language_server-protocol (~> 3.17.0.2)
|
44
51
|
lint_roller (~> 1.0)
|
@@ -64,6 +71,7 @@ DEPENDENCIES
|
|
64
71
|
heatmap-builder!
|
65
72
|
minitest (~> 5.0)
|
66
73
|
rake (~> 13.0)
|
74
|
+
simplecov (~> 0.22)
|
67
75
|
standard (~> 1.0)
|
68
76
|
|
69
77
|
BUNDLED WITH
|
data/README.md
CHANGED
@@ -8,10 +8,15 @@ A Ruby gem that generates embeddable SVG heatmap visualizations with GitHub-styl
|
|
8
8
|
|
9
9
|
- GitHub-style calendar layouts for date-based data.
|
10
10
|
- Linear heatmaps.
|
11
|
+
- Vector-based output (SVG) for crisp rendering at any resolution.
|
12
|
+
- Optional numeric values displayed in each cell.
|
13
|
+
- **Use pre-calculated scores or raw numeric values** - automatic mapping to color scales.
|
14
|
+
- Custom value-to-score conversion functions for advanced scoring logic.
|
11
15
|
- Parametric everything: customize cell size, spacing, colors, fonts, etc.
|
12
|
-
-
|
16
|
+
- Rounded corners (and circular cells, if you're into that kind of thing).
|
17
|
+
- Dynamic palette generation from two colors or manually-specified colors.
|
18
|
+
- OKLCH color interpolation for clean color transitions and perceptual uniformity.
|
13
19
|
- **Zero dependencies.**
|
14
|
-
- Responsive: SVG format scales perfectly at any size
|
15
20
|
|
16
21
|
## Installation
|
17
22
|
|
@@ -38,10 +43,10 @@ require 'heatmap-builder'
|
|
38
43
|
|
39
44
|
# Generate SVG for daily scores
|
40
45
|
scores = [0, 1, 2, 3, 4, 5, 2, 1]
|
41
|
-
svg = HeatmapBuilder.
|
46
|
+
svg = HeatmapBuilder.build_linear(scores: scores)
|
42
47
|
|
43
48
|
# In a Rails view
|
44
|
-
<%= raw HeatmapBuilder.
|
49
|
+
<%= raw HeatmapBuilder.build_linear(scores: @daily_scores) %>
|
45
50
|
```
|
46
51
|
|
47
52
|

|
@@ -57,64 +62,362 @@ scores_by_date = {
|
|
57
62
|
# ... more dates
|
58
63
|
}
|
59
64
|
|
60
|
-
svg = HeatmapBuilder.
|
65
|
+
svg = HeatmapBuilder.build_calendar(scores: scores_by_date)
|
61
66
|
```
|
62
67
|
|
63
|
-
|
68
|
+

|
69
|
+
|
70
|
+
### Linear Heatmap Options
|
71
|
+
|
72
|
+
You must provide either `scores:` or `values:` (but not both). All other options are optional keyword arguments with sensible defaults.
|
73
|
+
|
74
|
+
**Data options:**
|
75
|
+
|
76
|
+
- `scores` - Array of pre-calculated scores (integers from 0 to number of colors minus 1). Required if `values` is not provided.
|
77
|
+
- `values` - Array of arbitrary numeric values to be automatically mapped to scores. Required if `scores` is not provided. See [Using Raw Values Instead of Scores](#using-raw-values-instead-of-scores).
|
78
|
+
|
79
|
+
**Value-to-score conversion options** (only used with `values:`):
|
80
|
+
|
81
|
+
- `value_min` - Minimum boundary for value-to-score mapping. Defaults to the minimum value in your data.
|
82
|
+
- `value_max` - Maximum boundary for value-to-score mapping. Defaults to the maximum value in your data.
|
83
|
+
- `value_to_score` - Custom callable for value-to-score conversion. Receives `value:`, `index:`, `min:`, `max:`, `max_score:` parameters and must return an integer between 0 and `max_score`. See [Custom Scoring Logic](#custom-scoring-logic) for details.
|
84
|
+
|
85
|
+
**Appearance options:**
|
86
|
+
|
87
|
+
- `cell_size` - Size of each square in pixels. Defaults to 10.
|
88
|
+
- `cell_spacing` - Space between squares in pixels. Defaults to 1.
|
89
|
+
- `font_size` - Font size for score text in pixels. Defaults to 8.
|
90
|
+
- `border_width` - Border width around each cell in pixels. Defaults to 1.
|
91
|
+
- `corner_radius` - Corner radius for rounded cells. Must be between 0 (square corners) and `floor(cell_size/2)` (circular cells). Values outside this range are automatically clamped. Defaults to 0.
|
92
|
+
- `text_color` - Color of score text as a hex string. Defaults to `"#000000"` (black).
|
93
|
+
|
94
|
+
**Color options:**
|
95
|
+
|
96
|
+
- `colors` - Color palette for the heatmap. Can be a predefined palette constant (e.g., `HeatmapBuilder::GITHUB_GREEN`), an array of hex color strings (e.g., `%w[#ebedf0 #9be9a8 #40c463]`), or a hash for OKLCH interpolation (e.g., `{ from: "#ebedf0", to: "#216e39", steps: 5 }`). Defaults to `HeatmapBuilder::GITHUB_GREEN`. See [Predefined Color Palettes](#predefined-color-palettes) and [Dynamic Palettes Generation](#dynamic-palettes-generation).
|
97
|
+
|
98
|
+
### Calendar Heatmap Options
|
99
|
+
|
100
|
+
You must provide either `scores:` or `values:` (but not both). All other options are optional keyword arguments with sensible defaults.
|
101
|
+
|
102
|
+
**Data options:**
|
103
|
+
|
104
|
+
- `scores` - Hash of pre-calculated scores by date (integers from 0 to number of colors minus 1). Keys can be Date objects or date strings (e.g., `'2024-01-01'`). Required if `values` is not provided.
|
105
|
+
- `values` - Hash of arbitrary numeric values by date to be automatically mapped to scores. Keys can be Date objects or date strings. Required if `scores` is not provided. See [Using Raw Values Instead of Scores](#using-raw-values-instead-of-scores).
|
106
|
+
|
107
|
+
**Value-to-score conversion options** (only used with `values:`):
|
108
|
+
|
109
|
+
- `value_min` - Minimum boundary for value-to-score mapping. Defaults to the minimum value in your data.
|
110
|
+
- `value_max` - Maximum boundary for value-to-score mapping. Defaults to the maximum value in your data.
|
111
|
+
- `value_to_score` - Custom callable for value-to-score conversion. Receives `value:`, `date:`, `min:`, `max:`, `max_score:` parameters and must return an integer between 0 and `max_score`. See [Custom Scoring Logic](#custom-scoring-logic) for details.
|
112
|
+
|
113
|
+
**Appearance options:**
|
114
|
+
|
115
|
+
- `cell_size` - Size of each square in pixels. Defaults to 12.
|
116
|
+
- `cell_spacing` - Space between squares in pixels. Defaults to 1.
|
117
|
+
- `font_size` - Font size for labels in pixels. Defaults to 8.
|
118
|
+
- `border_width` - Border width around each cell in pixels. Defaults to 1.
|
119
|
+
- `corner_radius` - Corner radius for rounded cells. Must be between 0 (square corners) and `floor(cell_size/2)` (circular cells). Values outside this range are automatically clamped. Defaults to 0.
|
120
|
+
- `text_color` - Color of label text as a hex string. Defaults to `"#000000"` (black).
|
121
|
+
|
122
|
+
**Color options:**
|
123
|
+
|
124
|
+
- `colors` - Color palette for the heatmap. Can be a predefined palette constant (e.g., `HeatmapBuilder::GITHUB_GREEN`), an array of hex color strings (e.g., `%w[#ebedf0 #9be9a8 #40c463]`), or a hash for OKLCH interpolation (e.g., `{ from: "#ebedf0", to: "#216e39", steps: 5 }`). Defaults to `HeatmapBuilder::GITHUB_GREEN`. See [Predefined Color Palettes](#predefined-color-palettes) and [Dynamic Palettes Generation](#dynamic-palettes-generation).
|
125
|
+
|
126
|
+
**Calendar-specific options:**
|
127
|
+
|
128
|
+
- `start_of_week` - First day of the week. One of `:sunday`, `:monday`, `:tuesday`, `:wednesday`, `:thursday`, `:friday`, `:saturday`. Defaults to `:monday`.
|
129
|
+
- `month_spacing` - Extra horizontal space between months in pixels. Defaults to 5.
|
130
|
+
- `show_month_labels` - Show month names at the top of the calendar. Defaults to `true`.
|
131
|
+
- `show_day_labels` - Show day abbreviations on the left side of the calendar. Defaults to `true`.
|
132
|
+
- `show_outside_cells` - Show cells outside the date range with inactive styling. Defaults to `false`.
|
133
|
+
|
134
|
+
**Internationalization options:**
|
135
|
+
|
136
|
+
- `day_labels` - Array of day abbreviations starting from Sunday (7 elements). Defaults to `%w[S M T W T F S]`. See [I18n](#i18n).
|
137
|
+
- `month_labels` - Array of month abbreviations from January to December (12 elements). Defaults to `%w[Jan Feb Mar Apr May Jun Jul Aug Sep Oct Nov Dec]`. See [I18n](#i18n).
|
138
|
+
|
139
|
+
### Using Raw Values Instead of Scores
|
140
|
+
|
141
|
+
A **score** is an integer (0 to N-1) that maps directly to a color in your palette. For example, with 5 colors, valid scores are 0-4.
|
142
|
+
|
143
|
+
Instead of pre-calculating scores, you can provide raw numeric values (like 45.2, 78, 1000) and let the builder automatically map them to scores using linear distribution:
|
144
|
+
|
145
|
+
```ruby
|
146
|
+
# Linear heatmap with automatic score calculation
|
147
|
+
values = [10, 25, 50, 75, 100]
|
148
|
+
svg = HeatmapBuilder.build_linear(
|
149
|
+
values: values,
|
150
|
+
value_min: 0, # Optional: explicitly set minimum (defaults to actual min)
|
151
|
+
value_max: 100 # Optional: explicitly set maximum (defaults to actual max)
|
152
|
+
)
|
153
|
+
|
154
|
+
# Calendar heatmap with automatic score calculation
|
155
|
+
values_by_date = {
|
156
|
+
Date.new(2024, 1, 1) => 45.2,
|
157
|
+
Date.new(2024, 1, 2) => 78.5,
|
158
|
+
Date.new(2024, 1, 3) => 12.0
|
159
|
+
}
|
160
|
+
|
161
|
+
svg = HeatmapBuilder.build_calendar(
|
162
|
+
values: values_by_date,
|
163
|
+
value_min: 0,
|
164
|
+
value_max: 100
|
165
|
+
)
|
166
|
+
```
|
167
|
+
|
168
|
+
The builder will automatically:
|
169
|
+
- Calculate min/max boundaries from your data if not specified
|
170
|
+
- Map values to color scores using linear distribution
|
171
|
+
- Clamp values outside the boundaries
|
172
|
+
- Handle nil values by treating them as the minimum boundary
|
173
|
+
|
174
|
+
### Custom Scoring Logic
|
175
|
+
|
176
|
+
By default, values are mapped to scores using linear distribution. You can provide a custom value-to-score conversion function for different behaviors like logarithmic scales, exponential curves, or custom thresholds.
|
177
|
+
|
178
|
+
The callable receives these parameters:
|
179
|
+
- `value:` - The current value being converted
|
180
|
+
- `index:` or `date:` - The position in the data (linear heatmaps use `index:`, calendar heatmaps use `date:`)
|
181
|
+
- `min:` - The minimum boundary value
|
182
|
+
- `max:` - The maximum boundary value
|
183
|
+
- `max_score:` - The maximum valid score (color palette length minus 1)
|
184
|
+
|
185
|
+
The function must return an integer between 0 and `max_score`.
|
186
|
+
|
187
|
+
Custom scoring logic - linear distribution example:
|
64
188
|
|
65
189
|
```ruby
|
66
|
-
|
67
|
-
|
68
|
-
|
69
|
-
|
70
|
-
|
71
|
-
|
72
|
-
|
73
|
-
|
74
|
-
|
75
|
-
|
76
|
-
|
77
|
-
|
190
|
+
linear_formula = ->(value:, index:, min:, max:, max_score:) {
|
191
|
+
((value - min) / (max - min) * max_score).round
|
192
|
+
}
|
193
|
+
|
194
|
+
svg = HeatmapBuilder.build_linear(
|
195
|
+
values: [10, 20, 30],
|
196
|
+
value_to_score: linear_formula
|
197
|
+
)
|
198
|
+
```
|
199
|
+
|
200
|
+
Logarithmic scale for data with wide range (e.g., 1 to 10000):
|
201
|
+
|
202
|
+
```ruby
|
203
|
+
logarithmic_formula = ->(value:, index:, min:, max:, max_score:) {
|
204
|
+
return 0 if value <= 0 || min <= 0
|
205
|
+
|
206
|
+
log_value = Math.log10(value)
|
207
|
+
log_min = Math.log10(min)
|
208
|
+
log_max = Math.log10(max)
|
209
|
+
|
210
|
+
((log_value - log_min) / (log_max - log_min) * max_score).round.clamp(0, max_score)
|
78
211
|
}
|
79
212
|
|
80
|
-
svg = HeatmapBuilder.
|
213
|
+
svg = HeatmapBuilder.build_linear(
|
214
|
+
values: [1, 10, 100, 1000, 10000],
|
215
|
+
value_to_score: logarithmic_formula
|
216
|
+
)
|
217
|
+
```
|
218
|
+
|
219
|
+
### Predefined Color Palettes
|
220
|
+
|
221
|
+
#### GitHub Green (Default)
|
222
|
+
|
223
|
+
```ruby
|
224
|
+
HeatmapBuilder.build_linear(scores: scores, colors: HeatmapBuilder::GITHUB_GREEN)
|
225
|
+
```
|
226
|
+
|
227
|
+

|
228
|
+
|
229
|
+
```ruby
|
230
|
+
HeatmapBuilder.build_calendar(scores: calendar_data, colors: HeatmapBuilder::GITHUB_GREEN)
|
231
|
+
```
|
232
|
+
|
233
|
+

|
234
|
+
|
235
|
+
#### Blue Ocean
|
236
|
+
|
237
|
+
```ruby
|
238
|
+
HeatmapBuilder.build_linear(scores: scores, colors: HeatmapBuilder::BLUE_OCEAN)
|
239
|
+
```
|
240
|
+
|
241
|
+

|
242
|
+
|
243
|
+
```ruby
|
244
|
+
HeatmapBuilder.build_calendar(scores: calendar_data, colors: HeatmapBuilder::BLUE_OCEAN)
|
245
|
+
```
|
246
|
+
|
247
|
+

|
248
|
+
|
249
|
+
#### Warm Sunset
|
250
|
+
|
251
|
+
```ruby
|
252
|
+
HeatmapBuilder.build_linear(scores: scores, colors: HeatmapBuilder::WARM_SUNSET)
|
253
|
+
```
|
254
|
+
|
255
|
+

|
256
|
+
|
257
|
+
```ruby
|
258
|
+
HeatmapBuilder.build_calendar(scores: calendar_data, colors: HeatmapBuilder::WARM_SUNSET)
|
81
259
|
```
|
82
260
|
|
83
|
-

|
262
|
+
|
263
|
+
#### Purple Vibes
|
264
|
+
|
265
|
+
```ruby
|
266
|
+
HeatmapBuilder.build_linear(scores: scores, colors: HeatmapBuilder::PURPLE_VIBES)
|
267
|
+
```
|
268
|
+
|
269
|
+

|
270
|
+
|
271
|
+
```ruby
|
272
|
+
HeatmapBuilder.build_calendar(scores: calendar_data, colors: HeatmapBuilder::PURPLE_VIBES)
|
273
|
+
```
|
274
|
+
|
275
|
+

|
276
|
+
|
277
|
+
#### Red to Green
|
278
|
+
|
279
|
+
```ruby
|
280
|
+
HeatmapBuilder.build_linear(scores: scores, colors: HeatmapBuilder::RED_TO_GREEN)
|
281
|
+
```
|
282
|
+
|
283
|
+

|
284
|
+
|
285
|
+
```ruby
|
286
|
+
HeatmapBuilder.build_calendar(scores: calendar_data, colors: HeatmapBuilder::RED_TO_GREEN)
|
287
|
+
```
|
288
|
+
|
289
|
+

|
290
|
+
|
291
|
+
### Dynamic Palettes Generation
|
292
|
+
|
293
|
+
Generate custom color palettes from any two colors using OKLCH color space for superior color interpolation:
|
84
294
|
|
85
295
|
```ruby
|
86
|
-
#
|
87
|
-
|
88
|
-
|
89
|
-
|
90
|
-
|
296
|
+
# Generate a 5-step palette from electric cyan to hot magenta
|
297
|
+
neon_gradient = {
|
298
|
+
from: "#00FFFF",
|
299
|
+
to: "#FF1493",
|
300
|
+
steps: 5
|
91
301
|
}
|
92
302
|
|
93
|
-
svg = HeatmapBuilder.
|
303
|
+
svg = HeatmapBuilder.build_linear(scores: scores, colors: neon_gradient)
|
304
|
+
```
|
305
|
+
|
306
|
+

|
307
|
+
|
308
|
+
The OKLCH color space ensures perceptually uniform color transitions, making gradients appear smooth and natural to the human eye.
|
309
|
+
|
310
|
+
### Rounded Corners
|
311
|
+
|
312
|
+
Both linear and calendar heatmaps support rounded corners using the `corner_radius` option.
|
313
|
+
|
314
|
+
A typical value is around 2 pixels for a subtle rounded effect:
|
315
|
+
|
316
|
+
```ruby
|
317
|
+
# Linear heatmap with rounded corners
|
318
|
+
HeatmapBuilder.build_linear(
|
319
|
+
scores: scores,
|
320
|
+
corner_radius: 2,
|
321
|
+
cell_size: 18
|
322
|
+
)
|
323
|
+
```
|
324
|
+
|
325
|
+

|
326
|
+
|
327
|
+
The `corner_radius` value must be between 0 (square corners) and `floor(cell_size/2)`. Values outside this range are automatically clamped to the valid range (negative values become 0, values exceeding the maximum become `floor(cell_size/2)`). Maximum radius values render circular cells:
|
328
|
+
|
329
|
+
```ruby
|
330
|
+
# Linear heatmap with max radius rounded corners - circular cells
|
331
|
+
HeatmapBuilder.build_linear(
|
332
|
+
scores: scores,
|
333
|
+
corner_radius: 9,
|
334
|
+
cell_size: 18
|
335
|
+
)
|
94
336
|
```
|
95
337
|
|
96
|
-

|
97
339
|
|
98
|
-
|
340
|
+
Calendar heatmap examples:
|
99
341
|
|
100
|
-
|
101
|
-
|
102
|
-
|
342
|
+
```ruby
|
343
|
+
# Calendar heatmap with rounded corners
|
344
|
+
HeatmapBuilder.build_calendar(
|
345
|
+
scores: calendar_data,
|
346
|
+
corner_radius: 2,
|
347
|
+
cell_size: 14
|
348
|
+
)
|
349
|
+
```
|
350
|
+
|
351
|
+

|
352
|
+
|
353
|
+
```ruby
|
354
|
+
# Calendar heatmap with max radius rounded corners - circular cells
|
355
|
+
HeatmapBuilder.build_calendar(
|
356
|
+
scores: calendar_data,
|
357
|
+
corner_radius: 7,
|
358
|
+
cell_size: 14
|
359
|
+
)
|
360
|
+
```
|
361
|
+
|
362
|
+

|
363
|
+
|
364
|
+
### I18n
|
365
|
+
|
366
|
+
Calendar heatmaps support internationalization by customizing the `day_labels` and `month_labels` options:
|
367
|
+
|
368
|
+
```ruby
|
369
|
+
# French calendar
|
370
|
+
HeatmapBuilder.build_calendar(
|
371
|
+
scores: calendar_data,
|
372
|
+
day_labels: %w[D L M M J V S], # Dimanche, Lundi, Mardi, etc.
|
373
|
+
month_labels: %w[Jan Fév Mar Avr Mai Jun Jul Aoû Sep Oct Nov Déc]
|
374
|
+
)
|
375
|
+
|
376
|
+
# German calendar
|
377
|
+
HeatmapBuilder.build_calendar(
|
378
|
+
scores: calendar_data,
|
379
|
+
day_labels: %w[S M D M D F S], # Sonntag, Montag, Dienstag, etc.
|
380
|
+
month_labels: %w[Jan Feb Mär Apr Mai Jun Jul Aug Sep Okt Nov Dez]
|
381
|
+
)
|
382
|
+
|
383
|
+
# Italian calendar
|
384
|
+
HeatmapBuilder.build_calendar(
|
385
|
+
scores: calendar_data,
|
386
|
+
day_labels: %w[D L M M G V S], # Domenica, Lunedì, Martedì, etc.
|
387
|
+
month_labels: %w[Gen Feb Mar Apr Mag Giu Lug Ago Set Ott Nov Dic]
|
388
|
+
)
|
389
|
+
|
390
|
+
# Spanish calendar
|
391
|
+
HeatmapBuilder.build_calendar(
|
392
|
+
scores: calendar_data,
|
393
|
+
day_labels: %w[D L M X J V S], # Domingo, Lunes, Martes, etc.
|
394
|
+
month_labels: %w[Ene Feb Mar Abr May Jun Jul Ago Sep Oct Nov Dic]
|
395
|
+
)
|
396
|
+
```
|
397
|
+
|
398
|
+
The `day_labels` array should contain 7 elements starting from Sunday, and `month_labels` should contain 12 elements for January through December.
|
103
399
|
|
104
400
|
## Development
|
105
401
|
|
106
|
-
After checking out the repo, run `bin/setup` to install dependencies.
|
402
|
+
After checking out the repo, run `bin/setup` to install development dependencies.
|
403
|
+
|
404
|
+
### Running Tests
|
107
405
|
|
108
406
|
```bash
|
109
|
-
|
110
|
-
|
407
|
+
# Run all tests
|
408
|
+
rake test
|
409
|
+
|
410
|
+
# Run tests with code linting
|
411
|
+
rake
|
111
412
|
|
112
|
-
|
413
|
+
# Update test snapshots after making intentional changes to output
|
414
|
+
rake update_snapshots
|
415
|
+
```
|
113
416
|
|
114
417
|
To generate all example SVG files you see in this readme:
|
115
418
|
|
116
419
|
```bash
|
117
|
-
|
420
|
+
bin/generate_examples
|
118
421
|
```
|
119
422
|
|
120
423
|
## Contributing
|
@@ -127,4 +430,4 @@ The gem is available as open source under the terms of the [MIT License](https:/
|
|
127
430
|
|
128
431
|
## Code of Conduct
|
129
432
|
|
130
|
-
Everyone interacting in the HeatmapBuilder project's codebases, issue trackers, chat rooms and mailing lists is expected to follow the [code of conduct](https://github.com/dreikanter/heatmap-builder/blob/
|
433
|
+
Everyone interacting in the HeatmapBuilder project's codebases, issue trackers, chat rooms and mailing lists is expected to follow the [code of conduct](https://github.com/dreikanter/heatmap-builder/blob/main/CODE_OF_CONDUCT.md).
|
data/Rakefile
CHANGED
@@ -13,4 +13,19 @@ rescue LoadError
|
|
13
13
|
# standard is not available
|
14
14
|
end
|
15
15
|
|
16
|
+
desc "Update test snapshots"
|
17
|
+
task :update_snapshots do
|
18
|
+
require "fileutils"
|
19
|
+
snapshots_dir = File.expand_path("test/snapshots", __dir__)
|
20
|
+
|
21
|
+
if Dir.exist?(snapshots_dir)
|
22
|
+
puts "Removing existing snapshots..."
|
23
|
+
FileUtils.rm_rf(Dir["#{snapshots_dir}/*"])
|
24
|
+
end
|
25
|
+
|
26
|
+
puts "Regenerating snapshots..."
|
27
|
+
ENV["UPDATE_SNAPSHOTS"] = "1"
|
28
|
+
Rake::Task[:test].invoke
|
29
|
+
end
|
30
|
+
|
16
31
|
task default: [:standard, :test]
|