lib/ctioga2/graphics/styles/factory.rb in ctioga2-0.0 vs lib/ctioga2/graphics/styles/factory.rb in ctioga2-0.1

- old
+ new

@@ -15,11 +15,11 @@ require 'ctioga2/log' # This module contains all the classes used by ctioga module CTioga2 - Version::register_svn_info('$Revision: 59 $', '$Date: 2009-05-28 23:15:50 +0200 (Thu, 28 May 2009) $') + Version::register_svn_info('$Revision: 217 $', '$Date: 2010-12-31 16:18:20 +0100 (Fri, 31 Dec 2010) $') module Graphics module Styles @@ -50,22 +50,27 @@ attr_accessor :short_option # The MetaBuilder::Type object that can convert a String to # an Array suitable for use with CircularArray. attr_accessor :sets_type + + # If this attribute is on, then CurveStyleFactory will not + # generate commands for this parameter, only the option. + attr_accessor :disable_commands # Creates a new CurveStyleFactoryParameter object. def initialize(name, type, sets, description, - short_option = nil) + short_option = nil, disable_cmds = false) @name = name @type = Commands::CommandType.get_type(type) @sets = sets @description = description @short_option = short_option - - # TODO: it is not very satisfying to mix CommandTypes and + @disable_commands = disable_cmds + + ## \todo it is not very satisfying to mix CommandTypes and # MetaBuilder::Type on the same level. if @sets @sets_type = MetaBuilder::Type.get_type({ :type => :set, @@ -93,15 +98,20 @@ AutoRE = /auto/i # Sets some parameter to _false_. DisableRE = /no(ne)?|off/i + # If that matches, we use the value as a link to other values. + LinkRE = /(?:=|->)(\S+)/ # Creates a new parameter for the style factory. + # + # \todo add a way to add some more text to the description; + # possibly a self.describe_parameter function ? def self.define_parameter(target, name, type, sets, description, - short_option = nil) + short_option = nil, disable_cmds = false) # We define two new types: # - first, the color-or-auto type: base_type = Commands::CommandType.get_type(type) if ! Commands::Interpreter.type("#{base_type.name}-or-auto") @@ -110,28 +120,32 @@ mb_type.re_shortcuts.dup : {}) mb_type.re_shortcuts[AutoRE] = 'auto' mb_type.re_shortcuts[DisableRE] = false + # Add passthrough for expressions such as =color... + mb_type.passthrough = LinkRE + # Now, register a type for the type or automatic. CmdType.new("#{base_type.name}-or-auto", mb_type, - "Same thing as type #{base_type.name}, or 'auto'") + "Same thing as {type:#{base_type.name}}, or @auto@ to let the style factory handle automatically.") end if sets and ! Commands::Interpreter.type("#{base_type.name}-set") # Now, register a type for the type or automatic. CmdType.new("#{base_type.name}-set",{ :type => :set, :subtype => base_type.type, :shortcuts => sets } , - "Sets of #{base_type.name}") + "Sets of {type: #{base_type.name}}") end param = CurveStyleFactoryParameter.new(name, type, sets, - description, short_option) + description, short_option, + disable_cmds) @parameters ||= {} @parameters[target] = param @name_to_target ||= {} @name_to_target[name] = target @@ -149,45 +163,49 @@ end # The CmdGroup for stylistic information about # curves. CurveStyleGroup = - CmdGroup.new('curve-style', "Curve styles", - "Set stylistic details about curves", 1) + CmdGroup.new('curve-style', "Curves styles", + "Set stylistic details of curves or other object drawn from data", 1) # Creates two commands for each parameter of the object: # * a command to set the override # * a command to choose the sets. def self.create_commands parameters.each do |target, param| + next if param.disable_commands override_cmd = Cmd.new("#{param.name}", param.short_option, "--#{param.name}", [ CmdArg.new("#{param.type.name}-or-auto") ], {}, "Sets the #{param.description} for subsequent curves", - "Sets the #{param.description} for subsequent curves, until cancelled with 'auto' as argument.", CurveStyleGroup) do |plotmaker, value| + "Sets the #{param.description} for subsequent curves, until cancelled with @auto@ as argument.", CurveStyleGroup) do |plotmaker, value| plotmaker.curve_generator.style_factory. set_parameter_override(target, value) end if param.sets + next if param.disable_commands set_cmd = Cmd.new("#{param.name}-set", nil, "--#{param.name}-set", [ CmdArg.new("#{param.type.name}-set") ], {}, "Chooses a set for the #{param.description} of subsequent curves", - "Chooses a set for the #{param.description} of subsequent curves", + "Chooses a set for the #{param.description} of subsequent curves. Also sets {command: #{param.name}} to @auto@, so that the set takes effect immediately", CurveStyleGroup) do |plotmaker, value| plotmaker.curve_generator.style_factory. set_parameter_set(target, value) + plotmaker.curve_generator.style_factory. + set_parameter_override(target, 'auto') end end end end @@ -242,12 +260,11 @@ for target, array in @parameters_carrays base[target] = array.next end base.merge!(@override_parameters) base.merge!(hash_name_to_target(one_time)) - # TODO: here, resolve 'links', such as :->color ? - return CurveStyle.from_hash(base) + return CurveStyle.from_hash(resolve_links(base)) end # Sets the override for the given parameter. This corresponds @@ -263,12 +280,22 @@ # Perform automatic type conversion only on strings. if value.is_a? String if value =~ AutoRE @override_parameters.delete(target) return - end - if value =~ DisableRE + elsif value =~ LinkRE + t = $1 + convert = self.class.name_to_target + if convert.key?(t) + value = "=#{convert[t]}".to_sym + else + warn { "No known key: #{t}, treating as auto" } + @override_parameters.delete(target) + return + end + + elsif value =~ DisableRE value = false else value = param.type.string_to_type(value) end end @@ -285,29 +312,79 @@ end @parameters_carrays[target].set = value end # Now, the parameters: + + # Lines: define_parameter 'line_color', 'color', 'color', Sets::ColorSets, "color", "-c" define_parameter 'line_width', 'line-width', 'float', Sets::LineWidthSets, "line width", nil define_parameter 'line_style', 'line-style', 'line-style', Sets::LineStyleSets, "line style", nil + # Markers define_parameter 'marker_marker', 'marker', 'marker', Sets::MarkerSets, "marker", '-m' define_parameter 'marker_color', 'marker-color', 'color', Sets::ColorSets, "marker color", nil define_parameter 'marker_scale', 'marker-scale', 'float', Sets::LineWidthSets, "marker scale", nil + # Error bars: + define_parameter 'error_bar_color', 'error-bar-color', 'color', + Sets::ColorSets, "error bar color", nil + # Location: + define_parameter 'location_xaxis', 'xaxis', 'axis', + nil, "X axis", nil, true + + define_parameter 'location_yaxis', 'yaxis', 'axis', + nil, "Y axis", nil, true + + # Now, fill style + define_parameter 'fill_y0', 'fill', 'fill-until', + {}, "Fill until", nil + + define_parameter 'fill_color', 'fill-color', 'color', + Sets::ColorSets, "fill color", nil + + define_parameter 'fill_transparency', 'fill-transparency', 'float', + {}, "Fill transparency", nil + + # Region handling + define_parameter 'region_position', 'region-side', 'region-side', + {"default" => [:above, :below]}, "region side", nil + + + define_parameter 'style', 'style', 'text', + {}, "Path style", nil + + # Only for xyz-maps or xy-parametric + define_parameter 'color_map', 'color-map', 'colormap', + nil, "Color map", nil + + + define_parameter 'zaxis', 'zaxis', 'text', + nil, "Name for the Z axis", nil + + ## @todo For xy-parametric, there should be a way to specify + ## to which z value the maps apply (ie lines = y2, marker = + ## y3...). Although for readability, it is probably better + ## to avoid that... + define_parameter 'marker_color_map', 'marker-color-map', 'colormap', + nil, "Marker color map", nil + + define_parameter 'split_on_nan', 'split-on-nan', 'boolean', + nil, "Split on NaN", nil + + # And finally, we register all necessary commands... create_commands # A constant suitable for use as the optional arguments of the # plot command. @@ -337,13 +414,52 @@ convert = self.class.name_to_target for k,v in h if convert.key? k retval[convert[k]] = v else - warn "Unkown key for hash_name_to_target: #{k}" + warn { "Unkown key for hash_name_to_target: #{k}" } end end return retval + end + + # Resolve potential links in the form of :=stuff within the + # given hash, and returns a new version of the hash. + # + # \warning the _h_ parameter is completely destroyed in the + # process + def resolve_links(h) + tv = {} + + # First, copy plain values + for k,v in h + if v.is_a?(Symbol) && v.to_s =~ /^(=|->)/ + # We keep for later + else + tv[k] = v + h.delete(k) + end + end + + # Now, we will iterate over the remaining things; we will + # stop with an error if the number of remaining keys does + # not decrease after one step + while h.size > 0 + pre_size = h.size + for k,v in h + v.to_s =~ /^(?:=|->)(\S+)/ + target = $1 + if tv.key? target + tv[k] = tv[target] + h.delete(k) + end + end + if h.size >= pre_size + raise "Error: infinite recursion loop while gathering styles" + end + end + + return tv end end end end end