• Login
sketchucation logo sketchucation
  • Login
🤑 SketchPlus 1.3 | 44 Tools for $15 until June 20th Buy Now

PolygonMesh triangulation... ?

Scheduled Pinned Locked Moved Developers' Forum
23 Posts 6 Posters 2.3k Views 6 Watching
Loading More Posts
  • Oldest to Newest
  • Newest to Oldest
  • Most Votes
Reply
  • Reply as topic
Log in to reply
This topic has been deleted. Only users with topic management privileges can see it.
  • O Offline
    oajfh
    last edited by 24 Apr 2015, 13:10

    Greetings,

    I am a fairly new Sketchup plugin developer, and I have a couple of questions about some aspects of the PolygonMesh class which I need for what I am doing.

    Essentially, I've been trying to understand the exact behavior of this class. From what I've seen, it can create meshes -polygon meshes- from faces of a SketchUp model. It also enables one to get the vertex normals from the face (I've no interest in texturing at the moment).

    So essentially, it doesn't triangulate my faces. It just gives me the points and point indexes for each face.

    On the other hand, this old topic states that there is some triangulation involved :
    http://sketchucation.com/forums/viewtopic.php?f=180%26amp;t=40006%26amp;p=354223%26amp;hilit=triangulation#p354223

    I'm a little confused. It is an old topic, so perhaps the behavior of that class has changed, but why would the possibility to access the triangles directly from the API be removed in that case ?

    Anyway, if I'm right, and it isn't possible to obtain pretriangulated meshes, then I'm going to assume I either need to use the SDK, or do it by hand using Ruby and some triangulation algorithm.

    If I'm wrong, and it is possible to get the SketchUp triangles from within the API, then how is it I obtain some polygons with more than three indexes for the faces, and how should I proceed in order to obtain triangulated meshes ?

    Sorry if this seems obvious to some of you, but I find the API documentation a little unclear for that class, and the research I've done hasn't clarified it for me.

    Cheers.

    1 Reply Last reply Reply Quote 0
    • J Offline
      Jim
      last edited by 24 Apr 2015, 15:07

      A PolygonMesh object is not geometry like Faces and Edges are. It is simply a convenient data container for points and "polygons."

      The points are Sketchup Point3D objects.

      The "polygons" are Arrays of Integers representing the indices of the Point3d objects. The polygon arrays have 3 elements (are triangles.) A negative index means the Edge between the points is hidden (soft/smooth.)

      2015-04_256.png

      To construct the Mesh as geometry, you need to use Entities.add_faces_from_mesh or Entities.fill_from_mesh .

      Hi

      1 Reply Last reply Reply Quote 0
      • O Offline
        oajfh
        last edited by 24 Apr 2015, 15:53

        Hi.

        First off, thanks for the speedy reply !

        If I understand correctly what you mean, then my mesh creation piece of code would be what causes the problem. I am trying to have a general PolygonMesh that contains the points, indexes and face point indexes for a cube, and this is what the code looks like :

        def self.incomplete_make_mesh faces puts "Mesh creation started" mesh = Geom::PolygonMesh.new faces.each_with_index do |face,index| if face.is_a? Sketchup::Face temp_mesh = face.mesh mesh.add_polygon(temp_mesh.points) end end puts 'Polygons : '+ mesh.polygons.to_s puts 'Points : '+ mesh.points.to_s return mesh end

        When I make a component of a cube, I break it recursively down into its faces (so far I've been assuming I only have one component, I haven't yet tried compensating local component origins, but that's another matter) and then creating the polygon this way.

        Judging from your reply, I'm assuming there's something wrong with my approach. I can't debug this piece of code for now as my computer is not with me (just my Ruby scripts, ahaha), but you get three indexes per face with your piece of code, and I get more with mine.

        1 Reply Last reply Reply Quote 0
        • T Offline
          thomthom
          last edited by 24 Apr 2015, 16:50

          @oajfh said:

          Anyway, if I'm right, and it isn't possible to obtain pretriangulated meshes, then I'm going to assume I either need to use the SDK, or do it by hand using Ruby and some triangulation algorithm.

          Internally in SketchUp it's only used to obtain the triangulation of faces. But it's been somewhat abused by the Ruby API to also allow you to build new meshes (which can be turned into faces).

          
          mesh = face.mesh
          triangles = mesh.polygons
          
          

          This gives you an array where each item is also an array - with three indices. You can use these to obtain the points from the mesh.

          Or:

          
          mesh.count_polygons.times { |i|
            triangle_points = mesh.polygon_points_at(i)
          }
          
          

          @oajfh said:

          If I'm wrong, and it is possible to get the SketchUp triangles from within the API, then how is it I obtain some polygons with more than three indexes for the faces, and how should I proceed in order to obtain triangulated meshes ?

          The polygons from the mesh are always triangles. But you only get triangles per face - don't get a PolygonMesh from multiple faces. Even though you can create a PolygonMesh from scratch that will generate multiple faces. This is due to the two different roles this class has which I mentioned earlier.

          Thomas Thomassen — SketchUp Monkey & Coding addict
          List of my plugins and link to the CookieWare fund

          1 Reply Last reply Reply Quote 0
          • T Offline
            thomthom
            last edited by 24 Apr 2015, 16:53

            @oajfh said:

            Hi.

            def self.incomplete_make_mesh faces puts "Mesh creation started" mesh = Geom::PolygonMesh.new faces.each_with_index do |face,index| if face.is_a? Sketchup::Face temp_mesh = face.mesh mesh.add_polygon(temp_mesh.points) end end puts 'Polygons : '+ mesh.polygons.to_s puts 'Points : '+ mesh.points.to_s return mesh end

            When you obtain a PolygonMesh from a Face it's just a copy of the triangulation data. It doesn't relate to the face any more. adding polygons won't add more faces.
            You can add individual faces using Entities.add_face - though it's generally slower, but an easier interface. What's best for you really depend on what the final product is.

            Thomas Thomassen — SketchUp Monkey & Coding addict
            List of my plugins and link to the CookieWare fund

            1 Reply Last reply Reply Quote 0
            • D Offline
              Dan Rathbun
              last edited by 24 Apr 2015, 18:02

              @jim said:

              A PolygonMesh object is not geometry like Faces and Edges are. It is simply a convenient data container for points and "polygons."

              @oajfh: This is one aspect of the API that is confusing to new coders.

              All the classes wrapped in the Geom module, are actually NOT model geometry objects, ... they are virtual geometry helper objects.

              So the wrapping module name was really a bad choice.

              Only Sketchup::Enitity subclasses can be put in the model database, and only subclasses of it's child Sketchup::Drawingelement class, can be put into the model's Sketchup::Entities collection.

              As an example, Geom::Point3d class instances are virtual helper objects, and do not actually exist in the model. Their counterpart that does exist in the model, are instances of Sketchup::Vertex. (Edge and face objects have or share vertices, not 3D points.)

              So try to remember that the Geom module (and it's classes) are for helping to create model geometry, but are themselves virtual geometry helpers.

              I'm not here much anymore.

              1 Reply Last reply Reply Quote 0
              • O Offline
                oajfh
                last edited by 25 Apr 2015, 08:10

                Thanks for the replies everyone.

                What you said about Geom makes sense. However, the way I intend to use it is as a mesh exporter, so I would be constructing meshes from existing geometry, and wouldn't be adding the meshes to the model.

                With the way PolygonMesh works it seems that if I want a classic "points, face indexes and face point indexes" format I am going to have to reconstruct that format from the various PolygonMeshes I will have obtained.

                Won't that be slightly long in terms of number of calculations required ? Or is there a straightforward way to do it ?

                1 Reply Last reply Reply Quote 0
                • T Offline
                  thomthom
                  last edited by 25 Apr 2015, 18:21

                  If you are using it for a mesh exporter you might want to create your own helper that is better suited for your specific need.

                  Thomas Thomassen — SketchUp Monkey & Coding addict
                  List of my plugins and link to the CookieWare fund

                  1 Reply Last reply Reply Quote 0
                  • D Offline
                    Dan Rathbun
                    last edited by 25 Apr 2015, 18:53

                    Here is an example that might give some idea(s):

                    obj can be the model, a group or component definition. (Anything that has an entities method and returns a reference to it's collection of entities.)

                    <span class="syntaxdefault">  obj </span><span class="syntaxkeyword">=</span><span class="syntaxdefault"> Sketchup</span><span class="syntaxkeyword">;;</span><span class="syntaxdefault">active_model<br />  eset </span><span class="syntaxkeyword">=</span><span class="syntaxdefault"> obj</span><span class="syntaxkeyword">.</span><span class="syntaxdefault">entities<br /><br />  </span><span class="syntaxcomment"># Use hash for speed and<br /></span><span class="syntaxdefault">  </span><span class="syntaxcomment">#  because keys are unique.<br /></span><span class="syntaxdefault">  pset </span><span class="syntaxkeyword">=</span><span class="syntaxdefault"> </span><span class="syntaxkeyword">{}<br /></span><span class="syntaxdefault">  <br />  </span><span class="syntaxcomment"># Use for...in because it does not create a<br /></span><span class="syntaxdefault">  </span><span class="syntaxcomment">#  new scope with each iteration, (ie, speed!)<br /></span><span class="syntaxdefault">  for f in eset</span><span class="syntaxkeyword">.</span><span class="syntaxdefault">grep</span><span class="syntaxkeyword">(</span><span class="syntaxdefault">Sketchup</span><span class="syntaxkeyword">;;</span><span class="syntaxdefault">Face</span><span class="syntaxkeyword">)<br /></span><span class="syntaxdefault">    for v in f</span><span class="syntaxkeyword">.</span><span class="syntaxdefault">vertices<br />      pset</span><span class="syntaxkeyword">[</span><span class="syntaxdefault">v</span><span class="syntaxkeyword">.</span><span class="syntaxdefault">position</span><span class="syntaxkeyword">]=</span><span class="syntaxdefault"> v </span><span class="syntaxcomment"># use pts as hash keys<br /></span><span class="syntaxdefault">    end<br />  end<br />  <br />  pts </span><span class="syntaxkeyword">=</span><span class="syntaxdefault"> pset</span><span class="syntaxkeyword">.</span><span class="syntaxdefault">keys </span><span class="syntaxcomment"># array of unique pts<br /></span><span class="syntaxdefault">  <br />  mesh </span><span class="syntaxkeyword">=</span><span class="syntaxdefault"> Geom</span><span class="syntaxkeyword">;;</span><span class="syntaxdefault">PolygonMesh</span><span class="syntaxkeyword">.new(</span><span class="syntaxdefault"> pts</span><span class="syntaxkeyword">.</span><span class="syntaxdefault">size </span><span class="syntaxkeyword">)<br /></span><span class="syntaxdefault">  <br />  for pt in pts<br />    idx </span><span class="syntaxkeyword">=</span><span class="syntaxdefault"> mesh</span><span class="syntaxkeyword">.</span><span class="syntaxdefault">add_point</span><span class="syntaxkeyword">(</span><span class="syntaxdefault"> pt </span><span class="syntaxkeyword">)<br /></span><span class="syntaxdefault">    </span><span class="syntaxcomment"># Use idx for subsequent calls<br /></span><span class="syntaxdefault">    </span><span class="syntaxcomment"># to set_uv() and add_polygon(). <br /></span><span class="syntaxdefault">  end<br /></span>
                    

                    But eset could also be the selection set, ie:
                    Sketchup::active_model.selection

                    I'm not here much anymore.

                    1 Reply Last reply Reply Quote 0
                    • O Offline
                      oajfh
                      last edited by 26 Apr 2015, 11:08

                      Thanks for the tips (I didn't know about grep, seems like it'll save having to recurse through ComponentInstances and Groups).

                      I think I'll try and keep the normals, so I might have to proceed a bit differently, but this should set me on the right track.

                      The PolygonMesh doc is confusing though. I was interpreting it differently than what its philosophy suggests, and I'm probably not the first person to do so.

                      1 Reply Last reply Reply Quote 0
                      • D Offline
                        Dan Rathbun
                        last edited by 26 Apr 2015, 19:08

                        @oajfh said:

                        (I didn't know about grep, seems like it'll save having to ...).

                        OH, grep() comes from the API collection classes mixing in, via include(), the library module Enumerable . There are quite a number of nifty filter, query and iteration methods that get mixed in.

                        Also, remember that a class inherits all it's ancestors methods and constants all the way back to BasicObject, (or Object for the 1.8 branch of Ruby.)

                        You can see if the class [or module] has mixin modules included, and what it's ancestor classes are [if it's a class,] via:
                        Identifier.ancestors
                        (Note that the method returns an array of object references. They are not strings or symbols, but you can use to_s or inspect to get a string representation of the array, or the name() method for stringnames of individual ancestor members.

                        I'm not here much anymore.

                        1 Reply Last reply Reply Quote 0
                        • D Offline
                          Dan Rathbun
                          last edited by 26 Apr 2015, 19:54

                          @oajfh said:

                          (..., seems like it'll save having to recurse through ComponentInstances and Groups).

                          The fastest and easiest way to "walk" group and component instances, is via the model's singleton DefinitionList collection.

                          Group's are a special kind of Component, that also has a definition in the collection. You check each definition for a boolean result to group? or image?. Both false and it's a component definition. If of course group? is true than it is a definition for a group. Ignore any that are for images.

                          This is where those nifty Enumerable methods come in handy.

                          Getting the group definitions only:
                          dlist = Sketchup::active_model.definitions glist = dlist.find_all {|d| d.group? }

                          Getting the image definitions only:
                          ilist = dlist.find_all {|d| d.image? }

                          • NOTE: Modifying images via Ruby has caused SketchUp to crash, or become unstable. Team members recommend explode of an instance be the only manipulation via Ruby.

                          Getting the component definitions only:
                          dlist = Sketchup::active_model.definitions clist = dlist.find_all {|d| !d.group? && !d.image? }

                          The results are Array instances. The Array class also has Enumerable mixed in.

                          Let's say you are not interested in processing definitions that do not have any instances:
                          clist.reject! {|d| d.count_instances == 0 }
                          The count_instances method is nice (for speed,) because you need not get a reference to the definition's instances collection in order to call size.

                          For those that do have instances in the model, you can process all of them at once, by iterating it's instances collection. (To get the transformation of each one, for example.)

                          Since all instances share their definition's entities collection, ... you might as well process it once, as you go thru the DefinitionList .

                          I'm not here much anymore.

                          1 Reply Last reply Reply Quote 0
                          • A Offline
                            Anton_S
                            last edited by 26 Apr 2015, 20:50

                            I also have a method that returns PolygonMesh object of a group/component:

                            # Get group/component entities.
                            # @param [Sketchup;;Group, Sketchup;;ComponentInstance] entity
                            # @return [Sketchup;;Entities]
                            def get_entities(entity)
                              entity.is_a?(Sketchup;;ComponentInstance) ? entity.definition.entities ; entity.entities
                            end
                            
                            # Get triangular mesh of the group.
                            # @param [Sketchup;;Group, Sketchup;;ComponentInstance] entity
                            # @param [Boolean] recursive Whether to include all the child groups and
                            #   components.
                            # @param [Boolean] transform Whether to give points in global coordinates.
                            # @yield A procedure to determine whether particular child group/component
                            #   should be considered a part of the collection.
                            # @yieldparam [Sketchup;;Group, Sketchup;;ComponentInstance] entity
                            # @yieldreturn [Boolean] Pass true to consider an entity as part of the
                            #   collection. Pass false to not consider an entity as part of the
                            #   collection.
                            # @return [Geom;;PolygonMesh]
                            def get_triangular_mesh(entity, recursive = true, transform = false, &entity_validation)
                              mesh = Geom;;PolygonMesh.new
                              get_entities(entity).each { |e|
                                if e.is_a?(;;Sketchup;;Face)
                                  e.mesh.polygons.each_index{ |i|
                                    pts = e.mesh.polygon_points_at(i+1)
                                    mesh.add_polygon(pts)
                                  }
                                elsif recursive && (e.is_a?(;;Sketchup;;Group) || e.is_a?(;;Sketchup;;ComponentInstance)) && entity_validation.call(e)
                                  mesh2 = get_triangular_mesh(e, true, true, &entity_validation)
                                  mesh2.polygons.each_index { |i|
                                    mesh.add_polygon(mesh2.polygon_points_at(i+1))
                                  }
                                end
                              }
                              mesh.transform!(entity.transformation) if transform
                              mesh
                            end
                            

                            Same usage as using get_polygons_from_faces method, but instead of returning triplets it returns a PolygonMesh object containing information about all faces of group geometry. One can simply recreate a group through mesh:
                            group = Sketchup.active_model.entities[0] mesh = get_triangular_mesh(group, true, true){ |e| true } pt = Geom::Point3d.new(100,0,0) mesh.transform!(pt) # Move mesh 100 units on xaxis. Sketchup.active_model.entities.add_faces_from_mesh(mesh, 0)

                            1 Reply Last reply Reply Quote 0
                            • A Offline
                              Anton_S
                              last edited by 26 Apr 2015, 20:50

                              It is easy to obtain face triangulation using PolygonMesh. Triangular data is how SketchyPhysics and MSPhysics gather their information about group geometry. Here is a way to gather triangular data from one face:

                              triplets = []
                              face.mesh.polygons.each_index{ |i|
                                triplets << e.mesh.polygon_points_at(i+1)
                              }
                              

                              In this little snippet a triplets array contains triangles of the face. Each triangle represents an array of three Point3d objects.

                              Now, this snippet was to get triangular data from single face. But, what if you want to get triangular data from the whole group and what about its sub-groups? Well, it's not easy to write such method, but it's easy to paste it when you have it:

                              
                              # Get group/component entities.
                              # @param [Sketchup;;Group, Sketchup;;ComponentInstance] entity
                              # @return [Sketchup;;Entities]
                              def get_entities(entity)
                                entity.is_a?(Sketchup;;ComponentInstance) ? entity.definition.entities ; entity.entities
                              end
                              
                              # Get triplets from all faces of a group/component.
                              # @param [Sketchup;;Group, Sketchup;;ComponentInstance] entity
                              # @param [Boolean] recursive Whether to include all the child groups and
                              #   components.
                              # @param [Boolean] transform Whether to give points in global coordinates.
                              # @yield A procedure to determine whether particular child group/component
                              #   should be considered a part of the collection.
                              # @yieldparam [Sketchup;;Group, Sketchup;;ComponentInstance] entity
                              # @yieldreturn [Boolean] Pass true to consider an entity as part of the
                              #   collection. Pass false to not consider an entity as part of the
                              #   collection.
                              # @return [Array<Array<Geom;;Point3d>>] An array of polygons. Each polygon
                              #   represents an array of three points - a triplex.
                              def get_polygons_from_faces(entity, recursive = true, transform = false, &entity_validation)
                                triplets = []
                                get_entities(entity).each { |e|
                                  if e.is_a?(Sketchup;;Face)
                                    e.mesh.polygons.each_index{ |i|
                                      triplets << e.mesh.polygon_points_at(i+1)
                                    }
                                  elsif recursive && (e.is_a?(Sketchup;;Group) || e.is_a?(Sketchup;;ComponentInstance)) && entity_validation.call(e)
                                    triplets.concat get_polygons_from_faces(e, true, true, &entity_validation)
                                  end
                                }
                                if transform
                                  tra = entity.transformation
                                  for i in 0...triplets.size
                                    triplets[i].each { |pt| pt.transform!(tra) }
                                  end
                                end
                                triplets
                              end
                              
                              

                              Read the Yardoc generation comments above the method for parameters info. Here is an example:

                              group = Sketchup.active_model.entities[0]
                              recursive = true # Have geometry gathered from sub group too.
                              transform = false # We want our coordinates to be relative to group transformation.
                              triplets = get_polygons_from_faces(group, recursive, transform) { |e|
                                true # Have every sub-group/sub-component be considered
                              }
                              
                              1 Reply Last reply Reply Quote 0
                              • D Offline
                                Dan Rathbun
                                last edited by 26 Apr 2015, 23:09

                                This: for i in 0...triplets.size
                                will cause a "fence post error" (attempt to read past the last member.)

                                Use instead:

                                    for trip in triplets
                                      trip.each { |pt| pt.transform!(tra) }
                                    end
                                
                                

                                There is no sense in using a index when you want the members of the enumerable collection.

                                I'm not here much anymore.

                                1 Reply Last reply Reply Quote 0
                                • A Offline
                                  Anton_S
                                  last edited by 27 Apr 2015, 07:13

                                  Nope, it wont. I'm using 3 dots (...), not 2 (..). 3 dots mean n minus 1.

                                  1 Reply Last reply Reply Quote 0
                                  • D Offline
                                    Dan Rathbun
                                    last edited by 27 Apr 2015, 10:23

                                    @anton_s said:

                                    Nope, it wont. I'm using 3 dots (...), not 2 (..). 3 dots mean n minus 1.

                                    OK, but it's still a bit obscure styling. .. and each iteration you're calling the [] method when you don't need to. Just sayin'

                                    I'm not here much anymore.

                                    1 Reply Last reply Reply Quote 0
                                    • O Offline
                                      oajfh
                                      last edited by 27 Apr 2015, 12:03

                                      Thanks again for the replies !

                                      Anton, your piece of code that constructs a mesh is exactly what I was looking for. I'd tried a couple of things that weren't too different in their approach, but your code handles Transformations really cleanly.

                                      On a side note, you do need to add an extra check at the beginning of your get_triangular_mesh, otherwise it will fail as Edge objects do not have an 'entities' method.

                                      On another side note, in another approach I'd had, I used Sketchup::Set instances to store points and make sure I wasn't getting multiple objects which actually referred to the same point. Strangely enough, the Sketchup::Set does manage to get unique points, but the Object::Set does not - I guess the Sketchup::Set used something other than hashes and eql? to compare points. Yet the Sketchup API directs users to the Object::Set class since Sketchup::Set is now deprecated. Aren't there going to be compatibility issues if users create Sets of points ?

                                      Anyway, thanks again for the answers. Dan, the tips you gave me will also definitely come in handy when I start optimizing my bad Ruby code.

                                      1 Reply Last reply Reply Quote 0
                                      • A Offline
                                        Anton_S
                                        last edited by 27 Apr 2015, 13:32

                                        @dan rathbun said:

                                        OK, but it's still a bit obscure styling. .. and each iteration you're calling the [] method when you don't need to. Just sayin'

                                        I think that each loop is slower than for loop. That's why I used for loop.

                                        @oajfh said:

                                        On a side note, you do need to add an extra check at the beginning of your get_triangular_mesh, otherwise it will fail as Edge objects do not have an 'entities' method.

                                        I had these in checkers. I just removed them because these checkers were from other functions, which I did not wan't to paste because they would have probably made the code a bit confusing:

                                        # Validate object type.
                                        # @param [Object] object
                                        # @param [Object, Array<Object>] types An object or an array of objects to
                                        #   check against.
                                        # @return [void]
                                        # @raise [TypeError] if object class doesn't match with any of the specified
                                        #   types.
                                        def validate_type(object, *types)
                                          types = types.flatten
                                          return if types.empty?
                                          types.each { |type| return if object.is_a?(type) }
                                          string = case types.size
                                          when 1
                                            types[0]
                                          when 2
                                            "#{types[0]} or #{types[1]}"
                                          else
                                            "#{types[0...-1].join(', ')}, or #{types[-1]}"
                                          end
                                          raise TypeError, "Expected #{string}, but got #{object.class}.", caller
                                        end
                                        
                                        def get_triangular_mesh(entity, recursive = true, transform = false, &entity_validation)
                                          validate_type(entity, ;;Sketchup;;Group, ;;Sketchup;;ComponentInstance)
                                          mesh = Geom;;PolygonMesh.new
                                          get_entities(entity).each { |e|
                                            if e.is_a?(;;Sketchup;;Face)
                                              e.mesh.polygons.each_index{ |i|
                                                pts = e.mesh.polygon_points_at(i+1)
                                                mesh.add_polygon(pts)
                                              }
                                            elsif recursive && (e.is_a?(;;Sketchup;;Group) || e.is_a?(;;Sketchup;;ComponentInstance)) && entity_validation.call(e)
                                              mesh2 = get_triangular_mesh(e, true, true, &entity_validation)
                                              mesh2.polygons.each_index { |i|
                                                mesh.add_polygon(mesh2.polygon_points_at(i+1))
                                              }
                                            end
                                          }
                                          mesh.transform!(entity.transformation) if transform
                                          mesh
                                        end
                                        

                                        On another side, I know nothing about Sketchup::Set or Object::Set, although I do recall reading a thread mentioning them. I'm pretty sure other developers have good knowledge of them.

                                        1 Reply Last reply Reply Quote 0
                                        • fredo6F Offline
                                          fredo6
                                          last edited by 28 Apr 2015, 20:38

                                          @anton_s said:

                                          I think that each loop is slower than for loop. That's why I used for loop.

                                          Not so sure in Ruby 2.x. See this interesting benchmark .

                                          It is also said that the fastest is the while loop, when this is appropriate.

                                          Fredo

                                          1 Reply Last reply Reply Quote 0
                                          • 1
                                          • 2
                                          • 1 / 2
                                          1 / 2
                                          • First post
                                            1/23
                                            Last post
                                          Buy SketchPlus
                                          Buy SUbD
                                          Buy WrapR
                                          Buy eBook
                                          Buy Modelur
                                          Buy Vertex Tools
                                          Buy SketchCuisine
                                          Buy FormFonts

                                          Advertisement