Class: CTioga2::Graphics::Elements::Curve2D

Inherits:
PlotBasedElement show all
Includes:
Log, Dobjects
Defined in:
lib/ctioga2/graphics/elements/curve2d.rb

Overview

A Curve2D object represents a 2D curve, along with its style and so on.

todo Put back various stylistic aspects that were present in the old ctioga, such as:

  • transparency

  • drawing order

Direct Known Subclasses

Histogram

Constant Summary

Constants inherited from TiogaElement

TiogaElement::StyleBaseOptions

Instance Attribute Summary collapse

Attributes inherited from PlotBasedElement

#curve_style, #dataset

Attributes inherited from TiogaElement

#clipped, #depth, #hidden, #location, #object_classes, #object_id, #object_parent, #parent

Instance Method Summary collapse

Methods included from Log

context, counts, debug, error, fatal, #format_exception, #identify, info, init_logger, log_to, logger, set_level, #spawn, warn

Methods inherited from PlotBasedElement

#clipped, #depth, #location

Methods inherited from TiogaElement

all_styles, base_style, #check_styled, define_style, #do, find_object, find_objects, #get_style, #has_style?, inherited, #inspect, register_object, register_style, #setup_style, #style_class, style_class, style_name, #style_name, styled_classes, #update_style

Constructor Details

#initialize(dataset, style = nil) ⇒ Curve2D

Creates a new Curve2D object with the given dataset and style.



49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
# File 'lib/ctioga2/graphics/elements/curve2d.rb', line 49

def initialize(dataset, style = nil)
  super()
  @dataset = dataset
  if @dataset.size > 2
    warn { "Columns Y2 and further were ignored for set #{dataset.name}" }
  end
  # We build the function on a duplicate of the values ?
  @function = Function.new(@dataset.x.values.dup, 
                           @dataset.y.values.dup)
  @curve_style = style

  # Preparation of the subpath elements
  if @curve_style.split_on_nan
    # This requires Tioga r601 !
    @path_elements = @function.split_on_nan(:xy)
    info { "Dividing into #{@path_elements.size} subpaths" }
  else
    @path_elements = [@function]
  end
  @function.strip_nan

end

Instance Attribute Details

#functionObject

A Dobjects::Function holding the “real” X and Y values, for the sake of manipulations.



41
42
43
# File 'lib/ctioga2/graphics/elements/curve2d.rb', line 41

def function
  @function
end

#path_elementsObject

Elements of the path, when there are more than one:



44
45
46
# File 'lib/ctioga2/graphics/elements/curve2d.rb', line 44

def path_elements
  @path_elements
end

Instance Method Details

#can_clip?Boolean

Returns:

  • (Boolean)


77
78
79
80
81
82
83
84
85
# File 'lib/ctioga2/graphics/elements/curve2d.rb', line 77

def can_clip?
  if @curve_style.clipped or 
      ( @curve_style.fill && @curve_style.fill.fill?) or
      ( parent.is_a?(Region))
    return false
  else
    return true
  end
end

#draw_errorbars(t) ⇒ Object



199
200
201
202
203
204
# File 'lib/ctioga2/graphics/elements/curve2d.rb', line 199

def draw_errorbars(t)
  return unless @dataset.has_xy_errors?
  @dataset.each_values(true, true) do |*vals|
    @curve_style.error_bar.show_error_bar(t, *(vals[1..6]))
  end
end

#draw_fill(t) ⇒ Object

Draws the filled region according to the :fill_type element of the style pseudo-hash. It can be:



187
188
189
190
191
192
193
194
195
196
197
# File 'lib/ctioga2/graphics/elements/curve2d.rb', line 187

def draw_fill(t)
  return unless (@curve_style.fill &&  
                 @curve_style.fill.close_type &&
                 @curve_style.fill.close_type.fill?)
  t.context do
    # Remember: first setup_fill, then draw path, then do_fill
    @curve_style.fill.setup_fill(t)
    make_closed_path(t)
    @curve_style.fill.do_fill(t)
  end
end

