openstudio-standards 0.8.5 → 0.8.7

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (174) hide show
  1. checksums.yaml +4 -4
  2. data/data/standards/test_performance_expected_dd_results.csv +90 -90
  3. data/lib/openstudio-standards/btap/activity.rb +858 -61
  4. data/lib/openstudio-standards/btap/attributes.rb +297 -103
  5. data/lib/openstudio-standards/btap/bridging.rb +1335 -593
  6. data/lib/openstudio-standards/btap/btap.rb +60 -181
  7. data/lib/openstudio-standards/btap/carbon/btap_carbon.rb +199 -142
  8. data/lib/openstudio-standards/btap/common/constructions.json +1275 -0
  9. data/lib/openstudio-standards/btap/common/costs.csv +1969 -0
  10. data/lib/openstudio-standards/btap/common/costs_local_factors.csv +2315 -0
  11. data/lib/openstudio-standards/btap/common/materials_glazing.csv +61 -0
  12. data/lib/openstudio-standards/btap/{common_resources → common}/materials_hvac.csv +1685 -1685
  13. data/lib/openstudio-standards/btap/{common_resources → common}/materials_lighting.csv +266 -266
  14. data/lib/openstudio-standards/btap/common/materials_opaque.csv +214 -0
  15. data/lib/openstudio-standards/btap/common/necb_building_types.csv +35 -0
  16. data/lib/openstudio-standards/btap/common/necb_space_types.csv +110 -0
  17. data/lib/openstudio-standards/btap/common/thermal_bridging.csv +73 -0
  18. data/lib/openstudio-standards/btap/costing/btap_costing.rb +346 -429
  19. data/lib/openstudio-standards/btap/costing/database.rb +104 -0
  20. data/lib/openstudio-standards/btap/costing/daylighting_sensor_control_costing.rb +356 -344
  21. data/lib/openstudio-standards/btap/costing/dcv_costing.rb +282 -296
  22. data/lib/openstudio-standards/btap/costing/envelope_costing.rb +179 -192
  23. data/lib/openstudio-standards/btap/costing/heating_cooling_costing.rb +2477 -2316
  24. data/lib/openstudio-standards/btap/costing/led_lighting_costing.rb +127 -142
  25. data/lib/openstudio-standards/btap/costing/lighting_costing.rb +216 -198
  26. data/lib/openstudio-standards/btap/costing/nv_costing.rb +522 -523
  27. data/lib/openstudio-standards/btap/costing/pv_ground_costing.rb +649 -649
  28. data/lib/openstudio-standards/btap/costing/shw_costing.rb +626 -625
  29. data/lib/openstudio-standards/btap/costing/thermal_bridging_costing.rb +72 -0
  30. data/lib/openstudio-standards/btap/costing/ventilation_costing.rb +2363 -2361
  31. data/lib/openstudio-standards/btap/deprecated.rb +1033 -0
  32. data/lib/openstudio-standards/btap/envelope.rb +313 -572
  33. data/lib/openstudio-standards/btap/fileio.rb +152 -1070
  34. data/lib/openstudio-standards/btap/geometry.rb +486 -766
  35. data/lib/openstudio-standards/btap/linear_regression.rb +130 -0
  36. data/lib/openstudio-standards/btap/paths.rb +46 -0
  37. data/lib/openstudio-standards/btap/qaqc/analysis.rb +163 -0
  38. data/lib/openstudio-standards/btap/qaqc/data.rb +2293 -0
  39. data/lib/openstudio-standards/btap/qaqc/datapoint.rb +641 -0
  40. data/lib/openstudio-standards/btap/schedules.rb +11 -611
  41. data/lib/openstudio-standards/btap/structure.rb +574 -189
  42. data/lib/openstudio-standards/constructions/materials/information.rb +1 -1
  43. data/lib/openstudio-standards/create_typical/create_typical.rb +1 -1
  44. data/lib/openstudio-standards/exterior_lighting/create.rb +135 -85
  45. data/lib/openstudio-standards/exterior_lighting/data/typical_exterior_lighting.json +20 -0
  46. data/lib/openstudio-standards/exterior_lighting/information.rb +23 -2
  47. data/lib/openstudio-standards/geometry/create.rb +7 -3
  48. data/lib/openstudio-standards/prototypes/ashrae_90_1/ashrae_90_1_2016/ashrae_90_1_2016.Model.rb +3 -2
  49. data/lib/openstudio-standards/prototypes/ashrae_90_1/ashrae_90_1_2019/ashrae_90_1_2019.Model.rb +3 -2
  50. data/lib/openstudio-standards/prototypes/common/data/thermostat_schedule_lookup.json +84 -84
  51. data/lib/openstudio-standards/prototypes/common/objects/Prototype.hvac_systems.rb +4 -4
  52. data/lib/openstudio-standards/refrigeration/create_walkin.rb +45 -5
  53. data/lib/openstudio-standards/refrigeration/data/refrigerated_walkins.csv +75 -75
  54. data/lib/openstudio-standards/schedules/parametric.rb +6 -0
  55. data/lib/openstudio-standards/service_water_heating/create_typical.rb +10 -3
  56. data/lib/openstudio-standards/service_water_heating/create_water_heating_loop.rb +30 -14
  57. data/lib/openstudio-standards/service_water_heating/information.rb +4 -1
  58. data/lib/openstudio-standards/sql_file/unmet_hours.rb +2 -1
  59. data/lib/openstudio-standards/standards/Standards.CoilCoolingDXSingleSpeed.rb +3 -1
  60. data/lib/openstudio-standards/standards/Standards.CoilHeatingDXSingleSpeed.rb +3 -1
  61. data/lib/openstudio-standards/standards/ashrae_90_1/ashrae_90_1_2004/data/ashrae_90_1_2004.boilers.json +12 -0
  62. data/lib/openstudio-standards/standards/ashrae_90_1/ashrae_90_1_2004/data/ashrae_90_1_2004.construction_properties.json +18432 -3072
  63. data/lib/openstudio-standards/standards/ashrae_90_1/ashrae_90_1_2007/data/ashrae_90_1_2007.boilers.json +26 -0
  64. data/lib/openstudio-standards/standards/ashrae_90_1/ashrae_90_1_2007/data/ashrae_90_1_2007.construction_properties.json +8532 -1422
  65. data/lib/openstudio-standards/standards/ashrae_90_1/ashrae_90_1_2010/data/ashrae_90_1_2010.boilers.json +26 -0
  66. data/lib/openstudio-standards/standards/ashrae_90_1/ashrae_90_1_2010/data/ashrae_90_1_2010.construction_properties.json +8532 -1422
  67. data/lib/openstudio-standards/standards/ashrae_90_1/ashrae_90_1_2013/data/ashrae_90_1_2013.boilers.json +16 -0
  68. data/lib/openstudio-standards/standards/ashrae_90_1/ashrae_90_1_2013/data/ashrae_90_1_2013.computer_room_acs.json +30 -30
  69. data/lib/openstudio-standards/standards/ashrae_90_1/ashrae_90_1_2013/data/ashrae_90_1_2013.construction_properties.json +7560 -1260
  70. data/lib/openstudio-standards/standards/ashrae_90_1/ashrae_90_1_2016/ashrae_90_1_2016.PumpVariableSpeed.rb +1 -2
  71. data/lib/openstudio-standards/standards/ashrae_90_1/ashrae_90_1_2016/data/ashrae_90_1_2016.boilers.json +16 -0
  72. data/lib/openstudio-standards/standards/ashrae_90_1/ashrae_90_1_2016/data/ashrae_90_1_2016.construction_properties.json +7560 -1260
  73. data/lib/openstudio-standards/standards/ashrae_90_1/ashrae_90_1_2019/ashrae_90_1_2019.AirLoopHVAC.rb +3 -3
  74. data/lib/openstudio-standards/standards/ashrae_90_1/ashrae_90_1_2019/ashrae_90_1_2019.PumpVariableSpeed.rb +1 -2
  75. data/lib/openstudio-standards/standards/ashrae_90_1/ashrae_90_1_2019/data/ashrae_90_1_2019.boilers.json +22 -0
  76. data/lib/openstudio-standards/standards/ashrae_90_1/ashrae_90_1_2019/data/ashrae_90_1_2019.construction_properties.json +7236 -1206
  77. data/lib/openstudio-standards/standards/ashrae_90_1/ashrae_90_1_2019/data/ashrae_90_1_2019.furnaces.json +223 -19
  78. data/lib/openstudio-standards/standards/ashrae_90_1/data/ashrae_90_1.schedules.json +37 -1
  79. data/lib/openstudio-standards/standards/ashrae_90_1_prm/ashrae_90_1_prm_2019/data/ashrae_90_1_prm_2019.boilers.json +3 -0
  80. data/lib/openstudio-standards/standards/ashrae_90_1_prm/ashrae_90_1_prm_2019/data/ashrae_90_1_prm_2019.construction_properties.json +6900 -1150
  81. data/lib/openstudio-standards/standards/cbes/data/cbes.schedules.json +1 -1
  82. data/lib/openstudio-standards/standards/deer/data/deer.schedules.json +1 -1
  83. data/lib/openstudio-standards/standards/necb/BTAP1980TO2010/data/surface_thermal_transmittance.json +76 -40
  84. data/lib/openstudio-standards/standards/necb/BTAPPRE1980/data/surface_thermal_transmittance.json +76 -40
  85. data/lib/openstudio-standards/standards/necb/ECMS/hvac_systems.rb +6 -4
  86. data/lib/openstudio-standards/standards/necb/NECB2011/NECB2011_QAQC.json +2988 -0
  87. data/lib/openstudio-standards/standards/necb/NECB2011/autozone.rb +25 -3
  88. data/lib/openstudio-standards/standards/necb/NECB2011/building_envelope.rb +672 -534
  89. data/lib/openstudio-standards/standards/necb/NECB2011/data/geometry/Hospital.osm +1791 -1792
  90. data/lib/openstudio-standards/standards/necb/NECB2011/data/geometry/LEEPMultiTower.osm +7 -1
  91. data/lib/openstudio-standards/standards/necb/NECB2011/data/geometry/LEEPTownHouse.osm +7 -1
  92. data/lib/openstudio-standards/standards/necb/NECB2011/data/geometry/LargeHotel.osm +5 -6
  93. data/lib/openstudio-standards/standards/necb/NECB2011/data/geometry/LargeOffice.osm +8 -9
  94. data/lib/openstudio-standards/standards/necb/NECB2011/data/geometry/NorthernEducation.osm +10 -4
  95. data/lib/openstudio-standards/standards/necb/NECB2011/data/geometry/NorthernHealthCare.osm +14 -15
  96. data/lib/openstudio-standards/standards/necb/NECB2011/data/geometry/Outpatient.osm +7 -1
  97. data/lib/openstudio-standards/standards/necb/NECB2011/data/surface_thermal_transmittance.json +76 -40
  98. data/lib/openstudio-standards/standards/necb/NECB2011/hvac_systems.rb +14 -6
  99. data/lib/openstudio-standards/standards/necb/NECB2011/necb_2011.rb +542 -130
  100. data/lib/openstudio-standards/standards/necb/NECB2011/qaqc/necb_qaqc.rb +30 -63
  101. data/lib/openstudio-standards/standards/necb/NECB2011/service_water_heating.rb +7 -2
  102. data/lib/openstudio-standards/standards/necb/NECB2015/data/surface_thermal_transmittance.json +90 -0
  103. data/lib/openstudio-standards/standards/necb/NECB2015/necb_2015.rb +8 -6
  104. data/lib/openstudio-standards/standards/necb/NECB2017/data/surface_thermal_transmittance.json +76 -40
  105. data/lib/openstudio-standards/standards/necb/NECB2017/necb_2017.rb +2 -1
  106. data/lib/openstudio-standards/standards/necb/NECB2020/building_envelope.rb +0 -117
  107. data/lib/openstudio-standards/standards/necb/NECB2020/data/surface_thermal_transmittance.json +76 -45
  108. data/lib/openstudio-standards/standards/necb/NECB2020/necb_2020.rb +218 -0
  109. data/lib/openstudio-standards/standards/necb/common/construction_defaults.osm +12 -7
  110. data/lib/openstudio-standards/standards/standard.rb +1 -1
  111. data/lib/openstudio-standards/version.rb +1 -1
  112. data/lib/openstudio-standards.rb +9 -4
  113. metadata +59 -66
  114. data/data/inventory/thermal_bridging.csv +0 -90
  115. data/data/standards/OpenStudio_Standards-ashrae_90_1-ALL-comstock(space_types).xlsx +0 -0
  116. data/data/standards/openstudio_standards_duplicates_log.csv +0 -7962
  117. data/lib/openstudio-standards/btap/NECB_building_types.csv +0 -35
  118. data/lib/openstudio-standards/btap/NECB_space_types.csv +0 -109
  119. data/lib/openstudio-standards/btap/WeatherData1.json +0 -2110
  120. data/lib/openstudio-standards/btap/WeatherData1.xlsx +0 -0
  121. data/lib/openstudio-standards/btap/btap_result.rb +0 -1144
  122. data/lib/openstudio-standards/btap/btap_test_helper.rb +0 -86
  123. data/lib/openstudio-standards/btap/common_paths.rb +0 -81
  124. data/lib/openstudio-standards/btap/common_resources/ConstructionProperties.csv +0 -52
  125. data/lib/openstudio-standards/btap/common_resources/Constructions.csv +0 -37
  126. data/lib/openstudio-standards/btap/common_resources/construction_sets.csv +0 -1270
  127. data/lib/openstudio-standards/btap/common_resources/constructions_glazing.csv +0 -121
  128. data/lib/openstudio-standards/btap/common_resources/constructions_opaque.csv +0 -2256
  129. data/lib/openstudio-standards/btap/common_resources/costs.csv +0 -1897
  130. data/lib/openstudio-standards/btap/common_resources/costs_local_factors.csv +0 -2315
  131. data/lib/openstudio-standards/btap/common_resources/materials_glazing.csv +0 -35
  132. data/lib/openstudio-standards/btap/common_resources/materials_opaque.csv +0 -164
  133. data/lib/openstudio-standards/btap/costing/btap_measure_helper.rb +0 -359
  134. data/lib/openstudio-standards/btap/costing/btap_workflow.rb +0 -117
  135. data/lib/openstudio-standards/btap/costing/copy_test_results_files_to_expected_results.rb +0 -11
  136. data/lib/openstudio-standards/btap/costing/cost_building_from_file.rb +0 -136
  137. data/lib/openstudio-standards/btap/costing/costing_database_wrapper.rb +0 -173
  138. data/lib/openstudio-standards/btap/costing/dummy.epw +0 -8768
  139. data/lib/openstudio-standards/btap/costing/dummy.osm +0 -5320
  140. data/lib/openstudio-standards/btap/costing/neb_end_use_prices.csv +0 -42
  141. data/lib/openstudio-standards/btap/costing/necb_2011_spacetype_info.csv +0 -225
  142. data/lib/openstudio-standards/btap/costing/necb_reference_runs.csv +0 -28705
  143. data/lib/openstudio-standards/btap/costing/parallel_tests.rb +0 -92
  144. data/lib/openstudio-standards/btap/costing/test_list.txt +0 -15
  145. data/lib/openstudio-standards/btap/costing/test_run_all_test_locally.rb +0 -27
  146. data/lib/openstudio-standards/btap/costing/test_run_costing_tests.rb +0 -80
  147. data/lib/openstudio-standards/btap/necb_2011_spacetype_info.csv +0 -225
  148. data/lib/openstudio-standards/btap/utilities/Add_template_field_to_json.rb +0 -22
  149. data/lib/openstudio-standards/btap/utilities/convert_surfaces_to_adiabatic_necb_8426.rb +0 -204
  150. data/lib/openstudio-standards/btap/utilities/generate_space_types.rb +0 -8
  151. data/lib/openstudio-standards/btap/utilities/necb_to_epw_map.rb +0 -50
  152. data/lib/openstudio-standards/btap/utilities/os_sim_extract.rb +0 -48
  153. data/lib/openstudio-standards/btap/utilities/rename_surfaces.rb +0 -24
  154. data/lib/openstudio-standards/btap/utilities/round_surf_coords.rb +0 -32
  155. data/lib/openstudio-standards/btap/utilities/sched_create.rb +0 -145
  156. data/lib/openstudio-standards/btap/utilities/set_mult_to_adiabatic.rb +0 -53
  157. data/lib/openstudio-standards/btap/utilities/space_type.rb +0 -78
  158. data/lib/openstudio-standards/btap/utilities/weatherData1_xlsx_to_json.rb +0 -56
  159. data/lib/openstudio-standards/btap/vintagizer.rb +0 -161
  160. data/lib/openstudio-standards/btap/visualization.rb +0 -72
  161. data/lib/openstudio-standards/standards/necb/NECB2011/data/necb_2011_spacetype_info.csv +0 -225
  162. data/lib/openstudio-standards/standards/necb/common/btap_analysis.rb +0 -79
  163. data/lib/openstudio-standards/standards/necb/common/btap_data.rb +0 -2656
  164. data/lib/openstudio-standards/standards/necb/common/btap_datapoint.rb +0 -653
  165. data/lib/openstudio-standards/standards/necb/common/necb_reference_runs.csv +0 -28705
  166. data/lib/openstudio-standards/standards/necb/common/utility_pricing_2025-02-20.csv +0 -92
  167. /data/lib/openstudio-standards/btap/{common_resources → common}/carbon_frame.csv +0 -0
  168. /data/lib/openstudio-standards/btap/{common_resources → common}/carbon_glazing.csv +0 -0
  169. /data/lib/openstudio-standards/btap/{common_resources → common}/carbon_opaque.csv +0 -0
  170. /data/lib/openstudio-standards/btap/{common_resources → common}/hvac_vent_ahu.csv +0 -0
  171. /data/lib/openstudio-standards/btap/{common_resources → common}/lighting.csv +0 -0
  172. /data/lib/openstudio-standards/btap/{common_resources → common}/lighting_sets.csv +0 -0
  173. /data/lib/openstudio-standards/btap/{common_resources → common}/locations.csv +0 -0
  174. /data/lib/openstudio-standards/btap/{costing → common}/mech_sizing.json +0 -0
