tracksperanto 3.3.6 → 3.3.7
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.
- data/{DEVELOPER_DOCS.rdoc → CONTRIBUTING.md} +59 -58
- data/Gemfile +2 -3
- data/History.txt +5 -0
- data/README.rdoc +3 -3
- data/bin/tracksperanto +4 -2
- data/lib/import/base.rb +2 -2
- data/lib/import/flame_stabilizer.rb +37 -28
- data/lib/import/nuke_script.rb +19 -2
- data/lib/import/pftrack.rb +3 -1
- data/lib/import/shake_script.rb +2 -1
- data/lib/tracksperanto.rb +1 -1
- data/lib/tracksperanto/block_init.rb +2 -1
- data/lib/tracksperanto/keyframe.rb +4 -4
- data/test/export/test_boujou_export.rb +1 -1
- data/test/export/test_flame_stabilizer_export.rb +2 -2
- data/test/import/README.rdoc +10 -0
- data/test/import/test_flame_import.rb +3 -3
- data/test/import/test_match_mover_import.rb +9 -0
- data/test/import/test_nuke_import.rb +27 -1
- data/test/test_block_init.rb +49 -0
- data/tracksperanto.gemspec +9 -12
- metadata +9 -25
- data/test/import/README_SAMPLES.txt +0 -5
|
@@ -1,11 +1,11 @@
|
|
|
1
|
-
|
|
1
|
+
## Hacking Tracksperanto
|
|
2
2
|
|
|
3
|
-
|
|
3
|
+
### Tests
|
|
4
4
|
|
|
5
5
|
Tracksperanto is heavily tested. Please use the Github checkout to also receive some 17+ megabytes of test files that the test suite
|
|
6
6
|
can chew on. Contributions or patches without tests will be rejected at sight.
|
|
7
7
|
|
|
8
|
-
|
|
8
|
+
### Development environment
|
|
9
9
|
|
|
10
10
|
Tracksperanto is currently being developed on Ruby 1.9.3 but it should also work fine on 1.8.7.
|
|
11
11
|
What you will need is everything mentioned in the Gemfile plus Bundler.
|
|
@@ -16,13 +16,13 @@ The test corpus on Tracksperanto is HUGE at the moment, and it makes no sense to
|
|
|
16
16
|
|
|
17
17
|
So, to get started:
|
|
18
18
|
|
|
19
|
-
* Make sure you have Ruby and Bundler (
|
|
19
|
+
* Make sure you have Ruby and Bundler (`sudo gem install bundler` if unsure)
|
|
20
20
|
* Checkout the repo at http://github.com/guerilla-di/tracksperanto
|
|
21
|
-
* Run
|
|
21
|
+
* Run `bundle install` and `bundle exec rake` to run the tests
|
|
22
22
|
* Do your thing, in a separate branch
|
|
23
|
-
* File a pull request or send a patch to
|
|
23
|
+
* File a pull request or send a patch to **me at julik dot nl**
|
|
24
24
|
|
|
25
|
-
|
|
25
|
+
### Internal tracker representation
|
|
26
26
|
|
|
27
27
|
The trackers are represented by Tracker objects, which work like addressable hashes per frame number. The Tracker objects
|
|
28
28
|
contain Keyframe objects, and those in turn contain coordinates. The coordinates are stored in absolute pixels, relative to
|
|
@@ -31,80 +31,81 @@ the zero coordinate in the lower left corner.
|
|
|
31
31
|
Note on subpixel precision: the absolute left/bottom of the image has coordinates 0,0
|
|
32
32
|
(at the lower left corner of the leftmost bottommost pixel) and 0.5x0.5 in the middle of that pixel.
|
|
33
33
|
|
|
34
|
-
|
|
34
|
+
### Importing your own formats
|
|
35
35
|
|
|
36
36
|
To write an import module refer to Tracksperanto::Import::Base
|
|
37
37
|
docs. Your importer will be configured with width and height of the comp that it is importing, and will get an IO
|
|
38
38
|
object with the file that you are processing. You should then yield parsed trackers within the each method (tracker objects should
|
|
39
39
|
be Tracksperanto::Tracker objects or compatibles)
|
|
40
40
|
|
|
41
|
-
|
|
41
|
+
### Exporting your own formats
|
|
42
42
|
|
|
43
43
|
You can easily write an exporter. Refer to the Tracksperanto::Export::Base docs. Note that your exporter should be able to chew alot of data
|
|
44
44
|
(hundreds of trackers with thousands of keyframes with exported files growing up to 5-10 megs in size are not uncommon!). This means that
|
|
45
45
|
the exporter should work with streams (smaller parts of the file being exported will be held in memory at a time).
|
|
46
46
|
|
|
47
|
-
|
|
47
|
+
### Ading your own processing steps
|
|
48
48
|
|
|
49
49
|
You probably want to write a Tool (consult the Tracksperanto::Tool::Base docs) if you need some processing applied to the tracks
|
|
50
50
|
or their data. A Tool is just like an export module, except that instead it sits between the exporter and the exporting routine. Tools wrap export
|
|
51
51
|
modules or each other, so you can stack different tool modules together (like "scale first, then move").
|
|
52
52
|
|
|
53
|
-
|
|
53
|
+
### Writing your own processing pipelines from start to finish
|
|
54
54
|
|
|
55
55
|
You probably want to write a descendant of Tracksperanto::Pipeline::Base. This is a class that manages a conversion from start to finish, including detecting the
|
|
56
56
|
input format, allocating output files and building a chain of Tools to process the export. If you want to make a GUI for Tracksperanto you will likely need
|
|
57
57
|
to write your own Pipeline class or reimplement parts of it.
|
|
58
58
|
|
|
59
|
-
|
|
59
|
+
### Reporting status from long-running operations
|
|
60
60
|
|
|
61
61
|
Almost every module in Tracksperanto has a method called report_progress. This method is used to notify an external callback of what you are doing, and helps keep
|
|
62
62
|
the software user-friendly. A well-behaved Tracksperanto module should manage it's progress reports properly.
|
|
63
63
|
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
|
|
93
|
-
|
|
94
|
-
|
|
95
|
-
|
|
96
|
-
|
|
97
|
-
|
|
98
|
-
|
|
99
|
-
|
|
100
|
-
|
|
101
|
-
|
|
102
|
-
|
|
103
|
-
|
|
104
|
-
|
|
64
|
+
### Sample script
|
|
65
|
+
|
|
66
|
+
require "rubygems"
|
|
67
|
+
require "tracksperanto"
|
|
68
|
+
|
|
69
|
+
include Tracksperanto
|
|
70
|
+
|
|
71
|
+
# Create the importer object, for example for a Shake script.
|
|
72
|
+
# get_importer will give you the good class even you get the capitalization
|
|
73
|
+
# wrong!
|
|
74
|
+
some_importer = Tracksperanto.get_importer("shakescript").new
|
|
75
|
+
|
|
76
|
+
# This importer needs to know width and height
|
|
77
|
+
some_importer.width = 1024
|
|
78
|
+
some_importer.height = 576
|
|
79
|
+
some_importer.io = File.open("source_file.shk")
|
|
80
|
+
|
|
81
|
+
# The importer responds to each() so if your file is not too big you can just load all the trackers
|
|
82
|
+
# as an array. If you expect to have alot of trackers investigate a way to buffer them on disk
|
|
83
|
+
# instead (see Obuf)
|
|
84
|
+
trackers = some_importer.to_a
|
|
85
|
+
|
|
86
|
+
# Create the exporter and pass the output file to it
|
|
87
|
+
destination_file = File.open("exported_file.other", "wb")
|
|
88
|
+
some_exporter = Tracksperanto.get_exporter("flamestabilizer").new(destination_file)
|
|
89
|
+
|
|
90
|
+
# Now add some tools, for example a Scale
|
|
91
|
+
scaler = Tool::Scaler.new(some_exporter, :x_factor => 2)
|
|
92
|
+
# ... and a slip. Tools wrap exporters and other tools, so you can chain them
|
|
93
|
+
# ad nauseam
|
|
94
|
+
slipper = Tool::Slipper.new(scaler, :offset => 2)
|
|
95
|
+
|
|
96
|
+
# Now when we send export commands to the Slipper it will play them through
|
|
97
|
+
# to the Scaler and the Scaler in turn will send commands to the exporter.
|
|
98
|
+
# As you can see when you run export commands you do not have to use the Tracker
|
|
99
|
+
# objects, you just have to stream the right arguments in the right sequence
|
|
100
|
+
slipper.start_export(1024, 576)
|
|
101
|
+
trackers.each do | t |
|
|
102
|
+
slipper.start_tracker_segment(t.name)
|
|
103
|
+
t.each do | keyframe |
|
|
104
|
+
slipper.export_point(keyframe.frame, keyframe.abs_x, keyframe.abs_y, keyframe.residual)
|
|
105
|
+
end
|
|
106
|
+
slipper.end_tracker_segment
|
|
105
107
|
end
|
|
106
|
-
slipper.
|
|
107
|
-
|
|
108
|
-
|
|
109
|
-
|
|
110
|
-
# And we are done!
|
|
108
|
+
slipper.end_export
|
|
109
|
+
|
|
110
|
+
# And we are done!
|
|
111
|
+
|
data/Gemfile
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
# -*- ruby -*-
|
|
2
|
-
source
|
|
2
|
+
source 'http://rubygems.org'
|
|
3
3
|
|
|
4
4
|
gem "bundler"
|
|
5
5
|
|
|
@@ -19,12 +19,11 @@ group :development do
|
|
|
19
19
|
gem "linebyline"
|
|
20
20
|
|
|
21
21
|
if RUBY_VERSION > "1.8"
|
|
22
|
-
gem "flexmock", "~> 1.3", :require => %w( flexmock flexmock/test_unit )
|
|
22
|
+
gem "flexmock", "~> 1.3.2", :require => %w( flexmock flexmock/test_unit )
|
|
23
23
|
else
|
|
24
24
|
gem "flexmock", "~> 0.8", :require => %w( flexmock flexmock/test_unit )
|
|
25
25
|
end
|
|
26
26
|
|
|
27
27
|
gem "cli_test", "~>1.0"
|
|
28
28
|
gem "rake-hooks"
|
|
29
|
-
gem "ruby-prof"
|
|
30
29
|
end
|
data/History.txt
CHANGED
data/README.rdoc
CHANGED
|
@@ -52,7 +52,7 @@ that you export - and you can apply multiple transformations if desired.
|
|
|
52
52
|
== Using Tracksperanto from the command line
|
|
53
53
|
|
|
54
54
|
To run on your own computer, make sure you have Ruby installed. Versions from 1.8.7
|
|
55
|
-
and up are supported.
|
|
55
|
+
and up are supported. Wea re also testing on Ruby 2.0.0 now.
|
|
56
56
|
|
|
57
57
|
$ ruby -v
|
|
58
58
|
ruby 1.9.3p286 (2012-10-12 revision 37165) [x86_64-darwin12.2.0]
|
|
@@ -100,7 +100,7 @@ If you want your own copy of the web application at your facility we can discuss
|
|
|
100
100
|
* MatchMover REALVIZ Ascii Point Tracks .rz2 file
|
|
101
101
|
* MatchMover RZML .rzml file
|
|
102
102
|
* Maya Live track export file
|
|
103
|
-
* Nuke .nk script file with Tracker, Reconcile3D and
|
|
103
|
+
* Nuke .nk script file with Tracker, Reconcile3D, PlanarTracker and CornerPin nodes
|
|
104
104
|
* PFTrack/PFMatchit .2dt file
|
|
105
105
|
* Shake .shk script file
|
|
106
106
|
* Shake .txt tracker file and Nuke CameraTracker auto tracks export
|
|
@@ -139,7 +139,7 @@ Consult the --help option to see what is available.
|
|
|
139
139
|
== Development
|
|
140
140
|
|
|
141
141
|
If you are interested in reusing Tracksperanto's code or adding modules to the software consult
|
|
142
|
-
the {short developer introduction}[https://github.com/guerilla-di/tracksperanto/blob/master/
|
|
142
|
+
the {short developer introduction}[https://github.com/guerilla-di/tracksperanto/blob/master/CONTRIBUTING.md]
|
|
143
143
|
|
|
144
144
|
== Limitations
|
|
145
145
|
|
data/bin/tracksperanto
CHANGED
|
@@ -10,6 +10,9 @@
|
|
|
10
10
|
# == Author
|
|
11
11
|
# Julik <me@julik.nl>
|
|
12
12
|
|
|
13
|
+
# Show the message just in case
|
|
14
|
+
$stdout.puts "Starting Tracksperanto. For information and support please contact info#{64.chr}guerilla-di.org"
|
|
15
|
+
|
|
13
16
|
# Require the xperanto lib, which in turn requires Bundler and pulls in all the gems
|
|
14
17
|
require File.dirname(__FILE__) + '/../lib/tracksperanto' unless defined?(Tracksperanto)
|
|
15
18
|
require 'update_hints'
|
|
@@ -17,8 +20,7 @@ require 'optparse'
|
|
|
17
20
|
require 'progressbar'
|
|
18
21
|
|
|
19
22
|
def disclaimer
|
|
20
|
-
"Please consider a small donation to keep Tracksperanto going: http://guerilla-di.org/source-and-license
|
|
21
|
-
"For information and support please contact info#{64.chr}guerilla-di.org"
|
|
23
|
+
"Please consider a small donation to keep Tracksperanto going: http://guerilla-di.org/source-and-license/"
|
|
22
24
|
end
|
|
23
25
|
|
|
24
26
|
options = {}
|
data/lib/import/base.rb
CHANGED
|
@@ -22,7 +22,7 @@ class Tracksperanto::Import::Base
|
|
|
22
22
|
# Trakcksperanto will assign the passed width and height to the importer object before running
|
|
23
23
|
# the import. If not, you can replace the assigned values with your own. At the end of the import
|
|
24
24
|
# procedure, Tracksperanto will read the values from you again and will use the read values
|
|
25
|
-
# for determining the original comp size. +width+ and +height+ MUST return integer values after
|
|
25
|
+
# for determining the original comp size. +width+ and +height+ MUST return unsigned integer values after
|
|
26
26
|
# the import completes
|
|
27
27
|
attr_accessor :width, :height
|
|
28
28
|
|
|
@@ -64,7 +64,7 @@ class Tracksperanto::Import::Base
|
|
|
64
64
|
end
|
|
65
65
|
|
|
66
66
|
# The main method of the parser. Should
|
|
67
|
-
# yield each
|
|
67
|
+
# yield each Tracker that has been fully parsed. After calling this method the caller can ask for
|
|
68
68
|
# width and height as well.
|
|
69
69
|
def each
|
|
70
70
|
end
|
|
@@ -65,28 +65,41 @@ class Tracksperanto::Import::FlameStabilizer < Tracksperanto::Import::Base
|
|
|
65
65
|
end
|
|
66
66
|
end
|
|
67
67
|
|
|
68
|
+
# Extracts the animation channels and stores them in Obufs
|
|
69
|
+
# keyed by the channel path (like "tracker1/ref/x")
|
|
68
70
|
def extract_channels_from_stream(io)
|
|
69
71
|
parser = StabilizerParser.new
|
|
70
72
|
parser.logger_proc = method(:report_progress)
|
|
71
73
|
|
|
72
|
-
|
|
73
|
-
names = []
|
|
74
|
+
channel_map = {}
|
|
74
75
|
parser.parse(io) do | channel |
|
|
75
|
-
|
|
76
|
-
|
|
76
|
+
# Serialize the channel and store it on disk.
|
|
77
|
+
# Flame stabilizers are NOT likely to contain hundreds of
|
|
78
|
+
# trackers unless they were machine-exported from something,
|
|
79
|
+
# but we need to be memory-aware when we do things like this.
|
|
80
|
+
# On our test suite we lose half a second on disk IO overhead
|
|
81
|
+
# of the Obuf here, which is an acceptable compromise.
|
|
82
|
+
# To get rid of the disk-based cache just toss the outer
|
|
83
|
+
# Obuf constructor and pass in an Array
|
|
84
|
+
channel_map[channel.path] = Obuf.new([channel])
|
|
77
85
|
end
|
|
78
86
|
|
|
79
|
-
|
|
87
|
+
channel_map
|
|
80
88
|
end
|
|
81
89
|
|
|
82
|
-
def scavenge_trackers_from_channels(
|
|
83
|
-
|
|
84
|
-
|
|
90
|
+
def scavenge_trackers_from_channels(channel_map, names)
|
|
91
|
+
# Use Hash#keys.sort because we want a consistent export order
|
|
92
|
+
# irregardless of the Ruby version in use
|
|
93
|
+
# (hash keys are ordered on 1.9 and not ordered on 1.8)
|
|
94
|
+
channel_map.keys.sort.each do |c|
|
|
95
|
+
next unless c =~ /\/ref\/x$/
|
|
85
96
|
|
|
86
|
-
report_progress("Detected reference channel #{c.
|
|
97
|
+
report_progress("Detected reference channel #{c.inspect}")
|
|
87
98
|
|
|
88
|
-
|
|
89
|
-
|
|
99
|
+
extracted_tracker = grab_tracker(channel_map, c)
|
|
100
|
+
if extracted_tracker
|
|
101
|
+
yield(extracted_tracker)
|
|
102
|
+
end
|
|
90
103
|
end
|
|
91
104
|
end
|
|
92
105
|
|
|
@@ -94,29 +107,25 @@ class Tracksperanto::Import::FlameStabilizer < Tracksperanto::Import::Base
|
|
|
94
107
|
chan.map{|key| [key.frame, key.value]}
|
|
95
108
|
end
|
|
96
109
|
|
|
97
|
-
def grab_tracker(
|
|
98
|
-
t = Tracksperanto::Tracker.new(:name =>
|
|
110
|
+
def grab_tracker(channel_map, ref_x_channel_name)
|
|
111
|
+
t = Tracksperanto::Tracker.new(:name => ref_x_channel_name.split('/').shift)
|
|
99
112
|
|
|
100
113
|
report_progress("Extracting tracker #{t.name}")
|
|
101
114
|
|
|
102
|
-
|
|
103
|
-
|
|
104
|
-
|
|
105
|
-
|
|
106
|
-
shift_y_idx = names.index("#{t.name}/shift/y")
|
|
107
|
-
|
|
108
|
-
track_y = channels[ref_idx]
|
|
109
|
-
shift_x = channels[shift_x_idx]
|
|
110
|
-
shift_y = channels[shift_y_idx]
|
|
115
|
+
shift_x = channel_map["#{t.name}/shift/x"][0]
|
|
116
|
+
shift_y = channel_map["#{t.name}/shift/y"][0]
|
|
117
|
+
ref_x = channel_map["#{t.name}/ref/x"][0]
|
|
118
|
+
ref_y = channel_map["#{t.name}/ref/y"][0]
|
|
111
119
|
|
|
120
|
+
# Collapse separate X and Y curves into series of XY values
|
|
112
121
|
shift_tuples = zip_curve_tuples(channel_to_frames_and_values(shift_x), channel_to_frames_and_values(shift_y))
|
|
113
|
-
|
|
122
|
+
ref_tuples = zip_curve_tuples(channel_to_frames_and_values(ref_x), channel_to_frames_and_values(ref_y))
|
|
114
123
|
|
|
115
124
|
# If the channels are just empty go to next tracker
|
|
116
|
-
return if shift_tuples.empty? ||
|
|
125
|
+
return if shift_tuples.empty? || ref_tuples.empty?
|
|
117
126
|
|
|
118
127
|
report_progress("Detecting base value")
|
|
119
|
-
base_x, base_y = find_base_x_and_y(
|
|
128
|
+
base_x, base_y = find_base_x_and_y(ref_tuples, shift_tuples)
|
|
120
129
|
|
|
121
130
|
total_kf = 1
|
|
122
131
|
t.keyframes = shift_tuples.map do | (at, x, y) |
|
|
@@ -131,10 +140,10 @@ class Tracksperanto::Import::FlameStabilizer < Tracksperanto::Import::Base
|
|
|
131
140
|
return t
|
|
132
141
|
end
|
|
133
142
|
|
|
134
|
-
def find_base_x_and_y(
|
|
135
|
-
|
|
143
|
+
def find_base_x_and_y(ref_tuples, shift_tuples)
|
|
144
|
+
base_ref_tuple = ref_tuples.find do | track_tuple |
|
|
136
145
|
shift_tuples.find { |shift_tuple| shift_tuple[0] == track_tuple[0] }
|
|
137
146
|
end
|
|
138
|
-
(
|
|
147
|
+
(base_ref_tuple || ref_tuples[0])[1..2]
|
|
139
148
|
end
|
|
140
149
|
end
|
data/lib/import/nuke_script.rb
CHANGED
|
@@ -5,7 +5,7 @@ require 'tickly'
|
|
|
5
5
|
class Tracksperanto::Import::NukeScript < Tracksperanto::Import::Base
|
|
6
6
|
|
|
7
7
|
def self.human_name
|
|
8
|
-
"Nuke .nk script file with Tracker, Reconcile3D and
|
|
8
|
+
"Nuke .nk script file with Tracker, Reconcile3D, PlanarTracker and CornerPin nodes"
|
|
9
9
|
end
|
|
10
10
|
|
|
11
11
|
def self.distinct_file_ext
|
|
@@ -13,7 +13,8 @@ class Tracksperanto::Import::NukeScript < Tracksperanto::Import::Base
|
|
|
13
13
|
end
|
|
14
14
|
|
|
15
15
|
def self.known_snags
|
|
16
|
-
'The only supported nodes that we can extract tracks from are Reconcile3D,
|
|
16
|
+
'The only supported nodes that we can extract tracks from are Reconcile3D,'
|
|
17
|
+
+ ' PlanarTracker and Tracker (supported Nuke versions are 5, 6 and 7)'
|
|
17
18
|
end
|
|
18
19
|
|
|
19
20
|
def each
|
|
@@ -21,7 +22,9 @@ class Tracksperanto::Import::NukeScript < Tracksperanto::Import::Base
|
|
|
21
22
|
parser.add_node_handler_class(Tracker3)
|
|
22
23
|
parser.add_node_handler_class(Reconcile3D)
|
|
23
24
|
parser.add_node_handler_class(PlanarTracker1_0)
|
|
25
|
+
parser.add_node_handler_class(PlanarTracker)
|
|
24
26
|
parser.add_node_handler_class(Tracker4)
|
|
27
|
+
parser.add_node_handler_class(CornerPin2D)
|
|
25
28
|
|
|
26
29
|
parser.parse(@io) do | node |
|
|
27
30
|
node.trackers.each do | t |
|
|
@@ -87,12 +90,26 @@ class Tracksperanto::Import::NukeScript < Tracksperanto::Import::Base
|
|
|
87
90
|
end
|
|
88
91
|
end
|
|
89
92
|
|
|
93
|
+
# Planar tracker in Nuke 6
|
|
90
94
|
class PlanarTracker1_0 < Tracker3
|
|
91
95
|
def point_channels
|
|
92
96
|
%w( outputBottomLeft outputBottomRight outputTopLeft outputTopRight)
|
|
93
97
|
end
|
|
94
98
|
end
|
|
95
99
|
|
|
100
|
+
# Planar tracker in Nuke 7
|
|
101
|
+
class PlanarTracker < PlanarTracker1_0
|
|
102
|
+
def point_channels
|
|
103
|
+
%w( outputBottomLeft outputBottomRight outputTopLeft outputTopRight)
|
|
104
|
+
end
|
|
105
|
+
end
|
|
106
|
+
|
|
107
|
+
class CornerPin2D < Tracker3
|
|
108
|
+
def point_channels
|
|
109
|
+
%w( to1 to2 to3 to4 )
|
|
110
|
+
end
|
|
111
|
+
end
|
|
112
|
+
|
|
96
113
|
class Tracker4 < Tracker3
|
|
97
114
|
|
|
98
115
|
def initialize(options)
|
data/lib/import/pftrack.rb
CHANGED
|
@@ -76,7 +76,9 @@ class Tracksperanto::Import::PFTrack < Tracksperanto::Import::Base
|
|
|
76
76
|
(1..num_of_keyframes).map do | keyframe_idx |
|
|
77
77
|
report_progress("Reading keyframe #{keyframe_idx} of #{num_of_keyframes} in #{t.name}")
|
|
78
78
|
f, x, y, residual = io.gets.chomp.split
|
|
79
|
-
t.keyframe!(:frame => f.to_f - 1,
|
|
79
|
+
t.keyframe!(:frame => f.to_f - 1,
|
|
80
|
+
:abs_x => from_pfcoord(x), :abs_y => from_pfcoord(y),
|
|
81
|
+
:residual => residual.to_f * 8)
|
|
80
82
|
end
|
|
81
83
|
end
|
|
82
84
|
|
data/lib/import/shake_script.rb
CHANGED
|
@@ -13,7 +13,8 @@ class Tracksperanto::Import::ShakeScript < Tracksperanto::Import::Base
|
|
|
13
13
|
end
|
|
14
14
|
|
|
15
15
|
def self.known_snags
|
|
16
|
-
'Expressions in node parameters may cause parse errors or incomplete imports.
|
|
16
|
+
'Expressions in node parameters may cause parse errors or incomplete imports. ' +
|
|
17
|
+
'Take care to remove expressions or nodes containing them first.'
|
|
17
18
|
end
|
|
18
19
|
|
|
19
20
|
def each
|
data/lib/tracksperanto.rb
CHANGED
|
@@ -2,7 +2,8 @@
|
|
|
2
2
|
# Implements the conventional constructor with "hash of attributes" and block support
|
|
3
3
|
module Tracksperanto::BlockInit
|
|
4
4
|
def initialize(object_attribute_hash = {})
|
|
5
|
-
|
|
5
|
+
m = method(respond_to?(:public_send) ? :public_send : :send)
|
|
6
|
+
object_attribute_hash.map { |(k, v)| m.call("#{k}=", v) }
|
|
6
7
|
yield(self) if block_given?
|
|
7
8
|
end
|
|
8
9
|
end
|
|
@@ -9,16 +9,16 @@
|
|
|
9
9
|
class Tracksperanto::Keyframe
|
|
10
10
|
include Tracksperanto::Casts, Tracksperanto::BlockInit, Comparable
|
|
11
11
|
|
|
12
|
-
#
|
|
12
|
+
# Integer frame where this keyframe is placed, 0-based
|
|
13
13
|
attr_accessor :frame
|
|
14
14
|
|
|
15
|
-
#
|
|
15
|
+
# Float X value of the point, zero is lower left
|
|
16
16
|
attr_accessor :abs_x
|
|
17
17
|
|
|
18
|
-
#
|
|
18
|
+
# Float Y value of the point, zero is lower left
|
|
19
19
|
attr_accessor :abs_y
|
|
20
20
|
|
|
21
|
-
#
|
|
21
|
+
# Float residual (0 is "spot on")
|
|
22
22
|
attr_accessor :residual
|
|
23
23
|
|
|
24
24
|
cast_to_float :abs_x, :abs_y, :residual
|
|
@@ -7,7 +7,7 @@ class BoujouExportTest < Test::Unit::TestCase
|
|
|
7
7
|
|
|
8
8
|
def test_export_output_written
|
|
9
9
|
t = Time.local(2010, "Apr", 15, 17, 21, 26)
|
|
10
|
-
flexmock(Time).should_receive(:now).
|
|
10
|
+
flexmock(Time).should_receive(:now).and_return(t)
|
|
11
11
|
ensure_same_output Tracksperanto::Export::Boujou, P
|
|
12
12
|
end
|
|
13
13
|
|
|
@@ -1,13 +1,13 @@
|
|
|
1
1
|
# -*- encoding : utf-8 -*-
|
|
2
2
|
require File.expand_path(File.dirname(__FILE__)) + '/../helper'
|
|
3
3
|
|
|
4
|
-
class
|
|
4
|
+
class TestFlameStabilizerExport < Test::Unit::TestCase
|
|
5
5
|
include ParabolicTracks
|
|
6
6
|
P = File.dirname(__FILE__) + "/samples/ref_flame.stabilizer"
|
|
7
7
|
|
|
8
8
|
def test_export_output_written
|
|
9
9
|
t = Time.local(2010, "Feb", 18, 17, 22, 12)
|
|
10
|
-
flexmock(Time).should_receive(:now).
|
|
10
|
+
flexmock(Time).should_receive(:now).and_return(t)
|
|
11
11
|
ensure_same_output Tracksperanto::Export::FlameStabilizer, P
|
|
12
12
|
end
|
|
13
13
|
|
|
@@ -0,0 +1,10 @@
|
|
|
1
|
+
Tracksperanto has a huge test corpus of test files, which is stored in the project's Github repo.
|
|
2
|
+
|
|
3
|
+
This is why the repo is so big.
|
|
4
|
+
|
|
5
|
+
However, most users of Tracksperanto are actually _users_, they won't need
|
|
6
|
+
all the testing data. And without this data, tracksperanto is less than
|
|
7
|
+
100 kilobytes in size.
|
|
8
|
+
|
|
9
|
+
If you intend to actually write code that uses Tracksperanto or to fix
|
|
10
|
+
a bug, you will most definitely need all the test data. In that case, just do a git checkout.
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
# -*- encoding : utf-8 -*-
|
|
2
2
|
require File.expand_path(File.dirname(__FILE__)) + '/../helper'
|
|
3
3
|
|
|
4
|
-
class
|
|
4
|
+
class TestFlameImport < Test::Unit::TestCase
|
|
5
5
|
DELTA = 0.1
|
|
6
6
|
|
|
7
7
|
def test_parsing_from_flame
|
|
@@ -105,8 +105,8 @@ class FlameImportTest < Test::Unit::TestCase
|
|
|
105
105
|
def test_bilinear_passes_proper_naming
|
|
106
106
|
fixture = File.open(File.dirname(__FILE__) + '/samples/flame_stabilizer/cornerpin_2012.stabilizer')
|
|
107
107
|
trackers = Tracksperanto::Import::FlameStabilizer.new(:io => fixture).to_a
|
|
108
|
-
|
|
109
|
-
assert_equal
|
|
108
|
+
tracker_order = ["bottom_left", "bottom_right", "top_left", "top_right"]
|
|
109
|
+
assert_equal tracker_order, trackers.map(&:name)
|
|
110
110
|
end
|
|
111
111
|
|
|
112
112
|
end
|
|
@@ -6,6 +6,7 @@ class MatchMoverImportTest < Test::Unit::TestCase
|
|
|
6
6
|
|
|
7
7
|
P = File.dirname(__FILE__) + '/samples/match_mover/kipPointsMatchmover.rz2'
|
|
8
8
|
P2 = File.dirname(__FILE__) + '/samples/match_mover/NonSequentialMatchmoverPoints.rz2'
|
|
9
|
+
P3 = File.dirname(__FILE__) + '/samples/match_mover/cha_171_1020_atb_v001.rz2'
|
|
9
10
|
|
|
10
11
|
def test_introspects_properly
|
|
11
12
|
i = Tracksperanto::Import::MatchMover
|
|
@@ -22,6 +23,14 @@ class MatchMoverImportTest < Test::Unit::TestCase
|
|
|
22
23
|
assert_equal 0, trackers[0][0].frame, "Should have offset the first frame to 0"
|
|
23
24
|
end
|
|
24
25
|
|
|
26
|
+
def test_parsing_case_nil_exception
|
|
27
|
+
fixture = File.open(P3)
|
|
28
|
+
|
|
29
|
+
parser = Tracksperanto::Import::MatchMover.new(:io => fixture)
|
|
30
|
+
trackers = parser.to_a
|
|
31
|
+
assert_equal 6, trackers.length
|
|
32
|
+
end
|
|
33
|
+
|
|
25
34
|
def test_parsing_from_matchmover
|
|
26
35
|
fixture = File.open(P)
|
|
27
36
|
|
|
@@ -6,7 +6,7 @@ class NukeImportTest < Test::Unit::TestCase
|
|
|
6
6
|
|
|
7
7
|
def test_introspects_properly
|
|
8
8
|
i = Tracksperanto::Import::NukeScript
|
|
9
|
-
assert_equal "Nuke .nk script file with Tracker, Reconcile3D and
|
|
9
|
+
assert_equal "Nuke .nk script file with Tracker, Reconcile3D, PlanarTracker and CornerPin nodes", i.human_name
|
|
10
10
|
assert !i.autodetects_size?
|
|
11
11
|
end
|
|
12
12
|
|
|
@@ -34,6 +34,17 @@ class NukeImportTest < Test::Unit::TestCase
|
|
|
34
34
|
assert_equal 128, trackers[0].length
|
|
35
35
|
end
|
|
36
36
|
|
|
37
|
+
def test_parsing_cornerpin
|
|
38
|
+
fixture = File.open(File.dirname(__FILE__) + '/samples/nuke/cornerpin.nk')
|
|
39
|
+
|
|
40
|
+
parser = Tracksperanto::Import::NukeScript.new(:io => fixture)
|
|
41
|
+
parser.width = 4096
|
|
42
|
+
parser.height = 2304
|
|
43
|
+
|
|
44
|
+
trackers = parser.to_a
|
|
45
|
+
assert_equal 4, trackers.length
|
|
46
|
+
end
|
|
47
|
+
|
|
37
48
|
def test_parsing_planar_tracker
|
|
38
49
|
fixture = File.open(File.dirname(__FILE__) + '/samples/nuke/planar.nk')
|
|
39
50
|
|
|
@@ -49,6 +60,21 @@ class NukeImportTest < Test::Unit::TestCase
|
|
|
49
60
|
assert_equal ref_names, trackers.map{|e| e.name }
|
|
50
61
|
end
|
|
51
62
|
|
|
63
|
+
def test_parsing_planar_tracker_nuke7
|
|
64
|
+
fixture = File.open(File.dirname(__FILE__) + '/samples/nuke/nuke7_planar.nk')
|
|
65
|
+
|
|
66
|
+
parser = Tracksperanto::Import::NukeScript.new(:io => fixture)
|
|
67
|
+
parser.width = 1920
|
|
68
|
+
parser.height = 1080
|
|
69
|
+
|
|
70
|
+
trackers = parser.to_a
|
|
71
|
+
assert_equal 4, trackers.length
|
|
72
|
+
|
|
73
|
+
ref_names = %w( PlanarTracker1_outputBottomLeft PlanarTracker1_outputBottomRight
|
|
74
|
+
PlanarTracker1_outputTopLeft PlanarTracker1_outputTopRight )
|
|
75
|
+
assert_equal ref_names, trackers.map{|e| e.name }
|
|
76
|
+
end
|
|
77
|
+
|
|
52
78
|
def test_parsing_from_nuke
|
|
53
79
|
fixture = File.open(File.dirname(__FILE__) + '/samples/nuke/one_tracker_with_break.nk')
|
|
54
80
|
|
|
@@ -0,0 +1,49 @@
|
|
|
1
|
+
# -*- encoding : utf-8 -*-
|
|
2
|
+
require File.expand_path(File.dirname(__FILE__)) + '/helper'
|
|
3
|
+
|
|
4
|
+
class TestBlockInit < Test::Unit::TestCase
|
|
5
|
+
|
|
6
|
+
class Settable
|
|
7
|
+
include Tracksperanto::BlockInit
|
|
8
|
+
|
|
9
|
+
attr_accessor :foo, :bar
|
|
10
|
+
|
|
11
|
+
private
|
|
12
|
+
def privatized=(something)
|
|
13
|
+
end
|
|
14
|
+
end
|
|
15
|
+
|
|
16
|
+
def test_block_init_with_hash
|
|
17
|
+
s = Settable.new(:foo => "x", :bar => "y")
|
|
18
|
+
assert_equal "x", s.foo
|
|
19
|
+
assert_equal "y", s.bar
|
|
20
|
+
end
|
|
21
|
+
|
|
22
|
+
def test_block_init_with_hash_raises_on_unknown_attribute
|
|
23
|
+
assert_raise(NoMethodError) { Settable.new(:nonexistent => true) }
|
|
24
|
+
end
|
|
25
|
+
|
|
26
|
+
def test_block_init_with_block_yields_the_object
|
|
27
|
+
within_blk = nil
|
|
28
|
+
s = Settable.new do | f |
|
|
29
|
+
within_blk = f
|
|
30
|
+
f.foo = "x"
|
|
31
|
+
end
|
|
32
|
+
|
|
33
|
+
assert_equal s, within_blk, "Should have yielded the object to the blk"
|
|
34
|
+
assert_equal "x", s.foo
|
|
35
|
+
end
|
|
36
|
+
|
|
37
|
+
def test_block_init_attributes_overwrite_hash_attributes
|
|
38
|
+
s = Settable.new(:foo => "bar") { |f| f.foo = "x" }
|
|
39
|
+
assert_equal "x", s.foo
|
|
40
|
+
end
|
|
41
|
+
|
|
42
|
+
if RUBY_VERSION > '1.9'
|
|
43
|
+
def test_block_init_uses_public_send
|
|
44
|
+
assert_raise(NoMethodError) { Settable.new(:privatized => true) }
|
|
45
|
+
end
|
|
46
|
+
end
|
|
47
|
+
|
|
48
|
+
|
|
49
|
+
end
|
data/tracksperanto.gemspec
CHANGED
|
@@ -5,20 +5,19 @@
|
|
|
5
5
|
|
|
6
6
|
Gem::Specification.new do |s|
|
|
7
7
|
s.name = "tracksperanto"
|
|
8
|
-
s.version = "3.3.
|
|
8
|
+
s.version = "3.3.7"
|
|
9
9
|
|
|
10
10
|
s.required_rubygems_version = Gem::Requirement.new(">= 0") if s.respond_to? :required_rubygems_version=
|
|
11
11
|
s.authors = ["Julik Tarkhanov"]
|
|
12
|
-
s.date = "2013-
|
|
12
|
+
s.date = "2013-07-25"
|
|
13
13
|
s.description = "Converts 2D track exports between different apps like Flame, MatchMover, PFTrack..."
|
|
14
14
|
s.email = "me@julik.nl"
|
|
15
15
|
s.executables = ["tracksperanto"]
|
|
16
16
|
s.extra_rdoc_files = [
|
|
17
|
-
"DEVELOPER_DOCS.rdoc",
|
|
18
17
|
"README.rdoc"
|
|
19
18
|
]
|
|
20
19
|
s.files = [
|
|
21
|
-
"
|
|
20
|
+
"CONTRIBUTING.md",
|
|
22
21
|
"Gemfile",
|
|
23
22
|
"History.txt",
|
|
24
23
|
"MIT_LICENSE.txt",
|
|
@@ -141,7 +140,7 @@ Gem::Specification.new do |s|
|
|
|
141
140
|
"test/export/test_syntheyes_export.rb",
|
|
142
141
|
"test/export/test_xsi_python_export.rb",
|
|
143
142
|
"test/helper.rb",
|
|
144
|
-
"test/import/
|
|
143
|
+
"test/import/README.rdoc",
|
|
145
144
|
"test/import/test_3de_import.rb",
|
|
146
145
|
"test/import/test_3de_import3.rb",
|
|
147
146
|
"test/import/test_boujou_import.rb",
|
|
@@ -165,6 +164,7 @@ Gem::Specification.new do |s|
|
|
|
165
164
|
"test/subpixel/subpixel_grid.sni",
|
|
166
165
|
"test/subpixel/subpixel_grid.tif",
|
|
167
166
|
"test/subpixel/sy_subpix_2dpaths.txt",
|
|
167
|
+
"test/test_block_init.rb",
|
|
168
168
|
"test/test_buffer_io.rb",
|
|
169
169
|
"test/test_casts.rb",
|
|
170
170
|
"test/test_cli.rb",
|
|
@@ -202,7 +202,7 @@ Gem::Specification.new do |s|
|
|
|
202
202
|
s.homepage = "http://guerilla-di.org/tracksperanto"
|
|
203
203
|
s.licenses = ["MIT"]
|
|
204
204
|
s.require_paths = ["lib"]
|
|
205
|
-
s.rubygems_version = "1.8.
|
|
205
|
+
s.rubygems_version = "1.8.25"
|
|
206
206
|
s.summary = "A universal 2D tracks converter"
|
|
207
207
|
|
|
208
208
|
if s.respond_to? :specification_version then
|
|
@@ -221,10 +221,9 @@ Gem::Specification.new do |s|
|
|
|
221
221
|
s.add_development_dependency(%q<jeweler>, [">= 0"])
|
|
222
222
|
s.add_development_dependency(%q<rake>, [">= 0"])
|
|
223
223
|
s.add_development_dependency(%q<linebyline>, [">= 0"])
|
|
224
|
-
s.add_development_dependency(%q<flexmock>, ["~> 1.3"])
|
|
224
|
+
s.add_development_dependency(%q<flexmock>, ["~> 1.3.2"])
|
|
225
225
|
s.add_development_dependency(%q<cli_test>, ["~> 1.0"])
|
|
226
226
|
s.add_development_dependency(%q<rake-hooks>, [">= 0"])
|
|
227
|
-
s.add_development_dependency(%q<ruby-prof>, [">= 0"])
|
|
228
227
|
else
|
|
229
228
|
s.add_dependency(%q<bundler>, [">= 0"])
|
|
230
229
|
s.add_dependency(%q<obuf>, ["~> 1.1"])
|
|
@@ -238,10 +237,9 @@ Gem::Specification.new do |s|
|
|
|
238
237
|
s.add_dependency(%q<jeweler>, [">= 0"])
|
|
239
238
|
s.add_dependency(%q<rake>, [">= 0"])
|
|
240
239
|
s.add_dependency(%q<linebyline>, [">= 0"])
|
|
241
|
-
s.add_dependency(%q<flexmock>, ["~> 1.3"])
|
|
240
|
+
s.add_dependency(%q<flexmock>, ["~> 1.3.2"])
|
|
242
241
|
s.add_dependency(%q<cli_test>, ["~> 1.0"])
|
|
243
242
|
s.add_dependency(%q<rake-hooks>, [">= 0"])
|
|
244
|
-
s.add_dependency(%q<ruby-prof>, [">= 0"])
|
|
245
243
|
end
|
|
246
244
|
else
|
|
247
245
|
s.add_dependency(%q<bundler>, [">= 0"])
|
|
@@ -256,10 +254,9 @@ Gem::Specification.new do |s|
|
|
|
256
254
|
s.add_dependency(%q<jeweler>, [">= 0"])
|
|
257
255
|
s.add_dependency(%q<rake>, [">= 0"])
|
|
258
256
|
s.add_dependency(%q<linebyline>, [">= 0"])
|
|
259
|
-
s.add_dependency(%q<flexmock>, ["~> 1.3"])
|
|
257
|
+
s.add_dependency(%q<flexmock>, ["~> 1.3.2"])
|
|
260
258
|
s.add_dependency(%q<cli_test>, ["~> 1.0"])
|
|
261
259
|
s.add_dependency(%q<rake-hooks>, [">= 0"])
|
|
262
|
-
s.add_dependency(%q<ruby-prof>, [">= 0"])
|
|
263
260
|
end
|
|
264
261
|
end
|
|
265
262
|
|
metadata
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
|
2
2
|
name: tracksperanto
|
|
3
3
|
version: !ruby/object:Gem::Version
|
|
4
|
-
version: 3.3.
|
|
4
|
+
version: 3.3.7
|
|
5
5
|
prerelease:
|
|
6
6
|
platform: ruby
|
|
7
7
|
authors:
|
|
@@ -9,7 +9,7 @@ authors:
|
|
|
9
9
|
autorequire:
|
|
10
10
|
bindir: bin
|
|
11
11
|
cert_chain: []
|
|
12
|
-
date: 2013-
|
|
12
|
+
date: 2013-07-25 00:00:00.000000000 Z
|
|
13
13
|
dependencies:
|
|
14
14
|
- !ruby/object:Gem::Dependency
|
|
15
15
|
name: bundler
|
|
@@ -210,7 +210,7 @@ dependencies:
|
|
|
210
210
|
requirements:
|
|
211
211
|
- - ~>
|
|
212
212
|
- !ruby/object:Gem::Version
|
|
213
|
-
version:
|
|
213
|
+
version: 1.3.2
|
|
214
214
|
type: :development
|
|
215
215
|
prerelease: false
|
|
216
216
|
version_requirements: !ruby/object:Gem::Requirement
|
|
@@ -218,7 +218,7 @@ dependencies:
|
|
|
218
218
|
requirements:
|
|
219
219
|
- - ~>
|
|
220
220
|
- !ruby/object:Gem::Version
|
|
221
|
-
version:
|
|
221
|
+
version: 1.3.2
|
|
222
222
|
- !ruby/object:Gem::Dependency
|
|
223
223
|
name: cli_test
|
|
224
224
|
requirement: !ruby/object:Gem::Requirement
|
|
@@ -251,22 +251,6 @@ dependencies:
|
|
|
251
251
|
- - ! '>='
|
|
252
252
|
- !ruby/object:Gem::Version
|
|
253
253
|
version: '0'
|
|
254
|
-
- !ruby/object:Gem::Dependency
|
|
255
|
-
name: ruby-prof
|
|
256
|
-
requirement: !ruby/object:Gem::Requirement
|
|
257
|
-
none: false
|
|
258
|
-
requirements:
|
|
259
|
-
- - ! '>='
|
|
260
|
-
- !ruby/object:Gem::Version
|
|
261
|
-
version: '0'
|
|
262
|
-
type: :development
|
|
263
|
-
prerelease: false
|
|
264
|
-
version_requirements: !ruby/object:Gem::Requirement
|
|
265
|
-
none: false
|
|
266
|
-
requirements:
|
|
267
|
-
- - ! '>='
|
|
268
|
-
- !ruby/object:Gem::Version
|
|
269
|
-
version: '0'
|
|
270
254
|
description: Converts 2D track exports between different apps like Flame, MatchMover,
|
|
271
255
|
PFTrack...
|
|
272
256
|
email: me@julik.nl
|
|
@@ -274,10 +258,9 @@ executables:
|
|
|
274
258
|
- tracksperanto
|
|
275
259
|
extensions: []
|
|
276
260
|
extra_rdoc_files:
|
|
277
|
-
- DEVELOPER_DOCS.rdoc
|
|
278
261
|
- README.rdoc
|
|
279
262
|
files:
|
|
280
|
-
-
|
|
263
|
+
- CONTRIBUTING.md
|
|
281
264
|
- Gemfile
|
|
282
265
|
- History.txt
|
|
283
266
|
- MIT_LICENSE.txt
|
|
@@ -400,7 +383,7 @@ files:
|
|
|
400
383
|
- test/export/test_syntheyes_export.rb
|
|
401
384
|
- test/export/test_xsi_python_export.rb
|
|
402
385
|
- test/helper.rb
|
|
403
|
-
- test/import/
|
|
386
|
+
- test/import/README.rdoc
|
|
404
387
|
- test/import/test_3de_import.rb
|
|
405
388
|
- test/import/test_3de_import3.rb
|
|
406
389
|
- test/import/test_boujou_import.rb
|
|
@@ -424,6 +407,7 @@ files:
|
|
|
424
407
|
- test/subpixel/subpixel_grid.sni
|
|
425
408
|
- test/subpixel/subpixel_grid.tif
|
|
426
409
|
- test/subpixel/sy_subpix_2dpaths.txt
|
|
410
|
+
- test/test_block_init.rb
|
|
427
411
|
- test/test_buffer_io.rb
|
|
428
412
|
- test/test_casts.rb
|
|
429
413
|
- test/test_cli.rb
|
|
@@ -472,7 +456,7 @@ required_ruby_version: !ruby/object:Gem::Requirement
|
|
|
472
456
|
version: '0'
|
|
473
457
|
segments:
|
|
474
458
|
- 0
|
|
475
|
-
hash: -
|
|
459
|
+
hash: -4537325556160571006
|
|
476
460
|
required_rubygems_version: !ruby/object:Gem::Requirement
|
|
477
461
|
none: false
|
|
478
462
|
requirements:
|
|
@@ -481,7 +465,7 @@ required_rubygems_version: !ruby/object:Gem::Requirement
|
|
|
481
465
|
version: '0'
|
|
482
466
|
requirements: []
|
|
483
467
|
rubyforge_project:
|
|
484
|
-
rubygems_version: 1.8.
|
|
468
|
+
rubygems_version: 1.8.25
|
|
485
469
|
signing_key:
|
|
486
470
|
specification_version: 3
|
|
487
471
|
summary: A universal 2D tracks converter
|
|
@@ -1,5 +0,0 @@
|
|
|
1
|
-
Tracksperanto has a huge test corpus of test files, which is stored in the project's Github repo.
|
|
2
|
-
Checkout the repo to run tests against these samples.
|
|
3
|
-
Since most users of Tracksperanto are actually USERS, they do not need to download the 4 megabytes of test fixtures
|
|
4
|
-
as opposed to 80 kilobytes of the program itself. If you want to develop against Tracksperanto we advise you DO use
|
|
5
|
-
the test corpus though, so please pretty please just pull it off Github.
|