#draw_markers(t) ⇒ Object

Draws the markers, if applicable.



169
170
171
172
173
174
175
# File 'lib/ctioga2/graphics/elements/curve2d.rb', line 169

def draw_markers(t)
  if @curve_style.has_marker?
    xs = @function.x
    ys = @function.y
    @curve_style.marker.draw_markers_at(t, xs, ys)
  end
end

#draw_path(t) ⇒ Object

Strokes the path.



158
159
160
161
162
163
164
165
166
# File 'lib/ctioga2/graphics/elements/curve2d.rb', line 158

def draw_path(t)
  if @curve_style.has_line?
    t.context do 
      @curve_style.line.set_stroke_style(t)
      make_path(t)
      t.stroke
    end
  end
end

#get_axesObject

Returns the AxisSyle objects for the X and Y axes as an array.



178
179
180
181
182
183
# File 'lib/ctioga2/graphics/elements/curve2d.rb', line 178

def get_axes
  return [ 
          parent.style.get_axis_style(@curve_style.xaxis),
          parent.style.get_axis_style(@curve_style.yaxis)
         ]
end

#get_boundariesObject

Returns the Types::Boundaries of this curve.



73
74
75
# File 'lib/ctioga2/graphics/elements/curve2d.rb', line 73

def get_boundaries
  return Types::Boundaries.bounds(@function.x, @function.y)
end

#make_closed_path(t, close_type = nil) ⇒ Object

TODO:

Make sure this is called only on sub-plots when

Adds a closed path to the given FigureMaker object. The path is closed according to the specification given as fv, which is the same as the y0 attribute of a CurveFillStyle.

It must not be false

splitting on NaN !



149
150
151
152
153
154
155
# File 'lib/ctioga2/graphics/elements/curve2d.rb', line 149

def make_closed_path(t, close_type = nil)
  make_path(t)
  close_type ||= @curve_style.fill.close_type
  bnds = parent.get_el_boundaries(self)
  close_type.close_path(t, bnds, @function[0], 
                        @function[@function.size - 1])
end

#make_path(t) ⇒ Object

Creates a path for the given curve. This should be defined with care, as it will be used for instance for region coloring and stroking. The function should only append to the current path, not attempt to create a new path or empty what was done before.



93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
# File 'lib/ctioga2/graphics/elements/curve2d.rb', line 93

def make_path(t)
  bnds = parent.get_el_boundaries(self)

  for func in @path_elements
    case @curve_style.path_style
    when /^splines/
      for f in func.split_monotonic
        new_f = if can_clip?
                  f.bound_values(*bnds.extrema)
                else
                  f.dup
                end
        t.append_interpolant_to_path(new_f.make_interpolant)
      end
    when /^impulses/
      # We draw lines from y = 0
      for x,y in func
        t.move_to_point(x, 0)
        t.append_point_to_path(x, y)
      end
    else

      # Hmmmm. This may get the wrong thing if you happen to
      # draw something completely outside.
      if can_clip? 
        f = func.bound_values(*bnds.extrema)
      else
        f = func
      end
      # If for some reason, there is no point left, we plot
      # the original function.
      if f.size < 2
        f = func
      end
      
      if f.size < 1
        warn { "Empty curve for dataset '#{@dataset.name}'" }
        return 
      end
      
      t.move_to_point(f.x.first, f.y.first)
      t.append_points_to_path(f.x[1..-1], f.y[1..-1])
    end
  end
  
end

#real_do(t) ⇒ Object

Actually draws the curve



207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
# File 'lib/ctioga2/graphics/elements/curve2d.rb', line 207

def real_do(t)
  debug { "Plotting curve #{to_yaml}" }
  t.context do
    ## \todo allow customization of the order of drawing,
    ## using a simple user-specificable array of path,
    ## markers... and use the corresponding #draw_path or
    ## #draw_markers... Ideally, any string could be used, and
    ## warnings should be issued on missing symbols.

    draw_fill(t)
    draw_errorbars(t)
    draw_path(t)
    draw_markers(t)
  end
end