@@ -17,123 +17,8 @@
17
17
  # * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
18
18
  # **********************************************************************/
19
19
 
20
- require 'tbd'
21
-
22
20
  module BTAP
23
21
  module Geometry
24
- def self.enumerate_spaces_model(model, prepend_name = false)
25
- #enumerate stories.
26
- BTAP::Geometry::BuildingStoreys::auto_assign_spaces_to_stories(model)
27
- #Enumerate spaces
28
- model.getBuildingStorys.sort.each do |story|
29
- spaces = Array.new
30
- spaces.concat(story.spaces)
31
- spaces.sort! do |a, b|
32
- (a.xOrigin <=> b.xOrigin).nonzero? ||
33
- (a.yOrigin <=> b.yOrigin)
34
- end
35
- counter = 1
36
- spaces.sort.each do |space|
37
- #puts "old space name : #{space.name}"
38
- if prepend_name == true
39
- space.setName("#{story.name}-#{counter.to_s}:#{space.name}")
40
- else
41
- space.setName("#{story.name}-#{counter.to_s}")
42
- end
43
- counter = counter + 1
44
- p #uts "new space name : #{space.name}"
45
- end
46
- end
47
- end
48
-
49
- #this was a copy of the sketchup plugin method.
50
- def self.rename_zones_based_on_spaces(model)
51
-
52
- # loop through thermal zones
53
- model.getThermalZones.sort.each do |thermal_zone| # this is going through all, not just selection
54
- #puts "old zone name : #{thermal_zone.name}"
55
- # reset the array of spaces to be empty
56
- spaces_in_thermal_zone = []
57
- # reset length of array of spaces
58
- number_of_spaces = 0
59
-
60
- # get list of spaces in thermal zone
61
- spaces = thermal_zone.spaces
62
- spaces.sort.each do |space|
63
-
64
- # make an array instead of the puts statement
65
- spaces_in_thermal_zone.push space.name.to_s
66
-
67
- end
68
-
69
- # store length of array
70
- number_of_spaces = spaces_in_thermal_zone.size
71
-
72
- # sort the array
73
- spaces_in_thermal_zone = spaces_in_thermal_zone.sort
74
-
75
- # setup a suffix if the thermal zone contains more than one space
76
- if number_of_spaces > 1
77
- multi = " - Plus"
78
- else
79
- multi = ""
80
- end
81
-
82
- # rename thermal zone based on first space with prefix added e.g. ThermalZone 203
83
- if number_of_spaces > 0
84
- new_name = "ZN:" + spaces_in_thermal_zone[0] + multi
85
- thermal_zone.setName(new_name)
86
- else
87
- puts "#{thermal_zone.name.to_s} did not have any spaces, and will not be renamed."
88
- end
89
- #puts "new zone name : #{thermal_zone.name}"
90
- end
91
- end
92
-
93
- #This method will rename the zone equipment to have the zone name as a prefix for a model.
94
- #It will also rename the hot water coils for:
95
- # AirTerminalSingleDuctVAVReheat
96
- # ZoneHVACBaseboardConvectiveWater
97
- # ZoneHVACUnitHeater
98
-
99
- def self.prefix_equipment_with_zone_name(model)
100
- #puts "Renaming zone equipment."
101
- # get all thermal zones
102
- thermal_zones = model.getThermalZones
103
-
104
- # loop through thermal zones
105
- thermal_zones.each do |thermal_zone| # this is going through all, not just selection
106
-
107
- thermal_zone.equipment.each do |equip|
108
-
109
- #For the hydronic conditions below only, it will rename the zonal coils as well.
110
- if not equip.to_AirTerminalSingleDuctVAVReheat.empty?
111
-
112
- equip.setName("#{thermal_zone.name}:AirTerminalSingleDuctVAVReheat")
113
- reheat_coil = equip.to_AirTerminalSingleDuctVAVReheat.get.reheatCoil
114
- reheat_coil.setName("#{thermal_zone.name}:ReheatCoil")
115
- #puts reheat_coil.name
116
- elsif not equip.to_ZoneHVACBaseboardConvectiveWater.empty?
117
- equip.setName("#{thermal_zone.name}:ZoneHVACBaseboardConvectiveWater")
118
- heatingCoil = equip.to_ZoneHVACBaseboardConvectiveWater.get.heatingCoil
119
- heatingCoil.setName("#{thermal_zone.name}:Baseboard HW Htg Coil")
120
- #puts heatingCoil.name
121
- elsif not equip.to_ZoneHVACUnitHeater.empty?
122
- equip.setName("#{thermal_zone.name}:ZoneHVACUnitHeater")
123
- heatingCoil = equip.to_ZoneHVACUnitHeater.get.heatingCoil
124
- heatingCoil.setName("#{thermal_zone.name}:Unit Heater Htg Coil")
125
- #puts heatingCoil.name
126
- #Add more cases if you wish!!!!!
127
- else #if the equipment does not follow the above cases, rename
128
- # it generically and not touch the underlying coils, etc.
129
- equip.setName("#{thermal_zone.name}:#{equip.name}")
130
- end
131
-
132
- end
133
- end
134
- #puts "Done zone renaming equipment"
135
- end
136
-
137
22
  def self.match_surfaces(model)
138
23
  model.getSpaces.sort.each do |space1|
139
24
  model.getSpaces.sort.each do |space2|
@@ -173,6 +58,45 @@ module BTAP
173
58
  return model
174
59
  end
175
60
 
61
+ def self.model_horizontal_dimensions(model)
62
+ bounding_box = OpenStudio::BoundingBox.new
63
+ model.getSpaces.each do |space|
64
+ space.surfaces.each do |surface|
65
+ bounding_box.addPoints(space.transformation * surface.vertices)
66
+ end
67
+ end
68
+
69
+ raise ArgumentError, 'Cannot calculate footprint dimensions for a model without space geometry.' if bounding_box.isEmpty
70
+
71
+ x_dimension = bounding_box.maxX.get - bounding_box.minX.get
72
+ y_dimension = bounding_box.maxY.get - bounding_box.minY.get
73
+ if x_dimension <= 0.0 || y_dimension <= 0.0
74
+ raise ArgumentError, 'Building footprint dimensions must be greater than zero.'
75
+ end
76
+
77
+ return x_dimension, y_dimension
78
+ end
79
+
80
+ def self.set_footprint_aspect_ratio(model, target_aspect_ratio)
81
+ target_aspect_ratio = target_aspect_ratio.to_f
82
+ unless target_aspect_ratio.finite? && target_aspect_ratio >= 1.0
83
+ raise ArgumentError, 'Footprint aspect ratio must be a finite number greater than or equal to 1.0.'
84
+ end
85
+
86
+ x_dimension, y_dimension = model_horizontal_dimensions(model)
87
+ current_aspect_ratio = [x_dimension, y_dimension].max / [x_dimension, y_dimension].min
88
+ major_axis_scale = Math.sqrt(target_aspect_ratio / current_aspect_ratio)
89
+ minor_axis_scale = 1.0 / major_axis_scale
90
+
91
+ if x_dimension >= y_dimension
92
+ scale_model(model, major_axis_scale, minor_axis_scale, 1.0)
93
+ else
94
+ scale_model(model, minor_axis_scale, major_axis_scale, 1.0)
95
+ end
96
+
97
+ return model
98
+ end
99
+
176
100
  def self.get_fwdr(model)
177
101
  outdoor_surfaces = BTAP::Geometry::Surfaces::filter_by_boundary_condition(model.getSurfaces(), "Outdoors")
178
102
  outdoor_walls = BTAP::Geometry::Surfaces::filter_by_surface_types(outdoor_surfaces, "Wall")
