module Rubyvis::SvgScene

Public Class Methods

area(scenes) click to toggle source
# File lib/rubyvis/scene/svg_area.rb, line 3
def self.area(scenes)
  #e = scenes._g.elements[1]
  e=scenes._g.get_element(1)
  return e if scenes.size==0
  s=scenes[0]
  # segmented
  return self.area_segment(scenes) if (s.segmented) 
  # visible
  return e if (!s.visible)
  fill = s.fill_style
  stroke = s.stroke_style
  return e if (fill.opacity==0 and stroke.opacity==0)
  
  # Computes the straight path for the range [i, j]
  path=lambda {|ii,j|
    p1 = []
    p2 = []
    k=j
    (ii..k).each {|i|
      si = scenes[i]
      sj = scenes[j]
      pi = "#{si.left},#{si.top}"
      pj = "#{(sj.left + sj.width)},#{(sj.top + sj.height)}"
      puts "#{i}:"+pi+","+pj if $DEBUG
      #/* interpolate */
      
      if (i < k)
        sk = scenes[i + 1]
        sl = scenes[j - 1]
        case (s.interpolate) 
          when "step-before"
            pi = pi+"V#{sk.top}"
            pj = pj+"H#{sl.left + sl.width}"
          
          when "step-after"
            pi = pi+"H#{sk.left}"
            pj = pj+"V#{sl.top + sl.height}"
        end
      end

      p1.push(pi)
      p2.push(pj)
      j=j-1
    }
    (p1+p2).join("L");
  }
    
  # @private Computes the curved path for the range [i, j]. */
  path_curve=lambda {|ii, j|
    pointsT = []
    pointsB = []
    pathT=nil
    pathB=nil
    
    
    k=j
    (ii..k).each {|i|
      sj = scenes[j];
      pointsT.push(scenes[i])
      pointsB.push(OpenStruct.new({:left=> sj.left + sj.width, :top=> sj.top + sj.height}))
      j=j-1
    }
    
    if (s.interpolate == "basis") 
      pathT = Rubyvis::SvgScene.curve_basis(pointsT)
      pathB = Rubyvis::SvgScene.curve_basis(pointsB)
    elsif (s.interpolate == "cardinal") 
        pathT = Rubyvis::SvgScene.curve_cardinal(pointsT, s.tension);
        pathB = Rubyvis::SvgScene.curve_cardinal(pointsB, s.tension);
    elsif # monotone
      pathT = Rubyvis::SvgScene.curve_monotone(pointsT);
      pathB = Rubyvis::SvgScene.curve_monotone(pointsB);
    end
    
    "#{pointsT[0].left },#{ pointsT[0].top }#{ pathT }L#{ pointsB[0].left},#{pointsB[0].top}#{pathB}"
  }
    
  #/* points */
  d = []
  si=nil
  sj=nil
  i=0
  # puts "Scenes:#{scenes.size}, interpolation:#{scenes[0].interpolate}"
  
  while(i < scenes.size)
    si = scenes[i]
    if (si.width==0 and si.height==0)
      i+=1
      next
    end
    
    j=i+1
    while(j<scenes.size) do
      sj=scenes[j]
      break if sj.width==0 and sj.height==0
      j+=1
    end
    
    puts "j:#{j}" if $DEBUG
    
    i=i-1 if (i!=0 and (s.interpolate != "step-after"))
    
    j=j+1 if ((j < scenes.size) and (s.interpolate != "step-before"))
    
    d.push(((j - i > 2 and (s.interpolate == "basis" or s.interpolate == "cardinal" or s.interpolate == "monotone")) ? path_curve : path).call(i, j - 1))
    
    i = j - 1
    i+=1
    
  end
  
  return e if d.size==0

  e = self.expect(e, "path", {
    "shape-rendering"=> s.antialias ? nil : "crispEdges",
    "pointer-events"=> s.events,
    "cursor"=> s.cursor,
    "d"=> "M" + d.join("ZM") + "Z",
    "fill"=> fill.color,
    "fill-opacity"=> fill.opacity==0 ? nil : fill.opacity,
    "stroke"=> stroke.color,
    "stroke-opacity"=> stroke.opacity==0 ? nil : stroke.opacity,
    "stroke-width"=> stroke.opacity!=0 ? s.line_width / self.scale : nil
  })
  self.append(e, scenes, 0);
