sketchucation logo sketchucation
    • Login
    1. Home
    2. jeemang
    3. Posts
    ℹ️ Licensed Extensions | FredoBatch, ElevationProfile, FredoSketch, LayOps, MatSim and Pic2Shape will require license from Sept 1st More Info
    J
    Offline
    • Profile
    • Following 0
    • Followers 0
    • Topics 8
    • Posts 20
    • Groups 1

    Posts

    Recent Best Controversial
    • Model refresh issues on Windows

      I'm working on a pretty complicated plug-in used to render curtainwall in Sketchup. We're having some trouble with the performance of the plug-in in Windows, so I thought I'd see if I could get some insight.

      Specifically, the model doesn't seem to be redrawing at the rate that Ruby appears to be processing operations. This can be seen in the below video:

      In this video, I've set up a simple test slider (a jQuery UI widget, for what it's worth) that sends mullion spacing values to Ruby based on the position of its handle. I've coded the slider to record the clock reading when it sends queuries to Ruby, and to not send a queury if the time since the previous query is below a specified threshold. At the beginning of the video, I set the delay to zero, meaning all requests are sent instantly. As the video shows, the resulting performance is quite jerky.

      I tested exactly this configuration on OSX and got much smoother performance. When looking into this discrepancy, I added code to print clock readings to the console after operations of interest, and found that, on Windows, queries were actually being sent about 2.5 more frequently than they were on OSX. It got me thinking that perhaps requests were being sent to Windows too frequently.

      So, in the video, after I set the delay to zero and move the slider around a bit, I then set it to 60 ms. As the video shows, the resultant performance is much smoother. So, despite the fact Ruby is receiving and executing less queries, modifications to model are registering to the screen more rapidly.

      What makes this particularly puzzling is that Ruby seems to think it's completing all of the operations resulting from queries despite not actually doing so. In the final part of the video, I set the delay back to 0, and open up the Ruby console. We've set up the Ruby side of the plug-in code to report the model redraw time after it completes a redraw operation. The video shows that Ruby produces about a dozen or so of these messages for every frame or two of on-screen adjustment. This suggests a disconnect between what Ruby is doing and what it's displaying.

      By way of possible solutions: we've built a queuing mechanism into the software that holds queries in an array in javascript and only sends them to Ruby if the queue was previously empty or if it receives confirmation from Ruby that the previous query was dealt with. It doesn't help in this case, however, because, as I mentioned, Ruby thinks it's completing the requested operations (and probably is; it just isn't displaying them).

      Has anyone dealt with any similar problems? Any insight would be very much appreciated.

      posted in Developers' Forum
      J
      jeemang
    • RE: Resizing WebDialog windows

      @thomthom said:

      Callbacks from WebDialog Javascript under OSX is asynchronous - so sending multiple callbacks rapidly will eat some of the messages. You'd have to have a messagepump system to verify each message is received by Ruby before sending a new callback from Javascript.

      Do you know if anyone has yet managed to build such a system? I searched around a little on the forums and all I could seem to find was stuff like this: http://forums.sketchucation.com/viewtopic.php?f=180&t=22698

      The javascript code in the Dynamic Components folder shows what seems to be one half of such a system, and thus gives a bit of a starting point, but it would be nice to have something a little more substantial to start from...

      posted in Developers' Forum
      J
      jeemang
    • RE: Resizing WebDialog windows

      @dan rathbun said:

      The Sketchup DynamicComponents extension adds a few undocumented methods to several other classes, including UI::WebDialog, but the DC extension must be loaded to use them.

      Thanks for the info, Dan! I searched around a little to find some more info on this, but wasn't able to (and unfortunately, as I'm sure you know, the Ruby code for this extension is scrambled). Is there a list of these methods collected anywhere? Although the last_height and last_width methods you list are interesting, what I really need is some sort of last_position method...

      posted in Developers' Forum
      J
      jeemang
    • Resizing WebDialog windows

      Howdy;

      I'm working on the UI for a plugin that uses the WebDialog class.

      One feature of this UI is "accordion panels:" sections of the UI that collapse or expand when their header is clicked. Because the WebDialog.set_size method resizes windows by moving the top down instead of moving the bottom up (goofy!), and there isn't any way I'm aware of from which I can retrieve the window position so as to correct this behaviour using WebDialog.set_position, I initially implemented the resizing using the resizeTo method in Javascript.

      This worked great in OSX, but I'm now testing in Windows and for some reason resizeTo and its good friend, resizeBy, don't want to work. They work in IE when called from the JavaScript console, but not within a WebDialog, whether called in a script or from the Ruby console. So, I'm wondering:

      1. Has anyone encountered this problem? If so, are you aware of the source, and any fixes or workarounds?
      2. Is there another way to resize windows in Javascript that consistently works within a WebDialog?
      3. Alternatively, is their a way to retrieve window position within Ruby so I can use a combination of the WebDialog.set_size and WebDialog.set_position methods to accomplish what I'm after?

      The only solution I can think of is to use Javascript to get the window position and send it back to Ruby, but I'm always leery of sending data back and forth between Ruby and Javascript in rapid succession as it almost always breaks. So, any suggestions would be most welcome.

      Here's hoping future versions of the API address the bug-ridden nightmare that is UI development.

      posted in Developers' Forum
      J
      jeemang
    • RE: JSON in Ruby

      Hi Dan;

      Thanks for the response -- I'll give that a shot.

      Josh

      posted in Developers' Forum
      J
      jeemang
    • JSON in Ruby

      Quick question: what are people using to create JSON strings in Ruby? I did a little poking around and apparently even the "pure" version of the JSON implementation for ruby doesn't work within Sketchup, which my testing seems to confirm. So, I thought I'd see if anyone had any suggestions in this area.

      Thanks,

      Josh

      posted in Developers' Forum
      J
      jeemang
    • WebDialog.execute_script on OSX

      Howdy all;

      For the last couple of months I've been working on building a UI for a plug-in using the APIs WebDialog object. During this time I've encountered a fair amount of difficulty in exchanging data between WebDialog API objects and their HTML/CSS/Javascript. The most significant problem I've had is getting the WebDialog's execute_script method to work as I expect on OSX. I wanted to use this post to document some of the problems I've discovered, and workarounds I've found.

      My difficulty centres on using the execute_script method to call user-defined JavaScript functions during WebDialog creation.

      For example:

      HTML

      
      <html>
      <script type="text/javascript">
        function test_alert() {
      	alert("Hello world!");
        }
      </script>
      <body>
      <h3>TEST</h3>
      </body>
      </html>
      
      

      Ruby

      def test_wd
          $my_dialog = UI;;WebDialog.new("Selection Info", false, "Selection Info", 200, 200, 200, 200, true)
          html_path = Sketchup.find_support_file "rubyjstest.html" ,"Plugins/testing"
          $my_dialog.set_file(html_path)
          $my_dialog.show_modal()
          $my_dialog.execute_script("alert('Hello World!');")
          $my_dialog.execute_script("test_alert();")
      end
      

      When I execute the test_wd method, the alert that's called directly within the execute_script method fires, but the one wrapped in the test_alert function in the HTML file does not. Changing the order of the execute_script calls doesn't change this.

      However, if the test_alert method is called later -- whether by attaching it to a UI event (eg a button), or from the Ruby console, it works as expected. This suggests the issue in some way involves timing -- whether because of threading issues, or asynchronism, or something else.

      To investigate the issue of timing, I did some more tests. First, I modified the Ruby code to look like so:

      def test_wd
          $my_dialog = UI;;WebDialog.new("Selection Info", false, "Selection Info", 200, 200, 200, 200, true)
          html_path = Sketchup.find_support_file "rubyjstest.html" ,"Plugins/testing"
          $my_dialog.set_file(html_path)
          $my_dialog.show_modal()
          $my_dialog.execute_script("alert('Hello World!');")
          sleep 5  ## ADDED THIS LINE, USING RUBY'S BUILT IN SLEEP COMMAND
          $my_dialog.execute_script("test_alert();")
      end
      

      I quickly found that this crashes Sketchup every time. To test if this was just a matter of the "sleep" command and the API not getting along period, I ran the following code:

      def test_sleep
          puts "start"
          sleep 5
          puts "end"
      end
      

      This runs as expected. Armed with this new knowledge, I further modified the test_wd method to look like this:

      def test_wd
          $my_dialog = UI;;WebDialog.new("Selection Info", false, "Selection Info", 200, 200, 200, 200, true)
          html_path = Sketchup.find_support_file "rubyjstest.html" ,"Plugins/testing"
          $my_dialog.set_file(html_path)
          $my_dialog.show_modal()
          $my_dialog.execute_script("alert('Hello World!');")
          for i in 1..9999999   ##
              2 + 4 / 3             ## UGLY JUNK ADDED TO USE UP TIME
          end                         ##
          $my_dialog.execute_script("test_alert();")
      end
      

      Running this results in the first alert fires as expected. At about the same time, an empty WebDialog window is created, but the HTML within it is not rendered -- this doesn't happen until after the for loop finishes executing. And, most importantly, the second alert still doesn't fire.

      Knowing that the HTML doesn't render until, it seems, everything within the test_wd method has run, I got the idea to use jQuery's ready() method. So, I modified the HTML to look like so:

      <html>
      <script type="text/javascript" src="./jquery-1.4.4.min.js"></script>
      <script type="text/javascript">
      	function test_alert() {
      		alert("This is a test alert");
      	}
      	
      	$(document).ready(function() {
      		test_alert();
      	})
      </script>
      <body>
      <h3>TEST</h3>
      </body>
      </html>
      

      I also got rid of the $my_dialog.execute_script("test_alert();") line from the test_wd method -- essentially, I moved it, from the Ruby code into the $(document).ready() callback function. The result: SUCCESS!

      After completing these tests, I set about restructuring my code to take advantage of the knowledge I had gained, and ended sticking a few consecutive calls to functions I'd created for my menu's initialization into the $(document).ready() callback. It was here that I ran into yet another stumbling block that has been noted in other posts: the asynchronism of Ruby callbacks. The final thing I'll add to this thread is a simple workaround I developed that seems to help here: the JavaScript setTimeout function. This function allows you to execute a JavaScript command, provided as a string argument, after a given delay, provided, in milliseconds, as a numerical argument. For example:

      setTimeout("my_function()",1000)
      

      calls my_function() after a 1 second delay. I found that setting the delay to even 1 ms seems to avoid any trouble with consecutive Ruby callbacks.

      So, that's it. Hopefully this is in some way helpful to people like me who are hacking around with the eccentric, somewhat buggy, and sparsely-documented world of WebDialogs.

      posted in Developers' Forum
      J
      jeemang
    • RE: Keep WebDialog in front on XP/Vista?

      Wowsers, thanks for all the responses. Question answered!

      posted in Developers' Forum
      J
      jeemang
    • Keep WebDialog in front on XP/Vista?

      Very simple question: does anyone know how to keep a WebDialog in front of the other Sketchup windows on XP/Vista? I know this can be accomplished in OSX using WebDialog.show_modal, but I'm not aware of a good way to accomplish this on other platforms.

      Any help would be much appreciated.

      Josh

      posted in Developers' Forum
      J
      jeemang
    • RE: How to detect internal browser usage?

      Thanks for the reply. I assume the fellow you mention is somewhere on this forum?

      Josh

      posted in Plugins
      J
      jeemang
    • How to detect internal browser usage?

      Howdy;

      I'm working on developing an HTML user interface component using the WebDialog class and since it has a whole bunch of JavaScript I'd like to be able to debug it in Safari. The problem, of course, are the Ruby callbacks: almost every event that occurs sets the URL of the browser to skp:action_callback@action_name, which Safari obviously can't open.

      So, I'd like to modify my Ruby call method to include a check to see if the HTML is being rendered within Sketchup or not, but I'm having some trouble figuring out how to do this. The best I've been able to come up with is to check the output of the navigator.userAgent method but this doesn't seem very robust.

      Does anyone know of a better way to do this?

      Many thanks,

      Josh

      posted in Plugins
      J
      jeemang
    • RE: Can you store Entities as attributes?

      Awesome -- just stuck that into my code and it seems to be working great. Many thanks to both of you for your help.

      Josh

      posted in Plugins
      J
      jeemang
    • RE: Can you store Entities as attributes?

      Ah ha -- I am starting to understand, thanks a lot!

      I have one more question for you: thomthom suggested that I "attach an attribute to your Group's definition so you can recognize it" -- I'm wondering the best way to access the Group's definition. I can't seem to find a method within the Group class or any of its parents that returns an object's definition. Is model.definitions the only way to do this?

      posted in Plugins
      J
      jeemang
    • RE: Can you store Entities as attributes?

      So: are Groups essentially "internal" Components? How come this:

      
      Sketchup.active_model.definitions[0].class
      
      

      will return "Sketchup::ComponentDefinition" when Sketchup.active_model.definitions[0] is a group?

      What was confusing me is that I thought model.definitions would return a DefinitionList object listing only the model's Components (as defined in the typical sense, ie outside .skm files inserted into the model).

      posted in Plugins
      J
      jeemang
    • RE: Can you store Entities as attributes?

      Thanks a lot for reply -- that is good to know.

      I need to be able to identify the group in question across sessions. If possible, I'd like to find a way to do this that doesn't involve iterating through all of my model's entities and checking for an attribute. I'm interested in the idea you mention about looking within model.definitions, but I'm going to have to admit that I don't know exactly what you're talking about. Could you possibly explain it a little further?

      Thanks a lot for your help!

      Josh

      posted in Plugins
      J
      jeemang
    • RE: Can you store Entities as attributes?

      Whoops, just realized I put in the code example correctly. It should actually read:

      
      test = layer.set_attribute("layerData", "nodeValueGroup", nvg)
      puts "set correctly" unless test == nil
      test2 = layer.get_attribute("layerData", "nodeValueGroup")
      puts "it's not there" if test2 == nil
      
      
      posted in Plugins
      J
      jeemang
    • Can you store Entities as attributes?

      My question is pretty simple: does anyone know if Entities (specifically, Groups) can be stored as attributes within other Entities?

      I ask because the following code

      
      test = layer.set_attribute("layerData", "nodeValueGroup", nvg)
      puts "set correctly" unless test == nil
      test2 = layer.get_attribute("layerData", "nodeValueGroup")
      puts "it's not there" if test == nil
      
      

      will return

      
      set correctly
      it's not there
      
      

      when given a non-nil "nvg", which strikes me as strange. Any help would be greatly appreciated!

      Josh

      posted in Plugins
      J
      jeemang
    • RE: Optimize plugin code

      Ah, you did mention it -- I found that in the links you provided.

      What exactly do you mean by "doing as much as you can in bulks?"

      posted in Plugins
      J
      jeemang
    • RE: Optimize plugin code

      Hi thomthom;

      Thanks a lot for the response! I seem to have solved my problem quite simply: by adding Sketchup.active_model.start_operation("task",true) to my code before calling my import method.

      Thanks again for the links -- very useful!

      Josh

      posted in Plugins
      J
      jeemang
    • Optimize plugin code

      Howdy all;

      I'm developing a plugin to get Sketchup to work with DAYSIM (http://www.daysim.com). For those not familiar with DAYSIM, it's a daylight analysis program. Basically, you send it some geometry in RADIANCE format along with a simple file describing the locations of the points within that geometry you want analyzed, and it calculates a number of daylighting metrics at each of the points. My plug-in is meant to create the necessary input files for DAYSIM, and then import the results back in to Sketchup for interpretation.

      Right now, I'm specifically concerned with the import of results. My problem is this: the import just takes a really, really long time, despite being relatively simple computationally. It's not too bad on models in which there isn't already a lot of geometry, but if there is the import can can take a very long time. I'll post the code for import class below if anyone would like to specifically look at what I've done, but I'd also love to hear any tips or general principles anyone has with respect to making plugins run faster. The only one I've heard is that it's good to use the built-in Ruby functions as much as possible.

      Any help would be greatly appreciated!

      Thanks,

      Josh

      
      require "su2dslib/exportbase.rb"
      
      ## this class imports and displays DAYSIM analysis results
      ## new for su2ds
      
      class ResultsGrid < ExportBase
          
          def initialize
              @lines = []  ## array of DAYSIM results read from Daysim output file
              @xLines = [] ## @lines array sorted in order of ascending x coordinates
              @yLines = [] ## @lines array sorted in order of ascending y coordinates
              @spacing = 0 ## analysis grid spacing
              @minV = 0    ## minimum value of results
              @maxV = 0    ## maximum value of results
              @layerName = getLayerName('results') ## get unique layer name for results layer
              Sketchup.active_model.layers.add(@layerName)
              @entities = Sketchup.active_model.entities
              @resultsGroup = @entities.add_group
              @resultsGroup.layer = @layerName
              $nameContext = [] ## added for ExportBase.uimessage to work
              $log = [] ## no log implemented at this point; again, justed added for ExportBase.uimessage
          end
          
          ## read and pre-process DAYSIM results
          def readResults 
              # get file path
              path = UI.openpanel("select results file",'','')
              if not path
                  uimessage("import cancelled")
                  return false
              end
              # read file
              f = File.new(path)
              @lines = f.readlines
              f.close
              cleanLines
              processLines
              return true
          end
          
          ## convert numbers to floats, remove comments lines and such
          def cleanLines
              newlines = []
              @lines.each { |l|
                  # skip comment lines
                  if l.strip[0,1] == "#"
                      next
                  end
                  parts = l.split
                  begin
                      parts.collect! { |p| p.to_f}
                      newlines.push(parts)
                  rescue
                      uimessage("line ignored; '#{l}'")
                  end
              }
              @lines = newlines
          end
          
          ## calculate @xLines, @yLines, @spacing, @minV, @maxV
          def processLines
              # create @xLines and @yLines
              @xLines = @lines.sort
              @yLines = @lines.sort { |x,y|
                  [x[1], x[0], x[2], x[3]] <=> [y[1], y[0], y[2], y[3]]
              }
              x = []
              v = []
              # calculate @minV and @maxV
              @lines.collect { |l|
                  v.push(l[3])
              }
              v.sort!
              @minV = v[0]
              @maxV = v[v.length - 1]
              # calculate spacing
              @xLines.each_index { |i|
                  if @xLines[i][0] != @xLines[i+1][0]
                      if i == (@xLines.length - 1)
                          uimessage("improperly formatted grid; grid spacing could not be calculated")
                          break
                      end
                      @spacing = @xLines[i+1][0] - @xLines[i][0]
                      break
                  else
                      next
                  end
              }
          end
          
          ## draw coloured grid representing results
          def drawGrid
      
              
              # create "north-south" grid lines
              @xLines.each_index { |i|
                  if i == (@xLines.length - 1)
                      next
                  end
                  # check if next point on same "north-south" line
                  if @xLines[i][0] == @xLines[i+1][0]
                      # check if next point spaced at @spacing
                      if ((@xLines[i][1] + @spacing) * 1000).round == (@xLines[i+1][1] * 1000).round
                          # check if z-coordinate equal
                          if @xLines[i][2] == @xLines[i+1][2]
                              # create geometry
                              createEdge(@xLines[i], @xLines[i+1])
                          end
                      end
                  end        
              }
              
              # create "east-west" grid lines
              @yLines.each_index { |i|
                  if i == (@yLines.length - 1)
                      next
                  end
                  # check if next point on same "east-west" line
                  if @yLines[i][1] == @yLines[i+1][1]
                      # check if next point spaced at @spacing
                      if ((@yLines[i][0] + @spacing) * 1000).round == (@yLines[i+1][0] * 1000).round
                          # check if z-coordinate equal
                          if @yLines[i][2] == @yLines[i+1][2]
                              # create geometry
                              createEdge(@yLines[i], @yLines[i+1])
                          end
                      end
                  end        
              }
              
              puts ## hack -- stops @yLines from being output to Ruby console   
          end
          
          ## create Sketchup;;Edge object between two points, create any possible faces and
          ## colour faces appropriately
          def createEdge(pt1, pt2)
              # convert coordinates to Sketchup units
              ptc = [pt1[0..2], pt2[0..2]].each { |p| p.collect! { |e| e/$UNIT}}
              # create edge
              edges = @resultsGroup.entities.add_edges(ptc[0], ptc[1])
              # set edge characteristics, and draw faces
              edges.each { |e|
                  e.layer = @layerName
                  e.hidden = true
                  value = (pt1[3] + pt2[3]) / 2
                  e.set_attribute("values", "value", value)
                  # draw faces
                  if e.find_faces > 0
                      faces = e.faces
                      faces.each { |f|
                          processFace(f)
                      }
                  end
              } 
          end
          
          ## process Faces (ie, set characteristics)
          def processFace(f)
              if f.edges.length > 4
                  f.erase!
              else
                  f.layer = @layerName
                  val = 0
                  f.edges.each { |e|
                      val += e.get_attribute("values", "value") / 4
                  }
                  setColor(f, val)
              end
              
          end
          
          ## set Face color
          def setColor(f, val)
              colorVal = (val - @minV) * 255 / (@maxV - @minV)
              faceCol = Sketchup;;Color.new
              faceCol.red = 127
              faceCol.blue = 127
              faceCol.green = colorVal
              f.material = faceCol
              f.back_material = f.material
          end
          
      end # class
      
      
      posted in Plugins
      J
      jeemang
    • 1 / 1