• Login
sketchucation logo sketchucation
  • Login
πŸ€‘ SketchPlus 1.3 | 44 Tools for $15 until June 20th Buy Now

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 28 Apr 2016, 10:21

    I'm new to Ruby and have written this quick and dirty script to make a hole in a wall (wall has thickness), from the boundary of a window component glued to the outside face. The script pushpull's from inside and outside to retain materials applied to each wall face, and the window component insets as a bonus!
    The script will only work for rectangular window/door components on vertical walls which are orthagonal with global axes, which is the majority of cases!
    The script works fine with wall 'faces', but I'm having trouble punching holes in walls that are part of a group. Also, in the rare cases I might have a non-orthogonal wall ie 45 degrees to x-plane the routine doesn't work if I change the axes to align with the wall prior to running the routine!
    I would appreciate any guidance with the above. I'm sure I'm missing something basic. Apologies in advance for this!! : )

    My code so far is attached.


    dbWindOpen.rb

    1 Reply Last reply Reply Quote 0
    • R Offline
      Rich O Brien Moderator
      last edited by 28 Apr 2016, 10:44

      Did you look at TIG's Hole Punch Tool?

      Link Preview Image
      SketchUp Plugins | PluginStore | SketchUcation

      SketchUp Plugin and Extension Store by SketchUcation provides free downloads of hundreds of SketchUp extensions and plugins

      favicon

      (sketchucation.com)

      You might see something in his code to help

      Download the free D'oh Book for SketchUp πŸ“–

      1 Reply Last reply Reply Quote 0
      • S Offline
        sdmitch
        last edited by 28 Apr 2016, 17:49

        @deanby7 said:

        I'm new to Ruby and have written this quick and dirty script to make a hole in a wall (wall has thickness), from the boundary of a window component glued to the outside face. The script pushpull's from inside and outside to retain materials applied to each wall face, and the window component insets as a bonus!
        The script will only work for rectangular window/door components on vertical walls which are orthagonal with global axes, which is the majority of cases!
        The script works fine with wall 'faces', but I'm having trouble punching holes in walls that are part of a group. Also, in the rare cases I might have a non-orthogonal wall ie 45 degrees to x-plane the routine doesn't work if I change the axes to align with the wall prior to running the routine!
        I would appreciate any guidance with the above. I'm sure I'm missing something basic. Apologies in advance for this!! : )

        My code so far is attached.

        Anytime you are using component instances or groups, you have to consider its' transformation. In this case you would need to "move" the component.transformation.origin into the group by applying the inverse of the groups transformation.

        The bounding box of a component instance changes as its' orientation changes.
        Use the definition bounds of the component instance instead. Use a combination of the bounds.height,.width, and .depth instead of .min and .max.

        Attached is my version of your plugin.


        Deanby7.rb

        Nothing is worthless, it can always be used as a bad example.

        http://sdmitch.blogspot.com/

        1 Reply Last reply Reply Quote 0
        • J Offline
          JQL
          last edited by 28 Apr 2016, 18:16

          @deanby7 said:

          I'm new to Ruby and have written this quick and dirty script to make a hole in a wall (wall has thickness), from the boundary of a window component glued to the outside face...

          I would appreciate any guidance with the above...

          My code so far is attached.

          And then the pros come in the form of assistance...

          @sdmitch said:

          Attached is my version of your plugin.

          I love this forum!

          www.casca.pt
          Visit us on facebook!

          1 Reply Last reply Reply Quote 0
          • D Offline
            Deanby7
            last edited by 28 Apr 2016, 18:47

            Many thanks for the information sdmitch. I love this forum too! I am learning a lot from the experts!

            1 Reply Last reply Reply Quote 0
            • D Offline
              Deanby7
              last edited by 28 Apr 2016, 22:05

              sdmitch - Your revised code works a treat, except the window components don't move into the reveals!
              The transformation is not working and I am struggling to understand what you are doing here.
              cdi.transform!(trn*cdi.transformation.inverse)
              Does "trn" not cancel out "cdi.transformation.inverse" ?

              1 Reply Last reply Reply Quote 0
              • S Offline
                sdmitch
                last edited by 29 Apr 2016, 00:10

                @deanby7 said:

                sdmitch - Your revised code works a treat, except the window components don't move into the reveals!
                The transformation is not working and I am struggling to understand what you are doing here.
                cdi.transform!(trn*cdi.transformation.inverse)
                Does "trn" not cancel out "cdi.transformation.inverse" ?

                I didn't know that you wanted that move so that statement puts the component back where it started so just delete or comment the statement.

                trn is the original component transformation so, by applying the inverse of the current transformation then the original transformation, the component is put back in its original position. You can combine transformations by multiplying them. They will be applied in a "right to left" order.

                Nothing is worthless, it can always be used as a bad example.

                http://sdmitch.blogspot.com/

                1 Reply Last reply Reply Quote 0
                • D Offline
                  Deanby7
                  last edited by 29 Apr 2016, 06:51

                  sdmitch - I'm not getting this transformation thing! I'm sure the penny will drop soon! πŸ˜„

                  I've tried

                  						cdi.glued_to=nil
                  						#cdi.transform!(trn*cdi.transformation.inverse)
                  						pg = org.offset!(ya,olt)
                  						cdi.transform!(pg)
                  
                  

                  The component moves but not where I expected!! πŸ˜’

                  1 Reply Last reply Reply Quote 0
                  • D Offline
                    Deanby7
                    last edited by 29 Apr 2016, 06:55

                    sdmitch - To clarify, I'm trying to move the component along its Y-axis by distance olt. ie into the hole just created.

                    1 Reply Last reply Reply Quote 0
                    • TIGT Offline
                      TIG Moderator
                      last edited by 29 Apr 2016, 07:23

                      If it's a gluing component the glued_to face's normal is always the component's Z axis.
                      If it's glued onto the face it can't be moved in that direction at all.
                      If it's a non-gluing component, then its axes can be in any direction !
                      You have not unequivocally explained that ?
                      If it has any axis in the norm=face.normal direction, then why not transform [translate] it along that vect=norm.reverse to move it 'inwards', making vect.length=2.0 [for say 2"]...
                      cdi.transform!(Geom::Transformation.translation(vect))
                      Assuming ' cdi' is the component instance.
                      If you know the component is a 'gluing' type, but the 'face' is not readily accessible, and want to move it along its current Z axis, then you can get it thus:
                      vect=cdi.transformation.zaxis.reverse
                      vect.length=2.0 or vect.length=50.mm etc
                      Then do the .transform!() ...

                      TIG

                      1 Reply Last reply Reply Quote 0
                      • D Offline
                        Deanby7
                        last edited by 29 Apr 2016, 10:56

                        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
                        • J Offline
                          JQL
                          last edited by 29 Apr 2016, 12:05

                          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 29 Apr 2016, 12:50

                            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 29 Apr 2016, 13:04

                              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 29 Apr 2016, 14:48

                                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 29 Apr 2016, 15:31

                                  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 29 Apr 2016, 16:16

                                    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 29 Apr 2016, 20:52

                                      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 29 Apr 2016, 21:10

                                        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 29 Apr 2016, 21:17

                                          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
                                          • 1
                                          • 2
                                          • 1 / 2
                                          1 / 2
                                          • First post
                                            1/30
                                            Last post
                                          Buy SketchPlus
                                          Buy SUbD
                                          Buy WrapR
                                          Buy eBook
                                          Buy Modelur
                                          Buy Vertex Tools
                                          Buy SketchCuisine
                                          Buy FormFonts

                                          Advertisement