end
area_segment(scenes) click to toggle source
# File lib/rubyvis/scene/svg_area.rb, line 130
def self.area_segment(scenes)
  e=scenes._g.get_element(1)
  #e = scenes._g.elements[1]
  s = scenes[0]
  pathsT=nil
  pathsB=nil
  if (s.interpolate == "basis" or s.interpolate == "cardinal" or s.interpolate == "monotone") 
    pointsT = []
    pointsB = []
    n=scenes.size
    n.times {|i|
      sj = scenes[n - i - 1]
      pointsT.push(scenes[i])
      pointsB.push(OpenStruct.new({:left=> sj.left + sj.width, :top=> sj.top + sj.height}));
    }

    if (s.interpolate == "basis") 
      pathsT = Rubyvis::SvgScene.curve_basis_segments(pointsT)
      pathsB = Rubyvis::SvgScene.curve_basis_segments(pointsB)
    elsif (s.interpolate == "cardinal")
        pathsT = Rubyvis::SvgScene.curve_cardinal_segments(pointsT, s.tension);
        pathsB = Rubyvis::SvgScene.curve_cardinal_segments(pointsB, s.tension);
    elsif # monotone
      pathsT = Rubyvis::SvgScene.curve_monotone_segments(pointsT)
      pathsB = Rubyvis::SvgScene.curve_monotone_segments(pointsB)
    end
  end
  
  n=scenes.size-1
  n.times {|i|
    
    s1 = scenes[i]
    s2 = scenes[i + 1]

    # /* visible */
    next if (!s1.visible or !s2.visible)
    
    fill = s1.fill_style
    stroke = s1.stroke_style            
    next if (fill.opacity==0 and stroke.opacity==0)
    
    d=nil
    if (pathsT) 
      pathT = pathsT[i]
      pb=pathsB[n - i - 1]
      pathB = "L" + pb[1,pb.size-1]
      d = pathT + pathB + "Z";
    else 
      #/* interpolate */
      si = s1
      sj = s2
      
      case (s1.interpolate) 
        when "step-before"
          si = s2
        when "step-after"
          sj = s1
      end
      

      #/* path */
      d = "M#{s1.left},#{si.top}L#{s2.left},#{sj.top }L#{s2.left + s2.width},#{sj.top + sj.height}L#{s1.left + s1.width},#{si.top + si.height}Z"
    end

    e = self.expect(e, "path", {
        "shape-rendering"=> s1.antialias ? nil : "crispEdges",
        "pointer-events"=> s1.events,
        "cursor"=> s1.cursor,
        "d"=> d,
        "fill"=> fill.color,
        "fill-opacity"=> fill.opacity==0 ? nil : fill.opacity,
      "stroke"=> stroke.color,
      "stroke-opacity"=> stroke.opacity==0 ? nil : stroke.opacity,
      "stroke-width"=> stroke.opacity!=0 ? s1.line_width / self.scale : nil
      });
    e = self.append(e, scenes, i);
  }
  return e
end
bar(scenes) click to toggle source
# File lib/rubyvis/scene/svg_bar.rb, line 3
def self.bar(scenes)
  #e=scenes._g.elements[1]
  e=scenes._g.get_element(1)
  scenes.each_with_index do |s,i|
    next unless s.visible
    fill=s.fill_style
    stroke=s.stroke_style
    next if(fill.opacity==0 and stroke.opacity==0)
    e=SvgScene.expect(e, 'rect', {
      "shape-rendering"=> s.antialias ? nil : "crispEdges",
      "pointer-events"=> s.events,
      "cursor"=> s.cursor,
      "x"=> s.left,
      "y"=> s.top,
      "width"=> [1E-10, s.width].max,
      "height"=> [1E-10, s.height].max,
      "fill"=> fill.color,
      "fill-opacity"=> (fill.opacity==0) ? nil : fill.opacity,
      "stroke"=> stroke.color,
      "stroke-opacity"=> (stroke.opacity==0) ? nil : stroke.opacity,
      "stroke-width"=> stroke.opacity ? s.line_width / SvgScene.scale.to_f : nil
    })

    e=SvgScene.append(e,scenes,i)

  end
  e
end
cardinal_tangents(points, tension) click to toggle source

Computes the tangents for the given points needed for cardinal spline interpolation. Returns an array of tangent vectors. Note: that for n points only the n-2 well defined tangents are returned.

  • @param points the array of points.

  • @param tension the tension of hte cardinal spline.

# File lib/rubyvis/scene/svg_curve.rb, line 214
def self.cardinal_tangents(points, tension) 
  tangents = []
  a = (1 - tension) / 2.0
  p0 = points[0]
  p1 = points[1]
  p2 = points[2]
  3.upto(points.size-1) {|i|
    tangents.push(OpenStruct.new({:x=> a * (p2.left - p0.left), :y=> a * (p2.top - p0.top)}))
    p0 = p1;
    p1 = p2;
    p2 = points[i];
  }

  tangents.push(OpenStruct.new({:x=> a * (p2.left - p0.left), :y=> a * (p2.top - p0.top)}))
  return tangents;
end
curve_basis(points) click to toggle source

Interpolates the given points using the basis spline interpolation. Returns an SVG path without the leading M instruction to allow path appending.

# File lib/rubyvis/scene/svg_curve.rb, line 65
def self.curve_basis(points) 
  return "" if (points.size <= 2)
  path = ""
  p0 = points[0]
  p1 = p0
  p2 = p0
  p3 = points[1]
  
  path += self.path_basis(p0, p1, p2, p3).to_s
  2.upto(points.size-1) {|i|
    p0 = p1
    p1 = p2
    p2 = p3
    p3 = points[i]
    path += self.path_basis(p0, p1, p2, p3).to_s
  }
  #  Cycle through to get the last point.
  path += self.path_basis(p1, p2, p3, p3).to_s
  path += self.path_basis(p2, p3, p3, p3).to_s
  path;
