sketchucation logo sketchucation
    • Login
    1. Home
    2. Dan Rathbun
    3. Posts
    Oops, your profile's looking a bit empty! To help us tailor your experience, please fill in key details like your SketchUp version, skill level, operating system, and more. Update and save your info on your profile page today!
    ⚠️ Important | Libfredo 15.6b introduces important bugfixes for Fredo's Extensions Update
    Offline
    • Profile
    • Following 0
    • Followers 1
    • Topics 92
    • Posts 4,904
    • Groups 2

    Posts

    Recent Best Controversial
    • RE: SketchUp Update Broke the Foundation Plugin

      I would also suggest avoiding naming objects "new" as this is the standard name for the class constructor method. This could confuse the Ruby interpreter under some situations, as it must decide whether you are trying to make a method call or creating a local reference of the same name.

      The idea is to train yourself to avoid hard to find errors.

      posted in Developers' Forum
      Dan RathbunD
      Dan Rathbun
    • RE: SketchUp Update Broke the Foundation Plugin

      @medeek said:

      I've indicated the exact line where the error is happening below.

      You cannot use bbCode bold tags inside a bbCode code block.
      Try marking errors inside code using comments like:
      erroneous code # <<<<<<<<<<--------------<<<<<<<< ERROR !

      Or, bracketing:

      ` ####

      THIS DOES NOT WORK

      result = some_code()
      a = b + result

      ####`


      Regarding your NoMethodError error. The BoundingBox class does not have (nor ever did,) an instance method named volume(). So I doubt that it could work as intended on older versions.

      EDIT: However, it is possible you were relying upon a modification to the BoundingBox class made by some extension that you've not loaded in the latest version ? (Generally, you should not rely upon API modifications made by other extensions.)

      You also seem to have ignored the advice in the API docs concerning the BoundingBox#intersect method.

      You'd have to either define a singleton volume() method and attach to that single object:

      module BB_Volume
        def volume()
          height * width * depth
        end
      end
      
      bb_obj.extend(BB_Volume)
      unless bb_obj.volume == 0.0
        # do code here
      end
      
      

      OR .. just use the Geom::BoundingBox#empty? method:

      
      unless bb_obj.empty?
        # do code here
      end
      
      
      posted in Developers' Forum
      Dan RathbunD
      Dan Rathbun
    • RE: Pass array to a c/c++ ruby extension

      Never could understand SWIG much. Cryptic interface language.
      If you intended to write a library that needs interfaces in multiple scripting languages, then SWIG might lessen the workload. Or if you are wanting a Ruby interface for some library already written that has SWIG interface files, then again I can see using SWIG.

      But for an extension that will be written specifically for SketchUp, since it only uses Ruby, I cannot see much use for SWIG.

      https://en.wikipedia.org/wiki/SWIG

      http://www.swig.org/tutorial.html

      http://www.swig.org/

      posted in Developers' Forum
      Dan RathbunD
      Dan Rathbun
    • RE: Delete a list of components by their definitions.

      @tntdavid said:

      For this reason the problem is strange and requires an investigation.

      I agree. Sounds like a good learning experience for you.
      I'll let you do the investigation, and fix it.

      @tntdavid said:

      Thank you in advance for your help.

      I've given you enough help for now.

      Your turn to do some of the work. πŸ˜‰

      posted in Developers' Forum
      Dan RathbunD
      Dan Rathbun
    • RE: Multi-Point Tool

      @medeek said:

      What I would like to do is make it possible to hold down the control key or some other key while selecting the point to position the footing, thereby the allowing the user to select multiple points.

      (As said also in the other forum...)

      The convention for SketchUp is that the CTRL key acts as a toggle in and out of copy mode. (This is why the API defines the constants as COPY_MODIFIER_KEY and COPY_MODIFIER_MASK.

      Examine how the SelectionTool switches in and out of add or subtract mode, and the cursor changes to show + or - satellite icons. Or the MoveTool, ... etc.

      Also see this other thread here at SCF:
      http://sketchucation.com/forums/viewtopic.php?f=180%26amp;t=67837#p621579


      You'll need to decide how to let the user tell the tool when they are done picking points.

      Another convention is that the ESC key resets the tool to it's initial state, or cancels the current state. (See the onCancel callback.)

      The ENTER is another way, detected via a onReturn or onKeyUp callback.

      posted in Developers' Forum
      Dan RathbunD
      Dan Rathbun
    • RE: Pass array to a c/c++ ruby extension

      @anton_s said:

      Note: I did not check this code for typos, so it's up to you to fix it (if there are any).

      I see one. The Ruby initiator function should begin:

      
      void Init_some_lib() {
      // ...
      
      

      where the name of the library file is exactly "some_lib" (with whatever the platform shared library extension is, ie ".so", ".dll", ".bundle". etc.)

      posted in Developers' Forum
      Dan RathbunD
      Dan Rathbun
    • RE: Pass array to a c/c++ ruby extension

      Read this book for that kind of normal Ruby C extension stuff:

      This is the Pragmatic Programmer's Guide to Extending Ruby (for v1.9.2)
      by Dave Thomas.
      (A companion to the good ol' "Pick Axe" Pragmatic Programmer's Guide):

      http://media.pragprog.com/titles/ruby3/ext_ruby.pdf

      posted in Developers' Forum
      Dan RathbunD
      Dan Rathbun
    • RE: Delete a list of components by their definitions.
      
      # To test;
      # purge_comps('CUBE','TOTO','LOLA')
      # ... or;
      # del = ['CUBE','TOTO','LOLA']
      # purge_comps(del)
      #
      # ver; 2.0
      
      def purge_comps(*cnames)
        #
        return false if cnames.empty?
        cnames.flatten!
        #
        match = /#{cnames.join('|')}/
        #
        model=Sketchup.active_model
        # collect matching definitions
        defstogo=model.definitions.find_all{|d| d.name =~ match }
        return 0 if defstogo.empty?
        # collect materials.
        matstogo=[]
        defstogo.each{|d|
          # first used by instances
          d.instances.each{|i|
            matstogo << i.material unless matstogo.include?(i.material)
          }
          # now used by its entities
          d.entities.each{|e|
            matstogo << e.material unless matstogo.include?(e.material)
            ( matstogo << e.back_material unless matstogo.include?(e.back_material) ) if e.is_a?(Sketchup;;Face)
          }
        }
        matstogo.compact!
        matstogo.uniq!
        # now check if materials used elsewhere
        defsNOTtogo=model.definitions.find_all{|d| ! d.name =~ match }
        # collect materials.
        matsNOTtogo=[]
        defsNOTtogo.each{|d|
          # first used by instances
          d.instances.each{|i|
            matsNOTtogo << i.material unless matsNOTtogo.include?(i.material)
          }
          # now used by its entities
          d.entities.each{|e|
            matsNOTtogo << e.material unless matsNOTtogo.include?(e.material)
            ( matsNOTtogo << e.back_material unless matsNOTtogo.include?(e.back_material) ) if e.is_a?(Sketchup;;Face)
          }
        }
        # now used by model entities
        model.entities.each{|e|
          matsNOTtogo << e.material unless matsNOTtogo.include?(e.material)
          ( matsNOTtogo << e.back_material unless matsNOTtogo.include?(e.back_material) ) if e.is_a?(Sketchup;;Face)
        }
        matsNOTtogo.compact!
        matsNOTtogo.uniq!
        # now reduce matstogo as necessary...
        matstogo.clone.each{|e|
          matstogo.delete(e) if matsNOTtogo.include?(e)
        }
        #
        defsgone = defstogo.size
        matsgone = matstogo.size
        # start and operation to ensure Garbage Collection works...
        model.start_operation('Purger', true)
        # now delete the selected entities
        defstogo.each{|d| d.entities.clear! }
        # now delete the unneeded materials
        matstogo.each{|m| model.materials.remove(m) }
        # clear the arrays of references to deleted objects;
        matstogo.clear
        defstogo.clear
        # commit
        model.commit_operation
        msg = "#{defsgone} definitions purged\n#{matsgone} materials purged"
        puts msg
        UI.messagebox(msg,MB_OK)
        return defsgone
      end
      
      
      posted in Developers' Forum
      Dan RathbunD
      Dan Rathbun
    • RE: Delete a list of components by their definitions.

      @tntdavid said:

      Your method works very well and offers new benefits.

      It is TIG's method with a few extra "bells and whistles".

      @tntdavid said:

      I noticed a small problem:
      How to avoid deleting the texture of 'CUBE', if it is used elsewhere in SketchUp?

      I'll look at it today a bit and see if there is an issue.

      EDIT: Okay, YES I see the issue. The method does not check the model level entities collection for used materials (on primitive entity objects.)

      posted in Developers' Forum
      Dan RathbunD
      Dan Rathbun
    • RE: Delete a list of components by their definitions.

      @tntdavid said:

      Here is the TIG method, which works for several definitions:

      Your implementation still has a few errors.

      David, I say again, that I gave you the code for a complete corrected method above.
      It defines a method name purge_comps(*cnames)

      posted in Developers' Forum
      Dan RathbunD
      Dan Rathbun
    • RE: Delete a list of components by their definitions.

      @tntdavid said:

      @tig said:

      What made you think you need to diverge from my guidance ?

      I used a array to "match" because there will be several different definitions to delete.

      TIG is showing you how to purge ONEdefinition and it's materials.

      @tntdavid said:

      Your code works perfectly for a single definition, ...

      No it does not. because you made 3 edits that make it NOT work properly.

      CORRECTION: Actually I found 4 errors that made it not delete all the components or materials.
      The second use of the Regexp was ! d.name =~ /#{name}/ (the regexp var is match.)
      matsNOTtogo=[] was defined as matsNOYtogo=[], resulting in NameError
      The comparison of back_material was conditional upon inclusion of front material in two statements.

      @tntdavid said:

      ... what if we have "CUBE", "TOTO" and "LOLA" to delete?

      First, learn how and why TIG's code works for ONE definition, then learn how to modify it for multiple definitions.

      Then either you wrap what TIG showed you into a method, and call it 3 times from a loop:

      
      for cname in ['CUBE','TOTO','LOLA'] # WHOOPS this not needed --> do |cname|
        purge_comp(cname)
      end
      
      

      ... or you build a multi-match regular expression ( Regexp😞
      cnames = ['CUBE','TOTO','LOLA'] match = /#{cnames.join('|')}/

      Test file (SU2016):
      CubeLolaToto.skp
      "Color B04" and "Color J08" are unused.
      "Color K03", "Color M06" are used by primitive faces.
      "Material 2" is the leader color.

      So here is a method with extra quirks:

      • Returns false if array of cnames is empty.
      • Short circuits and returns 0 if no definitions are found to delete.
      • Reports both to console and message box number of definitions and materials purged.
      • Returns number of definitions deleted.
      
      # To test;
      # purge_comps('CUBE','TOTO','LOLA')
      # ... or;
      # del = ['CUBE','TOTO','LOLA']
      # purge_comps(del)
      
      def purge_comps(*cnames)
        #
        return false if cnames.empty?
        cnames.flatten!
        #
        match = /#{cnames.join('|')}/
        #
        model=Sketchup.active_model
        # collect matching definitions
        defstogo=model.definitions.find_all{|d| d.name =~ match }
        return 0 if defstogo.empty?
        # collect materials.
        matstogo=[]
        defstogo.each{|d|
          # first used by instances
          d.instances.each{|i|
            matstogo << i.material unless matstogo.include?(i.material)
          }
          # now used by its entities
          d.entities.each{|e|
            matstogo << e.material unless matstogo.include?(e.material)
            ( matstogo << e.back_material unless matstogo.include?(e.back_material) ) if e.is_a?(Sketchup;;Face)
          }
        }
        matstogo.compact!
        matstogo.uniq!
        # now check if materials used elsewhere
        defsNOTtogo=model.definitions.find_all{|d| ! d.name =~ match }
        # collect materials.
        matsNOTtogo=[]
        defsNOTtogo.each{|d|
          # first used by instances
          d.instances.each{|i|
            matsNOTtogo << i.material unless matsNOTtogo.include?(i.material)
          }
          # now used by its entities
          d.entities.each{|e|
            matsNOTtogo << e.material unless matsNOTtogo.include?(e.material)
            ( matsNOTtogo << e.back_material unless matsNOTtogo.include?(e.back_material) ) if e.is_a?(Sketchup;;Face)
          }
        }
        matsNOTtogo.compact!
        matsNOTtogo.uniq!
        # now reduce matstogo as necessary...
        matstogo.clone.each{|e|
          matstogo.delete(e) if matsNOTtogo.include?(e)
        }
        #
        defsgone = defstogo.size
        matsgone = matstogo.size
        # start and operation to ensure Garbage Collection works...
        model.start_operation('Purger', true)
        # now delete the selected entities
        defstogo.each{|d| d.entities.clear! }
        # now delete the unneeded materials
        matstogo.each{|m| model.materials.remove(m) }
        # clear the arrays of references to deleted objects;
        matstogo.clear
        defstogo.clear
        # commit
        model.commit_operation
        msg = "#{defsgone} definitions purged\n#{matsgone} materials purged"
        puts msg
        UI.messagebox(msg,MB_OK)
        return defsgone
      end
      
      

      If you absolutely must, you can call GC.start to ensure Ruby garbage collection runs.

      posted in Developers' Forum
      Dan RathbunD
      Dan Rathbun
    • RE: Delete a list of components by their definitions.

      @sdmitch said:

      You are trying to define the variable "cname" twice in the same statement. delete the do | cname |

      MY BAD! I had a bad example (above.) [ Commented out that " do | cname |" part ]

      @sdmitch said:

      As usual Dan is right. You just need to wrap TIG's code with

      ["cube","toto","lola"].each{|name|
      >   match = /#{name}/i
      > .
      > .
      > .
      > }
      

      That will actually create a separate undo operation for each entity (and it's materials) and will be much slower.

      @tntdavid said:

      I have to move on from something obvious that I can not understand.

      I gave you a complete working method at the bottom of that previous post !!!!!!

      https://forums.sketchup.com/t/ruby-learning-resources-wikilists/22861
      Get the old "Pick Axe" book and read it.

      πŸ€“

      posted in Developers' Forum
      Dan RathbunD
      Dan Rathbun
    • RE: Weird DimensionLinear behavior

      @ittayd said:

      Thanks, I'm asking about ways to interact with the text: getting, setting.

      No this is not what you asked!

      Such a simple thing (as what method's a class has) can be easily found by looking at the API dictionary.

      http://ruby.sketchup.com/Sketchup/DimensionLinear.html

      ... which is a subclass of (and therefore inherits methods from😞
      http://ruby.sketchup.com/Sketchup/Dimension.html

      ... which is a subclass of (and therefore inherits methods from😞
      http://ruby.sketchup.com/Sketchup/Drawingelement.html

      ... which is a subclass of (and therefore inherits methods from😞
      http://ruby.sketchup.com/Sketchup/Entity.html

      ... which is a subclass of (and therefore inherits methods from😞
      http://ruby-doc.org/core-2.2.4/Object.html


      You asked about dims nested inside transformed instances.

      I am not sure if the .text getter will return transformed lengths or not. Also not sure what happens when the context is open and not. So test.

      The means I showed above avoids the conversion of text ( String) into a numeric ( Length) object, and just gets the length.

      posted in Developers' Forum
      Dan RathbunD
      Dan Rathbun
    • RE: Knowing the ComponentInstance a DimensionLinear is attached

      FYI, it's been shown By ittayd that this is bugged. Thomas Thomassen has filed the bug internally.

      The method is supposed (according to the API docs) to return the instance in favor of any primitive (vertex, edge, etc.)

      See:
      https://forums.sketchup.com/t/how-can-i-tell-to-which-instance-a-dimension-is-attached/47309

      posted in Developers' Forum
      Dan RathbunD
      Dan Rathbun
    • RE: Bounds change depending on active context?

      @ittayd said:

      And, I wouldn't mind the coordinates changing if contains? would have acted sanely, always returning true to a point that is inside the group

      BoundingBoxes are always aligned with the model's axes. This means if you rotate a cuboid instance, it's bounding box will increase.

      The second thing you need to realize is that bounding boxes have an untransformed state (ie, the definition's entities bounds,) and each of their instances transformed bounds. The transformed bounds are not just translational, they can also be affected by the rotation and scaling of the instance.

      @ittayd said:

      I would expect the coordinates to never change no matter what entity is opened.

      As a coder, most of time when generating geometry, you are just working in the definition's local coordinates with the IDENTITY transform, (ie, everything is 1:1 and you're specifying geometry oriented in relation to the definition's local origin and axes.)

      But when the user double-clicks into an instance, that might be rotated or scaled (transparently they are actually editing the definition,) but perhaps not at the 1:1 scale.
      Imagine an instance that has been scaled 2x. The user would be drawing edges that are twice as long on the screen. So their needs to be some transforming back to the definition's scale and coordinates.

      Julia wrote a really good explanation of this recently (ie past few months.) Need to find this and give you the link.

      posted in Developers' Forum
      Dan RathbunD
      Dan Rathbun
    • RE: Bounds change depending on active context?

      @ittayd said:

      I think the docs do mention returning coordinates in model space. I read "model space" to mean a global standard coordinate system.

      Because SketchUp was early on designed to create models for Google Earth, it can get confusing if we use terms like other CAD systems use, ie "World Coordinates" or "Global Coordinates". GE modelers might have taken our comments to mean in the GE space.

      So, in SketchUp, each model is itself a component, that has a model axes that cannot be moved when it is opened directly. This is referenced (in all versions) by the Ruby constants ORIGIN, X_AXIS, Y_AXIS and Z_AXIS. (Do not make reassignments on these references!)

      Do not confuse this with the drawing axes (called a UCS in AutoCAD.) A user can move the drawing axes anytime and the native tools will honor it, ... but the original model axes and origin remain as they were.
      In 2016+ versions, an API Axes class was exposed so Ruby tools could react to the user changing the drawing axes.

      When the model is opened directly in order to change the model' origin or axes, you'd need to select everything and move it, thereby changing the geometry's orientation / location with regard to the model origin/axes.

      But model definitions within the model can more easily have their axes changed. If you were to insert the same model into another, it becomes a component. Right-click on the instance, and you'll see a "Change Axes" choice. If you change the instances origin/axes, it applies to the definition and all of the instances in the new parent model.
      (I suppose if you then saved this component back out and overwrote the original, then you'd be changing it's model axes in a more GUI way, and without the danger of not selecting everything that needed moving. But I've never had the need to do this.)


      So anyway,... we try to refer to model origin, model space or model axes, instead of "global" or "world", but you'll find some users still use those if they also still use (or came from many years in) traditional CAD.

      posted in Developers' Forum
      Dan RathbunD
      Dan Rathbun
    • RE: Weird DimensionLinear behavior

      @ittayd said:

      Is there a way to get all texts of the dimension instance?

      As John explains you have to iterate the definition's instances collection, and apply the scaling of each instance's transform, to the value of the length between the start and end points (vertices.)

      dim_length = defn_dim.start[1].distance(defn_dim.end[1])

      The DC extension adds the yscale, zaxis, zscalemethods to the
      Geom::Transformation class.

      @ittayd said:

      Is there a way of programmatically change one?

      NO. If the dimension is a member of a definition's entities, changing 1 changes them all.

      posted in Developers' Forum
      Dan RathbunD
      Dan Rathbun
    • RE: Bounds change depending on active context?

      Eneroth explains this somewhere here.
      Basically the API is trying to "help" you out when in edit mode by converting coordinates to model space.

      Personally I find it unhelpful, and unexpected because the docs don't explain it.

      posted in Developers' Forum
      Dan RathbunD
      Dan Rathbun
    • RE: Knowing the ComponentInstance a DimensionLinear is attached

      @ittayd said:

      I have a dimension attached to a component instance (it moves with it, changes when it is stretched, etc.).

      Using the API, dim.start[1].parent returns a ComponentDefinition. This of course doesn't tell me which instance it is attached to. Is there a way of knowing?

      Yes.

      dim.start[0]

      Test it. If it is nil the dimension is unassociated.

      dim.start[0].nil?

      or simply in Ruby:
      ` inst_start = dim.start[0]
      if inst_start

      use it

      else

      assume it is nil as nil evals as FALSE in Ruby

      end`

      You can also use Ruby's multiple assignment:
      ent, point = dim.start

      Ref: http://ruby-doc.org/core-2.0.0/doc/syntax/assignment_rdoc.html

      @ittayd said:

      Or is it a bug?

      Sounds like a brain bug. πŸ˜‰ (Reference to "Starship Troopers II".)

      As the API doc is quite clear that the first member of the returned array is supposed to be a reference to the entity.

      posted in Developers' Forum
      Dan RathbunD
      Dan Rathbun
    • RE: Dynamic Component DCFunctionsV1 within in Module

      @michaelwhksg said:

      Like you mentioned previously, the EWH team initially rejected the idea of having the DCFunctionsV1 approach. After I explained the rationale of using it, they finally gave the approval.

      Remember I said this ?

      @dan rathbun said:

      (3) Yes, please use a unique method name, as your implementation of the atan2 method (specifically the parameter list) is not how the rest of the world would define it for use by ALL coders.

      I really hope you used a unique function name and not atan2 as you had previously defined it early on.

      posted in Developers' Forum
      Dan RathbunD
      Dan Rathbun
    • 1 / 1