From 9028186a7403b4e5486cc709caedbcbe85ae37a0 Mon Sep 17 00:00:00 2001 From: IDONTSUDO Date: Tue, 23 Jan 2024 17:26:59 +0300 Subject: [PATCH] added scene manager --- .../src/core/middlewares/validation_model.ts | 2 +- ...d_json_to_plain_instance_class_scenario.ts | 2 +- .../core/repository/core_there_repository.ts | 2 + ui/src/core/store/base_store.ts | 7 +- .../all_projects/data/project_repository.ts | 6 +- .../presentation/all_projects_screen.tsx | 4 +- .../presentation/all_projects_store.ts | 17 +- .../data/create_pipeline_repository.ts | 18 +- .../presentation/create_pipeline_screen.tsx | 7 +- .../presentation/create_pipeline_store.ts | 1 - .../create_process/data/process_repostiory.ts | 7 +- .../presentation/create_process_screen.tsx | 28 +-- .../create_project_repository.ts | 13 +- .../create_project_instance.tsx | 13 +- .../create_project_instance_repository.ts | 19 +- .../create_project_instance_store.ts | 34 +++- .../new_project_model.ts | 17 ++ .../create_trigger/data/trigger_repository.ts | 7 +- .../presentation/trigger_store.ts | 2 +- .../components/static_asset_item_view.tsx | 14 -- .../scene_manager/data/scene_repository.ts | 19 ++ .../model/robossembler_assets.ts | 160 ++++++++++++++++ .../scene_manager/model/scene_assets.ts | 164 ++++++++++++++++ .../scene_manager/model/scene_view.ts | 27 +++ .../presentation/components/scene_menu.tsx | 27 +++ .../presentation/components/scene_widget.tsx | 68 +++++++ .../components/static_asset_item_view.tsx | 45 +++++ .../presentation/scene_manager.tsx | 162 ++++++++++++++++ .../presentation/scene_manager_store.ts | 179 ++++++++++++++++++ .../features/scene_manager/scene_manager.tsx | 60 ------ .../scene_manager/scene_manager_store.ts | 96 ---------- .../data/select_project_repository.ts | 4 +- .../presentation/select_project.tsx | 5 +- .../presentation/select_project_store.ts | 7 +- 34 files changed, 962 insertions(+), 281 deletions(-) create mode 100644 ui/src/features/create_project_instance/new_project_model.ts delete mode 100644 ui/src/features/scene_manager/components/static_asset_item_view.tsx create mode 100644 ui/src/features/scene_manager/data/scene_repository.ts create mode 100644 ui/src/features/scene_manager/model/robossembler_assets.ts create mode 100644 ui/src/features/scene_manager/model/scene_assets.ts create mode 100644 ui/src/features/scene_manager/model/scene_view.ts create mode 100644 ui/src/features/scene_manager/presentation/components/scene_menu.tsx create mode 100644 ui/src/features/scene_manager/presentation/components/scene_widget.tsx create mode 100644 ui/src/features/scene_manager/presentation/components/static_asset_item_view.tsx create mode 100644 ui/src/features/scene_manager/presentation/scene_manager.tsx create mode 100644 ui/src/features/scene_manager/presentation/scene_manager_store.ts delete mode 100644 ui/src/features/scene_manager/scene_manager.tsx delete mode 100644 ui/src/features/scene_manager/scene_manager_store.ts diff --git a/server/src/core/middlewares/validation_model.ts b/server/src/core/middlewares/validation_model.ts index ec5986d..91525e2 100644 --- a/server/src/core/middlewares/validation_model.ts +++ b/server/src/core/middlewares/validation_model.ts @@ -6,7 +6,7 @@ export const validationModelMiddleware = ( type: any, value = "body", skipMissingProperties = false, - whitelist = true, + whitelist = false, forbidNonWhitelisted = true ): RequestHandler => { return (req, res, next) => { diff --git a/server/src/core/scenarios/read_file_and_json_to_plain_instance_class_scenario.ts b/server/src/core/scenarios/read_file_and_json_to_plain_instance_class_scenario.ts index 8a93795..03738ba 100644 --- a/server/src/core/scenarios/read_file_and_json_to_plain_instance_class_scenario.ts +++ b/server/src/core/scenarios/read_file_and_json_to_plain_instance_class_scenario.ts @@ -3,7 +3,7 @@ import { ReadFileAndParseJsonUseCase } from "../usecases/read_file_and_parse_jso import { Result } from "../helpers/result"; import { validate, ValidationError } from "class-validator"; const skipMissingProperties = false, - whitelist = true, + whitelist = false, forbidNonWhitelisted = true; export class ReadingJsonFileAndConvertingToInstanceClassScenario { diff --git a/ui/src/core/repository/core_there_repository.ts b/ui/src/core/repository/core_there_repository.ts index 54be617..df7dca3 100644 --- a/ui/src/core/repository/core_there_repository.ts +++ b/ui/src/core/repository/core_there_repository.ts @@ -100,12 +100,14 @@ export class CoreThereRepository extends TypedEvent { loadInstances(robossemblerAssets: RobossemblerAssets) { robossemblerAssets.instances.forEach(async (el) => { + console.log(el); if (el instanceof InstanceRgbCamera) { const cameraModel = CameraViewModel.fromInstanceRgbCamera(el); cameraModel.mapPerspectiveCamera(this.htmlSceneWidth, this.htmlSceneHeight).forEach((el) => this.scene.add(el)); this.emit(cameraModel); } if (el instanceof SceneSimpleObject) { + console.log(el); const asset = robossemblerAssets.getAssetAtInstance(el.instanceAt as string); this.loader([asset.meshPath], () => {}, asset.name); } diff --git a/ui/src/core/store/base_store.ts b/ui/src/core/store/base_store.ts index 3149052..f771f62 100644 --- a/ui/src/core/store/base_store.ts +++ b/ui/src/core/store/base_store.ts @@ -1,5 +1,6 @@ // TODO(IDONTSUDO): нужно переписать все запросы под BaseStore +import { NavigateFunction } from "react-router-dom"; import { Result } from "../helper/result"; import { UiBaseError } from "../model/ui_base_error"; import { HttpError } from "../repository/http_repository"; @@ -8,7 +9,7 @@ export type CoreError = HttpError | Error; export abstract class UiLoader { isLoading = false; - async loadingHelper(callBack: Promise>) { + async httpHelper(callBack: Promise>) { this.isLoading = true; const result = await callBack; @@ -25,7 +26,7 @@ export abstract class UiLoader { mapOk = async (property: string, callBack: Promise>) => { return ( - (await this.loadingHelper(callBack)) + (await this.httpHelper(callBack)) // eslint-disable-next-line array-callback-return .map((el) => { // @ts-ignore @@ -43,7 +44,7 @@ export class SimpleErrorState extends UiLoader { export abstract class UiErrorState extends UiLoader { abstract errorHandingStrategy: (error: T) => void; - abstract init(): Promise; + abstract init(navigate?: NavigateFunction): Promise; dispose() {} errors: UiBaseError[] = []; } diff --git a/ui/src/features/all_projects/data/project_repository.ts b/ui/src/features/all_projects/data/project_repository.ts index 00950ec..4cc8038 100644 --- a/ui/src/features/all_projects/data/project_repository.ts +++ b/ui/src/features/all_projects/data/project_repository.ts @@ -4,13 +4,13 @@ import { IProjectModel } from "../model/project_model"; export class ProjectRepository extends HttpRepository { async getAllProject() { - return this.jsonRequest(HttpMethod.GET, "/project_instance"); + return this._jsonRequest(HttpMethod.GET, "/project_instance"); } async getActivePipeline() { - return this.jsonRequest(HttpMethod.GET, "/realtime"); + return this._jsonRequest(HttpMethod.GET, "/realtime"); } async setActivePipeline(id: string) { - return this.jsonRequest(HttpMethod.POST, `/project_instance/set/active/project?id=${id}`); + return this._jsonRequest(HttpMethod.POST, `/project_instance/set/active/project?id=${id}`); } } diff --git a/ui/src/features/all_projects/presentation/all_projects_screen.tsx b/ui/src/features/all_projects/presentation/all_projects_screen.tsx index b55ef8f..92bcea5 100644 --- a/ui/src/features/all_projects/presentation/all_projects_screen.tsx +++ b/ui/src/features/all_projects/presentation/all_projects_screen.tsx @@ -32,12 +32,12 @@ export const AllProjectScreen: React.FunctionComponent = observer(() => {
- {allProjectStore.activePipeline?.projectUUID ?? "loading"} + {allProjectStore.activePipeline?.projectId ?? "loading"}
{allProjectStore.projectsModels?.map((el) => { return ( diff --git a/ui/src/features/all_projects/presentation/all_projects_store.ts b/ui/src/features/all_projects/presentation/all_projects_store.ts index ce98ff6..7e512dc 100644 --- a/ui/src/features/all_projects/presentation/all_projects_store.ts +++ b/ui/src/features/all_projects/presentation/all_projects_store.ts @@ -35,28 +35,15 @@ export class AllProjectStore extends SimpleErrorState { async getActiveProject(): Promise { await this.mapOk("activePipeline", this.repository.getActivePipeline()); } - async foo(): Promise { - this.isLoading = true; - const result = await this.repository.getActivePipeline(); - result.fold( - (success) => { - this.activePipeline = success; - this.isLoading = false; - }, - (_error) => { - this.isError = true; - } - ); - } async init() { await Promise.all([this.getProjects(), this.getActiveProject()]); await this.projectViewGenerate(); } projectViewGenerate() { - this.projectsModels = this.projectsModels?.filter((el) => el._id === this.activePipeline?.projectUUID); + this.projectsModels = this.projectsModels?.filter((el) => el._id !== this.activePipeline?.projectId); } async setPipelineActive(id: string) { - await this.loadingHelper(this.repository.setActivePipeline(id)); + await this.httpHelper(this.repository.setActivePipeline(id)); } } diff --git a/ui/src/features/create_pipeline/data/create_pipeline_repository.ts b/ui/src/features/create_pipeline/data/create_pipeline_repository.ts index c12a8f1..7532737 100644 --- a/ui/src/features/create_pipeline/data/create_pipeline_repository.ts +++ b/ui/src/features/create_pipeline/data/create_pipeline_repository.ts @@ -1,26 +1,18 @@ -import { - HttpMethod, - HttpRepository, -} from "../../../core/repository/http_repository"; +import { HttpMethod, HttpRepository } from "../../../core/repository/http_repository"; import { ITriggerModel } from "../../../core/model/trigger_model"; import { Result } from "../../../core/helper/result"; import { IProcess } from "../../create_process/model/process_model"; import { PipelineModelDataBase } from "../model/pipeline_model"; export class CreatePipelineRepository extends HttpRepository { - async savePipeline( - model: PipelineModelDataBase - ): Promise> { - return await this.jsonRequest(HttpMethod.POST, `/pipeline`, model); + async savePipeline(model: PipelineModelDataBase): Promise> { + return await this._jsonRequest(HttpMethod.POST, `/pipeline`, model); } async getTriggers(page = 1): Promise> { - return await this.jsonRequest(HttpMethod.GET, `/trigger?${page}`); + return await this._jsonRequest(HttpMethod.GET, `/trigger?${page}`); } async getProcessed(page = 1): Promise> { - return await this.jsonRequest( - HttpMethod.GET, - `/process?${page}` - ); + return await this._jsonRequest(HttpMethod.GET, `/process?${page}`); } } diff --git a/ui/src/features/create_pipeline/presentation/create_pipeline_screen.tsx b/ui/src/features/create_pipeline/presentation/create_pipeline_screen.tsx index c5f170c..aac631a 100644 --- a/ui/src/features/create_pipeline/presentation/create_pipeline_screen.tsx +++ b/ui/src/features/create_pipeline/presentation/create_pipeline_screen.tsx @@ -1,15 +1,20 @@ import * as React from "react"; import { Row, Button } from "antd"; import { LoadPage } from "../../../core/ui/pages/load_page"; -import { createPipelineStore } from "./create_pipeline_store"; + import { observer } from "mobx-react-lite"; import { Icon, List } from "../../../core/ui/list/list"; import { CreateTriggerScreenPath } from "../../create_trigger/presentation/create_trigger_screen"; import { CreateProcessScreenPath } from "../../create_process/presentation/create_process_screen"; +import { CreatePipelineStore } from "./create_pipeline_store"; +import { CreatePipelineRepository } from "../data/create_pipeline_repository"; export const CreatePipelineScreenPath = "/create_pipeline"; export const CreatePipelineScreen: React.FunctionComponent = observer(() => { + const [createPipelineStore] = React.useState(() => new CreatePipelineStore(new CreatePipelineRepository())); + + React.useEffect(() => {}, [createPipelineStore]); return ( <> { - await this.jsonRequest(HttpMethod.POST, "/process", model); + await this._jsonRequest(HttpMethod.POST, "/process", model); } } diff --git a/ui/src/features/create_process/presentation/create_process_screen.tsx b/ui/src/features/create_process/presentation/create_process_screen.tsx index 27159ce..0bf13d7 100644 --- a/ui/src/features/create_process/presentation/create_process_screen.tsx +++ b/ui/src/features/create_process/presentation/create_process_screen.tsx @@ -1,18 +1,11 @@ import * as React from "react"; import { processStore } from "./logic/process_store"; import { observer } from "mobx-react-lite"; -import { - SubmitButton, - Input, - ResetButton, - Form, - Radio, - Switch, -} from "formik-antd"; +import { SubmitButton, Input, ResetButton, Form, Radio, Switch } from "formik-antd"; import { Formik } from "formik"; import { Row, Col } from "antd"; import { EXEC_TYPE, IssueType, processModelMock } from "../model/process_model"; -export const CreateProcessScreenPath = '/create/process' +export const CreateProcessScreenPath = "/create/process"; export const CreateProcessScreen = observer(() => { return (
@@ -49,22 +42,11 @@ export const CreateProcessScreen = observer(() => { - + - - + + diff --git a/ui/src/features/create_project/create_project_repository.ts b/ui/src/features/create_project/create_project_repository.ts index 325ccfe..68082f3 100644 --- a/ui/src/features/create_project/create_project_repository.ts +++ b/ui/src/features/create_project/create_project_repository.ts @@ -1,10 +1,7 @@ import { Result } from "../../core/helper/result"; import { DatabaseModel } from "../../core/model/database_model"; import { ITriggerModel } from "../../core/model/trigger_model"; -import { - HttpMethod, - HttpRepository, -} from "../../core/repository/http_repository"; +import { HttpMethod, HttpRepository } from "../../core/repository/http_repository"; import { IProcess } from "../create_process/model/process_model"; import { ICreateProjectViewModel } from "./project_model"; @@ -15,11 +12,9 @@ export interface PipelineModel extends DatabaseModel { export class CreateProjectRepository extends HttpRepository { async getAllPipelines(page = 1): Promise> { - return await this.jsonRequest(HttpMethod.GET, "/pipeline"); + return await this._jsonRequest(HttpMethod.GET, "/pipeline"); } - async saveProject( - model: ICreateProjectViewModel - ): Promise> { - return await this.jsonRequest(HttpMethod.POST, "/project", model); + async saveProject(model: ICreateProjectViewModel): Promise> { + return await this._jsonRequest(HttpMethod.POST, "/project", model); } } diff --git a/ui/src/features/create_project_instance/create_project_instance.tsx b/ui/src/features/create_project_instance/create_project_instance.tsx index b762f4a..3927516 100644 --- a/ui/src/features/create_project_instance/create_project_instance.tsx +++ b/ui/src/features/create_project_instance/create_project_instance.tsx @@ -3,7 +3,8 @@ import { CreateProjectInstanceStore } from "./create_project_instance_store"; import { CreateProjectInstanceRepository } from "./create_project_instance_repository"; import { observer } from "mobx-react-lite"; import { Upload, Button } from "antd"; -import { useParams } from "react-router-dom"; +import { useNavigate, useParams } from "react-router-dom"; +import { Input } from "antd"; export const CreateProjectInstancePath = "/create/project/instance/"; @@ -12,9 +13,17 @@ export const CreateProjectInstanceScreen = observer(() => { () => new CreateProjectInstanceStore(new CreateProjectInstanceRepository()) ); const id = useParams().id; - React.useEffect(() => {}, [id, createProjectInstanceStore]); + const navigate = useNavigate(); + + React.useEffect(() => { + createProjectInstanceStore.init(navigate, id as string); + }, [id, createProjectInstanceStore, navigate]); + return ( <> +

project description

+ createProjectInstanceStore.setProjectDescription(e.target.value)}> +

root entity

{ createProjectInstanceStore.file = e.file.originFileObj; diff --git a/ui/src/features/create_project_instance/create_project_instance_repository.ts b/ui/src/features/create_project_instance/create_project_instance_repository.ts index a1c0db6..b835961 100644 --- a/ui/src/features/create_project_instance/create_project_instance_repository.ts +++ b/ui/src/features/create_project_instance/create_project_instance_repository.ts @@ -1,13 +1,16 @@ import { HttpMethod, HttpRepository } from "../../core/repository/http_repository"; +import { NewProjectModel } from "./new_project_model"; -// this.subRoutes.push({ -// method: "POST", -// subUrl: "upload export class CreateProjectInstanceRepository extends HttpRepository { - async setProjectRootFile() { - return await this.formDataRequest(HttpMethod.POST, "/project_instance/upload/"); + async setProjectRootFile(file: File) { + return await this._formDataRequest(HttpMethod.POST, "/project_instance/upload", file); + } + + async createNewProject(project: NewProjectModel) { + return await this._jsonRequest(HttpMethod.POST, "/project_instance", project); + } + + async setActiveProject(id: string) { + return await this._jsonRequest(HttpMethod.POST, `/project_instance/set/active/project?id=${id}`); } - // async getProjectInstance(id: string) { - // return await this.jsonRequest(HttpMethod.GET, ""); - // } } diff --git a/ui/src/features/create_project_instance/create_project_instance_store.ts b/ui/src/features/create_project_instance/create_project_instance_store.ts index 6f86c05..1730a75 100644 --- a/ui/src/features/create_project_instance/create_project_instance_store.ts +++ b/ui/src/features/create_project_instance/create_project_instance_store.ts @@ -3,21 +3,37 @@ import { SimpleErrorState } from "../../core/store/base_store"; import { CreateProjectInstanceRepository } from "./create_project_instance_repository"; import { message } from "antd"; import { HttpMethod } from "../../core/repository/http_repository"; +import { NavigateFunction } from "react-router-dom"; +import { NewProjectModel } from "./new_project_model"; export class CreateProjectInstanceStore extends SimpleErrorState { + newProjectModel: NewProjectModel; + repository: CreateProjectInstanceRepository; file?: File; - - saveInstance(): void { - if (this.file === undefined) { - message.error("Need upload file"); - } else { - // this.repository.formDataRequest(HttpMethod.POST, "", this.file); - } - } + navigate?: NavigateFunction; constructor(repository: CreateProjectInstanceRepository) { super(); this.repository = repository; makeAutoObservable(this); + this.newProjectModel = NewProjectModel.empty(); + } + + setProjectDescription(value: string): void { + this.newProjectModel.description = value; + } + init(navigate: NavigateFunction, projectId: string) { + this.navigate = navigate; + this.newProjectModel.project = projectId; + } + async saveInstance(): Promise { + if (this.file === undefined) { + message.error("Need upload file"); + } else { + if (this.newProjectModel.isValid()) { + await this.repository.createNewProject(this.newProjectModel); + await this.repository.setProjectRootFile(this.file); + if (this.navigate !== undefined) this.navigate("/"); + } + } } - repository: CreateProjectInstanceRepository; } diff --git a/ui/src/features/create_project_instance/new_project_model.ts b/ui/src/features/create_project_instance/new_project_model.ts new file mode 100644 index 0000000..805ec64 --- /dev/null +++ b/ui/src/features/create_project_instance/new_project_model.ts @@ -0,0 +1,17 @@ +export class NewProjectModel { + project: string; + description: string; + constructor(project: string, description: string) { + this.project = project; + this.description = description; + } + static empty() { + return new NewProjectModel("", ""); + } + isValid(): boolean { + return this.project.isNotEmpty() && this.description.isNotEmpty(); + } + messages() { + return ""; + } +} diff --git a/ui/src/features/create_trigger/data/trigger_repository.ts b/ui/src/features/create_trigger/data/trigger_repository.ts index 3127310..9cd2b25 100644 --- a/ui/src/features/create_trigger/data/trigger_repository.ts +++ b/ui/src/features/create_trigger/data/trigger_repository.ts @@ -1,11 +1,8 @@ -import { - HttpMethod, - HttpRepository, -} from "../../../core/repository/http_repository"; +import { HttpMethod, HttpRepository } from "../../../core/repository/http_repository"; import { ITriggerModel } from "../../../core/model/trigger_model"; export class TriggerRepository extends HttpRepository { public async save(model: ITriggerModel) { - return await this.jsonRequest(HttpMethod.POST, "/trigger", model); + return await this._jsonRequest(HttpMethod.POST, "/trigger", model); } } diff --git a/ui/src/features/create_trigger/presentation/trigger_store.ts b/ui/src/features/create_trigger/presentation/trigger_store.ts index d570ade..bc251d7 100644 --- a/ui/src/features/create_trigger/presentation/trigger_store.ts +++ b/ui/src/features/create_trigger/presentation/trigger_store.ts @@ -70,7 +70,7 @@ export class TriggerStore extends SimpleErrorState { } } async saveResult(): Promise { - await this.loadingHelper( + await this.httpHelper( this.repository.save({ type: this.getTriggerDescription(), description: this.triggerDescription, diff --git a/ui/src/features/scene_manager/components/static_asset_item_view.tsx b/ui/src/features/scene_manager/components/static_asset_item_view.tsx deleted file mode 100644 index a60272b..0000000 --- a/ui/src/features/scene_manager/components/static_asset_item_view.tsx +++ /dev/null @@ -1,14 +0,0 @@ -import * as React from "react"; -import { StaticAssetItemModel } from "../scene_manager_store"; - -export interface IStaticAssetModelViewProps { - model: StaticAssetItemModel; -} - -export function StaticAssetModelView(props: IStaticAssetModelViewProps) { - return ( -
- {props.model.name} -
- ); -} diff --git a/ui/src/features/scene_manager/data/scene_repository.ts b/ui/src/features/scene_manager/data/scene_repository.ts new file mode 100644 index 0000000..30e8575 --- /dev/null +++ b/ui/src/features/scene_manager/data/scene_repository.ts @@ -0,0 +1,19 @@ +import { Result } from "../../../core/helper/result"; +import { HttpMethod, HttpRepository } from "../../../core/repository/http_repository"; +import { CoreError } from "../../../core/store/base_store"; +import { RobossemblerAssets } from "../model/robossembler_assets"; + +export class SceneHttpRepository extends HttpRepository { + async getRobossemblerAssets() { + return this._jsonToClassInstanceRequest( + HttpMethod.GET, + "/robossembler_assets", + RobossemblerAssets + ) as unknown as Promise>; + } + async saveScene(robossemblerAssets: RobossemblerAssets) { + return this._jsonRequest(HttpMethod.POST, "/robossembler_assets", robossemblerAssets) as unknown as Promise< + Result + >; + } +} diff --git a/ui/src/features/scene_manager/model/robossembler_assets.ts b/ui/src/features/scene_manager/model/robossembler_assets.ts new file mode 100644 index 0000000..7c98cd8 --- /dev/null +++ b/ui/src/features/scene_manager/model/robossembler_assets.ts @@ -0,0 +1,160 @@ +import { IsArray, IsEnum, IsNumber, IsOptional, IsString, ValidateNested } from "class-validator"; +import { Type } from "class-transformer"; + +export class Gravity { + @IsNumber() + x: number; + @IsNumber() + y: number; + @IsNumber() + z: number; +} + +export class Pose { + @IsNumber() + x: number; + @IsNumber() + y: number; + @IsNumber() + z: number; + @IsNumber() + roll: number; + @IsNumber() + pitch: number; + @IsNumber() + yaw: number; +} + +export class Position { + @IsNumber() + x: number; + @IsNumber() + y: number; + @IsNumber() + z: number; +} + +export enum InstanceType { + RGB_CAMERA = "rgb_camera", + SCENE_SIMPLE_OBJECT = "scene_simple_object", +} + +abstract class CoreInstances {} + +export class Instance extends CoreInstances { + @IsEnum(InstanceType) + instanceType: InstanceType; + @Type(() => Position) + position: Position; + @IsArray() + quaternion: number[]; + @IsOptional() + @IsString() + instanceAt: null | string = null; +} + +export class SceneSimpleObject extends Instance {} + +export class InstanceRgbCamera extends Instance { + @IsString() + cameraLink: string; + @IsString() + topicCameraInfo: string; + @IsOptional() + @IsString() + topicDepth: string | null; + @IsString() + topicImage: string; +} +export class Asset { + @IsString() + name: string; + @IsString() + ixx: string; + @IsString() + ixy: string; + @IsString() + ixz: string; + @IsString() + iyy: string; + @IsString() + izz: string; + @IsString() + mass: string; + @IsString() + posX: string; + @IsString() + posY: string; + @IsString() + posZ: string; + @IsString() + eulerX: string; + @IsString() + eulerY: string; + @IsString() + eulerZ: string; + @IsString() + iyz: string; + @IsString() + meshPath: string; + @IsString() + friction: string; + @IsString() + centerMassX: string; + @IsString() + centerMassY: string; + @IsString() + centerMassZ: string; +} + +export class Physics { + @IsString() + engine_name: string; + @Type(() => Gravity) + gravity: Gravity; +} + +export class RobossemblerAssets { + @ValidateNested() + @Type(() => Asset) + assets: Asset[]; + + @IsArray() + @Type(() => Instance, { + discriminator: { + property: "instanceType", + subTypes: [ + { value: InstanceRgbCamera, name: InstanceType.RGB_CAMERA }, + { value: SceneSimpleObject, name: InstanceType.SCENE_SIMPLE_OBJECT }, + ], + }, + keepDiscriminatorProperty: true, + }) + instances: Instance[]; + + @IsOptional() + @ValidateNested() + @Type(() => Physics) + physics: Physics; + + convertLocalPathsToServerPaths(server_address: string): RobossemblerAssets { + this.assets = this.assets.map((el) => { + el.meshPath = server_address + el.meshPath; + return el; + }); + return this; + } + + getAssetPath(assetName: string): string { + const findElement = this.assets.find((el) => el.name === assetName); + + if (findElement === undefined) { + throw new Error("RobossemblerAssets.getAssetPath not found asset by name:" + assetName); + } + return findElement.meshPath; + } + + getAssetAtInstance(instanceAt: string): Asset { + return this.assets.filter((el) => el.name === instanceAt)[0]; + } +} diff --git a/ui/src/features/scene_manager/model/scene_assets.ts b/ui/src/features/scene_manager/model/scene_assets.ts new file mode 100644 index 0000000..f37f883 --- /dev/null +++ b/ui/src/features/scene_manager/model/scene_assets.ts @@ -0,0 +1,164 @@ +import { CameraHelper, Object3D, PerspectiveCamera, Quaternion, Scene, Vector3 } from "three"; +import { v4 as uuidv4 } from "uuid"; +import { UserData } from "../../../core/repository/core_there_repository"; +import { Asset, Instance, InstanceRgbCamera, InstanceType, SceneSimpleObject } from "./robossembler_assets"; + +export enum RobossemblerFiles { + robossemblerAssets = "robossembler_assets.json", +} + +export enum SceneModelsType { + ASSET, +} + +export abstract class BaseSceneItemModel { + id: string; + name: string; + position: Vector3; + quaternion: Quaternion; + + constructor(name: string) { + this.id = uuidv4(); + this.name = name; + } + + abstract toInstance(): Instance; + + abstract deleteToScene(scene: Scene): Object3D[]; + + toQuaternionArray() { + return [this.quaternion.x, this.quaternion.y, this.quaternion.z, this.quaternion.w]; + } +} + +export class StaticAssetItemModel extends BaseSceneItemModel { + name: string; + type = SceneModelsType.ASSET; + + constructor(name: string, position: Vector3, quaternion: Quaternion) { + super(name); + this.name = name; + this.position = position; + this.quaternion = quaternion; + } + + static fromSceneSimpleObjectAndAsset(sceneSimpleObject: SceneSimpleObject, asset: Asset) { + const { x, y, z } = sceneSimpleObject.position; + const quaternion = sceneSimpleObject.quaternion; + return new StaticAssetItemModel( + asset.name, + new Vector3(x, y, z), + new Quaternion(quaternion[0], quaternion[1], quaternion[2], quaternion[3]) + ); + } + + toInstance(): Instance { + const instance = new Instance(); + instance.instanceType = InstanceType.SCENE_SIMPLE_OBJECT; + instance.position = this.position; + instance.instanceAt = this.name; + instance.quaternion = this.toQuaternionArray(); + + return instance; + } + + deleteToScene(scene: Scene): Object3D[] { + return scene.children.filter((el) => el.name !== this.name); + } +} + +export interface ICoreInstance { + instanceAt: null | string; + type: string; + position: Vector3; + quaternion: Quaternion; +} + +export interface ICameraInstance extends ICoreInstance { + cameraLink: string; + topicImage: string; + topicCameraInfo: string; + topicDepth: string | null; +} + +export class CameraViewModel extends BaseSceneItemModel { + cameraLink: string; + topicImage: string; + topicCameraInfo: string; + topicDepth: null | string; + + constructor(cameraLink: string, topicImage: string, topicCameraInfo: string, topicDepth: string | null) { + super(cameraLink); + this.cameraLink = cameraLink; + this.topicImage = topicImage; + this.topicCameraInfo = topicCameraInfo; + this.topicDepth = topicDepth; + } + + static fromInstanceRgbCamera(instanceRgbCamera: InstanceRgbCamera) { + const { cameraLink, topicImage, topicCameraInfo, topicDepth, position, quaternion } = instanceRgbCamera; + const instance = new CameraViewModel(cameraLink, topicImage, topicCameraInfo, topicDepth); + const { x, y, z } = position; + + instance.position = new Vector3(x, y, z); + instance.quaternion = new Quaternion(quaternion[0], quaternion[1], quaternion[2], quaternion[3]); + + return instance; + } + + deleteToScene(scene: Scene): Object3D[] { + return scene.children.filter((el) => { + if (el.name === this.name) { + return false; + } + if (el.name === this.cameraLink + "camera_helper") { + return false; + } + return true; + }); + } + + toInstance(): InstanceRgbCamera { + return { + instanceType: InstanceType.RGB_CAMERA, + position: this.position, + quaternion: this.toQuaternionArray(), + instanceAt: null, + cameraLink: this.cameraLink, + topicCameraInfo: this.topicCameraInfo, + topicDepth: this.topicDepth, + topicImage: this.topicImage, + }; + } + + validate(cameraLinksNames: string[]) { + if (cameraLinksNames.filter((el) => this.cameraLink === el).isNotEmpty()) { + return { cameraLink: "the name for the camera is not unique" }; + } + if (this.cameraLink.isEmpty()) { + return { cameraLink: "is empty" }; + } + if (this.topicImage.isEmpty()) { + return { topicImage: "is empty" }; + } + if (this.topicCameraInfo.isEmpty()) { + return { topicCameraInfo: "is empty" }; + } + } + + mapPerspectiveCamera(htmlSceneWidth: number, htmlSceneHeight: number) { + const perspectiveCamera = new PerspectiveCamera(48, htmlSceneWidth / htmlSceneHeight, 7.1, 28.5); + perspectiveCamera.position.copy(this.position); + perspectiveCamera.quaternion.copy(this.quaternion); + perspectiveCamera.name = this.cameraLink; + const cameraHelper = new CameraHelper(perspectiveCamera); + perspectiveCamera.userData[UserData.selectedObject] = true; + cameraHelper.userData[UserData.selectedObject] = true; + cameraHelper.name = this.cameraLink + "camera_helper"; + return [cameraHelper, perspectiveCamera]; + } + + static empty() { + return new CameraViewModel("", "", "", ""); + } +} diff --git a/ui/src/features/scene_manager/model/scene_view.ts b/ui/src/features/scene_manager/model/scene_view.ts new file mode 100644 index 0000000..2265a58 --- /dev/null +++ b/ui/src/features/scene_manager/model/scene_view.ts @@ -0,0 +1,27 @@ +export class SceneMenu { + x?: number; + y?: number; + isShow?: boolean; + + constructor(x: number | undefined, y: number | undefined, isShow: boolean | undefined) { + this.x = x; + this.y = y; + this.isShow = isShow; + } + + static empty() { + return new SceneMenu(undefined, undefined, false); + } +} + +export interface SceneManagerView { + name: string; + clickHandel: Function; +} + +export enum SceneMode { + ROTATE = "Rotate", + MOVING = "Moving", + EMPTY = "Empty", + ADD_CAMERA = "Add camera", +} diff --git a/ui/src/features/scene_manager/presentation/components/scene_menu.tsx b/ui/src/features/scene_manager/presentation/components/scene_menu.tsx new file mode 100644 index 0000000..3504579 --- /dev/null +++ b/ui/src/features/scene_manager/presentation/components/scene_menu.tsx @@ -0,0 +1,27 @@ +interface SceneMenuProps { + x?: number; + y?: number; +} + +export const SceneMenu = (props: SceneMenuProps) => { + const sceneMenuStyle = { + transform: "rotate(0deg)", + background: "rgb(73 73 73)", + color: "#FFFFFF", + fontSize: "20px", + display: "flex", + justifyContent: "center", + alignItems: "center", + borderColor: "aqua", + border: "solid", + borderRadius: "30px", + width: "150px", + height: "300px", + }; + + return ( +
+
+
+ ); +}; diff --git a/ui/src/features/scene_manager/presentation/components/scene_widget.tsx b/ui/src/features/scene_manager/presentation/components/scene_widget.tsx new file mode 100644 index 0000000..9cf137b --- /dev/null +++ b/ui/src/features/scene_manager/presentation/components/scene_widget.tsx @@ -0,0 +1,68 @@ +import React from "react"; + +export const SceneWidget = () => { + const [pressed, setPressed] = React.useState(false); + const [position, setPosition] = React.useState({ x: 0, y: 0 }); + + React.useEffect(() => { + if (pressed) { + window.addEventListener("mousemove", onMouseMove); + window.addEventListener("mouseup", togglePressed); + } + + return () => { + window.removeEventListener("mousemove", onMouseMove); + window.removeEventListener("mouseup", togglePressed); + }; + }, [position, pressed]); + + const onMouseMove = (event: any) => { + const x = position.x + event.movementX; + const y = position.y + event.movementY; + setPosition({ x, y }); + }; + + const togglePressed = () => { + setPressed((prev) => !prev); + }; + + const quickAndDirtyStyle = { + transform: "rotate(0deg)", + background: "rgb(73 73 73)", + color: "#FFFFFF", + fontSize: "20px", + display: "flex", + justifyContent: "center", + alignItems: "center", + borderColor: "aqua", + border: "solid", + borderRadius: "30px", + }; + return ( +
+
{ + event.stopPropagation(); + setPressed(false); + }} + onMouseDown={togglePressed} + > +

{pressed ? "Dragging..." : "Press to drag"}

+

{ + event.stopPropagation(); + console.log(201); + }} + > + HYO +

+
+
+ ); +}; diff --git a/ui/src/features/scene_manager/presentation/components/static_asset_item_view.tsx b/ui/src/features/scene_manager/presentation/components/static_asset_item_view.tsx new file mode 100644 index 0000000..040faba --- /dev/null +++ b/ui/src/features/scene_manager/presentation/components/static_asset_item_view.tsx @@ -0,0 +1,45 @@ +import * as React from "react"; +import { BaseSceneItemModel, CameraViewModel, StaticAssetItemModel } from "../../model/scene_assets"; +import { Button } from "antd"; + +export interface IStaticAssetModelViewProps { + model: BaseSceneItemModel; + onTap: Function; +} + +export function StaticAssetModelView(props: IStaticAssetModelViewProps) { + if (props.model instanceof CameraViewModel) { + return ( +
+ {props.model.cameraLink} + +
+ ); + } + + if (props.model instanceof StaticAssetItemModel) { + return ( +
+ {props.model.name} + +
+ ); + } + return <>; +} diff --git a/ui/src/features/scene_manager/presentation/scene_manager.tsx b/ui/src/features/scene_manager/presentation/scene_manager.tsx new file mode 100644 index 0000000..bb774c3 --- /dev/null +++ b/ui/src/features/scene_manager/presentation/scene_manager.tsx @@ -0,0 +1,162 @@ +import * as React from "react"; +import { SceneMangerStore } from "./scene_manager_store"; +import { observer } from "mobx-react-lite"; +import { StaticAssetModelView } from "./components/static_asset_item_view"; +import { useParams } from "react-router-dom"; +import { SceneManagerView, SceneMode } from "../model/scene_view"; +import { Button } from "antd"; +import { Form, Input, ResetButton, SubmitButton } from "formik-antd"; +import { Formik } from "formik"; +import { CameraViewModel } from "../model/scene_assets"; + +export const SceneManagerPath = "/scene/manager/"; + +export const SceneManger = observer(() => { + const canvasRef = React.useRef(null); + const [sceneMangerStore] = React.useState(() => new SceneMangerStore()); + const id = useParams().id as string; + + React.useEffect(() => { + sceneMangerStore.init(); + sceneMangerStore.loadScene(id, canvasRef.current!); + document.body.style.overflow = "hidden"; + return () => { + document.body.style.overflow = "scroll"; + sceneMangerStore.dispose(); + }; + }, [id, sceneMangerStore]); + + const sceneIcons: SceneManagerView[] = [SceneMode.ROTATE, SceneMode.MOVING, SceneMode.ADD_CAMERA].map((el) => { + return { name: el, clickHandel: () => sceneMangerStore.setSceneMode(el) }; + }); + + return ( +
+ +
+
+ {sceneIcons.map((el) => { + return ( +
{ + el.clickHandel(); + }} + > + {el.name} +
+ ); + })} +
+
Scene manager
+ {sceneMangerStore.isVisibleSaveButton ? ( + <> + + + ) : ( + <> + )} + {sceneMangerStore.isLoading ? <>Loading... : <>} + {sceneMangerStore.sceneMode === SceneMode.ADD_CAMERA ? ( +
+ { + sceneMangerStore.addNewCamera(model); + actions.setSubmitting(false); + actions.resetForm(); + }} + validate={(model) => { + return model.validate(sceneMangerStore.getCameraLinkNames()); + }} + render={() => ( +
+
+ + + + + + Reset + Submit +
+
+ )} + /> +
+ ) : ( + <> + )} + {sceneMangerStore.sceneMode === SceneMode.MOVING || SceneMode.ROTATE ? ( + <> + {sceneMangerStore.robossemblerAssets?.assets.map((el) => { + return ( +
+
+ {el.name} + {sceneMangerStore.isRenderedAsset(el.name) ? ( + <> + ) : ( + + )} +
+
+ ); + })} + + ) : ( + <> + )} +
+
+
+ {sceneMangerStore.sceneModels.map((el) => { + return sceneMangerStore.deleteSceneItem(el)} model={el} />; + })} +
+ + {/* {sceneMangerStore.sceneMenuIsShow ? ( + <> + + + ) : ( + <> + )} */} +
+
+ ); +}); diff --git a/ui/src/features/scene_manager/presentation/scene_manager_store.ts b/ui/src/features/scene_manager/presentation/scene_manager_store.ts new file mode 100644 index 0000000..3e7572d --- /dev/null +++ b/ui/src/features/scene_manager/presentation/scene_manager_store.ts @@ -0,0 +1,179 @@ +import makeAutoObservable from "mobx-store-inheritance"; +import { CoreThereRepository } from "../../../core/repository/core_there_repository"; +import { Object3D, Vector2 } from "three"; +import { HttpError } from "../../../core/repository/http_repository"; +import { UiErrorState } from "../../../core/store/base_store"; +import { UiBaseError } from "../../../core/model/ui_base_error"; +import { SceneMenu, SceneMode } from "../model/scene_view"; +import { BaseSceneItemModel, CameraViewModel, RobossemblerFiles, StaticAssetItemModel } from "../model/scene_assets"; +import { SceneHttpRepository } from "../data/scene_repository"; +import { message } from "antd"; +import { RobossemblerAssets } from "../model/robossembler_assets"; + +export class SceneMangerStore extends UiErrorState { + sceneMode: SceneMode; + sceneMenu: SceneMenu; + isVisibleSaveButton: boolean = false; + coreThereRepository: null | CoreThereRepository = null; + sceneHttpRepository: SceneHttpRepository; + sceneModels: BaseSceneItemModel[] = []; + isSceneMenuShow = false; + robossemblerAssets?: RobossemblerAssets; + + constructor() { + super(); + makeAutoObservable(this); + this.sceneHttpRepository = new SceneHttpRepository(); + this.sceneMode = SceneMode.EMPTY; + this.sceneMenu = SceneMenu.empty(); + } + + onTapSave(): void { + this.robossemblerAssets!.instances = []; + this.sceneModels.forEach((el) => this.robossemblerAssets?.instances.push(el.toInstance())); + this.httpHelper(this.sceneHttpRepository.saveScene(this.robossemblerAssets as RobossemblerAssets)); + this.isVisibleSaveButton = false; + } + + deleteSceneItem(item: BaseSceneItemModel) { + const itm = this.sceneModels.filter((el) => el.id === item.id); + this.coreThereRepository!.deleteSceneItem(itm[0]); + this.sceneModels = this.sceneModels.filter((el) => el.name !== item.name); + this.visibleSaveButton(); + } + + visibleSaveButton() { + this.isVisibleSaveButton = true; + } + + addNewCamera(model: CameraViewModel) { + model.position = this.coreThereRepository!.camera.position; + model.quaternion = this.coreThereRepository!.camera.quaternion; + this.sceneModels.push(model); + this.coreThereRepository?.addSceneCamera(model); + this.visibleSaveButton(); + } + + getCameraLinkNames(): string[] { + return this.sceneModels.map((el) => { + if (el instanceof CameraViewModel) { + return el.cameraLink; + } + return ""; + }); + } + + loaderWatcher() {} + + loadSceneRobossemblerAsset(name: string) { + try { + const assetPath = this.robossemblerAssets?.getAssetPath(name) as string; + this.coreThereRepository?.loader([assetPath], this.loaderWatcher, name); + this.visibleSaveButton(); + } catch (error) { + message.error(String(error)); + } + } + + isRenderedAsset(name: string): boolean { + return this.sceneModels + .filter((el) => { + if (el instanceof StaticAssetItemModel) { + return el.name === name; + } + return false; + }) + .isNotEmpty(); + } + + hiddenMenu() { + this.isSceneMenuShow = false; + } + + setSceneMode = (mode: SceneMode) => { + if (this.sceneMode === undefined || this.sceneMode !== mode) { + this.sceneMode = mode; + } else if (this.sceneMode === mode) { + this.sceneMode = SceneMode.EMPTY; + } + this.coreThereRepository?.setTransformMode(this.sceneMode); + this.sceneModeWatcher(); + }; + + sceneModeWatcher() {} + + async init(): Promise {} + + errorHandingStrategy = (error: HttpError) => { + if (error.status === 404) { + this.errors.push(new UiBaseError(`${RobossemblerFiles.robossemblerAssets} not found to project`)); + } + }; + + async loadScene(sceneId: string, canvasRef: HTMLCanvasElement) { + this.loadWebGl(canvasRef); + await this.mapOk("robossemblerAssets", this.sceneHttpRepository.getRobossemblerAssets()); + if (this.robossemblerAssets) { + this.coreThereRepository?.loadInstances(this.robossemblerAssets); + } + } + + loadWebGl(canvasRef: HTMLCanvasElement): void { + this.coreThereRepository = new CoreThereRepository(canvasRef as HTMLCanvasElement, this.watcherSceneEditorObject); + this.coreThereRepository.on(this.watcherThereObjects); + this.coreThereRepository.render(); + this.sceneModels = this.coreThereRepository.getAllSceneModels(); + + window.addEventListener("click", (event) => this.clickLister(event)); + window.addEventListener("mousedown", (e) => this.sceneContextMenu(e)); + } + + clickLister(event: MouseEvent) { + if (this.sceneMode === SceneMode.EMPTY) { + return; + } + if (this.sceneMode === SceneMode.MOVING || this.sceneMode === SceneMode.ROTATE) { + const vector = new Vector2(); + vector.x = (event.clientX / window.innerWidth) * 2 - 1; + vector.y = -(event.clientY / window.innerHeight) * 2 + 1; + + this.transformContollsCall(vector); + } + } + + sceneContextMenu(e: MouseEvent): void { + if (e.button === 2) { + this.isSceneMenuShow = true; + this.sceneMenu.x = e.clientX; + this.sceneMenu.y = e.clientY; + } + } + + watcherThereObjects = (sceneItemModel: BaseSceneItemModel): void => { + this.sceneModels.push(sceneItemModel); + }; + + watcherSceneEditorObject = (mesh: Object3D) => { + this.sceneModels = this.sceneModels.map((el) => { + if (el instanceof CameraViewModel || (el instanceof StaticAssetItemModel && el.name === mesh.name)) { + el.position = mesh.position; + el.quaternion = mesh.quaternion; + return el; + } + return el; + }); + this.visibleSaveButton(); + }; + + transformContollsCall = (vector: Vector2) => { + this.coreThereRepository?.setRayCastAndGetFirstObject(vector).fold( + (success) => this.coreThereRepository?.setTransformControlsAttach(success), + (_error) => this.coreThereRepository?.disposeTransformControlsMode() + ); + }; + + dispose() { + window.removeEventListener("click", this.clickLister); + window.removeEventListener("mousedown", (e) => this.sceneContextMenu(e)); + } +} diff --git a/ui/src/features/scene_manager/scene_manager.tsx b/ui/src/features/scene_manager/scene_manager.tsx deleted file mode 100644 index cdec5ae..0000000 --- a/ui/src/features/scene_manager/scene_manager.tsx +++ /dev/null @@ -1,60 +0,0 @@ -import * as React from "react"; -import { SceneMangerStore, StaticAssetItemModel } from "./scene_manager_store"; -import { observer } from "mobx-react-lite"; -import { StaticAssetModelView } from "./components/static_asset_item_view"; -import { useParams } from "react-router-dom"; - -// 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 const SceneManagerPath = "/scene/manager/"; -export const SceneManger = observer(() => { - const canvasRef = React.useRef(null); - - const [sceneMangerStore] = React.useState(() => new SceneMangerStore()); - const id = useParams().id as string; - - React.useEffect(() => { - if (id) { - sceneMangerStore.loadScene(id, canvasRef.current!); - } - return () => { - sceneMangerStore.dispose(); - }; - }, [id, sceneMangerStore]); - - return ( -
-
{sceneMangerStore.errors.isNotEmpty() ? <>{sceneMangerStore.errors[0].text} : <>}
-
- {sceneMangerStore.sceneItems.map((el) => { - if (el instanceof StaticAssetItemModel) { - return StaticAssetModelView({ model: el }); - } - return <>; - })} -
- -
- ); -}); diff --git a/ui/src/features/scene_manager/scene_manager_store.ts b/ui/src/features/scene_manager/scene_manager_store.ts deleted file mode 100644 index 5162597..0000000 --- a/ui/src/features/scene_manager/scene_manager_store.ts +++ /dev/null @@ -1,96 +0,0 @@ -/* eslint-disable array-callback-return */ -import makeAutoObservable from "mobx-store-inheritance"; -import { CoreThereRepository } from "../../core/repository/core_there_repository"; -import { v4 as uuidv4 } from "uuid"; -import { Vector2 } from "three"; -import { HttpError, HttpMethod, HttpRepository } from "../../core/repository/http_repository"; -import { UiErrorState } from "../../core/store/base_store"; -import { UiBaseError } from "../../core/model/ui_base_error"; - -export class BaseSceneItemModel { - id: string; - constructor() { - this.id = uuidv4(); - } -} - -enum SceneModelsType { - ASSET, -} - -export class StaticAssetItemModel extends BaseSceneItemModel { - name: string; - type = SceneModelsType.ASSET; - constructor(name: string) { - super(); - this.name = name; - } -} - -export enum RobossemblerFiles { - robossemblerAssets = "robossembler_assets.json", -} - -export class SceneMangerStore extends UiErrorState { - async init(): Promise {} - errorHandingStrategy = (error: HttpError) => { - if (error.status === 404) { - this.errors.push(new UiBaseError(`${RobossemblerFiles.robossemblerAssets} not found to project`)); - } - }; - - async loadScene(sceneId: string, canvasRef: HTMLCanvasElement) { - ( - await this.loadingHelper( - this.httpRepository.jsonRequest(HttpMethod.GET, "/" + sceneId + "/" + RobossemblerFiles.robossemblerAssets) - ) - ).map((el) => { - this.loadGl(canvasRef); - }); - } - coreThereRepository: null | CoreThereRepository = null; - httpRepository: HttpRepository; - sceneItems: BaseSceneItemModel[] = []; - - constructor() { - super(); - makeAutoObservable(this); - this.httpRepository = new HttpRepository(); - } - - loadGl(canvasRef: HTMLCanvasElement): void { - this.coreThereRepository = new CoreThereRepository(canvasRef as HTMLCanvasElement); - this.coreThereRepository.on(this.watcherThereObjects); - this.coreThereRepository.render(); - this.coreThereRepository.fitCameraToCenteredObject(this.coreThereRepository.getAllSceneNameModels()); - - this.sceneItems = this.coreThereRepository.getAllSceneModels(); - - window.addEventListener("click", this.handleMouseClick); - } - - watcherThereObjects(sceneItemModel: BaseSceneItemModel): void { - this.sceneItems.push(sceneItemModel); - } - - handleMouseClick = (event: MouseEvent) => { - const vector = new Vector2(); - console.log("===="); - console.log(event.pageX); - console.log(event.clientX); - console.log(event.x); - console.log(event.movementX); - console.log(event.screenX); - console.log("===="); - vector.x = (event.clientX / window.innerWidth) * 2 - 1; - vector.y = -(event.clientY / window.innerHeight) * 2 + 1; - - this.coreThereRepository?.setRayCastAndGetFirstObject(vector).map((el) => { - this.coreThereRepository?.switchObjectEmissive(el); - }); - }; - - dispose() { - window.removeEventListener("click", this.handleMouseClick); - } -} diff --git a/ui/src/features/select_project/data/select_project_repository.ts b/ui/src/features/select_project/data/select_project_repository.ts index 482f509..16607a5 100644 --- a/ui/src/features/select_project/data/select_project_repository.ts +++ b/ui/src/features/select_project/data/select_project_repository.ts @@ -4,9 +4,9 @@ import { IProjectModel } from "../model/project_model"; export class SelectProjectRepository extends HttpRepository { async setActiveProject(id: string) { - return await this.jsonRequest(HttpMethod.POST, `/project?${id}`); + return await this._jsonRequest(HttpMethod.POST, `/project?${id}`); } async getAllProjects(page = 1): Promise> { - return await this.jsonRequest(HttpMethod.GET, `/project?${page}`); + return await this._jsonRequest(HttpMethod.GET, `/project?${page}`); } } diff --git a/ui/src/features/select_project/presentation/select_project.tsx b/ui/src/features/select_project/presentation/select_project.tsx index f8e6d36..c4e29cc 100644 --- a/ui/src/features/select_project/presentation/select_project.tsx +++ b/ui/src/features/select_project/presentation/select_project.tsx @@ -10,10 +10,11 @@ export const SelectProjectScreenPath = "/select_project"; export const SelectProjectScreen: React.FunctionComponent = observer(() => { const [selectProjectStore] = React.useState(() => new SelectProjectStore()); + const navigate = useNavigate(); + React.useEffect(() => { selectProjectStore.init(); - }, [selectProjectStore]); - const navigate = useNavigate(); + }, [selectProjectStore, navigate]); return ( <> diff --git a/ui/src/features/select_project/presentation/select_project_store.ts b/ui/src/features/select_project/presentation/select_project_store.ts index 35f82ea..dd210dd 100644 --- a/ui/src/features/select_project/presentation/select_project_store.ts +++ b/ui/src/features/select_project/presentation/select_project_store.ts @@ -4,10 +4,7 @@ import { IProjectModel } from "../model/project_model"; import { CoreError, UiErrorState } from "../../../core/store/base_store"; export class SelectProjectStore extends UiErrorState { - errorHandingStrategy = (error: CoreError) => { - console.log(error); - }; - + errorHandingStrategy = (error: CoreError) => {}; repository: SelectProjectRepository; errors = []; page = 1; @@ -20,7 +17,7 @@ export class SelectProjectStore extends UiErrorState { makeAutoObservable(this); } async setActiveProject(id: string): Promise { - this.loadingHelper(this.repository.setActiveProject(id)); + this.httpHelper(this.repository.setActiveProject(id)); } async getPipelines(): Promise { await this.mapOk("projects", this.repository.getAllProjects(this.page));