end
curve_basis_segments(points) click to toggle source

Interpolates the given points using the basis spline interpolation. If points.length == tangents.length then a regular Hermite interpolation is performed, if points.length == tangents.length + 2 then the first and last segments are filled in with cubic bazier segments. Returns an array of path strings.

# File lib/rubyvis/scene/svg_curve.rb, line 93
def self.curve_basis_segments(points) 
  return "" if (points.size <= 2)
  paths = []
  p0 = points[0]
  p1 = p0
  p2 = p0
  p3 = points[1]
  firstPath = self.path_basis(p0, p1, p2, p3).segment
  p0 = p1;
  p1 = p2;
  p2 = p3;
  p3 = points[2];
  paths.push(firstPath + self.path_basis(p0, p1, p2, p3).to_s) # merge first & second path
  3.upto(points.size-1) {|i|
    p0 = p1;
    p1 = p2;
    p2 = p3;
    p3 = points[i];
    paths.push(path_basis(p0, p1, p2, p3).segment);
  }
  
  # merge last & second-to-last path
  paths.push(path_basis(p1, p2, p3, p3).segment + path_basis(p2, p3, p3, p3).to_s)
  paths
end
curve_cardinal(points, tension) click to toggle source

Interpolates the given points using cardinal spline interpolation. Returns an SVG path without the leading M instruction to allow path appending.

  • @param points the array of points.

  • @param tension the tension of hte cardinal spline.

# File lib/rubyvis/scene/svg_curve.rb, line 238
def self.curve_cardinal(points, tension)
  return "" if (points.size <= 2) 
  self.curve_hermite(points, self.cardinal_tangents(points, tension))
end
curve_cardinal_segments(points, tension) click to toggle source

Interpolates the given points using cardinal spline interpolation. Returns an array of path strings.

@param points the array of points. @param tension the tension of hte cardinal spline.

# File lib/rubyvis/scene/svg_curve.rb, line 247
def self.curve_cardinal_segments(points, tension) 
  return "" if (points.size <= 2) 
  self.curve_hermite_segments(points, self.cardinal_tangents(points, tension))
end
curve_hermite(points, tangents) click to toggle source
Interpolates the given points with respective tangents using the cubic
Hermite spline interpolation. If points.length == tangents.length then a regular
Hermite interpolation is performed, if points.length == tangents.length + 2 then
the first and last segments are filled in with cubic bazier segments.
Returns an SVG path without the leading M instruction to allow path appending.

* @param points the array of points.
* @param tangents the array of tangent vectors.

/

# File lib/rubyvis/scene/svg_curve.rb, line 129
def self.curve_hermite(points, tangents)
  return "" if (tangents.size < 1 or (points.size != tangents.size and points.size != tangents.size + 2)) 
  quad = points.size != tangents.size
  path = ""
  p0 = points[0]
  p = points[1]
  t0 = tangents[0]
  t = t0
  pi = 1

  if (quad) 
      path += "Q#{(p.left - t0.x * 2 / 3)},#{(p.top - t0.y * 2 / 3)},#{p.left},#{p.top}"
      p0 = points[1];
      pi = 2;
  end

  if (tangents.length > 1) 
    t = tangents[1]
    p = points[pi]
    pi+=1
    path += "C#{(p0.left + t0.x)},#{(p0.top + t0.y) },#{(p.left - t.x) },#{(p.top - t.y)},#{p.left},#{p.top}"
    
    2.upto(tangents.size-1) {|i|
      p = points[pi];
      t = tangents[i];
      path += "S#{(p.left - t.x)},#{(p.top - t.y)},#{p.left},#{p.top}"
      pi+=1
    }
  end

  if (quad) 
  lp = points[pi];
  path += "Q#{(p.left + t.x * 2 / 3)},#{(p.top + t.y * 2 / 3)},#{lp.left},#{lp.top}"
  end

  path;
end
curve_hermite_segments(points, tangents) click to toggle source

Interpolates the given points with respective tangents using the cubic Hermite spline interpolation. Returns an array of path strings.

  • @param points the array of points.

  • @param tangents the array of tangent vectors.

# File lib/rubyvis/scene/svg_curve.rb, line 171
def self.curve_hermite_segments(points, tangents)
  return [] if (tangents.size < 1 or  (points.size != tangents.size and points.size != tangents.size + 2)) 
  quad = points.size != tangents.size
  paths = []
  p0 = points[0]
  p = p0
  t0 = tangents[0]
  t = t0
  pi = 1
  
  if (quad) 
  p = points[1]
  paths.push("M#{p0.left},#{p0.top }Q#{(p.left - t.x * 2 / 3.0 )},#{(p.top - t.y * 2 / 3)},#{p.left},#{p.top}")
  pi = 2
  end
  
  1.upto(tangents.size-1) {|i|
  p0 = p;
  t0 = t;
  p = points[pi]
  t = tangents[i]
  paths.push("M#{p0.left },#{p0.top
    }C#{(p0.left + t0.x) },#{(p0.top + t0.y)
    },#{(p.left - t.x) },#{(p.top - t.y)
    },#{p.left },#{p.top}")
  pi+=1
  }
  
  if (quad) 
  lp = points[pi];
  paths.push("M#{p.left },#{p.top
      }Q#{(p.left + t.x * 2 / 3) },#{(p.top + t.y * 2 / 3) },#{lp.left },#{lp.top}")
  end
  
  paths
