radmesh 0.98.1 → 0.98.2

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 CHANGED
@@ -1,15 +1,15 @@
1
1
  ---
2
2
  !binary "U0hBMQ==":
3
3
  metadata.gz: !binary |-
4
- MzQ1MmUxMTEwM2EzY2IzNzdhMjMwOGY1MzMwMDFhNzdhN2RlMTgwOQ==
4
+ NzY3MGMzMWVhMmJkMWE4YTlhNGZjOWNiYWYzMGJkYzZkMzdjMmI5Ng==
5
5
  data.tar.gz: !binary |-
6
- ZjE0MDUyMTcwNDE5YmJjNTUwNWI0ZjVhYTMyMWUzMjAwYWQ3NDBlNg==
6
+ Mjc5OTk5ZmQ4OGMzNGJiZmQzMDg1YWE3YzVmNjc1ZDQ2MTcyYWIxNQ==
7
7
  SHA512:
8
8
  metadata.gz: !binary |-
9
- NzhiYTM3NGNmZjI4N2JkZjMzZTE5YjkxZTU4MjkxNTg3OTVhMTI2MzY2NGU0
10
- Y2M5N2JhZTAwZTcxOWI2YjIyZDJhMTgxYTRjNzFkNjBiMzNmYTA5ZWFkZWQx
11
- NTBkMWJlNWJjMGU3ZGIxNWVkODEyYzQyNjBiYzdhZDVhM2Q5NTE=
9
+ ZWFlZTBjZGFhNzczZmI2MWQ4ODYxYjRjNzNhYWIzZjY3MzQ5MGRkODdiZDMy
10
+ Nzk1ZmRkZmJmZThhYWU2YmI3OTc3ZTk2NzU0MDg5ZmRjNDVhNGIwNjY2MDNi
11
+ YTNjZDc2NGE3NWMwY2RlMjI3ZTdjMTM2ZDcwMGExYzMyYWNmMWE=
12
12
  data.tar.gz: !binary |-
13
- OWQ5ZmRhZjBmZDZlMjUzOWM2OTYzMjM3ZDU2ZDU4ZjU1NjllODY0MGZiNWJm
14
- MDQ0YzUwNTg2NzJlOGMxMzUyZjhhYzBhNjhkMmY4OTU0YzcxOTRjNDdmNGU2
15
- OGY3MGY3NGJmMzllYjAxZGFhMTdkZDc2ZjA5NzE1MmU1NTdkNDM=
13
+ YTM5OGQwNWUwNzhhNWVkYWI0NTc4NjgwYzllZmVhMWVhZGNhNzVkNzA3ZmM5
14
+ OWE3NGUxODE4MTc5NmM4ZmY3NTYxZjkzMjA0YjVkNDE0ZGMwMzg5ODU4MzM4
15
+ ZDk0NGRkOGU5NjgxZGFmZTM4NjA0YmQ4ZGRjMmVjYTAzYTMyNjc=
data/README.md CHANGED
@@ -49,4 +49,5 @@ Usage
49
49
  # and save it
50
50
  stl.write_binary 'block2.stl'
51
51
 
