konpeito 0.1.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.
Files changed (180) hide show
  1. checksums.yaml +7 -0
  2. data/.ruby-version +1 -0
  3. data/CHANGELOG.md +75 -0
  4. data/CONTRIBUTING.md +123 -0
  5. data/LICENSE +21 -0
  6. data/README.md +257 -0
  7. data/Rakefile +11 -0
  8. data/bin/konpeito +6 -0
  9. data/konpeito.gemspec +43 -0
  10. data/lib/konpeito/ast/typed_ast.rb +620 -0
  11. data/lib/konpeito/ast/visitor.rb +78 -0
  12. data/lib/konpeito/cache/cache_manager.rb +230 -0
  13. data/lib/konpeito/cache/dependency_graph.rb +192 -0
  14. data/lib/konpeito/cache.rb +8 -0
  15. data/lib/konpeito/cli/base_command.rb +187 -0
  16. data/lib/konpeito/cli/build_command.rb +220 -0
  17. data/lib/konpeito/cli/check_command.rb +104 -0
  18. data/lib/konpeito/cli/config.rb +231 -0
  19. data/lib/konpeito/cli/deps_command.rb +128 -0
  20. data/lib/konpeito/cli/doctor_command.rb +340 -0
  21. data/lib/konpeito/cli/fmt_command.rb +199 -0
  22. data/lib/konpeito/cli/init_command.rb +312 -0
  23. data/lib/konpeito/cli/lsp_command.rb +40 -0
  24. data/lib/konpeito/cli/run_command.rb +150 -0
  25. data/lib/konpeito/cli/test_command.rb +248 -0
  26. data/lib/konpeito/cli/watch_command.rb +212 -0
  27. data/lib/konpeito/cli.rb +301 -0
  28. data/lib/konpeito/codegen/builtin_methods.rb +229 -0
  29. data/lib/konpeito/codegen/cruby_backend.rb +1090 -0
  30. data/lib/konpeito/codegen/debug_info.rb +352 -0
  31. data/lib/konpeito/codegen/inliner.rb +486 -0
  32. data/lib/konpeito/codegen/jvm_backend.rb +197 -0
  33. data/lib/konpeito/codegen/jvm_generator.rb +13412 -0
  34. data/lib/konpeito/codegen/llvm_generator.rb +13191 -0
  35. data/lib/konpeito/codegen/loop_optimizer.rb +363 -0
  36. data/lib/konpeito/codegen/monomorphizer.rb +359 -0
  37. data/lib/konpeito/codegen/profile_runtime.c +341 -0
  38. data/lib/konpeito/codegen/profiler.rb +99 -0
  39. data/lib/konpeito/compiler.rb +592 -0
  40. data/lib/konpeito/dependency_resolver.rb +296 -0
  41. data/lib/konpeito/diagnostics/collector.rb +127 -0
  42. data/lib/konpeito/diagnostics/diagnostic.rb +237 -0
  43. data/lib/konpeito/diagnostics/renderer.rb +144 -0
  44. data/lib/konpeito/formatter/formatter.rb +1214 -0
  45. data/lib/konpeito/hir/builder.rb +7167 -0
  46. data/lib/konpeito/hir/nodes.rb +2465 -0
  47. data/lib/konpeito/lsp/document_manager.rb +820 -0
  48. data/lib/konpeito/lsp/server.rb +183 -0
  49. data/lib/konpeito/lsp/transport.rb +38 -0
  50. data/lib/konpeito/parser/prism_adapter.rb +65 -0
  51. data/lib/konpeito/platform.rb +103 -0
  52. data/lib/konpeito/profile/report.rb +136 -0
  53. data/lib/konpeito/rbs_inline/preprocessor.rb +199 -0
  54. data/lib/konpeito/stdlib/compression/compression.rb +72 -0
  55. data/lib/konpeito/stdlib/compression/compression.rbs +60 -0
  56. data/lib/konpeito/stdlib/compression/compression_native.c +415 -0
  57. data/lib/konpeito/stdlib/compression/extconf.rb +19 -0
  58. data/lib/konpeito/stdlib/crypto/crypto.rb +85 -0
  59. data/lib/konpeito/stdlib/crypto/crypto.rbs +74 -0
  60. data/lib/konpeito/stdlib/crypto/crypto_native.c +312 -0
  61. data/lib/konpeito/stdlib/crypto/extconf.rb +40 -0
  62. data/lib/konpeito/stdlib/http/extconf.rb +19 -0
  63. data/lib/konpeito/stdlib/http/http.rb +125 -0
  64. data/lib/konpeito/stdlib/http/http.rbs +57 -0
  65. data/lib/konpeito/stdlib/http/http_native.c +440 -0
  66. data/lib/konpeito/stdlib/json/extconf.rb +17 -0
  67. data/lib/konpeito/stdlib/json/json.rb +44 -0
  68. data/lib/konpeito/stdlib/json/json.rbs +33 -0
  69. data/lib/konpeito/stdlib/json/json_native.c +286 -0
  70. data/lib/konpeito/stdlib/ui/extconf.rb +216 -0
  71. data/lib/konpeito/stdlib/ui/konpeito_ui_native.cpp +1625 -0
  72. data/lib/konpeito/stdlib/ui/konpeito_ui_native.h +162 -0
  73. data/lib/konpeito/stdlib/ui/ui.rb +318 -0
  74. data/lib/konpeito/stdlib/ui/ui.rbs +247 -0
  75. data/lib/konpeito/type_checker/annotation_parser.rb +67 -0
  76. data/lib/konpeito/type_checker/hm_inferrer.rb +2565 -0
  77. data/lib/konpeito/type_checker/inferrer.rb +565 -0
  78. data/lib/konpeito/type_checker/rbs_loader.rb +1621 -0
  79. data/lib/konpeito/type_checker/type_resolver.rb +276 -0
  80. data/lib/konpeito/type_checker/types.rb +1434 -0
  81. data/lib/konpeito/type_checker/unification.rb +323 -0
  82. data/lib/konpeito/ui/animation/animated_state.rb +80 -0
  83. data/lib/konpeito/ui/animation/easing.rb +59 -0
  84. data/lib/konpeito/ui/animation/value_tween.rb +66 -0
  85. data/lib/konpeito/ui/app.rb +379 -0
  86. data/lib/konpeito/ui/box.rb +38 -0
  87. data/lib/konpeito/ui/castella.rb +70 -0
  88. data/lib/konpeito/ui/castella_native.rb +76 -0
  89. data/lib/konpeito/ui/chart/area_chart.rb +305 -0
  90. data/lib/konpeito/ui/chart/bar_chart.rb +288 -0
  91. data/lib/konpeito/ui/chart/base_chart.rb +210 -0
  92. data/lib/konpeito/ui/chart/chart_helpers.rb +79 -0
  93. data/lib/konpeito/ui/chart/gauge_chart.rb +171 -0
  94. data/lib/konpeito/ui/chart/heatmap_chart.rb +222 -0
  95. data/lib/konpeito/ui/chart/line_chart.rb +289 -0
  96. data/lib/konpeito/ui/chart/pie_chart.rb +219 -0
  97. data/lib/konpeito/ui/chart/scales.rb +77 -0
  98. data/lib/konpeito/ui/chart/scatter_chart.rb +303 -0
  99. data/lib/konpeito/ui/chart/stacked_bar_chart.rb +276 -0
  100. data/lib/konpeito/ui/column.rb +271 -0
  101. data/lib/konpeito/ui/core.rb +2199 -0
  102. data/lib/konpeito/ui/dsl.rb +443 -0
  103. data/lib/konpeito/ui/frame.rb +171 -0
  104. data/lib/konpeito/ui/frame_native.rb +494 -0
  105. data/lib/konpeito/ui/markdown/ast.rb +124 -0
  106. data/lib/konpeito/ui/markdown/mermaid/layout.rb +387 -0
  107. data/lib/konpeito/ui/markdown/mermaid/models.rb +232 -0
  108. data/lib/konpeito/ui/markdown/mermaid/parser.rb +519 -0
  109. data/lib/konpeito/ui/markdown/mermaid/renderer.rb +336 -0
  110. data/lib/konpeito/ui/markdown/parser.rb +805 -0
  111. data/lib/konpeito/ui/markdown/renderer.rb +639 -0
  112. data/lib/konpeito/ui/markdown/theme.rb +165 -0
  113. data/lib/konpeito/ui/render_node.rb +260 -0
  114. data/lib/konpeito/ui/row.rb +207 -0
  115. data/lib/konpeito/ui/spacer.rb +18 -0
  116. data/lib/konpeito/ui/style.rb +799 -0
  117. data/lib/konpeito/ui/theme.rb +563 -0
  118. data/lib/konpeito/ui/themes/material.rb +35 -0
  119. data/lib/konpeito/ui/themes/tokyo_night.rb +6 -0
  120. data/lib/konpeito/ui/widgets/button.rb +103 -0
  121. data/lib/konpeito/ui/widgets/calendar.rb +1034 -0
  122. data/lib/konpeito/ui/widgets/checkbox.rb +119 -0
  123. data/lib/konpeito/ui/widgets/container.rb +91 -0
  124. data/lib/konpeito/ui/widgets/data_table.rb +667 -0
  125. data/lib/konpeito/ui/widgets/divider.rb +29 -0
  126. data/lib/konpeito/ui/widgets/image.rb +105 -0
  127. data/lib/konpeito/ui/widgets/input.rb +485 -0
  128. data/lib/konpeito/ui/widgets/markdown.rb +57 -0
  129. data/lib/konpeito/ui/widgets/modal.rb +163 -0
  130. data/lib/konpeito/ui/widgets/multiline_input.rb +968 -0
  131. data/lib/konpeito/ui/widgets/multiline_text.rb +180 -0
  132. data/lib/konpeito/ui/widgets/net_image.rb +100 -0
  133. data/lib/konpeito/ui/widgets/progress_bar.rb +70 -0
  134. data/lib/konpeito/ui/widgets/radio_buttons.rb +93 -0
  135. data/lib/konpeito/ui/widgets/slider.rb +133 -0
  136. data/lib/konpeito/ui/widgets/switch.rb +84 -0
  137. data/lib/konpeito/ui/widgets/tabs.rb +157 -0
  138. data/lib/konpeito/ui/widgets/text.rb +110 -0
  139. data/lib/konpeito/ui/widgets/tree.rb +426 -0
  140. data/lib/konpeito/version.rb +5 -0
  141. data/lib/konpeito.rb +109 -0
  142. data/test_native_array.rb +172 -0
  143. data/test_native_array_class.rb +197 -0
  144. data/test_native_class.rb +151 -0
  145. data/tools/konpeito-asm/build.sh +65 -0
  146. data/tools/konpeito-asm/lib/asm-9.7.1.jar +0 -0
  147. data/tools/konpeito-asm/runtime-classes/konpeito/runtime/KArray.class +0 -0
  148. data/tools/konpeito-asm/runtime-classes/konpeito/runtime/KCompression.class +0 -0
  149. data/tools/konpeito-asm/runtime-classes/konpeito/runtime/KConditionVariable.class +0 -0
  150. data/tools/konpeito-asm/runtime-classes/konpeito/runtime/KCrypto.class +0 -0
  151. data/tools/konpeito-asm/runtime-classes/konpeito/runtime/KFile.class +0 -0
  152. data/tools/konpeito-asm/runtime-classes/konpeito/runtime/KHTTP.class +0 -0
  153. data/tools/konpeito-asm/runtime-classes/konpeito/runtime/KHash.class +0 -0
  154. data/tools/konpeito-asm/runtime-classes/konpeito/runtime/KJSON$Parser.class +0 -0
  155. data/tools/konpeito-asm/runtime-classes/konpeito/runtime/KJSON.class +0 -0
  156. data/tools/konpeito-asm/runtime-classes/konpeito/runtime/KMath.class +0 -0
  157. data/tools/konpeito-asm/runtime-classes/konpeito/runtime/KRactor.class +0 -0
  158. data/tools/konpeito-asm/runtime-classes/konpeito/runtime/KRactorPort.class +0 -0
  159. data/tools/konpeito-asm/runtime-classes/konpeito/runtime/KSizedQueue.class +0 -0
  160. data/tools/konpeito-asm/runtime-classes/konpeito/runtime/KThread.class +0 -0
  161. data/tools/konpeito-asm/runtime-classes/konpeito/runtime/KTime.class +0 -0
  162. data/tools/konpeito-asm/runtime-classes/konpeito/runtime/RubyDispatch.class +0 -0
  163. data/tools/konpeito-asm/src/ClassIntrospector.java +312 -0
  164. data/tools/konpeito-asm/src/KonpeitoAssembler.java +659 -0
  165. data/tools/konpeito-asm/src/konpeito/runtime/KArray.java +390 -0
  166. data/tools/konpeito-asm/src/konpeito/runtime/KCompression.java +168 -0
  167. data/tools/konpeito-asm/src/konpeito/runtime/KConditionVariable.java +48 -0
  168. data/tools/konpeito-asm/src/konpeito/runtime/KCrypto.java +151 -0
  169. data/tools/konpeito-asm/src/konpeito/runtime/KFile.java +100 -0
  170. data/tools/konpeito-asm/src/konpeito/runtime/KHTTP.java +113 -0
  171. data/tools/konpeito-asm/src/konpeito/runtime/KHash.java +228 -0
  172. data/tools/konpeito-asm/src/konpeito/runtime/KJSON.java +405 -0
  173. data/tools/konpeito-asm/src/konpeito/runtime/KMath.java +54 -0
  174. data/tools/konpeito-asm/src/konpeito/runtime/KRactor.java +244 -0
  175. data/tools/konpeito-asm/src/konpeito/runtime/KRactorPort.java +53 -0
  176. data/tools/konpeito-asm/src/konpeito/runtime/KSizedQueue.java +49 -0
  177. data/tools/konpeito-asm/src/konpeito/runtime/KThread.java +49 -0
  178. data/tools/konpeito-asm/src/konpeito/runtime/KTime.java +53 -0
  179. data/tools/konpeito-asm/src/konpeito/runtime/RubyDispatch.java +416 -0
  180. metadata +267 -0