end
curve_monotone(points) click to toggle source

Interpolates the given points using Fritsch-Carlson Monotone cubic Hermite interpolation. Returns an SVG path without the leading M instruction to allow path appending.

  • @param points the array of points.

# File lib/rubyvis/scene/svg_curve.rb, line 324
def self.curve_monotone(points) 
   return "" if (points.length <= 2)
   return self.curve_hermite(points, self.monotone_tangents(points))
end
curve_monotone_segments(points) click to toggle source
Interpolates the given points using Fritsch-Carlson Monotone cubic
Hermite interpolation.
Returns an array of path strings.

* @param points the array of points.

/

# File lib/rubyvis/scene/svg_curve.rb, line 335
def self.curve_monotone_segments(points) 
  return "" if (points.size <= 2)
   self.curve_hermite_segments(points, self.monotone_tangents(points))
end
dot(scenes) click to toggle source
# File lib/rubyvis/scene/svg_dot.rb, line 3
def self.dot(scenes)
  #e = scenes._g.elements[1]
  e=scenes._g.get_element(1)
  scenes.each_with_index {|s,i|
  s = scenes[i];
  
  # visible */
  next if !s.visible
  fill = s.fill_style
  stroke = s.stroke_style
  next if (fill.opacity==0 and stroke.opacity==0)
  
  #/* points */
  radius = s.shape_radius
  path = nil
  case s.shape
  when 'cross'
    path = "M#{-radius},#{-radius}L#{radius},#{radius}M#{radius},#{ -radius}L#{ -radius},#{radius}"
  when "triangle"
    h = radius
    w = radius * 1.1547; # // 2 / Math.sqrt(3)
    path = "M0,#{h}L#{w},#{-h} #{-w},#{-h}Z"
  when  "diamond"
    radius=radius* Math::sqrt(2)
    path = "M0,#{-radius}L#{radius},0 0,#{radius} #{-radius},0Z";
  when  "square"
    path = "M#{-radius},#{-radius}L#{radius},#{-radius} #{radius},#{radius} #{-radius},#{radius}Z"
  when  "tick"
    path = "M0,0L0,#{-s.shapeSize}"
  when  "bar"
    path = "M0,#{s.shape_size / 2.0}L0,#{-(s.shapeSize / 2.0)}"

  end
  
  #/* Use <circle> for circles, <path> for everything else. */
  svg = {
  "shape-rendering"=> s.antialias ? nil : "crispEdges",
  "pointer-events"=> s.events,
  "cursor"=> s.cursor,
  "fill"=> fill.color,
  "fill-opacity"=> (fill.opacity==0) ? nil : fill.opacity,
  "stroke"=> stroke.color,
  "stroke-opacity"=> (stroke.opacity==0) ? nil : stroke.opacity,
  "stroke-width"=> (stroke.opacity!=0) ? s.line_width / self.scale : nil
  }
  
  if (path) 
      svg["transform"] = "translate(#{s.left},#{s.top})"
      if (s.shape_angle) 
        svg["transform"] += " rotate(#{180 * s.shape_angle / Math.PI})";
      end
    svg["d"] = path
    e = self.expect(e, "path", svg);
  else 
    svg["cx"] = s.left;
    svg["cy"] = s.top;
    svg["r"] = radius;
    e = self.expect(e, "circle", svg);
  end
  e = self.append(e, scenes, i);
  }
  return e
end
fill(e,scenes,i) click to toggle source
# File lib/rubyvis/scene/svg_panel.rb, line 107
def self.fill(e,scenes,i)
  s=scenes[i]
  fill=s.fill_style
  if(fill.opacity>0 or s.events=='all')
    e=SvgScene.expect(e,'rect', {
      "shape-rendering"=> s.antialias ? nil : "crispEdges",
      "pointer-events"=> s.events,
      "cursor"=> s.cursor,
      "x"=> s.left,
      "y"=> s.top,
      "width"=> s.width,
      "height"=> s.height,
      "fill"=> fill.color,
      "fill-opacity"=> fill.opacity,
      "stroke"=> nil
    })
    e=SvgScene.append(e,scenes, i)
  end
  e
