module BTAP::Geometry::Surfaces
Public Class Methods
# File lib/openstudio-standards/btap/geometry.rb, line 549 def self.create_surface(model, name, os_point3d_array, boundary_condition = "", construction = "") os_surface = OpenStudio::Model::Surface.new(os_point3d_array, model) os_surface.setName(name) if OpenStudio::Model::Surface::validOutsideBoundaryConditionValues.include?(boundary_condition) self.set_surfaces_boundary_condition([os_surface], boundary_condition) else puts "boundary condition not set for #{name}" end os_surface.setConstruction(construction) return os_surface end
Azimuth start from Y axis, Tilts starts from Z-axis
# File lib/openstudio-standards/btap/geometry.rb, line 743 def self.filter_by_azimuth_and_tilt(surfaces, azimuth_from, azimuth_to, tilt_from, tilt_to, tolerance = 1.0) return_surfaces = [] surfaces.each do |surface| unless OpenStudio::Model::PlanarSurface::findPlanarSurfaces([surface], OpenStudio::OptionalDouble.new(azimuth_from), OpenStudio::OptionalDouble.new(azimuth_to), OpenStudio::OptionalDouble.new(tilt_from), OpenStudio::OptionalDouble.new(tilt_to), tolerance).empty? return_surfaces << surface end end return return_surfaces end
# File lib/openstudio-standards/btap/geometry.rb, line 674 def self.filter_by_boundary_condition(surfaces, boundary_conditions) #check to see if a string or an array was passed. if boundary_conditions.kind_of?(String) temp = boundary_conditions boundary_conditions = Array.new() boundary_conditions.push(temp) end #ensure boundary conditions are valid boundary_conditions.each do |boundary_condition| unless OpenStudio::Model::Surface::validOutsideBoundaryConditionValues.include?(boundary_condition) raise "ERROR: Invalid Boundary Condition = " + boundary_condition + "Correct Values are:" + OpenStudio::Model::Surface::validOutsideBoundaryConditionValues.to_s end end #create return array. return_array = Array.new() if boundary_conditions.size == 0 or boundary_conditions[0].upcase == "All".upcase return_array = surfaces else surfaces.each do |surface| boundary_conditions.each do |condition| if surface.outsideBoundaryCondition == condition return_array.push(surface) end end end end return return_array end
# File lib/openstudio-standards/btap/geometry.rb, line 732 def self.filter_by_interzonal_surface(surfaces) return_array = Array.new() surfaces.each do |surface| unless surface.adjacentSurface().empty? return_array.push(surface) end return return_array end end
# File lib/openstudio-standards/btap/geometry.rb, line 668 def self.filter_by_non_defaulted_surfaces(surfaces) non_defaulted_surfaces = Array.new() surfaces.each {|surface| non_defaulted_surfaces << surface unless surface.isConstructionDefaulted} return non_defaulted_surfaces end
# File lib/openstudio-standards/btap/geometry.rb, line 704 def self.filter_by_surface_types(surfaces, surfaceTypes) #check to see if a string or an array was passed. if surfaceTypes.kind_of?(String) temp = surfaceTypes surfaceTypes = Array.new() surfaceTypes.push(temp) end surfaceTypes.each do |surfaceType| unless OpenStudio::Model::Surface::validSurfaceTypeValues.include?(surfaceType) raise("ERROR: Invalid surface type = #{surfaceType} Correct Values are: #{OpenStudio::Model::Surface::validSurfaceTypeValues}") end end return_array = Array.new() if surfaceTypes.size == 0 or surfaceTypes[0].upcase == "All".upcase return_array = self else surfaces.each do |surface| surfaceTypes.each do |surfaceType| if surface.surfaceType == surfaceType return_array.push(surface) end end end end return return_array end
- “FixedWindow” , “OperableWindow” , “Door” , “GlassDoor”, “OverheadDoor” , “Skylight”, “TubularDaylightDiffuser”,“TubularDaylightDome”
# File lib/openstudio-standards/btap/geometry.rb, line 595 def self.filter_subsurfaces_by_types(subsurfaces, subSurfaceTypes) #check to see if a string or an array was passed. if subSurfaceTypes.kind_of?(String) temp = subSurfaceTypes subSurfaceTypes = Array.new() subSurfaceTypes.push(temp) end subSurfaceTypes.each do |subSurfaceType| unless OpenStudio::Model::SubSurface::validSubSurfaceTypeValues.include?(subSurfaceType) raise("ERROR: Invalid surface type = #{subSurfaceType} Correct Values are: #{OpenStudio::Model::SubSurface::validSubSurfaceTypeValues}") end end return_array = Array.new() if subSurfaceTypes.size == 0 or subSurfaceTypes[0].upcase == "All".upcase return_array = self else subsurfaces.each do |subsurface| subSurfaceTypes.each do |subSurfaceType| if subsurface.subSurfaceType == subSurfaceType return_array.push(subsurface) end end end end return return_array end
This method calculates the surface area of a 2-D polygon from an array of OpenStudio vertices. It ignores any z vertices. This method assumes that the polygon is complete, has no holes, does not cross itself, and that the vertices are provided in counter-clockwise order. This method is used in cases when you want to find the area of something before creating an OpenStudio surface/subsurface.
Input arguments: vetices: Array
of openstudio vertices.
Output: Area: Float, area of polygot represented by the vertices.
# File lib/openstudio-standards/btap/geometry.rb, line 1557 def self.getSurfaceAreafromVertices(vertices:) area = 0.0 numberVertices = vertices.size # Check that a polygon is actually provided and not just a line or point. Return 0 if the vertices are a line # or point. return 0.0 if numberVertices < 3 # Go through the vertices and get the cross product. This adopted from: # https://web.archive.org/web/20100405070507/http://valis.cs.uiuc.edu/~sariel/research/CG/compgeom/msg00831.html vertices.each_with_index do |vertex, i| j = (i + 1) % numberVertices area += vertex.x.to_f * vertices[j].y.to_f area -= vertex.y.to_f * vertices[j].x.to_f end return area end
This method takes the y projections of a bunch of overlapping line segments and sorts them to determines which are unique and, if they are not unique, which is closest to the current, upwardly pointing, line. If several overlapping segments belong to the same line they are put together (after the ‘subdivide_overlaps’ method broke them apart). The end result is the method returns the closet point downward pointing line segments closest to the given upward pointing line segment.
overlap_segs: This is an array of hashes that looks like:
overlap_seg = { index_a1: i, index_a2: i-1, index_b1: j, index_b2: j-1, point_b1: surf_verts[j], point_b2: surf_verts[j-1], overlap_y: overlap_y }
index_a1: The index of the array of points that cooresponds with the top of line a (points up) index_a1: The index of the array of points that cooresponds with the bottom of line a (points up) index_b1: The index of the array of points that cooresponds with the bottom of line b (points down) index_b1: The index of the array of points that cooresponds with the top of line b (points down) point_b1: The coordinates of the bottom of line b point_b2: The coordinates of the top of line b
overlap_y: A hash that contains the coordinates of the top and bottom of the y projection of the overlapping lines (line a and line b)
overlap_y = { overlap_start: overlap_start, overlap_end: overlap_end }
overlap_start: The y coordinate of the top of the overlap overlap_end: The y coordinate of the bottom of the overlap
index: The index of the array of points that cooresponds with the top of of the current upward pointing line.
point_a1: The coordinates of the top of the first line point_a2: The coordinates of the bottom of the first line This naming convention was chosen because this method was originally designed to work with the ‘make_concave_surfaces’ method (see above). That method choses lines that point up and then sees where they overlap with lines pointing down. The point_1 of each line is the end of the line. In this case a lines point up and b lines point down.
# File lib/openstudio-standards/btap/geometry.rb, line 987 def self.get_overlapping_segments(overlap_segs:, index:, point_a1:, point_a2:) closest_overlaps = [] linea_overlaps = [] # This goes through all the line segments and determines which correspond to the current upward pointing line # segment(line a). It also determines the x coordinate distance between the top and bottom of the overlapping # portions of the line segments. curr_overlap_segs = overlap_segs.select { |seg| (seg[:index_a1] == index) && (seg[:index_a2] == (index - 1)) } curr_overlap_segs.each do |overlap_seg| line_a_x_top = line_segment_overlap_x_coord(y_check: overlap_seg[:overlap_y][:overlap_start], point_b1: point_a1, point_b2: point_a2) line_a_x_bottom = line_segment_overlap_x_coord(y_check: overlap_seg[:overlap_y][:overlap_end], point_b1: point_a1, point_b2: point_a2) line_b_x_top = line_segment_overlap_x_coord(y_check: overlap_seg[:overlap_y][:overlap_start], point_b1: overlap_seg[:point_b1], point_b2: overlap_seg[:point_b2]) line_b_x_bottom = line_segment_overlap_x_coord(y_check: overlap_seg[:overlap_y][:overlap_end], point_b1: overlap_seg[:point_b1], point_b2: overlap_seg[:point_b2]) x_distance_top = line_a_x_top - line_b_x_top x_distance_bottom = line_a_x_bottom - line_b_x_bottom linea_overlap = { dx_top: x_distance_top, dx_bottom: x_distance_bottom, overlap: overlap_seg } linea_overlaps << linea_overlap end # This sorts through the overlapping downward pointing line segments corresponding to the current upward pointing # line a. The overlapping downward pointing line segments closest to the current upward pointing line segment # are kept. The other are discarded. Unique overlapping line segments are kept as well. There should only be # unuique overlapping line segments or overlapping line segments that precisely match one another because of # the 'subdivide_overlaps' method which this method is supposed to work with. linea_overlaps.each do |line_a_overlap| overlaps = linea_overlaps.select { |seg| seg[:overlap][:overlap_y] == line_a_overlap[:overlap][:overlap_y]} if overlaps.size > 1 redundant_overlap = closest_overlaps.select { |dup_seg| dup_seg[:overlap_y] == overlaps[0][:overlap][:overlap_y] } closest_overlaps << (overlaps.min_by { |dup_seg| dup_seg[:dx_top] })[:overlap] if redundant_overlap.empty? elsif overlaps.size == 1 closest_overlaps << overlaps[0][:overlap] end end # This combines the line segments that belong together. These were broken apart because of the # 'subdivide_overlaps' method. overlap_exts = [closest_overlaps[0]] for j in 0..(closest_overlaps.length - 1) index = 0 found = false for l in 0..(overlap_exts.length - 1) if overlap_exts[l][:index_b1] == closest_overlaps[j][:index_b1] && overlap_exts[l][:index_b2] == closest_overlaps[j][:index_b2] index = l found = true break end end if found == false overlap_exts << closest_overlaps[j] index = overlap_exts.length - 1 end for k in 0..(closest_overlaps.length - 1) if (closest_overlaps[j][:index_b1] == closest_overlaps[k][:index_b1]) && (closest_overlaps[j][:index_b2] == closest_overlaps[k][:index_b2]) if closest_overlaps[k][:overlap_y][:overlap_start] >= overlap_exts[index][:overlap_y][:overlap_start] overlap_exts[index][:overlap_y][:overlap_start] = closest_overlaps[k][:overlap_y][:overlap_start] end if closest_overlaps[k][:overlap_y][:overlap_end] <= overlap_exts[index][:overlap_y][:overlap_end] overlap_exts[index][:overlap_y][:overlap_end] = closest_overlaps[k][:overlap_y][:overlap_end] end end end end return overlap_exts end
# File lib/openstudio-standards/btap/geometry.rb, line 763 def self.hide(surfaces) surfaces.each do |surface| if drawing_interface = surface.drawing_interface if entity = drawing_interface.entity entity.visible = false end end end end
This method determines the x coordinate of where a given y coordinate crosses a given line. y_check: The y coordinate that you want to determine the x coordinate for on a line point_b1: The coordinates of the bottom of the line point_b2: The coordinates of the top of the line
# File lib/openstudio-standards/btap/geometry.rb, line 1511 def self.line_segment_overlap_x_coord(y_check:, point_b1:, point_b2:) # If the line is vertical then all x coordinates are the same if point_b1[:x] == point_b2[:x] xcross = point_b2[:x] # If the line is horizontal you cannot find the y intercept elsif (point_b1[:y] == point_b2[:y]) raise("This line is horizontal so no y intercept can be found.") # Otherwise determine the line coefficients and get the intercept else a = (point_b1[:y] - point_b2[:y]) / (point_b1[:x] - point_b2[:x]) b = point_b1[:y] - a * point_b1[:x] xcross = (y_check - b) / a end return xcross end
This method determines if the y component of 2 lines overlap. point_a1: The top of the first line point_a2: The bottom of the first line point_b1: The bottom of the second line point_b2: The top of the second line. This naming convention was chosen because this method was originally designed to work with the ‘make_concave_surfaces’ method (see above). That method choses lines that point up and then sees where they overlap with lines pointing down. The point_1 of each line is the end of the line. In this case a lines point up and b lines point down.
# File lib/openstudio-standards/btap/geometry.rb, line 1473 def self.line_segment_overlap_y?(point_a1:, point_a2:, point_b1:, point_b2:) overlap_start = nil overlap_end = nil # If line a overlaps with the bottom of line b do this: if (point_a1 >= point_b1) && (point_a2 <= point_b1) overlap_start = point_a1 overlap_end = point_b1 # This checks if all of line b is overlapped by line a if point_a1 >= point_b2 overlap_start = point_b2 end # If line a overlaps with the top of line b do this: elsif (point_a1 >= point_b2) && (point_a2 <= point_b2) overlap_start = point_b2 overlap_end = point_a2 # This checks if all of line b is overlapped by line a if point_a2 <= point_b1 overlap_end = point_b1 end # This checks if all of line a fits in line b elsif (point_a1 <= point_b2) && (point_a2 >= point_b1) overlap_start = point_a1 overlap_end = point_a2 end # Overlap vectors always point down. Thus overlap_start is the y location of the top of the overlap vector and # overlap_end is the y location of the bottom of the overlap vector. The overlap vector will later be constructed # using point_b1 and point_b2 and checking which overlaps are closest (and not obstructed) by other overlaps. overlap_y = { overlap_start: overlap_start, overlap_end: overlap_end } return overlap_y end
2018-09-27 Chris Kirney This method takes a surface in the x-y plane (z coordinates are ignored) with an upwardly pointing normal and turns it into convex quadrialaterals. If the original surface is already a convex quadrilateral then this method will go to a lot of trouble to return the same thing (only with the coordinates of the points rounded). If the surface is already a concave surface then this method will return it broken into a bunch of quadrilaters (maybe a triangle here and there). Neither of the above are especially useful. However, the point of this method is if you pass this a concave surface it will return convex surfaces that you can then use with other methods that only apply to convex surfaces (such as a method which fits skylights into a roof). Note that surfaces per say are not returned. Rather, an array containing 4 points arranged in counter clockwise order is returned. These points are also in the x-y plane with an upwardly pointing normal. No z coordinate is returned.
The method works by first looking for upward pointing lines. It then looks for cooresponding downward pointing lines. Since all of the surfaces are closed there should always be enough upward and downward pointing lines. Horizontal lines are ignored. It then checks to see which y projections of the upward and downward pointing lines overlap. It then sees which of these overlaping lines overlap. Ultimately you wind up with a whole bunch of overlapping y projections that coorespond with different upward pointing lines. These overlapping y projects are either unique, or they precisely match other overlapping y projections. The point is that, in the case of a convex shape, an upward pointing line may overlap with some lines close, and some far away, with some lines in between. The method then sorts through the overlapping y projections to see which are closest to a given upward pointing line. It keeps the unique ones, and the ones that are closest. The end result should be downward pointing line segments that correspond to an upward pointing line segment with no intervening lines. The last part of the method assembles the quadrilaterals from the remaining downward pointing line segments which correspond with a given upward pointing line segment.
# File lib/openstudio-standards/btap/geometry.rb, line 796 def self.make_convex_surfaces(surface:, tol: 12) # Note that points on surfaces are given counterclockwise when looking at the surface from the opposite direction as # the outward normal (i.e. the outward normal is pointing at you). I use point_a1, point_a2, point_b1 and point b2 # lots. For this, point_a refers to vectors pointing up. In this case point_a1 is at the top of the vector and # point_a2 is at the bottom of the vector. Contrarily, point_b refers to vectors pointing down. In this case # point_b1 is at the bottom of the vector and point_b2 is at the top. All of this comes about because I cycle # through the points starting at the 2nd point and and going to the last point. I count vectors as starting from # the last point and going toward the current point. # See following where P1 through P4 are the points. When cycling through a is where you start and b is where you # end. the o is the tip of the outward normal pointing at you. # P2b------------aP1 # a b # | | # | o | # | | # b a # P3a-----------bP4 surf_verts = [] # Get the vertices from the surface, keep the x and y coordinates, and turn the vertices from OpenStudio's # data structure to a differet one which is a little easier to deal with. Also, round them to the given # tolerance. This is done because some numbers that should match don't because of tiny errors. surface.vertices.each do |vert| surf_vert = { x: vert.x.to_f.round(tol), y: vert.y.to_f.round(tol), z: vert.z.to_f } surf_verts << surf_vert end # If the surface is a triangle or less then do nothing and return it. return surf_verts if surf_verts.length <= 3 # Adding the first vertex to the end so that it is accounted for. surf_verts << surf_verts[0] # Following we go through the points, look for upward pointing lines, then look for downward pointing lines to # their left (only to the left because everything goes counter-clockwise). If there is a line find how much the # current upward pointing line overlaps with it in the y direction. overlap_segs = [] new_surfs = [] for i in 1..(surf_verts.length - 1) # Is this line segment pointing up? If no, then ignore it and go to the next line segment. if surf_verts[i][:y] > surf_verts[i - 1][:y] # Go through each line segment for j in 1..(surf_verts.length - 1) # Is the line segment to the left of the current (index i) line segment? If no, then ignore it and go to the next one. # I revised this to check if the start or end of the current (index i) line segment is to the left of the # line segment being checked. #if surf_verts[j][:x] < surf_verts[i][:x] and surf_verts[j - 1][:x] < surf_verts[i - 1][:x] if surf_verts[j][:x] < surf_verts[i][:x] || surf_verts[j - 1][:x] < surf_verts[i - 1][:x] # Is the line segment pointing down? If no, then ignore it and go to the next line segment. if surf_verts[j][:y] < surf_verts[j - 1][:y] # Do the y coordinates of the line segment overlap with the current (index i) line segment? If no # then ignore it and go to the next line segment. overlap_y = line_segment_overlap_y?(point_a1: surf_verts[i][:y], point_a2: surf_verts[i - 1][:y], point_b1: surf_verts[j][:y], point_b2: surf_verts[j - 1][:y]) unless overlap_y[:overlap_start].nil? || overlap_y[:overlap_end].nil? unless overlap_y[:overlap_start] == overlap_y[:overlap_end] overlap_seg = { index_a1: i, index_a2: i - 1, index_b1: j, index_b2: j - 1, point_b1: surf_verts[j], point_b2: surf_verts[j - 1], overlap_y: overlap_y } overlap_segs << overlap_seg end end end end end end end # This part: # 1. Subdivides the overlapping segments found above into either unique overlaps between the upward and downward # pointing lines or overlapping segments that exactly match one another. # 2. Goes through each upward pointing line and finds the closest overlapping downward pointing line segments (if # these downward pointing segments belong together they are re-attached). # 3. Makes quadrilaterals (or triangles as the case may be) out of each upward pointing line and the closest # downward pointing line segment. if overlap_segs.length > 1 # Subdivide the overlapping segments found above into either unique overlaps between the upward and downward # pointing lines or overlapping segments that exactly match one another. overlap_segs = subdivide_overlaps(overlap_segs: overlap_segs) # Remove redundant overlapping segments recheck = true while recheck recheck = false # Go through each overlapping segment and look for duplicate segments overlap_segs.each_with_index do |ind_overlap_seg, seg_index| # Find duplicate overlapping segments redundant_segs = overlap_segs.select { |check_seg| check_seg == ind_overlap_seg} # Remove the first one and then restart the while loop to recompile the seg_index if redundant_segs.size > 1 overlap_segs.delete_at(seg_index) recheck = true end end end for i in 1..(surf_verts.length - 1) # Does the line point up? No then ignore and go on to the next one. if surf_verts[i][:y] > surf_verts[i - 1][:y] # Finds the closest overlapping downward pointing line segments that correspond to this upward pointing # line (if some of these downward pointing segments belong together then re-attached them). closest_overlaps = get_overlapping_segments(overlap_segs: overlap_segs, index: i, point_a1: surf_verts[i], point_a2: surf_verts[i - 1]) closest_overlaps = closest_overlaps.sort_by {|closest_overlap| closest_overlap[:overlap_y][:overlap_start]} # Create the quadrilaterals out of the downward pointing line segments closest to the current upward # pointing line. for j in 0..(closest_overlaps.length - 1) new_surf = [] z_loc = surf_verts[closest_overlaps[j][:index_a1]][:z] y_loc = closest_overlaps[j][:overlap_y][:overlap_start] x_loc = line_segment_overlap_x_coord(y_check: y_loc, point_b1: surf_verts[closest_overlaps[j][:index_a1]], point_b2: surf_verts[closest_overlaps[j][:index_a2]]) new_surf << {x: x_loc.to_f.round(tol), y: y_loc.to_f.round(tol), z: z_loc.to_f.round(tol)} x_loc = line_segment_overlap_x_coord(y_check: y_loc, point_b1: closest_overlaps[j][:point_b1], point_b2: closest_overlaps[j][:point_b2]) z_loc = surf_verts[closest_overlaps[j][:index_b2]][:z] new_surf << {x: x_loc.to_f.round(tol), y: y_loc.to_f.round(tol), z: z_loc.to_f.round(tol)} y_loc = closest_overlaps[j][:overlap_y][:overlap_end] x_loc = line_segment_overlap_x_coord(y_check: y_loc, point_b1: closest_overlaps[j][:point_b1], point_b2: closest_overlaps[j][:point_b2]) z_loc = surf_verts[closest_overlaps[j][:index_b1]][:z] new_surf << {x: x_loc.to_f.round(tol), y: y_loc.to_f.round(tol), z: z_loc.to_f.round(tol)} x_loc = line_segment_overlap_x_coord(y_check: y_loc, point_b1: surf_verts[closest_overlaps[j][:index_a1]], point_b2: surf_verts[closest_overlaps[j][:index_a2]]) z_loc = surf_verts[closest_overlaps[j][:index_a2]][:z] new_surf << {x: x_loc.to_f.round(tol), y: y_loc.to_f.round(tol), z: z_loc.to_f.round(tol)} # Check if this should be a triangle. for k in 0..(new_surf.length - 1) break_now = false for l in 0..(new_surf.length - 1) next if k == l if (new_surf[k][:x] == new_surf[l][:x]) && (new_surf[k][:y] == new_surf[l][:y]) new_surf.delete_at(l) break_now = true break end end if break_now == true break end end new_surfs << new_surf end end end elsif overlap_segs.length == 1 # There is only one overlapping downward line, thus this is a quadrilateral already so just return it. # Remove the last vertex as we had artificially added it at the start. surf_verts.pop new_surfs << surf_verts end return new_surfs end
This method will rotate a surface @param planar_surfaces [Array<OpenStudio::Model::Surface>] an array of surfaces @param azimuth_degrees [Float] rotation value @param tilt_degrees [Float] rotation value @param translation_vector [OpenStudio::Vector3d] a vector along which to move all surfaces @return [OpenStudio::Model::Model] the model object.
# File lib/openstudio-standards/btap/geometry.rb, line 567 def self.rotate_tilt_translate_surfaces(planar_surfaces, azimuth_degrees, tilt_degrees = 0.0, translation_vector = OpenStudio::Vector3d.new(0.0, 0.0, 0.0)) # Identity matrix for setting space origins azimuth_matrix = OpenStudio::Transformation::rotation(OpenStudio::Vector3d.new(0, 0, 1), azimuth_degrees * Math::PI / 180) tilt_matrix = OpenStudio::Transformation::rotation(OpenStudio::Vector3d.new(0, 0, 1), tilt_degrees * Math::PI / 180) translation_matrix = OpenStudio::createTranslation(translation_vector) planar_surfaces.each do |surface| surface.changeTransformation(azimuth_matrix) surface.changeTransformation(tilt_matrix) surface.changeTransformation(translation_matrix) end return planar_surfaces end
# File lib/openstudio-standards/btap/geometry.rb, line 580 def self.set_fenestration_to_wall_ratio(surfaces, ratio, offset = 0, height_offset_from_floor = true, floor = "all") surfaces.each do |surface| result = surface.setWindowToWallRatio(ratio, offset, height_offset_from_floor) raise("Unable to set FWR for surface " + surface.name.get.to_s + " . Possible reasons are if the surface is not a wall, if the surface is not rectangular in face coordinates, if requested ratio is too large (window area ~= surface area) or too small (min dimension of window < 1 foot), or if the window clips any remaining sub surfaces. Otherwise, removes all existing windows and adds new window to meet requested ratio.") unless result end return surfaces end
This method sets the boundary condition for a surface and it’s matching surface. If set to adiabatic, it will remove all subsurfaces since E+ cannot have adiabatic sub surfaces.
# File lib/openstudio-standards/btap/geometry.rb, line 640 def self.set_surfaces_boundary_condition(model, surfaces, boundaryCondition) surfaces = BTAP::Common::validate_array(model, surfaces, "Surface") if OpenStudio::Model::Surface::validOutsideBoundaryConditionValues.include?(boundaryCondition) surfaces.each do |surface| if boundaryCondition == "Adiabatic" #need to remove subsurface as you cannot have a adiabatic surface with a #subsurface. surface.subSurfaces.each do |subsurface| subsurface.remove end #A bug with adiabatic surfaces. They do not hold the default contruction. surface.setConstruction(surface.construction.get()) if surface.isConstructionDefaulted end surface.setOutsideBoundaryCondition(boundaryCondition) adj_surface = surface.adjacentSurface unless adj_surface.empty? adj_surface.get.setOutsideBoundaryCondition(boundaryCondition) end end else puts "ERROR: Invalid Boundary Condition = " + boundary_condition puts "Correct Values are:" puts OpenStudio::Model::Surface::validOutsideBoundaryConditionValues end end
This method creates a new construction based on the current, changes the rsi and assign the construction to the current surface. Most of the meat of this method is in the construction class. Testing is done there.
# File lib/openstudio-standards/btap/geometry.rb, line 626 def self.set_surfaces_construction_conductance(surfaces, conductance) surfaces.each do |surface| #a bit of acrobatics to get the construction object from the ConstrustionBase object's name. construction = OpenStudio::Model::getConstructionByName(surface.model, surface.construction.get.name.to_s).get #create a new construction with the requested conductance value based on the current construction. new_construction = BTAP::Resources::Envelope::Constructions::customize_opaque_construction(surface.model, construction, conductance) surface.setConstruction(new_construction) end return surfaces end
# File lib/openstudio-standards/btap/geometry.rb, line 753 def self.show(surfaces) surfaces.each do |surface| if drawing_interface = surface.drawing_interface if entity = drawing_interface.entity entity.visible = false end end end end
This method was originally written to work with the ‘make_concave_surfaces’ method above. It takes the y-components of a bunch of line segemnts and cuts them up until they either are unique (no other overlapping components) or they match the y-components of other line segments. overlap_segs: This is an array of hashes that looks like:
overlap_seg = { index_a1: i, index_a2: i-1, index_b1: j, index_b2: j-1, point_b1: surf_verts[j], point_b2: surf_verts[j-1], overlap_y: overlap_y }
index_a1: The index of the array of points that cooresponds with the top of line a (points up) index_a1: The index of the array of points that cooresponds with the bottom of line a (points up) index_b1: The index of the array of points that cooresponds with the bottom of line b (points down) index_b1: The index of the array of points that cooresponds with the top of line b (points down) point_b1: The coordinates of the bottom of line b point_b2: The coordinates of the top of line b
overlap_y: A hash that contains the coordinates of the top and bottom of the y projection of the overlapping lines (line a and line b)
overlap_y = { overlap_start: overlap_start, overlap_end: overlap_end }
overlap_start: The y coordinate of the top of the overlap overlap_end: The y coordinate of the bottom of the overlap
# File lib/openstudio-standards/btap/geometry.rb, line 1083 def self.subdivide_overlaps(overlap_segs:) restart = true # Keep doing this until the y projections of the lines are either unique or the match the y projections of other # lines. while restart == true restart = false overlap_segs.each_with_index do |overlap_seg, curr_seg_index| for j in 0..(overlap_segs.length - 1) # Skip this y projection if it is the same as that in overlap_seg if overlap_seg == overlap_segs[j] next end # Check to see if the y projection of line a overlaps with the y projection of line b overlap_segs_overlap = line_segment_overlap_y?(point_a1: overlap_seg[:overlap_y][:overlap_start], point_a2: overlap_seg[:overlap_y][:overlap_end], point_b1: overlap_segs[j][:overlap_y][:overlap_end], point_b2: overlap_segs[j][:overlap_y][:overlap_start]) # If the y projections of the two lines overlap then the components of overlap_segs_overlap should not be # nil. unless ((overlap_segs_overlap[:overlap_start].nil?) || (overlap_segs_overlap[:overlap_end].nil?)) # If the two overlaping segments start and end at the same point then do nothing and go to the next segment. if (overlap_seg[:overlap_y][:overlap_start] == overlap_segs[j][:overlap_y][:overlap_start]) && (overlap_seg[:overlap_y][:overlap_end] == overlap_segs[j][:overlap_y][:overlap_end]) next # If the start point of one overlapping segment shares the end point of the other overlapping segment then # they are not really overlapping. Ignore and go to the next point. elsif overlap_segs_overlap[:overlap_start] == overlap_segs_overlap[:overlap_end] next # If the overlap_seg segment covers beyond the overlap_segs[j] segment then break overlap_seg into three smaller pieces: # -One piece for where overlap_seg starts to where overlap_segs[j] starts; # -One piece to cover overlap_segs[j] (the middle part); and # -One piece for where overlap_segs[j] ends to where overlap_seg ends (the bottom part). # The overlap_segs[j] remains as it is associated with another upward pointing line segment. # If overlap_seg starts at the same point as overlap_segs[j] or ends at the same point as overlap_segs[j] # then overlap_seg is broken into two pieces (no mid piece). elsif (overlap_seg[:overlap_y][:overlap_start] >= overlap_segs[j][:overlap_y][:overlap_start]) && (overlap_seg[:overlap_y][:overlap_end] <= overlap_segs[j][:overlap_y][:overlap_end]) # If the overlap_seg and overlap_segs[j] start at the same point replace overlap_seg with two segments ( # one top and one bottom). if overlap_seg[:overlap_y][:overlap_start] == overlap_segs[j][:overlap_y][:overlap_start] overlap_top = { index_a1: overlap_seg[:index_a1], index_a2: overlap_seg[:index_a2], index_b1: overlap_seg[:index_b1], index_b2: overlap_seg[:index_b2], point_b1: overlap_seg[:point_b1], point_b2: overlap_seg[:point_b2], overlap_y: overlap_segs_overlap } overlap_bottom_over = { overlap_start: overlap_segs_overlap[:overlap_end], overlap_end: overlap_seg[:overlap_y][:overlap_end] } overlap_bottom = { index_a1: overlap_seg[:index_a1], index_a2: overlap_seg[:index_a2], index_b1: overlap_seg[:index_b1], index_b2: overlap_seg[:index_b2], point_b1: overlap_seg[:point_b1], point_b2: overlap_seg[:point_b2], overlap_y: overlap_bottom_over } # delete the existing y projection overlaps and replace it with the ones we just made. overlap_segs.delete_at(curr_seg_index) overlap_segs << overlap_top overlap_segs << overlap_bottom elsif overlap_seg[:overlap_y][:overlap_end] == overlap_segs[j][:overlap_y][:overlap_end] # If the overlap_seg and overlap_segs[j] end at the same point replace overlap_seg with two segments ( # one top and one bottom). overlap_top_over = { overlap_start: overlap_seg[:overlap_y][:overlap_start], overlap_end: overlap_segs_overlap[:overlap_start] } overlap_top = { index_a1: overlap_seg[:index_a1], index_a2: overlap_seg[:index_a2], index_b1: overlap_seg[:index_b1], index_b2: overlap_seg[:index_b2], point_b1: overlap_seg[:point_b1], point_b2: overlap_seg[:point_b2], overlap_y: overlap_top_over } overlap_bottom = { index_a1: overlap_seg[:index_a1], index_a2: overlap_seg[:index_a2], index_b1: overlap_seg[:index_b1], index_b2: overlap_seg[:index_b2], point_b1: overlap_seg[:point_b1], point_b2: overlap_seg[:point_b2], overlap_y: overlap_segs_overlap } # delete the existing y projection overlaps and replace it with the ones we just made. overlap_segs.delete_at(curr_seg_index) overlap_segs << overlap_top overlap_segs << overlap_bottom elsif (overlap_seg[:overlap_y][:overlap_start] > overlap_segs[j][:overlap_y][:overlap_start]) && (overlap_seg[:overlap_y][:overlap_end] < overlap_segs[j][:overlap_y][:overlap_end]) # If the overlap_seg stretches above and below overlap_segs[j] then break overlap_seg into three pieces # (one top, one middle, one bottom). overlap_top_over = { overlap_start: overlap_seg[:overlap_y][:overlap_start], overlap_end: overlap_segs_overlap[:overlap_start] } overlap_top = { index_a1: overlap_seg[:index_a1], index_a2: overlap_seg[:index_a2], index_b1: overlap_seg[:index_b1], index_b2: overlap_seg[:index_b2], point_b1: overlap_seg[:point_b1], point_b2: overlap_seg[:point_b2], overlap_y: overlap_top_over } overlap_mid = { index_a1: overlap_seg[:index_a1], index_a2: overlap_seg[:index_a2], index_b1: overlap_seg[:index_b1], index_b2: overlap_seg[:index_b2], point_b1: overlap_seg[:point_b1], point_b2: overlap_seg[:point_b2], overlap_y: overlap_segs_overlap } overlap_bottom_over = { overlap_start: overlap_segs_overlap[:overlap_end], overlap_end: overlap_seg[:overlap_y][:overlap_end] } overlap_bottom = { index_a1: overlap_seg[:index_a1], index_a2: overlap_seg[:index_a2], index_b1: overlap_seg[:index_b1], index_b2: overlap_seg[:index_b2], point_b1: overlap_seg[:point_b1], point_b2: overlap_seg[:point_b2], overlap_y: overlap_bottom_over } # delete the existing y projection overlaps and replace it with the ones we just made. overlap_segs.delete_at(curr_seg_index) overlap_segs << overlap_top overlap_segs << overlap_mid overlap_segs << overlap_bottom end restart = true break # If the overlap_segs[j] segment covers beyond the overlap_seg segment then break overlap_segs[j] into three smaller pieces: # -One piece for where overlap_segs[j] starts to where overlap_seg starts; # -One piece to cover overlap_seg (the middle part); and # -One piece for where overlap_seg ends to where overlap_segs[j] ends (the bottom part). # The overlap_seg remains as it is associated with another upward pointing line segment. # If overlap_segs[j] starts at the same point as overlap_seg or ends at the same point as overlap_seg # then overlap_segs[j] is broken into two pieces (no mid piece). elsif overlap_seg[:overlap_y][:overlap_start] <= overlap_segs[j][:overlap_y][:overlap_start] && overlap_seg[:overlap_y][:overlap_end] >= overlap_segs[j][:overlap_y][:overlap_end] # If the overlap_seg and overlap_segs[j] start at the same point replace overlap_segs[j] with two segments ( # one top and one bottom). if overlap_seg[:overlap_y][:overlap_start] == overlap_segs[j][:overlap_y][:overlap_start] overlap_top = { index_a1: overlap_segs[j][:index_a1], index_a2: overlap_segs[j][:index_a2], index_b1: overlap_segs[j][:index_b1], index_b2: overlap_segs[j][:index_b2], point_b1: overlap_segs[j][:point_b1], point_b2: overlap_segs[j][:point_b2], overlap_y: overlap_segs_overlap } overlap_bottom_over = { overlap_start: overlap_segs_overlap[:overlap_end], overlap_end: overlap_segs[j][:overlap_y][:overlap_end] } overlap_bottom = { index_a1: overlap_segs[j][:index_a1], index_a2: overlap_segs[j][:index_a2], index_b1: overlap_segs[j][:index_b1], index_b2: overlap_segs[j][:index_b2], point_b1: overlap_segs[j][:point_b1], point_b2: overlap_segs[j][:point_b2], overlap_y: overlap_bottom_over } # delete the existing y projection overlaps and replace it with the ones we just made. overlap_segs.delete_at(j) overlap_segs << overlap_top overlap_segs << overlap_bottom elsif overlap_seg[:overlap_y][:overlap_end] == overlap_segs[j][:overlap_y][:overlap_end] # If the overlap_seg and overlap_segs[j] end at the same point replace overlap_segs[j] with two segments ( # one top and one bottom). overlap_top_over = { overlap_start: overlap_segs[j][:overlap_y][:overlap_start], overlap_end: overlap_segs_overlap[:overlap_start] } overlap_top = { index_a1: overlap_segs[j][:index_a1], index_a2: overlap_segs[j][:index_a2], index_b1: overlap_segs[j][:index_b1], index_b2: overlap_segs[j][:index_b2], point_b1: overlap_segs[j][:point_b1], point_b2: overlap_segs[j][:point_b2], overlap_y: overlap_top_over } overlap_bottom = { index_a1: overlap_segs[j][:index_a1], index_a2: overlap_segs[j][:index_a2], index_b1: overlap_segs[j][:index_b1], index_b2: overlap_segs[j][:index_b2], point_b1: overlap_segs[j][:point_b1], point_b2: overlap_segs[j][:point_b2], overlap_y: overlap_segs_overlap } # delete the existing y projection overlaps and replace it with the ones we just made. overlap_segs.delete_at(j) overlap_segs << overlap_top overlap_segs << overlap_bottom elsif overlap_seg[:overlap_y][:overlap_start] < overlap_segs[j][:overlap_y][:overlap_start] && overlap_seg[:overlap_y][:overlap_end] > overlap_segs[j][:overlap_y][:overlap_end] # If the overlap_segs[j] stretches above and below overlap_seg then break overlap_segs[j] into three pieces # (one top, one middle, one bottom). overlap_top_over = { overlap_start: overlap_segs[j][:overlap_y][:overlap_start], overlap_end: overlap_segs_overlap[:overlap_start] } overlap_top = { index_a1: overlap_segs[j][:index_a1], index_a2: overlap_segs[j][:index_a2], index_b1: overlap_segs[j][:index_b1], index_b2: overlap_segs[j][:index_b2], point_b1: overlap_segs[j][:point_b1], point_b2: overlap_segs[j][:point_b2], overlap_y: overlap_top_over } overlap_mid = { index_a1: overlap_segs[j][:index_a1], index_a2: overlap_segs[j][:index_a2], index_b1: overlap_segs[j][:index_b1], index_b2: overlap_segs[j][:index_b2], point_b1: overlap_segs[j][:point_b1], point_b2: overlap_segs[j][:point_b2], overlap_y: overlap_segs_overlap } overlap_bottom_over = { overlap_start: overlap_segs_overlap[:overlap_end], overlap_end: overlap_segs[j][:overlap_y][:overlap_end] } overlap_bottom = { index_a1: overlap_segs[j][:index_a1], index_a2: overlap_segs[j][:index_a2], index_b1: overlap_segs[j][:index_b1], index_b2: overlap_segs[j][:index_b2], point_b1: overlap_segs[j][:point_b1], point_b2: overlap_segs[j][:point_b2], overlap_y: overlap_bottom_over } # delete the existing y projection overlaps and replace it with the ones we just made. overlap_segs.delete_at(j) overlap_segs << overlap_top overlap_segs << overlap_mid overlap_segs << overlap_bottom end restart = true break # if overlap_seg covers the top of overlap_segs[j] then break overlap_seg into a top and an overlap portion # ond break overlap_segs[j] into an overlap portion and a bottom portion. elsif (overlap_seg[:overlap_y][:overlap_start] >= overlap_segs[j][:overlap_y][:overlap_start]) && (overlap_seg[:overlap_y][:overlap_end] <= overlap_segs[j][:overlap_y][:overlap_start]) && (overlap_seg[:overlap_y][:overlap_end] > overlap_segs[j][:overlap_y][:overlap_end]) overlap_top_over = { overlap_start: overlap_seg[:overlap_y][:overlap_start], overlap_end: overlap_segs_overlap[:overlap_start] } overlap_top = { index_a1: overlap_seg[:index_a1], index_a2: overlap_seg[:index_a2], index_b1: overlap_seg[:index_b1], index_b2: overlap_seg[:index_b2], point_b1: overlap_seg[:point_b1], point_b2: overlap_seg[:point_b2], overlap_y: overlap_top_over } overlap_mid_seg = { index_a1: overlap_seg[:index_a1], index_a2: overlap_seg[:index_a2], index_b1: overlap_seg[:index_b1], index_b2: overlap_seg[:index_b2], point_b1: overlap_seg[:point_b1], point_b2: overlap_seg[:point_b2], overlap_y: overlap_segs_overlap } overlap_mid_segs = { index_a1: overlap_segs[j][:index_a1], index_a2: overlap_segs[j][:index_a2], index_b1: overlap_segs[j][:index_b1], index_b2: overlap_segs[j][:index_b2], point_b1: overlap_segs[j][:point_b1], point_b2: overlap_segs[j][:point_b2], overlap_y: overlap_segs_overlap } overlap_bottom_over = { overlap_start: overlap_segs_overlap[:overlap_end], overlap_end: overlap_segs[j][:overlap_y][:overlap_end] } overlap_bottom = { index_a1: overlap_segs[j][:index_a1], index_a2: overlap_segs[j][:index_a2], index_b1: overlap_segs[j][:index_b1], index_b2: overlap_segs[j][:index_b2], point_b1: overlap_segs[j][:point_b1], point_b2: overlap_segs[j][:point_b2], overlap_y: overlap_bottom_over } # delete the existing y projection overlaps and replace it with the ones we just made. if curr_seg_index > j overlap_segs.delete_at(curr_seg_index) overlap_segs.delete_at(j) else overlap_segs.delete_at(j) overlap_segs.delete_at(curr_seg_index) end overlap_segs << overlap_top overlap_segs << overlap_mid_seg overlap_segs << overlap_mid_segs overlap_segs << overlap_bottom restart = true break elsif (overlap_seg[:overlap_y][:overlap_start] >= overlap_segs[j][:overlap_y][:overlap_end]) && (overlap_seg[:overlap_end] < overlap_segs[j][:overlap_end]) && (overlap_seg[:overlap_y][:overlap_start] <= overlap_segs[j][:overlap_y][:overlap_start]) # if overlap_seg covers the bottom of overlap_segs[j] then break overlap_segs[j] into a top and an overlap portion # ond break overlap_seg into an overlap portion and a bottom portion. overlap_top_over = { overlap_start: overlap_segs[j][:overlap_y][:overlap_start], overlap_end: overlap_segs_overlap[:overlap_start] } overlap_top = { index_a1: overlap_segs[j][:index_a1], index_a2: overlap_segs[j][:index_a2], index_b1: overlap_segs[j][:index_b1], index_b2: overlap_segs[j][:index_b2], point_b1: overlap_segs[j][:point_b1], point_b2: overlap_segs[j][:point_b2], overlap_y: overlap_top_over } overlap_mid_seg = { index_a1: overlap_seg[:index_a1], index_a2: overlap_seg[:index_a2], index_b1: overlap_seg[:index_b1], index_b2: overlap_seg[:index_b2], point_b1: overlap_seg[:point_b1], point_b2: overlap_seg[:point_b2], overlap_y: overlap_segs_overlap } overlap_mid_segs = { index_a1: overlap_segs[j][:index_a1], index_a2: overlap_segs[j][:index_a2], index_b1: overlap_segs[j][:index_b1], index_b2: overlap_segs[j][:index_b2], point_b1: overlap_segs[j][:point_b1], point_b2: overlap_segs[j][:point_b2], overlap_y: overlap_segs_overlap } overlap_bottom_over = { overlap_start: overlap_segs_overlap[:overlap_end], overlap_end: overlap_seg[:overlap_y][:overlap_end] } overlap_bottom = { index_a1: overlap_seg[:index_a1], index_a2: overlap_seg[:index_a2], index_b1: overlap_seg[:index_b1], index_b2: overlap_seg[:index_b2], point_b1: overlap_seg[:point_b1], point_b2: overlap_seg[:point_b2], overlap_y: overlap_bottom_over } # delete the existing y projection overlaps and replace it with the ones we just made. if curr_seg_index > j overlap_segs.delete_at(curr_seg_index) overlap_segs.delete_at(j) else overlap_segs.delete_at(j) overlap_segs.delete_at(curr_seg_index) end overlap_segs << overlap_top overlap_segs << overlap_mid_seg overlap_segs << overlap_mid_segs overlap_segs << overlap_bottom restart = true break end end end if restart == true break end end end return overlap_segs end
This method finds the centroid of a surface using the point averaging method. OpenStudio already has something which does this but you have to turn something into a special OpenStudio surface first which you may not want to do.
# File lib/openstudio-standards/btap/geometry.rb, line 1530 def self.surf_centroid(surf:) new_surf_cent = { x: 0, y: 0, z: 0 } surf.each do |surf_vert| new_surf_cent[:x] += surf_vert[:x] new_surf_cent[:y] += surf_vert[:y] new_surf_cent[:z] += surf_vert[:z] end new_surf_cent[:x] /= surf.length new_surf_cent[:y] /= surf.length new_surf_cent[:z] /= surf.length return new_surf_cent end