desk-hook.hs

A hook that can be attached to the side of a desk, possibly to hang headphones from.

Assumes a 18mm thick desk, but should be quite easy to modify for a different thickness.

desk-hook.stl

raw haskell source

#!/usr/bin/env stack
{- stack script --resolver lts-22.6 
    --package linear
    --package waterfall-cad
    --extra-dep waterfall-cad-0.3.0.1
    --extra-dep opencascade-hs-0.3.0.1
-}

-- short-description: Hook, for the Underside of a Desk 
--
-- description: A hook that can be attached to the side of a desk, possibly to hang headphones from. 
-- description: 
-- description: Assumes a 18mm thick desk, but should be quite easy to modify for a different thickness.
-- 
-- image: https://doscienceto.it/blog/photos/desk-hook-01.jpg

import qualified Waterfall
import Linear
import Data.Function ((&))
import Control.Lens ((^.), (.~ ))

hook :: Waterfall.Solid
hook = 
    let 
        w = 20
        deskT = 18
        t = 4
        l = 95
        clampA = Waterfall.centeredCube 
            & Waterfall.translate (zero & _yz .~ 0.5)
            & Waterfall.scale (V3 w 20 (deskT + t * 2))
            & Waterfall.roundFillet (t/2)
        clampB = Waterfall.centeredCube 
            & Waterfall.translate (zero & _yz .~ 0.5)
            & Waterfall.scale (V3 w l t)
            & Waterfall.roundConditionalFillet (\(s, e) -> if s ^. _xy == e ^. _xy then Just 2 else Nothing)
        clampGap = Waterfall.centeredCube 
            & Waterfall.translate (zero & _yz .~ 0.5)
            & Waterfall.scale (V3 (w*2) l deskT)
            & Waterfall.translate (zero & _y .~ t & _z .~ t)
        screwR = 1.5
        screwHole = Waterfall.centeredCylinder 
            & Waterfall.scale (V3 screwR screwR 100) 
            & (<> (Waterfall.unitCone & Waterfall.uScale 100 & Waterfall.rotate (unit _x) pi))
            & Waterfall.translate (zero & _z .~ t & _y .~ (l - w/2 - t))
            
        clamp = (clampA <> clampB) `Waterfall.difference` (clampGap <> screwHole)
        hookR = 30
        hookAH = 50
        hookPath = Waterfall.pathFrom ((zero & _y .~ (l/2 - 20)) :: V3 Double)
            [ Waterfall.bezierRelative (zero & _z .~ -hookAH/2) (zero & _z .~ -hookAH/2 & _y .~ hookR) (zero & _z .~ -hookAH & _y .~ hookR)
            , Waterfall.arcViaRelative (zero & _z .~ (-hookR) & _y .~ (-hookR)) (zero & _y .~ (-2 * hookR))
            ]
        {--hookPathB = Waterfall.pathFrom ((zero & _y .~ (l/2 + 5)) :: V3 Double)
            [ Waterfall.bezierRelative (zero & _z .~ -hookAH/2) (zero & _z .~ -hookAH/2 & _y .~ (hookR -25)) (zero & _z .~ -hookAH & _y .~ (hookR-25))
            ]--}
        hookPathC = Waterfall.pathFrom ((zero & _y .~ (l/2 + 35)) :: V3 Double)
            [ Waterfall.bezierRelative (zero & _z .~ -hookAH/2) (zero & _z .~ -hookAH/2 & _y .~ (hookR -55)) (zero & _z .~ -hookAH & _y .~ (hookR-55))
            ]
        hookTR = 12
        (_, end) = Waterfall.pathEndpoints hookPath

        hookEndcap = Waterfall.uScale hookTR Waterfall.unitSphere
            & Waterfall.translate end
        hookProfile = (Waterfall.uScale2D hookTR Waterfall.unitCircle)
        
        hookScrewR = 5
        hookScrewHole = Waterfall.centeredCylinder 
            & Waterfall.scale (V3 hookScrewR hookScrewR 400) 
            & Waterfall.translate (zero & _z .~ t & _y .~ (l - w/2 - t))
            
        hook = ((Waterfall.sweep hookPath hookProfile
            -- <> Waterfall.sweep hookPathB hookProfile
            <> Waterfall.sweep hookPathC hookProfile
            <> hookEndcap) `Waterfall.intersection` 
                ( Waterfall.centeredCube & Waterfall.scale (zero & _x .~ w & _yz .~ 400))
            ) `Waterfall.difference` hookScrewHole 
     
    in clamp <> hook

main :: IO ()
main = Waterfall.writeSolid 0.1 "desk-hook.stl" hook