end
image(scenes) click to toggle source
# File lib/rubyvis/scene/svg_image.rb, line 3
def self.image(scenes)
  #e=scenes._g.elements[1]
  e=scenes._g.get_element(1)
  scenes.each_with_index do |s,i|
    next unless s.visible
    e=self.fill(e,scenes,i)
    if s.image
      raise "Not implemented yet"
    else
      e = self.expect(e, "image", {
      "preserveAspectRatio"=> "none",
      "cursor"=> s.cursor,
      "x"=> s.left,
      "y"=> s.top,
      "width"=> s.width,
      "height"=> s.height,
      })
      e.set_attributes("xlink:href"=>s.url);
    end
    e = self.append(e, scenes, i);
    
    #/* stroke */
    e = self.stroke(e, scenes, i);
  end
  e
end
label(scenes) click to toggle source
# File lib/rubyvis/scene/svg_label.rb, line 3
def self.label(scenes)
  #e=scenes._g.elements[1]
  e=scenes._g.get_element(1)
  scenes.each_with_index do |s,i|
    next unless s.visible
    fill=s.text_style
    next if(fill.opacity==0 or s.text.nil?)
    x=0
    y=0
    dy=0
    anchor='start'
    case s.text_baseline
      when 'middle'
        dy=".35em"
      when "top"
        dy = ".71em"
        y = s.text_margin
      when "bottom"
        y = "-" + s.text_margin.to_s
    end
    
    case s.text_align
      when 'right'
        anchor = "end"
        x = "-" + s.text_margin.to_s
      when "center"
        anchor = "middle"
      when "left"
        x = s.text_margin
    end
    e=SvgScene.expect(e,'text', {
      "pointer-events"=> s.events,
      "cursor"=> s.cursor,
      "x"=> x,
      "y"=> y,
      "dy"=> dy,
      "transform"=> "translate(#{s.left},#{s.top})" + (s.text_angle!=0 ? " rotate(" + (180 * s.text_angle / Math::PI).to_s + ")" : "") + (self.scale != 1 ? " scale(" + 1 / self.scale + ")" : ""),
      "fill"=> fill.color,
      "fill-opacity"=> fill.opacity==0 ? nil : fill.opacity,
      "text-anchor"=> anchor
    }, {
    "font"=> s.font, "text-shadow"=> s.text_shadow, "text-decoration"=> s.text_decoration})
    
    e.text=s.text.frozen? ? s.text.dup : s.text


    e=SvgScene.append(e,scenes,i)

  end
  e
end
line(scenes) click to toggle source
# File lib/rubyvis/scene/svg_line.rb, line 3
def self.line(scenes)
  #e=scenes._g.elements[1]
  e=scenes._g.get_element(1)
  return e if (scenes.size < 2)
  s = scenes[0]
  # segmented */
  return self.line_segment(scenes) if (s.segmented)

  #/* visible */
  return e if (!s.visible)
  fill = s.fill_style
  stroke = s.stroke_style

  return e if (fill.opacity==0.0 and  stroke.opacity==0.0)
  #/* points */

  d = "M#{s.left},#{s.top}"

  if (scenes.size > 2 and (['basis', 'cardinal', 'monotone'].include? s.interpolate))
    case (s.interpolate)
      when "basis"
        d = d+ curve_basis(scenes)
      when "cardinal"
        d = d+curve_cardinal(scenes, s.tension)
      when "monotone"
        d = d+curve_monotone(scenes)
    end

  else
    (1...scenes.size).each {|i|
      d+= path_segment(scenes[i-1],scenes[i])
    }
  end

  e = SvgScene.expect(e, "path", {
    "shape-rendering"=> s.antialias ? nil : "crispEdges",
    "pointer-events"=> s.events,
    "cursor"=> s.cursor,
    "d"=> d,
    "fill"=> fill.color,
    "fill-opacity"=> (fill.opacity==0.0) ? nil : fill.opacity,
    "stroke"=> stroke.color,
    "stroke-opacity"=> (stroke.opacity==0.0) ? nil : stroke.opacity,
    "stroke-width"=> (stroke.opacity>0) ? s.line_width / self.scale : nil,
    "stroke-linejoin"=> s.line_join
  });
  return SvgScene.append(e, scenes, 0);
end
line_intersect(o1, d1, o2, d2) click to toggle source

@private Line-line intersection, per Akenine-Moller 16.16.1.

# File lib/rubyvis/scene/svg_line.rb, line 138
def self.line_intersect(o1, d1, o2, d2)
  return o1.plus(d1.times(o2.minus(o1).dot(d2.perp()) / d1.dot(d2.perp())));
