Module: BTAP::Geometry::Surfaces

Defined in:
lib/openstudio-standards/btap/geometry.rb

Class Method Summary collapse

Class Method Details

.create_surface(model, name, os_point3d_array, boundary_condition = "", construction = "") ⇒ Object



2635
2636
2637
2638
2639
2640
2641
2642
2643
2644
2645
# File 'lib/openstudio-standards/btap/geometry.rb', line 2635

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
  self.set_surfaces_construction([os_surface], construction)
  return os_surface
end

.filter_by_azimuth_and_tilt(surfaces, azimuth_from, azimuth_to, tilt_from, tilt_to, tolerance = 1.0) ⇒ Object

Azimuth start from Y axis, Tilts starts from Z-axis



2981
2982
2983
2984
2985
2986
2987
2988
2989
# File 'lib/openstudio-standards/btap/geometry.rb', line 2981

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

.filter_by_boundary_condition(surfaces, boundary_conditions) ⇒ Object



2912
2913
2914
2915
2916
2917
2918
2919
2920
2921
2922
2923
2924
2925
2926
2927
2928
2929
2930
2931
2932
2933
2934
2935
2936
2937
2938
2939
2940
# File 'lib/openstudio-standards/btap/geometry.rb', line 2912

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

.filter_by_interzonal_surface(surfaces) ⇒ Object



2970
2971
2972
2973
2974
2975
2976
2977
2978
# File 'lib/openstudio-standards/btap/geometry.rb', line 2970

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

.filter_by_non_defaulted_surfaces(surfaces) ⇒ Object



2905
2906
2907
2908
2909
# File 'lib/openstudio-standards/btap/geometry.rb', line 2905

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

.filter_by_surface_types(surfaces, surfaceTypes) ⇒ Object



2942
2943
2944
2945
2946
2947
2948
2949
2950
2951
2952
2953
2954
2955
2956
2957
2958
2959
2960
2961
2962
2963
2964
2965
2966
2967
2968
# File 'lib/openstudio-standards/btap/geometry.rb', line 2942

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

.filter_subsurfaces_by_types(subsurfaces, subSurfaceTypes) ⇒ Object

“FixedWindow” , “OperableWindow” , “Door” , “GlassDoor”, “OverheadDoor” , “Skylight”, “TubularDaylightDiffuser”,“TubularDaylightDome”


2790
2791
2792
2793
2794
2795
2796
2797
2798
2799
2800
2801
2802
2803
2804
2805
2806
2807
2808
2809
2810
2811
2812
2813
2814
2815
2816
2817
# File 'lib/openstudio-standards/btap/geometry.rb', line 2790

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

.get_overlapping_segments(overlap_segs:, index:, point_a1:, point_a2:) ⇒ Object

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.



3226
3227
3228
3229
3230
3231
3232
3233
3234
3235
3236
3237
3238
3239
3240
3241
3242
3243
3244
3245
3246
3247
3248
3249
3250
3251
3252
3253
3254
3255
3256
3257
3258
3259
3260
3261
3262
3263
3264
3265
3266
3267
3268
3269
3270
3271
3272
3273
3274
3275
3276
3277
3278
3279
3280
3281
3282
3283
3284
3285
3286
3287
3288
3289
3290
3291
3292
# File 'lib/openstudio-standards/btap/geometry.rb', line 3226

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

.get_sub_surface_net_area(subsurface) ⇒ Object



2863
2864
2865
# File 'lib/openstudio-standards/btap/geometry.rb', line 2863

def self.get_sub_surface_net_area(subsurface)
  return subsurface.netArea()
end

.get_subsurfaces_from_surfaces(surface_array) ⇒ Object



2717
2718
2719
2720
2721
2722
2723
# File 'lib/openstudio-standards/btap/geometry.rb', line 2717

