klear 0.0.1 → 0.1.0
Sign up to get free protection for your applications and to get access to all the features.
- data/.gitignore +1 -0
- data/Guardfile +1 -1
- data/README.md +154 -0
- data/bin/{klear.rb → klear} +6 -5
- data/klear.gemspec +5 -4
- data/lib/klear/choreography.rb +150 -0
- data/lib/klear/file.rb +89 -0
- data/lib/klear/filters.rb +19 -0
- data/lib/klear/frame.rb +71 -0
- data/lib/klear/frames.rb +43 -0
- data/lib/klear/motors.rb +53 -0
- data/lib/klear/version.rb +2 -2
- data/lib/klear.rb +3 -0
- data/spec/lib/klear/choreography_spec.rb +132 -0
- data/spec/lib/klear/file_spec.rb +46 -0
- data/spec/lib/klear/filters_spec.rb +15 -0
- data/spec/lib/klear/frame_spec.rb +19 -0
- data/spec/lib/klear/frames_spec.rb +50 -0
- data/spec/lib/klear/motors_spec.rb +79 -0
- data/spec/lib/klear/version_spec.rb +6 -0
- metadata +1764 -617
- data/Gemfile.lock +0 -44
data/.gitignore
CHANGED
data/Guardfile
CHANGED
@@ -1,6 +1,6 @@
|
|
1
1
|
# Guardfile info at: https://github.com/guard/guard#readme
|
2
2
|
|
3
|
-
guard 'rspec', all_on_start: true, cli: '--format nested --color' do
|
3
|
+
guard 'rspec', all_on_start: true, cli: '--format nested --debug --color' do
|
4
4
|
watch(%r{^spec/.+_spec\.rb$})
|
5
5
|
watch(%r{^lib/(.+)\.rb$}) { |m| "spec/lib/#{m[1]}_spec.rb" }
|
6
6
|
watch('spec/spec_helper.rb') { "spec" }
|
data/README.md
ADDED
@@ -0,0 +1,154 @@
|
|
1
|
+
# Kinetic Light Engine Archice - klear
|
2
|
+
|
3
|
+
Create and manage choreographies for motors and lights on the Manta Rhei. This
|
4
|
+
document describes the Kinetic Light Engine File Format which contains
|
5
|
+
Choreographies and all data needed for playing it.
|
6
|
+
|
7
|
+
To understand what this is all about please check the Manta Rhei project page:
|
8
|
+
|
9
|
+
- http://www.artcom.de/en/projects/project/detail/manta-rhei/
|
10
|
+
|
11
|
+
This gem now is the starting point for refactoring the Manta Rhei code. The klear repo will contain all code for handling the actual content. Running the stepper motors or driving the OLEDs or DMX is part of the kinetic-light-engine which goes online soon.
|
12
|
+
|
13
|
+
## File info
|
14
|
+
|
15
|
+
$ klear info choreo.kle
|
16
|
+
|
17
|
+
will show what is in the file.
|
18
|
+
|
19
|
+
## New File Generation
|
20
|
+
|
21
|
+
*Notice: file generation depends on jruby because of its usage of Java JAI*
|
22
|
+
|
23
|
+
Klear files are zipped directory structures which are generated from a set of images. The pixel values directly map to motor position and light intensity. On top of that, the klear file contains some additional meta info and cache date to speed up its loading at runtime. Generating a klear file from a images sequence in a directory goes like:
|
24
|
+
|
25
|
+
$ rvm jruby exec ./bin/klear.rb generate image_sequence_dir outfile.kle
|
26
|
+
|
27
|
+
and of course, more documentation needs to come (means must be converted from the internal wiki to here).
|
28
|
+
|
29
|
+
## FileFormat
|
30
|
+
|
31
|
+
File of this format have the file extension "kle", e.g. "calm_05.kle"
|
32
|
+
|
33
|
+
### Layout and container Structure
|
34
|
+
|
35
|
+
A .kle File contains multiple assets, incl. source PNGs, metatdata and a derived binary file containing frames in packed binary form. The Container Format is ZIP so a .kle file is just a zip-file containing files and folders.
|
36
|
+
|
37
|
+
*Directory Layout*
|
38
|
+
|
39
|
+
* Directory 'META-INF' (mandatory)
|
40
|
+
* 'kle.yml' containing meta-data describing aspects like framerate & more
|
41
|
+
* 'MANIFEST.MF' containing meta-data about the kle file itself (e.g. format version)
|
42
|
+
* Directory 'frames' (mandatory)
|
43
|
+
* Source PNGs which were used to generate the kle
|
44
|
+
* Directory 'cache' (optional)
|
45
|
+
* File 'frames.bin' containing binary data derived from the PNGs during creation
|
46
|
+
* further pre-processed data or application state for optimized restarts might be stored here.
|
47
|
+
* Directory 'icon'
|
48
|
+
* contains one file 'normal.png' for a normal sized icon (150 x 110 px)
|
49
|
+
|
50
|
+
### Workflow
|
51
|
+
|
52
|
+
The initial source of a kle is a sequence of PNGs + metadata. Those are used to generate a .kle file incl. the file 'frames.bin'. The metadata is stored in 'META-INF/kle.yml' and describes aspects like fps.
|
53
|
+
|
54
|
+
If a kle file does not have frame.bin in its cache directory it can be regenerated. This is also useful for future format changes together with the manifest to detect if a frames.bin is deprecated and needs to be regenerated.
|
55
|
+
|
56
|
+
The source sequence of PNGs is stored in the KLE-file as well, which allows features like frames.bin regeneration in the first place.
|
57
|
+
|
58
|
+
### File Format Details
|
59
|
+
|
60
|
+
#### PNGs
|
61
|
+
|
62
|
+
Each single PNG represents exactly one frame and is stored with 16-bit / channel. The size of the PNGs is determined by the number of columns and rows, where each tile is 10px x 10px in size. A Column represents the state of a blade at a frame (a certain point in time). The number of columns represents the number of blades. A Row represents one aspect across all blades, e.g. an outermost light or the state of the motor.
|
63
|
+
|
64
|
+
*Example*
|
65
|
+
|
66
|
+
!Waves_00129.png!
|
67
|
+
|
68
|
+
* We have 11 rows and 14 columns (blades)
|
69
|
+
* The lowest row describes the motor state
|
70
|
+
* The other rows describe the state of the lights from one direction to the other (TODO: Define the direction - what is a point of reference?)
|
71
|
+
* The Png then is 140x110px in size.
|
72
|
+
|
73
|
+
#### Sequence of PNGs
|
74
|
+
|
75
|
+
The order of the sequence is defined by the sorting delivered by the Posix command `sort -n`. So any natural alphabetical naming to order the sequence is allowed.
|
76
|
+
|
77
|
+
The number of columns and rows must be the same for all PNGs.
|
78
|
+
|
79
|
+
*Examples*
|
80
|
+
|
81
|
+
* `A.png, B.png, X.png` is valid sequence of 3 PNGs
|
82
|
+
* `Test_0001.png, Test_0002.png, Test__1000.png` is a valid sequence
|
83
|
+
|
84
|
+
A sequence does not need to be consecutive (it can have gaps e.g. `01.png,10.png` is valid and the existence of e.g. 05.png is not enforced).
|
85
|
+
|
86
|
+
#### kle.yml
|
87
|
+
|
88
|
+
Contains information about how to use the frames:
|
89
|
+
|
90
|
+
* Number of columns and rows (geometry)
|
91
|
+
* Frames per second
|
92
|
+
* recommended gamma value
|
93
|
+
* Potentially a free descriptor (name)
|
94
|
+
|
95
|
+
The geometry is determined automatically by reading the first png in the png sequence and dividing width and height by 10 respectively. This relies on one tile in the png being 10x10px in size.
|
96
|
+
|
97
|
+
*Example:*
|
98
|
+
<pre>
|
99
|
+
---
|
100
|
+
description: calm_02
|
101
|
+
geometry:
|
102
|
+
rows: 11
|
103
|
+
columns: 14
|
104
|
+
fps: 25
|
105
|
+
</pre>
|
106
|
+
|
107
|
+
#### MANIFEST.MF
|
108
|
+
|
109
|
+
The manifest contains meta information about the file and file format itself:
|
110
|
+
|
111
|
+
* `Manifest-Version` Version of the manifest itself
|
112
|
+
* `Kle-Version` Version of the kle-file format (e.g. `1.0`)
|
113
|
+
* `Created-By` Tool which created this file.
|
114
|
+
|
115
|
+
*Example:*
|
116
|
+
|
117
|
+
<pre>
|
118
|
+
Manifest-Version: 1.0
|
119
|
+
|
120
|
+
Kle-Version: 1.0
|
121
|
+
Created-By: ruby-kle-generator 0.344b
|
122
|
+
</pre>
|
123
|
+
|
124
|
+
#### frames.bin
|
125
|
+
|
126
|
+
The frames.bin file contains the extracted 16-bit values sampled from each tile of the PNGs. It contains all frames and each frame contains all columns and rows.
|
127
|
+
|
128
|
+
Each 16-bit value is saved as unsigned 16 bit integer big endian (network byte order) and uses 2 bytes in frames.bin.
|
129
|
+
|
130
|
+
A PNG with 11 rows and 14 columns uses `14 x 11 x 2 bytes = 308 bytes`.
|
131
|
+
|
132
|
+
Each frame is encoded rows bottom to top and the columns from left to right.
|
133
|
+
|
134
|
+
Example of a PNG with 3 rows & cols:
|
135
|
+
|
136
|
+
col 1 col 2 col 3
|
137
|
+
|-------|-------|-------|
|
138
|
+
| 27009 | 38885 | 47331 | <- row 3
|
139
|
+
|-------|-------|-------|
|
140
|
+
| 51027 | 51233 | 49789 | <- row 2
|
141
|
+
|-------|-------|-------|
|
142
|
+
| 47645 | 45039 | 41857 | <- row 1
|
143
|
+
|-------|-------|-------|
|
144
|
+
|
145
|
+
is written as a sequence of values like:
|
146
|
+
|
147
|
+
47645 45039 41857 51027 51233 49789 27009 38885 47331
|
148
|
+
|
149
|
+
|
150
|
+
which results in a binary big endian (network) byte order sequence like:
|
151
|
+
|
152
|
+
0xBA1D 0xAFEF 0xA381 0xC753 0xC821 0xC27D 0x6981 0x97E5 0xB8E3
|
153
|
+
|
154
|
+
As a note: row 1 is the motor state.
|
data/bin/{klear.rb → klear}
RENAMED
@@ -50,11 +50,12 @@ Applix.main(ARGV, Defaults) do
|
|
50
50
|
@app.regenerate(*args)
|
51
51
|
end
|
52
52
|
|
53
|
-
|
54
|
-
|
55
|
-
|
56
|
-
|
57
|
-
|
53
|
+
handle(:info) do |*args, opts|
|
54
|
+
(0 < args.size) or usage
|
55
|
+
path = args.shift
|
56
|
+
choreo = Klear::Choreography.load(path)
|
57
|
+
puts choreo.info
|
58
|
+
end
|
58
59
|
|
59
60
|
handle(:any) {|_, _| usage}
|
60
61
|
end
|
data/klear.gemspec
CHANGED
@@ -9,10 +9,11 @@ Gem::Specification.new do |s|
|
|
9
9
|
s.authors = ['art+com/dirk luesebrink']
|
10
10
|
s.email = ['dirk.luesebrink@artcom.de']
|
11
11
|
s.homepage = 'http://www.artcom.de/en/projects/project/detail/manta-rhei/'
|
12
|
-
s.summary = 'create
|
12
|
+
s.summary = 'create and manage choreographies for motors and lights on the Manta Rhei'
|
13
13
|
s.description = %q{
|
14
|
-
|
15
|
-
|
14
|
+
create and manage choreographies for motors and lights on the Manta Rhei.
|
15
|
+
klear is the kinetic-light-engine archive format and is used by the
|
16
|
+
kinetic-light-engine runtime to drive the Manta Rhei installation.
|
16
17
|
}
|
17
18
|
s.add_dependency 'applix'
|
18
19
|
s.add_dependency 'rubyzip'
|
@@ -27,7 +28,7 @@ Gem::Specification.new do |s|
|
|
27
28
|
s.add_development_dependency 'rb-fsevent', '~>0.9.1'
|
28
29
|
|
29
30
|
if RUBY_PLATFORM.match /java/i
|
30
|
-
|
31
|
+
s.add_development_dependency 'ruby-debug'
|
31
32
|
else
|
32
33
|
s.add_development_dependency 'debugger'
|
33
34
|
end
|
@@ -0,0 +1,150 @@
|
|
1
|
+
# TODO Add separate accessors for motors and lights?
|
2
|
+
# TODO Add row/blade accessors to Frames so a time-slice can be retrieved?
|
3
|
+
|
4
|
+
# Note: Row 0 is the motor setting / this is equivalent to the first value of a blade
|
5
|
+
class Klear::Choreography
|
6
|
+
|
7
|
+
#class Frames < BinData::Record
|
8
|
+
# array :numbers, :type => :uint16be, :read_until => :eof
|
9
|
+
#end
|
10
|
+
|
11
|
+
attr_reader :archive, :geometry, :gamma, :fps
|
12
|
+
attr_accessor :location
|
13
|
+
|
14
|
+
def self.load path
|
15
|
+
#choreo = Zip::ZipFile.open(path) { |klear| new klear }
|
16
|
+
self.new(Klear::File.new(path))
|
17
|
+
end
|
18
|
+
|
19
|
+
def initialize archive
|
20
|
+
@archive = archive
|
21
|
+
@location = @archive.path
|
22
|
+
#info = YAML.load(@archive.read("META-INF/kle.yml"))
|
23
|
+
info = @archive.info
|
24
|
+
@geometry = info['geometry']
|
25
|
+
@fps = info['fps'] || 25
|
26
|
+
@gamma = info['gamma'] || 1.0
|
27
|
+
@frames = @archive.frames
|
28
|
+
end
|
29
|
+
|
30
|
+
def info
|
31
|
+
puts <<-INFO
|
32
|
+
Klear file at: #{@location}
|
33
|
+
|
34
|
+
Settings:
|
35
|
+
----------------
|
36
|
+
fps : #{@fps}
|
37
|
+
gamma : #{@gamma}
|
38
|
+
geometry:
|
39
|
+
columns: #{@geometry[:columns]}
|
40
|
+
rows : #{@geometry[:rows]}
|
41
|
+
----------------
|
42
|
+
|
43
|
+
|
44
|
+
Frames:
|
45
|
+
----------------
|
46
|
+
framesize : #{framesize}
|
47
|
+
framecount: #{framecount}
|
48
|
+
----------------
|
49
|
+
|
50
|
+
|
51
|
+
MANIFEST:
|
52
|
+
----------------
|
53
|
+
#{@archive.manifest}
|
54
|
+
----------------
|
55
|
+
INFO
|
56
|
+
end
|
57
|
+
|
58
|
+
def id
|
59
|
+
File.basename(self.location, ".kle")
|
60
|
+
end
|
61
|
+
|
62
|
+
def framesize
|
63
|
+
@geometry[:columns] * @geometry[:rows]
|
64
|
+
end
|
65
|
+
|
66
|
+
def framecount
|
67
|
+
@frames.count
|
68
|
+
end
|
69
|
+
|
70
|
+
def frame no
|
71
|
+
@frames.get(no)
|
72
|
+
end
|
73
|
+
|
74
|
+
def each_frame &blk #block_given?
|
75
|
+
(0...framecount).each do |n|
|
76
|
+
myFrame = frame(n)
|
77
|
+
blk.call(myFrame, n+1)
|
78
|
+
end
|
79
|
+
end
|
80
|
+
end
|
81
|
+
|
82
|
+
__END__
|
83
|
+
class Klear::Choreography::Frame
|
84
|
+
|
85
|
+
attr_reader :data
|
86
|
+
|
87
|
+
def initialize data, geometry
|
88
|
+
@data = data
|
89
|
+
@geometry = geometry
|
90
|
+
@num_cols = @geometry[:columns] # local copy of columns to avoid per pixel symbol lookups
|
91
|
+
@num_rows = @geometry[:rows] # local copy of columns to avoid per pixel symbol lookups
|
92
|
+
end
|
93
|
+
|
94
|
+
def size
|
95
|
+
[@geometry[:columns], @geometry[:rows]]
|
96
|
+
end
|
97
|
+
|
98
|
+
def cell column, row
|
99
|
+
@data[@num_cols * row + column]
|
100
|
+
end
|
101
|
+
|
102
|
+
def row(no)
|
103
|
+
@data.slice(no * @geometry[:columns], @geometry[:columns])
|
104
|
+
end
|
105
|
+
|
106
|
+
# XXX returning an array would be better than hashed by row number...
|
107
|
+
def rows &blk
|
108
|
+
myRows = {}
|
109
|
+
(0...@geometry[:rows]).each do |n|
|
110
|
+
current = myRows[n] = row(n)
|
111
|
+
blk.call(current, n) if block_given?
|
112
|
+
end
|
113
|
+
myRows
|
114
|
+
end
|
115
|
+
|
116
|
+
def column theColumnNumber
|
117
|
+
col = []
|
118
|
+
(0...@geometry[:rows]).each { |curRow|
|
119
|
+
col << @data[theColumnNumber + curRow * @geometry[:columns]]
|
120
|
+
}
|
121
|
+
col
|
122
|
+
end
|
123
|
+
|
124
|
+
def columns &blk
|
125
|
+
myColumns = {}
|
126
|
+
(0...@geometry[:columns]).each do |n|
|
127
|
+
current = myColumns[n] = column(n)
|
128
|
+
blk.call(current, n) if block_given?
|
129
|
+
end
|
130
|
+
myColumns
|
131
|
+
end
|
132
|
+
|
133
|
+
#def each_column &blk
|
134
|
+
# (0...@geometry[:columns]).each do |n|
|
135
|
+
# myColumn = column(n)
|
136
|
+
# blk.call(myColumn, n)
|
137
|
+
# end
|
138
|
+
#end
|
139
|
+
|
140
|
+
def dump
|
141
|
+
myStr = ""
|
142
|
+
rows { |row, idx|
|
143
|
+
myStr << "#{idx} || " << row.collect{|x| x.to_s}.join(" | ") << "\n"
|
144
|
+
}
|
145
|
+
myStr
|
146
|
+
end
|
147
|
+
|
148
|
+
alias :blade :column
|
149
|
+
alias :blades :columns
|
150
|
+
end
|
data/lib/klear/file.rb
ADDED
@@ -0,0 +1,89 @@
|
|
1
|
+
require 'klear/frames'
|
2
|
+
require 'klear/motors'
|
3
|
+
|
4
|
+
class Klear::File
|
5
|
+
attr_accessor :path
|
6
|
+
|
7
|
+
def initialize path = nil
|
8
|
+
@path = path
|
9
|
+
end
|
10
|
+
|
11
|
+
def info
|
12
|
+
@info ||= YAML.load(zipfile.read('META-INF/kle.yml'))
|
13
|
+
end
|
14
|
+
|
15
|
+
def manifest
|
16
|
+
@manifest ||= YAML.load(zipfile.read('META-INF/MANIFEST.MF'))
|
17
|
+
end
|
18
|
+
|
19
|
+
def dimensions
|
20
|
+
@dimensions ||= info['geometry']
|
21
|
+
end
|
22
|
+
|
23
|
+
def frame_count
|
24
|
+
@frame_count ||= (zipfile.dir.entries("/frames").size)
|
25
|
+
end
|
26
|
+
|
27
|
+
def frames
|
28
|
+
@frames ||= (
|
29
|
+
data = zipfile.read("cache/frames.bin")
|
30
|
+
Klear::Frames.new(dimensions[:columns], dimensions[:rows], data)
|
31
|
+
)
|
32
|
+
end
|
33
|
+
|
34
|
+
def motors
|
35
|
+
@motors ||= (
|
36
|
+
#begin
|
37
|
+
# data = zipfile.read("cache/motors.bin")
|
38
|
+
# Klear::Motors.new(dimensions[:columns], data)
|
39
|
+
##rescue Errno::ENOENT => e
|
40
|
+
# puts " ~~ empty motors cache (#{e})"
|
41
|
+
# Klear::Motors.new(dimensions[:columns])
|
42
|
+
#end
|
43
|
+
|
44
|
+
motors = frames.all.map do |frame|
|
45
|
+
(frame.row 0) # convention: row zero is the motors
|
46
|
+
end
|
47
|
+
Klear::Motors.new(dimensions[:columns], motors)
|
48
|
+
|
49
|
+
#animations = {}
|
50
|
+
#frames.each do |frame, no|
|
51
|
+
# puts "frame: ##{no}"
|
52
|
+
# row = (frame.row 0) # convention: row zero is the motors
|
53
|
+
#
|
54
|
+
# # scale input onto manta geometry
|
55
|
+
# low, high = @config[:low], @config[:high]
|
56
|
+
# d = (high - low).to_f
|
57
|
+
#
|
58
|
+
# row.each_with_index do |v_in, x|
|
59
|
+
# v_out = (low + d * (v_in.value.to_f / 0xffff)).to_i
|
60
|
+
# (animations[x+1] ||= []) << v_out
|
61
|
+
# end
|
62
|
+
# end
|
63
|
+
)
|
64
|
+
end
|
65
|
+
|
66
|
+
private
|
67
|
+
def zipfile
|
68
|
+
@zipfile ||= (
|
69
|
+
@path or (raise 'path not specified')
|
70
|
+
Zip::ZipFile.new(@path)
|
71
|
+
)
|
72
|
+
end
|
73
|
+
end
|
74
|
+
|
75
|
+
__END__
|
76
|
+
|
77
|
+
def self.load theKleFile
|
78
|
+
myChoreography = nil
|
79
|
+
Zip::ZipFile.open(theKleFile) { |kle|
|
80
|
+
myChoreography = new kle
|
81
|
+
}
|
82
|
+
myChoreography
|
83
|
+
end
|
84
|
+
|
85
|
+
def initialize theKleFile
|
86
|
+
@kle_file = theKleFile
|
87
|
+
@geometry = YAML.load(@kle_file.read("META-INF/kle.yml"))['geometry']
|
88
|
+
@frames = Kleac::Choreography::Frames.read @kle_file.read("cache/frames.bin")
|
89
|
+
end
|
@@ -0,0 +1,19 @@
|
|
1
|
+
module Klear::Filters
|
2
|
+
# scale input onto manta geometry
|
3
|
+
def project values, low, high
|
4
|
+
#low, high = @config[:low], @config[:high]
|
5
|
+
d = (high - low).to_f
|
6
|
+
|
7
|
+
values.map do |val|
|
8
|
+
(low + d * (val.to_f / 0xffff)).to_i
|
9
|
+
end
|
10
|
+
end
|
11
|
+
|
12
|
+
# hack: sample(F14 by JJ) 600 down to 2 times 20 => 40 frame positions
|
13
|
+
def f14jj values
|
14
|
+
no = -1
|
15
|
+
values.select { (no += 1) % 30 == 0 }
|
16
|
+
end
|
17
|
+
|
18
|
+
extend(self)
|
19
|
+
end
|
data/lib/klear/frame.rb
ADDED
@@ -0,0 +1,71 @@
|
|
1
|
+
class Klear::Frame
|
2
|
+
|
3
|
+
attr_reader :data, :row_count, :column_count, :size
|
4
|
+
|
5
|
+
def initialize column_count, row_count, data = nil
|
6
|
+
@row_count, @column_count = row_count, column_count
|
7
|
+
@size = @row_count * @column_count
|
8
|
+
@data = data
|
9
|
+
end
|
10
|
+
|
11
|
+
def cell column, row
|
12
|
+
# TODO implement me
|
13
|
+
end
|
14
|
+
|
15
|
+
def row no
|
16
|
+
@data.slice(no * @column_count, @column_count)
|
17
|
+
end
|
18
|
+
|
19
|
+
#def rows *args, &blk
|
20
|
+
# if block_given?
|
21
|
+
# each_row(&blk)
|
22
|
+
# else
|
23
|
+
# myRows = {}
|
24
|
+
# (0...@row_count).each do |n|
|
25
|
+
# myRows[n] = row(n)
|
26
|
+
# end
|
27
|
+
# myRows
|
28
|
+
# end
|
29
|
+
#end
|
30
|
+
|
31
|
+
# returns array of rows
|
32
|
+
def each_row &blk
|
33
|
+
@rows ||= (0...@row_count).map {|n| row(n)}
|
34
|
+
|
35
|
+
if block_given?
|
36
|
+
@rows.each_with_index {|row, idx| blk.call(row, idx)}
|
37
|
+
end
|
38
|
+
|
39
|
+
@rows
|
40
|
+
end
|
41
|
+
alias_method :rows, :each_row
|
42
|
+
|
43
|
+
def column no
|
44
|
+
col = []
|
45
|
+
(0...@row_count).each do |curRow|
|
46
|
+
col << @data[no + curRow * @column_count]
|
47
|
+
end
|
48
|
+
col
|
49
|
+
end
|
50
|
+
alias_method :blade, :column
|
51
|
+
|
52
|
+
def each_column &blk
|
53
|
+
@columns ||= (0...@column_count).map {|n| column(n)}
|
54
|
+
|
55
|
+
if block_given?
|
56
|
+
@columns.each_with_index {|column, idx| blk.call(column, idx)}
|
57
|
+
end
|
58
|
+
|
59
|
+
@columns
|
60
|
+
end
|
61
|
+
alias_method :each_blade, :each_column
|
62
|
+
alias_method :columns, :each_column
|
63
|
+
|
64
|
+
#def dump
|
65
|
+
# myStr = ""
|
66
|
+
# rows do |row, idx|
|
67
|
+
# myStr << "#{idx} || " << row.collect{|x| x.to_s}.join(" | ") << "\n"
|
68
|
+
# end
|
69
|
+
# myStr
|
70
|
+
#end
|
71
|
+
end
|
data/lib/klear/frames.rb
ADDED
@@ -0,0 +1,43 @@
|
|
1
|
+
require 'klear/frame'
|
2
|
+
|
3
|
+
class Klear::Frames
|
4
|
+
class Data < BinData::Record
|
5
|
+
array :numbers, :type => :uint16be, :read_until => :eof
|
6
|
+
end
|
7
|
+
|
8
|
+
attr_reader :framesize, :rows, :columns
|
9
|
+
|
10
|
+
def initialize columns, rows, binary_string = nil
|
11
|
+
@rows, @columns = rows, columns
|
12
|
+
@framesize = @rows * @columns
|
13
|
+
@data = nil
|
14
|
+
load(binary_string) unless binary_string.nil?
|
15
|
+
end
|
16
|
+
|
17
|
+
def load binary_string
|
18
|
+
@data = Data.read(binary_string).numbers
|
19
|
+
self
|
20
|
+
end
|
21
|
+
|
22
|
+
def count
|
23
|
+
@data.size / framesize
|
24
|
+
end
|
25
|
+
|
26
|
+
def each &blk
|
27
|
+
0.upto(count-1) do |frame_no|
|
28
|
+
yield(get(frame_no), frame_no)
|
29
|
+
end
|
30
|
+
end
|
31
|
+
|
32
|
+
def get frame_no
|
33
|
+
(0 <= frame_no) or (raise "invalid negative frame no: #{frame_no}")
|
34
|
+
(frame_no < count) or (raise "frame no out of bound: #{frame_no}")
|
35
|
+
Klear::Frame.new(
|
36
|
+
@columns, @rows, @data.slice(frame_no * @framesize, @framesize)
|
37
|
+
)
|
38
|
+
end
|
39
|
+
|
40
|
+
def all
|
41
|
+
(0...count).map { |frame_no| get(frame_no) }
|
42
|
+
end
|
43
|
+
end
|
data/lib/klear/motors.rb
ADDED
@@ -0,0 +1,53 @@
|
|
1
|
+
class Klear::Motors
|
2
|
+
#class Data < BinData::Record
|
3
|
+
# array :numbers, :type => :uint16be, :read_until => :eof
|
4
|
+
#end
|
5
|
+
|
6
|
+
attr_reader :count, :frame_count
|
7
|
+
alias :blade_count :count
|
8
|
+
|
9
|
+
def initialize blade_count, frames = nil
|
10
|
+
@count = blade_count
|
11
|
+
@frames = frames || [[0] * @count] # init with one frame
|
12
|
+
@frame_count ||= @frames.size
|
13
|
+
end
|
14
|
+
|
15
|
+
#def frame_count
|
16
|
+
#end
|
17
|
+
|
18
|
+
def each &blk
|
19
|
+
#0.upto(@frame_count - 1) do |frame_no|
|
20
|
+
(1..@count).each do |motor_no|
|
21
|
+
yield(get(motor_no), motor_no)
|
22
|
+
end
|
23
|
+
end
|
24
|
+
|
25
|
+
# motor_no is one-based!!!
|
26
|
+
def get motor_no
|
27
|
+
motor_no = Integer(motor_no)
|
28
|
+
(0 < motor_no) or (raise "invalid motor no: #{motor_no}")
|
29
|
+
(motor_no <= @count) or (raise "motor no out of bounds: #{motor_no}")
|
30
|
+
@frames.map {|frame| frame[motor_no-1]}
|
31
|
+
end
|
32
|
+
|
33
|
+
# frame_no is zero based!!!
|
34
|
+
def frame frame_no
|
35
|
+
(0 <= frame_no) or (raise "invalid negative frame no: #{frame_no}")
|
36
|
+
(frame_no < @frame_count) or (raise "frame no out of bound: #{frame_no}")
|
37
|
+
@frames[frame_no]
|
38
|
+
end
|
39
|
+
|
40
|
+
def render motor_no, options = {}
|
41
|
+
low = options[:low] || 0
|
42
|
+
high = options[:high] || 20000
|
43
|
+
values = get(motor_no)
|
44
|
+
values = f14jj(values)
|
45
|
+
values = project(values, low, high)
|
46
|
+
end
|
47
|
+
|
48
|
+
def render_all options = {}
|
49
|
+
low = options[:low] || 0
|
50
|
+
high = options[:high] || 20000
|
51
|
+
(1..@count).map {|i| render(i, low, high)}
|
52
|
+
end
|
53
|
+
end
|
data/lib/klear/version.rb
CHANGED