end
line_segment(scenes) click to toggle source
# File lib/rubyvis/scene/svg_line.rb, line 52
def self.line_segment(scenes)

  #e=scenes._g.elements[1]
  e=scenes._g.get_element(1)
  s = scenes[0];
  paths=nil
  case s.interpolate
    when "basis"
      paths = curve_basis_segments(scenes)
    when "cardinal"
      paths=curve_cardinal_segments(scenes, s.tension)
    when "monotone"
      paths = curve_monotone_segments(scenes)
  end

  (0...(scenes.size-1)).each {|i|

    s1 = scenes[i]
    s2 = scenes[i + 1];
    #p "#{s1.top} #{s1.left} #{s1.line_width} #{s1.interpolate} #{s1.line_join}"
    # visible 
    next if (!s1.visible or !s2.visible)

    stroke = s1.stroke_style
    fill = Rubyvis::Color.transparent

    next if stroke.opacity==0.0

    # interpolate
    d=nil
    if ((s1.interpolate == "linear") and (s1.line_join == "miter"))
      fill = stroke
      stroke = Rubyvis::Color.transparent
      s0=((i-1) < 0) ? nil : scenes[i-1]
      s3=((i+2) >= scenes.size) ? nil : scenes[i+2]
      
      d = path_join(s0, s1, s2, s3)
    elsif(paths)
      d = paths[i]
    else
      d = "M#{s1.left},#{s1.top}#{path_segment(s1, s2)}"
    end

    e = SvgScene.expect(e, "path", {
      "shape-rendering"=> s1.antialias ? nil : "crispEdges",
      "pointer-events"=> s1.events,
      "cursor"=> s1.cursor,
      "d"=> d,
      "fill"=> fill.color,
      "fill-opacity"=> (fill.opacity==0.0) ? nil : fill.opacity,
      "stroke"=> stroke.color,
      "stroke-opacity"=> (stroke.opacity==0.0) ? nil : stroke.opacity,
      "stroke-width"=> stroke.opacity>0 ? s1.line_width / self.scale : nil,
      "stroke-linejoin"=> s1.line_join
    });
    e = SvgScene.append(e, scenes, i);
  }
  e
end
monotone_tangents(points) click to toggle source

Interpolates the given points using Fritsch-Carlson Monotone cubic Hermite interpolation. Returns an array of tangent vectors.

*@param points the array of points.

# File lib/rubyvis/scene/svg_curve.rb, line 256
  def self.monotone_tangents(points) 
    tangents = []
    d = []
    m = []
    dx = []
    #k=0
    
    #/* Compute the slopes of the secant lines between successive points. */
    
    
    0.upto(points.size-2) do |k| 
    
#    while(k < points.size-1) do 
      d[k] = (points[k+1].top - points[k].top) / (points[k+1].left - points[k].left).to_f
      k+=1
    end
    
    #/* Initialize the tangents at every point as the average of the secants. */
    m[0] = d[0]
    dx[0] = points[1].left - points[0].left
    
    
    1.upto(points.size-2) {|k|
      m[k] = (d[k-1]+d[k]) / 2.0
      dx[k] = (points[k+1].left - points[k-1].left) / 2.0
    }
    
    k=points.size-1
    
    m[k] = d[k-1];
    dx[k] = (points[k].left - points[k-1].left);
    
    # /* Step 3. Very important, step 3. Yep. Wouldn't miss it. */
    (points.size-1).times {|kk|
      if d[kk] == 0 
        m[ kk ] = 0;
        m[kk + 1] = 0;
      end
    }
    
    # /* Step 4 + 5. Out of 5 or more steps. */
    
    
    (points.size-1).times {|kk|
      next if ((m[kk].abs < 1e-5) or (m[kk+1].abs < 1e-5))
      akk = m[kk] / d[kk].to_f
      bkk = m[kk + 1] / d[kk].to_f
      s = akk * akk + bkk * bkk; # monotone constant (?)
      if (s > 9) 
        tkk = 3.0 / Math.sqrt(s)
        m[kk] = tkk * akk * d[kk]
        m[kk + 1] = tkk * bkk * d[kk]
      end
    }
    len=nil;
    points.size.times {|i|
      len = 1 + m[i] * m[i]; #// pv.vector(1, m[i]).norm().times(dx[i]/3)
      tangents.push(OpenStruct.new({:x=> dx[i] / 3.0 / len, :y=> m[i] * dx[i] / 3.0 / len}))
    }
    
    tangents;
  end
