Class: RADMesh::STL

Inherits:
Object
  • Object
show all
Defined in:
lib/radmesh/stl.rb

Overview

Class representing an STL file. It has factes and stats.

Instance Method Summary collapse

Constructor Details

#initialize(path = nil) ⇒ STL

Returns a new instance of STL.

Parameters:

  • path (String) (defaults to: nil)

    path to the STL file to load (optional)

Raises:

  • (IOError)

    when ADMesh cannot load the file

  • (NoMemoryError)

    when ADMesh cannot allocate empty STL struct



32
33
34
35
36
37
38
39
40
# File 'lib/radmesh/stl.rb', line 32

def initialize(path = nil)
  @stl_ptr = FFI::MemoryPointer.new CADMesh::STLFile, 1
  @stl_value = CADMesh::STLFile.new @stl_ptr
  init if path.nil?
  open path unless path.nil?
  ObjectSpace.define_finalizer self, self.class.finalize(@stl_ptr)
  @exact = false
  @shared = false
end

Instance Method Details

#[](idx) ⇒ Hash

Get a facet of given index

Returns:

  • (Hash)

    hash with the facet data

Raises:

  • (IndexError)


636
637
638
639
640
641
642
# File 'lib/radmesh/stl.rb', line 636

def [](idx)
  raise IndexError,
        "index #{idx} outside of STL bounds: 0..#{size - 1}" if idx >= size
  ptr = @stl_value[:facet_start].to_ptr + (idx * CADMesh::STLFacet.size)
  value = CADMesh::STLFacet.new ptr
  value.to_hash
end

#calculate_volume!STL

Note:

There is also the same method without ! working as expected. It is not in this reference guide, because it is automatically generated.

Calculate volume and save it to the stats

Returns:

  • (STL)

    returns itself



98
99
100
101
# File 'lib/radmesh/stl.rb', line 98

def calculate_volume!
  CADMesh.stl_calculate_volume(@stl_ptr)
  self
end

#check_facets_exact!STL

Note:

There is also the same method without ! working as expected. It is not in this reference guide, because it is automatically generated.

Check each facet of the mesh for its 3 neighbors.

Since each facet is a triangle, there should be exactly 3 neighboring facets for every facet in the mesh. Since the mesh defines a solid, there should be no unconnected edges in the mesh. When this option is specified, the 3 neighbors of every facet are searched for and, if found, the neighbors are added to an internal list that keeps track of the neighbors of each facet. A facet is only considered a neighbor if two of its vertices EXACTLY match two of the vertices of another facet. That means that there must be 0 difference between the x, y, and z coordinates of the two vertices of the first facet and the two vertices of the second facet.

Degenerate facets (facets with two or more vertices equal to each other) are removed during the exact check. No other changes are made to the mesh.

Returns:

  • (STL)

    returns itself



195
196
197
198
199
# File 'lib/radmesh/stl.rb', line 195

def check_facets_exact!
  CADMesh.stl_check_facets_exact(@stl_ptr)
  @exact = true
  self
end

#check_facets_nearby!(tolerance) ⇒ STL

Note:

There is also the same method without ! working as expected. It is not in this reference guide, because it is automatically generated.

Checks each unconnected facet of the mesh for facets that are almost connected but not quite.

Due to round-off errors and other factors, it is common for a mesh to have facets with neighbors that are very close but don’t match exactly. Often, this difference is only in the 8th decimal place of the vertices, but these facets will not show up as neighbors during the exact check. This option finds these nearby neighbors and it changes their vertices so that they match exactly. #check_facets_exact! should always be called before the nearby check, so only facets that remain unconnected after the exact check are candidates for the nearby check.

Parameters:

  • tolerance (Float)

    the distance that is searched for the neighboring facet

Returns:

  • (STL)

    returns itself



219
220
221
222
# File 'lib/radmesh/stl.rb', line 219

def check_facets_nearby!(tolerance)
  CADMesh.stl_check_facets_nearby(@stl_ptr, tolerance)
  self
end

#clear_error!STL

Clear the error flag on internal ADMesh’s STL structure

Only use this, if you know what you are doing.

Returns:

  • (STL)

    returns itself



