osut 0.2.3 → 0.2.7

Sign up to get free protection for your applications and to get access to all the features.
Files changed (4) hide show
  1. checksums.yaml +4 -4
  2. data/lib/osut/utils.rb +337 -81
  3. data/lib/osut/version.rb +1 -1
  4. metadata +3 -3
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: be4500de4b7207ef0f1bb50342c4f63e12714bf22a17779f928b51adba0b862f
4
- data.tar.gz: 3b4aa51dff5771c31fe5edc89d1326555447d2eaa7844b4944ff43b84b01cad2
3
+ metadata.gz: 6bbbc6887949cfd0d1aed5c460d993196fa86d1a13f366f7f852e418aa9457a0
4
+ data.tar.gz: b38eaa81d84a79655adb10a06bb1fa299ffff60051d187f2b36bad38fa4d78e0
5
5
  SHA512:
6
- metadata.gz: 47c84a2d136ec1b6552436cdcc555469813987485f64a2a4ddfa99788f5ee1d5b99d4b369b50766652b028f39204d3efccf7793276400e256d81492bbb85127f
7
- data.tar.gz: af28329f47ff40ac971ec3b9333c0e21cb0ef293f1c2697aeebe96d0009acba34696a1f43c9bf07828b854ae8bd110b95b295bcb849dc8e0ff3e21236e4de308
6
+ metadata.gz: 1eb2cd49987ce2a4bf7a6a9fe0ed7048bc78dce644aa88e68173861ce4e52fc2bfe59bb3c4a7b353426a1bfa30f45af102ca3e509ac91945b82673545f1a4d4d
7
+ data.tar.gz: bb8075d22d660b3f177716463baf3bc2b0e788269dc05f60086272dc5fec193b93a207f94500fa26cf8d786fdcd3810c0d3defb50eca4c39b3a05cf7c8d2412c
data/lib/osut/utils.rb CHANGED
@@ -304,19 +304,9 @@ module OSut
304
304
  end
305
305
  end
306
306
 
307
- # unless equip.to_ZoneHVACLowTemperatureRadiantElectric.empty?
308
- # equip = equip.to_ZoneHVACLowTemperatureRadiantElectric.get
309
- #
310
- # unless equip.heatingSetpointTemperatureSchedule.empty?
311
- # sched = equip.heatingSetpointTemperatureSchedule.get
312
- # end
313
- # end
314
-
315
307
  unless equip.to_ZoneHVACLowTemperatureRadiantElectric.empty?
316
308
  equip = equip.to_ZoneHVACLowTemperatureRadiantElectric.get
317
- unless equip.heatingSetpointTemperatureSchedule.empty?
318
- sched = equip.heatingSetpointTemperatureSchedule.get
319
- end
309
+ sched = equip.heatingSetpointTemperatureSchedule
320
310
  end
321
311
 
322
312
  unless equip.to_ZoneHVACLowTempRadiantConstFlow.empty?
@@ -638,7 +628,7 @@ module OSut
638
628
  mth = "OSut::#{__callee__}"
639
629
  cl = OpenStudio::Model::Model
640
630
 
641
- return mismatch("model", model, cl, mth, DBG, false) unless model.is_a?(cl)
631
+ return mismatch("model", model, cl, mth, DBG, false) unless model.is_a?(cl)
642
632
 
643
633
  model.getThermalZones.each do |zone|
644
634
  return true if minCoolScheduledSetpoint(zone)[:spt]
@@ -658,12 +648,12 @@ module OSut
658
648
  mth = "OSut::#{__callee__}"
659
649
  cl = OpenStudio::Model::Model
660
650
 
661
- return mismatch("model", model, cl, mth, DBG, false) unless model.is_a?(cl)
651
+ return mismatch("model", model, cl, mth, DBG, false) unless model.is_a?(cl)
662
652
 
663
653
  model.getThermalZones.each do |zone|
664
- next if zone.canBePlenum
665
- return true unless zone.airLoopHVACs.empty?
666
- return true if zone.isPlenum
654
+ next if zone.canBePlenum
655
+ return true unless zone.airLoopHVACs.empty?
656
+ return true if zone.isPlenum
667
657
  end
668
658
 
669
659
  false
@@ -688,7 +678,7 @@ module OSut
688
678
  # A space may be tagged as a plenum if:
689
679
  #
690
680
  # CASE A: its zone's "isPlenum" == true (SDK method) for a fully-developed
691
- # OpenStudio model (complete with HVAC air loops);
681
+ # OpenStudio model (complete with HVAC air loops); OR
692
682
  #
