lib/write_xlsx/chart/series.rb in write_xlsx-0.75.0 vs lib/write_xlsx/chart/series.rb in write_xlsx-0.76.0

- old
+ new

@@ -1,281 +1,263 @@ # -*- coding: utf-8 -*- -class Series - include Writexlsx::Utility +module Writexlsx + class Chart + class Chartline + include Writexlsx::Utility - attr_reader :values, :categories, :name, :name_formula, :name_id - attr_reader :cat_data_id, :val_data_id, :fill - attr_reader :trendline, :smooth, :labels, :invert_if_neg - attr_reader :x2_axis, :y2_axis, :error_bars, :points - attr_accessor :line, :marker + attr_reader :line, :fill, :type - def initialize(chart, params = {}) - @values = aref_to_formula(params[:values]) - @categories = aref_to_formula(params[:categories]) - @name, @name_formula = - chart.process_names(params[:name], params[:name_formula]) - @cat_data_id = chart.get_data_id(@categories, params[:categories_data]) - @val_data_id = chart.get_data_id(@values, params[:values_data]) - @name_id = chart.get_data_id(@name_formula, params[:name_data]) - if params[:border] - @line = line_properties(params[:border]) - else - @line = line_properties(params[:line]) + def initialize(params) + @line = params[:line] + @fill = params[:fill] + # Set the line properties for the marker.. + @line = line_properties(@line) + # Allow 'border' as a synonym for 'line'. + @line = line_properties(params[:border]) if params[:border] + + # Set the fill properties for the marker. + @fill = fill_properties(@fill) + end + + def line_defined? + line && ptrue?(line[:_defined]) + end + + def fill_defined? + fill && ptrue?(fill[:_defined]) + end end - @fill = fill_properties(params[:fill]) - @marker = marker_properties(params[:marker]) - @trendline = trendline_properties(params[:trendline]) - @smooth = params[:smooth] - @error_bars = { - :_x_error_bars => error_bars_properties(params[:x_error_bars]), - :_y_error_bars => error_bars_properties(params[:y_error_bars]) - } - @points = points_properties(params[:points]) - @labels = labels_properties(params[:data_labels]) - @invert_if_neg = params[:invert_if_negative] - @x2_axis = params[:x2_axis] - @y2_axis = params[:y2_axis] - end - def ==(other) - methods = %w[categories values name name_formula name_id - cat_data_id val_data_id - line fill marker trendline - smooth labels invert_if_neg x2_axis y2_axis error_bars points ] - methods.each do |method| - return false unless self.instance_variable_get("@#{method}") == other.instance_variable_get("@#{method}") + class Point < Chartline end - true - end - private + class Gridline < Chartline + attr_reader :visible - # - # Convert and aref of row col values to a range formula. - # - def aref_to_formula(data) # :nodoc: - # If it isn't an array ref it is probably a formula already. - return data unless data.kind_of?(Array) - xl_range_formula(*data) - end + def initialize(params) + super(params) + @visible = params[:visible] + end + end - # - # Convert user defined fill properties to the structure required internally. - # - def fill_properties(fill) # :nodoc: - return { :_defined => 0 } unless fill + class Trendline < Chartline + attr_reader :name, :forward, :backward, :order, :period - fill[:_defined] = 1 + def initialize(params) + super(params) - fill - end + @name = params[:name] + @forward = params[:forward] + @backward = params[:backward] + @order = params[:order] + @period = params[:period] + @type = value_or_raise(types, params[:type], 'trendline type') + end - # - # Convert user defined marker properties to the structure required internally. - # - def marker_properties(marker) # :nodoc: - return unless marker + private - types = { - :automatic => 'automatic', - :none => 'none', - :square => 'square', - :diamond => 'diamond', - :triangle => 'triangle', - :x => 'x', - :star => 'star', - :dot => 'dot', - :short_dash => 'dot', - :dash => 'dash', - :long_dash => 'dash', - :circle => 'circle', - :plus => 'plus', - :picture => 'picture' - } + def types + { + :exponential => 'exp', + :linear => 'linear', + :log => 'log', + :moving_average => 'movingAvg', + :polynomial => 'poly', + :power => 'power' + } + end + end - # Check for valid types. - marker_type = marker[:type] + class Marker < Chartline + attr_reader :size - if marker_type - marker[:automatic] = 1 if marker_type == 'automatic' - marker[:type] = value_or_raise(types, marker_type, 'maker type') - end + def initialize(params) + super(params) - # Set the line properties for the marker.. - line = line_properties(marker[:line]) + if params[:type] + @type = value_or_raise(types, params[:type], 'maker type') + end - # Allow 'border' as a synonym for 'line'. - line = line_properties(marker[:border]) if marker[:border] + @size = params[:size] + @automatic = false + @automatic = true if @type == 'automatic' + end - # Set the fill properties for the marker. - fill = fill_properties(marker[:fill]) + def automatic? + @automatic + end - marker[:_line] = line - marker[:_fill] = fill + private - marker - end + def types + { + :automatic => 'automatic', + :none => 'none', + :square => 'square', + :diamond => 'diamond', + :triangle => 'triangle', + :x => 'x', + :star => 'star', + :dot => 'dot', + :short_dash => 'dot', + :dash => 'dash', + :long_dash => 'dash', + :circle => 'circle', + :plus => 'plus', + :picture => 'picture' + } + end + end - # - # Convert user defined trendline properties to the structure required internally. - # - def trendline_properties(trendline) # :nodoc: - return unless trendline + class Errorbars + include Writexlsx::Utility - types = { - :exponential => 'exp', - :linear => 'linear', - :log => 'log', - :moving_average => 'movingAvg', - :polynomial => 'poly', - :power => 'power' - } + attr_reader :type, :direction, :endcap, :value, :line, :fill + attr_reader :plus_values, :minus_values, :plus_data, :minus_data - # Check the trendline type. - trend_type = trendline[:type] + def initialize(params) + @type = types[params[:type].to_sym] || 'fixedVal' + @value = params[:value] || 1 # value for error types that require it. + @endcap = params[:end_style] || 1 # end-cap style. - trendline[:type] = value_or_raise(types, trend_type, 'trendline type') + # Set the error bar direction. + @direction = error_bar_direction(params[:direction]) - # Set the line properties for the trendline.. - line = line_properties(trendline[:line]) + # Set any custom values + @plus_values = params[:plus_values] || [1] + @minus_values = params[:minus_values] || [1] + @plus_data = params[:plus_data] || [] + @minus_data = params[:minus_data] || [] - # Allow 'border' as a synonym for 'line'. - line = line_properties(trendline[:border]) if trendline[:border] + # Set the line properties for the error bars. + @line = line_properties(params[:line]) + @fill = params[:fill] + end - # Set the fill properties for the trendline. - fill = fill_properties(trendline[:fill]) + private - trendline[:_line] = line - trendline[:_fill] = fill + def types + { + :fixed => 'fixedVal', + :percentage => 'percentage', + :standard_deviation => 'stdDev', + :standard_error => 'stdErr', + :custom => 'cust' + } + end - return trendline - end + def error_bar_direction(direction) + case direction + when 'minus' + 'minus' + when 'plus' + 'plus' + else + 'both' + end + end + end - # - # Convert user defined error bars properties to structure required - # internally. - # - def error_bars_properties(params = {}) - return if !ptrue?(params) || params.empty? + class Series + include Writexlsx::Utility - # Default values. - error_bars = { - :_type => 'fixedVal', - :_value => 1, - :_endcap => 1, - :_direction => 'both', - :_plus_values => [1], - :_minus_values => [1], - :_plus_data => [], - :_minus_data => [] - } + attr_reader :values, :categories, :name, :name_formula, :name_id + attr_reader :cat_data_id, :val_data_id, :fill + attr_reader :trendline, :smooth, :labels, :invert_if_negative + attr_reader :x2_axis, :y2_axis, :error_bars, :points + attr_accessor :line, :marker - types = { - :fixed => 'fixedVal', - :percentage => 'percentage', - :standard_deviation => 'stdDev', - :standard_error => 'stdErr', - :custom => 'cust' - } + def initialize(chart, params = {}) + @values = aref_to_formula(params[:values]) + @categories = aref_to_formula(params[:categories]) + @name, @name_formula = + chart.process_names(params[:name], params[:name_formula]) - # Check the error bars type. - error_type = params[:type].to_sym + set_data_ids(chart, params) - if types.key?(error_type) - error_bars[:_type] = types[error_type] - else - raise "Unknown error bars type '#{error_type}'\n" - end + @line = line_properties(params[:border] || params[:line]) + @fill = fill_properties(params[:fill]) - # Set the value for error types that require it. - if params.key?(:value) - error_bars[:_value] = params[:value] - end + @marker = Marker.new(params[:marker]) if params[:marker] + @trendline = Trendline.new(params[:trendline]) if params[:trendline] + @error_bars = errorbars(params[:x_error_bars], params[:y_error_bars]) + @points = params[:points].collect { |p| p ? Point.new(p) : p } if params[:points] + @labels = labels_properties(params[:data_labels]) - # Set the end-cap style. - if params.key?(:end_style) - error_bars[:_endcap] = params[:end_style] - end + [:smooth, :invert_if_negative, :x2_axis, :y2_axis]. + each { |key| instance_variable_set("@#{key}", params[key]) } + end - # Set the error bar direction. - if params.key?(:direction) - if params[:direction] == 'minus' - error_bars[:_direction] = 'minus' - elsif params[:direction] == 'plus' - error_bars[:_direction] = 'plus' - else - # Default to 'both' + def ==(other) + methods = %w[categories values name name_formula name_id + cat_data_id val_data_id + line fill marker trendline + smooth labels invert_if_neg x2_axis y2_axis error_bars points ] + methods.each do |method| + return false unless self.instance_variable_get("@#{method}") == other.instance_variable_get("@#{method}") + end + true end - end - # Set any custom values - error_bars[:_plus_values] = params[:plus_values] if params[:plus_values] - error_bars[:_minus_values] = params[:minus_values] if params[:minus_values] - error_bars[:_plus_data] = params[:plus_data] if params[:plus_data] - error_bars[:_minus_data] = params[:minus_data] if params[:minus_data] + def line_defined? + line && ptrue?(line[:_defined]) + end - # Set the line properties for the error bars. - error_bars[:_line] = line_properties(params[:line]) + private - error_bars - end + # + # Convert and aref of row col values to a range formula. + # + def aref_to_formula(data) # :nodoc: + # If it isn't an array ref it is probably a formula already. + return data unless data.kind_of?(Array) + xl_range_formula(*data) + end - # - # Convert user defined points properties to structure required internally. - # - def points_properties(user_points = nil) - return unless user_points + def set_data_ids(chart, params) + @cat_data_id = chart.data_id(@categories, params[:categories_data]) + @val_data_id = chart.data_id(@values, params[:values_data]) + @name_id = chart.data_id(@name_formula, params[:name_data]) + end - points = [] - user_points.each do |user_point| - if user_point - # Set the lline properties for the point. - line = line_properties(user_point[:line]) + def errorbars(x, y) + { + :_x_error_bars => x ? Errorbars.new(x) : nil, + :_y_error_bars => y ? Errorbars.new(y) : nil + } + end - # Allow 'border' as a synonym for 'line'. - if user_point[:border] - line = line_properties(user_point[:border]) + # + # Convert user defined labels properties to the structure required internally. + # + def labels_properties(labels) # :nodoc: + return nil unless labels + + position = labels[:position] + if position.nil? || position.empty? + labels.delete(:position) + else + # Map user defined label positions to Excel positions. + labels[:position] = value_or_raise(positions, position, 'label position') end - # Set the fill properties for the chartarea. - fill = fill_properties(user_point[:fill]) + labels + end - point = {} - point[:_line] = line - point[:_fill] = fill + def positions + { + :center => 'ctr', + :right => 'r', + :left => 'l', + :top => 't', + :above => 't', + :bottom => 'b', + :below => 'b', + :inside_end => 'inEnd', + :outside_end => 'outEnd', + :best_fit => 'bestFit' + } end - points << point end - points - end - - # - # Convert user defined labels properties to the structure required internally. - # - def labels_properties(labels) # :nodoc: - return nil unless labels - - position = labels[:position] - if position.nil? || position.empty? - labels.delete(:position) - else - # Map user defined label positions to Excel positions. - positions = { - :center => 'ctr', - :right => 'r', - :left => 'l', - :top => 't', - :above => 't', - :bottom => 'b', - :below => 'b', - :inside_end => 'inEnd', - :outside_end => 'outEnd', - :best_fit => 'bestFit' - } - - labels[:position] = value_or_raise(positions, position, 'label position') - end - - labels end end