66
67
68
69
# File 'lib/radmesh/stl.rb', line 66

def clear_error!
  CADMesh.stl_clear_error(@stl_ptr)
  self
end

#cloneSTL

Crete a deep copy of the object

Returns:

  • (STL)

    deep copy of the object



708
709
710
711
712
713
714
715
716
# File 'lib/radmesh/stl.rb', line 708

def clone
  c = clone_props! self.class.new
  clone_stats! c
  CADMesh.stl_reallocate(c.stl_ptr)
  clone_facets! c
  clone_neighbors! c
  c.error_control_proc(NoMemoryError, 'could not clone').call
  c
end

#each_facetEnumerator

get an enumerator for each facet

Returns:

  • (Enumerator)


647
648
649
650
651
652
653
654
# File 'lib/radmesh/stl.rb', line 647

def each_facet
  return to_enum(:each_facet) unless block_given?
  idx = 0
  while idx < size
    yield self[idx]
    idx += 1
  end
end

#error?Boolean

Checks if there is an error flag on internal ADMesh’s STL structure

Returns:

  • (Boolean)

    whether there is and error flag



57
58
59
# File 'lib/radmesh/stl.rb', line 57

def error?
  CADMesh.stl_get_error(@stl_ptr) == 1
end

#fill_holes!STL

Note:

There is also the same method without ! working as expected. It is not in this reference guide, because it is automatically generated.

Fill holes in the mesh by adding facets.

This should be called after #check_facets_exact! and #check_facets_nearby!. If there are still unconnected facets, then facets will be added to the mesh, connecting the unconnected facets, until all of the holes have been filled. This is guaranteed to completely fix all unconnected facets. However, the resulting mesh may or may not be what the user expects.

Returns:

  • (STL)

    returns itself



256
257
258
259
# File 'lib/radmesh/stl.rb', line 256

def fill_holes!
  CADMesh.stl_fill_holes(@stl_ptr)
  self
end

#fix_normal_directions!STL

Note:

There is also the same method without ! working as expected. It is not in this reference guide, because it is automatically generated.

Check and fix if necessary the directions of the facets.

This only deals with whether the vertices of all the facets are oriented clockwise or counterclockwise, it doesn’t check or modify the value of the normal vector. Every facet should have its vertices defined in a counterclockwise order when looked at from the outside of the part. This option will orient all of the vertices so that they are all facing in the same direction. However, it it possible that this option will make all of the facets facet inwards instead of outwards. The algorithm tries to get a clue of which direction is inside and outside by checking the value of the normal vector so the chance is very good that the resulting mesh will be correct. However, it doesn’t explicitly check to find which direction is inside and which is outside.

Returns:

  • (STL)

    returns itself



277
278
279
280
# File 'lib/radmesh/stl.rb', line 277

def fix_normal_directions!
  CADMesh.stl_fix_normal_directions(@stl_ptr)
  self
end

#fix_normal_values!STL

Note:

There is also the same method without ! working as expected. It is not in this reference guide, because it is automatically generated.

Checks and fixes if necessary the normal vectors of every facet.

The normal vector will point outward for a counterclockwise facet. The length of the normal vector will be 1.

Returns:

  • (STL)

    returns itself



289
290
291
292
# File 'lib/radmesh/stl.rb', line 289

def fix_normal_values!
  CADMesh.stl_fix_normal_values(@stl_ptr)
  self
end

#generate_shared_vertices!STL

Note:

There is also the same method without ! working as expected. It is not in this reference guide, because it is automatically generated.

Generates shared vertices.

Those are needed for some of the output formats. No need to call this manually.

Returns:

  • (STL)

    returns itself



314
315
316
317
318
319
# File 'lib/radmesh/stl.rb', line 314

def generate_shared_vertices!
  check_facets_exact! unless @exact
  CADMesh.stl_generate_shared_vertices(@stl_ptr)
  @shared = true
  self
end

#mirror!(*args) ⇒ STL

Note:

There is also the same method without ! working as expected. It is not in this reference guide, because it is automatically generated.

Mirror the mesh about the specified plane.

