openstudio-common-measures 0.2.0 → 0.2.1

Sign up to get free protection for your applications and to get access to all the features.
Files changed (165) hide show
  1. checksums.yaml +4 -4
  2. data/CHANGELOG.md +44 -0
  3. data/Rakefile +3 -0
  4. data/lib/measures/ChangeBuildingLocation/measure.xml +11 -11
  5. data/lib/measures/ExportScheduleCSV/measure.rb +1 -1
  6. data/lib/measures/ExportScheduleCSV/measure.xml +16 -15
  7. data/lib/measures/ImportEnvelopeAndInternalLoadsFromIdf/measure.xml +7 -6
  8. data/lib/measures/MeterFlodPlot/measure.rb +1 -1
  9. data/lib/measures/MeterFlodPlot/measure.xml +16 -15
  10. data/lib/measures/ReportModelChanges/measure.xml +2 -2
  11. data/lib/measures/RunPeriodMultiple/measure.xml +12 -11
  12. data/lib/measures/ServerDirectoryCleanup/README.md +97 -3
  13. data/lib/measures/ServerDirectoryCleanup/measure.rb +1 -1
  14. data/lib/measures/ServerDirectoryCleanup/measure.xml +10 -59
  15. data/lib/measures/UnmetLoadHoursTroubleshooting/README.md +9 -3
  16. data/lib/measures/UnmetLoadHoursTroubleshooting/measure.rb +1 -1
  17. data/lib/measures/UnmetLoadHoursTroubleshooting/measure.xml +66 -17
  18. data/lib/measures/VentilationQAQC/README.md +9 -3
  19. data/lib/measures/VentilationQAQC/measure.rb +52 -45
  20. data/lib/measures/VentilationQAQC/measure.xml +25 -30
  21. data/lib/measures/ZoneReport/README.md +2 -2
  22. data/lib/measures/ZoneReport/measure.rb +1 -1
  23. data/lib/measures/ZoneReport/measure.xml +10 -11
  24. data/lib/measures/add_ems_to_control_ev_charging/LICENSE.MD.txt +15 -0
  25. data/lib/measures/{AddDaylightSensors → add_ems_to_control_ev_charging}/LICENSE.md +0 -0
  26. data/lib/measures/add_ems_to_control_ev_charging/ReadMe.MD +29 -0
  27. data/lib/measures/add_ems_to_control_ev_charging/measure.rb +288 -0
  28. data/lib/measures/add_ems_to_control_ev_charging/measure.xml +87 -0
  29. data/lib/measures/add_ev_load/LICENSE.MD.txt +15 -0
  30. data/lib/measures/{EnableDemandControlledVentilation → add_ev_load}/LICENSE.md +0 -0
  31. data/lib/measures/add_ev_load/ReadMe.MD +34 -0
  32. data/lib/measures/add_ev_load/measure.rb +328 -0
  33. data/lib/measures/add_ev_load/measure.xml +153 -0
  34. data/lib/measures/add_ev_load/resources/EV_Load_Profiles/PSN_BuildingKey.csv +104 -0
  35. data/lib/measures/add_ev_load/resources/EV_Load_Profiles/PSN_BuildingKey_v2.csv +104 -0
  36. data/lib/measures/add_ev_load/resources/EV_Load_Profiles/chg1_dow1_flex1.csv +96 -0
  37. data/lib/measures/add_ev_load/resources/EV_Load_Profiles/chg1_dow1_flex2.csv +96 -0
  38. data/lib/measures/add_ev_load/resources/EV_Load_Profiles/chg1_dow1_flex3.csv +96 -0
  39. data/lib/measures/add_ev_load/resources/EV_Load_Profiles/chg1_dow2_flex1.csv +96 -0
  40. data/lib/measures/add_ev_load/resources/EV_Load_Profiles/chg1_dow2_flex2.csv +96 -0
  41. data/lib/measures/add_ev_load/resources/EV_Load_Profiles/chg1_dow2_flex3.csv +96 -0
  42. data/lib/measures/add_ev_load/resources/EV_Load_Profiles/chg1_dow3_flex1.csv +96 -0
  43. data/lib/measures/add_ev_load/resources/EV_Load_Profiles/chg1_dow3_flex2.csv +96 -0
  44. data/lib/measures/add_ev_load/resources/EV_Load_Profiles/chg1_dow3_flex3.csv +96 -0
  45. data/lib/measures/add_ev_load/resources/EV_Load_Profiles/chg2_dow1_flex1.csv +96 -0
  46. data/lib/measures/add_ev_load/resources/EV_Load_Profiles/chg2_dow1_flex2.csv +96 -0
  47. data/lib/measures/add_ev_load/resources/EV_Load_Profiles/chg2_dow1_flex3.csv +96 -0
  48. data/lib/measures/add_ev_load/resources/EV_Load_Profiles/chg2_dow2_flex1.csv +96 -0
  49. data/lib/measures/add_ev_load/resources/EV_Load_Profiles/chg2_dow2_flex2.csv +96 -0
  50. data/lib/measures/add_ev_load/resources/EV_Load_Profiles/chg2_dow2_flex3.csv +96 -0
  51. data/lib/measures/add_ev_load/resources/EV_Load_Profiles/chg2_dow3_flex1.csv +96 -0
  52. data/lib/measures/add_ev_load/resources/EV_Load_Profiles/chg2_dow3_flex2.csv +96 -0
  53. data/lib/measures/add_ev_load/resources/EV_Load_Profiles/chg2_dow3_flex3.csv +96 -0
  54. data/lib/measures/add_ev_load/resources/EV_Load_Profiles/chg3_dow1_flex1.csv +96 -0
  55. data/lib/measures/add_ev_load/resources/EV_Load_Profiles/chg3_dow1_flex2.csv +96 -0
  56. data/lib/measures/add_ev_load/resources/EV_Load_Profiles/chg3_dow1_flex3.csv +96 -0
  57. data/lib/measures/add_ev_load/resources/EV_Load_Profiles/chg3_dow2_flex1.csv +96 -0
  58. data/lib/measures/add_ev_load/resources/EV_Load_Profiles/chg3_dow2_flex2.csv +96 -0
  59. data/lib/measures/add_ev_load/resources/EV_Load_Profiles/chg3_dow2_flex3.csv +96 -0
  60. data/lib/measures/add_ev_load/resources/EV_Load_Profiles/chg3_dow3_flex1.csv +96 -0
  61. data/lib/measures/add_ev_load/resources/EV_Load_Profiles/chg3_dow3_flex2.csv +96 -0
  62. data/lib/measures/add_ev_load/resources/EV_Load_Profiles/chg3_dow3_flex3.csv +96 -0
  63. data/lib/measures/add_ev_load/resources/EV_Load_Profiles/fast_charge_15min_Saturday.csv +96 -0
  64. data/lib/measures/add_ev_load/resources/EV_Load_Profiles/fast_charge_15min_Sunday.csv +96 -0
  65. data/lib/measures/add_ev_load/resources/EV_Load_Profiles/fast_charge_15min_Weekday.csv +96 -0
  66. data/lib/measures/envelope_and_internal_load_breakdown/README.md +4 -3
  67. data/lib/measures/envelope_and_internal_load_breakdown/measure.rb +7 -4
  68. data/lib/measures/envelope_and_internal_load_breakdown/measure.xml +16 -17
  69. data/lib/measures/envelope_and_internal_load_breakdown/resources/report.html.erb +1 -0
  70. data/lib/measures/example_report/README.md +27 -5
  71. data/lib/measures/example_report/measure.rb +7 -4
  72. data/lib/measures/example_report/measure.xml +16 -29
  73. data/lib/measures/example_report/resources/report.html.erb +1 -0
  74. data/lib/measures/gem_env_report/measure.xml +1 -1
  75. data/lib/measures/generic_qaqc/README.md +187 -5
  76. data/lib/measures/generic_qaqc/measure.rb +1 -1
  77. data/lib/measures/generic_qaqc/measure.xml +15 -85
  78. data/lib/measures/generic_qaqc/resources/report.html.erb +1 -0
  79. data/lib/measures/hvac_psychrometric_chart/README.md +11 -5
  80. data/lib/measures/hvac_psychrometric_chart/measure.rb +1 -1
  81. data/lib/measures/hvac_psychrometric_chart/measure.xml +13 -18
  82. data/lib/measures/hvac_psychrometric_chart/resources/report.html.erb +1 -0
  83. data/lib/measures/inject_idf_objects/measure.xml +25 -24
  84. data/lib/measures/openstudio_results/README.md +195 -5
  85. data/lib/measures/openstudio_results/measure.rb +2 -2
  86. data/lib/measures/openstudio_results/measure.xml +30 -154
  87. data/lib/measures/openstudio_results/resources/report.html.erb +1 -0
  88. data/lib/measures/set_run_period/measure.xml +2 -2
  89. data/lib/measures/{EnableEconomizerControl → view_data}/LICENSE.md +0 -0
  90. data/lib/measures/view_data/README.md +64 -0
  91. data/lib/measures/{AddDaylightSensors → view_data}/README.md.erb +0 -0
  92. data/lib/measures/view_data/measure.rb +454 -0
  93. data/lib/measures/view_data/measure.xml +159 -0
  94. data/lib/measures/view_data/resources/report.html.in +2881 -0
  95. data/lib/measures/view_data/resources/va3c.rb +1021 -0
  96. data/lib/measures/{ImproveFanBeltEfficiency → view_model}/LICENSE.md +0 -0
  97. data/lib/measures/view_model/README.md +26 -0
  98. data/lib/measures/{EnableDemandControlledVentilation → view_model}/README.md.erb +0 -0
  99. data/lib/measures/view_model/measure.rb +146 -0
  100. data/lib/measures/view_model/measure.xml +98 -0
  101. data/lib/measures/view_model/resources/report.html.in +2881 -0
  102. data/lib/measures/view_model/resources/va3c.rb +1021 -0
  103. data/lib/openstudio/common_measures/version.rb +1 -1
  104. data/openstudio-common-measures.gemspec +1 -1
  105. metadata +61 -71
  106. data/lib/measures/AddDaylightSensors/README.md +0 -136
  107. data/lib/measures/AddDaylightSensors/measure.rb +0 -521
  108. data/lib/measures/AddDaylightSensors/measure.xml +0 -233
  109. data/lib/measures/EnableDemandControlledVentilation/README.md +0 -32
  110. data/lib/measures/EnableDemandControlledVentilation/measure.rb +0 -154
  111. data/lib/measures/EnableDemandControlledVentilation/measure.xml +0 -99
  112. data/lib/measures/EnableEconomizerControl/README.md +0 -48
  113. data/lib/measures/EnableEconomizerControl/README.md.erb +0 -42
  114. data/lib/measures/EnableEconomizerControl/measure.rb +0 -172
  115. data/lib/measures/EnableEconomizerControl/measure.xml +0 -124
  116. data/lib/measures/ImproveFanBeltEfficiency/README.md +0 -104
  117. data/lib/measures/ImproveFanBeltEfficiency/README.md.erb +0 -42
  118. data/lib/measures/ImproveFanBeltEfficiency/measure.rb +0 -366
  119. data/lib/measures/ImproveFanBeltEfficiency/measure.xml +0 -185
  120. data/lib/measures/ImproveMotorEfficiency/LICENSE.md +0 -27
  121. data/lib/measures/ImproveMotorEfficiency/README.md +0 -104
  122. data/lib/measures/ImproveMotorEfficiency/README.md.erb +0 -42
  123. data/lib/measures/ImproveMotorEfficiency/measure.rb +0 -365
  124. data/lib/measures/ImproveMotorEfficiency/measure.xml +0 -182
  125. data/lib/measures/IncreaseInsulationRValueForExteriorWalls/LICENSE.md +0 -27
  126. data/lib/measures/IncreaseInsulationRValueForExteriorWalls/README.md +0 -64
  127. data/lib/measures/IncreaseInsulationRValueForExteriorWalls/README.md.erb +0 -42
  128. data/lib/measures/IncreaseInsulationRValueForExteriorWalls/measure.rb +0 -422
  129. data/lib/measures/IncreaseInsulationRValueForExteriorWalls/measure.xml +0 -150
  130. data/lib/measures/IncreaseInsulationRValueForRoofs/LICENSE.md +0 -27
  131. data/lib/measures/IncreaseInsulationRValueForRoofs/README.md +0 -64
  132. data/lib/measures/IncreaseInsulationRValueForRoofs/README.md.erb +0 -42
  133. data/lib/measures/IncreaseInsulationRValueForRoofs/measure.rb +0 -422
  134. data/lib/measures/IncreaseInsulationRValueForRoofs/measure.xml +0 -143
  135. data/lib/measures/ReduceElectricEquipmentLoadsByPercentage/LICENSE.md +0 -27
  136. data/lib/measures/ReduceElectricEquipmentLoadsByPercentage/README.md +0 -97
  137. data/lib/measures/ReduceElectricEquipmentLoadsByPercentage/README.md.erb +0 -42
  138. data/lib/measures/ReduceElectricEquipmentLoadsByPercentage/measure.rb +0 -450
  139. data/lib/measures/ReduceElectricEquipmentLoadsByPercentage/measure.xml +0 -186
  140. data/lib/measures/ReduceLightingLoadsByPercentage/LICENSE.md +0 -27
  141. data/lib/measures/ReduceLightingLoadsByPercentage/README.md +0 -96
  142. data/lib/measures/ReduceLightingLoadsByPercentage/README.md.erb +0 -42
  143. data/lib/measures/ReduceLightingLoadsByPercentage/measure.rb +0 -513
  144. data/lib/measures/ReduceLightingLoadsByPercentage/measure.xml +0 -191
  145. data/lib/measures/ReduceSpaceInfiltrationByPercentage/LICENSE.md +0 -27
  146. data/lib/measures/ReduceSpaceInfiltrationByPercentage/README.md +0 -104
  147. data/lib/measures/ReduceSpaceInfiltrationByPercentage/README.md.erb +0 -42
  148. data/lib/measures/ReduceSpaceInfiltrationByPercentage/measure.rb +0 -349
  149. data/lib/measures/ReduceSpaceInfiltrationByPercentage/measure.xml +0 -181
  150. data/lib/measures/ReduceVentilationByPercentage/LICENSE.md +0 -27
  151. data/lib/measures/ReduceVentilationByPercentage/README.md +0 -40
  152. data/lib/measures/ReduceVentilationByPercentage/README.md.erb +0 -42
  153. data/lib/measures/ReduceVentilationByPercentage/measure.rb +0 -291
  154. data/lib/measures/ReduceVentilationByPercentage/measure.xml +0 -96
  155. data/lib/measures/VentilationQAQC/ExampleModel.osm +0 -28468
  156. data/lib/measures/create_variable_speed_rtu/LICENSE.md +0 -27
  157. data/lib/measures/create_variable_speed_rtu/README.md +0 -120
  158. data/lib/measures/create_variable_speed_rtu/README.md.erb +0 -42
  159. data/lib/measures/create_variable_speed_rtu/measure.rb +0 -539
  160. data/lib/measures/create_variable_speed_rtu/measure.xml +0 -207
  161. data/lib/measures/radiant_slab_with_doas/LICENSE.md +0 -27
  162. data/lib/measures/radiant_slab_with_doas/README.md +0 -156
  163. data/lib/measures/radiant_slab_with_doas/README.md.erb +0 -42
  164. data/lib/measures/radiant_slab_with_doas/measure.rb +0 -403
  165. data/lib/measures/radiant_slab_with_doas/measure.xml +0 -345