@@ -222,447 +146,161 @@ module BTAP
222
146
  final_building_angle = building.setNorthAxis(building.northAxis + degrees)
223
147
  end
224
148
 
225
- module BuildingStoreys
226
- #This method will delete any exisiting stories and then try to assign stories based on
227
- # the z-axis origin of the space.
228
- def self.auto_assign_spaces_to_stories(model)
229
- #delete existing stories.
230
- model.getBuildingStorys.sort.each {|buildingstory| buildingstory.remove}
231
- #create hash of building storeys, index is the Z-axis origin of the space.
232
- building_story_hash = Hash.new()
233
- model.getSpaces.sort.each do |space|
234
- if building_story_hash[space.zOrigin].nil?
235
- building_story_hash[space.zOrigin] = OpenStudio::Model::BuildingStory.new(model)
236
- building_story_hash[space.zOrigin].setName(building_story_hash.length.to_s)
149
+ module Spaces
150
+ ##
151
+ # Fetch a space's full height, maybe stored as an AdditionalProperty -
152
+ # search for AdditionalProperty feature "space_height".
153
+ #
154
+ # @param space [OpenStudio::Model::Space] a space
155
+ #
156
+ # @return [Float] full height of space (0 if invalid input)
157
+ def self.space_height(space = nil)
158
+ hgt = 0.00
159
+ tag = "space_height"
160
+ return hgt unless space.is_a?(OpenStudio::Model::Space)
161
+
162
+ if space.additionalProperties.hasFeature(tag)
163
+ if space.additionalProperties.getFeatureAsDouble(tag).empty?
164
+ space.additionalProperties.resetFeature(tag)
165
+ hgt = TBD.spaceHeight(space)
166
+ space.additionalProperties.setFeature(tag, hgt)
167
+ return hgt
168
+ else
169
+ return space.additionalProperties.getFeatureAsDouble(tag).get
237
170
  end
238
-
239
-
240
- space.setBuildingStory(building_story_hash[space.zOrigin])
241
171
  end
242
- end
243
-
244
- # override run to implement the functionality of your script
245
- # model is an OpenStudio::Model::Model, runner is a OpenStudio::Ruleset::UserScriptRunner
246
- def self.auto_assign_stories(model)
247
172
 
248
- # get all spaces
249
- spaces = model.getSpaces
173
+ hgt = TBD.spaceHeight(space)
174
+ space.additionalProperties.setFeature(tag, hgt)
250
175
 
251
- #puts("Assigning Stories to Spaces")
176
+ hgt
177
+ end
252
178
 
253
- # make has of spaces and minz values
254
- sorted_spaces = Hash.new
255
- spaces.sort.each do |space|
256
- # loop through space surfaces to find min z value
257
- z_points = []
258
- space.surfaces.each do |surface|
259
- surface.vertices.each do |vertex|
260
- z_points << vertex.z
261
- end
179
+ ##
180
+ # Fetch a space's width, maybe stored as an AdditionalProperty -
181
+ # search for AdditionalProperty feature "space_width".
182
+ #
183
+ # @param space [OpenStudio::Model::Space] a space
184
+ #
185
+ # @return [Float] width of a space (0 if invalid input)
186
+ def self.space_width(space = nil)
187
+ wdt = 0.00
188
+ tag = "space_width"
189
+ return wdt unless space.is_a?(OpenStudio::Model::Space)
190
+
191
+ if space.additionalProperties.hasFeature(tag)
192
+ if space.additionalProperties.getFeatureAsDouble(tag).empty?
193
+ space.additionalProperties.resetFeature(tag)
194
+ wdt = TBD.spaceWidth(space)
195
+ space.additionalProperties.setFeature(tag, wdt)
196
+ return wdt
197
+ else
198
+ return space.additionalProperties.getFeatureAsDouble(tag).get
262
199
  end
263
- minz = z_points.min + space.zOrigin
264
- sorted_spaces[space] = minz
265
200
  end
266
201
 
267
- # pre-sort spaces
268
- sorted_spaces = sorted_spaces.sort {|a, b| a[1] <=> b[1]}
269
-
202
+ wdt = TBD.spaceWidth(space)
203
+ space.additionalProperties.setFeature(tag, wdt)
270
204
 
271
- # this should take the sorted list and make and assign stories
272
- sorted_spaces.sort.each do |space|
273
- space_obj = space[0]
274
- space_minz = space[1]
275
- if space_obj.buildingStory.empty?
276
- story = OpenstudioStandards::Geometry.model_get_building_story_for_nominal_height(model, space_minz)
277
- if story.nil?
278
- story = OpenStudio::Model::BuildingStory.new(model)
279
- story.setNominalZCoordinate(space_minz)
280
- story.setName("Building Story #{space_minz.round(1)}m")
281
- end
282
- space_obj.setBuildingStory(story)
283
- end
284
- end
205
+ wdt
285
206
  end
286
- end
287
-
288
- #This module contains helper functions that deal with Space objects.
289
- module Spaces
290
- #This method will return the horizontal placement type. (N,S,W,E,C) In the
291
- # case of a corner, it will take whatever surface area it faces is the
292
- # largest. It will also return the top, bottom or middle conditions.
293
-
294
- def self.get_space_placement(space)
295
- horizontal_placement = nil
296
- vertical_placement = nil
297
- json_data = nil
298
-
299
- #get all exterior surfaces.
300
- surfaces = BTAP::Geometry::Surfaces::filter_by_boundary_condition(space.surfaces,
301
- ["Outdoors",
302
- "Ground",
303
- "GroundFCfactorMethod",
304
- "GroundSlabPreprocessorAverage",
305
- "GroundSlabPreprocessorCore",
306
- "GroundSlabPreprocessorPerimeter",
307
- "GroundBasementPreprocessorAverageWall",
308
- "GroundBasementPreprocessorAverageFloor",
309
- "GroundBasementPreprocessorUpperWall",
310
- "GroundBasementPreprocessorLowerWall"])
311
-
312
- #exterior Surfaces
313
- ext_wall_surfaces = BTAP::Geometry::Surfaces::filter_by_surface_types(surfaces, ["Wall"])
314
- ext_bottom_surface = BTAP::Geometry::Surfaces::filter_by_surface_types(surfaces, ["Floor"])
315
- ext_top_surface = BTAP::Geometry::Surfaces::filter_by_surface_types(surfaces, ["RoofCeiling"])
316
-
317
- #Interior Surfaces..if needed....
318
- internal_surfaces = BTAP::Geometry::Surfaces::filter_by_boundary_condition(space.surfaces, ["Surface"])
319
- int_wall_surfaces = BTAP::Geometry::Surfaces::filter_by_surface_types(internal_surfaces, ["Wall"])
320
- int_bottom_surface = BTAP::Geometry::Surfaces::filter_by_surface_types(internal_surfaces, ["Floor"])
321
- int_top_surface = BTAP::Geometry::Surfaces::filter_by_surface_types(internal_surfaces, ["RoofCeiling"])
322
-
323
-
324
- vertical_placement = "NA"
325
- #determine if space is a top or bottom, both or middle space.
326
- if ext_bottom_surface.size > 0 and ext_top_surface.size > 0 and int_bottom_surface.size == 0 and int_top_surface.size == 0
327
- vertical_placement = "single_story_space"
328
- elsif int_bottom_surface.size > 0 and ext_top_surface.size > 0 and int_bottom_surface.size > 0
329
- vertical_placement = "top"
330
- elsif ext_bottom_surface.size > 0 and ext_top_surface.size == 0
331
- vertical_placement = "bottom"
332
- elsif ext_bottom_surface.size == 0 and ext_top_surface.size == 0
333
- vertical_placement = "middle"
334
- end
335
207
 
208
+ ##
209
+ # Return slab-on-grade perimeter (insulation) area for one or more spaces.
210
+ # Solution returns 0 if full basement. Relies on OpenStudio's BOOST-based
211
+ # 'joinAll' & 'buffer'. Boost methods have strict requirements when
212
+ # processing polygons. The solution will yield incorrect estimates with
213
+ # poorly-developed OpenStudio models.
214
+ #
215
+ # @param spaces [Set<OpenStudio::Model::Space>] target spaces
216
+ # @param w [Numeric] perimeter width
217
+ #
218
+ # @return [Numeric] perimeter area (0 if failure)
219
+ def self.perimeter_m2(spaces = [], w = 1.2)
220
+ tag = "btap_slab_perimeter_m2"
221
+ slabs = []
336
222
 
337
- #determine if what cardinal direction has the majority of external
338
- #surface area of the space.
339
- #set this to 'core' by default and change it if it is found to be a space exposed to a cardinal direction.
340
- horizontal_placement = nil
341
- #set up summing hashes for each direction.
342
- json_data = Hash.new
343
- walls_area_array = Hash.new
344
- subsurface_area_array = Hash.new
345
- boundary_conditions = {}
346
- boundary_conditions[:outdoors] = ["Outdoors"]
347
- boundary_conditions[:ground] = [
348
- "Ground",
349
- "GroundFCfactorMethod",
350
- "GroundSlabPreprocessorAverage",
351
- "GroundSlabPreprocessorCore",
352
- "GroundSlabPreprocessorPerimeter",
353
- "GroundBasementPreprocessorAverageWall",
354
- "GroundBasementPreprocessorAverageFloor",
355
- "GroundBasementPreprocessorUpperWall",
356
- "GroundBasementPreprocessorLowerWall"]
357
- #go through all directions.. need to do north twice since that goes around zero degree mark.
358
- orientations = [
359
- {:surface_type => 'Wall', :direction => 'north', :azimuth_from => 0.00, :azimuth_to => 45.0, :tilt_from => 0.0, :tilt_to => 180.0},
360
- {:surface_type => 'Wall', :direction => 'north', :azimuth_from => 315.001, :azimuth_to => 360.0, :tilt_from => 0.0, :tilt_to => 180.0},
361
- {:surface_type => 'Wall', :direction => 'east', :azimuth_from => 45.001, :azimuth_to => 135.0, :tilt_from => 0.0, :tilt_to => 180.0},
362
- {:surface_type => 'Wall', :direction => 'south', :azimuth_from => 135.001, :azimuth_to => 225.0, :tilt_from => 0.0, :tilt_to => 180.0},
363
- {:surface_type => 'Wall', :direction => 'west', :azimuth_from => 225.001, :azimuth_to => 315.0, :tilt_from => 0.0, :tilt_to => 180.0},
364
- {:surface_type => 'RoofCeiling', :direction => 'top', :azimuth_from => 0.0, :azimuth_to => 360.0, :tilt_from => 0.0, :tilt_to => 180.0},
365
- {:surface_type => 'Floor', :direction => 'bottom', :azimuth_from => 0.0, :azimuth_to => 360.0, :tilt_from => 0.0, :tilt_to => 180.0}
366
- ]
367
- [:outdoors, :ground].each do |bc|
368
- orientations.each do |orientation|
369
- walls_area_array[orientation[:direction]] = 0.0
370
- subsurface_area_array[orientation[:direction]] = 0.0
371
- json_data[orientation[:direction]] = {} if json_data[orientation[:direction]].nil?
372
- json_data[orientation[:direction]][bc] = {:surface_area => 0.0,
373
- :glazed_subsurface_area => 0.0,
374
- :opaque_subsurface_area => 0.0}
223
+ spaces = spaces.is_a?(OpenStudio::Model::Space) ? [spaces] : spaces
224
+ spaces = spaces.respond_to?(:to_a) ? spaces.to_a : []
225
+ return [] if spaces.empty?
375
226
 
376
- end
377
- end
227
+ bldg = spaces.first.model.getBuilding
378
228
 
229
+ # Check if pre-set as AdditionalProperty.
230
+ if bldg.additionalProperties.hasFeature(tag)
231
+ aire = bldg.additionalProperties.getFeatureAsDouble(tag)
379
232
 