@@ -0,0 +1,210 @@
1
+ # BaseChart - abstract base class for all chart widgets
2
+ # Provides margin management, title, legend, and hover detection
3
+
4
+ # Chart margin constants
5
+ CHART_MARGIN_TOP = 40.0
6
+ CHART_MARGIN_RIGHT = 20.0
7
+ CHART_MARGIN_BOTTOM = 50.0
8
+ CHART_MARGIN_LEFT = 60.0
9
+
10
+ # Default series colors (8-color palette, Tokyo Night inspired)
11
+ def series_color(index)
12
+ i = index % 8
13
+ c = 0
14
+ if i == 0
15
+ c = 4288807671
16
+ elsif i == 1
17
+ c = 4288452202
18
+ elsif i == 2
19
+ c = 4294604430
20
+ elsif i == 3
21
+ c = 4292886376
22
+ elsif i == 4
23
+ c = 4290530039
24
+ elsif i == 5
25
+ c = 4285791434
26
+ elsif i == 6
27
+ c = 4294934372
28
+ else
29
+ c = 4286361599
30
+ end
31
+ c
32
+ end
33
+
34
+ class BaseChart < Widget
35
+ def initialize
36
+ super()
37
+ @margin_top = CHART_MARGIN_TOP
38
+ @margin_right = CHART_MARGIN_RIGHT
39
+ @margin_bottom = CHART_MARGIN_BOTTOM
40
+ @margin_left = CHART_MARGIN_LEFT
41
+ @title_text = nil
42
+ @show_legend = true
43
+ @hover_index = -1
44
+ @hover_series = -1
45
+ @mouse_x = -1.0
46
+ @mouse_y = -1.0
47
+ @painter = nil
48
+ @width_policy = EXPANDING
49
+ @height_policy = EXPANDING
50
+ end
51
+
52
+ def title(t)
53
+ @title_text = t
54
+ self
55
+ end
56
+
57
+ def legend(show)
58
+ @show_legend = show
59
+ self
60
+ end
61
+
62
+ def margins(top, right, bottom, left)
63
+ @margin_top = top
64
+ @margin_right = right
65
+ @margin_bottom = bottom
66
+ @margin_left = left
67
+ self
68
+ end
69
+
70
+ def plot_x
71
+ @margin_left
72
+ end
73
+
74
+ def plot_y
75
+ @margin_top
76
+ end
77
+
78
+ def plot_w
79
+ w = @width - @margin_left - @margin_right
80
+ if w < 0.0
81
+ w = 0.0
82
+ end
83
+ w
84
+ end
85
+
86
+ def plot_h
87
+ h = @height - @margin_top - @margin_bottom
88
+ if h < 0.0
89
+ h = 0.0
90
+ end
91
+ h
92
+ end
93
+
94
+ def redraw(painter, completely)
95
+ @painter = painter
96
+ bg = $theme.bg_primary
97
+ painter.fill_rect(0.0, 0.0, @width, @height, bg)
98
+ draw_title(painter)
99
+ render_chart(painter, plot_x, plot_y, plot_w, plot_h)
100
+ end
101
+
102
+ def draw_title(painter)
103
+ return if @title_text == nil
104
+ tw = painter.measure_text_width(@title_text, $theme.font_family, 16.0)
105
+ tx = (@width - tw) / 2.0
106
+ ascent = painter.get_text_ascent($theme.font_family, 16.0)
107
+ tc = $theme.text_primary
108
+ painter.draw_text(@title_text, tx, 8.0 + ascent, $theme.font_family, 16.0, tc)
109
+ end
110
+
111
+ def render_chart(painter, px, py, pw, ph)
112
+ end
113
+
114
+ # Draw one legend item
115
+ def draw_legend_item(painter, label, color, cx, y)
116
+ painter.fill_rect(cx, y, 12.0, 12.0, color)
117
+ lx = cx + 16.0
118
+ ascent = painter.get_text_ascent($theme.font_family, 11.0)
119
+ tc = $theme.text_secondary
120
+ painter.draw_text(label, lx, y + ascent, $theme.font_family, 11.0, tc)
121
+ lw = painter.measure_text_width(label, $theme.font_family, 11.0)
122
+ lx + lw + 16.0
123
+ end
124
+
125
+ # Draw legend items
126
+ def draw_legend(painter, labels, colors, x, y)
127
+ return if labels.length == 0
128
+ cx = x
129
+ i = 0
130
+ while i < labels.length
131
+ c = colors[i % colors.length]
132
+ cx = draw_legend_item(painter, labels[i], c, cx, y)
133
+ i = i + 1
134
+ end
135
+ end
136
+
137
+ # Draw one Y-axis tick (grid line + tick mark)
138
+ def draw_y_tick(painter, px, pw, yy, grid_color, border_color)
139
+ painter.draw_line(px, yy, px + pw, yy, grid_color, 1.0)
140
+ painter.draw_line(px - 4.0, yy, px, yy, border_color, 1.0)
141
+ end
142
+
143
+ # Draw one Y-axis tick label
144
+ def draw_y_tick_label(painter, px, yy, label, label_color)
145
+ lw = painter.measure_text_width(label, $theme.font_family, 11.0)
146
+ ascent = painter.get_text_ascent($theme.font_family, 11.0)
147
+ lx = px - lw - 6.0
148
+ ly = yy + ascent / 2.0
149
+ painter.draw_text(label, lx, ly, $theme.font_family, 11.0, label_color)
150
+ end
151
+
152
+ # Draw Y-axis with tick marks and grid lines
153
+ def draw_y_axis(painter, px, py, pw, ph, ticks, scale)
154
+ bc = $theme.border
155
+ painter.draw_line(px, py, px, py + ph, bc, 1.0)
156
+ gc = painter.with_alpha(bc, 60)
157
+ lc = $theme.text_secondary
158
+ i = 0
159
+ while i < ticks.length
160
+ yy = scale.map(ticks[i])
161
+ draw_y_tick(painter, px, pw, yy, gc, bc)
162
+ label = format_axis_value(painter, ticks[i])
163
+ draw_y_tick_label(painter, px, yy, label, lc)
164
+ i = i + 1
165
+ end
166
+ end
167
+
168
+ # Draw X-axis line
169
+ def draw_x_axis_line(painter, px, py, pw, ph)
170
+ bc = $theme.border
171
+ painter.draw_line(px, py + ph, px + pw, py + ph, bc, 1.0)
172
+ end
173
+
174
+ # Mouse tracking for hover
175
+ def cursor_pos(ev)
176
+ @mouse_x = ev.pos.x
177
+ @mouse_y = ev.pos.y
178
+ old_hi = @hover_index
179
+ old_hs = @hover_series
180
+ update_hover
181
+ if @hover_index != old_hi
182
+ mark_dirty
183
+ update
184
+ elsif @hover_series != old_hs
185
+ mark_dirty
186
+ update
187
+ end
188
+ end
189
+
190
+ def mouse_out
191
+ changed = false
192
+ if @hover_index != -1
193
+ @hover_index = -1
194
+ changed = true
195
+ end
196
+ if @hover_series != -1
197
+ @hover_series = -1
198
+ changed = true
199
+ end
200
+ if changed
201
+ @mouse_x = -1.0
202
+ @mouse_y = -1.0
203
+ mark_dirty
204
+ update
205
+ end
206
+ end
207
+
208
+ def update_hover
209
+ end
210
+ end
@@ -0,0 +1,79 @@
1
+ # Chart helper functions
2
+ # NOTE: Color manipulation (with_alpha, lighten_color, darken_color)
3
+ # is done in KUIRuntime.java to avoid JVM verifier issues with bitwise ops.
4
+ # Call via: painter.with_alpha(color, alpha)
5
+ # painter.lighten_color(color, amount)
6
+ # painter.darken_color(color, amount)
7
+
8
+ # Compute nice axis tick values
9
+ # Simplified to avoid Math.log10 and ** operator (JVM compatibility)
10
+ def compute_ticks(min_val, max_val, target_count)
11
+ ticks = []
12
+ range = max_val - min_val
13
+ if range <= 0.0
14
+ ticks << min_val
15
+ return ticks
16
+ end
17
+ rough_step = range / target_count
18
+ nice_step = pick_nice_step(rough_step)
19
+ start = compute_tick_start(min_val, nice_step)
20
+ v = start
21
+ while v <= max_val + nice_step * 0.001
22
+ if v >= min_val - nice_step * 0.001
23
+ ticks << v
24
+ end
25
+ v = v + nice_step
26
+ end
27
+ ticks
28
+ end
29
+
30
+ def pick_nice_step(rough_step)
31
+ if rough_step >= 500.0
32
+ return 500.0
33
+ end
34
+ if rough_step >= 200.0
35
+ return 200.0
36
+ end
37
+ if rough_step >= 100.0
38
+ return 100.0
39
+ end
40
+ if rough_step >= 50.0
41
+ return 50.0
42
+ end
43
+ if rough_step >= 20.0
44
+ return 20.0
45
+ end
46
+ if rough_step >= 10.0
47
+ return 10.0
48
+ end
49
+ if rough_step >= 5.0
50
+ return 5.0
51
+ end
52
+ if rough_step >= 2.0
53
+ return 2.0
54
+ end
55
+ if rough_step >= 1.0
56
+ return 1.0
57
+ end
58
+ if rough_step >= 0.5
59
+ return 0.5
60
+ end
61
+ if rough_step >= 0.2
62
+ return 0.2
63
+ end
64
+ 0.1
65
+ end
66
+
67
+ def compute_tick_start(min_val, nice_step)
68
+ ratio = min_val / nice_step
69
+ int_part = ratio.to_i
70
+ int_f = int_part * 1.0
71
+ int_f * nice_step
72
+ end
73
+
74
+ # Format a number for axis labels
75
+ # NOTE: Uses painter.number_to_string (Java method) because
76
+ # .to_i/.to_s dispatch doesn't work on untyped JVM Objects
77
+ def format_axis_value(painter, val)
78
+ painter.number_to_string(val)
79
+ end
@@ -0,0 +1,171 @@
1
+ # GaugeChart - arc gauge with thresholds and center value display
2
+ # Uses fill_arc/stroke_arc APIs for drawing
3
+
4
+ GAUGE_PI = 3.14159265358979323846
5
+
6
+ class GaugeChart < BaseChart
7
+ def initialize(value, min_val, max_val)
8
+ super()
9
+ @value = value
10
+ @min_val = min_val
11
+ @max_val = max_val
12
+ @thresholds = nil # Array of [threshold_value, color] pairs
13
+ @arc_width = 20.0
14
+ @show_value = true
15
+ @value_format = nil
16
+ @unit_text = ""
17
+ @bg_arc_color = 0xFF3B4261
18
+ end
19
+
20
+ def thresholds(t)
21
+ @thresholds = t
22
+ self
23
+ end
24
+
25
+ def arc_width(w)
26
+ @arc_width = w
27
+ self
28
+ end
29
+
30
+ def show_value(v)
31
+ @show_value = v
32
+ self
33
+ end
34
+
35
+ def unit(u)
36
+ @unit_text = u
37
+ self
38
+ end
39
+
40
+ def set_value(v)
41
+ @value = v
42
+ mark_dirty
43
+ update
44
+ end
45
+
46
+ def render_chart(painter, px, py, pw, ph)
47
+ chart_size = pw
48
+ if ph < pw
49
+ chart_size = ph
50
+ end
51
+ radius = chart_size / 2.0 - @arc_width / 2.0 - 10.0
52
+ if radius < 30.0
53
+ radius = 30.0
54
+ end
55
+ cx = px + pw / 2.0
56
+ cy = py + ph / 2.0 + 10.0
57
+
58
+ # Background arc (270 degrees, from 135 to 405)
59
+ start_angle = 135.0
60
+ sweep_total = 270.0
61
+ painter.stroke_arc(cx, cy, radius, start_angle, sweep_total, @bg_arc_color, @arc_width)
62
+
63
+ # Value arc
64
+ range = @max_val - @min_val
65
+ if range <= 0.0
66
+ range = 1.0
67
+ end
68
+ fraction = (@value - @min_val) / range
69
+ if fraction < 0.0
70
+ fraction = 0.0
71
+ end
72
+ if fraction > 1.0
73
+ fraction = 1.0
74
+ end
75
+ value_sweep = fraction * sweep_total
76
+ arc_color = get_gauge_color(painter, fraction)
77
+ painter.stroke_arc(cx, cy, radius, start_angle, value_sweep, arc_color, @arc_width)
78
+
79
+ # Draw threshold tick marks
80
+ draw_gauge_ticks(painter, cx, cy, radius)
81
+
82
+ # Center value display
83
+ if @show_value
84
+ draw_gauge_value(painter, cx, cy)
85
+ end
86
+
87
+ # Min/Max labels
88
+ draw_gauge_min_max(painter, cx, cy, radius)
89
+ end
90
+
91
+ def get_gauge_color(painter, fraction)
92
+ if @thresholds == nil
93
+ return series_color(0)
94
+ end
95
+ # thresholds: [[0.5, green], [0.75, yellow], [1.0, red]]
96
+ i = 0
97
+ while i < @thresholds.length
98
+ threshold_frac = @thresholds[i][0]
99
+ if fraction <= threshold_frac
100
+ return @thresholds[i][1]
101
+ end
102
+ i = i + 1
103
+ end
104
+ # Past all thresholds, use last color
105
+ if @thresholds.length > 0
106
+ return @thresholds[@thresholds.length - 1][1]
107
+ end
108
+ series_color(0)
109
+ end
110
+
111
+ def draw_gauge_ticks(painter, cx, cy, radius)
112
+ return if @thresholds == nil
113
+ start_angle = 135.0
114
+ sweep_total = 270.0
115
+ tick_r_inner = radius - @arc_width / 2.0 - 2.0
116
+ tick_r_outer = radius + @arc_width / 2.0 + 2.0
117
+ tc = $theme.text_secondary
118
+ i = 0
119
+ while i < @thresholds.length
120
+ frac = @thresholds[i][0]
121
+ if frac < 1.0
122
+ angle_deg = start_angle + frac * sweep_total
123
+ angle_rad = angle_deg * GAUGE_PI / 180.0
124
+ cos_a = painter.math_cos(angle_rad)
125
+ sin_a = painter.math_sin(angle_rad)
126
+ x1 = cx + tick_r_inner * cos_a
127
+ y1 = cy + tick_r_inner * sin_a
128
+ x2 = cx + tick_r_outer * cos_a
129
+ y2 = cy + tick_r_outer * sin_a
130
+ painter.draw_line(x1, y1, x2, y2, tc, 1.0)
131
+ end
132
+ i = i + 1
133
+ end
134
+ end
135
+
136
+ def draw_gauge_value(painter, cx, cy)
137
+ vl = painter.number_to_string(@value)
138
+ if @unit_text != ""
139
+ vl = vl + @unit_text
140
+ end
141
+ vw = painter.measure_text_width(vl, $theme.font_family, 28.0)
142
+ ascent = painter.get_text_ascent($theme.font_family, 28.0)
143
+ vx = cx - vw / 2.0
144
+ vy = cy + ascent / 2.0
145
+ tc = $theme.text_primary
146
+ painter.draw_text(vl, vx, vy, $theme.font_family, 28.0, tc)
147
+ end
148
+
149
+ def draw_gauge_min_max(painter, cx, cy, radius)
150
+ lc = $theme.text_secondary
151
+ r_label = radius + @arc_width / 2.0 + 16.0
152
+ # Min label at 135 degrees
153
+ min_angle = 135.0 * GAUGE_PI / 180.0
154
+ min_label = painter.number_to_string(@min_val)
155
+ mlw = painter.measure_text_width(min_label, $theme.font_family, 11.0)
156
+ mx = cx + r_label * painter.math_cos(min_angle) - mlw
157
+ my = cy + r_label * painter.math_sin(min_angle)
158
+ ascent = painter.get_text_ascent($theme.font_family, 11.0)
159
+ painter.draw_text(min_label, mx, my + ascent, $theme.font_family, 11.0, lc)
160
+ # Max label at 405 degrees = 45 degrees
161
+ max_angle = 45.0 * GAUGE_PI / 180.0
162
+ max_label = painter.number_to_string(@max_val)
163
+ max_x = cx + r_label * painter.math_cos(max_angle)
164
+ max_y = cy + r_label * painter.math_sin(max_angle)
165
+ painter.draw_text(max_label, max_x, max_y + ascent, $theme.font_family, 11.0, lc)
166
+ end
167
+ end
168
+
169
+ def GaugeChart(value, min_val, max_val)
170
+ GaugeChart.new(value, min_val, max_val)
171
+ end
@@ -0,0 +1,222 @@
1
+ # HeatmapChart - 2D grid heatmap with color interpolation
2
+ # Supports axis labels, value display, color scale legend
3
+
4
+ class HeatmapChart < BaseChart
5
+ def initialize(x_labels, y_labels, data_2d)
6
+ super()
7
+ @x_labels = x_labels # Array of String (column headers)
8
+ @y_labels = y_labels # Array of String (row headers)
9
+ @data_2d = data_2d # Array of Array[Float] (rows x cols)
10
+ @show_cell_values = true
11
+ @color_low = 0xFF1A1B26 # Dark blue/black
12
+ @color_high = 0xFF7AA2F7 # Bright blue
13
+ @cell_padding = 2.0
14
+ @data_min = 0.0
15
+ @data_max = 1.0
16
+ @chart_px = 0.0
17
+ @chart_py = 0.0
18
+ @chart_pw = 0.0
19
+ @chart_ph = 0.0
20
+ @num_rows = 0
21
+ @num_cols = 0
22
+ compute_heatmap_range
23
+ end
24
+
25
+ def show_cell_values(v)
26
+ @show_cell_values = v
27
+ self
28
+ end
29
+
30
+ def color_range(low, high)
31
+ @color_low = low
32
+ @color_high = high
33
+ self
34
+ end
35
+
36
+ def cell_padding(p)
37
+ @cell_padding = p
38
+ self
39
+ end
40
+
41
+ def set_data(x_labels, y_labels, data_2d)
42
+ @x_labels = x_labels
43
+ @y_labels = y_labels
44
+ @data_2d = data_2d
45
+ compute_heatmap_range
46
+ mark_dirty
47
+ update
48
+ end
49
+
50
+ def render_chart(painter, px, py, pw, ph)
51
+ return if @x_labels.length == 0
52
+ return if @y_labels.length == 0
53
+ @chart_px = px
54
+ @chart_py = py
55
+ @chart_pw = pw
56
+ @chart_ph = ph
57
+ @num_rows = @y_labels.length
58
+ @num_cols = @x_labels.length
59
+ draw_heatmap_cells(painter)
60
+ draw_heatmap_x_labels(painter)
61
+ draw_heatmap_y_labels(painter)
62
+ draw_heatmap_color_legend(painter)
63
+ end
64
+
65
+ def draw_heatmap_cells(painter)
66
+ cell_w = @chart_pw / (@num_cols * 1.0)
67
+ cell_h = (@chart_ph - 20.0) / (@num_rows * 1.0)
68
+ range = @data_max - @data_min
69
+ if range <= 0.0
70
+ range = 1.0
71
+ end
72
+ ri = 0
73
+ while ri < @num_rows
74
+ ci = 0
75
+ while ci < @num_cols
76
+ draw_heatmap_cell(painter, ri, ci, cell_w, cell_h, range)
77
+ ci = ci + 1
78
+ end
79
+ ri = ri + 1
80
+ end
81
+ end
82
+
83
+ def draw_heatmap_cell(painter, ri, ci, cell_w, cell_h, range)
84
+ val = 0.0
85
+ if ri < @data_2d.length
86
+ if ci < @data_2d[ri].length
87
+ val = @data_2d[ri][ci]
88
+ end
89
+ end
90
+ t = (val - @data_min) / range
91
+ if t < 0.0
92
+ t = 0.0
93
+ end
94
+ if t > 1.0
95
+ t = 1.0
96
+ end
97
+ c = painter.interpolate_color(@color_low, @color_high, t)
98
+ cx = @chart_px + ci * 1.0 * cell_w + @cell_padding
99
+ cy = @chart_py + ri * 1.0 * cell_h + @cell_padding
100
+ cw = cell_w - @cell_padding * 2.0
101
+ ch = cell_h - @cell_padding * 2.0
102
+ painter.fill_round_rect(cx, cy, cw, ch, 3.0, c)
103
+
104
+ # Hover highlight
105
+ if @hover_series == ri
106
+ if @hover_index == ci
107
+ hc = painter.with_alpha(4294967295, 60)
108
+ painter.stroke_rect(cx, cy, cw, ch, hc, 2.0)
109
+ end
110
+ end
111
+
112
+ if @show_cell_values
113
+ vl = painter.number_to_string(val)
114
+ vw = painter.measure_text_width(vl, $theme.font_family, 10.0)
115
+ ascent = painter.get_text_ascent($theme.font_family, 10.0)
116
+ vx = cx + cw / 2.0 - vw / 2.0
117
+ vy = cy + ch / 2.0 + ascent / 2.0
118
+ # Use white text on dark cells, dark on light cells
119
+ text_c = 4294967295
120
+ if t > 0.6
121
+ text_c = 4278190080
122
+ end
123
+ painter.draw_text(vl, vx, vy, $theme.font_family, 10.0, text_c)
124
+ end
125
+ end
126
+
127
+ def draw_heatmap_x_labels(painter)
128
+ cell_w = @chart_pw / (@num_cols * 1.0)
129
+ lc = $theme.text_secondary
130
+ ascent = painter.get_text_ascent($theme.font_family, 11.0)
131
+ label_y = @chart_py + @chart_ph - 20.0 + 14.0 + ascent
132
+ i = 0
133
+ while i < @num_cols
134
+ label = @x_labels[i]
135
+ lw = painter.measure_text_width(label, $theme.font_family, 11.0)
136
+ lx = @chart_px + i * 1.0 * cell_w + cell_w / 2.0 - lw / 2.0
137
+ painter.draw_text(label, lx, label_y, $theme.font_family, 11.0, lc)
138
+ i = i + 1
139
+ end
140
+ end
141
+
142
+ def draw_heatmap_y_labels(painter)
143
+ cell_h = (@chart_ph - 20.0) / (@num_rows * 1.0)
144
+ lc = $theme.text_secondary
145
+ ascent = painter.get_text_ascent($theme.font_family, 11.0)
146
+ i = 0
147
+ while i < @num_rows
148
+ label = @y_labels[i]
149
+ lw = painter.measure_text_width(label, $theme.font_family, 11.0)
150
+ lx = @chart_px - lw - 6.0
151
+ ly = @chart_py + i * 1.0 * cell_h + cell_h / 2.0 + ascent / 2.0
152
+ painter.draw_text(label, lx, ly, $theme.font_family, 11.0, lc)
153
+ i = i + 1
154
+ end
155
+ end
156
+
157
+ def draw_heatmap_color_legend(painter)
158
+ # Draw a small gradient bar at the right side
159
+ lg_w = 16.0
160
+ lg_h = @chart_ph - 20.0
161
+ lg_x = @chart_px + @chart_pw + 8.0
162
+ lg_y = @chart_py
163
+ steps = 20
164
+ step_h = lg_h / (steps * 1.0)
165
+ i = 0
166
+ while i < steps
167
+ t = 1.0 - (i * 1.0 / (steps * 1.0))
168
+ c = painter.interpolate_color(@color_low, @color_high, t)
169
+ sy = lg_y + i * 1.0 * step_h
170
+ painter.fill_rect(lg_x, sy, lg_w, step_h + 1.0, c)
171
+ i = i + 1
172
+ end
173
+ # Labels
174
+ lc = $theme.text_secondary
175
+ ascent = painter.get_text_ascent($theme.font_family, 9.0)
176
+ max_label = painter.number_to_string(@data_max)
177
+ min_label = painter.number_to_string(@data_min)
178
+ painter.draw_text(max_label, lg_x + lg_w + 4.0, lg_y + ascent, $theme.font_family, 9.0, lc)
179
+ painter.draw_text(min_label, lg_x + lg_w + 4.0, lg_y + lg_h, $theme.font_family, 9.0, lc)
180
+ end
181
+
182
+ def update_hover
183
+ # Hover detection disabled for HeatmapChart on JVM
184
+ # (field type issues with @chart_px etc before first render)
185
+ end
186
+
187
+ private
188
+
189
+ def compute_heatmap_range
190
+ @data_min = 0.0
191
+ @data_max = 1.0
192
+ first = true
193
+ ri = 0
194
+ while ri < @data_2d.length
195
+ ci = 0
196
+ while ci < @data_2d[ri].length
197
+ v = @data_2d[ri][ci]
198
+ if first
199
+ @data_min = v
200
+ @data_max = v
201
+ first = false
202
+ else
203
+ if v < @data_min
204
+ @data_min = v
205
+ end
206
+ if v > @data_max
207
+ @data_max = v
208
+ end
209
+ end
210
+ ci = ci + 1
211
+ end
212
+ ri = ri + 1
213
+ end
214
+ if @data_min == @data_max
215
+ @data_max = @data_min + 1.0
216
+ end
217
+ end
218
+ end
219
+
220
+ def HeatmapChart(x_labels, y_labels, data_2d)
221
+ HeatmapChart.new(x_labels, y_labels, data_2d)
222
+ end