ivan 0.1.0.pre

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: dcd063d2fffdb9889308d5c7e7fb2a43ccbc4e14
4
+ data.tar.gz: 06dafb619d91ba6f672a7dc9f339dbdc913a76eb
5
+ SHA512:
6
+ metadata.gz: da3adf2ac9a6f67a2a4d64b03be438765e1ce98b2a2d60489f2d8fa13bd7a83fbd3dcc3636c1cdaf0ee6f4b5d9604e47c445081c4ad5e84269d386c2efe2040f
7
+ data.tar.gz: 6ba4cf550c4125084211595819d5704f2a8e93af7323a02a7c652dd988700cddebebf8ed9b6efa28db1b20e600f23804aa1a06d6533020a103ac571a74059497
@@ -0,0 +1,29 @@
1
+ class Composition
2
+ attr_reader :buffer
3
+
4
+ def initialize(buffer)
5
+ @buffer = buffer
6
+ end
7
+
8
+ def clip_to_boundary(boundary)
9
+ @buffer.each_slice(2).with_index do |slice, i|
10
+ index = i * 2
11
+ clipped_points = Point.clip_to_boundary(boundary, slice)
12
+ @buffer[index] = clipped_points[0]
13
+ @buffer[index+1] = clipped_points[1]
14
+ end
15
+ @buffer.reject! {|p| !p }
16
+ return self
17
+ end
18
+
19
+ def normalize(boundary)
20
+ scale = 1.0
21
+ trans = 127
22
+ @buffer = @buffer.map do |point|
23
+ point \
24
+ .scale([scale, scale, 0])
25
+ .translate([trans, trans, 0])
26
+ end
27
+ return self.clip_to_boundary(boundary)
28
+ end
29
+ end
@@ -0,0 +1,16 @@
1
+ class LineStructError < Exception
2
+ end
3
+
4
+ class LineIndexRangeError < Exception
5
+ end
6
+
7
+ Struct.new("Geometry", :points, :lines) do
8
+ def valid?
9
+ lines.each do |l|
10
+ raise LineStructError, "This line is not a 2-tuple" \
11
+ if l.length != 2
12
+ raise LineIndexRangeError, "Line vertex with invalid point index" \
13
+ if not points[l[0]] or not points[l[1]]
14
+ end
15
+ end
16
+ end
@@ -0,0 +1,47 @@
1
+ class Glyph
2
+ extend HasTransforms
3
+ attr_reader :children
4
+
5
+ has_transforms_for Point
6
+
7
+ def self.new_from_model(model)
8
+ return new(Ivan::Models[model])
9
+ end
10
+
11
+ def initialize(geometry)
12
+ if geometry.valid? then
13
+ @points = geometry[:points].map do |p|
14
+ Point.new(*p)
15
+ end
16
+ @lines = geometry[:lines]
17
+ @children = []
18
+ end
19
+ end
20
+
21
+ def << (child_glyph)
22
+ @children << child_glyph
23
+ end
24
+
25
+ def project(x = 0, y = 0, z = Ivan.default_focal_length)
26
+ @points = @points.map do |p|
27
+ p.project(x, y, z)
28
+ end
29
+ @children = @children.map do |c|
30
+ c.send(:project, x, y, z)
31
+ end
32
+ return self
33
+ end
34
+
35
+ def to_buffer
36
+ my_instructions = []
37
+ @lines.each do |l|
38
+ l.each do |p|
39
+ my_instructions << @points[p]
40
+ end
41
+ end
42
+ self.children.each do |c|
43
+ my_instructions << c.to_buffer
44
+ end
45
+ return my_instructions.flatten
46
+ end
47
+ end
@@ -0,0 +1,25 @@
1
+ module HasTransforms
2
+ private
3
+ def delegate_transforms(transforms)
4
+ transforms.each do |method_name|
5
+ define_method(method_name) do |param|
6
+ @points = @points.map do |p|
7
+ p.send(method_name, param)
8
+ end
9
+ @children = @children.map do |c|
10
+ c.send(method_name, param)
11
+ end
12
+ return self
13
+ end
14
+ end
15
+ end
16
+
17
+ def has_transforms_for(*class_names)
18
+ transform_methods = []
19
+ class_names.each do |c|
20
+ transform_methods += c.transforms
21
+ end
22
+ transform_methods.uniq!
23
+ delegate_transforms(transform_methods)
24
+ end
25
+ end
@@ -0,0 +1,61 @@
1
+ require 'yaml'
2
+
3
+ require_relative 'point'
4
+ require_relative 'has_transforms'
5
+ require_relative 'geometry'
6
+ require_relative 'glyph'
7
+ require_relative 'composition'
8
+ require_relative 'sender'
9
+ require_relative 'teensyv_sender'
10
+
11
+ module Ivan
12
+ @model_path = File.join(File.dirname(File.expand_path(__FILE__)), 'models')
13
+ @default_focal_length = -125.0
14
+ Models = {}
15
+
16
+ def self.models
17
+ return Models
18
+ end
19
+
20
+ def self.set_focal_length(val)
21
+ @default_focal_length = val
22
+ end
23
+
24
+ def self.set_model_path(path)
25
+ @model_path = path
26
+ end
27
+
28
+ def self.default_focal_length
29
+ return @default_focal_length
30
+ end
31
+
32
+ def self.load_models(*model_names)
33
+ model_names.each do |model_name|
34
+ model_file = File.read("#{ @model_path }/#{ model_name }.yml")
35
+ if model_file then
36
+ geom = YAML.load(model_file)
37
+ if geom.valid? then
38
+ Models[model_name] = geom
39
+ end
40
+ else
41
+ return false
42
+ end
43
+ end
44
+ return true
45
+ end
46
+
47
+ def self.copy_model(source, destination)
48
+ if Models[source] then
49
+ Models[destination] = Models[source]
50
+ end
51
+ end
52
+
53
+ def self.save_model(model_name)
54
+ yaml_out = YAML.dump(Models[model_name])
55
+ if yaml_out then
56
+ file_out = File.open("#{ @model_path }/#{model_name}.yml","w")
57
+ file_out << yaml_out
58
+ file_out.close
59
+ end
60
+ end
61
+ end
@@ -0,0 +1,51 @@
1
+ --- !ruby/struct:Struct::Geometry
2
+ points:
3
+ - - -1
4
+ - -1
5
+ - 1
6
+ - - 1
7
+ - -1
8
+ - 1
9
+ - - 1
10
+ - 1
11
+ - 1
12
+ - - -1
13
+ - 1
14
+ - 1
15
+ - - -1
16
+ - 1
17
+ - -1
18
+ - - 1
19
+ - 1
20
+ - -1
21
+ - - -1
22
+ - -1
23
+ - -1
24
+ - - 1
25
+ - -1
26
+ - -1
27
+ lines:
28
+ - - 0
29
+ - 1
30
+ - - 1
31
+ - 2
32
+ - - 2
33
+ - 3
34
+ - - 3
35
+ - 0
36
+ - - 3
37
+ - 4
38
+ - - 2
39
+ - 5
40
+ - - 1
41
+ - 7
42
+ - - 4
43
+ - 6
44
+ - - 4
45
+ - 5
46
+ - - 0
47
+ - 6
48
+ - - 6
49
+ - 7
50
+ - - 7
51
+ - 5
@@ -0,0 +1,23 @@
1
+ --- !ruby/struct:Struct::Geometry
2
+ points:
3
+ - - -1
4
+ - -1
5
+ - 0
6
+ - - 1
7
+ - -1
8
+ - 0
9
+ - - 1
10
+ - 1
11
+ - 0
12
+ - - -1
13
+ - 1
14
+ - 0
15
+ lines:
16
+ - - 0
17
+ - 1
18
+ - - 1
19
+ - 2
20
+ - - 2
21
+ - 3
22
+ - - 3
23
+ - 0
@@ -0,0 +1,27 @@
1
+ --- !ruby/struct:Struct::Geometry
2
+ points:
3
+ - - 1
4
+ - 1
5
+ - 1
6
+ - - -1
7
+ - -1
8
+ - 1
9
+ - - -1
10
+ - 1
11
+ - -1
12
+ - - 1
13
+ - -1
14
+ - -1
15
+ lines:
16
+ - - 0
17
+ - 1
18
+ - - 0
19
+ - 2
20
+ - - 0
21
+ - 3
22
+ - - 1
23
+ - 2
24
+ - - 1
25
+ - 3
26
+ - - 2
27
+ - 3
@@ -0,0 +1,153 @@
1
+ class Point
2
+ include Math
3
+ attr_reader :x, :y, :z
4
+
5
+ INSIDE = 0; # 0000
6
+ LEFT = 1; # 0001
7
+ RIGHT = 2; # 0010
8
+ BOTTOM = 4; # 0100
9
+ TOP = 8; # 1000
10
+
11
+ def initialize (x = 0, y = 0, z = 0)
12
+ @x = x;
13
+ @y = y;
14
+ @z = z;
15
+ end
16
+
17
+ def screen_safe?(boundary)
18
+ return ( @x >= boundary[:x_min] and \
19
+ @x <= boundary[:x_max] and \
20
+ @y >= boundary[:y_min] and \
21
+ @y <= boundary[:y_max] and \
22
+ @z == 0 )
23
+ end
24
+
25
+ def self.interpolate(point1, point2, weight)
26
+ return Point.new(
27
+ point1.x + (point2.x - point1.x) * weight,
28
+ point1.y + (point2.y - point1.y) * weight,
29
+ point1.z + (point2.z - point1.z) * weight,
30
+ )
31
+ end
32
+
33
+ def outcode(boundary)
34
+ outcode = 0
35
+
36
+ if self.x < boundary[:x_min] then
37
+ outcode |= LEFT
38
+ end
39
+ if self.x > boundary[:x_max] then
40
+ outcode |= RIGHT
41
+ end
42
+ if self.y < boundary[:y_min] then
43
+ outcode |= BOTTOM
44
+ end
45
+ if self.y > boundary[:y_max] then
46
+ outcode |= TOP
47
+ end
48
+
49
+ return outcode
50
+ end
51
+
52
+ def self.clip_to_boundary(boundary, points)
53
+ accepted = false
54
+
55
+ while !accepted do
56
+ outcode0 = points[0].outcode(boundary)
57
+ outcode1 = points[1].outcode(boundary)
58
+ if ( (outcode0 | outcode1) == 0 ) then
59
+ return points
60
+ elsif (outcode0 & outcode1 != 0) then
61
+ return [nil, nil]
62
+ else
63
+ if (outcode0 != 0) then
64
+ outcode_out = outcode0
65
+ else
66
+ outcode_out = outcode1
67
+ end
68
+ x0 = points[0].x
69
+ y0 = points[0].y
70
+ x1 = points[1].x
71
+ y1 = points[1].y
72
+ if (outcode_out & TOP != 0) then
73
+ x = x0 + (x1 - x0) * (boundary[:y_max] - y0) / (y1 - y0)
74
+ y = boundary[:y_max]
75
+ elsif (outcode_out & BOTTOM != 0) then
76
+ x = x0 + (x1 - x0) * (boundary[:y_min] - y0) / (y1 - y0)
77
+ y = boundary[:y_min]
78
+ elsif (outcode_out & RIGHT != 0) then
79
+ y = y0 + (y1 - y0) * (boundary[:x_max] - x0) / (x1 - x0)
80
+ x = boundary[:x_max]
81
+ elsif (outcode_out & LEFT != 0) then
82
+ y = y0 + (y1 - y0) * (boundary[:x_min] - x0) / (x1 - x0)
83
+ x = boundary[:x_min]
84
+ end
85
+
86
+ if (outcode_out == outcode0) then
87
+ x0 = x
88
+ y0 = y
89
+ else
90
+ x1 = x
91
+ y1 = y
92
+ end
93
+ points[0] = Point.new(x0,y0)
94
+ points[1] = Point.new(x1,y1)
95
+ end
96
+
97
+ end
98
+
99
+ return points
100
+ end
101
+
102
+ def self.transforms
103
+ return [:translate,
104
+ :scale,
105
+ :rotate_x,
106
+ :rotate_y,
107
+ :rotate_z ]
108
+ end
109
+
110
+ def translate(delta)
111
+ return Point.new(
112
+ @x + delta[0],
113
+ @y + delta[1],
114
+ @z + delta[2] )
115
+ end
116
+
117
+ def scale(delta)
118
+ return Point.new(
119
+ @x * delta[0],
120
+ @y * delta[1],
121
+ @z * delta[2] )
122
+ end
123
+
124
+ def rotate_x(theta)
125
+ return Point.new(
126
+ @x,
127
+ @y * cos(theta) - @z * sin(theta),
128
+ @y * sin(theta) + @z * cos(theta) )
129
+ end
130
+
131
+ def rotate_y(theta)
132
+ return Point.new(
133
+ @z * sin(theta) + @x * cos(theta),
134
+ @y,
135
+ @z * cos(theta) - @x * sin(theta) )
136
+ end
137
+
138
+ def rotate_z(theta)
139
+ return Point.new(
140
+ @x * cos(theta) - @y * sin(theta),
141
+ @x * sin(theta) + @y * cos(theta),
142
+ @z )
143
+ end
144
+
145
+ def project(x = 0, y = 0, z = Ivan.default_focal_length)
146
+ pov = Point.new(x, y, z)
147
+ return Point.new(
148
+ pov.z * (@x - pov.x) / (@z + pov.z) + pov.x,
149
+ pov.z * (@y - pov.y) / (@z + pov.z) + pov.y,
150
+ 0 )
151
+ end
152
+
153
+ end
@@ -0,0 +1,64 @@
1
+ class OutputInitError < Exception
2
+ end
3
+
4
+ class UnsafeOutputError < Exception
5
+ end
6
+
7
+ # Sender is an abstract class, and is not meant to be instantiated.
8
+ # Subclass it for the requirements of your application's display hardware.
9
+
10
+ class Sender
11
+ @output = nil
12
+ @boundary = nil
13
+
14
+ attr_reader :boundary
15
+
16
+ def initialize(config_params)
17
+ begin
18
+ post_initialize(config_params)
19
+ rescue
20
+ raise OutputInitError, "Output device couldn't be initialized"
21
+ end
22
+ end
23
+
24
+ def send(buffer)
25
+ check_safe(buffer)
26
+ pre_send(buffer)
27
+ buffer.each_slice(2) do |slice|
28
+ send_line(slice)
29
+ end
30
+ end
31
+
32
+ private
33
+
34
+ def check_safe(buffer)
35
+ buffer.each do |i|
36
+ raise UnsafeOutputError, "Found point #{i.inspect} outside output boundary #{@boundary.inspect}" \
37
+ if (!i.screen_safe?(@boundary))
38
+ end
39
+ end
40
+
41
+ def send_line(line)
42
+ [line[0].x, line[0].y, line[1].x, line[1].y].each do |coord|
43
+ @output.send(output_message, coordinate_format(coord) )
44
+ end
45
+ end
46
+
47
+ # Override these methods in concrete subclasses
48
+
49
+ def post_initialize(config_params)
50
+ raise NotImplementedError
51
+ end
52
+
53
+ def pre_send(buffer)
54
+ raise NotImplementedError
55
+ end
56
+
57
+ def output_message
58
+ raise NotImplementedError
59
+ end
60
+
61
+ def coordinate_format(value)
62
+ raise NotImplementedError
63
+ end
64
+ end
@@ -0,0 +1,41 @@
1
+ require 'serialport'
2
+
3
+ class TeensyVSender < Sender
4
+
5
+ private
6
+
7
+ def post_initialize(config_params)
8
+ @output = config_params.fetch(:output_class, SerialPort).new(
9
+ config_params[:port],
10
+ config_params.fetch(:baud, 9600),
11
+ config_params.fetch(:data_bits, 8),
12
+ config_params.fetch(:stop_bits, 1)
13
+ )
14
+ @boundary = config_params.fetch(:boundary, {
15
+ x_min: 0,
16
+ y_min: 0,
17
+ x_max: 255,
18
+ y_max: 255
19
+ })
20
+ end
21
+
22
+ def pre_send(buffer)
23
+ transmit_instruction_length(buffer.length / 2)
24
+ end
25
+
26
+ def output_message
27
+ return :write
28
+ end
29
+
30
+ def coordinate_format(value)
31
+ return value.to_i.chr
32
+ end
33
+
34
+ def transmit_instruction_length(length)
35
+ high_byte = (length >> 8) & 0xff
36
+ low_byte = length & 0xff
37
+ @output.write(high_byte.to_i.chr)
38
+ @output.write(low_byte.to_i.chr)
39
+ return length
40
+ end
41
+ end
metadata ADDED
@@ -0,0 +1,194 @@
1
+ --- !ruby/object:Gem::Specification
2
+ name: ivan
3
+ version: !ruby/object:Gem::Version
4
+ version: 0.1.0.pre
5
+ platform: ruby
6
+ authors:
7
+ - Duncan Malashock
8
+ autorequire:
9
+ bindir: bin
10
+ cert_chain: []
11
+ date: 2015-07-11 00:00:00.000000000 Z
12
+ dependencies:
13
+ - !ruby/object:Gem::Dependency
14
+ name: serialport
15
+ requirement: !ruby/object:Gem::Requirement
16
+ requirements:
17
+ - - "~>"
18
+ - !ruby/object:Gem::Version
19
+ version: '1.3'
20
+ type: :runtime
21
+ prerelease: false
22
+ version_requirements: !ruby/object:Gem::Requirement
23
+ requirements:
24
+ - - "~>"
25
+ - !ruby/object:Gem::Version
26
+ version: '1.3'
27
+ - !ruby/object:Gem::Dependency
28
+ name: bundler
29
+ requirement: !ruby/object:Gem::Requirement
30
+ requirements:
31
+ - - "~>"
32
+ - !ruby/object:Gem::Version
33
+ version: '0'
34
+ type: :development
35
+ prerelease: false
36
+ version_requirements: !ruby/object:Gem::Requirement
37
+ requirements:
38
+ - - "~>"
39
+ - !ruby/object:Gem::Version
40
+ version: '0'
41
+ - !ruby/object:Gem::Dependency
42
+ name: minitest
43
+ requirement: !ruby/object:Gem::Requirement
44
+ requirements:
45
+ - - "~>"
46
+ - !ruby/object:Gem::Version
47
+ version: '5.7'
48
+ type: :development
49
+ prerelease: false
50
+ version_requirements: !ruby/object:Gem::Requirement
51
+ requirements:
52
+ - - "~>"
53
+ - !ruby/object:Gem::Version
54
+ version: '5.7'
55
+ - !ruby/object:Gem::Dependency
56
+ name: guard
57
+ requirement: !ruby/object:Gem::Requirement
58
+ requirements:
59
+ - - "~>"
60
+ - !ruby/object:Gem::Version
61
+ version: '2.12'
62
+ type: :development
63
+ prerelease: false
64
+ version_requirements: !ruby/object:Gem::Requirement
65
+ requirements:
66
+ - - "~>"
67
+ - !ruby/object:Gem::Version
68
+ version: '2.12'
69
+ - !ruby/object:Gem::Dependency
70
+ name: guard-minitest
71
+ requirement: !ruby/object:Gem::Requirement
72
+ requirements:
73
+ - - "~>"
74
+ - !ruby/object:Gem::Version
75
+ version: '2.4'
76
+ type: :development
77
+ prerelease: false
78
+ version_requirements: !ruby/object:Gem::Requirement
79
+ requirements:
80
+ - - "~>"
81
+ - !ruby/object:Gem::Version
82
+ version: '2.4'
83
+ - !ruby/object:Gem::Dependency
84
+ name: rake
85
+ requirement: !ruby/object:Gem::Requirement
86
+ requirements:
87
+ - - "~>"
88
+ - !ruby/object:Gem::Version
89
+ version: '10.4'
90
+ type: :development
91
+ prerelease: false
92
+ version_requirements: !ruby/object:Gem::Requirement
93
+ requirements:
94
+ - - "~>"
95
+ - !ruby/object:Gem::Version
96
+ version: '10.4'
97
+ - !ruby/object:Gem::Dependency
98
+ name: pry
99
+ requirement: !ruby/object:Gem::Requirement
100
+ requirements:
101
+ - - "~>"
102
+ - !ruby/object:Gem::Version
103
+ version: '0.10'
104
+ type: :development
105
+ prerelease: false
106
+ version_requirements: !ruby/object:Gem::Requirement
107
+ requirements:
108
+ - - "~>"
109
+ - !ruby/object:Gem::Version
110
+ version: '0.10'
111
+ - !ruby/object:Gem::Dependency
112
+ name: pry-byebug
113
+ requirement: !ruby/object:Gem::Requirement
114
+ requirements:
115
+ - - "~>"
116
+ - !ruby/object:Gem::Version
117
+ version: '3.1'
118
+ type: :development
119
+ prerelease: false
120
+ version_requirements: !ruby/object:Gem::Requirement
121
+ requirements:
122
+ - - "~>"
123
+ - !ruby/object:Gem::Version
124
+ version: '3.1'
125
+ - !ruby/object:Gem::Dependency
126
+ name: ruby-prof
127
+ requirement: !ruby/object:Gem::Requirement
128
+ requirements:
129
+ - - "~>"
130
+ - !ruby/object:Gem::Version
131
+ version: '0.15'
132
+ type: :development
133
+ prerelease: false
134
+ version_requirements: !ruby/object:Gem::Requirement
135
+ requirements:
136
+ - - "~>"
137
+ - !ruby/object:Gem::Version
138
+ version: '0.15'
139
+ - !ruby/object:Gem::Dependency
140
+ name: codeclimate-test-reporter
141
+ requirement: !ruby/object:Gem::Requirement
142
+ requirements:
143
+ - - "~>"
144
+ - !ruby/object:Gem::Version
145
+ version: '0.4'
146
+ type: :development
147
+ prerelease: false
148
+ version_requirements: !ruby/object:Gem::Requirement
149
+ requirements:
150
+ - - "~>"
151
+ - !ruby/object:Gem::Version
152
+ version: '0.4'
153
+ description: Creates imagery on analog vector monitors
154
+ email: duncanmalashock@gmail.com
155
+ executables: []
156
+ extensions: []
157
+ extra_rdoc_files: []
158
+ files:
159
+ - lib/composition.rb
160
+ - lib/geometry.rb
161
+ - lib/glyph.rb
162
+ - lib/has_transforms.rb
163
+ - lib/ivan.rb
164
+ - lib/models/cube.yml
165
+ - lib/models/square.yml
166
+ - lib/models/tetrahedron.yml
167
+ - lib/point.rb
168
+ - lib/sender.rb
169
+ - lib/teensyv_sender.rb
170
+ homepage: http://rubygems.org/gems/ivan
171
+ licenses:
172
+ - MIT
173
+ metadata: {}
174
+ post_install_message:
175
+ rdoc_options: []
176
+ require_paths:
177
+ - lib
178
+ required_ruby_version: !ruby/object:Gem::Requirement
179
+ requirements:
180
+ - - ">="
181
+ - !ruby/object:Gem::Version
182
+ version: '0'
183
+ required_rubygems_version: !ruby/object:Gem::Requirement
184
+ requirements:
185
+ - - ">"
186
+ - !ruby/object:Gem::Version
187
+ version: 1.3.1
188
+ requirements: []
189
+ rubyforge_project:
190
+ rubygems_version: 2.4.5
191
+ signing_key:
192
+ specification_version: 4
193
+ summary: ivan
194
+ test_files: []