sketchucation logo sketchucation
    • Login
    ℹ️ Licensed Extensions | FredoBatch, ElevationProfile, FredoSketch, LayOps, MatSim and Pic2Shape will require license from Sept 1st More Info

    Save coordinates in an array

    Scheduled Pinned Locked Moved Developers' Forum
    17 Posts 3 Posters 647 Views 3 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.
    • M Offline
      michaelazer
      last edited by

      Hi all,

      I'm building a tool that should clone the line tool, but it should also save clicked coordinates in an array.
      So in other words, the user would click some points on the screen, a line should be drawn connecting the points he/she clicked, but also save these points in an array.
      I'm pretty sure it's easy, but I'm struggling with it.

      Thanks in advance,
      Mike

      1 Reply Last reply Reply Quote 0
      • TIGT Offline
        TIG Moderator
        last edited by

        As the tool starts [before the first point is clicked - probably in one of either the initialize or activate or reset methods [if you have that] which will setup an empty array to contain the points.
        @pts=[]

        When the user clicks each point to add new geometry simply use
        @pts << @pt
        to add the point to the array... assuming that this point is referenced by @pt...

        Now on completion you have an array of points as clicked by the user, which is called @pts - now you can do whatever you want with the list...

        A 'plain' reference - e.g. pts - is only seen in its method [ def] [or 'block' (e.g. pts.each{|pt|puts pt} unless it was declared beforehand outside of that block - e.g. pt=nil;pts.each{|pt|puts pt};puts pt which would leave pt as the last item in the array pts after the array has been printed to the Ruby Console]).
        A ' @pts' reference can be used across methods within that instance of the class - i.e. the one use of the tool...
        While ' @@pts' references are remembered with the class - in this case to remember ALL points picked while the use of your tool [perhaps repeatedly] during that Sketchup session, you'd declare @@pts=[] directly inside the body of the class, i.e. outside of any of the methods, but you can still push elements into it inside any of the class methods, using @@pts<<@pt etc...

        TIG

        1 Reply Last reply Reply Quote 0
        • M Offline
          michaelazer
          last edited by

          Hi TIG,
          Thanks for your reply, but I'm still quite confused.

          mod = Sketchup.active_model # Open model
          ent = mod.entities # All entities in model
          sel = mod.selection # Current selection
          class MyTool
          	def activate
          		puts "Your tool has been activated."
          		@pts=[]
              end
              def onLButtonDown(flag, x, y, view)
          		ip1 = view.inputpoint x,y
          		@pt = ip1.position
          		@pts << @pt
          	end
          end
          
          my_tool = MyTool.new
          Sketchup.active_model.select_tool my_tool
          
          onLButtonDown(1,100,100,Sketchup.active_model.active_view)
          pts.each{|pt|puts pt}
          

          I get this error:
          Error: #<NoMethodError: undefined method `onLButtonDown' for main:Object>
          Sorry I'm still new.

          1 Reply Last reply Reply Quote 0
          • TIGT Offline
            TIG Moderator
            last edited by

            The simpler
            Sketchup.active_model.select_tool(MyTool.new)
            needs to go into a menu-item after the class code - otherwise it just runs at startup !
            There are many examples of how to add a command to the Plugins menu...
            OR temporarily while testing just set it to mytool=Sketchup.active_model.select_tool(MyTool.new) outside of the class and run it from the Ruby Console by typing 'mytool'...

            Please change it's name to something that's more 'yours' too ! e.g. 'LazerTool' and 'lazertool' ???

            You need to add a def initialize() method, including the @pts=[] otherwise it's zeroed every time you click by the activate method.

            Your last lines of code makes little sense... If you want to see the contents of the array use something INSIDE the tool's class.
            onLButtonDown(1,100,100,Sketchup.active_model.active_view) pts.each{|pt|puts pt} is wrong on so many levels πŸ˜’
            I assume you want the tool to stop and print a list of points when the user does something specific.
            In the def onLButtonDown() method you could add a test after the @pt is set... like @pts.each{|pt|puts pt} if @pt==[1,100,100]
            Then the tool continues... which probably isn't what you want ??
            Since it will be difficult for a user to pick an exact point... why not report, then exit on a new double-click method [made within the tool's class like the other tool-methods]

            def onLButtonDoubleClick(flags, x, y, view)
              UI.messagebox("Done!") ### optional !
              @pts.each{|pt|puts pt}
              Sketchup.send_action("selectSelectionTool;")
              return nil
            end
            

            Incidentally don't define the mod etc outside of the class, if you need them define them inside the activate() and use @mod so it's accessible in all def methods...

            TIG

            1 Reply Last reply Reply Quote 0
            • M Offline
              michaelazer
              last edited by

              Hi TIG, I really can't thank you enough, but probably I have major issues in understanding how the program is performing.
              I got the syntax onLButtonDown(flag,x,y,view) from the API, which I don't get, by the way.

              Why am I entering an X and a Y, if the idea behind the function is get the X and Y?

              Also, I don't understand what the flag is?

              And lastly, why do I need the view? do I get relative coordinates? so if I move the view I may get a duplicate point?
              Sorry TIG for the hassle.

              1 Reply Last reply Reply Quote 0
              • TIGT Offline
                TIG Moderator
                last edited by

                All of the extra 'Tool' methods are automatically added by Sketchup to your code IF you add the special tool methods to your class and launch it as a 'tool'...
                Read the Tool API https://developers.google.com/sketchup/docs/ourdoc/tool
                These methods expect to receive various arguments like (flag,x,y,view) [this varies with the method].
                These are passed automatically by Sketchup - YOU do not do anything.
                The method:
                ` def onLButtonDown(flag,x,y,view)

                do some stuff here!

                endis called if the user presses the left mouse-button - you don't need to worry about how it gets the arguments passed to it - BUT you can use them in your code... Code like... ip1 = view.inputpoint(x,y)
                @pt = ip1.position which make use of the view's points clicked on screen [x/y] - with the view's ' .inputpoint' - there are several view methods you can call - see the API [https://developers.google.com/sketchup/docs/ourdoc/view](https://developers.google.com/sketchup/docs/ourdoc/view) So from that you can get the Point3d etc using an inputpoint method like ' .position`' ...

                TIG

                1 Reply Last reply Reply Quote 0
                • M Offline
                  michaelazer
                  last edited by

                  I think I got it πŸ˜„ thank you so much TIG.
                  I really appreciate your help.

                  1 Reply Last reply Reply Quote 0
                  • Dan RathbunD Offline
                    Dan Rathbun
                    last edited by

                    @michaelazer said:

                    I got the syntax onLButtonDown(flag,x,y,view) from the API, which I don't get, by the way.
                    ... Why am I entering an X and a Y, ...

                    Let me explain it specifically (and technically.)

                    All the methods listed in the API dictionary, for the Tool class, are a special kind of method, termed a "callback".

                    A Tool class object is an event-driven object, in this case, driven by what the user does.
                    When the user starts the tool, (by clicking a menu item or a toolbar button,) the SketchUp application "calls back" into the tool instance, specifically calling the activate() callback.

                    YOU are expected to overrride (ie, redefine,) this callback in YOUR custom Tool class. (Pretending here that the API Tool class actually does or hopefully will have in the future, a true superclass; .. because it really should have one. Anyway... it helps to understand basic Ruby subclassing. Most times in Ruby your custom classes will have a superclass that they inherit methods from.)

                    So whatever you define in the activate() callback runs. You are free to define normal instance methods, within your class, (often coders write a reset() method that gets called from the onCancel callback, but you can also call it from within activate() and initialize(), and/or whichever other method or callback you need to.)

                    As TIG said, arguments for callbacks, are passed by the SketchUp application engine.

                    In event-driven code like Tool class instances, you never know which callback will get called in what order. It all depends on what the user clicks on, or what key they press. So the code does not run sequentially.
                    This is why you must store references in instance variables, that ALL of the instance methods and callbacks can access. It is common to reset (or init,) these variables in a reset() method.

                    Now Ruby itself, also has callback methods that are called automatically. The most important is the private instance method initialize(), which is automatically called by the public class constructor method new(), after it has created the new instance object. The new() method passes all of the arguments IT got, into the initialize() callback, of the newly created instance.
                    !! Since this is Ruby Core stuff, you will not see it listed in the API dictionary. Please read the "guide" I wrote, click link in my signature tagline; follow the steps, and get the standard Ruby reference CHM and other resources, cheatsheets etc.

                    But most of all.. watch out! Your about to become addicted to Ruby. πŸ˜›

                    P.S.: Have you read the "Plugins/Examples/linetool.rb" ??

                    I'm not here much anymore.

                    1 Reply Last reply Reply Quote 0
                    • Dan RathbunD Offline
                      Dan Rathbun
                      last edited by

                      @tig said:

                      Incidentally don't define the mod etc outside of the class, if you need them, define them inside the activate() and use @mod so it's accessible in all def methods...

                      This is important, not only for access from all instance methods,...

                      .. but because SketchUp can (via user control,) close, open, and on the Mac, switch to another model. Each model maintains it's OWN toolstack. When your tool instance gets activated, and the activate() callback gets called, within it, you need to set these model specific references to the model ( @mod), it's selection or any of this model's collection objects THIS tool instance will access.

                      SO.. this is also why TIG suggested creating a new tool instance, each time the user selects your tool from the menu or a toolbar. (Even though this is considered bad form in Ruby, ... usually best practice is to create a var to hold a reference to the instance, while it's in use, and then release it, by @var = nil, so Garbage Collection can dispose of the now unneeded object. BUT... it is easier, and simplier [makes more readable code, etc.] and most of all less error-prone in SketchUp embedded Ruby, if you just let each model's toolstack hold the reference to it's own instance of your tool. When the tool is no longer in use, the toolstack will no longer reference it, and Garbage Collection will clean it up automatically.)

                      To understand why this is important... think about a user on the Mac, with 2 models open.

                      He is in "model one", activates your tool (which creates "instance 1" of your tool, referenced by "model one's" Tools collection (aka toolstack,)
                      He clicks a point and begins drawing a line...

                      But then he remembers a line he forgot to draw in "model two", and decides to do that first, before he forgets again...
                      So he switches to the document window for "model two" (which causes a change in the active model,) and he then activates your line tool, for "model two"...

                      At this point AN instance of your tool is the active tool in both model's toolstack, but "model two's" toolstack should reference "instance two" of your tool.

                      It is important that each model's toolstack, have it's own instance of your tool. Because the tool state (tool step, whatever you call it,) may differ between each model. At this point in time model two's tools state would be 0, as no starting point has yet been picked, but the tool instance in "model one" is at tool state 1 as the user has already picked the start point.

                      Your Tool class should allow the user to draw the line he forgot in "model two", then switch back to "model one" where it's OWN instance of your tool is still the active tool, and I believe the SketchUp engine will automatically call the resume() callback, which gives you the opportunity to redraw the view. Anyway the user should be able to continue where he was, with the first point already chosen.

                      All this will be automatic, if you use TIG's suggestion in your menu item (or toolbar,) command blocks:
                      Sketchup.active_model.select_tool(Mazer::LineTool.new)
                      and have the references to the model, and it's collections, as instance vars created in the initialize() method.

                      I'm not here much anymore.

                      1 Reply Last reply Reply Quote 0
                      • M Offline
                        michaelazer
                        last edited by

                        Thanks Dan, I think I actually got addicted to Ruby πŸ˜„
                        If I'm not asking for too much, I have a request and a question.
                        Request: I theoretically understood both your comments, but can you write me a small piece of code or point me to a link to give me a practical example of how to use the resume(), activate(),new(), and initialize()? I got confused on where to put the code that I want to run when I click the Left Mouse button.
                        Question: I don't understand what the @mod is. Is this a variable that Sketchup understands alone or do I need to declare it?
                        Sorry, but after I thought I understood, I couldn't apply what I understood.
                        Thanks a lot

                        1 Reply Last reply Reply Quote 0
                        • Dan RathbunD Offline
                          Dan Rathbun
                          last edited by

                          @michaelazer said:

                          I don't understand what the @mod is.

                          It is an instance variable that YOU use within your custom class definition.

                          Ruby does not make you define @name instance variables, before use.
                          Usually they are initialized to some value, within a class' initialize() method.
                          But a SU Tool class does not (usually,) do anything when it is initialized, only after it becomes active, and the activate() callback gets called. So, it's kind of take your pick.

                          But it DOES make you define @@name module/class variables and NAME constants before they are accessed.

                          Using the word 'declare' is un-ruby, because Ruby does not really have variables, they are actually object references. (But somebody decided they would scare off old BASIC programmers if it got out that Ruby had no variables.) Anyway, do not worry about this for now, except to know that Ruby variables/references can point at any class of object when initialized, and at anytime later, can be re-assigned to point at an object of a completely different, unrelated class.
                          Realize that = is the reference assignment operator, not equals (which is == in Ruby.)

                          I'm not here much anymore.

                          1 Reply Last reply Reply Quote 0
                          • Dan RathbunD Offline
                            Dan Rathbun
                            last edited by

                            Links... Others and I have written and sampled our fingers raw.

                            Check the [ Code Snippets ] indexes. It is one of the "Sticky" topics that is always at the top of the first forum page.

                            Then use the forum search to search on "Tool" or "Tool +activate +resume" etc.

                            About new(), don't worry about, because at this stage your not ready to override a class constructor. And you do not need to. Only know that after it does it's creation work, it calls initialize() in the newly created instance, passing along any arguments it itself was passed.

                            So if you wish to pass something into your instance, via new(), then define initialize() with arguments in it's parameter list.

                            class Bear
                              def initialize(myname,father)
                                @myname = myname
                                @father = father
                              end
                            end
                            
                            booboo = Bear.new('BooBoo','YogiBear')
                            

                            And in case you are wondering, method arguments ARE local variables, whose scope is local to the method.
                            But these 'locals' will "go out of scope" when the method ends, and the objects they point at, will become unreferenced.
                            So.. we create instance variable references (whose scope is the entire instance,) that are assigned to point at those same objects; .. and after the method ends, these objects are still referenced, and will not be Garbage Collected.(ie, GC'd, as we say.)

                            So, this:
                            @myname = myname
                            means:
                            The reference @myname (shall be created if not,) and assigned to point at the object, that myname is pointing at.

                            I'm not here much anymore.

                            1 Reply Last reply Reply Quote 0
                            • M Offline
                              michaelazer
                              last edited by

                              Really, Dan, I can't thank you enough. I think I now have my feet on the right path.
                              I believe I will be a regular here on this forum. Again, many thanks to you and TIG.

                              1 Reply Last reply Reply Quote 0
                              • M Offline
                                michaelazer
                                last edited by

                                After I got your comments I worked on my program, I wrote the following code:

                                class MyTool
                                	
                                		# Initialization
                                	def activate
                                		@input = Sketchup;;InputPoint.new
                                		@pts[]
                                	end
                                
                                	# Respond to left click
                                	def onLButtonDown flags, x, y, view
                                		@input.pick view, x, y
                                		sketchup;;set_status_text "Left click ", SB_VCB_LABEL
                                		pos = @input.position
                                		@pts << pos
                                		str = "%.2f, %.2f,%.2f" % [pos.x, pos.y, pos.z]
                                		Sketchup;;set_status_text str, SB_VCB_VALUE
                                	end
                                	
                                end
                                
                                # Create Command object
                                simple_cmd = UI;;Command.new("Click") {
                                	Sketchup.active_model.select_tool MyTool.new
                                }
                                	
                                #Add command to tools menu
                                tool_menu = UI.menu "Tools"
                                tool_menu.add_separator
                                tool_menu.add_item simple_cmd	
                                

                                But still something is not right! I'm getting the following error:

                                @unknownuser said:

                                Error: #<NoMethodError: undefined method <<' for nil:NilClass> C:/Program Files/Google/Google SketchUp 8/Plugins/click.rb:14:in onLButtonDown'
                                C:/Program Files/Google/Google SketchUp 8/Plugins/click.rb:14

                                Can you please help me out? Thanks!

                                1 Reply Last reply Reply Quote 0
                                • Dan RathbunD Offline
                                  Dan Rathbun
                                  last edited by

                                  It's just a couple of typos.

                                  Line 6 should be:
                                  @pts = []
                                  so it is defined as an empty Array.

                                  Line 12 needs to spec the Sketchup module with a captial first letter. (All module and class Identifiers in Ruby are constants. Constants begin with a capital letter.)

                                  P.S.: Constants values are traditionally ALLCAPS. πŸ€“

                                  And... please learn to wrap ALL your code with a toplevel module with your unique name, before you release anything.

                                  I'm not here much anymore.

                                  1 Reply Last reply Reply Quote 0
                                  • M Offline
                                    michaelazer
                                    last edited by

                                    Thank you so much, Dan. Your tips solved the problem.
                                    Now, I have the program running, but I don't get the correct coordinates.
                                    I was expecting to get some positive and some negative, according to where I am from the axes.
                                    I clicked the 4 corners of the screen and I got the following, which doesn't look right:

                                    @unknownuser said:

                                    x: 46 y: 48 z: 48
                                    x: 53 y: 52 z: 46
                                    x: 54 y: 57 z: 46
                                    x: 52 y: 46 z: 56

                                    1 Reply Last reply Reply Quote 0
                                    • M Offline
                                      michaelazer
                                      last edited by

                                      Thank you so much, Dan. Your tips solved the problem.
                                      Now, I have the program running, but I don't get the correct coordinates.
                                      I was expecting to get some positive and some negative, according to where I am from the axes.
                                      I clicked the 4 corners of the screen and I got the following, which doesn't look right:

                                      @unknownuser said:

                                      x: 46 y: 48 z: 48
                                      x: 53 y: 52 z: 46
                                      x: 54 y: 57 z: 46
                                      x: 52 y: 46 z: 56
                                      
                                      1 Reply Last reply Reply Quote 0
                                      • 1 / 1
                                      • First post
                                        Last post
                                      Buy SketchPlus
                                      Buy SUbD
                                      Buy WrapR
                                      Buy eBook
                                      Buy Modelur
                                      Buy Vertex Tools
                                      Buy SketchCuisine
                                      Buy FormFonts

                                      Advertisement