radmesh 0.98.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- checksums.yaml +15 -0
- data/Gemfile +13 -0
- data/LICENSE +339 -0
- data/README.md +52 -0
- data/Rakefile +17 -0
- data/block.stl +86 -0
- data/lib/cadmesh.rb +192 -0
- data/lib/radmesh.rb +730 -0
- data/radmesh.gemspec +21 -0
- data/spec/radmesh_spec.rb +269 -0
- metadata +55 -0
data/radmesh.gemspec
ADDED
@@ -0,0 +1,21 @@
|
|
1
|
+
Gem::Specification.new do |s|
|
2
|
+
s.name = 'radmesh'
|
3
|
+
s.version = '0.98.1'
|
4
|
+
s.date = Time.now.strftime('%Y-%m-%d')
|
5
|
+
s.summary = 'Ruby wrapper around ADMesh'
|
6
|
+
s.description = <<-eof
|
7
|
+
ADMesh is a library for processing triangulated solid meshes.
|
8
|
+
Currently, ADMesh only reads the STL file format that is used
|
9
|
+
for rapid prototyping applications, although it can write STL,
|
10
|
+
VRML, OFF, and DXF files. Those are bindings for Ruby.
|
11
|
+
You'll need the ADMesh C library in version 0.98.x.
|
12
|
+
eof
|
13
|
+
s.authors = ['Miro Hrončok']
|
14
|
+
s.email = 'miro@hroncok.cz'
|
15
|
+
s.files = Dir.glob('{doc,lib,spec}/**/*') +
|
16
|
+
['README.md', 'LICENSE', 'Rakefile', 'Gemfile', 'block.stl', __FILE__]
|
17
|
+
s.homepage =
|
18
|
+
'https://github.com/admesh/rubygem-admesh'
|
19
|
+
s.platform = Gem::Platform::RUBY
|
20
|
+
s.license = 'GPL-2.0+'
|
21
|
+
end
|
@@ -0,0 +1,269 @@
|
|
1
|
+
gem 'minitest'
|
2
|
+
require 'minitest/autorun'
|
3
|
+
require 'radmesh'
|
4
|
+
|
5
|
+
describe RADMesh::STL do
|
6
|
+
before do
|
7
|
+
@stl = RADMesh::STL.new 'block.stl'
|
8
|
+
@axes = [:x, :y, :z]
|
9
|
+
end
|
10
|
+
|
11
|
+
describe 'block.stl' do
|
12
|
+
it 'must have 12 facets' do
|
13
|
+
@stl.stats[:number_of_facets].must_equal 12
|
14
|
+
end
|
15
|
+
it 'must have "solid admesh" header' do
|
16
|
+
@stl.stats[:header].must_equal 'solid admesh'
|
17
|
+
end
|
18
|
+
it 'must calculate volume to 1' do
|
19
|
+
@stl.calculate_volume!.stats[:volume].must_equal 1
|
20
|
+
end
|
21
|
+
it 'must have size 1 for each axis' do
|
22
|
+
@axes.each do |axis|
|
23
|
+
@stl.stats[:size][axis].must_equal 1
|
24
|
+
end
|
25
|
+
end
|
26
|
+
it 'must have max 1 for each axis' do
|
27
|
+
@axes.each do |axis|
|
28
|
+
@stl.stats[:max][axis].must_equal 1
|
29
|
+
end
|
30
|
+
end
|
31
|
+
it 'must have min 0 for each axis' do
|
32
|
+
@axes.each do |axis|
|
33
|
+
@stl.stats[:min][axis].must_equal 0
|
34
|
+
end
|
35
|
+
end
|
36
|
+
it 'must be recognized as ASCII' do
|
37
|
+
@stl.stats[:type].must_equal :ascii
|
38
|
+
end
|
39
|
+
it 'must be able to write as ASCII STL' do
|
40
|
+
@stl.write_ascii '.block_ascii.stl'
|
41
|
+
stl_ascii = RADMesh::STL.new '.block_ascii.stl'
|
42
|
+
stl_ascii.stats[:type].must_equal :ascii
|
43
|
+
end
|
44
|
+
it 'must be able to write as binary STL' do
|
45
|
+
@stl.write_binary '.block_binary.stl'
|
46
|
+
stl_binary = RADMesh::STL.new '.block_binary.stl'
|
47
|
+
stl_binary.stats[:type].must_equal :binary
|
48
|
+
end
|
49
|
+
it 'must be able to write as OBJ' do
|
50
|
+
@stl.write_obj '.block.obj'
|
51
|
+
end
|
52
|
+
it 'must be able to write as OFF' do
|
53
|
+
@stl.write_off '.block.off'
|
54
|
+
end
|
55
|
+
it 'must be able to write as DXF' do
|
56
|
+
@stl.write_dxf '.block.dxf'
|
57
|
+
end
|
58
|
+
it 'must be able to write as VRML' do
|
59
|
+
@stl.write_vrml '.block.vrml'
|
60
|
+
end
|
61
|
+
it 'must check nerby facets without blow up' do
|
62
|
+
@stl.check_facets_nearby! 0.001
|
63
|
+
end
|
64
|
+
it 'must remove unconnected facets without blow up' do
|
65
|
+
@stl.remove_unconnected_facets!
|
66
|
+
end
|
67
|
+
it 'must verify neighbors without blow up' do
|
68
|
+
skip 'this is very verbose'
|
69
|
+
@stl.verify_neighbors!
|
70
|
+
end
|
71
|
+
it 'must fill holes without blow up' do
|
72
|
+
@stl.fill_holes!
|
73
|
+
end
|
74
|
+
it 'must fix normal directions without blow up' do
|
75
|
+
@stl.fix_normal_directions!
|
76
|
+
end
|
77
|
+
it 'must fix normal values without blow up' do
|
78
|
+
@stl.fix_normal_values!
|
79
|
+
end
|
80
|
+
it 'must reverse all facets correctly' do
|
81
|
+
@stl.reverse_all_facets!
|
82
|
+
@stl.calculate_volume!.stats[:volume].must_equal(-1)
|
83
|
+
@stl.reverse_all_facets!
|
84
|
+
@stl.calculate_volume!.stats[:volume].must_equal 1
|
85
|
+
end
|
86
|
+
it 'must translate to absolute coordinates' do
|
87
|
+
@stl.translate! 10, 20, 30
|
88
|
+
@stl.stats[:min][:x].must_equal 10
|
89
|
+
@stl.stats[:min][:y].must_equal 20
|
90
|
+
@stl.stats[:min][:z].must_equal 30
|
91
|
+
end
|
92
|
+
it 'must translate to absolute coordinates array' do
|
93
|
+
@stl.translate! [0, 20, 30]
|
94
|
+
@stl.stats[:min][:x].must_equal 0
|
95
|
+
@stl.stats[:min][:y].must_equal 20
|
96
|
+
@stl.stats[:min][:z].must_equal 30
|
97
|
+
end
|
98
|
+
it 'must translate to absolute coordinates hash' do
|
99
|
+
@stl.translate! y: 20, z: 30
|
100
|
+
@stl.stats[:min][:x].must_equal 0
|
101
|
+
@stl.stats[:min][:y].must_equal 20
|
102
|
+
@stl.stats[:min][:z].must_equal 30
|
103
|
+
end
|
104
|
+
it 'must translate by relative coordinates' do
|
105
|
+
@stl.translate_relative! 0, 10, 15
|
106
|
+
@stl.translate_relative! 0, 10, 15
|
107
|
+
@stl.stats[:min][:x].must_equal 0
|
108
|
+
@stl.stats[:min][:y].must_equal 20
|
109
|
+
@stl.stats[:min][:z].must_equal 30
|
110
|
+
end
|
111
|
+
it 'must translate by relative coordinates array' do
|
112
|
+
@stl.translate_relative! [0, 10, 15]
|
113
|
+
@stl.translate_relative! [0, 10, 15]
|
114
|
+
@stl.stats[:min][:x].must_equal 0
|
115
|
+
@stl.stats[:min][:y].must_equal 20
|
116
|
+
@stl.stats[:min][:z].must_equal 30
|
117
|
+
end
|
118
|
+
it 'must translate by relative coordinates hash' do
|
119
|
+
@stl.translate_relative! y: 10, z: 15
|
120
|
+
@stl.translate_relative! y: 10, z: 15
|
121
|
+
@stl.stats[:min][:x].must_equal 0
|
122
|
+
@stl.stats[:min][:y].must_equal 20
|
123
|
+
@stl.stats[:min][:z].must_equal 30
|
124
|
+
end
|
125
|
+
it 'must scale by factor' do
|
126
|
+
@stl.scale! 50
|
127
|
+
@axes.each do |axis|
|
128
|
+
@stl.stats[:size][axis].must_equal 50
|
129
|
+
end
|
130
|
+
@stl.scale! 10
|
131
|
+
@axes.each do |axis|
|
132
|
+
@stl.stats[:size][axis].must_equal 500
|
133
|
+
end
|
134
|
+
@stl.scale!(1.0 / 500)
|
135
|
+
@axes.each do |axis|
|
136
|
+
@stl.stats[:size][axis].must_equal 1
|
137
|
+
end
|
138
|
+
end
|
139
|
+
it 'must scale by versor' do
|
140
|
+
@stl.scale_versor! 1, 10, 100
|
141
|
+
@stl.stats[:size][:x].must_equal 1
|
142
|
+
@stl.stats[:size][:y].must_equal 10
|
143
|
+
@stl.stats[:size][:z].must_equal 100
|
144
|
+
end
|
145
|
+
it 'must scale by versor as array' do
|
146
|
+
@stl.scale_versor! [1, 10, 100]
|
147
|
+
@stl.stats[:size][:x].must_equal 1
|
148
|
+
@stl.stats[:size][:y].must_equal 10
|
149
|
+
@stl.stats[:size][:z].must_equal 100
|
150
|
+
end
|
151
|
+
it 'must scale by versor as hash' do
|
152
|
+
@stl.scale_versor! y: 10, z: 100
|
153
|
+
@stl.stats[:size][:x].must_equal 1
|
154
|
+
@stl.stats[:size][:y].must_equal 10
|
155
|
+
@stl.stats[:size][:z].must_equal 100
|
156
|
+
end
|
157
|
+
it 'must rotate by each axis' do
|
158
|
+
@axes.each_with_index do |axis, idx|
|
159
|
+
@stl.rotate!(axis, 45)
|
160
|
+
check_axis = @axes[(idx + 1) % 3]
|
161
|
+
@stl.stats[:size][axis].must_be_within_epsilon 1
|
162
|
+
@stl.stats[:size][check_axis].must_be_within_epsilon 1.414
|
163
|
+
@stl.rotate!(axis, -45)
|
164
|
+
end
|
165
|
+
end
|
166
|
+
it 'must not rotate by bad axis' do
|
167
|
+
proc { @stl.rotate! :o, 45 }.must_raise ArgumentError
|
168
|
+
end
|
169
|
+
it 'must mirror by each axis pair' do
|
170
|
+
@axes.each_with_index do |axis, idx|
|
171
|
+
a1 = @axes[(idx + 1) % 3]
|
172
|
+
a2 = @axes[(idx + 2) % 3]
|
173
|
+
@stl.mirror! a1, a2 # two arguments
|
174
|
+
@stl.stats[:min][axis].must_equal(-1)
|
175
|
+
@stl.stats[:max][axis].must_equal 0
|
176
|
+
@stl.mirror! [a1, a2] # one array argument
|
177
|
+
@stl.stats[:min][axis].must_equal 0
|
178
|
+
@stl.stats[:max][axis].must_equal 1
|
179
|
+
end
|
180
|
+
end
|
181
|
+
it 'must not mirror by too many axes' do
|
182
|
+
proc { @stl.mirror! :x, :y, :z }.must_raise ArgumentError
|
183
|
+
end
|
184
|
+
it 'must not mirror by too few axes' do
|
185
|
+
proc { @stl.mirror! :x }.must_raise ArgumentError
|
186
|
+
end
|
187
|
+
it 'must not mirror by bad axis' do
|
188
|
+
proc { @stl.mirror! :x, :o }.must_raise ArgumentError
|
189
|
+
end
|
190
|
+
it 'must open merge other STLs' do
|
191
|
+
@stl.translate! 10, 10, 10
|
192
|
+
@stl.open_merge! 'block.stl'
|
193
|
+
@stl.calculate_volume!.stats[:volume].must_equal 2
|
194
|
+
end
|
195
|
+
it 'must repair without blowing up' do
|
196
|
+
@stl.repair! verbose: false
|
197
|
+
end
|
198
|
+
it 'must repair with set tolerance' do
|
199
|
+
@stl.repair! tolerance: 0.2, verbose: false
|
200
|
+
end
|
201
|
+
it 'must have size 12' do
|
202
|
+
@stl.size.must_equal 12
|
203
|
+
end
|
204
|
+
it 'must give array of size 12' do
|
205
|
+
@stl.to_a.size.must_equal 12
|
206
|
+
end
|
207
|
+
it 'must access facets on index' do
|
208
|
+
# don't rally test extra, because it might be random rubbish
|
209
|
+
@stl[5].merge(extra: ' ').must_equal normal: { x: 0, y: -1, z: -0 },
|
210
|
+
vertex: [{ x: 1, y: 0, z: 1 },
|
211
|
+
{ x: 0, y: 0, z: 0 },
|
212
|
+
{ x: 1, y: 0, z: 0 }],
|
213
|
+
extra: ' '
|
214
|
+
end
|
215
|
+
it 'must blow up when accessing facets on index out of range' do
|
216
|
+
proc { @stl[13] }.must_raise IndexError
|
217
|
+
end
|
218
|
+
it 'must walk facets' do
|
219
|
+
count = 0
|
220
|
+
@stl.each_facet do |facet|
|
221
|
+
facet.must_be_kind_of Hash
|
222
|
+
count += 1
|
223
|
+
end
|
224
|
+
count.must_equal 12
|
225
|
+
end
|
226
|
+
it 'must clone properly' do
|
227
|
+
other = @stl.clone
|
228
|
+
idx = 0
|
229
|
+
while idx < @stl.size
|
230
|
+
@stl[idx].merge(extra: ' ').must_equal other[idx].merge(extra: ' ')
|
231
|
+
idx += 1
|
232
|
+
end
|
233
|
+
other.scale! 10
|
234
|
+
other.stats[:size][:z].must_equal 10
|
235
|
+
@stl.stats[:size][:z].must_equal 1
|
236
|
+
other.stats[:header].must_equal @stl.stats[:header]
|
237
|
+
end
|
238
|
+
it 'must have clone on-demand methods' do
|
239
|
+
@stl.scale(10).stats[:size][:x].must_equal 10
|
240
|
+
@stl.stats[:size][:x].must_equal 1
|
241
|
+
|
242
|
+
@stl.mirror(:x, :y).stats[:min][:z].must_equal(-1)
|
243
|
+
@stl.stats[:min][:z].must_equal 0
|
244
|
+
|
245
|
+
@stl.translate_relative(x: 5).stats[:min][:x].must_equal 5
|
246
|
+
@stl.stats[:min][:x].must_equal 0
|
247
|
+
end
|
248
|
+
it 'must have normal string representation' do
|
249
|
+
@stl.to_s.must_equal '#<RADMesh::STL header="solid admesh">'
|
250
|
+
end
|
251
|
+
end
|
252
|
+
|
253
|
+
describe 'when opening an non-existing file' do
|
254
|
+
it 'must blow up' do
|
255
|
+
proc { RADMesh::STL.new 'bad_filename.stl' }.must_raise IOError
|
256
|
+
end
|
257
|
+
end
|
258
|
+
|
259
|
+
describe 'when having an empty STL' do
|
260
|
+
it 'must initialize fine' do
|
261
|
+
RADMesh::STL.new
|
262
|
+
end
|
263
|
+
it 'must open merge fine' do
|
264
|
+
stl = RADMesh::STL.new
|
265
|
+
stl.open_merge! 'block.stl'
|
266
|
+
stl.stats[:size][:x].must_equal 1
|
267
|
+
end
|
268
|
+
end
|
269
|
+
end
|
metadata
ADDED
@@ -0,0 +1,55 @@
|
|
1
|
+
--- !ruby/object:Gem::Specification
|
2
|
+
name: radmesh
|
3
|
+
version: !ruby/object:Gem::Version
|
4
|
+
version: 0.98.1
|
5
|
+
platform: ruby
|
6
|
+
authors:
|
7
|
+
- Miro Hrončok
|
8
|
+
autorequire:
|
9
|
+
bindir: bin
|
10
|
+
cert_chain: []
|
11
|
+
date: 2016-01-25 00:00:00.000000000 Z
|
12
|
+
dependencies: []
|
13
|
+
description: ! " ADMesh is a library for processing triangulated solid meshes.\n
|
14
|
+
\ Currently, ADMesh only reads the STL file format that is used\n for rapid
|
15
|
+
prototyping applications, although it can write STL,\n VRML, OFF, and DXF files.
|
16
|
+
Those are bindings for Ruby.\n You'll need the ADMesh C library in version 0.98.x.\n"
|
17
|
+
email: miro@hroncok.cz
|
18
|
+
executables: []
|
19
|
+
extensions: []
|
20
|
+
extra_rdoc_files: []
|
21
|
+
files:
|
22
|
+
- Gemfile
|
23
|
+
- LICENSE
|
24
|
+
- README.md
|
25
|
+
- Rakefile
|
26
|
+
- block.stl
|
27
|
+
- lib/cadmesh.rb
|
28
|
+
- lib/radmesh.rb
|
29
|
+
- radmesh.gemspec
|
30
|
+
- spec/radmesh_spec.rb
|
31
|
+
homepage: https://github.com/admesh/rubygem-admesh
|
32
|
+
licenses:
|
33
|
+
- GPL-2.0+
|
34
|
+
metadata: {}
|
35
|
+
post_install_message:
|
36
|
+
rdoc_options: []
|
37
|
+
require_paths:
|
38
|
+
- lib
|
39
|
+
required_ruby_version: !ruby/object:Gem::Requirement
|
40
|
+
requirements:
|
41
|
+
- - ! '>='
|
42
|
+
- !ruby/object:Gem::Version
|
43
|
+
version: '0'
|
44
|
+
required_rubygems_version: !ruby/object:Gem::Requirement
|
45
|
+
requirements:
|
46
|
+
- - ! '>='
|
47
|
+
- !ruby/object:Gem::Version
|
48
|
+
version: '0'
|
49
|
+
requirements: []
|
50
|
+
rubyforge_project:
|
51
|
+
rubygems_version: 2.4.5
|
52
|
+
signing_key:
|
53
|
+
specification_version: 4
|
54
|
+
summary: Ruby wrapper around ADMesh
|
55
|
+
test_files: []
|