Geodome frames for SU / geodome_frame.rb

# Tuk Bredsdorff, 2012.
# Script to add a timber frame to a geodome model.

require "sketchup.rb"


module GeodomeFramer
	
	# Default width, depth
	STRUT_SIZE = [5.cm, 10.cm]

	# Primary strut colors. If there are more strut lengths
	# than colors here then a random one will be assigned.
	PRIMARY_COLORS = ["Red", "Blue", "Green", "Yellow"]
	STRUT_NAMES = ["A", "B", "C", "D"]

	def edges_in_selection
		selection = Sketchup.active_model.selection
		return selection.find_all { |e| e.kind_of?(Sketchup::Edge) }
	end

	# Create a strut (as a box in a group)
	# along an edge, facing a center point.
	def create_strut edge, center, size
		entities = Sketchup.active_model.active_entities
		width, depth = size
	
		vec_to_center = edge.start.position.vector_to center
		plane_intersecting_with_center = Geom.fit_plane_to_points center, edge.start.position, edge.end.position
	
		# We need a vector in the plane of the top of the strut.
		# It is the same as the normal to the plane intersecting with the center
		# which is the same as the three first values in the plane we just calculated.
		width_vec = Geom::Vector3d.new plane_intersecting_with_center[0..2]
		width_vec.length = width / 2
	
		# We add half a strut-width to the end points
		# of the edge to get the corners of the top of the strut.
		e1 = edge.start.position + width_vec
		e2 = edge.start.position - width_vec
		e3 = edge.end.position - width_vec
		e4 = edge.end.position + width_vec
	
		# We add a group for the strut and create the strut top face
		strut_group = entities.add_group
		strut_top_rect = [e1, e2, e3, e4]
		strut_top_face = strut_group.entities.add_face strut_top_rect
	
		# Push pull the top face to create the final strut
		strut_top_face.pushpull -depth
	
		return strut_group
	end

	# Loops through all edges in the current selection and
	# creates struts. And adds all the struts to a group.
	def create_frame edges, center, size
		strut_lengths_sorted = find_strut_lengths.keys.sort
		all_struts = []
		for edge in edges
			strut = create_strut edge, center, size
			index = strut_lengths_sorted.index round(edge.length)
			strut.name = STRUT_NAMES[index]
			strut.material = PRIMARY_COLORS[index]
			all_struts << strut
		end
		frame_group = Sketchup.active_model.active_entities.add_group all_struts
		frame_group.name = "Geodome frame"
	end

	# Print all entities.
	# TODO: Fails. Test properly for entities attribute?
	def print_entities entities=nil
		if not entities
			entities = Sketchup.active_model.entities
		end
	
		for e in entities
			puts e
			if e.entities
				print_entities e.entities
			end
		end
	end

	def round f
		return (f*10000).round/10000.0
	end

	# Which strut lengths  and counts are this dome composed of?
	# Returns an hash table like this:
	# {
	#   48.0376=>{"length"=>48.037567357133, "count"=>30},
	#   56.8284=>{"length"=>56.8283548261602, "count"=>80}
	# }
	# The length keys are Length objects and we store them to
	# so we can display nicely formatted lengths using .to_s later.
	def find_strut_lengths
		strut_info = {}
	
		for e in edges_in_selection
			# We need to round the lengths as they
			# can differ in the last very insignificant decimals.
			rounded_float = round e.length
		
			if not strut_info[rounded_float]
				strut_info[rounded_float] = {'count' => 1, 'length' => e.length}
			else
				strut_info[rounded_float]['count'] += 1
			end
		end
	
		return strut_info
	end

	# Print an overview of the struts
	def print_strut_lengths
		strut_info = find_strut_lengths
		strut_lengths_sorted = strut_info.keys.sort
		puts "#{strut_info.size} strut lengths:"
	
		strut_lengths_sorted.each_with_index do | length_rounded, index |
			count = strut_info[length_rounded]['count']
			length = strut_info[length_rounded]['length'].to_s
			name = STRUT_NAMES[index]
			color = PRIMARY_COLORS[index]
			puts "#{count} x #{name} (#{color}): #{length}"
		end
	end

	def main
		Sketchup.active_model.start_operation "Geodome frame", true
			selection = Sketchup.active_model.selection
		
			# For now we assume the geodesic dome
			# has its center in the origin.
			center = [0, 0, 0]
		
			if selection.count > 0
				edges = edges_in_selection
				if edges.nitems > 0
					prompts = ["Width:", "Depth:"]
					size = inputbox prompts, STRUT_SIZE, "Strut Dimensions"
					if size
						create_frame edges, center, size
						print_strut_lengths
					end
				else
					UI.messagebox "No edges in selection to build from."
				end
			else
				UI.messagebox "Please select geodome geometry for construction."
			end
		Sketchup.active_model.commit_operation
	end

	unless file_loaded? __FILE__
		UI.menu("Plugins").add_item("Geodome frame") { main }
	end

	file_loaded __FILE__
end
Tip: Filter by directory path e.g. /media app.js to search for public/media/app.js.
Tip: Use camelCasing e.g. ProjME to search for ProjectModifiedEvent.java.
Tip: Filter by extension type e.g. /repo .js to search for all .js files in the /repo directory.
Tip: Separate your search with spaces e.g. /ssh pom.xml to search for src/ssh/pom.xml.
Tip: Use ↑ and ↓ arrow keys to navigate and return to view the file.
Tip: You can also navigate files with Ctrl+j (next) and Ctrl+k (previous) and view the file with Ctrl+o.
Tip: You can also navigate files with Alt+j (next) and Alt+k (previous) and view the file with Alt+o.