osut 0.2.6 → 0.2.7

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (4) hide show
  1. checksums.yaml +4 -4
  2. data/lib/osut/utils.rb +336 -70
  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: 04db3007d81904287c0c36c677270a1e1bc5e45375d3b37bbac9068d51c43754
4
- data.tar.gz: aafa2bb60bdfaf5b5df03e99aca0cb3181f6270468a761652d2b6d0438037daa
3
+ metadata.gz: 6bbbc6887949cfd0d1aed5c460d993196fa86d1a13f366f7f852e418aa9457a0
4
+ data.tar.gz: b38eaa81d84a79655adb10a06bb1fa299ffff60051d187f2b36bad38fa4d78e0
5
5
  SHA512:
6
- metadata.gz: ca60fd088aa106b52bfe15fe4fd352c55a2c79083f95212d7e9e37250f1832b3b94f0dc415e664f5289e837600aad2ab936eff304e9d5cdba0c3a977a56cd7d0
7
- data.tar.gz: 8aa96937a55520f99a9c122d0fe98f69ec97d45cbe0cac1e0db7626b48cacee5d65f801eb4a9791e3a6953d64473555b981b3bd9646d7948b6b5c26b2d02e041
6
+ metadata.gz: 1eb2cd49987ce2a4bf7a6a9fe0ed7048bc78dce644aa88e68173861ce4e52fc2bfe59bb3c4a7b353426a1bfa30f45af102ca3e509ac91945b82673545f1a4d4d
7
+ data.tar.gz: bb8075d22d660b3f177716463baf3bc2b0e788269dc05f60086272dc5fec193b93a207f94500fa26cf8d786fdcd3810c0d3defb50eca4c39b3a05cf7c8d2412c
data/lib/osut/utils.rb CHANGED
@@ -628,7 +628,7 @@ module OSut
628
628
  mth = "OSut::#{__callee__}"
629
629
  cl = OpenStudio::Model::Model
630
630
 
631
- 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)
632
632
 
633
633
  model.getThermalZones.each do |zone|
634
634
  return true if minCoolScheduledSetpoint(zone)[:spt]
@@ -648,12 +648,12 @@ module OSut
648
648
  mth = "OSut::#{__callee__}"
649
649
  cl = OpenStudio::Model::Model
650
650
 
651
- 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)
652
652
 
653
653
  model.getThermalZones.each do |zone|
654
- next if zone.canBePlenum
655
- return true unless zone.airLoopHVACs.empty?
656
- return true if zone.isPlenum
654
+ next if zone.canBePlenum
655
+ return true unless zone.airLoopHVACs.empty?
656
+ return true if zone.isPlenum
657
657
  end
658
658
 
659
659
  false
@@ -678,7 +678,7 @@ module OSut
678
678
  # A space may be tagged as a plenum if:
679
679
  #
680
680
  # CASE A: its zone's "isPlenum" == true (SDK method) for a fully-developed
681
- # OpenStudio model (complete with HVAC air loops);
681
+ # OpenStudio model (complete with HVAC air loops); OR
682
682
  #
683
683
  # CASE B: (IN ABSENCE OF HVAC AIRLOOPS) if it's excluded from a building's
684
684
  # total floor area yet linked to a zone holding an 'inactive'
@@ -737,8 +737,8 @@ module OSut
737
737
  cl = OpenStudio::Model::Model
738
738
  limits = nil
739
739
 
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)
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)
742
742
 
743
743
  # Either fetch availability ScheduleTypeLimits object, or create one.
744
744
  model.getScheduleTypeLimitss.each do |l|
@@ -769,9 +769,9 @@ module OSut
769
769
  off = OpenStudio::Model::ScheduleDay.new(model, 0)
770
770
 
771
771
  # Seasonal availability start/end dates.
772
- year = model.yearDescription
773
- return empty("yearDescription", mth, ERR) if year.empty?
774
- year = year.get
772
+ year = model.yearDescription
773
+ return empty("yearDescription", mth, ERR) if year.empty?
774
+ year = year.get
775
775
  may01 = year.makeDate(OpenStudio::MonthOfYear.new("May"), 1)
776
776
  oct31 = year.makeDate(OpenStudio::MonthOfYear.new("Oct"), 31)
777
777
 
@@ -957,17 +957,17 @@ module OSut
957
957
  cl1 = OpenStudio::Model::Model
958
958
  cl2 = OpenStudio::Model::Surface
959
959
 
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)
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)
964
964
 
965
965
  ok = s.isConstructionDefaulted
966
966
  log(ERR, "'#{id}' construction not defaulted (#{mth})") unless ok
967
967
  return nil unless ok
968
- return empty("'#{id}' construction", mth, ERR) if s.construction.empty?
968
+ return empty("'#{id}' construction", mth, ERR) if s.construction.empty?
969
969
  base = s.construction.get