693
683
  # CASE B: (IN ABSENCE OF HVAC AIRLOOPS) if it's excluded from a building's
694
684
  # total floor area yet linked to a zone holding an 'inactive'
@@ -747,8 +737,8 @@ module OSut
747
737
  cl = OpenStudio::Model::Model
748
738
  limits = nil
749
739
 
750
- return mismatch("model", model, cl, mth) unless model.is_a?(cl)
751
- return invalid("availability", avl, 2, mth) unless avl.respond_to?(:to_s)
740
+ return mismatch("model", model, cl, mth) unless model.is_a?(cl)
741
+ return invalid("availability", avl, 2, mth) unless avl.respond_to?(:to_s)
752
742
 
753
743
  # Either fetch availability ScheduleTypeLimits object, or create one.
754
744
  model.getScheduleTypeLimitss.each do |l|
@@ -779,9 +769,9 @@ module OSut
779
769
  off = OpenStudio::Model::ScheduleDay.new(model, 0)
780
770
 
781
771
  # Seasonal availability start/end dates.
782
- year = model.yearDescription
783
- return empty("yearDescription", mth, ERR) if year.empty?
784
- year = year.get
772
+ year = model.yearDescription
773
+ return empty("yearDescription", mth, ERR) if year.empty?
774
+ year = year.get
785
775
  may01 = year.makeDate(OpenStudio::MonthOfYear.new("May"), 1)
786
776
  oct31 = year.makeDate(OpenStudio::MonthOfYear.new("Oct"), 31)
787
777
 
@@ -967,17 +957,17 @@ module OSut
967
957
  cl1 = OpenStudio::Model::Model
968
958
  cl2 = OpenStudio::Model::Surface
969
959
 
970
- return mismatch("model", model, cl1, mth) unless model.is_a?(cl1)
971
- return invalid("s", mth, 2) unless s.respond_to?(NS)
972
- id = s.nameString
973
- return mismatch(id, s, cl2, mth) unless s.is_a?(cl2)
960
+ return mismatch("model", model, cl1, mth) unless model.is_a?(cl1)
961
+ return invalid("s", mth, 2) unless s.respond_to?(NS)
962
+ id = s.nameString
963
+ return mismatch(id, s, cl2, mth) unless s.is_a?(cl2)
974
964
 
975
965
  ok = s.isConstructionDefaulted
976
966
  log(ERR, "'#{id}' construction not defaulted (#{mth})") unless ok
977
967
  return nil unless ok
978
- return empty("'#{id}' construction", mth, ERR) if s.construction.empty?
968
+ return empty("'#{id}' construction", mth, ERR) if s.construction.empty?
979
969
  base = s.construction.get
980
- return empty("'#{id}' space", mth, ERR) if s.space.empty?
970
+ return empty("'#{id}' space", mth, ERR) if s.space.empty?
981
971
  space = s.space.get
982
972
  type = s.surfaceType
983
973
  ground = false
@@ -991,7 +981,7 @@ module OSut
991
981
 
992
982
  unless space.defaultConstructionSet.empty?
993
983
  set = space.defaultConstructionSet.get
994
- return set if holdsConstruction?(set, base, ground, exterior, type)
984
+ return set if holdsConstruction?(set, base, ground, exterior, type)
995
985
  end
996
986
 
997
987
  unless space.spaceType.empty?
@@ -999,7 +989,7 @@ module OSut
999
989
 
1000
990
  unless spacetype.defaultConstructionSet.empty?
1001
991
  set = spacetype.defaultConstructionSet.get
1002
- return set if holdsConstruction?(set, base, ground, exterior, type)
992
+ return set if holdsConstruction?(set, base, ground, exterior, type)
1003
993
  end
1004
994
  end
1005
995
 
@@ -1008,7 +998,7 @@ module OSut
1008
998
 
1009
999
  unless story.defaultConstructionSet.empty?
1010
1000
  set = story.defaultConstructionSet.get
1011
- return set if holdsConstruction?(set, base, ground, exterior, type)
1001
+ return set if holdsConstruction?(set, base, ground, exterior, type)
1012
1002
  end
1013
1003
  end
1014
1004
 
@@ -1016,7 +1006,7 @@ module OSut
1016
1006
 
1017
1007
  unless building.defaultConstructionSet.empty?
1018
1008
  set = building.defaultConstructionSet.get
1019
- return set if holdsConstruction?(set, base, ground, exterior, type)
1009
+ return set if holdsConstruction?(set, base, ground, exterior, type)
1020
1010
  end
1021
1011
 