Mirroring involves reversing the sign of all of the coordinates in a particular axis. For example, to mirror a mesh about the XY plane, the signs of all of the Z coordinates in the mesh are reversed.

Parameters:

  • args (Array<Symbol>)

    array with 2 axis symbols

  • args (Symbol, Symbol)

    2 axis symbols (such as :z and :x)

Returns:

  • (STL)

    returns itself

Raises:

  • (ArgumentError)

    when the plane is invalid or the arguments could not be parsed



519
520
521
522
523
524
525
526
527
528
529
# File 'lib/radmesh/stl.rb', line 519

def mirror!(*args)
  args = args[0] if args.size == 1
  raise ArgumentError,
        "wrong number of arguments (#{args.size} for 2)" if args.size != 2
  args.sort!
  begin
    send("mirror_#{args[0]}#{args[1]}!")
  rescue
    raise ArgumentError, "invalid axis pair #{args[0]}#{args[1]}"
  end
end

#mirror_xy!STL

Note:

There is also the same method without ! working as expected. It is not in this reference guide, because it is automatically generated.

Mirror the mesh about the XY plane.

Mirroring involves reversing the sign of all of the coordinates in a particular axis. For example, to mirror a mesh about the XY plane, the signs of all of the Z coordinates in the mesh are reversed.

Returns:

  • (STL)

    returns itself



482
483
484
485
# File 'lib/radmesh/stl.rb', line 482

def mirror_xy!
  CADMesh.stl_mirror_xy(@stl_ptr)
  self
end

#mirror_xz!STL

Note:

There is also the same method without ! working as expected. It is not in this reference guide, because it is automatically generated.

Mirror the mesh about the XZ plane.

Mirroring involves reversing the sign of all of the coordinates in a particular axis. For example, to mirror a mesh about the XY plane, the signs of all of the Z coordinates in the mesh are reversed.

Returns:

  • (STL)

    returns itself



504
505
506
507
# File 'lib/radmesh/stl.rb', line 504

def mirror_xz!
  CADMesh.stl_mirror_xz(@stl_ptr)
  self
end

#mirror_yz!STL

Note:

There is also the same method without ! working as expected. It is not in this reference guide, because it is automatically generated.

Mirror the mesh about the YZ plane.

Mirroring involves reversing the sign of all of the coordinates in a particular axis. For example, to mirror a mesh about the XY plane, the signs of all of the Z coordinates in the mesh are reversed.

Returns:

  • (STL)

    returns itself



493
494
495
496
# File 'lib/radmesh/stl.rb', line 493

def mirror_yz!
  CADMesh.stl_mirror_yz(@stl_ptr)
  self
end

#open_merge!(path) ⇒ STL

Note:

There is also the same method without ! working as expected. It is not in this reference guide, because it is automatically generated.

Note:

Due to some limitations in the C ADMesh library, when the exception occurs it is no longer safe to touch the object. If you are not sure the file is readable and parsable, check before, or use the method without ! and throw the object away when necessary.

Merge the specified file with self.

No translation is done, so if, for example, a file was merged with itself, the resulting file would end up with two meshes exactly the same, occupying exactly the same space. So generally, translations need to be done to the files to be merged so that when the two meshes are merged into one, the two resulting parts are properly spaced. If you know the nature of the parts to be merged, it is possible to “nest” one part inside the other. Note, however, that no warnings will be given if one part intersects with the other.

It is possible to place one part against another, with no space in between, but you will still end up with two separately defined parts. If such a mesh was made on a rapid-prototyping machine, the result would depend on the nature of the machine. Machines that use a photopolymer would produce a single solid part because the two parts would be “bonded” during the build process. Machines that use a cutting process would yield two or more parts.

Parameters:

  • path (String)

    path to the file to merge

Returns:

  • (STL)

    returns itself

Raises:

  • (IOError)

    when the file cannot be read/parsed (makes the object unsafe!)



559
560
561
562
563
# File 'lib/radmesh/stl.rb', line 559

def open_merge!(path)
  CADMesh.stl_open_merge(@stl_ptr, path)
  error_control_proc(IOError, "Could not open #{path}").call
  self
end

#remove_unconnected_facets!STL

