osut 0.2.8 → 0.3.0
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/.gitignore +2 -0
- data/lib/osut/utils.rb +590 -2
- data/lib/osut/version.rb +1 -1
- metadata +3 -3
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA256:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: 2491bf7ed2ed4277ccfe4087a661660e720bd7f417b1e51f3c1f81715e53193f
|
4
|
+
data.tar.gz: 7c07191bf8cb725fdb9a04e15269aa6431fd7aa66955b9b99b2c1d61929033c6
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: ed0ad6091cf8bd858a1b3de7c17b4e13fd21816871c1dc8bf539d4b7b03f232dfa1621d3e6a839eebde9a779d7562e9c56e04bf10a52c417d884ebba7128e9c0
|
7
|
+
data.tar.gz: 5be1c0c83a39becabf70d6202916b4012ca1923b2102084515dfccae765e9fffc5e4db27787523a1a7694a852e15029176216d1fff255e0653baba97c399eacd
|
data/.gitignore
CHANGED
data/lib/osut/utils.rb
CHANGED
@@ -41,6 +41,8 @@ module OSut
|
|
41
41
|
ERR = OSut::ERROR # flag invalid .osm inputs (then exit via 'return')
|
42
42
|
FTL = OSut::FATAL # not currently used in OSut
|
43
43
|
NS = "nameString" # OpenStudio IdfObject nameString method
|
44
|
+
HEAD = 2.032 # standard 80" door
|
45
|
+
SILL = 0.762 # standard 30" window sill
|
44
46
|
|
45
47
|
# This first set of utilities (~750 lines) help distinguishing spaces that
|
46
48
|
# are directly vs indirectly CONDITIONED, vs SEMI-HEATED. The solution here
|
@@ -1447,6 +1449,9 @@ module OSut
|
|
1447
1449
|
area = area.get
|
1448
1450
|
return false if area < TOL
|
1449
1451
|
|
1452
|
+
delta = (area - area1 - area2).abs
|
1453
|
+
return false if delta < TOL
|
1454
|
+
|
1450
1455
|
true
|
1451
1456
|
end
|
1452
1457
|
|
@@ -1460,7 +1465,7 @@ module OSut
|
|
1460
1465
|
# @return [OpenStudio::Point3dVector] offset points if successful
|
1461
1466
|
# @return [OpenStudio::Point3dVector] original points if invalid input
|
1462
1467
|
def offset(p1 = [], w = 0, v = 0)
|
1463
|
-
mth = "
|
1468
|
+
mth = "OSut::#{__callee__}"
|
1464
1469
|
cl = OpenStudio::Point3d
|
1465
1470
|
vrsn = OpenStudio.openStudioVersion.split(".").map(&:to_i).join.to_i
|
1466
1471
|
|
@@ -1499,7 +1504,7 @@ module OSut
|
|
1499
1504
|
|
1500
1505
|
pz = OpenStudio::Point3dVector.new
|
1501
1506
|
offset.each { |o| pz << OpenStudio::Point3d.new(o.x, o.y, o.z ) }
|
1502
|
-
|
1507
|
+
|
1503
1508
|
return pz
|
1504
1509
|
else # brute force approach
|
1505
1510
|
pz = {}
|
@@ -1684,6 +1689,589 @@ module OSut
|
|
1684
1689
|
end
|
1685
1690
|
end
|
1686
1691
|
|
1692
|
+
##
|
1693
|
+
# Validate whether an OpenStudio planar surface is safe to process.
|
1694
|
+
#
|
1695
|
+
# @param s [OpenStudio::Model::PlanarSurface] a surface
|
1696
|
+
#
|
1697
|
+
# @return [Bool] true if valid surface
|
1698
|
+
def surface_valid?(s = nil)
|
1699
|
+
mth = "OSut::#{__callee__}"
|
1700
|
+
cl = OpenStudio::Model::PlanarSurface
|
1701
|
+
|
1702
|
+
return mismatch("surface", s, cl, mth, DBG, false) unless s.is_a?(cl)
|
1703
|
+
|
1704
|
+
id = s.nameString
|
1705
|
+
size = s.vertices.size
|
1706
|
+
last = size - 1
|
1707
|
+
|
1708
|
+
log(ERR, "#{id} #{size} vertices? need +3 (#{mth})") unless size > 2
|
1709
|
+
return false unless size > 2
|
1710
|
+
|
1711
|
+
[0, last].each do |i|
|
1712
|
+
v1 = s.vertices[i]
|
1713
|
+
v2 = s.vertices[i + 1] unless i == last
|
1714
|
+
v2 = s.vertices.first if i == last
|
1715
|
+
vec = v2 - v1
|
1716
|
+
bad = vec.length < TOL
|
1717
|
+
|
1718
|
+
# As is, this comparison also catches collinear vertices (< 10mm apart)
|
1719
|
+
# along an edge. Should avoid red-flagging such cases. TO DO.
|
1720
|
+
log(ERR, "#{id}: < #{TOL}m (#{mth})") if bad
|
1721
|
+
return false if bad
|
1722
|
+
end
|
1723
|
+
|
1724
|
+
# Add as many extra tests as needed ...
|
1725
|
+
true
|
1726
|
+
end
|
1727
|
+
|
1728
|
+
##
|
1729
|
+
# Add sub surfaces (e.g. windows, doors, skylights) to surface.
|
1730
|
+
#
|
1731
|
+
# @param model [OpenStudio::Model::Model] a model
|
1732
|
+
# @param s [OpenStudio::Model::Surface] a model surface
|
1733
|
+
# @param subs [Array] requested sub surface attributes
|
1734
|
+
# @param clear [Bool] remove current sub surfaces if true
|
1735
|
+
# @param bfr [Double] safety buffer (m), when ~aligned along other edges
|
1736
|
+
#
|
1737
|
+
# @return [Bool] true if successful (check for logged messages if failures)
|
1738
|
+
def addSubs(model = nil, s = nil, subs = [], clear = false, bfr = 0.005)
|
1739
|
+
mth = "OSut::#{__callee__}"
|
1740
|
+
v = OpenStudio.openStudioVersion.split(".").join.to_i
|
1741
|
+
cl1 = OpenStudio::Model::Model
|
1742
|
+
cl2 = OpenStudio::Model::Surface
|
1743
|
+
cl3 = Array
|
1744
|
+
cl4 = Hash
|
1745
|
+
cl5 = Numeric
|
1746
|
+
min = 0.050 # minimum ratio value ( 5%)
|
1747
|
+
max = 0.950 # maximum ratio value (95%)
|
1748
|
+
no = false
|
1749
|
+
|
1750
|
+
# --- --- --- --- --- --- --- --- --- --- --- --- --- --- --- --- --- --- #
|
1751
|
+
# Exit if mismatched or invalid argument classes.
|
1752
|
+
return mismatch("model", model, cl1, mth, DBG, no) unless model.is_a?(cl1)
|
1753
|
+
return mismatch("surface", s, cl2, mth, DBG, no) unless s.is_a?(cl2)
|
1754
|
+
return mismatch("subs", subs, cl3, mth, DBG, no) unless subs.is_a?(cl3)
|
1755
|
+
return no unless surface_valid?(s)
|
1756
|
+
|
1757
|
+
# --- --- --- --- --- --- --- --- --- --- --- --- --- --- --- --- --- --- #
|
1758
|
+
# Clear existing sub surfaces if requested.
|
1759
|
+
nom = s.nameString
|
1760
|
+
|
1761
|
+
unless clear == true || clear == false
|
1762
|
+
log(WRN, "#{nom}: Keeping existing sub surfaces (#{mth})")
|
1763
|
+
clear = false
|
1764
|
+
end
|
1765
|
+
|
1766
|
+
s.subSurfaces.map(&:remove) if clear
|
1767
|
+
|
1768
|
+
# --- --- --- --- --- --- --- --- --- --- --- --- --- --- --- --- --- --- #
|
1769
|
+
# Allowable sub surface types ... & Frame&Divider enabled
|
1770
|
+
# - "FixedWindow" | true
|
1771
|
+
# - "OperableWindow" | true
|
1772
|
+
# - "Door" | false
|
1773
|
+
# - "GlassDoor" | true
|
1774
|
+
# - "OverheadDoor" | false
|
1775
|
+
# - "Skylight" | false if v < 321
|
1776
|
+
# - "TubularDaylightDome" | false
|
1777
|
+
# - "TubularDaylightDiffuser" | false
|
1778
|
+
type = "FixedWindow"
|
1779
|
+
types = OpenStudio::Model::SubSurface.validSubSurfaceTypeValues
|
1780
|
+
stype = s.surfaceType # Wall, RoofCeiling or Floor
|
1781
|
+
|
1782
|
+
# --- --- --- --- --- --- --- --- --- --- --- --- --- --- --- --- --- --- #
|
1783
|
+
# Fetch transform, as if host surface vertices were to "align", i.e.:
|
1784
|
+
# - rotated/tilted ... then flattened along XY plane
|
1785
|
+
# - all Z-axis coordinates ~= 0
|
1786
|
+
# - vertices with the lowest X-axis values are "aligned" along X-axis (0)
|
1787
|
+
# - vertices with the lowest Z-axis values are "aligned" along Y-axis (0)
|
1788
|
+
# - Z-axis values are represented as Y-axis values
|
1789
|
+
tr = OpenStudio::Transformation.alignFace(s.vertices)
|
1790
|
+
|
1791
|
+
# Aligned vertices of host surface, and fetch attributes.
|
1792
|
+
aligned = tr.inverse * s.vertices
|
1793
|
+
max_x = aligned.max_by(&:x).x
|
1794
|
+
max_y = aligned.max_by(&:y).y
|
1795
|
+
mid_x = max_x / 2
|
1796
|
+
mid_y = max_y / 2
|
1797
|
+
|
1798
|
+
# --- --- --- --- --- --- --- --- --- --- --- --- --- --- --- --- --- --- #
|
1799
|
+
# Assign default values to certain sub keys (if missing), +more validation.
|
1800
|
+
subs.each_with_index do |sub, index|
|
1801
|
+
return mismatch("sub", sub, cl4, mth, DBG, no) unless sub.is_a?(cl4)
|
1802
|
+
|
1803
|
+
# Required key:value pairs (either set by the user or defaulted).
|
1804
|
+
sub[:id ] = "" unless sub.key?(:id ) # "Window 007"
|
1805
|
+
sub[:type ] = type unless sub.key?(:type ) # "FixedWindow"
|
1806
|
+
sub[:count ] = 1 unless sub.key?(:count ) # for an array
|
1807
|
+
sub[:multiplier] = 1 unless sub.key?(:multiplier)
|
1808
|
+
sub[:frame ] = nil unless sub.key?(:frame ) # frame/divider
|
1809
|
+
sub[:assembly ] = nil unless sub.key?(:assembly ) # construction
|
1810
|
+
|
1811
|
+
# Optional key:value pairs.
|
1812
|
+
# sub[:ratio ] # e.g. %FWR
|
1813
|
+
# sub[:head ] # e.g. std 80" door + frame/buffers (+ m)
|
1814
|
+
# sub[:sill ] # e.g. std 30" sill + frame/buffers (+ m)
|
1815
|
+
# sub[:height ] # any sub surface height, below "head" (+ m)
|
1816
|
+
# sub[:width ] # e.g. 1.200 m
|
1817
|
+
# sub[:offset ] # if array (+ m)
|
1818
|
+
# sub[:centreline] # left or right of base surface centreline (+/- m)
|
1819
|
+
# sub[:r_buffer ] # buffer between sub/array and right-side corner (+ m)
|
1820
|
+
# sub[:l_buffer ] # buffer between sub/array and left-side corner (+ m)
|
1821
|
+
|
1822
|
+
sub[:id] = "#{nom}|#{index}" if sub[:id].empty?
|
1823
|
+
id = sub[:id]
|
1824
|
+
|
1825
|
+
# If sub surface type is invalid, log/reset. Additional corrections may
|
1826
|
+
# be enabled once a sub surface is actually instantiated.
|
1827
|
+
unless types.include?(sub[:type])
|
1828
|
+
log(WRN, "Reset invalid '#{id}' type to '#{type}' (#{mth})")
|
1829
|
+
sub[:type] = type
|
1830
|
+
end
|
1831
|
+
|
1832
|
+
# Log/ignore (optional) frame & divider object.
|
1833
|
+
unless sub[:frame].nil?
|
1834
|
+
if sub[:frame].respond_to?(:frameWidth)
|
1835
|
+
sub[:frame] = nil if sub[:type] == "Skylight" && v < 321
|
1836
|
+
sub[:frame] = nil if sub[:type] == "Door"
|
1837
|
+
sub[:frame] = nil if sub[:type] == "OverheadDoor"
|
1838
|
+
sub[:frame] = nil if sub[:type] == "TubularDaylightDome"
|
1839
|
+
sub[:frame] = nil if sub[:type] == "TubularDaylightDiffuser"
|
1840
|
+
log(WRN, "Skip '#{id}' FrameDivider (#{mth})") if sub[:frame].nil?
|
1841
|
+
else
|
1842
|
+
sub[:frame] = nil
|
1843
|
+
log(WRN, "Skip '#{id}' invalid FrameDivider object (#{mth})")
|
1844
|
+
end
|
1845
|
+
end
|
1846
|
+
|
1847
|
+
# The (optional) "assembly" must reference a valid OpenStudio
|
1848
|
+
# construction base, to explicitly assign to each instantiated sub
|
1849
|
+
# surface. If invalid, log/reset/ignore. Additional checks are later
|
1850
|
+
# activated once a sub surface is actually instantiated.
|
1851
|
+
unless sub[:assembly].nil?
|
1852
|
+
unless sub[:assembly].respond_to?(:isFenestration)
|
1853
|
+
log(WRN, "Skip invalid '#{id}' construction (#{mth})")
|
1854
|
+
sub[:assembly] = nil
|
1855
|
+
end
|
1856
|
+
end
|
1857
|
+
|
1858
|
+
# Log/reset negative numerical values. Set ~0 values to 0.
|
1859
|
+
sub.each do |key, value|
|
1860
|
+
next if key == :id
|
1861
|
+
next if key == :type
|
1862
|
+
next if key == :frame
|
1863
|
+
next if key == :assembly
|
1864
|
+
|
1865
|
+
return mismatch(key, value, cl5, mth, DBG, no) unless value.is_a?(cl5)
|
1866
|
+
next if key == :centreline
|
1867
|
+
|
1868
|
+
negative(key, mth, WRN) if value < 0
|
1869
|
+
value = 0.0 if value.abs < TOL
|
1870
|
+
end
|
1871
|
+
end
|
1872
|
+
|
1873
|
+
# --- --- --- --- --- --- --- --- --- --- --- --- --- --- --- --- --- --- #
|
1874
|
+
# Log/reset (or abandon) conflicting user-set geometry key:value pairs:
|
1875
|
+
# :head e.g. std 80" door + frame/buffers (+ m)
|
1876
|
+
# :sill e.g. std 30" sill + frame/buffers (+ m)
|
1877
|
+
# :height any sub surface height, below "head" (+ m)
|
1878
|
+
# :width e.g. 1.200 m
|
1879
|
+
# :offset if array (+ m)
|
1880
|
+
# :centreline left or right of base surface centreline (+/- m)
|
1881
|
+
# :r_buffer buffer between sub/array and right-side corner (+ m)
|
1882
|
+
# :l_buffer buffer between sub/array and left-side corner (+ m)
|
1883
|
+
#
|
1884
|
+
# If successful, this will generate sub surfaces and add them to the model.
|
1885
|
+
subs.each do |sub|
|
1886
|
+
# Set-up unique sub parameters:
|
1887
|
+
# - Frame & Divider "width"
|
1888
|
+
# - minimum "clear glazing" limits
|
1889
|
+
# - buffers, etc.
|
1890
|
+
id = sub[:id]
|
1891
|
+
frame = 0
|
1892
|
+
frame = sub[:frame].frameWidth unless sub[:frame].nil?
|
1893
|
+
frames = 2 * frame
|
1894
|
+
buffer = frame + bfr
|
1895
|
+
buffers = 2 * buffer
|
1896
|
+
dim = 0.200 unless (3 * frame) > 0.200
|
1897
|
+
dim = 3 * frame if (3 * frame) > 0.200
|
1898
|
+
glass = dim - frames
|
1899
|
+
min_sill = buffer
|
1900
|
+
min_head = buffers + glass
|
1901
|
+
max_head = max_y - buffer
|
1902
|
+
max_sill = max_head - (buffers + glass)
|
1903
|
+
min_ljamb = buffer
|
1904
|
+
max_ljamb = max_x - (buffers + glass)
|
1905
|
+
min_rjamb = buffers + glass
|
1906
|
+
max_rjamb = max_x - buffer
|
1907
|
+
max_height = max_y - buffers
|
1908
|
+
max_width = max_x - buffers
|
1909
|
+
|
1910
|
+
# Default sub surface "head" & "sill" height (unless user-specified).
|
1911
|
+
typ_head = HEAD # standard 80" door
|
1912
|
+
typ_sill = SILL # standard 30" window sill
|
1913
|
+
|
1914
|
+
if sub.key?(:ratio)
|
1915
|
+
typ_head = mid_y * (1 + sub[:ratio]) if sub[:ratio] > 0.75
|
1916
|
+
typ_head = mid_y * (1 + sub[:ratio]) unless stype.downcase == "wall"
|
1917
|
+
typ_sill = mid_y * (1 - sub[:ratio]) if sub[:ratio] > 0.75
|
1918
|
+
typ_sill = mid_y * (1 - sub[:ratio]) unless stype.downcase == "wall"
|
1919
|
+
end
|
1920
|
+
|
1921
|
+
# Log/reset "height" if beyond min/max.
|
1922
|
+
if sub.key?(:height)
|
1923
|
+
unless sub[:height].between?(glass, max_height)
|
1924
|
+
sub[:height] = glass if sub[:height] < glass
|
1925
|
+
sub[:height] = max_height if sub[:height] > max_height
|
1926
|
+
log(WRN, "Reset '#{id}' height to #{sub[:height]} m (#{mth})")
|
1927
|
+
end
|
1928
|
+
end
|
1929
|
+
|
1930
|
+
# Log/reset "head" height if beyond min/max.
|
1931
|
+
if sub.key?(:head)
|
1932
|
+
unless sub[:head].between?(min_head, max_head)
|
1933
|
+
sub[:head] = max_head if sub[:head] > max_head
|
1934
|
+
sub[:head] = min_head if sub[:head] < min_head
|
1935
|
+
log(WRN, "Reset '#{id}' head height to #{sub[:head]} m (#{mth})")
|
1936
|
+
end
|
1937
|
+
end
|
1938
|
+
|
1939
|
+
# Log/reset "sill" height if beyond min/max.
|
1940
|
+
if sub.key?(:sill)
|
1941
|
+
unless sub[:sill].between?(min_sill, max_sill)
|
1942
|
+
sub[:sill] = max_sill if sub[:sill] > max_sill
|
1943
|
+
sub[:sill] = min_sill if sub[:sill] < min_sill
|
1944
|
+
log(WRN, "Reset '#{id}' sill height to #{sub[:sill]} m (#{mth})")
|
1945
|
+
end
|
1946
|
+
end
|
1947
|
+
|
1948
|
+
# At this point, "head", "sill" and/or "height" have been tentatively
|
1949
|
+
# validated (and/or have been corrected) independently from one another.
|
1950
|
+
# Log/reset "head" & "sill" heights if conflicting.
|
1951
|
+
if sub.key?(:head) && sub.key?(:sill) && sub[:head] < sub[:sill] + glass
|
1952
|
+
sill = sub[:head] - glass
|
1953
|
+
|
1954
|
+
if sill < min_sill
|
1955
|
+
sub[:ratio ] = 0 if sub.key?(:ratio)
|
1956
|
+
sub[:count ] = 0
|
1957
|
+
sub[:multiplier] = 0
|
1958
|
+
sub[:height ] = 0 if sub.key?(:height)
|
1959
|
+
sub[:width ] = 0 if sub.key?(:width)
|
1960
|
+
log(ERR, "Skip: invalid '#{id}' head/sill combo (#{mth})")
|
1961
|
+
next
|
1962
|
+
else
|
1963
|
+
sub[:sill] = sill
|
1964
|
+
log(WRN, "(Re)set '#{id}' sill height to #{sub[:sill]} m (#{mth})")
|
1965
|
+
end
|
1966
|
+
end
|
1967
|
+
|
1968
|
+
# Attempt to reconcile "head", "sill" and/or "height". If successful,
|
1969
|
+
# all 3x parameters are set (if missing), or reset if invalid.
|
1970
|
+
if sub.key?(:head) && sub.key?(:sill)
|
1971
|
+
height = sub[:head] - sub[:sill]
|
1972
|
+
|
1973
|
+
if sub.key?(:height) && (sub[:height] - height).abs > TOL
|
1974
|
+
log(WRN, "(Re)set '#{id}' height to #{height} m (#{mth})")
|
1975
|
+
end
|
1976
|
+
|
1977
|
+
sub[:height] = height
|
1978
|
+
elsif sub.key?(:head) # no "sill"
|
1979
|
+
if sub.key?(:height)
|
1980
|
+
sill = sub[:head] - sub[:height]
|
1981
|
+
|
1982
|
+
if sill < min_sill
|
1983
|
+
sill = min_sill
|
1984
|
+
height = sub[:head] - sill
|
1985
|
+
|
1986
|
+
if height < glass
|
1987
|
+
sub[:ratio ] = 0 if sub.key?(:ratio)
|
1988
|
+
sub[:count ] = 0
|
1989
|
+
sub[:multiplier] = 0
|
1990
|
+
sub[:height ] = 0 if sub.key?(:height)
|
1991
|
+
sub[:width ] = 0 if sub.key?(:width)
|
1992
|
+
log(ERR, "Skip: invalid '#{id}' head/height combo (#{mth})")
|
1993
|
+
next
|
1994
|
+
else
|
1995
|
+
sub[:sill ] = sill
|
1996
|
+
sub[:height] = height
|
1997
|
+
log(WRN, "(Re)set '#{id}' height to #{sub[:height]} m (#{mth})")
|
1998
|
+
end
|
1999
|
+
else
|
2000
|
+
sub[:sill] = sill
|
2001
|
+
end
|
2002
|
+
else
|
2003
|
+
sub[:sill ] = typ_sill
|
2004
|
+
sub[:height] = sub[:head] - sub[:sill]
|
2005
|
+
end
|
2006
|
+
elsif sub.key?(:sill) # no "head"
|
2007
|
+
if sub.key?(:height)
|
2008
|
+
head = sub[:sill] + sub[:height]
|
2009
|
+
|
2010
|
+
if head > max_head
|
2011
|
+
head = max_head
|
2012
|
+
height = head - sub[:sill]
|
2013
|
+
|
2014
|
+
if height < glass
|
2015
|
+
sub[:ratio ] = 0 if sub.key?(:ratio)
|
2016
|
+
sub[:count ] = 0
|
2017
|
+
sub[:multiplier] = 0
|
2018
|
+
sub[:height ] = 0 if sub.key?(:height)
|
2019
|
+
sub[:width ] = 0 if sub.key?(:width)
|
2020
|
+
log(ERR, "Skip: invalid '#{id}' sill/height combo (#{mth})")
|
2021
|
+
next
|
2022
|
+
else
|
2023
|
+
sub[:head ] = head
|
2024
|
+
sub[:height] = height
|
2025
|
+
log(WRN, "(Re)set '#{id}' height to #{sub[:height]} m (#{mth})")
|
2026
|
+
end
|
2027
|
+
else
|
2028
|
+
sub[:head] = head
|
2029
|
+
end
|
2030
|
+
else
|
2031
|
+
sub[:head ] = typ_head
|
2032
|
+
sub[:height] = sub[:head] - sub[:sill]
|
2033
|
+
end
|
2034
|
+
elsif sub.key?(:height) # neither "head" nor "sill"
|
2035
|
+
head = typ_head
|
2036
|
+
sill = head - sub[:height]
|
2037
|
+
|
2038
|
+
if sill < min_sill
|
2039
|
+
sill = min_sill
|
2040
|
+
head = sill + sub[:height]
|
2041
|
+
end
|
2042
|
+
|
2043
|
+
sub[:head] = head
|
2044
|
+
sub[:sill] = sill
|
2045
|
+
else
|
2046
|
+
sub[:head ] = typ_head
|
2047
|
+
sub[:sill ] = typ_sill
|
2048
|
+
sub[:height] = sub[:head] - sub[:sill]
|
2049
|
+
end
|
2050
|
+
|
2051
|
+
# Log/reset "width" if beyond min/max.
|
2052
|
+
if sub.key?(:width)
|
2053
|
+
unless sub[:width].between?(glass, max_width)
|
2054
|
+
sub[:width] = glass if sub[:width] < glass
|
2055
|
+
sub[:width] = max_width if sub[:width] > max_width
|
2056
|
+
log(WRN, "Reset '#{id}' width to #{sub[:width]} m (#{mth})")
|
2057
|
+
end
|
2058
|
+
end
|
2059
|
+
|
2060
|
+
# Log/reset "count" if < 1.
|
2061
|
+
if sub.key?(:count)
|
2062
|
+
if sub[:count] < 1
|
2063
|
+
sub[:count] = 1
|
2064
|
+
log(WRN, "Reset '#{id}' count to #{sub[:count]} (#{mth})")
|
2065
|
+
end
|
2066
|
+
end
|
2067
|
+
|
2068
|
+
sub[:count] = 1 unless sub.key?(:count)
|
2069
|
+
|
2070
|
+
# Log/reset if left-sided buffer under min jamb position.
|
2071
|
+
if sub.key?(:l_buffer)
|
2072
|
+
if sub[:l_buffer] < min_ljamb
|
2073
|
+
sub[:l_buffer] = min_ljamb
|
2074
|
+
log(WRN, "Reset '#{id}' left buffer to #{sub[:l_buffer]} m (#{mth})")
|
2075
|
+
end
|
2076
|
+
end
|
2077
|
+
|
2078
|
+
# Log/reset if right-sided buffer beyond max jamb position.
|
2079
|
+
if sub.key?(:r_buffer)
|
2080
|
+
if sub[:r_buffer] > max_rjamb
|
2081
|
+
sub[:r_buffer] = min_rjamb
|
2082
|
+
log(WRN, "Reset '#{id}' right buffer to #{sub[:r_buffer]} m (#{mth})")
|
2083
|
+
end
|
2084
|
+
end
|
2085
|
+
|
2086
|
+
centre = mid_x
|
2087
|
+
centre += sub[:centreline] if sub.key?(:centreline)
|
2088
|
+
n = sub[:count ]
|
2089
|
+
h = sub[:height ] + frames
|
2090
|
+
w = 0 # overall width of sub(s) bounding box (to calculate)
|
2091
|
+
x0 = 0 # left-side X-axis coordinate of sub(s) bounding box
|
2092
|
+
xf = 0 # right-side X-axis coordinate of sub(s) bounding box
|
2093
|
+
|
2094
|
+
# Log/reset "offset", if conflicting vs "width".
|
2095
|
+
if sub.key?(:ratio)
|
2096
|
+
if sub[:ratio] < TOL
|
2097
|
+
sub[:ratio ] = 0
|
2098
|
+
sub[:count ] = 0
|
2099
|
+
sub[:multiplier] = 0
|
2100
|
+
sub[:height ] = 0 if sub.key?(:height)
|
2101
|
+
sub[:width ] = 0 if sub.key?(:width)
|
2102
|
+
log(ERR, "Skip: '#{id}' ratio ~0 (#{mth})")
|
2103
|
+
next
|
2104
|
+
end
|
2105
|
+
|
2106
|
+
# Log/reset if "ratio" beyond min/max?
|
2107
|
+
unless sub[:ratio].between?(min, max)
|
2108
|
+
sub[:ratio] = min if sub[:ratio] < min
|
2109
|
+
sub[:ratio] = max if sub[:ratio] > max
|
2110
|
+
log(WRN, "Reset ratio (min/max) to #{sub[:ratio]} (#{mth})")
|
2111
|
+
end
|
2112
|
+
|
2113
|
+
# Log/reset "count" unless 1.
|
2114
|
+
unless sub[:count] == 1
|
2115
|
+
sub[:count] = 1
|
2116
|
+
log(WRN, "Reset count (ratio) to 1 (#{mth})")
|
2117
|
+
end
|
2118
|
+
|
2119
|
+
area = s.grossArea * sub[:ratio] # sub m2, including (optional) frames
|
2120
|
+
w = area / h
|
2121
|
+
width = w - frames
|
2122
|
+
x0 = centre - w/2
|
2123
|
+
xf = centre + w/2
|
2124
|
+
|
2125
|
+
if sub.key?(:l_buffer)
|
2126
|
+
if sub.key?(:centreline)
|
2127
|
+
log(WRN, "Skip #{id} left buffer (vs centreline) (#{mth})")
|
2128
|
+
else
|
2129
|
+
x0 = sub[:l_buffer] - frame
|
2130
|
+
xf = x0 + w
|
2131
|
+
centre = x0 + w/2
|
2132
|
+
end
|
2133
|
+
elsif sub.key?(:r_buffer)
|
2134
|
+
if sub.key?(:centreline)
|
2135
|
+
log(WRN, "Skip #{id} right buffer (vs centreline) (#{mth})")
|
2136
|
+
else
|
2137
|
+
xf = max_x - sub[:r_buffer] + frame
|
2138
|
+
x0 = xf - w
|
2139
|
+
centre = x0 + w/2
|
2140
|
+
end
|
2141
|
+
end
|
2142
|
+
|
2143
|
+
# Too wide?
|
2144
|
+
if x0 < min_ljamb || xf > max_rjamb
|
2145
|
+
sub[:ratio ] = 0 if sub.key?(:ratio)
|
2146
|
+
sub[:count ] = 0
|
2147
|
+
sub[:multiplier] = 0
|
2148
|
+
sub[:height ] = 0 if sub.key?(:height)
|
2149
|
+
sub[:width ] = 0 if sub.key?(:width)
|
2150
|
+
log(ERR, "Skip: invalid (ratio) width/centreline (#{mth})")
|
2151
|
+
next
|
2152
|
+
end
|
2153
|
+
|
2154
|
+
if sub.key?(:width) && (sub[:width] - width).abs > TOL
|
2155
|
+
sub[:width] = width
|
2156
|
+
log(WRN, "Reset width (ratio) to #{sub[:width]} (#{mth})")
|
2157
|
+
end
|
2158
|
+
|
2159
|
+
sub[:width] = width unless sub.key?(:width)
|
2160
|
+
else
|
2161
|
+
unless sub.key?(:width)
|
2162
|
+
sub[:ratio ] = 0 if sub.key?(:ratio)
|
2163
|
+
sub[:count ] = 0
|
2164
|
+
sub[:multiplier] = 0
|
2165
|
+
sub[:height ] = 0 if sub.key?(:height)
|
2166
|
+
sub[:width ] = 0 if sub.key?(:width)
|
2167
|
+
log(ERR, "Skip: missing '#{id}' width (#{mth})")
|
2168
|
+
next
|
2169
|
+
end
|
2170
|
+
|
2171
|
+
width = sub[:width] + frames
|
2172
|
+
gap = (max_x - n * width) / (n + 1)
|
2173
|
+
gap = sub[:offset] - width if sub.key?(:offset)
|
2174
|
+
gap = 0 if gap < bfr
|
2175
|
+
offset = gap + width
|
2176
|
+
|
2177
|
+
if sub.key?(:offset) && (offset - sub[:offset]).abs > TOL
|
2178
|
+
sub[:offset] = offset
|
2179
|
+
log(WRN, "Reset sub offset to #{sub[:offset]} m (#{mth})")
|
2180
|
+
end
|
2181
|
+
|
2182
|
+
sub[:offset] = offset unless sub.key?(:offset)
|
2183
|
+
|
2184
|
+
# Overall width (including frames) of bounding box around array.
|
2185
|
+
w = n * width + (n - 1) * gap
|
2186
|
+
x0 = centre - w/2
|
2187
|
+
xf = centre + w/2
|
2188
|
+
|
2189
|
+
if sub.key?(:l_buffer)
|
2190
|
+
if sub.key?(:centreline)
|
2191
|
+
log(WRN, "Skip #{id} left buffer (vs centreline) (#{mth})")
|
2192
|
+
else
|
2193
|
+
x0 = sub[:l_buffer] - frame
|
2194
|
+
xf = x0 + w
|
2195
|
+
centre = x0 + w/2
|
2196
|
+
end
|
2197
|
+
elsif sub.key?(:r_buffer)
|
2198
|
+
if sub.key?(:centreline)
|
2199
|
+
log(WRN, "Skip #{id} right buffer (vs centreline) (#{mth})")
|
2200
|
+
else
|
2201
|
+
xf = max_x - sub[:r_buffer] + frame
|
2202
|
+
x0 = xf - w
|
2203
|
+
centre = x0 + w/2
|
2204
|
+
end
|
2205
|
+
end
|
2206
|
+
|
2207
|
+
# Too wide?
|
2208
|
+
if x0 < bfr || xf > max_x - bfr
|
2209
|
+
sub[:ratio ] = 0 if sub.key?(:ratio)
|
2210
|
+
sub[:count ] = 0
|
2211
|
+
sub[:multiplier] = 0
|
2212
|
+
sub[:height ] = 0 if sub.key?(:height)
|
2213
|
+
sub[:width ] = 0 if sub.key?(:width)
|
2214
|
+
log(ERR, "Skip: invalid array width/centreline (#{mth})")
|
2215
|
+
next
|
2216
|
+
end
|
2217
|
+
end
|
2218
|
+
|
2219
|
+
# Initialize left-side X-axis coordinate of only/first sub.
|
2220
|
+
pos = x0 + frame
|
2221
|
+
|
2222
|
+
# Generate sub(s).
|
2223
|
+
sub[:count].times do |i|
|
2224
|
+
name = "#{id}:#{i}"
|
2225
|
+
fr = 0
|
2226
|
+
fr = sub[:frame].frameWidth if sub[:frame]
|
2227
|
+
|
2228
|
+
vec = OpenStudio::Point3dVector.new
|
2229
|
+
vec << OpenStudio::Point3d.new(pos, sub[:head], 0)
|
2230
|
+
vec << OpenStudio::Point3d.new(pos, sub[:sill], 0)
|
2231
|
+
vec << OpenStudio::Point3d.new(pos + sub[:width], sub[:sill], 0)
|
2232
|
+
vec << OpenStudio::Point3d.new(pos + sub[:width], sub[:head], 0)
|
2233
|
+
vec = tr * vec
|
2234
|
+
|
2235
|
+
# Log/skip if conflict between individual sub and base surface.
|
2236
|
+
vc = vec
|
2237
|
+
vc = offset(vc, fr, 300) if fr > 0
|
2238
|
+
ok = fits?(vc, s.vertices, name, nom)
|
2239
|
+
log(ERR, "Skip '#{name}': won't fit in '#{nom}' (#{mth})") unless ok
|
2240
|
+
break unless ok
|
2241
|
+
|
2242
|
+
# Log/skip if conflicts with existing subs (even if same array).
|
2243
|
+
s.subSurfaces.each do |sb|
|
2244
|
+
nome = sb.nameString
|
2245
|
+
fd = sb.windowPropertyFrameAndDivider
|
2246
|
+
fr = 0 if fd.empty?
|
2247
|
+
fr = fd.get.frameWidth unless fd.empty?
|
2248
|
+
vk = sb.vertices
|
2249
|
+
vk = offset(vk, fr, 300) if fr > 0
|
2250
|
+
oops = overlaps?(vc, vk, name, nome)
|
2251
|
+
log(ERR, "Skip '#{name}': overlaps '#{nome}' (#{mth})") if oops
|
2252
|
+
ok = false if oops
|
2253
|
+
break if oops
|
2254
|
+
end
|
2255
|
+
|
2256
|
+
break unless ok
|
2257
|
+
|
2258
|
+
sb = OpenStudio::Model::SubSurface.new(vec, model)
|
2259
|
+
sb.setName(name)
|
2260
|
+
sb.setSubSurfaceType(sub[:type])
|
2261
|
+
sb.setConstruction(sub[:assembly]) if sub[:assembly]
|
2262
|
+
ok = sb.allowWindowPropertyFrameAndDivider
|
2263
|
+
sb.setWindowPropertyFrameAndDivider(sub[:frame]) if sub[:frame] && ok
|
2264
|
+
sb.setMultiplier(sub[:multiplier]) if sub[:multiplier] > 1
|
2265
|
+
sb.setSurface(s)
|
2266
|
+
|
2267
|
+
# Reset "pos" if array.
|
2268
|
+
pos += sub[:offset] if sub.key?(:offset)
|
2269
|
+
end
|
2270
|
+
end
|
2271
|
+
|
2272
|
+
true
|
2273
|
+
end
|
2274
|
+
|
1687
2275
|
##
|
1688
2276
|
# Callback when other modules extend OSlg
|
1689
2277
|
#
|
data/lib/osut/version.rb
CHANGED
metadata
CHANGED
@@ -1,14 +1,14 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: osut
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 0.
|
4
|
+
version: 0.3.0
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- Denis Bourgeois
|
8
8
|
autorequire:
|
9
9
|
bindir: exe
|
10
10
|
cert_chain: []
|
11
|
-
date: 2023-
|
11
|
+
date: 2023-05-15 00:00:00.000000000 Z
|
12
12
|
dependencies:
|
13
13
|
- !ruby/object:Gem::Dependency
|
14
14
|
name: oslg
|
@@ -90,7 +90,7 @@ licenses:
|
|
90
90
|
- BSD-3-Clause
|
91
91
|
metadata:
|
92
92
|
homepage_uri: https://github.com/rd2/osut
|
93
|
-
source_code_uri: https://github.com/rd2/osut/tree/v0.
|
93
|
+
source_code_uri: https://github.com/rd2/osut/tree/v0.3.0
|
94
94
|
bug_tracker_uri: https://github.com/rd2/osut/issues
|
95
95
|
post_install_message:
|
96
96
|
rdoc_options: []
|