feat: project setup for week 3
This commit is contained in:
parent
0d4fae3638
commit
fac9c78ee0
0
Week-3/GeneralNotes.md
Normal file
0
Week-3/GeneralNotes.md
Normal file
24
Week-3/Task-1/r3f/.gitignore
vendored
Normal file
24
Week-3/Task-1/r3f/.gitignore
vendored
Normal file
@ -0,0 +1,24 @@
|
||||
# Logs
|
||||
logs
|
||||
*.log
|
||||
npm-debug.log*
|
||||
yarn-debug.log*
|
||||
yarn-error.log*
|
||||
pnpm-debug.log*
|
||||
lerna-debug.log*
|
||||
|
||||
node_modules
|
||||
dist
|
||||
dist-ssr
|
||||
*.local
|
||||
|
||||
# Editor directories and files
|
||||
.vscode/*
|
||||
!.vscode/extensions.json
|
||||
.idea
|
||||
.DS_Store
|
||||
*.suo
|
||||
*.ntvs*
|
||||
*.njsproj
|
||||
*.sln
|
||||
*.sw?
|
||||
10
Week-3/Task-1/r3f/README.md
Normal file
10
Week-3/Task-1/r3f/README.md
Normal file
@ -0,0 +1,10 @@
|
||||
# React + 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
|
||||
29
Week-3/Task-1/r3f/eslint.config.js
Normal file
29
Week-3/Task-1/r3f/eslint.config.js
Normal file
@ -0,0 +1,29 @@
|
||||
import js from '@eslint/js'
|
||||
import globals from 'globals'
|
||||
import reactHooks from 'eslint-plugin-react-hooks'
|
||||
import reactRefresh from 'eslint-plugin-react-refresh'
|
||||
import { defineConfig, globalIgnores } from 'eslint/config'
|
||||
|
||||
export default defineConfig([
|
||||
globalIgnores(['dist']),
|
||||
{
|
||||
files: ['**/*.{js,jsx}'],
|
||||
extends: [
|
||||
js.configs.recommended,
|
||||
reactHooks.configs.flat.recommended,
|
||||
reactRefresh.configs.vite,
|
||||
],
|
||||
languageOptions: {
|
||||
ecmaVersion: 2020,
|
||||
globals: globals.browser,
|
||||
parserOptions: {
|
||||
ecmaVersion: 'latest',
|
||||
ecmaFeatures: { jsx: true },
|
||||
sourceType: 'module',
|
||||
},
|
||||
},
|
||||
rules: {
|
||||
'no-unused-vars': ['error', { varsIgnorePattern: '^[A-Z_]' }],
|
||||
},
|
||||
},
|
||||
])
|
||||
13
Week-3/Task-1/r3f/index.html
Normal file
13
Week-3/Task-1/r3f/index.html
Normal file
@ -0,0 +1,13 @@
|
||||
<!doctype html>
|
||||
<html lang="en">
|
||||
<head>
|
||||
<meta charset="UTF-8" />
|
||||
<link rel="icon" type="image/svg+xml" href="/favicon.svg" />
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
|
||||
<title>r3f</title>
|
||||
</head>
|
||||
<body>
|
||||
<div id="root"></div>
|
||||
<script type="module" src="/src/main.jsx"></script>
|
||||
</body>
|
||||
</html>
|
||||
31
Week-3/Task-1/r3f/package.json
Normal file
31
Week-3/Task-1/r3f/package.json
Normal file
@ -0,0 +1,31 @@
|
||||
{
|
||||
"name": "week3-task1-r3f",
|
||||
"private": true,
|
||||
"version": "0.0.0",
|
||||
"type": "module",
|
||||
"scripts": {
|
||||
"dev": "vite",
|
||||
"build": "vite build",
|
||||
"lint": "eslint .",
|
||||
"preview": "vite preview"
|
||||
},
|
||||
"dependencies": {
|
||||
"@react-three/drei": "9.105.6",
|
||||
"@react-three/fiber": "^9.5.0",
|
||||
"react": "^19.2.4",
|
||||
"react-dom": "^19.2.4",
|
||||
"three": "^0.183.2"
|
||||
},
|
||||
"packageManager": "yarn@1.22.22",
|
||||
"devDependencies": {
|
||||
"@eslint/js": "^9.39.4",
|
||||
"@types/react": "^19.2.14",
|
||||
"@types/react-dom": "^19.2.3",
|
||||
"@vitejs/plugin-react": "^6.0.1",
|
||||
"eslint": "^9.39.4",
|
||||
"eslint-plugin-react-hooks": "^7.0.1",
|
||||
"eslint-plugin-react-refresh": "^0.5.2",
|
||||
"globals": "^17.4.0",
|
||||
"vite": "^8.0.1"
|
||||
}
|
||||
}
|
||||
41
Week-3/Task-1/r3f/src/App.css
Normal file
41
Week-3/Task-1/r3f/src/App.css
Normal file
@ -0,0 +1,41 @@
|
||||
.app-container {
|
||||
width: 100vw;
|
||||
height: 100vh;
|
||||
position: relative;
|
||||
background-color: white;
|
||||
overflow: hidden;
|
||||
}
|
||||
|
||||
#controls {
|
||||
position: absolute;
|
||||
top: 20px;
|
||||
left: 20px;
|
||||
z-index: 10;
|
||||
display: flex;
|
||||
gap: 10px;
|
||||
}
|
||||
|
||||
button {
|
||||
padding: 8px 16px;
|
||||
cursor: pointer;
|
||||
background: #f0f0f0;
|
||||
border: 1px solid #ccc;
|
||||
border-radius: 4px;
|
||||
font-weight: 500;
|
||||
transition: all 0.2s;
|
||||
}
|
||||
|
||||
button:hover {
|
||||
background: #e0e0e0;
|
||||
}
|
||||
|
||||
button.active {
|
||||
background: #333;
|
||||
color: white;
|
||||
border-color: #333;
|
||||
}
|
||||
|
||||
.canvas-wrapper {
|
||||
width: 100%;
|
||||
height: 100%;
|
||||
}
|
||||
55
Week-3/Task-1/r3f/src/App.jsx
Normal file
55
Week-3/Task-1/r3f/src/App.jsx
Normal file
@ -0,0 +1,55 @@
|
||||
import React, { useState } from 'react';
|
||||
import { Canvas } from '@react-three/fiber';
|
||||
import { OrbitControls, PerspectiveCamera } from '@react-three/drei';
|
||||
import './App.css';
|
||||
|
||||
const materials = {
|
||||
black: { color: "black", roughness: 1, metalness: 0 },
|
||||
silver: { color: "#C0C0C0", roughness: 0.3, metalness: 1 },
|
||||
gold: { color: "#FFD700", roughness: 0.4, metalness: 1 },
|
||||
};
|
||||
|
||||
function Scene({ variant }) {
|
||||
return (
|
||||
<>
|
||||
<color attach="background" args={["white"]} />
|
||||
|
||||
<ambientLight intensity={0.6 * Math.PI} />
|
||||
<pointLight position={[5, 5, 5]} intensity={1.0 * Math.PI} />
|
||||
|
||||
<mesh rotation={[0, 0, 0]}>
|
||||
<torusKnotGeometry args={[0.8, 0.3, 100, 16]} />
|
||||
<meshStandardMaterial {...materials[variant]}/>
|
||||
</mesh>
|
||||
|
||||
<OrbitControls makeDefault enableDamping dampingFactor={0.05} />
|
||||
</>
|
||||
);
|
||||
}
|
||||
|
||||
export default function App() {
|
||||
const [activeVariant, setActiveVariant] = useState('black');
|
||||
|
||||
return (
|
||||
<div className="app-container">
|
||||
<div id="controls">
|
||||
{Object.keys(materials).map((variant) => (
|
||||
<button
|
||||
key={variant}
|
||||
className={activeVariant === variant ? 'active' : ''}
|
||||
onClick={() => setActiveVariant(variant)}
|
||||
>
|
||||
{variant.charAt(0).toUpperCase() + variant.slice(1)}
|
||||
</button>
|
||||
))}
|
||||
</div>
|
||||
|
||||
<div className="canvas-wrapper">
|
||||
<Canvas dpr={[1, 2]}>
|
||||
<PerspectiveCamera makeDefault position={[0, 0, 5]} />
|
||||
<Scene variant={activeVariant} />
|
||||
</Canvas>
|
||||
</div>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
8
Week-3/Task-1/r3f/src/main.jsx
Normal file
8
Week-3/Task-1/r3f/src/main.jsx
Normal file
@ -0,0 +1,8 @@
|
||||
import { StrictMode } from 'react'
|
||||
import { createRoot } from 'react-dom/client'
|
||||
import App from './App.jsx'
|
||||
createRoot(document.getElementById('root')).render(
|
||||
<StrictMode>
|
||||
<App />
|
||||
</StrictMode>,
|
||||
)
|
||||
7
Week-3/Task-1/r3f/vite.config.js
Normal file
7
Week-3/Task-1/r3f/vite.config.js
Normal file
@ -0,0 +1,7 @@
|
||||
import { defineConfig } from 'vite'
|
||||
import react from '@vitejs/plugin-react'
|
||||
|
||||
// https://vite.dev/config/
|
||||
export default defineConfig({
|
||||
plugins: [react()],
|
||||
})
|
||||
1
Week-3/Task-1/report.md
Normal file
1
Week-3/Task-1/report.md
Normal file
@ -0,0 +1 @@
|
||||
# Task 1 Report
|
||||
22
Week-3/Task-1/thob/notes.md
Normal file
22
Week-3/Task-1/thob/notes.md
Normal file
@ -0,0 +1,22 @@
|
||||
# Thob Builder Notes: Task 1 - Material Variant Switcher
|
||||
|
||||
## Overview
|
||||
Implemented the basic material configuration pattern using Thob's built-in `MaterialVariant` and `Material` properties.
|
||||
|
||||
## Visual Observations
|
||||
- **Lighting Limitation**: I observed that by default, the light only hits the object from one side, leaving the other side completely dark.
|
||||
- **Surface Definition**: Standard material properties (color, roughness, metalness) were easy to edit via the sidebar properties panel.
|
||||
|
||||
## Technical Observations (Console Logs)
|
||||
During development in the builder, several warnings and errors were observed:
|
||||
- **404 Errors**: `GET https://builder.thob.studio/builder/... 404 (Not Found)`.
|
||||
- **Hydration Warnings**: `No HydrateFallback element provided to render during initial hydration`.
|
||||
- **Component State**: `undefined is changing from uncontrolled to controlled`. This suggests a potential issue in how the builder manages internal component state when swapping materials.
|
||||
|
||||
## Builder Workflow
|
||||
- **Node Structure**: The scene was built using a `Canvas` -> `mesh` -> `sphereGeometry` hierarchy.
|
||||
- **Variant Linking**: A `MaterialVariant` node was used to manage the switching logic.
|
||||
- **UI Integration**: A simple `Button` was added under the `UI` group to trigger the material change.
|
||||
|
||||
## Conclusion
|
||||
The builder makes it very easy to define and link material variants without writing code, but the default lighting environment needs more control (e.g., adding more lights or an environment map) to avoid the "dark side" effect.
|
||||
17
Week-3/Task-1/vanilla/index.html
Normal file
17
Week-3/Task-1/vanilla/index.html
Normal file
@ -0,0 +1,17 @@
|
||||
<!DOCTYPE html>
|
||||
<html lang="en">
|
||||
<head>
|
||||
<meta charset="UTF-8" />
|
||||
<title>Material Variant Switcher</title>
|
||||
</head>
|
||||
<body>
|
||||
|
||||
<div id="controls">
|
||||
<button data-variant="black" class="active">Black</button>
|
||||
<button data-variant="silver">Silver</button>
|
||||
<button data-variant="gold">Gold</button>
|
||||
</div>
|
||||
|
||||
<script type="module" src="./main.js"></script>
|
||||
</body>
|
||||
</html>
|
||||
80
Week-3/Task-1/vanilla/main.js
Normal file
80
Week-3/Task-1/vanilla/main.js
Normal file
@ -0,0 +1,80 @@
|
||||
import * as THREE from "three";
|
||||
import { OrbitControls } from "three/examples/jsm/controls/OrbitControls.js";
|
||||
|
||||
const scene = new THREE.Scene();
|
||||
scene.background = new THREE.Color("white");
|
||||
|
||||
const camera = new THREE.PerspectiveCamera(
|
||||
75,
|
||||
window.innerWidth / window.innerHeight,
|
||||
0.1,
|
||||
1000
|
||||
);
|
||||
camera.position.z = 5;
|
||||
|
||||
const renderer = new THREE.WebGLRenderer();
|
||||
renderer.setSize(window.innerWidth, window.innerHeight);
|
||||
document.body.appendChild(renderer.domElement);
|
||||
|
||||
const geometry = new THREE.TorusKnotGeometry(1.5, 0.5, 100, 16);
|
||||
|
||||
const materials = {
|
||||
black: new THREE.MeshStandardMaterial({
|
||||
color: "black",
|
||||
roughness: 1,
|
||||
metalness: 0,
|
||||
}),
|
||||
silver: new THREE.MeshStandardMaterial({
|
||||
color: "#C0C0C0",
|
||||
roughness: 0.3,
|
||||
metalness: 1,
|
||||
}),
|
||||
gold: new THREE.MeshStandardMaterial({
|
||||
color: "#FFD700",
|
||||
roughness: 0.4,
|
||||
metalness: 1,
|
||||
}),
|
||||
};
|
||||
|
||||
const sphere = new THREE.Mesh(geometry, materials.black);
|
||||
scene.add(sphere);
|
||||
|
||||
const ambientLight = new THREE.AmbientLight(0xffffff, 0.6);
|
||||
scene.add(ambientLight);
|
||||
|
||||
const pointLight = new THREE.PointLight(0xffffff, 1);
|
||||
pointLight.position.set(5, 5, 5);
|
||||
scene.add(pointLight);
|
||||
|
||||
const controls = new OrbitControls(camera, renderer.domElement);
|
||||
controls.enableDamping = true;
|
||||
|
||||
const buttons = document.querySelectorAll("button");
|
||||
|
||||
buttons.forEach((btn) => {
|
||||
btn.addEventListener("click", () => {
|
||||
const variant = btn.getAttribute("data-variant");
|
||||
|
||||
sphere.material = materials[variant];
|
||||
|
||||
buttons.forEach((b) => b.classList.remove("active"));
|
||||
btn.classList.add("active");
|
||||
});
|
||||
});
|
||||
|
||||
function animate() {
|
||||
requestAnimationFrame(animate);
|
||||
|
||||
controls.update();
|
||||
|
||||
sphere.rotation.y += 0.01;
|
||||
|
||||
renderer.render(scene, camera);
|
||||
}
|
||||
animate();
|
||||
|
||||
window.addEventListener("resize", () => {
|
||||
camera.aspect = window.innerWidth / window.innerHeight;
|
||||
camera.updateProjectionMatrix();
|
||||
renderer.setSize(window.innerWidth, window.innerHeight);
|
||||
});
|
||||
18
Week-3/Task-1/vanilla/package.json
Normal file
18
Week-3/Task-1/vanilla/package.json
Normal file
@ -0,0 +1,18 @@
|
||||
{
|
||||
"name": "week3-task1-vanilla",
|
||||
"version": "0.0.0",
|
||||
"private": true,
|
||||
"type": "module",
|
||||
"scripts": {
|
||||
"dev": "vite",
|
||||
"build": "vite build",
|
||||
"preview": "vite preview"
|
||||
},
|
||||
"packageManager": "yarn@1.22.22",
|
||||
"devDependencies": {
|
||||
"vite": "^8.0.1"
|
||||
},
|
||||
"dependencies": {
|
||||
"three": "^0.183.2"
|
||||
}
|
||||
}
|
||||
0
Week-3/Task-1/vanilla/style.css
Normal file
0
Week-3/Task-1/vanilla/style.css
Normal file
24
Week-3/Task-2/r3f/.gitignore
vendored
Normal file
24
Week-3/Task-2/r3f/.gitignore
vendored
Normal file
@ -0,0 +1,24 @@
|
||||
# Logs
|
||||
logs
|
||||
*.log
|
||||
npm-debug.log*
|
||||
yarn-debug.log*
|
||||
yarn-error.log*
|
||||
pnpm-debug.log*
|
||||
lerna-debug.log*
|
||||
|
||||
node_modules
|
||||
dist
|
||||
dist-ssr
|
||||
*.local
|
||||
|
||||
# Editor directories and files
|
||||
.vscode/*
|
||||
!.vscode/extensions.json
|
||||
.idea
|
||||
.DS_Store
|
||||
*.suo
|
||||
*.ntvs*
|
||||
*.njsproj
|
||||
*.sln
|
||||
*.sw?
|
||||
10
Week-3/Task-2/r3f/README.md
Normal file
10
Week-3/Task-2/r3f/README.md
Normal file
@ -0,0 +1,10 @@
|
||||
# React + 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
|
||||
29
Week-3/Task-2/r3f/eslint.config.js
Normal file
29
Week-3/Task-2/r3f/eslint.config.js
Normal file
@ -0,0 +1,29 @@
|
||||
import js from '@eslint/js'
|
||||
import globals from 'globals'
|
||||
import reactHooks from 'eslint-plugin-react-hooks'
|
||||
import reactRefresh from 'eslint-plugin-react-refresh'
|
||||
import { defineConfig, globalIgnores } from 'eslint/config'
|
||||
|
||||
export default defineConfig([
|
||||
globalIgnores(['dist']),
|
||||
{
|
||||
files: ['**/*.{js,jsx}'],
|
||||
extends: [
|
||||
js.configs.recommended,
|
||||
reactHooks.configs.flat.recommended,
|
||||
reactRefresh.configs.vite,
|
||||
],
|
||||
languageOptions: {
|
||||
ecmaVersion: 2020,
|
||||
globals: globals.browser,
|
||||
parserOptions: {
|
||||
ecmaVersion: 'latest',
|
||||
ecmaFeatures: { jsx: true },
|
||||
sourceType: 'module',
|
||||
},
|
||||
},
|
||||
rules: {
|
||||
'no-unused-vars': ['error', { varsIgnorePattern: '^[A-Z_]' }],
|
||||
},
|
||||
},
|
||||
])
|
||||
13
Week-3/Task-2/r3f/index.html
Normal file
13
Week-3/Task-2/r3f/index.html
Normal file
@ -0,0 +1,13 @@
|
||||
<!doctype html>
|
||||
<html lang="en">
|
||||
<head>
|
||||
<meta charset="UTF-8" />
|
||||
<link rel="icon" type="image/svg+xml" href="/favicon.svg" />
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
|
||||
<title>r3f</title>
|
||||
</head>
|
||||
<body>
|
||||
<div id="root"></div>
|
||||
<script type="module" src="/src/main.jsx"></script>
|
||||
</body>
|
||||
</html>
|
||||
31
Week-3/Task-2/r3f/package.json
Normal file
31
Week-3/Task-2/r3f/package.json
Normal file
@ -0,0 +1,31 @@
|
||||
{
|
||||
"name": "week3-task2-r3f",
|
||||
"private": true,
|
||||
"version": "0.0.0",
|
||||
"type": "module",
|
||||
"scripts": {
|
||||
"dev": "vite",
|
||||
"build": "vite build",
|
||||
"lint": "eslint .",
|
||||
"preview": "vite preview"
|
||||
},
|
||||
"dependencies": {
|
||||
"@react-three/drei": "9.105.6",
|
||||
"@react-three/fiber": "^9.5.0",
|
||||
"react": "^19.2.4",
|
||||
"react-dom": "^19.2.4",
|
||||
"three": "^0.183.2"
|
||||
},
|
||||
"packageManager": "yarn@1.22.22",
|
||||
"devDependencies": {
|
||||
"@eslint/js": "^9.39.4",
|
||||
"@types/react": "^19.2.14",
|
||||
"@types/react-dom": "^19.2.3",
|
||||
"@vitejs/plugin-react": "^6.0.1",
|
||||
"eslint": "^9.39.4",
|
||||
"eslint-plugin-react-hooks": "^7.0.1",
|
||||
"eslint-plugin-react-refresh": "^0.5.2",
|
||||
"globals": "^17.4.0",
|
||||
"vite": "^8.0.1"
|
||||
}
|
||||
}
|
||||
41
Week-3/Task-2/r3f/src/App.css
Normal file
41
Week-3/Task-2/r3f/src/App.css
Normal file
@ -0,0 +1,41 @@
|
||||
.app-container {
|
||||
width: 100vw;
|
||||
height: 100vh;
|
||||
position: relative;
|
||||
background-color: white;
|
||||
overflow: hidden;
|
||||
}
|
||||
|
||||
#controls {
|
||||
position: absolute;
|
||||
top: 20px;
|
||||
left: 20px;
|
||||
z-index: 10;
|
||||
display: flex;
|
||||
gap: 10px;
|
||||
}
|
||||
|
||||
button {
|
||||
padding: 8px 16px;
|
||||
cursor: pointer;
|
||||
background: #f0f0f0;
|
||||
border: 1px solid #ccc;
|
||||
border-radius: 4px;
|
||||
font-weight: 500;
|
||||
transition: all 0.2s;
|
||||
}
|
||||
|
||||
button:hover {
|
||||
background: #e0e0e0;
|
||||
}
|
||||
|
||||
button.active {
|
||||
background: #333;
|
||||
color: white;
|
||||
border-color: #333;
|
||||
}
|
||||
|
||||
.canvas-wrapper {
|
||||
width: 100%;
|
||||
height: 100%;
|
||||
}
|
||||
53
Week-3/Task-2/r3f/src/App.jsx
Normal file
53
Week-3/Task-2/r3f/src/App.jsx
Normal file
@ -0,0 +1,53 @@
|
||||
import React, { useState} from 'react';
|
||||
import { Canvas } from '@react-three/fiber';
|
||||
import { OrbitControls, PerspectiveCamera, useTexture } from '@react-three/drei';
|
||||
import './App.css';
|
||||
|
||||
const planetUrls = {
|
||||
earth: 'https://encrypted-tbn0.gstatic.com/images?q=tbn:ANd9GcQZVsp7bSmsdGhM1GouOYgZ6l06Za__Z1ZY8A&s',
|
||||
moon: 'https://encrypted-tbn0.gstatic.com/images?q=tbn:ANd9GcROh1go667NHsMdzLyvI-0tt9Mn0eugRp0xhQ&s',
|
||||
sun: 'https://upload.wikimedia.org/wikipedia/commons/a/a4/Solarsystemscope_texture_8k_sun.jpg',
|
||||
};
|
||||
|
||||
function Planet({ variant }) {
|
||||
const textures = useTexture(planetUrls);
|
||||
|
||||
return (
|
||||
<mesh rotation={[0, 0, 0]}>
|
||||
<sphereGeometry args={[1.5, 100, 100]} />
|
||||
<meshStandardMaterial map={textures[variant]} roughness={0.6} />
|
||||
</mesh>
|
||||
);
|
||||
}
|
||||
|
||||
export default function App() {
|
||||
const [activeVariant, setActiveVariant] = useState('earth');
|
||||
|
||||
return (
|
||||
<div className="app-container">
|
||||
<div id="controls">
|
||||
{Object.keys(planetUrls).map((variant) => (
|
||||
<button
|
||||
key={variant}
|
||||
className={activeVariant === variant ? 'active' : ''}
|
||||
onClick={() => setActiveVariant(variant)}
|
||||
>
|
||||
{variant.charAt(0).toUpperCase() + variant.slice(1)}
|
||||
</button>
|
||||
))}
|
||||
</div>
|
||||
|
||||
<div className="canvas-wrapper">
|
||||
<Canvas dpr={[1, 2]}>
|
||||
<color attach="background" args={["white"]} />
|
||||
<PerspectiveCamera makeDefault position={[0, 0, 5]} />
|
||||
<ambientLight intensity={0.6 * Math.PI} />
|
||||
<pointLight position={[5, 5, 5]} intensity={1.0 * Math.PI} />
|
||||
<Planet variant={activeVariant} />
|
||||
|
||||
<OrbitControls makeDefault enableDamping dampingFactor={0.05} />
|
||||
</Canvas>
|
||||
</div>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
12
Week-3/Task-2/r3f/src/index.css
Normal file
12
Week-3/Task-2/r3f/src/index.css
Normal file
@ -0,0 +1,12 @@
|
||||
* {
|
||||
box-sizing: border-box;
|
||||
}
|
||||
|
||||
body, html, #root {
|
||||
margin: 0;
|
||||
padding: 0;
|
||||
width: 100%;
|
||||
height: 100%;
|
||||
overflow: hidden;
|
||||
background-color: #111;
|
||||
}
|
||||
10
Week-3/Task-2/r3f/src/main.jsx
Normal file
10
Week-3/Task-2/r3f/src/main.jsx
Normal file
@ -0,0 +1,10 @@
|
||||
import { StrictMode } from 'react'
|
||||
import { createRoot } from 'react-dom/client'
|
||||
import './index.css'
|
||||
import App from './App.jsx'
|
||||
|
||||
createRoot(document.getElementById('root')).render(
|
||||
<StrictMode>
|
||||
<App />
|
||||
</StrictMode>,
|
||||
)
|
||||
7
Week-3/Task-2/r3f/vite.config.js
Normal file
7
Week-3/Task-2/r3f/vite.config.js
Normal file
@ -0,0 +1,7 @@
|
||||
import { defineConfig } from 'vite'
|
||||
import react from '@vitejs/plugin-react'
|
||||
|
||||
// https://vite.dev/config/
|
||||
export default defineConfig({
|
||||
plugins: [react()],
|
||||
})
|
||||
1
Week-3/Task-2/report.md
Normal file
1
Week-3/Task-2/report.md
Normal file
@ -0,0 +1 @@
|
||||
# Task 2 Report
|
||||
22
Week-3/Task-2/thob/notes.md
Normal file
22
Week-3/Task-2/thob/notes.md
Normal file
@ -0,0 +1,22 @@
|
||||
# Thob Builder Notes: Task 2 - Texture Switcher
|
||||
|
||||
## Overview
|
||||
Implemented a planetary texture switcher (Earth/Moon) using a `RadioGroup` for the UI and the built-in `MaterialVariant` system for texture swapping.
|
||||
|
||||
## Visual Observations
|
||||
- **Texture Resolution**: The builder handles texture mapping on a sphere smoothly.
|
||||
- **UI Interaction**: Using a `RadioGroup` feels more natural for a configurator pattern compared to a single toggle button.
|
||||
|
||||
## Technical Observations (Console Logs)
|
||||
During development in the builder, several warnings and errors were observed:
|
||||
- **404 Errors**: `GET https://builder.thob.studio/builder/... 404 (Not Found)`.
|
||||
- **Method Registration**: `GetBindingData... method already registered`. This appears repeatedly in the logs during the preview phase.
|
||||
- **Hydration Warnings**: `No HydrateFallback element provided to render during initial hydration`.
|
||||
- **Component State**: `undefined is changing from uncontrolled to controlled`. This suggests a potential issue in how the builder manages internal component state when switching variants via `RadioGroup`.
|
||||
|
||||
## Builder Workflow
|
||||
- **Node Hierarchy**: The UI was structured using a `RadioGroup` -> `For` loop -> `RadioGroupItem` layout, demonstrating high-level UI component support in the builder.
|
||||
- **State Linking**: Connecting the `RadioGroup` selection to the `MaterialVariant` property was straightforward in the properties panel.
|
||||
|
||||
## Conclusion
|
||||
The Thob Builder's `RadioGroup` component is a strong candidate for professional configurators. The process of linking textures to UI elements is intuitive and requires zero code. However, the runtime console warnings should be investigated to ensure production stability.
|
||||
17
Week-3/Task-2/vanilla/index.html
Normal file
17
Week-3/Task-2/vanilla/index.html
Normal file
@ -0,0 +1,17 @@
|
||||
<!DOCTYPE html>
|
||||
<html lang="en">
|
||||
<head>
|
||||
<meta charset="UTF-8" />
|
||||
<title>Task 2 — Texture / Surface Variant Switcher</title>
|
||||
</head>
|
||||
<body>
|
||||
|
||||
<div id="controls">
|
||||
<button data-variant="earth" class="active">Earth</button>
|
||||
<button data-variant="moon">Moon</button>
|
||||
<button data-variant="sun">Sun</button>
|
||||
</div>
|
||||
|
||||
<script type="module" src="./main.js"></script>
|
||||
</body>
|
||||
</html>
|
||||
75
Week-3/Task-2/vanilla/main.js
Normal file
75
Week-3/Task-2/vanilla/main.js
Normal file
@ -0,0 +1,75 @@
|
||||
import * as THREE from "three";
|
||||
import { OrbitControls } from "three/examples/jsm/controls/OrbitControls.js";
|
||||
|
||||
const scene = new THREE.Scene();
|
||||
scene.background = new THREE.Color("white");
|
||||
|
||||
const camera = new THREE.PerspectiveCamera(
|
||||
75,
|
||||
window.innerWidth / window.innerHeight,
|
||||
0.1,
|
||||
1000
|
||||
);
|
||||
camera.position.z = 5;
|
||||
|
||||
const renderer = new THREE.WebGLRenderer();
|
||||
renderer.setSize(window.innerWidth, window.innerHeight);
|
||||
document.body.appendChild(renderer.domElement);
|
||||
|
||||
const geometry = new THREE.SphereGeometry(1.5, 100, 100);
|
||||
|
||||
const loader = new THREE.TextureLoader();
|
||||
|
||||
const textures = {
|
||||
earth: loader.load('https://encrypted-tbn0.gstatic.com/images?q=tbn:ANd9GcQZVsp7bSmsdGhM1GouOYgZ6l06Za__Z1ZY8A&s'),
|
||||
moon: loader.load('https://encrypted-tbn0.gstatic.com/images?q=tbn:ANd9GcROh1go667NHsMdzLyvI-0tt9Mn0eugRp0xhQ&s'),
|
||||
sun: loader.load('https://upload.wikimedia.org/wikipedia/commons/a/a4/Solarsystemscope_texture_8k_sun.jpg')
|
||||
};
|
||||
|
||||
const material = new THREE.MeshStandardMaterial({
|
||||
map: textures.earth,
|
||||
roughness: 0.6
|
||||
});
|
||||
const mesh = new THREE.Mesh(geometry, material);
|
||||
scene.add(mesh);
|
||||
|
||||
const ambientLight = new THREE.AmbientLight(0xffffff, 0.6);
|
||||
scene.add(ambientLight);
|
||||
|
||||
const pointLight = new THREE.PointLight(0xffffff, 1);
|
||||
pointLight.position.set(5, 5, 5);
|
||||
scene.add(pointLight);
|
||||
|
||||
const controls = new OrbitControls(camera, renderer.domElement);
|
||||
controls.enableDamping = true;
|
||||
|
||||
const buttons = document.querySelectorAll("button");
|
||||
|
||||
buttons.forEach((btn) => {
|
||||
btn.addEventListener("click", () => {
|
||||
const variant = btn.getAttribute("data-variant");
|
||||
|
||||
mesh.material.map = textures[variant];
|
||||
mesh.material.needsUpdate = true;
|
||||
|
||||
buttons.forEach((b) => b.classList.remove("active"));
|
||||
btn.classList.add("active");
|
||||
});
|
||||
});
|
||||
|
||||
function animate() {
|
||||
requestAnimationFrame(animate);
|
||||
|
||||
controls.update();
|
||||
|
||||
mesh.rotation.y += 0.01;
|
||||
|
||||
renderer.render(scene, camera);
|
||||
}
|
||||
animate();
|
||||
|
||||
window.addEventListener("resize", () => {
|
||||
camera.aspect = window.innerWidth / window.innerHeight;
|
||||
camera.updateProjectionMatrix();
|
||||
renderer.setSize(window.innerWidth, window.innerHeight);
|
||||
});
|
||||
18
Week-3/Task-2/vanilla/package.json
Normal file
18
Week-3/Task-2/vanilla/package.json
Normal file
@ -0,0 +1,18 @@
|
||||
{
|
||||
"name": "week3-task2-vanilla",
|
||||
"version": "0.0.0",
|
||||
"private": true,
|
||||
"type": "module",
|
||||
"scripts": {
|
||||
"dev": "vite",
|
||||
"build": "vite build",
|
||||
"preview": "vite preview"
|
||||
},
|
||||
"packageManager": "yarn@1.22.22",
|
||||
"devDependencies": {
|
||||
"vite": "^8.0.1"
|
||||
},
|
||||
"dependencies": {
|
||||
"three": "^0.183.2"
|
||||
}
|
||||
}
|
||||
24
Week-3/Task-3/r3f/.gitignore
vendored
Normal file
24
Week-3/Task-3/r3f/.gitignore
vendored
Normal file
@ -0,0 +1,24 @@
|
||||
# Logs
|
||||
logs
|
||||
*.log
|
||||
npm-debug.log*
|
||||
yarn-debug.log*
|
||||
yarn-error.log*
|
||||
pnpm-debug.log*
|
||||
lerna-debug.log*
|
||||
|
||||
node_modules
|
||||
dist
|
||||
dist-ssr
|
||||
*.local
|
||||
|
||||
# Editor directories and files
|
||||
.vscode/*
|
||||
!.vscode/extensions.json
|
||||
.idea
|
||||
.DS_Store
|
||||
*.suo
|
||||
*.ntvs*
|
||||
*.njsproj
|
||||
*.sln
|
||||
*.sw?
|
||||
10
Week-3/Task-3/r3f/README.md
Normal file
10
Week-3/Task-3/r3f/README.md
Normal file
@ -0,0 +1,10 @@
|
||||
# React + 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
|
||||
29
Week-3/Task-3/r3f/eslint.config.js
Normal file
29
Week-3/Task-3/r3f/eslint.config.js
Normal file
@ -0,0 +1,29 @@
|
||||
import js from '@eslint/js'
|
||||
import globals from 'globals'
|
||||
import reactHooks from 'eslint-plugin-react-hooks'
|
||||
import reactRefresh from 'eslint-plugin-react-refresh'
|
||||
import { defineConfig, globalIgnores } from 'eslint/config'
|
||||
|
||||
export default defineConfig([
|
||||
globalIgnores(['dist']),
|
||||
{
|
||||
files: ['**/*.{js,jsx}'],
|
||||
extends: [
|
||||
js.configs.recommended,
|
||||
reactHooks.configs.flat.recommended,
|
||||
reactRefresh.configs.vite,
|
||||
],
|
||||
languageOptions: {
|
||||
ecmaVersion: 2020,
|
||||
globals: globals.browser,
|
||||
parserOptions: {
|
||||
ecmaVersion: 'latest',
|
||||
ecmaFeatures: { jsx: true },
|
||||
sourceType: 'module',
|
||||
},
|
||||
},
|
||||
rules: {
|
||||
'no-unused-vars': ['error', { varsIgnorePattern: '^[A-Z_]' }],
|
||||
},
|
||||
},
|
||||
])
|
||||
13
Week-3/Task-3/r3f/index.html
Normal file
13
Week-3/Task-3/r3f/index.html
Normal file
@ -0,0 +1,13 @@
|
||||
<!doctype html>
|
||||
<html lang="en">
|
||||
<head>
|
||||
<meta charset="UTF-8" />
|
||||
<link rel="icon" type="image/svg+xml" href="/favicon.svg" />
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
|
||||
<title>r3f</title>
|
||||
</head>
|
||||
<body>
|
||||
<div id="root"></div>
|
||||
<script type="module" src="/src/main.jsx"></script>
|
||||
</body>
|
||||
</html>
|
||||
31
Week-3/Task-3/r3f/package.json
Normal file
31
Week-3/Task-3/r3f/package.json
Normal file
@ -0,0 +1,31 @@
|
||||
{
|
||||
"name": "week3-task3-r3f",
|
||||
"private": true,
|
||||
"version": "0.0.0",
|
||||
"type": "module",
|
||||
"scripts": {
|
||||
"dev": "vite",
|
||||
"build": "vite build",
|
||||
"lint": "eslint .",
|
||||
"preview": "vite preview"
|
||||
},
|
||||
"dependencies": {
|
||||
"@react-three/drei": "9.105.6",
|
||||
"@react-three/fiber": "^9.5.0",
|
||||
"react": "^19.2.4",
|
||||
"react-dom": "^19.2.4",
|
||||
"three": "^0.183.2"
|
||||
},
|
||||
"packageManager": "yarn@1.22.22",
|
||||
"devDependencies": {
|
||||
"@eslint/js": "^9.39.4",
|
||||
"@types/react": "^19.2.14",
|
||||
"@types/react-dom": "^19.2.3",
|
||||
"@vitejs/plugin-react": "^6.0.1",
|
||||
"eslint": "^9.39.4",
|
||||
"eslint-plugin-react-hooks": "^7.0.1",
|
||||
"eslint-plugin-react-refresh": "^0.5.2",
|
||||
"globals": "^17.4.0",
|
||||
"vite": "^8.0.1"
|
||||
}
|
||||
}
|
||||
12
Week-3/Task-3/r3f/src/App.css
Normal file
12
Week-3/Task-3/r3f/src/App.css
Normal file
@ -0,0 +1,12 @@
|
||||
.app-container {
|
||||
width: 100vw;
|
||||
height: 100vh;
|
||||
position: relative;
|
||||
background-color: white;
|
||||
overflow: hidden;
|
||||
}
|
||||
|
||||
.canvas-wrapper {
|
||||
width: 100%;
|
||||
height: 100%;
|
||||
}
|
||||
64
Week-3/Task-3/r3f/src/App.jsx
Normal file
64
Week-3/Task-3/r3f/src/App.jsx
Normal file
@ -0,0 +1,64 @@
|
||||
import React, { useState } from 'react';
|
||||
import { Canvas } from '@react-three/fiber';
|
||||
import { OrbitControls, PerspectiveCamera } from '@react-three/drei';
|
||||
import './App.css';
|
||||
|
||||
const variants = {
|
||||
black: {
|
||||
geometry: <boxGeometry args={[2, 2, 2]} />,
|
||||
material: { color: "black", roughness: 1, metalness: 0 }
|
||||
},
|
||||
silver: {
|
||||
geometry: <sphereGeometry args={[1.5, 32, 32]} />,
|
||||
material: { color: "#C0C0C0", roughness: 0.3, metalness: 1 }
|
||||
},
|
||||
gold: {
|
||||
geometry: <torusGeometry args={[1.2, 0.4, 16, 100]} />,
|
||||
material: { color: "#FFD700", roughness: 0.4, metalness: 1 }
|
||||
}
|
||||
};
|
||||
|
||||
function Product({ variant }) {
|
||||
const { geometry, material } = variants[variant];
|
||||
|
||||
return (
|
||||
<mesh>
|
||||
{geometry}
|
||||
<meshStandardMaterial {...material} />
|
||||
</mesh>
|
||||
);
|
||||
}
|
||||
|
||||
export default function App() {
|
||||
const [activeVariant, setActiveVariant] = useState('black');
|
||||
|
||||
return (
|
||||
<div className="app-container">
|
||||
<div id="controls">
|
||||
{Object.keys(variants).map((variant) => (
|
||||
<button
|
||||
key={variant}
|
||||
className={activeVariant === variant ? 'active' : ''}
|
||||
onClick={() => setActiveVariant(variant)}
|
||||
>
|
||||
{variant.charAt(0).toUpperCase() + variant.slice(1)}
|
||||
</button>
|
||||
))}
|
||||
</div>
|
||||
|
||||
<div className="canvas-wrapper">
|
||||
<Canvas dpr={[1, 2]}>
|
||||
<color attach="background" args={["white"]} />
|
||||
<PerspectiveCamera makeDefault position={[0, 0, 5]} />
|
||||
|
||||
<ambientLight intensity={0.6 * Math.PI} />
|
||||
<pointLight position={[5, 5, 5]} intensity={1.0 * Math.PI} />
|
||||
|
||||
<Product variant={activeVariant} />
|
||||
|
||||
<OrbitControls makeDefault enableDamping dampingFactor={0.05} />
|
||||
</Canvas>
|
||||
</div>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
12
Week-3/Task-3/r3f/src/index.css
Normal file
12
Week-3/Task-3/r3f/src/index.css
Normal file
@ -0,0 +1,12 @@
|
||||
* {
|
||||
box-sizing: border-box;
|
||||
}
|
||||
|
||||
body, html, #root {
|
||||
margin: 0;
|
||||
padding: 0;
|
||||
width: 100%;
|
||||
height: 100%;
|
||||
overflow: hidden;
|
||||
background-color: #111;
|
||||
}
|
||||
10
Week-3/Task-3/r3f/src/main.jsx
Normal file
10
Week-3/Task-3/r3f/src/main.jsx
Normal file
@ -0,0 +1,10 @@
|
||||
import { StrictMode } from 'react'
|
||||
import { createRoot } from 'react-dom/client'
|
||||
import './index.css'
|
||||
import App from './App.jsx'
|
||||
|
||||
createRoot(document.getElementById('root')).render(
|
||||
<StrictMode>
|
||||
<App />
|
||||
</StrictMode>,
|
||||
)
|
||||
7
Week-3/Task-3/r3f/vite.config.js
Normal file
7
Week-3/Task-3/r3f/vite.config.js
Normal file
@ -0,0 +1,7 @@
|
||||
import { defineConfig } from 'vite'
|
||||
import react from '@vitejs/plugin-react'
|
||||
|
||||
// https://vite.dev/config/
|
||||
export default defineConfig({
|
||||
plugins: [react()],
|
||||
})
|
||||
1
Week-3/Task-3/report.md
Normal file
1
Week-3/Task-3/report.md
Normal file
@ -0,0 +1 @@
|
||||
# Task 3 Report
|
||||
0
Week-3/Task-3/thob/notes.md
Normal file
0
Week-3/Task-3/thob/notes.md
Normal file
18
Week-3/Task-3/vanilla/index.html
Normal file
18
Week-3/Task-3/vanilla/index.html
Normal file
@ -0,0 +1,18 @@
|
||||
<!DOCTYPE html>
|
||||
<html lang="en">
|
||||
<head>
|
||||
<meta charset="UTF-8" />
|
||||
<title>Task 3 — UI-Controlled Product Option</title>
|
||||
<link rel="stylesheet" href="./style.css">
|
||||
</head>
|
||||
<body>
|
||||
|
||||
<div id="controls">
|
||||
<button data-variant="black" class="active">Black</button>
|
||||
<button data-variant="silver">Silver</button>
|
||||
<button data-variant="gold">Gold</button>
|
||||
</div>
|
||||
|
||||
<script type="module" src="./main.js"></script>
|
||||
</body>
|
||||
</html>
|
||||
92
Week-3/Task-3/vanilla/main.js
Normal file
92
Week-3/Task-3/vanilla/main.js
Normal file
@ -0,0 +1,92 @@
|
||||
import * as THREE from "three";
|
||||
import { OrbitControls } from "three/examples/jsm/controls/OrbitControls.js";
|
||||
|
||||
const scene = new THREE.Scene();
|
||||
scene.background = new THREE.Color("white");
|
||||
|
||||
const camera = new THREE.PerspectiveCamera(
|
||||
75,
|
||||
window.innerWidth / window.innerHeight,
|
||||
0.1,
|
||||
1000
|
||||
);
|
||||
camera.position.z = 5;
|
||||
|
||||
const renderer = new THREE.WebGLRenderer();
|
||||
renderer.setSize(window.innerWidth, window.innerHeight);
|
||||
document.body.appendChild(renderer.domElement);
|
||||
|
||||
const geometries = {
|
||||
black: new THREE.BoxGeometry(2, 2, 2),
|
||||
silver: new THREE.SphereGeometry(1.5, 32, 32),
|
||||
gold: new THREE.TorusGeometry(1.2, 0.4, 16, 100),
|
||||
};
|
||||
|
||||
const materials = {
|
||||
black: new THREE.MeshStandardMaterial({
|
||||
color: "black",
|
||||
roughness: 1,
|
||||
metalness: 0,
|
||||
}),
|
||||
silver: new THREE.MeshStandardMaterial({
|
||||
color: "#C0C0C0",
|
||||
roughness: 0.3,
|
||||
metalness: 1,
|
||||
}),
|
||||
gold: new THREE.MeshStandardMaterial({
|
||||
color: "#FFD700",
|
||||
roughness: 0.4,
|
||||
metalness: 1,
|
||||
}),
|
||||
};
|
||||
|
||||
let currentVariant = "black";
|
||||
|
||||
let mesh = new THREE.Mesh(
|
||||
geometries[currentVariant],
|
||||
materials[currentVariant]
|
||||
);
|
||||
scene.add(mesh);
|
||||
|
||||
scene.add(new THREE.AmbientLight(0xffffff, 0.6));
|
||||
|
||||
const pointLight = new THREE.PointLight(0xffffff, 1);
|
||||
pointLight.position.set(5, 5, 5);
|
||||
scene.add(pointLight);
|
||||
|
||||
const controls = new OrbitControls(camera, renderer.domElement);
|
||||
controls.enableDamping = true;
|
||||
|
||||
function updateScene() {
|
||||
mesh.geometry = geometries[currentVariant];
|
||||
mesh.material = materials[currentVariant];
|
||||
}
|
||||
|
||||
const buttons = document.querySelectorAll("button");
|
||||
|
||||
buttons.forEach((btn) => {
|
||||
btn.addEventListener("click", () => {
|
||||
const variant = btn.getAttribute("data-variant");
|
||||
|
||||
currentVariant = variant;
|
||||
updateScene();
|
||||
|
||||
buttons.forEach((b) => b.classList.remove("active"));
|
||||
btn.classList.add("active");
|
||||
});
|
||||
});
|
||||
|
||||
function animate() {
|
||||
requestAnimationFrame(animate);
|
||||
|
||||
controls.update();
|
||||
|
||||
renderer.render(scene, camera);
|
||||
}
|
||||
animate();
|
||||
|
||||
window.addEventListener("resize", () => {
|
||||
camera.aspect = window.innerWidth / window.innerHeight;
|
||||
camera.updateProjectionMatrix();
|
||||
renderer.setSize(window.innerWidth, window.innerHeight);
|
||||
});
|
||||
18
Week-3/Task-3/vanilla/package.json
Normal file
18
Week-3/Task-3/vanilla/package.json
Normal file
@ -0,0 +1,18 @@
|
||||
{
|
||||
"name": "week3-task3-vanilla",
|
||||
"version": "0.0.0",
|
||||
"private": true,
|
||||
"type": "module",
|
||||
"scripts": {
|
||||
"dev": "vite",
|
||||
"build": "vite build",
|
||||
"preview": "vite preview"
|
||||
},
|
||||
"packageManager": "yarn@1.22.22",
|
||||
"devDependencies": {
|
||||
"vite": "^8.0.1"
|
||||
},
|
||||
"dependencies": {
|
||||
"three": "^0.183.2"
|
||||
}
|
||||
}
|
||||
24
Week-3/Task-4/r3f/.gitignore
vendored
Normal file
24
Week-3/Task-4/r3f/.gitignore
vendored
Normal file
@ -0,0 +1,24 @@
|
||||
# Logs
|
||||
logs
|
||||
*.log
|
||||
npm-debug.log*
|
||||
yarn-debug.log*
|
||||
yarn-error.log*
|
||||
pnpm-debug.log*
|
||||
lerna-debug.log*
|
||||
|
||||
node_modules
|
||||
dist
|
||||
dist-ssr
|
||||
*.local
|
||||
|
||||
# Editor directories and files
|
||||
.vscode/*
|
||||
!.vscode/extensions.json
|
||||
.idea
|
||||
.DS_Store
|
||||
*.suo
|
||||
*.ntvs*
|
||||
*.njsproj
|
||||
*.sln
|
||||
*.sw?
|
||||
10
Week-3/Task-4/r3f/README.md
Normal file
10
Week-3/Task-4/r3f/README.md
Normal file
@ -0,0 +1,10 @@
|
||||
# React + 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
|
||||
29
Week-3/Task-4/r3f/eslint.config.js
Normal file
29
Week-3/Task-4/r3f/eslint.config.js
Normal file
@ -0,0 +1,29 @@
|
||||
import js from '@eslint/js'
|
||||
import globals from 'globals'
|
||||
import reactHooks from 'eslint-plugin-react-hooks'
|
||||
import reactRefresh from 'eslint-plugin-react-refresh'
|
||||
import { defineConfig, globalIgnores } from 'eslint/config'
|
||||
|
||||
export default defineConfig([
|
||||
globalIgnores(['dist']),
|
||||
{
|
||||
files: ['**/*.{js,jsx}'],
|
||||
extends: [
|
||||
js.configs.recommended,
|
||||
reactHooks.configs.flat.recommended,
|
||||
reactRefresh.configs.vite,
|
||||
],
|
||||
languageOptions: {
|
||||
ecmaVersion: 2020,
|
||||
globals: globals.browser,
|
||||
parserOptions: {
|
||||
ecmaVersion: 'latest',
|
||||
ecmaFeatures: { jsx: true },
|
||||
sourceType: 'module',
|
||||
},
|
||||
},
|
||||
rules: {
|
||||
'no-unused-vars': ['error', { varsIgnorePattern: '^[A-Z_]' }],
|
||||
},
|
||||
},
|
||||
])
|
||||
13
Week-3/Task-4/r3f/index.html
Normal file
13
Week-3/Task-4/r3f/index.html
Normal file
@ -0,0 +1,13 @@
|
||||
<!doctype html>
|
||||
<html lang="en">
|
||||
<head>
|
||||
<meta charset="UTF-8" />
|
||||
<link rel="icon" type="image/svg+xml" href="/favicon.svg" />
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
|
||||
<title>r3f</title>
|
||||
</head>
|
||||
<body>
|
||||
<div id="root"></div>
|
||||
<script type="module" src="/src/main.jsx"></script>
|
||||
</body>
|
||||
</html>
|
||||
31
Week-3/Task-4/r3f/package.json
Normal file
31
Week-3/Task-4/r3f/package.json
Normal file
@ -0,0 +1,31 @@
|
||||
{
|
||||
"name": "week3-task4-r3f",
|
||||
"private": true,
|
||||
"version": "0.0.0",
|
||||
"type": "module",
|
||||
"scripts": {
|
||||
"dev": "vite",
|
||||
"build": "vite build",
|
||||
"lint": "eslint .",
|
||||
"preview": "vite preview"
|
||||
},
|
||||
"dependencies": {
|
||||
"@react-three/drei": "9.105.6",
|
||||
"@react-three/fiber": "^9.5.0",
|
||||
"react": "^19.2.4",
|
||||
"react-dom": "^19.2.4",
|
||||
"three": "^0.183.2"
|
||||
},
|
||||
"packageManager": "yarn@1.22.22",
|
||||
"devDependencies": {
|
||||
"@eslint/js": "^9.39.4",
|
||||
"@types/react": "^19.2.14",
|
||||
"@types/react-dom": "^19.2.3",
|
||||
"@vitejs/plugin-react": "^6.0.1",
|
||||
"eslint": "^9.39.4",
|
||||
"eslint-plugin-react-hooks": "^7.0.1",
|
||||
"eslint-plugin-react-refresh": "^0.5.2",
|
||||
"globals": "^17.4.0",
|
||||
"vite": "^8.0.1"
|
||||
}
|
||||
}
|
||||
12
Week-3/Task-4/r3f/src/App.css
Normal file
12
Week-3/Task-4/r3f/src/App.css
Normal file
@ -0,0 +1,12 @@
|
||||
.app-container {
|
||||
width: 100vw;
|
||||
height: 100vh;
|
||||
position: relative;
|
||||
background-color: white;
|
||||
overflow: hidden;
|
||||
}
|
||||
|
||||
.canvas-wrapper {
|
||||
width: 100%;
|
||||
height: 100%;
|
||||
}
|
||||
48
Week-3/Task-4/r3f/src/App.jsx
Normal file
48
Week-3/Task-4/r3f/src/App.jsx
Normal file
@ -0,0 +1,48 @@
|
||||
import React, { useState } from 'react';
|
||||
import { Canvas } from '@react-three/fiber';
|
||||
import { OrbitControls, PerspectiveCamera } from '@react-three/drei';
|
||||
import './App.css';
|
||||
|
||||
function InteractiveObject() {
|
||||
const [selected, setSelected] = useState(false);
|
||||
|
||||
return (
|
||||
<mesh
|
||||
onClick={() => setSelected(!selected)}
|
||||
scale={selected ? 1.2 : 1}
|
||||
rotation={[0, 0, 0]}
|
||||
>
|
||||
<torusKnotGeometry args={[1, 0.4, 100, 16]} />
|
||||
<meshStandardMaterial
|
||||
color={selected ? "#3498db" : "#ccc"}
|
||||
emissive={selected ? "#1e3799" : "#000"}
|
||||
emissiveIntensity={selected ? 0.5 : 0}
|
||||
roughness={0.5}
|
||||
/>
|
||||
</mesh>
|
||||
);
|
||||
}
|
||||
|
||||
export default function App() {
|
||||
return (
|
||||
<div className="app-container">
|
||||
<div id="overlay">
|
||||
<h1>Interaction Pattern — Click on the object</h1>
|
||||
</div>
|
||||
|
||||
<div className="canvas-wrapper">
|
||||
<Canvas dpr={[1, 2]}>
|
||||
<color attach="background" args={["white"]} />
|
||||
<PerspectiveCamera makeDefault position={[0, 0, 5]} />
|
||||
|
||||
<ambientLight intensity={0.6 * Math.PI} />
|
||||
<pointLight position={[5, 5, 5]} intensity={1.0 * Math.PI} />
|
||||
|
||||
<InteractiveObject />
|
||||
|
||||
<OrbitControls makeDefault enableDamping dampingFactor={0.05} />
|
||||
</Canvas>
|
||||
</div>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
12
Week-3/Task-4/r3f/src/index.css
Normal file
12
Week-3/Task-4/r3f/src/index.css
Normal file
@ -0,0 +1,12 @@
|
||||
* {
|
||||
box-sizing: border-box;
|
||||
}
|
||||
|
||||
body, html, #root {
|
||||
margin: 0;
|
||||
padding: 0;
|
||||
width: 100%;
|
||||
height: 100%;
|
||||
overflow: hidden;
|
||||
background-color: #111;
|
||||
}
|
||||
10
Week-3/Task-4/r3f/src/main.jsx
Normal file
10
Week-3/Task-4/r3f/src/main.jsx
Normal file
@ -0,0 +1,10 @@
|
||||
import { StrictMode } from 'react'
|
||||
import { createRoot } from 'react-dom/client'
|
||||
import './index.css'
|
||||
import App from './App.jsx'
|
||||
|
||||
createRoot(document.getElementById('root')).render(
|
||||
<StrictMode>
|
||||
<App />
|
||||
</StrictMode>,
|
||||
)
|
||||
7
Week-3/Task-4/r3f/vite.config.js
Normal file
7
Week-3/Task-4/r3f/vite.config.js
Normal file
@ -0,0 +1,7 @@
|
||||
import { defineConfig } from 'vite'
|
||||
import react from '@vitejs/plugin-react'
|
||||
|
||||
// https://vite.dev/config/
|
||||
export default defineConfig({
|
||||
plugins: [react()],
|
||||
})
|
||||
1
Week-3/Task-4/report.md
Normal file
1
Week-3/Task-4/report.md
Normal file
@ -0,0 +1 @@
|
||||
# Task 4 Report
|
||||
0
Week-3/Task-4/thob/notes.md
Normal file
0
Week-3/Task-4/thob/notes.md
Normal file
14
Week-3/Task-4/vanilla/index.html
Normal file
14
Week-3/Task-4/vanilla/index.html
Normal file
@ -0,0 +1,14 @@
|
||||
<!DOCTYPE html>
|
||||
<html lang="en">
|
||||
<head>
|
||||
<meta charset="UTF-8" />
|
||||
<title>Task 4 — Click To Highlight / Select</title>
|
||||
<style>
|
||||
body { margin: 0; padding: 0; overflow: hidden; background: white; font-family: sans-serif; }
|
||||
</style>
|
||||
</head>
|
||||
<body>
|
||||
<h1>Interaction Pattern- click on the object</h1>
|
||||
<script type="module" src="./main.js"></script>
|
||||
</body>
|
||||
</html>
|
||||
70
Week-3/Task-4/vanilla/main.js
Normal file
70
Week-3/Task-4/vanilla/main.js
Normal file
@ -0,0 +1,70 @@
|
||||
import * as THREE from "three";
|
||||
import { OrbitControls } from "three/examples/jsm/controls/OrbitControls.js";
|
||||
|
||||
const scene = new THREE.Scene();
|
||||
scene.background = new THREE.Color("white");
|
||||
|
||||
const camera = new THREE.PerspectiveCamera(75, window.innerWidth / window.innerHeight, 0.1, 1000);
|
||||
camera.position.z = 5;
|
||||
|
||||
const renderer = new THREE.WebGLRenderer({ antialias: true });
|
||||
renderer.setSize(window.innerWidth, window.innerHeight);
|
||||
document.body.appendChild(renderer.domElement);
|
||||
|
||||
const geometry = new THREE.TorusKnotGeometry(1, 0.4, 100, 16);
|
||||
const material = new THREE.MeshStandardMaterial({ color: "#ccc", roughness: 0.5 });
|
||||
const mesh = new THREE.Mesh(geometry, material);
|
||||
scene.add(mesh);
|
||||
|
||||
scene.add(new THREE.AmbientLight(0xffffff, 0.6));
|
||||
const pointLight = new THREE.PointLight(0xffffff, 1);
|
||||
pointLight.position.set(5, 5, 5);
|
||||
scene.add(pointLight);
|
||||
|
||||
const controls = new OrbitControls(camera, renderer.domElement);
|
||||
controls.enableDamping = true;
|
||||
|
||||
const raycaster = new THREE.Raycaster();
|
||||
const mouse = new THREE.Vector2();
|
||||
let isSelected = false;
|
||||
|
||||
window.addEventListener("click", (event) => {
|
||||
mouse.x = (event.clientX / window.innerWidth) * 2 - 1;
|
||||
mouse.y = -(event.clientY / window.innerHeight) * 2 + 1;
|
||||
|
||||
raycaster.setFromCamera(mouse, camera);
|
||||
const intersects = raycaster.intersectObject(mesh);
|
||||
|
||||
if (intersects.length > 0) {
|
||||
isSelected = !isSelected;
|
||||
updateAppearance();
|
||||
}
|
||||
});
|
||||
|
||||
function updateAppearance() {
|
||||
if (isSelected) {
|
||||
mesh.scale.set(1.2, 1.2, 1.2);
|
||||
mesh.material.color.set("#3498db");
|
||||
mesh.material.emissive.set("#1e3799");
|
||||
mesh.material.emissiveIntensity = 0.5;
|
||||
} else {
|
||||
mesh.scale.set(1, 1, 1);
|
||||
mesh.material.color.set("#ccc");
|
||||
mesh.material.emissive.set("#000");
|
||||
mesh.material.emissiveIntensity = 0;
|
||||
}
|
||||
}
|
||||
|
||||
function animate() {
|
||||
requestAnimationFrame(animate);
|
||||
controls.update();
|
||||
mesh.rotation.y += 0.01;
|
||||
renderer.render(scene, camera);
|
||||
}
|
||||
animate();
|
||||
|
||||
window.addEventListener("resize", () => {
|
||||
camera.aspect = window.innerWidth / window.innerHeight;
|
||||
camera.updateProjectionMatrix();
|
||||
renderer.setSize(window.innerWidth, window.innerHeight);
|
||||
});
|
||||
18
Week-3/Task-4/vanilla/package.json
Normal file
18
Week-3/Task-4/vanilla/package.json
Normal file
@ -0,0 +1,18 @@
|
||||
{
|
||||
"name": "week3-task4-vanilla",
|
||||
"version": "0.0.0",
|
||||
"private": true,
|
||||
"type": "module",
|
||||
"scripts": {
|
||||
"dev": "vite",
|
||||
"build": "vite build",
|
||||
"preview": "vite preview"
|
||||
},
|
||||
"packageManager": "yarn@1.22.22",
|
||||
"devDependencies": {
|
||||
"vite": "^8.0.1"
|
||||
},
|
||||
"dependencies": {
|
||||
"three": "^0.183.2"
|
||||
}
|
||||
}
|
||||
1
Week-3/Week-3-PersonalSummary.md
Normal file
1
Week-3/Week-3-PersonalSummary.md
Normal file
@ -0,0 +1 @@
|
||||
# Week 3 Personal Summary
|
||||
@ -19,7 +19,8 @@
|
||||
"packageManager": "yarn@1.22.22",
|
||||
"workspaces": [
|
||||
"Week-1/**",
|
||||
"Week-2/**"
|
||||
"Week-2/**",
|
||||
"Week-3/**"
|
||||
],
|
||||
"dependencies": {
|
||||
"@react-three/fiber": "^9.5.0",
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user