Devesh36/usb-c-phone-stand-light
A 3D-printed tilted phone stand integrating USB‑C PD power, a Qi wireless charging coil with magnetic alignment ring, indicator LEDs/light pipes, control and protection PCBs, wiring, fasteners, and rubber feet.
- Version
- 0.0.6
- License
- unset
- Stars
- 0
stand-mechanics.tsx
import { Fragment } from "react";
import { jscad } from "tscircuit";
export const dock = {
width: 92,
depth: 110,
bodyHeight: 12.2,
lidThickness: 1.8,
tilt: (70 * Math.PI) / 180,
chargerHeight: 85,
pcbX: -3,
pcbY: -29,
pcbZ: 3,
};
export const chargerFrame = {
x: 0,
y: -8 + 1 + dock.chargerHeight * Math.cos(dock.tilt),
z: 14 + 5 + dock.chargerHeight * Math.sin(dock.tilt),
};
export function BaseBody() {
return (
<>
<jscad.subtract>
<jscad.union>
<jscad.subtract>
<RoundedPlate width={92} length={110} thickness={12.2} radius={16} z={6.1} />
<RoundedPlate width={88} length={106} thickness={25} radius={14} z={14.3} />
</jscad.subtract>
{[-31, 31].flatMap((x) =>
[-10, 10].map((y) => (
<Fragment key={`${x}_${y}`}>
<jscad.cylinder
radius={3.2}
height={3}
center={[x - 3, y - 29, 1.5]}
/>
</Fragment>
)),
)}
{[-24, 24].flatMap((x) =>
[-4, 4].map((y) => (
<Fragment key={`b_${x}_${y}`}>
<jscad.cylinder
radius={4}
height={12.2}
center={[x, y - 8, 6.1]}
/>
</Fragment>
)),
)}
{[-34, 34].flatMap((x) =>
[-42, 42].map((y) => (
<Fragment key={`l_${x}_${y}`}>
<jscad.cylinder
radius={3.5}
height={12.2}
center={[x, y, 6.1]}
/>
</Fragment>
)),
)}
{[-17,17].map(x=><jscad.cylinder key={x} radius={4} height={12.2} center={[x,39,6.1]}/>)}
<jscad.cuboid size={[25, 22, 2]} center={[-32, 12, 2]} />
<jscad.cuboid size={[2, 22, 5]} center={[-20, 12, 3.5]} />
<jscad.cuboid size={[25, 2, 5]} center={[-32, 24, 3.5]} />
<jscad.cuboid size={[25, 2, 5]} center={[-32, 0, 3.5]} />
</jscad.union>
{[-31, 31].flatMap((x) =>
[-10, 10].map((y) => (
<Fragment key={`p_${x}_${y}`}>
<jscad.cylinder
radius={0.85}
height={4.8}
center={[x - 3, y - 29, 3.2]}
/>
</Fragment>
)),
)}
{[-24, 24].flatMap((x) =>
[-4, 4].map((y) => (
<Fragment key={`s_${x}_${y}`}>
<jscad.cylinder
radius={1.25}
height={11}
center={[x, y - 8, 8]}
/>
</Fragment>
)),
)}
{[-34, 34].flatMap((x) =>
[-42, 42].map((y) => (
<Fragment key={`ls_${x}_${y}`}>
<jscad.cylinder radius={0.85} height={8} center={[x, y, 10]} />
</Fragment>
)),
)}
{[-17,17].map(x=><jscad.cylinder key={x} radius={1.25} height={10} center={[x,39,9]}/>)}
<jscad.cuboid size={[6,8,5]} center={[-20,12,6.6]}/>
<jscad.translate offset={[-45,12,6.6]}><jscad.rotate angles={[0,Math.PI/2,0]}><RoundedPlate width={7} length={13} thickness={8} radius={1.3}/></jscad.rotate></jscad.translate>
{[-22, -14, -6, 2, 10, 18, 26].map((x) => (
<Fragment key={`v_${x}`}>
<jscad.cuboid size={[4, 2, 8]} center={[x, 54, 7]} />
</Fragment>
))}
</jscad.subtract>
<jscad.translate offset={[-3, -29, 3]}>
<jscad.rectangle name="pcb" reference size={[72, 28]} />
</jscad.translate>
<jscad.translate offset={[0, 0, 12.2]}>
<jscad.rectangle name="lid" reference size={[92, 110]} />
</jscad.translate>
</>
);
}
export function BaseLid() {
return (
<>
<jscad.subtract>
<RoundedPlate width={92} length={110} thickness={1.8} radius={16} z={0.9} />
{[-9,9].map(x=><jscad.cylinder key={x} radius={2.7} height={6} center={[x,-39,1]}/>)}
<jscad.cylinder radius={3} height={5} center={[20, 4, 1]} />
{[-17,17].map(x=><jscad.cylinder key={x} radius={1.7} height={6} center={[x,39,1]}/>)}
{[-24, 24].flatMap((x) =>
[-4, 4].map((y) => (
<Fragment key={`b_${x}_${y}`}>
<jscad.cylinder radius={1.7} height={6} center={[x, y - 8, 1]} />
</Fragment>
)),
)}
{[-34, 34].flatMap((x) =>
[-42, 42].map((y) => (
<Fragment key={`l_${x}_${y}`}>
<jscad.cylinder radius={1.1} height={6} center={[x, y, 1]} />
</Fragment>
)),
)}
</jscad.subtract>
<jscad.rotate angles={[Math.PI, 0, 0]}>
<jscad.rectangle name="bottom" reference size={[92, 110]} />
</jscad.rotate>
<jscad.translate offset={[0,0,1.8]}><jscad.rectangle name="support" reference size={[42,14]}/></jscad.translate>
<jscad.translate offset={[0, -8, 1.8]}>
<jscad.rectangle name="backrest" reference size={[64, 14]} />
</jscad.translate>
<jscad.translate offset={[0,-39,-1.6]}><jscad.rectangle name="indicators" reference size={[34,14]}/></jscad.translate>
<jscad.translate offset={[0,-39,1.8]}><jscad.rectangle name="light" reference size={[34,14]}/></jscad.translate>
<jscad.translate offset={[-14, -20, 1.8]}>
<jscad.rectangle name="switch" reference size={[13, 6]} />
</jscad.translate>
</>
);
}
export function Backrest() {
return (
<>
<jscad.subtract>
<jscad.union>
<jscad.roundedCuboid
size={[64, 14, 5]}
center={[0, 0, 2.5]}
roundRadius={1.5}
/>
<jscad.translate offset={[0, 1, 5]}>
<jscad.rotate angles={[dock.tilt, 0, 0]}>
<jscad.subtract>
<jscad.union>
<jscad.cuboid size={[84,108,4]} center={[0,54,0]}/>
<jscad.cylinder radius={42} height={4} center={[0,108,0]}/>
</jscad.union>
<jscad.cuboid
size={[56.6, 56.6, 12]}
center={[0, dock.chargerHeight, 0]}
/>
{[-38, 38].map((x) => (
<Fragment key={x}>
<jscad.cylinder
radius={1.25}
height={12}
center={[x, dock.chargerHeight, 0]}
/>
</Fragment>
))}
</jscad.subtract>
{[-25,25].flatMap(x=>[-25,25].map(y=><jscad.cuboid key={`${x}_${y}`} size={[8,4,1]} center={[x,dock.chargerHeight+y,-1]}/>))}
{[-26, 26].map((x) => (
<Fragment key={`c_${x}`}>
<jscad.cuboid size={[26, 4, 20]} center={[x, 0, 9]} />
<jscad.roundedCuboid
size={[26, 12, 4]}
center={[x, 6, 19]}
roundRadius={1.5}
/>
</Fragment>
))}
</jscad.rotate>
</jscad.translate>
</jscad.union>
{[-24, 24].flatMap((x) =>
[-4, 4].map((y) => (
<Fragment key={`f_${x}_${y}`}>
<jscad.cylinder radius={1.7} height={12} center={[x, y, 3]} />
</Fragment>
)),
)}
</jscad.subtract>
<jscad.rotate angles={[Math.PI, 0, 0]}>
<jscad.rectangle name="foot" reference size={[64, 14]} />
</jscad.rotate>
<jscad.translate offset={[0, 1, 5]}>
<jscad.rotate angles={[dock.tilt, 0, 0]}>
<jscad.translate offset={[0, dock.chargerHeight, 2]}>
<jscad.rectangle name="charger" reference size={[66, 66]} />
</jscad.translate>
<jscad.translate offset={[0, dock.chargerHeight, -2]}>
<jscad.rotate angles={[Math.PI, 0, 0]}>
<jscad.rectangle name="charger_rear" reference size={[66, 66]} />
</jscad.rotate>
</jscad.translate>
</jscad.rotate>
</jscad.translate>
</>
);
}
export function ChargingCover() {
return (
<>
<jscad.subtract>
<jscad.union>
<jscad.cylinder radius={42} height={1.2} center={[0, 0, 6]} />
{[-38, 38].map((x) => (
<Fragment key={x}>
<jscad.cuboid size={[7, 9, 4.3]} center={[x, 0, 4.15]} />
</Fragment>
))}
</jscad.union>
<jscad.subtract>
<jscad.cylinder radius={29.2} height={4} center={[0, 0, 7.9]} />
<jscad.cylinder radius={23.8} height={6} center={[0, 0, 7.9]} />
</jscad.subtract>
{[-38, 38].map((x) => (
<Fragment key={`h_${x}`}>
<jscad.cylinder radius={1.7} height={14} center={[x, 0, 3]} />
</Fragment>
))}
{[-38, 38].map((x) => (
<Fragment key={`head_${x}`}>
<jscad.cylinder radius={3} height={2} center={[x, 0, 5.7]} />
</Fragment>
))}
</jscad.subtract>
<jscad.translate offset={[0, 0, 2]}>
<jscad.rotate angles={[Math.PI, 0, 0]}>
<jscad.rectangle name="back" reference size={[76, 76]} />
</jscad.rotate>
</jscad.translate>
</>
);
}
function RearOutline({inset=0,thickness,z}:{inset?:number;thickness:number;z:number}){
const r=42-inset;
return <jscad.union>
<jscad.cuboid size={[84-2*inset,101-inset,thickness]} center={[0,-27.5+inset/2,z]}/>
<jscad.cylinder radius={r} height={thickness} center={[0,23,z]}/>
</jscad.union>
}
export function ChargerRearHousing(){
return <>
<jscad.subtract>
<jscad.union>
<jscad.subtract>
<RearOutline thickness={10} z={-7}/>
<RearOutline inset={2} thickness={10} z={-5.5}/>
</jscad.subtract>
{[-25,25].flatMap(x=>[-20,20].map(y=><jscad.cylinder key={`${x}_${y}`} radius={2.5} height={0.5} center={[x,y,-10.25]}/>))}
{[-38,38].map(x=><jscad.cuboid key={x} size={[7,9,8]} center={[x,0,-6]}/>)}
{[-60,-50,-40,-30,30,40,50,57].map(y=><jscad.cuboid key={y} size={[y>40?48:72,1.8,0.8]} center={[0,y,-12.4]}/>)}
<jscad.translate offset={[0,-18,-15.5]}><jscad.rotate angles={[0,Math.PI/2,0]}><jscad.cylinder radius={4.3} height={36}/></jscad.rotate></jscad.translate>
</jscad.union>
<jscad.cuboid size={[14,8,12]} center={[20,-78,-9]}/>
{[-12,-4,4,12].map(y=><jscad.cuboid key={y} size={[54,2,5]} center={[0,y,-11.5]}/>)}
{[-38,38].map(x=><jscad.cylinder key={x} radius={1.25} height={12} center={[x,0,-4]}/>)}
</jscad.subtract>
<jscad.translate offset={[0,0,-2]}><jscad.rectangle name="front" reference size={[84,140]}/></jscad.translate>
</>
}
// Separate clear light pipes above the two indicator-board LEDs.
export function LightWindow({positions=[-9,9]}:{positions?:number[]}={}) {
return <>
{positions.map(x=><jscad.colorize key={x} color="#d9dce2"><jscad.union>
<jscad.cylinder radius={2.5} height={0.6} center={[x,0,0.3]}/>
<jscad.cylinder radius={3.6} height={0.4} center={[x,0,0.7]}/>
</jscad.union></jscad.colorize>)}
<jscad.rotate angles={[Math.PI,0,0]}><jscad.rectangle name="bottom" reference size={[34,14]}/></jscad.rotate>
</>
}
export function RoundedPlate({width,length,thickness,radius,z=0}:{width:number;length:number;thickness:number;radius:number;z?:number}){
return <jscad.hull>{[-1,1].flatMap(x=>[-1,1].map(y=><jscad.cylinder key={`${x}_${y}`} radius={radius} height={thickness} center={[x*(width/2-radius),y*(length/2-radius),z]}/>))}</jscad.hull>
}
// Fixed-angle rear brace: rounded central strut and two transverse hinge barrels.
export function RearSupport(){
return <><jscad.union>
<jscad.subtract><jscad.translate offset={[0,39,23.5]}><RoundedPlate width={42} length={14} thickness={3} radius={3}/></jscad.translate>
{[-17,17].map(x=><jscad.cylinder key={x} radius={1.7} height={8} center={[x,39,24]}/>)}</jscad.subtract>
<jscad.translate offset={[0,39,26.2]}>
<jscad.rotate angles={[98.08*Math.PI/180,0,0]}>
<jscad.translate offset={[0,29.52,0]}><RoundedPlate width={24} length={59.04} thickness={3.5} radius={11}/></jscad.translate>
</jscad.rotate>
</jscad.translate>
{[[0,39,26.2],[0,30.7,84.65]].map((p,i)=><jscad.translate key={i} offset={p as [number,number,number]}><jscad.rotate angles={[0,Math.PI/2,0]}><jscad.cylinder radius={4.2} height={34}/></jscad.rotate></jscad.translate>)}
</jscad.union>
<jscad.translate offset={[0,0,22]}><jscad.rotate angles={[Math.PI,0,0]}><jscad.rectangle name="bottom" reference size={[42,14]}/></jscad.rotate></jscad.translate></>
}
export function PowerLens(){return <LightWindow positions={[-9]}/>;}
export function ChargeLens(){return <LightWindow positions={[9]}/>;}
// Black printed surround around the exposed, flush USB-C connector.
export function UsbBezel(){return <>
<jscad.translate offset={[-46.15,12,6.6]}><jscad.rotate angles={[0,Math.PI/2,0]}><jscad.subtract>
<RoundedPlate width={9} length={16} thickness={0.8} radius={2}/>
<RoundedPlate width={7} length={13} thickness={2} radius={1.3}/>
</jscad.subtract></jscad.rotate></jscad.translate>
<jscad.translate offset={[0,0,12.2]}><jscad.rotate angles={[Math.PI,0,0]}><jscad.rectangle name="datum" reference size={[92,110]}/></jscad.rotate></jscad.translate>
</>}