1022
1012
  nil
@@ -1046,15 +1036,15 @@ module OSut
1046
1036
  #
1047
1037
  # @param lc [OpenStudio::LayeredConstruction] a layered construction
1048
1038
  #
1049
- # @return [Double] total layered construction thickness
1050
- # @return [Double] 0 if invalid input
1039
+ # @return [Float] total layered construction thickness
1040
+ # @return [Float] 0 if invalid input
1051
1041
  def thickness(lc = nil)
1052
1042
  mth = "OSut::#{__callee__}"
1053
1043
  cl = OpenStudio::Model::LayeredConstruction
1054
1044
 
1055
- return invalid("lc", mth, 1, DBG, 0.0) unless lc.respond_to?(NS)
1045
+ return invalid("lc", mth, 1, DBG, 0.0) unless lc.respond_to?(NS)
1056
1046
  id = lc.nameString
1057
- return mismatch(id, lc, cl, mth, DBG, 0.0) unless lc.is_a?(cl)
1047
+ return mismatch(id, lc, cl, mth, DBG, 0.0) unless lc.is_a?(cl)
1058
1048
 
1059
1049
  ok = standardOpaqueLayers?(lc)
1060
1050
  log(ERR, "'#{id}' holds non-StandardOpaqueMaterial(s) (#{mth})") unless ok
@@ -1176,9 +1166,9 @@ module OSut
1176
1166
  res = { index: nil, type: nil, r: 0.0 }
1177
1167
  i = 0 # iterator
1178
1168
 
1179
- return invalid("lc", mth, 1, DBG, res) unless lc.respond_to?(NS)
1180
- id = lc.nameString
1181
- return mismatch(id, lc, cl1, mth, DBG, res) unless lc.is_a?(cl)
1169
+ return invalid("lc", mth, 1, DBG, res) unless lc.respond_to?(NS)
1170
+ id = lc.nameString
1171
+ return mismatch(id, lc, cl1, mth, DBG, res) unless lc.is_a?(cl)
1182
1172
 
1183
1173
  lc.layers.each do |m|
1184
1174
  unless m.to_MasslessOpaqueMaterial.empty?
@@ -1188,9 +1178,9 @@ module OSut
1188
1178
  i += 1
1189
1179
  next
1190
1180
  else
1191
- res[:r] = m.thermalResistance
1181
+ res[:r ] = m.thermalResistance
1192
1182
  res[:index] = i
1193
- res[:type] = :massless
1183
+ res[:type ] = :massless
1194
1184
  end
1195
1185
  end
1196
1186
 
@@ -1203,9 +1193,9 @@ module OSut
1203
1193
  i += 1
1204
1194
  next
1205
1195
  else
1206
- res[:r] = d / k
1196
+ res[:r ] = d / k
1207
1197
  res[:index] = i
1208
- res[:type] = :standard
1198
+ res[:type ] = :standard
1209
1199
  end
1210
1200
  end
1211
1201
 
@@ -1240,6 +1230,28 @@ module OSut
1240
1230
  res
1241
1231
  end
1242
1232
 
1233
+ ##
1234
+ # Return a scalar product of an OpenStudio Vector3d.
1235
+ #
1236
+ # @param v [OpenStudio::Vector3d] a vector
1237
+ # @param m [Float] a scalar
1238
+ #
1239
+ # @return [OpenStudio::Vector3d] modified vector
1240
+ # @return [OpenStudio::Vector3d] provided (or empty) vector if invalid input
1241
+ def scalar(v = OpenStudio::Vector3d.new(0,0,0), m = 0)
1242
+ mth = "OSut::#{__callee__}"
1243
+ cl1 = OpenStudio::Vector3d
1244
+ cl2 = Numeric
1245
+
1246
+ return mismatch("vector", v, cl1, mth, DBG, v) unless v.is_a?(cl1)
1247
+ return mismatch("x", v.x, cl2, mth, DBG, v) unless v.x.respond_to?(:to_f)
1248
+ return mismatch("y", v.y, cl2, mth, DBG, v) unless v.y.respond_to?(:to_f)
1249
+ return mismatch("z", v.z, cl2, mth, DBG, v) unless v.z.respond_to?(:to_f)
1250
+ return mismatch("m", m, cl2, mth, DBG, v) unless m.respond_to?(:to_f)
1251
+
1252
+ OpenStudio::Vector3d.new(m * v.x, m * v.y, m * v.z)
1253
+ end
1254
+
1243
1255
  ##
1244
1256
  # Flatten OpenStudio 3D points vs Z-axis (Z=0).