panel(scenes) click to toggle source
# File lib/rubyvis/scene/svg_panel.rb, line 3
def self.panel(scenes)
  puts " -> panel: #{scenes.inspect}" if $DEBUG
  g=scenes._g
  #e=(g.nil?) ? nil : g.elements[1]
  e=(g.nil?) ? nil : g.get_element(1) 
  if g
    #e=g.elements[1]
    e=g.get_element(1)
  end
  scenes.each_with_index do |s,i|
    next unless s.visible

    if(!scenes.parent)
      if g and g.parent!=s.canvas
        #g=s.canvas.elements[1]
        g=s.canvas.get_element(1)
        #e=(@g.nil?) ? nil : @g.elements[1]
        e=(@g.nil?) ? nil : @g.get_element(1)
      end
      if(!g)
        g=s.canvas.add_element(self.create('svg'))
        g.set_attributes(
          {
            'font-size'=>"10px",
            'font-family'=>'sans-serif',
            'fill'=>'none',
            'stroke'=>'none',
            'stroke-width'=>1.5
          }
        )
        e=g.get_element(1)
        # g.attributes["font-size"]="10px"
        # g.attributes["font-family"]="sans-serif"
        # g.attributes["fill"]="none"
        # g.attributes["stroke"]="none"
        # g.attributes["stroke-width"]=1.5
        # e=g.elements[1]
      end
      scenes._g=g
      #p s
      g.set_attributes({
          'width'=>s.width+s.left+s.right,
          'height'=>s.height+s.top+s.bottom
      })
      #g.attributes['width']=s.width+s.left+s.right
      #g.attributes['height']=s.height+s.top+s.bottom
    end
    if s.overflow=='hidden'
      id=Rubyvis.id.to_s(36)
      c=self.expect(e,'g',{'clip-path'=>'url(#'+id+')'});
      g.add_element(c) if(!c.parent)
      scenes._g=g=c
      #e=c.elements[1]
      e=c.get_element(1)
      e=self.expect(e,'clipPath',{'id'=>id})
      #r=(e.elements[1]) ? e.elements[1] : e.add_element(self.create('rect'))
      r=(e.get_element(1)) ? e.get_element(1) : e.add_element(self.create('rect'))
      r.set_attributes({
          'x'=>s.left,
          'y'=>s.top,
          'width'=>s.width,
          'height'=>s.height
      })
      #r.attributes['x']=s.left
      #r.attributes['y']=s.top
      #r.attributes['width']=s.width
      #r.attributes['height']=s.height
      g.add_element(e) if !e.parent
      e=e.next_sibling_node

    end
    # fill
    e=self.fill(e,scenes, i)
    # transform
    k=self.scale
    t=s.transform
    x=s.left+t.x
    y=s.top+t.y
    SvgScene.scale=SvgScene.scale*t.k
    # children
    s.children.each_with_index {|child, i2|
      child._g=e=SvgScene.expect(e, "g", {
        "transform"=> "translate(" + x.to_s + "," + y.to_s + ")" + (t.k != 1 ? " scale(" + t.k.to_s + ")" : "")
      })
      SvgScene.update_all(child)
      g.add_element(e) if(!e.parent)
      e=e.next_sibling_node
    }
    # transform (pop)
    SvgScene.scale=k
    # stroke
    e=SvgScene.stroke(e,scenes,i)
    # clip
    if (s.overflow=='hidden')
      scenes._g=g=c.parent
      e=c.next_sibling_node
    end
  end
  return e
  
end
path_basis(p0,p1,p2,p3) click to toggle source

Converts the specified b-spline curve segment to a bezier curve compatible with SVG ā€œCā€.

  • @param p0 the first control point.

  • @param p1 the second control point.

  • @param p2 the third control point.

  • @param p3 the fourth control point.

# File lib/rubyvis/scene/svg_curve.rb, line 56
def self.path_basis(p0,p1,p2,p3)
  PathBasis.new(p0,p1,p2,p3)
end
path_join(s0, s1, s2, s3) click to toggle source

@private Returns the miter join path for the specified points.

# File lib/rubyvis/scene/svg_line.rb, line 143
def self.path_join(s0, s1, s2, s3)
  #
  # P1-P2 is the current line segment. V is a vector that is perpendicular to
  # the line segment, and has length lineWidth / 2. ABCD forms the initial
  # bounding box of the line segment (i.e., the line segment if we were to do
  # no joins).
  #

  p1 = Rubyvis.vector(s1.left, s1.top)

  p2 = Rubyvis.vector(s2.left, s2.top)

  _p = p2.minus(p1)

  v = _p.perp().norm()
  
  w = v.times(s1.line_width / (2.0 * self.scale))

  a = p1.plus(w)
  b = p2.plus(w)
  c = p2.minus(w)
  d = p1.minus(w)
  #/*
  # * Start join. P0 is the previous line segment's start point. We define the
  # * cutting plane as the average of the vector perpendicular to P0-P1, and
  # * the vector perpendicular to P1-P2. This insures that the cross-section of
  # * the line on the cutting plane is equal if the line-width is unchanged.
  # * Note that we don't implement miter limits, so these can get wild.
  # */
  if (s0 and s0.visible)
    v1 = p1.minus(s0.left, s0.top).perp().norm().plus(v)
    d = line_intersect(p1, v1, d, _p)
    a = line_intersect(p1, v1, a, _p)
  end

  #/* Similarly, for end join. */
  if (s3 and s3.visible)
    v2 = Rubyvis.vector(s3.left, s3.top).minus(p2).perp().norm().plus(v);
    c = line_intersect(p2, v2, c, _p);
    b = line_intersect(p2, v2, b, _p);
  end

  d="M#{a.x},#{a.y}L#{b.x},#{b.y} #{c.x},#{c.y} #{d.x},#{d.y}"
  d
end
path_segment(s1, s2) click to toggle source

Returns the path segment for the specified points. */

# File lib/rubyvis/scene/svg_line.rb, line 114
def self.path_segment(s1, s2) 
  l = 1; # sweep-flag
  l = 0 if s1.interpolate=='polar-reverse'
  
  if s1.interpolate=='polar' or s1.interpolate=='polar-reverse'
    dx = s2.left - s1.left
    dy = s2.top - s1.top
    e = 1 - s1.eccentricity
    r = Math.sqrt(dx * dx + dy * dy) / (2 * e)
    if !((e<=0) or (e>1))
      return "A#{r},#{r} 0 0,#{l} #{s2.left},#{s2.top}"
    end
  end
  
  if s1.interpolate=="step-before"
    return "V#{s2.top}H#{s2.left}"
  elsif s1.interpolate=="step-after"
    return "H#{s2.left}V#{s2.top}"
  end
  
  return "L#{s2.left},#{s2.top}"
