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,305 @@
1
+ # AreaChart - line chart with filled areas below
2
+ # Supports multiple series (stacked or overlapping), grid, hover detection
3
+
4
+ class AreaChart < BaseChart
5
+ def initialize(x_labels, series_data, series_names)
6
+ super()
7
+ @x_labels = x_labels
8
+ @series_data = series_data
9
+ @series_names = series_names
10
+ @line_width = 2.0
11
+ @fill_alpha = 80
12
+ @show_grid = true
13
+ @stacked = false
14
+ @data_min = 0.0
15
+ @data_max = 0.0
16
+ @y_scale = nil
17
+ @y_ticks = nil
18
+ @x_step = 0.0
19
+ @num_points = 0
20
+ @num_series = 0
21
+ @y_range_min = 0.0
22
+ @y_range_max = 1.0
23
+ @chart_px = 0.0
24
+ @chart_py = 0.0
25
+ @chart_pw = 0.0
26
+ @chart_ph = 0.0
27
+ compute_area_range
28
+ end
29
+
30
+ def line_width(w)
31
+ @line_width = w
32
+ self
33
+ end
34
+
35
+ def fill_alpha(a)
36
+ @fill_alpha = a
37
+ self
38
+ end
39
+
40
+ def show_grid(v)
41
+ @show_grid = v
42
+ self
43
+ end
44
+
45
+ def stacked(v)
46
+ @stacked = v
47
+ self
48
+ end
49
+
50
+ def set_data(x_labels, series_data, series_names)
51
+ @x_labels = x_labels
52
+ @series_data = series_data
53
+ @series_names = series_names
54
+ compute_area_range
55
+ mark_dirty
56
+ update
57
+ end
58
+
59
+ def render_chart(painter, px, py, pw, ph)
60
+ return if @x_labels.length == 0
61
+ return if @series_data.length == 0
62
+ @chart_px = px
63
+ @chart_py = py
64
+ @chart_pw = pw
65
+ @chart_ph = ph
66
+ @num_points = @x_labels.length
67
+ @num_series = @series_data.length
68
+ setup_area_ticks
69
+ setup_area_y_scale
70
+ setup_area_x_step
71
+ draw_y_axis(painter, px, py, pw, ph, @y_ticks, @y_scale)
72
+ draw_x_axis_line(painter, px, py, pw, ph)
73
+ draw_area_x_labels(painter)
74
+ draw_area_hover_line(painter)
75
+ draw_all_areas(painter)
76
+ draw_area_legend(painter, px, py)
77
+ end
78
+
79
+ def setup_area_ticks
80
+ @y_ticks = compute_ticks(@data_min, @data_max, 5)
81
+ @y_range_min = @data_min
82
+ @y_range_max = @data_max
83
+ if @y_ticks.length > 0
84
+ @y_range_min = @y_ticks[0]
85
+ @y_range_max = @y_ticks[@y_ticks.length - 1]
86
+ end
87
+ end
88
+
89
+ def setup_area_y_scale
90
+ bottom = @chart_py + @chart_ph
91
+ @y_scale = LinearScale.new(@y_range_min, @y_range_max, bottom, @chart_py)
92
+ end
93
+
94
+ def setup_area_x_step
95
+ @x_step = 0.0
96
+ if @num_points > 1
97
+ divisor = (@num_points - 1) * 1.0
98
+ @x_step = @chart_pw / divisor
99
+ end
100
+ end
101
+
102
+ def draw_area_x_labels(painter)
103
+ lc = $theme.text_secondary
104
+ i = 0
105
+ while i < @num_points
106
+ i_f = i * 1.0
107
+ xx = @chart_px + i_f * @x_step
108
+ label = @x_labels[i]
109
+ lw = painter.measure_text_width(label, $theme.font_family, 11.0)
110
+ ascent = painter.get_text_ascent($theme.font_family, 11.0)
111
+ label_y = @chart_py + @chart_ph + 14.0 + ascent
112
+ painter.draw_text(label, xx - lw / 2.0, label_y, $theme.font_family, 11.0, lc)
113
+ i = i + 1
114
+ end
115
+ end
116
+
117
+ def draw_area_hover_line(painter)
118
+ if @hover_index >= 0
119
+ if @hover_index < @num_points
120
+ hi_f = @hover_index * 1.0
121
+ hx = @chart_px + hi_f * @x_step
122
+ hc = painter.with_alpha($theme.accent, 80)
123
+ bottom = @chart_py + @chart_ph
124
+ painter.draw_line(hx, @chart_py, hx, bottom, hc, 1.0)
125
+ end
126
+ end
127
+ end
128
+
129
+ def draw_all_areas(painter)
130
+ baseline_y = @y_scale.map(0.0)
131
+ bottom = @chart_py + @chart_ph
132
+ if baseline_y > bottom
133
+ baseline_y = bottom
134
+ end
135
+ # Draw areas back to front (last series first)
136
+ si = @num_series - 1
137
+ while si >= 0
138
+ c = series_color(si)
139
+ fill_c = painter.with_alpha(c, @fill_alpha)
140
+ data = @series_data[si]
141
+ draw_area_fill(painter, data, fill_c, baseline_y)
142
+ draw_area_line(painter, data, c)
143
+ si = si - 1
144
+ end
145
+ # Draw hover points on top
146
+ if @hover_index >= 0
147
+ if @hover_index < @num_points
148
+ draw_area_hover_points(painter)
149
+ end
150
+ end
151
+ end
152
+
153
+ def draw_area_fill(painter, data, fill_c, baseline_y)
154
+ return if @num_points < 2
155
+ # Build path: top line left-to-right, then bottom line right-to-left
156
+ painter.begin_path
157
+ # First point
158
+ x0 = @chart_px
159
+ y0 = @y_scale.map(data[0])
160
+ painter.path_move_to(x0, y0)
161
+ # Line across top
162
+ i = 1
163
+ while i < @num_points
164
+ i_f = i * 1.0
165
+ xx = @chart_px + i_f * @x_step
166
+ yy = @y_scale.map(data[i])
167
+ painter.path_line_to(xx, yy)
168
+ i = i + 1
169
+ end
170
+ # Line down to baseline at last point
171
+ last_f = (@num_points - 1) * 1.0
172
+ last_x = @chart_px + last_f * @x_step
173
+ painter.path_line_to(last_x, baseline_y)
174
+ # Line back to first x at baseline
175
+ painter.path_line_to(@chart_px, baseline_y)
176
+ painter.close_fill_path(fill_c)
177
+ end
178
+
179
+ def draw_area_line(painter, data, c)
180
+ j = 0
181
+ while j < @num_points - 1
182
+ j_f = j * 1.0
183
+ x1 = @chart_px + j_f * @x_step
184
+ y1 = @y_scale.map(data[j])
185
+ j1_f = (j + 1) * 1.0
186
+ x2 = @chart_px + j1_f * @x_step
187
+ y2 = @y_scale.map(data[j + 1])
188
+ painter.draw_line(x1, y1, x2, y2, c, @line_width)
189
+ j = j + 1
190
+ end
191
+ end
192
+
193
+ def draw_area_hover_points(painter)
194
+ si = 0
195
+ while si < @num_series
196
+ c = series_color(si)
197
+ data = @series_data[si]
198
+ if @hover_index < data.length
199
+ hi_f = @hover_index * 1.0
200
+ xx = @chart_px + hi_f * @x_step
201
+ yy = @y_scale.map(data[@hover_index])
202
+ painter.fill_circle(xx, yy, 5.0, c)
203
+ vl = painter.number_to_string(data[@hover_index])
204
+ vw = painter.measure_text_width(vl, $theme.font_family, 10.0)
205
+ tc = $theme.text_primary
206
+ painter.draw_text(vl, xx - vw / 2.0, yy - 8.0, $theme.font_family, 10.0, tc)
207
+ end
208
+ si = si + 1
209
+ end
210
+ end
211
+
212
+ def draw_area_legend(painter, px, py)
213
+ if @show_legend
214
+ if @series_names.length > 1
215
+ colors = []
216
+ si = 0
217
+ while si < @series_names.length
218
+ colors << series_color(si)
219
+ si = si + 1
220
+ end
221
+ draw_legend(painter, @series_names, colors, px + 8.0, py - 20.0)
222
+ end
223
+ end
224
+ end
225
+
226
+ def update_hover
227
+ return if @y_scale == nil
228
+ if @num_points <= 1
229
+ @hover_index = -1
230
+ return
231
+ end
232
+ mx = @mouse_x
233
+ px = plot_x
234
+ pw = plot_w
235
+ left_bound = px - @x_step / 2.0
236
+ right_bound = px + pw + @x_step / 2.0
237
+ if mx < left_bound
238
+ @hover_index = -1
239
+ return
240
+ end
241
+ if mx > right_bound
242
+ @hover_index = -1
243
+ return
244
+ end
245
+ best = -1
246
+ best_dist = 999999.0
247
+ i = 0
248
+ while i < @num_points
249
+ i_f = i * 1.0
250
+ xx = px + i_f * @x_step
251
+ d = mx - xx
252
+ if d < 0.0
253
+ d = 0.0 - d
254
+ end
255
+ if d < best_dist
256
+ best_dist = d
257
+ best = i
258
+ end
259
+ i = i + 1
260
+ end
261
+ @hover_index = best
262
+ end
263
+
264
+ private
265
+
266
+ def compute_area_range
267
+ @data_min = 0.0
268
+ @data_max = 1.0
269
+ first = true
270
+ si = 0
271
+ while si < @series_data.length
272
+ ci = 0
273
+ while ci < @series_data[si].length
274
+ v = @series_data[si][ci]
275
+ if first
276
+ @data_min = v
277
+ @data_max = v
278
+ first = false
279
+ else
280
+ if v > @data_max
281
+ @data_max = v
282
+ end
283
+ if v < @data_min
284
+ @data_min = v
285
+ end
286
+ end
287
+ ci = ci + 1
288
+ end
289
+ si = si + 1
290
+ end
291
+ if @data_min > 0.0
292
+ @data_min = 0.0
293
+ end
294
+ range = @data_max - @data_min
295
+ if range > 0.0
296
+ @data_max = @data_max + range * 0.1
297
+ else
298
+ @data_max = @data_max + 1.0
299
+ end
300
+ end
301
+ end
302
+
303
+ def AreaChart(x_labels, series_data, series_names)
304
+ AreaChart.new(x_labels, series_data, series_names)
305
+ end
@@ -0,0 +1,288 @@
1
+ # BarChart - grouped bar chart widget
2
+ # Supports multiple series, hover highlight, value labels
3
+
4
+ class BarChart < BaseChart
5
+ def initialize(categories, series_data, series_names)
6
+ super()
7
+ @categories = categories
8
+ @series_data = series_data
9
+ @series_names = series_names
10
+ @show_values = false
11
+ @bar_radius = 2.0
12
+ @data_min = 0.0
13
+ @data_max = 0.0
14
+ # Computed per-frame
15
+ @y_scale = nil
16
+ @y_ticks = nil
17
+ @band = nil
18
+ @bar_w = 2.0
19
+ @bar_gap = 2.0
20
+ @baseline_y = 0.0
21
+ @num_series = 0
22
+ @num_cats = 0
23
+ @y_range_min = 0.0
24
+ @y_range_max = 1.0
25
+ @chart_px = 0.0
26
+ @chart_py = 0.0
27
+ @chart_pw = 0.0
28
+ @chart_ph = 0.0
29
+ compute_data_range
30
+ end
31
+
32
+ def show_values(v)
33
+ @show_values = v
34
+ self
35
+ end
36
+
37
+ def bar_radius(r)
38
+ @bar_radius = r
39
+ self
40
+ end
41
+
42
+ def set_data(categories, series_data, series_names)
43
+ @categories = categories
44
+ @series_data = series_data
45
+ @series_names = series_names
46
+ compute_data_range
47
+ mark_dirty
48
+ update
49
+ end
50
+
51
+ def render_chart(painter, px, py, pw, ph)
52
+ return if @categories.length == 0
53
+ return if @series_data.length == 0
54
+ @chart_px = px
55
+ @chart_py = py
56
+ @chart_pw = pw
57
+ @chart_ph = ph
58
+ setup_chart_counts
59
+ setup_chart_ticks
60
+ setup_chart_y_scale
61
+ setup_chart_band
62
+ setup_chart_bar_width
63
+ setup_chart_baseline
64
+ draw_y_axis(painter, px, py, pw, ph, @y_ticks, @y_scale)
65
+ draw_x_axis_line(painter, px, py, pw, ph)
66
+ draw_cat_labels(painter)
67
+ draw_all_bars(painter)
68
+ draw_bar_legend(painter, px, py)
69
+ end
70
+
71
+ def setup_chart_counts
72
+ @num_series = @series_data.length
73
+ @num_cats = @categories.length
74
+ end
75
+
76
+ def setup_chart_ticks
77
+ @y_ticks = compute_ticks(@data_min, @data_max, 5)
78
+ @y_range_min = @data_min
79
+ @y_range_max = @data_max
80
+ if @y_ticks.length > 0
81
+ @y_range_min = @y_ticks[0]
82
+ @y_range_max = @y_ticks[@y_ticks.length - 1]
83
+ end
84
+ end
85
+
86
+ def setup_chart_y_scale
87
+ bottom = @chart_py + @chart_ph
88
+ @y_scale = LinearScale.new(@y_range_min, @y_range_max, bottom, @chart_py)
89
+ end
90
+
91
+ def setup_chart_band
92
+ right = @chart_px + @chart_pw
93
+ nc = @num_cats * 1.0
94
+ @band = BandScale.new(nc, @chart_px, right, 12.0)
95
+ end
96
+
97
+ def setup_chart_bar_width
98
+ @bar_gap = 2.0
99
+ group_w = @band.band_width
100
+ ns = @num_series * 1.0
101
+ ns_minus_1 = ns - 1.0
102
+ @bar_w = (group_w - @bar_gap * ns_minus_1) / ns
103
+ if @bar_w < 2.0
104
+ @bar_w = 2.0
105
+ end
106
+ end
107
+
108
+ def setup_chart_baseline
109
+ @baseline_y = @y_scale.map(0.0)
110
+ if @baseline_y < @chart_py
111
+ @baseline_y = @chart_py
112
+ end
113
+ bmax = @chart_py + @chart_ph
114
+ if @baseline_y > bmax
115
+ @baseline_y = bmax
116
+ end
117
+ end
118
+
119
+ def draw_cat_labels(painter)
120
+ lc = $theme.text_secondary
121
+ i = 0
122
+ while i < @num_cats
123
+ draw_one_cat_label(painter, i, lc)
124
+ i = i + 1
125
+ end
126
+ end
127
+
128
+ def draw_one_cat_label(painter, i, lc)
129
+ i_f = i * 1.0
130
+ bx = @band.map(i_f)
131
+ label = @categories[i]
132
+ lw = painter.measure_text_width(label, $theme.font_family, 11.0)
133
+ ascent = painter.get_text_ascent($theme.font_family, 11.0)
134
+ label_x = bx + @band.band_width / 2.0 - lw / 2.0
135
+ label_y = @chart_py + @chart_ph + 14.0 + ascent
136
+ painter.draw_text(label, label_x, label_y, $theme.font_family, 11.0, lc)
137
+ end
138
+
139
+ def draw_all_bars(painter)
140
+ si = 0
141
+ while si < @num_series
142
+ ci = 0
143
+ while ci < @num_cats
144
+ draw_one_bar(painter, si, ci)
145
+ ci = ci + 1
146
+ end
147
+ si = si + 1
148
+ end
149
+ end
150
+
151
+ def draw_one_bar(painter, si, ci)
152
+ val = @series_data[si][ci]
153
+ bx = compute_bar_x(ci, si)
154
+ bar_top = @y_scale.map(val)
155
+ bar_h = @baseline_y - bar_top
156
+ if bar_h < 0.0
157
+ bar_top = @baseline_y
158
+ bar_h = 0.0 - bar_h
159
+ end
160
+ c = get_bar_color(painter, si, ci)
161
+ painter.fill_round_rect(bx, bar_top, @bar_w, bar_h, @bar_radius, c)
162
+ if @show_values
163
+ draw_bar_value(painter, val, bx, bar_top)
164
+ end
165
+ end
166
+
167
+ def compute_bar_x(ci, si)
168
+ ci_f = ci * 1.0
169
+ si_f = si * 1.0
170
+ @band.map(ci_f) + si_f * (@bar_w + @bar_gap)
171
+ end
172
+
173
+ def get_bar_color(painter, si, ci)
174
+ c = series_color(si)
175
+ if @hover_index == ci
176
+ if @hover_series == si
177
+ c = painter.lighten_color(c, 0.3)
178
+ end
179
+ end
180
+ c
181
+ end
182
+
183
+ def draw_bar_value(painter, val, bx, bar_top)
184
+ vl = format_axis_value(painter, val)
185
+ vw = painter.measure_text_width(vl, $theme.font_family, 10.0)
186
+ vx = bx + @bar_w / 2.0 - vw / 2.0
187
+ vy = bar_top - 4.0
188
+ painter.draw_text(vl, vx, vy, $theme.font_family, 10.0, $theme.text_secondary)
189
+ end
190
+
191
+ def draw_bar_legend(painter, px, py)
192
+ if @show_legend
193
+ if @series_names.length > 1
194
+ colors = []
195
+ si = 0
196
+ while si < @series_names.length
197
+ colors << series_color(si)
198
+ si = si + 1
199
+ end
200
+ draw_legend(painter, @series_names, colors, px + 8.0, py - 20.0)
201
+ end
202
+ end
203
+ end
204
+
205
+ def update_hover
206
+ return if @band == nil
207
+ mx = @mouse_x
208
+ my = @mouse_y
209
+ px = plot_x
210
+ py = plot_y
211
+ pw = plot_w
212
+ ph = plot_h
213
+ if mx < px
214
+ @hover_index = -1
215
+ @hover_series = -1
216
+ return
217
+ end
218
+ if mx > px + pw
219
+ @hover_index = -1
220
+ @hover_series = -1
221
+ return
222
+ end
223
+ if my < py
224
+ @hover_index = -1
225
+ @hover_series = -1
226
+ return
227
+ end
228
+ if my > py + ph
229
+ @hover_index = -1
230
+ @hover_series = -1
231
+ return
232
+ end
233
+ find_bar_hover(mx)
234
+ end
235
+
236
+ def find_bar_hover(mx)
237
+ @hover_index = -1
238
+ @hover_series = -1
239
+ ci = 0
240
+ while ci < @num_cats
241
+ si = 0
242
+ while si < @num_series
243
+ bx = compute_bar_x(ci, si)
244
+ if mx >= bx
245
+ if mx <= bx + @bar_w
246
+ @hover_index = ci
247
+ @hover_series = si
248
+ return
249
+ end
250
+ end
251
+ si = si + 1
252
+ end
253
+ ci = ci + 1
254
+ end
255
+ end
256
+
257
+ private
258
+
259
+ def compute_data_range
260
+ @data_min = 0.0
261
+ @data_max = 1.0
262
+ si = 0
263
+ while si < @series_data.length
264
+ ci = 0
265
+ while ci < @series_data[si].length
266
+ v = @series_data[si][ci]
267
+ if v > @data_max
268
+ @data_max = v
269
+ end
270
+ if v < @data_min
271
+ @data_min = v
272
+ end
273
+ ci = ci + 1
274
+ end
275
+ si = si + 1
276
+ end
277
+ range = @data_max - @data_min
278
+ if range > 0.0
279
+ @data_max = @data_max + range * 0.1
280
+ else
281
+ @data_max = @data_min + 1.0
282
+ end
283
+ end
284
+ end
285
+
286
+ def BarChart(categories, series_data, series_names)
287
+ BarChart.new(categories, series_data, series_names)
288
+ end