970
- return empty("'#{id}' space", mth, ERR) if s.space.empty?
970
+ return empty("'#{id}' space", mth, ERR) if s.space.empty?
971
971
  space = s.space.get
972
972
  type = s.surfaceType
973
973
  ground = false
@@ -981,7 +981,7 @@ module OSut
981
981
 
982
982
  unless space.defaultConstructionSet.empty?
983
983
  set = space.defaultConstructionSet.get
984
- return set if holdsConstruction?(set, base, ground, exterior, type)
984
+ return set if holdsConstruction?(set, base, ground, exterior, type)
985
985
  end
986
986
 
987
987
  unless space.spaceType.empty?
@@ -989,7 +989,7 @@ module OSut
989
989
 
990
990
  unless spacetype.defaultConstructionSet.empty?
991
991
  set = spacetype.defaultConstructionSet.get
992
- return set if holdsConstruction?(set, base, ground, exterior, type)
992
+ return set if holdsConstruction?(set, base, ground, exterior, type)
993
993
  end
994
994
  end
995
995
 
@@ -998,7 +998,7 @@ module OSut
998
998
 
999
999
  unless story.defaultConstructionSet.empty?
1000
1000
  set = story.defaultConstructionSet.get
1001
- return set if holdsConstruction?(set, base, ground, exterior, type)
1001
+ return set if holdsConstruction?(set, base, ground, exterior, type)
1002
1002
  end
1003
1003
  end
1004
1004
 
@@ -1006,7 +1006,7 @@ module OSut
1006
1006
 
1007
1007
  unless building.defaultConstructionSet.empty?
1008
1008
  set = building.defaultConstructionSet.get
1009
- return set if holdsConstruction?(set, base, ground, exterior, type)
1009
+ return set if holdsConstruction?(set, base, ground, exterior, type)
1010
1010
  end
1011
1011
 
1012
1012
  nil
@@ -1036,15 +1036,15 @@ module OSut
1036
1036
  #
1037
1037
  # @param lc [OpenStudio::LayeredConstruction] a layered construction
1038
1038
  #
1039
- # @return [Double] total layered construction thickness
1040
- # @return [Double] 0 if invalid input
1039
+ # @return [Float] total layered construction thickness
1040
+ # @return [Float] 0 if invalid input
1041
1041
  def thickness(lc = nil)
1042
1042
  mth = "OSut::#{__callee__}"
1043
1043
  cl = OpenStudio::Model::LayeredConstruction
1044
1044
 
1045
- 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)
1046
1046
  id = lc.nameString
1047
- 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)
1048
1048
 
1049
1049
  ok = standardOpaqueLayers?(lc)
1050
1050
  log(ERR, "'#{id}' holds non-StandardOpaqueMaterial(s) (#{mth})") unless ok
@@ -1166,9 +1166,9 @@ module OSut
1166
1166
  res = { index: nil, type: nil, r: 0.0 }
1167
1167
  i = 0 # iterator
1168
1168
 
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)
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)
1172
1172
 
1173
1173
  lc.layers.each do |m|
1174
1174
  unless m.to_MasslessOpaqueMaterial.empty?
@@ -1178,9 +1178,9 @@ module OSut
1178
1178
  i += 1
1179
1179
  next
1180
1180
  else
1181
- res[:r] = m.thermalResistance
1181
+ res[:r ] = m.thermalResistance
1182
1182
  res[:index] = i
1183
- res[:type] = :massless
1183
+ res[:type ] = :massless
1184
1184
  end
1185
1185
  end
1186
1186
 
@@ -1193,9 +1193,9 @@ module OSut
1193
1193
  i += 1
1194
1194
  next
1195
1195
  else
1196
- res[:r] = d / k
1196
+ res[:r ] = d / k
1197
1197
  res[:index] = i
1198
- res[:type] = :standard
1198
+ res[:type ] = :standard
1199
1199
  end
1200
1200
  end
1201
1201
 
@@ -1230,6 +1230,28 @@ module OSut
1230
1230
  res
1231
1231
  end
1232
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
+
1233
1255
  ##
1234
1256
  # Flatten OpenStudio 3D points vs Z-axis (Z=0).
1235
1257
  #
@@ -1243,8 +1265,8 @@ module OSut
1243
1265
  v = OpenStudio::Point3dVector.new
1244
1266
 
1245
1267
  valid = pts.is_a?(cl1) || pts.is_a?(Array)
1246
- return mismatch("points", pts, cl1, mth, DBG, v) unless valid
1247
- 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) }
1248
1270
  pts.each { |pt| v << OpenStudio::Point3d.new(pt.x, pt.y, 0) }
1249
1271
 
1250
1272
  v
@@ -1268,40 +1290,47 @@ module OSut
1268
1290
 
1269
1291
  return invalid("id1", mth, 3, DBG, a) unless id1.respond_to?(:to_s)
