ply 0.9

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/.autotest ADDED
@@ -0,0 +1,23 @@
1
+ # -*- ruby -*-
2
+
3
+ require 'autotest/restart'
4
+
5
+ # Autotest.add_hook :initialize do |at|
6
+ # at.extra_files << "../some/external/dependency.rb"
7
+ #
8
+ # at.libs << ":../some/external"
9
+ #
10
+ # at.add_exception 'vendor'
11
+ #
12
+ # at.add_mapping(/dependency.rb/) do |f, _|
13
+ # at.files_matching(/test_.*rb$/)
14
+ # end
15
+ #
16
+ # %w(TestA TestB).each do |klass|
17
+ # at.extra_class_map[klass] = "test/test_misc.rb"
18
+ # end
19
+ # end
20
+
21
+ # Autotest.add_hook :run_command do |at|
22
+ # system "rake build"
23
+ # end
data/.gemtest ADDED
File without changes
data/History.txt ADDED
@@ -0,0 +1,3 @@
1
+ === 0.9.0 / 2013-02-04
2
+
3
+ * Initial release
data/LICENSE.txt ADDED
@@ -0,0 +1,28 @@
1
+ == LICENSE:
2
+
3
+ (The BSD 2-clause License)
4
+
5
+ Copyright (c) 2013 Jim Wise
6
+ All rights reserved.
7
+
8
+ Redistribution and use in source and binary forms, with or without
9
+ modification, are permitted provided that the following conditions
10
+ are met:
11
+
12
+ 1. Redistributions of source code must retain the above copyright
13
+ notice, this list of conditions and the following disclaimer.
14
+ 2. Redistributions in binary form must reproduce the above copyright
15
+ notice, this list of conditions and the following disclaimer in the
16
+ documentation and/or other materials provided with the distribution.
17
+
18
+ THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
19
+ "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
20
+ TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
21
+ PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS
22
+ BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
23
+ CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
24
+ SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
25
+ INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
26
+ CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
27
+ ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
28
+ POSSIBILITY OF SUCH DAMAGE.
data/Manifest.txt ADDED
@@ -0,0 +1,15 @@
1
+ .autotest
2
+ History.txt
3
+ LICENSE.txt
4
+ Manifest.txt
5
+ README.txt
6
+ README.rdoc
7
+ Rakefile
8
+ bin/ply2ascii
9
+ examples/cube_ascii.ply
10
+ examples/cube_binary_big_endian.ply
11
+ examples/cube_binary_little_endian.ply
12
+ examples/examples.rb
13
+ lib/ply.rb
14
+ test/test_ply.rb
15
+ test/test_ply2ascii.rb
data/README.rdoc ADDED
@@ -0,0 +1,227 @@
1
+ = ply
2
+
3
+ https://github.com/jimwise/ply
4
+
5
+ Author:: Jim Wise (mailto:jwise@draga.com)
6
+ Copyright:: Copyright (c) 2013 Jim Wise
7
+ License:: 2-clause BSD-Style (see LICENSE.txt)
8
+
9
+ == DESCRIPTION:
10
+
11
+ Ply is a ruby gem for reading Stanford PLY-format 3D model files.
12
+
13
+ The PLY file format is a flexible format for storing semi-structured binary data,
14
+ and is often used to stored polygonalized 3D models generated with range
15
+ scanning hardware. You can find some examples of the format at the the
16
+ {Stanford 3D Scanning Repository}[http://graphics.stanford.edu/data/3Dscanrep/].
17
+
18
+ Ply provides a simple API for quick access to the data in a PLY file
19
+ (including examining the structure of a particular file's content), and
20
+ an almost-as-simple event-driven API which can be used to process extremely
21
+ large ply files in a streaming fashion, without needing to keep the full
22
+ dataset represented in the file in memory. Ply handles all three types of
23
+ PLY files (ascii, binary-big-endian and binary-little-endian).
24
+
25
+ If you don't have any Stanford PLY files on hand, you probably don't need
26
+ this gem, but if you're curious, the PLY file format, described at
27
+ Wikipedia[http://en.wikipedia.org/wiki/PLY_(file_format)]
28
+
29
+
30
+ == REQUIREMENTS:
31
+
32
+ Ply currently requires Ruby 1.9.3 -- if you have a need to run Ply with Ruby
33
+ 1.8.7, drop me an email[mailto:jwise@draga.com], and I'll look into
34
+ backporting it. Ply has no other dependencies.
35
+
36
+ == INSTALL:
37
+
38
+ $ gem install ply
39
+
40
+ == SYNOPSIS:
41
+
42
+ === How to Use This Gem
43
+
44
+ To get started, include this gem using
45
+
46
+ require 'ply'
47
+
48
+ This gem provides the Ply module. This module provides a single class,
49
+ Ply::PlyFile, which can be used directly to parse a PLY file into memory, or
50
+ subclassed to take advantage of Ply's event-driven API for handling large
51
+ PLY files.
52
+
53
+ === The Simple API
54
+
55
+ To parse a PLY file into memory, simply instantiate the Ply::PlyFile class,
56
+ passing either the name of the PLY file or an IO object open on a PLY file
57
+ to Ply::PlyFile.new. Thus this:
58
+
59
+ pf = Ply::PlyFile.new "horse.ply"
60
+
61
+ and this:
62
+
63
+ pf = Ply::PlyFile.new File.new("horse.ply")
64
+
65
+ do the same thing.
66
+
67
+ The PLY file is parsed at construction time, and provides the following
68
+ read-only accessors:
69
+
70
+ Ply::PlyFile#version::
71
+ The version of the PLY format used in this file, as a String -- at this
72
+ time the only defined PLY version is 1.0.
73
+
74
+ Ply::PlyFile#format::
75
+ The format of this PLY file as a String-- one of +ascii+,
76
+ +binary_big_endian+, or +binary_little_endian+.
77
+
78
+ Ply::PlyFile#elements::
79
+ The structure of the data in this PLY file, as an array of Hashes, in the
80
+ order the elements will appear in the file. Each Hash contains the
81
+ following keys:
82
+
83
+ :name:: the name of this element type
84
+
85
+ :count:: the number of elements of this type in this PLY file
86
+
87
+ :properties::
88
+ an array of Hashes describing the properties of this element
89
+ type. Each Hash in the +:properties+ array contains the following keys:
90
+
91
+ :name:: the name of this property of the current element
92
+
93
+ :type::
94
+ the type of this property (an integral or floating point type, as
95
+ defined in the PLY file format, or +list+. If the current property is
96
+ of type +list+ (a PLY array type), its property Hash also contains the
97
+ following keys:
98
+
99
+ :index_type::
100
+ the type of the index of this list
101
+
102
+ :element_type::
103
+ the type of the elements in this list
104
+
105
+ If you are using the Ply::PlyFile class directly (as opposed to subclassing
106
+ it in order to use Ply's event-driven API for handling large PLY files), an
107
+ additional read-only accessor is available
108
+
109
+ Ply::PlyFile#data::
110
+
111
+ The actual data in this file, in the structure defined by the return value
112
+ of Ply::PlyFile#elements, as a Hash of Arrays of Hashes. This data is returned
113
+ as a Hash keyed by the name of each element type in the file (as a
114
+ String), and containing an array of Hashes for each element type in the
115
+ file, keyed by the property names of that element, as Strings.
116
+
117
+ That's not as complicated as it sounds! If the file +horse.ply+ defines a
118
+ +vertex+ element, with properties +x+, +y+, and +z+, then
119
+
120
+ pf = Ply::PlyFile.new "horse.ply"
121
+ verts = pf.data["vertex"]
122
+
123
+ will return an array of all vertices in the file, and
124
+
125
+ verts[0]["x"]
126
+
127
+ will give you the value of the +x+ property of the first +vertex+ element
128
+ in the file, as an Integer, Float, or Array, depending on the declared
129
+ type of that property in the PLY file.
130
+
131
+ === The Event-Driven API
132
+
133
+ The above API is easy to use, but has the disadvantage that the entire data
134
+ stored in the PLY file must be able to fit in memory, in order to be
135
+ returned by Ply::PlyFile::data. For this reason, a simple event-driven API
136
+ for handling PLY data is also provided. To make use of this, simply
137
+ subclass the class Ply::PlyFile, and provide your own version of the method
138
+ Ply::PlyFile#element_callback. When a new object of your subclass is
139
+ constructed from a PLY file, your implementation of this method will be
140
+ called once for each element in the file, and passed two arguments:
141
+
142
+ * The type of the current element, in the same format as a member of the
143
+ array returned by Ply::PlyFile::elements
144
+
145
+ * The current element, in the same format as the members of the arrays
146
+ returned by Ply::PlyFile::data
147
+
148
+ Note that if your subclass provides a #initialize method, it is your
149
+ responsibility to ensure that PlyFile
150
+
151
+ As an example, the following code defines a subclass of PlyFile which merely
152
+ *counts* the elements of each type present in PLY file, without reading them
153
+ all into memory at the same time:
154
+
155
+ class PlyFileCounter < Ply::PlyFile
156
+ attr_reader :counts
157
+
158
+ def initialize f
159
+ @counts = {}
160
+ super f
161
+ end
162
+
163
+ def element_callback e, v
164
+ @counts[e[:name]] ||= 0
165
+ @counts[e[:name]] = @counts[e[:name]] + 1
166
+ end
167
+ end
168
+
169
+ p = PlyFileCounter.new "horse.ply"
170
+ puts "There are #{p.counts["vertex"]} vertices in the file."
171
+
172
+ === Example Files
173
+
174
+ In addition to the large models in the {Stanford 3D Scanning
175
+ Repository}[http://graphics.stanford.edu/data/3Dscanrep/], the +examples/+
176
+ subdirectory of this gem includes a simple triangulated cube model in all
177
+ three PLY file formats, as well as a script to generate these three files
178
+ which you may modify for your own purposes.
179
+
180
+ === ply2ascii
181
+
182
+ Finally, this gem includes a simple script, +ply2ascii+, which can be used
183
+ to parse any of the three PLY file formats containing a triangulated scene
184
+ with vertices in an element named +vertex+ with (at least) scalar properties
185
+ +x+, +y+, and +z+, and faces in an elemnt named +face+ containing a list
186
+ property +vertex_indices+, and produce a simple ASCII dump of the described
187
+ scene in two files.
188
+
189
+ Run +ply2ascii+ with no arguments for (a little) more information.
190
+
191
+ == DEVELOPERS:
192
+
193
+ After checking out the source, run:
194
+
195
+ $ rake newb
196
+
197
+ This task will install any missing dependencies, run the tests/specs,
198
+ and generate the RDoc.
199
+
200
+ == LICENSE:
201
+
202
+ (The BSD 2-clause License)
203
+
204
+ Copyright (c) 2013 Jim Wise
205
+ All rights reserved.
206
+
207
+ Redistribution and use in source and binary forms, with or without
208
+ modification, are permitted provided that the following conditions
209
+ are met:
210
+
211
+ 1. Redistributions of source code must retain the above copyright
212
+ notice, this list of conditions and the following disclaimer.
213
+ 2. Redistributions in binary form must reproduce the above copyright
214
+ notice, this list of conditions and the following disclaimer in the
215
+ documentation and/or other materials provided with the distribution.
216
+
217
+ THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
218
+ "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
219
+ TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
220
+ PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS
221
+ BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
222
+ CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
223
+ SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
224
+ INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
225
+ CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
226
+ ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
227
+ POSSIBILITY OF SUCH DAMAGE.
data/README.txt ADDED
@@ -0,0 +1,227 @@
1
+ = ply
2
+
3
+ https://github.com/jimwise/ply
4
+
5
+ Author:: Jim Wise (mailto:jwise@draga.com)
6
+ Copyright:: Copyright (c) 2013 Jim Wise
7
+ License:: 2-clause BSD-Style (see LICENSE.txt)
8
+
9
+ == DESCRIPTION:
10
+
11
+ Ply is a ruby gem for reading Stanford PLY-format 3D model files.
12
+
13
+ The PLY file format is a flexible format for storing semi-structured binary data,
14
+ and is often used to stored polygonalized 3D models generated with range
15
+ scanning hardware. You can find some examples of the format at the the
16
+ {Stanford 3D Scanning Repository}[http://graphics.stanford.edu/data/3Dscanrep/].
17
+
18
+ Ply provides a simple API for quick access to the data in a PLY file
19
+ (including examining the structure of a particular file's content), and
20
+ an almost-as-simple event-driven API which can be used to process extremely
21
+ large ply files in a streaming fashion, without needing to keep the full
22
+ dataset represented in the file in memory. Ply handles all three types of
23
+ PLY files (ascii, binary-big-endian and binary-little-endian).
24
+
25
+ If you don't have any Stanford PLY files on hand, you probably don't need
26
+ this gem, but if you're curious, the PLY file format, described at
27
+ Wikipedia[http://en.wikipedia.org/wiki/PLY_(file_format)]
28
+
29
+
30
+ == REQUIREMENTS:
31
+
32
+ Ply currently requires Ruby 1.9.3 -- if you have a need to run Ply with Ruby
33
+ 1.8.7, drop me an email[mailto:jwise@draga.com], and I'll look into
34
+ backporting it. Ply has no other dependencies.
35
+
36
+ == INSTALL:
37
+
38
+ $ gem install ply
39
+
40
+ == SYNOPSIS:
41
+
42
+ === How to Use This Gem
43
+
44
+ To get started, include this gem using
45
+
46
+ require 'ply'
47
+
48
+ This gem provides the Ply module. This module provides a single class,
49
+ Ply::PlyFile, which can be used directly to parse a PLY file into memory, or
50
+ subclassed to take advantage of Ply's event-driven API for handling large
51
+ PLY files.
52
+
53
+ === The Simple API
54
+
55
+ To parse a PLY file into memory, simply instantiate the Ply::PlyFile class,
56
+ passing either the name of the PLY file or an IO object open on a PLY file
57
+ to Ply::PlyFile.new. Thus this:
58
+
59
+ pf = Ply::PlyFile.new "horse.ply"
60
+
61
+ and this:
62
+
63
+ pf = Ply::PlyFile.new File.new("horse.ply")
64
+
65
+ do the same thing.
66
+
67
+ The PLY file is parsed at construction time, and provides the following
68
+ read-only accessors:
69
+
70
+ Ply::PlyFile#version::
71
+ The version of the PLY format used in this file, as a String -- at this
72
+ time the only defined PLY version is 1.0.
73
+
74
+ Ply::PlyFile#format::
75
+ The format of this PLY file as a String-- one of +ascii+,
76
+ +binary_big_endian+, or +binary_little_endian+.
77
+
78
+ Ply::PlyFile#elements::
79
+ The structure of the data in this PLY file, as an array of Hashes, in the
80
+ order the elements will appear in the file. Each Hash contains the
81
+ following keys:
82
+
83
+ :name:: the name of this element type
84
+
85
+ :count:: the number of elements of this type in this PLY file
86
+
87
+ :properties::
88
+ an array of Hashes describing the properties of this element
89
+ type. Each Hash in the +:properties+ array contains the following keys:
90
+
91
+ :name:: the name of this property of the current element
92
+
93
+ :type::
94
+ the type of this property (an integral or floating point type, as
95
+ defined in the PLY file format, or +list+. If the current property is
96
+ of type +list+ (a PLY array type), its property Hash also contains the
97
+ following keys:
98
+
99
+ :index_type::
100
+ the type of the index of this list
101
+
102
+ :element_type::
103
+ the type of the elements in this list
104
+
105
+ If you are using the Ply::PlyFile class directly (as opposed to subclassing
106
+ it in order to use Ply's event-driven API for handling large PLY files), an
107
+ additional read-only accessor is available
108
+
109
+ Ply::PlyFile#data::
110
+
111
+ The actual data in this file, in the structure defined by the return value
112
+ of Ply::PlyFile#elements, as a Hash of Arrays of Hashes. This data is returned
113
+ as a Hash keyed by the name of each element type in the file (as a
114
+ String), and containing an array of Hashes for each element type in the
115
+ file, keyed by the property names of that element, as Strings.
116
+
117
+ That's not as complicated as it sounds! If the file +horse.ply+ defines a
118
+ +vertex+ element, with properties +x+, +y+, and +z+, then
119
+
120
+ pf = Ply::PlyFile.new "horse.ply"
121
+ verts = pf.data["vertex"]
122
+
123
+ will return an array of all vertices in the file, and
124
+
125
+ verts[0]["x"]
126
+
127
+ will give you the value of the +x+ property of the first +vertex+ element
128
+ in the file, as an Integer, Float, or Array, depending on the declared
129
+ type of that property in the PLY file.
130
+
131
+ === The Event-Driven API
132
+
133
+ The above API is easy to use, but has the disadvantage that the entire data
134
+ stored in the PLY file must be able to fit in memory, in order to be
135
+ returned by Ply::PlyFile::data. For this reason, a simple event-driven API
136
+ for handling PLY data is also provided. To make use of this, simply
137
+ subclass the class Ply::PlyFile, and provide your own version of the method
138
+ Ply::PlyFile#element_callback. When a new object of your subclass is
139
+ constructed from a PLY file, your implementation of this method will be
140
+ called once for each element in the file, and passed two arguments:
141
+
142
+ * The type of the current element, in the same format as a member of the
143
+ array returned by Ply::PlyFile::elements
144
+
145
+ * The current element, in the same format as the members of the arrays
146
+ returned by Ply::PlyFile::data
147
+
148
+ Note that if your subclass provides a #initialize method, it is your
149
+ responsibility to ensure that PlyFile
150
+
151
+ As an example, the following code defines a subclass of PlyFile which merely
152
+ *counts* the elements of each type present in PLY file, without reading them
153
+ all into memory at the same time:
154
+
155
+ class PlyFileCounter < Ply::PlyFile
156
+ attr_reader :counts
157
+
158
+ def initialize f
159
+ @counts = {}
160
+ super f
161
+ end
162
+
163
+ def element_callback e, v
164
+ @counts[e[:name]] ||= 0
165
+ @counts[e[:name]] = @counts[e[:name]] + 1
166
+ end
167
+ end
168
+
169
+ p = PlyFileCounter.new "horse.ply"
170
+ puts "There are #{p.counts["vertex"]} vertices in the file."
171
+
172
+ === Example Files
173
+
174
+ In addition to the large models in the {Stanford 3D Scanning
175
+ Repository}[http://graphics.stanford.edu/data/3Dscanrep/], the +examples/+
176
+ subdirectory of this gem includes a simple triangulated cube model in all
177
+ three PLY file formats, as well as a script to generate these three files
178
+ which you may modify for your own purposes.
179
+
180
+ === ply2ascii
181
+
182
+ Finally, this gem includes a simple script, +ply2ascii+, which can be used
183
+ to parse any of the three PLY file formats containing a triangulated scene
184
+ with vertices in an element named +vertex+ with (at least) scalar properties
185
+ +x+, +y+, and +z+, and faces in an elemnt named +face+ containing a list
186
+ property +vertex_indices+, and produce a simple ASCII dump of the described
187
+ scene in two files.
188
+
189
+ Run +ply2ascii+ with no arguments for (a little) more information.
190
+
191
+ == DEVELOPERS:
192
+
193
+ After checking out the source, run:
194
+
195
+ $ rake newb
196
+
197
+ This task will install any missing dependencies, run the tests/specs,
198
+ and generate the RDoc.
199
+
200
+ == LICENSE:
201
+
202
+ (The BSD 2-clause License)
203
+
204
+ Copyright (c) 2013 Jim Wise
205
+ All rights reserved.
206
+
207
+ Redistribution and use in source and binary forms, with or without
208
+ modification, are permitted provided that the following conditions
209
+ are met:
210
+
211
+ 1. Redistributions of source code must retain the above copyright
212
+ notice, this list of conditions and the following disclaimer.
213
+ 2. Redistributions in binary form must reproduce the above copyright
214
+ notice, this list of conditions and the following disclaimer in the
215
+ documentation and/or other materials provided with the distribution.
216
+
217
+ THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
218
+ "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
219
+ TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
220
+ PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS
221
+ BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
222
+ CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
223
+ SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
224
+ INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
225
+ CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
226
+ ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
227
+ POSSIBILITY OF SUCH DAMAGE.
data/Rakefile ADDED
@@ -0,0 +1,8 @@
1
+ # -*- ruby -*-
2
+
3
+ require 'rubygems'
4
+ require 'hoe'
5
+
6
+ Hoe.spec 'ply' do
7
+ developer('Jim Wise', 'jwise@draga.com')
8
+ end
data/bin/ply2ascii ADDED
@@ -0,0 +1,49 @@
1
+ #!/usr/bin/env ruby
2
+
3
+ require 'ply'
4
+
5
+ USAGE = "usage: #{$0} <ply file> ..."
6
+ HELP = <<EOL
7
+ This utility translates ply files with vertex and triangle information
8
+ into simple ASCII files.
9
+
10
+ Each plyfile specified on the command line is read, and corresponding
11
+ triangle and vertex files are created in the current directory.
12
+ EOL
13
+
14
+ class Ply2Ascii < Ply::PlyFile
15
+ def initialize infile, outname
16
+ @verts = File.new("vertices_#{outname}.ascii", "w")
17
+ @verts.puts("# written by ply.rb")
18
+ @tris = File.new("triangles_#{outname}.ascii", "w")
19
+ @tris.puts("# written by ply.rb")
20
+ super infile
21
+ end
22
+
23
+ def element_callback elt, val
24
+ case elt[:name]
25
+ when "vertex"
26
+ @verts.puts "#{val["x"]} #{val["y"]} #{val["z"]}"
27
+ when "face"
28
+ @tris.puts val["vertex_indices"].join " "
29
+ else
30
+ puts "skipping unknown element type #{elt[:name]}"
31
+ end
32
+ end
33
+ end
34
+
35
+ if ARGV.size == 0
36
+ puts USAGE
37
+ puts HELP
38
+ end
39
+
40
+ ARGV.each do |fname|
41
+ begin
42
+ outname = File.basename(fname, ".ply")
43
+ Ply2Ascii.new(fname, outname)
44
+ rescue Ply::BadFile => bf
45
+ puts "#{fname}: #{bf}"
46
+ end
47
+ end
48
+
49
+ # -*- ruby -*-
@@ -0,0 +1,30 @@
1
+ ply
2
+ format ascii 1.0
3
+ comment simple triangulated cube example
4
+ element vertex 8
5
+ property float32 x
6
+ property float32 y
7
+ property float32 z
8
+ element face 12
9
+ property list uint8 uint32 vertex_indices
10
+ end_header
11
+ 0.0 0.0 0.0
12
+ 0.0 1.0 0.0
13
+ 1.0 0.0 0.0
14
+ 1.0 1.0 0.0
15
+ 0.0 0.0 1.0
16
+ 0.0 1.0 1.0
17
+ 1.0 0.0 1.0
18
+ 1.0 1.0 1.0
19
+ 3 0 1 3
20
+ 3 1 3 2
21
+ 3 0 1 5
22
+ 3 0 5 4
23
+ 3 4 0 2
24
+ 3 4 2 6
25
+ 3 2 3 7
26
+ 3 2 7 6
27
+ 3 4 5 7
28
+ 3 4 7 6
29
+ 3 1 5 7
30
+ 3 1 7 3
Binary file
@@ -0,0 +1,100 @@
1
+ #!/usr/local/bin/ruby
2
+
3
+ # 6---------7
4
+ # /: /|
5
+ # / : / |
6
+ # / : / |
7
+ # 2---------3 |
8
+ # | 4.....|...5
9
+ # | ' | /
10
+ # | ' | /
11
+ # |' |/
12
+ # 0---------1
13
+ #
14
+ # Y Z
15
+ # |/
16
+ # *-X
17
+
18
+ Cube_vertices = [
19
+ [0.0, 0.0, 0.0],
20
+ [0.0, 1.0, 0.0],
21
+ [1.0, 0.0, 0.0],
22
+ [1.0, 1.0, 0.0],
23
+ [0.0, 0.0, 1.0],
24
+ [0.0, 1.0, 1.0],
25
+ [1.0, 0.0, 1.0],
26
+ [1.0, 1.0, 1.0]
27
+ ]
28
+
29
+ Cube_faces = [
30
+ [0, 1, 3],
31
+ [1, 3, 2],
32
+
33
+ [0, 1, 5],
34
+ [0, 5, 4],
35
+
36
+ [4, 0, 2],
37
+ [4, 2, 6],
38
+
39
+ [2, 3, 7],
40
+ [2, 7, 6],
41
+
42
+ [4, 5, 7],
43
+ [4, 7, 6],
44
+
45
+ [1, 5, 7],
46
+ [1, 7, 3]
47
+ ]
48
+
49
+ def header f, format
50
+ f.puts "ply"
51
+ f.puts "format #{format} 1.0"
52
+ f.puts "comment simple triangulated cube example"
53
+ f.puts "element vertex 8"
54
+ f.puts "property float32 x"
55
+ f.puts "property float32 y"
56
+ f.puts "property float32 z"
57
+ f.puts "element face 12"
58
+ f.puts "property list uint8 uint32 vertex_indices"
59
+ f.puts "end_header"
60
+ end
61
+
62
+ def ascii_cube fname
63
+ f = File.new(fname, "w")
64
+ header f, "ascii"
65
+ Cube_vertices.each do |vert|
66
+ f.puts vert.map{|p| p.to_s}.join(" ")
67
+ end
68
+ Cube_faces.each do |face|
69
+ f.print "#{face.size} "
70
+ f.puts face.map{|p| p.to_s}.join(" ")
71
+ end
72
+ end
73
+
74
+ def bin_be_cube fname
75
+ f = File.new(fname, "w")
76
+ header f, "binary_big_endian"
77
+ Cube_vertices.each do |vert|
78
+ f.write vert.pack("g*")
79
+ end
80
+ Cube_faces.each do |face|
81
+ f.write [ face.size ].pack("C")
82
+ f.write face.pack("L>*")
83
+ end
84
+ end
85
+
86
+ def bin_le_cube fname
87
+ f = File.new(fname, "w")
88
+ header f, "binary_little_endian"
89
+ Cube_vertices.each do |vert|
90
+ f.write vert.pack("e*")
91
+ end
92
+ Cube_faces.each do |face|
93
+ f.write [ face.size ].pack("C")
94
+ f.write face.pack("L<*")
95
+ end
96
+ end
97
+
98
+ ascii_cube "cube_ascii.ply"
99
+ bin_be_cube "cube_binary_big_endian.ply"
100
+ bin_le_cube "cube_binary_little_endian.ply"
data/lib/ply.rb ADDED
@@ -0,0 +1,188 @@
1
+ # ply.rb
2
+
3
+ # Copyright (c) 2013 Jim Wise
4
+ # All rights reserved.
5
+ #
6
+ # Redistribution and use in source and binary forms, with or without
7
+ # modification, are permitted provided that the following conditions
8
+ # are met:
9
+ #
10
+ # 1. Redistributions of source code must retain the above copyright
11
+ # notice, this list of conditions and the following disclaimer.
12
+ # 2. Redistributions in binary form must reproduce the above copyright
13
+ # notice, this list of conditions and the following disclaimer in the
14
+ # documentation and/or other materials provided with the distribution.
15
+ #
16
+ # THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
17
+ # "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
18
+ # TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
19
+ # PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS
20
+ # BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
21
+ # CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
22
+ # SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
23
+ # INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
24
+ # CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
25
+ # ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
26
+ # POSSIBILITY OF SUCH DAMAGE.
27
+
28
+ module Ply
29
+ VERSION = '0.9'
30
+
31
+ # module for working with ply file format -- see
32
+ # http://en.wikipedia.org/wiki/PLY_(file_format)
33
+ # right now, this supports reading, but not writing ply files
34
+
35
+ class PlyFile
36
+ attr_reader :format, :version, :elements, :data
37
+
38
+ # versions of the ply file format we know how to parse (this is the only defined version as of the time of writing)
39
+ Versions = %w{1.0}
40
+ # formats of the ply file format we know about (we don't yet implement ascii)
41
+ Formats = %w{binary_big_endian binary_little_endian ascii}
42
+ # property types defined by the ply format; we assume ILP32 meaning of types without explicit widths
43
+ Types = %w{char uchar short ushort int uint float double int8 uint8 int16 uint16 int32 uint32 float32 float64 list}
44
+
45
+ # parse a ply file takes a file name or an IO stream as argument
46
+ def initialize f
47
+ unless f.instance_of? IO
48
+ f = File.new(f)
49
+ end
50
+ @source = f
51
+ @elements = []
52
+ parse_header f
53
+ parse_body f
54
+ end
55
+
56
+ private
57
+
58
+ def parse_header f
59
+ raise BadFile.new("missing magic") unless f.gets.chomp == "ply"
60
+
61
+ raise BadFile.new("malformed format line") unless md =
62
+ f.gets.chomp.match(/^format\s+(?<format>[^\s]+)\s+(?<version>[\d.]+)$/)
63
+ @format = md[:format]
64
+ @version = md[:version]
65
+
66
+ raise BadFile.new("unknown version: #{version}") unless Versions.find(version)
67
+ raise BadFile.new("unknown format: #{format}") unless Formats.find(version)
68
+
69
+ current_element = false
70
+ f.each do |s|
71
+ cmd = s.chomp.split
72
+ case cmd[0]
73
+ when "comment"
74
+ when "obj_info"
75
+ when "end_header"
76
+ # stash last element
77
+ @elements << current_element if current_element
78
+ break
79
+ when "element"
80
+ # stash previous element
81
+ @elements << current_element if current_element
82
+ # puts "new element #{cmd[1]}"
83
+ current_element = { name: cmd[1], count: cmd[2].to_i, properties: [] }
84
+ when "property"
85
+ type = cmd[1]
86
+ raise BadFile.new("unknown element type: #{type}") unless Types.find(type)
87
+
88
+ current_element[:properties] << if type == "list"
89
+ # puts "new property #{cmd[4]} of element #{current_element[:name]} has type list indexed by #{cmd[2]} of #{cmd[3]}"
90
+ { name: cmd[4], type: "list", index_type: cmd[2], element_type: cmd[3]}
91
+ else
92
+ # puts "new property #{cmd[2]} of element #{current_element[:name]} has type #{type}"
93
+ { name: cmd[2], type: type}
94
+ end
95
+ else
96
+ raise BadFile.new("unknown ply command #{cmd[0]}")
97
+ end
98
+ end
99
+ end
100
+
101
+ # parse the body of a ply file, using the structures already parsed out of the header
102
+ # for each element type foo defined in the header, fills in @data[foo] with an array of hashes keyed on the property names
103
+ def parse_body f
104
+ @elements.each do |elt|
105
+ elt[:count].times do
106
+ element_callback elt, read_element(f, elt)
107
+ end
108
+ end
109
+ end
110
+
111
+ # by default, we accumulate all elements of a given type e, in-memory, into an array @data[e]
112
+ # this is non-ideal for big files, of course -- to change this behavior, subclass PlyFile, and provide your own
113
+ # element callback -- it will be called once for each read element, and passed the element definition (in elt), and
114
+ # the actual values just read (in val)
115
+ def element_callback elt, val
116
+ @data ||= {}
117
+ @data[elt[:name]] ||= []
118
+ @data[elt[:name]] << val
119
+ end
120
+
121
+ def read_element f, elt
122
+ current_element = {}
123
+ if @format == "ascii"
124
+ fields = f.gets.chomp.split
125
+ elt[:properties].each do |prop|
126
+ current_element[prop[:name]] = read_property_ascii(fields, prop[:type], prop[:index_type], prop[:element_type])
127
+ end
128
+ else
129
+ elt[:properties].each do |prop|
130
+ # last two will be nil for non-list
131
+ current_element[prop[:name]] = read_property_binary(f, prop[:type], prop[:index_type], prop[:element_type])
132
+ end
133
+ end
134
+ current_element
135
+ end
136
+
137
+ def read_property_ascii fields, type, index_type=false, element_type=false
138
+ #print "reading one #{type}: "
139
+ case type
140
+ when "char", "int8", "uchar", "uint8", "short", "int16", "ushort", "uint16", "int", "int32", "uint", "uint32"
141
+ # integer types
142
+ fields.shift.to_i
143
+ when "float", "float32", "double", "float64"
144
+ # floating point types
145
+ fields.shift.to_f
146
+ when "list"
147
+ count = read_property_ascii(fields, index_type)
148
+ count.times.collect { read_property_ascii(fields, element_type) }
149
+ end
150
+ end
151
+
152
+ # read one property from an IO stream in ply binary format, assuming ILP32 widths for generic types
153
+ def read_property_binary f, type, index_type=false, element_type=false
154
+ # print "reading one #{type}: "
155
+ # pick String#unpack specifiers based on our endian-ness
156
+ case @format
157
+ when "binary_big_endian"
158
+ e, f32, f64 = ">", "g", "G"
159
+ when "binary_little_endian"
160
+ e, f32, f64 = "<", "e", "E"
161
+ end
162
+ case type
163
+ when "char", "int8"
164
+ f.read(1).unpack("c").first
165
+ when "uchar", "uint8"
166
+ f.read(1).unpack("C").first
167
+ when "short", "int16"
168
+ f.read(2).unpack("s#{e}").first
169
+ when "ushort", "uint16"
170
+ f.read(2).unpack("S#{e}").first
171
+ when "int", "int32"
172
+ f.read(4).unpack("l#{e}").first
173
+ when "uint", "uint32"
174
+ f.read(4).unpack("L#{e}").first
175
+ when "float", "float32"
176
+ f.read(4).unpack(f32).first
177
+ when "double", "float64"
178
+ f.read(8).unpack(f64).first
179
+ when "list"
180
+ count = read_property_binary(f, index_type)
181
+ count.times.collect { read_property_binary(f, element_type) }
182
+ end
183
+ end
184
+ end
185
+
186
+ class BadFile < Exception
187
+ end
188
+ end
data/test/test_ply.rb ADDED
@@ -0,0 +1,56 @@
1
+ require "test/unit"
2
+ require "ply"
3
+
4
+ class TestPly < Test::Unit::TestCase
5
+ def test_ascii
6
+ verify_cube_file "examples/cube_ascii.ply"
7
+ end
8
+
9
+ def test_binary_big_endian
10
+ verify_cube_file "examples/cube_binary_big_endian.ply"
11
+ end
12
+
13
+ def test_binary_little_endian
14
+ verify_cube_file "examples/cube_binary_little_endian.ply"
15
+ end
16
+
17
+ def test_ascii_callback
18
+ verify_cube_callback "examples/cube_ascii.ply"
19
+ end
20
+
21
+ def test_binary_big_endian_callback
22
+ verify_cube_callback "examples/cube_binary_big_endian.ply"
23
+ end
24
+
25
+ def test_binary_little_endian_callback
26
+ verify_cube_callback "examples/cube_binary_little_endian.ply"
27
+ end
28
+
29
+ def verify_cube_file c
30
+ p = Ply::PlyFile.new c
31
+ assert p.data["vertex"].size == 8
32
+ assert p.data["face"].size == 12
33
+ assert p.data["vertex"][7]["x"] == 1.0
34
+ assert p.data["face"][11]["vertex_indices"][2] == 3
35
+ end
36
+
37
+ class PlyFileCounter < Ply::PlyFile
38
+ attr_reader :counts
39
+
40
+ def initialize f
41
+ @counts = {}
42
+ super f
43
+ end
44
+
45
+ def element_callback e, v
46
+ @counts[e[:name]] ||= 0
47
+ @counts[e[:name]] = @counts[e[:name]] + 1
48
+ end
49
+ end
50
+
51
+ def verify_cube_callback c
52
+ p = PlyFileCounter.new c
53
+ assert p.counts["vertex"] == 8
54
+ assert p.counts["face"] == 12
55
+ end
56
+ end
@@ -0,0 +1,23 @@
1
+ require "test/unit"
2
+ require "ply"
3
+
4
+ class TestPly2Ascii < Test::Unit::TestCase
5
+ def test_ply2ascii
6
+ assert system("env RUBYOPT=-I./lib bin/ply2ascii examples/cube_binary_big_endian.ply")
7
+ verts = File.new "vertices_cube_binary_big_endian.ascii"
8
+ tris = File.new "triangles_cube_binary_big_endian.ascii"
9
+ nv, nt = 0, 0
10
+ verts.lines {|l| nv = nv + 1}
11
+ tris.lines {|l| nt = nt + 1}
12
+ assert nv == 9
13
+ assert nt == 13
14
+ ensure
15
+ File.unlink "vertices_cube_binary_big_endian.ascii"
16
+ File.unlink "triangles_cube_binary_big_endian.ascii"
17
+ end
18
+
19
+ def test_help
20
+ out = `env RUBYOPT=-I./lib bin/ply2ascii`
21
+ assert out.match(/^This utility/)
22
+ end
23
+ end
metadata ADDED
@@ -0,0 +1,113 @@
1
+ --- !ruby/object:Gem::Specification
2
+ name: ply
3
+ version: !ruby/object:Gem::Version
4
+ version: '0.9'
5
+ prerelease:
6
+ platform: ruby
7
+ authors:
8
+ - Jim Wise
9
+ autorequire:
10
+ bindir: bin
11
+ cert_chain: []
12
+ date: 2013-02-05 00:00:00.000000000 Z
13
+ dependencies:
14
+ - !ruby/object:Gem::Dependency
15
+ name: rdoc
16
+ requirement: !ruby/object:Gem::Requirement
17
+ none: false
18
+ requirements:
19
+ - - ~>
20
+ - !ruby/object:Gem::Version
21
+ version: '3.10'
22
+ type: :development
23
+ prerelease: false
24
+ version_requirements: !ruby/object:Gem::Requirement
25
+ none: false
26
+ requirements:
27
+ - - ~>
28
+ - !ruby/object:Gem::Version
29
+ version: '3.10'
30
+ - !ruby/object:Gem::Dependency
31
+ name: hoe
32
+ requirement: !ruby/object:Gem::Requirement
33
+ none: false
34
+ requirements:
35
+ - - ~>
36
+ - !ruby/object:Gem::Version
37
+ version: '3.5'
38
+ type: :development
39
+ prerelease: false
40
+ version_requirements: !ruby/object:Gem::Requirement
41
+ none: false
42
+ requirements:
43
+ - - ~>
44
+ - !ruby/object:Gem::Version
45
+ version: '3.5'
46
+ description: ! "Ply is a ruby gem for reading Stanford PLY-format 3D model files.\n\nThe
47
+ PLY file format is a flexible format for storing semi-structured binary data,\nand
48
+ is often used to stored polygonalized 3D models generated with range\nscanning hardware.
49
+ \ You can find some examples of the format at the the\n{Stanford 3D Scanning Repository}[http://graphics.stanford.edu/data/3Dscanrep/].\n
50
+ \nPly provides a simple API for quick access to the data in a PLY file\n(including
51
+ examining the structure of a particular file's content), and\nan almost-as-simple
52
+ event-driven API which can be used to process extremely\nlarge ply files in a streaming
53
+ fashion, without needing to keep the full\ndataset represented in the file in memory.
54
+ \ Ply handles all three types of\nPLY files (ascii, binary-big-endian and binary-little-endian).\n\nIf
55
+ you don't have any Stanford PLY files on hand, you probably don't need\nthis gem,
56
+ but if you're curious, the PLY file format, described at\nWikipedia[http://en.wikipedia.org/wiki/PLY_(file_format)]"
57
+ email:
58
+ - jwise@draga.com
59
+ executables:
60
+ - ply2ascii
61
+ extensions: []
62
+ extra_rdoc_files:
63
+ - History.txt
64
+ - LICENSE.txt
65
+ - Manifest.txt
66
+ - README.txt
67
+ - README.rdoc
68
+ files:
69
+ - .autotest
70
+ - History.txt
71
+ - LICENSE.txt
72
+ - Manifest.txt
73
+ - README.txt
74
+ - README.rdoc
75
+ - Rakefile
76
+ - bin/ply2ascii
77
+ - examples/cube_ascii.ply
78
+ - examples/cube_binary_big_endian.ply
79
+ - examples/cube_binary_little_endian.ply
80
+ - examples/examples.rb
81
+ - lib/ply.rb
82
+ - test/test_ply.rb
83
+ - test/test_ply2ascii.rb
84
+ - .gemtest
85
+ homepage: https://github.com/jimwise/ply
86
+ licenses: []
87
+ post_install_message:
88
+ rdoc_options:
89
+ - --main
90
+ - README.txt
91
+ require_paths:
92
+ - lib
93
+ required_ruby_version: !ruby/object:Gem::Requirement
94
+ none: false
95
+ requirements:
96
+ - - ! '>='
97
+ - !ruby/object:Gem::Version
98
+ version: '0'
99
+ required_rubygems_version: !ruby/object:Gem::Requirement
100
+ none: false
101
+ requirements:
102
+ - - ! '>='
103
+ - !ruby/object:Gem::Version
104
+ version: '0'
105
+ requirements: []
106
+ rubyforge_project: ply
107
+ rubygems_version: 1.8.24
108
+ signing_key:
109
+ specification_version: 3
110
+ summary: Ply is a ruby gem for reading Stanford PLY-format 3D model files
111
+ test_files:
112
+ - test/test_ply.rb
113
+ - test/test_ply2ascii.rb