Hi Karsten, Thank you for taking the time to walk me through some of the goodies that await me in thi.ng !
I do agree that matrix multiplications are the way to go for efficiently applying a bunch of geometric transformations on a number of points. Actually, I intended to do it when the nb of transforms n more points that would be displayed i.e. when going 3D. I assumed that for 2D lines, the svg display would dwarf the transformations computation (aside : any way to assess this assumption ? I know nothing of profiling Clojurescript in a browser). I also look forward to use your mix function to compute animations for 2d polygon games (a reimplementation of a game engine à la [[https://en.wikipedia.org/wiki/Another_World_%2528video_game%2529][Another World]] ). However, for the task at hand, I want to decompose the transforms just the way I composed them to make apparent the composition itself. This is for a class when I want to teach programming as composing (a sort of intro to the spirit of functional programming, even if in Python because of the curriculum). The kind of result I wanted to achieve can be seen in https://scientific-coder.github.io/fractals/resources/public/fractals.html (I took model on your https://github.com/thi-ng/demos/blob/master/geom/src/physics_demos/strands.cljs, hoping you would not mind). In order to do that in a generic way (i.e. working on any kind of transformations composition), I have to work on the "AST" of the composite transformations, and walk it to adjust the parameters according to the step (I could/should also walk the "AST" to "compile it to matrix multiplication. The thing is that I already have the "AST" as the composition of partial applications of the functions ( e.g. https://github.com/scientific-coder/fractals/blob/master/src/animations_2d/fractals.cljs#L128 ) and I can easily walk (e.g. with multimethods https://github.com/scientific-coder/fractals/blob/master/src/animations_2d/fractals.cljs#L150 ) if it exposes the data the it has to hold anyway. The cleanest way might be to use a "real" DSL (like you do with your L-system interpreter in https://gist.github.com/postspectacular/47d62cc0386452fe3f9f0465c1fb1b73#file-lsys-clj ), but I think that better extensibility/interoperability would have been achieved with a standard access to partial, comp, juxt & al. attributes. Even python, not really the weapon of choice for functional programming, gives access to the bound arguments of partial ( https://docs.python.org/3/library/functools.html#partial-objects ). Anyway, thanks again for the amazing thi.ng libraries, I'm having great fun with it, and intend to use them a lot .(Now, if only I found a way to have clojurescript code create/update svg / Webgl in my [[https://github.com/yjwen/org-reveal][org-reveal]] slides… ) I look forward to hooking students to generative programming thanks to you ☺! Best Regards, bernard On Tuesday, June 28, 2016 at 4:09:07 PM UTC+2, Karsten Schmidt wrote: > > 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 <un.compte....@gmail.com <javascript:>> > 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 clo...@googlegroups.com > <javascript:> > > Note that posts from new members are moderated - please be patient with > your > > first post. > > To unsubscribe from this group, send email to > > clojure+u...@googlegroups.com <javascript:> > > 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 clojure+u...@googlegroups.com <javascript:>. > > 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 clojure@googlegroups.com Note that posts from new members are moderated - please be patient with your first post. To unsubscribe from this group, send email to clojure+unsubscr...@googlegroups.com 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 clojure+unsubscr...@googlegroups.com. For more options, visit https://groups.google.com/d/optout.