1245
1257
  #
@@ -1253,8 +1265,8 @@ module OSut
1253
1265
  v = OpenStudio::Point3dVector.new
1254
1266
 
1255
1267
  valid = pts.is_a?(cl1) || pts.is_a?(Array)
1256
- return mismatch("points", pts, cl1, mth, DBG, v) unless valid
1257
- pts.each { |pt| mismatch("pt", pt, cl2, mth, ERR, v) unless pt.is_a?(cl2) }
1268
+ return mismatch("points", pts, cl1, mth, DBG, v) unless valid
1269
+ pts.each { |pt| mismatch("pt", pt, cl2, mth, ERR, v) unless pt.is_a?(cl2) }
1258
1270
  pts.each { |pt| v << OpenStudio::Point3d.new(pt.x, pt.y, 0) }
1259
1271
 
1260
1272
  v
@@ -1278,40 +1290,47 @@ module OSut
1278
1290
 
1279
1291
  return invalid("id1", mth, 3, DBG, a) unless id1.respond_to?(:to_s)
1280
1292
  return invalid("id2", mth, 4, DBG, a) unless id2.respond_to?(:to_s)
1293
+
1281
1294
  i1 = id1.to_s
1282
1295
  i2 = id2.to_s
1283
1296
  i1 = "poly1" if i1.empty?
1284
1297
  i2 = "poly2" if i2.empty?
1298
+
1285
1299
  valid1 = p1.is_a?(cl1) || p1.is_a?(Array)
1286
1300
  valid2 = p2.is_a?(cl1) || p2.is_a?(Array)
1301
+
1287
1302
  return mismatch(i1, p1, cl1, mth, DBG, a) unless valid1
1288
1303
  return mismatch(i2, p2, cl1, mth, DBG, a) unless valid2
1289
- return empty(i1, mth, ERR, a) if p1.empty?
1290
- return empty(i2, mth, ERR, a) if p2.empty?
1304
+ return empty(i1, mth, ERR, a) if p1.empty?
1305
+ return empty(i2, mth, ERR, a) if p2.empty?
1306
+
1291
1307
  p1.each { |v| return mismatch(i1, v, cl2, mth, ERR, a) unless v.is_a?(cl2) }
1292
1308
  p2.each { |v| return mismatch(i2, v, cl2, mth, ERR, a) unless v.is_a?(cl2) }
1293
1309
 
1294
- ft = OpenStudio::Transformation::alignFace(p1).inverse
1295
- ft_p1 = flatZ( (ft * p1).reverse )
1296
- return false if ft_p1.empty?
1297
- area1 = OpenStudio::getArea(ft_p1)
1298
- return empty("#{i1} area", mth, ERR, a) if area1.empty?
1310
+ # XY-plane transformation matrix ... needs to be clockwise for boost.
1311
+ ft = OpenStudio::Transformation.alignFace(p1)
1312
+ ft_p1 = flatZ( (ft.inverse * p1) )
1313
+ return false if ft_p1.empty?
1314
+ cw = OpenStudio.pointInPolygon(ft_p1.first, ft_p1, TOL)
1315
+ ft_p1 = flatZ( (ft.inverse * p1).reverse ) unless cw
1316
+ ft_p2 = flatZ( (ft.inverse * p2).reverse ) unless cw
1317
+ ft_p2 = flatZ( (ft.inverse * p2) ) if cw
1318
+ return false if ft_p2.empty?
1319
+ area1 = OpenStudio.getArea(ft_p1)
1320
+ area2 = OpenStudio.getArea(ft_p2)
1321
+ return empty("#{i1} area", mth, ERR, a) if area1.empty?
1322
+ return empty("#{i2} area", mth, ERR, a) if area2.empty?
1299
1323
  area1 = area1.get
1300
- ft_p2 = flatZ( (ft * p2).reverse )
1301
- return false if ft_p2.empty?
1302
- area2 = OpenStudio::getArea(ft_p2)
1303
- return empty("#{i2} area", mth, ERR, a) if area2.empty?
1304
1324
  area2 = area2.get
1305
- union = OpenStudio::join(ft_p1, ft_p2, TOL2)
1306
- return false if union.empty?
1325
+ union = OpenStudio.join(ft_p1, ft_p2, TOL2)
1326
+ return false if union.empty?
1307
1327
  union = union.get
1308
- area = OpenStudio::getArea(union)
1309
- return empty("#{i1}:#{i2} union area", mth, ERR, a) if area.empty?
1328
+ area = OpenStudio.getArea(union)
1329
+ return empty("#{i1}:#{i2} union area", mth, ERR, a) if area.empty?
1310
1330
  area = area.get
