there repository
This commit is contained in:
parent
71cd1061cb
commit
5d0e5f7f1c
10 changed files with 257 additions and 2 deletions
|
@ -74,6 +74,7 @@ export class App {
|
|||
this.app.use("/", route.router);
|
||||
});
|
||||
}
|
||||
|
||||
async loadAppDependencies() {
|
||||
await new DataBaseConnectUseCase().call();
|
||||
await new CheckAndCreateStaticFilesFolderUseCase().call();
|
||||
|
|
|
@ -26,6 +26,7 @@
|
|||
"react-scripts": "5.0.1",
|
||||
"sass": "^1.66.1",
|
||||
"socket.io-client": "^4.7.2",
|
||||
"three": "^0.159.0",
|
||||
"typescript": "^4.9.5",
|
||||
"uuid": "^9.0.0",
|
||||
"web-vitals": "^2.1.4"
|
||||
|
|
|
@ -45,4 +45,10 @@ export const ArrayExtensions = () => {
|
|||
return this.length !== 0;
|
||||
};
|
||||
}
|
||||
if ([].hasIncludeElement === undefined) {
|
||||
// eslint-disable-next-line no-extend-native
|
||||
Array.prototype.hasIncludeElement = function (element) {
|
||||
return this.indexOf(element) !== -1;
|
||||
};
|
||||
}
|
||||
};
|
||||
|
|
|
@ -1,19 +1,27 @@
|
|||
import { ArrayExtensions } from "./array";
|
||||
import { MapExtensions } from "./map";
|
||||
import { StringExtensions } from "./string";
|
||||
|
||||
export type CallBackFunction = <T>(value: T) => void;
|
||||
|
||||
declare global {
|
||||
interface Array<T> {
|
||||
// @strict: The parameter is determined whether the arrays must be exactly the same in content and order of this relationship or simply follow the same requirements.
|
||||
equals(array: Array<T>, strict: boolean): boolean;
|
||||
lastElement(): T | undefined;
|
||||
isEmpty(): boolean;
|
||||
isNotEmpty():boolean;
|
||||
isNotEmpty(): boolean;
|
||||
hasIncludeElement(element: T): boolean;
|
||||
}
|
||||
interface String {
|
||||
isEmpty(): boolean;
|
||||
}
|
||||
interface Map<K, V> {
|
||||
addValueOrMakeCallback(key: K, value: V, callBack: CallBackFunction): void;
|
||||
}
|
||||
}
|
||||
export const extensions = () => {
|
||||
ArrayExtensions();
|
||||
StringExtensions();
|
||||
MapExtensions();
|
||||
};
|
||||
|
|
15
ui/src/core/extensions/map.ts
Normal file
15
ui/src/core/extensions/map.ts
Normal file
|
@ -0,0 +1,15 @@
|
|||
export const MapExtensions = () => {
|
||||
if (Map.prototype.addValueOrMakeCallback === undefined) {
|
||||
// eslint-disable-next-line no-extend-native
|
||||
Map.prototype.addValueOrMakeCallback = function (key, value, fn) {
|
||||
if (this.has(key)) {
|
||||
this.set(key, value);
|
||||
fn(this);
|
||||
return;
|
||||
} else {
|
||||
this.set(key, value);
|
||||
}
|
||||
};
|
||||
}
|
||||
};
|
||||
Object();
|
26
ui/src/core/hook/key_listner.tsx
Normal file
26
ui/src/core/hook/key_listner.tsx
Normal file
|
@ -0,0 +1,26 @@
|
|||
import React from "react";
|
||||
|
||||
export const useKeyLister = (fn: Function) => {
|
||||
// eslint-disable-next-line react-hooks/exhaustive-deps
|
||||
const pressed = new Map();
|
||||
|
||||
const registerKeyPress = React.useCallback(
|
||||
(event: KeyboardEvent, codes: string[], callBack: Function) => {
|
||||
if (codes.hasIncludeElement(event.code)) {
|
||||
pressed.addValueOrMakeCallback(event.code, event.type, (e) => {
|
||||
if (Array.from(pressed.values()).equals(["keydown", "keydown"], false)) {
|
||||
callBack();
|
||||
}
|
||||
});
|
||||
}
|
||||
},
|
||||
[pressed]
|
||||
);
|
||||
|
||||
React.useEffect(() => {
|
||||
window.addEventListener("keyup", (e) => registerKeyPress(e, ["KeyQ", "KeyW"], () => fn));
|
||||
window.addEventListener("keydown", (e) => registerKeyPress(e, ["KeyQ", "KeyW"], () => {}));
|
||||
}, [fn, registerKeyPress]);
|
||||
|
||||
return [];
|
||||
};
|
144
ui/src/core/repository/core_there_repository.ts
Normal file
144
ui/src/core/repository/core_there_repository.ts
Normal file
|
@ -0,0 +1,144 @@
|
|||
import {
|
||||
DirectionalLight,
|
||||
Object3D,
|
||||
PerspectiveCamera,
|
||||
Scene,
|
||||
WebGLRenderer,
|
||||
AmbientLight,
|
||||
Vector3,
|
||||
MeshBasicMaterial,
|
||||
Mesh,
|
||||
BoxGeometry,
|
||||
Object3DEventMap,
|
||||
Box3,
|
||||
Sphere,
|
||||
} from "three";
|
||||
import { OrbitControls } from "three/examples/jsm/controls/OrbitControls";
|
||||
|
||||
export class CoreThereRepository {
|
||||
scene = new Scene();
|
||||
camera: PerspectiveCamera;
|
||||
webGlRender: WebGLRenderer;
|
||||
htmlCanvasRef: HTMLCanvasElement;
|
||||
constructor(htmlCanvasRef: HTMLCanvasElement) {
|
||||
const renderer = new WebGLRenderer({
|
||||
canvas: htmlCanvasRef as HTMLCanvasElement,
|
||||
antialias: true,
|
||||
alpha: true,
|
||||
});
|
||||
const aspectCamera = window.outerWidth / window.outerHeight;
|
||||
this.camera = new PerspectiveCamera(800, aspectCamera, 0.1, 10000);
|
||||
this.webGlRender = renderer;
|
||||
this.htmlCanvasRef = htmlCanvasRef;
|
||||
this.init();
|
||||
}
|
||||
addCube(num: number, translateTo: string = "X") {
|
||||
const geometry = new BoxGeometry(1, 1, 1);
|
||||
const material = new MeshBasicMaterial({ color: 0x00ff00 });
|
||||
const cube = new Mesh(geometry, material);
|
||||
cube.name = "Cube" + String(num);
|
||||
// cube.translateX(position.x);
|
||||
// cube.translateY(position.y);
|
||||
// cube.translateZ(position.z);
|
||||
eval(`cube.translate${translateTo}(${num * 10})`);
|
||||
this.scene.add(cube);
|
||||
}
|
||||
init() {
|
||||
const directionalLight = new DirectionalLight(0xffffff, 0.2);
|
||||
directionalLight.castShadow = true;
|
||||
directionalLight.position.set(-1, 2, 4);
|
||||
this.scene.add(directionalLight);
|
||||
const ambientLight = new AmbientLight(0xffffff, 0.7);
|
||||
this.scene.add(ambientLight);
|
||||
|
||||
this.addCube(1);
|
||||
this.addCube(2);
|
||||
this.addCube(3);
|
||||
this.addCube(4);
|
||||
|
||||
const onResize = () => {
|
||||
this.camera.aspect = window.outerWidth / window.outerHeight;
|
||||
this.camera.updateProjectionMatrix();
|
||||
this.webGlRender.setSize(window.outerWidth, window.outerHeight);
|
||||
};
|
||||
window.addEventListener("resize", onResize, false);
|
||||
new OrbitControls(this.camera, this.htmlCanvasRef);
|
||||
}
|
||||
render() {
|
||||
this.webGlRender.setSize(window.outerWidth, window.outerHeight);
|
||||
this.webGlRender.setAnimationLoop(() => {
|
||||
this.webGlRender.render(this.scene, this.camera);
|
||||
});
|
||||
}
|
||||
getAllSceneNameModels(): string[] {
|
||||
return this.scene.children.filter((el) => el.name !== "").map((el) => el.name);
|
||||
}
|
||||
getObjectsAtName(name: string): Object3D<Object3DEventMap> {
|
||||
return this.scene.children.filter((el) => el.name === name)[0];
|
||||
}
|
||||
fitCameraToCenteredObject(objects: string[], offset = 4) {
|
||||
// https://wejn.org/2020/12/cracking-the-threejs-object-fitting-nut/
|
||||
const boundingBox = new Box3().setFromPoints(
|
||||
objects.map((el) => this.getObjectsAtName(el)).map((el) => el.position)
|
||||
);
|
||||
|
||||
var size = new Vector3();
|
||||
boundingBox.getSize(size);
|
||||
|
||||
const fov = this.camera.fov * (Math.PI / 180);
|
||||
const fovh = 2 * Math.atan(Math.tan(fov / 2) * this.camera.aspect);
|
||||
let dx = size.z / 2 + Math.abs(size.x / 2 / Math.tan(fovh / 2));
|
||||
let dy = size.z / 2 + Math.abs(size.y / 2 / Math.tan(fov / 2));
|
||||
let cameraZ = Math.max(dx, dy);
|
||||
|
||||
if (offset !== undefined && offset !== 0) cameraZ *= offset;
|
||||
|
||||
this.camera.position.set(0, 0, cameraZ);
|
||||
|
||||
const minZ = boundingBox.min.z;
|
||||
const cameraToFarEdge = minZ < 0 ? -minZ + cameraZ : cameraZ - minZ;
|
||||
|
||||
this.camera.far = cameraToFarEdge * 3;
|
||||
this.camera.updateProjectionMatrix();
|
||||
let orbitControls = new OrbitControls(this.camera, this.htmlCanvasRef);
|
||||
|
||||
orbitControls.maxDistance = cameraToFarEdge * 2;
|
||||
new OrbitControls(this.camera, this.htmlCanvasRef);
|
||||
}
|
||||
|
||||
fitSelectedObjectToScreen(objects: string[]) {
|
||||
//https://stackoverflow.com/questions/14614252/how-to-fit-camera-to-object
|
||||
|
||||
let boundBox = new Box3().setFromPoints(objects.map((el) => this.getObjectsAtName(el)).map((el) => el.position));
|
||||
let boundSphere = boundBox.getBoundingSphere(new Sphere());
|
||||
let vFoV = this.camera.getEffectiveFOV();
|
||||
let hFoV = this.camera.fov * this.camera.aspect;
|
||||
|
||||
let FoV = Math.min(vFoV, hFoV);
|
||||
let FoV2 = FoV / 2;
|
||||
|
||||
let dir = new Vector3();
|
||||
this.camera.getWorldDirection(dir);
|
||||
|
||||
let bsWorld = boundSphere.center.clone();
|
||||
|
||||
let th = (FoV2 * Math.PI) / 180.0;
|
||||
let sina = Math.sin(th);
|
||||
let R = boundSphere.radius;
|
||||
let FL = R / sina;
|
||||
|
||||
let cameraDir = new Vector3();
|
||||
|
||||
let cameraOffs = cameraDir.clone();
|
||||
console.log(cameraOffs);
|
||||
cameraOffs.multiplyScalar(-FL);
|
||||
console.log(-FL);
|
||||
let newCameraPos = bsWorld.clone().add(cameraOffs);
|
||||
console.log(newCameraPos);
|
||||
this.camera.translateX(newCameraPos.x);
|
||||
this.camera.translateY(newCameraPos.y);
|
||||
this.camera.translateZ(newCameraPos.z);
|
||||
this.camera.lookAt(bsWorld);
|
||||
new OrbitControls(this.camera, this.htmlCanvasRef);
|
||||
}
|
||||
}
|
43
ui/src/features/scene_manager/scene_manager.tsx
Normal file
43
ui/src/features/scene_manager/scene_manager.tsx
Normal file
|
@ -0,0 +1,43 @@
|
|||
import * as React from "react";
|
||||
import { CoreThereRepository } from "../../core/repository/core_there_repository";
|
||||
|
||||
const useKeyLister = (fn: Function) => {
|
||||
const pressed = new Map();
|
||||
|
||||
const registerKeyPress = React.useCallback(
|
||||
(event: KeyboardEvent, codes: string[], callBack: Function) => {
|
||||
if (codes.hasIncludeElement(event.code)) {
|
||||
pressed.addValueOrMakeCallback(event.code, event.type, (e) => {
|
||||
if (Array.from(pressed.values()).equals(["keydown", "keydown"], false)) {
|
||||
callBack();
|
||||
}
|
||||
});
|
||||
}
|
||||
},
|
||||
[pressed]
|
||||
);
|
||||
|
||||
React.useEffect(() => {
|
||||
window.addEventListener("keyup", (e) => registerKeyPress(e, ["KeyQ", "KeyW"], () => fn));
|
||||
window.addEventListener("keydown", (e) => registerKeyPress(e, ["KeyQ", "KeyW"], () => {}));
|
||||
}, [fn, registerKeyPress]);
|
||||
|
||||
return [];
|
||||
};
|
||||
export function SceneManger() {
|
||||
const canvasRef = React.useRef<HTMLCanvasElement>(null);
|
||||
let thereRepository: null | CoreThereRepository = null;
|
||||
React.useEffect(() => {
|
||||
// eslint-disable-next-line react-hooks/exhaustive-deps
|
||||
thereRepository = new CoreThereRepository(canvasRef.current as HTMLCanvasElement);
|
||||
thereRepository.render();
|
||||
// thereRepository.fitSelectedObjectToScreen(thereRepository.getAllSceneNameModels());
|
||||
thereRepository.fitCameraToCenteredObject(thereRepository.getAllSceneNameModels());
|
||||
});
|
||||
|
||||
return (
|
||||
<div>
|
||||
<canvas ref={canvasRef} />
|
||||
</div>
|
||||
);
|
||||
}
|
9
ui/src/index.test.tsx
Normal file
9
ui/src/index.test.tsx
Normal file
|
@ -0,0 +1,9 @@
|
|||
import React from "react";
|
||||
import { render, screen } from "@testing-library/react";
|
||||
import { SceneManger } from "./features/scene_manager/scene_manager";
|
||||
|
||||
test("Content contains var image", () => {
|
||||
render(<SceneManger />);
|
||||
const car = screen;
|
||||
expect(car).toBeInTheDocument();
|
||||
});
|
|
@ -7,6 +7,7 @@ import { RouterProvider } from "react-router-dom";
|
|||
import { router } from "./core/routers/routers";
|
||||
import { SocketLister } from "./features/socket_lister/socket_lister";
|
||||
import { extensions } from "./core/extensions/extensions";
|
||||
import { SceneManger } from "./features/scene_manager/scene_manager";
|
||||
|
||||
extensions();
|
||||
const root = ReactDOM.createRoot(document.getElementById("root") as HTMLElement);
|
||||
|
@ -14,7 +15,8 @@ const root = ReactDOM.createRoot(document.getElementById("root") as HTMLElement)
|
|||
root.render(
|
||||
<>
|
||||
<SocketLister>
|
||||
<RouterProvider router={router} />
|
||||
{/* <RouterProvider router={router} /> */}
|
||||
<SceneManger />
|
||||
</SocketLister>
|
||||
</>
|
||||
);
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue