glimmer-libui-cc-graphs_and_charts 0.2.3 → 0.3.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/CHANGELOG.md +6 -0
- data/README.md +201 -3
- data/VERSION +1 -1
- data/examples/graphs_and_charts/basic_bubble_chart.rb +95 -0
- data/glimmer-libui-cc-graphs_and_charts.gemspec +7 -5
- data/lib/glimmer/view/bubble_chart.rb +579 -0
- data/lib/glimmer/view/line_graph.rb +6 -2
- metadata +7 -5
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA256:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: 05df9fa97ed359a26f750b606fabd320c0c114ab6f0f9a68806f144b0782f4c0
|
4
|
+
data.tar.gz: ca02e1bcd1c9c0997f9241a8f16718eb6e5ca72ddc0f539b052f144d92aedb8f
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: 8066883e70794b6c6d4d6ebab139d97bf1c9fe01d3b7b8ed9b94fc61f8a69b5c7a82a2b89c9e2c7eaa7dab326cf40f3da2f348e7a1f2945c16604850b9bab0b9
|
7
|
+
data.tar.gz: 3dd5b11b42f701d3f30064060c6c08bb01bcb1391316ec307725b9b8a668c8052d22fffafa794555423ed15b38155924fdaf48019437177114415331488fed84
|
data/CHANGELOG.md
CHANGED
@@ -1,5 +1,11 @@
|
|
1
1
|
# Change Log
|
2
2
|
|
3
|
+
## 0.3.0
|
4
|
+
|
5
|
+
- Initial implementation of `bubble_chart` custom control
|
6
|
+
- New `examples/graphs_and_charts/basic_bubble_chart.rb`
|
7
|
+
- Ensure that dynamically setting `lines` option in `line_graph` normalizes `lines` into `Array` if value is a `Hash`
|
8
|
+
|
3
9
|
## 0.2.3
|
4
10
|
|
5
11
|
- Automatically scale number of `bar_chart` horizontal grid markers so that if the chart width gets small enough for them to run into each other, less of them are displayed
|
data/README.md
CHANGED
@@ -1,20 +1,22 @@
|
|
1
|
-
# Graphs and Charts 0.
|
1
|
+
# Graphs and Charts 0.3.0 (Alpha)
|
2
2
|
## [Glimmer DSL for LibUI](https://github.com/AndyObtiva/glimmer-dsl-libui) Custom Controls
|
3
3
|
[](http://badge.fury.io/rb/glimmer-libui-cc-graphs_and_charts)
|
4
4
|
[](https://gitter.im/AndyObtiva/glimmer?utm_source=badge&utm_medium=badge&utm_campaign=pr-badge&utm_content=badge)
|
5
5
|
|
6
|
-
Graphs and Charts (Custom Controls
|
6
|
+
Graphs and Charts (Custom Controls for [Glimmer DSL for LibUI](https://github.com/AndyObtiva/glimmer-dsl-libui))
|
7
7
|
|
8
8
|

|
9
9
|
|
10
10
|

|
11
11
|
|
12
|
+

|
13
|
+
|
12
14
|
## Setup
|
13
15
|
|
14
16
|
Add this line to Bundler `Gemfile`:
|
15
17
|
|
16
18
|
```ruby
|
17
|
-
gem 'glimmer-libui-cc-graphs_and_charts', '~> 0.
|
19
|
+
gem 'glimmer-libui-cc-graphs_and_charts', '~> 0.3.0'
|
18
20
|
```
|
19
21
|
|
20
22
|
Run:
|
@@ -314,6 +316,202 @@ BasicLineGraphRelative.launch
|
|
314
316
|
|
315
317
|

|
316
318
|
|
319
|
+
### Bubble Chart
|
320
|
+
|
321
|
+
To load the `bubble_chart` custom control, add this line to your Ruby file:
|
322
|
+
|
323
|
+
```ruby
|
324
|
+
require 'glimmer/view/bubble_chart'
|
325
|
+
```
|
326
|
+
|
327
|
+
This makes the `bubble_chart` [Glimmer DSL for LibUI Custom Control](https://github.com/AndyObtiva/glimmer-dsl-libui#custom-components) available in the Glimmer GUI DSL.
|
328
|
+
You can then nest `bubble_chart` under `window` or some container like `vertical_box`. By the way, `bubble_chart` is implemented on top of the [`area` Glimmer DSL for LibUI control](https://github.com/AndyObtiva/glimmer-dsl-libui#area-api).
|
329
|
+
|
330
|
+
`values` are a `Hash` map of `Time` x-axis values to `Hash` map of `Numeric` y-axis values to `Numeric` z-axis values.
|
331
|
+
|
332
|
+
```ruby
|
333
|
+
bubble_chart(
|
334
|
+
width: 900,
|
335
|
+
height: 300,
|
336
|
+
chart_color_bubble: [239, 9, 9],
|
337
|
+
values: {
|
338
|
+
Time.new(2030, 12, 1, 13, 0, 0) => {
|
339
|
+
1 => 4,
|
340
|
+
2 => 8,
|
341
|
+
8 => 3,
|
342
|
+
10 => 0
|
343
|
+
},
|
344
|
+
Time.new(2030, 12, 1, 13, 0, 2) => {
|
345
|
+
1 => 1,
|
346
|
+
2 => 5,
|
347
|
+
7 => 2,
|
348
|
+
10 => 0
|
349
|
+
},
|
350
|
+
Time.new(2030, 12, 1, 13, 0, 4) => {
|
351
|
+
1 => 2,
|
352
|
+
2 => 3,
|
353
|
+
4 => 4,
|
354
|
+
10 => 0
|
355
|
+
},
|
356
|
+
Time.new(2030, 12, 1, 13, 0, 6) => {
|
357
|
+
1 => 7,
|
358
|
+
2 => 2,
|
359
|
+
7 => 5,
|
360
|
+
10 => 0
|
361
|
+
},
|
362
|
+
Time.new(2030, 12, 1, 13, 0, 8) => {
|
363
|
+
1 => 6,
|
364
|
+
2 => 8,
|
365
|
+
8 => 1,
|
366
|
+
10 => 0
|
367
|
+
},
|
368
|
+
Time.new(2030, 12, 1, 13, 0, 10) => {
|
369
|
+
1 => 1,
|
370
|
+
2 => 2,
|
371
|
+
3 => 9,
|
372
|
+
10 => 0
|
373
|
+
},
|
374
|
+
Time.new(2030, 12, 1, 13, 0, 12) => {
|
375
|
+
1 => 5,
|
376
|
+
2 => 12,
|
377
|
+
5 => 17,
|
378
|
+
10 => 0
|
379
|
+
},
|
380
|
+
Time.new(2030, 12, 1, 13, 0, 14) => {
|
381
|
+
1 => 9,
|
382
|
+
2 => 2,
|
383
|
+
6 => 10,
|
384
|
+
10 => 0
|
385
|
+
},
|
386
|
+
Time.new(2030, 12, 1, 13, 0, 16) => {
|
387
|
+
1 => 0,
|
388
|
+
2 => 5,
|
389
|
+
7 => 8,
|
390
|
+
10 => 0
|
391
|
+
},
|
392
|
+
Time.new(2030, 12, 1, 13, 0, 18) => {
|
393
|
+
1 => 9,
|
394
|
+
3 => 3,
|
395
|
+
5 => 6,
|
396
|
+
10 => 0
|
397
|
+
},
|
398
|
+
Time.new(2030, 12, 1, 13, 0, 20) => {
|
399
|
+
2 => 2,
|
400
|
+
4 => 4,
|
401
|
+
7 => 7,
|
402
|
+
10 => 0
|
403
|
+
},
|
404
|
+
},
|
405
|
+
x_value_format: -> (time) {time.strftime('%M:%S')},
|
406
|
+
)
|
407
|
+
```
|
408
|
+
|
409
|
+

|
410
|
+
|
411
|
+
Look into [lib/glimmer/view/bar_chart.rb](/lib/glimmer/view/bar_chart.rb) to learn about all supported options.
|
412
|
+
|
413
|
+
**Basic Bubble Chart Example:**
|
414
|
+
|
415
|
+
[examples/graphs_and_charts/basic_bar_chart.rb](/examples/graphs_and_charts/basic_bar_chart.rb)
|
416
|
+
|
417
|
+
```ruby
|
418
|
+
require 'glimmer-dsl-libui'
|
419
|
+
require 'glimmer/view/bubble_chart'
|
420
|
+
|
421
|
+
class BasicBubbleChart
|
422
|
+
include Glimmer::LibUI::Application
|
423
|
+
|
424
|
+
body {
|
425
|
+
window('Basic Line Graph', 900, 300) { |main_window|
|
426
|
+
@bubble_chart = bubble_chart(
|
427
|
+
width: 900,
|
428
|
+
height: 300,
|
429
|
+
chart_color_bubble: [239, 9, 9],
|
430
|
+
values: {
|
431
|
+
Time.new(2030, 12, 1, 13, 0, 0) => {
|
432
|
+
1 => 4,
|
433
|
+
2 => 8,
|
434
|
+
8 => 3,
|
435
|
+
10 => 0
|
436
|
+
},
|
437
|
+
Time.new(2030, 12, 1, 13, 0, 2) => {
|
438
|
+
1 => 1,
|
439
|
+
2 => 5,
|
440
|
+
7 => 2,
|
441
|
+
10 => 0
|
442
|
+
},
|
443
|
+
Time.new(2030, 12, 1, 13, 0, 4) => {
|
444
|
+
1 => 2,
|
445
|
+
2 => 3,
|
446
|
+
4 => 4,
|
447
|
+
10 => 0
|
448
|
+
},
|
449
|
+
Time.new(2030, 12, 1, 13, 0, 6) => {
|
450
|
+
1 => 7,
|
451
|
+
2 => 2,
|
452
|
+
7 => 5,
|
453
|
+
10 => 0
|
454
|
+
},
|
455
|
+
Time.new(2030, 12, 1, 13, 0, 8) => {
|
456
|
+
1 => 6,
|
457
|
+
2 => 8,
|
458
|
+
8 => 1,
|
459
|
+
10 => 0
|
460
|
+
},
|
461
|
+
Time.new(2030, 12, 1, 13, 0, 10) => {
|
462
|
+
1 => 1,
|
463
|
+
2 => 2,
|
464
|
+
3 => 9,
|
465
|
+
10 => 0
|
466
|
+
},
|
467
|
+
Time.new(2030, 12, 1, 13, 0, 12) => {
|
468
|
+
1 => 5,
|
469
|
+
2 => 12,
|
470
|
+
5 => 17,
|
471
|
+
10 => 0
|
472
|
+
},
|
473
|
+
Time.new(2030, 12, 1, 13, 0, 14) => {
|
474
|
+
1 => 9,
|
475
|
+
2 => 2,
|
476
|
+
6 => 10,
|
477
|
+
10 => 0
|
478
|
+
},
|
479
|
+
Time.new(2030, 12, 1, 13, 0, 16) => {
|
480
|
+
1 => 0,
|
481
|
+
2 => 5,
|
482
|
+
7 => 8,
|
483
|
+
10 => 0
|
484
|
+
},
|
485
|
+
Time.new(2030, 12, 1, 13, 0, 18) => {
|
486
|
+
1 => 9,
|
487
|
+
3 => 3,
|
488
|
+
5 => 6,
|
489
|
+
10 => 0
|
490
|
+
},
|
491
|
+
Time.new(2030, 12, 1, 13, 0, 20) => {
|
492
|
+
2 => 2,
|
493
|
+
4 => 4,
|
494
|
+
7 => 7,
|
495
|
+
10 => 0
|
496
|
+
},
|
497
|
+
},
|
498
|
+
x_value_format: -> (time) {time.strftime('%M:%S')},
|
499
|
+
)
|
500
|
+
|
501
|
+
on_content_size_changed do
|
502
|
+
@bubble_chart.width = main_window.content_size[0]
|
503
|
+
@bubble_chart.height = main_window.content_size[1]
|
504
|
+
end
|
505
|
+
}
|
506
|
+
}
|
507
|
+
end
|
508
|
+
|
509
|
+
BasicBubbleChart.launch
|
510
|
+
```
|
511
|
+
|
512
|
+

|
513
|
+
|
514
|
+
|
317
515
|
Contributing to glimmer-libui-cc-graphs_and_charts
|
318
516
|
------------------------------------------
|
319
517
|
|
data/VERSION
CHANGED
@@ -1 +1 @@
|
|
1
|
-
0.
|
1
|
+
0.3.0
|
@@ -0,0 +1,95 @@
|
|
1
|
+
# This line is only needed when running the example from inside the project directory
|
2
|
+
$LOAD_PATH.prepend(File.expand_path(File.join(__dir__, '..', '..', 'lib'))) if File.exist?(File.join(__dir__, '..', '..', 'lib'))
|
3
|
+
|
4
|
+
require 'glimmer-dsl-libui'
|
5
|
+
require 'glimmer/view/bubble_chart'
|
6
|
+
|
7
|
+
class BasicBubbleChart
|
8
|
+
include Glimmer::LibUI::Application
|
9
|
+
|
10
|
+
body {
|
11
|
+
window('Basic Bubble Chart', 900, 300) { |main_window|
|
12
|
+
@bubble_chart = bubble_chart(
|
13
|
+
width: 900,
|
14
|
+
height: 300,
|
15
|
+
chart_color_bubble: [239, 9, 9],
|
16
|
+
values: {
|
17
|
+
Time.new(2030, 12, 1, 13, 0, 0) => {
|
18
|
+
1 => 4,
|
19
|
+
2 => 8,
|
20
|
+
8 => 3,
|
21
|
+
10 => 0
|
22
|
+
},
|
23
|
+
Time.new(2030, 12, 1, 13, 0, 2) => {
|
24
|
+
1 => 1,
|
25
|
+
2 => 5,
|
26
|
+
7 => 2,
|
27
|
+
10 => 0
|
28
|
+
},
|
29
|
+
Time.new(2030, 12, 1, 13, 0, 4) => {
|
30
|
+
1 => 2,
|
31
|
+
2 => 3,
|
32
|
+
4 => 4,
|
33
|
+
10 => 0
|
34
|
+
},
|
35
|
+
Time.new(2030, 12, 1, 13, 0, 6) => {
|
36
|
+
1 => 7,
|
37
|
+
2 => 2,
|
38
|
+
7 => 5,
|
39
|
+
10 => 0
|
40
|
+
},
|
41
|
+
Time.new(2030, 12, 1, 13, 0, 8) => {
|
42
|
+
1 => 6,
|
43
|
+
2 => 8,
|
44
|
+
8 => 1,
|
45
|
+
10 => 0
|
46
|
+
},
|
47
|
+
Time.new(2030, 12, 1, 13, 0, 10) => {
|
48
|
+
1 => 1,
|
49
|
+
2 => 2,
|
50
|
+
3 => 9,
|
51
|
+
10 => 0
|
52
|
+
},
|
53
|
+
Time.new(2030, 12, 1, 13, 0, 12) => {
|
54
|
+
1 => 5,
|
55
|
+
2 => 12,
|
56
|
+
5 => 17,
|
57
|
+
10 => 0
|
58
|
+
},
|
59
|
+
Time.new(2030, 12, 1, 13, 0, 14) => {
|
60
|
+
1 => 9,
|
61
|
+
2 => 2,
|
62
|
+
6 => 10,
|
63
|
+
10 => 0
|
64
|
+
},
|
65
|
+
Time.new(2030, 12, 1, 13, 0, 16) => {
|
66
|
+
1 => 0,
|
67
|
+
2 => 5,
|
68
|
+
7 => 8,
|
69
|
+
10 => 0
|
70
|
+
},
|
71
|
+
Time.new(2030, 12, 1, 13, 0, 18) => {
|
72
|
+
1 => 9,
|
73
|
+
3 => 3,
|
74
|
+
5 => 6,
|
75
|
+
10 => 0
|
76
|
+
},
|
77
|
+
Time.new(2030, 12, 1, 13, 0, 20) => {
|
78
|
+
2 => 2,
|
79
|
+
4 => 4,
|
80
|
+
7 => 7,
|
81
|
+
10 => 0
|
82
|
+
},
|
83
|
+
},
|
84
|
+
x_value_format: -> (time) {time.strftime('%M:%S')},
|
85
|
+
)
|
86
|
+
|
87
|
+
on_content_size_changed do
|
88
|
+
@bubble_chart.width = main_window.content_size[0]
|
89
|
+
@bubble_chart.height = main_window.content_size[1]
|
90
|
+
end
|
91
|
+
}
|
92
|
+
}
|
93
|
+
end
|
94
|
+
|
95
|
+
BasicBubbleChart.launch
|
@@ -2,17 +2,17 @@
|
|
2
2
|
# DO NOT EDIT THIS FILE DIRECTLY
|
3
3
|
# Instead, edit Juwelier::Tasks in Rakefile, and run 'rake gemspec'
|
4
4
|
# -*- encoding: utf-8 -*-
|
5
|
-
# stub: glimmer-libui-cc-graphs_and_charts 0.
|
5
|
+
# stub: glimmer-libui-cc-graphs_and_charts 0.3.0 ruby lib
|
6
6
|
|
7
7
|
Gem::Specification.new do |s|
|
8
8
|
s.name = "glimmer-libui-cc-graphs_and_charts".freeze
|
9
|
-
s.version = "0.
|
9
|
+
s.version = "0.3.0".freeze
|
10
10
|
|
11
11
|
s.required_rubygems_version = Gem::Requirement.new(">= 0".freeze) if s.respond_to? :required_rubygems_version=
|
12
12
|
s.require_paths = ["lib".freeze]
|
13
13
|
s.authors = ["Andy Maleh".freeze]
|
14
|
-
s.date = "2024-
|
15
|
-
s.description = "Graphs and Charts (
|
14
|
+
s.date = "2024-02-17"
|
15
|
+
s.description = "Graphs and Charts (Glimmer DSL for LibUI Custom Controls), like Line Graph, Bar Chart, and Bubble Chart.".freeze
|
16
16
|
s.email = "andy.am@gmail.com".freeze
|
17
17
|
s.extra_rdoc_files = [
|
18
18
|
"CHANGELOG.md",
|
@@ -25,17 +25,19 @@ Gem::Specification.new do |s|
|
|
25
25
|
"README.md",
|
26
26
|
"VERSION",
|
27
27
|
"examples/graphs_and_charts/basic_bar_chart.rb",
|
28
|
+
"examples/graphs_and_charts/basic_bubble_chart.rb",
|
28
29
|
"examples/graphs_and_charts/basic_line_graph.rb",
|
29
30
|
"examples/graphs_and_charts/basic_line_graph_relative.rb",
|
30
31
|
"glimmer-libui-cc-graphs_and_charts.gemspec",
|
31
32
|
"lib/glimmer-libui-cc-graphs_and_charts.rb",
|
32
33
|
"lib/glimmer/view/bar_chart.rb",
|
34
|
+
"lib/glimmer/view/bubble_chart.rb",
|
33
35
|
"lib/glimmer/view/line_graph.rb"
|
34
36
|
]
|
35
37
|
s.homepage = "http://github.com/AndyObtiva/glimmer-libui-cc-graphs_and_charts".freeze
|
36
38
|
s.licenses = ["MIT".freeze]
|
37
39
|
s.rubygems_version = "3.5.3".freeze
|
38
|
-
s.summary = "Graphs and Charts
|
40
|
+
s.summary = "Graphs and Charts (Glimmer DSL for LibUI Custom Controls)".freeze
|
39
41
|
|
40
42
|
s.specification_version = 4
|
41
43
|
|
@@ -0,0 +1,579 @@
|
|
1
|
+
require 'glimmer-dsl-libui'
|
2
|
+
|
3
|
+
module Glimmer
|
4
|
+
module View
|
5
|
+
# General-Purpose Bubble Chart Custom Control
|
6
|
+
class BubbleChart
|
7
|
+
class << self
|
8
|
+
def interpret_color(color_object)
|
9
|
+
@color_cache ||= {}
|
10
|
+
@color_cache[color_object] ||= Glimmer::LibUI.interpret_color(color_object)
|
11
|
+
end
|
12
|
+
end
|
13
|
+
|
14
|
+
include Glimmer::LibUI::CustomControl
|
15
|
+
|
16
|
+
DEFAULT_CHART_PADDING_WIDTH = 5.0
|
17
|
+
DEFAULT_CHART_PADDING_HEIGHT = 5.0
|
18
|
+
DEFAULT_CHART_GRID_MARKER_PADDING_WIDTH = 37.0
|
19
|
+
DEFAULT_CHART_POINT_DISTANCE = 15.0
|
20
|
+
DEFAULT_CHART_POINT_RADIUS = 1.0
|
21
|
+
DEFAULT_CHART_SELECTED_POINT_RADIUS = 3.0
|
22
|
+
|
23
|
+
DEFAULT_CHART_STROKE_GRID = [185, 184, 185]
|
24
|
+
DEFAULT_CHART_STROKE_MARKER = [185, 184, 185]
|
25
|
+
DEFAULT_CHART_STROKE_MARKER_LINE = [217, 217, 217, thickness: 1, dashes: [1, 1]]
|
26
|
+
DEFAULT_CHART_STROKE_PERIODIC_LINE = [121, 121, 121, thickness: 1, dashes: [1, 1]]
|
27
|
+
DEFAULT_CHART_STROKE_HOVER_LINE = [133, 133, 133]
|
28
|
+
|
29
|
+
DEFAULT_CHART_FILL_SELECTED_POINT = :white
|
30
|
+
|
31
|
+
DEFAULT_CHART_COLOR_BUBBLE = [92, 122, 190]
|
32
|
+
DEFAULT_CHART_COLOR_MARKER_TEXT = [96, 96, 96]
|
33
|
+
DEFAULT_CHART_COLOR_PERIOD_TEXT = [163, 40, 39]
|
34
|
+
|
35
|
+
DEFAULT_CHART_FONT_MARKER_TEXT = {family: "Arial", size: 14}
|
36
|
+
|
37
|
+
DEFAULT_CHART_STATUS_HEIGHT = 30.0
|
38
|
+
|
39
|
+
DAY_IN_SECONDS = 60*60*24
|
40
|
+
|
41
|
+
option :width, default: 600
|
42
|
+
option :height, default: 200
|
43
|
+
|
44
|
+
option :lines, default: [] # TODO remove this once conversion of code to bubble chart is complete
|
45
|
+
option :values, default: []
|
46
|
+
|
47
|
+
option :chart_padding_width, default: DEFAULT_CHART_PADDING_WIDTH
|
48
|
+
option :chart_padding_height, default: DEFAULT_CHART_PADDING_HEIGHT
|
49
|
+
option :chart_grid_marker_padding_width, default: DEFAULT_CHART_GRID_MARKER_PADDING_WIDTH
|
50
|
+
option :chart_point_distance, default: DEFAULT_CHART_POINT_DISTANCE
|
51
|
+
option :chart_point_radius, default: DEFAULT_CHART_POINT_RADIUS
|
52
|
+
option :chart_selected_point_radius, default: DEFAULT_CHART_SELECTED_POINT_RADIUS
|
53
|
+
|
54
|
+
option :chart_stroke_grid, default: DEFAULT_CHART_STROKE_GRID
|
55
|
+
option :chart_stroke_marker, default: DEFAULT_CHART_STROKE_MARKER
|
56
|
+
option :chart_stroke_marker_line, default: DEFAULT_CHART_STROKE_MARKER_LINE
|
57
|
+
option :chart_stroke_periodic_line, default: DEFAULT_CHART_STROKE_PERIODIC_LINE
|
58
|
+
option :chart_stroke_hover_line, default: DEFAULT_CHART_STROKE_HOVER_LINE
|
59
|
+
|
60
|
+
option :chart_fill_selected_point, default: DEFAULT_CHART_FILL_SELECTED_POINT
|
61
|
+
|
62
|
+
option :chart_color_bubble, default: DEFAULT_CHART_COLOR_BUBBLE
|
63
|
+
option :chart_color_marker_text, default: DEFAULT_CHART_COLOR_MARKER_TEXT
|
64
|
+
option :chart_color_period_text, default: DEFAULT_CHART_COLOR_PERIOD_TEXT
|
65
|
+
|
66
|
+
option :chart_font_marker_text, default: DEFAULT_CHART_FONT_MARKER_TEXT
|
67
|
+
|
68
|
+
option :chart_status_height, default: DEFAULT_CHART_STATUS_HEIGHT
|
69
|
+
|
70
|
+
option :display_attributes_on_hover, default: false
|
71
|
+
|
72
|
+
before_body do
|
73
|
+
generate_lines
|
74
|
+
end
|
75
|
+
|
76
|
+
after_body do
|
77
|
+
observe(self, :values) do
|
78
|
+
generate_lines
|
79
|
+
clear_drawing_cache
|
80
|
+
body_root.queue_redraw_all
|
81
|
+
end
|
82
|
+
observe(self, :width) do
|
83
|
+
clear_drawing_cache
|
84
|
+
end
|
85
|
+
observe(self, :height) do
|
86
|
+
clear_drawing_cache
|
87
|
+
end
|
88
|
+
end
|
89
|
+
|
90
|
+
body {
|
91
|
+
area { |chart_area|
|
92
|
+
on_draw do
|
93
|
+
calculate_dynamic_options
|
94
|
+
chart_background
|
95
|
+
grid_lines
|
96
|
+
all_bubble_charts
|
97
|
+
periodic_lines
|
98
|
+
hover_stats
|
99
|
+
end
|
100
|
+
|
101
|
+
on_mouse_moved do |event|
|
102
|
+
@hover_point = {x: event[:x], y: event[:y]}
|
103
|
+
|
104
|
+
if @hover_point && lines && lines[0] && @points && @points[lines[0]] && !@points[lines[0]].empty?
|
105
|
+
x = @hover_point[:x]
|
106
|
+
closest_point_index = ((width - chart_padding_width - x) / chart_point_distance_for_line(lines[0])).round
|
107
|
+
if closest_point_index != @closest_point_index
|
108
|
+
@closest_point_index = closest_point_index
|
109
|
+
chart_area.queue_redraw_all
|
110
|
+
end
|
111
|
+
end
|
112
|
+
end
|
113
|
+
|
114
|
+
on_mouse_exited do |outside|
|
115
|
+
if !@hover_point.nil?
|
116
|
+
@hover_point = nil
|
117
|
+
@closest_point_index = nil
|
118
|
+
chart_area.queue_redraw_all
|
119
|
+
end
|
120
|
+
end
|
121
|
+
}
|
122
|
+
}
|
123
|
+
|
124
|
+
private
|
125
|
+
|
126
|
+
def generate_lines
|
127
|
+
normalized_values = []
|
128
|
+
values.each do |x_value, y_z_hash|
|
129
|
+
y_z_hash.each do |y_value, z_value|
|
130
|
+
normalized_values << {x_value: x_value, y_value: y_value, z_value: z_value}
|
131
|
+
end
|
132
|
+
end
|
133
|
+
normalized_lines_values = []
|
134
|
+
normalized_values.each do |normalized_value|
|
135
|
+
normalized_line_values = normalized_lines_values.detect do |line|
|
136
|
+
!line.include?(normalized_value[:x_value])
|
137
|
+
end
|
138
|
+
if normalized_line_values.nil?
|
139
|
+
normalized_line_values = {}
|
140
|
+
normalized_lines_values << normalized_line_values
|
141
|
+
end
|
142
|
+
normalized_line_values[normalized_value[:x_value]] = normalized_value[:y_value]
|
143
|
+
end
|
144
|
+
self.lines = normalized_lines_values.map do |normalized_line_values|
|
145
|
+
# TODO take name from component options/constants
|
146
|
+
{name: 'Bubble Chart', values: normalized_line_values}
|
147
|
+
end
|
148
|
+
end
|
149
|
+
|
150
|
+
def clear_drawing_cache
|
151
|
+
@chart_point_distance_per_line = nil
|
152
|
+
@y_value_max_for_all_lines = nil
|
153
|
+
@x_resolution = nil
|
154
|
+
@y_resolution = nil
|
155
|
+
@x_value_range_for_all_lines = nil
|
156
|
+
@x_value_min_for_all_lines = nil
|
157
|
+
@x_value_max_for_all_lines = nil
|
158
|
+
@grid_marker_points = nil
|
159
|
+
@points = nil
|
160
|
+
@grid_marker_number_values = nil
|
161
|
+
@grid_marker_numbers = nil
|
162
|
+
@chart_stroke_marker_values = nil
|
163
|
+
@mod_values = nil
|
164
|
+
end
|
165
|
+
|
166
|
+
def calculate_dynamic_options
|
167
|
+
calculate_chart_point_distance_per_line
|
168
|
+
end
|
169
|
+
|
170
|
+
def calculate_chart_point_distance_per_line
|
171
|
+
return unless lines[0]&.[](:y_values) && chart_point_distance == :width_divided_by_point_count
|
172
|
+
|
173
|
+
@chart_point_distance_per_line ||= lines.inject({}) do |hash, line|
|
174
|
+
value = (width - 2.0*chart_padding_width - chart_grid_marker_padding_width) / (line[:y_values].size - 1).to_f
|
175
|
+
value = [value, width_drawable].min
|
176
|
+
hash.merge(line => value)
|
177
|
+
end
|
178
|
+
end
|
179
|
+
|
180
|
+
def width_drawable
|
181
|
+
width - 2.0*chart_padding_width - chart_grid_marker_padding_width
|
182
|
+
end
|
183
|
+
|
184
|
+
def height_drawable
|
185
|
+
height - 2.0*chart_padding_height
|
186
|
+
end
|
187
|
+
|
188
|
+
def chart_point_distance_for_line(line)
|
189
|
+
@chart_point_distance_per_line&.[](line) || chart_point_distance
|
190
|
+
end
|
191
|
+
|
192
|
+
def chart_background
|
193
|
+
rectangle(0, 0, width, height + (display_attributes_on_hover ? chart_status_height : 0)) {
|
194
|
+
fill 255, 255, 255
|
195
|
+
}
|
196
|
+
end
|
197
|
+
|
198
|
+
def grid_lines
|
199
|
+
line(chart_padding_width, chart_padding_height, chart_padding_width, height - chart_padding_height) {
|
200
|
+
stroke chart_stroke_grid
|
201
|
+
}
|
202
|
+
line(chart_padding_width, height - chart_padding_height, width - chart_padding_width, height - chart_padding_height) {
|
203
|
+
stroke chart_stroke_grid
|
204
|
+
}
|
205
|
+
grid_marker_number_font = chart_font_marker_text.merge(size: 11)
|
206
|
+
@grid_marker_number_values ||= []
|
207
|
+
@grid_marker_numbers ||= []
|
208
|
+
@chart_stroke_marker_values ||= []
|
209
|
+
@mod_values ||= []
|
210
|
+
grid_marker_points.each_with_index do |marker_point, index|
|
211
|
+
@grid_marker_number_values[index] ||= begin
|
212
|
+
value = (grid_marker_points.size - index).to_i
|
213
|
+
value = y_value_max_for_all_lines if !y_value_max_for_all_lines.nil? && y_value_max_for_all_lines.to_i != y_value_max_for_all_lines && index == 0
|
214
|
+
value
|
215
|
+
end
|
216
|
+
grid_marker_number_value = @grid_marker_number_values[index]
|
217
|
+
# TODO consider not caching the following line as that might save memory and run faster without caching
|
218
|
+
@grid_marker_numbers[index] ||= (grid_marker_number_value >= 1000) ? "#{grid_marker_number_value / 1000}K" : grid_marker_number_value.to_s
|
219
|
+
grid_marker_number = @grid_marker_numbers[index]
|
220
|
+
@chart_stroke_marker_values[index] ||= BubbleChart.interpret_color(chart_stroke_marker).tap do |color_hash|
|
221
|
+
color_hash[:thickness] = (index != grid_marker_points.size - 1 ? 2 : 1) if color_hash[:thickness].nil?
|
222
|
+
end
|
223
|
+
chart_stroke_marker_value = @chart_stroke_marker_values[index]
|
224
|
+
@mod_values[index] ||= begin
|
225
|
+
mod_value_multiplier = ((grid_marker_points.size / max_marker_count) + 1)
|
226
|
+
[((mod_value_multiplier <= 2 ? 2 : 5) * mod_value_multiplier), 1].max
|
227
|
+
end
|
228
|
+
mod_value = @mod_values[index]
|
229
|
+
comparison_value = (mod_value > 2) ? 0 : 1
|
230
|
+
if mod_value > 2
|
231
|
+
if grid_marker_number_value % mod_value == comparison_value
|
232
|
+
line(marker_point[:x], marker_point[:y], marker_point[:x] + 4, marker_point[:y]) {
|
233
|
+
stroke chart_stroke_marker_value
|
234
|
+
}
|
235
|
+
end
|
236
|
+
else
|
237
|
+
line(marker_point[:x], marker_point[:y], marker_point[:x] + 4, marker_point[:y]) {
|
238
|
+
stroke chart_stroke_marker_value
|
239
|
+
}
|
240
|
+
end
|
241
|
+
if grid_marker_number_value % mod_value == comparison_value && grid_marker_number_value != grid_marker_points.size
|
242
|
+
line(marker_point[:x], marker_point[:y], marker_point[:x] + width - chart_padding_width, marker_point[:y]) {
|
243
|
+
stroke chart_stroke_marker_line
|
244
|
+
}
|
245
|
+
end
|
246
|
+
if grid_marker_number_value % mod_value == comparison_value || grid_marker_number_value != grid_marker_number_value.to_i
|
247
|
+
grid_marker_number_width = estimate_width_of_text(grid_marker_number, grid_marker_number_font)
|
248
|
+
text(marker_point[:x] + 4 + 3, marker_point[:y] - 6, grid_marker_number_width) {
|
249
|
+
string(grid_marker_number) {
|
250
|
+
font grid_marker_number_font
|
251
|
+
color chart_color_marker_text
|
252
|
+
}
|
253
|
+
}
|
254
|
+
end
|
255
|
+
end
|
256
|
+
end
|
257
|
+
|
258
|
+
def grid_marker_points
|
259
|
+
if @grid_marker_points.nil?
|
260
|
+
if lines[0]&.[](:y_values)
|
261
|
+
chart_y_max = [y_value_max_for_all_lines, 1].max
|
262
|
+
current_chart_height = (height - chart_padding_height * 2)
|
263
|
+
division_height = current_chart_height / chart_y_max
|
264
|
+
@grid_marker_points = chart_y_max.to_i.times.map do |marker_index|
|
265
|
+
x = chart_padding_width
|
266
|
+
y = chart_padding_height + marker_index * division_height
|
267
|
+
{x: x, y: y}
|
268
|
+
end
|
269
|
+
else
|
270
|
+
chart_y_max = y_value_max_for_all_lines
|
271
|
+
y_value_count = chart_y_max.ceil
|
272
|
+
@grid_marker_points = y_value_count.times.map do |marker_index|
|
273
|
+
x = chart_padding_width
|
274
|
+
y_value = y_value_count - marker_index
|
275
|
+
if marker_index == 0 && chart_y_max.ceil != chart_y_max.to_i
|
276
|
+
y_value = chart_y_max
|
277
|
+
end
|
278
|
+
scaled_y_value = y_value.to_f * y_resolution.to_f
|
279
|
+
y = height - chart_padding_height - scaled_y_value
|
280
|
+
{x: x, y: y}
|
281
|
+
end
|
282
|
+
end
|
283
|
+
end
|
284
|
+
|
285
|
+
@grid_marker_points
|
286
|
+
end
|
287
|
+
|
288
|
+
def max_marker_count
|
289
|
+
[(0.15*height).to_i, 1].max
|
290
|
+
end
|
291
|
+
|
292
|
+
def all_bubble_charts
|
293
|
+
lines.each { |chart_line| single_bubble_chart(chart_line) }
|
294
|
+
end
|
295
|
+
|
296
|
+
def single_bubble_chart(chart_line)
|
297
|
+
points = calculate_points(chart_line)
|
298
|
+
points.to_a.each do |point|
|
299
|
+
# circle(point[:x], point[:y], chart_point_radius) {
|
300
|
+
circle(point[:x], point[:y], point[:z]) {
|
301
|
+
fill chart_color_bubble
|
302
|
+
}
|
303
|
+
end
|
304
|
+
end
|
305
|
+
|
306
|
+
def calculate_points(chart_line)
|
307
|
+
if lines[0]&.[](:y_values)
|
308
|
+
calculate_points_relative(chart_line)
|
309
|
+
else
|
310
|
+
calculate_points_absolute(chart_line)
|
311
|
+
end
|
312
|
+
end
|
313
|
+
|
314
|
+
def calculate_points_relative(chart_line)
|
315
|
+
@points ||= {}
|
316
|
+
if @points[chart_line].nil?
|
317
|
+
y_values = chart_line[:y_values] || []
|
318
|
+
y_values = y_values[0, max_visible_point_count(chart_line)]
|
319
|
+
chart_y_max = [y_value_max_for_all_lines, 1].max
|
320
|
+
points = y_values.each_with_index.map do |y_value, index|
|
321
|
+
x = width - chart_padding_width - (index * chart_point_distance_for_line(chart_line))
|
322
|
+
y = ((height - chart_padding_height) - y_value * ((height - chart_padding_height * 2) / chart_y_max))
|
323
|
+
{x: x, y: y, index: index, y_value: y_value}
|
324
|
+
end
|
325
|
+
@points[chart_line] = translate_points(chart_line, points)
|
326
|
+
end
|
327
|
+
@points[chart_line]
|
328
|
+
end
|
329
|
+
|
330
|
+
def calculate_points_absolute(chart_line)
|
331
|
+
@points ||= {}
|
332
|
+
# and then use them to produce a :z key in the hash below
|
333
|
+
if @points[chart_line].nil?
|
334
|
+
values = chart_line[:values] || []
|
335
|
+
# all points are visible when :values is supplied because we stretch the chart to show them all
|
336
|
+
chart_y_max = [y_value_max_for_all_lines, 1].max
|
337
|
+
x_value_range_for_all_lines
|
338
|
+
points = values.each_with_index.map do |(x_value, y_value), index|
|
339
|
+
z_value = self.values[x_value][y_value]
|
340
|
+
relative_x_value = x_value - x_value_min_for_all_lines
|
341
|
+
scaled_x_value = x_value_range_for_all_lines == 0 ? 0 : relative_x_value.to_f * x_resolution.to_f
|
342
|
+
scaled_y_value = y_value_max_for_all_lines == 0 ? 0 : y_value.to_f * y_resolution.to_f
|
343
|
+
x = width - chart_padding_width - scaled_x_value
|
344
|
+
y = height - chart_padding_height - scaled_y_value
|
345
|
+
# z = z_value == 0 ? z_value : z_value + 1 # TODO change 1 with magnification factor or something
|
346
|
+
z = z_value
|
347
|
+
{x: x, y: y, z: z, index: index, x_value: x_value, y_value: y_value}
|
348
|
+
end
|
349
|
+
# Translation is not supported today
|
350
|
+
# TODO consider supporting in the future
|
351
|
+
# @points[chart_line] = translate_points(chart_line, points)
|
352
|
+
@points[chart_line] = points
|
353
|
+
end
|
354
|
+
@points[chart_line]
|
355
|
+
end
|
356
|
+
|
357
|
+
# this is the multiplier that we must multiply by the relative x value
|
358
|
+
def x_resolution
|
359
|
+
@x_resolution ||= width_drawable.to_f / x_value_range_for_all_lines.to_f
|
360
|
+
end
|
361
|
+
|
362
|
+
# this is the multiplier that we must multiply by the relative y value
|
363
|
+
def y_resolution
|
364
|
+
# TODO in the future, we will use the y range, but today, we assume it starts at 0
|
365
|
+
@y_resolution ||= height_drawable.to_f / y_value_max_for_all_lines.to_f
|
366
|
+
end
|
367
|
+
|
368
|
+
def x_value_range_for_all_lines
|
369
|
+
@x_value_range_for_all_lines ||= x_value_max_for_all_lines - x_value_min_for_all_lines
|
370
|
+
end
|
371
|
+
|
372
|
+
def x_value_min_for_all_lines
|
373
|
+
if @x_value_min_for_all_lines.nil?
|
374
|
+
line_visible_x_values = lines.map { |line| line[:values].to_h.keys }
|
375
|
+
all_visible_x_values = line_visible_x_values.reduce(:+) || []
|
376
|
+
# Right now, we assume Time objects
|
377
|
+
# TODO support String representations of Time (w/ some auto-detection of format)
|
378
|
+
@x_value_min_for_all_lines = all_visible_x_values.min
|
379
|
+
end
|
380
|
+
@x_value_min_for_all_lines
|
381
|
+
end
|
382
|
+
|
383
|
+
def x_value_max_for_all_lines
|
384
|
+
if @x_value_max_for_all_lines.nil?
|
385
|
+
line_visible_x_values = lines.map { |line| line[:values].to_h.keys }
|
386
|
+
all_visible_x_values = line_visible_x_values.reduce(:+) || []
|
387
|
+
# Right now, we assume Time objects
|
388
|
+
# TODO support String representations of Time (w/ some auto-detection of format)
|
389
|
+
@x_value_max_for_all_lines = all_visible_x_values.max
|
390
|
+
end
|
391
|
+
@x_value_max_for_all_lines
|
392
|
+
end
|
393
|
+
|
394
|
+
def y_value_max_for_all_lines
|
395
|
+
if @y_value_max_for_all_lines.nil?
|
396
|
+
if lines[0]&.[](:y_values)
|
397
|
+
line_visible_y_values = lines.map { |line| line[:y_values][0, max_visible_point_count(line)] }
|
398
|
+
else
|
399
|
+
# When using :values , we always stretch the chart so that all points are visible
|
400
|
+
line_visible_y_values = lines.map { |line| line[:values].to_h.values }
|
401
|
+
end
|
402
|
+
all_visible_y_values = line_visible_y_values.reduce(:+) || []
|
403
|
+
@y_value_max_for_all_lines = all_visible_y_values.max.to_f
|
404
|
+
end
|
405
|
+
@y_value_max_for_all_lines
|
406
|
+
end
|
407
|
+
|
408
|
+
def translate_points(chart_line, points)
|
409
|
+
max_job_count_before_translation = ((width / chart_point_distance_for_line(chart_line)).to_i + 1)
|
410
|
+
x_translation = [(points.size - max_job_count_before_translation) * chart_point_distance_for_line(chart_line), 0].max
|
411
|
+
if x_translation > 0
|
412
|
+
points.each do |point|
|
413
|
+
# need to check if point[:x] is present because if the user shrinks the window, we drop points
|
414
|
+
point[:x] = point[:x] - x_translation if point[:x]
|
415
|
+
end
|
416
|
+
end
|
417
|
+
points
|
418
|
+
end
|
419
|
+
|
420
|
+
def max_visible_point_count(chart_line) = ((width - chart_grid_marker_padding_width) / chart_point_distance_for_line(chart_line)).to_i + 1
|
421
|
+
|
422
|
+
def periodic_lines
|
423
|
+
return unless lines && lines[0] && lines[0][:x_interval_in_seconds] && lines[0][:x_interval_in_seconds] == DAY_IN_SECONDS
|
424
|
+
day_count = lines[0][:y_values].size
|
425
|
+
case day_count
|
426
|
+
when ..7
|
427
|
+
@points[lines[0]].each_with_index do |point, index|
|
428
|
+
next if index == 0
|
429
|
+
|
430
|
+
line(point[:x], chart_padding_height, point[:x], height - chart_padding_height) {
|
431
|
+
stroke chart_stroke_periodic_line
|
432
|
+
}
|
433
|
+
day = calculated_x_value(point[:index]).strftime("%e")
|
434
|
+
font_size = chart_font_marker_text[:size]
|
435
|
+
text(point[:x], height - chart_padding_height - font_size*1.4, font_size*2) {
|
436
|
+
string(day) {
|
437
|
+
font chart_font_marker_text
|
438
|
+
color chart_color_period_text
|
439
|
+
}
|
440
|
+
}
|
441
|
+
end
|
442
|
+
when ..30
|
443
|
+
@points[lines[0]].each_with_index do |point, index|
|
444
|
+
day_number = index + 1
|
445
|
+
if day_number % 7 == 0
|
446
|
+
line(point[:x], chart_padding_height, point[:x], height - chart_padding_height) {
|
447
|
+
stroke chart_stroke_periodic_line
|
448
|
+
}
|
449
|
+
date = calculated_x_value(point[:index]).strftime("%b %e")
|
450
|
+
font_size = chart_font_marker_text[:size]
|
451
|
+
text(point[:x] + 4, height - chart_padding_height - font_size*1.4, font_size*6) {
|
452
|
+
string(date) {
|
453
|
+
font chart_font_marker_text
|
454
|
+
color chart_color_period_text
|
455
|
+
}
|
456
|
+
}
|
457
|
+
end
|
458
|
+
end
|
459
|
+
else
|
460
|
+
@points[lines[0]].each do |point|
|
461
|
+
if calculated_x_value(point[:index]).strftime("%d") == "01"
|
462
|
+
line(point[:x], chart_padding_height, point[:x], height - chart_padding_height) {
|
463
|
+
stroke chart_stroke_periodic_line
|
464
|
+
}
|
465
|
+
date = calculated_x_value(point[:index]).strftime("%b")
|
466
|
+
font_size = chart_font_marker_text[:size]
|
467
|
+
text(point[:x] + 4, height - chart_padding_height - font_size*1.4, font_size*6) {
|
468
|
+
string(date) {
|
469
|
+
font chart_font_marker_text
|
470
|
+
color chart_color_period_text
|
471
|
+
}
|
472
|
+
}
|
473
|
+
end
|
474
|
+
end
|
475
|
+
end
|
476
|
+
end
|
477
|
+
|
478
|
+
def hover_stats
|
479
|
+
return unless display_attributes_on_hover && @closest_point_index
|
480
|
+
|
481
|
+
require "bigdecimal"
|
482
|
+
require "perfect_shape/point"
|
483
|
+
|
484
|
+
if @hover_point && lines && lines[0] && @points && @points[lines[0]] && !@points[lines[0]].empty?
|
485
|
+
x = @hover_point[:x]
|
486
|
+
closest_points = lines.map { |line| @points[line][@closest_point_index] }
|
487
|
+
closest_x = closest_points[0]&.[](:x)
|
488
|
+
line(closest_x, chart_padding_height, closest_x, height - chart_padding_height) {
|
489
|
+
stroke chart_stroke_hover_line
|
490
|
+
}
|
491
|
+
closest_points.each_with_index do |closest_point, index|
|
492
|
+
next unless closest_point && closest_point[:x] && closest_point[:y]
|
493
|
+
|
494
|
+
circle(closest_point[:x], closest_point[:y], chart_selected_point_radius) {
|
495
|
+
fill chart_fill_selected_point == :line_stroke ? chart_color_bubble : chart_fill_selected_point
|
496
|
+
stroke_value = chart_color_bubble.dup
|
497
|
+
stroke_value << {} unless stroke_value.last.is_a?(Hash)
|
498
|
+
stroke_value.last[:thickness] = 2
|
499
|
+
stroke stroke_value
|
500
|
+
}
|
501
|
+
end
|
502
|
+
text_label = formatted_x_value(@closest_point_index)
|
503
|
+
text_label_width = estimate_width_of_text(text_label, DEFAULT_CHART_FONT_MARKER_TEXT)
|
504
|
+
lines_with_closest_points = lines.each_with_index.map do |line, index|
|
505
|
+
next if closest_points[index].nil?
|
506
|
+
|
507
|
+
line
|
508
|
+
end.compact
|
509
|
+
closest_point_texts = lines_with_closest_points.map { |line| "#{line[:name]}: #{line[:y_values][@closest_point_index]}" }
|
510
|
+
closest_point_text_widths = closest_point_texts.map do |text|
|
511
|
+
estimate_width_of_text(text, chart_font_marker_text)
|
512
|
+
end
|
513
|
+
square_size = 12.0
|
514
|
+
square_to_label_padding = 10.0
|
515
|
+
label_padding = 10.0
|
516
|
+
text_label_x = width - chart_padding_width - text_label_width - label_padding -
|
517
|
+
(lines_with_closest_points.size*(square_size + square_to_label_padding) + (lines_with_closest_points.size - 1)*label_padding + closest_point_text_widths.sum)
|
518
|
+
text_label_y = height + chart_padding_height
|
519
|
+
|
520
|
+
text(text_label_x, text_label_y, text_label_width) {
|
521
|
+
string(text_label) {
|
522
|
+
font DEFAULT_CHART_FONT_MARKER_TEXT
|
523
|
+
color chart_color_marker_text
|
524
|
+
}
|
525
|
+
}
|
526
|
+
|
527
|
+
relative_x = text_label_x + text_label_width
|
528
|
+
lines_with_closest_points.size.times do |index|
|
529
|
+
square_x = relative_x + label_padding
|
530
|
+
|
531
|
+
square(square_x, text_label_y + 2, square_size) {
|
532
|
+
fill chart_color_bubble
|
533
|
+
}
|
534
|
+
|
535
|
+
attribute_label_x = square_x + square_size + square_to_label_padding
|
536
|
+
attribute_text = closest_point_texts[index]
|
537
|
+
attribute_text_width = closest_point_text_widths[index]
|
538
|
+
relative_x = attribute_label_x + attribute_text_width
|
539
|
+
|
540
|
+
text(attribute_label_x, text_label_y, attribute_text_width) {
|
541
|
+
string(attribute_text) {
|
542
|
+
font chart_font_marker_text
|
543
|
+
color chart_color_marker_text
|
544
|
+
}
|
545
|
+
}
|
546
|
+
end
|
547
|
+
end
|
548
|
+
end
|
549
|
+
|
550
|
+
def formatted_x_value(x_value_index)
|
551
|
+
# Today, we make the assumption that all lines have points along the same x-axis values
|
552
|
+
# TODO In the future, we can support different x values along different lines
|
553
|
+
chart_line = lines[0]
|
554
|
+
x_value_format = chart_line[:x_value_format] || :to_s
|
555
|
+
x_value = calculated_x_value(x_value_index)
|
556
|
+
if (x_value_format.is_a?(Symbol) || x_value_format.is_a?(String))
|
557
|
+
x_value.send(x_value_format)
|
558
|
+
else
|
559
|
+
x_value_format.call(x_value)
|
560
|
+
end
|
561
|
+
end
|
562
|
+
|
563
|
+
def calculated_x_value(x_value_index)
|
564
|
+
# Today, we make the assumption that all lines have points along the same x-axis values
|
565
|
+
# TODO In the future, we can support different x values along different lines
|
566
|
+
chart_line = lines[0]
|
567
|
+
chart_line[:x_value_start] - (chart_line[:x_interval_in_seconds] * x_value_index)
|
568
|
+
end
|
569
|
+
|
570
|
+
def estimate_width_of_text(text_string, font_properties)
|
571
|
+
return 0 if text_string.to_s.empty?
|
572
|
+
font_size = font_properties[:size] || 16
|
573
|
+
estimated_font_width = 0.63 * font_size
|
574
|
+
text_string.chars.size * estimated_font_width
|
575
|
+
end
|
576
|
+
|
577
|
+
end
|
578
|
+
end
|
579
|
+
end
|
@@ -81,8 +81,12 @@ module Glimmer
|
|
81
81
|
|
82
82
|
after_body do
|
83
83
|
observe(self, :lines) do
|
84
|
-
|
85
|
-
|
84
|
+
if lines.is_a?(Hash)
|
85
|
+
self.lines = [lines]
|
86
|
+
else
|
87
|
+
clear_drawing_cache
|
88
|
+
body_root.queue_redraw_all
|
89
|
+
end
|
86
90
|
end
|
87
91
|
observe(self, :width) do
|
88
92
|
clear_drawing_cache
|
metadata
CHANGED
@@ -1,14 +1,14 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: glimmer-libui-cc-graphs_and_charts
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 0.
|
4
|
+
version: 0.3.0
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- Andy Maleh
|
8
8
|
autorequire:
|
9
9
|
bindir: bin
|
10
10
|
cert_chain: []
|
11
|
-
date: 2024-
|
11
|
+
date: 2024-02-17 00:00:00.000000000 Z
|
12
12
|
dependencies:
|
13
13
|
- !ruby/object:Gem::Dependency
|
14
14
|
name: glimmer-dsl-libui
|
@@ -72,8 +72,8 @@ dependencies:
|
|
72
72
|
- - ">="
|
73
73
|
- !ruby/object:Gem::Version
|
74
74
|
version: '0'
|
75
|
-
description: Graphs and Charts (
|
76
|
-
Graph.
|
75
|
+
description: Graphs and Charts (Glimmer DSL for LibUI Custom Controls), like Line
|
76
|
+
Graph, Bar Chart, and Bubble Chart.
|
77
77
|
email: andy.am@gmail.com
|
78
78
|
executables: []
|
79
79
|
extensions: []
|
@@ -87,11 +87,13 @@ files:
|
|
87
87
|
- README.md
|
88
88
|
- VERSION
|
89
89
|
- examples/graphs_and_charts/basic_bar_chart.rb
|
90
|
+
- examples/graphs_and_charts/basic_bubble_chart.rb
|
90
91
|
- examples/graphs_and_charts/basic_line_graph.rb
|
91
92
|
- examples/graphs_and_charts/basic_line_graph_relative.rb
|
92
93
|
- glimmer-libui-cc-graphs_and_charts.gemspec
|
93
94
|
- lib/glimmer-libui-cc-graphs_and_charts.rb
|
94
95
|
- lib/glimmer/view/bar_chart.rb
|
96
|
+
- lib/glimmer/view/bubble_chart.rb
|
95
97
|
- lib/glimmer/view/line_graph.rb
|
96
98
|
homepage: http://github.com/AndyObtiva/glimmer-libui-cc-graphs_and_charts
|
97
99
|
licenses:
|
@@ -115,5 +117,5 @@ requirements: []
|
|
115
117
|
rubygems_version: 3.5.3
|
116
118
|
signing_key:
|
117
119
|
specification_version: 4
|
118
|
-
summary: Graphs and Charts
|
120
|
+
summary: Graphs and Charts (Glimmer DSL for LibUI Custom Controls)
|
119
121
|
test_files: []
|