From 645dff2f06f771c85ece43596188a7fd88695192 Mon Sep 17 00:00:00 2001 From: Phil Hagelberg Date: Fri, 23 Aug 2019 17:55:47 -0700 Subject: [PATCH] First pass at 4th deck prototype. --- case/case.rkt | 3 + case/deck.rkt | 196 ++++++++++++++++++++++++++++++++++++++++++++++++++ 2 files changed, 199 insertions(+) create mode 100644 case/deck.rkt diff --git a/case/case.rkt b/case/case.rkt index 5f94b76..f20aa3e 100644 --- a/case/case.rkt +++ b/case/case.rkt @@ -178,6 +178,9 @@ (display-xml (layer layer-name) out)) #:exists 'replace)) +;; live-reload with: +;; qiv --watch case2-switch.svg + (write-out-layer 'switch) (write-out-layer 'bottom) (write-out-layer 'spacer) diff --git a/case/deck.rkt b/case/deck.rkt new file mode 100644 index 0000000..4265e7c --- /dev/null +++ b/case/deck.rkt @@ -0,0 +1,196 @@ +#lang racket +;; Atreus 2 deck design +;; Copyright © 2019 Phil Hagelberg and contributors +;; released under the GPLv3 or later + +;; TODO: +;; * port notch on bottom plate and spacer +;; * top layer + +(require xml) + +;; glowforge uses 96 dpi, 25.4 mm in an inch +(define scale (/ 96 25.4)) +(define width 260) +(define height 232) + + +(define cols 6) ; per hand +(define rows 4) +(define angle (degrees->radians 10)) +(define corner-radius 6.0) + +(define alps-switch-width 15.887) +(define alps-switch-height 13.087) +(define cherry-switch-width 13.62) +(define cherry-switch-height 13.72) +(define cherry? true) +(define switch-height (if cherry? cherry-switch-height alps-switch-height)) +(define switch-width (if cherry? cherry-switch-width alps-switch-width)) + +(define switch-spacing 19.0) + +(define screw-radius 1.4) ; for M3 screws + kerf + +(define side-screw-distance (* switch-spacing rows)) +(define bottom-screw-distance (* switch-spacing cols)) +(define left corner-radius) +(define bottom 95) ; outer bottom +(define left-top (+ left (* side-screw-distance (sin angle)))) +(define top (- bottom (* side-screw-distance (cos angle)))) +(define right (- width corner-radius)) +(define right-top (- right (* side-screw-distance (sin angle)))) +(define mid-bottom (+ bottom (* bottom-screw-distance (sin angle)) -3)) +(define mid-offset 25) +(define mid-x (/ width 2)) +(define mid-left (- mid-x mid-offset)) +(define mid-right (+ mid-x mid-offset)) + +(define upper-height 75) +(define left-upper-top (+ left-top (* upper-height (sin angle)))) +(define right-upper-top (- right-top (* upper-height (sin angle)))) +(define upper-top (- top (* upper-height (cos angle)))) + +(define hull-coords (list (list right-upper-top upper-top) + (list right-top top) + (list right bottom) + (list mid-right mid-bottom) + (list mid-left mid-bottom) + (list left bottom) + (list left-top top) + (list left-upper-top upper-top))) + +;;; screws +(define screws + `(g () ,@(for/list ([s (append (take hull-coords 3) + ;; the bottom middle has only one screw but + ;; two hull positions + (list (list (/ width 2) mid-bottom)) + (drop hull-coords 5))]) + `(circle ((r ,(number->string screw-radius)) + (cx ,(number->string (first s))) + (cy ,(number->string (second s)))))))) + +;;; outline +(define outline-coords (append hull-coords (take hull-coords 2))) + +(define (to-next-screw? theta current-screw) + (let* ([current (list-ref outline-coords current-screw)] + [cx (first current)] [cy (second current)] + [next (list-ref outline-coords (add1 current-screw))] + [nx (first next)] [ny (second next)] + [dx (- nx cx)] [dy (- ny cy)] + [next-theta (- (radians->degrees (atan dy dx)))]) + (= (floor (modulo (floor next-theta) 180)) + (floor (modulo (- theta 90) 180))))) + +;; trace the outline by going from screw to screw until you've gone full-circle +(define (outline-points coords theta current-screw) + (if (< -360 (- theta 90) 360) + (let* ([current (list-ref outline-coords current-screw)] + [sx (first current)] [sy (second current)] + [x (+ sx (* (cos (degrees->radians theta)) corner-radius))] + [y (- sy (* (sin (degrees->radians theta)) corner-radius))] + [coords (cons (format "~s,~s" x y) coords)]) + (if (to-next-screw? theta current-screw) + (begin (printf "~a~n" current) + (outline-points coords theta (add1 current-screw))) + (outline-points coords (sub1 theta) current-screw))) + coords)) + +(define port-depth 8) + +(define port-curve (list (format "~s,~s" mid-right (- top corner-radius)) + (format "~s,~s" (+ mid-x corner-radius) + (+ top port-depth + (- corner-radius))) + (format "~s,~s" (- mid-x corner-radius) + (+ top port-depth + (- corner-radius))) + (format "~s,~s" mid-left (- top corner-radius)))) + +(define (outline with-port?) + `(polygon ((points ,(string-join (let ((noport (outline-points '() 90 0))) + (if with-port? + (append noport port-curve) + noport))))))) + +;;; switches + +(define column-offsets `(8 5 0 6 11 ,(+ 8 switch-spacing switch-spacing))) + +(define (switch row col) + (let* ([x (* (+ 1 col) switch-spacing)] + [y (+ (list-ref column-offsets col) (* switch-spacing row))]) + `(rect ((height ,(number->string switch-height)) + (width ,(number->string switch-width)) + (x ,(number->string x)) + (y ,(number->string y)))))) + +(define hand-height (+ (* switch-spacing rows) (- switch-spacing switch-height) + (list-ref column-offsets 0))) +(define switch-x-offset -6.5) +(define switch-y-offset (- bottom hand-height -3.5)) + +(define switches + `(g ((transform ,(format "translate(~s, ~s) rotate(~s, ~s, ~s)" + switch-x-offset switch-y-offset + (radians->degrees angle) + 0 hand-height))) + ,@(for/list ([col (in-range cols)] + #:when true + [row (if (= 5 col) '(0 1) (in-range rows))]) + (switch row col)))) + +(define switches-right + `(g ((transform ,(format "translate(~s,~s) scale(-1, 1)" width 0))) + ,switches)) + +(define logo-doc (call-with-input-file "logo-fragment.svg" read-xml)) + +(define pcb-doc (call-with-input-file "pcb-fragment.svg" read-xml)) + +(define (layer plate) + (document (prolog '() false '()) + (xexpr->xml + `(svg ((xmlns:svg "http://www.w3.org/2000/svg") + (height ,(number->string (* height scale))) + (width ,(number->string (* width scale)))) + ,@(if (eq? plate 'switch) + `((g ((transform ,(format "translate(436, ~s)" + (+ (* scale upper-height) 115))) + (stroke "red")) + ,(xml->xexpr (document-element logo-doc)))) + '()) + ,@(if (eq? plate 'spacer) + (list (xml->xexpr (document-element pcb-doc))) + (list)) + (g ((transform ,(format "scale(~s, ~s) translate(0, ~s)" + scale scale upper-height)) + (stroke-width "1") + (stroke "black") + (fill-opacity "0")) + ,screws + ,(outline (not (eq? plate 'switch))) + ,@(if (eq? plate 'switch) + (list switches switches-right) + (list))))) + '())) + +;; to laser cut these, you have to open them in inkscape, then save, +;; and then upload; for some reason the glowforge crashes if you try to cut +;; them directly. + +(define (write-out-layer layer-name) + (call-with-output-file (format "deck-~a.svg" (symbol->string layer-name)) + (lambda (out) + (display "" out) + (display-xml (layer layer-name) out)) + #:exists 'replace)) + +;; live-reload with: +;; qiv --watch deck-switch.svg + +(write-out-layer 'switch) +;; (write-out-layer 'bottom) +;; (write-out-layer 'spacer) -- 2.39.2