def self.get_subsurfaces_from_surfaces(surface_array)
  subsurfaces = Array.new()
  surface_array.each do |surface|
    subsurfaces.concat(surface.subSurfaces)
  end
  return subsurfaces
end

.get_surface_construction_conductance(surface) ⇒ Object

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.



2835
2836
2837
2838
2839
2840
# File 'lib/openstudio-standards/btap/geometry.rb', line 2835

def self.get_surface_construction_conductance(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 RSI value based on the current construction.
  return BTAP::Resources::Envelope::Constructions::get_conductance(construction)
end

.get_surface_construction_shgc(surface) ⇒ Object

This method gets the shgc for a surface



2843
2844
2845
2846
2847
2848
# File 'lib/openstudio-standards/btap/geometry.rb', line 2843

def self.get_surface_construction_shgc(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 RSI value based on the current construction.
  return BTAP::Resources::Envelope::Constructions::get_shgc(surface.model,construction)
end

.get_surface_construction_tvis(surface) ⇒ Object

This method gets the tvis for the surface



2851
2852
2853
2854
2855
2856
# File 'lib/openstudio-standards/btap/geometry.rb', line 2851

def self.get_surface_construction_tvis(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 RSI value based on the current construction.
  return BTAP::Resources::Envelope::Constructions::get_tvis(model,construction)
end

.get_surface_net_area(surface) ⇒ Object



2859
2860
2861
# File 'lib/openstudio-standards/btap/geometry.rb', line 2859

def self.get_surface_net_area(surface)
  return surface.netArea()
end

.get_surfaces_from_building_stories(model, story_array) ⇒ Object



2699
2700
2701
2702
2703
2704
2705
# File 'lib/openstudio-standards/btap/geometry.rb', line 2699

def self.get_surfaces_from_building_stories(model, story_array)
  surfaces = Array.new()
  BTAP::Geometry::Spaces::get_spaces_from_storeys(model, story_array).each do |space|
    surfaces.concat(space.surfaces())
  end
  return surfaces
end

.get_surfaces_from_spaces(spaces_array) ⇒ Object



2691
2692
2693
2694
2695
2696
2697
# File 'lib/openstudio-standards/btap/geometry.rb', line 2691

def self.get_surfaces_from_spaces(spaces_array)
  surfaces = Array.new()
  spaces_array.each do |space|
    surfaces.concat(space.surfaces())
  end
  return surfaces
end

.get_surfaces_from_thermal_zones(thermal_zone_array) ⇒ Object



2707
2708
2709
2710
2711
2712
2713
2714
2715
# File 'lib/openstudio-standards/btap/geometry.rb', line 2707

def self.get_surfaces_from_thermal_zones(thermal_zone_array)
  surfaces = Array.new()
  thermal_zone_array.each do |thermal_zone|
    thermal_zone.spaces.sort.each do |space|
      surfaces.concat(space.surfaces())
    end
    return surfaces
  end
end

.get_total_ext_fenestration_area(model) ⇒ Object



2779
2780
2781
# File 'lib/openstudio-standards/btap/geometry.rb', line 2779

def self.get_total_ext_fenestration_area(model)

end

.get_total_ext_floor_area(model) ⇒ Object



2774
2775
2776
2777
# File 'lib/openstudio-standards/btap/geometry.rb', line 2774

def self.get_total_ext_floor_area(model)

  outdoor_floors = BTAP::Geometry::Surfaces::filter_by_surface_types(outdoor_surfaces, "Floor")
end

.get_total_ext_roof_area(model) ⇒ Object



2783
2784
2785
2786
# File 'lib/openstudio-standards/btap/geometry.rb', line 2783

def self.get_total_ext_roof_area(model)
  outdoor_roofs = BTAP::Geometry::Surfaces::filter_by_surface_types(outdoor_surfaces, "RoofCeiling")

end

.get_total_ext_wall_area(model) ⇒ Object

get total exterior surface area of building.



2767
2768
2769
2770
2771
2772
# File 'lib/openstudio-standards/btap/geometry.rb', line 2767

def self.get_total_ext_wall_area(model)
  outdoor_surfaces = BTAP::Geometry::Surfaces::filter_by_boundary_condition(model.getSurfaces(), "Outdoors")
  outdoor_walls = BTAP::Geometry::Surfaces::filter_by_surface_types(outdoor_surfaces, "Wall")


end

.get_weighted_average_surface_conductance(surfaces) ⇒ Object

determine average conductance on set of surfaces or subsurfaces.



2727
2728
2729
2730
2731
2732
2733
2734
2735
2736
2737
# File 'lib/openstudio-standards/btap/geometry.rb', line 2727

def self.get_weighted_average_surface_conductance(surfaces)
  total_area = 0.0
  temp = 0.0
  surfaces.each do |surface|
    temp = temp + BTAP::Geometry::Surfaces::get_surface_net_area(surface) * BTAP::Geometry::Surfaces::get_surface_construction_conductance(surface)
    total_area = total_area + BTAP::Geometry::Surfaces::get_surface_net_area(surface)
  end
  average_conductance = "NA"
  average_conductance = temp / total_area unless total_area == 0.0
  return average_conductance
end

.get_weighted_average_surface_shgc(surfaces) ⇒ Object

determine average conductance on set of surfaces or subsurfaces.



2740
2741
2742
2743
2744
2745
2746
2747
2748
2749
2750
# File 'lib/openstudio-standards/btap/geometry.rb', line 2740

def self.get_weighted_average_surface_shgc(surfaces)
  total_area = 0.0
  temp = 0.0
  surfaces.each do |surface|
    temp = temp + BTAP::Geometry::Surfaces::get_surface_net_area(surface) * BTAP::Geometry::Surfaces::get_surface_construction_shgc(surface)
    total_area = total_area + BTAP::Geometry::Surfaces::get_surface_net_area(surface)
  end
  ave_shgc = "NA"
  ave_shgc = temp / total_area unless total_area == 0.0
  return ave_shgc
end

.get_weighted_average_surface_tvis(surfaces) ⇒ Object

determine average conductance on set of surfaces or subsurfaces.



2753
2754
2755
2756
2757
2758
2759
2760
2761
2762
2763
# File 'lib/openstudio-standards/btap/geometry.rb', line 2753

def self.get_weighted_average_surface_tvis(surfaces)
  total_area = 0.0
  temp = 0.0
  surfaces.each do |surface|
    temp = temp + BTAP::Geometry::Surfaces::get_surface_net_area(surface) * BTAP::Geometry::Surfaces::get_surface_construction_tvis(surface)
    total_area = total_area + BTAP::Geometry::Surfaces::get_surface_net_area(surface)
  end
  ave_tvis = "NA"
  ave_tvis = temp / total_area unless total_area == 0.0
  return ave_tvis
end

.getSurfaceAreafromVertices(vertices:) ⇒ Object

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.



3796
3797
3798
3799
3800
3801
3802
3803
3804
3805
3806
3807
3808
3809
3810
3811
3812
# File 'lib/openstudio-standards/btap/geometry.rb', line 3796

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

.hide(surfaces) ⇒ Object



3002
3003
3004
3005
3006
3007
3008
3009
3010
# File 'lib/openstudio-standards/btap/geometry.rb', line 3002

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

.line_segment_overlap_x_coord(y_check:, point_b1:, point_b2:) ⇒ Object

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



3750
3751
3752
3753
3754
3755
3756
3757
3758
3759
3760
3761
3762
3763
3764
# File 'lib/openstudio-standards/btap/geometry.rb', line 3750

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

.line_segment_overlap_y?(point_a1:, point_a2:, point_b1:, point_b2:) ⇒ Boolean

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.

Returns:

  • (Boolean)


3712
3713
3714
3715
3716
3717
3718
3719
3720
3721
3722
3723
3724
3725
3726
3727
3728
3729
3730
3731
3732
3733
3734
3735
3736
3737
3738
3739
3740
3741
3742
3743
3744
# File 'lib/openstudio-standards/btap/geometry.rb', line 3712

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

.make_convex_surfaces(surface:, tol: 12) ⇒ Object

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.



3035
3036
3037
3038
3039
3040
3041
3042
3043
3044
3045
3046
3047
3048
3049
3050
3051
3052
3053
3054
3055
3056
3057
3058
3059
3060
3061
3062
3063
3064
3065
3066
3067
3068
3069
3070
3071
3072
3073
3074
3075
3076
3077
3078
3079
3080
3081
3082
3083
3084
3085
3086
3087
3088
3089
3090
3091
3092
3093
3094
3095
3096
3097
3098
3099
3100
3101
3102
3103
3104
3105
3106
3107
3108
3109
3110
3111
3112
3113
3114
3115
3116
3117
3118
3119
3120
3121
3122
3123
3124
3125
3126
3127
3128
3129
3130
3131
3132
3133
3134
3135
3136
3137
3138
3139
3140
3141
3142
3143
3144
3145
3146
3147
3148
3149
3150
3151
3152
3153
3154
3155
3156
3157
3158
3159
3160
3161
3162
3163
3164
3165
3166
3167
3168
3169
3170
3171
3172
3173
3174
3175
3176
3177
3178
3179
3180
3181
3182
3183
3184
# File 'lib/openstudio-standards/btap/geometry.rb', line 3035

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

.remove_all_subsurfaces(surfaces) ⇒ OpenStudio::Model::Model

This Method removes all the subsurfaces in a model (Windows, Doors )

Returns:

  • (OpenStudio::Model::Model)

    the OpenStudio model object (self reference).

Author:

  • Phylroy A. Lopez



2684
2685
2686
2687
2688
2689
# File 'lib/openstudio-standards/btap/geometry.rb', line 2684

def self.remove_all_subsurfaces(surfaces)
  surfaces.each do |subsurface|
    subsurface.remove
  end
  return surfaces
end

.rotate_tilt_translate_surfaces(planar_surfaces, azimuth_degrees, tilt_degrees = 0.0, translation_vector = OpenStudio::Vector3d.new(0.0, 0.0, 0.0)) ⇒ OpenStudio::Model::Model

This method will rotate a surface

Parameters:

  • planar_surfaces (Array<OpenStudio::Model::Surface>)

    an array of surfaces

  • azimuth_degrees (Float)

    rotation value

  • tilt_degrees (Float) (defaults to: 0.0)

    rotation value

  • translation_vector (OpenStudio::Vector3d) (defaults to: OpenStudio::Vector3d.new(0.0, 0.0, 0.0))

    a vector along which to move all surfaces

Returns:

  • (OpenStudio::Model::Model)

    the model object.



2653
2654
2655
2656
2657
2658
2659
2660
2661
2662
2663
2664
# File 'lib/openstudio-standards/btap/geometry.rb', line 2653

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

.set_fenestration_to_wall_ratio(surfaces, ratio, offset = 0, height_offset_from_floor = true, floor = "all") ⇒ Object



2666
2667
2668
2669
2670
2671
2672
2673
2674
2675
2676
2677
2678
# File 'lib/openstudio-standards/btap/geometry.rb', line 2666

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

.set_surfaces_boundary_condition(model, surfaces, boundaryCondition) ⇒ Object

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.


2877
2878
2879
2880
2881
2882
2883
2884
2885
2886
2887
2888
2889
2890
2891
2892
2893
2894
2895
2896
2897
2898
2899
2900
2901
2902
2903
# File 'lib/openstudio-standards/btap/geometry.rb', line 2877

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

.set_surfaces_construction(surfaces, construction) ⇒ Object



2868
2869
2870
2871
2872
2873
# File 'lib/openstudio-standards/btap/geometry.rb', line 2868

def self.set_surfaces_construction(surfaces, construction)
  surfaces.each do |surface|
    surface.setConstruction(construction)
  end
  return true
end

.set_surfaces_construction_conductance(surfaces, conductance) ⇒ Object

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.



2821
2822
2823
2824
2825
2826
2827
2828
2829
2830
2831
# File 'lib/openstudio-standards/btap/geometry.rb', line 2821

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

.show(surfaces) ⇒ Object



2992
2993
2994
2995
2996
2997
2998
2999
3000
# File 'lib/openstudio-standards/btap/geometry.rb', line 2992

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

.subdivide_overlaps(overlap_segs:) ⇒ Object

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



3322
3323
3324
3325
3326
3327
3328
3329
3330
3331
3332
3333
3334
3335
3336
3337
3338
3339
3340
3341
3342
3343
3344
3345
3346
3347
3348
3349
3350
3351
3352
3353
3354
3355
3356
3357
3358
3359
3360
3361
3362
3363
3364
3365
3366
3367
3368
3369
3370
3371
3372
3373
3374
3375
3376
3377
3378
3379
3380
3381
3382
3383
3384
3385
3386
3387
3388
3389
3390
3391
3392
3393
3394
3395
3396
3397
3398
3399
3400
3401
3402
3403
3404
3405
3406
3407
3408
3409
3410
3411
3412
3413
3414
3415
3416
3417
3418
3419
3420
3421
3422
3423
3424
3425
3426
3427
3428
3429
3430
3431
3432
3433
3434
3435
3436
3437
3438
3439
3440
3441
3442
3443
3444
3445
3446
3447
3448
3449
3450
3451
3452
3453
3454
3455
3456
3457
3458
3459
3460
3461
3462
3463
3464
3465
3466
3467
3468
3469
3470
3471
3472
3473
3474
3475
3476
3477
3478
3479
3480
3481
3482
3483
3484
3485
3486
3487
3488
3489
3490
3491
3492
3493
3494
3495
3496
3497
3498
3499
3500
3501
3502
3503
3504
3505
3506
3507
3508
3509
3510
3511
3512
3513
3514
3515
3516
3517
3518
3519
3520
3521
3522
3523
3524
3525
3526
3527
3528
3529
3530
3531
3532
3533
3534
3535
3536
3537
3538
3539
3540
3541
3542
3543
3544
3545
3546
3547
3548
3549
3550
3551
3552
3553
3554
3555
3556
3557
3558
3559
3560
3561
3562
3563
3564
3565
3566
3567
3568
3569
3570
3571
3572
3573
3574
3575
3576
3577
3578
3579
3580
3581
3582
3583
3584
3585
3586
3587
3588
3589
3590
3591
3592
3593
3594
3595
3596
3597
3598
3599
3600
3601
3602
3603
3604
3605
3606
3607
3608
3609
3610
3611
3612
3613
3614
3615
3616
3617
3618
3619
3620
3621
3622
3623
3624
3625
3626
3627
3628
3629
3630
3631
3632
3633
3634
3635
3636
3637
3638
3639
3640
3641
3642
3643
3644
3645
3646
3647
3648
3649
3650
3651
3652
3653
3654
3655
3656
3657
3658
3659
3660
3661
3662
3663
3664
3665
3666
3667
3668
3669
3670
3671
3672
3673
3674
3675
3676
3677
3678
3679
3680
3681
3682
3683
3684
3685
3686
3687
3688
3689
3690
3691
3692
3693
3694
3695
3696
3697
3698
3699
3700
3701
# File 'lib/openstudio-standards/btap/geometry.rb', line 3322

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

.surf_centroid(surf:) ⇒ Object

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.



3769
3770
3771
3772
3773
3774
3775
3776
3777
3778
3779
3780
3781
3782
3783
3784
# File 'lib/openstudio-standards/btap/geometry.rb', line 3769

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