From 9127384becbe0fcc9f7b0f0b00d74cb751f3b29f Mon Sep 17 00:00:00 2001 From: Viet Nguyen <77735678+Viet281101@users.noreply.github.com> Date: Thu, 13 Jun 2024 19:46:34 +0200 Subject: [PATCH] update tools (#27) * update icons * update popup cube * fix board input * add more popup * clear grid func * add create new grid func * start cube popup * clean up code --- 2D/js/ai.js | 4 +- 2D/js/main.js | 30 +++------- 2D/js/popup/solve.js | 21 ++----- 2D/js/popup/tutorial.js | 15 ++--- 3D/js/board.js | 68 ++++++++++++----------- 3D/js/main.js | 51 ++++++++--------- 3D/js/popup/cube.js | 113 +++++++++++++++++++++++++++++++++++++- 3D/js/popup/grid.js | 98 +++++++++++++++++++++++++++++++++ 3D/js/popup/popup3d.js | 101 ++++++++++++++++++++++++++++++++++ 3D/js/popup/solve.js | 8 +++ 3D/js/popup/tutorial.js | 8 +++ 3D/js/toolbar.js | 20 +++++-- assets/ic_left_click.png | Bin 0 -> 1236 bytes assets/ic_reset.png | Bin 931 -> 912 bytes assets/ic_right_click.png | Bin 0 -> 1299 bytes assets/keyboard_117.png | Bin 0 -> 408 bytes assets/keyboard_16.png | Bin 0 -> 450 bytes rapport.md | 26 +++++++++ 18 files changed, 444 insertions(+), 119 deletions(-) create mode 100644 3D/js/popup/popup3d.js create mode 100644 3D/js/popup/solve.js create mode 100644 3D/js/popup/tutorial.js create mode 100644 assets/ic_left_click.png create mode 100644 assets/ic_right_click.png create mode 100644 assets/keyboard_117.png create mode 100644 assets/keyboard_16.png diff --git a/2D/js/ai.js b/2D/js/ai.js index a7e304e..b9d0521 100644 --- a/2D/js/ai.js +++ b/2D/js/ai.js @@ -240,7 +240,6 @@ export function randomBacktrackingTiling(polyominoes, gridBoard, placePolyomino, placeNextPolyomino(); }; - /** * Automatic tiling the grid based on polyomino placed on the field by the user. * @@ -253,7 +252,6 @@ export function randomBacktrackingTiling(polyominoes, gridBoard, placePolyomino, * @param {Function} message - An optional callback function to be called when the tiling is complete. * @return {void} This function does not return anything. */ - export function fullAutoTiling(gridBoard, polyominoes, placePolyomino, removePolyomino, redraw, duplicatePolyomino, message) { function canPlace(polyomino, x, y) { const originalX = polyomino.x; @@ -318,5 +316,5 @@ export function fullAutoTiling(gridBoard, polyominoes, placePolyomino, removePol console.log("rien trouve"); redraw(); } -} +}; diff --git a/2D/js/main.js b/2D/js/main.js index 0cb3faa..88c9e89 100644 --- a/2D/js/main.js +++ b/2D/js/main.js @@ -55,20 +55,10 @@ class MainApp { }; addEventListeners() { - this.canvas.addEventListener('mousedown', (e) => { - const mousePos = this.gridBoard.getMousePos(e); - this.handleMouseDown(mousePos); - }); - this.canvas.addEventListener('mousemove', (e) => { - const mousePos = this.gridBoard.getMousePos(e); - this.handleMouseMove(mousePos); - }); - this.canvas.addEventListener('mouseup', (e) => { - this.handleMouseUp(); - }); - window.addEventListener('keydown', (e) => { - if (e.key === 'r' && this.selectedPolyomino) { this.selectedPolyomino.rotate(); this.redraw(); } - }); + this.canvas.addEventListener('mousedown', (e) => { const mousePos = this.gridBoard.getMousePos(e); this.handleMouseDown(mousePos); }); + this.canvas.addEventListener('mousemove', (e) => { const mousePos = this.gridBoard.getMousePos(e); this.handleMouseMove(mousePos); }); + this.canvas.addEventListener('mouseup', (e) => { this.handleMouseUp(); }); + window.addEventListener('keydown', (e) => { if (e.key === 'r' && this.selectedPolyomino) { this.selectedPolyomino.rotate(); this.redraw(); } }); window.addEventListener('resize', () => { this.gridBoard.resizeCanvas(); this.gridBoard.drawGrid(); @@ -135,7 +125,7 @@ class MainApp { }; handleMouseMove(mousePos) { - if (this.isBlackening) this.canvas.style.cursor = 'url("../assets/cursor_blackend.png"), auto'; + if (this.isBlackening) this.canvas.style.cursor = 'url("../assets/cursor_blacken.png"), auto'; this.polyominoes.forEach(polyomino => polyomino.onMouseMove(mousePos)); if (this.tooltipPolyomino && !this.selectedPolyomino?.isDragging) { let found = false; @@ -182,7 +172,7 @@ class MainApp { do { newColor = getRandomColor(); } while (newColor === polyomino.color); const newShape = polyomino.shape.map(row => row.slice()); - const newPolyomino = new Polyomino(newShape, polyomino.x, polyomino.y, newColor, this); + const newPolyomino = new Polyomino(newShape, polyomino.x, polyomino.y, newColor, this, polyomino.name); this.polyominoes.push(newPolyomino); this.redraw(); }; @@ -422,9 +412,7 @@ class MainApp { fullAutoTiling() { this.resetBoard(); - const messageBox = this.createMessageBox(2); - setTimeout(() => { fullAutoTiling( this.gridBoard, @@ -433,13 +421,9 @@ class MainApp { this.gridBoard.removePolyomino.bind(this.gridBoard), this.redraw.bind(this), this.duplicatePolyomino.bind(this), - () => { - this.showMessageBox(messageBox); - } + () => { this.showMessageBox(messageBox); } ); }, 1000); - - console.log("Auto Tiling process initiated."); }; }; diff --git a/2D/js/popup/solve.js b/2D/js/popup/solve.js index 3dd6796..3339231 100644 --- a/2D/js/popup/solve.js +++ b/2D/js/popup/solve.js @@ -15,7 +15,6 @@ export function showSolvePopup(toolbar) { { label: '5) Full Automatic Tiling :', underline: true, icon: '../assets/ic_solution.png', description: 'Automatically fills the grid using user-selected pieces from outside the grid, such as filling the grid entirely with monominoes if only one monomino is chosen.' } ]; - const startY = 60; const rowHeight = 60; const colX = 30; @@ -146,21 +145,11 @@ function attachSolveClickEvent(toolbar, popup, row, y) { if (toolbar.isInside(mouseX, mouseY, { x: popup.width - 94, y: y - 14, width: 50, height: 50 })) { switch (row.label) { - case '1) Backtracking method :': - toolbar.mainApp.backtrackingAutoTiling(); - break; - case '2) Brute force method :': - toolbar.mainApp.bruteForceTiling(); - break; - case '3) Random method :': - toolbar.mainApp.randomTiling(); - break; - case '4) Random backtracking :': - toolbar.mainApp.randomBacktrackingTiling(); - break; - case '5) Full Automatic Tiling :': - toolbar.mainApp.fullAutoTiling(); - break; + case '1) Backtracking method :': toolbar.mainApp.backtrackingAutoTiling(); break; + case '2) Brute force method :': toolbar.mainApp.bruteForceTiling(); break; + case '3) Random method :': toolbar.mainApp.randomTiling(); break; + case '4) Random backtracking :': toolbar.mainApp.randomBacktrackingTiling(); break; + case '5) Full Automatic Tiling :': toolbar.mainApp.fullAutoTiling(); break; } if (toolbar.isMobile) toolbar.closePopup('solve'); } diff --git a/2D/js/popup/tutorial.js b/2D/js/popup/tutorial.js index ff9c9e2..c390cdd 100644 --- a/2D/js/popup/tutorial.js +++ b/2D/js/popup/tutorial.js @@ -9,12 +9,11 @@ export function showTutorialPopup(toolbar) { const rows = [ { label: 'Auto Tiling the Polyominoes Blocks', title: true }, { label: '1) Create Any Polyomino :', underline: true, icon: '../assets/ic_plus.png', description: 'Click this icon to open a menu. Select the polyomino you want to spawn it on the field. Drag and drop it onto the grid.' }, - { label: '2) Manipulate the Polyominoes :', underline: true, icon: '../assets/ic_solution.png', description: 'Each polyomino has an interactive menu. Click on the polyomino to : \n - rotate left or rotate right \n - flip \n - duplicate \n(the clone will appear below \n the piece selected in another color) \n - or delete.' }, + { label: '2) Manipulate the Polyominoes', underline: true, icon: '../assets/ic_solution.png', description: 'Each polyomino has an interactive menu. Click on the polyomino to : \n - rotate left or rotate right \n - flip \n - duplicate \n(the clone will appear below \n the piece selected in another color) \n - or delete.' }, { label: '3) Grid Settings :', underline: true, icon: '../assets/ic_table.png', description: 'This menu lets you create a new board by setting the grid\'s length and height. Click to create the grid. \n Options include : \n - deleting the grid \n - blocking cells to prevent polyomino placement \n - creating automatically random \n black cells \n - Clear all black cells \n - and swapping black and empty cells. ' }, { label: '4) Solving Grid :', underline: true, icon: '../assets/ic_solving.png', description: 'Click this menu to use various AI solvers. Descriptions are available for each solver; just click to read them.' }, { label: '5) Use Settings :', underline: true, icon: '../assets/ic_solution.png', description: 'This section facilitates user testing. In this menu, you can: \n - Reset the position \n of the polyominoes to their original spawn points.\n - Shuffle the positions \n of the polyominoes on the field, useful if duplicated pieces overlap.\n - Delete all polyominoes \n from the field and the grid.' } ]; - const subIcons = [ { path: '../assets/ic_rotate_right.png', x: 245, y: 339 }, @@ -33,7 +32,6 @@ export function showTutorialPopup(toolbar) { { path: '../assets/ic_trash.png', x: 250, y: 1107}, ]; - const startY = 60; const rowHeight = 60; const colX = 25; @@ -48,7 +46,6 @@ export function showTutorialPopup(toolbar) { ctx.fillStyle = '#000'; ctx.fillText(row.label, colX, y + 20); - if (row.description) { dropdowns[index] = { description: row.description, expanded: true, y: y + 40 }; } @@ -58,12 +55,12 @@ export function showTutorialPopup(toolbar) { function wrapText(ctx, text, x, y, maxWidth, lineHeight) { const paragraphs = text.split('\n'); let totalLines = 0; - + paragraphs.forEach(paragraph => { const words = paragraph.split(' '); let line = ''; const lines = []; - + for (let n = 0; n < words.length; n++) { let testLine = line + words[n] + ' '; let metrics = ctx.measureText(testLine); @@ -81,10 +78,8 @@ export function showTutorialPopup(toolbar) { }); totalLines += lines.length; }); - return totalLines; - } - + }; function redrawPopup() { ctx.clearRect(0, 0, popup.width, popup.height); @@ -123,7 +118,6 @@ export function showTutorialPopup(toolbar) { }; } }); - clickAreas.push({ index, rect: { x: colX, y, width: popup.width - colX - 100, height: rowHeight }, type: 'label' }); @@ -135,7 +129,6 @@ export function showTutorialPopup(toolbar) { } }); }; - redrawPopup(); }; diff --git a/3D/js/board.js b/3D/js/board.js index 0d34af3..54f2e7b 100644 --- a/3D/js/board.js +++ b/3D/js/board.js @@ -1,7 +1,7 @@ import * as THREE from 'three'; export class Board { - constructor(scene, size = 10) { + constructor(scene, size = { x: 10, y: 10, z: 10 }) { this.scene = scene; this.size = size; this.grid = new THREE.Group(); @@ -20,29 +20,33 @@ export class Board { const gridMaterial = new THREE.LineBasicMaterial({ color: 0x000000 }); const gridStep = 1; - for (let i = -size / 2; i <= size / 2; i += gridStep) { - this.addLine(gridMaterial, [i, -size / 2, -size / 2], [i, size / 2, -size / 2], this.grid); - this.addLine(gridMaterial, [i, -size / 2, size / 2], [i, size / 2, size / 2], this.grid); - this.addLine(gridMaterial, [i, -size / 2, -size / 2], [i, -size / 2, size / 2], this.grid); - this.addLine(gridMaterial, [i, size / 2, -size / 2], [i, size / 2, size / 2], this.grid); + for (let i = -size.x / 2; i <= size.x / 2; i += gridStep) { + this.addLine(gridMaterial, [i, -size.y / 2, -size.z / 2], [i, size.y / 2, -size.z / 2], this.grid); + this.addLine(gridMaterial, [i, -size.y / 2, size.z / 2], [i, size.y / 2, size.z / 2], this.grid); + this.addLine(gridMaterial, [i, -size.y / 2, -size.z / 2], [i, -size.y / 2, size.z / 2], this.grid); + this.addLine(gridMaterial, [i, size.y / 2, -size.z / 2], [i, size.y / 2, size.z / 2], this.grid); + } - this.addLine(gridMaterial, [-size / 2, i, -size / 2], [size / 2, i, -size / 2], this.grid); - this.addLine(gridMaterial, [-size / 2, i, size / 2], [size / 2, i, size / 2], this.grid); - this.addLine(gridMaterial, [-size / 2, i, -size / 2], [-size / 2, i, size / 2], this.grid); - this.addLine(gridMaterial, [size / 2, i, -size / 2], [size / 2, i, size / 2], this.grid); + for (let j = -size.y / 2; j <= size.y / 2; j += gridStep) { + this.addLine(gridMaterial, [-size.x / 2, j, -size.z / 2], [size.x / 2, j, -size.z / 2], this.grid); + this.addLine(gridMaterial, [-size.x / 2, j, size.z / 2], [size.x / 2, j, size.z / 2], this.grid); + this.addLine(gridMaterial, [-size.x / 2, j, -size.z / 2], [-size.x / 2, j, size.z / 2], this.grid); + this.addLine(gridMaterial, [size.x / 2, j, -size.z / 2], [size.x / 2, j, size.z / 2], this.grid); + } - this.addLine(gridMaterial, [-size / 2, -size / 2, i], [size / 2, -size / 2, i], this.grid); - this.addLine(gridMaterial, [-size / 2, size / 2, i], [size / 2, size / 2, i], this.grid); - this.addLine(gridMaterial, [-size / 2, -size / 2, i], [-size / 2, size / 2, i], this.grid); - this.addLine(gridMaterial, [size / 2, -size / 2, i], [size / 2, size / 2, i], this.grid); + for (let k = -size.z / 2; k <= size.z / 2; k += gridStep) { + this.addLine(gridMaterial, [-size.x / 2, -size.y / 2, k], [size.x / 2, -size.y / 2, k], this.grid); + this.addLine(gridMaterial, [-size.x / 2, size.y / 2, k], [size.x / 2, size.y / 2, k], this.grid); + this.addLine(gridMaterial, [-size.x / 2, -size.y / 2, k], [-size.x / 2, size.y / 2, k], this.grid); + this.addLine(gridMaterial, [size.x / 2, -size.y / 2, k], [size.x / 2, size.y / 2, k], this.grid); } - for (let x = -size / 2 + gridStep; x < size / 2; x += gridStep) { - for (let y = -size / 2 + gridStep; y < size / 2; y += gridStep) { - for (let z = -size / 2 + gridStep; z < size / 2; z += gridStep) { - this.addLine(gridMaterial, [x, y, -size / 2], [x, y, size / 2], this.innerGrid); - this.addLine(gridMaterial, [x, -size / 2, z], [x, size / 2, z], this.innerGrid); - this.addLine(gridMaterial, [-size / 2, y, z], [size / 2, y, z], this.innerGrid); + for (let x = -size.x / 2 + gridStep; x < size.x / 2; x += gridStep) { + for (let y = -size.y / 2 + gridStep; y < size.y / 2; y += gridStep) { + for (let z = -size.z / 2 + gridStep; z < size.z / 2; z += gridStep) { + this.addLine(gridMaterial, [x, y, -size.z / 2], [x, y, size.z / 2], this.innerGrid); + this.addLine(gridMaterial, [x, -size.y / 2, z], [x, size.y / 2, z], this.innerGrid); + this.addLine(gridMaterial, [-size.x / 2, y, z], [size.x / 2, y, z], this.innerGrid); } } } @@ -57,20 +61,20 @@ export class Board { group.add(line); }; - addPolycube(polycube) { - this.grid.add(polycube.group); - }; - - removePolycube(polycube) { - this.grid.remove(polycube.group); - }; - toggleInnerGrid(show) { this.showInnerGrid = show; - if (show) { - this.scene.add(this.innerGrid); - } else { - this.scene.remove(this.innerGrid); + if (show) { this.scene.add(this.innerGrid); } + else { this.scene.remove(this.innerGrid); } + }; + + clearGrid() { + while (this.grid.children.length > 0) { + this.grid.remove(this.grid.children[0]); + } + while (this.innerGrid.children.length > 0) { + this.innerGrid.remove(this.innerGrid.children[0]); } + this.scene.remove(this.grid); + this.scene.remove(this.innerGrid); }; }; diff --git a/3D/js/main.js b/3D/js/main.js index 2c92c13..eef6549 100644 --- a/3D/js/main.js +++ b/3D/js/main.js @@ -10,6 +10,7 @@ class MainApp { this.selectedPolycube = null; this.isDragging = false; this.isRightClick = false; + this.polys = []; this.init(); this.animate(); this.eventListener(); @@ -28,11 +29,10 @@ class MainApp { this.controls = new OrbitControls(this.camera, this.renderer.domElement); - this.board = new Board(this.scene, 10); + this.board = new Board(this.scene, { x: 3, y: 3, z: 3 }); this.guiController = new GUIController(this); this.toolbar = new Toolbar(this); - this.polys = []; this.addPolycube({ n: 1, cubes: [[0, 0, 0]], color: 0x00ff00, position: { x: 0, y: 3, z: 0 } }); this.addPolycube({ n: 3, cubes: [[0, 0, 0], [0, 1, 0], [0, 0, 1]], color: 0xff0000, position: { x: 2, y: 2, z: 2 } }); }; @@ -47,7 +47,7 @@ class MainApp { addPolycube(cubeData) { const polycube = new Polycube(cubeData); - this.board.addPolycube(polycube); + this.scene.add(polycube.group); this.polys.push(polycube); }; @@ -57,10 +57,6 @@ class MainApp { this.renderer.setSize(window.innerWidth, window.innerHeight); }; - updateGridSize(size) { - this.board.grid.scale.set(size / 10, size / 10, size / 10); - }; - animate() { requestAnimationFrame(this.animate.bind(this)); this.renderer.render(this.scene, this.camera); @@ -89,9 +85,7 @@ class MainApp { this.isDragging = true; this.controls.enabled = false; - if (event.button === 2) { - this.isRightClick = true; - } + if (event.button === 2) { this.isRightClick = true; } this.lastMousePosition = { x: event.clientX, y: event.clientY }; this.lastValidPosition = this.selectedPolycube.group.position.clone(); @@ -138,18 +132,14 @@ class MainApp { selectPolycube(polycube) { this.selectedPolycube = polycube; this.selectedPolycube.group.children.forEach(child => { - if (child instanceof THREE.LineSegments) { - child.material.color.set(0xffffff); - } + if (child instanceof THREE.LineSegments) { child.material.color.set(0xffffff); } }); }; deselectPolycube() { if (this.selectedPolycube) { this.selectedPolycube.group.children.forEach(child => { - if (child instanceof THREE.LineSegments) { - child.material.color.set(0x000000); - } + if (child instanceof THREE.LineSegments) { child.material.color.set(0x000000); } }); this.selectedPolycube = null; } @@ -158,12 +148,16 @@ class MainApp { snapToGrid(polycube) { const gridSize = 1; const group = polycube.group; + const size = this.board.size; + + const offsetX = (size.x % 2 === 0) ? gridSize / 2 : 0; + const offsetY = (size.y % 2 === 0) ? gridSize / 2 : 0; + const offsetZ = (size.z % 2 === 0) ? gridSize / 2 : 0; - const offset = gridSize / 2; const newPosition = new THREE.Vector3( - Math.round((group.position.x - offset) / gridSize) * gridSize + offset, - Math.round((group.position.y - offset) / gridSize) * gridSize + offset, - Math.round((group.position.z - offset) / gridSize) * gridSize + offset + Math.round((group.position.x - offsetX) / gridSize) * gridSize + offsetX, + Math.round((group.position.y - offsetY) / gridSize) * gridSize + offsetY, + Math.round((group.position.z - offsetZ) / gridSize) * gridSize + offsetZ ); const rotationMatrix = new THREE.Matrix4().makeRotationFromQuaternion(group.quaternion); @@ -199,13 +193,8 @@ class MainApp { return newCubesPositions.some(newPos => newPos.equals(position)); }); }); - - if (overlapping) { - return false; - } else { - group.position.copy(newPosition); - return true; - } + if (overlapping) { return false; } + else { group.position.copy(newPosition); return true; } }; updatePolycubeColor(color) { @@ -217,6 +206,14 @@ class MainApp { }); } }; + + clearBoard() { this.board.clearGrid(); this.board = null; } + createNewBoard(x, y, z) { + const showInnerGrid = this.board ? this.board.showInnerGrid : false; + if (this.board) { this.clearBoard(); } + this.board = new Board(this.scene, { x, y, z }); + this.board.toggleInnerGrid(showInnerGrid); + }; }; const app = new MainApp(); diff --git a/3D/js/popup/cube.js b/3D/js/popup/cube.js index 2016a0b..a9d9c4f 100644 --- a/3D/js/popup/cube.js +++ b/3D/js/popup/cube.js @@ -1,4 +1,4 @@ -import { Polycube } from '../polycube.js'; +import { create3DPopup } from './popup3d.js'; export function createCubePopup(toolbar) { const popupContainer = toolbar.createPopupContainer('cubePopup', toolbar.buttons[0].name); @@ -7,4 +7,115 @@ export function createCubePopup(toolbar) { ctx.fillStyle = '#a0a0a0'; ctx.fillRect(0, 0, popup.width, popup.height); + + const rows = [ + { label: 'Enter values to make Polycubes', box: true, title: true }, + { label: 'N° squares per cube: n = ', type: 'input' }, + { label: 'Position x: ', type: 'input' }, + { label: 'Position y: ', type: 'input' }, + { label: 'Position z: ', type: 'input' }, + { label: 'Open 3D Editor : ', button: true }, + ]; + + const startY = 76; + const rowHeight = 76; + const colX = 20; + let n = 1; + let position = { x: 0, y: 0, z: 0 }; + let coordinates = []; + + rows.forEach((row, index) => { + const y = startY + index * rowHeight; + if (row.box) { + ctx.strokeStyle = '#fff'; + ctx.strokeRect(10, (y - 30), (popup.width - 20), (rowHeight * (row.title ? 5 : 1))); + } + ctx.font = '22px Pixellari'; + ctx.fillStyle = '#000'; + ctx.fillText(row.label, colX, y + 20); + + if (row.type === 'input') { + createInputField(popupContainer, y, index === 0 ? n : 0); + } + if (row.button) { + createButton(popupContainer, y, 'Open 3D Editor', () => { + create3DPopup(toolbar, n, (coords) => { + coordinates = coords; + console.log('Selected Coordinates:', coordinates); + }); + }); + } + }); + + popupContainer.querySelectorAll('input[type="number"]').forEach((input, index) => { + input.addEventListener('change', (e) => { + if (index === 0) n = parseInt(e.target.value); + if (index === 1) position.x = parseInt(e.target.value); + if (index === 2) position.y = parseInt(e.target.value); + if (index === 3) position.z = parseInt(e.target.value); + }); + }); + + popup.addEventListener('mousemove', (e) => { + const rect = popup.getBoundingClientRect(); + const mouseX = e.clientX - rect.left; + const mouseY = e.clientY - rect.top; + let cursor = 'default'; + + rows.forEach((row, index) => { + const y = startY + index * rowHeight; + if (row.button && toolbar.isInside(mouseX, mouseY, { x: popup.width - 120, y: y - 14, width: 110, height: 30 })) { + cursor = 'pointer'; + } + }); + + popup.style.cursor = cursor; + }); + + createButton(popupContainer, startY + rows.length * rowHeight, 'Create Cube', () => { + if (coordinates.length === 0) { + alert('Please select coordinates in the 3D Editor first!'); + return; + } + toolbar.mainApp.addPolycube({ n, cubes: coordinates, color: 0x00ff00, position }); + toolbar.closePopup('cube'); + }); +}; + +function createInputField(popupContainer, y, defaultValue) { + const input = document.createElement('input'); + input.type = 'number'; + input.value = defaultValue; + input.style.position = 'absolute'; + input.style.left = 'calc(100% - 90px)'; + input.style.top = `${y}px`; + input.style.width = '60px'; + input.style.height = '24px'; + input.style.border = '1px solid #000'; + input.style.backgroundColor = '#fff'; + input.style.fontSize = '22px'; + input.style.fontFamily = 'Pixellari'; + input.style.color = '#000'; + input.style.zIndex = '1001'; + input.classList.add('popup-input'); + popupContainer.appendChild(input); +}; + +function createButton(popupContainer, y, label, onClick) { + const button = document.createElement('button'); + button.innerText = label; + button.style.position = 'absolute'; + button.style.left = 'calc(100% - 180px)'; + button.style.top = `${y}px`; + button.style.width = '160px'; + button.style.height = '30px'; + button.style.border = '1px solid #000'; + button.style.backgroundColor = '#fff'; + button.style.fontSize = '20px'; + button.style.fontFamily = 'Pixellari'; + button.style.color = '#0000ff'; + button.style.zIndex = '1001'; + button.style.cursor = 'pointer'; + button.addEventListener('click', onClick); + popupContainer.appendChild(button); }; diff --git a/3D/js/popup/grid.js b/3D/js/popup/grid.js index 28f346a..667b675 100644 --- a/3D/js/popup/grid.js +++ b/3D/js/popup/grid.js @@ -5,4 +5,102 @@ export function showGridPopup(toolbar) { ctx.fillStyle = '#a0a0a0'; ctx.fillRect(0, 0, popup.width, popup.height); + + const rows = [ + { label: 'Create new grid board here', box: true, title: true }, + { label: 'Enter n° x size', type: 'input' }, + { label: 'Enter n° y size', type: 'input' }, + { label: 'Enter n° z size', type: 'input' }, + { label: 'Draw grid by click to =>', icon: '../assets/ic_draw.png' }, + { label: 'Delete current grid :', icon: '../assets/ic_trash.png' }, + ]; + + const startY = 76; + const rowHeight = 76; + const colX = 30; + + rows.forEach((row, index) => { + const y = startY + index * rowHeight; + if (row.box) { + ctx.strokeStyle = '#fff'; + ctx.strokeRect(10, (y - 30), (popup.width - 20), (rowHeight * (row.title ? 5 : 1))); + } + ctx.font = '22px Pixellari'; + ctx.fillStyle = '#000'; + ctx.fillText(row.label, colX, y + 20); + + if (row.icon) { + const icon = new Image(); + icon.src = row.icon; + icon.onload = () => { + ctx.drawImage(icon, popup.width - 94, y - 14, 50, 50); + }; + } else if (row.type === 'input') { + createInputField(popupContainer, y, 3); + } + }); + + let x_size = 3; + let y_size = 3; + let z_size = 3; + + popupContainer.querySelectorAll('input[type="number"]').forEach((input, index) => { + input.addEventListener('change', (e) => { + if (index === 0) x_size = parseInt(e.target.value); + if (index === 1) y_size = parseInt(e.target.value); + if (index === 2) z_size = parseInt(e.target.value); + }); + }); + + popup.addEventListener('mousemove', (e) => { + const rect = popup.getBoundingClientRect(); + const mouseX = e.clientX - rect.left; + const mouseY = e.clientY - rect.top; + let cursor = 'default'; + + rows.forEach((row, index) => { + const y = startY + index * rowHeight; + if (row.icon && toolbar.isInside(mouseX, mouseY, { x: popup.width - 94, y: y - 14, width: 50, height: 50 })) { + cursor = 'pointer'; + } + }); + + popup.style.cursor = cursor; + }); + + popup.addEventListener('click', (e) => { + const rect = popup.getBoundingClientRect(); + const mouseX = e.clientX - rect.left; + const mouseY = e.clientY - rect.top; + + rows.forEach((row, index) => { + const y = startY + index * rowHeight; + if (row.icon && toolbar.isInside(mouseX, mouseY, { x: popup.width - 94, y: y - 14, width: 50, height: 50 })) { + switch (index) { + case 4: toolbar.mainApp.createNewBoard(x_size, y_size, z_size); break; + case 5: toolbar.mainApp.clearBoard(); break; + } + if (toolbar.isMobile) {toolbar.closePopup('grid');} + } + }); + }); +}; + +function createInputField(popupContainer, y, defaultValue) { + const input = document.createElement('input'); + input.type = 'number'; + input.value = defaultValue; + input.style.position = 'absolute'; + input.style.left = 'calc(100% - 120px)'; + input.style.top = `${y}px`; + input.style.width = '80px'; + input.style.height = '24px'; + input.style.border = '1px solid #000'; + input.style.backgroundColor = '#fff'; + input.style.fontSize = '22px'; + input.style.fontFamily = 'Pixellari'; + input.style.color = '#000'; + input.style.zIndex = '1001'; + input.classList.add('popup-input'); + popupContainer.appendChild(input); }; diff --git a/3D/js/popup/popup3d.js b/3D/js/popup/popup3d.js new file mode 100644 index 0000000..d6fb0a1 --- /dev/null +++ b/3D/js/popup/popup3d.js @@ -0,0 +1,101 @@ +import * as THREE from 'three'; +import { OrbitControls } from 'three/addons/controls/OrbitControls.js'; + +export function create3DPopup(toolbar, n, callback) { + const popupContainer = document.createElement('div'); + popupContainer.style.position = 'fixed'; + popupContainer.style.top = '50%'; + popupContainer.style.left = '50%'; + popupContainer.style.transform = 'translate(-50%, -50%)'; + popupContainer.style.width = '80%'; + popupContainer.style.height = '80%'; + popupContainer.style.border = '3px solid #000'; + popupContainer.style.backgroundColor = '#fff'; + popupContainer.style.zIndex = '1000'; + document.body.appendChild(popupContainer); + + const closeButton = document.createElement('button'); + closeButton.innerText = 'Close'; + closeButton.style.position = 'absolute'; + closeButton.style.top = '10px'; + closeButton.style.right = '10px'; + closeButton.style.zIndex = '1001'; + closeButton.style.cursor = 'pointer'; + closeButton.addEventListener('click', () => { + document.body.removeChild(popupContainer); + }); + popupContainer.appendChild(closeButton); + + const saveButton = document.createElement('button'); + saveButton.innerText = 'Save'; + saveButton.style.position = 'absolute'; + saveButton.style.top = '10px'; + saveButton.style.right = '80px'; + saveButton.style.zIndex = '1001'; + saveButton.style.cursor = 'pointer'; + saveButton.addEventListener('click', () => { + callback(selectedCubes); + document.body.removeChild(popupContainer); + }); + popupContainer.appendChild(saveButton); + + const canvas = document.createElement('canvas'); + canvas.width = popupContainer.clientWidth; + canvas.height = popupContainer.clientHeight; + popupContainer.appendChild(canvas); + + const renderer = new THREE.WebGLRenderer({ canvas }); + renderer.setSize(canvas.width, canvas.height); + + const scene = new THREE.Scene(); + const camera = new THREE.PerspectiveCamera(75, canvas.width / canvas.height, 0.1, 1000); + camera.position.set(5, 5, 5); + + const controls = new OrbitControls(camera, renderer.domElement); + controls.enableDamping = true; + + const gridHelper = new THREE.GridHelper(20, 20); + scene.add(gridHelper); + + const cubeGeometry = new THREE.BoxGeometry(1, 1, 1); + const cubeMaterial = new THREE.MeshBasicMaterial({ color: 0x00ff00, transparent: true, opacity: 0.5 }); + const selectedCubeMaterial = new THREE.MeshBasicMaterial({ color: 0xff0000, transparent: true, opacity: 0.5 }); + + let selectedCubes = []; + + const raycaster = new THREE.Raycaster(); + const mouse = new THREE.Vector2(); + + function onMouseClick(event) { + mouse.x = (event.clientX / canvas.clientWidth) * 2 - 1; + mouse.y = -(event.clientY / canvas.clientHeight) * 2 + 1; + raycaster.setFromCamera(mouse, camera); + + const intersects = raycaster.intersectObjects(scene.children); + if (intersects.length > 0) { + const intersect = intersects[0]; + const position = new THREE.Vector3().copy(intersect.point).floor().addScalar(0.5); + + const existingCube = selectedCubes.find(cube => cube.position.equals(position)); + if (existingCube) { + scene.remove(existingCube); + selectedCubes = selectedCubes.filter(cube => !cube.position.equals(position)); + } else if (selectedCubes.length < n) { + const cube = new THREE.Mesh(cubeGeometry, selectedCubeMaterial); + cube.position.copy(position); + scene.add(cube); + selectedCubes.push(cube); + } + } + } + + canvas.addEventListener('click', onMouseClick); + + function animate() { + requestAnimationFrame(animate); + controls.update(); + renderer.render(scene, camera); + } + + animate(); +}; diff --git a/3D/js/popup/solve.js b/3D/js/popup/solve.js new file mode 100644 index 0000000..0a53b44 --- /dev/null +++ b/3D/js/popup/solve.js @@ -0,0 +1,8 @@ +export function showSolvePopup(toolbar) { + const popupContainer = toolbar.createPopupContainer('solvePopup', toolbar.buttons[2].name); + const popup = popupContainer.querySelector('canvas'); + const ctx = popup.getContext('2d'); + + ctx.fillStyle = '#a0a0a0'; + ctx.fillRect(0, 0, popup.width, popup.height); +}; diff --git a/3D/js/popup/tutorial.js b/3D/js/popup/tutorial.js new file mode 100644 index 0000000..b41580a --- /dev/null +++ b/3D/js/popup/tutorial.js @@ -0,0 +1,8 @@ +export function showTutorialPopup(toolbar) { + const popupContainer = toolbar.createPopupContainer('tutorialPopup', toolbar.buttons[3].name); + const popup = popupContainer.querySelector('canvas'); + const ctx = popup.getContext('2d'); + + ctx.fillStyle = '#a0a0a0'; + ctx.fillRect(0, 0, popup.width, popup.height); +}; diff --git a/3D/js/toolbar.js b/3D/js/toolbar.js index 0b25311..331e252 100644 --- a/3D/js/toolbar.js +++ b/3D/js/toolbar.js @@ -1,5 +1,7 @@ import { createCubePopup } from './popup/cube.js'; import { showGridPopup } from './popup/grid.js'; +import { showSolvePopup } from './popup/solve.js'; +import { showTutorialPopup } from './popup/tutorial.js'; export class Toolbar { constructor(mainApp) { @@ -18,7 +20,6 @@ export class Toolbar { }; checkIfMobile() { return window.innerWidth <= 800; }; - setupCanvas() { this.canvas = document.createElement('canvas'); this.ctx = this.canvas.getContext('2d'); @@ -33,7 +34,9 @@ export class Toolbar { createButtons() { return [ { name: 'Create Cube', icon: '../assets/ic_plus.png', action: () => this.togglePopup('cube'), description: 'To create a new cube and add it to the scene.' }, - { name: 'Grid Settings', icon: '../assets/ic_table.png', action: () => this.togglePopup('grid'), description: 'To change the grid settings.' } + { name: 'Grid Settings', icon: '../assets/ic_table.png', action: () => this.togglePopup('grid'), description: 'To change the grid settings.' }, + { name: 'Solving Polycube', icon: '../assets/ic_solving.png', action: () => this.togglePopup('solve'), description: 'To solve the polycube puzzle.\nUse different algorithms to solve.' }, + { name: 'Tutorial', icon: '../assets/ic_question.png', action: () => this.togglePopup('tutorial'), description: 'To view the tutorial.\nLearn how to use the application.' }, ]; }; @@ -135,8 +138,12 @@ export class Toolbar { if (this.popupOpen) { const cubePopup = document.getElementById('cubePopup'); const gridPopup = document.getElementById('gridPopup'); + const solvePopup = document.getElementById('solvePopup'); + const tutorialPopup = document.getElementById('tutorialPopup'); if ((cubePopup && !cubePopup.contains(e.target) && !this.canvas.contains(e.target)) || - (gridPopup && !gridPopup.contains(e.target) && !this.canvas.contains(e.target))) { + (gridPopup && !gridPopup.contains(e.target) && !this.canvas.contains(e.target)) || + (solvePopup && !solvePopup.contains(e.target) && !this.canvas.contains(e.target)) || + (tutorialPopup && !tutorialPopup.contains(e.target) && !this.canvas.contains(e.target))) { this.closeCurrentPopup(); this.tooltip.style.display = 'none'; } @@ -185,9 +192,8 @@ export class Toolbar { }; togglePopup(type) { - if (this.currentPopup === type) { - this.closePopup(type); - } else { + if (this.currentPopup === type) { this.closePopup(type); } + else { this.closeCurrentPopup(); this.showPopup(type); this.currentPopup = type; @@ -199,6 +205,8 @@ export class Toolbar { switch (type) { case 'cube': createCubePopup(this); break; case 'grid': showGridPopup(this); break; + case 'solve': showSolvePopup(this); break; + case 'tutorial': showTutorialPopup(this); break; } }; diff --git a/assets/ic_left_click.png b/assets/ic_left_click.png new file mode 100644 index 0000000000000000000000000000000000000000..4d3f08da6ab093ebe774f0dc698571ce56e13779 GIT binary patch literal 1236 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 JPDHLkV1k(RY_R|U literal 0 HcmV?d00001 diff --git a/assets/keyboard_117.png b/assets/keyboard_117.png new file mode 100644 index 0000000000000000000000000000000000000000..84699f839461a47e08f5ebb7b274cd57d68a044c GIT binary patch literal 408 zcmV;J0cZY+P)Px#1ZP1_K>z@;j|==^1poj532;bRa{vGi!vFvd!vV){sAK>D0WC>HK~z{r?Uun2 zf-n$8bFv3(@n9Kt;oUl{z>5vofd>olW;Hl3!^aR8A&@XKLuQ@{n{4(4bHJ_buiEi+ zt>&OE*KSN)Zjb8zd=-QJ!IU5vE-Hcc{}KpKA10J!yE%L$<~V)Kes^A|0Et~#>$_lm zUEVnYLj(|wi^f=a7VPDVGa!&Ez*vkG@E4H*fmDGZ@n0y%zkXPpfCckbo&{?mtALf$ z#bg0(5DM^Y9iUhvKrcfjl>`Ydz>kVbUPx#1ZP1_K>z@;j|==^1poj532;bRa{vGi!vFvd!vV){sAK>D0ar;xK~z{r&6dG& z!Y~j;b)pAq;XoPbf_v(qf?Q~T4mhNM+;T`8a7^Y2z9X+!u`OF7o_R>tYPHX{JSL>a z+xh*w1&?z5gTUqEvnpTLTWnp476O38K;ZCtawpe7cVWAE`ITrX07wi3WYz*|?RUpA z0TOd~^F4|M*gUqV#mlp-B0yhg47@xGwW#?he}4t%3QQT#@xKJdC z?*w!gTA+)3ngHl-#LuxFfr9k^LUS~~gnU;vk+fd1 sG15HsX>(Ak6X4JZff{IiO9&zS0K4_iYFzPQ(f|Me07*qoM6N<$f}e!B82|tP literal 0 HcmV?d00001 diff --git a/rapport.md b/rapport.md index 718e7c7..9864c8f 100644 --- a/rapport.md +++ b/rapport.md @@ -11,4 +11,30 @@ Nous exprimons également notre gratitude envers Monsieur **Farès BELHADJ** pou ## I) Introduction +Ce document représente un état de l'art approfondi sur le sujet des pavages de polyominos. Cette étude s'inscrit dans le cadre de notre projet tuteuré, mené au sein de notre cursus à l’Université Paris 8 Vincennes - Saint-Denis, pendant notre troisième année. Ce projet tuteuré se décline en deux phases distinctes réparties sur les deux semestres. La première phase consiste en une recherche et réflexion approfondie, aboutissant à la rédaction de cet état de l'art que vous êtes en train de lire. La seconde partie de notre projet implique la mise en pratique des connaissances acquises, avec la concrétisation du projet dans son ensemble. + +Le sujet qui nous a été attribué est particulièrement captivant. Il nous offre l'opportunité d'explorer les domaines des mathématiques à travers l'étude des polyominos. Ce sont des formes géométriques, constituées de l'assemblage de carrés unitaires connectés entre eux, pouvant varier en taille allant d’une taille 1 à une limite indéterminée. Les polyominos ont gagné en popularité au cours du 20e siècle, tant en raison des problèmes mathématiques complexes qu'ils suscitent que grâce à la diffusion mondiale du jeu Tetris. Ce dernier, basé sur des pièces de polyominos tombant sur une grille de dimensions finies, a contribué à populariser ces formes simples. Le jeu consiste à compléter des lignes en largeur pour augmenter le score du joueur. + +Ce projet nous offre une perspective unique en nous permettant d'explorer non seulement le domaine des mathématiques, grâce au pavage qui consiste en une disposition régulière d’éléments les uns à-côtés des autres afin d’obtenir une nouvelle forme. Dans le cadre de notre sujet, il s’agit de l'emboîtement des polyominos, mais également celui de l'informatique et de l'algorithmique. Nous mettrons en œuvre nos connaissances, notamment en utilisant JavaScript, un langage de programmation offrant des capacités graphiques avancées tout tout en restant flexible. Sa popularité et sa compatibilité avec les navigateurs web en font un outil idéal pour la visualisation des résultats des algorithmes de pavage. De plus, la syntaxe claire et la facilité de manipulation des objets dans JavaScript faciliteront le processus de conception et de mise en œuvre de notre programme. + +Tout au long de ce document, nous allons plonger dans les aspects théoriques des pavages de polyominos, en préparation à la phase pratique de notre projet tuteuré dans laquelle nous exploiteront pleinement les fonctionnalités de JavaScript pour concrétiser nos idées et visualiser les résultats obtenus. + + +## II) Exploration des Types de Polyomino en 2D + +Dans ce chapitre, nous explorerons les divers polyominos existants, offrant ainsi une compréhension approfondie de l'enjeu de notre projet tuteuré. Pour ce faire, nous reverrons les fondements même d'un polyomino, cela nous amènera à voir leurs caractéristiques essentielles, tout en élargissant notre analyse pour examiner les nombreuses variantes qui existent pour certains polyominos spécifiques. Cette démarche nous permettra de comprendre les défis et les problèmes liés à ces derniers, nous permettant d’avoir un point de vue global des possibilités et des complexités que nous devrons aborder dans la réalisation de notre projet. + +### Introduction aux Polyomino + +Avant de poursuivre notre étude, revenons sur la nature même d'un polyomino. + +Le terme "polyomino" a été introduit en 1953 par Solomon W. Golomb (Source : [Solomon W. Golomb - Wikipedia](https://en.wikipedia.org/wiki/Solomon_W._Golomb)), un ingénieur et mathématicien célèbre pour ses contributions variées, dont l'invention des "échecodames" 1 et son empreinte laissée grâce à ses travaux sur les polyominos. + +L'appellation "polyomino" est dérivée du mot "domino", et le préfixe "poly" peut être substitué par le nombre de parties composant le polyomino en grec. Un polyomino, tel que décrit précédemment, se compose d'un ensemble de carrés connectés entre eux par les bords, le différenciant des autres polyformes tels que ceux formés de répétition pyramides ou des cubes. + +Dans le cadre de notre projet tuteuré, nous focalisons notre attention sur les polyominos en raison de leur prédominance et de leur utilisation évidente dans le contexte d’un plan (leurs équivalents tridimensionnels étant les polycubes). En effet, travailler avec des polyominos présente des avantages pratiques, notamment sur le plan 2D. Par rapport aux polyiamondes, qui ont une forme de base triangulaire, les polyominos offrent une plus grande simplicité, réduisant les complications liées aux espaces perdus engendrés par cette forme particulière. D'un autre côté, les formes plus rectangulaires s’avèrent être des équivalents aux polyominos, rendant leur étude moins captivante dans le cadre de notre projet tuteuré. + +### Description du pavage + +