dem-curves 0.0.1

Sign up to get free protection for your applications and to get access to all the features.
@@ -0,0 +1,7 @@
1
+ ---
2
+ SHA1:
3
+ metadata.gz: 95c1fab002f919736b30a60e299e82c05a10f1fb
4
+ data.tar.gz: 9a9a364baf601ea4c82d302b38cb2fb7b62fa543
5
+ SHA512:
6
+ metadata.gz: ce2ca54589e1a097467d565a315383b3269a3cee37f7dbfc8c6e12514ae9ebf38a909721ce7de4c797896f8e16f2b0ea49034bcec57efd2eb7d3def32ffb6757
7
+ data.tar.gz: f78075a71096ba4ffc79600ed2ddc28a2667a5d234b86923a85b69295ade8c878823960109b19f07f3846347fabf402e7d07a47dfe15bda70901b31039eba1ba
@@ -0,0 +1,79 @@
1
+ module DemCurves
2
+ module BaseConstraint
3
+ master_point = nil
4
+ slave_points = []
5
+
6
+ def notify(src, orig_src, params)
7
+ if src == @master_point
8
+ handle_master src, orig_src, params
9
+ elsif @slave_points.include? src
10
+ handle_slave src, orig_src, params
11
+ end
12
+ end
13
+
14
+ def handle_master(master, orig_src, params)
15
+ # This has to be implemented by the class
16
+ end
17
+
18
+ def handle_slave(slave, orig_src, params)
19
+ # This has to be implemented by the class
20
+ end
21
+ end
22
+
23
+
24
+ class LineUpConstraint
25
+ include BaseConstraint
26
+ def initialize(pivot, p0, p1, mirror_distance=false, follow=:pivot)
27
+ pivot.add_constraint self
28
+ p0.add_constraint self
29
+ p1.add_constraint self
30
+
31
+ @master_point = pivot
32
+ @slave_points = [p0, p1]
33
+ @mirror_distance = mirror_distance
34
+ @follow = follow
35
+ p1.move_to p1.loc #hacky way to trigger readjustment
36
+ end
37
+
38
+ def handle_master(master, orig_src, params)
39
+ if (params.include? :new_pos and params.include? :old_pos) or params.include? :rel
40
+ case @follow
41
+ when :pivot
42
+ unless params.include? :rel
43
+ params[:rel] = params[:new_pos], params[:old_pos]
44
+ end
45
+
46
+ params.delete(:new_pos)
47
+ params.delete(:old_pos)
48
+
49
+ @slave_points.each do |slave|
50
+ slave.notify_to_move orig_src, self, params
51
+ end
52
+ when :p0
53
+ direction = (params[:new_pos] - @slave_points[0].loc).unit
54
+ distance = (params[:new_pos] - @slave_points[1].loc).r
55
+ @slave_points[1].notify_to_move orig_src, self, {:new_pos => params[:new_pos] + (distance * direction)}
56
+ when :p1
57
+ direction = (params[:new_pos] - @slave_points[1].loc).unit
58
+ distance = (params[:new_pos] - @slave_points[0].loc).r
59
+ @slave_points[0].notify_to_move orig_src, self, {:new_pos => params[:new_pos] + (distance * direction)}
60
+ end
61
+ end
62
+ end
63
+
64
+ def handle_slave(slave, orig_src, params)
65
+ if params.include? :new_pos
66
+ other_slave = (@slave_points.select {|s| s!=slave})[0]
67
+ direction = (params[:new_pos] - @master_point.loc).unit
68
+ if @mirror_distance
69
+ distance = (slave.loc - @master_point.loc).r
70
+ else
71
+ distance = (other_slave.loc - @master_point.loc).r
72
+ end
73
+
74
+ new_loc = @master_point.loc + (-1 * direction * distance)
75
+ other_slave.notify_to_move orig_src, self, {:new_pos => new_loc}
76
+ end
77
+ end
78
+ end
79
+ end
@@ -0,0 +1,106 @@
1
+ require 'matrix'
2
+ require 'core/util.rb'
3
+
4
+ module DemCurves
5
+ class ControlPoint
6
+ # this class is necessary to let other curves and path elements modify
7
+ # points, it works much better than calling "notify movement" functions
8
+ # every time a control point moves, this comes in handy when you want a curve
9
+ # to use another curve's end point as a starting point.
10
+ attr_reader :loc
11
+
12
+ def initialize(loc)
13
+ @loc = Vector.elements(loc)
14
+ @path_elements = []
15
+ @constraints = []
16
+ end
17
+
18
+ def add_path_element(path_element)
19
+ @path_elements << path_element
20
+ end
21
+
22
+ def replace(other)
23
+ old_pos = @loc
24
+ case other
25
+ when ControlPoint
26
+ @loc = Vector.elements(other.loc)
27
+ when Vector, Array
28
+ unless other.size == 2
29
+ raise "Wrong number of dimensions, must be [x, y]"
30
+ end
31
+ @loc = Vector.elements(other)
32
+ else
33
+ raise "Argument is instance of #{other.class}! Replacement argument must be an instance of Vector, Array or ControlPoint."
34
+ end
35
+
36
+ @path_elements.each do |path_element|
37
+ path_element.generate
38
+ end
39
+ end
40
+
41
+ def shift(rel)
42
+ unless rel.size == 2
43
+ raise "Wrong number of dimensions, must be [x, y]"
44
+ end
45
+
46
+ old_pos = @loc
47
+ move_to @loc + Vector.elements(rel)
48
+ end
49
+
50
+ def move_to(destination)
51
+ old_pos = @loc
52
+ replace destination
53
+
54
+ new_pos = @loc
55
+ rel = new_pos - old_pos
56
+
57
+ @constraints.each do |constraint|
58
+ constraint.notify self, self, {:new_pos => new_pos, :old_pos => old_pos, :rel => rel}
59
+ end
60
+ end
61
+
62
+ def rotate_around(pivot_ctl, angle)
63
+ offset = @loc - pivot_ctl.loc
64
+ new_offset = Matrix[[Math.cos(angle), -Math.sin(angle)], [Math.sin(angle), Math.cos(angle)]] * offset
65
+ replace new_offset + pivot_ctl.loc
66
+ end
67
+
68
+ def notify_to_move(orig_src, src_constraint, params)
69
+ unless orig_src == self
70
+ # Safety measure, it avoids infinite recursion, but produces weird results
71
+ # with cyclic constraint structures.
72
+ old_pos = @loc
73
+ if params.include? :new_pos
74
+ replace params[:new_pos]
75
+ elsif params.include? :rel
76
+ replace params[:rel] + @loc
77
+ else
78
+ return
79
+ end
80
+
81
+ new_pos = @loc
82
+ rel = new_pos - old_pos
83
+
84
+ @constraints.each do |constraint|
85
+ unless constraint == src_constraint
86
+ constraint.notify self, orig_src, {:new_pos => new_pos, :old_pos => old_pos, :rel => rel}
87
+ end
88
+ end
89
+ end
90
+ end
91
+
92
+ def add_constraint(constraint)
93
+ unless @constraints.include? constraint
94
+ @constraints << constraint
95
+ end
96
+ end
97
+
98
+ def clear_constraints
99
+ @constraints = []
100
+ end
101
+ end
102
+
103
+ def ControlPoint.[](*loc)
104
+ return ControlPoint.new loc
105
+ end
106
+ end
@@ -0,0 +1,88 @@
1
+ require 'core/util.rb'
2
+
3
+ module DemCurves
4
+ class PathElement
5
+ # Do not directly instantiate this class
6
+ attr_reader :path_points, :control_points
7
+ def initialize(control_points)
8
+ @control_points = control_points
9
+ @control_points.each_value do |control_point|
10
+ control_point.add_path_element self
11
+ end
12
+
13
+ @path_points = []
14
+ generate
15
+ end
16
+
17
+ def generate
18
+ @path_points = @control_points.values.collect {|point| point.loc}
19
+ end
20
+
21
+ def set_control(control_id, loc)
22
+ unless control_id.class == Symbol
23
+ raise 'control_id must be a symbol'
24
+ end
25
+
26
+ @control_points[control_id].move_to location
27
+ generate
28
+ end
29
+
30
+ def []=(control_id, loc)
31
+ set_control control_id, loc
32
+ end
33
+
34
+ def get_control(control_id)
35
+ unless control_id.class == Symbol
36
+ raise 'control_id must be a symbol'
37
+ end
38
+
39
+ return @control_points[control_id]
40
+ end
41
+
42
+ def [](control_id)
43
+ get_control control_id
44
+ end
45
+ end
46
+
47
+
48
+ class CubicBezier < PathElement
49
+ def initialize(start_point, start_handle, end_handle, end_point)
50
+ super({
51
+ :start => start_point,
52
+ :start_handle => start_handle,
53
+ :end_handle => end_handle,
54
+ :end => end_point})
55
+ end
56
+
57
+ def generate(t_freq=32)
58
+ step = 1.0 / t_freq
59
+ @path_points = (0..t_freq).collect {|i| interpolate(step * i)}
60
+ end
61
+
62
+ def interpolate(t)
63
+ # http://mathworld.wolfram.com/BezierCurve.html
64
+ (0..3).inject(Vector[0, 0]) do |mem, i|
65
+ mem += @control_points.values[i].loc.clone * bernstein_basis(i, 3, t)
66
+ end
67
+ end
68
+
69
+ def get_guides
70
+ # this will be removed soon.
71
+ return [[self[:start].loc, self[:start_handle].loc], [self[:end_handle].loc, self[:end].loc]]
72
+ end
73
+ end
74
+
75
+
76
+ class Line < PathElement
77
+ def initialize(start_point, center_point, end_point)
78
+ super({
79
+ :start => start_point,
80
+ :center => center_point,
81
+ :end => end_point})
82
+ end
83
+
84
+ def generate
85
+ @path_points = [self[:start].loc, self[:end].loc]
86
+ end
87
+ end
88
+ end
@@ -0,0 +1,87 @@
1
+ require 'core/path-element.rb'
2
+
3
+ module DemCurves
4
+ class Path
5
+ include Enumerable
6
+ attr_reader :path_points, :path_elements, :control_points
7
+
8
+ def initialize(point)
9
+ @path_elements = []
10
+
11
+ @start_point = ControlPoint[*point]
12
+ @end_point = @start_point
13
+
14
+ @path_points = to_a
15
+ @control_points = [@start_point]
16
+ end
17
+
18
+ def add_bezier(start_handle, end_handle, end_point, tangent_lock=true)
19
+ new_bezier = CubicBezier.new(
20
+ @end_point,
21
+ ControlPoint[*start_handle],
22
+ ControlPoint[*end_handle],
23
+ ControlPoint[*end_point])
24
+
25
+ if tangent_lock and @path_elements.last
26
+ start_length = (new_bezier[:start_handle].loc - @end_point.loc).r
27
+ last_element = @path_elements.last
28
+
29
+ case last_element
30
+ when CubicBezier
31
+ LineUpConstraint.new @end_point, last_element[:end_handle], new_bezier[:start_handle]
32
+ when Line
33
+ LineUpConstraint.new @end_point, last_element[:center], new_bezier[:start_handle], morror_distance=false, follow=:p0
34
+ end
35
+ end
36
+
37
+ @end_point = new_bezier[:end]
38
+ @path_elements << new_bezier
39
+
40
+ @path_points = to_a
41
+ @control_points += new_bezier.control_points.values[1..-1]
42
+ end
43
+
44
+ def add_line(end_point, tangent_lock=true)
45
+ center_point = ControlPoint[*(@end_point.loc + (Vector[*end_point] - @end_point.loc) * 0.5)]
46
+ new_line = Line.new @end_point, center_point, ControlPoint[*end_point]
47
+ LineUpConstraint.new center_point, new_line[:end], @end_point
48
+
49
+ if tangent_lock and @path_elements.last
50
+ last_element = @path_elements.last
51
+
52
+ case last_element
53
+ when CubicBezier
54
+ LineUpConstraint.new @end_point, last_element[:end_handle], new_line[:end], morror_distance=false, follow=:p1
55
+ when Line
56
+ @end_point.move_to end_point
57
+ return
58
+ end
59
+ end
60
+
61
+ @end_point = new_line[:end]
62
+ @path_elements << new_line
63
+
64
+ @path_points = to_a
65
+ @control_points += new_line.control_points.values[1..-1]
66
+ end
67
+
68
+ def each
69
+ yield @start_point.loc
70
+ @path_elements.each do |path_element|
71
+ (1..path_element.path_points.size-1).each do |index|
72
+ yield path_element.path_points[index]
73
+ end
74
+ end
75
+ end
76
+
77
+ def size
78
+ return @path_points.size
79
+ end
80
+
81
+ def get_guides
82
+ @path_elements.inject([]) do |mem, element|
83
+ mem += element.get_guides
84
+ end
85
+ end
86
+ end
87
+ end
@@ -0,0 +1,26 @@
1
+ class Integer
2
+ def choose(k)
3
+ # binominal coefficient
4
+ return self.factorial / ((self -k).factorial * k.factorial)
5
+ end
6
+
7
+ def factorial()
8
+ # n!
9
+ return (1..self).inject(1, &:*)
10
+ end
11
+ end
12
+
13
+ def bernstein_basis(i, n, t)
14
+ # http://mathworld.wolfram.com/BernsteinPolynomial.html
15
+ return n.choose(i) * t ** i * (1 - t) ** (n - i)
16
+ end
17
+
18
+ class Vector
19
+ def unit
20
+ return self / self.r
21
+ end
22
+
23
+ def angle(other)
24
+ return Math.acos(self.inner_product other / (self.r * other.r))
25
+ end
26
+ end
@@ -0,0 +1,19 @@
1
+ # The backend files
2
+ require 'core/control-point'
3
+ require 'core/constraint'
4
+ require 'core/path-element'
5
+ require 'core/path'
6
+ require 'core/util'
7
+
8
+ # These are the utils for integration with rubygame
9
+ begin
10
+ require 'rubygame'
11
+ unless Rubygame::Surface.public_method_defined? :draw_line
12
+ raise LoadError, 'Loading the Rubygame utils for DemCurves requires SDL_GFX to be present on the system'
13
+ end
14
+
15
+ require 'rubygame-util/control-handles'
16
+ require 'rubygame-util/gfx'
17
+ rescue LoadError => e
18
+ puts 'The Rubygame utils for DemCurves require SDL_GFX and Rubygame.'
19
+ end
@@ -0,0 +1,76 @@
1
+ require 'rubygame'
2
+ require 'matrix'
3
+
4
+ module DemCurves
5
+ module RubygameUtils
6
+ def self.populate_handles(ctl_points, drag_group)
7
+ ctl_points.each do |control_point|
8
+ new_handle = EditorHandle.new
9
+ new_handle.attach_to control_point
10
+ drag_group << new_handle
11
+ end
12
+ end
13
+
14
+ class DragGroup < Rubygame::Sprites::Group
15
+ dragged_object = nil
16
+
17
+ def on_press(evt)
18
+ self.each do |sprite|
19
+ if sprite.rect.collide_point? *evt.pos
20
+ @dragged_object = sprite
21
+ break
22
+ end
23
+ end
24
+ end
25
+
26
+ def on_release(evt)
27
+ @dragged_object = nil
28
+ end
29
+
30
+ def on_move(evt)
31
+ if @dragged_object
32
+ @dragged_object.move evt.rel
33
+ end
34
+ end
35
+ end
36
+
37
+
38
+ class EditorHandle
39
+ include Rubygame::Sprites::Sprite
40
+
41
+ def initialize(loc=[50, 50], size=10)
42
+ @groups =[]
43
+ @depth = 0
44
+
45
+ @rect = Rubygame::Rect.new 0, 0, size, size
46
+ @rect.c = loc
47
+
48
+ @image = Rubygame::Surface.new [size, size], 0, [Rubygame::HWSURFACE, Rubygame::SRCALPHA]
49
+ @image.fill([180, 180, 180])
50
+ @attached = false
51
+
52
+ @constraints = []
53
+ end
54
+
55
+ def attach_to(control_point)
56
+ unless @attached
57
+ @attached = true
58
+ @control_point = control_point
59
+ @rect.c = control_point.loc.to_a
60
+ end
61
+ end
62
+
63
+ def move(rel)
64
+ if @attached
65
+ @control_point.shift rel
66
+ end
67
+ end
68
+
69
+ def update
70
+ if @attached
71
+ @rect.c = @control_point.loc.to_a
72
+ end
73
+ end
74
+ end
75
+ end
76
+ end
@@ -0,0 +1,15 @@
1
+ require 'rubygame'
2
+
3
+ class Rubygame::Surface
4
+ def draw_path(path, color)
5
+ (0..path.size - 2).each do |index|
6
+ _draw_line path.to_a[index], path.to_a[index + 1], color, false
7
+ end
8
+ end
9
+
10
+ def draw_path_a(path, color)
11
+ (0..path.size - 2).each do |index|
12
+ _draw_line path.to_a[index], path.to_a[index + 1], color, true
13
+ end
14
+ end
15
+ end
metadata ADDED
@@ -0,0 +1,52 @@
1
+ --- !ruby/object:Gem::Specification
2
+ name: dem-curves
3
+ version: !ruby/object:Gem::Version
4
+ version: 0.0.1
5
+ platform: ruby
6
+ authors:
7
+ - Huba Nagy
8
+ autorequire:
9
+ bindir: bin
10
+ cert_chain: []
11
+ date: 2015-02-14 00:00:00.000000000 Z
12
+ dependencies: []
13
+ description:
14
+ email: 12huba@gmail.com
15
+ executables: []
16
+ extensions: []
17
+ extra_rdoc_files: []
18
+ files:
19
+ - lib/core/constraint.rb
20
+ - lib/core/control-point.rb
21
+ - lib/core/path-element.rb
22
+ - lib/core/path.rb
23
+ - lib/core/util.rb
24
+ - lib/dem-curves.rb
25
+ - lib/rubygame-util/control-handles.rb
26
+ - lib/rubygame-util/gfx.rb
27
+ homepage: https://github.com/huba/DemCurves
28
+ licenses:
29
+ - MIT
30
+ metadata: {}
31
+ post_install_message:
32
+ rdoc_options: []
33
+ require_paths:
34
+ - lib
35
+ required_ruby_version: !ruby/object:Gem::Requirement
36
+ requirements:
37
+ - - ">="
38
+ - !ruby/object:Gem::Version
39
+ version: '0'
40
+ required_rubygems_version: !ruby/object:Gem::Requirement
41
+ requirements:
42
+ - - ">="
43
+ - !ruby/object:Gem::Version
44
+ version: '0'
45
+ requirements: []
46
+ rubyforge_project:
47
+ rubygems_version: 2.4.5
48
+ signing_key:
49
+ specification_version: 4
50
+ summary: A library for generating bezier curve based paths from control_points. It
51
+ can be used with Rubygame
52
+ test_files: []