380
- [:outdoors, :ground].each do |bc|
381
- orientations.each do |orientation|
382
- # puts "bc= #{bc}"
383
- # puts boundary_conditions[bc.to_sym]
384
- # puts boundary_conditions
385
- surfaces = BTAP::Geometry::Surfaces::filter_by_boundary_condition(space.surfaces, boundary_conditions[bc])
386
- selected_surfaces = BTAP::Geometry::Surfaces::filter_by_surface_types(surfaces, [orientation[:surface_type]])
387
- BTAP::Geometry::Surfaces::filter_by_azimuth_and_tilt(selected_surfaces, orientation[:azimuth_from], orientation[:azimuth_to], orientation[:tilt_from], orientation[:tilt_to]).each do |surface|
388
- #sum wall area and subsurface area by direction. This is the old way so excluding top and bottom surfaces.
389
- walls_area_array[orientation[:direction]] += surface.grossArea unless ['RoofCeiling', 'Floor'].include?(orientation[:surface_type])
390
- subsurface_area_array[orientation[:direction]] += surface.subSurfaces.map {|subsurface| subsurface.grossArea}.inject(0) {|sum, x| sum + x}
391
- json_data[orientation[:direction]][bc][:surface_area] += surface.grossArea
392
- glazings = BTAP::Geometry::Surfaces::filter_subsurfaces_by_types(surface.subSurfaces, ["FixedWindow", "OperableWindow", "GlassDoor", "Skylight", "TubularDaylightDiffuser", "TubularDaylightDome"])
393
- doors = BTAP::Geometry::Surfaces::filter_subsurfaces_by_types(surface.subSurfaces, ["Door", "OverheadDoor"])
394
- json_data[orientation[:direction]][bc][:glazed_subsurface_area] += glazings.map {|subsurface| subsurface.grossArea}.inject(0) {|sum, x| sum + x}
395
- json_data[orientation[:direction]][bc][:opaque_subsurface_area] += doors.map {|subsurface| subsurface.grossArea}.inject(0) {|sum, x| sum + x}
396
- end
233
+ unless aire.empty?
234
+ return aire.get if aire.get > 0
397
235
  end
398
236
  end
399
- puts JSON.pretty_generate(json_data)
400
-
401
- puts walls_area_array
402
- #find if no direction
403
- sum= 0.0
404
- ['north','east','south','west'].each do |direction|
405
- [:outdoors,:ground].each do |bc|
406
- sum += json_data[direction][bc][:surface_area]
407
- end
408
- end
409
- if sum == 0.0
410
- horizontal_placement = "core"
411
- else
412
- #find our which cardinal direction has the most exterior surface and declare it that orientation.
413
- horizontal_placement = walls_area_array.max_by {|k, v| v}[0] #include ext and ground.
414
- end
415
237
 
416
- #save JSON data
417
- json_data = ({:horizontal_placement => horizontal_placement,
418
- :vertical_placement => vertical_placement,
419
- }).merge(json_data)
420
- puts JSON.pretty_generate(json_data)
238
+ # Fetch slabs-on-grade.
239
+ spaces.each do |space|
240
+ t = space.siteTransformation
421
241
 
422
- return json_data
423
- end
424
-
425
- def self.is_perimeter_space?(model, space)
426
- exterior_surfaces = BTAP::Geometry::Surfaces::filter_by_boundary_condition(space.surfaces,
427
- ["Outdoors",
428
- "Ground",
429
- "GroundFCfactorMethod",
430
- "GroundSlabPreprocessorAverage",
431
- "GroundSlabPreprocessorCore",
432
- "GroundSlabPreprocessorPerimeter",
433
- "GroundBasementPreprocessorAverageWall",
434
- "GroundBasementPreprocessorAverageFloor",
435
- "GroundBasementPreprocessorUpperWall",
436
- "GroundBasementPreprocessorLowerWall"])
437
-
438
- return BTAP::Geometry::Surfaces::filter_by_surface_types(exterior_surfaces, ["Wall"]).size > 0
242
+ slbs = []
243
+ slbs = TBD.facets(space, "all", "floor")
244
+ slbs = slbs.select { |slb| slb.isGroundSurface }
245
+ next if slbs.empty?
439
246
 
440
- end
247
+ wlls = []
248
+ wlls = TBD.facets(space, "all", "wall")
249
+ wlls = wlls.select { |wll| wll.isGroundSurface }
250
+ next unless wlls.empty?
441
251
 
442
- def self.show(model, space)
443
- if drawing_interface = BTAP::Common::validate_array(model, space, "Space").first.drawing_interface
444
- if entity = drawing_interface.entity
445
- entity.visible = true
446
- end
252
+ slbs.each { |slb| slabs << t * slb.vertices }
447
253
  end
448
- end
449
254
 
450
- def self.hide(model, space)
451
- if drawing_interface = BTAP::Common::validate_array(model, space, "Space").first.drawing_interface
452
- if entity = drawing_interface.entity
453
- entity.visible = false
454
- end
455
- end
456
- end
255
+ return 0 if slabs.empty?
256
+ return 0 unless w.is_a?(Numeric)
257
+ return 0 if w < 0.1
457
258
 
458
- # This method will filter an array of spaces that have an external wall
459
- # passed floors. Note: if you wish to avoid to create an array of spaces,
460
- # simply put the space variable in [] brackets
461
- # Ex: get_all_surfaces_from_spaces( [space1,space2] )
462
- # @param spaces_array an array of type [OpenStudio::Model::Space]
463
- # @return an array of spaces.
464
- def self.filter_perimeter_spaces(model, spaces_array)
465
- spaces_array = BTAP::Common::validate_array(model, spaces_array, "Space")
466
- array = Array.new()
467
- spaces_array.each do |space|
468
- if space.is_a_perimeter_space?()
469
- array.push(space)
470
- end
471
- end
472
- return array
473
- end
259
+ # Slab surfaces might not site perfectly at Z-axis '0'. Flatten.
260
+ slabs = slabs.map { |slab| TBD.flatten(slab) }
261
+ ctr = 0
262
+ total = 0
474
263
 
475
- # This method will filter an array of spaces that have no external wall
476
- # passed floors. Note: if you wish to avoid to create an array of spaces,
477
- # simply put the space variable in [] brackets
478
- # Ex: get_all_surfaces_from_spaces( [space1,space2] )
479
- # @param spaces_array an array of type [OpenStudio::Model::Space]
480
- # @return an array of spaces.
481
- def self.filter_core_spaces(model, spaces_array)
482
- spaces_array = BTAP::Common::validate_array(model, spaces_array, "Space")
483
- array = Array.new()
484
- spaces_array.each do |space|
485
- unless space.is_a_perimeter_space?()
486
- array.push(space)
487
- end
488
- end
489
- return array
490
- end
264
+ # OpenStudio's 'joinAll' is sensitive to the order/sequence of polygons.
265
+ # A model may hold 20 slabs: a first call to 'joinAll' may successfully
266
+ # merge all 20 into 3 groups. A follow-up call may finally unite all.
267
+ while slabs.size != 1
268
+ break if ctr > 100
491
269
 
492
- def self.filter_spaces_by_space_types(model, spaces_array, spacetype_array)
493
- spaces_array = BTAP::Common::validate_array(model, spaces_array, "Space")
494
- spacetype_array = BTAP::Common::validate_array(model, spacetype_array, "SpaceType")
495
- #validate space array
496
- returnarray = Array.new()
497
- spaces_array.each do |space|
498
- returnarray << spacetype_array.include?(space.spaceType())
270
+ slabs = OpenStudio.joinAll(slabs.shuffle, 0.001)
271
+ ctr += 1
499
272
  end
500
- return returnarray
501
- end
502
273
 
503
- ##
504
- # Fetch a space's full height.
505
- #
506
- # @param space [OpenStudio::Model::Space] a space
507
- #
508
- # @return [Float] full height of space (0 if invalid input)
509
- def self.space_height(space = nil)
510
- return 0 unless space.is_a?(OpenStudio::Model::Space)
274
+ # If multiple unions, possibly dealing with separate pavillions. Add up.
275
+ slabs.each do |slab|
276
+ polyg = TBD.poly(slab, false, true, true, true, :cw)
277
+ next if polyg.empty?
511
278
 
512
- minZ = 10000
513
- maxZ = -10000
279
+ offset = OpenStudio.buffer(polyg, -w, 0.1)
280
+ next if offset.empty?
514
281
 
515
- space.surfaces.each do |surface|
516
- minZ = [surface.vertices.min_by(&:z).z, minZ].min
517
- maxZ = [surface.vertices.max_by(&:z).z, maxZ].max
518
- end
519
-
520
- maxZ < minZ ? 0 : maxZ - minZ
521
- end
282
+ offset = offset.get
283
+ area = OpenStudio.getArea(polyg)
284
+ m2 = OpenStudio.getArea(offset)
285
+ next if m2.empty?
286
+ next if area.empty?
522
287
 
523
- ##
524
- # Fetch a space's width.
525
- #
526
- # @param space [OpenStudio::Model::Space] a space
527
- #
528
- # @return [Float] width of a space (0 if invalid input)
529
- def self.space_width(space = nil)
530
- return 0 unless space.is_a?(OpenStudio::Model::Space)
531
-
532
- floors = facets(space, "all", "Floor")
533
- return 0 if floors.empty?
534
-
535
- # Automatically determining a space's "width" is not straightforward:
536
- # - a space may hold multiple floor surfaces at various Z-axis levels
537
- # - a space may hold multiple floor surfaces, with unique "widths"
538
- # - a floor surface may expand/contract (in "width") along its length.
539
- #
540
- # First, attempt to merge all floor surfaces together as 1x polygon:
541
- # - select largest floor surface (in area)
542
- # - determine its 3D plane
543
- # - retain only other floor surfaces sharing same 3D plane
544
- # - recover potential union between floor surfaces
545
- # - fall back to largest floor surface if invalid union
546
- # - return width of largest bounded box
547
- floors = floors.sort_by(&:grossArea).reverse
548
- floor = floors.first
549
- plane = floor.plane
550
- t = OpenStudio::Transformation.alignFace(floor.vertices)
551
- polyg = poly(floor, false, true, true, t, :ulc).to_a.reverse
552
- return 0 if polyg.empty?
553
-
554
- if floors.size > 1
555
- floors = floors.select { |flr| plane.equal(flr.plane, 0.001) }
556
-
557
- if floors.size > 1
558
- polygs = floors.map { |flr| poly(flr, false, true, true, t, :ulc) }
559
- polygs = polygs.reject { |plg| plg.empty? }
560
- polygs = polygs.map { |plg| plg.to_a.reverse }
561
- union = OpenStudio.joinAll(polygs, 0.01).first
562
- polyg = poly(union, false, true, true)
563
- return 0 if polyg.empty?
564
- end
288
+ total += area.get - m2.get
565
289
  end
566
290
 
567
- res = realignedFace(polyg.to_a.reverse)
568
- return 0 if res[:box].nil?
569
-
570
- # A bounded box's 'height', at its narrowest, is its 'width'.
571
- height(res[:box])
572
- end
573
- end
574
-
575
- #This Module contains methods that create, modify and query Thermal zone objects.
576
- module Zones
577
- # This method will filter an array of zones that have an external wall
578
- # passed floors. Note: if you wish to avoid to create an array of spaces,
579
- # simply put the space variable in [] brackets
580
- # Ex: get_all_surfaces_from_spaces( [space1,space2] )
581
- # @param thermal_zones [Array<OpenStudio::Model::ThermalZone>] an array of zones
582
- # @return [Array<OpenStudio::Model::ThermalZone] an array of thermal zones.
583
- def self.filter_perimeter_zones(thermal_zones)
584
- array = Array.new()
585
- thermal_zones.each do |zone|
586
- zone.space.each do |space|
587
- if space.is_a_perimeter_space?()
588
- array.push(zone)
589
- next
590
- end
291
+ # Add as AdditionalProperty (if missing) for future calls.
292
+ if total > 0
293
+ unless bldg.additionalProperties.hasFeature(tag)
294
+ bldg.additionalProperties.setFeature(tag, total)
591
295
  end
592
296
  end
593
- return array
594
- end
595
297
 
596
- # This method will filter an array of zones that have no external wall
597
- # passed floors. Note: if you wish to avoid to create an array of spaces,
598
- # simply put the space variable in [] brackets
599
- # Ex: ( [space1,space2] )
600
- # @param thermal_zones [Array<OpenStudio::Model::ThermalZone] an array of zones
601
- # @return [Array<OpenStudio::Model::ThermalZone] an array of zones
602
- def self.filter_core_zones(thermal_zones)
603
- array = Array.new()
604
- thermal_zones.getThermalZones.sort.each do |zone|
605
- zone.space.each do |space|
606
- if not space.is_a_perimeter_space?()
607
- array.push(zone)
608
- next
609
- end
610
- end
611
- end
612
- return array
298
+ total
613
299
  end
614
300
  end
615
301
 
616
302
  module Surfaces
