From e182818cd972fff4d78cc810d1b58af18a621e61 Mon Sep 17 00:00:00 2001 From: Robert Mosolgo Date: Thu, 3 Mar 2022 22:14:11 -0500 Subject: [PATCH 1/2] Support min: / max: options for y-axis range --- lib/squid.rb | 2 ++ lib/squid/axis.rb | 8 ++++---- lib/squid/configuration.rb | 6 ++++++ lib/squid/graph.rb | 4 ++-- spec/axis_spec.rb | 7 +++++++ spec/squid_spec.rb | 5 +++++ 6 files changed, 26 insertions(+), 6 deletions(-) diff --git a/lib/squid.rb b/lib/squid.rb index f57ca31..4b0db34 100644 --- a/lib/squid.rb +++ b/lib/squid.rb @@ -27,6 +27,8 @@ module Interface # @option settings [Numeric] :steps (4) the number of gridlines. # @option settings [Boolean] :ticks (true) whether to plot the ticks. # @option settings [Symbol] :type (:column) the type of graph. + # @option settings [Numeric] :min + # @option settings [Numeric] :max def chart(data = {}, settings = {}) Graph.new(self, data, settings).draw end diff --git a/lib/squid/axis.rb b/lib/squid/axis.rb index 5f63c74..8e2e6d0 100644 --- a/lib/squid/axis.rb +++ b/lib/squid/axis.rb @@ -7,8 +7,8 @@ class Axis include Format attr_reader :data - def initialize(data, steps:, stack:, format:, &block) - @data, @steps, @stack, @format = data, steps, stack, format + def initialize(data, steps:, stack:, format:, min: nil, max: nil, &block) + @data, @steps, @stack, @format, @min, @max = data, steps, stack, format, min, max @width_proc = block if block_given? end @@ -40,13 +40,13 @@ def label_width(label) def min if @data.any? && values.first && values.first.any? - [values.first.min, 0].min + @min || [values.first.min, 0].min end end def max if @data.any? && values.last && values.last.any? - closest_step_to values.last.max + closest_step_to(@max || values.last.max) end end diff --git a/lib/squid/configuration.rb b/lib/squid/configuration.rb index 431af18..e1c0e67 100644 --- a/lib/squid/configuration.rb +++ b/lib/squid/configuration.rb @@ -36,6 +36,10 @@ def self.integer -> (value) { value.to_i } end + def self.optional_integer + -> (value) { value && value.to_i } + end + def self.symbol -> (value) { value.to_sym } end @@ -62,6 +66,8 @@ def self.array(proc = nil) steps: {as: integer, default: '4'}, ticks: {as: boolean, default: 'true'}, type: {as: symbol, default: 'column'}, + min: {as: optional_integer, default: nil}, + max: {as: optional_integer, default: nil}, } attr_accessor *ATTRIBUTES.keys diff --git a/lib/squid/graph.rb b/lib/squid/graph.rb index cf0bb7e..d527a82 100644 --- a/lib/squid/graph.rb +++ b/lib/squid/graph.rb @@ -13,7 +13,7 @@ module Squid class Graph extend Settings has_settings :baseline, :border, :chart, :colors, :every, :formats, :height - has_settings :legend, :line_widths, :steps, :ticks, :type, :labels + has_settings :legend, :line_widths, :steps, :ticks, :type, :labels, :min, :max def initialize(document, data = {}, settings = {}) @data, @settings = data, settings @@ -96,7 +96,7 @@ def right def axis(first:, last:) series = @data.values[first, last].map(&:values) - options = {steps: steps, stack: stack?, format: formats[first]} + options = {steps: steps, stack: stack?, format: formats[first], min: min, max: max} Axis.new(series, options) {|label| @plot.width_of label} end diff --git a/spec/axis_spec.rb b/spec/axis_spec.rb index 754b22f..61bf487 100644 --- a/spec/axis_spec.rb +++ b/spec/axis_spec.rb @@ -76,6 +76,13 @@ expect(labels).to eq %w(9.9 -5.1 -20 -35 -50) end end + + describe 'given :min and :max' do + let(:options) { {steps: steps, stack: stack?, format: format, min: -500, max: 1000 } } + it "uses the give values as first and last label" do + expect(labels).to eq ["1,000", "625", "250", "-125", "-500"] + end + end end describe '#width' do diff --git a/spec/squid_spec.rb b/spec/squid_spec.rb index f0530fa..52ab8aa 100644 --- a/spec/squid_spec.rb +++ b/spec/squid_spec.rb @@ -241,6 +241,11 @@ it { expect(colors_of(chart).fill_color).to eq [1.0, 0.0, 0.0] } end + describe 'uses a given range with max and min options' do + let(:settings) { options.merge(steps: 5, max: 100, min: -100).except(:format) } + it { expect(strings_of chart).to eq ["100", "60", "20", "-20", "-60", "-100"] } + end + describe 'formats value labels with the value of the :format option' do let(:settings) { options.merge label: true, format: :percentage } it { expect(strings_of chart).to eq %w(50.0% -30.0% 20.0%) } From b97ddf522c498ff044e37b146da32f448ac36be2 Mon Sep 17 00:00:00 2001 From: Robert Mosolgo Date: Mon, 7 Mar 2022 13:50:52 -0500 Subject: [PATCH 2/2] Fix points so that they're plotted relative to the given positive min --- lib/squid/point.rb | 11 ++++++++--- spec/point_spec.rb | 10 ++++++++++ 2 files changed, 18 insertions(+), 3 deletions(-) diff --git a/lib/squid/point.rb b/lib/squid/point.rb index 1a34131..4de6b52 100644 --- a/lib/squid/point.rb +++ b/lib/squid/point.rb @@ -9,11 +9,16 @@ def self.for(series, minmax:, height:, labels:, stack:, formats:) @max = Hash.new 0 min, max = minmax offset = -> (value) { value * height.to_f / (max-min) } + y_offset = nil series.map.with_index do |values, series_i| values.map.with_index do |value, i| - h = y_for value, index: i, stack: false, &offset if value - y = y_for value, index: i, stack: stack, &offset if value - y = y - offset.call([min, 0].min) if value + if value + h = y_for value, index: i, stack: false, &offset + y = y_for value, index: i, stack: stack, &offset + y_offset ||= offset.call(min || 0) # only calculate this once, since the result will never change + y = y - y_offset + end + label = format_for value, formats[series_i] if labels[series_i] new y: y, height: h, index: i, label: label, negative: value.to_f < 0 end diff --git a/spec/point_spec.rb b/spec/point_spec.rb index cf4aa7d..9262961 100644 --- a/spec/point_spec.rb +++ b/spec/point_spec.rb @@ -59,5 +59,15 @@ expect(points.last.map &:y).to eq [nil, 89.95, 0.0] end end + + context "given a positive min" do + let(:minmax) { [100, 150] } + let(:series) { [[110.0, 100, 130.0], [nil, 120.0, 150.0]] } + + it 'generates y values relative to the minimum' do + expect(points.first.map &:y).to eq [20.0, 0.0, 60.0] + expect(points.last.map &:y).to eq [nil, 40.0, 100.0] + end + end end end