Note:

There is also the same method without ! working as expected. It is not in this reference guide, because it is automatically generated.

Removes facets that have 0 neighbors.

You should probably call #check_facets_nearby! before to get better results.

Returns:

  • (STL)

    returns itself



231
232
233
234
# File 'lib/radmesh/stl.rb', line 231

def remove_unconnected_facets!
  CADMesh.stl_remove_unconnected_facets(@stl_ptr)
  self
end

#repair!(opts = {}) ⇒ STL

Note:

There is also the same method without ! working as expected. It is not in this reference guide, because it is automatically generated.

Complex repair of the mesh.

Does various repairing procedures on the mesh depending on the options.

Parameters:

Returns:

  • (STL)

    returns itself

Raises:

  • (RuntimeError)

    when something went wrong internaly



617
618
619
620
621
622
623
624
# File 'lib/radmesh/stl.rb', line 617

def repair!(opts = {})
  opts = self.class.default_repair_opts.merge(opts)
  CADMesh.stl_repair(@stl_ptr, *self.class.opts_to_int_array(opts))
  error_control_proc(RuntimeError,
                     'something went wrong during repair').call
  @exact = true if self.class.exact? opts
  self
end

#reverse_all_facets!STL

Note:

There is also the same method without ! working as expected. It is not in this reference guide, because it is automatically generated.

Reverses the directions of all of the facets and normals.

If #fix_normal_directions! ended up making all of the facets facing inwards instead of outwards, then this method can be used to reverse all of the facets

Returns:

  • (STL)

    returns itself



302
303
304
305
# File 'lib/radmesh/stl.rb', line 302

def reverse_all_facets!
  CADMesh.stl_reverse_all_facets(@stl_ptr)
  self
end

#rotate!(axis, angle) ⇒ STL

Note:

There is also the same method without ! working as expected. It is not in this reference guide, because it is automatically generated.

Rotate the entire mesh about the given axis by the given number of degrees.

The rotation is counter-clockwise about the axis as seen by looking along the positive axis towards the origin.

Parameters:

  • axis (Symbol)

    :x, :y or :z

  • angle (Float)

    angle in degrees

Returns:

  • (STL)

    returns itself

Raises:

  • (ArgumentError)

    when the axis is invalid



467
468
469
470
471
# File 'lib/radmesh/stl.rb', line 467

def rotate!(axis, angle)
  send("rotate_#{axis}!", angle)
rescue
  raise ArgumentError, "invalid axis #{axis}"
end

#rotate_x!(angle) ⇒ STL

Note:

There is also the same method without ! working as expected. It is not in this reference guide, because it is automatically generated.

Rotate the entire mesh about the X axis by the given number of degrees.

The rotation is counter-clockwise about the axis as seen by looking along the positive axis towards the origin.

Parameters:

  • angle (Float)

    angle in degrees

Returns:

  • (STL)

    returns itself



428
429
430
431
# File 'lib/radmesh/stl.rb', line 428

def rotate_x!(angle)
  CADMesh.stl_rotate_x(@stl_ptr, angle)
  self
end

#rotate_y!(angle) ⇒ STL

Note:

There is also the same method without ! working as expected. It is not in this reference guide, because it is automatically generated.

Rotate the entire mesh about the Y axis by the given number of degrees.

The rotation is counter-clockwise about the axis as seen by looking along the positive axis towards the origin.

Parameters:

  • angle (Float)

    angle in degrees

Returns:

  • (STL)

    returns itself



440
441
442
443
# File 'lib/radmesh/stl.rb', line 440

def rotate_y!(angle)
  CADMesh.stl_rotate_y(@stl_ptr, angle)
  self
end

#rotate_z!(angle) ⇒ STL

Note:

There is also the same method without ! working as expected. It is not in this reference guide, because it is automatically generated.

Rotate the entire mesh about the Z axis by the given number of degrees.

The rotation is counter-clockwise about the axis as seen by looking along the positive axis towards the origin.

Parameters:

  • angle (Float)

    angle in degrees

Returns:

  • (STL)

    returns itself



452
453
454
455
# File 'lib/radmesh/stl.rb', line 452

