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,303 @@
1
+ # ScatterChart - XY scatter plot with point markers
2
+ # Supports multiple series, hover detection, value labels
3
+
4
+ class ScatterChart < BaseChart
5
+ def initialize(x_data, y_data, series_names)
6
+ super()
7
+ @x_data = x_data # Array of Array[Float] (one per series)
8
+ @y_data = y_data # Array of Array[Float] (one per series)
9
+ @series_names = series_names
10
+ @point_radius = 5.0
11
+ @show_grid = true
12
+ @data_x_min = 0.0
13
+ @data_x_max = 1.0
14
+ @data_y_min = 0.0
15
+ @data_y_max = 1.0
16
+ @x_scale = nil
17
+ @y_scale = nil
18
+ @x_ticks = nil
19
+ @y_ticks = nil
20
+ @num_series = 0
21
+ @chart_px = 0.0
22
+ @chart_py = 0.0
23
+ @chart_pw = 0.0
24
+ @chart_ph = 0.0
25
+ compute_scatter_range
26
+ end
27
+
28
+ def point_radius(r)
29
+ @point_radius = r
30
+ self
31
+ end
32
+
33
+ def show_grid(v)
34
+ @show_grid = v
35
+ self
36
+ end
37
+
38
+ def set_data(x_data, y_data, series_names)
39
+ @x_data = x_data
40
+ @y_data = y_data
41
+ @series_names = series_names
42
+ compute_scatter_range
43
+ mark_dirty
44
+ update
45
+ end
46
+
47
+ def render_chart(painter, px, py, pw, ph)
48
+ return if @x_data.length == 0
49
+ @chart_px = px
50
+ @chart_py = py
51
+ @chart_pw = pw
52
+ @chart_ph = ph
53
+ @num_series = @x_data.length
54
+ setup_scatter_scales
55
+ draw_scatter_grid(painter)
56
+ draw_y_axis(painter, px, py, pw, ph, @y_ticks, @y_scale)
57
+ draw_x_axis_line(painter, px, py, pw, ph)
58
+ draw_scatter_x_ticks(painter)
59
+ draw_all_scatter_points(painter)
60
+ draw_scatter_legend(painter, px, py)
61
+ end
62
+
63
+ def setup_scatter_scales
64
+ @x_ticks = compute_ticks(@data_x_min, @data_x_max, 5)
65
+ @y_ticks = compute_ticks(@data_y_min, @data_y_max, 5)
66
+ x_min = @data_x_min
67
+ x_max = @data_x_max
68
+ y_min = @data_y_min
69
+ y_max = @data_y_max
70
+ if @x_ticks.length > 0
71
+ x_min = @x_ticks[0]
72
+ x_max = @x_ticks[@x_ticks.length - 1]
73
+ end
74
+ if @y_ticks.length > 0
75
+ y_min = @y_ticks[0]
76
+ y_max = @y_ticks[@y_ticks.length - 1]
77
+ end
78
+ bottom = @chart_py + @chart_ph
79
+ right = @chart_px + @chart_pw
80
+ @x_scale = LinearScale.new(x_min, x_max, @chart_px, right)
81
+ @y_scale = LinearScale.new(y_min, y_max, bottom, @chart_py)
82
+ end
83
+
84
+ def draw_scatter_grid(painter)
85
+ return if !@show_grid
86
+ bc = $theme.border
87
+ gc = painter.with_alpha(bc, 40)
88
+ # Vertical grid lines at x ticks
89
+ i = 0
90
+ while i < @x_ticks.length
91
+ xx = @x_scale.map(@x_ticks[i])
92
+ bottom = @chart_py + @chart_ph
93
+ painter.draw_line(xx, @chart_py, xx, bottom, gc, 1.0)
94
+ i = i + 1
95
+ end
96
+ end
97
+
98
+ def draw_scatter_x_ticks(painter)
99
+ lc = $theme.text_secondary
100
+ i = 0
101
+ while i < @x_ticks.length
102
+ xx = @x_scale.map(@x_ticks[i])
103
+ label = painter.number_to_string(@x_ticks[i])
104
+ lw = painter.measure_text_width(label, $theme.font_family, 11.0)
105
+ ascent = painter.get_text_ascent($theme.font_family, 11.0)
106
+ label_y = @chart_py + @chart_ph + 14.0 + ascent
107
+ painter.draw_text(label, xx - lw / 2.0, label_y, $theme.font_family, 11.0, lc)
108
+ i = i + 1
109
+ end
110
+ end
111
+
112
+ def draw_all_scatter_points(painter)
113
+ si = 0
114
+ while si < @num_series
115
+ c = series_color(si)
116
+ draw_scatter_series(painter, si, c)
117
+ si = si + 1
118
+ end
119
+ end
120
+
121
+ def draw_scatter_series(painter, si, c)
122
+ xs = @x_data[si]
123
+ ys = @y_data[si]
124
+ n = xs.length
125
+ if ys.length < n
126
+ n = ys.length
127
+ end
128
+ j = 0
129
+ while j < n
130
+ xx = @x_scale.map(xs[j])
131
+ yy = @y_scale.map(ys[j])
132
+ r = @point_radius
133
+ if @hover_series == si
134
+ if @hover_index == j
135
+ r = @point_radius + 3.0
136
+ end
137
+ end
138
+ painter.fill_circle(xx, yy, r, c)
139
+ if @hover_series == si
140
+ if @hover_index == j
141
+ draw_scatter_label(painter, xs[j], ys[j], xx, yy, r)
142
+ end
143
+ end
144
+ j = j + 1
145
+ end
146
+ end
147
+
148
+ def draw_scatter_label(painter, xv, yv, xx, yy, r)
149
+ xl = painter.number_to_string(xv)
150
+ yl = painter.number_to_string(yv)
151
+ label = "(" + xl + ", " + yl + ")"
152
+ lw = painter.measure_text_width(label, $theme.font_family, 10.0)
153
+ tc = $theme.text_primary
154
+ painter.draw_text(label, xx - lw / 2.0, yy - r - 4.0, $theme.font_family, 10.0, tc)
155
+ end
156
+
157
+ def draw_scatter_legend(painter, px, py)
158
+ if @show_legend
159
+ if @series_names.length > 1
160
+ colors = []
161
+ si = 0
162
+ while si < @series_names.length
163
+ colors << series_color(si)
164
+ si = si + 1
165
+ end
166
+ draw_legend(painter, @series_names, colors, px + 8.0, py - 20.0)
167
+ end
168
+ end
169
+ end
170
+
171
+ def update_hover
172
+ return if @x_scale == nil
173
+ mx = @mouse_x
174
+ my = @mouse_y
175
+ px = plot_x
176
+ py = plot_y
177
+ pw = plot_w
178
+ ph = plot_h
179
+ if mx < px
180
+ @hover_index = -1
181
+ @hover_series = -1
182
+ return
183
+ end
184
+ if mx > px + pw
185
+ @hover_index = -1
186
+ @hover_series = -1
187
+ return
188
+ end
189
+ if my < py
190
+ @hover_index = -1
191
+ @hover_series = -1
192
+ return
193
+ end
194
+ if my > py + ph
195
+ @hover_index = -1
196
+ @hover_series = -1
197
+ return
198
+ end
199
+ find_nearest_scatter_point(mx, my)
200
+ end
201
+
202
+ def find_nearest_scatter_point(mx, my)
203
+ best_si = -1
204
+ best_j = -1
205
+ best_dist = 999999.0
206
+ si = 0
207
+ while si < @num_series
208
+ xs = @x_data[si]
209
+ ys = @y_data[si]
210
+ n = xs.length
211
+ if ys.length < n
212
+ n = ys.length
213
+ end
214
+ j = 0
215
+ while j < n
216
+ xx = @x_scale.map(xs[j])
217
+ yy = @y_scale.map(ys[j])
218
+ dx = mx - xx
219
+ dy = my - yy
220
+ d = dx * dx + dy * dy
221
+ if d < best_dist
222
+ best_dist = d
223
+ best_si = si
224
+ best_j = j
225
+ end
226
+ j = j + 1
227
+ end
228
+ si = si + 1
229
+ end
230
+ threshold = (@point_radius + 8.0) * (@point_radius + 8.0)
231
+ if best_dist < threshold
232
+ @hover_series = best_si
233
+ @hover_index = best_j
234
+ else
235
+ @hover_series = -1
236
+ @hover_index = -1
237
+ end
238
+ end
239
+
240
+ private
241
+
242
+ def compute_scatter_range
243
+ @data_x_min = 0.0
244
+ @data_x_max = 1.0
245
+ @data_y_min = 0.0
246
+ @data_y_max = 1.0
247
+ first = true
248
+ si = 0
249
+ while si < @x_data.length
250
+ xs = @x_data[si]
251
+ ys = @y_data[si]
252
+ n = xs.length
253
+ if ys.length < n
254
+ n = ys.length
255
+ end
256
+ j = 0
257
+ while j < n
258
+ xv = xs[j]
259
+ yv = ys[j]
260
+ if first
261
+ @data_x_min = xv
262
+ @data_x_max = xv
263
+ @data_y_min = yv
264
+ @data_y_max = yv
265
+ first = false
266
+ else
267
+ if xv < @data_x_min
268
+ @data_x_min = xv
269
+ end
270
+ if xv > @data_x_max
271
+ @data_x_max = xv
272
+ end
273
+ if yv < @data_y_min
274
+ @data_y_min = yv
275
+ end
276
+ if yv > @data_y_max
277
+ @data_y_max = yv
278
+ end
279
+ end
280
+ j = j + 1
281
+ end
282
+ si = si + 1
283
+ end
284
+ xr = @data_x_max - @data_x_min
285
+ yr = @data_y_max - @data_y_min
286
+ if xr > 0.0
287
+ @data_x_max = @data_x_max + xr * 0.05
288
+ @data_x_min = @data_x_min - xr * 0.05
289
+ else
290
+ @data_x_max = @data_x_max + 1.0
291
+ end
292
+ if yr > 0.0
293
+ @data_y_max = @data_y_max + yr * 0.05
294
+ @data_y_min = @data_y_min - yr * 0.05
295
+ else
296
+ @data_y_max = @data_y_max + 1.0
297
+ end
298
+ end
299
+ end
300
+
301
+ def ScatterChart(x_data, y_data, series_names)
302
+ ScatterChart.new(x_data, y_data, series_names)
303
+ end
@@ -0,0 +1,276 @@
1
+ # StackedBarChart - stacked bar chart widget
2
+ # Supports multiple series stacked on top of each other, hover highlight
3
+
4
+ class StackedBarChart < 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 = 0.0
12
+ @data_min = 0.0
13
+ @data_max = 0.0
14
+ @y_scale = nil
15
+ @y_ticks = nil
16
+ @band = nil
17
+ @bar_w = 2.0
18
+ @baseline_y = 0.0
19
+ @num_series = 0
20
+ @num_cats = 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_stacked_range
28
+ end
29
+
30
+ def show_values(v)
31
+ @show_values = v
32
+ self
33
+ end
34
+
35
+ def bar_radius(r)
36
+ @bar_radius = r
37
+ self
38
+ end
39
+
40
+ def set_data(categories, series_data, series_names)
41
+ @categories = categories
42
+ @series_data = series_data
43
+ @series_names = series_names
44
+ compute_stacked_range
45
+ mark_dirty
46
+ update
47
+ end
48
+
49
+ def render_chart(painter, px, py, pw, ph)
50
+ return if @categories.length == 0
51
+ return if @series_data.length == 0
52
+ @chart_px = px
53
+ @chart_py = py
54
+ @chart_pw = pw
55
+ @chart_ph = ph
56
+ @num_series = @series_data.length
57
+ @num_cats = @categories.length
58
+ setup_stacked_ticks
59
+ setup_stacked_y_scale
60
+ setup_stacked_band
61
+ @baseline_y = @y_scale.map(0.0)
62
+ bmax = @chart_py + @chart_ph
63
+ if @baseline_y > bmax
64
+ @baseline_y = bmax
65
+ end
66
+ draw_y_axis(painter, px, py, pw, ph, @y_ticks, @y_scale)
67
+ draw_x_axis_line(painter, px, py, pw, ph)
68
+ draw_stacked_cat_labels(painter)
69
+ draw_all_stacked_bars(painter)
70
+ draw_stacked_legend(painter, px, py)
71
+ end
72
+
73
+ def setup_stacked_ticks
74
+ @y_ticks = compute_ticks(@data_min, @data_max, 5)
75
+ @y_range_min = @data_min
76
+ @y_range_max = @data_max
77
+ if @y_ticks.length > 0
78
+ @y_range_min = @y_ticks[0]
79
+ @y_range_max = @y_ticks[@y_ticks.length - 1]
80
+ end
81
+ end
82
+
83
+ def setup_stacked_y_scale
84
+ bottom = @chart_py + @chart_ph
85
+ @y_scale = LinearScale.new(@y_range_min, @y_range_max, bottom, @chart_py)
86
+ end
87
+
88
+ def setup_stacked_band
89
+ right = @chart_px + @chart_pw
90
+ nc = @num_cats * 1.0
91
+ @band = BandScale.new(nc, @chart_px, right, 12.0)
92
+ @bar_w = @band.band_width
93
+ end
94
+
95
+ def draw_stacked_cat_labels(painter)
96
+ lc = $theme.text_secondary
97
+ i = 0
98
+ while i < @num_cats
99
+ i_f = i * 1.0
100
+ bx = @band.map(i_f)
101
+ label = @categories[i]
102
+ lw = painter.measure_text_width(label, $theme.font_family, 11.0)
103
+ ascent = painter.get_text_ascent($theme.font_family, 11.0)
104
+ label_x = bx + @bar_w / 2.0 - lw / 2.0
105
+ label_y = @chart_py + @chart_ph + 14.0 + ascent
106
+ painter.draw_text(label, label_x, label_y, $theme.font_family, 11.0, lc)
107
+ i = i + 1
108
+ end
109
+ end
110
+
111
+ def draw_all_stacked_bars(painter)
112
+ ci = 0
113
+ while ci < @num_cats
114
+ draw_stacked_column(painter, ci)
115
+ ci = ci + 1
116
+ end
117
+ end
118
+
119
+ def draw_stacked_column(painter, ci)
120
+ ci_f = ci * 1.0
121
+ bx = @band.map(ci_f)
122
+ cumulative = 0.0
123
+ si = 0
124
+ while si < @num_series
125
+ val = @series_data[si][ci]
126
+ top_val = cumulative + val
127
+ bar_bottom = @y_scale.map(cumulative)
128
+ bar_top = @y_scale.map(top_val)
129
+ bar_h = bar_bottom - bar_top
130
+ if bar_h < 0.0
131
+ bar_h = 0.0 - bar_h
132
+ bar_top = bar_bottom
133
+ end
134
+ c = series_color(si)
135
+ if @hover_index == ci
136
+ if @hover_series == si
137
+ c = painter.lighten_color(c, 0.3)
138
+ end
139
+ end
140
+ painter.fill_round_rect(bx, bar_top, @bar_w, bar_h, @bar_radius, c)
141
+ if @show_values
142
+ if val > 0.0
143
+ vl = painter.number_to_string(val)
144
+ vw = painter.measure_text_width(vl, $theme.font_family, 9.0)
145
+ ascent = painter.get_text_ascent($theme.font_family, 9.0)
146
+ vx = bx + @bar_w / 2.0 - vw / 2.0
147
+ vy = bar_top + bar_h / 2.0 + ascent / 2.0
148
+ painter.draw_text(vl, vx, vy, $theme.font_family, 9.0, 4294967295)
149
+ end
150
+ end
151
+ cumulative = top_val
152
+ si = si + 1
153
+ end
154
+ end
155
+
156
+ def draw_stacked_legend(painter, px, py)
157
+ if @show_legend
158
+ if @series_names.length > 1
159
+ colors = []
160
+ si = 0
161
+ while si < @series_names.length
162
+ colors << series_color(si)
163
+ si = si + 1
164
+ end
165
+ draw_legend(painter, @series_names, colors, px + 8.0, py - 20.0)
166
+ end
167
+ end
168
+ end
169
+
170
+ def update_hover
171
+ return if @band == nil
172
+ mx = @mouse_x
173
+ my = @mouse_y
174
+ px = plot_x
175
+ py = plot_y
176
+ pw = plot_w
177
+ ph = plot_h
178
+ if mx < px
179
+ @hover_index = -1
180
+ @hover_series = -1
181
+ return
182
+ end
183
+ if mx > px + pw
184
+ @hover_index = -1
185
+ @hover_series = -1
186
+ return
187
+ end
188
+ if my < py
189
+ @hover_index = -1
190
+ @hover_series = -1
191
+ return
192
+ end
193
+ if my > py + ph
194
+ @hover_index = -1
195
+ @hover_series = -1
196
+ return
197
+ end
198
+ find_stacked_hover(mx, my)
199
+ end
200
+
201
+ def find_stacked_hover(mx, my)
202
+ @hover_index = -1
203
+ @hover_series = -1
204
+ ci = 0
205
+ while ci < @num_cats
206
+ ci_f = ci * 1.0
207
+ bx = @band.map(ci_f)
208
+ if mx >= bx
209
+ if mx <= bx + @bar_w
210
+ # Found the category, now find which series segment
211
+ cumulative = 0.0
212
+ si = 0
213
+ while si < @num_series
214
+ val = @series_data[si][ci]
215
+ top_val = cumulative + val
216
+ bar_bottom = @y_scale.map(cumulative)
217
+ bar_top = @y_scale.map(top_val)
218
+ if bar_top > bar_bottom
219
+ tmp = bar_top
220
+ bar_top = bar_bottom
221
+ bar_bottom = tmp
222
+ end
223
+ if my >= bar_top
224
+ if my <= bar_bottom
225
+ @hover_index = ci
226
+ @hover_series = si
227
+ return
228
+ end
229
+ end
230
+ cumulative = top_val
231
+ si = si + 1
232
+ end
233
+ @hover_index = ci
234
+ return
235
+ end
236
+ end
237
+ ci = ci + 1
238
+ end
239
+ end
240
+
241
+ private
242
+
243
+ def compute_stacked_range
244
+ @data_min = 0.0
245
+ @data_max = 1.0
246
+ return if @series_data.length == 0
247
+ return if @categories.length == 0
248
+ max_stack = 0.0
249
+ ci = 0
250
+ while ci < @categories.length
251
+ stack_total = 0.0
252
+ si = 0
253
+ while si < @series_data.length
254
+ if ci < @series_data[si].length
255
+ stack_total = stack_total + @series_data[si][ci]
256
+ end
257
+ si = si + 1
258
+ end
259
+ if stack_total > max_stack
260
+ max_stack = stack_total
261
+ end
262
+ ci = ci + 1
263
+ end
264
+ @data_max = max_stack
265
+ range = @data_max - @data_min
266
+ if range > 0.0
267
+ @data_max = @data_max + range * 0.1
268
+ else
269
+ @data_max = @data_min + 1.0
270
+ end
271
+ end
272
+ end
273
+
274
+ def StackedBarChart(categories, series_data, series_names)
275
+ StackedBarChart.new(categories, series_data, series_names)
276
+ end