by Pixero » Sat Jan 08, 2011 7:34 pm
Could you tell me how you debug your ruby scripts? I'm using the most basic techique and would like to learn from the pro's. I use Notepad++ and when I get an error in the console I check that line number and try to fix the problem. I then restart SU and reload my test scene. There must be a better/faster way... 
-

Pixero
-
- Posts: 2654
- Joined: Thu May 24, 2018 12:49 pm
- Location: Halmstad, Sweden
- Name: Pixero
- Operating system: Windows
- SketchUp version: 2019
- License type: Pro
- SketchUp use: architecture
- Level of SketchUp: Advanced
-
by TIG » Sat Jan 08, 2011 10:42 pm
Pixero wrote:Could you tell me how you debug your ruby scripts? I'm using the most basic techique and would like to learn from the pro's. I use Notepad++ and when I get an error in the console I check that line number and try to fix the problem. I then restart SU and reload my test scene. There must be a better/faster way... 
That's basically the way I do it... You need to split you code into parts that fail/succeed and as they succeed you add more functions until one breaks - tht way you know where you made the balls-up and so you can try to fix it... 
TIG
-

TIG
- Global Moderator
-
- Posts: 20275
- Joined: Mon Nov 12, 2007 7:24 pm
- Location: Northumbria UK
- Name: TIG
- Operating system: Windows
- SketchUp version: 2021
- License type: Pro
- SketchUp use: architecture
- Level of SketchUp: Advanced
by todd burch » Sat Jan 08, 2011 11:02 pm
I don't do the "start and stop SU" piece, unless I'm working with menu items. If I have a static scene I'm working with, and the script corrupts it, I'll just delete everything in the scene and reload the model. Then, I reload the script, and keep going. (unless of course there are pieces of the script that require restarting SU, like constant definition changes, etc. During development, I'll usually put logic aroud constant definitions (et al) to not re-execute if they are already defined)
-
todd burch
-
- Posts: 913
- Joined: Mon Nov 12, 2007 2:43 pm
- Location: Katy, Texas
- Name: Todd Burch
-
by thomthom » Sun Jan 09, 2011 12:34 pm
I use puts thoughout my code when I find issues to track the flow of the data and ensure it is what I expect it to be. When I make changes to the script I use use the load method to reload the .rb file without reloading SketchUp.
Occationally I make changes to the code that requires me to restart SketchUp, but that's rare.
I also use Jim's Ruby Console plugin that allows me to clear the text of the console - as the more text there is in the console, the slower it performs.
-

thomthom
- PluginStore Author

-
- Posts: 19478
- Joined: Tue Nov 13, 2007 12:47 pm
- Location: Trondheim, Norway
- Name: Thomas Thomassen
- Operating system: Windows
- SketchUp version: 2019
- License type: Pro
- SketchUp use: other
- Level of SketchUp: Advanced
-
by Jim » Sun Jan 09, 2011 3:18 pm
As the others have said, you don't need to close/re-open SketchUp after each minor edit. You can re-load a script from the Ruby Console. load "dev/test_script.rb" (Assume you have a dev/ folder in Plugins.) From there, you could use a temporary menu and shortcut to reload the script; just enter it in the Ruby Console: UI.menu("Plugins").add_item("Reload My Script") { load("dev/test_script.rb") }This is how my Ruby Toolbar started - on Windows, it can open/close the Ruby Console, Clear it, and load/reload a file. Then for the truly lazy - automatically reloading a script. viewtopic.php?f=180&t=31394viewtopic.php?f=180&t=31463
Hi
-
Jim
- Global Moderator
-
- Posts: 4678
- Joined: Mon Nov 12, 2007 10:13 pm
- Location: ohio
- Name: Jim
- Operating system: Windows
- SketchUp version: 2017
- License type: Pro
- SketchUp use: hobby
- Level of SketchUp: Intermediate
-
by Pixero » Sun Jan 09, 2011 4:31 pm
Thanks for your answers. I use a lot of puts too to check values and also Jims Ruby toolbar. Seems I'm doing something right. About reloading, doesnt that create a duplicate of the plugin name in the plugin menu list? I've tried this before and got a new duplicate for each reload. Maybe it's because the dev script has a menu? Is it "good practice" to NOT create a menu until the script is finished? Maybe a "quit_without_save_and_reload_last_used_scene_with_ruby_console_open.rb" button. 
-

Pixero
-
- Posts: 2654
- Joined: Thu May 24, 2018 12:49 pm
- Location: Halmstad, Sweden
- Name: Pixero
- Operating system: Windows
- SketchUp version: 2019
- License type: Pro
- SketchUp use: architecture
- Level of SketchUp: Advanced
-
by TIG » Sun Jan 09, 2011 4:34 pm
If you make your menu code in the script something like this - Code: Select all
if not file_loaded?(__FILE__) UI.menu("Plugins").add_item("xxxxx"){xxxx.new()} end#if file_loaded(__FILE__)
then you only get one menu entry UNLESS you rename the file itself then you'll get another!
TIG
-

TIG
- Global Moderator
-
- Posts: 20275
- Joined: Mon Nov 12, 2007 7:24 pm
- Location: Northumbria UK
- Name: TIG
- Operating system: Windows
- SketchUp version: 2021
- License type: Pro
- SketchUp use: architecture
- Level of SketchUp: Advanced
by Pixero » Sun Jan 09, 2011 4:44 pm
Ok, thanks!
-

Pixero
-
- Posts: 2654
- Joined: Thu May 24, 2018 12:49 pm
- Location: Halmstad, Sweden
- Name: Pixero
- Operating system: Windows
- SketchUp version: 2019
- License type: Pro
- SketchUp use: architecture
- Level of SketchUp: Advanced
-
by thomthom » Sun Jan 09, 2011 5:35 pm
Remember to use those file_loaded method you must require 'sketchup.rb' before using them.
-

thomthom
- PluginStore Author

-
- Posts: 19478
- Joined: Tue Nov 13, 2007 12:47 pm
- Location: Trondheim, Norway
- Name: Thomas Thomassen
- Operating system: Windows
- SketchUp version: 2019
- License type: Pro
- SketchUp use: other
- Level of SketchUp: Advanced
-
by Jim » Sun Jan 09, 2011 5:37 pm
Also, you can define your own version of "puts" which writes the message to a file: - Code: Select all
def debug_puts(s) File.open("/sketchup_log.txt", "a+") { |fp| fp.puts(s) } end
(Which should work on Windows or Mac.) If on Windows, you could send the output to a system debugger: viewtopic.php?f=180&t=31216
Hi
-
Jim
- Global Moderator
-
- Posts: 4678
- Joined: Mon Nov 12, 2007 10:13 pm
- Location: ohio
- Name: Jim
- Operating system: Windows
- SketchUp version: 2017
- License type: Pro
- SketchUp use: hobby
- Level of SketchUp: Intermediate
-
by Pixero » Sun Jan 09, 2011 6:22 pm
Wow, I think this thread is turning into something great! Keep 'em coming!
-

Pixero
-
- Posts: 2654
- Joined: Thu May 24, 2018 12:49 pm
- Location: Halmstad, Sweden
- Name: Pixero
- Operating system: Windows
- SketchUp version: 2019
- License type: Pro
- SketchUp use: architecture
- Level of SketchUp: Advanced
-
by AdamB » Sun Jan 09, 2011 6:36 pm
And if you get really stuck as what is going wrong, turning on tracing can be invaluable. That is logging every single call made - it will slow down your script execution to a crawl, so only turn on when needed.
(This is a single line of code)
set_trace_func proc { |event, file, line, id, binding, classname| File.open("mylogfile","a+") {|fp| fp.write "%8s %s:%-2d %10s %8s\n" % [event, file, line, id, classname]}}
From where you put this line onward, every call is logged - so choose a spot when your script does something useful.
-

AdamB
- LightUp Support

-
- Posts: 936
- Joined: Wed Dec 12, 2007 10:49 am
- Location: Brighton, UK
- Name: AdamB
- Operating system: Mac
- SketchUp version: 2014
- License type: Pro
- SketchUp use: other
- Level of SketchUp: Advanced
-
by Pixero » Sun Jan 09, 2011 6:42 pm
AdamB wrote:And if you get really stuck as what is going wrong, turning on tracing can be invaluable. That is logging every single call made - it will slow down your script execution to a crawl, so only turn on when needed.
(This is a single line of code)
set_trace_func proc { |event, file, line, id, binding, classname| File.open("mylogfile","a+") {|fp| fp.write "%8s %s:%-2d %10s %8s\n" % [event, file, line, id, classname]}}
From where you put this line onward, every call is logged - so choose a spot when your script does something useful.
Ah, you seem to have read my mind. I was about to ask something along this line but didn't quite know how to express what I was after.  Is there no way of ending it if you want to check something in the middle of a script?
-

Pixero
-
- Posts: 2654
- Joined: Thu May 24, 2018 12:49 pm
- Location: Halmstad, Sweden
- Name: Pixero
- Operating system: Windows
- SketchUp version: 2019
- License type: Pro
- SketchUp use: architecture
- Level of SketchUp: Advanced
-
by thomthom » Sun Jan 09, 2011 6:56 pm
Good one Adam. Jim - one for your Ruby Console plug?
-

thomthom
- PluginStore Author

-
- Posts: 19478
- Joined: Tue Nov 13, 2007 12:47 pm
- Location: Trondheim, Norway
- Name: Thomas Thomassen
- Operating system: Windows
- SketchUp version: 2019
- License type: Pro
- SketchUp use: other
- Level of SketchUp: Advanced
-
by thomthom » Sun Jan 09, 2011 6:58 pm
And under Windows you have this snippet to output the data to debuggers like DebugView: viewtopic.php?f=180&t=31216&p=274668#p274467
-

thomthom
- PluginStore Author

-
- Posts: 19478
- Joined: Tue Nov 13, 2007 12:47 pm
- Location: Trondheim, Norway
- Name: Thomas Thomassen
- Operating system: Windows
- SketchUp version: 2019
- License type: Pro
- SketchUp use: other
- Level of SketchUp: Advanced
-
by Jim » Sun Jan 09, 2011 7:02 pm
If you want to trace specific functions, you can output the caller. Also, special variables exist for the file and line #: puts "line no. #{ __LINE__ }" puts "File: #{ __FILE__ }"But these are less useful in scrambled plugins.
Hi
-
Jim
- Global Moderator
-
- Posts: 4678
- Joined: Mon Nov 12, 2007 10:13 pm
- Location: ohio
- Name: Jim
- Operating system: Windows
- SketchUp version: 2017
- License type: Pro
- SketchUp use: hobby
- Level of SketchUp: Intermediate
-
by Jim » Sun Jan 09, 2011 7:13 pm
Because of Ruby's weak typing, the cause of many errors can be detected by simple type checking.
fail unless face.is_a? Sketchup::Face
No output required, so less of a need to go through later and remove them unless it's inside a critical loop.
Last edited by Jim on Sun Jan 09, 2011 10:07 pm, edited 1 time in total.
Hi
-
Jim
- Global Moderator
-
- Posts: 4678
- Joined: Mon Nov 12, 2007 10:13 pm
- Location: ohio
- Name: Jim
- Operating system: Windows
- SketchUp version: 2017
- License type: Pro
- SketchUp use: hobby
- Level of SketchUp: Intermediate
-
by Dan Rathbun » Sun Jan 09, 2011 9:21 pm
Jim wrote:Because of Ruby's weak typing, the cause of many errors can be detected by simple type checking.
fail unless face.is_a? Sketchup::Face
No output required, so no need to go through later and remove them later unless it's inside a critical loop.
assuming your code is namespaced wrapped (within a module or a class,) what I do is declare local boolean constants like TRACE, DEBUG, POPUP and use them to conditionally execute or not, certain debug statements. To use Jim's example: fail unless face.is_a?(Sketchup::Face) && TYPECHECKwhere TYPECHECK would be a local boolean constant. EDIT: actually the code would run faster like: fail unless TYPECHECK && face.is_a?(Sketchup::Face)Often I use POPUPS to control debug messagebox calls from inside one of my methods: UI.messagebox(%[MyModule method:'widget'\n\nArgs:\n#{args.join("\n")}]) if POPUPSthat way I can turn on and off my debug popup dialogs at the top of a module declaration.
I'm not here much anymore. But a PM will fire email notifications.
-

Dan Rathbun
- PluginStore Author

-
- Posts: 5042
- Joined: Tue Oct 06, 2009 3:06 am
- Location: Florida, USA
- Name: Dan Rathbun
- Operating system: Windows
- SketchUp version: 2015
- License type: Pro
- SketchUp use: education
- Level of SketchUp: Advanced
by Dan Rathbun » Sun Jan 09, 2011 10:20 pm
Pixero wrote:AdamB wrote:And if you get really stuck as what is going wrong, turning on tracing can be invaluable. set_trace_func proc { |event, file, line, id, binding, classname| File.open("mylogfile","a+") {|fp| fp.write "%8s %s:%-2d %10s %8s\n" % [event, file, line, id, classname]}}
From where you put this line onward, every call is logged - so choose a spot when your script does something useful.
Is there no way of ending it if you want to check something in the middle of a script?
Yes there is. set_trace_func(nil)This method is actually defined in the Kernel module, which is mixed-in to Object class, and therefore inherited by all other clases and modules. So for it's reference, see Kernel.set_trace_func in the Ruby Core Reference (CHM.) One reason Adam's example is slow, is that it has to open the logfile, write a line to it, then close the file for safety, in case of an exception. HOWEVER.. the block can be any Ruby code. If you are only interested in certain events such as a call to a ruby method, then you can test for that using an if clause, and ignore all other events, so as to speed up tracing. - Code: Select all
def start_logging(stopclass='',stopmethod='') set_trace_func proc { |event, file, line, id, binding, classname| if event.to_s=='call' File.open("mylogfile","a+") {|fp| fp.write "%8s %s:%-2d %10s %8s\n" % [event, file, line, id, classname] } stop_logging if classname.to_s==stopclass.to_s && id.to_s==stopmethod.to_s end } end
def stop_logging set_trace_func(nil) end
* example only
P.S. @ThomThom (in regards to another topic on overriding API methods,) this method is one of the ways you can trace a method back to the file that overrode it.
I'm not here much anymore. But a PM will fire email notifications.
-

Dan Rathbun
- PluginStore Author

-
- Posts: 5042
- Joined: Tue Oct 06, 2009 3:06 am
- Location: Florida, USA
- Name: Dan Rathbun
- Operating system: Windows
- SketchUp version: 2015
- License type: Pro
- SketchUp use: education
- Level of SketchUp: Advanced
by Jim » Tue Jan 18, 2011 4:18 pm
You could set an environmental variable, then enable/disable debugging based on it. By using a unique variable name, you can gurantee debugging is enabled only on the machine you are working on.
Hi
-
Jim
- Global Moderator
-
- Posts: 4678
- Joined: Mon Nov 12, 2007 10:13 pm
- Location: ohio
- Name: Jim
- Operating system: Windows
- SketchUp version: 2017
- License type: Pro
- SketchUp use: hobby
- Level of SketchUp: Intermediate
-
by Cleverbeans » Tue Jan 18, 2011 11:10 pm
I've gotten into a few habits with debugging that I've found helpful. Firstly, as some folks have already mentioned I break my code up into small bits as much as possible. I've got a module that around 1000 lines of code and the longest method is only 7 lines, and it was a dream to debug. It's really easy to spot what's going wrong when it's such a tiny box. Also, there is no reason to be wary of writing a method with only a single line of code in it's body if it will meaningfully capture some logic in your overall code. This way the method names become a part of your documentation, and allow you to follow the logic easily and it naturally describes the problem you're working on. Secondly, anytime I'm going to be writing a complicated method I will generally write my tests before I write my code. This often helps me to clarify the design, build some of the documentation, and it can speed up the debugging process considerably. I will also keep my test code even if I never use it again. You'll see a lot of coders type a few quick lines in the console to test something while they're writing a method and then throw it away. I do the same thing, but I just copy/paste it into another file and hold onto it. I never end up using almost all that code, but for the few times I do when Thirdly, I will test my code after almost every change. There is no harm in testing the method after changing just a single line of code, particularly when you're working in a scripted language like Ruby and don't have to recompile. I found that if I deferred debugging till I had "something worth running" I'd often find a chain of bugs which would obscure the real error behind the scenes. You catch a lot of little things like typos, missing brackets, type mismatches and such which are easy to quash. Also, I find it's easier to debug what I'm doing while it's fresh in my mind. If I find a semantic error in code I wrote even an hour earlier it's not fresh and I have to figure out what I was doing at that point again to see where it went wrong. Fourthly, I avoid state data as much as possible. Anytime declare a global, class, or instance variables you open up the possibility of side effects in your code which can make debugging a nightmare. I would also tend to avoid methods with a bang over their un-banged counterparts, so I would almost always use Array.map instead of Array.map! unless I had good reason to do so. State data exists for a reason, and they're good reasons but my experience tells me they're often used without any justification other than familiarity with procedural languages like C, Basic or Pascal. It's actually possible to write every program imaginable without any state data, which may have costs in runtime, modularity, and readability but it at least gives the sense that you can in fact avoid them and if it makes your code easier to debug and maintain that's often a good trade. Also, I never make state data private or protected while writing my code. It's much easier to debug this way since you can sniff their values, and I can always go back and make them private or protected at a later time. This applies to both variables and class definitions as well. Finally, I read my documentation when I'm debugging. If I'm coming back to a project where new features have been introduced and suddenly I've got unintended behavior it's really nice to have a clear understanding of what the codes purpose is, why I chose that particular implementation over others, and some of the bugs I'd previously squashed in the code. I really hate fixing one bug only to introduce a new bug I had previously fixed and just forgotten about by making the same mistake again. A one-liner saying "this prevents and off-by-one error in the loop that follows" can be really handy. Also, I consider variable names as documentation. It's easier to figure out what "for point in points" means when compared to "for pt in pts". I also prefer "and" to "&&" and "not" to "!" for the same reasons, and I would rather use a for...in loop to the .each method. The more expressive your code, the easier it is to debug. Also, it can be handy to familiarize yourself with a few different types of common bugs. It's easier to detect a bug when you're getting odd behavior if that behavior resembles what you'd expect from a particular type of bug and you also begin to build up a set of techniques to track them down and fix them.
-
Cleverbeans
-
- Posts: 41
- Joined: Thu Oct 21, 2010 4:57 pm
by thomthom » Tue Jan 18, 2011 11:57 pm
Cleverbeans wrote:Thirdly, I will test my code after almost every change. There is no harm in testing the method after changing just a single line of code, particularly when you're working in a scripted language like Ruby and don't have to recompile. I found that if I deferred debugging till I had "something worth running" I'd often find a chain of bugs which would obscure the real error behind the scenes. You catch a lot of little things like typos, missing brackets, type mismatches and such which are easy to quash. Also, I find it's easier to debug what I'm doing while it's fresh in my mind. If I find a semantic error in code I wrote even an hour earlier it's not fresh and I have to figure out what I was doing at that point again to see where it went wrong.
Yes, test often. So many times I've tripped over a bug do to a typo I made to a quick change. If I code larger chunks, the higher the likely hood of a typo bug or something silly sneaking its way in. (I'm often surprised if there is no errors after I've written a longer section of code. I begin to think that there must be something seriously wrong I haven't spotted yet.)
-