617
- def self.create_surface(model, name, os_point3d_array, boundary_condition = "", construction = "")
618
- os_surface = OpenStudio::Model::Surface.new(os_point3d_array, model)
619
- os_surface.setName(name)
620
- if OpenStudio::Model::Surface::validOutsideBoundaryConditionValues.include?(boundary_condition)
621
- self.set_surfaces_boundary_condition([os_surface], boundary_condition)
622
- else
623
- puts "boundary condition not set for #{name}"
624
- end
625
- os_surface.setConstruction(construction)
626
- return os_surface
627
- end
628
-
629
- # This method will rotate a surface
630
- # @param planar_surfaces [Array<OpenStudio::Model::Surface>] an array of surfaces
631
- # @param azimuth_degrees [Float] rotation value
632
- # @param tilt_degrees [Float] rotation value
633
- # @param translation_vector [OpenStudio::Vector3d] a vector along which to move all surfaces
634
- # @return [OpenStudio::Model::Model] the model object.
635
- 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))
636
- # Identity matrix for setting space origins
637
- azimuth_matrix = OpenStudio::Transformation::rotation(OpenStudio::Vector3d.new(0, 0, 1), azimuth_degrees * Math::PI / 180)
638
- tilt_matrix = OpenStudio::Transformation::rotation(OpenStudio::Vector3d.new(0, 0, 1), tilt_degrees * Math::PI / 180)
639
- translation_matrix = OpenStudio::createTranslation(translation_vector)
640
- planar_surfaces.each do |surface|
641
- surface.changeTransformation(azimuth_matrix)
642
- surface.changeTransformation(tilt_matrix)
643
- surface.changeTransformation(translation_matrix)
644
- end
645
- return planar_surfaces
646
- end
647
-
648
- def self.set_fenestration_to_wall_ratio(surfaces, ratio, offset = 0, height_offset_from_floor = true, floor = "all")
649
- surfaces.each do |surface|
650
- result = surface.setWindowToWallRatio(ratio, offset, height_offset_from_floor)
651
- raise("Unable to set FWR for surface " +
652
- surface.name.get.to_s +
653
- " . Possible reasons are if the surface is not a wall, if the surface
654
- is not rectangular in face coordinates, if requested ratio is too large
655
- (window area ~= surface area) or too small (min dimension of window < 1 foot),
656
- or if the window clips any remaining sub surfaces. Otherwise, removes all
657
- existing windows and adds new window to meet requested ratio.") unless result
658
- end
659
- return surfaces
660
- end
661
-
662
- #["FixedWindow" , "OperableWindow" , "Door" , "GlassDoor", "OverheadDoor" , "Skylight", "TubularDaylightDiffuser","TubularDaylightDome"]
663
303
  def self.filter_subsurfaces_by_types(subsurfaces, subSurfaceTypes)
664
-
665
- #check to see if a string or an array was passed.
666
304
  if subSurfaceTypes.kind_of?(String)
667
305
  temp = subSurfaceTypes
668
306
  subSurfaceTypes = Array.new()
@@ -686,25 +324,29 @@ module BTAP
686
324
  end
687
325
  end
688
326
  return return_array
689
-
690
327
  end
691
328
 
692
- #This method creates a new construction based on the current, changes the rsi and assign the construction to the current surface.
693
- #Most of the meat of this method is in the construction class. Testing is done there.
329
+ # This method creates a new construction based on the current, changes
330
+ # the rsi and assigns the construction to the current surface. Most of the
331
+ # meat of this method is in the construction class. Testing is done there.
694
332
  def self.set_surfaces_construction_conductance(surfaces, conductance)
695
333
  surfaces.each do |surface|
696
- #a bit of acrobatics to get the construction object from the ConstrustionBase object's name.
334
+
335
+ # A bit of acrobatics to get the construction object from the
336
+ # ConstrustionBase object's name.
697
337
  construction = OpenStudio::Model::getConstructionByName(surface.model, surface.construction.get.name.to_s).get
698
- #create a new construction with the requested conductance value based on the current construction.
699
338
 
339
+ # Create a new construction with the requested conductance value based
340
+ # on the current construction.
700
341
  new_construction = BTAP::Resources::Envelope::Constructions::customize_opaque_construction(surface.model, construction, conductance)
701
342
  surface.setConstruction(new_construction)
702
343
  end
703
344
  return surfaces
704
345
  end
705
346
 
706
- # This method sets the boundary condition for a surface and it's matching surface.
707
- # If set to adiabatic, it will remove all subsurfaces since E+ cannot have adiabatic sub surfaces.
347
+ # This method sets the boundary condition for a surface and it's matching
348
+ # surface. If set to adiabatic, it will remove all subsurfaces since E+
349
+ # cannot have adiabatic sub surfaces.
708
350
  def self.set_surfaces_boundary_condition(model, surfaces, boundaryCondition)
709
351
  surfaces = BTAP::Common::validate_array(model, surfaces, "Surface")
710
352
  if OpenStudio::Model::Surface::validOutsideBoundaryConditionValues.include?(boundaryCondition)
@@ -733,26 +375,19 @@ module BTAP
733
375
  end
734
376
  end
735
377
 
736
- def self.filter_by_non_defaulted_surfaces(surfaces)
737
- non_defaulted_surfaces = Array.new()
738
- surfaces.each {|surface| non_defaulted_surfaces << surface unless surface.isConstructionDefaulted}
739
- return non_defaulted_surfaces
740
- end
741
-
742
378
  def self.filter_by_boundary_condition(surfaces, boundary_conditions)
743
- #check to see if a string or an array was passed.
744
379
  if boundary_conditions.kind_of?(String)
745
380
  temp = boundary_conditions
746
381
  boundary_conditions = Array.new()
747
382
  boundary_conditions.push(temp)
748
383
  end
749
- #ensure boundary conditions are valid
384
+
385
+ # Ensure boundary conditions are valid.
750
386
  boundary_conditions.each do |boundary_condition|
751
387
  unless OpenStudio::Model::Surface::validOutsideBoundaryConditionValues.include?(boundary_condition)
752
388
  raise "ERROR: Invalid Boundary Condition = " + boundary_condition + "Correct Values are:" + OpenStudio::Model::Surface::validOutsideBoundaryConditionValues.to_s
753
389
  end
754
390
  end
755
- #create return array.
756
391
  return_array = Array.new()
757
392
 
758
393
  if boundary_conditions.size == 0 or boundary_conditions[0].upcase == "All".upcase
@@ -770,8 +405,6 @@ module BTAP
770
405
  end
771
406
 
772
407
  def self.filter_by_surface_types(surfaces, surfaceTypes)
773
-
774
- #check to see if a string or an array was passed.
775
408
  if surfaceTypes.kind_of?(String)
776
409
  temp = surfaceTypes
777
410
  surfaceTypes = Array.new()
@@ -797,80 +430,57 @@ module BTAP
797
430
  return return_array
798
431
  end
799
432
 
800
- def self.filter_by_interzonal_surface(surfaces)
801
- return_array = Array.new()
802
- surfaces.each do |surface|
803
- unless surface.adjacentSurface().empty?
804
- return_array.push(surface)
805
- end
806
- return return_array
807
- end
808
- end
809
-
810
- # Azimuth start from Y axis, Tilts starts from Z-axis
811
- def self.filter_by_azimuth_and_tilt(surfaces, azimuth_from, azimuth_to, tilt_from, tilt_to, tolerance = 1.0)
812
- return_surfaces = []
813
- surfaces.each do |surface|
814
- 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?
815
- return_surfaces << surface
816
- end
817
- end
818
- return return_surfaces
819
- end
820
-
821
- def self.show(surfaces)
822
- surfaces.each do |surface|
823
- if drawing_interface = surface.drawing_interface
824
- if entity = drawing_interface.entity
825
- entity.visible = false
826
- end
827
- end
828
- end
829
- end
830
-
831
- def self.hide(surfaces)
832
- surfaces.each do |surface|
833
- if drawing_interface = surface.drawing_interface
834
- if entity = drawing_interface.entity
835
- entity.visible = false
836
- end
837
- end
838
- end
839
- end
840
-
841
433
  # 2018-09-27 Chris Kirney
842
- # This method takes a surface in the x-y plane (z coordinates are ignored) with an upwardly pointing normal and
843
- # turns it into convex quadrialaterals. If the original surface is already a convex quadrilateral then this method
844
- # will go to a lot of trouble to return the same thing (only with the coordinates of the points rounded). If
845
- # the surface is already a concave surface then this method will return it broken into a bunch of quadrilaters
846
- # (maybe a triangle here and there). Neither of the above are especially useful. However, the point of this
847
- # method is if you pass this a concave surface it will return convex surfaces that you can then use with other
848
- # methods that only apply to convex surfaces (such as a method which fits skylights into a roof). Note that
849
- # surfaces per say are not returned. Rather, an array containing 4 points arranged in counter clockwise order is
850
- # returned. These points are also in the x-y plane with an upwardly pointing normal. No z coordinate is returned.
434
+ # This method takes a surface in the x-y plane (z coordinates are
435
+ # ignored) with an upwardly pointing normal and turns it into convex
436
+ # quadrialaterals. If the original surface is already a convex
437
+ # quadrilateral then this method will go to a lot of trouble to return
438
+ # the same thing (only with the coordinates of the points rounded). If
439
+ # the surface is already a concave surface then this method will return it
440
+ # broken into a bunch of quadrilaters (maybe a triangle here and there).
441
+ # Neither of the above are especially useful. However, the point of this
442
+ # method is if you pass this a concave surface it will return convex
443
+ # surfaces that you can then use with other methods that only apply to
444
+ # convex surfaces (such as a method which fits skylights into a roof).
445
+ # Note that surfaces per say are not returned. Rather, an array containing
446
+ # 4 points arranged in counter clockwise order is returned. These points
447
+ # are also in the x-y plane with an upwardly pointing normal. No z
448
+ # coordinate is returned.
851
449
  #
852
- # The method works by first looking for upward pointing lines. It then looks for cooresponding downward pointing
853
- # lines. Since all of the surfaces are closed there should always be enough upward and downward pointing lines.
854
- # Horizontal lines are ignored. It then checks to see which y projections of the upward and downward pointing
855
- # lines overlap. It then sees which of these overlaping lines overlap. Ultimately you wind up with a whole bunch
856
- # of overlapping y projections that coorespond with different upward pointing lines. These overlapping
857
- # y projects are either unique, or they precisely match other overlapping y projections. The point is that, in
858
- # the case of a convex shape, an upward pointing line may overlap with some lines close, and some far away, with
859
- # some lines in between. The method then sorts through the overlapping y projections to see which are closest
860
- # to a given upward pointing line. It keeps the unique ones, and the ones that are closest. The end result
861
- # should be downward pointing line segments that correspond to an upward pointing line segment with no intervening
862
- # lines. The last part of the method assembles the quadrilaterals from the remaining downward pointing line
863
- # segments which correspond with a given upward pointing line segment.
450
+ # The method works by first looking for upward pointing lines. It then
451
+ # looks for cooresponding downward pointing lines. Since all of the
452
+ # surfaces are closed there should always be enough upward and downward
453
+ # pointing lines. Horizontal lines are ignored. It then checks to see
454
+ # which y projections of the upward and downward pointing lines overlap.
455
+ # It then sees which of these overlaping lines overlap. Ultimately you
456
+ # wind up with a whole bunch of overlapping y projections that coorespond
457
+ # with different upward pointing lines. These overlapping y projects are
458
+ # either unique, or they precisely match other overlapping y projections.
459
+ # The point is that, in the case of a convex shape, an upward pointing
460
+ # line may overlap with some lines close, and some far away, with some
461
+ # lines in between. The method then sorts through the overlapping y
462
+ # projections to see which are closest to a given upward pointing line.
463
+ # It keeps the unique ones, and the ones that are closest. The end result
464
+ # should be downward pointing line segments that correspond to an upward
465
+ # pointing line segment with no intervening lines. The last part of
466
+ # the method assembles the quadrilaterals from the remaining downward
467
+ # pointing line segments which correspond with a given upward pointing
468
+ # line segment.
864
469
  def self.make_convex_surfaces(surface:, tol: 12)
865
- # Note that points on surfaces are given counterclockwise when looking at the surface from the opposite direction as
866
- # the outward normal (i.e. the outward normal is pointing at you). I use point_a1, point_a2, point_b1 and point b2
867
- # lots. For this, point_a refers to vectors pointing up. In this case point_a1 is at the top of the vector and
868
- # point_a2 is at the bottom of the vector. Contrarily, point_b refers to vectors pointing down. In this case
869
- # point_b1 is at the bottom of the vector and point_b2 is at the top. All of this comes about because I cycle
870
- # through the points starting at the 2nd point and and going to the last point. I count vectors as starting from
871
- # the last point and going toward the current point.
872
- # See following where P1 through P4 are the points. When cycling through a is where you start and b is where you
873
- # end. the o is the tip of the outward normal pointing at you.
470
+
471
+ # Note that points on surfaces are given counterclockwise when looking
472
+ # at the surface from the opposite direction as the outward normal (i.e.
473
+ # the outward normal is pointing at you). I use point_a1, point_a2,
474
+ # point_b1 and point b2 lots. For this, point_a refers to vectors
475
+ # pointing up. In this case point_a1 is at the top of the vector and
476
+ # point_a2 is at the bottom of the vector. Contrarily, point_b refers to
477
+ # vectors pointing down. In this case point_b1 is at the bottom of the
478
+ # vector and point_b2 is at the top. All of this comes about because I
479
+ # cycle through the points starting at the 2nd point and and going to
480
+ # the last point. I count vectors as starting from the last point and
481
+ # going toward the current point. See following where P1 through P4 are
482
+ # the points. When cycling through a is where you start and b is where
483
+ # you end. the o is the tip of the outward normal pointing at you.
874
484
  # P2b------------aP1
875
485
  # a b
876
486
  # | |
@@ -878,10 +488,14 @@ module BTAP
878
488
  # | |
879
489
  # b a
880
490
  # P3a-----------bP4
491
+
881
492
  surf_verts = []
882
- # Get the vertices from the surface, keep the x and y coordinates, and turn the vertices from OpenStudio's
883
- # data structure to a differet one which is a little easier to deal with. Also, round them to the given
884
- # tolerance. This is done because some numbers that should match don't because of tiny errors.
493
+
494
+ # Get the vertices from the surface, keep the x and y coordinates, and
495
+ # turn the vertices from OpenStudio's data structure to a differet one
496
+ # which is a little easier to deal with. Also, round them to the given
497
+ # tolerance. This is done because some numbers that should match don't
498
+ # because of tiny errors.
885
499
  surface.vertices.each do |vert|
886
500
  surf_vert = {
887
501
  x: vert.x.to_f.round(tol),
@@ -890,29 +504,42 @@ module BTAP
890
504
  }
891
505
  surf_verts << surf_vert
892
506
  end
507
+
893
508
  # If the surface is a triangle or less then do nothing and return it.
894
509
  return surf_verts if surf_verts.length <= 3
510
+
895
511
  # Adding the first vertex to the end so that it is accounted for.
896
512
  surf_verts << surf_verts[0]
897
- # Following we go through the points, look for upward pointing lines, then look for downward pointing lines to
898
- # their left (only to the left because everything goes counter-clockwise). If there is a line find how much the
899
- # current upward pointing line overlaps with it in the y direction.
513
+
514
+ # Following we go through the points, look for upward pointing lines,
515
+ # then look for downward pointing lines to their left (only to the left
516
+ # because everything goes counter-clockwise). If there is a line find
517
+ # how much the current upward pointing line overlaps with it in the
518
+ # y direction.
900
519
  overlap_segs = []
901
520
  new_surfs = []
902
521
  for i in 1..(surf_verts.length - 1)
903
- # Is this line segment pointing up? If no, then ignore it and go to the next line segment.
522
+
523
+ # Is this line segment pointing up? If no, then ignore it and go to
524
+ # the next line segment.
904
525
  if surf_verts[i][:y] > surf_verts[i - 1][:y]
526
+
905
527
  # Go through each line segment
906
528
  for j in 1..(surf_verts.length - 1)
907
- # 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.
908
- # I revised this to check if the start or end of the current (index i) line segment is to the left of the
909
- # line segment being checked.
910
- #if surf_verts[j][:x] < surf_verts[i][:x] and surf_verts[j - 1][:x] < surf_verts[i - 1][:x]
529
+
530
+ # Is the line segment to the left of the current (index i) line
531
+ # segment? If no, then ignore it and go to the next one. I revised
532
+ # this to check if the start or end of the current (index i) line
533
+ # segment is to the left of the line segment being checked.
911
534
  if surf_verts[j][:x] < surf_verts[i][:x] || surf_verts[j - 1][:x] < surf_verts[i - 1][:x]
912
- # Is the line segment pointing down? If no, then ignore it and go to the next line segment.
535
+
536
+ # Is the line segment pointing down? If no, then ignore it and
537
+ # go to the next line segment.
913
538
  if surf_verts[j][:y] < surf_verts[j - 1][:y]
914
- # Do the y coordinates of the line segment overlap with the current (index i) line segment? If no
915
- # then ignore it and go to the next line segment.
539
+
540
+ # Do the y coordinates of the line segment overlap with the
541
+ # current (index i) line segment? If no then ignore it and go
542
+ # to the next line segment.
916
543
  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])
