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

Optimize plugin code

Scheduled Pinned Locked Moved Plugins
6 Posts 2 Posters 1.3k Views
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.
  • J Offline
    jeemang
    last edited by 30 Mar 2010, 22:58

    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
    
    
    1 Reply Last reply Reply Quote 0
    • T Offline
      thomthom
      last edited by 31 Mar 2010, 11:25

      I've not looked at your code yes - I'll do so later.
      But just wanted to post some general links first from the Developer section at this forum:

      Optimization Tips
      http://forums.sketchucation.com/viewtopic.php?f=180&t=25305
      Key info here: creating variables are expensive. But do use variables to void recalculation within a loop.

      In regard to "simple computationally" - in Ruby every single - even simple - operation adds significant time. http://forums.sketchucation.com/viewtopic.php?f=180&t=27307

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

      1 Reply Last reply Reply Quote 0
      • J Offline
        jeemang
        last edited by 31 Mar 2010, 16:13

        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

        1 Reply Last reply Reply Quote 0
        • T Offline
          thomthom
          last edited by 31 Mar 2010, 16:35

          Ah, yes. Forgot to mention that. Something I took for granted. SU is very slow when adding geometry. It slows down more as when there are more existing geometry in the model space you add to.

          start_operation is one way to speed things up, when you use the argument that disables the UI. And it is also worth doing as much as you can in bulks.

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

          1 Reply Last reply Reply Quote 0
          • J Offline
            jeemang
            last edited by 31 Mar 2010, 16:59

            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?"

            1 Reply Last reply Reply Quote 0
            • T Offline
              thomthom
              last edited by 31 Mar 2010, 17:26

              For instance: iterating through a collection of entities and deleting some:

              Example - deleting faces.

              This is slow
              entities.each { |e| e.erase! if e.is_a?(Sketchup::Face) }

              This is faster:
              faces = [] entities.each { |e| faces << e if e.is_a?(Sketchup::Face) } entities.erase_entities(faces)

              Same goes for selection/deselecting.
              Anything where SU offers to do either per item or as an array of items - try to pass array of items.

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

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

              Advertisement