def rotate_z!(angle)
  CADMesh.stl_rotate_z(@stl_ptr, angle)
  self
end

#scale!(factor) ⇒ STL

Note:

There is also the same method without ! working as expected. It is not in this reference guide, because it is automatically generated.

Scale the mesh by the given factor.

This multiplies all of the coordinates by the specified number. This method could be used to change the “units” (there are no units explicitly specified in an STL file) of the mesh. For example, to change a part from inches to millimeters, just use factor 25.4.

Parameters:

  • factor (Float)

    scale factor

Returns:

  • (STL)

    returns itself



393
394
395
396
# File 'lib/radmesh/stl.rb', line 393

def scale!(factor)
  CADMesh.stl_scale(@stl_ptr, factor)
  self
end

#scale_versor!(*args) ⇒ STL

Note:

There is also the same method without ! working as expected. It is not in this reference guide, because it is automatically generated.

Scale the mesh by the given versor.

This scales the mesh in different dimensions.

Parameters:

  • args (Array<Float>)

    3 items array with scale factors

  • args (Float, Float, Float)

    3 floats with scale factors

  • args (Object)

    object responding to .x, .y and .z

  • args (Hash)

    hash with :x, :y and :z (some can be omitted to use 1 as default)

Returns:

  • (STL)

    returns itself

Raises:

  • (ArgumentError)

    when the arguments cannot be parsed



410
411
412
413
414
415
416
417
# File 'lib/radmesh/stl.rb', line 410

def scale_versor!(*args)
  vec = self.class.vector_probe args, 1
  FFI::MemoryPointer.new(:float, 3) do |p|
    p.write_array_of_float(vec)
    CADMesh.stl_scale_versor(@stl_ptr, p)
  end
  self
end

#sizeFixnum

Get the number of facets

Returns:

  • (Fixnum)

    number of facets



629
630
631
# File 'lib/radmesh/stl.rb', line 629

def size
  @stl_value[:stats][:number_of_facets]
end

#statsHash

Get the statistics about the STL file

Returns:

  • (Hash)

    statistics



90
91
92
# File 'lib/radmesh/stl.rb', line 90

def stats
  @stl_value[:stats].to_hash
end

#to_aArray<Hash>

Get an array of facets

Returns:

  • (Array<Hash>)


659
660
661
# File 'lib/radmesh/stl.rb', line 659

def to_a
  each_facet.to_a
end

#to_sString

Get a String representation of STL

Returns:

  • (String)


666
667
668
# File 'lib/radmesh/stl.rb', line 666

def to_s
  "#<RADMesh::STL header=\"#{stats[:header]}\">"
end

#translate!(*args) ⇒ STL

Note:

There is also the same method without ! working as expected. It is not in this reference guide, because it is automatically generated.

Translate the mesh to the position x,y,z.

This moves the minimum x, y, and z values of the mesh to the specified position.

Parameters:

  • args (Array<Float>)

    3 items array with coordinates

  • args (Float, Float, Float)

    3 floats with coordinates

  • args (Object)

    object responding to .x, .y and .z

  • args (Hash)

    hash with :x, :y and :z (some can be omitted to use 0 as default)

Returns:

  • (STL)

    returns itself

Raises:

  • (ArgumentError)

    when the arguments cannot be parsed



359
360
361
362
363
# File 'lib/radmesh/stl.rb', line 359

def translate!(*args)
  vec = self.class.vector_probe args
  CADMesh.stl_translate(@stl_ptr, vec[0], vec[1], vec[2])
  self
end

#translate_relative!(*args) ⇒ STL

Note:

There is also the same method without ! working as expected. It is not in this reference guide, because it is automatically generated.

Translate the mesh by a vector x,y,z.

This moves the mesh relatively to it’s current position.

Parameters:

  • args (Array<Float>)

    3 items array with coordinates

  • args (Float, Float, Float)

    3 floats with coordinates

  • args (Object)

    object responding to .x, .y and .z

  • args (Hash)

    hash with :x, :y and :z (some can be omitted to use 0 as default)

Returns:

  • (STL)

    returns itself

Raises:

  • (ArgumentError)

    when the arguments cannot be parsed



