⚠️ Important | Libfredo 15.6b introduces important bugfixes for Fredo's Extensions Update
  • Operations not commited

    2
    0 Votes
    2 Posts
    63 Views
    Dan RathbunD
    (1) It's easy to see why the 2nd op is aborted because you call model.abort_operation instead of committing it. (2) Perhaps some other plugin is also creating ops and "gluing" their's transparently between your 2 ops ?? I always disable all other 3rd party plugins when developing!* (But I leave the OEM Dynamic Components extension ON.) (3) Do yourself and us a big favor.. wrap your operations and trap any errors that cause the ops to abort. See [Code] wrapping operations
  • Unload / reload plugin without closing SketchUp

    17
    0 Votes
    17 Posts
    2k Views
    thomthomT
    Yea - the key is that you're plugin is not the only one operating and observer events can trigger in the middle of other operations. That's why I talked about caching the observer data until you find a "safe point" to execute. Mapping out this is important to avoid headache. Keeping observers to a minimum is best IMO - reduces overhead and risk of conflicts. Consider each observer if you really need it. I'm a bit more liberal with observers when I use them within the operation of my own custom Tools. (Within a Tool class) as I then assume I have greater control over the environment.
  • Hightlight/select component with mouse over

    7
    0 Votes
    7 Posts
    892 Views
    F
    WOW! Thanks sdmitch!! That's way over my pay grade, Damn! I tested it and it works great, though I must admit I'll have to study the code a lot to figure out how you did it and even then I'm not sure I'll get it.
  • Ruby "good practice" using constants?

    23
    0 Votes
    23 Posts
    3k Views
    Dan RathbunD
    @brewsky said: And after thinking on this, maybe TT's way of just making a module as a base-object for the plugin is a better approach than my BimTools-class. Because is't only used once, the module-approach seems more fitting... Oh hell yes. If you need only one copy of a code object, then generally it should be a module. If you need multiple copies of a code object (usually because the code must adapt to many other instance objects,) then you make it a class, and instantiate instances that are syncronized to a particular instance object. Often coders try to avoid using a module, because they think it's a static kind of object, and they believe it is harder to use than a class instance. What they miss is, that a module definition, is really an instance of class Module. So for example, your nested module Brewsky::BimTools::Manager is actually an instance object, and the preceeding identifier is the reference to the instance. If you remember this... then it can be easier to understand how using an anonymous singleton proxy class instance inside your Module class instance, makes sense. Imagine the Module class is "the hen". It lays an egg, which is your nested Manager module instance, that could be created thus: ` Brewsky::BimTools::Manager = Module.new { plugin managerial code here }But the Ruby interpreter calls the new()` constructor for you on the C-side of things. (Ie, the defintion block syntax for scripts, was invented for human happiness and readability; Ruby itself does not really need it.) But having methods in modules communicate (call each other,) works a bit different than in a class definition. Instance method definitions in a module, are meant for Library Mixin modules. (Read up on the include and extend methods.) They become different kinds of methods, depending on whether they are mixed into a class or module, and whether include and extend is used to do the mixing.) So at first blush, the coder thinks they must define all methods in a module as module functions that must be called with self.method_name(), ... they find this cumbersome, and they switch back to using a class defintion, and using only one instance of it. (A sort of psuedo-singleton class.) BUT.. the egg can have a membrane inside it's shell. This membrane analogy is the anonymous singleton proxy class instance created with the following syntax: module Brewsky module BimTools end end module Brewsky;;BimTools;;Manager # module variables and constants declared here MGR_VERSION ||= '1.2.3' @@bimmenu ||= nil class << self # self evaluates to the module instance # Everything in here acts like a class # instance BECAUSE IT ACTUALLY IS ! # In here we can access the module @@vars directly. # In here we define instance methods, not module methods. # But if they are public, they can be called like module # functions, from anywhere outside the module. private def get_version() MGR_VERSION end # In here we can call any other method in here, # without module qualification. public def version() get_version() end end # proxy class # Out here we can call any of the methods inside # the proxy class directly, without qualification. unless @@bimmenu @@bimmenu = UI.menu('Plugins').add_submenu('BIMTools') @@bimmenu.add_item('Version') { UI.messagebox("BIMTools ver #{version()}") } end end # module Brewsky;;BimTools;;Manager # 99.9% of the time, there is no good reason to # have any executing code outside your namespace. EDIT(2012-12-16): changed post title to "Why use a module instead of a class ?"
  • [Code]Text label system

    8
    0 Votes
    8 Posts
    237 Views
    TIGT
    Plain variables, or perhaps more correctly 'references', [e.g. x] are only used/changed in their specific def [method] that defines them. Ones starting with @ [e.g. @x] - their current value is accessible to all methods during that instance of the class/module. Any method can define or redefine them, Ones starting with @@ [e.g. @@x] are similar to @ ones, BUT their current value is remembered across multiple uses of that class/module - e.g. good for remebering values last used in dialogs. They need to be 'declared' outside of any method, but after that any method can then redefine them. Constants [starting with a capital letter, or commonly with all caps, e.g. Z_AXIS] are similar to @@ ones, they are remembered across all uses of that class/method, BUT they are not readily changeable - because they are 'constants'! Move your 'start_operation' out of the iteration block [to match the 'commit_'] that way all we become 'undo-able' in one go. The selection.each{} block adds a piece of text to every selected object when the code starts. However, the Sketchup.send_action("selectSelectionTool:") will stop more that one iteration. It simply exits the tool and activates the 'Select' tool. If you want to be able to 'pick objects' in turn you will need to [re]write/launch this as a proper Tool... this will involve getting input-points, reading mouse-positions and mouse-clicks etc... Then using those for new text objects etc... See the API and examples - 'linetool.rb' etc - for some Tool method ideas/examples... Or other authors' tools' .rb files - like say some of my 2dTools... There is limited access to 'Text' objects' properties through the API - see the relevant pages - http://code.google.com/apis/sketchup/docs/ - height is not one of them - unless you make it as '3d-text'... It's a long standing request...
  • Start a Java application asynchronous

    12
    0 Votes
    12 Posts
    1k Views
    TIGT
    As I explained... Dan's idea of opening [executing] a file, can't work by passing a 'command' or even an exectable with following arguments, it will only work if you pass it a file. So UI.openURL(full_url) opens that web-page in the default browser. And UI.openURL('file:///'<<full_path_to_file) opens that file in its default application, so if it's a .txt file then Notepad opens it, if it's a .doc file then Word opens it and so on [for a PC at least]. If it's an 'executable' then it 'runs' - these kinds of file include .exe, .cmd, .bat, .vbs etc on a PC [.command on a MAC etc]. Some executables take arguments when they are run from a 'shell'/'console'/'terminal' - e.g. in a PC command-shell typing Notepad[.exe] opens that program with a new empty/untitled window; but Notepad "txt_file" would open that file if it exists, or if it doesn't exist it asks you if you want to make it... BUT you CAN'T pass arguments with UI.openURL() So UI.openURL("Notepad.exe") will open an instance of 'Notepad' [true] UI.openURL("\"Notepad.exe\" \"path_to_my_txt_file\"") will fail [false] UI.openURL('file:///'<<"path_to_my_txt_file") will open that existing txt file with Notepad [true] or it will fail [false] if that txt file doesn't exist. Using system(commands) in Ruby should give similar results to typing the exact same text into a command-shell - hence Dan's advice to 'quote' arguments - because 'spaces' in file paths will be taken as the start of a new argument otherwise and mess you up... So using system(system("\"Notepad.exe\" \"path_to_test.txt\"")) is the same as using the very same strings inside a command-shell... BUT this runs as part of the Ruby thread, and Ruby will wait for it to complete... The way to pass arguments to an external executable, that runs independently of Ruby once it's started, is to run it as something like a .cmd/.bat [.command on MAC]. You can't avoid the startup black PC window [although you can reduce it to a flicker by having one .cmd file to start another main .cmd file, the first opens and closes almost instantaneously while the second then runs silently]. So you can write a temporary .cmd' file [etc] then use UI.openURL('file:///'<<path_to_cmd_file) to run it [also changing directory briefly to the file's folder does allow you to 'open' it 'by name only'...] If you use some other kinds of executables like .jar or .vbs these can be set to run 'silently' - however, passing arguments to them is then awkward [although you could 'write' a temporary .vbs file etc containing ALL of the info needed to complete things*, or perhaps use an .ini. file containing the data needed by the .jar to read in for that to do it's stuff; on a MAC you would use an AppleScript or equivalent]. *For a 'silent' VBS solution you would use something like this line of code that you 'puts' into a 'temp.vbs' file... createobject("wscript.shell").RUN "java -jar \"full_path_to_Model2GCode.jar\" \"full_path_to_fileForJava\"", 0, False Obviously you need to adjust it for you own full file paths/names etc... Then in Ruby you'd use UI.openURL('file:///'<<"full_path_to_temp.vbs") to run it - and it then executes asynchronously, quite separate from Ruby...
  • Determining when a model has changed

    4
    0 Votes
    4 Posts
    84 Views
    Dan RathbunD
    @al hart said: @tig said: The model.modified? method determines if the Model has been modified since the last save. That would work if there was a "saved" Observer, ... The "saved" callbacks are in the Sketchup::ModelObserver class (where they should be, because there can be more than one model open on the Mac. But you can write a generic model observer that handles all open model instances, since the model instance's reference is passed to most callbacks.)
  • [Code] Locking down the API

    3
    0 Votes
    3 Posts
    187 Views
    thomthomT
    @dan rathbun said: You can shorten the file using ObjectSpace.each_object() iterators: I'm locking down more than the API - also the Ruby core classes. @dan rathbun said: It NEEDS to modify several classes, so you will need to add a require('dynamiccomponents.rb') at the top of the file, before freezing. Well, "needs"... ...they chose to. But yes. One adds exceptions as one pleases. This list is just a boilerplate lock-down.
  • Problem with group.copy

    20
    0 Votes
    20 Posts
    935 Views
    Dan RathbunD
    @thomthom said: hm... I'm tempted of adding a script that loads first that freezes the base ruby and SketchUp classes and modules just to see what happens. Could be useful to catch misbehaving plugins. Problem is that Ruby is designed to allow this by people who know what they are doing. Many of the Extended ruby Library files add needed methods to base classes, or modify some methods (RubyGems is an example that modifies the global require method, although I never understood why it really needed to do it.) Even though methods are objects, it is difficult to get a reference to an instance method within the class definition (very easy within an instance of the class.) So it is hard to freeze particular instance methods class-wide, without freezing the whole class or module. (Perhaps the gurus on the Ruby Forum know a trick?) An alternative would possibly be (untested): class Sketchup;;Group # Make a copy of method ;copy alias_method(;_copy_,;copy) def self.method_added(sym) # # callback called by Ruby when a new method is defined; # alias_method(;copy,;_copy_) if sym == ;copy # end end EDIT: method_added is a class callback, so must be defined using def self.method_added(sym)
  • Intersecting line with BoundingBox

    10
    0 Votes
    10 Posts
    491 Views
    A
    I hadn't considered that because planes are infinite, but it works well combined with BoundingBox.contains? def intersect_line_boundingbox(line, bb) # Intersect with 6 planes of the bounding box's sides. p0 = bb.corner(0) p1 = bb.corner(7) [ [p0, X_AXIS], [p0, Y_AXIS], [p0, Z_AXIS], [p1, X_AXIS], [p1, Y_AXIS], [p1, Z_AXIS] ].each{|plane| intersection = Geom;;intersect_line_plane(line, plane) # Stop when the intersection point is inside the bounding box. return intersection if bb.contains?(intersection) # else continue } # No intersection (because bb is aside line or behind line start). return nil end There is certainly room for optimization (ie. maybe less than 6 planes, or checking whether BB is behind line start). I first had the code with an infinite straight line (starting from -infinity) and only one intersection with a plane but that failed due to precision errors.
  • Intersecting and Attribute inheritance

    12
    0 Votes
    12 Posts
    498 Views
    TIGT
    Put very simply we need to see what face is under what point. We find all of the 'combined faces' in group3 [after the explosion of copies of group1 and group2 we added into it] - an array which I named faces3. Each face in faces3 has vertices, we consider the first one in each case = vertices[0], we find it's .position, we offset that position a little and rotate it around the Z_AXIS and the vertex.position by steps of 1.degree until we know it's on the 'face'. That's the classify_point method. So now we have a point we know is on the face. We test all of the faces in faces1 until we get on that 'contains' the point - we remember its 'Plot' attributes. We test all of the faces in faces2 until we get on that 'contains' the point - we remember its 'Zone' attributes. We add new attributes [Combo] to 'face' combining these two sets of attributes... You could probably do with testing group1 and group2 faces for both attribute dictionaries, since you can't be sure which group is which in the selection - unless they have 'names' that tell us?
  • UI::WebDialog =&gt; set_html =&gt; large HTML pages

    12
    0 Votes
    12 Posts
    2k Views
    N
    Thanks guys for the hints! I got a message from Noel!
  • Mysterious change of group bounding box coords

    3
    0 Votes
    3 Posts
    138 Views
    sdmitchS
    thomthom, Thanks for the help. The solution was to obvious I guess.
  • Using win32ole on startup

    14
    0 Votes
    14 Posts
    485 Views
    S
    @thomthom said: For timer delay you should immediately stop the timer in it's block - because if a modal window appear within the block, like a messagebox or an error message (which you get when you start SU) will make the timer repeat until the modal window is closed. <span class="syntaxdefault"><br />timer </span><span class="syntaxkeyword">=</span><span class="syntaxdefault"> UI</span><span class="syntaxkeyword">.</span><span class="syntaxdefault">start_timer</span><span class="syntaxkeyword">(</span><span class="syntaxdefault">0</span><span class="syntaxkeyword">,</span><span class="syntaxdefault">false</span><span class="syntaxkeyword">){<br /></span><span class="syntaxdefault">  UI</span><span class="syntaxkeyword">.</span><span class="syntaxdefault">stop_timer</span><span class="syntaxkeyword">(</span><span class="syntaxdefault">timer</span><span class="syntaxkeyword">)<br /></span><span class="syntaxdefault">  </span><span class="syntaxcomment"># ...<br /></span><span class="syntaxkeyword">}<br />&nbsp;</span><span class="syntaxdefault"></span> Hi Thom and TIG, that's exactly how ì solved it. Noticed the fact that a UI.messagebox in the block will make the timer repeat too. Never used UI.start_timer before but I can think of some nice implementations of it.
  • Purpose of Single Line Consoles for Ruby?

    10
    0 Votes
    10 Posts
    499 Views
    thomthomT
    @chris fullmer said: I think I can see that I'm conflating the concepts of editor vs. console. If so, I guess I only see the need for a built in simple editor, and I don't comprehend what a single line console is good for. I think this is the key here. You use it to develop plugins / snippets directly into SketchUp. (Which makes sense as you get a direct result.) Personally I'm too paranoid that SketchUp crashes and bye-bye goes all my work. And I have grown to rely heavily on the features Notepad++ or Sublime2 gives me that I always write my code in an editor. I use the console for debug output, reloading, and minor testing (multiple command over multiple lines, or multi-command-one-liners.) (Btw, isn't it Shift+Return for the new-line in the SU developer console?)
  • [API] Menu.add_item( caption, index )

    8
    0 Votes
    8 Posts
    527 Views
    J
    Just confirmed it does work in sub-menus. [image: 8NrV_2012-11-04_134125.jpg]
  • WebDialogs - The Lost Manual — R1 09 November 2009

    43
    0 Votes
    43 Posts
    23k Views
    thomthomT
    hm.. interesting. Wasn't aware of this Pages feature. Nice.
  • Losing focus on window

    2
    0 Votes
    2 Posts
    550 Views
    thomthomT
    Some times the toolwindows get the focus. Say if you undo a change where you edited a material in the Material Editor - SU will send the focus back to that window. Annoying.
  • Scaling a component with subcomponents

    12
    0 Votes
    12 Posts
    394 Views
    F
    Hey Fredo6, I actually mentionned you plugin in my other post and how I was trying to figure out how you did it. Only problem is you're a ruby/sketchup god and I'm more of a peasant. Thanks to Chris, I now know that what I'm trying to accomplish is called parametric modeling, learn something new everyday . So I did a search for that and came up with this file parametric.rb (attached) and a couple of interesting websites, though I haven't had a chance to go through them so I apologize if they aren't that good. http://drivingdimensions.com/SketchUp/FAQ/ http://www.sketchup.com/intl/en/download/rubyscripts.html [EDIT: attachment removed by admin]
  • Creating a nested component

    4
    0 Votes
    4 Posts
    409 Views
    TIGT
    Entering a faces edges/vertices 'in order' to add a clone into another context will determine the normal of the new_face. But it's always possible that the original face has been reversed if you took the edges ? So get the original_face.normal and compare it with the new_face.normal [allowing for the transformation differences!] - then if they don't match you must reverse the new_face...

Advertisement