1311
-
1312
- return false if area < TOL
1313
- return true if (area - area2).abs < TOL
1314
- return false if (area - area2).abs > TOL
1331
+ return false if area < TOL
1332
+ return true if (area - area2).abs < TOL
1333
+ return false if (area - area2).abs > TOL
1315
1334
 
1316
1335
  true
1317
1336
  end
@@ -1334,42 +1353,279 @@ module OSut
1334
1353
 
1335
1354
  return invalid("id1", mth, 3, DBG, a) unless id1.respond_to?(:to_s)
1336
1355
  return invalid("id2", mth, 4, DBG, a) unless id2.respond_to?(:to_s)
1356
+
1337
1357
  i1 = id1.to_s
1338
1358
  i2 = id2.to_s
1339
1359
  i1 = "poly1" if i1.empty?
1340
1360
  i2 = "poly2" if i2.empty?
1361
+
1341
1362
  valid1 = p1.is_a?(cl1) || p1.is_a?(Array)
1342
1363
  valid2 = p2.is_a?(cl1) || p2.is_a?(Array)
1364
+
1343
1365
  return mismatch(i1, p1, cl1, mth, DBG, a) unless valid1
1344
1366
  return mismatch(i2, p2, cl1, mth, DBG, a) unless valid2
1345
- return empty(i1, mth, ERR, a) if p1.empty?
1346
- return empty(i2, mth, ERR, a) if p2.empty?
1367
+ return empty(i1, mth, ERR, a) if p1.empty?
1368
+ return empty(i2, mth, ERR, a) if p2.empty?
1369
+
1347
1370
  p1.each { |v| return mismatch(i1, v, cl2, mth, ERR, a) unless v.is_a?(cl2) }
1348
1371
  p2.each { |v| return mismatch(i2, v, cl2, mth, ERR, a) unless v.is_a?(cl2) }
1349
1372
 
1350
- ft = OpenStudio::Transformation::alignFace(p1).inverse
1351
- ft_p1 = flatZ( (ft * p1).reverse )
1352
- return false if ft_p1.empty?
1353
- area1 = OpenStudio::getArea(ft_p1)
1354
- return empty("#{i1} area", mth, ERR, a) if area1.empty?
1373
+ # XY-plane transformation matrix ... needs to be clockwise for boost.
1374
+ ft = OpenStudio::Transformation.alignFace(p1)
1375
+ ft_p1 = flatZ( (ft.inverse * p1) )
1376
+ ft_p2 = flatZ( (ft.inverse * p2) )
1377
+ return false if ft_p1.empty?
1378
+ return false if ft_p2.empty?
1379
+ cw = OpenStudio.pointInPolygon(ft_p1.first, ft_p1, TOL)
1380
+ ft_p1 = flatZ( (ft.inverse * p1).reverse ) unless cw
1381
+ ft_p2 = flatZ( (ft.inverse * p2).reverse ) unless cw
1382
+ return false if ft_p1.empty?
1383
+ return false if ft_p2.empty?
1384
+ area1 = OpenStudio.getArea(ft_p1)
1385
+ area2 = OpenStudio.getArea(ft_p2)
1386
+ return empty("#{i1} area", mth, ERR, a) if area1.empty?
1387
+ return empty("#{i2} area", mth, ERR, a) if area2.empty?
1355
1388
  area1 = area1.get
1356
- ft_p2 = flatZ( (ft * p2).reverse )
1357
- return false if ft_p2.empty?
1358
- area2 = OpenStudio::getArea(ft_p2)
1359
- return empty("#{i2} area", mth, ERR, a) if area2.empty?
1360
1389
  area2 = area2.get
1361
- union = OpenStudio::join(ft_p1, ft_p2, TOL2)
1362
- return false if union.empty?
1390
+ union = OpenStudio.join(ft_p1, ft_p2, TOL2)
1391
+ return false if union.empty?
1363
1392
  union = union.get
1364
- area = OpenStudio::getArea(union)
1365
- return empty("#{i1}:#{i2} union area", mth, ERR, a) if area.empty?
1393
+ area = OpenStudio.getArea(union)
1394
+ return empty("#{i1}:#{i2} union area", mth, ERR, a) if area.empty?
1366
1395
  area = area.get
1367
-
1368
- return false if area < TOL
1396
+ return false if area < TOL
1369
1397
 
1370
1398
  true
1371
1399
  end
1372
1400
 