thomthom
- PluginStore Author

-
- Posts: 19478
- Joined: Tue Nov 13, 2007 12:47 pm
- Location: Trondheim, Norway
- Name: Thomas Thomassen
- Operating system: Windows
- SketchUp version: 2019
- License type: Pro
- SketchUp use: other
- Level of SketchUp: Advanced
-
by liquid98 » Thu Jun 09, 2011 5:31 pm
I know, I'm a complete outsider in this discussion, but I have a question and an idea.
My question: In what debugging situations is it necessary to restart Sketchup?
And my idea:
A plugin that resets Sketchup to it's initial state. So you never have to restart SU or manually reload a plugin to test a script.
Things that flourish fall into decay. This is not-Tao, And what is not-Tao soon ends ~ Lao tse
-

liquid98
-
- Posts: 124
- Joined: Sat Feb 14, 2009 10:42 pm
- Location: The Netherlands
- Name: liquid98
-
by TIG » Thu Jun 09, 2011 7:41 pm
liquid98 wrote:I know, I'm a complete outsider in this discussion, but I have a question and an idea.
My question: In what debugging situations is it necessary to restart Sketchup?
And my idea:
A plugin that resets Sketchup to it's initial state. So you never have to restart SU or manually reload a plugin to test a script.
If your script makes menu items or toolbars you will [usually] have trapped it with file_loaded() so you don't get multiple entries... so if you change those reloading the script won't affect those... but otherwise any code changes are usually reflected in the tool's operation without a need to restart if you use load "myscript.rb"....... if you tool has decent start/commit blocks to undo what it's just done [incorrectly] you rarely need to restart Sketchup after tweaking your code...
TIG
-