1270
1292
  return invalid("id2", mth, 4, DBG, a) unless id2.respond_to?(:to_s)
1293
+
1271
1294
  i1 = id1.to_s
1272
1295
  i2 = id2.to_s
1273
1296
  i1 = "poly1" if i1.empty?
1274
1297
  i2 = "poly2" if i2.empty?
1298
+
1275
1299
  valid1 = p1.is_a?(cl1) || p1.is_a?(Array)
1276
1300
  valid2 = p2.is_a?(cl1) || p2.is_a?(Array)
1301
+
1277
1302
  return mismatch(i1, p1, cl1, mth, DBG, a) unless valid1
1278
1303
  return mismatch(i2, p2, cl1, mth, DBG, a) unless valid2
1279
- return empty(i1, mth, ERR, a) if p1.empty?
1280
- 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
+
1281
1307
  p1.each { |v| return mismatch(i1, v, cl2, mth, ERR, a) unless v.is_a?(cl2) }
1282
1308
  p2.each { |v| return mismatch(i2, v, cl2, mth, ERR, a) unless v.is_a?(cl2) }
1283
1309
 
1284
- ft = OpenStudio::Transformation::alignFace(p1).inverse
1285
- ft_p1 = flatZ( (ft * p1).reverse )
1286
- return false if ft_p1.empty?
1287
- area1 = OpenStudio::getArea(ft_p1)
1288
- 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?
1289
1323
  area1 = area1.get
1290
- ft_p2 = flatZ( (ft * p2).reverse )
1291
- return false if ft_p2.empty?
1292
- area2 = OpenStudio::getArea(ft_p2)
1293
- return empty("#{i2} area", mth, ERR, a) if area2.empty?
1294
1324
  area2 = area2.get
1295
- union = OpenStudio::join(ft_p1, ft_p2, TOL2)
1296
- return false if union.empty?
1325
+ union = OpenStudio.join(ft_p1, ft_p2, TOL2)
1326
+ return false if union.empty?
1297
1327
  union = union.get
1298
- area = OpenStudio::getArea(union)
1299
- 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?
1300
1330
  area = area.get
1301
-
1302
- return false if area < TOL
1303
- return true if (area - area2).abs < TOL
1304
- 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
1305
1334
 
1306
1335
  true
1307
1336
  end
@@ -1324,42 +1353,279 @@ module OSut
1324
1353
 
1325
1354
  return invalid("id1", mth, 3, DBG, a) unless id1.respond_to?(:to_s)
1326
1355
  return invalid("id2", mth, 4, DBG, a) unless id2.respond_to?(:to_s)
1356
+
1327
1357
  i1 = id1.to_s
1328
1358
  i2 = id2.to_s
1329
1359
  i1 = "poly1" if i1.empty?
1330
1360
  i2 = "poly2" if i2.empty?
1361
+
1331
1362
  valid1 = p1.is_a?(cl1) || p1.is_a?(Array)
1332
1363
  valid2 = p2.is_a?(cl1) || p2.is_a?(Array)
1364
+
1333
1365
  return mismatch(i1, p1, cl1, mth, DBG, a) unless valid1
1334
1366
  return mismatch(i2, p2, cl1, mth, DBG, a) unless valid2
1335
- return empty(i1, mth, ERR, a) if p1.empty?
1336
- 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
+
1337
1370
  p1.each { |v| return mismatch(i1, v, cl2, mth, ERR, a) unless v.is_a?(cl2) }
1338
1371
  p2.each { |v| return mismatch(i2, v, cl2, mth, ERR, a) unless v.is_a?(cl2) }
1339
1372
 
1340
- ft = OpenStudio::Transformation::alignFace(p1).inverse
1341
- ft_p1 = flatZ( (ft * p1).reverse )
1342
- return false if ft_p1.empty?
1343
- area1 = OpenStudio::getArea(ft_p1)
1344
- 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?
1345
1388
  area1 = area1.get
1346
- ft_p2 = flatZ( (ft * p2).reverse )
1347
- return false if ft_p2.empty?
1348
- area2 = OpenStudio::getArea(ft_p2)
1349
- return empty("#{i2} area", mth, ERR, a) if area2.empty?
1350
1389
  area2 = area2.get
1351
- union = OpenStudio::join(ft_p1, ft_p2, TOL2)
1352
- return false if union.empty?
1390
+ union = OpenStudio.join(ft_p1, ft_p2, TOL2)
1391
+ return false if union.empty?
1353
1392
  union = union.get
1354
- area = OpenStudio::getArea(union)
1355
- 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?
1356
1395
  area = area.get
1357
-
1358
- return false if area < TOL
1396
+ return false if area < TOL
1359
1397
 
1360
1398
  true
1361
1399
  end
1362
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
+
1363
1629
  ##
1364
1630
  # Callback when other modules extend OSlg
1365
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.6".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.6
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.6
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: []