Spaces:
Sleeping
Sleeping
add controls
Browse files- src/app/providers.tsx +7 -1
- src/components/canvas.tsx +5 -22
- src/components/ui-controls.tsx +34 -6
- src/components/ui.tsx +2 -0
- src/contexts/control-context.tsx +56 -0
src/app/providers.tsx
CHANGED
|
@@ -2,6 +2,12 @@
|
|
| 2 |
|
| 3 |
import { HeroUIProvider } from '@heroui/react';
|
| 4 |
|
|
|
|
|
|
|
| 5 |
export const Providers = ({ children }: { children: React.ReactNode }) => {
|
| 6 |
-
return
|
|
|
|
|
|
|
|
|
|
|
|
|
| 7 |
};
|
|
|
|
| 2 |
|
| 3 |
import { HeroUIProvider } from '@heroui/react';
|
| 4 |
|
| 5 |
+
import { ControlProvider } from '@/contexts/control-context';
|
| 6 |
+
|
| 7 |
export const Providers = ({ children }: { children: React.ReactNode }) => {
|
| 8 |
+
return (
|
| 9 |
+
<HeroUIProvider className="h-full w-full">
|
| 10 |
+
<ControlProvider>{children}</ControlProvider>
|
| 11 |
+
</HeroUIProvider>
|
| 12 |
+
);
|
| 13 |
};
|
src/components/canvas.tsx
CHANGED
|
@@ -1,38 +1,21 @@
|
|
| 1 |
'use client';
|
| 2 |
|
| 3 |
-
import { PresetsType } from '@react-three/drei/helpers/environment-assets';
|
| 4 |
import { Canvas as ThreeCanvas } from '@react-three/fiber';
|
| 5 |
-
import {
|
| 6 |
-
|
|
|
|
| 7 |
|
| 8 |
import { Env } from '../components/env';
|
| 9 |
-
import { Actions } from './consts';
|
| 10 |
import { RubiksCube, RubiksCubeRef } from './rubiks-cube';
|
| 11 |
|
| 12 |
export const Canvas = () => {
|
| 13 |
const rubiksCubeRef = useRef<RubiksCubeRef>(null);
|
|
|
|
| 14 |
|
| 15 |
-
|
| 16 |
-
const [_, startTransition] = useTransition();
|
| 17 |
-
const [background, setBackground] = useState<PresetsType>('sunset');
|
| 18 |
-
|
| 19 |
-
const { cubeRoughness, cubeSpeed } = useControls({
|
| 20 |
-
cubeRoughness: { value: 0.5, min: 0.2, max: 0.8 },
|
| 21 |
-
cubeSpeed: { value: 2, min: 1, max: 10 },
|
| 22 |
-
background: {
|
| 23 |
-
value: background,
|
| 24 |
-
options: ['sunset', 'dawn', 'forest'],
|
| 25 |
-
onChange: (value) => startTransition(() => setBackground(value)),
|
| 26 |
-
},
|
| 27 |
-
Scramble: button(() => {
|
| 28 |
-
const scrambleSteps = Array.from({ length: 20 }, () => Actions[Math.floor(Math.random() * Actions.length)]);
|
| 29 |
-
rubiksCubeRef.current?.rotate(scrambleSteps);
|
| 30 |
-
}),
|
| 31 |
-
});
|
| 32 |
|
| 33 |
return (
|
| 34 |
<>
|
| 35 |
-
<Leva />
|
| 36 |
<ThreeCanvas shadows camera={{ position: [0, 0, 4.5], fov: 50 }}>
|
| 37 |
<RubiksCube ref={rubiksCubeRef} cubeRoughness={cubeRoughness} cubeSpeed={cubeSpeed} />
|
| 38 |
<Env background={background} />
|
|
|
|
| 1 |
'use client';
|
| 2 |
|
|
|
|
| 3 |
import { Canvas as ThreeCanvas } from '@react-three/fiber';
|
| 4 |
+
import { useEffect, useRef } from 'react';
|
| 5 |
+
|
| 6 |
+
import { useControlContext } from '@/contexts/control-context';
|
| 7 |
|
| 8 |
import { Env } from '../components/env';
|
|
|
|
| 9 |
import { RubiksCube, RubiksCubeRef } from './rubiks-cube';
|
| 10 |
|
| 11 |
export const Canvas = () => {
|
| 12 |
const rubiksCubeRef = useRef<RubiksCubeRef>(null);
|
| 13 |
+
const { cubeRoughness, cubeSpeed, background, setRubiksCubeRef } = useControlContext();
|
| 14 |
|
| 15 |
+
useEffect(() => setRubiksCubeRef(rubiksCubeRef), [setRubiksCubeRef]);
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 16 |
|
| 17 |
return (
|
| 18 |
<>
|
|
|
|
| 19 |
<ThreeCanvas shadows camera={{ position: [0, 0, 4.5], fov: 50 }}>
|
| 20 |
<RubiksCube ref={rubiksCubeRef} cubeRoughness={cubeRoughness} cubeSpeed={cubeSpeed} />
|
| 21 |
<Env background={background} />
|
src/components/ui-controls.tsx
CHANGED
|
@@ -2,7 +2,19 @@
|
|
| 2 |
|
| 3 |
import { Button, ButtonGroup, Card, CardBody, Slider } from '@heroui/react';
|
| 4 |
|
|
|
|
|
|
|
|
|
|
|
|
|
| 5 |
export const UIControls = () => {
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 6 |
return (
|
| 7 |
<div className="z-10 pointer-events-none">
|
| 8 |
<Card className="max-w-sm bg-white/30 border border-white/80 backdrop-blur-xl pointer-events-auto">
|
|
@@ -12,17 +24,33 @@ export const UIControls = () => {
|
|
| 12 |
<div className="flex items-center justify-between">
|
| 13 |
<div className="text-sm">Background</div>
|
| 14 |
<ButtonGroup size="sm">
|
| 15 |
-
<Button>Sunset</Button>
|
| 16 |
-
<Button>Dawn</Button>
|
| 17 |
-
<Button>Forest</Button>
|
| 18 |
</ButtonGroup>
|
| 19 |
</div>
|
| 20 |
-
<Slider
|
| 21 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 22 |
</div>
|
| 23 |
<div className="flex flex-col gap-2">
|
| 24 |
<div className="flex gap-2">
|
| 25 |
-
<Button>Scramble</Button>
|
| 26 |
<Button>Reset</Button>
|
| 27 |
<Button className="ms-auto" color="success">
|
| 28 |
Solve
|
|
|
|
| 2 |
|
| 3 |
import { Button, ButtonGroup, Card, CardBody, Slider } from '@heroui/react';
|
| 4 |
|
| 5 |
+
import { useControlContext } from '@/contexts/control-context';
|
| 6 |
+
|
| 7 |
+
import { Actions } from './consts';
|
| 8 |
+
|
| 9 |
export const UIControls = () => {
|
| 10 |
+
const { rubiksCubeRef, setBackground, cubeRoughness, setCubeRoughness, cubeSpeed, setCubeSpeed } =
|
| 11 |
+
useControlContext();
|
| 12 |
+
|
| 13 |
+
const scramble = () => {
|
| 14 |
+
const scrambleSteps = Array.from({ length: 20 }, () => Actions[Math.floor(Math.random() * Actions.length)]);
|
| 15 |
+
rubiksCubeRef?.current?.rotate(scrambleSteps);
|
| 16 |
+
};
|
| 17 |
+
|
| 18 |
return (
|
| 19 |
<div className="z-10 pointer-events-none">
|
| 20 |
<Card className="max-w-sm bg-white/30 border border-white/80 backdrop-blur-xl pointer-events-auto">
|
|
|
|
| 24 |
<div className="flex items-center justify-between">
|
| 25 |
<div className="text-sm">Background</div>
|
| 26 |
<ButtonGroup size="sm">
|
| 27 |
+
<Button onPress={() => setBackground('sunset')}>Sunset</Button>
|
| 28 |
+
<Button onPress={() => setBackground('dawn')}>Dawn</Button>
|
| 29 |
+
<Button onPress={() => setBackground('forest')}>Forest</Button>
|
| 30 |
</ButtonGroup>
|
| 31 |
</div>
|
| 32 |
+
<Slider
|
| 33 |
+
size="sm"
|
| 34 |
+
label="Cube Roughness"
|
| 35 |
+
value={cubeRoughness}
|
| 36 |
+
onChange={(value) => setCubeRoughness(value as number)}
|
| 37 |
+
minValue={0.2}
|
| 38 |
+
maxValue={1}
|
| 39 |
+
step={0.01}
|
| 40 |
+
/>
|
| 41 |
+
<Slider
|
| 42 |
+
size="sm"
|
| 43 |
+
label="Cube Speed"
|
| 44 |
+
value={cubeSpeed}
|
| 45 |
+
onChange={(value) => setCubeSpeed(value as number)}
|
| 46 |
+
minValue={1}
|
| 47 |
+
maxValue={10}
|
| 48 |
+
step={1}
|
| 49 |
+
/>
|
| 50 |
</div>
|
| 51 |
<div className="flex flex-col gap-2">
|
| 52 |
<div className="flex gap-2">
|
| 53 |
+
<Button onPress={scramble}>Scramble</Button>
|
| 54 |
<Button>Reset</Button>
|
| 55 |
<Button className="ms-auto" color="success">
|
| 56 |
Solve
|
src/components/ui.tsx
CHANGED
|
@@ -1,3 +1,5 @@
|
|
|
|
|
|
|
|
| 1 |
import { UIControls } from './ui-controls';
|
| 2 |
import { UITitle } from './ui-title';
|
| 3 |
|
|
|
|
| 1 |
+
'use client';
|
| 2 |
+
|
| 3 |
import { UIControls } from './ui-controls';
|
| 4 |
import { UITitle } from './ui-title';
|
| 5 |
|
src/contexts/control-context.tsx
ADDED
|
@@ -0,0 +1,56 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
'use client';
|
| 2 |
+
|
| 3 |
+
import { PresetsType } from '@react-three/drei/helpers/environment-assets';
|
| 4 |
+
import { RefObject, createContext, useContext, useState } from 'react';
|
| 5 |
+
|
| 6 |
+
import { RubiksCubeRef } from '@/components/rubiks-cube';
|
| 7 |
+
|
| 8 |
+
type ControlContextType = {
|
| 9 |
+
cubeRoughness: number;
|
| 10 |
+
setCubeRoughness: (cubeRoughness: number) => void;
|
| 11 |
+
cubeSpeed: number;
|
| 12 |
+
setCubeSpeed: (cubeSpeed: number) => void;
|
| 13 |
+
background: PresetsType;
|
| 14 |
+
setBackground: (background: PresetsType) => void;
|
| 15 |
+
rubiksCubeRef?: RefObject<RubiksCubeRef | null>;
|
| 16 |
+
setRubiksCubeRef: (rubiksCubeRef: RefObject<RubiksCubeRef | null>) => void;
|
| 17 |
+
};
|
| 18 |
+
|
| 19 |
+
export const ControlContext = createContext<ControlContextType>({
|
| 20 |
+
cubeRoughness: 0.5,
|
| 21 |
+
setCubeRoughness: () => {},
|
| 22 |
+
cubeSpeed: 2,
|
| 23 |
+
setCubeSpeed: () => {},
|
| 24 |
+
background: 'sunset',
|
| 25 |
+
setBackground: () => {},
|
| 26 |
+
rubiksCubeRef: undefined,
|
| 27 |
+
setRubiksCubeRef: () => {},
|
| 28 |
+
});
|
| 29 |
+
|
| 30 |
+
export const useControlContext = () => {
|
| 31 |
+
return useContext(ControlContext);
|
| 32 |
+
};
|
| 33 |
+
|
| 34 |
+
export const ControlProvider = ({ children }: { children: React.ReactNode }) => {
|
| 35 |
+
const [cubeRoughness, setCubeRoughness] = useState(0.5);
|
| 36 |
+
const [cubeSpeed, setCubeSpeed] = useState(2);
|
| 37 |
+
const [background, setBackground] = useState<PresetsType>('sunset');
|
| 38 |
+
const [rubiksCubeRef, setRubiksCubeRef] = useState<RefObject<RubiksCubeRef | null> | undefined>(undefined);
|
| 39 |
+
|
| 40 |
+
return (
|
| 41 |
+
<ControlContext.Provider
|
| 42 |
+
value={{
|
| 43 |
+
cubeRoughness,
|
| 44 |
+
setCubeRoughness,
|
| 45 |
+
cubeSpeed,
|
| 46 |
+
setCubeSpeed,
|
| 47 |
+
background,
|
| 48 |
+
setBackground,
|
| 49 |
+
rubiksCubeRef,
|
| 50 |
+
setRubiksCubeRef,
|
| 51 |
+
}}
|
| 52 |
+
>
|
| 53 |
+
{children}
|
| 54 |
+
</ControlContext.Provider>
|
| 55 |
+
);
|
| 56 |
+
};
|