@principal-ai/file-city-react 0.5.17 → 0.5.18
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.
- package/dist/components/FileCity3D/FileCity3D.d.ts.map +1 -1
- package/dist/components/FileCity3D/FileCity3D.js +36 -13
- package/dist/components/FileCity3D/index.d.ts +2 -2
- package/dist/components/FileCity3D/index.d.ts.map +1 -1
- package/dist/components/FileCity3D/index.js +1 -1
- package/package.json +1 -1
- package/src/components/FileCity3D/FileCity3D.tsx +50 -18
- package/src/components/FileCity3D/index.ts +13 -2
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"FileCity3D.d.ts","sourceRoot":"","sources":["../../../src/components/FileCity3D/FileCity3D.tsx"],"names":[],"mappings":"AAAA;;;;;;;GAOG;AAEH,OAAO,KAA4D,MAAM,OAAO,CAAC;AAMjF,OAAO,KAAK,EACV,QAAQ,EACR,YAAY,EACZ,YAAY,EAEb,MAAM,iCAAiC,CAAC;AAEzC,OAAO,KAAK,EAAE,aAAa,EAAE,MAAM,oBAAoB,CAAC;AAGxD,OAAO,QAAQ,OAAO,CAAC;IAErB,UAAU,GAAG,CAAC;QAEZ,UAAU,iBAAkB,SAAQ,aAAa;SAAG;KACrD;CACF;AAGD,YAAY,EAAE,QAAQ,EAAE,YAAY,EAAE,YAAY,EAAE,CAAC;AAGrD,MAAM,WAAW,cAAc;IAC7B,wBAAwB;IACxB,EAAE,EAAE,MAAM,CAAC;IACX,mBAAmB;IACnB,IAAI,EAAE,MAAM,CAAC;IACb,8BAA8B;IAC9B,OAAO,EAAE,OAAO,CAAC;IACjB,4BAA4B;IAC5B,KAAK,EAAE,MAAM,CAAC;IACd,yBAAyB;IACzB,KAAK,EAAE,aAAa,EAAE,CAAC;IACvB,0CAA0C;IAC1C,OAAO,CAAC,EAAE,MAAM,CAAC;CAClB;AAED,MAAM,WAAW,aAAa;IAC5B,6BAA6B;IAC7B,IAAI,EAAE,MAAM,CAAC;IACb,mBAAmB;IACnB,IAAI,EAAE,MAAM,GAAG,WAAW,CAAC;CAC5B;AAED,gDAAgD;AAChD,MAAM,MAAM,aAAa,GACrB,MAAM,GACN,aAAa,GACb,UAAU,GACV,MAAM,CAAC;AAGX,MAAM,WAAW,eAAe;IAC9B,0CAA0C;IAC1C,SAAS,CAAC,EAAE,OAAO,CAAC;IACpB,mFAAmF;IACnF,cAAc,CAAC,EAAE,MAAM,GAAG,IAAI,CAAC;IAC/B,2CAA2C;IAC3C,YAAY,CAAC,EAAE,MAAM,CAAC;IACtB,4CAA4C;IAC5C,YAAY,CAAC,EAAE,MAAM,CAAC;IACtB,gDAAgD;IAChD,OAAO,CAAC,EAAE,MAAM,CAAC;IACjB,6CAA6C;IAC7C,QAAQ,CAAC,EAAE,MAAM,CAAC;CACnB;AAED,wCAAwC;AACxC,MAAM,MAAM,aAAa,GAAG,aAAa,GAAG,QAAQ,CAAC;
|
|
1
|
+
{"version":3,"file":"FileCity3D.d.ts","sourceRoot":"","sources":["../../../src/components/FileCity3D/FileCity3D.tsx"],"names":[],"mappings":"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"}
|
|
@@ -439,11 +439,11 @@ function InstancedBuildings({ buildings, centerOffset, onHover, onClick, hovered
|
|
|
439
439
|
})), growProgress: growProgress, minHeight: minHeight, baseOffset: baseOffset, springDuration: springDuration, heightMultipliersRef: heightMultipliersRef })] }));
|
|
440
440
|
}
|
|
441
441
|
function AnimatedIcon({ x, z, targetHeight, iconSize, texture, opacity, growProgress, staggerDelayMs, springDuration, }) {
|
|
442
|
-
const
|
|
442
|
+
const meshRef = useRef(null);
|
|
443
443
|
const startTimeRef = useRef(null);
|
|
444
444
|
const materialRef = useRef(null);
|
|
445
445
|
useFrame(({ clock }) => {
|
|
446
|
-
if (!
|
|
446
|
+
if (!meshRef.current)
|
|
447
447
|
return;
|
|
448
448
|
if (startTimeRef.current === null && growProgress > 0) {
|
|
449
449
|
startTimeRef.current = clock.elapsedTime * 1000;
|
|
@@ -465,13 +465,26 @@ function AnimatedIcon({ x, z, targetHeight, iconSize, texture, opacity, growProg
|
|
|
465
465
|
const baseOffset = 0.2;
|
|
466
466
|
const height = animProgress * targetHeight + minHeight;
|
|
467
467
|
const buildingTop = height + baseOffset;
|
|
468
|
-
|
|
469
|
-
|
|
468
|
+
// When flat (animProgress=0): icon lies flat at ground level
|
|
469
|
+
// When grown (animProgress=1): icon floats above building
|
|
470
|
+
const flatY = minHeight + baseOffset + 0.5;
|
|
471
|
+
const grownY = buildingTop + iconSize / 2 + 2;
|
|
472
|
+
const yPosition = flatY + (grownY - flatY) * animProgress;
|
|
473
|
+
meshRef.current.position.y = yPosition;
|
|
474
|
+
// Rotate from flat (facing up) to upright (facing camera-ish)
|
|
475
|
+
// Flat: -Math.PI / 2 (facing up)
|
|
476
|
+
// Grown: 0 (facing forward)
|
|
477
|
+
const flatRotationX = -Math.PI / 2;
|
|
478
|
+
const grownRotationX = 0;
|
|
479
|
+
meshRef.current.rotation.x = flatRotationX + (grownRotationX - flatRotationX) * animProgress;
|
|
470
480
|
if (materialRef.current) {
|
|
471
|
-
|
|
481
|
+
// Show icons even when flat, fade out only slightly
|
|
482
|
+
const minOpacity = 0.8;
|
|
483
|
+
const effectiveOpacity = minOpacity + (1 - minOpacity) * animProgress;
|
|
484
|
+
materialRef.current.opacity = opacity * effectiveOpacity;
|
|
472
485
|
}
|
|
473
486
|
});
|
|
474
|
-
return (
|
|
487
|
+
return (_jsxs("mesh", { ref: meshRef, position: [x, 0, z], scale: [iconSize, iconSize, 1], raycast: () => null, children: [_jsx("planeGeometry", { args: [1, 1] }), _jsx("meshBasicMaterial", { ref: materialRef, map: texture, transparent: true, opacity: 0.8, depthTest: true, depthWrite: false, side: THREE.DoubleSide })] }));
|
|
475
488
|
}
|
|
476
489
|
function BuildingIcons({ buildings, centerOffset, growProgress, heightScaling, linearScale, highlightLayers, isolationMode, hasActiveHighlights, staggerIndices, springDuration, staggerDelay, }) {
|
|
477
490
|
// Pre-compute buildings with icons
|
|
@@ -516,9 +529,7 @@ function BuildingIcons({ buildings, centerOffset, growProgress, heightScaling, l
|
|
|
516
529
|
staggerIndices,
|
|
517
530
|
staggerDelay,
|
|
518
531
|
]);
|
|
519
|
-
//
|
|
520
|
-
if (growProgress < 0.1)
|
|
521
|
-
return null;
|
|
532
|
+
// Icons are now always rendered (flat or grown)
|
|
522
533
|
return (_jsx(_Fragment, { children: buildingsWithIcons.map(({ building, config, x, z, targetHeight, shouldDim, staggerDelayMs }) => {
|
|
523
534
|
const icon = config.icon;
|
|
524
535
|
const texture = getIconTexture(icon.name, icon.color || '#ffffff');
|
|
@@ -526,14 +537,14 @@ function BuildingIcons({ buildings, centerOffset, growProgress, heightScaling, l
|
|
|
526
537
|
return null;
|
|
527
538
|
// Icon size based on building dimensions
|
|
528
539
|
const [width] = building.dimensions;
|
|
529
|
-
const baseSize = Math.max(width *
|
|
540
|
+
const baseSize = Math.max(width * 1.2, 8);
|
|
530
541
|
const heightBoost = Math.min(targetHeight / 20, 3);
|
|
531
542
|
const iconSize = (baseSize + heightBoost) * (icon.size || 1);
|
|
532
543
|
const opacity = shouldDim && isolationMode === 'transparent' ? 0.3 : 1;
|
|
533
544
|
return (_jsx(AnimatedIcon, { x: x, z: z, targetHeight: targetHeight, iconSize: iconSize, texture: texture, opacity: opacity, growProgress: growProgress, staggerDelayMs: staggerDelayMs, springDuration: springDuration }, building.path));
|
|
534
545
|
}) }));
|
|
535
546
|
}
|
|
536
|
-
function DistrictFloor({ district, centerOffset, highlightColor }) {
|
|
547
|
+
function DistrictFloor({ district, centerOffset, highlightColor, growProgress }) {
|
|
537
548
|
const { worldBounds } = district;
|
|
538
549
|
const width = worldBounds.maxX - worldBounds.minX;
|
|
539
550
|
const depth = worldBounds.maxZ - worldBounds.minZ;
|
|
@@ -545,7 +556,19 @@ function DistrictFloor({ district, centerOffset, highlightColor }) {
|
|
|
545
556
|
const borderColor = highlightColor || '#475569';
|
|
546
557
|
const lineWidth = highlightColor ? 3 : 1;
|
|
547
558
|
const labelColor = highlightColor || '#cbd5e1';
|
|
548
|
-
|
|
559
|
+
// Interpolate text rotation and position based on growProgress
|
|
560
|
+
// Flat: -Math.PI / 2 (facing up), positioned at center of district
|
|
561
|
+
// Grown: -Math.PI / 6 (angled), positioned at edge of district
|
|
562
|
+
const flatRotationX = -Math.PI / 2;
|
|
563
|
+
const grownRotationX = -Math.PI / 6;
|
|
564
|
+
const textRotationX = flatRotationX + (grownRotationX - flatRotationX) * growProgress;
|
|
565
|
+
const flatY = 0.5;
|
|
566
|
+
const grownY = 1.5;
|
|
567
|
+
const textY = flatY + (grownY - flatY) * growProgress;
|
|
568
|
+
const flatZ = 0; // Center of district when flat
|
|
569
|
+
const grownZ = depth / 2 + 2; // Edge of district when grown
|
|
570
|
+
const textZ = flatZ + (grownZ - flatZ) * growProgress;
|
|
571
|
+
return (_jsxs("group", { position: [centerX, 0, centerZ], children: [_jsxs("lineSegments", { rotation: [-Math.PI / 2, 0, 0], position: [0, floorY, 0], renderOrder: -1, children: [_jsx("edgesGeometry", { args: [new THREE.PlaneGeometry(width, depth)], attach: "geometry" }), _jsx("lineBasicMaterial", { color: borderColor, linewidth: lineWidth, depthWrite: false })] }), highlightColor && (_jsxs("mesh", { rotation: [-Math.PI / 2, 0, 0], position: [0, floorY - 0.1, 0], renderOrder: -2, children: [_jsx("planeGeometry", { args: [width, depth] }), _jsx("meshBasicMaterial", { color: highlightColor, transparent: true, opacity: 0.15, depthWrite: false })] })), district.label && (_jsx(Text, { position: [0, textY, textZ], rotation: [textRotationX, 0, 0], fontSize: Math.min(3, width / 6), color: labelColor, anchorX: "center", anchorY: "middle", outlineWidth: 0.1, outlineColor: "#0f172a", children: dirName }))] }));
|
|
549
572
|
}
|
|
550
573
|
let cameraApi = null;
|
|
551
574
|
export function resetCamera() {
|
|
@@ -1297,7 +1320,7 @@ function CityScene({ cityData, onBuildingHover, onBuildingClick, hoveredBuilding
|
|
|
1297
1320
|
// Use buildingFocusColor (synced with animation) instead of focusColor prop
|
|
1298
1321
|
// Focus color takes priority, then highlight layer color
|
|
1299
1322
|
const districtColor = (isFocused && buildingFocusColor) ? buildingFocusColor : highlightLayerColor;
|
|
1300
|
-
return (_jsx(DistrictFloor, { district: district, centerOffset: centerOffset, opacity: 1, highlightColor: districtColor }, district.path));
|
|
1323
|
+
return (_jsx(DistrictFloor, { district: district, centerOffset: centerOffset, opacity: 1, highlightColor: districtColor, growProgress: growProgress }, district.path));
|
|
1301
1324
|
}), _jsx(InstancedBuildings, { buildings: cityData.buildings, centerOffset: centerOffset, onHover: onBuildingHover, onClick: onBuildingClick, hoveredIndex: hoveredIndex, selectedIndex: selectedIndex, growProgress: growProgress, animationConfig: animationConfig, heightScaling: heightScaling, linearScale: linearScale, staggerIndices: staggerIndices, focusDirectory: buildingFocusDirectory, highlightLayers: highlightLayers, isolationMode: isolationMode }), _jsx(BuildingIcons, { buildings: cityData.buildings, centerOffset: centerOffset, growProgress: growProgress, heightScaling: heightScaling, linearScale: linearScale, highlightLayers: highlightLayers, isolationMode: isolationMode, hasActiveHighlights: activeHighlights, staggerIndices: staggerIndices, springDuration: springDuration, staggerDelay: animationConfig.staggerDelay || 15 })] }));
|
|
1302
1325
|
}
|
|
1303
1326
|
/**
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
/**
|
|
2
2
|
* FileCity3D - 3D visualization component
|
|
3
3
|
*/
|
|
4
|
-
export { FileCity3D, resetCamera,
|
|
5
|
-
export type { FileCity3DProps, AnimationConfig, HighlightLayer, HighlightItem, IsolationMode, HeightScaling, CityData, CityBuilding, CityDistrict,
|
|
4
|
+
export { FileCity3D, resetCamera, getCameraAngle, getCameraTarget, getCameraTilt, rotateCameraTo, rotateCameraBy, tiltCameraTo, tiltCameraBy, moveCameraTo, setCameraTarget, } from './FileCity3D';
|
|
5
|
+
export type { FileCity3DProps, AnimationConfig, HighlightLayer, HighlightItem, IsolationMode, HeightScaling, CityData, CityBuilding, CityDistrict, } from './FileCity3D';
|
|
6
6
|
//# sourceMappingURL=index.d.ts.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../../src/components/FileCity3D/index.ts"],"names":[],"mappings":"AAAA;;GAEG;AAEH,OAAO,
|
|
1
|
+
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../../src/components/FileCity3D/index.ts"],"names":[],"mappings":"AAAA;;GAEG;AAEH,OAAO,EACL,UAAU,EACV,WAAW,EACX,cAAc,EACd,eAAe,EACf,aAAa,EACb,cAAc,EACd,cAAc,EACd,YAAY,EACZ,YAAY,EACZ,YAAY,EACZ,eAAe,GAChB,MAAM,cAAc,CAAC;AACtB,YAAY,EACV,eAAe,EACf,eAAe,EACf,cAAc,EACd,aAAa,EACb,aAAa,EACb,aAAa,EACb,QAAQ,EACR,YAAY,EACZ,YAAY,GACb,MAAM,cAAc,CAAC"}
|
|
@@ -1,4 +1,4 @@
|
|
|
1
1
|
/**
|
|
2
2
|
* FileCity3D - 3D visualization component
|
|
3
3
|
*/
|
|
4
|
-
export { FileCity3D, resetCamera,
|
|
4
|
+
export { FileCity3D, resetCamera, getCameraAngle, getCameraTarget, getCameraTilt, rotateCameraTo, rotateCameraBy, tiltCameraTo, tiltCameraBy, moveCameraTo, setCameraTarget, } from './FileCity3D';
|
package/package.json
CHANGED
|
@@ -745,12 +745,12 @@ function AnimatedIcon({
|
|
|
745
745
|
staggerDelayMs,
|
|
746
746
|
springDuration,
|
|
747
747
|
}: AnimatedIconProps) {
|
|
748
|
-
const
|
|
748
|
+
const meshRef = useRef<THREE.Mesh>(null);
|
|
749
749
|
const startTimeRef = useRef<number | null>(null);
|
|
750
|
-
const materialRef = useRef<THREE.
|
|
750
|
+
const materialRef = useRef<THREE.MeshBasicMaterial>(null);
|
|
751
751
|
|
|
752
752
|
useFrame(({ clock }) => {
|
|
753
|
-
if (!
|
|
753
|
+
if (!meshRef.current) return;
|
|
754
754
|
|
|
755
755
|
if (startTimeRef.current === null && growProgress > 0) {
|
|
756
756
|
startTimeRef.current = clock.elapsedTime * 1000;
|
|
@@ -775,31 +775,48 @@ function AnimatedIcon({
|
|
|
775
775
|
const baseOffset = 0.2;
|
|
776
776
|
const height = animProgress * targetHeight + minHeight;
|
|
777
777
|
const buildingTop = height + baseOffset;
|
|
778
|
-
const yPosition = buildingTop + iconSize / 2 + 2;
|
|
779
778
|
|
|
780
|
-
|
|
779
|
+
// When flat (animProgress=0): icon lies flat at ground level
|
|
780
|
+
// When grown (animProgress=1): icon floats above building
|
|
781
|
+
const flatY = minHeight + baseOffset + 0.5;
|
|
782
|
+
const grownY = buildingTop + iconSize / 2 + 2;
|
|
783
|
+
const yPosition = flatY + (grownY - flatY) * animProgress;
|
|
784
|
+
|
|
785
|
+
meshRef.current.position.y = yPosition;
|
|
786
|
+
|
|
787
|
+
// Rotate from flat (facing up) to upright (facing camera-ish)
|
|
788
|
+
// Flat: -Math.PI / 2 (facing up)
|
|
789
|
+
// Grown: 0 (facing forward)
|
|
790
|
+
const flatRotationX = -Math.PI / 2;
|
|
791
|
+
const grownRotationX = 0;
|
|
792
|
+
meshRef.current.rotation.x = flatRotationX + (grownRotationX - flatRotationX) * animProgress;
|
|
781
793
|
|
|
782
794
|
if (materialRef.current) {
|
|
783
|
-
|
|
795
|
+
// Show icons even when flat, fade out only slightly
|
|
796
|
+
const minOpacity = 0.8;
|
|
797
|
+
const effectiveOpacity = minOpacity + (1 - minOpacity) * animProgress;
|
|
798
|
+
materialRef.current.opacity = opacity * effectiveOpacity;
|
|
784
799
|
}
|
|
785
800
|
});
|
|
786
801
|
|
|
787
802
|
return (
|
|
788
|
-
<
|
|
789
|
-
ref={
|
|
803
|
+
<mesh
|
|
804
|
+
ref={meshRef}
|
|
790
805
|
position={[x, 0, z]}
|
|
791
806
|
scale={[iconSize, iconSize, 1]}
|
|
792
807
|
raycast={() => null}
|
|
793
808
|
>
|
|
794
|
-
<
|
|
809
|
+
<planeGeometry args={[1, 1]} />
|
|
810
|
+
<meshBasicMaterial
|
|
795
811
|
ref={materialRef}
|
|
796
812
|
map={texture}
|
|
797
813
|
transparent
|
|
798
|
-
opacity={0}
|
|
814
|
+
opacity={0.8}
|
|
799
815
|
depthTest={true}
|
|
800
816
|
depthWrite={false}
|
|
817
|
+
side={THREE.DoubleSide}
|
|
801
818
|
/>
|
|
802
|
-
</
|
|
819
|
+
</mesh>
|
|
803
820
|
);
|
|
804
821
|
}
|
|
805
822
|
|
|
@@ -871,9 +888,7 @@ function BuildingIcons({
|
|
|
871
888
|
staggerDelay,
|
|
872
889
|
]);
|
|
873
890
|
|
|
874
|
-
//
|
|
875
|
-
if (growProgress < 0.1) return null;
|
|
876
|
-
|
|
891
|
+
// Icons are now always rendered (flat or grown)
|
|
877
892
|
return (
|
|
878
893
|
<>
|
|
879
894
|
{buildingsWithIcons.map(
|
|
@@ -884,7 +899,7 @@ function BuildingIcons({
|
|
|
884
899
|
|
|
885
900
|
// Icon size based on building dimensions
|
|
886
901
|
const [width] = building.dimensions;
|
|
887
|
-
const baseSize = Math.max(width *
|
|
902
|
+
const baseSize = Math.max(width * 1.2, 8);
|
|
888
903
|
const heightBoost = Math.min(targetHeight / 20, 3);
|
|
889
904
|
const iconSize = (baseSize + heightBoost) * (icon.size || 1);
|
|
890
905
|
|
|
@@ -916,9 +931,10 @@ interface DistrictFloorProps {
|
|
|
916
931
|
centerOffset: { x: number; z: number };
|
|
917
932
|
opacity: number;
|
|
918
933
|
highlightColor?: string | null;
|
|
934
|
+
growProgress: number;
|
|
919
935
|
}
|
|
920
936
|
|
|
921
|
-
function DistrictFloor({ district, centerOffset, highlightColor }: DistrictFloorProps) {
|
|
937
|
+
function DistrictFloor({ district, centerOffset, highlightColor, growProgress }: DistrictFloorProps) {
|
|
922
938
|
const { worldBounds } = district;
|
|
923
939
|
const width = worldBounds.maxX - worldBounds.minX;
|
|
924
940
|
const depth = worldBounds.maxZ - worldBounds.minZ;
|
|
@@ -934,6 +950,21 @@ function DistrictFloor({ district, centerOffset, highlightColor }: DistrictFloor
|
|
|
934
950
|
const lineWidth = highlightColor ? 3 : 1;
|
|
935
951
|
const labelColor = highlightColor || '#cbd5e1';
|
|
936
952
|
|
|
953
|
+
// Interpolate text rotation and position based on growProgress
|
|
954
|
+
// Flat: -Math.PI / 2 (facing up), positioned at center of district
|
|
955
|
+
// Grown: -Math.PI / 6 (angled), positioned at edge of district
|
|
956
|
+
const flatRotationX = -Math.PI / 2;
|
|
957
|
+
const grownRotationX = -Math.PI / 6;
|
|
958
|
+
const textRotationX = flatRotationX + (grownRotationX - flatRotationX) * growProgress;
|
|
959
|
+
|
|
960
|
+
const flatY = 0.5;
|
|
961
|
+
const grownY = 1.5;
|
|
962
|
+
const textY = flatY + (grownY - flatY) * growProgress;
|
|
963
|
+
|
|
964
|
+
const flatZ = 0; // Center of district when flat
|
|
965
|
+
const grownZ = depth / 2 + 2; // Edge of district when grown
|
|
966
|
+
const textZ = flatZ + (grownZ - flatZ) * growProgress;
|
|
967
|
+
|
|
937
968
|
return (
|
|
938
969
|
<group position={[centerX, 0, centerZ]}>
|
|
939
970
|
{/* Border outline */}
|
|
@@ -952,8 +983,8 @@ function DistrictFloor({ district, centerOffset, highlightColor }: DistrictFloor
|
|
|
952
983
|
|
|
953
984
|
{district.label && (
|
|
954
985
|
<Text
|
|
955
|
-
position={[0,
|
|
956
|
-
rotation={[
|
|
986
|
+
position={[0, textY, textZ]}
|
|
987
|
+
rotation={[textRotationX, 0, 0]}
|
|
957
988
|
fontSize={Math.min(3, width / 6)}
|
|
958
989
|
color={labelColor}
|
|
959
990
|
anchorX="center"
|
|
@@ -2018,6 +2049,7 @@ function CityScene({
|
|
|
2018
2049
|
centerOffset={centerOffset}
|
|
2019
2050
|
opacity={1}
|
|
2020
2051
|
highlightColor={districtColor}
|
|
2052
|
+
growProgress={growProgress}
|
|
2021
2053
|
/>
|
|
2022
2054
|
);
|
|
2023
2055
|
})}
|
|
@@ -2,7 +2,19 @@
|
|
|
2
2
|
* FileCity3D - 3D visualization component
|
|
3
3
|
*/
|
|
4
4
|
|
|
5
|
-
export {
|
|
5
|
+
export {
|
|
6
|
+
FileCity3D,
|
|
7
|
+
resetCamera,
|
|
8
|
+
getCameraAngle,
|
|
9
|
+
getCameraTarget,
|
|
10
|
+
getCameraTilt,
|
|
11
|
+
rotateCameraTo,
|
|
12
|
+
rotateCameraBy,
|
|
13
|
+
tiltCameraTo,
|
|
14
|
+
tiltCameraBy,
|
|
15
|
+
moveCameraTo,
|
|
16
|
+
setCameraTarget,
|
|
17
|
+
} from './FileCity3D';
|
|
6
18
|
export type {
|
|
7
19
|
FileCity3DProps,
|
|
8
20
|
AnimationConfig,
|
|
@@ -13,5 +25,4 @@ export type {
|
|
|
13
25
|
CityData,
|
|
14
26
|
CityBuilding,
|
|
15
27
|
CityDistrict,
|
|
16
|
-
RotateOptions,
|
|
17
28
|
} from './FileCity3D';
|