917
544
  unless overlap_y[:overlap_start].nil? || overlap_y[:overlap_end].nil?
918
545
  unless overlap_y[:overlap_start] == overlap_y[:overlap_end]
@@ -933,25 +560,35 @@ module BTAP
933
560
  end
934
561
  end
935
562
  end
563
+
936
564
  # This part:
937
- # 1. Subdivides the overlapping segments found above into either unique overlaps between the upward and downward
938
- # pointing lines or overlapping segments that exactly match one another.
939
- # 2. Goes through each upward pointing line and finds the closest overlapping downward pointing line segments (if
940
- # these downward pointing segments belong together they are re-attached).
941
- # 3. Makes quadrilaterals (or triangles as the case may be) out of each upward pointing line and the closest
942
- # downward pointing line segment.
565
+ # 1. Subdivides the overlapping segments found above into either
566
+ # unique overlaps between the upward and downward pointing lines or
567
+ # overlapping segments that exactly match one another.
568
+ # 2. Goes through each upward pointing line and finds the closest
569
+ # overlapping downward pointing line segments (if these downward
570
+ # pointing segments belong together they are re-attached).
571
+ # 3. Makes quadrilaterals (or triangles as the case may be) out of
572
+ # each upward pointing line and the closest downward pointing line
573
+ # segment.
943
574
  if overlap_segs.length > 1
944
- # Subdivide the overlapping segments found above into either unique overlaps between the upward and downward
945
- # pointing lines or overlapping segments that exactly match one another.
575
+
576
+ # Subdivide the overlapping segments found above into either
577
+ # unique overlaps between the upward and downward pointing lines or
578
+ # overlapping segments that exactly match one another.
946
579
  overlap_segs = subdivide_overlaps(overlap_segs: overlap_segs)
580
+
947
581
  # Remove redundant overlapping segments
948
582
  recheck = true
949
583
  while recheck
950
584
  recheck = false
585
+
951
586
  # Go through each overlapping segment and look for duplicate segments
952
587
  overlap_segs.each_with_index do |ind_overlap_seg, seg_index|
588
+
953
589
  # Find duplicate overlapping segments
954
590
  redundant_segs = overlap_segs.select { |check_seg| check_seg == ind_overlap_seg}
591
+
955
592
  # Remove the first one and then restart the while loop to recompile the seg_index
956
593
  if redundant_segs.size > 1
957
594
  overlap_segs.delete_at(seg_index)
@@ -959,15 +596,21 @@ module BTAP
959
596
  end
960
597
  end
961
598
  end
599
+
962
600
  for i in 1..(surf_verts.length - 1)
963
- # Does the line point up? No then ignore and go on to the next one.
601
+
602
+ # Does the line point up? No then ignore and go on to the next one.
964
603
  if surf_verts[i][:y] > surf_verts[i - 1][:y]
965
- # Finds the closest overlapping downward pointing line segments that correspond to this upward pointing
966
- # line (if some of these downward pointing segments belong together then re-attached them).
604
+
605
+ # Finds the closest overlapping downward pointing line segments
606
+ # that correspond to this upward pointing line (if some of these
607
+ # downward pointing segments belong together then re-attached
608
+ # them).
967
609
  closest_overlaps = get_overlapping_segments(overlap_segs: overlap_segs, index: i, point_a1: surf_verts[i], point_a2: surf_verts[i - 1])
968
610
  closest_overlaps = closest_overlaps.sort_by {|closest_overlap| closest_overlap[:overlap_y][:overlap_start]}
969
- # Create the quadrilaterals out of the downward pointing line segments closest to the current upward
970
- # pointing line.
611
+
612
+ # Create the quadrilaterals out of the downward pointing line
613
+ # segments closest to the current upward pointing line.
971
614
  for j in 0..(closest_overlaps.length - 1)
972
615
  new_surf = []
973
616
  z_loc = surf_verts[closest_overlaps[j][:index_a1]][:z]
@@ -1004,60 +647,69 @@ module BTAP
1004
647
  end
1005
648
  end
1006
649
  elsif overlap_segs.length == 1
1007
- # There is only one overlapping downward line, thus this is a quadrilateral already so just return it.
1008
- # Remove the last vertex as we had artificially added it at the start.
650
+
651
+ # There is only one overlapping downward line, thus this is a
652
+ # quadrilateral already so just return it. Remove the last vertex as
653
+ # we had artificially added it at the start.
1009
654
  surf_verts.pop
1010
655
  new_surfs << surf_verts
1011
656
  end
1012
657
  return new_surfs
1013
658
  end
1014
659
 
1015
- # This method takes the y projections of a bunch of overlapping line segments and sorts them to determines which
1016
- # are unique and, if they are not unique, which is closest to the current, upwardly pointing, line. If several
1017
- # overlapping segments belong to the same line they are put together (after the 'subdivide_overlaps' method broke
1018
- # them apart). The end result is the method returns the closet point downward pointing line segments closest to
1019
- # the given upward pointing line segment.
660
+ # This method takes the y projections of a bunch of overlapping line
661
+ # segments and sorts them to determines which are unique and, if they are
662
+ # not unique, which is closest to the current, upwardly pointing, line.
663
+ # If several overlapping segments belong to the same line they are put
664
+ # together (after the 'subdivide_overlaps' method broke them apart). The
665
+ # end result is the method returns the closet point downward pointing line
666
+ # segments closest to the given upward pointing line segment.
1020
667
  #
1021
668
  # overlap_segs: This is an array of hashes that looks like:
1022
- # overlap_seg = {
1023
- # index_a1: i,
1024
- # index_a2: i-1,
1025
- # index_b1: j,
1026
- # index_b2: j-1,
1027
- # point_b1: surf_verts[j],
1028
- # point_b2: surf_verts[j-1],
1029
- # overlap_y: overlap_y
1030
- # }
1031
- # index_a1: The index of the array of points that cooresponds with the top of line a (points up)
1032
- # index_a1: The index of the array of points that cooresponds with the bottom of line a (points up)
1033
- # index_b1: The index of the array of points that cooresponds with the bottom of line b (points down)
1034
- # index_b1: The index of the array of points that cooresponds with the top of line b (points down)
1035
- # point_b1: The coordinates of the bottom of line b
1036
- # point_b2: The coordinates of the top of line b
669
+ # {
670
+ # index_a1: i,
671
+ # index_a2: i-1,
672
+ # index_b1: j,
673
+ # index_b2: j-1,
674
+ # point_b1: surf_verts[j],
675
+ # point_b2: surf_verts[j-1],
676
+ # overlap_y: overlap_y
677
+ # }
1037
678
  #
1038
- # overlap_y: A hash that contains the coordinates of the top and bottom of the y projection of the overlapping
1039
- # lines (line a and line b)
1040
- # overlap_y = {
1041
- # overlap_start: overlap_start,
1042
- # overlap_end: overlap_end
1043
- # }
1044
- # overlap_start: The y coordinate of the top of the overlap
1045
- # overlap_end: The y coordinate of the bottom of the overlap
679
+ # index_a1: The index of the array of points that cooresponds with the top of line a (points up)
680
+ # index_a1: The index of the array of points that cooresponds with the bottom of line a (points up)
681
+ # index_b1: The index of the array of points that cooresponds with the bottom of line b (points down)
682
+ # index_b1: The index of the array of points that cooresponds with the top of line b (points down)
683
+ # point_b1: The coordinates of the bottom of line b
684
+ # point_b2: The coordinates of the top of line b
685
+ #
686
+ # overlap_y: A hash that contains the coordinates of the top and bottom of
687
+ # the y projection of the overlapping lines (line a and line b)
688
+ # overlap_y = {
689
+ # overlap_start: overlap_start,
690
+ # overlap_end: overlap_end
691
+ # }
692
+ # overlap_start: The y coordinate of the top of the overlap
693
+ # overlap_end: The y coordinate of the bottom of the overlap
1046
694
  #
1047
695
  # index: The index of the array of points that cooresponds with the top of of the current upward pointing line.
1048
696
  #
1049
697
  # point_a1: The coordinates of the top of the first line
1050
698
  # point_a2: The coordinates of the bottom of the first line
1051
- # This naming convention was chosen because this method was originally designed to work with the
1052
- # 'make_concave_surfaces' method (see above). That method choses lines that point up and then sees where they
1053
- # overlap with lines pointing down. The point_1 of each line is the end of the line. In this case a lines point
1054
- # up and b lines point down.
699
+ #
700
+ # This naming convention was chosen because this method was originally
701
+ # designed to work with the 'make_concave_surfaces' method (see above).
702
+ # That method choses lines that point up and then sees where they overlap
703
+ # with lines pointing down. The point_1 of each line is the end of the
704
+ # line. In this case a lines point up and b lines point down.
1055
705
  def self.get_overlapping_segments(overlap_segs:, index:, point_a1:, point_a2:)
1056
706
  closest_overlaps = []
1057
707
  linea_overlaps = []
1058
- # This goes through all the line segments and determines which correspond to the current upward pointing line
1059
- # segment(line a). It also determines the x coordinate distance between the top and bottom of the overlapping
1060
- # portions of the line segments.
708
+
709
+ # This goes through all the line segments and determines which
710
+ # correspond to the current upward pointing line segment(line a). It
711
+ # also determines the x coordinate distance between the top and bottom
712
+ # of the overlapping portions of the line segments.
1061
713
  curr_overlap_segs = overlap_segs.select { |seg| (seg[:index_a1] == index) && (seg[:index_a2] == (index - 1)) }
