@sequent-org/ifc-viewer 1.2.4-ci.49.0 → 1.2.4-ci.51.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.
@@ -1373,6 +1373,10 @@ export class Viewer {
1373
1373
  this.activeModel = object3D;
1374
1374
  this.scene.add(object3D);
1375
1375
 
1376
+ // Сброс MMB-pan (viewOffset) при загрузке новой модели:
1377
+ // иначе экранный сдвиг может "унести" модель из кадра даже при корректном кадрировании по bbox.
1378
+ try { this._mmbPan?.controller?.reset?.(); } catch (_) {}
1379
+
1376
1380
  // Пересчитать плоскость под моделью (3x по площади bbox по X/Z)
1377
1381
  this.#updateShadowReceiverFromModel(object3D);
1378
1382
 
@@ -1627,13 +1631,40 @@ export class Viewer {
1627
1631
  const center = box.getCenter(new THREE.Vector3());
1628
1632
  const minY = box.min.y;
1629
1633
 
1630
- // Требование: площадь плоскости = 3x площади объекта (bbox по X/Z).
1634
+ // Базовая плоскость: площадь = 3x площади объекта (bbox по X/Z).
1631
1635
  // => множитель по размерам = sqrt(3).
1632
1636
  const areaMultiplier = 3;
1633
1637
  const dimMul = Math.sqrt(areaMultiplier);
1634
1638
 
1639
+ // Доп. запас по X/Z из-за длины тени: высокая модель при наклонном солнце
1640
+ // может давать тень далеко за bbox по X/Z.
1641
+ // Оценка смещения тени по земле: displacementXZ ≈ height * |dirXZ| / |dirY|
1642
+ // где dir = (target - lightPos) нормализованный.
1643
+ let extraX = 0;
1644
+ let extraZ = 0;
1645
+ try {
1646
+ const sun = this.sunLight;
1647
+ if (sun) {
1648
+ const targetPos = (sun.target?.position?.clone?.() || center.clone());
1649
+ const dir = targetPos.sub(sun.position).normalize();
1650
+ const ay = Math.max(1e-3, Math.abs(dir.y));
1651
+ extraX = Math.abs(dir.x) * (Math.max(0, size.y) / ay);
1652
+ extraZ = Math.abs(dir.z) * (Math.max(0, size.y) / ay);
1653
+ // небольшой коэффициент запаса, чтобы не ловить «пограничные» обрезания
1654
+ const pad = 1.05;
1655
+ extraX *= pad;
1656
+ extraZ *= pad;
1657
+ }
1658
+ } catch (_) {
1659
+ extraX = 0;
1660
+ extraZ = 0;
1661
+ }
1662
+
1635
1663
  this.shadowReceiver.position.set(center.x, minY + 0.001, center.z);
1636
- this.shadowReceiver.scale.set(Math.max(0.001, size.x * dimMul), Math.max(0.001, size.z * dimMul), 1);
1664
+ // receiver.scale: X->world X, Y->world Z (PlaneGeometry is X/Y in local, rotated -90° around X)
1665
+ const receiverX = Math.max(0.001, (size.x * dimMul) + extraX * 2);
1666
+ const receiverZ = Math.max(0.001, (size.z * dimMul) + extraZ * 2);
1667
+ this.shadowReceiver.scale.set(receiverX, receiverZ, 1);
1637
1668
  this.shadowReceiver.updateMatrixWorld();
1638
1669
 
1639
1670
  // Обновим bbox здания для градиента тени (в XZ)
@@ -1647,8 +1678,8 @@ export class Viewer {
1647
1678
  // чтобы при включении теней они не "обрезались" слишком маленькой областью.
1648
1679
  if (this.sunLight) {
1649
1680
  const cam = this.sunLight.shadow.camera;
1650
- const halfX = (size.x * dimMul) / 2;
1651
- const halfZ = (size.z * dimMul) / 2;
1681
+ const halfX = receiverX / 2;
1682
+ const halfZ = receiverZ / 2;
1652
1683
  cam.left = -halfX;
1653
1684
  cam.right = halfX;
1654
1685
  cam.top = halfZ;