@@ -0,0 +1,1021 @@
1
+ # *******************************************************************************
2
+ # OpenStudio(R), Copyright (c) 2008-2020, Alliance for Sustainable Energy, LLC.
3
+ # All rights reserved.
4
+ # Redistribution and use in source and binary forms, with or without
5
+ # modification, are permitted provided that the following conditions are met:
6
+ #
7
+ # (1) Redistributions of source code must retain the above copyright notice,
8
+ # this list of conditions and the following disclaimer.
9
+ #
10
+ # (2) Redistributions in binary form must reproduce the above copyright notice,
11
+ # this list of conditions and the following disclaimer in the documentation
12
+ # and/or other materials provided with the distribution.
13
+ #
14
+ # (3) Neither the name of the copyright holder nor the names of any contributors
15
+ # may be used to endorse or promote products derived from this software without
16
+ # specific prior written permission from the respective party.
17
+ #
18
+ # (4) Other than as required in clauses (1) and (2), distributions in any form
19
+ # of modifications or other derivative works may not use the "OpenStudio"
20
+ # trademark, "OS", "os", or any other confusingly similar designation without
21
+ # specific prior written permission from Alliance for Sustainable Energy, LLC.
22
+ #
23
+ # THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDER(S) AND ANY CONTRIBUTORS
24
+ # "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO,
25
+ # THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
26
+ # ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER(S), ANY CONTRIBUTORS, THE
27
+ # UNITED STATES GOVERNMENT, OR THE UNITED STATES DEPARTMENT OF ENERGY, NOR ANY OF
28
+ # THEIR EMPLOYEES, BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
29
+ # EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT
30
+ # OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
31
+ # INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT,
32
+ # STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
33
+ # OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
34
+ # *******************************************************************************
35
+
36
+ require 'openstudio'
37
+
38
+ if /^1\.8/.match(RUBY_VERSION)
39
+ class Struct
40
+ def to_h
41
+ h = {}
42
+ self.class.members.each { |m| h[m.to_sym] = self[m] }
43
+ return h
44
+ end
45
+ end
46
+ end
47
+
48
+ module THREE
49
+ FrontSide = 0
50
+ BackSide = 1
51
+ DoubleSide = 2
52
+ end
53
+
54
+ # Va3c class converts an OpenStudio model to vA3C JSON format for rendering in Three.js
55
+ # using export at http://va3c.github.io/projects/#./osm-data-viewer/latest/index.html# as a guide
56
+ # many thanks to Theo Armour and the vA3C team for figuring out many of the details here
57
+ class VA3C
58
+ Scene = Struct.new(:geometries, :materials, :object)
59
+
60
+ Geometry = Struct.new(:uuid, :type, :data)
61
+ GeometryData = Struct.new(:vertices, :normals, :uvs, :faces, :scale, :visible, :castShadow, :receiveShadow, :doubleSided)
62
+
63
+ Material = Struct.new(:uuid, :type, :color, :ambient, :emissive, :specular, :shininess, :side, :opacity, :transparent, :wireframe)
64
+
65
+ SceneObject = Struct.new(:uuid, :type, :matrix, :children)
66
+ SceneChild = Struct.new(:uuid, :name, :type, :geometry, :material, :matrix, :userData)
67
+ UserData = Struct.new(:handle, :name, :surfaceType, :constructionName, :spaceName, :thermalZoneName, :spaceTypeName, :buildingStoryName,
68
+ :outsideBoundaryCondition, :outsideBoundaryConditionObjectName,
69
+ :outsideBoundaryConditionObjectHandle, :coincidentWithOutsideObject,
70
+ :sunExposure, :windExposure, #:vertices,
71
+ :surfaceTypeMaterialName, :boundaryMaterialName, :constructionMaterialName, :thermalZoneMaterialName,
72
+ :spaceTypeMaterialName, :buildingStoryMaterialName) do
73
+ def initialize(*)
74
+ super
75
+ self.surfaceTypeMaterialName = 'Undefined' if surfaceTypeMaterialName.nil?
76
+ self.boundaryMaterialName = 'Undefined' if boundaryMaterialName.nil?
77
+ self.constructionMaterialName = 'Undefined' if constructionMaterialName.nil?
78
+ self.thermalZoneMaterialName = 'Undefined' if thermalZoneMaterialName.nil?
79
+ self.spaceTypeMaterialName = 'Undefined' if spaceTypeMaterialName.nil?
80
+ self.buildingStoryMaterialName = 'Undefined' if buildingStoryMaterialName.nil?
81
+
82
+ self.constructionName = '' if constructionName.nil?
83
+ self.spaceName = '' if spaceName.nil?
84
+ self.thermalZoneName = '' if thermalZoneName.nil?
85
+ self.spaceTypeName = '' if spaceTypeName.nil?
86
+ self.buildingStoryName = '' if buildingStoryName.nil?
87
+ self.outsideBoundaryCondition = '' if outsideBoundaryCondition.nil?
88
+ self.outsideBoundaryConditionObjectName = '' if outsideBoundaryConditionObjectName.nil?
89
+ end
90
+ end
91
+ Vertex = Struct.new(:x, :y, :z)
92
+
93
+ AmbientLight = Struct.new(:uuid, :type, :color, :matrix)
94
+
95
+ def self.convert_model(model)
96
+ scene = build_scene(model)
97
+
98
+ northAxis = -model.getBuilding.northAxis
99
+
100
+ boundingBox = OpenStudio::BoundingBox.new
101
+ boundingBox.addPoint(OpenStudio::Point3d.new(0, 0, 0))
102
+ boundingBox.addPoint(OpenStudio::Point3d.new(1, 1, 1))
103
+ model.getPlanarSurfaceGroups.each do |group|
104
+ boundingBox.add(group.transformation * group.boundingBox)
105
+ end
106
+
107
+ lookAtX = 0 # (boundingBox.minX.get + boundingBox.maxX.get) / 2.0
108
+ lookAtY = 0 # (boundingBox.minY.get + boundingBox.maxY.get) / 2.0
109
+ lookAtZ = 0 # (boundingBox.minZ.get + boundingBox.maxZ.get) / 2.0
110
+ lookAtR = [Math.sqrt((boundingBox.maxX.get / 2.0)**2 + (boundingBox.maxY.get / 2.0)**2 + (boundingBox.maxZ.get / 2.0)**2),
111
+ Math.sqrt((boundingBox.minX.get / 2.0)**2 + (boundingBox.maxY.get / 2.0)**2 + (boundingBox.maxZ.get / 2.0)**2),
112
+ Math.sqrt((boundingBox.maxX.get / 2.0)**2 + (boundingBox.minY.get / 2.0)**2 + (boundingBox.maxZ.get / 2.0)**2),
113
+ Math.sqrt((boundingBox.maxX.get / 2.0)**2 + (boundingBox.maxY.get / 2.0)**2 + (boundingBox.minZ.get / 2.0)**2),
114
+ Math.sqrt((boundingBox.minX.get / 2.0)**2 + (boundingBox.minY.get / 2.0)**2 + (boundingBox.maxZ.get / 2.0)**2),
115
+ Math.sqrt((boundingBox.minX.get / 2.0)**2 + (boundingBox.maxY.get / 2.0)**2 + (boundingBox.minZ.get / 2.0)**2),
116
+ Math.sqrt((boundingBox.maxX.get / 2.0)**2 + (boundingBox.minY.get / 2.0)**2 + (boundingBox.minZ.get / 2.0)**2),
117
+ Math.sqrt((boundingBox.minX.get / 2.0)**2 + (boundingBox.minY.get / 2.0)**2 + (boundingBox.minZ.get / 2.0)**2)].max
118
+
119
+ boundingBoxHash = { 'minX' => boundingBox.minX.get, 'minY' => boundingBox.minY.get, 'minZ' => boundingBox.minZ.get,
120
+ 'maxX' => boundingBox.maxX.get, 'maxY' => boundingBox.maxY.get, 'maxZ' => boundingBox.maxZ.get,
121
+ 'lookAtX' => lookAtX, 'lookAtY' => lookAtY, 'lookAtZ' => lookAtZ, 'lookAtR' => lookAtR }
122
+
123
+ buildingStoryNames = []
124
+ model.getBuildingStorys.each do |buildingStory|
125
+ buildingStoryNames << buildingStory.name.to_s
126
+ end
127
+ buildingStoryNames.sort! { |x, y| x.upcase <=> y.upcase } # case insensitive sort
128
+
129
+ # build up the json hash
130
+ result = {}
131
+ result['metadata'] = { 'version' => 4.3, 'type' => 'Object', 'generator' => 'OpenStudio',
132
+ 'buildingStoryNames' => buildingStoryNames, 'boundingBox' => boundingBoxHash,
133
+ 'northAxis' => northAxis }
134
+ result['geometries'] = scene.geometries
135
+ result['materials'] = scene.materials
136
+ result['object'] = scene.object
137
+
138
+ return result
139
+ end
140
+
141
+ # format a uuid
142
+ def self.format_uuid(uuid)
143
+ return uuid.to_s.gsub('{', '').gsub('}', '')
144
+ end
145
+
146
+ # format color
147
+ def self.format_color(r, g, b)
148
+ return "0x#{r.to_s(16).rjust(2, '0')}#{g.to_s(16).rjust(2, '0')}#{b.to_s(16).rjust(2, '0')}"
149
+ end
150
+
151
+ # create a material
152
+ def self.make_material(name, color, opacity, side, shininess = 50)
153
+ transparent = false
154
+ if opacity < 1
155
+ transparent = true
156
+ end
157
+
158
+ material = { uuid: format_uuid(OpenStudio.createUUID).to_s,
159
+ name: name,
160
+ type: 'MeshPhongMaterial',
161
+ color: color.to_s.hex,
162
+ ambient: color.to_s.hex,
163
+ emissive: '0x000000'.hex,
164
+ specular: color.to_s.hex,
165
+ shininess: shininess,
166
+ opacity: opacity,
167
+ transparent: transparent,
168
+ wireframe: false,
169
+ side: side }
170
+ return material
171
+ end
172
+
173
+ # create the standard materials
174
+ def self.build_materials(model)
175
+ materials = []
176
+
177
+ # materials << make_material('Undefined', format_color(255, 255, 255), 1, THREE::DoubleSide)
178
+ materials << { uuid: format_uuid(OpenStudio.createUUID).to_s, name: 'Undefined', type: 'MeshBasicMaterial', color: '0xffffff'.hex, side: THREE::DoubleSide }
179
+
180
+ materials << make_material('NormalMaterial', format_color(255, 255, 255), 1, THREE::DoubleSide)
181
+ # materials << make_material('NormalMaterial_Ext', format_color(255, 255, 255), 1, THREE::FrontSide)
182
+ materials << { uuid: format_uuid(OpenStudio.createUUID).to_s, name: 'NormalMaterial_Ext', type: 'MeshBasicMaterial', color: '0xffffff'.hex, side: THREE::FrontSide }
183
+ materials << make_material('NormalMaterial_Int', format_color(255, 0, 0), 1, THREE::BackSide)
184
+
185
+ # materials from 'openstudio\openstudiocore\ruby\openstudio\sketchup_plugin\lib\interfaces\MaterialsInterface.rb'
186
+ materials << make_material('Floor', format_color(128, 128, 128), 1, THREE::DoubleSide)
187
+ materials << make_material('Floor_Ext', format_color(128, 128, 128), 1, THREE::FrontSide)
188
+ materials << make_material('Floor_Int', format_color(191, 191, 191), 1, THREE::BackSide)
189
+
190
+ materials << make_material('Wall', format_color(204, 178, 102), 1, THREE::DoubleSide)
191
+ materials << make_material('Wall_Ext', format_color(204, 178, 102), 1, THREE::FrontSide)
192
+ materials << make_material('Wall_Int', format_color(235, 226, 197), 1, THREE::BackSide)
193
+
194
+ materials << make_material('RoofCeiling', format_color(153, 76, 76), 1, THREE::DoubleSide)
195
+ materials << make_material('RoofCeiling_Ext', format_color(153, 76, 76), 1, THREE::FrontSide)
196
+ materials << make_material('RoofCeiling_Int', format_color(202, 149, 149), 1, THREE::BackSide)
197
+
198
+ materials << make_material('Window', format_color(102, 178, 204), 0.6, THREE::DoubleSide)
199
+ materials << make_material('Window_Ext', format_color(102, 178, 204), 0.6, THREE::FrontSide)
200
+ materials << make_material('Window_Int', format_color(192, 226, 235), 0.6, THREE::BackSide)
201
+
202
+ materials << make_material('Door', format_color(153, 133, 76), 1, THREE::DoubleSide)
203
+ materials << make_material('Door_Ext', format_color(153, 133, 76), 1, THREE::FrontSide)
204
+ materials << make_material('Door_Int', format_color(202, 188, 149), 1, THREE::BackSide)
205
+
206
+ materials << make_material('SiteShading', format_color(75, 124, 149), 1, THREE::DoubleSide)
207
+ materials << make_material('SiteShading_Ext', format_color(75, 124, 149), 1, THREE::FrontSide)
208
+ materials << make_material('SiteShading_Int', format_color(187, 209, 220), 1, THREE::BackSide)
209
+
210
+ materials << make_material('BuildingShading', format_color(113, 76, 153), 1, THREE::DoubleSide)
211
+ materials << make_material('BuildingShading_Ext', format_color(113, 76, 153), 1, THREE::FrontSide)
212
+ materials << make_material('BuildingShading_Int', format_color(216, 203, 229), 1, THREE::BackSide)
213
+
214
+ materials << make_material('SpaceShading', format_color(76, 110, 178), 1, THREE::DoubleSide)
215
+ materials << make_material('SpaceShading_Ext', format_color(76, 110, 178), 1, THREE::FrontSide)
216
+ materials << make_material('SpaceShading_Int', format_color(183, 197, 224), 1, THREE::BackSide)
217
+
218
+ materials << make_material('InteriorPartitionSurface', format_color(158, 188, 143), 1, THREE::DoubleSide)
219
+ materials << make_material('InteriorPartitionSurface_Ext', format_color(158, 188, 143), 1, THREE::FrontSide)
220
+ materials << make_material('InteriorPartitionSurface_Int', format_color(213, 226, 207), 1, THREE::BackSide)
221
+
222
+ # start textures for boundary conditions
223
+ materials << make_material('Boundary_Surface', format_color(0, 153, 0), 1, THREE::DoubleSide)
224
+ materials << make_material('Boundary_Adiabatic', format_color(255, 101, 178), 1, THREE::DoubleSide)
225
+ materials << make_material('Boundary_Space', format_color(255, 0, 0), 1, THREE::DoubleSide)
226
+ materials << make_material('Boundary_Outdoors', format_color(163, 204, 204), 1, THREE::DoubleSide)
227
+ materials << make_material('Boundary_Outdoors_Sun', format_color(40, 204, 204), 1, THREE::DoubleSide)
228
+ materials << make_material('Boundary_Outdoors_Wind', format_color(9, 159, 162), 1, THREE::DoubleSide)
229
+ materials << make_material('Boundary_Outdoors_SunWind', format_color(68, 119, 161), 1, THREE::DoubleSide)
230
+ materials << make_material('Boundary_Ground', format_color(204, 183, 122), 1, THREE::DoubleSide)
231
+ materials << make_material('Boundary_Groundfcfactormethod', format_color(153, 122, 30), 1, THREE::DoubleSide)
232
+ materials << make_material('Boundary_Groundslabpreprocessoraverage', format_color(255, 191, 0), 1, THREE::DoubleSide)
233
+ materials << make_material('Boundary_Groundslabpreprocessorcore', format_color(255, 182, 50), 1, THREE::DoubleSide)
234
+ materials << make_material('Boundary_Groundslabpreprocessorperimeter', format_color(255, 178, 101), 1, THREE::DoubleSide)
235
+ materials << make_material('Boundary_Groundbasementpreprocessoraveragewall', format_color(204, 51, 0), 1, THREE::DoubleSide)
236
+ materials << make_material('Boundary_Groundbasementpreprocessoraveragefloor', format_color(204, 81, 40), 1, THREE::DoubleSide)
237
+ materials << make_material('Boundary_Groundbasementpreprocessorupperwall', format_color(204, 112, 81), 1, THREE::DoubleSide)
238
+ materials << make_material('Boundary_Groundbasementpreprocessorlowerwall', format_color(204, 173, 163), 1, THREE::DoubleSide)
239
+ materials << make_material('Boundary_Othersidecoefficients', format_color(63, 63, 63), 1, THREE::DoubleSide)
240
+ materials << make_material('Boundary_Othersideconditionsmodel', format_color(153, 0, 76), 1, THREE::DoubleSide)
241
+
242
+ # make construction materials
243
+ model.getConstructionBases.each do |construction|
244
+ color = construction.renderingColor
245
+ if color.empty?
246
+ color = OpenStudio::Model::RenderingColor.new(model)
247
+ construction.setRenderingColor(color)
248
+ else
249
+ color = color.get
250
+ end
251
+ name = "Construction_#{construction.name}"
252
+ materials << make_material(name, format_color(color.renderingRedValue, color.renderingGreenValue, color.renderingBlueValue), color.renderingAlphaValue / 255.to_f, THREE::DoubleSide)
253
+ end
254
+
255
+ # make thermal zone materials
256
+ model.getThermalZones.each do |zone|
257
+ color = zone.renderingColor
258
+ if color.empty?
259
+ color = OpenStudio::Model::RenderingColor.new(model)
260
+ zone.setRenderingColor(color)
261
+ else
262
+ color = color.get
263
+ end
264
+ name = "ThermalZone_#{zone.name}"
265
+ materials << make_material(name, format_color(color.renderingRedValue, color.renderingGreenValue, color.renderingBlueValue), color.renderingAlphaValue / 255.to_f, THREE::DoubleSide)
266
+ end
267
+
268
+ # make space type materials
269
+ model.getSpaceTypes.each do |spaceType|
270
+ color = spaceType.renderingColor
271
+ if color.empty?
272
+ color = OpenStudio::Model::RenderingColor.new(model)
273
+ spaceType.setRenderingColor(color)
274
+ else
275
+ color = color.get
276
+ end
277
+ name = "SpaceType_#{spaceType.name}"
278
+ materials << make_material(name, format_color(color.renderingRedValue, color.renderingGreenValue, color.renderingBlueValue), color.renderingAlphaValue / 255.to_f, THREE::DoubleSide)
279
+ end
280
+
281
+ # make building story materials
282
+ model.getBuildingStorys.each do |buildingStory|
283
+ color = buildingStory.renderingColor
284
+ if color.empty?
285
+ color = OpenStudio::Model::RenderingColor.new(model)
286
+ buildingStory.setRenderingColor(color)
287
+ else
288
+ color = color.get
289
+ end
290
+ name = "BuildingStory_#{buildingStory.name}"
291
+ materials << make_material(name, format_color(color.renderingRedValue, color.renderingGreenValue, color.renderingBlueValue), color.renderingAlphaValue / 255.to_f, THREE::DoubleSide)
292
+ end
293
+
294
+ return materials
295
+ end
296
+
297
+ # get the index of a vertex out of a list
298
+ def self.get_vertex_index(vertex, vertices, tol = 0.001)
299
+ vertices.each_index do |i|
300
+ if OpenStudio.getDistance(vertex, vertices[i]) < tol
301
+ return i
302
+ end
303
+ end
304
+ vertices << vertex
305
+ return (vertices.length - 1)
306
+ end
307
+
308
+ # flatten array of vertices into a single array
309
+ def self.flatten_vertices(vertices)
310
+ result = []
311
+ vertices.each do |vertex|
312
+ # result << vertex.x
313
+ # result << vertex.y
314
+ # result << vertex.z
315
+
316
+ result << vertex.x.round(3)
317
+ result << vertex.z.round(3)
318
+ result << -vertex.y.round(3)
319
+ end
320
+ return result
321
+ end
322
+
323
+ # turn a surface into geometries, the first one is the surface, remaining are sub surfaces
324
+ def self.make_geometries(surface)
325
+ geometries = []
326
+ user_datas = []
327
+
328
+ # get the transformation to site coordinates
329
+ site_transformation = OpenStudio::Transformation.new
330
+ building = surface.model.getBuilding
331
+
332
+ space = surface.space
333
+ if space.is_initialized
334
+ site_transformation = building.transformation * space.get.transformation
335
+ else
336
+ site_transformation = building.transformation
337
+ end
338
+
339
+ # get the vertices
340
+ surface_vertices = surface.vertices
341
+ t = OpenStudio::Transformation.alignFace(surface_vertices)
342
+ r = t.rotationMatrix
343
+ tInv = t.inverse
344
+ surface_vertices = OpenStudio.reverse(tInv * surface_vertices)
345
+
346
+ # get vertices of all sub surfaces
347
+ sub_surface_vertices = OpenStudio::Point3dVectorVector.new
348
+ sub_surfaces = surface.subSurfaces
349
+ sub_surfaces.each do |sub_surface|
350
+ sub_surface_vertices << OpenStudio.reverse(tInv * sub_surface.vertices)
351
+ end
352
+
353
+ # triangulate surface
354
+ triangles = OpenStudio.computeTriangulation(surface_vertices, sub_surface_vertices)
355
+ if triangles.empty?
356
+ puts "Failed to triangulate surface #{surface.name} with #{sub_surfaces.size} sub surfaces"
357
+ return geometries
358
+ end
359
+
360
+ all_vertices = []
361
+ face_indices = []
362
+ triangles.each do |vertices|
363
+ vertices = site_transformation * t * vertices
364
+ # normal = site_transformation.rotationMatrix*r*z
365
+
366
+ # https://github.com/mrdoob/three.js/wiki/JSON-Model-format-3
367
+ # 0 indicates triangle
368
+ # 16 indicates triangle with normals
369
+ face_indices << 0
370
+ vertices.reverse_each do |vertex|
371
+ face_indices << get_vertex_index(vertex, all_vertices)
372
+ end
373
+
374
+ # convert to 1 based indices
375
+ # face_indices.each_index {|i| face_indices[i] = face_indices[i] + 1}
376
+ end
377
+
378
+ data = GeometryData.new
379
+ data.vertices = flatten_vertices(all_vertices)
380
+ data.normals = []
381
+ data.uvs = []
382
+ data.faces = face_indices
383
+ data.scale = 1
384
+ data.visible = true
385
+ data.castShadow = true
386
+ data.receiveShadow = false
387
+ data.doubleSided = true
388
+
389
+ geometry = Geometry.new
390
+ geometry.uuid = format_uuid(surface.handle)
391
+ geometry.type = 'Geometry'
392
+ geometry.data = data.to_h
393
+ geometries << geometry.to_h
394
+
395
+ surface_user_data = UserData.new
396
+ surface_user_data.handle = format_uuid(surface.handle)
397
+ surface_user_data.name = surface.name.to_s
398
+ surface_user_data.coincidentWithOutsideObject = false
399
+ surface_user_data.surfaceType = surface.surfaceType
400
+ surface_user_data.surfaceTypeMaterialName = surface.surfaceType
401
+
402
+ surface_user_data.outsideBoundaryCondition = surface.outsideBoundaryCondition
403
+ adjacent_surface = surface.adjacentSurface
404
+ if adjacent_surface.is_initialized
405
+ surface_user_data.outsideBoundaryConditionObjectName = adjacent_surface.get.name.to_s
406
+ surface_user_data.outsideBoundaryConditionObjectHandle = format_uuid(adjacent_surface.get.handle)
407
+
408
+ other_site_transformation = OpenStudio::Transformation.new
409
+ other_space = adjacent_surface.get.space
410
+ if !other_space.empty?
411
+ other_site_transformation = building.transformation * other_space.get.transformation
412
+ else
413
+ other_site_transformation = building.transformation
414
+ end
415
+
416
+ other_vertices = other_site_transformation * adjacent_surface.get.vertices
417
+ if OpenStudio.circularEqual(site_transformation * surface.vertices, OpenStudio.reverse(other_vertices))
418
+ # puts "adjacent surfaces are coincident"
419
+ surface_user_data.coincidentWithOutsideObject = true # controls display only, not energy model
420
+ else
421
+ # puts "adjacent surfaces are not coincident"
422
+ surface_user_data.coincidentWithOutsideObject = false # controls display only, not energy model
423
+ end
424
+
425
+ end
426
+ surface_user_data.sunExposure = surface.sunExposure
427
+ surface_user_data.windExposure = surface.windExposure
428
+
429
+ if surface.outsideBoundaryCondition == 'Outdoors'
430
+ if surface.sunExposure == 'SunExposed' && surface.windExposure == 'WindExposed'
431
+ surface_user_data.boundaryMaterialName = 'Boundary_Outdoors_SunWind'
432
+ elsif surface.sunExposure == 'SunExposed'
433
+ surface_user_data.boundaryMaterialName = 'Boundary_Outdoors_Sun'
434
+ elsif surface.sunExposure == 'WindExposed'
435
+ surface_user_data.boundaryMaterialName = 'Boundary_Outdoors_Wind'
436
+ else
437
+ surface_user_data.boundaryMaterialName = 'Boundary_Outdoors'
438
+ end
439
+ else
440
+ surface_user_data.boundaryMaterialName = 'Boundary_' + surface.outsideBoundaryCondition
441
+ end
442
+
443
+ construction = surface.construction
444
+ if construction.is_initialized
445
+ surface_user_data.constructionName = construction.get.name.to_s
446
+ surface_user_data.constructionMaterialName = 'Construction_' + construction.get.name.to_s
447
+ end
448
+
449
+ space = surface.space
450
+ if space.is_initialized
451
+ space = space.get
452
+ surface_user_data.spaceName = space.name.to_s
453
+
454
+ thermal_zone = space.thermalZone
455
+ if thermal_zone.is_initialized
456
+ surface_user_data.thermalZoneName = thermal_zone.get.name.to_s
457
+ surface_user_data.thermalZoneMaterialName = 'ThermalZone_' + thermal_zone.get.name.to_s
458
+ end
459
+
460
+ space_type = space.spaceType
461
+ if space_type.is_initialized
462
+ surface_user_data.spaceTypeName = space_type.get.name.to_s
463
+ surface_user_data.spaceTypeMaterialName = 'SpaceType_' + space_type.get.name.to_s
464
+ end
465
+
466
+ building_story = space.buildingStory
467
+ if building_story.is_initialized
468
+ surface_user_data.buildingStoryName = building_story.get.name.to_s
469
+ surface_user_data.buildingStoryMaterialName = 'BuildingStory_' + building_story.get.name.to_s
470
+ end
471
+ end
472
+
473
+ # vertices = []
474
+ # surface.vertices.each do |v|
475
+ # vertex = Vertex.new
476
+ # vertex.x = v.x
477
+ # vertex.y = v.y
478
+ # vertex.z = v.z
479
+ # vertices << vertex.to_h
480
+ # end
481
+ # surface_user_data.vertices = vertices
482
+ user_datas << surface_user_data.to_h
483
+
484
+ # now add geometry for each sub surface
485
+ sub_surfaces.each do |sub_surface|
486
+ # triangulate sub surface
487
+ sub_surface_vertices = OpenStudio.reverse(tInv * sub_surface.vertices)
488
+ triangles = OpenStudio.computeTriangulation(sub_surface_vertices, OpenStudio::Point3dVectorVector.new)
489
+
490
+ all_vertices = []
491
+ face_indices = []
492
+ triangles.each do |vertices|
493
+ vertices = site_transformation * t * vertices
494
+ # normal = site_transformation.rotationMatrix*r*z
495
+
496
+ # https://github.com/mrdoob/three.js/wiki/JSON-Model-format-3
497
+ # 0 indicates triangle
498
+ # 16 indicates triangle with normals
499
+ face_indices << 0
500
+ vertices.reverse_each do |vertex|
501
+ face_indices << get_vertex_index(vertex, all_vertices)
502
+ end
503
+
504
+ # convert to 1 based indices
505
+ # face_indices.each_index {|i| face_indices[i] = face_indices[i] + 1}
506
+ end
507
+
508
+ data = GeometryData.new
509
+ data.vertices = flatten_vertices(all_vertices)
510
+ data.normals = []
511
+ data.uvs = []
512
+ data.faces = face_indices
513
+ data.scale = 1
514
+ data.visible = true
515
+ data.castShadow = true
516
+ data.receiveShadow = false
517
+ data.doubleSided = true
518
+
519
+ geometry = Geometry.new
520
+ geometry.uuid = format_uuid(sub_surface.handle)
521
+ geometry.type = 'Geometry'
522
+ geometry.data = data.to_h
523
+ geometries << geometry.to_h
524
+
525
+ sub_surface_user_data = UserData.new
526
+ sub_surface_user_data.handle = format_uuid(sub_surface.handle)
527
+ sub_surface_user_data.name = sub_surface.name.to_s
528
+ sub_surface_user_data.coincidentWithOutsideObject = false
529
+
530
+ sub_surface_user_data.surfaceType = sub_surface.subSurfaceType
531
+ if /Window/.match(sub_surface.subSurfaceType) || /Glass/.match(sub_surface.subSurfaceType) ||
532
+ /Skylight/.match(sub_surface.subSurfaceType) || /TubularDaylight/.match(sub_surface.subSurfaceType)
533
+ sub_surface_user_data.surfaceTypeMaterialName = 'Window'
534
+ else
535
+ sub_surface_user_data.surfaceTypeMaterialName = 'Door'
536
+ end
537
+
538
+ sub_surface_user_data.outsideBoundaryCondition = surface_user_data.outsideBoundaryCondition
539
+ adjacent_sub_surface = sub_surface.adjacentSubSurface
540
+ if adjacent_sub_surface.is_initialized
541
+ sub_surface_user_data.outsideBoundaryConditionObjectName = adjacent_sub_surface.get.name.to_s
542
+ sub_surface_user_data.outsideBoundaryConditionObjectHandle = format_uuid(adjacent_sub_surface.get.handle)
543
+
544
+ other_site_transformation = OpenStudio::Transformation.new
545
+ other_space = adjacent_sub_surface.get.space
546
+ if !other_space.empty?
547
+ other_site_transformation = building.transformation * other_space.get.transformation
548
+ else
549
+ other_site_transformation = building.transformation
550
+ end
551
+
552
+ other_vertices = other_site_transformation * adjacent_sub_surface.get.vertices
553
+ if OpenStudio.circularEqual(site_transformation * sub_surface.vertices, OpenStudio.reverse(other_vertices))
554
+ # puts "adjacent sub surfaces are coincident"
555
+ surface_user_data.coincidentWithOutsideObject = true # controls display only, not energy model
556
+ else
557
+ # puts "adjacent sub surfaces are not coincident"
558
+ surface_user_data.coincidentWithOutsideObject = false # controls display only, not energy model
559
+ end
560
+
561
+ sub_surface_user_data.boundaryMaterialName = 'Boundary_Surface'
562
+ else
563
+ if surface_user_data.boundaryMaterialName == 'Boundary_Surface'
564
+ sub_surface_user_data.boundaryMaterialName = 'Undefined'
565
+ else
566
+ sub_surface_user_data.boundaryMaterialName = surface_user_data.boundaryMaterialName
567
+ end
568
+ end
569
+ sub_surface_user_data.sunExposure = surface_user_data.sunExposure
570
+ sub_surface_user_data.windExposure = surface_user_data.windExposure
571
+
572
+ construction = sub_surface.construction
573
+ if construction.is_initialized
574
+ sub_surface_user_data.constructionName = construction.get.name.to_s
575
+ sub_surface_user_data.constructionMaterialName = 'Construction_' + construction.get.name.to_s
576
+ end
577
+ sub_surface_user_data.spaceName = surface_user_data.spaceName
578
+ sub_surface_user_data.thermalZoneName = surface_user_data.thermalZoneName
579
+ sub_surface_user_data.thermalZoneMaterialName = surface_user_data.thermalZoneMaterialName
580
+ sub_surface_user_data.spaceTypeName = surface_user_data.spaceTypeName
581
+ sub_surface_user_data.spaceTypeMaterialName = surface_user_data.spaceTypeMaterialName
582
+ sub_surface_user_data.buildingStoryName = surface_user_data.buildingStoryName
583
+ sub_surface_user_data.buildingStoryMaterialName = surface_user_data.buildingStoryMaterialName
584
+
585
+ # vertices = []
586
+ # surface.vertices.each do |v|
587
+ # vertex = Vertex.new
588
+ # vertex.x = v.x
589
+ # vertex.y = v.y
590
+ # vertex.z = v.z
591
+ # vertices << vertex.to_h
592
+ # end
593
+ # sub_surface_user_data.vertices = vertices
594
+ user_datas << sub_surface_user_data.to_h
595
+ end
596
+
597
+ return [geometries, user_datas]
598
+ end
599
+
600
+ # turn a shading surface into geometries
601
+ def self.make_shade_geometries(surface)
602
+ geometries = []
603
+ user_datas = []
604
+
605
+ # get the transformation to site coordinates
606
+ site_transformation = OpenStudio::Transformation.new
607
+ building = surface.model.getBuilding
608
+
609
+ shading_surface_group = surface.shadingSurfaceGroup
610
+ shading_surface_type = 'Building'
611
+ space_name = nil
612
+ thermal_zone_name = nil
613
+ space_type_name = nil
614
+ building_story_name = nil
615
+ if !shading_surface_group.empty?
616
+ shading_surface_type = shading_surface_group.get.shadingSurfaceType
617
+
618
+ space = shading_surface_group.get.space
619
+ if space.is_initialized
620
+ space = space.get
621
+ space_name = space.name.to_s
622
+
623
+ thermal_zone = space.thermalZone
624
+ if thermal_zone.is_initialized
625
+ thermal_zone_name = thermal_zone.get.name.to_s
626
+ end
627
+
628
+ space_type = space.spaceType
629
+ if space_type.is_initialized
630
+ space_type_name = space_type.get.name.to_s
631
+ end
632
+
633
+ building_story = space.buildingStory
634
+ if building_story.is_initialized
635
+ building_story_name = building_story.get.name.to_s
636
+ end
637
+
638
+ site_transformation = building.transformation * space.transformation * shading_surface_group.get.transformation
639
+ elsif /Site/i.match(shading_surface_type)
640
+ site_transformation = shading_surface_group.get.transformation
641
+ else
642
+ site_transformation = building.transformation * shading_surface_group.get.transformation
643
+ end
644
+
645
+ end
646
+
647
+ # get the vertices
648
+ surface_vertices = surface.vertices
649
+ t = OpenStudio::Transformation.alignFace(surface_vertices)
650
+ r = t.rotationMatrix
651
+ tInv = t.inverse
652
+ surface_vertices = OpenStudio.reverse(tInv * surface_vertices)
653
+
654
+ # triangulate surface
655
+ triangles = OpenStudio.computeTriangulation(surface_vertices, OpenStudio::Point3dVectorVector.new)
656
+ if triangles.empty?
657
+ puts "Failed to triangulate shading surface #{surface.name}"
658
+ return geometries
659
+ end
660
+
661
+ all_vertices = []
662
+ face_indices = []
663
+ triangles.each do |vertices|
664
+ vertices = site_transformation * t * vertices
665
+ # normal = site_transformation.rotationMatrix*r*z
666
+
667
+ # https://github.com/mrdoob/three.js/wiki/JSON-Model-format-3
668
+ # 0 indicates triangle
669
+ # 16 indicates triangle with normals
670
+ face_indices << 0
671
+ vertices.reverse_each do |vertex|
672
+ face_indices << get_vertex_index(vertex, all_vertices)
673
+ end
674
+
675
+ # convert to 1 based indices
676
+ # face_indices.each_index {|i| face_indices[i] = face_indices[i] + 1}
677
+ end
678
+
679
+ data = GeometryData.new
680
+ data.vertices = flatten_vertices(all_vertices)
681
+ data.normals = []
682
+ data.uvs = []
683
+ data.faces = face_indices
684
+ data.scale = 1
685
+ data.visible = true
686
+ data.castShadow = true
687
+ data.receiveShadow = false
688
+ data.doubleSided = true
689
+
690
+ geometry = Geometry.new
691
+ geometry.uuid = format_uuid(surface.handle)
692
+ geometry.type = 'Geometry'
693
+ geometry.data = data.to_h
694
+ geometries << geometry.to_h
695
+
696
+ surface_user_data = UserData.new
697
+ surface_user_data.handle = format_uuid(surface.handle)
698
+ surface_user_data.name = surface.name.to_s
699
+ surface_user_data.coincidentWithOutsideObject = false
700
+
701
+ surface_user_data.surfaceType = shading_surface_type + 'Shading'
702
+ surface_user_data.surfaceTypeMaterialName = shading_surface_type + 'Shading'
703
+
704
+ # surface_user_data.outsideBoundaryCondition = nil
705
+ # surface_user_data.outsideBoundaryConditionObjectName = nil
706
+ # surface_user_data.outsideBoundaryConditionObjectHandle = nil
707
+ surface_user_data.sunExposure = 'SunExposed'
708
+ surface_user_data.windExposure = 'WindExposed'
709
+
710
+ construction = surface.construction
711
+ if construction.is_initialized
712
+ surface_user_data.constructionName = construction.get.name.to_s
713
+ surface_user_data.constructionMaterialName = 'Construction_' + construction.get.name.to_s
714
+ end
715
+
716
+ if space_name
717
+ surface_user_data.spaceName = space_name
718
+ end
719
+ if thermal_zone_name
720
+ surface_user_data.thermalZoneName = thermal_zone_name
721
+ surface_user_data.thermalZoneMaterialName = 'ThermalZone_' + thermal_zone_name
722
+ end
723
+ if space_type_name
724
+ surface_user_data.spaceTypeName = space_type_name
725
+ surface_user_data.spaceTypeMaterialName = 'SpaceType_' + space_type_name
726
+ end
727
+ if building_story_name
728
+ surface_user_data.buildingStoryName = building_story_name
729
+ surface_user_data.buildingStoryMaterialName = 'BuildingStory_' + building_story_name
730
+ end
731
+
732
+ # vertices = []
733
+ # surface.vertices.each do |v|
734
+ # vertex = Vertex.new
735
+ # vertex.x = v.x
736
+ # vertex.y = v.y
737
+ # vertex.z = v.z
738
+ # vertices << vertex.to_h
739
+ # end
740
+ # surface_user_data.vertices = vertices
741
+ user_datas << surface_user_data.to_h
742
+
743
+ return [geometries, user_datas]
744
+ end
745
+
746
+ # turn an interior partition surface into geometries
747
+ def self.make_interior_partition_geometries(surface)
748
+ geometries = []
749
+ user_datas = []
750
+
751
+ # get the transformation to site coordinates
752
+ site_transformation = OpenStudio::Transformation.new
753
+ building = surface.model.getBuilding
754
+ interior_partition_surface_group = surface.interiorPartitionSurfaceGroup
755
+
756
+ space_name = nil
757
+ thermal_zone_name = nil
758
+ space_type_name = nil
759
+ building_story_name = nil
760
+ if !interior_partition_surface_group.empty?
761
+
762
+ space = interior_partition_surface_group.get.space
763
+ if space.is_initialized
764
+ space = space.get
765
+ space_name = space.name.to_s
766
+
767
+ thermal_zone = space.thermalZone
768
+ if thermal_zone.is_initialized
769
+ thermal_zone_name = thermal_zone.get.name.to_s
770
+ end
771
+
772
+ space_type = space.spaceType
773
+ if space_type.is_initialized
774
+ space_type_name = space_type.get.name.to_s
775
+ end
776
+
777
+ building_story = space.buildingStory
778
+ if building_story.is_initialized
779
+ building_story_name = building_story.get.name.to_s
780
+ end
781
+
782
+ site_transformation = building.transformation * space.transformation * interior_partition_surface_group.get.transformation
783
+ else
784
+ site_transformation = building.transformation * interior_partition_surface_group.get.transformation
785
+ end
786
+ end
787
+
788
+ # get the vertices
789
+ surface_vertices = surface.vertices
790
+ t = OpenStudio::Transformation.alignFace(surface_vertices)
791
+ r = t.rotationMatrix
792
+ tInv = t.inverse
793
+ surface_vertices = OpenStudio.reverse(tInv * surface_vertices)
794
+
795
+ # triangulate surface
796
+ triangles = OpenStudio.computeTriangulation(surface_vertices, OpenStudio::Point3dVectorVector.new)
797
+ if triangles.empty?
798
+ puts "Failed to triangulate interior partition surface #{surface.name}"
799
+ return geometries
800
+ end
801
+
802
+ all_vertices = []
803
+ face_indices = []
804
+ triangles.each do |vertices|
805
+ vertices = site_transformation * t * vertices
806
+ # normal = site_transformation.rotationMatrix*r*z
807
+
808
+ # https://github.com/mrdoob/three.js/wiki/JSON-Model-format-3
809
+ # 0 indicates triangle
810
+ # 16 indicates triangle with normals
811
+ face_indices << 0
812
+ vertices.reverse_each do |vertex|
813
+ face_indices << get_vertex_index(vertex, all_vertices)
814
+ end
815
+
816
+ # convert to 1 based indices
817
+ # face_indices.each_index {|i| face_indices[i] = face_indices[i] + 1}
818
+ end
819
+
820
+ data = GeometryData.new
821
+ data.vertices = flatten_vertices(all_vertices)
822
+ data.normals = []
823
+ data.uvs = []
824
+ data.faces = face_indices
825
+ data.scale = 1
826
+ data.visible = true
827
+ data.castShadow = true
828
+ data.receiveShadow = false
829
+ data.doubleSided = true
830
+
831
+ geometry = Geometry.new
832
+ geometry.uuid = format_uuid(surface.handle)
833
+ geometry.type = 'Geometry'
834
+ geometry.data = data.to_h
835
+ geometries << geometry.to_h
836
+
837
+ surface_user_data = UserData.new
838
+ surface_user_data.handle = format_uuid(surface.handle)
839
+ surface_user_data.name = surface.name.to_s
840
+ surface_user_data.coincidentWithOutsideObject = false
841
+
842
+ surface_user_data.surfaceType = 'InteriorPartitionSurface'
843
+ surface_user_data.surfaceTypeMaterialName = 'InteriorPartitionSurface'
844
+
845
+ # surface_user_data.outsideBoundaryCondition = nil
846
+ # surface_user_data.outsideBoundaryConditionObjectName = nil
847
+ # surface_user_data.outsideBoundaryConditionObjectHandle = nil
848
+ surface_user_data.sunExposure = 'NoSun'
849
+ surface_user_data.windExposure = 'NoWind'
850
+
851
+ construction = surface.construction
852
+ if construction.is_initialized
853
+ surface_user_data.constructionName = construction.get.name.to_s
854
+ surface_user_data.constructionMaterialName = 'Construction_' + construction.get.name.to_s
855
+ end
856
+
857
+ if space_name
858
+ surface_user_data.spaceName = space_name
859
+ end
860
+ if thermal_zone_name
861
+ surface_user_data.thermalZoneName = thermal_zone_name
862
+ surface_user_data.thermalZoneMaterialName = 'ThermalZone_' + thermal_zone_name
863
+ end
864
+ if space_type_name
865
+ surface_user_data.spaceTypeName = space_type_name
866
+ surface_user_data.spaceTypeMaterialName = 'SpaceType_' + space_type_name
867
+ end
868
+ if building_story_name
869
+ surface_user_data.buildingStoryName = building_story_name
870
+ surface_user_data.buildingStoryMaterialName = 'BuildingStory_' + building_story_name
871
+ end
872
+
873
+ # vertices = []
874
+ # surface.vertices.each do |v|
875
+ # vertex = Vertex.new
876
+ # vertex.x = v.x
877
+ # vertex.y = v.y
878
+ # vertex.z = v.z
879
+ # vertices << vertex.to_h
880
+ # end
881
+ # surface_user_data.vertices = vertices
882
+ user_datas << surface_user_data.to_h
883
+
884
+ return [geometries, user_datas]
885
+ end
886
+
887
+ def self.identity_matrix
888
+ return [1, 0, 0, 0, 0, 1, 0, 0, 0, 0, 1, 0, 0, 0, 0, 1]
889
+ end
890
+
891
+ def self.build_scene(model)
892
+ materials = build_materials(model)
893
+
894
+ object = {}
895
+ object[:uuid] = format_uuid(OpenStudio.createUUID)
896
+ object[:type] = 'Scene'
897
+ object[:matrix] = identity_matrix
898
+ object[:children] = []
899
+
900
+ floor_material = materials.find { |m| m[:name] == 'Floor' }
901
+ wall_material = materials.find { |m| m[:name] == 'Wall' }
902
+ roof_material = materials.find { |m| m[:name] == 'RoofCeiling' }
903
+ window_material = materials.find { |m| m[:name] == 'Window' }
904
+ door_material = materials.find { |m| m[:name] == 'Door' }
905
+ site_shading_material = materials.find { |m| m[:name] == 'SiteShading' }
906
+ building_shading_material = materials.find { |m| m[:name] == 'BuildingShading' }
907
+ space_shading_material = materials.find { |m| m[:name] == 'SpaceShading' }
908
+ interior_partition_surface_material = materials.find { |m| m[:name] == 'InteriorPartitionSurface' }
909
+
910
+ # loop over all surfaces
911
+ all_geometries = []
912
+ model.getSurfaces.each do |surface|
913
+ material = nil
914
+ surfaceType = surface.surfaceType.upcase
915
+ if surfaceType == 'FLOOR'
916
+ material = floor_material
917
+ elsif surfaceType == 'WALL'
918
+ material = wall_material
919
+ elsif surfaceType == 'ROOFCEILING'
920
+ material = roof_material
921
+ end
922
+
923
+ geometries, user_datas = make_geometries(surface)
924
+ geometries&.each_index do |i|
925
+ geometry = geometries[i]
926
+ user_data = user_datas[i]
927
+
928
+ all_geometries << geometry
929
+
930
+ scene_child = SceneChild.new
931
+ scene_child.uuid = format_uuid(OpenStudio.createUUID)
932
+ scene_child.name = user_data[:name]
933
+ scene_child.type = 'Mesh'
934
+ scene_child.geometry = geometry[:uuid]
935
+
936
+ if i == 0
937
+ # first geometry is base surface
938
+ scene_child.material = material[:uuid]
939
+ else
940
+ # sub surface
941
+ if /Window/.match(user_data[:surfaceType]) || /Glass/.match(user_data[:surfaceType])
942
+ scene_child.material = window_material[:uuid]
943
+ else
944
+ scene_child.material = door_material[:uuid]
945
+ end
946
+ end
947
+
948
+ scene_child.matrix = identity_matrix
949
+ scene_child.userData = user_data
950
+ object[:children] << scene_child.to_h
951
+ end
952
+ end
953
+
954
+ # loop over all shading surfaces
955
+ model.getShadingSurfaces.each do |surface|
956
+ geometries, user_datas = make_shade_geometries(surface)
957
+ geometries&.each_index do |i|
958
+ geometry = geometries[i]
959
+ user_data = user_datas[i]
960
+
961
+ material = nil
962
+ if /Site/.match(user_data[:surfaceType])
963
+ material = site_shading_material
964
+ elsif /Building/.match(user_data[:surfaceType])
965
+ material = building_shading_material
966
+ elsif /Space/.match(user_data[:surfaceType])
967
+ material = space_shading_material
968
+ end
969
+
970
+ all_geometries << geometry
971
+
972
+ scene_child = SceneChild.new
973
+ scene_child.uuid = format_uuid(OpenStudio.createUUID)
974
+ scene_child.name = user_data[:name]
975
+ scene_child.type = 'Mesh'
976
+ scene_child.geometry = geometry[:uuid]
977
+ scene_child.material = material[:uuid]
978
+ scene_child.matrix = identity_matrix
979
+ scene_child.userData = user_data
980
+ object[:children] << scene_child.to_h
981
+ end
982
+ end
983
+
984
+ # loop over all interior partition surfaces
985
+ model.getInteriorPartitionSurfaces.each do |surface|
986
+ geometries, user_datas = make_interior_partition_geometries(surface)
987
+ geometries.each_index do |i|
988
+ geometry = geometries[i]
989
+ user_data = user_datas[i]
990
+
991
+ material = interior_partition_surface_material
992
+
993
+ all_geometries << geometry
994
+
995
+ scene_child = SceneChild.new
996
+ scene_child.uuid = format_uuid(OpenStudio.createUUID)
997
+ scene_child.name = user_data[:name]
998
+ scene_child.type = 'Mesh'
999
+ scene_child.geometry = geometry[:uuid]
1000
+ scene_child.material = material[:uuid]
1001
+ scene_child.matrix = identity_matrix
1002
+ scene_child.userData = user_data
1003
+ object[:children] << scene_child.to_h
1004
+ end
1005
+ end
1006
+
1007
+ # light = AmbientLight.new
1008
+ # light.uuid = "#{format_uuid(OpenStudio::createUUID)}"
1009
+ # light.type = "AmbientLight"
1010
+ # light.color = "0xFFFFFF".hex
1011
+ # light.matrix = identity_matrix
1012
+ # object[:children] << light.to_h
1013
+
1014
+ scene = Scene.new
1015
+ scene.geometries = all_geometries
1016
+ scene.materials = materials
1017
+ scene.object = object
1018
+
1019
+ return scene
1020
+ end
1021
+ end