1401
+ ##
1402
+ # Generate offset vertices (by width) for a 3- or 4-sided, convex polygon.
1403
+ #
1404
+ # @param p1 [OpenStudio::Point3dVector] OpenStudio Point3D vector/array
1405
+ # @param w [Float] offset width (min: 0.0254m)
1406
+ # @param v [Integer] OpenStudio SDK version, eg '321' for 'v3.2.1' (optional)
1407
+ #
1408
+ # @return [OpenStudio::Point3dVector] offset points if successful
1409
+ # @return [OpenStudio::Point3dVector] original points if invalid input
1410
+ def offset(p1 = [], w = 0, v = 0)
1411
+ mth = "TBD::#{__callee__}"
1412
+ cl = OpenStudio::Point3d
1413
+ vrsn = OpenStudio.openStudioVersion.split(".").map(&:to_i).join.to_i
1414
+
1415
+ valid = p1.is_a?(OpenStudio::Point3dVector) || p1.is_a?(Array)
1416
+ return mismatch("pts", p1, cl1, mth, DBG, p1) unless valid
1417
+ return empty("pts", mth, ERR, p1) if p1.empty?
1418
+ valid = p1.size == 3 || p1.size == 4
1419
+ iv = true if p1.size == 4
1420
+ return invalid("pts", mth, 1, DBG, p1) unless valid
1421
+ return invalid("width", mth, 2, DBG, p1) unless w.respond_to?(:to_f)
1422
+ w = w.to_f
1423
+ return p1 if w < 0.0254
1424
+ v = v.to_i if v.respond_to?(:to_i)
1425
+ v = 0 unless v.respond_to?(:to_i)
1426
+ v = vrsn if v.zero?
1427
+
1428
+ p1.each { |x| return mismatch("p", x, cl, mth, ERR, p1) unless x.is_a?(cl) }
1429
+
1430
+ unless v < 340
1431
+ # XY-plane transformation matrix ... needs to be clockwise for boost.
1432
+ ft = OpenStudio::Transformation::alignFace(p1)
1433
+ ft_pts = flatZ( (ft.inverse * p1) )
1434
+ return p1 if ft_pts.empty?
1435
+ cw = OpenStudio::pointInPolygon(ft_pts.first, ft_pts, TOL)
1436
+ ft_pts = flatZ( (ft.inverse * p1).reverse ) unless cw
1437
+ offset = OpenStudio.buffer(ft_pts, w, TOL)
1438
+ return p1 if offset.empty?
1439
+ offset = offset.get
1440
+ offset = ft * offset if cw
1441
+ offset = (ft * offset).reverse unless cw
1442
+
1443
+ pz = OpenStudio::Point3dVector.new
1444
+ offset.each { |o| pz << OpenStudio::Point3d.new(o.x, o.y, o.z ) }
1445
+ return pz
1446
+ else # brute force approach
1447
+ pz = {}
1448
+ pz[:A] = {}
1449
+ pz[:B] = {}
1450
+ pz[:C] = {}
1451
+ pz[:D] = {} if iv
1452
+
1453
+ pz[:A][:p] = OpenStudio::Point3d.new(p1[0].x, p1[0].y, p1[0].z)
1454
+ pz[:B][:p] = OpenStudio::Point3d.new(p1[1].x, p1[1].y, p1[1].z)
1455
+ pz[:C][:p] = OpenStudio::Point3d.new(p1[2].x, p1[2].y, p1[2].z)
1456
+ pz[:D][:p] = OpenStudio::Point3d.new(p1[3].x, p1[3].y, p1[3].z) if iv
1457
+
1458
+ pzAp = pz[:A][:p]
1459
+ pzBp = pz[:B][:p]
1460
+ pzCp = pz[:C][:p]
1461
+ pzDp = pz[:D][:p] if iv
1462
+
1463
+ # Generate vector pairs, from next point & from previous point.
1464
+ # :f_n : "from next"
1465
+ # :f_p : "from previous"
1466
+ #
1467
+ #
1468
+ #
1469
+ #
1470
+ #
1471
+ #
1472
+ # A <---------- B
1473
+ # ^
1474
+ # \
1475
+ # \
1476
+ # C (or D)
1477
+ #
1478
+ pz[:A][:f_n] = pzAp - pzBp
1479
+ pz[:A][:f_p] = pzAp - pzCp unless iv
1480
+ pz[:A][:f_p] = pzAp - pzDp if iv
1481
+
1482
+ pz[:B][:f_n] = pzBp - pzCp
1483
+ pz[:B][:f_p] = pzBp - pzAp
1484
+
1485
+ pz[:C][:f_n] = pzCp - pzAp unless iv
1486
+ pz[:C][:f_n] = pzCp - pzDp if iv
1487
+ pz[:C][:f_p] = pzCp - pzBp
1488
+
1489
+ pz[:D][:f_n] = pzDp - pzAp if iv
1490
+ pz[:D][:f_p] = pzDp - pzCp if iv
1491
+
1492
+ # Generate 3D plane from vectors.
1493
+ #
1494
+ #
1495
+ # | <<< 3D plane ... from point A, with normal B>A
1496
+ # |
1497
+ # |
1498
+ # |
1499
+ # <---------- A <---------- B
1500
+ # |\
1501
+ # | \
1502
+ # | \
1503
+ # | C (or D)
1504
+ #
1505
+ pz[:A][:pl_f_n] = OpenStudio::Plane.new(pzAp, pz[:A][:f_n])
1506
+ pz[:A][:pl_f_p] = OpenStudio::Plane.new(pzAp, pz[:A][:f_p])
1507
+
1508
+ pz[:B][:pl_f_n] = OpenStudio::Plane.new(pzBp, pz[:B][:f_n])
1509
+ pz[:B][:pl_f_p] = OpenStudio::Plane.new(pzBp, pz[:B][:f_p])
1510
+
1511
+ pz[:C][:pl_f_n] = OpenStudio::Plane.new(pzCp, pz[:C][:f_n])
1512
+ pz[:C][:pl_f_p] = OpenStudio::Plane.new(pzCp, pz[:C][:f_p])
1513
+
1514
+ pz[:D][:pl_f_n] = OpenStudio::Plane.new(pzDp, pz[:D][:f_n]) if iv
1515
+ pz[:D][:pl_f_p] = OpenStudio::Plane.new(pzDp, pz[:D][:f_p]) if iv
1516
+
1517
+ # Project an extended point (pC) unto 3D plane.
1518
+ #
1519
+ # pC <<< projected unto extended B>A 3D plane
1520
+ # eC |
1521
+ # \ |
1522
+ # \ |
1523
+ # \|
1524
+ # <---------- A <---------- B
1525
+ # |\
1526
+ # | \
1527
+ # | \
1528
+ # | C (or D)
1529
+ #
1530
+ pz[:A][:p_n_pl] = pz[:A][:pl_f_n].project(pz[:A][:p] + pz[:A][:f_p])
1531
+ pz[:A][:n_p_pl] = pz[:A][:pl_f_p].project(pz[:A][:p] + pz[:A][:f_n])
1532
+
1533
+ pz[:B][:p_n_pl] = pz[:B][:pl_f_n].project(pz[:B][:p] + pz[:B][:f_p])
1534
+ pz[:B][:n_p_pl] = pz[:B][:pl_f_p].project(pz[:B][:p] + pz[:B][:f_n])
1535
+
1536
+ pz[:C][:p_n_pl] = pz[:C][:pl_f_n].project(pz[:C][:p] + pz[:C][:f_p])
1537
+ pz[:C][:n_p_pl] = pz[:C][:pl_f_p].project(pz[:C][:p] + pz[:C][:f_n])
1538
+
1539
+ pz[:D][:p_n_pl] = pz[:D][:pl_f_n].project(pz[:D][:p] + pz[:D][:f_p]) if iv
1540
+ pz[:D][:n_p_pl] = pz[:D][:pl_f_p].project(pz[:D][:p] + pz[:D][:f_n]) if iv
1541
+
1542
+ # Generate vector from point (e.g. A) to projected extended point (pC).
1543
+ #
1544
+ # pC
1545
+ # eC ^
1546
+ # \ |
1547
+ # \ |
1548
+ # \|
1549
+ # <---------- A <---------- B
1550
+ # |\
1551
+ # | \
1552
+ # | \
1553
+ # | C (or D)
1554
+ #
1555
+ pz[:A][:n_p_n_pl] = pz[:A][:p_n_pl] - pzAp
1556
+ pz[:A][:n_n_p_pl] = pz[:A][:n_p_pl] - pzAp
1557
+
1558
+ pz[:B][:n_p_n_pl] = pz[:B][:p_n_pl] - pzBp
1559
+ pz[:B][:n_n_p_pl] = pz[:B][:n_p_pl] - pzBp
1560
+
1561
+ pz[:C][:n_p_n_pl] = pz[:C][:p_n_pl] - pzCp
1562
+ pz[:C][:n_n_p_pl] = pz[:C][:n_p_pl] - pzCp
1563
+
1564
+ pz[:D][:n_p_n_pl] = pz[:D][:p_n_pl] - pzDp if iv
1565
+ pz[:D][:n_n_p_pl] = pz[:D][:n_p_pl] - pzDp if iv
1566
+
1567
+ # Fetch angle between both extended vectors (A>pC & A>pB),
1568
+ # ... then normalize (Cn).
1569
+ #
1570
+ # pC
1571
+ # eC ^
1572
+ # \ |
1573
+ # \ Cn
1574
+ # \|
1575
+ # <---------- A <---------- B
1576
+ # |\
1577
+ # | \
1578
+ # | \
1579
+ # | C (or D)
1580
+ #
1581
+ a1 = OpenStudio.getAngle(pz[:A][:n_p_n_pl], pz[:A][:n_n_p_pl])
1582
+ a2 = OpenStudio.getAngle(pz[:B][:n_p_n_pl], pz[:B][:n_n_p_pl])
1583
+ a3 = OpenStudio.getAngle(pz[:C][:n_p_n_pl], pz[:C][:n_n_p_pl])
1584
+ a4 = OpenStudio.getAngle(pz[:D][:n_p_n_pl], pz[:D][:n_n_p_pl]) if iv
1585
+
1586
+ # Generate new 3D points A', B', C' (and D') ... zigzag.
1587
+ #
1588
+ #
1589
+ #
1590
+ #
1591
+ # A' ---------------------- B'
1592
+ # \
1593
+ # \ A <---------- B
1594
+ # \ \
1595
+ # \ \
1596
+ # \ \
1597
+ # C' C
1598
+ pz[:A][:f_n].normalize
1599
+ pz[:A][:n_p_n_pl].normalize
1600
+ pzAp = pzAp + scalar(pz[:A][:n_p_n_pl], w)
1601
+ pzAp = pzAp + scalar(pz[:A][:f_n], w * Math.tan(a1/2))
1602
+
1603
+ pz[:B][:f_n].normalize
1604
+ pz[:B][:n_p_n_pl].normalize
1605
+ pzBp = pzBp + scalar(pz[:B][:n_p_n_pl], w)
1606
+ pzBp = pzBp + scalar(pz[:B][:f_n], w * Math.tan(a2/2))
1607
+
1608
+ pz[:C][:f_n].normalize
1609
+ pz[:C][:n_p_n_pl].normalize
1610
+ pzCp = pzCp + scalar(pz[:C][:n_p_n_pl], w)
1611
+ pzCp = pzCp + scalar(pz[:C][:f_n], w * Math.tan(a3/2))
1612
+
1613
+ pz[:D][:f_n].normalize if iv
1614
+ pz[:D][:n_p_n_pl].normalize if iv
1615
+ pzDp = pzDp + scalar(pz[:D][:n_p_n_pl], w) if iv
1616
+ pzDp = pzDp + scalar(pz[:D][:f_n], w * Math.tan(a4/2)) if iv
1617
+
1618
+ # Re-convert to OpenStudio 3D points.
1619
+ vec = OpenStudio::Point3dVector.new
1620
+ vec << OpenStudio::Point3d.new(pzAp.x, pzAp.y, pzAp.z)
1621
+ vec << OpenStudio::Point3d.new(pzBp.x, pzBp.y, pzBp.z)
1622
+ vec << OpenStudio::Point3d.new(pzCp.x, pzCp.y, pzCp.z)
1623
+ vec << OpenStudio::Point3d.new(pzDp.x, pzDp.y, pzDp.z) if iv
1624
+
1625
+ return vec
1626
+ end
1627
+ end
1628
+
1373
1629
  ##
1374
1630
  # Callback when other modules extend OSlg
1375
1631
  #
data/lib/osut/version.rb CHANGED
@@ -29,5 +29,5 @@
29
29
  # OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
30
30
 
31
31
  module OSut
32
- VERSION = "0.2.3".freeze
32
+ VERSION = "0.2.7".freeze
33
33
  end
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.2.3
4
+ version: 0.2.7
5
5
  platform: ruby
6
6
  authors:
7
7
  - Denis Bourgeois
8
8
  autorequire:
9
9
  bindir: exe
10
10
  cert_chain: []
11
- date: 2022-08-15 00:00:00.000000000 Z
11
+ date: 2022-08-21 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.2.3
93
+ source_code_uri: https://github.com/rd2/osut/tree/v0.2.7
94
94
  bug_tracker_uri: https://github.com/rd2/osut/issues
95
95
  post_install_message:
96
96
  rdoc_options: []