# Name : Walltool # Description : Create a wall segment in a group # Author : Didier Bur # Usage : Select plugins/Multi-layer wall segment, enter values and height, click 2 points # Date : 01.Oct.2oo4 # Type : tool # History: 1.0 (01.Oct.2oo4) - beta version #----------------------------------------------------------------------------- # Permission to use, copy, modify, and distribute this software for # any purpose and without fee is hereby granted, provided that the above # copyright notice appear in all copies. # THIS SOFTWARE IS PROVIDED "AS IS" AND WITHOUT ANY EXPRESS OR # IMPLIED WARRANTIES, INCLUDING, WITHOUT LIMITATION, THE IMPLIED # WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE. #----------------------------------------------------------------------------- require 'sketchup.rb' #----------------------------------------------------------------------------- def polar(p1, p2, d, angle) v = p2 - p1 v.length = d translate = Geom::Transformation.translation(v) p_temp = p1.transform(translate) rotate = Geom::Transformation.rotation( p1, Geom::Vector3d.new(0, 0, 1), angle) result = p_temp.transform(rotate) return result end class MultiwallTool # This is the standard Ruby initialize method that is called when you create # a new object. def initialize @ip1 = nil @ip2 = nil @xdown = 0 @ydown = 0 mat = [] mat_names = [] inputbox1 = %w[Yes No].join("|") inputbox2 = %w[Left Center Right].join("|") layer_number = %w[1 2 3 4].join("|") array_of_dropdowns = [inputbox1, inputbox2, layer_number] # sets the default wall settings $axis = "No" if not $axis $just = "Center" if not $just $layer_n = 3 if not $layer_n $height = 250.cm if not $height # Dialog box 1: main params prompts = ["Draw walls axis ", "Wall positionning ", "Number of layers ", "Wall height "] values = [$axis, $just, $layer_n, $height] $axis, $just, $layer_n, $height = inputbox prompts,values, array_of_dropdowns, "Wall main parameters" model = Sketchup.active_model materials=model.materials all_colors = Sketchup::Color.names mat_names = materials.collect {|m| m.name} if mat_names.empty? mat = [all_colors.join("|")] else mat = [mat_names.join("|")] end layer_1 = mat layer_2 = mat layer_3 = mat layer_4 = mat # sets the default layers settings $layer_wid_1 = 3.cm if not $layer_wid_1 $layer_wid_2 = 5.cm if not $layer_wid_2 $layer_wid_3 = 25.cm if not $layer_wid_3 $layer_wid_4 = 3.cm if not $layer_wid_4 $layer_mat_1 = "None" if not $layer_mat_1 $layer_mat_2 = "None" if not $layer_mat_2 $layer_mat_3 = "None" if not $layer_mat_3 $layer_mat_4 = "None" if not $layer_mat_4 # Dialog box 2: materials and widths if( $layer_n == 1 ) array_of_layers = [layer_1] prompts = ["Layer 1 material ", "Layer 1 width "] values = [$layer_mat_1, $layer_wid_1] $layer_mat_1, $layer_wid_1, = inputbox prompts, values, array_of_layers, "Layers parameters" $layer_mat_2 = nil $layer_wid_2 = nil $layer_mat_3 = nil $layer_wid_3 = nil $layer_mat_4 = nil $layer_wid_4 = nil $wall_width = $layer_wid_1 end # of if2 if( $layer_n == 2 ) array_of_layers = [layer_1, layer_2] prompts = ["Layer 1 material ", "Layer 2 material ", "Layer 1 width ", "Layer 2 width "] values = [$layer_mat_1, $layer_mat_2, $layer_wid_1, $layer_wid_2] $layer_mat_1, $layer_mat_2, $layer_wid_1, $layer_wid_2 = inputbox prompts, values, array_of_layers, "Layers parameters" $layer_mat_3 = nil $layer_wid_3 = nil $layer_mat_4 = nil $layer_wid_4 = nil $wall_width = $layer_wid_1 + $layer_wid_2 end # of if2 if( $layer_n == 3 ) array_of_layers = [layer_1, layer_2, layer_3] prompts = ["Layer 1 material ", "Layer 2 material ", "Layer 3 material ", "Layer 1 width ", "Layer 2 width ", "Layer 3 width "] values = [$layer_mat_1, $layer_mat_2, $layer_mat_3, $layer_wid_1, $layer_wid_2, $layer_wid_3] $layer_mat_1, $layer_mat_2, $layer_mat_3, $layer_wid_1, $layer_wid_2, $layer_wid_3 = inputbox prompts, values, array_of_layers, "Layers parameters" $layer_mat_4 = nil $layer_wid_4 = nil $wall_width = $layer_wid_1 + $layer_wid_2 + $layer_wid_3 end # of if3 if( $layer_n == 4 ) array_of_layers = [layer_1, layer_2, layer_3, layer_4] prompts = ["Layer 1 material ", "Layer 2 material ", "Layer 3 material ", "Layer 4 material ", "Layer 1 width ", "Layer 2 width ", "Layer 3 width ", "Layer 4 width "] values = [$layer_mat_1, $layer_mat_2, $layer_mat_3, $layer_mat_4, $layer_wid_1, $layer_wid_2, $layer_wid_3, $layer_wid_4] $layer_mat_1, $layer_mat_2, $layer_mat_3, $layer_mat_4, $layer_wid_1, $layer_wid_2, $layer_wid_3, $layer_wid_4 = inputbox prompts, values, array_of_layers, "Layers parameters" $wall_width = $layer_wid_1 + $layer_wid_2 + $layer_wid_3 + $layer_wid_4 end # of if4 end # The activate method is called by SketchUp when the tool is first selected. # it is a good place to put most of your initialization def activate # The Sketchup::InputPoint class is used to get 3D points from screen # positions. It uses the SketchUp inferencing code. # In this tool, we will have two points for the endpoints of the line. @ip1 = Sketchup::InputPoint.new @ip2 = Sketchup::InputPoint.new @ip = Sketchup::InputPoint.new @drawn = false # This sets the label for the VCB Sketchup::set_status_text "Wall length", SB_VCB_LABEL self.reset(nil) end # deactivate is called when the tool is deactivated because # a different tool was selected def deactivate(view) view.invalidate if @drawn end # The onMouseMove method is called whenever the user moves the mouse. # because it is called so often, it is important to try to make it efficient. # In a lot of tools, your main interaction will occur in this method. def onMouseMove(flags, x, y, view) if( @state == 0 ) # We are getting the first end of the line. Call the pick method # on the InputPoint to get a 3D position from the 2D screen position # that is bassed as an argument to this method. @ip.pick view, x, y if( @ip != @ip1 ) # if the point has changed from the last one we got, then # see if we need to display the point. We need to display it # if it has a display representation or if the previous point # was displayed. The invalidate method on the view is used # to tell the view that something has changed so that you need # to refresh the view. view.invalidate if( @ip.display? or @ip1.display? ) @ip1.copy! @ip # set the tooltip that should be displayed to this point view.tooltip = @ip1.tooltip end else # Getting the second end of the line # If you pass in another InputPoint on the pick method of InputPoint # it uses that second point to do additional inferencing such as # parallel to an axis. @ip2.pick view, x, y, @ip1 view.tooltip = @ip2.tooltip if( @ip2.valid? ) view.invalidate # Update the length displayed in the VCB if( @ip2.valid? ) length = @ip1.position.distance(@ip2.position) Sketchup::set_status_text length.to_s, SB_VCB_VALUE end # Check to see if the mouse was moved far enough to create a line. # This is used so that you can create a line by either draggin # or doing click-move-click if( (x-@xdown).abs > 10 || (y-@ydown).abs > 10 ) @dragging = true end end end # The onLButtonDOwn method is called when the user presses the left mouse button. def onLButtonDown(flags, x, y, view) # When the user clicks the first time, we switch to getting the # second point. When they click a second time we create the line if( @state == 0 ) @ip1.pick view, x, y if( @ip1.valid? ) @state = 1 Sketchup::set_status_text "End of wall axis ", SB_PROMPT @xdown = x @ydown = y end else # create the line on the second click if( @ip2.valid? ) self.create_geometry(@ip1.position, @ip2.position,view) self.reset(view) end end # Clear any inference lock view.lock_inference end # The onLButtonUp method is called when the user releases the left mouse button. def onLButtonUp(flags, x, y, view) # If we are doing a drag, then create the line on the mouse up event if( @dragging && @ip2.valid? ) self.create_geometry(@ip1.position, @ip2.position,view) self.reset(view) end end # onKeyDown is called when the user presses a key on the keyboard. # We are checking it here to see if the user pressed the shift key # so that we can do inference locking def onKeyDown(key, repeat, flags, view) if( key == CONSTRAIN_MODIFIER_KEY && repeat == 1 ) @shift_down_time = Time.now # if we already have an inference lock, then unlock it if( view.inference_locked? ) # calling lock_inference with no arguments actually unlocks view.lock_inference elsif( @state == 0 && @ip1.valid? ) view.lock_inference @ip1 elsif( @state == 1 && @ip2.valid? ) view.lock_inference @ip2, @ip1 end end end # onKeyUp is called when the user releases the key # We use this to unlock the interence # If the user holds down the shift key for more than 1/2 second, then we # unlock the inference on the release. Otherwise, the user presses shift # once to lock and a second time to unlock. def onKeyUp(key, repeat, flags, view) if( key == CONSTRAIN_MODIFIER_KEY && view.inference_locked? && (Time.now - @shift_down_time) > 0.5 ) view.lock_inference end end # onUserText is called when the user enters something into the VCB # In this implementation, we create a line of the entered length if # the user types a length while selecting the second point def onUserText(text, view) # We only accept input when the state is 1 (i.e. getting the second point) # This could be enhanced to also modify the last line created if a length # is entered after creating a line. return if not @state == 1 return if not @ip2.valid? # The user may type in something that we can't parse as a length # so we set up some exception handling to trap that begin value = text.to_l rescue # Error parsing the text UI.beep puts "Cannot convert #{text} to a Length" value = nil Sketchup::set_status_text "", SB_VCB_VALUE end return if !value # Compute the direction and the second point pt1 = @ip1.position vec = @ip2.position - pt1 if( vec.length == 0.0 ) UI.beep return end vec.length = value pt2 = pt1 + vec # Create a line self.create_geometry(pt1, pt2, view) self.reset(view) end # The draw method is called whenever the view is refreshed. It lets the # tool draw any temporary geometry that it needs to. def draw(view) if( @ip1.valid? ) if( @ip1.display? ) @ip1.draw(view) @drawn = true end if( @ip2.valid? ) @ip2.draw(view) if( @ip2.display? ) # The set_color_from_line method determines what color # to use to draw a line based on its direction. For example # red, green or blue. view.set_color_from_line(@ip1, @ip2) self.draw_geometry(@ip1.position, @ip2.position, view) @drawn = true end end end # onCancel is called when the user hits the escape key def onCancel(flag, view) self.reset(view) end # The following methods are not directly called from SketchUp. They are # internal methods that are used to support the other methods in this class. # Reset the tool back to its initial state def reset(view) # This variable keeps track of which point we are currently getting @state = 0 # Display a prompt on the status bar Sketchup::set_status_text("Select first end", SB_PROMPT) # clear the InputPoints @ip1.clear @ip2.clear if( view ) view.tooltip = nil view.invalidate if @drawn end @drawn = false @dragging = false end # Create new geometry when the user has selected two points. def create_geometry(p1, p2, view) # define a new group model = Sketchup.active_model group = Sketchup.active_model.entities.add_group entities = group.entities model.start_operation "Create Wall" #if( $axis == "Yes" ) #view.model.entities.add_cline(p1,p2) #end # Computes the other lines pis2 = 1.5707963267948965 pt1_0 = @ip1.position pt2_0 = p2 vec = @ip2.position - pt1_0 rotate_left_1 = Geom::Transformation.rotation( pt1_0, Geom::Vector3d.new(0, 0, 1), pis2) rotate_right_1 = Geom::Transformation.rotation( pt1_0, Geom::Vector3d.new(0, 0, 1), (0 - pis2)) rotate_left_2 = Geom::Transformation.rotation( pt2_0, Geom::Vector3d.new(0, 0, 1), pis2) rotate_right_2 = Geom::Transformation.rotation( pt2_0, Geom::Vector3d.new(0, 0, 1), (0 - pis2)) # ----------------------------------------------------------------------------------------------------- One layer if( $layer_n == 1) vec.length = ($wall_width.to_f / 2 ) translate = Geom::Transformation.translation(vec) pt1t = pt1_0.transform(translate) if( $just == "Left" ) pt1l = pt1t.transform(rotate_left_1) pt1r = pt1_0 end if( $just == "Center" ) pt1l = pt1t.transform(rotate_left_1) pt1r = pt1t.transform(rotate_right_1) end if( $just == "Right" ) pt1l = pt1_0 pt1r = pt1t.transform(rotate_right_1) end #-- pt2t = pt2_0.transform(translate) if( $just == "Left" ) pt2l = pt2t.transform(rotate_left_2) pt2r = pt2_0 end if( $just == "Center" ) pt2l = pt2t.transform(rotate_left_2) pt2r = pt2t.transform(rotate_right_2) end if( $just == "Right" ) pt2l = pt2_0 pt2r = pt2t.transform(rotate_right_2) end base = entities.add_face([pt1l, pt2l, pt2r,pt1r]) base.reverse! base.material = $layer_mat_1 base.pushpull $height end #of if $layer_n == 1 # ----------------------------------------------------------------------------------------------------- Two layers if( $layer_n > 1) # First layer vec.length = $wall_width.to_f translate = Geom::Transformation.translation(vec) pt1t = pt1_0.transform(translate) pt2t = pt2_0.transform(translate) pt1_1l = polar( pt1_0, pt2_0, $wall_width.to_f, pis2 ) pt1_1r = polar( pt1_0, pt2_0, ($wall_width.to_f - $layer_wid_1.to_f), pis2 ) pt2_1l = polar( pt2_0, pt1_0, $wall_width.to_f, (0 - pis2)) pt2_1r = polar( pt2_0, pt1_0, ($wall_width.to_f - $layer_wid_1.to_f), (0 - pis2)) base = entities.add_face([pt1_1l, pt2_1l, pt2_1r,pt1_1r]) base.reverse! base.material = $layer_mat_1 base.pushpull $height # Second layer vec.length = $wall_width.to_f - $layer_wid_1.to_f translate = Geom::Transformation.translation(vec) pt1t = pt1_0.transform(translate) pt2t = pt2_0.transform(translate) pt1_2l = polar( pt1_0, pt2_0, vec.length, pis2 ) pt1_2r = polar( pt1_0, pt2_0, (vec.length - $layer_wid_2.to_f), pis2 ) pt2_2l = polar( pt2_0, pt1_0, vec.length, (0 - pis2)) pt2_2r = polar( pt2_0, pt1_0, (vec.length - $layer_wid_2.to_f), (0 - pis2)) base = entities.add_face([pt1_2l, pt2_2l, pt2_2r,pt1_2r]) base.reverse! base.material = $layer_mat_2 base.pushpull $height base = entities.add_face([pt1_2l, pt2_2l, pt2_2r,pt1_2r]) base.material = $layer_mat_2 end #of if $layer_n > 1 # ----------------------------------------------------------------------------------------------------- Three layers if( $layer_n > 2) # Third layer vec.length = $wall_width.to_f - $layer_wid_1.to_f - $layer_wid_2.to_f translate = Geom::Transformation.translation(vec) pt1t = pt1_0.transform(translate) pt2t = pt2_0.transform(translate) pt1_3l = polar( pt1_0, pt2_0, vec.length, pis2 ) pt1_3r = polar( pt1_0, pt2_0, (vec.length - $layer_wid_3.to_f), pis2 ) pt2_3l = polar( pt2_0, pt1_0, vec.length, (0 - pis2)) pt2_3r = polar( pt2_0, pt1_0, (vec.length - $layer_wid_3.to_f), (0 - pis2)) base = entities.add_face([pt1_3l, pt2_3l, pt2_3r,pt1_3r]) base.reverse! base.material = $layer_mat_3 base.pushpull $height base = entities.add_face([pt1_3l, pt2_3l, pt2_3r,pt1_3r]) base.material = $layer_mat_3 end #of if $layer_n > 2 # ----------------------------------------------------------------------------------------------------- Four layers if( $layer_n > 3) # Fourth layer vec.length = $wall_width.to_f - $layer_wid_1.to_f - $layer_wid_2.to_f - $layer_wid_3.to_f translate = Geom::Transformation.translation(vec) pt1t = pt1_0.transform(translate) pt2t = pt2_0.transform(translate) pt1_4l = polar( pt1_0, pt2_0, vec.length, pis2 ) pt1_4r = polar( pt1_0, pt2_0, (vec.length - $layer_wid_4.to_f), pis2 ) pt2_4l = polar( pt2_0, pt1_0, vec.length, (0 - pis2)) pt2_4r = polar( pt2_0, pt1_0, (vec.length - $layer_wid_4.to_f), (0 - pis2)) base = entities.add_face([pt1_4l, pt2_4l, pt2_4r,pt1_4r]) base.reverse! base.material = $layer_mat_4 base.pushpull $height base = entities.add_face([pt1_4l, pt2_4l, pt2_4r,pt1_4r]) base.material = $layer_mat_4 end #of if $layer_n > 3 # group move if( $just == "Center" ) vec = pt1_0 - pt1_1l vec.length = $wall_width.to_f / 2 translate = Geom::Transformation.translation(vec) group.transform!(translate) end if( $just == "Right" ) vec = pt1_0 - pt1_1l vec.length = $wall_width.to_f translate = Geom::Transformation.translation(vec) group.transform!(translate) end # Draw center line if any if( $axis == "Yes" ) view.model.entities.add_cline(p1,p2) end model.commit_operation end #of def create_geometry # Draw the geometry def draw_geometry(pt1, pt2, view) view.draw_line(pt1, pt2) end end # class WallTool #----------------------------------------------------------------------------- def multiwalltool Sketchup.active_model.select_tool MultiwallTool.new end if( not file_loaded?("multiwalltool.rb") ) add_separator_to_menu("Plugins") UI.menu("Plugins").add_item("Multi-layer wall segment") { multiwalltool } end #----------------------------------------------------------------------------- file_loaded("multiwalltool.rb")