Hi Bernard, firstly, thanks for the kind words! I think you're overcomplicating your problem and the extreme use of comp & partial would not help making things run v.smoothly (obviously depending on number of generated points / line segments). Instead I think you should look into matrix based transformations, which, once constructed, have an O(1) cost, no matter how complex the transformation. E.g. With http://thi.ng/geom you can create a matrix, encoding a combined translation, rotation and scale op like this:
;; assumes [thi.ng/geom "0.0.1173-SNAPSHOT"] (require '[thi.ng.geom.core :as g] '[thi.ng.geom.matrix :as mat] '[thi.ng.math.core :as m]) ;; for 2D: M32 is a 3x2 identity matrix (def tx (-> mat/M32 (g/translate [10 20]) (g/rotate Math/PI) (g/scale 10))) ;; for 3D: M44 is 4x4 identity matrix (def tx (-> mat/M44 (g/translate [10 20 30]) (g/rotate-x m/HALF_PI) (g/scale 10))) Then to transform a point: (g/transform-vector tx [x y z]) To reverse the transformation, simply apply the matrix inverse to a transformed point: (g/transform-vector (m/invert tx) [x' y' z']) This approach also makes it trivial to create animations, simply keep a reference to the untransformed elements, then compute a matrix for each animation frame, transform & visualize them... If you just want to tween between a two sets of elements (e.g. the original and transformed elements), you can use the `mix` protocol function defined for all vector types (2D/3D) in thi.ng/geom: (require '[thi.ng.geom.vector :as v]) (defn tween-points [src dest t] (map (fn [a b] (m/mix a b t)) src dest)) (tween-points [(v/vec2 0 0) (v/vec2 100 50)] [(v/vec2 100 200) (v/vec2 200 100)] 0.5) ;; ([50.0 100.0] [150.0 75.0]) Hth! K. On 28 June 2016 at 11:17, bernardH <[email protected]> wrote: > > Hi, > > Inspired by Karsten Schmidt's amazing work[0], I'm thinking about porting > some generative > code from python to Clojure[script]. > > However, I would like to use the (expected) performance boost (from python > turtle module) to add some animation effects. I believe my current code is > quite clean > and would like to avoid the second-system effect [1] > > I currently have some very generic fractal generators taking some geometric > transforms as arguments. > Those geometric transforms are composition of curryfied primitive operations > (rotate, add, zoom), as in > (def transfo (compose (partial rotate pi) (partial add [0. 1.]) (partial > zoom (/ 1 3)))) > > My wish would be to be able to compute smooth animations by "scaling" > smoothly > the composite transformations between identity (no transformation) and the > full > (original) transformation (if fact I would also use the opposite transforms, > the > one that would compose to identity). I would do so by scaling all the > parameters involved > with a scaling factor in [0 …1] ([0…-1] for opposite transforms) either by > multiplication (for "additive" transformations like rotate and add) or by > exponentiation (for "multiplicative" transformations like zoom). > > "Half" of the above "transfo" would thus be : > (def transfo_half (comp (partial rotate (deep_multiply 0.5 pi)) (partial add > (deep_multiply 0.5 [0. 1.])) (partial zoom (Math.pow (/ 1 3) 0.5)))) > > with > > (defn deep_f [f x] (if (seq? x) (map deep_f x) (f x))) > > (defn deep_multiply [k x] (deep_f (partial * k) x)) > > to unify meutiplication because for additive functions, some params are > vectors (for add) while others are scalars (for rotate). > > and the opposite of the original full transformation would be : > (def transfo_inv (comp (partial rotate (deep_multiply -1 pi)) (partial add > (deep_multiply -1 [0. 1.])) (partial zoom (Math.pow (/ 1 3) -1)))) > > > The ideal solution for me would be to be able to write a "scale" function so > that : > > (def transfo_half (scale 0.5 transfo)) > (def transfo_inv (scale -1 transfo)) > > The scale function could look like > > (def scale [k f] > (condp is f > comp (apply comp (map (partial scale k) (args f))) > partial (let[ [ fun bound] (args f)] (if (or (is f add) (is f rotate)) > (partial f (deep_multiply k bound )) (if (is f zoom) (partial f (Math.pow > bound k)) f))) > f) > ) > > But that would require some help from the values returned by comp and > partial : > - the ability to identify them as such (above with an "is" function) > - the ability to access the bound values passed to them as arguments (above > with an "args" function) > > Of course, I would have to shadow clojure.core/comp and clojure.core/partial > to > add those functionalities. I suppose I would have to create records and > protocols. However, I find this a bit cumbersome and thought that there > might be > a simpler way to achieve my goal of keeping the transformation generation > separate from the animation code that must scale those functions. > Piggiebacking metadata might be easier if more hackish. > > Best would be to be able to write a higher order function (or macro) like: > > (def comp (with-is-args comp)) > (def partial (with-is-args partial)) > > Maybe using metadata on the elementary transformation functions (rotate, > add, > zoom) to indicate that comp and partial should have specific results so that > the generic case would not be changed. > > But that is getting over my clojure-foo ☹. > How would you go about it ? I have a hard time to believe I'd be the first > to want to modify composed and curryfied functions. > > Any piece of advice would be greatly appreciated ! > > Best Regards, > > b. > > [0] > https://medium.com/@thi.ng/workshop-report-generative-design-with-clojure-7d6d8ea9a6e8#.w6te331y0 > [1] https://en.wikipedia.org/wiki/Second-system_effect . > > -- > You received this message because you are subscribed to the Google > Groups "Clojure" group. > To post to this group, send email to [email protected] > Note that posts from new members are moderated - please be patient with your > first post. > To unsubscribe from this group, send email to > [email protected] > For more options, visit this group at > http://groups.google.com/group/clojure?hl=en > --- > You received this message because you are subscribed to the Google Groups > "Clojure" group. > To unsubscribe from this group and stop receiving emails from it, send an > email to [email protected]. > For more options, visit https://groups.google.com/d/optout. -- Karsten Schmidt http://postspectacular.com | http://thi.ng | http://toxiclibs.org -- You received this message because you are subscribed to the Google Groups "Clojure" group. To post to this group, send email to [email protected] Note that posts from new members are moderated - please be patient with your first post. To unsubscribe from this group, send email to [email protected] For more options, visit this group at http://groups.google.com/group/clojure?hl=en --- You received this message because you are subscribed to the Google Groups "Clojure" group. To unsubscribe from this group and stop receiving emails from it, send an email to [email protected]. For more options, visit https://groups.google.com/d/optout.