377
378
379
380
381
# File 'lib/radmesh/stl.rb', line 377

def translate_relative!(*args)
  vec = self.class.vector_probe args
  CADMesh.stl_translate_relative(@stl_ptr, vec[0], vec[1], vec[2])
  self
end

#verify_neighbors!STL

TODO:

Check what does this actually do :)

Note:

There is also the same method without ! working as expected. It is not in this reference guide, because it is automatically generated.

Returns itself

Returns:

  • (STL)

    returns itself



240
241
242
243
# File 'lib/radmesh/stl.rb', line 240

def verify_neighbors!
  CADMesh.stl_verify_neighbors(@stl_ptr)
  self
end

#write_ascii(path, label = 'admesh') ⇒ STL

Save the contents of the instance to an ASCII STL file

Parameters:

  • path (String)

    path for the output file

  • label (String) (defaults to: 'admesh')

    label used internally in the output file

Returns:

  • (STL)

    returns itself

Raises:

  • (IOError)

    when ADMesh cannot save the file



109
110
111
112
113
# File 'lib/radmesh/stl.rb', line 109

def write_ascii(path, label = 'admesh')
  CADMesh.stl_write_ascii(@stl_ptr, path, label)
  error_control_proc(IOError, "Could not write to #{path}").call
  self
end

#write_binary(path, label = 'admesh') ⇒ STL

Save the contents of the instance to a binary STL file

Parameters:

  • path (String)

    path for the output file

  • label (String) (defaults to: 'admesh')

    label used internally in the output file

Returns:

  • (STL)

    returns itself

Raises:

  • (IOError)

    when ADMesh cannot save the file



121
122
123
124
125
# File 'lib/radmesh/stl.rb', line 121

def write_binary(path, label = 'admesh')
  CADMesh.stl_write_binary(@stl_ptr, path, label)
  error_control_proc(IOError, "Could not write to #{path}").call
  self
end

#write_dxf(path, label = 'admesh') ⇒ STL

Save the contents of the instance to a DXF file

Parameters:

  • path (String)

    path for the output file

  • label (String) (defaults to: 'admesh')

    label used internally in the output file

Returns:

  • (STL)

    returns itself

Raises:

  • (IOError)

    when ADMesh cannot save the file



157
158
159
160
161
# File 'lib/radmesh/stl.rb', line 157

def write_dxf(path, label = 'admesh')
  CADMesh.stl_write_dxf(@stl_ptr, path, label)
  error_control_proc(IOError, "Could not write to #{path}").call
  self
end

#write_obj(path) ⇒ STL

Save the contents of the instance to an OBJ file

Parameters:

  • path (String)

    path for the output file

Returns:

  • (STL)

    returns itself

Raises:

  • (IOError)

    when ADMesh cannot save the file



132
133
134
135
136
137
# File 'lib/radmesh/stl.rb', line 132

def write_obj(path)
  generate_shared_vertices! unless @shared
  CADMesh.stl_write_obj(@stl_ptr, path)
  error_control_proc(IOError, "Could not write to #{path}").call
  self
end

#write_off(path) ⇒ STL

Save the contents of the instance to an OFF file

Parameters:

  • path (String)

    path for the output file

Returns:

  • (STL)

    returns itself

Raises:

  • (IOError)

    when ADMesh cannot save the file



144
145
146
147
148
149
# File 'lib/radmesh/stl.rb', line 144

def write_off(path)
  generate_shared_vertices! unless @shared
  CADMesh.stl_write_off(@stl_ptr, path)
  error_control_proc(IOError, "Could not write to #{path}").call
  self
end

#write_vrml(path) ⇒ STL

Save the contents of the instance to a VRML file

Parameters:

  • path (String)

    path for the output file

Returns:

  • (STL)

    returns itself

Raises:

  • (IOError)

    when ADMesh cannot save the file



168
169
170
171
172
173
# File 'lib/radmesh/stl.rb', line 168

def write_vrml(path)
  generate_shared_vertices! unless @shared
  CADMesh.stl_write_vrml(@stl_ptr, path)
  error_control_proc(IOError, "Could not write to #{path}").call
  self
end