TIG
- Global Moderator
-
- Posts: 20275
- Joined: Mon Nov 12, 2007 7:24 pm
- Location: Northumbria UK
- Name: TIG
- Operating system: Windows
- SketchUp version: 2021
- License type: Pro
- SketchUp use: architecture
- Level of SketchUp: Advanced
by thomthom » Thu Jun 09, 2011 7:51 pm
liquid98 wrote:My question: In what debugging situations is it necessary to restart Sketchup?
In additions to what TIG mentioned in regard to menus, you might want to restart after doing some major refactoring where you might have renamed methods etc. You might still be calling the old method. And there might be things your plugin has to do at startup, such as some observers. But it belong to the rare cases.
-

thomthom
- PluginStore Author

-
- Posts: 19478
- Joined: Tue Nov 13, 2007 12:47 pm
- Location: Trondheim, Norway
- Name: Thomas Thomassen
- Operating system: Windows
- SketchUp version: 2019
- License type: Pro
- SketchUp use: other
- Level of SketchUp: Advanced
-
by liquid98 » Fri Jun 10, 2011 10:35 am
Thanx guys,
I'm working with dynamic screen notes and dynamic tooltips, and have to restart SU all the time to get them refreshed.. But that's not common for most plugins I suppose.
Things that flourish fall into decay. This is not-Tao, And what is not-Tao soon ends ~ Lao tse
-

