dem-curves 0.0.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -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: []