1062
714
  curr_overlap_segs.each do |overlap_seg|
1063
715
  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)
@@ -1074,11 +726,14 @@ module BTAP
1074
726
  linea_overlaps << linea_overlap
1075
727
  end
1076
728
 
1077
- # This sorts through the overlapping downward pointing line segments corresponding to the current upward pointing
1078
- # line a. The overlapping downward pointing line segments closest to the current upward pointing line segment
1079
- # are kept. The other are discarded. Unique overlapping line segments are kept as well. There should only be
1080
- # unuique overlapping line segments or overlapping line segments that precisely match one another because of
1081
- # the 'subdivide_overlaps' method which this method is supposed to work with.
729
+ # This sorts through the overlapping downward pointing line segments
730
+ # corresponding to the current upward pointing line a. The overlapping
731
+ # downward pointing line segments closest to the current upward pointing
732
+ # line segment are kept. The other are discarded. Unique overlapping
733
+ # line segments are kept as well. There should only be unique
734
+ # overlapping line segments or overlapping line segments that precisely
735
+ # match one another because of the 'subdivide_overlaps' method which
736
+ # this method is supposed to work with.
1082
737
  linea_overlaps.each do |line_a_overlap|
1083
738
  overlaps = linea_overlaps.select { |seg| seg[:overlap][:overlap_y] == line_a_overlap[:overlap][:overlap_y]}
1084
739
  if overlaps.size > 1
@@ -1089,8 +744,8 @@ module BTAP
1089
744
  end
1090
745
  end
1091
746
 
1092
- # This combines the line segments that belong together. These were broken apart because of the
1093
- # 'subdivide_overlaps' method.
747
+ # This combines the line segments that belong together. These were
748
+ # broken apart because of the 'subdivide_overlaps' method.
1094
749
  overlap_exts = [closest_overlaps[0]]
1095
750
  for j in 0..(closest_overlaps.length - 1)
1096
751
  index = 0
@@ -1120,19 +775,21 @@ module BTAP
1120
775
  return overlap_exts
1121
776
  end
1122
777
 
1123
- # This method was originally written to work with the 'make_concave_surfaces' method above. It takes the
1124
- # y-components of a bunch of line segemnts and cuts them up until they either are unique (no other overlapping
1125
- # components) or they match the y-components of other line segments.
1126
- # overlap_segs: This is an array of hashes that looks like:
1127
- # overlap_seg = {
1128
- # index_a1: i,
1129
- # index_a2: i-1,
1130
- # index_b1: j,
1131
- # index_b2: j-1,
1132
- # point_b1: surf_verts[j],
1133
- # point_b2: surf_verts[j-1],
1134
- # overlap_y: overlap_y
1135
- # }
778
+ # This method was originally written to work with the
779
+ # 'make_concave_surfaces' method above.It takes the y-components of a
780
+ # bunch of line segemnts and cuts them up until they either are unique (no
781
+ # other overlapping components) or they match the y-components of other
782
+ # line segments. overlap_segs: This is an array of hashes that looks like:
783
+ # {
784
+ # index_a1: i,
785
+ # index_a2: i-1,
786
+ # index_b1: j,
787
+ # index_b2: j-1,
788
+ # point_b1: surf_verts[j],
789
+ # point_b2: surf_verts[j-1],
790
+ # overlap_y: overlap_y
791
+ # }
792
+ #
1136
793
  # index_a1: The index of the array of points that cooresponds with the top of line a (points up)
1137
794
  # index_a1: The index of the array of points that cooresponds with the bottom of line a (points up)
1138
795
  # index_b1: The index of the array of points that cooresponds with the bottom of line b (points down)
@@ -1140,48 +797,63 @@ module BTAP
1140
797
  # point_b1: The coordinates of the bottom of line b
1141
798
  # point_b2: The coordinates of the top of line b
1142
799
  #
1143
- # overlap_y: A hash that contains the coordinates of the top and bottom of the y projection of the overlapping
1144
- # lines (line a and line b)
1145
- # overlap_y = {
1146
- # overlap_start: overlap_start,
1147
- # overlap_end: overlap_end
1148
- # }
800
+ # overlap_y: A hash that contains the coordinates of the top and bottom of
801
+ # the y projection of the overlapping lines (line a and line b)
802
+ # overlap_y = {
803
+ # overlap_start: overlap_start,
804
+ # overlap_end: overlap_end
805
+ # }
1149
806
  # overlap_start: The y coordinate of the top of the overlap
1150
807
  # overlap_end: The y coordinate of the bottom of the overlap
1151
808
  def self.subdivide_overlaps(overlap_segs:)
1152
809
  restart = true
1153
- # Keep doing this until the y projections of the lines are either unique or the match the y projections of other
1154
- # lines.
810
+
811
+ # Keep doing this until the y projections of the lines are either unique
812
+ # or the match the y projections of other lines.
1155
813
  while restart == true
1156
814
  restart = false
1157
815
  overlap_segs.each_with_index do |overlap_seg, curr_seg_index|
1158
816
  for j in 0..(overlap_segs.length - 1)
817
+
1159
818
  # Skip this y projection if it is the same as that in overlap_seg
1160
819
  if overlap_seg == overlap_segs[j]
1161
820
  next
1162
821
  end
1163
- # Check to see if the y projection of line a overlaps with the y projection of line b
822
+
823
+ # Check to see if the y projection of line a overlaps with the y
824
+ # projection of line b
1164
825
  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])
1165
- # If the y projections of the two lines overlap then the components of overlap_segs_overlap should not be
1166
- # nil.
826
+
827
+ # If the y projections of the two lines overlap then the
828
+ # components of overlap_segs_overlap should not be nil.
1167
829
  unless ((overlap_segs_overlap[:overlap_start].nil?) || (overlap_segs_overlap[:overlap_end].nil?))
1168
- # If the two overlaping segments start and end at the same point then do nothing and go to the next segment.
830
+
831
+ # If the two overlaping segments start and end at the same point
832
+ # then do nothing and go to the next segment.
1169
833
  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])
1170
834
  next
1171
- # If the start point of one overlapping segment shares the end point of the other overlapping segment then
1172
- # they are not really overlapping. Ignore and go to the next point.
835
+
836
+ # If the start point of one overlapping segment shares the
837
+ # end point of the other overlapping segment then they are not
838
+ # really overlapping. Ignore and go to the next point.
1173
839
  elsif overlap_segs_overlap[:overlap_start] == overlap_segs_overlap[:overlap_end]
1174
840
  next
1175
- # If the overlap_seg segment covers beyond the overlap_segs[j] segment then break overlap_seg into three smaller pieces:
1176
- # -One piece for where overlap_seg starts to where overlap_segs[j] starts;
1177
- # -One piece to cover overlap_segs[j] (the middle part); and
1178
- # -One piece for where overlap_segs[j] ends to where overlap_seg ends (the bottom part).
1179
- # The overlap_segs[j] remains as it is associated with another upward pointing line segment.
1180
- # If overlap_seg starts at the same point as overlap_segs[j] or ends at the same point as overlap_segs[j]
1181
- # then overlap_seg is broken into two pieces (no mid piece).
841
+
842
+ # If the overlap_seg segment covers beyond the overlap_segs[j]
843
+ # segment then break overlap_seg into three smaller pieces:
844
+ # - One piece for where overlap_seg starts to where overlap_segs[j] starts;
845
+ # - One piece to cover overlap_segs[j] (the middle part); and
846
+ # - One piece for where overlap_segs[j] ends to where overlap_seg ends (the bottom part).
847
+ # The overlap_segs[j] remains as it is associated with another
848
+ # upward pointing line segment. If overlap_seg starts at the
849
+ # same point as overlap_segs[j] or ends at the same point as
850
+ # overlap_segs[j] then overlap_seg is broken into two pieces
851
+ # (no mid piece).
1182
852
  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])
1183
- # If the overlap_seg and overlap_segs[j] start at the same point replace overlap_seg with two segments (
1184
- # one top and one bottom).
853
+
854
+ # If the overlap_seg and overlap_segs[j] start at the same
855
+ # point replace overlap_seg with two segments ( one top and
856
+ # one bottom).
1185
857
  if overlap_seg[:overlap_y][:overlap_start] == overlap_segs[j][:overlap_y][:overlap_start]
1186
858
  overlap_top = {
1187
859
  index_a1: overlap_seg[:index_a1],
@@ -1205,13 +877,16 @@ module BTAP
1205
877
  point_b2: overlap_seg[:point_b2],
1206
878
  overlap_y: overlap_bottom_over
1207
879
  }
1208
- # delete the existing y projection overlaps and replace it with the ones we just made.
880
+ # Delete the existing y projection overlaps and replace it
881
+ # with the ones we just made.
1209
882
  overlap_segs.delete_at(curr_seg_index)
1210
883
  overlap_segs << overlap_top
1211
884
  overlap_segs << overlap_bottom
1212
885
  elsif overlap_seg[:overlap_y][:overlap_end] == overlap_segs[j][:overlap_y][:overlap_end]
1213
- # If the overlap_seg and overlap_segs[j] end at the same point replace overlap_seg with two segments (
1214
- # one top and one bottom).
886
+
887
+ # If the overlap_seg and overlap_segs[j] end at the same
888
+ # point replace overlap_seg with two segments ( one top and
889
+ # one bottom).
1215
890
  overlap_top_over = {
1216
891
  overlap_start: overlap_seg[:overlap_y][:overlap_start],
1217
892
  overlap_end: overlap_segs_overlap[:overlap_start]
@@ -1234,12 +909,16 @@ module BTAP
1234
909
  point_b2: overlap_seg[:point_b2],
1235
910
  overlap_y: overlap_segs_overlap
1236
911
  }
1237
- # delete the existing y projection overlaps and replace it with the ones we just made.
912
+
913
+ # Delete the existing y projection overlaps and replace it
914
+ # with the ones we just made.
1238
915
  overlap_segs.delete_at(curr_seg_index)
1239
916
  overlap_segs << overlap_top
1240
917
  overlap_segs << overlap_bottom
1241
918
  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])
1242
- # If the overlap_seg stretches above and below overlap_segs[j] then break overlap_seg into three pieces
919
+
920
+ # If the overlap_seg stretches above and below
921
+ # overlap_segs[j] then break overlap_seg into three pieces
1243
922
  # (one top, one middle, one bottom).
1244
923
  overlap_top_over = {
1245
924
  overlap_start: overlap_seg[:overlap_y][:overlap_start],
@@ -1276,7 +955,9 @@ module BTAP
1276
955
  point_b2: overlap_seg[:point_b2],
1277
956
  overlap_y: overlap_bottom_over
1278
957
  }
1279
- # delete the existing y projection overlaps and replace it with the ones we just made.
958
+
959
+ # Delete the existing y projection overlaps and replace it
960
+ # with the ones we just made.
1280
961
  overlap_segs.delete_at(curr_seg_index)
1281
962
  overlap_segs << overlap_top
1282
963
  overlap_segs << overlap_mid
@@ -1284,16 +965,23 @@ module BTAP
1284
965
  end
1285
966
  restart = true
1286
967
  break
1287
- # If the overlap_segs[j] segment covers beyond the overlap_seg segment then break overlap_segs[j] into three smaller pieces:
1288
- # -One piece for where overlap_segs[j] starts to where overlap_seg starts;
1289
- # -One piece to cover overlap_seg (the middle part); and
1290
- # -One piece for where overlap_seg ends to where overlap_segs[j] ends (the bottom part).
1291
- # The overlap_seg remains as it is associated with another upward pointing line segment.
1292
- # If overlap_segs[j] starts at the same point as overlap_seg or ends at the same point as overlap_seg
1293
- # then overlap_segs[j] is broken into two pieces (no mid piece).
968
+
969
+ # If the overlap_segs[j] segment covers beyond the overlap_seg
970
+ # segment then break overlap_segs[j] into three smaller
971
+ # pieces:
972
+ # - One piece for where overlap_segs[j] starts to where overlap_seg starts;
973
+ # - One piece to cover overlap_seg (the middle part); and
974
+ # - One piece for where overlap_seg ends to where overlap_segs[j] ends (the bottom part).
975
+ # The overlap_seg remains as it is associated with another
976
+ # upward pointing line segment. If overlap_segs[j] starts at
977
+ # the same point as overlap_seg or ends at the same point as
978
+ # overlap_seg then overlap_segs[j] is broken into two pieces
979
+ # (no mid piece).
1294
980
  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]
1295
- # If the overlap_seg and overlap_segs[j] start at the same point replace overlap_segs[j] with two segments (
1296
- # one top and one bottom).
981
+
982
+ # If the overlap_seg and overlap_segs[j] start at the same
983
+ # point replace overlap_segs[j] with two segments (one top and
984
+ # one bottom).
1297
985
  if overlap_seg[:overlap_y][:overlap_start] == overlap_segs[j][:overlap_y][:overlap_start]
