Devesh36/facet-four
FACET Four: a USB-C macro controller with four angled keys, a volume encoder, blue LEDs, an OLED and a fitted enclosure. Includes assembled, exploded and routed PCB views. Hardware prototype; firmware required.
- Version
- 0.1.3
- License
- unset
- Stars
- 0
mechanics.tsx
import {Fragment} from 'react'
import {jscad} from 'tscircuit'
import {keys,leds,mounts} from './layout'
import {wordmarkContours} from './wordmark'
export function Cylinder({r,h,x=0,y=0,z=0}:{r:number,h:number,x?:number,y?:number,z?:number}){
const points=Array.from({length:48},(_,i)=>[r*Math.cos(i*Math.PI/24),r*Math.sin(i*Math.PI/24)] as [number,number])
return <jscad.translate offset={[x,y,z-h/2]}><jscad.extrudeLinear height={h}><jscad.polygon points={points}/></jscad.extrudeLinear></jscad.translate>
}
export function RoundBox({w,h,d,r=2,x=0,y=0,z=0}:{w:number,h:number,d:number,r?:number,x?:number,y?:number,z?:number}){return <jscad.hull>{[-1,1].flatMap(a=>[-1,1].map(b=><Fragment key={`${a},${b}`}><Cylinder r={r} h={d} x={x+a*(w/2-r)} y={y+b*(h/2-r)} z={z}/></Fragment>))}</jscad.hull>}
export function Shell(){return <jscad.colorize color="#233044"><jscad.subtract><jscad.union>
<jscad.subtract><RoundBox w={85} h={69} d={15} r={6} z={-1}/><RoundBox w={81} h={65} d={18} r={4} z={2.5}/></jscad.subtract>
{mounts.map(([x,y],i)=><Fragment key={i}><Cylinder r={2.5} h={7.7} x={x} y={y} z={-4.65}/></Fragment>)}
</jscad.union>
{mounts.map(([x,y],i)=><Fragment key={i}><Cylinder r={0.85} h={10} x={x} y={y} z={-3}/></Fragment>)}
{/* USB-C plug opening at the underside module's connector. */}
<RoundBox w={14} h={14} d={6} r={1} x={24} y={-34} z={-3.5}/>
{/* Base access exposes the module's onboard BOOT and RESET controls. */}
<RoundBox w={19} h={22} d={4} r={2} x={24} y={-15} z={-8}/>
</jscad.subtract></jscad.colorize>}
export function Lid(){return <jscad.colorize color="#dce4ed"><jscad.subtract>
<RoundBox w={85} h={69} d={1.6} r={6} z={7.3}/>
{keys.map((k,i)=><Fragment key={i}><jscad.translate offset={[k.x,k.y,7.3]}><jscad.rotate angles={[0,0,k.angle]}><RoundBox w={13} h={13} d={5} r={1}/></jscad.rotate></jscad.translate></Fragment>)}
<RoundBox w={29} h={9} d={5} r={1} x={-12} y={24} z={7.3}/>
<Cylinder r={3.6} h={5} x={24} y={12} z={7.3}/>
<jscad.translate offset={[24,12,6.9]}><jscad.rotate angles={[0,0,45]}><jscad.cuboid size={[15,16,1.4]}/></jscad.rotate></jscad.translate>
{leds.map((p,i)=><Fragment key={i}><Cylinder r={1} h={5} x={p.x} y={p.y} z={7.3}/></Fragment>)}
{mounts.map(([x,y],i)=><Fragment key={i}><Cylinder r={1.2} h={5} x={x} y={y} z={7.3}/></Fragment>)}
</jscad.subtract></jscad.colorize>}
export function KeyCap({index}:{index:number}){const k=keys[index];return <jscad.colorize color={index===0?'#2584ff':'#405775'}><jscad.translate offset={[k.x,k.y,0]}><jscad.rotate angles={[0,0,k.angle]}><jscad.union>
<RoundBox w={12.3} h={12.3} d={2.8} r={1.2} z={8.4}/>
<RoundBox w={14} h={14} d={0.5} r={1.3} z={6.25}/>
<Cylinder r={1.5} h={1.2} z={6.4}/>
</jscad.union></jscad.rotate></jscad.translate></jscad.colorize>}
export function Knob(){return <jscad.colorize color="#2584ff"><jscad.subtract><Cylinder r={10} h={12} x={24} y={12} z={19}/><Cylinder r={3.05} h={12} x={24} y={12} z={18}/>{Array.from({length:32},(_,i)=><Fragment key={i}><Cylinder r={0.55} h={10} x={24+10*Math.cos(i*Math.PI/16)} y={12+10*Math.sin(i*Math.PI/16)} z={19}/></Fragment>)}</jscad.subtract></jscad.colorize>}
export function Lenses(){return <jscad.colorize color="#5cb8ff"><jscad.union>{leds.map((p,i)=><Fragment key={i}><Cylinder r={0.8} h={6.1} x={p.x} y={p.y} z={5.05}/></Fragment>)}</jscad.union></jscad.colorize>}
export function OledBoard(){return <jscad.colorize color="#174e6a"><jscad.cuboid size={[36,12.5,1.2]} center={[-12,24,5.5]}/></jscad.colorize>}
export function OledGlass(){return <jscad.colorize color="#071923"><jscad.cuboid size={[30,10,1.1]} center={[-12,24,6.65]}/></jscad.colorize>}
export function ScreenPixels(){return <jscad.colorize color="#54d8ff"><jscad.union>{[0,1,2,3].map(i=><Fragment key={i}><jscad.cuboid size={[2.3,2+i*0.8,0.05]} center={[-23+i*3.2,23,7.225]}/></Fragment>)}<jscad.cuboid size={[8,0.45,0.05]} center={[-3,25,7.225]}/><jscad.cuboid size={[5,0.45,0.05]} center={[-4.5,23,7.225]}/></jscad.union></jscad.colorize>}
export function DisplaySupports(){return <jscad.colorize color="#233044"><jscad.union>{[-29,5].map(x=><Fragment key={x}><jscad.cuboid size={[2,10,4.1]} center={[x,24,2.85]}/></Fragment>)}</jscad.union></jscad.colorize>}
export function Brand(){return <jscad.colorize color="#2584ff"><jscad.translate offset={[23,-5,8.13]}><jscad.extrudeLinear height={0.08}><jscad.union>{wordmarkContours.map((points,i)=><jscad.polygon key={i} points={points}/>)}</jscad.union></jscad.extrudeLinear></jscad.translate></jscad.colorize>}
export function Screws(){return <jscad.colorize color="#9ca7b6"><jscad.union>{mounts.map(([x,y],i)=><Fragment key={i}><Cylinder r={1} h={13} x={x} y={y} z={2}/><jscad.subtract><Cylinder r={1.8} h={0.8} x={x} y={y} z={8.5}/><jscad.cuboid size={[2.3,0.5,0.6]} center={[x,y,8.9]}/></jscad.subtract></Fragment>)}</jscad.union></jscad.colorize>}
export function ModuleBoard(){return <jscad.colorize color="#207b70"><jscad.cuboid size={[18,23.5,1]} center={[24,-17,-1.5]}/></jscad.colorize>}
export function UsbShell(){return <jscad.colorize color="#aab4c0"><jscad.subtract><RoundBox w={8.9} h={7.5} d={3.2} r={1.2} x={24} y={-26.7} z={-3.6}/><RoundBox w={7.5} h={9} d={2} r={0.7} x={24} y={-27.7} z={-3.6}/></jscad.subtract></jscad.colorize>}
export function Harness(){return <jscad.union>{['#e15a53','#303641','#e5b940','#4b9ee7'].map((color,i)=><Fragment key={i}><jscad.colorize color={color}><jscad.hull><jscad.sphere radius={0.25} center={[-17+i*3,16,1.1]}/><jscad.sphere radius={0.25} center={[-17+i*3,19,4.8]}/></jscad.hull></jscad.colorize></Fragment>)}</jscad.union>}