Fork me on GitHub
#clojure-art
<
2023-02-25
>
Daniel Craig01:02:52

I'm having a lot of fun generating Lego models with Clojure and then rendering them with http://Stud.io, here's my two favorite gifs that I've made so far.

🎨 6
🆒 11
🤯 2
Mattias10:02:00

That is fantastic. Had no idea there were tools to help (but of course there are, on the internet… 🙂 ) Care to explain the process a bit more?

Gerome16:02:43

Yeah, I would also be really interested in how this came about.

Daniel Craig17:02:08

I'm really flattered! So the way this is done is that I wrote a little program that writes an https://www.ldraw.org/article/218.html file, the details of the format are in the link but basically it's the open standard for modeling with Legos. I write each part as its own line in the ldraw file, (keeping in mind the dimensions of the lego brick, 20 x 20 x 24 Lego Draw Units). In the conventions of the ldraw file format, one part out of a lego model looks like this:

1 4 0.0 0.0 0.0 0.0 0.0 1.0 0.0 1.0 0.0 -1.0 0.0 0.0 18654.dat
where which is generalized as
1 <colour> x y z a b c d e f g h i <file>
The leading 1 declares that this is a lego part, the color is represented by an integer, and the x y z a b c d e f g h i represent a transformation matrix in either of the following forms:
/ a d g 0 \   / a b c x \
| b e h 0 |   | d e f y |
| c f i 0 |   | g h i z |
\ x y z 1 /   \ 0 0 0 1 /
And here is my code snippet that does the magic:

Daniel Craig17:02:31

(ns greenbeam.core
  (:gen-class)
  (:require [clojure.string :as str]
            [ :as io]
            [clojure.core.matrix :as matrix]))

(defn part [color [[a b c x] [d e f y] [g h i z] _] file]
  [1 color x y z a b c d e f g h i file])

(defn pin-connector-perpendicular-3L-with-4-pins
  [color x y z a b c d e f g h i]
  (part color x y z a b c d e f g h i "3005.dat"))

(def origin (matrix/identity-matrix 4))

;; 90 degree in y direction
(def xm1 [[0 0 1 0]
          [0 1 0 0]
          [-1 0 0 0]
          [0 0 0 1]])

;; 90 degree in x direction
(def xm2 [[1 0 0 0]
          [0 0 -1 0]
          [0 1 0 0]
          [0 0 0 1]])

(def intervals-of-twenty (iterate #(+ % 20) 0))

(defn xmf [x y z]
  [[1 0 0 x]
   [0 1 0 y]
   [0 0 1 z]
   [0 0 0 1]])

(defn brick18654
  [color xm]
  (part color xm "18654.dat")
  )

(defn get-locations [n]
  (for [x (take n intervals-of-twenty)
        y (take n intervals-of-twenty)
        z (take n intervals-of-twenty)]
    (xmf x y z)))

(defn write-file
  [filename & _]
  (let [locations (get-locations 30)
        rotated-locations (map #(matrix/transform % (rand-nth [xm1 xm2 origin])) locations)
        parts (map #(brick18654 %1 %2) (repeatedly #(rand-int 7)) rotated-locations)]
    (spit filename (str/join " " ["0" "FILE" filename "\n"]))
    (doseq [part parts]
      (spit filename (str/join " " part) :append true)
      (spit filename "\n" :append true))))

(defn -main
  "I don't do a whole lot ... yet."
  [& args]
  (let [filename "model.ldr"]
    (write-file filename)))

Daniel Craig17:02:29

As implemented, this will generate quite a large (30x30) block of 1L techic liftarms in random colors.

Daniel Craig17:02:23

Then I open the .ldr file with http://stud.io and I render it

Daniel Craig17:02:38

The code above is a bit crufty, not really ready for the gaze of the world, but such as it is, I give it to you 😁

Gerome17:02:40

Wow! Thanks! We'll go forth and create the equivalent of Mandalas in Lego now 😄

Daniel Craig18:02:54

That's a great idea

Daniel Craig18:02:23

Maybe there's demand for a declarative Clojure DSL for lego creations

2
Daniel Craig18:02:08

I'm interested in writing a technic-focused CAD tool, with physics and stuff

Mattias12:02:50

Thanks so much for writing it up!

Hendursaga21:03:59

@USDPTD3FY some time ago, I had a shelved project that was basically a limited LEGO editor that was modal and keyboard-focused and could be scripted, as well!

❤️ 2
😱 2
Daniel Craig02:03:58

@U04S798T3GW that sounds awesome! Is that project open source?

Hendursaga17:03:52

No, it's not, sorry! I didn't get too far beyond the conceptual, but I did find a good base to start on: there's this ANCIENT program buried in LDraw called LEdit and then there's also https://deeice.github.io/ldglite/

Hendursaga17:03:44

My idea was, until I became more proficient at 3D rendering and such, to use the C rendering part of the above and simply pass the ldraw output generated by the Lisp side along each time. I couldn't settle on which Lisp to use.

Daniel Craig21:02:08

Here's another, not quite as favorite but still pretty cool

🆒 6