summaryrefslogtreecommitdiff
path: root/src/dactyl_cave/text.clj
diff options
context:
space:
mode:
authorMatt Adereth <adereth@gmail.com>2015-03-06 15:26:01 -0800
committerMatt Adereth <adereth@gmail.com>2015-03-06 15:26:01 -0800
commitd466da8a90a7678d844ed1f2b8c114e0fcbde5d9 (patch)
tree3cbf6dc99ddc43f2e79454e08c8d1146079b468f /src/dactyl_cave/text.clj
parentc8465079bae87c56123adf01c4af9a57bec13f8c (diff)
Initial commit
Diffstat (limited to 'src/dactyl_cave/text.clj')
-rw-r--r--src/dactyl_cave/text.clj134
1 files changed, 134 insertions, 0 deletions
diff --git a/src/dactyl_cave/text.clj b/src/dactyl_cave/text.clj
new file mode 100644
index 0000000..db37bfd
--- /dev/null
+++ b/src/dactyl_cave/text.clj
@@ -0,0 +1,134 @@
+(ns dactyl-cave.text
+ (:use [scad-clj.scad])
+ (:use [scad-clj.model])
+ (:import (java.awt Font RenderingHints)
+ (java.awt.font FontRenderContext)
+ (java.awt.geom PathIterator)))
+
+(def segment-type
+ {PathIterator/SEG_CLOSE :close
+ PathIterator/SEG_CUBICTO :cubic-to
+ PathIterator/SEG_LINETO :line-to
+ PathIterator/SEG_MOVETO :move-to
+ PathIterator/SEG_QUADTO :quad-to})
+
+;; How many points are specified for each segment type
+(def segment-length
+ {PathIterator/SEG_CLOSE 0
+ PathIterator/SEG_CUBICTO 3
+ PathIterator/SEG_LINETO 1
+ PathIterator/SEG_MOVETO 1
+ PathIterator/SEG_QUADTO 2})
+
+(defn path-iterator->segments
+ "Converts a PathIterator into a sequence of segments of the form [segment-type [& points]]"
+ [^PathIterator path-iterator]
+ (if (not (.isDone path-iterator))
+ (let [coords (double-array (* 2 (apply max (vals segment-length))))
+ segment-code (.currentSegment path-iterator coords)]
+ (cons [(segment-type segment-code)
+ (take (segment-length segment-code) (partition 2 coords))]
+ (lazy-seq (path-iterator->segments (doto path-iterator (.next))))))))
+
+(defn quad->fn
+ "Returns the parametric control equation f(t), 0 <= t <= 1
+for the quadratic interpolation of 3 points."
+ [cp p1 p2]
+ (fn [t]
+ (letfn [(interp [a b c] (+ (* (Math/pow (- 1 t) 2) a)
+ (* 2 t (- 1 t) b)
+ (* (Math/pow t 2) c)))]
+ [(apply interp (map first [cp p1 p2]))
+ (apply interp (map second [cp p1 p2]))])))
+
+(defn cubic->fn
+ "Returns the parametric control equation f(t), 0 <= t <= 1
+for the cubic interpolation of 4 points."
+ [cp p1 p2 p3]
+ (fn [t]
+ (letfn [(interp [a b c d]
+ (+ (* (Math/pow (- 1 t) 3) a)
+ (* 3 t (Math/pow (- 1 t) 2) b)
+ (* 3 (Math/pow t 2) (- 1 t) c)
+ (* (Math/pow t 3) d)))]
+ [(apply interp (map first [cp p1 p2 p3]))
+ (apply interp (map second [cp p1 p2 p3]))])))
+
+(defn segments->lines
+ "Takes a sequence of segments of the form [segment-type [& points]]
+and transforms each segment into a sequence of interpolated points"
+ [segments]
+ (reductions (fn [prev-line-points [segment-type control-points]]
+ #_(println segment-type)
+ (condp = segment-type
+ :move-to control-points
+ :line-to control-points
+ :quad-to (map (apply quad->fn
+ (last prev-line-points)
+ control-points)
+ (range 1/10 11/10 1/10))
+ :cubic-to (map (apply cubic->fn
+ (last prev-line-points)
+ control-points)
+ (range 1/10 11/10 1/10))))
+ (last (rest (first segments)))
+ (rest segments)))
+
+
+(defn path2d [points]
+ (let [path (doto (java.awt.geom.Path2D$Double.)
+ (.moveTo (-> points first first)
+ (-> points first second)))]
+ (doseq [point (rest points)]
+ (.lineTo path (first point) (second point)))
+ path))
+
+(defn split-intersecting [paths]
+ (let [polygons (map path2d paths)
+ starting-points (map first paths)]
+ (reduce (fn [acc path]
+ (let [polygon (path2d path)]
+ (if (some #(.contains % (-> path first first) (-> path first second))
+ (:polygons acc))
+ (merge-with concat acc
+ {:difference [path]})
+ (merge-with concat acc
+ {:polygons [polygon]
+ :union [path]}))))
+ {:polygons []
+ :union []
+ :difference []}
+ paths)))
+
+(defn text-polygon [font size text]
+ (let [frc (FontRenderContext. nil
+ RenderingHints/VALUE_TEXT_ANTIALIAS_DEFAULT
+ RenderingHints/VALUE_FRACTIONALMETRICS_DEFAULT)
+ path-iter (-> (Font. font Font/PLAIN size)
+ (.createGlyphVector frc text)
+ (.getOutline)
+ (.getPathIterator nil))
+ paths (->> (path-iterator->segments path-iter)
+ (partition-by #(= (first %) :close))
+ (take-nth 2)
+ (map segments->lines)
+ (map flatten)
+ (map (partial partition 2)))
+ split-paths (split-intersecting paths)]
+ (difference
+ (apply union (map polygon (:union split-paths)))
+ (apply union (map polygon (:difference split-paths))))))
+
+(spit "/Users/madereth/text.scad"
+ (write-scad
+ (->> "Anonymous Pro" #_(str (java.util.Date.))
+ (text-polygon "Anonymous Pro" 12)
+ (extrude-linear {:height 50 :twist 0 :convexity 0}))))
+
+
+
+(spit "/Users/madereth/text.scad"
+ (write-scad
+ (->> "Anonymous Pro" #_(str (java.util.Date.))
+ (text-polygon "Anonymous Pro" 12)
+ (extrude-linear {:height 12 :twist 0 :convexity 0})))) \ No newline at end of file