Sha256: ecc7371f1e3eb23dc91109ea560152c1613dd3f81a6c3582527ede50b4142738

Contents?: true

Size: 1.59 KB

Versions: 1

Compression:

Stored size: 1.59 KB

Contents

def midpoint(a, b); 0.5*(a+b); end

# Applies an affine linear transformation matrix to a set of points
def transform_points(trf, pts)
  pts.each {|pt| pt[0],pt[1] = *(trf * Vector[*pt,1]) }
end

class Outline
  Segment = Struct.new(:beg, :end, :ctrl)
  
  attr_reader :points, :segments
  
  def initialize
    @points, @segments = [], []
  end

  def clip_points(width, height)
    @points.each do |pt|
      pt[0] = pt[0].clamp(0, width.pred)
      pt[1] = pt[1].clamp(0, height.pred)
    end
  end

  def render(transform, image)
    transform_points(transform, @points)
    clip_points(image.width, image.height)
    buf = Raster.new(image.width, image.height)
    @segments.each do |seg|
      seg.ctrl ? tesselate_curve(seg) : buf.draw_line(@points[seg.beg], @points[seg.end])
    end
    image.pixels = buf.post_process
    return image
  end

  def tesselate_curve(curve)
    if is_flat(curve)
      @segments << Segment.new(curve.beg, curve.end)
      return
    end
    ctrl0 = @points.length
    @points << midpoint(@points[curve.beg], @points[curve.ctrl])
    ctrl1 = @points.length
    @points << midpoint(@points[curve.ctrl], @points[curve.end])
    pivot = @points.length
    @points << midpoint(@points[ctrl0], @points[ctrl1])
    tesselate_curve(Segment.new(curve.beg, pivot, ctrl0))
    tesselate_curve(Segment.new(pivot, curve.end, ctrl1))
  end

  # A heuristic to tell whether a given curve can be approximated closely enough by a line. */
  def is_flat(curve)
    g = @points[curve.ctrl] - @points[curve.beg]
    h = @points[curve.end]  - @points[curve.beg]
    (g[0]*h[1]-g[1]*h[0]).abs <= 2.0
  end
end

Version data entries

1 entries across 1 versions & 1 rubygems

Version Path
skrift-0.1.0 lib/skrift/outline.rb