Standardize GlitchComponent: glitch.json config, build/deploy setup, ignore node_modules
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
This commit is contained in:
@@ -1,2 +1,3 @@
|
|||||||
node_modules/
|
node_modules/
|
||||||
.DS_Store
|
.DS_Store
|
||||||
|
node_modules
|
||||||
|
|||||||
Vendored
-1
File diff suppressed because one or more lines are too long
+154
-154
File diff suppressed because one or more lines are too long
Vendored
+1
File diff suppressed because one or more lines are too long
+95
@@ -0,0 +1,95 @@
|
|||||||
|
{
|
||||||
|
"componentId": "diophantine-sphere",
|
||||||
|
"displayName": "Diophantine Sphere",
|
||||||
|
"description": "Diophantine Sphere glitch component for Glitch University",
|
||||||
|
"version": "1.0.0",
|
||||||
|
"folderName": "glitch_diophantine_sphere",
|
||||||
|
"packageName": "@glitch-components/diophantine-sphere",
|
||||||
|
"entry": "dist/diophantine-sphere.js",
|
||||||
|
"source": "src/index.tsx",
|
||||||
|
"tags": [
|
||||||
|
"glitch-component",
|
||||||
|
"diophantine",
|
||||||
|
"sphere",
|
||||||
|
"integer",
|
||||||
|
"visualization"
|
||||||
|
],
|
||||||
|
"paramSchema": {
|
||||||
|
"allowNegative": {
|
||||||
|
"type": "boolean",
|
||||||
|
"label": "Allow Negative Values",
|
||||||
|
"default": true
|
||||||
|
},
|
||||||
|
"normalizeDistance": {
|
||||||
|
"type": "boolean",
|
||||||
|
"label": "Normalize Distance",
|
||||||
|
"default": true
|
||||||
|
},
|
||||||
|
"showComplexityOverlay": {
|
||||||
|
"type": "boolean",
|
||||||
|
"label": "Show Complexity Range",
|
||||||
|
"default": true
|
||||||
|
},
|
||||||
|
"complexityMode": {
|
||||||
|
"type": "select",
|
||||||
|
"label": "Complexity Mode",
|
||||||
|
"default": "surface",
|
||||||
|
"options": [
|
||||||
|
{
|
||||||
|
"value": "surface",
|
||||||
|
"label": "Surface"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"value": "single",
|
||||||
|
"label": "Single C"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"value": "range",
|
||||||
|
"label": "Range 1..C"
|
||||||
|
}
|
||||||
|
]
|
||||||
|
},
|
||||||
|
"radius": {
|
||||||
|
"type": "range",
|
||||||
|
"label": "D",
|
||||||
|
"default": 4,
|
||||||
|
"min": 1,
|
||||||
|
"max": 8,
|
||||||
|
"step": 0.25
|
||||||
|
},
|
||||||
|
"complexity": {
|
||||||
|
"type": "range",
|
||||||
|
"label": "C",
|
||||||
|
"default": 17,
|
||||||
|
"min": 1,
|
||||||
|
"max": 96,
|
||||||
|
"step": 1
|
||||||
|
},
|
||||||
|
"dotRadius": {
|
||||||
|
"type": "range",
|
||||||
|
"label": "R",
|
||||||
|
"default": 0.5,
|
||||||
|
"min": 0.05,
|
||||||
|
"max": 1.2,
|
||||||
|
"step": 0.01
|
||||||
|
},
|
||||||
|
"axisSize": {
|
||||||
|
"type": "range",
|
||||||
|
"label": "A",
|
||||||
|
"default": 1,
|
||||||
|
"min": 0.25,
|
||||||
|
"max": 2.5,
|
||||||
|
"step": 0.05
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"defaultParams": {
|
||||||
|
"allowNegative": true,
|
||||||
|
"normalizeDistance": true,
|
||||||
|
"showComplexityOverlay": true,
|
||||||
|
"complexityMode": "surface",
|
||||||
|
"radius": 4,
|
||||||
|
"complexity": 17,
|
||||||
|
"dotRadius": 0.5,
|
||||||
|
"axisSize": 1
|
||||||
|
}
|
||||||
|
}
|
||||||
+2
-1
@@ -15,7 +15,8 @@
|
|||||||
"files": [
|
"files": [
|
||||||
"dist",
|
"dist",
|
||||||
"src/types.ts",
|
"src/types.ts",
|
||||||
"glitch.manifest.json"
|
"glitch.manifest.json",
|
||||||
|
"glitch.json"
|
||||||
],
|
],
|
||||||
"scripts": {
|
"scripts": {
|
||||||
"dev": "vite",
|
"dev": "vite",
|
||||||
|
|||||||
+274
-43
@@ -7,9 +7,11 @@ import type { GlitchComponentProps } from './types'
|
|||||||
interface ComponentParams {
|
interface ComponentParams {
|
||||||
allowNegative?: boolean
|
allowNegative?: boolean
|
||||||
normalizeDistance?: boolean
|
normalizeDistance?: boolean
|
||||||
|
showComplexityOverlay?: boolean
|
||||||
radius?: number
|
radius?: number
|
||||||
complexity?: number
|
complexity?: number
|
||||||
dotRadius?: number
|
dotRadius?: number
|
||||||
|
axisSize?: number
|
||||||
complexityMode?: string
|
complexityMode?: string
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -47,6 +49,8 @@ const DEFAULT_CAMERA_DISTANCE = 13
|
|||||||
const IDLE_ROTATION_SPEED = 0.0005
|
const IDLE_ROTATION_SPEED = 0.0005
|
||||||
const MAX_COMPLEXITY = 96
|
const MAX_COMPLEXITY = 96
|
||||||
const MAX_DOT_RADIUS = 1.2
|
const MAX_DOT_RADIUS = 1.2
|
||||||
|
const MAX_AXIS_SIZE = 2.5
|
||||||
|
const INITIAL_ROTATION = { x: -0.25, y: -0.85 }
|
||||||
|
|
||||||
const gcd2 = (a: number, b: number): number => {
|
const gcd2 = (a: number, b: number): number => {
|
||||||
let left = Math.abs(a)
|
let left = Math.abs(a)
|
||||||
@@ -82,11 +86,40 @@ const colorRamp = (weight: number) => {
|
|||||||
return warm.lerp(hot, (weight - 0.5) / 0.5)
|
return warm.lerp(hot, (weight - 0.5) / 0.5)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
const createSphereShellGeometry = (allowNegative: boolean) =>
|
||||||
|
allowNegative
|
||||||
|
? new THREE.SphereGeometry(1, 96, 48)
|
||||||
|
: new THREE.SphereGeometry(1, 48, 24, Math.PI / 2, Math.PI / 2, 0, Math.PI / 2)
|
||||||
|
|
||||||
|
const createL1ShellGeometry = (allowNegative: boolean) => {
|
||||||
|
if (allowNegative) return new THREE.OctahedronGeometry(1, 0)
|
||||||
|
|
||||||
|
const geometry = new THREE.BufferGeometry()
|
||||||
|
geometry.setAttribute(
|
||||||
|
'position',
|
||||||
|
new THREE.Float32BufferAttribute([
|
||||||
|
1, 0, 0,
|
||||||
|
0, 1, 0,
|
||||||
|
0, 0, 1
|
||||||
|
], 3)
|
||||||
|
)
|
||||||
|
geometry.setIndex([0, 1, 2])
|
||||||
|
geometry.computeVertexNormals()
|
||||||
|
|
||||||
|
return geometry
|
||||||
|
}
|
||||||
|
|
||||||
const getComplexityMode = (value: unknown): ComplexityMode => {
|
const getComplexityMode = (value: unknown): ComplexityMode => {
|
||||||
if (value === 'surface' || value === 'single' || value === 'range') return value
|
if (value === 'surface' || value === 'single' || value === 'range') return value
|
||||||
return 'surface'
|
return 'surface'
|
||||||
}
|
}
|
||||||
|
|
||||||
|
const getComplexityOverlayText = (mode: ComplexityMode, complexity: number) => {
|
||||||
|
if (mode === 'surface') return 'Complexity: continuous surface'
|
||||||
|
if (mode === 'range') return `Complexity range: 1..${complexity}`
|
||||||
|
return `Complexity: ${complexity}`
|
||||||
|
}
|
||||||
|
|
||||||
const getVectorRays = (
|
const getVectorRays = (
|
||||||
complexity: number,
|
complexity: number,
|
||||||
radius: number,
|
radius: number,
|
||||||
@@ -172,16 +205,20 @@ export default function Component({
|
|||||||
const params = config.params as ComponentParams
|
const params = config.params as ComponentParams
|
||||||
const initialAllowNegative = typeof params.allowNegative === 'boolean' ? params.allowNegative : true
|
const initialAllowNegative = typeof params.allowNegative === 'boolean' ? params.allowNegative : true
|
||||||
const initialNormalizeDistance = typeof params.normalizeDistance === 'boolean' ? params.normalizeDistance : true
|
const initialNormalizeDistance = typeof params.normalizeDistance === 'boolean' ? params.normalizeDistance : true
|
||||||
|
const initialShowComplexityOverlay = typeof params.showComplexityOverlay === 'boolean' ? params.showComplexityOverlay : true
|
||||||
const initialRadius = clamp(numberParam(params.radius, 4), 1, 8)
|
const initialRadius = clamp(numberParam(params.radius, 4), 1, 8)
|
||||||
const initialComplexity = Math.round(clamp(numberParam(params.complexity, 8), 1, MAX_COMPLEXITY))
|
const initialComplexity = Math.round(clamp(numberParam(params.complexity, 17), 1, MAX_COMPLEXITY))
|
||||||
const initialDotRadius = clamp(numberParam(params.dotRadius, 0.17), 0.05, MAX_DOT_RADIUS)
|
const initialDotRadius = clamp(numberParam(params.dotRadius, 0.5), 0.05, MAX_DOT_RADIUS)
|
||||||
|
const initialAxisSize = clamp(numberParam(params.axisSize, 1), 0.25, MAX_AXIS_SIZE)
|
||||||
const initialComplexityMode = getComplexityMode(params.complexityMode)
|
const initialComplexityMode = getComplexityMode(params.complexityMode)
|
||||||
|
|
||||||
const [allowNegative, setAllowNegative] = useState(initialAllowNegative)
|
const [allowNegative, setAllowNegative] = useState(initialAllowNegative)
|
||||||
const [normalizeDistance, setNormalizeDistance] = useState(initialNormalizeDistance)
|
const [normalizeDistance, setNormalizeDistance] = useState(initialNormalizeDistance)
|
||||||
|
const [showComplexityOverlay, setShowComplexityOverlay] = useState(initialShowComplexityOverlay)
|
||||||
const [radius, setRadius] = useState(initialRadius)
|
const [radius, setRadius] = useState(initialRadius)
|
||||||
const [complexity, setComplexity] = useState(initialComplexity)
|
const [complexity, setComplexity] = useState(initialComplexity)
|
||||||
const [dotRadius, setDotRadius] = useState(initialDotRadius)
|
const [dotRadius, setDotRadius] = useState(initialDotRadius)
|
||||||
|
const [axisSize, setAxisSize] = useState(initialAxisSize)
|
||||||
const [complexityMode, setComplexityMode] = useState<ComplexityMode>(initialComplexityMode)
|
const [complexityMode, setComplexityMode] = useState<ComplexityMode>(initialComplexityMode)
|
||||||
const [completed, setCompleted] = useState(false)
|
const [completed, setCompleted] = useState(false)
|
||||||
const mountRef = useRef<HTMLDivElement | null>(null)
|
const mountRef = useRef<HTMLDivElement | null>(null)
|
||||||
@@ -191,13 +228,18 @@ export default function Component({
|
|||||||
const pointMaterialRef = useRef<THREE.PointsMaterial | null>(null)
|
const pointMaterialRef = useRef<THREE.PointsMaterial | null>(null)
|
||||||
const shellRef = useRef<THREE.Mesh | null>(null)
|
const shellRef = useRef<THREE.Mesh | null>(null)
|
||||||
const l1ShellRef = useRef<THREE.Mesh | null>(null)
|
const l1ShellRef = useRef<THREE.Mesh | null>(null)
|
||||||
|
const shellRimRef = useRef<THREE.Mesh | null>(null)
|
||||||
|
const l1ShellRimRef = useRef<THREE.Mesh | null>(null)
|
||||||
|
const axesRef = useRef<THREE.Group | null>(null)
|
||||||
|
const originRef = useRef<THREE.Mesh | null>(null)
|
||||||
const dragRef = useRef<DragState | null>(null)
|
const dragRef = useRef<DragState | null>(null)
|
||||||
const pointersRef = useRef<Map<number, PointerPoint>>(new Map())
|
const pointersRef = useRef<Map<number, PointerPoint>>(new Map())
|
||||||
const pinchRef = useRef<PinchState | null>(null)
|
const pinchRef = useRef<PinchState | null>(null)
|
||||||
const cameraDistanceRef = useRef(DEFAULT_CAMERA_DISTANCE)
|
const cameraDistanceRef = useRef(DEFAULT_CAMERA_DISTANCE)
|
||||||
const currentCameraDistanceRef = useRef(DEFAULT_CAMERA_DISTANCE)
|
const currentCameraDistanceRef = useRef(DEFAULT_CAMERA_DISTANCE)
|
||||||
const idleRotationFactorRef = useRef(1)
|
const idleRotationFactorRef = useRef(1)
|
||||||
const rotationRef = useRef({ x: -0.45, y: 0.58 })
|
const rotationRef = useRef({ ...INITIAL_ROTATION })
|
||||||
|
const currentRotationRef = useRef({ ...INITIAL_ROTATION })
|
||||||
|
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
setAllowNegative(initialAllowNegative)
|
setAllowNegative(initialAllowNegative)
|
||||||
@@ -207,6 +249,10 @@ export default function Component({
|
|||||||
setNormalizeDistance(initialNormalizeDistance)
|
setNormalizeDistance(initialNormalizeDistance)
|
||||||
}, [initialNormalizeDistance])
|
}, [initialNormalizeDistance])
|
||||||
|
|
||||||
|
useEffect(() => {
|
||||||
|
setShowComplexityOverlay(initialShowComplexityOverlay)
|
||||||
|
}, [initialShowComplexityOverlay])
|
||||||
|
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
setRadius(initialRadius)
|
setRadius(initialRadius)
|
||||||
}, [initialRadius])
|
}, [initialRadius])
|
||||||
@@ -219,6 +265,10 @@ export default function Component({
|
|||||||
setDotRadius(initialDotRadius)
|
setDotRadius(initialDotRadius)
|
||||||
}, [initialDotRadius])
|
}, [initialDotRadius])
|
||||||
|
|
||||||
|
useEffect(() => {
|
||||||
|
setAxisSize(initialAxisSize)
|
||||||
|
}, [initialAxisSize])
|
||||||
|
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
setComplexityMode(initialComplexityMode)
|
setComplexityMode(initialComplexityMode)
|
||||||
}, [initialComplexityMode])
|
}, [initialComplexityMode])
|
||||||
@@ -237,6 +287,7 @@ export default function Component({
|
|||||||
const statVectors = isSurfaceMode ? '∞' : String(totalVectors)
|
const statVectors = isSurfaceMode ? '∞' : String(totalVectors)
|
||||||
const statOverlap = isSurfaceMode ? 'surface' : String(duplicateCount)
|
const statOverlap = isSurfaceMode ? 'surface' : String(duplicateCount)
|
||||||
const statMaxStack = isSurfaceMode ? '--' : String(maxMultiplicity)
|
const statMaxStack = isSurfaceMode ? '--' : String(maxMultiplicity)
|
||||||
|
const complexityOverlayText = getComplexityOverlayText(complexityMode, complexity)
|
||||||
|
|
||||||
const themeStyle = useMemo(() => ({
|
const themeStyle = useMemo(() => ({
|
||||||
'--gc-primary': theme?.primary,
|
'--gc-primary': theme?.primary,
|
||||||
@@ -264,6 +315,9 @@ export default function Component({
|
|||||||
const renderer = new THREE.WebGLRenderer({ antialias: true, alpha: true })
|
const renderer = new THREE.WebGLRenderer({ antialias: true, alpha: true })
|
||||||
renderer.setPixelRatio(Math.min(window.devicePixelRatio || 1, 2))
|
renderer.setPixelRatio(Math.min(window.devicePixelRatio || 1, 2))
|
||||||
renderer.setClearColor(0x000000, 0)
|
renderer.setClearColor(0x000000, 0)
|
||||||
|
renderer.outputColorSpace = THREE.SRGBColorSpace
|
||||||
|
renderer.toneMapping = THREE.ACESFilmicToneMapping
|
||||||
|
renderer.toneMappingExposure = 1.2
|
||||||
mount.appendChild(renderer.domElement)
|
mount.appendChild(renderer.domElement)
|
||||||
rendererRef.current = renderer
|
rendererRef.current = renderer
|
||||||
|
|
||||||
@@ -271,23 +325,25 @@ export default function Component({
|
|||||||
scene.add(group)
|
scene.add(group)
|
||||||
groupRef.current = group
|
groupRef.current = group
|
||||||
|
|
||||||
const ambient = new THREE.AmbientLight(0xf1ffc4, 0.45)
|
const ambient = new THREE.AmbientLight(0xf1ffc4, 0.1)
|
||||||
scene.add(ambient)
|
scene.add(ambient)
|
||||||
const keyLight = new THREE.DirectionalLight(0xf2ff9b, 2.25)
|
const keyLight = new THREE.DirectionalLight(0xf2ff9b, 3.35)
|
||||||
keyLight.position.set(3.5, 4.5, 6)
|
keyLight.position.set(6, 7, 4.5)
|
||||||
scene.add(keyLight)
|
scene.add(keyLight)
|
||||||
const fillLight = new THREE.DirectionalLight(0x79ff8c, 0.85)
|
const fillLight = new THREE.DirectionalLight(0x79ff8c, 0.95)
|
||||||
fillLight.position.set(-5, -2, 3)
|
fillLight.position.set(-6, 1.5, 3)
|
||||||
scene.add(fillLight)
|
scene.add(fillLight)
|
||||||
const rimLight = new THREE.DirectionalLight(0xffffb2, 1.05)
|
const rimLight = new THREE.DirectionalLight(0x9eeaff, 2.35)
|
||||||
rimLight.position.set(-2, 3, -5)
|
rimLight.position.set(-4.5, 5, -6.5)
|
||||||
scene.add(rimLight)
|
scene.add(rimLight)
|
||||||
|
|
||||||
const shellGeometry = new THREE.SphereGeometry(1, 96, 48)
|
const shellGeometry = createSphereShellGeometry(initialAllowNegative)
|
||||||
const shellMaterial = new THREE.MeshLambertMaterial({
|
const shellMaterial = new THREE.MeshStandardMaterial({
|
||||||
color: 0xcfff55,
|
color: 0xcfff55,
|
||||||
emissive: 0x16260a,
|
emissive: 0x16260a,
|
||||||
emissiveIntensity: 0.18,
|
emissiveIntensity: 0.045,
|
||||||
|
roughness: 0.22,
|
||||||
|
metalness: 0.04,
|
||||||
transparent: true,
|
transparent: true,
|
||||||
opacity: 0.055,
|
opacity: 0.055,
|
||||||
wireframe: true,
|
wireframe: true,
|
||||||
@@ -296,12 +352,26 @@ export default function Component({
|
|||||||
const shell = new THREE.Mesh(shellGeometry, shellMaterial)
|
const shell = new THREE.Mesh(shellGeometry, shellMaterial)
|
||||||
group.add(shell)
|
group.add(shell)
|
||||||
shellRef.current = shell
|
shellRef.current = shell
|
||||||
|
const shellRimMaterial = new THREE.MeshBasicMaterial({
|
||||||
|
color: 0xeaff83,
|
||||||
|
transparent: true,
|
||||||
|
opacity: 0.18,
|
||||||
|
side: THREE.BackSide,
|
||||||
|
depthWrite: false,
|
||||||
|
blending: THREE.AdditiveBlending
|
||||||
|
})
|
||||||
|
const shellRim = new THREE.Mesh(shellGeometry.clone(), shellRimMaterial)
|
||||||
|
shellRim.scale.setScalar(1.018)
|
||||||
|
group.add(shellRim)
|
||||||
|
shellRimRef.current = shellRim
|
||||||
|
|
||||||
const l1ShellGeometry = new THREE.OctahedronGeometry(1, 0)
|
const l1ShellGeometry = createL1ShellGeometry(initialAllowNegative)
|
||||||
const l1ShellMaterial = new THREE.MeshLambertMaterial({
|
const l1ShellMaterial = new THREE.MeshStandardMaterial({
|
||||||
color: 0xcfff55,
|
color: 0xcfff55,
|
||||||
emissive: 0x16260a,
|
emissive: 0x16260a,
|
||||||
emissiveIntensity: 0.16,
|
emissiveIntensity: 0.035,
|
||||||
|
roughness: 0.48,
|
||||||
|
metalness: 0.03,
|
||||||
transparent: true,
|
transparent: true,
|
||||||
opacity: 0.055,
|
opacity: 0.055,
|
||||||
wireframe: true,
|
wireframe: true,
|
||||||
@@ -312,18 +382,58 @@ export default function Component({
|
|||||||
l1Shell.visible = false
|
l1Shell.visible = false
|
||||||
group.add(l1Shell)
|
group.add(l1Shell)
|
||||||
l1ShellRef.current = l1Shell
|
l1ShellRef.current = l1Shell
|
||||||
|
const l1ShellRimMaterial = new THREE.MeshBasicMaterial({
|
||||||
|
color: 0xeaff83,
|
||||||
|
transparent: true,
|
||||||
|
opacity: 0.16,
|
||||||
|
side: THREE.BackSide,
|
||||||
|
depthWrite: false,
|
||||||
|
blending: THREE.AdditiveBlending
|
||||||
|
})
|
||||||
|
const l1ShellRim = new THREE.Mesh(l1ShellGeometry.clone(), l1ShellRimMaterial)
|
||||||
|
l1ShellRim.scale.setScalar(1.018)
|
||||||
|
l1ShellRim.visible = false
|
||||||
|
group.add(l1ShellRim)
|
||||||
|
l1ShellRimRef.current = l1ShellRim
|
||||||
|
|
||||||
const axisMaterial = new THREE.LineBasicMaterial({ color: 0xd7ff57, transparent: true, opacity: 0.12 })
|
const axisGeometry = new THREE.CylinderGeometry(0.024, 0.024, 2, 12)
|
||||||
const axisGeometry = new THREE.BufferGeometry().setFromPoints([
|
const createAxisMaterial = (color: number) => new THREE.MeshBasicMaterial({
|
||||||
new THREE.Vector3(-1, 0, 0),
|
color,
|
||||||
new THREE.Vector3(1, 0, 0),
|
transparent: true,
|
||||||
new THREE.Vector3(0, -1, 0),
|
opacity: 0.68,
|
||||||
new THREE.Vector3(0, 1, 0),
|
depthTest: false,
|
||||||
new THREE.Vector3(0, 0, -1),
|
depthWrite: false
|
||||||
new THREE.Vector3(0, 0, 1)
|
})
|
||||||
])
|
const axisXMaterial = createAxisMaterial(0xffdb47)
|
||||||
const axes = new THREE.LineSegments(axisGeometry, axisMaterial)
|
const axisYMaterial = createAxisMaterial(0x7dff9a)
|
||||||
|
const axisZMaterial = createAxisMaterial(0x8ce6ff)
|
||||||
|
const axes = new THREE.Group()
|
||||||
|
const xAxis = new THREE.Mesh(axisGeometry, axisXMaterial)
|
||||||
|
xAxis.rotation.z = Math.PI / 2
|
||||||
|
const yAxis = new THREE.Mesh(axisGeometry, axisYMaterial)
|
||||||
|
const zAxis = new THREE.Mesh(axisGeometry, axisZMaterial)
|
||||||
|
zAxis.rotation.x = Math.PI / 2
|
||||||
|
for (const axis of [xAxis, yAxis, zAxis]) {
|
||||||
|
axis.renderOrder = 4
|
||||||
|
axes.add(axis)
|
||||||
|
}
|
||||||
|
axes.renderOrder = 4
|
||||||
group.add(axes)
|
group.add(axes)
|
||||||
|
axesRef.current = axes
|
||||||
|
|
||||||
|
const originGeometry = new THREE.SphereGeometry(0.095, 24, 16)
|
||||||
|
const originMaterial = new THREE.MeshBasicMaterial({
|
||||||
|
color: 0xf6ff9a,
|
||||||
|
transparent: true,
|
||||||
|
opacity: 0.9,
|
||||||
|
depthTest: false,
|
||||||
|
depthWrite: false,
|
||||||
|
blending: THREE.AdditiveBlending
|
||||||
|
})
|
||||||
|
const origin = new THREE.Mesh(originGeometry, originMaterial)
|
||||||
|
origin.renderOrder = 5
|
||||||
|
group.add(origin)
|
||||||
|
originRef.current = origin
|
||||||
|
|
||||||
const pointMaterial = new THREE.PointsMaterial({
|
const pointMaterial = new THREE.PointsMaterial({
|
||||||
color: 0xffffff,
|
color: 0xffffff,
|
||||||
@@ -357,19 +467,28 @@ export default function Component({
|
|||||||
|
|
||||||
let animationFrame = 0
|
let animationFrame = 0
|
||||||
const render = () => {
|
const render = () => {
|
||||||
const rotation = rotationRef.current
|
const targetRotation = rotationRef.current
|
||||||
|
const currentRotation = currentRotationRef.current
|
||||||
currentCameraDistanceRef.current += (cameraDistanceRef.current - currentCameraDistanceRef.current) * 0.12
|
currentCameraDistanceRef.current += (cameraDistanceRef.current - currentCameraDistanceRef.current) * 0.12
|
||||||
camera.position.z = currentCameraDistanceRef.current
|
|
||||||
camera.lookAt(0, 0, 0)
|
|
||||||
group.rotation.x += (rotation.x - group.rotation.x) * 0.08
|
|
||||||
group.rotation.y += (rotation.y - group.rotation.y) * 0.08
|
|
||||||
if (dragRef.current || pinchRef.current) {
|
if (dragRef.current || pinchRef.current) {
|
||||||
idleRotationFactorRef.current = 0
|
idleRotationFactorRef.current = 0
|
||||||
} else {
|
} else {
|
||||||
idleRotationFactorRef.current += (1 - idleRotationFactorRef.current) * 0.006
|
idleRotationFactorRef.current += (1 - idleRotationFactorRef.current) * 0.006
|
||||||
group.rotation.y += IDLE_ROTATION_SPEED * idleRotationFactorRef.current
|
targetRotation.y += IDLE_ROTATION_SPEED * idleRotationFactorRef.current
|
||||||
}
|
}
|
||||||
rotation.y = group.rotation.y
|
currentRotation.x += (targetRotation.x - currentRotation.x) * 0.08
|
||||||
|
currentRotation.y += (targetRotation.y - currentRotation.y) * 0.08
|
||||||
|
|
||||||
|
const cameraDistance = currentCameraDistanceRef.current
|
||||||
|
const vertical = clamp(currentRotation.x, -1.45, 1.45)
|
||||||
|
const horizontal = currentRotation.y
|
||||||
|
const orbitRadius = Math.cos(vertical) * cameraDistance
|
||||||
|
camera.position.set(
|
||||||
|
Math.sin(horizontal) * orbitRadius,
|
||||||
|
Math.sin(vertical) * cameraDistance,
|
||||||
|
Math.cos(horizontal) * orbitRadius
|
||||||
|
)
|
||||||
|
camera.lookAt(0, 0, 0)
|
||||||
renderer.render(scene, camera)
|
renderer.render(scene, camera)
|
||||||
animationFrame = window.requestAnimationFrame(render)
|
animationFrame = window.requestAnimationFrame(render)
|
||||||
}
|
}
|
||||||
@@ -382,11 +501,19 @@ export default function Component({
|
|||||||
pointGeometry.dispose()
|
pointGeometry.dispose()
|
||||||
pointMaterial.map?.dispose()
|
pointMaterial.map?.dispose()
|
||||||
pointMaterial.dispose()
|
pointMaterial.dispose()
|
||||||
|
originGeometry.dispose()
|
||||||
|
originMaterial.dispose()
|
||||||
axisGeometry.dispose()
|
axisGeometry.dispose()
|
||||||
axisMaterial.dispose()
|
axisXMaterial.dispose()
|
||||||
shellGeometry.dispose()
|
axisYMaterial.dispose()
|
||||||
|
axisZMaterial.dispose()
|
||||||
|
shell.geometry.dispose()
|
||||||
|
shellRim.geometry.dispose()
|
||||||
|
shellRimMaterial.dispose()
|
||||||
shellMaterial.dispose()
|
shellMaterial.dispose()
|
||||||
l1ShellGeometry.dispose()
|
l1Shell.geometry.dispose()
|
||||||
|
l1ShellRim.geometry.dispose()
|
||||||
|
l1ShellRimMaterial.dispose()
|
||||||
l1ShellMaterial.dispose()
|
l1ShellMaterial.dispose()
|
||||||
renderer.dispose()
|
renderer.dispose()
|
||||||
rendererRef.current = null
|
rendererRef.current = null
|
||||||
@@ -395,6 +522,10 @@ export default function Component({
|
|||||||
pointMaterialRef.current = null
|
pointMaterialRef.current = null
|
||||||
shellRef.current = null
|
shellRef.current = null
|
||||||
l1ShellRef.current = null
|
l1ShellRef.current = null
|
||||||
|
shellRimRef.current = null
|
||||||
|
l1ShellRimRef.current = null
|
||||||
|
axesRef.current = null
|
||||||
|
originRef.current = null
|
||||||
}
|
}
|
||||||
}, [])
|
}, [])
|
||||||
|
|
||||||
@@ -422,12 +553,46 @@ export default function Component({
|
|||||||
pointCloud.geometry = nextGeometry
|
pointCloud.geometry = nextGeometry
|
||||||
}, [maxMultiplicity, points])
|
}, [maxMultiplicity, points])
|
||||||
|
|
||||||
|
useEffect(() => {
|
||||||
|
const shell = shellRef.current
|
||||||
|
const shellRim = shellRimRef.current
|
||||||
|
const l1Shell = l1ShellRef.current
|
||||||
|
const l1ShellRim = l1ShellRimRef.current
|
||||||
|
if (!shell || !shellRim || !l1Shell || !l1ShellRim) return
|
||||||
|
|
||||||
|
const nextShellGeometry = createSphereShellGeometry(allowNegative)
|
||||||
|
const nextL1ShellGeometry = createL1ShellGeometry(allowNegative)
|
||||||
|
const previousShellGeometry = shell.geometry
|
||||||
|
const previousShellRimGeometry = shellRim.geometry
|
||||||
|
const previousL1ShellGeometry = l1Shell.geometry
|
||||||
|
const previousL1ShellRimGeometry = l1ShellRim.geometry
|
||||||
|
|
||||||
|
shell.geometry = nextShellGeometry
|
||||||
|
shellRim.geometry = nextShellGeometry.clone()
|
||||||
|
l1Shell.geometry = nextL1ShellGeometry
|
||||||
|
l1ShellRim.geometry = nextL1ShellGeometry.clone()
|
||||||
|
|
||||||
|
for (const material of [shell.material, l1Shell.material]) {
|
||||||
|
if (material instanceof THREE.MeshStandardMaterial) {
|
||||||
|
material.side = allowNegative ? THREE.FrontSide : THREE.DoubleSide
|
||||||
|
material.needsUpdate = true
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
previousShellGeometry.dispose()
|
||||||
|
previousShellRimGeometry.dispose()
|
||||||
|
previousL1ShellGeometry.dispose()
|
||||||
|
previousL1ShellRimGeometry.dispose()
|
||||||
|
}, [allowNegative])
|
||||||
|
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
shellRef.current?.scale.setScalar(radius)
|
shellRef.current?.scale.setScalar(radius)
|
||||||
l1ShellRef.current?.scale.setScalar(radius)
|
l1ShellRef.current?.scale.setScalar(radius)
|
||||||
const axes = groupRef.current?.children.find((child) => child.type === 'LineSegments')
|
shellRimRef.current?.scale.setScalar(radius * 1.018)
|
||||||
axes?.scale.setScalar(radius * 1.18)
|
l1ShellRimRef.current?.scale.setScalar(radius * 1.018)
|
||||||
}, [radius])
|
axesRef.current?.children.forEach((axis) => axis.scale.set(axisSize, radius * 1.18, axisSize))
|
||||||
|
originRef.current?.scale.setScalar(clamp(radius / 4, 0.75, 1.45))
|
||||||
|
}, [axisSize, radius])
|
||||||
|
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
if (pointsRef.current) {
|
if (pointsRef.current) {
|
||||||
@@ -436,32 +601,47 @@ export default function Component({
|
|||||||
|
|
||||||
const euclideanShell = shellRef.current
|
const euclideanShell = shellRef.current
|
||||||
const l1Shell = l1ShellRef.current
|
const l1Shell = l1ShellRef.current
|
||||||
|
const euclideanRim = shellRimRef.current
|
||||||
|
const l1Rim = l1ShellRimRef.current
|
||||||
if (euclideanShell) {
|
if (euclideanShell) {
|
||||||
euclideanShell.visible = normalizeDistance
|
euclideanShell.visible = normalizeDistance
|
||||||
}
|
}
|
||||||
if (l1Shell) {
|
if (l1Shell) {
|
||||||
l1Shell.visible = !normalizeDistance
|
l1Shell.visible = !normalizeDistance
|
||||||
}
|
}
|
||||||
|
if (euclideanRim) {
|
||||||
|
euclideanRim.visible = isSurfaceMode && normalizeDistance && allowNegative
|
||||||
|
}
|
||||||
|
if (l1Rim) {
|
||||||
|
l1Rim.visible = isSurfaceMode && !normalizeDistance && allowNegative
|
||||||
|
}
|
||||||
|
|
||||||
for (const shell of [euclideanShell, l1Shell]) {
|
for (const shell of [euclideanShell, l1Shell]) {
|
||||||
const shellMaterial = shell?.material
|
const shellMaterial = shell?.material
|
||||||
if (shellMaterial instanceof THREE.MeshLambertMaterial) {
|
if (shellMaterial instanceof THREE.MeshStandardMaterial) {
|
||||||
shellMaterial.wireframe = !isSurfaceMode
|
shellMaterial.wireframe = !isSurfaceMode
|
||||||
shellMaterial.opacity = isSurfaceMode ? 0.34 : 0.055
|
shellMaterial.opacity = isSurfaceMode ? 0.5 : 0.055
|
||||||
shellMaterial.depthWrite = false
|
shellMaterial.depthWrite = false
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}, [isSurfaceMode, normalizeDistance])
|
}, [allowNegative, isSurfaceMode, normalizeDistance])
|
||||||
|
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
const accent = new THREE.Color(theme?.accent ?? '#4de1c1')
|
const accent = new THREE.Color(theme?.accent ?? '#4de1c1')
|
||||||
|
const rimAccent = accent.clone().lerp(new THREE.Color('#ffffaa'), 0.42)
|
||||||
for (const shell of [shellRef.current, l1ShellRef.current]) {
|
for (const shell of [shellRef.current, l1ShellRef.current]) {
|
||||||
const shellMaterial = shell?.material
|
const shellMaterial = shell?.material
|
||||||
if (shellMaterial instanceof THREE.MeshLambertMaterial) {
|
if (shellMaterial instanceof THREE.MeshStandardMaterial) {
|
||||||
shellMaterial.color.copy(accent)
|
shellMaterial.color.copy(accent)
|
||||||
shellMaterial.emissive.set('#16260a')
|
shellMaterial.emissive.set('#16260a')
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
for (const shell of [shellRimRef.current, l1ShellRimRef.current]) {
|
||||||
|
const shellMaterial = shell?.material
|
||||||
|
if (shellMaterial instanceof THREE.MeshBasicMaterial) {
|
||||||
|
shellMaterial.color.copy(rimAccent)
|
||||||
|
}
|
||||||
|
}
|
||||||
}, [theme?.accent])
|
}, [theme?.accent])
|
||||||
|
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
@@ -485,9 +665,11 @@ export default function Component({
|
|||||||
data: {
|
data: {
|
||||||
allowNegative,
|
allowNegative,
|
||||||
normalizeDistance,
|
normalizeDistance,
|
||||||
|
showComplexityOverlay,
|
||||||
complexityMode,
|
complexityMode,
|
||||||
radius,
|
radius,
|
||||||
dotRadius,
|
dotRadius,
|
||||||
|
axisSize,
|
||||||
complexity,
|
complexity,
|
||||||
rays: points.length,
|
rays: points.length,
|
||||||
vectors: totalVectors,
|
vectors: totalVectors,
|
||||||
@@ -506,6 +688,8 @@ export default function Component({
|
|||||||
})
|
})
|
||||||
}, [
|
}, [
|
||||||
allowNegative,
|
allowNegative,
|
||||||
|
showComplexityOverlay,
|
||||||
|
axisSize,
|
||||||
completed,
|
completed,
|
||||||
complexity,
|
complexity,
|
||||||
config.id,
|
config.id,
|
||||||
@@ -533,6 +717,11 @@ export default function Component({
|
|||||||
setNormalizeDistance((value) => !value)
|
setNormalizeDistance((value) => !value)
|
||||||
}, [playClick])
|
}, [playClick])
|
||||||
|
|
||||||
|
const updateShowComplexityOverlay = useCallback(() => {
|
||||||
|
playClick('complexity-overlay')
|
||||||
|
setShowComplexityOverlay((value) => !value)
|
||||||
|
}, [playClick])
|
||||||
|
|
||||||
const updateRadius = useCallback((value: number) => {
|
const updateRadius = useCallback((value: number) => {
|
||||||
playClick('radius')
|
playClick('radius')
|
||||||
setRadius(clamp(value, 1, 8))
|
setRadius(clamp(value, 1, 8))
|
||||||
@@ -548,6 +737,11 @@ export default function Component({
|
|||||||
setDotRadius(clamp(value, 0.05, MAX_DOT_RADIUS))
|
setDotRadius(clamp(value, 0.05, MAX_DOT_RADIUS))
|
||||||
}, [playClick])
|
}, [playClick])
|
||||||
|
|
||||||
|
const updateAxisSize = useCallback((value: number) => {
|
||||||
|
playClick('axis-size')
|
||||||
|
setAxisSize(clamp(value, 0.25, MAX_AXIS_SIZE))
|
||||||
|
}, [playClick])
|
||||||
|
|
||||||
const updateComplexityMode = useCallback((nextMode: ComplexityMode) => {
|
const updateComplexityMode = useCallback((nextMode: ComplexityMode) => {
|
||||||
playClick(`${nextMode}-mode`)
|
playClick(`${nextMode}-mode`)
|
||||||
setComplexityMode(nextMode)
|
setComplexityMode(nextMode)
|
||||||
@@ -693,6 +887,11 @@ export default function Component({
|
|||||||
<span>y</span>
|
<span>y</span>
|
||||||
<span>z</span>
|
<span>z</span>
|
||||||
</div>
|
</div>
|
||||||
|
{showComplexityOverlay && (
|
||||||
|
<div className={styles.complexityOverlay} aria-live="polite">
|
||||||
|
{complexityOverlayText}
|
||||||
|
</div>
|
||||||
|
)}
|
||||||
</section>
|
</section>
|
||||||
|
|
||||||
<aside className={styles.controlPanel}>
|
<aside className={styles.controlPanel}>
|
||||||
@@ -738,6 +937,14 @@ export default function Component({
|
|||||||
<input type="checkbox" checked={normalizeDistance} onChange={updateNormalizeDistance} />
|
<input type="checkbox" checked={normalizeDistance} onChange={updateNormalizeDistance} />
|
||||||
</label>
|
</label>
|
||||||
|
|
||||||
|
<label className={styles.toggleRow}>
|
||||||
|
<span>
|
||||||
|
<strong>Show complexity range</strong>
|
||||||
|
<small>{showComplexityOverlay ? 'Visible' : 'Hidden'}</small>
|
||||||
|
</span>
|
||||||
|
<input type="checkbox" checked={showComplexityOverlay} onChange={updateShowComplexityOverlay} />
|
||||||
|
</label>
|
||||||
|
|
||||||
<label className={styles.sliderRow}>
|
<label className={styles.sliderRow}>
|
||||||
<span>
|
<span>
|
||||||
<strong>D</strong>
|
<strong>D</strong>
|
||||||
@@ -810,6 +1017,30 @@ export default function Component({
|
|||||||
</div>
|
</div>
|
||||||
</label>
|
</label>
|
||||||
|
|
||||||
|
<label className={styles.sliderRow}>
|
||||||
|
<span>
|
||||||
|
<strong>A</strong>
|
||||||
|
<small>{axisSize.toFixed(2)}</small>
|
||||||
|
</span>
|
||||||
|
<div className={styles.sliderControl}>
|
||||||
|
<button type="button" onClick={() => updateAxisSize(axisSize - 0.05)} aria-label="Decrease axis size">
|
||||||
|
-
|
||||||
|
</button>
|
||||||
|
<input
|
||||||
|
type="range"
|
||||||
|
min={0.25}
|
||||||
|
max={MAX_AXIS_SIZE}
|
||||||
|
step={0.05}
|
||||||
|
value={axisSize}
|
||||||
|
onInput={(event) => updateAxisSize(Number(event.currentTarget.value))}
|
||||||
|
onChange={(event) => updateAxisSize(Number(event.currentTarget.value))}
|
||||||
|
/>
|
||||||
|
<button type="button" onClick={() => updateAxisSize(axisSize + 0.05)} aria-label="Increase axis size">
|
||||||
|
+
|
||||||
|
</button>
|
||||||
|
</div>
|
||||||
|
</label>
|
||||||
|
|
||||||
<div className={styles.rayNote}>
|
<div className={styles.rayNote}>
|
||||||
<span className={styles.kicker}>colour rule</span>
|
<span className={styles.kicker}>colour rule</span>
|
||||||
<p>
|
<p>
|
||||||
|
|||||||
+4
-2
@@ -9,6 +9,7 @@ function DevHarness() {
|
|||||||
const params = useControls({
|
const params = useControls({
|
||||||
allowNegative: { value: true },
|
allowNegative: { value: true },
|
||||||
normalizeDistance: { value: true },
|
normalizeDistance: { value: true },
|
||||||
|
showComplexityOverlay: { value: true },
|
||||||
complexityMode: {
|
complexityMode: {
|
||||||
value: 'surface',
|
value: 'surface',
|
||||||
options: {
|
options: {
|
||||||
@@ -18,8 +19,9 @@ function DevHarness() {
|
|||||||
}
|
}
|
||||||
},
|
},
|
||||||
radius: { value: 4, min: 1, max: 8, step: 0.25 },
|
radius: { value: 4, min: 1, max: 8, step: 0.25 },
|
||||||
complexity: { value: 8, min: 1, max: 96, step: 1 },
|
complexity: { value: 17, min: 1, max: 96, step: 1 },
|
||||||
dotRadius: { value: 0.17, min: 0.05, max: 1.2, step: 0.01 }
|
dotRadius: { value: 0.5, min: 0.05, max: 1.2, step: 0.01 },
|
||||||
|
axisSize: { value: 1, min: 0.25, max: 2.5, step: 0.05 }
|
||||||
})
|
})
|
||||||
|
|
||||||
return (
|
return (
|
||||||
|
|||||||
+19
-4
@@ -18,6 +18,11 @@ export const metadata: GlitchComponentMetadata = {
|
|||||||
label: 'Normalize Distance',
|
label: 'Normalize Distance',
|
||||||
default: true
|
default: true
|
||||||
},
|
},
|
||||||
|
showComplexityOverlay: {
|
||||||
|
type: 'boolean',
|
||||||
|
label: 'Show Complexity Range',
|
||||||
|
default: true
|
||||||
|
},
|
||||||
complexityMode: {
|
complexityMode: {
|
||||||
type: 'select',
|
type: 'select',
|
||||||
label: 'Complexity Mode',
|
label: 'Complexity Mode',
|
||||||
@@ -39,7 +44,7 @@ export const metadata: GlitchComponentMetadata = {
|
|||||||
complexity: {
|
complexity: {
|
||||||
type: 'range',
|
type: 'range',
|
||||||
label: 'C',
|
label: 'C',
|
||||||
default: 8,
|
default: 17,
|
||||||
min: 1,
|
min: 1,
|
||||||
max: 96,
|
max: 96,
|
||||||
step: 1
|
step: 1
|
||||||
@@ -47,19 +52,29 @@ export const metadata: GlitchComponentMetadata = {
|
|||||||
dotRadius: {
|
dotRadius: {
|
||||||
type: 'range',
|
type: 'range',
|
||||||
label: 'R',
|
label: 'R',
|
||||||
default: 0.17,
|
default: 0.5,
|
||||||
min: 0.05,
|
min: 0.05,
|
||||||
max: 1.2,
|
max: 1.2,
|
||||||
step: 0.01
|
step: 0.01
|
||||||
|
},
|
||||||
|
axisSize: {
|
||||||
|
type: 'range',
|
||||||
|
label: 'A',
|
||||||
|
default: 1,
|
||||||
|
min: 0.25,
|
||||||
|
max: 2.5,
|
||||||
|
step: 0.05
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
defaultParams: {
|
defaultParams: {
|
||||||
allowNegative: true,
|
allowNegative: true,
|
||||||
normalizeDistance: true,
|
normalizeDistance: true,
|
||||||
|
showComplexityOverlay: true,
|
||||||
complexityMode: 'surface',
|
complexityMode: 'surface',
|
||||||
radius: 4,
|
radius: 4,
|
||||||
complexity: 8,
|
complexity: 17,
|
||||||
dotRadius: 0.17
|
dotRadius: 0.5,
|
||||||
|
axisSize: 1
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
+51
-16
@@ -10,6 +10,8 @@
|
|||||||
--gc-font-display: var(--font-display, var(--gc-font-main));
|
--gc-font-display: var(--font-display, var(--gc-font-main));
|
||||||
--gc-font-mono: var(--font-mono, monospace);
|
--gc-font-mono: var(--font-mono, monospace);
|
||||||
--gc-shell-padding: clamp(8px, 2vw, 18px);
|
--gc-shell-padding: clamp(8px, 2vw, 18px);
|
||||||
|
--gc-viewport-size: 1000px;
|
||||||
|
--gc-control-width: 14em;
|
||||||
width: 100%;
|
width: 100%;
|
||||||
min-height: 100dvh;
|
min-height: 100dvh;
|
||||||
display: grid;
|
display: grid;
|
||||||
@@ -26,9 +28,8 @@
|
|||||||
|
|
||||||
.frame {
|
.frame {
|
||||||
position: relative;
|
position: relative;
|
||||||
width: min(100%, calc((100dvh - (var(--gc-shell-padding) * 2)) * 1.9));
|
width: calc(var(--gc-viewport-size) + var(--gc-control-width) + 2.7em);
|
||||||
max-height: calc(100dvh - (var(--gc-shell-padding) * 2));
|
height: calc(var(--gc-viewport-size) + 2em);
|
||||||
aspect-ratio: 1.9 / 1;
|
|
||||||
display: grid;
|
display: grid;
|
||||||
grid-template-rows: minmax(0, 1fr);
|
grid-template-rows: minmax(0, 1fr);
|
||||||
overflow: hidden;
|
overflow: hidden;
|
||||||
@@ -65,7 +66,7 @@
|
|||||||
z-index: 5;
|
z-index: 5;
|
||||||
left: 1em;
|
left: 1em;
|
||||||
top: 0.9em;
|
top: 0.9em;
|
||||||
width: min(25em, 34%);
|
width: var(--gc-control-width);
|
||||||
pointer-events: none;
|
pointer-events: none;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -92,7 +93,7 @@
|
|||||||
z-index: 5;
|
z-index: 5;
|
||||||
left: 1em;
|
left: 1em;
|
||||||
bottom: 1em;
|
bottom: 1em;
|
||||||
width: min(25em, 34%);
|
width: var(--gc-control-width);
|
||||||
display: grid;
|
display: grid;
|
||||||
grid-template-columns: repeat(2, minmax(0, 1fr));
|
grid-template-columns: repeat(2, minmax(0, 1fr));
|
||||||
gap: 0.4em;
|
gap: 0.4em;
|
||||||
@@ -129,7 +130,7 @@
|
|||||||
min-height: 0;
|
min-height: 0;
|
||||||
grid-row: 1;
|
grid-row: 1;
|
||||||
display: grid;
|
display: grid;
|
||||||
grid-template-columns: minmax(16em, 0.48fr) minmax(0, 1fr);
|
grid-template-columns: var(--gc-control-width) var(--gc-viewport-size);
|
||||||
align-items: center;
|
align-items: center;
|
||||||
gap: 0.7em;
|
gap: 0.7em;
|
||||||
}
|
}
|
||||||
@@ -146,10 +147,13 @@
|
|||||||
grid-column: 2;
|
grid-column: 2;
|
||||||
min-width: 0;
|
min-width: 0;
|
||||||
min-height: 0;
|
min-height: 0;
|
||||||
width: min(100%, calc(100dvh - 5.2em));
|
width: var(--gc-viewport-size);
|
||||||
|
height: var(--gc-viewport-size);
|
||||||
aspect-ratio: 1;
|
aspect-ratio: 1;
|
||||||
justify-self: end;
|
justify-self: end;
|
||||||
overflow: hidden;
|
overflow: hidden;
|
||||||
|
border: 3px solid #ffbd16;
|
||||||
|
box-shadow: 0 0 0 1px rgb(255 189 22 / 18%);
|
||||||
cursor: grab;
|
cursor: grab;
|
||||||
touch-action: none;
|
touch-action: none;
|
||||||
}
|
}
|
||||||
@@ -186,7 +190,8 @@
|
|||||||
|
|
||||||
.axisLabels {
|
.axisLabels {
|
||||||
position: absolute;
|
position: absolute;
|
||||||
inset: 0.8em;
|
left: 1.9em;
|
||||||
|
bottom: 2.25em;
|
||||||
display: flex;
|
display: flex;
|
||||||
align-items: flex-end;
|
align-items: flex-end;
|
||||||
justify-content: flex-start;
|
justify-content: flex-start;
|
||||||
@@ -208,6 +213,25 @@
|
|||||||
text-transform: uppercase;
|
text-transform: uppercase;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
.complexityOverlay {
|
||||||
|
position: absolute;
|
||||||
|
z-index: 3;
|
||||||
|
right: 1.9em;
|
||||||
|
bottom: 2.25em;
|
||||||
|
max-width: min(24em, calc(100% - 15em));
|
||||||
|
padding: 0.52em 0.72em;
|
||||||
|
border: 1px solid color-mix(in srgb, var(--gc-accent) 34%, transparent);
|
||||||
|
background: rgb(5 10 14 / 72%);
|
||||||
|
color: color-mix(in srgb, var(--gc-accent) 84%, white);
|
||||||
|
font-family: var(--gc-font-mono);
|
||||||
|
font-size: 0.72em;
|
||||||
|
font-weight: 800;
|
||||||
|
letter-spacing: 0.04em;
|
||||||
|
line-height: 1.15;
|
||||||
|
text-transform: uppercase;
|
||||||
|
pointer-events: none;
|
||||||
|
}
|
||||||
|
|
||||||
.controlPanel {
|
.controlPanel {
|
||||||
grid-column: 1;
|
grid-column: 1;
|
||||||
grid-row: 1;
|
grid-row: 1;
|
||||||
@@ -215,8 +239,8 @@
|
|||||||
min-height: 0;
|
min-height: 0;
|
||||||
display: grid;
|
display: grid;
|
||||||
align-content: start;
|
align-content: start;
|
||||||
gap: 0.55em;
|
gap: 0.3em;
|
||||||
padding: 0.7em;
|
padding: 0.46em;
|
||||||
margin-top: 4.2em;
|
margin-top: 4.2em;
|
||||||
margin-bottom: 6.8em;
|
margin-bottom: 6.8em;
|
||||||
}
|
}
|
||||||
@@ -250,8 +274,8 @@
|
|||||||
.toggleRow,
|
.toggleRow,
|
||||||
.sliderRow {
|
.sliderRow {
|
||||||
display: grid;
|
display: grid;
|
||||||
gap: 0.4em;
|
gap: 0.28em;
|
||||||
padding: 0.58em;
|
padding: 0.4em;
|
||||||
border: 1px solid color-mix(in srgb, var(--gc-border) 72%, transparent);
|
border: 1px solid color-mix(in srgb, var(--gc-border) 72%, transparent);
|
||||||
background: rgb(5 10 14 / 54%);
|
background: rgb(5 10 14 / 54%);
|
||||||
}
|
}
|
||||||
@@ -337,12 +361,12 @@
|
|||||||
display: grid;
|
display: grid;
|
||||||
grid-template-columns: auto minmax(0, 1fr) auto;
|
grid-template-columns: auto minmax(0, 1fr) auto;
|
||||||
align-items: center;
|
align-items: center;
|
||||||
gap: 0.45em;
|
gap: 0.28em;
|
||||||
}
|
}
|
||||||
|
|
||||||
.sliderControl button {
|
.sliderControl button {
|
||||||
width: 1.9em;
|
width: 1.55em;
|
||||||
height: 1.9em;
|
height: 1.55em;
|
||||||
display: grid;
|
display: grid;
|
||||||
place-items: center;
|
place-items: center;
|
||||||
border: 1px solid color-mix(in srgb, var(--gc-border) 78%, transparent);
|
border: 1px solid color-mix(in srgb, var(--gc-border) 78%, transparent);
|
||||||
@@ -350,7 +374,7 @@
|
|||||||
color: var(--gc-text);
|
color: var(--gc-text);
|
||||||
font: inherit;
|
font: inherit;
|
||||||
font-family: var(--gc-font-mono);
|
font-family: var(--gc-font-mono);
|
||||||
font-size: 0.72em;
|
font-size: 0.68em;
|
||||||
font-weight: 900;
|
font-weight: 900;
|
||||||
cursor: pointer;
|
cursor: pointer;
|
||||||
}
|
}
|
||||||
@@ -426,6 +450,17 @@
|
|||||||
justify-self: center;
|
justify-self: center;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
.axisLabels {
|
||||||
|
left: 1.45em;
|
||||||
|
bottom: 1.8em;
|
||||||
|
}
|
||||||
|
|
||||||
|
.complexityOverlay {
|
||||||
|
right: 1.45em;
|
||||||
|
bottom: 4.4em;
|
||||||
|
max-width: calc(100% - 2.9em);
|
||||||
|
}
|
||||||
|
|
||||||
.controlPanel {
|
.controlPanel {
|
||||||
grid-column: 1;
|
grid-column: 1;
|
||||||
grid-row: 1;
|
grid-row: 1;
|
||||||
|
|||||||
@@ -19,6 +19,7 @@ export default defineConfig(({ mode }) => ({
|
|||||||
react: 'React',
|
react: 'React',
|
||||||
'react-dom': 'ReactDOM'
|
'react-dom': 'ReactDOM'
|
||||||
},
|
},
|
||||||
|
entryFileNames: 'diophantine-sphere.js',
|
||||||
assetFileNames: 'assets/[name][extname]'
|
assetFileNames: 'assets/[name][extname]'
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
|||||||
Reference in New Issue
Block a user