# Name : HouseTool # Description : Create simple houses for mass modeling applications # Author : Didier Bur and @Last (front end stuff, mouse initialization etc.) # Tim (house & roof stuff) # Usage : click 2 points, create a simple house # Type : tool # Revised Status: 2004,10,18 still having distorted triangle problem # 2004,10,22 almost readly for prime time, you will get # an odd looking house, if you dont dont pull the @length # of the house greater than its @width. #----------------------------------------------------------------------------- # 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' class Housetool # 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 # sets the default slab settings @width = 700.cm if not @width @height = 300.cm if not @height # Dialog box prompts = ["House width", "House height "] values = [@width, @height] results = inputbox prompts, values, "House parameters" return if not results @width, @height = results 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 "House 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 House ", 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) model = view.model model.start_operation "Create house" # Math::PI is better, but... (DB comment) # Computes the 2 other points pi = 3.141592653589793 # 180 degree angle = pi radians pi0 = 0.7853981633974482 # 45 degree angle = pi/4 radians pi2 = 1.5707963267948965 # 90 degree angle = pi/2 radians pi3 = 4.7123889803846895 # 270 degree angle = 3*pi/2 pi4 = 6.283185307179586 # 360 degree angle = 2*pi radians hip = @width/2/Math.sin(pi0) #length of roof hip in plan pt1 = @ip1.position pt2 = @ip2.position pt2 = p2 #@pt1 = @ip1.position #@pt2 = @ip2.position pt1=Geom::Point3d.new(pt1[0],pt1[1],pt1[2]+@height) pt2=Geom::Point3d.new(pt2[0],pt2[1],pt2[2]+@height) @pt1=Geom::Point3d.new(pt1[0],pt1[1],pt1[2]) @pt2=Geom::Point3d.new(pt2[0],pt2[1],pt2[2]) @hip = @width/2/Math.sin(pi0) #---create new points by using the co-cords x,y,z of each #---and add the PITCH to each @pt1h=Geom::Point3d.new(@pt1[0],@pt1[1],@pt1[2]+@width/4) @pt2h=Geom::Point3d.new(@pt2[0],@pt2[1],@pt2[2]+@width/4) #---Rotation definition for around Z axis def translate_rotate_z(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 #---Rotation definition for around Y axis def translate_rotate_y(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,1,0),angle) result = p_temp.transform(rotate) return result end #------create a new point pt1r @pt1r = translate_rotate_z( pt1, pt2, @width, -pi2) #------create a new point pt2r @pt2r = translate_rotate_z( pt2, pt1, @width, pi2) base = Sketchup.active_model.entities.add_face([@pt1, @pt2, @pt2r, @pt1r]) base.pushpull @height #------PITCH-create a new point pt1ch @pt1ch = translate_rotate_z( @pt1h, @pt2h, @hip, -pi/4) #------create roof plane base = Sketchup.active_model.entities.add_face([@pt1, @pt1r, @pt1ch]) #------PITCH-create a new point pt2ch @pt2ch = translate_rotate_z( @pt2h, @pt1h, @hip, pi/4) #------create roof plane base = Sketchup.active_model.entities.add_face([@pt2, @pt2r, @pt2ch]) #------create other 2 roof planes base = Sketchup.active_model.entities.add_face([@pt1, @pt1ch, @pt2ch, @pt2]) base = Sketchup.active_model.entities.add_face([@pt1r, @pt1ch, @pt2ch, @pt2r]) #---------------------------------------------------------------------------- view = model.active_view view.zoom_extents #---------------------------------------------------------------------------- model.commit_operation end #---------------------------------------------------------------------------- # Draw the geometry def draw_geometry(pt1, pt2, view) view.draw_line(pt1, pt2) end end # class housetool #----------------------------------------------------------------------------- # This functions is just a shortcut for selecting the new tool def Housetool Sketchup.active_model.select_tool Housetool.new end if( not file_loaded?("housetool.rb") ) UI.menu("Plugins").add_item("House ") { Sketchup.active_model.select_tool Housetool.new } end #----------------------------------------------------------------------------- file_loaded("housetool.rb")