liquid98
-
- Posts: 124
- Joined: Sat Feb 14, 2009 10:42 pm
- Location: The Netherlands
- Name: liquid98
-
by Dan Rathbun » Sat Jun 11, 2011 12:02 am
A tooltip for a UI::Command instance object can be changed at ANY time, simply by calling the instance object's tooltip= method, with an argument that is different than what the current tooltip text is.
This means that you must make and keep available, a reference (sloppily called a variable,) to that instance object, so you can later call it's instance methods.
You can also change the status bar text and the validation proc dynamically in the same way.
I'm not here much anymore. But a PM will fire email notifications.
-

Dan Rathbun
- PluginStore Author

-
- Posts: 5042
- Joined: Tue Oct 06, 2009 3:06 am
- Location: Florida, USA
- Name: Dan Rathbun
- Operating system: Windows
- SketchUp version: 2015
- License type: Pro
- SketchUp use: education
- Level of SketchUp: Advanced
by tomot » Tue Aug 23, 2011 9:01 pm
Its enough to make a grown man cry! It only took me 3 hours to find the 2 letter typing error. I hope this is not age related?  There was no hint of a problem in the Ruby console, Notepad++ or SciTE. Its enough to make one - Code: Select all
if( @stud != "YES" ) #...get the number of joists required for the related @vec/@joc #...@soc = joist on center spacing @num_stud=((@vec/@soc)+1).to_i definitions=model.definitions count=definitions.add entities=count.entities #...draw 1st stud base=entities.add_face(@pt1, @pt3, @pt33, @pt11) base=entities.add_line(@pt1, @pt33) base=entities.add_line(@pt3, @pt11) #...transform stud location t=Geom::Transformation.translation(Geom::Vector3d.new(0, 0, 0)) entities=model.active_entities entities.add_instance(count, t) #...copy studs to their new locations i = 1 while i < @num_stud # Transformation i = i + 1 vec = @pt5 - @pt6 # width between which studs are to be drawn vec.length = @soc*(i-1) t=Geom::Transformation.translation(Geom::Vector3d.new(0,@soc*(i-1), 0)) t=Geom::Transformation.translation(vec) entities.add_instance(count, t) end end if # end if @stud
I feel better now!
-
tomot
- PluginStore Author