end
rule(scenes) click to toggle source
# File lib/rubyvis/scene/svg_rule.rb, line 3
def self.rule(scenes)
  #e=scenes._g.elements[1]
  e=scenes._g.get_element(1)
  scenes.each_with_index do |s,i|
    next unless s.visible
    stroke=s.stroke_style
    next if(stroke.opacity==0.0)
    e=SvgScene.expect(e,'line', {
      "shape-rendering"=> s.antialias ? nil : "crispEdges",
      "pointer-events"=> s.events,
      "cursor"=> s.cursor,
      "x1"=> s.left,
      "y1"=> s.top,
      'x2'=> s.left+s.width,
      'y2'=>s.top+s.height,
      "stroke"=> stroke.color,
      "stroke-opacity"=> stroke.opacity,
      "stroke-width"=> s.line_width / self.scale
    })

    e=SvgScene.append(e,scenes,i)

  end
  e
end
stroke(e, scenes, i) click to toggle source
# File lib/rubyvis/scene/svg_panel.rb, line 129
def self.stroke(e, scenes, i)
  s = scenes[i]
  stroke = s.stroke_style
  if (stroke.opacity>0 or s.events == "all")
    e = self.expect(e, "rect", {
      "shape-rendering"=> s.antialias ? nil : "crispEdges",
      "pointer-events"=> s.events == "all" ? "stroke" : s.events,
      "cursor"=> s.cursor,
      "x"=> s.left,
      "y"=> s.top,
      "width"=> [1E-10, s.width].max,
      "height"=>[1E-10, s.height].max,
      "fill"=>"none",
      "stroke"=> stroke.color,
      "stroke-opacity"=> stroke.opacity,
      "stroke-width"=> s.line_width / self.scale.to_f
    });
    e = self.append(e, scenes, i);
  end
  return e
end
wedge(scenes) click to toggle source
# File lib/rubyvis/scene/svg_wedge.rb, line 3
def self.wedge(scenes)
  #e=scenes._g.elements[1]
  e=scenes._g.get_element(1)
  scenes.each_with_index do |s,i|
    next unless s.visible
    fill=s.fill_style
    stroke=s.stroke_style
    next if(fill.opacity==0.0 and stroke.opacity==0.0)
    # /* points */
    
    r1 = s.inner_radius
    r2 = s.outer_radius

    a = (s.angle).abs
    _p=nil
    
    if (a >= 2 * Math::PI) 
      if (r1!=0) 
        _p = "M0,#{r2 }A#{r2},#{r2} 0 1,1 0,#{-r2}A#{r2 },#{r2 } 0 1,1 0,#{r2}M0,#{r1}A#{r1},#{r1} 0 1,1 0,#{-r1}A#{r1},#{r1} 0 1,1 0,#{r1 }Z"
      else 
        _p = "M0,#{r2}A#{r2},#{r2} 0 1,1 0,#{-r2}A#{r2},#{r2} 0 1,1 0,#{r2 }Z"
      end
    else 
      sa = [s.start_angle, s.end_angle].min
      ea = [s.start_angle, s.end_angle].max
      c1 = Math.cos(sa)
      c2 = Math.cos(ea)
      s1 = Math.sin(sa)
      s2 = Math.sin(ea)
      if (r1!=0)
        _p = "M#{r2 * c1},#{r2 * s1}A#{r2},#{r2} 0 #{((a < Math::PI) ? "0" : "1")},1 #{r2 * c2},#{r2 * s2}L#{r1 * c2},#{r1 * s2}A#{r1},#{r1} 0 #{((a < Math::PI) ? "0" : "1")},0 #{r1 * c1},#{r1 * s1}Z"
      else 
        _p = "M#{r2 * c1},#{r2 * s1}A#{r2},#{r2} 0 #{((a < Math::PI) ? "0" : "1")},1 #{r2 * c2},#{r2 * s2}L0,0Z"
      end
    end
    
    e = self.expect(e, "path", {
      "shape-rendering"=> s.antialias ? nil : "crispEdges",
      "pointer-events"=> s.events,
      "cursor"=> s.cursor,
      "transform"=> "translate(#{s.left},#{s.top})",
      "d"=> _p,
      "fill"=> fill.color,
      "fill-rule"=> "evenodd",
      "fill-opacity"=>  (fill.opacity==0) ? nil : fill.opacity,
      "stroke"=> stroke.color,
      "stroke-opacity"=> (stroke.opacity==0) ? nil : stroke.opacity,
      "stroke-width"=> stroke.opacity>0 ? s.line_width / self.scale.to_f : nil
    });
    e=SvgScene.append(e,scenes,i)

  end
  e
end