52
- You can generate the full documentation with [yard](http://yardoc.org/).
52
+ You can generate the full documentation with [yard](http://yardoc.org/)
53
+ or see it online on [RubyDoc.info](http://www.rubydoc.info/gems/radmesh/RADMesh/STL).
data/lib/radmesh.rb CHANGED
@@ -1,730 +1,6 @@
1
- require 'cadmesh'
2
- require 'ffi'
3
-
4
- # @!macro [new] returnself
5
- # @return [STL] returns itself
6
-
7
- # @!macro [new] nobang
8
- # @note There is also the same method without ! working as expected.
9
- # It is not in this reference guide, because it is automatically
10
- # generated.
1
+ require 'radmesh/version'
2
+ require 'radmesh/stl'
11
3
 
12
4
  # High level wrapper around ADMesh
13
5
  module RADMesh
14
- # Class representing an STL file.
15
- # It has factes and stats.
16
- class STL
17
- protected
18
-
19
- attr_accessor :stl_ptr
20
- attr_accessor :stl_value
21
-
22
- private
23
-
24
- attr_accessor :exact
25
- attr_accessor :shared
26
-
27
- public
28
-
29
- # @param path [String] path to the STL file to load (optional)
30
- # @raise [IOError] when ADMesh cannot load the file
31
- # @raise [NoMemoryError] when ADMesh cannot allocate empty STL struct
32
- def initialize(path = nil)
33
- @stl_ptr = FFI::MemoryPointer.new CADMesh::STLFile, 1
34
- @stl_value = CADMesh::STLFile.new @stl_ptr
35
- init if path.nil?
36
- open path unless path.nil?
37
- ObjectSpace.define_finalizer self, self.class.finalize(@stl_ptr)
38
- @exact = false
39
- @shared = false
40
- end
41
-
42
- def init
43
- CADMesh.stl_initialize(@stl_ptr)
44
- error_control_proc(NoMemoryError, 'Could not initialize').call
45
- end
46
-
47
- def open(path)
48
- CADMesh.stl_open(@stl_ptr, path)
49
- error_control_proc(IOError, "Could not open #{path}").call
50
- end
51
-
52
- private :init, :open
53
-
54
- # Checks if there is an error flag on internal ADMesh's STL structure
55
- #
56
- # @return [Boolean] whether there is and error flag
57
- def error?
58
- CADMesh.stl_get_error(@stl_ptr) == 1
59
- end
60
-
61
- # Clear the error flag on internal ADMesh's STL structure
62
- #
63
- # Only use this, if you know what you are doing.
64
- #
65
- # @macro returnself
66
- def clear_error!
67
- CADMesh.stl_clear_error(@stl_ptr)
68
- self
69
- end
70
-
71
- def error_control_proc(exception, message)
72
- proc do
73
- if error?
74
- clear_error!
75
- fail exception, message
76
- end
77
- end
78
- end
79
-
80
- protected :error_control_proc
81
-
82
- # @!visibility private
83
- def self.finalize(ptr)
84
- proc { CADMesh.stl_close(ptr) }
85
- end
86
-
87
- # Get the statistics about the STL file
88
- #
89
- # @return [Hash] statistics
90
- def stats
91
- @stl_value[:stats].to_hash
92
- end
93
-
94
- # Calculate volume and save it to the stats
95
- #
96
- # @macro nobang
97
- # @macro returnself
98
- def calculate_volume!
99
- CADMesh.stl_calculate_volume(@stl_ptr)
100
- self
101
- end
102
-
103
- # Save the contents of the instance to an ASCII STL file
104
- #
105
- # @param path [String] path for the output file
106
- # @param label [String] label used internally in the output file
107
- # @macro returnself
108
- # @raise [IOError] when ADMesh cannot save the file
109
- def write_ascii(path, label = 'admesh')
110
- CADMesh.stl_write_ascii(@stl_ptr, path, label)
111
- error_control_proc(IOError, "Could not write to #{path}").call
112
- self
113
- end
114
-
115
- # Save the contents of the instance to a binary STL file
116
- #
117
- # @param path [String] path for the output file
118
- # @param label [String] label used internally in the output file
119
- # @macro returnself
120
- # @raise [IOError] when ADMesh cannot save the file
121
- def write_binary(path, label = 'admesh')
122
- CADMesh.stl_write_binary(@stl_ptr, path, label)
123
- error_control_proc(IOError, "Could not write to #{path}").call
124
- self
125
- end
126
-
127
- # Save the contents of the instance to an OBJ file
128
- #
129
- # @param path [String] path for the output file
130
- # @macro returnself
131
- # @raise [IOError] when ADMesh cannot save the file
132
- def write_obj(path)
133
- generate_shared_vertices! unless @shared
134
- CADMesh.stl_write_obj(@stl_ptr, path)
135
- error_control_proc(IOError, "Could not write to #{path}").call
136
- self
137
- end
138
-
139
- # Save the contents of the instance to an OFF file
140
- #
141
- # @param path [String] path for the output file
142
- # @macro returnself
143
- # @raise [IOError] when ADMesh cannot save the file
144
- def write_off(path)
145
- generate_shared_vertices! unless @shared
146
- CADMesh.stl_write_off(@stl_ptr, path)
147
- error_control_proc(IOError, "Could not write to #{path}").call
148
- self
149
- end
150
-
151
- # Save the contents of the instance to a DXF file
152
- #
153
- # @param path [String] path for the output file
154
- # @param label [String] label used internally in the output file
155
- # @macro returnself
156
- # @raise [IOError] when ADMesh cannot save the file
157
- def write_dxf(path, label = 'admesh')
158
- CADMesh.stl_write_dxf(@stl_ptr, path, label)
159
- error_control_proc(IOError, "Could not write to #{path}").call
160
- self
161
- end
162
-
163
- # Save the contents of the instance to a VRML file
164
- #
165
- # @param path [String] path for the output file
166
- # @macro returnself
167
- # @raise [IOError] when ADMesh cannot save the file
168
- def write_vrml(path)
169
- generate_shared_vertices! unless @shared
170
- CADMesh.stl_write_vrml(@stl_ptr, path)
171
- error_control_proc(IOError, "Could not write to #{path}").call
172
- self
173
- end
174
-
175
- # Check each facet of the mesh for its 3 neighbors.
176
- #
177
- # Since each facet is a triangle, there should be exactly 3
178
- # neighboring facets for every facet in the mesh. Since the
179
- # mesh defines a solid, there should be no unconnected edges
180
- # in the mesh. When this option is specified, the 3 neighbors
181
- # of every facet are searched for and, if found, the neighbors
182
- # are added to an internal list that keeps track of the neighbors
183
- # of each facet. A facet is only considered a neighbor if two of
184
- # its vertices EXACTLY match two of the vertices of another facet.
185
- # That means that there must be 0 difference between the x, y, and
186
- # z coordinates of the two vertices of the first facet and the two
187
- # vertices of the second facet.
188
- #
189
- # Degenerate facets (facets with two or more vertices equal to each
190
- # other) are removed during the exact check. No other changes are
191
- # made to the mesh.
192
- #
193
- # @macro returnself
194
- # @macro nobang
195
- def check_facets_exact!
196
- CADMesh.stl_check_facets_exact(@stl_ptr)
197
- @exact = true
198
- self
199
- end
200
-
201
- # Checks each unconnected facet of the mesh for facets that are
202
- # almost connected but not quite.
203
- #
204
- # Due to round-off errors and other
205
- # factors, it is common for a mesh to have facets with neighbors that
206
- # are very close but don't match exactly. Often, this difference is
207
- # only in the 8th decimal place of the vertices, but these facets will
208
- # not show up as neighbors during the exact check. This option finds
209
- # these nearby neighbors and it changes their vertices so that they
210
- # match exactly. {#check_facets_exact!} should always be called before
211
- # the nearby check,
212
- # so only facets that remain unconnected after the exact check are
213
- # candidates for the nearby check.
214
- #
215
- # @param tolerance [Float] the distance that is searched for the neighboring
216
- # facet
217
- # @macro returnself
218
- # @macro nobang
219
- def check_facets_nearby!(tolerance)
220
- CADMesh.stl_check_facets_nearby(@stl_ptr, tolerance)
221
- self
222
- end
223
-
224
- # Removes facets that have 0 neighbors.
225
- #
226
- # You should probably call {#check_facets_nearby!} before
227
- # to get better results.
228
- #
229
- # @macro returnself
230
- # @macro nobang
231
- def remove_unconnected_facets!
232
- CADMesh.stl_remove_unconnected_facets(@stl_ptr)
233
- self
234
- end
235
-
236
- # @todo Check what does this actually do :)
237
- #
238
- # @macro returnself
239
- # @macro nobang
240
- def verify_neighbors!
241
- CADMesh.stl_verify_neighbors(@stl_ptr)
242
- self
243
- end
244
-
245
- # Fill holes in the mesh by adding facets.
246
- #
247
- # This should be called after
248
- # {#check_facets_exact!} and {#check_facets_nearby!}.
249
- # If there are still unconnected facets, then facets will be added to the
250
- # mesh, connecting the unconnected facets, until all of the holes have
251
- # been filled. This is guaranteed to completely fix all unconnected facets.
252
- # However, the resulting mesh may or may not be what the user expects.
253
- #
254
- # @macro returnself
255
- # @macro nobang
256
- def fill_holes!
257
- CADMesh.stl_fill_holes(@stl_ptr)
258
- self
259
- end
260
-
261
- # Check and fix if necessary the directions of the facets.
262
- #
263
- # This only deals with whether the vertices of all the facets are oriented
264
- # clockwise or counterclockwise, it doesn't check or modify the value of
265
- # the normal vector. Every facet should have its vertices defined in a
266
- # counterclockwise order when looked at from the outside of the part.
267
- # This option will orient all of the vertices so that they are all facing
268
- # in the same direction. However, it it possible that this option will make
269
- # all of the facets facet inwards instead of outwards. The algorithm tries
270
- # to get a clue of which direction is inside and outside by checking the
271
- # value of the normal vector so the chance is very good that the resulting
272
- # mesh will be correct. However, it doesn't explicitly check to find which
273
- # direction is inside and which is outside.
274
- #
275
- # @macro returnself
276
- # @macro nobang
277
- def fix_normal_directions!
278
- CADMesh.stl_fix_normal_directions(@stl_ptr)
279
- self
280
- end
281
-
282
- # Checks and fixes if necessary the normal vectors of every facet.
283
- #
284
- # The normal vector will point outward for a counterclockwise facet.
285
- # The length of the normal vector will be 1.
286
- #
287
- # @macro returnself
288
- # @macro nobang
289
- def fix_normal_values!
290
- CADMesh.stl_fix_normal_values(@stl_ptr)
291
- self
292
- end
293
-
294
- # Reverses the directions of all of the facets and normals.
295
- #
296
- # If {#fix_normal_directions!} ended up making all of the facets facing
297
- # inwards instead of outwards, then this method can be used to reverse
298
- # all of the facets
299
- #
300
- # @macro returnself
301
- # @macro nobang
302
- def reverse_all_facets!
303
- CADMesh.stl_reverse_all_facets(@stl_ptr)
304
- self
305
- end
306
-
307
- # Generates shared vertices.
308
- #
309
- # Those are needed for some of the output formats.
310
- # No need to call this manually.
311
- #
312
- # @macro returnself
313
- # @macro nobang
314
- def generate_shared_vertices!
315
- check_facets_exact! unless @exact
316
- CADMesh.stl_generate_shared_vertices(@stl_ptr)
317
- @shared = true
318
- self
319
- end
320
-
321
- # @!visibility private
322
- def self.to_vec(arg, default = 0)
323
- hash = { x: default, y: default, z: default }.merge(arg)
324
- [hash[:x], hash[:y], hash[:z]]
325
- rescue
326
- begin
327
- [arg.x, arg.y, arg.z]
328
- rescue
329
- [arg[0], arg[1], arg[2]]
330
- end
331
- end
332
-
333
- # @!visibility private
334
- def self.vector_probe(args, default = 0)
335
- if args.size == 3
336
- vec = args
337
- elsif args.size == 1
338
- vec = to_vec(args[0], default)
339
- else
340
- fail ArgumentError,
341
- "wrong number of arguments (#{args.size} for 1 or 3)"
342
- end
343
- vec
344
- end
345
-
346
- # Translate the mesh to the position x,y,z.
347
- #
348
- # This moves the minimum x, y, and z values
349
- # of the mesh to the specified position.
350
- #
351
- # @param args [Array<Float>] 3 items array with coordinates
352
- # @param args [Float, Float, Float] 3 floats with coordinates
353
- # @param args [Object] object responding to .x, .y and .z
354
- # @param args [Hash] hash with :x, :y and :z (some can be omitted
355
- # to use 0 as default)
356
- # @macro returnself
357
- # @macro nobang
358
- # @raise [ArgumentError] when the arguments cannot be parsed
359
- def translate!(*args)
360
- vec = self.class.vector_probe args
361
- CADMesh.stl_translate(@stl_ptr, vec[0], vec[1], vec[2])
362
- self
363
- end
364
-
365
- # Translate the mesh by a vector x,y,z.
366
- #
367
- # This moves the mesh relatively to it's current position.
368
- #
369
- # @param args [Array<Float>] 3 items array with coordinates
370
- # @param args [Float, Float, Float] 3 floats with coordinates
371
- # @param args [Object] object responding to .x, .y and .z
372
- # @param args [Hash] hash with :x, :y and :z (some can be omitted
373
- # to use 0 as default)
374
- # @macro returnself
375
- # @macro nobang
376
- # @raise [ArgumentError] when the arguments cannot be parsed
377
- def translate_relative!(*args)
378
- vec = self.class.vector_probe args
379
- CADMesh.stl_translate_relative(@stl_ptr, vec[0], vec[1], vec[2])
380
- self
381
- end
382
-
383
- # Scale the mesh by the given factor.
384
- #
385
- # This multiplies all of the coordinates by the specified number.
386
- # This method could be used to change the "units" (there are no units
387
- # explicitly specified in an STL file) of the mesh. For example,
388
- # to change a part from inches to millimeters, just use factor 25.4.
389
- #
390
- # @param factor [Float] scale factor
391
- # @macro returnself
392
- # @macro nobang
393
- def scale!(factor)
394
- CADMesh.stl_scale(@stl_ptr, factor)
395
- self
396
- end
397
-
398
- # Scale the mesh by the given versor.
399
- #
400
- # This scales the mesh in different dimensions.
401
- #
402
- # @param args [Array<Float>] 3 items array with scale factors
403
- # @param args [Float, Float, Float] 3 floats with scale factors
404
- # @param args [Object] object responding to .x, .y and .z
405
- # @param args [Hash] hash with :x, :y and :z (some can be omitted
406
- # to use 1 as default)
407
- # @macro returnself
408
- # @macro nobang
409
- # @raise [ArgumentError] when the arguments cannot be parsed
410
- def scale_versor!(*args)
411
- vec = self.class.vector_probe args, 1
412
- FFI::MemoryPointer.new(:float, 3) do |p|
413
- p.write_array_of_float(vec)
414
- CADMesh.stl_scale_versor(@stl_ptr, p)
415
- end
416
- self
417
- end
418
-
419
- # Rotate the entire mesh about the X axis by the given number of degrees.
420
- #
421
- # @!macro [new] rotate
422
- # The rotation is counter-clockwise about the axis as
423
- # seen by looking along the positive axis towards the origin.
424
- #
425
- # @param angle [Float] angle in degrees
426
- # @macro returnself
427
- # @macro nobang
428
- def rotate_x!(angle)
429
- CADMesh.stl_rotate_x(@stl_ptr, angle)
430
- self
431
- end
432
-
433
- # Rotate the entire mesh about the Y axis by the given number of degrees.
434
- #
435
- # @macro rotate
436
- #
437
- # @param angle [Float] angle in degrees
438
- # @macro returnself
439
- # @macro nobang
440
- def rotate_y!(angle)
441
- CADMesh.stl_rotate_y(@stl_ptr, angle)
442
- self
443
- end
444
-
445
- # Rotate the entire mesh about the Z axis by the given number of degrees.
446
- #
447
- # @macro rotate
448
- #
449
- # @param angle [Float] angle in degrees
450
- # @macro returnself
451
- # @macro nobang
452
- def rotate_z!(angle)
453
- CADMesh.stl_rotate_z(@stl_ptr, angle)
454
- self
455
- end
456
-
457
- # Rotate the entire mesh about the given axis
458
- # by the given number of degrees.
459
- #
460
- # @macro rotate
461
- #
462
- # @param axis [Symbol] :x, :y or :z
463
- # @param angle [Float] angle in degrees
464
- # @macro returnself
465
- # @macro nobang
466
- # @raise [ArgumentError] when the axis is invalid
467
- def rotate!(axis, angle)
468
- send("rotate_#{axis}!", angle)
469
- rescue
470
- raise ArgumentError, "invalid axis #{axis}"
471
- end
472
-
473
- # Mirror the mesh about the XY plane.
474
- #
475
- # @!macro [new] mirror
476
- # Mirroring involves reversing the sign of all of the coordinates in a
477
- # particular axis. For example, to mirror a mesh about the XY plane,
478
- # the signs of all of the Z coordinates in the mesh are reversed.
479
- #
480
- # @macro returnself
481
- # @macro nobang
482
- def mirror_xy!
483
- CADMesh.stl_mirror_xy(@stl_ptr)
484
- self
485
- end
486
-
487
- # Mirror the mesh about the YZ plane.
488
- #
489
- # @macro mirror
490
- #
491
- # @macro returnself
492
- # @macro nobang
493
- def mirror_yz!
494
- CADMesh.stl_mirror_yz(@stl_ptr)
495
- self
496
- end
497
-
498
- # Mirror the mesh about the XZ plane.
499
- #
500
- # @macro mirror
501
- #
502
- # @macro returnself
503
- # @macro nobang
504
- def mirror_xz!
505
- CADMesh.stl_mirror_xz(@stl_ptr)
506
- self
507
- end
508
-
509
- # Mirror the mesh about the specified plane.
510
- #
511
- # @macro mirror
512
- #
513
- # @param args [Array<Symbol>] array with 2 axis symbols
514
- # @param args [Symbol, Symbol] 2 axis symbols (such as :z and :x)
515
- # @macro returnself
516
- # @macro nobang
517
- # @raise [ArgumentError] when the plane is invalid or
518
- # the arguments could not be parsed
519
- def mirror!(*args)
520
- args = args[0] if args.size == 1
521
- fail ArgumentError,
522
- "wrong number of arguments (#{args.size} for 2)" if args.size != 2
523
- args.sort!
524
- begin
525
- send("mirror_#{args[0]}#{args[1]}!")
526
- rescue
527
- raise ArgumentError, "invalid axis pair #{args[0]}#{args[1]}"
528
- end
529
- end
530
-
531
- # Merge the specified file with self.
532
- #
533
- # No translation is done, so if, for example, a file was merged with itself,
534
- # the resulting file would end up with two meshes exactly the same,
535
- # occupying exactly the same space. So generally, translations need to be
536
- # done to the files to be merged so that when the two meshes are merged
537
- # into one, the two resulting parts are properly spaced. If you know the
538
- # nature of the parts to be merged, it is possible to "nest" one part
539
- # inside the other. Note, however, that no warnings will be given if one
540
- # part intersects with the other.
541
- #
542
- # It is possible to place one part against another, with no space in
543
- # between, but you will still end up with two separately defined parts.
544
- # If such a mesh was made on a rapid-prototyping machine, the result
545
- # would depend on the nature of the machine. Machines that use a
546
- # photopolymer would produce a single solid part because the two parts
547
- # would be "bonded" during the build process. Machines that use a cutting
548
- # process would yield two or more parts.
549
- #
550
- # @param path [String] path to the file to merge
551
- # @macro returnself
552
- # @macro nobang
553
- # @raise [IOError] when the file cannot be read/parsed
554
- # (makes the object unsafe!)
555
- # @note Due to some limitations in the C ADMesh library, when the exception
556
- # occurs it is no longer safe to touch the object. If you are not
557
- # sure the file is readable and parsable, check before, or use the
558
- # method without ! and throw the object away when necessary.
559
- def open_merge!(path)
560
- CADMesh.stl_open_merge(@stl_ptr, path)
561
- error_control_proc(IOError, "Could not open #{path}").call
562
- self
563
- end
564
-
565
- # @!visibility private
566
- def self.default_repair_opts
567
- { fixall: true, exact: false, tolerance: 0, increment: 0,
568
- nearby: false, iterations: 2, remove_unconnected: false,
569
- fill_holes: false, normal_directions: false,
570
- normal_values: false, reverse_all: false, verbose: true }
571
- end
572
-
573
- # @!visibility private
574
- def self.exact?(o)
575
- o[:exact] || o[:fixall] || o[:nearby] || o[:remove_unconnected] ||
576
- o[:fill_holes] || o[:normal_directions]
577
- end
578
-
579
- # @!visibility private
580
- def self.bools_to_ints(a)
581
- a.each_with_index do |value, idx|
582
- a[idx] = 1 if value.class == TrueClass
583
- a[idx] = 0 if value.class == FalseClass
584
- end
585
- a
586
- end
587
-
588
- # @!visibility private
589
- def self.opts_to_int_array(o)
590
- bools_to_ints([o[:fixall], o[:exact], o[:tolerance] != 0, o[:tolerance],
591
- o[:increment] != 0, o[:increment], o[:nearby],
592
- o[:iterations], o[:remove_unconnected], o[:fill_holes],
593
- o[:normal_directions], o[:normal_values], o[:reverse_all],
594
- o[:verbose]])
595
- end
596
-
597
- # Complex repair of the mesh.
598
- #
599
- # Does various repairing procedures on the mesh depending on the options.
600
- #
601
- # @param opts [Hash] hash with options:
602
- # * *fixall* (true) - run all the fixes and ignore other flags
603
- # * *exact* (false) - run {#check_facets_exact!}
604
- # * *tolerance* (0) - set the tolerance level for {#check_facets_nearby!}
605
- # * *increment* (0) - increment level of tolerance for each step
606
- # * *nearby* (false) - run {#check_facets_nearby!}
607
- # * *iterations* (2) - {#check_facets_nearby!} steps count
608
- # * *remove_unconnected* (false) - run {#remove_unconnected_facets!}
609
- # * *fill_holes* (false) - run {#fill_holes!}
610
- # * *normal_directions* (false) - run {#fix_normal_directions!}
611
- # * *normal_values* (false) - run {#fix_normal_values!}
612
- # * *reverse_all* (false) - run {#reverse_all_facets!}
613
- # * *verbose* (false) - be verbose to stout
614
- # @macro returnself
615
- # @macro nobang
616
- # @raise [RuntimeError] when something went wrong internaly
617
- def repair!(opts = {})
618
- opts = self.class.default_repair_opts.merge(opts)
619
- CADMesh.stl_repair(@stl_ptr, *self.class.opts_to_int_array(opts))
620
- error_control_proc(RuntimeError,
621
- 'something went wrong during repair').call
622
- @exact = true if self.class.exact? opts
623
- self
624
- end
625
-
626
- # Get the number of facets
627
- #
628
- # @return [Fixnum] number of facets
629
- def size
630
- @stl_value[:stats][:number_of_facets]
631
- end
632
-
633
- # Get a facet of given index
634
- #
635
- # @return [Hash] hash with the facet data
636
- def [](idx)
637
- fail IndexError,
638
- "index #{idx} outside of STL bounds: 0..#{size - 1}" if idx >= size
639
- ptr = @stl_value[:facet_start].to_ptr + (idx * CADMesh::STLFacet.size)
640
- value = CADMesh::STLFacet.new ptr
641
- value.to_hash
642
- end
643
-
644
- # get an enumerator for each facet
645
- #
646
- # @return [Enumerator]
647
- def each_facet
648
- return to_enum(:each_facet) unless block_given?
649
- idx = 0
650
- while idx < size
651
- yield self[idx]
652
- idx += 1
653
- end
654
- end
655
-
656
- # Get an array of facets
657
- #
658
- # @return [Array<Hash>]
659
- def to_a
660
- each_facet.to_a
661
- end
662
-
663
- # Get a String representation of STL
664
- #
665
- # @return [String]
666
- def to_s
667
- "#<RADMesh::STL header=\"#{stats[:header]}\">"
668
- end
669
-
670
- # @!visibility private
671
- def self.copy_bulk(src, dest, len)
672
- LibC.memcpy(dest, src, len)
673
- end
674
-
675
- def clone_facets!(c)
676
- self.class.copy_bulk(@stl_value[:facet_start].to_ptr,
677
- c.stl_value[:facet_start].to_ptr,
678
- size * CADMesh::STLFacet.size)
679
- c
680
- end
681
-
682
- def clone_neighbors!(c)
683
- self.class.copy_bulk(@stl_value[:neighbors_start],
684
- c.stl_value[:neighbors_start],
685
- size * CADMesh::STLNeighbors.size)
686
- c
687
- end
688
-
689
- def clone_props!(c)
690
- [:fp, :M, :error].each do |key|
691
- c.stl_value[key] = @stl_value[key]
692
- end
693
- c
694
- end
695
-
696
- def clone_stats!(c)
697
- self.class.copy_bulk(@stl_value[:stats].to_ptr,
698
- c.stl_value[:stats].to_ptr,
699
- CADMesh::STLStats.size)
700
- c
701
- end
702
-
703
- private :clone_facets!, :clone_neighbors!, :clone_props!, :clone_stats!
704
-
705
- # Crete a deep copy of the object
706
- #
707
- # @return [STL] deep copy of the object
708
- def clone
709
- c = clone_props! self.class.new
710
- clone_stats! c
711
- CADMesh.stl_reallocate(c.stl_ptr)
712
- clone_facets! c
713
- clone_neighbors! c
714
- c.error_control_proc(NoMemoryError, 'could not clone').call
715
- c
716
- end
717
-
718
- # take (almost) all ! methods and create their clone on-demand copies
719
- instance_methods.each do |method|
720
- next unless method.to_s[-1] == '!'
721
- next if method == :!
722
- next if method == :clear_error!
723
- newmethod = proc do |*args|
724
- c = clone
725
- c.send(method, *args)
726
- end
727
- define_method(method.to_s.chomp('!').to_sym, newmethod)
728
- end
729
- end
730
6
  end