#+SETUPFILE: ../../src/setup.org * Contents :toc_4_gh: - [[#thinggeombezier][thi.ng.geom.bezier]] - [[#shared-2d3d-helper-functions][Shared 2d/3d helper functions]] - [[#the-bernstein-polynomial][The Bernstein polynomial]] - [[#interpolation--curve-samplings][Interpolation & curve samplings]] - [[#automatic-curve-generation][Automatic curve generation]] - [[#constructors][Constructors]] - [[#todo-bezier2][TODO Bezier2]] - [[#pedgeaccess-pvertexaccess][PEdgeAccess, PVertexAccess]] - [[#pgraph][PGraph]] - [[#pmeshconvert][PMeshConvert]] - [[#pproximity][PProximity]] - [[#psample][PSample]] - [[#end-of-implementations][End of implementations]] - [[#todo-bezier3][TODO Bezier3]] - [[#pedgeaccess-pvertexaccess][PEdgeAccess, PVertexAccess]] - [[#pgraph][PGraph]] - [[#pmeshconvert][PMeshConvert]] - [[#pproximity][PProximity]] - [[#psample][PSample]] - [[#end-of-implementations][End of implementations]] - [[#complete-namespace-definition][Complete namespace definition]] * thi.ng.geom.bezier ** Shared 2d/3d helper functions *** The Bernstein polynomial #+BEGIN_SRC clojure :noweb-ref helpers (defn bernstein [t] (let [it (- 1.0 t) it2 (* it it) t2 (* t t)] [(* it it2) (* 3 (* t it2)) (* 3 (* it t2)) (* t t2)])) #+END_SRC *** Interpolation & curve samplings #+BEGIN_SRC clojure :noweb-ref helpers (defn interpolate [[a b c d] t] (let [it (- 1.0 t), it2 (* it it), t2 (* t t)] (->> (g/* a (* it it2)) (g/madd b (* 3 (* t it2))) (g/madd c (* 3 (* it t2))) (g/madd d (* t t2))))) (defn sample-segment [seg res] (map #(interpolate seg %) (butlast (m/norm-range res)))) (defn sample-with-res [res include-last? points] (let [ls (->> points (d/successive-nth 4) (take-nth 3) (mapcat #(sample-segment % res)))] (if include-last? (concat ls [(last points)]) ls))) #+END_SRC *** Automatic curve generation The following two functions allow us to compute a bezier curve which passes through all given points and automatically computes the required control points. This only works for non-closed curves, though. #+BEGIN_SRC clojure :noweb-ref helpers (defn- find-cpoints* [ctor tight points] (let [np (count points) invt (/ 1.0 tight) points (vec points) c1 (g/subm (points 2) (first points) tight) [bi coeff] (reduce (fn [[bi coeff] i] (let [b (/ -1.0 (+ invt (peek bi))) c (peek coeff) p (get points (dec i)) q (get points (inc i))] [(conj bi b) (conj coeff (g/* (g/- q p c) (- b)))])) [[0 (- tight)] [(ctor) c1]] (range 2 (dec np)))] (reduce (fn [delta i] (assoc delta i (g/madd (delta (inc i)) (bi i) (coeff i)))) (vec (repeatedly np ctor)) (range (- np 2) 0 -1)))) (defn auto-spline* [points cpoints] (concat (->> cpoints (d/successive-nth 2) (interleave (d/successive-nth 2 points)) (partition 2) (mapcat (fn [[[p q] [dp dq]]] [p (g/+ p dp) (g/- q dq)]))) [(last points)])) #+END_SRC ** Constructors #+BEGIN_SRC clojure :noweb-ref ctors (defn bezier2 [points] (thi.ng.geom.types.Bezier2. (mapv vec2 points))) (defn auto-spline2 ([points] (->> points (find-cpoints* vec2 0.25) (auto-spline* points) (thi.ng.geom.types.Bezier2.))) ([points closed?] (auto-spline2 (if closed? (conj (vec points) (first points)) points)))) (defn bezier3 [points] (thi.ng.geom.types.Bezier3. (mapv vec3 points))) (defn auto-spline3 ([points] (->> points (find-cpoints* vec3 0.25) (auto-spline* points) (thi.ng.geom.types.Bezier3.))) ([points closed?] (auto-spline3 (if closed? (conj (vec points) (first points)) points)))) #+END_SRC ** TODO Bezier2 #+BEGIN_SRC clojure :noweb-ref impl2 (extend-type thi.ng.geom.types.Bezier2 #+END_SRC *** PEdgeAccess, PVertexAccess #+BEGIN_SRC clojure :noweb-ref impl2 g/PVertexAccess (vertices ([_] (g/vertices _ *resolution*)) ([_ res] (sample-with-res res true (:points _)))) g/PEdgeAccess (edges ([_] (d/successive-nth 2 (g/vertices _ *resolution*))) ([_ res] (d/successive-nth 2 (g/vertices _ res)))) #+END_SRC *** PGraph #+BEGIN_SRC clojure :noweb-ref impl2 g/PGraph (vertex-neighbors [_ v] (d/neighbors v (g/vertices _))) (vertex-valence [_ v] (let [points (g/vertices _)] (if-let [p (d/neighbors v points)] (if (or (m/delta= p (first points)) (m/delta= p (peek points))) 1 2) 0))) #+END_SRC *** PMeshConvert #+BEGIN_SRC clojure :noweb-ref impl2 g/PMeshConvert (as-mesh [_ {:keys [res dist profile] :as opts}] (let [points (if dist (g/sample-uniform _ dist true) (g/vertices _ (or res *resolution*)))] (ptf/sweep-mesh (map vec3 points) profile opts))) #+END_SRC *** PProximity #+BEGIN_SRC clojure :noweb-ref impl2 :noweb yes <> #+END_SRC *** PSample #+BEGIN_SRC clojure :noweb-ref impl2 :noweb yes g/PSample (point-at [_ t] (gu/point-at t (:points _) nil)) (random-point [_] (gu/point-at (m/random) (:points _) nil)) (random-point-inside [_] (g/random-point _)) (sample-uniform [_ udist include-last?] (gu/sample-uniform udist include-last? (g/vertices _))) #+END_SRC *** End of implementations :noexport: #+BEGIN_SRC clojure :noweb-ref impl2 ) #+END_SRC ** TODO Bezier3 #+BEGIN_SRC clojure :noweb-ref impl3 (extend-type thi.ng.geom.types.Bezier3 #+END_SRC *** PEdgeAccess, PVertexAccess #+BEGIN_SRC clojure :noweb-ref impl3 g/PVertexAccess (vertices ([_] (g/vertices _ *resolution*)) ([_ res] (sample-with-res res true (:points _)))) g/PEdgeAccess (edges ([_] (d/successive-nth 2 (g/vertices _ *resolution*))) ([_ res] (d/successive-nth 2 (g/vertices _ res)))) #+END_SRC *** PGraph #+BEGIN_SRC clojure :noweb-ref impl3 g/PGraph (vertex-neighbors [_ v] (d/neighbors v (g/vertices _))) (vertex-valence [_ v] (let [points (g/vertices _)] (if-let [p (d/neighbors v points)] (if (or (m/delta= p (first points)) (m/delta= p (peek points))) 1 2) 0))) #+END_SRC *** PMeshConvert #+BEGIN_SRC clojure :noweb-ref impl3 g/PMeshConvert (as-mesh [_ {:keys [res dist profile] :as opts}] (let [points (if dist (g/sample-uniform _ dist true) (g/vertices _ (or res *resolution*)))] (ptf/sweep-mesh points profile opts))) #+END_SRC *** PProximity #+BEGIN_SRC clojure :noweb-ref impl3 :noweb yes <> #+END_SRC *** PSample #+BEGIN_SRC clojure :noweb-ref impl3 :noweb yes g/PSample (point-at [_ t] (gu/point-at t (:points _) nil)) (random-point [_] (gu/point-at (m/random) (:points _) nil)) (random-point-inside [_] (g/random-point _)) (sample-uniform [_ udist include-last?] (gu/sample-uniform udist include-last? (g/vertices _))) #+END_SRC *** End of implementations :noexport: #+BEGIN_SRC clojure :noweb-ref impl3 ) #+END_SRC ** Complete namespace definition #+BEGIN_SRC clojure :tangle ../babel/src/thi/ng/geom/bezier.cljc :noweb yes :mkdirp yes :padline no (ns thi.ng.geom.bezier (:require [thi.ng.geom.core :as g :refer [*resolution*]] [thi.ng.geom.core.vector :as v :refer [vec2 vec3]] [thi.ng.geom.core.utils :as gu] [thi.ng.geom.types] [thi.ng.geom.types.utils :as tu] [thi.ng.geom.types.utils.ptf :as ptf] [thi.ng.dstruct.core :as d] [thi.ng.math.core :as m])) <> <> <> <> #+END_SRC