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

    Help with window hole in wall script

    Scheduled Pinned Locked Moved Developers' Forum
    30 Posts 6 Posters 5.5k Views 6 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.
    • D Offline
      Deanby7
      last edited by

      TIG - thanks for that. The components will be glued to the wall component/face and the
      vect=cdi.transformation.zaxis.reverse and vect.length=50.mm worked for me.

      Now all I need to do is work out how to have the user settings (if changed) remembered between uses of the script within the same Sketchup session. I assume I will need to use instance variables and use getters. This is all very alien to me coming from AutoLisp!! More reading required!!

      Thanks for help all.

      1 Reply Last reply Reply Quote 0
      • JQLJ Offline
        JQL
        last edited by

        Deanby7,

        I don't fully understand what's going on but I think your plugin performs a pushpull from the inner face towards the glued component.

        This is pretty much waht Hole punching tool does (Tig's plugin) have you thought of creating a component in the inner face like this:

        Link Preview Image
        Window to Cut Hole on Thick Wall | SketchUcation

        In this tutorial, we demonstrate how to create a component (more exactly a complex component) that can cut the holes on both the outer and inner faces of a thick wall in SketchUp.

        favicon

        (sketchucation.com)

        The component inside could be based on the hole cutting shape from the first component (or whatever's possible) and the material applied could be of the wall.

        www.casca.pt
        Visit us on facebook!

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

          There are 5 'flavors' or variable [aka reference].
          Their 'scopes' differ.

          1. 'Local'- e.g. distance - only remembered in the method [def] or module it is defined in for that one use only. If it's initially created within a 'block' then it is only usable within the block, so if you want to get something from an iteration set it outside - e.g. v=nil;es.each{|e|if e<0;v=e;break;end} - now v is set to the first value in the array es that is < 0... A 'plain' variable starts with a lower case letter [a-z]

          2. 'Instance' - e.g. @distance - remembered across uses in the module it is defined in, if defined within a module's method [def] then it is remembered across uses, and is available to all other methods within that module; if used within a class if persists for that one use, but is available in all other methods within that class [for that one use]. This is useful for remembering user input across uses within a session. It does not work for classes.

          3. 'Class' - e.g. @@distance - remembered across uses in the class in which is defined [note it should be initially created in the body of the class itself NOT within a method [def]], it is also available to all methods in that class - so this is useful for remembering user input within a session, when using a class.

          4. 'Global' - e.g. $LOAD_PATH - never use these, because they are available to all methods, modules and classes, and you could overwrite some other code's variable that ill-advisedly used a global. It is seen in 'system' global variables used by Ruby...

          5. 'Constants' - e.g. VERSION - these can be set within the body of a module and are available to all methods etc within it. It's normal to set up constants as the module code first load. You can actually reset a Constant without issue, other that a warning that you've done it in the Ruby Console. However, you can 'freeze' a constant so it cannot be reset... Use Constants for any thing that is not going to change - like your Extensions 'VERSION' - a Constant always starts with a Capital letter, and often they are defined to be all CAPITALS. SketchUp has many Constants of it's own like ORIGIN, Z_AXIS etc.

          Using variables only keeps then set up during that session, so exiting SketchUp and restarting defaults them again.
          There are several ways to sidestep this...

          a) To save the current user inputs with the particular model use a attribute...
          let's say you've go the user's input as @@dist and it's say 50.mm, then remember it as...
          model.set_attribute('Deanby7', 'dist', @@dist)
          When you next open the model use something like this to recover the stored value...
          @@dist=model.get_attribute('Deanby7', 'dist', 50.mm) model.set_attribute('Deanby7', 'dist', @@dist)
          Note how in this example it defaults to 50mm if the model has never had that attribute set...
          You can attach attribute dictionaries to many things - from the model to definitions and even materials. You save a key/value pair, the value can be:
          integer: 1
          float: 1.2
          length: 50.mm
          boolean: true/false
          string: "cat"
          array: [1, 1.2, 1.234.m, true, false, "dog"]

          To save a 'hash'... 'inspect' first it and set that as a string, then 'eval' that string when you get it back to recover the hash...

          b) To save the current users inputs with SketchUp - so that they become available to the user in all models across sessions use:
          Sketchup.write_default('Deanby7', 'dist', @@dist)
          To recover them use:
          @@dist=Sketchup.read_default('Deanby7', 'dist', 50.mm)
          Sketchup.write_default('Deanby7', 'dist', @@dist)
          These are stored in the SketchUp registry entry for that user.
          You can store integers, floats, lengths, booleans, strings, and arrays and hashes [they are all stored as strings, but SketchUp uses eval etc to restore them into the correct form when read] - but because of the way the Registry stores the backslash \ characters in strings remember in file paths etc to swap them - e.g. str.tr!("\\", "/")

          c) I'd stick with the Registry method in b)... but to save the current users inputs completely outside of SketchUp - so that they become available to the user in all models across sessions, then you could use a file [e.g. .txt] located somewhere that the users can read/write strings [let's call it '[ruby:m0hx44zf]str[/ruby:m0hx44zf]'], and use [ruby:m0hx44zf]lines=IO.read(path_to_file)[/ruby:m0hx44zf] to get its contents - you might want to parse that at [ruby:m0hx44zf]\n[/ruby:m0hx44zf] [or some other unlikely divider you choose] to get sets of key/value pairs...
          [ruby:m0hx44zf]fi=File.open(path_to_file, 'w')
          fi.puts(str)
          fi.close[/ruby:m0hx44zf]
          This overwrites the contents of that file with the latest '[ruby:m0hx44zf]str[/ruby:m0hx44zf]'...
          Of course as you are saving this as a string you need to manually decide that your key [ruby:m0hx44zf]dist[/ruby:m0hx44zf] is a 'length' and use [ruby:m0hx44zf]dist=dist.to_l[/ruby:m0hx44zf] etc.
          Common 'changers' are:
          .to_i a string or a float to an integer, "1".to_i >>> 1, 1.2.to_i >>> 1
          .to_f a string or an integer to a float, "1".to_f >>> 1.0, 1.to_f >>> 1.0
          .to_l a string to a length, "1.m".to_l >>> 1 meter - or 39.3701 inches [SketchUp's base units]
          .to_s a number to a string, 1.234.to_s >>> "1.234"
          etc...

          TIG

          1 Reply Last reply Reply Quote 0
          • D Offline
            Deanby7
            last edited by

            Thanks for suggestions. I have used TIG's holepunch tool and it is very good (complicated code!) but I wanted a script which would pushpull from both sides to retain materials applied to both sides of the wall, ie brick on the outer and a colour on the inside for instance. Doing it this way, populates the reveals with the corresponding material.
            I've also looked at the technique you suggested in url. This assumes you know the wall thickness and construct the reveals on your window components. The window components need to be modified for different wall constructions. Not what I want to do.
            With sdmitch and TIG's help, my script is working ok for my needs. Just need to sort out the user changeable variables to be persistent between script uses now!! 😐
            Thanks anyway.

            1 Reply Last reply Reply Quote 0
            • D Offline
              Deanby7
              last edited by

              TIGs thanks for very useful detailed information regarding variables etc. I thought I would try the simple solution of using instance variables as I only want the values to persist within the current session and model. If I've read your explanation correctly. Not sure I have as this script works on the first run through but on the second run of the script I get an error ......
              Error: #<ArgumentError: comparison of Length with nil failed>

              module DeanBy7
              	module Window_Opening
              		def self.main
              		    if @olt==nil; @olt = 103.mm; end
              			if @twt==nil; @twt = 315.mm; end
              			if @wfd==nil; @wfd = 70.mm; end
              			if @wdi==nil; @wdi = 50.mm; end
              			
              			mod = Sketchup.active_model
              			sel = mod.selection
              			unless sel.grep(Sketchup;;ComponentInstance).empty?
              				prompts = ["Wall external leaf thickness", "Wall total thickness", "Window Frame Depth", "Window inset"]
              				results = UI.inputbox prompts, [@olt,@twt,@wfd,@wdi], "db Window Opening"
              				if results
              					@olt,@twt,@wfd,@wdi = results; mod.start_operation "db Window Opening"
              					sel.grep(Sketchup;;ComponentInstance){|cdi|
              						trn = cdi.transformation
              						org = cdi.transformation.origin
              						wid = cdi.definition.bounds.width
              						hgt = [cdi.definition.bounds.depth,cdi.definition.bounds.height].max
              						fog = cdi.glued_to
              						if fog!=nil
              						    if fog.is_a?(Sketchup;;Face)
              							    p0 = org; ents = mod.active_entities
              							    xa,ya,za = fog.normal.axes
              						    elsif fog.is_a?(Sketchup;;Group)
              							    gt = fog.transformation;ents = fog.entities
              							    p0 = org.transform(gt.inverse)
              							    gf = ents.grep(Sketchup;;Face).find{|f|f.classify_point(p0)==1}
              							    xa,ya,za = gf.normal.axes
              						    end
              							
              						    cdi.glued_to = nil
              						
              						    p1 = p0.offset(xa,wid) 
              						    p2 = p1.offset(ya,-hgt)
              						    p3 = p0.offset(ya,-hgt)
              						    cut = ents.add_face(p0,p1,p2,p3)
              						    cut.pushpull(-@olt)
              						
              						    p0.offset!(za,-@twt)
              						    p1 = p0.offset(xa,wid)
              						    p2 = p1.offset(ya,-hgt)
              						    p3 = p0.offset(ya,-hgt)
              						    cut = ents.add_face(p0,p1,p2,p3)
              						    cut.pushpull(@olt-@twt)
              						
              						    vect = cdi.transformation.zaxis.reverse
                                          vect.length = @wfd+@wdi
              						    cdi.transform!(vect)
                                      else
                                          UI.messagebox "Component not Glued!", MB_OK					  
                                      end
              					}
              					mod.commit_operation
              				end
              			end
              		end
              	end
              end
              
              unless file_loaded?(__FILE__)
              	UI.menu("Plugins").add_item("db Window Opening") { DeanBy7;;Window_Opening.main }
              	file_loaded(__FILE__)
              end
              
              

              Ruby is obviously "picky" about nil and lengths, unlike AutoLisp! 😞
              Or have I done something stupid?

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

                Where you have code like:
                if @olt==nil;...
                change to:
                unless @olt;...
                So IF @olt exists we do nothing, if not we set it up...

                You could also recast the whole line from:
                if @olt==nil; @olt = 103.mm; end
                to
                @olt = 103.mm unless @olt
                which I think reads better...

                Fix all such tests...
                [I think there are 5 ?]

                TIG

                1 Reply Last reply Reply Quote 0
                • G Offline
                  Garry K
                  last edited by

                  Since we are now discussing stylistic issues

                  Personally I like

                  @olt = 103.mm if (! @olt)

                  I generally use parentheses for if statements and for methods even though ruby does not require them. This helps me with speed reading code as you can immediately distinguish a mehod from a var

                  Aslo I prefer ! instead of not
                  and I prefer || instead of or
                  This is because of my C / C++ background

                  I use single quotes ' for simple strings (slightly faster)
                  'my simple string'

                  I use double quotes " when strings include escape characters or variable replacements
                  "my variable#{variable} \n with a newline"

                  1 Reply Last reply Reply Quote 0
                  • D Offline
                    Deanby7
                    last edited by

                    Me again!! 😎
                    Trying to group the reveal entities to make it easy to move a window and it opening if I need to. I have managed to create the group but it ends up being transformed and its not inside the wall groupd as expected. I also get a copy of the elements (not grouped but within the wall group transformed to the wall group origin. Also get a BugSplat! Anyone have an idea what I've done wrong?

                    module DeanBy7
                    	module Window_Opening
                    		def self.main
                    		    @olt = 103.mm if(!@olt)
                    			@twt = 315.mm if(!@twt)
                    			@wfd = 70.mm if(!@wfd)
                    			@wdi = 50.mm if(!@wdi)
                    			
                    			mod = Sketchup.active_model
                    			sel = mod.selection
                    			unless sel.grep(Sketchup;;ComponentInstance).empty?
                    				prompts = ["Wall external leaf thickness", "Wall total thickness", "Window Frame Depth", "Window inset"]
                    				results = UI.inputbox prompts, [@olt,@twt,@wfd,@wdi], "db Window Opening"
                    				if results
                    					@olt,@twt,@wfd,@wdi = results; mod.start_operation "db Window Opening"
                    					sel.grep(Sketchup;;ComponentInstance){|cdi|
                    						trn = cdi.transformation
                    						org = cdi.transformation.origin
                    						wid = cdi.definition.bounds.width
                    						hgt = [cdi.definition.bounds.depth,cdi.definition.bounds.height].max
                    						fog = cdi.glued_to
                    						if fog!=nil
                    						    if fog.is_a?(Sketchup;;Face)
                    							    p0 = org; ents = mod.active_entities
                    							    xa,ya,za = fog.normal.axes
                    						    elsif fog.is_a?(Sketchup;;Group)
                    							    gt = fog.transformation;ents = fog.entities
                    							    p0 = org.transform(gt.inverse)
                    							    gf = ents.grep(Sketchup;;Face).find{|f|f.classify_point(p0)==1}
                    							    xa,ya,za = gf.normal.axes
                    						    end
                    							
                    						    cdi.glued_to = nil
                    						
                    						    p1 = p0.offset(xa,wid) 
                    						    p2 = p1.offset(ya,-hgt)
                    						    p3 = p0.offset(ya,-hgt)
                    						    cut = ents.add_face(p0,p1,p2,p3)
                    						    cut.pushpull(-@olt)
                    						
                    						    p0.offset!(za,-@twt)
                    						    p1 = p0.offset(xa,wid)
                    						    p2 = p1.offset(ya,-hgt)
                    						    p3 = p0.offset(ya,-hgt)
                    						    cut = ents.add_face(p0,p1,p2,p3)
                    						    cut.pushpull(@olt-@twt)
                    							
                    							q0 = p0.offset(za, @twt - @olt)
                    							q2 = p2.offset(za, @twt - @olt)
                    							
                    							rev_faces = []
                    							ents.each {|e|
                                                if e.is_a? Sketchup;;Face
                                                    clp = e.classify_point q0
                    	                            if clp == 2
                    	                                rev_faces << e
                    					            end
                                                end
                                                }
                    							ents.each {|e|
                                                if e.is_a? Sketchup;;Face
                                                    clp = e.classify_point q2
                    	                            if clp == 2
                    	                                rev_faces << e
                    					            end
                                                end
                                                }
                    							
                    						    rev_grp = fog.entities.add_group rev_faces
                    							rev_grp.name = "Reveals"
                    							
                    						    vect = cdi.transformation.zaxis.reverse
                                                vect.length = @wfd+@wdi
                    						    cdi.transform!(vect)
                                            else
                                                UI.messagebox "Component not Glued!", MB_OK					  
                                            end
                    					}
                    					mod.commit_operation
                    				end
                    			end
                    		end
                    	end
                    end
                    
                    unless file_loaded?("dbwindowwallopen")
                    	UI.menu("Plugins").add_item("db Window Opening") { DeanBy7;;Window_Opening.main }
                    	file_loaded("dbwindowwallopen")
                    end
                    
                    

                    I'm learning through a lot of mistakes!!!

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

                      Doon't know the exact issue...
                      BUT I see this...
                      Once you have made a face [cut] and used it to PushPull you can't reuse it.
                      The PushPull destroys it.
                      Recreate it where you expect it to be and then redo the PushPull...
                      I think you do this...

                      Stop your script by adding a 'return' after some operation.
                      Then you can work out where it fails...

                      TIG

                      1 Reply Last reply Reply Quote 0
                      • D Offline
                        Deanby7
                        last edited by

                        Here is an image of my test wall opening!

                        https://dl.dropboxusercontent.com/u/2572853/WallOpenTest.jpg

                        After the pushpull operation, I've tested the face entities to see if they connect with 2 vertices i've offset into the middle of the reveals (diagonally opposite corners of opening). These faces then reside in rev_faces. I'm trying to create a reveal group within the wall group.

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

                          You can't use entities.add_group(somethings) UNLESS the entities==model.active_entities !!!

                          So you need to create an empty group, then add new 'cloned' entities into that...
                          rev_grp = fog.entities.add_group() rev_faces.each{|face| rev_grp.entities.add_face(face.outer_loop.vertices) }

                          This is totally untested !
                          But you get the idea...
                          Make an empty group and add faces into it by 'cloning'.
                          Also remember to erase any redundant faces in the original context.

                          It might be best to do this operation inside a group, initially clone face, material etc, then do the pushpull ? ... ?

                          TIG

                          1 Reply Last reply Reply Quote 0
                          • JQLJ Offline
                            JQL
                            last edited by

                            @deanby7 said:

                            Trying to group the reveal entities to make it easy to move a window and it opening if I need to.

                            Here comes the outsider (growing an interest in your plugin)... That's a nice idea!

                            www.casca.pt
                            Visit us on facebook!

                            1 Reply Last reply Reply Quote 0
                            • D Offline
                              Deanby7
                              last edited by

                              It's the faces produced by the pushpull operations (the reveal faces "rev_faces") that I want to group together. Can this be done?

                              1 Reply Last reply Reply Quote 0
                              • G Offline
                                Garry K
                                last edited by

                                I would probably not use pushpull in that case. First Make the cutouts in the opposing wall faces. Then create the frame group and create the rest of the geometry inside the new group.

                                1 Reply Last reply Reply Quote 0
                                • D Offline
                                  Deanby7
                                  last edited by

                                  By using the pushpull, the reveals inherit the materials of the main wall faces. In the case of the outer face the reveal brickwork pattern is maintained round the corner, which is good! However, it appears that moving geometry with Ruby is a no-no. Therefore if I want to group the reveal geometry for later ease of movement of window etc then I will have to create it as you say within a group. But I will have to add the material and probably align the texture to suit. It's a trade off I think! Unless anyone has a solution!! Here's hoping 😄

                                  1 Reply Last reply Reply Quote 0
                                  • G Offline
                                    Garry K
                                    last edited by

                                    pushpull doesn't align the texture completely. The sides are close. But the top and bottom are not.

                                    Notice the panel raised door through the window opening. It was created with a ruby script with 1 texture.

                                    Parts were created vertically and then moved and rotated into position.


                                    pushpull wall.png

                                    1 Reply Last reply Reply Quote 0
                                    • D Offline
                                      Deanby7
                                      last edited by

                                      I take your point. The sides of the reveal are good enough. The bottom is usually covered by a window cill, and the top reveal could be adjusted if needed but could be adjusted but is not as visible as the sides in views of a building. Your door looks good. Was it created with your own plugin? I'd like to play with that! I have a quick and dirty window builder which I created as my first Ruby project. The code is nor pretty, but it works!!

                                      1 Reply Last reply Reply Quote 0
                                      • G Offline
                                        Garry K
                                        last edited by

                                        All my plugins have a 7 or 14 day free trial.

                                        I have 2 versions of door maker - same code base. Pro makes doors for cabinets and furniture. Gold adds more features including Passage Doors, Bifolds and Entry doors. There are built in features such as pivot hinges for large heavy doors.
                                        http://sketchucation.com/pluginstore?pln=gkware_doormaker

                                        I have been asked to build a window maker along the same lines as door maker.

                                        I also have stair maker for curved stairs and spirals.
                                        http://sketchucation.com/pluginstore?pln=gkware_stairmaker

                                        And I've put quite a bit of work into CabMaker which creates kitchen cabinets, vanities and various types of furniture.
                                        http://sketchucation.com/pluginstore?pln=gkware_cabmaker

                                        I am also currently working on a Wall Maker that works a bit like Chief Architect

                                        1 Reply Last reply Reply Quote 0
                                        • G Offline
                                          Garry K
                                          last edited by

                                          Here's the entry door with side lites and astragal.

                                          wall maker.png

                                          Here's a bit of the wall maker. You can turn on / off the dimension layer. You can edit the length of the wall by double clicking on the dimension text. Change it and the wall changes length. The tool tip shows the length of the wall. You can straight type and let the VCB make the change to length, or provide length and height with a comma between the values (semi colon for users that have their decimal indicator as a comma). There is a button to toggle between top view orthographic and perspective. The wall being created is in ortho.

                                          Walls have a category and a name which corresponds to a file with inside material, outside material, height and wall width. Users can create their own wall specifications.

                                          I just started this plugin a week ago.

                                          edit wall.png

                                          1 Reply Last reply Reply Quote 0
                                          • D Offline
                                            Deanby7
                                            last edited by

                                            Gary K - They look interesting, I will check these out!
                                            With TIG and sdmitch's very informative help I have succeeded with my little script!
                                            Posted here for information. Cheers guys.
                                            Now to look at automating the move of window component with its associated reveals! Hmmm observers?


                                            dbwindowwallopen.rb

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

                                            Advertisement