Class: BezierCurve

Inherits:
Object
  • Object
show all
Defined in:
lib/bezier_curve.rb,
lib/bezier_curve/version.rb

Overview

bezier_curve/version.rb Just the version information for this library

Defined Under Namespace

Classes: DifferingDimensionError, InsufficientPointsError, ZeroDimensionError

Constant Summary collapse

VERSION =
"0.8.0"
RELEASE_DATE =
"2015-06-18"

Instance Attribute Summary collapse

Instance Method Summary collapse

Constructor Details

#initialize(*controls) ⇒ BezierCurve

create a new curve, from a list of points.



19
20
21
22
23
24
25
26
# File 'lib/bezier_curve.rb', line 19

def initialize(*controls)
  # check for argument errors
  ZeroDimensionError.check! controls
  DifferingDimensionError.check! controls
  InsufficientPointsError.check! controls

  @controls = controls.map(&:to_np)
end

Instance Attribute Details

#controlsObject (readonly)

Returns the value of attribute controls.



28
29
30
# File 'lib/bezier_curve.rb', line 28

def controls
  @controls
end

Instance Method Details

#degreeObject Also known as: order

the degree of the curve



38
39
40
# File 'lib/bezier_curve.rb', line 38

def degree
  controls.size - 1
end

#dimensionsObject

the number of dimensions given



43
44
45
# File 'lib/bezier_curve.rb', line 43

def dimensions
  controls[0].size
end

#firstObject Also known as: start

the first control point



31
# File 'lib/bezier_curve.rb', line 31

def first() controls.first; end

#index(t) ⇒ Object Also known as: []

find the point for a given value of ‘t`.



48
49
50
51
52
53
54
55
56
# File 'lib/bezier_curve.rb', line 48

def index(t)
  pts = controls
  while pts.size > 1
    pts = (0..pts.size-2).map do |i|
      pts[i].zip(pts[i+1]).map{|a,b| t*(b-a)+a}
    end
  end
  pts[0].to_np
end

#is_straight?(tolerance) ⇒ Boolean

test this curve to see of it can be considered straight, optionally within the given angular tolerance, in radians

Returns:

  • (Boolean)


102
103
104
105
106
107
108
109
110
111
112
113
# File 'lib/bezier_curve.rb', line 102

def is_straight?(tolerance)
  # sanity check for tolerance in radians
  if first.angle_to(index(0.5), last) <= tolerance
    # maximum wavyness is `degree` - 1; split at `degree` points
    pts = points(count:degree)
    # size-3, because we ignore the last 2 points as starting points;
    # check all angles against `tolerance`
    (0..pts.size-3).all? do |i|
      pts[i].angle_to(pts[i+1], pts[i+2]) < tolerance
    end
  end
end

#lastObject Also known as: end

the last control point



34
# File 'lib/bezier_curve.rb', line 34

def last()  controls.last;  end

#points(count: nil, tolerance: Math::PI/64) ⇒ Object

Returns a list of points on this curve. If you specify ‘count`, returns that many points, evenly spread over values of `t`. If you specify `tolerance`, no adjoining line segments will deviate from 180 by an angle of more than the value given (in radians). If unspecified, defaults to `tolerance: 1/64pi` (~3 deg)



79
80
81
82
83
84
85
86
# File 'lib/bezier_curve.rb', line 79

def points(count:nil, tolerance:Math::PI/64)
  if count
    (0...count).map{|i| index i/(count-1.0)}
  else
    lines = subdivide(tolerance)
    lines.map{|seg|seg.first} + [lines.last.last]
  end
end

#split_at(t) ⇒ Object

divide this bezier curve into two curves, at the given ‘t`



60
61
62
63
64
65
66
67
68
69
70
71
# File 'lib/bezier_curve.rb', line 60

def split_at(t)
  pts = controls
  a,b = [pts.first],[pts.last]
  while pts.size > 1
    pts = (0..pts.size-2).map do |i|
      pts[i].zip(pts[i+1]).map{|a,b| t*(b-a)+a}
    end
    a<<pts.first
    b<<pts.last
  end
  [BezierCurve.new(*a), BezierCurve.new(*b.reverse)]
end

#subdivide(tolerance) ⇒ Object

recursively subdivides the curve until each is straight within the given tolerance value, in radians



90
91
92
93
94
95
96
97
# File 'lib/bezier_curve.rb', line 90

def subdivide(tolerance)
  if is_straight?(tolerance)
    [self]
  else
    a,b = split_at(0.5)
    a.subdivide(tolerance) + b.subdivide(tolerance)
  end
end