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:
2026-08-14 09:40:21 +02:00
co-authored by Claude Opus 4.8
parent 6ad4f51838
commit c75e156b8f
11 changed files with 602 additions and 221 deletions
+1
View File
@@ -1,2 +1,3 @@
node_modules/
.DS_Store
node_modules
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
+95
View File
@@ -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
View File
@@ -15,7 +15,8 @@
"files": [
"dist",
"src/types.ts",
"glitch.manifest.json"
"glitch.manifest.json",
"glitch.json"
],
"scripts": {
"dev": "vite",
+274 -43
View File
@@ -7,9 +7,11 @@ import type { GlitchComponentProps } from './types'
interface ComponentParams {
allowNegative?: boolean
normalizeDistance?: boolean
showComplexityOverlay?: boolean
radius?: number
complexity?: number
dotRadius?: number
axisSize?: number
complexityMode?: string
}
@@ -47,6 +49,8 @@ const DEFAULT_CAMERA_DISTANCE = 13
const IDLE_ROTATION_SPEED = 0.0005
const MAX_COMPLEXITY = 96
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 => {
let left = Math.abs(a)
@@ -82,11 +86,40 @@ const colorRamp = (weight: number) => {
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 => {
if (value === 'surface' || value === 'single' || value === 'range') return value
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 = (
complexity: number,
radius: number,
@@ -172,16 +205,20 @@ export default function Component({
const params = config.params as ComponentParams
const initialAllowNegative = typeof params.allowNegative === 'boolean' ? params.allowNegative : 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 initialComplexity = Math.round(clamp(numberParam(params.complexity, 8), 1, MAX_COMPLEXITY))
const initialDotRadius = clamp(numberParam(params.dotRadius, 0.17), 0.05, MAX_DOT_RADIUS)
const initialComplexity = Math.round(clamp(numberParam(params.complexity, 17), 1, MAX_COMPLEXITY))
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 [allowNegative, setAllowNegative] = useState(initialAllowNegative)
const [normalizeDistance, setNormalizeDistance] = useState(initialNormalizeDistance)
const [showComplexityOverlay, setShowComplexityOverlay] = useState(initialShowComplexityOverlay)
const [radius, setRadius] = useState(initialRadius)
const [complexity, setComplexity] = useState(initialComplexity)
const [dotRadius, setDotRadius] = useState(initialDotRadius)
const [axisSize, setAxisSize] = useState(initialAxisSize)
const [complexityMode, setComplexityMode] = useState<ComplexityMode>(initialComplexityMode)
const [completed, setCompleted] = useState(false)
const mountRef = useRef<HTMLDivElement | null>(null)
@@ -191,13 +228,18 @@ export default function Component({
const pointMaterialRef = useRef<THREE.PointsMaterial | null>(null)
const shellRef = 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 pointersRef = useRef<Map<number, PointerPoint>>(new Map())
const pinchRef = useRef<PinchState | null>(null)
const cameraDistanceRef = useRef(DEFAULT_CAMERA_DISTANCE)
const currentCameraDistanceRef = useRef(DEFAULT_CAMERA_DISTANCE)
const idleRotationFactorRef = useRef(1)
const rotationRef = useRef({ x: -0.45, y: 0.58 })
const rotationRef = useRef({ ...INITIAL_ROTATION })
const currentRotationRef = useRef({ ...INITIAL_ROTATION })
useEffect(() => {
setAllowNegative(initialAllowNegative)
@@ -207,6 +249,10 @@ export default function Component({
setNormalizeDistance(initialNormalizeDistance)
}, [initialNormalizeDistance])
useEffect(() => {
setShowComplexityOverlay(initialShowComplexityOverlay)
}, [initialShowComplexityOverlay])
useEffect(() => {
setRadius(initialRadius)
}, [initialRadius])
@@ -219,6 +265,10 @@ export default function Component({
setDotRadius(initialDotRadius)
}, [initialDotRadius])
useEffect(() => {
setAxisSize(initialAxisSize)
}, [initialAxisSize])
useEffect(() => {
setComplexityMode(initialComplexityMode)
}, [initialComplexityMode])
@@ -237,6 +287,7 @@ export default function Component({
const statVectors = isSurfaceMode ? '∞' : String(totalVectors)
const statOverlap = isSurfaceMode ? 'surface' : String(duplicateCount)
const statMaxStack = isSurfaceMode ? '--' : String(maxMultiplicity)
const complexityOverlayText = getComplexityOverlayText(complexityMode, complexity)
const themeStyle = useMemo(() => ({
'--gc-primary': theme?.primary,
@@ -264,6 +315,9 @@ export default function Component({
const renderer = new THREE.WebGLRenderer({ antialias: true, alpha: true })
renderer.setPixelRatio(Math.min(window.devicePixelRatio || 1, 2))
renderer.setClearColor(0x000000, 0)
renderer.outputColorSpace = THREE.SRGBColorSpace
renderer.toneMapping = THREE.ACESFilmicToneMapping
renderer.toneMappingExposure = 1.2
mount.appendChild(renderer.domElement)
rendererRef.current = renderer
@@ -271,23 +325,25 @@ export default function Component({
scene.add(group)
groupRef.current = group
const ambient = new THREE.AmbientLight(0xf1ffc4, 0.45)
const ambient = new THREE.AmbientLight(0xf1ffc4, 0.1)
scene.add(ambient)
const keyLight = new THREE.DirectionalLight(0xf2ff9b, 2.25)
keyLight.position.set(3.5, 4.5, 6)
const keyLight = new THREE.DirectionalLight(0xf2ff9b, 3.35)
keyLight.position.set(6, 7, 4.5)
scene.add(keyLight)
const fillLight = new THREE.DirectionalLight(0x79ff8c, 0.85)
fillLight.position.set(-5, -2, 3)
const fillLight = new THREE.DirectionalLight(0x79ff8c, 0.95)
fillLight.position.set(-6, 1.5, 3)
scene.add(fillLight)
const rimLight = new THREE.DirectionalLight(0xffffb2, 1.05)
rimLight.position.set(-2, 3, -5)
const rimLight = new THREE.DirectionalLight(0x9eeaff, 2.35)
rimLight.position.set(-4.5, 5, -6.5)
scene.add(rimLight)
const shellGeometry = new THREE.SphereGeometry(1, 96, 48)
const shellMaterial = new THREE.MeshLambertMaterial({
const shellGeometry = createSphereShellGeometry(initialAllowNegative)
const shellMaterial = new THREE.MeshStandardMaterial({
color: 0xcfff55,
emissive: 0x16260a,
emissiveIntensity: 0.18,
emissiveIntensity: 0.045,
roughness: 0.22,
metalness: 0.04,
transparent: true,
opacity: 0.055,
wireframe: true,
@@ -296,12 +352,26 @@ export default function Component({
const shell = new THREE.Mesh(shellGeometry, shellMaterial)
group.add(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 l1ShellMaterial = new THREE.MeshLambertMaterial({
const l1ShellGeometry = createL1ShellGeometry(initialAllowNegative)
const l1ShellMaterial = new THREE.MeshStandardMaterial({
color: 0xcfff55,
emissive: 0x16260a,
emissiveIntensity: 0.16,
emissiveIntensity: 0.035,
roughness: 0.48,
metalness: 0.03,
transparent: true,
opacity: 0.055,
wireframe: true,
@@ -312,18 +382,58 @@ export default function Component({
l1Shell.visible = false
group.add(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.BufferGeometry().setFromPoints([
new THREE.Vector3(-1, 0, 0),
new THREE.Vector3(1, 0, 0),
new THREE.Vector3(0, -1, 0),
new THREE.Vector3(0, 1, 0),
new THREE.Vector3(0, 0, -1),
new THREE.Vector3(0, 0, 1)
])
const axes = new THREE.LineSegments(axisGeometry, axisMaterial)
const axisGeometry = new THREE.CylinderGeometry(0.024, 0.024, 2, 12)
const createAxisMaterial = (color: number) => new THREE.MeshBasicMaterial({
color,
transparent: true,
opacity: 0.68,
depthTest: false,
depthWrite: false
})
const axisXMaterial = createAxisMaterial(0xffdb47)
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)
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({
color: 0xffffff,
@@ -357,19 +467,28 @@ export default function Component({
let animationFrame = 0
const render = () => {
const rotation = rotationRef.current
const targetRotation = rotationRef.current
const currentRotation = currentRotationRef.current
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) {
idleRotationFactorRef.current = 0
} else {
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)
animationFrame = window.requestAnimationFrame(render)
}
@@ -382,11 +501,19 @@ export default function Component({
pointGeometry.dispose()
pointMaterial.map?.dispose()
pointMaterial.dispose()
originGeometry.dispose()
originMaterial.dispose()
axisGeometry.dispose()
axisMaterial.dispose()
shellGeometry.dispose()
axisXMaterial.dispose()
axisYMaterial.dispose()
axisZMaterial.dispose()
shell.geometry.dispose()
shellRim.geometry.dispose()
shellRimMaterial.dispose()
shellMaterial.dispose()
l1ShellGeometry.dispose()
l1Shell.geometry.dispose()
l1ShellRim.geometry.dispose()
l1ShellRimMaterial.dispose()
l1ShellMaterial.dispose()
renderer.dispose()
rendererRef.current = null
@@ -395,6 +522,10 @@ export default function Component({
pointMaterialRef.current = null
shellRef.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
}, [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(() => {
shellRef.current?.scale.setScalar(radius)
l1ShellRef.current?.scale.setScalar(radius)
const axes = groupRef.current?.children.find((child) => child.type === 'LineSegments')
axes?.scale.setScalar(radius * 1.18)
}, [radius])
shellRimRef.current?.scale.setScalar(radius * 1.018)
l1ShellRimRef.current?.scale.setScalar(radius * 1.018)
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(() => {
if (pointsRef.current) {
@@ -436,32 +601,47 @@ export default function Component({
const euclideanShell = shellRef.current
const l1Shell = l1ShellRef.current
const euclideanRim = shellRimRef.current
const l1Rim = l1ShellRimRef.current
if (euclideanShell) {
euclideanShell.visible = normalizeDistance
}
if (l1Shell) {
l1Shell.visible = !normalizeDistance
}
if (euclideanRim) {
euclideanRim.visible = isSurfaceMode && normalizeDistance && allowNegative
}
if (l1Rim) {
l1Rim.visible = isSurfaceMode && !normalizeDistance && allowNegative
}
for (const shell of [euclideanShell, l1Shell]) {
const shellMaterial = shell?.material
if (shellMaterial instanceof THREE.MeshLambertMaterial) {
if (shellMaterial instanceof THREE.MeshStandardMaterial) {
shellMaterial.wireframe = !isSurfaceMode
shellMaterial.opacity = isSurfaceMode ? 0.34 : 0.055
shellMaterial.opacity = isSurfaceMode ? 0.5 : 0.055
shellMaterial.depthWrite = false
}
}
}, [isSurfaceMode, normalizeDistance])
}, [allowNegative, isSurfaceMode, normalizeDistance])
useEffect(() => {
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]) {
const shellMaterial = shell?.material
if (shellMaterial instanceof THREE.MeshLambertMaterial) {
if (shellMaterial instanceof THREE.MeshStandardMaterial) {
shellMaterial.color.copy(accent)
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])
useEffect(() => {
@@ -485,9 +665,11 @@ export default function Component({
data: {
allowNegative,
normalizeDistance,
showComplexityOverlay,
complexityMode,
radius,
dotRadius,
axisSize,
complexity,
rays: points.length,
vectors: totalVectors,
@@ -506,6 +688,8 @@ export default function Component({
})
}, [
allowNegative,
showComplexityOverlay,
axisSize,
completed,
complexity,
config.id,
@@ -533,6 +717,11 @@ export default function Component({
setNormalizeDistance((value) => !value)
}, [playClick])
const updateShowComplexityOverlay = useCallback(() => {
playClick('complexity-overlay')
setShowComplexityOverlay((value) => !value)
}, [playClick])
const updateRadius = useCallback((value: number) => {
playClick('radius')
setRadius(clamp(value, 1, 8))
@@ -548,6 +737,11 @@ export default function Component({
setDotRadius(clamp(value, 0.05, MAX_DOT_RADIUS))
}, [playClick])
const updateAxisSize = useCallback((value: number) => {
playClick('axis-size')
setAxisSize(clamp(value, 0.25, MAX_AXIS_SIZE))
}, [playClick])
const updateComplexityMode = useCallback((nextMode: ComplexityMode) => {
playClick(`${nextMode}-mode`)
setComplexityMode(nextMode)
@@ -693,6 +887,11 @@ export default function Component({
<span>y</span>
<span>z</span>
</div>
{showComplexityOverlay && (
<div className={styles.complexityOverlay} aria-live="polite">
{complexityOverlayText}
</div>
)}
</section>
<aside className={styles.controlPanel}>
@@ -738,6 +937,14 @@ export default function Component({
<input type="checkbox" checked={normalizeDistance} onChange={updateNormalizeDistance} />
</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}>
<span>
<strong>D</strong>
@@ -810,6 +1017,30 @@ export default function Component({
</div>
</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}>
<span className={styles.kicker}>colour rule</span>
<p>
+4 -2
View File
@@ -9,6 +9,7 @@ function DevHarness() {
const params = useControls({
allowNegative: { value: true },
normalizeDistance: { value: true },
showComplexityOverlay: { value: true },
complexityMode: {
value: 'surface',
options: {
@@ -18,8 +19,9 @@ function DevHarness() {
}
},
radius: { value: 4, min: 1, max: 8, step: 0.25 },
complexity: { value: 8, min: 1, max: 96, step: 1 },
dotRadius: { value: 0.17, min: 0.05, max: 1.2, step: 0.01 }
complexity: { value: 17, min: 1, max: 96, step: 1 },
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 (
+19 -4
View File
@@ -18,6 +18,11 @@ export const metadata: GlitchComponentMetadata = {
label: 'Normalize Distance',
default: true
},
showComplexityOverlay: {
type: 'boolean',
label: 'Show Complexity Range',
default: true
},
complexityMode: {
type: 'select',
label: 'Complexity Mode',
@@ -39,7 +44,7 @@ export const metadata: GlitchComponentMetadata = {
complexity: {
type: 'range',
label: 'C',
default: 8,
default: 17,
min: 1,
max: 96,
step: 1
@@ -47,19 +52,29 @@ export const metadata: GlitchComponentMetadata = {
dotRadius: {
type: 'range',
label: 'R',
default: 0.17,
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: 8,
dotRadius: 0.17
complexity: 17,
dotRadius: 0.5,
axisSize: 1
}
}
+51 -16
View File
@@ -10,6 +10,8 @@
--gc-font-display: var(--font-display, var(--gc-font-main));
--gc-font-mono: var(--font-mono, monospace);
--gc-shell-padding: clamp(8px, 2vw, 18px);
--gc-viewport-size: 1000px;
--gc-control-width: 14em;
width: 100%;
min-height: 100dvh;
display: grid;
@@ -26,9 +28,8 @@
.frame {
position: relative;
width: min(100%, calc((100dvh - (var(--gc-shell-padding) * 2)) * 1.9));
max-height: calc(100dvh - (var(--gc-shell-padding) * 2));
aspect-ratio: 1.9 / 1;
width: calc(var(--gc-viewport-size) + var(--gc-control-width) + 2.7em);
height: calc(var(--gc-viewport-size) + 2em);
display: grid;
grid-template-rows: minmax(0, 1fr);
overflow: hidden;
@@ -65,7 +66,7 @@
z-index: 5;
left: 1em;
top: 0.9em;
width: min(25em, 34%);
width: var(--gc-control-width);
pointer-events: none;
}
@@ -92,7 +93,7 @@
z-index: 5;
left: 1em;
bottom: 1em;
width: min(25em, 34%);
width: var(--gc-control-width);
display: grid;
grid-template-columns: repeat(2, minmax(0, 1fr));
gap: 0.4em;
@@ -129,7 +130,7 @@
min-height: 0;
grid-row: 1;
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;
gap: 0.7em;
}
@@ -146,10 +147,13 @@
grid-column: 2;
min-width: 0;
min-height: 0;
width: min(100%, calc(100dvh - 5.2em));
width: var(--gc-viewport-size);
height: var(--gc-viewport-size);
aspect-ratio: 1;
justify-self: end;
overflow: hidden;
border: 3px solid #ffbd16;
box-shadow: 0 0 0 1px rgb(255 189 22 / 18%);
cursor: grab;
touch-action: none;
}
@@ -186,7 +190,8 @@
.axisLabels {
position: absolute;
inset: 0.8em;
left: 1.9em;
bottom: 2.25em;
display: flex;
align-items: flex-end;
justify-content: flex-start;
@@ -208,6 +213,25 @@
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 {
grid-column: 1;
grid-row: 1;
@@ -215,8 +239,8 @@
min-height: 0;
display: grid;
align-content: start;
gap: 0.55em;
padding: 0.7em;
gap: 0.3em;
padding: 0.46em;
margin-top: 4.2em;
margin-bottom: 6.8em;
}
@@ -250,8 +274,8 @@
.toggleRow,
.sliderRow {
display: grid;
gap: 0.4em;
padding: 0.58em;
gap: 0.28em;
padding: 0.4em;
border: 1px solid color-mix(in srgb, var(--gc-border) 72%, transparent);
background: rgb(5 10 14 / 54%);
}
@@ -337,12 +361,12 @@
display: grid;
grid-template-columns: auto minmax(0, 1fr) auto;
align-items: center;
gap: 0.45em;
gap: 0.28em;
}
.sliderControl button {
width: 1.9em;
height: 1.9em;
width: 1.55em;
height: 1.55em;
display: grid;
place-items: center;
border: 1px solid color-mix(in srgb, var(--gc-border) 78%, transparent);
@@ -350,7 +374,7 @@
color: var(--gc-text);
font: inherit;
font-family: var(--gc-font-mono);
font-size: 0.72em;
font-size: 0.68em;
font-weight: 900;
cursor: pointer;
}
@@ -426,6 +450,17 @@
justify-self: center;
}
.axisLabels {
left: 1.45em;
bottom: 1.8em;
}
.complexityOverlay {
right: 1.45em;
bottom: 4.4em;
max-width: calc(100% - 2.9em);
}
.controlPanel {
grid-column: 1;
grid-row: 1;
+1
View File
@@ -19,6 +19,7 @@ export default defineConfig(({ mode }) => ({
react: 'React',
'react-dom': 'ReactDOM'
},
entryFileNames: 'diophantine-sphere.js',
assetFileNames: 'assets/[name][extname]'
}
},