1298
986
  overlap_top = {
1299
987
  index_a1: overlap_segs[j][:index_a1],
@@ -1317,13 +1005,17 @@ module BTAP
1317
1005
  point_b2: overlap_segs[j][:point_b2],
1318
1006
  overlap_y: overlap_bottom_over
1319
1007
  }
1320
- # delete the existing y projection overlaps and replace it with the ones we just made.
1008
+
1009
+ # Delete the existing y projection overlaps and replace it
1010
+ # with the ones we just made.
1321
1011
  overlap_segs.delete_at(j)
1322
1012
  overlap_segs << overlap_top
1323
1013
  overlap_segs << overlap_bottom
1324
1014
  elsif overlap_seg[:overlap_y][:overlap_end] == overlap_segs[j][:overlap_y][:overlap_end]
1325
- # If the overlap_seg and overlap_segs[j] end at the same point replace overlap_segs[j] with two segments (
1326
- # one top and one bottom).
1015
+
1016
+ # If the overlap_seg and overlap_segs[j] end at the same
1017
+ # point replace overlap_segs[j] with two segments (one top
1018
+ # and one bottom).
1327
1019
  overlap_top_over = {
1328
1020
  overlap_start: overlap_segs[j][:overlap_y][:overlap_start],
1329
1021
  overlap_end: overlap_segs_overlap[:overlap_start]
@@ -1346,12 +1038,16 @@ module BTAP
1346
1038
  point_b2: overlap_segs[j][:point_b2],
1347
1039
  overlap_y: overlap_segs_overlap
1348
1040
  }
1349
- # delete the existing y projection overlaps and replace it with the ones we just made.
1041
+
1042
+ # Delete the existing y projection overlaps and replace it
1043
+ # with the ones we just made.
1350
1044
  overlap_segs.delete_at(j)
1351
1045
  overlap_segs << overlap_top
1352
1046
  overlap_segs << overlap_bottom
1353
1047
  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]
1354
- # If the overlap_segs[j] stretches above and below overlap_seg then break overlap_segs[j] into three pieces
1048
+
1049
+ # If the overlap_segs[j] stretches above and below
1050
+ # overlap_seg then break overlap_segs[j] into three pieces
1355
1051
  # (one top, one middle, one bottom).
1356
1052
  overlap_top_over = {
1357
1053
  overlap_start: overlap_segs[j][:overlap_y][:overlap_start],
@@ -1388,7 +1084,9 @@ module BTAP
1388
1084
  point_b2: overlap_segs[j][:point_b2],
1389
1085
  overlap_y: overlap_bottom_over
1390
1086
  }
1391
- # delete the existing y projection overlaps and replace it with the ones we just made.
1087
+
1088
+ # Delete the existing y projection overlaps and replace it
1089
+ # with the ones we just made.
1392
1090
  overlap_segs.delete_at(j)
1393
1091
  overlap_segs << overlap_top
1394
1092
  overlap_segs << overlap_mid
@@ -1396,8 +1094,10 @@ module BTAP
1396
1094
  end
1397
1095
  restart = true
1398
1096
  break
1399
- # if overlap_seg covers the top of overlap_segs[j] then break overlap_seg into a top and an overlap portion
1400
- # ond break overlap_segs[j] into an overlap portion and a bottom portion.
1097
+
1098
+ # If overlap_seg covers the top of overlap_segs[j] then break
1099
+ # overlap_seg into a top and an overlap portion ond break
1100
+ # overlap_segs[j] into an overlap portion and a bottom portion.
1401
1101
  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])
1402
1102
  overlap_top_over = {
1403
1103
  overlap_start: overlap_seg[:overlap_y][:overlap_start],
@@ -1443,7 +1143,9 @@ module BTAP
1443
1143
  point_b2: overlap_segs[j][:point_b2],
1444
1144
  overlap_y: overlap_bottom_over
1445
1145
  }
1446
- # delete the existing y projection overlaps and replace it with the ones we just made.
1146
+
1147
+ # Delete the existing y projection overlaps and replace it
1148
+ # with the ones we just made.
1447
1149
  if curr_seg_index > j
1448
1150
  overlap_segs.delete_at(curr_seg_index)
1449
1151
  overlap_segs.delete_at(j)
@@ -1458,8 +1160,11 @@ module BTAP
1458
1160
  restart = true
1459
1161
  break
1460
1162
  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])
1461
- # if overlap_seg covers the bottom of overlap_segs[j] then break overlap_segs[j] into a top and an overlap portion
1462
- # ond break overlap_seg into an overlap portion and a bottom portion.
1163
+
1164
+ # If overlap_seg covers the bottom of overlap_segs[j] then
1165
+ # break overlap_segs[j] into a top and an overlap portion
1166
+ # and break overlap_seg into an overlap portion and a bottom
1167
+ # portion.
1463
1168
  overlap_top_over = {
1464
1169
  overlap_start: overlap_segs[j][:overlap_y][:overlap_start],
1465
1170
  overlap_end: overlap_segs_overlap[:overlap_start]
@@ -1504,7 +1209,9 @@ module BTAP
1504
1209
  point_b2: overlap_seg[:point_b2],
1505
1210
  overlap_y: overlap_bottom_over
1506
1211
  }
1507
- # delete the existing y projection overlaps and replace it with the ones we just made.
1212
+
1213
+ # Delete the existing y projection overlaps and replace it
1214
+ # with the ones we just made.
1508
1215
  if curr_seg_index > j
1509
1216
  overlap_segs.delete_at(curr_seg_index)
1510
1217
  overlap_segs.delete_at(j)
@@ -1534,37 +1241,46 @@ module BTAP
1534
1241
  # point_a2: The bottom of the first line
1535
1242
  # point_b1: The bottom of the second line
1536
1243
  # point_b2: The top of the second line.
1537
- # This naming convention was chosen because this method was originally designed to work with the
1538
- # 'make_concave_surfaces' method (see above). That method choses lines that point up and then sees where they
1539
- # overlap with lines pointing down. The point_1 of each line is the end of the line. In this case a lines point
1540
- # up and b lines point down.
1244
+ # This naming convention was chosen because this method was originally
1245
+ # designed to work with the 'make_concave_surfaces' method (see above).
1246
+ # That method choses lines that point up and then sees where they overlap
1247
+ # with lines pointing down. The point_1 of each line is the end of the
1248
+ # line. In this case a lines point up and b lines point down.
1541
1249
  def self.line_segment_overlap_y?(point_a1:, point_a2:, point_b1:, point_b2:)
1542
1250
  overlap_start = nil
1543
1251
  overlap_end = nil
1252
+
1544
1253
  # If line a overlaps with the bottom of line b do this:
1545
1254
  if (point_a1 >= point_b1) && (point_a2 <= point_b1)
1546
1255
  overlap_start = point_a1
1547
1256
  overlap_end = point_b1
1257
+
1548
1258
  # This checks if all of line b is overlapped by line a
1549
1259
  if point_a1 >= point_b2
1550
1260
  overlap_start = point_b2
1551
1261
  end
1262
+
1552
1263
  # If line a overlaps with the top of line b do this:
1553
1264
  elsif (point_a1 >= point_b2) && (point_a2 <= point_b2)
1554
1265
  overlap_start = point_b2
1555
1266
  overlap_end = point_a2
1267
+
1556
1268
  # This checks if all of line b is overlapped by line a
1557
1269
  if point_a2 <= point_b1
1558
1270
  overlap_end = point_b1
1559
1271
  end
1272
+
1560
1273
  # This checks if all of line a fits in line b
1561
1274
  elsif (point_a1 <= point_b2) && (point_a2 >= point_b1)
1562
1275
  overlap_start = point_a1
1563
1276
  overlap_end = point_a2
1564
1277
  end
1565
- # Overlap vectors always point down. Thus overlap_start is the y location of the top of the overlap vector and
1566
- # overlap_end is the y location of the bottom of the overlap vector. The overlap vector will later be constructed
1567
- # using point_b1 and point_b2 and checking which overlaps are closest (and not obstructed) by other overlaps.
1278
+
1279
+ # Overlap vectors always point down. Thus overlap_start is the y
1280
+ # location of the top of the overlap vector and overlap_end is the y
1281
+ # location of the bottom of the overlap vector. The overlap vector will
1282
+ # later be constructed using point_b1 and point_b2 and checking which
1283
+ # overlaps are closest (and not obstructed) by other overlaps.
1568
1284
  overlap_y = {
1569
1285
  overlap_start: overlap_start,
1570
1286
  overlap_end: overlap_end
@@ -1572,7 +1288,8 @@ module BTAP
1572
1288
  return overlap_y
1573
1289
  end
1574
1290
 
1575
- # This method determines the x coordinate of where a given y coordinate crosses a given line.
1291
+ # This method determines the x coordinate of where a given y coordinate
1292
+ # crosses a given line.
1576
1293
  # y_check: The y coordinate that you want to determine the x coordinate for on a line
1577
1294
  # point_b1: The coordinates of the bottom of the line
1578
1295
  # point_b2: The coordinates of the top of the line
@@ -1592,9 +1309,10 @@ module BTAP
1592
1309
  return xcross
1593
1310
  end
1594
1311
 
1595
- # This method finds the centroid of a surface using the point averaging method. OpenStudio already has something
1596
- # which does this but you have to turn something into a special OpenStudio surface first which you may not want
1597
- # to do.
1312
+ # This method finds the centroid of a surface using the point averaging
1313
+ # method. OpenStudio already has something which does this but you have
1314
+ # to turn something into a special OpenStudio surface first which you may
1315
+ # not want to do.
1598
1316
  def self.surf_centroid(surf:)
1599
1317
  new_surf_cent = {
1600
1318
  x: 0,
@@ -1612,25 +1330,24 @@ module BTAP
1612
1330
  return new_surf_cent
1613
1331
  end
1614
1332
 
1615
- # This method calculates the surface area of a 2-D polygon from an array of OpenStudio vertices. It ignores any z
1616
- # vertices. This method assumes that the polygon is complete, has no holes, does not cross itself, and that the
1617
- # vertices are provided in counter-clockwise order. This method is used in cases when you want to find the area
1618
- # of something before creating an OpenStudio surface/subsurface.
1619
- #
1620
- # Input arguments:
1621
- # vetices: Array of openstudio vertices.
1333
+ # This method calculates the surface area of a 2-D polygon from an array
1334
+ # of OpenStudio vertices. It ignores any z vertices. This method assumes
1335
+ # that the polygon is complete, has no holes, does not cross itself, and
1336
+ # that the vertices are provided in counter-clockwise order. This method
1337
+ # is used in cases when you want to find the area of something before
1338
+ # creating an OpenStudio surface/subsurface.
1622
1339
  #
1623
- # Output:
1624
- # Area: Float, area of polygot represented by the vertices.
1340
+ # @param vertices [Array[OpenStudio::Point3d]]
1341
+ # @return [Float] Area of polygon represented by the vertices.
1625
1342
  def self.getSurfaceAreafromVertices(vertices:)
1626
1343
  area = 0.0
1627
1344
  numberVertices = vertices.size
1628
1345
 
1629
- # Check that a polygon is actually provided and not just a line or point. Return 0 if the vertices are a line
1630
- # or point.
1346
+ # Check that a polygon is actually provided and not just a line or
1347
+ # point. Return 0 if the vertices are a line or point.
1631
1348
  return 0.0 if numberVertices < 3
1632
1349
 
1633
- # Go through the vertices and get the cross product. This adopted from:
1350
+ # Go through the vertices and get the cross product. This adopted from:
1634
1351
  # https://web.archive.org/web/20100405070507/http://valis.cs.uiuc.edu/~sariel/research/CG/compgeom/msg00831.html
1635
1352
  vertices.each_with_index do |vertex, i|
1636
1353
  j = (i + 1) % numberVertices
@@ -1640,9 +1357,12 @@ module BTAP
1640
1357
  return area
1641
1358
  end
1642
1359
 
1643
- # Calculate the perimeter from a set of OpenStudio vertices.
1644
- # Calculated by iterating through vertex pairs, subtracting the difference, and getting the length of the returned
1645
- # Vector3d object.
1360
+ # Calculate the perimeter from a set of OpenStudio vertices. Calculated by
1361
+ # iterating through vertex pairs, subtracting the difference, and getting
1362
+ # the length of the returned Vector3d object.
1363
+ #
1364
+ # @param vertices [Array[OpenStudio::Point3d]]
1365
+ # @return [Float] Perimeter from the vertices.
1646
1366
  def self.getSurfacePerimeterFromVertices(vertices:)
1647
1367
  perimeter = 0.0
1648
1368
  return 0.0 if vertices.size < 2
@@ -1651,6 +1371,6 @@ module BTAP
1651
1371
  end
1652
1372
  return perimeter
1653
1373
  end
1654
- end #Module Surfaces
1655
- end #module Geometry
1374
+ end # module Surfaces
1375
+ end # module Geometry
1656
1376
  end