heatmap-builder 0.1.0 → 0.4.3

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.
@@ -1,128 +0,0 @@
1
- module HeatmapBuilder
2
- class LinearHeatmapBuilder
3
- DEFAULT_OPTIONS = {
4
- cell_size: 10,
5
- cell_spacing: 1,
6
- font_size: 8,
7
- cells_per_row: 7,
8
- border_width: 1,
9
- colors: %w[#ebedf0 #9be9a8 #40c463 #30a14e #216e39]
10
- }.freeze
11
-
12
- def initialize(scores, options = {})
13
- @scores = scores
14
- @options = DEFAULT_OPTIONS.merge(options)
15
- validate_options!
16
- end
17
-
18
- def generate
19
- build_svg
20
- end
21
-
22
- private
23
-
24
- attr_reader :scores, :options
25
-
26
- def validate_options!
27
- raise Error, "scores must be an array" unless scores.is_a?(Array)
28
- raise Error, "cell_size must be positive" unless options[:cell_size] > 0
29
- raise Error, "font_size must be positive" unless options[:font_size] > 0
30
- raise Error, "cells_per_row must be positive" unless options[:cells_per_row] > 0
31
- raise Error, "colors must be an array" unless options[:colors].is_a?(Array)
32
- raise Error, "must have at least 2 colors" unless options[:colors].length >= 2
33
- end
34
-
35
- def build_svg
36
- width = svg_width
37
- height = svg_height
38
-
39
- svg_content = scores.first(options[:cells_per_row]).map.with_index do |score, index|
40
- cell_svg(score, index)
41
- end.join
42
-
43
- <<~SVG
44
- <svg width="#{width}" height="#{height}" xmlns="http://www.w3.org/2000/svg">
45
- #{svg_content}
46
- </svg>
47
- SVG
48
- end
49
-
50
- def cell_svg(score, index)
51
- # Calculate x position - each cell takes cell_size + spacing
52
- x = index * (options[:cell_size] + options[:cell_spacing])
53
- y = 0
54
-
55
- color = score_to_color(score)
56
-
57
- # Create colored square (full cell size)
58
- colored_rect = "<rect x=\"#{x}\" y=\"#{y}\" width=\"#{options[:cell_size]}\" height=\"#{options[:cell_size]}\" fill=\"#{color}\"/>"
59
-
60
- # Create border overlay completely inside the colored square
61
- border_rect = if options[:border_width] > 0
62
- # Inset the border rect by half the stroke width so stroke stays inside
63
- inset = options[:border_width] / 2.0
64
- border_x = x + inset
65
- border_y = y + inset
66
- border_size = options[:cell_size] - options[:border_width]
67
- border_color = darker_color(color)
68
- "<rect x=\"#{border_x}\" y=\"#{border_y}\" width=\"#{border_size}\" height=\"#{border_size}\" fill=\"none\" stroke=\"#{border_color}\" stroke-width=\"#{options[:border_width]}\"/>"
69
- else
70
- ""
71
- end
72
-
73
- # Calculate text position (center of cell)
74
- text_x = x + options[:cell_size] / 2
75
- # For better vertical centering: cell center + font_size * 0.35 (accounts for baseline)
76
- text_y = y + options[:cell_size] / 2 + options[:font_size] * 0.35
77
-
78
- text_element = "<text x=\"#{text_x}\" y=\"#{text_y}\" text-anchor=\"middle\" font-family=\"Arial, sans-serif\" font-size=\"#{options[:font_size]}\" fill=\"#{text_color(color)}\">#{score}</text>"
79
-
80
- "#{colored_rect}#{border_rect}#{text_element}"
81
- end
82
-
83
- def score_to_color(score)
84
- return options[:colors].first if score == 0
85
-
86
- max_color_index = options[:colors].length - 1
87
- # Map score to color index, ensuring we don't exceed available colors
88
- color_index = 1 + (score - 1) % max_color_index
89
- options[:colors][color_index]
90
- end
91
-
92
- def text_color(background_color)
93
- # Simple contrast check - if dark background, use white text
94
- hex = background_color.delete("#")
95
- r = hex[0..1].to_i(16)
96
- g = hex[2..3].to_i(16)
97
- b = hex[4..5].to_i(16)
98
- brightness = (r * 299 + g * 587 + b * 114) / 1000
99
- (brightness > 128) ? "#000000" : "#ffffff"
100
- end
101
-
102
- def svg_width
103
- options[:cells_per_row] * options[:cell_size] +
104
- (options[:cells_per_row] - 1) * options[:cell_spacing]
105
- end
106
-
107
- def svg_height
108
- options[:cell_size]
109
- end
110
-
111
- def darker_color(hex_color)
112
- # Remove # if present
113
- hex = hex_color.delete("#")
114
-
115
- # Extract RGB values
116
- r = hex[0..1].to_i(16)
117
- g = hex[2..3].to_i(16)
118
- b = hex[4..5].to_i(16)
119
-
120
- # Make 30% darker
121
- r = (r * 0.7).to_i
122
- g = (g * 0.7).to_i
123
- b = (b * 0.7).to_i
124
-
125
- "#%02x%02x%02x" % [r, g, b]
126
- end
127
- end
128
- end
data/mise.toml DELETED
@@ -1,2 +0,0 @@
1
- [tools]
2
- ruby = "3.3"