Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
202 changes: 129 additions & 73 deletions README.md
Original file line number Diff line number Diff line change
@@ -1,73 +1,129 @@
# React + TypeScript + Vite

This template provides a minimal setup to get React working in Vite with HMR and some ESLint rules.

Currently, two official plugins are available:

- [@vitejs/plugin-react](https://github.com/vitejs/vite-plugin-react/blob/main/packages/plugin-react) uses [Oxc](https://oxc.rs)
- [@vitejs/plugin-react-swc](https://github.com/vitejs/vite-plugin-react/blob/main/packages/plugin-react-swc) uses [SWC](https://swc.rs/)

## React Compiler

The React Compiler is not enabled on this template because of its impact on dev & build performances. To add it, see [this documentation](https://react.dev/learn/react-compiler/installation).

## Expanding the ESLint configuration

If you are developing a production application, we recommend updating the configuration to enable type-aware lint rules:

```js
export default defineConfig([
globalIgnores(['dist']),
{
files: ['**/*.{ts,tsx}'],
extends: [
// Other configs...

// Remove tseslint.configs.recommended and replace with this
tseslint.configs.recommendedTypeChecked,
// Alternatively, use this for stricter rules
tseslint.configs.strictTypeChecked,
// Optionally, add this for stylistic rules
tseslint.configs.stylisticTypeChecked,

// Other configs...
],
languageOptions: {
parserOptions: {
project: ['./tsconfig.node.json', './tsconfig.app.json'],
tsconfigRootDir: import.meta.dirname,
},
// other options...
},
},
])
```

You can also install [eslint-plugin-react-x](https://github.com/Rel1cx/eslint-react/tree/main/packages/plugins/eslint-plugin-react-x) and [eslint-plugin-react-dom](https://github.com/Rel1cx/eslint-react/tree/main/packages/plugins/eslint-plugin-react-dom) for React-specific lint rules:

```js
// eslint.config.js
import reactX from 'eslint-plugin-react-x'
import reactDom from 'eslint-plugin-react-dom'

export default defineConfig([
globalIgnores(['dist']),
{
files: ['**/*.{ts,tsx}'],
extends: [
// Other configs...
// Enable lint rules for React
reactX.configs['recommended-typescript'],
// Enable lint rules for React DOM
reactDom.configs.recommended,
],
languageOptions: {
parserOptions: {
project: ['./tsconfig.node.json', './tsconfig.app.json'],
tsconfigRootDir: import.meta.dirname,
},
// other options...
},
},
])
```
[![build](https://github.com/a1unade/refloor-room-builder/actions/workflows/build.yml/badge.svg)](https://github.com/a1unade/refloor-room-builder/actions/workflows/build.yml)
[![deploy](https://github.com/a1unade/refloor-room-builder/actions/workflows/deploy.yml/badge.svg)](https://github.com/a1unade/refloor-room-builder/actions/workflows/deploy.yml)

Интерактивная 3D-сцена комнаты с настройкой размеров помещения, визуализацией стен, пола, плинтуса и автоматическим расчётом материалов.

Проект реализован на **React**, **TypeScript** и **Three.js**.
Основная 3D-логика вынесена в локальный пакет `@refloor/core`, а React-приложение отвечает за UI и взаимодействие с пользователем.

## Demo

GitHub Pages: https://a1unade.github.io/refloor-room-builder/

## Возможности

- построение 3D-комнаты по параметрам:

- длина;

- ширина;

- высота;

- отображение стен с материалом покраски;

- построение пола с отдельными плашками;

- поддержка двух типов раскладки:

- прямая палубная раскладка;

- ёлочка / herringbone;

- зазоры между досками;

- тепловой зазор между полом и стенами;

- плинтус по периметру комнаты;

- аккуратное примыкание плинтуса в углах;

- расчёт количества плашек пола;

- расчёт погонажа плинтуса;

- управление камерой через OrbitControls;

- адаптивная панель управления;

- слайдеры для изменения параметров комнаты;

- оптимизация пола через `InstancedMesh`.

## Технологии

- `React`

- `TypeScript`

- `Three.js`

- `Vite`

- `Fluent UI`

- `tsyringe`

- `GitHub Actions`

- `GitHub Pages`

## Основные допущения

- Единица измерения в 3D-сцене соответствует одному метру.

- Размер плашки пола по умолчанию: 600 × 100 мм.

- Толщина плашки используется только для визуализации.

- Количество плашек рассчитывается по площади пола и площади одной плашки.

- Для раскладки ёлочкой применяется повышающий коэффициент запаса.

- Тепловой зазор у стен учитывается при генерации пола.

- Плинтус визуально перекрывает тепловой зазор.

- Подрезка плашек у границ комнаты визуально ограничивается областью пола.

- Расчёт погонажа плинтуса выполняется по периметру комнаты.

- Расчёт краски не используется в итоговом UI, так как основной расчёт в текущей версии сфокусирован на напольном покрытии и плинтусе.

## Архитектура

В проекте используется разделение на React-приложение и 3D-ядро.

`@refloor/core` отвечает за:

- создание renderer;

- управление сценой;

- управление камерой и `OrbitControls`;

- построение стен;

- построение пола;

- построение плинтуса;

- расчёты материалов;

- публичный API через `AppHub`.

React-приложение отвечает за:

- canvas;

- панель управления;

- ввод параметров;

- отображение расчётов;

- обновление сцены при изменении параметров.

Такой подход позволяет отделить Three.js-логику от UI и упростить поддержку проекта.

<img src="./materials/1.png" alt="архитектура ядра">
Binary file added materials/1.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
18 changes: 1 addition & 17 deletions packages/core/src/modules/scene-module.ts
Original file line number Diff line number Diff line change
Expand Up @@ -18,20 +18,12 @@ export class SceneModule implements IRuntimeModule, IMeshApi {
/** Объекты, добавленные в сцену через модуль */
private _objects: THREE.Object3D[] = [];

/** Сетка сцены */
private _grid: THREE.GridHelper | null = null;

/** Базовый свет сцены */
private _light: THREE.HemisphereLight | null = null;

public constructor(@inject('ISceneApi') private _api: ISceneApi) {}

public init(): void {
// Сетка
this._grid = new THREE.GridHelper(10, 10);
this._grid.position.y = -0.001;
this._api.addToScene(this._grid);

// Свет
this._light = new THREE.HemisphereLight(0xffffff, 0x444444, 0.6);
this._api.addToScene(this._light);
Expand Down Expand Up @@ -72,22 +64,14 @@ export class SceneModule implements IRuntimeModule, IMeshApi {
}

/** Освобождает ресурсы модуля */
public dispose(): Promise<void> | void {
public dispose(): void {
// Объекты сцены
for (const object of this._objects) {
this._api.removeFromScene(object);
}

this._objects.length = 0;

// Сетка
if (this._grid) {
this._api.removeFromScene(this._grid);
this._grid.geometry.dispose();
(this._grid.material as THREE.Material).dispose();
this._grid = null;
}

// Свет
if (this._light) {
this._api.removeFromScene(this._light);
Expand Down
Loading