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.
- checksums.yaml +4 -4
- data/data/standards/test_performance_expected_dd_results.csv +90 -90
- data/lib/openstudio-standards/btap/activity.rb +858 -61
- data/lib/openstudio-standards/btap/attributes.rb +297 -103
- data/lib/openstudio-standards/btap/bridging.rb +1335 -593
- data/lib/openstudio-standards/btap/btap.rb +60 -181
- data/lib/openstudio-standards/btap/carbon/btap_carbon.rb +199 -142
- data/lib/openstudio-standards/btap/common/constructions.json +1275 -0
- data/lib/openstudio-standards/btap/common/costs.csv +1969 -0
- data/lib/openstudio-standards/btap/common/costs_local_factors.csv +2315 -0
- data/lib/openstudio-standards/btap/common/materials_glazing.csv +61 -0
- data/lib/openstudio-standards/btap/{common_resources → common}/materials_hvac.csv +1685 -1685
- data/lib/openstudio-standards/btap/{common_resources → common}/materials_lighting.csv +266 -266
- data/lib/openstudio-standards/btap/common/materials_opaque.csv +214 -0
- data/lib/openstudio-standards/btap/common/necb_building_types.csv +35 -0
- data/lib/openstudio-standards/btap/common/necb_space_types.csv +110 -0
- data/lib/openstudio-standards/btap/common/thermal_bridging.csv +73 -0
- data/lib/openstudio-standards/btap/costing/btap_costing.rb +346 -429
- data/lib/openstudio-standards/btap/costing/database.rb +104 -0
- data/lib/openstudio-standards/btap/costing/daylighting_sensor_control_costing.rb +356 -344
- data/lib/openstudio-standards/btap/costing/dcv_costing.rb +282 -296
- data/lib/openstudio-standards/btap/costing/envelope_costing.rb +179 -192
- data/lib/openstudio-standards/btap/costing/heating_cooling_costing.rb +2477 -2316
- data/lib/openstudio-standards/btap/costing/led_lighting_costing.rb +127 -142
- data/lib/openstudio-standards/btap/costing/lighting_costing.rb +216 -198
- data/lib/openstudio-standards/btap/costing/nv_costing.rb +522 -523
- data/lib/openstudio-standards/btap/costing/pv_ground_costing.rb +649 -649
- data/lib/openstudio-standards/btap/costing/shw_costing.rb +626 -625
- data/lib/openstudio-standards/btap/costing/thermal_bridging_costing.rb +72 -0
- data/lib/openstudio-standards/btap/costing/ventilation_costing.rb +2363 -2361
- data/lib/openstudio-standards/btap/deprecated.rb +1033 -0
- data/lib/openstudio-standards/btap/envelope.rb +313 -572
- data/lib/openstudio-standards/btap/fileio.rb +152 -1070
- data/lib/openstudio-standards/btap/geometry.rb +486 -766
- data/lib/openstudio-standards/btap/linear_regression.rb +130 -0
- data/lib/openstudio-standards/btap/paths.rb +46 -0
- data/lib/openstudio-standards/btap/qaqc/analysis.rb +163 -0
- data/lib/openstudio-standards/btap/qaqc/data.rb +2293 -0
- data/lib/openstudio-standards/btap/qaqc/datapoint.rb +641 -0
- data/lib/openstudio-standards/btap/schedules.rb +11 -611
- data/lib/openstudio-standards/btap/structure.rb +574 -189
- data/lib/openstudio-standards/constructions/materials/information.rb +1 -1
- data/lib/openstudio-standards/create_typical/create_typical.rb +1 -1
- data/lib/openstudio-standards/exterior_lighting/create.rb +135 -85
- data/lib/openstudio-standards/exterior_lighting/data/typical_exterior_lighting.json +20 -0
- data/lib/openstudio-standards/exterior_lighting/information.rb +23 -2
- data/lib/openstudio-standards/geometry/create.rb +7 -3
- data/lib/openstudio-standards/prototypes/ashrae_90_1/ashrae_90_1_2016/ashrae_90_1_2016.Model.rb +3 -2
- data/lib/openstudio-standards/prototypes/ashrae_90_1/ashrae_90_1_2019/ashrae_90_1_2019.Model.rb +3 -2
- data/lib/openstudio-standards/prototypes/common/data/thermostat_schedule_lookup.json +84 -84
- data/lib/openstudio-standards/prototypes/common/objects/Prototype.hvac_systems.rb +4 -4
- data/lib/openstudio-standards/refrigeration/create_walkin.rb +45 -5
- data/lib/openstudio-standards/refrigeration/data/refrigerated_walkins.csv +75 -75
- data/lib/openstudio-standards/schedules/parametric.rb +6 -0
- data/lib/openstudio-standards/service_water_heating/create_typical.rb +10 -3
- data/lib/openstudio-standards/service_water_heating/create_water_heating_loop.rb +30 -14
- data/lib/openstudio-standards/service_water_heating/information.rb +4 -1
- data/lib/openstudio-standards/sql_file/unmet_hours.rb +2 -1
- data/lib/openstudio-standards/standards/Standards.CoilCoolingDXSingleSpeed.rb +3 -1
- data/lib/openstudio-standards/standards/Standards.CoilHeatingDXSingleSpeed.rb +3 -1
- data/lib/openstudio-standards/standards/ashrae_90_1/ashrae_90_1_2004/data/ashrae_90_1_2004.boilers.json +12 -0
- data/lib/openstudio-standards/standards/ashrae_90_1/ashrae_90_1_2004/data/ashrae_90_1_2004.construction_properties.json +18432 -3072
- data/lib/openstudio-standards/standards/ashrae_90_1/ashrae_90_1_2007/data/ashrae_90_1_2007.boilers.json +26 -0
- data/lib/openstudio-standards/standards/ashrae_90_1/ashrae_90_1_2007/data/ashrae_90_1_2007.construction_properties.json +8532 -1422
- data/lib/openstudio-standards/standards/ashrae_90_1/ashrae_90_1_2010/data/ashrae_90_1_2010.boilers.json +26 -0
- data/lib/openstudio-standards/standards/ashrae_90_1/ashrae_90_1_2010/data/ashrae_90_1_2010.construction_properties.json +8532 -1422
- data/lib/openstudio-standards/standards/ashrae_90_1/ashrae_90_1_2013/data/ashrae_90_1_2013.boilers.json +16 -0
- data/lib/openstudio-standards/standards/ashrae_90_1/ashrae_90_1_2013/data/ashrae_90_1_2013.computer_room_acs.json +30 -30
- data/lib/openstudio-standards/standards/ashrae_90_1/ashrae_90_1_2013/data/ashrae_90_1_2013.construction_properties.json +7560 -1260
- data/lib/openstudio-standards/standards/ashrae_90_1/ashrae_90_1_2016/ashrae_90_1_2016.PumpVariableSpeed.rb +1 -2
- data/lib/openstudio-standards/standards/ashrae_90_1/ashrae_90_1_2016/data/ashrae_90_1_2016.boilers.json +16 -0
- data/lib/openstudio-standards/standards/ashrae_90_1/ashrae_90_1_2016/data/ashrae_90_1_2016.construction_properties.json +7560 -1260
- data/lib/openstudio-standards/standards/ashrae_90_1/ashrae_90_1_2019/ashrae_90_1_2019.AirLoopHVAC.rb +3 -3
- data/lib/openstudio-standards/standards/ashrae_90_1/ashrae_90_1_2019/ashrae_90_1_2019.PumpVariableSpeed.rb +1 -2
- data/lib/openstudio-standards/standards/ashrae_90_1/ashrae_90_1_2019/data/ashrae_90_1_2019.boilers.json +22 -0
- data/lib/openstudio-standards/standards/ashrae_90_1/ashrae_90_1_2019/data/ashrae_90_1_2019.construction_properties.json +7236 -1206
- data/lib/openstudio-standards/standards/ashrae_90_1/ashrae_90_1_2019/data/ashrae_90_1_2019.furnaces.json +223 -19
- data/lib/openstudio-standards/standards/ashrae_90_1/data/ashrae_90_1.schedules.json +37 -1
- data/lib/openstudio-standards/standards/ashrae_90_1_prm/ashrae_90_1_prm_2019/data/ashrae_90_1_prm_2019.boilers.json +3 -0
- 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
- data/lib/openstudio-standards/standards/cbes/data/cbes.schedules.json +1 -1
- data/lib/openstudio-standards/standards/deer/data/deer.schedules.json +1 -1
- data/lib/openstudio-standards/standards/necb/BTAP1980TO2010/data/surface_thermal_transmittance.json +76 -40
- data/lib/openstudio-standards/standards/necb/BTAPPRE1980/data/surface_thermal_transmittance.json +76 -40
- data/lib/openstudio-standards/standards/necb/ECMS/hvac_systems.rb +6 -4
- data/lib/openstudio-standards/standards/necb/NECB2011/NECB2011_QAQC.json +2988 -0
- data/lib/openstudio-standards/standards/necb/NECB2011/autozone.rb +25 -3
- data/lib/openstudio-standards/standards/necb/NECB2011/building_envelope.rb +672 -534
- data/lib/openstudio-standards/standards/necb/NECB2011/data/geometry/Hospital.osm +1791 -1792
- data/lib/openstudio-standards/standards/necb/NECB2011/data/geometry/LEEPMultiTower.osm +7 -1
- data/lib/openstudio-standards/standards/necb/NECB2011/data/geometry/LEEPTownHouse.osm +7 -1
- data/lib/openstudio-standards/standards/necb/NECB2011/data/geometry/LargeHotel.osm +5 -6
- data/lib/openstudio-standards/standards/necb/NECB2011/data/geometry/LargeOffice.osm +8 -9
- data/lib/openstudio-standards/standards/necb/NECB2011/data/geometry/NorthernEducation.osm +10 -4
- data/lib/openstudio-standards/standards/necb/NECB2011/data/geometry/NorthernHealthCare.osm +14 -15
- data/lib/openstudio-standards/standards/necb/NECB2011/data/geometry/Outpatient.osm +7 -1
- data/lib/openstudio-standards/standards/necb/NECB2011/data/surface_thermal_transmittance.json +76 -40
- data/lib/openstudio-standards/standards/necb/NECB2011/hvac_systems.rb +14 -6
- data/lib/openstudio-standards/standards/necb/NECB2011/necb_2011.rb +542 -130
- data/lib/openstudio-standards/standards/necb/NECB2011/qaqc/necb_qaqc.rb +30 -63
- data/lib/openstudio-standards/standards/necb/NECB2011/service_water_heating.rb +7 -2
- data/lib/openstudio-standards/standards/necb/NECB2015/data/surface_thermal_transmittance.json +90 -0
- data/lib/openstudio-standards/standards/necb/NECB2015/necb_2015.rb +8 -6
- data/lib/openstudio-standards/standards/necb/NECB2017/data/surface_thermal_transmittance.json +76 -40
- data/lib/openstudio-standards/standards/necb/NECB2017/necb_2017.rb +2 -1
- data/lib/openstudio-standards/standards/necb/NECB2020/building_envelope.rb +0 -117
- data/lib/openstudio-standards/standards/necb/NECB2020/data/surface_thermal_transmittance.json +76 -45
- data/lib/openstudio-standards/standards/necb/NECB2020/necb_2020.rb +218 -0
- data/lib/openstudio-standards/standards/necb/common/construction_defaults.osm +12 -7
- data/lib/openstudio-standards/standards/standard.rb +1 -1
- data/lib/openstudio-standards/version.rb +1 -1
- data/lib/openstudio-standards.rb +9 -4
- metadata +59 -66
- data/data/inventory/thermal_bridging.csv +0 -90
- data/data/standards/OpenStudio_Standards-ashrae_90_1-ALL-comstock(space_types).xlsx +0 -0
- data/data/standards/openstudio_standards_duplicates_log.csv +0 -7962
- data/lib/openstudio-standards/btap/NECB_building_types.csv +0 -35
- data/lib/openstudio-standards/btap/NECB_space_types.csv +0 -109
- data/lib/openstudio-standards/btap/WeatherData1.json +0 -2110
- data/lib/openstudio-standards/btap/WeatherData1.xlsx +0 -0
- data/lib/openstudio-standards/btap/btap_result.rb +0 -1144
- data/lib/openstudio-standards/btap/btap_test_helper.rb +0 -86
- data/lib/openstudio-standards/btap/common_paths.rb +0 -81
- data/lib/openstudio-standards/btap/common_resources/ConstructionProperties.csv +0 -52
- data/lib/openstudio-standards/btap/common_resources/Constructions.csv +0 -37
- data/lib/openstudio-standards/btap/common_resources/construction_sets.csv +0 -1270
- data/lib/openstudio-standards/btap/common_resources/constructions_glazing.csv +0 -121
- data/lib/openstudio-standards/btap/common_resources/constructions_opaque.csv +0 -2256
- data/lib/openstudio-standards/btap/common_resources/costs.csv +0 -1897
- data/lib/openstudio-standards/btap/common_resources/costs_local_factors.csv +0 -2315
- data/lib/openstudio-standards/btap/common_resources/materials_glazing.csv +0 -35
- data/lib/openstudio-standards/btap/common_resources/materials_opaque.csv +0 -164
- data/lib/openstudio-standards/btap/costing/btap_measure_helper.rb +0 -359
- data/lib/openstudio-standards/btap/costing/btap_workflow.rb +0 -117
- data/lib/openstudio-standards/btap/costing/copy_test_results_files_to_expected_results.rb +0 -11
- data/lib/openstudio-standards/btap/costing/cost_building_from_file.rb +0 -136
- data/lib/openstudio-standards/btap/costing/costing_database_wrapper.rb +0 -173
- data/lib/openstudio-standards/btap/costing/dummy.epw +0 -8768
- data/lib/openstudio-standards/btap/costing/dummy.osm +0 -5320
- data/lib/openstudio-standards/btap/costing/neb_end_use_prices.csv +0 -42
- data/lib/openstudio-standards/btap/costing/necb_2011_spacetype_info.csv +0 -225
- data/lib/openstudio-standards/btap/costing/necb_reference_runs.csv +0 -28705
- data/lib/openstudio-standards/btap/costing/parallel_tests.rb +0 -92
- data/lib/openstudio-standards/btap/costing/test_list.txt +0 -15
- data/lib/openstudio-standards/btap/costing/test_run_all_test_locally.rb +0 -27
- data/lib/openstudio-standards/btap/costing/test_run_costing_tests.rb +0 -80
- data/lib/openstudio-standards/btap/necb_2011_spacetype_info.csv +0 -225
- data/lib/openstudio-standards/btap/utilities/Add_template_field_to_json.rb +0 -22
- data/lib/openstudio-standards/btap/utilities/convert_surfaces_to_adiabatic_necb_8426.rb +0 -204
- data/lib/openstudio-standards/btap/utilities/generate_space_types.rb +0 -8
- data/lib/openstudio-standards/btap/utilities/necb_to_epw_map.rb +0 -50
- data/lib/openstudio-standards/btap/utilities/os_sim_extract.rb +0 -48
- data/lib/openstudio-standards/btap/utilities/rename_surfaces.rb +0 -24
- data/lib/openstudio-standards/btap/utilities/round_surf_coords.rb +0 -32
- data/lib/openstudio-standards/btap/utilities/sched_create.rb +0 -145
- data/lib/openstudio-standards/btap/utilities/set_mult_to_adiabatic.rb +0 -53
- data/lib/openstudio-standards/btap/utilities/space_type.rb +0 -78
- data/lib/openstudio-standards/btap/utilities/weatherData1_xlsx_to_json.rb +0 -56
- data/lib/openstudio-standards/btap/vintagizer.rb +0 -161
- data/lib/openstudio-standards/btap/visualization.rb +0 -72
- data/lib/openstudio-standards/standards/necb/NECB2011/data/necb_2011_spacetype_info.csv +0 -225
- data/lib/openstudio-standards/standards/necb/common/btap_analysis.rb +0 -79
- data/lib/openstudio-standards/standards/necb/common/btap_data.rb +0 -2656
- data/lib/openstudio-standards/standards/necb/common/btap_datapoint.rb +0 -653
- data/lib/openstudio-standards/standards/necb/common/necb_reference_runs.csv +0 -28705
- data/lib/openstudio-standards/standards/necb/common/utility_pricing_2025-02-20.csv +0 -92
- /data/lib/openstudio-standards/btap/{common_resources → common}/carbon_frame.csv +0 -0
- /data/lib/openstudio-standards/btap/{common_resources → common}/carbon_glazing.csv +0 -0
- /data/lib/openstudio-standards/btap/{common_resources → common}/carbon_opaque.csv +0 -0
- /data/lib/openstudio-standards/btap/{common_resources → common}/hvac_vent_ahu.csv +0 -0
- /data/lib/openstudio-standards/btap/{common_resources → common}/lighting.csv +0 -0
- /data/lib/openstudio-standards/btap/{common_resources → common}/lighting_sets.csv +0 -0
- /data/lib/openstudio-standards/btap/{common_resources → common}/locations.csv +0 -0
- /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
|
|
226
|
-
|
|
227
|
-
#
|
|
228
|
-
|
|
229
|
-
|
|
230
|
-
|
|
231
|
-
|
|
232
|
-
|
|
233
|
-
|
|
234
|
-
|
|
235
|
-
|
|
236
|
-
|
|
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
|
-
|
|
249
|
-
|
|
173
|
+
hgt = TBD.spaceHeight(space)
|
|
174
|
+
space.additionalProperties.setFeature(tag, hgt)
|
|
250
175
|
|
|
251
|
-
|
|
176
|
+
hgt
|
|
177
|
+
end
|
|
252
178
|
|
|
253
|
-
|
|
254
|
-
|
|
255
|
-
|
|
256
|
-
|
|
257
|
-
|
|
258
|
-
|
|
259
|
-
|
|
260
|
-
|
|
261
|
-
|
|
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
|
-
|
|
268
|
-
|
|
269
|
-
|
|
202
|
+
wdt = TBD.spaceWidth(space)
|
|
203
|
+
space.additionalProperties.setFeature(tag, wdt)
|
|
270
204
|
|
|
271
|
-
|
|
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
|
-
|
|
338
|
-
|
|
339
|
-
|
|
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
|
-
|
|
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
|
-
|
|
381
|
-
|
|
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
|
-
#
|
|
417
|
-
|
|
418
|
-
|
|
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
|
-
|
|
423
|
-
|
|
424
|
-
|
|
425
|
-
|
|
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
|
-
|
|
247
|
+
wlls = []
|
|
248
|
+
wlls = TBD.facets(space, "all", "wall")
|
|
249
|
+
wlls = wlls.select { |wll| wll.isGroundSurface }
|
|
250
|
+
next unless wlls.empty?
|
|
441
251
|
|
|
442
|
-
|
|
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
|
-
|
|
451
|
-
|
|
452
|
-
|
|
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
|
-
|
|
459
|
-
|
|
460
|
-
|
|
461
|
-
|
|
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
|
-
|
|
476
|
-
|
|
477
|
-
|
|
478
|
-
|
|
479
|
-
|
|
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
|
-
|
|
493
|
-
|
|
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
|
-
|
|
505
|
-
|
|
506
|
-
|
|
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
|
-
|
|
513
|
-
|
|
279
|
+
offset = OpenStudio.buffer(polyg, -w, 0.1)
|
|
280
|
+
next if offset.empty?
|
|
514
281
|
|
|
515
|
-
|
|
516
|
-
|
|
517
|
-
|
|
518
|
-
|
|
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
|
-
|
|
568
|
-
|
|
569
|
-
|
|
570
|
-
|
|
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
|
-
|
|
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
|
|
693
|
-
#
|
|
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
|
-
|
|
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
|
-
#
|
|
707
|
-
#
|
|
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
|
-
|
|
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
|
|
843
|
-
#
|
|
844
|
-
#
|
|
845
|
-
#
|
|
846
|
-
#
|
|
847
|
-
#
|
|
848
|
-
#
|
|
849
|
-
#
|
|
850
|
-
#
|
|
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.
|
|
853
|
-
# lines.
|
|
854
|
-
#
|
|
855
|
-
# lines
|
|
856
|
-
#
|
|
857
|
-
#
|
|
858
|
-
#
|
|
859
|
-
#
|
|
860
|
-
#
|
|
861
|
-
#
|
|
862
|
-
#
|
|
863
|
-
#
|
|
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
|
-
|
|
866
|
-
#
|
|
867
|
-
#
|
|
868
|
-
#
|
|
869
|
-
# point_b1
|
|
870
|
-
#
|
|
871
|
-
#
|
|
872
|
-
#
|
|
873
|
-
#
|
|
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
|
-
|
|
883
|
-
#
|
|
884
|
-
#
|
|
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
|
-
|
|
898
|
-
#
|
|
899
|
-
#
|
|
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
|
-
|
|
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
|
-
|
|
908
|
-
#
|
|
909
|
-
#
|
|
910
|
-
#if
|
|
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
|
-
|
|
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
|
-
|
|
915
|
-
#
|
|
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
|
|
938
|
-
#
|
|
939
|
-
#
|
|
940
|
-
#
|
|
941
|
-
#
|
|
942
|
-
#
|
|
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
|
-
|
|
945
|
-
#
|
|
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
|
-
|
|
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
|
-
|
|
966
|
-
#
|
|
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
|
-
|
|
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
|
-
|
|
1008
|
-
#
|
|
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
|
|
1016
|
-
#
|
|
1017
|
-
#
|
|
1018
|
-
#
|
|
1019
|
-
# the
|
|
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
|
-
#
|
|
1023
|
-
#
|
|
1024
|
-
#
|
|
1025
|
-
#
|
|
1026
|
-
#
|
|
1027
|
-
#
|
|
1028
|
-
#
|
|
1029
|
-
#
|
|
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
|
-
#
|
|
1039
|
-
#
|
|
1040
|
-
#
|
|
1041
|
-
#
|
|
1042
|
-
#
|
|
1043
|
-
#
|
|
1044
|
-
#
|
|
1045
|
-
#
|
|
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
|
-
#
|
|
1052
|
-
#
|
|
1053
|
-
#
|
|
1054
|
-
#
|
|
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
|
-
|
|
1059
|
-
#
|
|
1060
|
-
#
|
|
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
|
|
1078
|
-
#
|
|
1079
|
-
#
|
|
1080
|
-
#
|
|
1081
|
-
#
|
|
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.
|
|
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
|
|
1124
|
-
# y-components of a
|
|
1125
|
-
#
|
|
1126
|
-
#
|
|
1127
|
-
#
|
|
1128
|
-
#
|
|
1129
|
-
#
|
|
1130
|
-
#
|
|
1131
|
-
#
|
|
1132
|
-
#
|
|
1133
|
-
#
|
|
1134
|
-
#
|
|
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
|
|
1144
|
-
# lines (line a and line b)
|
|
1145
|
-
#
|
|
1146
|
-
#
|
|
1147
|
-
#
|
|
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
|
-
|
|
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
|
-
|
|
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
|
-
|
|
1166
|
-
#
|
|
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
|
-
|
|
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
|
-
|
|
1172
|
-
#
|
|
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
|
-
|
|
1176
|
-
#
|
|
1177
|
-
#
|
|
1178
|
-
# -One piece for where
|
|
1179
|
-
#
|
|
1180
|
-
#
|
|
1181
|
-
#
|
|
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
|
-
|
|
1184
|
-
#
|
|
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
|
-
#
|
|
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
|
-
|
|
1214
|
-
#
|
|
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
|
-
|
|
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
|
-
|
|
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
|
-
|
|
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
|
-
|
|
1288
|
-
#
|
|
1289
|
-
#
|
|
1290
|
-
#
|
|
1291
|
-
#
|
|
1292
|
-
#
|
|
1293
|
-
#
|
|
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
|
-
|
|
1296
|
-
#
|
|
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
|
-
|
|
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
|
-
|
|
1326
|
-
#
|
|
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
|
-
|
|
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
|
-
|
|
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
|
-
|
|
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
|
-
|
|
1400
|
-
|
|
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
|
-
|
|
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
|
-
|
|
1462
|
-
#
|
|
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
|
-
|
|
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
|
|
1538
|
-
# 'make_concave_surfaces' method (see above).
|
|
1539
|
-
#
|
|
1540
|
-
#
|
|
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
|
-
|
|
1566
|
-
#
|
|
1567
|
-
#
|
|
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
|
|
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
|
|
1596
|
-
#
|
|
1597
|
-
# to
|
|
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
|
|
1616
|
-
# vertices.
|
|
1617
|
-
#
|
|
1618
|
-
#
|
|
1619
|
-
#
|
|
1620
|
-
#
|
|
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
|
-
#
|
|
1624
|
-
#
|
|
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
|
|
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.
|
|
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
|
-
#
|
|
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 #
|
|
1655
|
-
end #module Geometry
|
|
1374
|
+
end # module Surfaces
|
|
1375
|
+
end # module Geometry
|
|
1656
1376
|
end
|