-
- Posts: 706
- Joined: Mon Apr 07, 2008 12:18 am
- Operating system: Windows
- SketchUp version: 8
- License type: Free/Make
- SketchUp use: architecture
- Level of SketchUp: Advanced
-
by Dan Rathbun » Tue Aug 23, 2011 9:34 pm
line 7: no argument for the instance method DefinitionsList.add()
I'm not here much anymore. But a PM will fire email notifications.
-

Dan Rathbun
- PluginStore Author

-
- Posts: 5042
- Joined: Tue Oct 06, 2009 3:06 am
- Location: Florida, USA
- Name: Dan Rathbun
- Operating system: Windows
- SketchUp version: 2015
- License type: Pro
- SketchUp use: education
- Level of SketchUp: Advanced
by tomot » Tue Aug 23, 2011 9:54 pm
No that's not it ! ..... nice try!..... try again?
-
tomot
- PluginStore Author

-
- Posts: 706
- Joined: Mon Apr 07, 2008 12:18 am
- Operating system: Windows
- SketchUp version: 8
- License type: Free/Make
- SketchUp use: architecture
- Level of SketchUp: Advanced
-
by Ad Machine » 5 minutes ago
-
Ad Machine
- Robot
-
- Posts: 2012
-
Return to Developers' Forum
|