From: LuisGLopez
Subject: Hypercube in lisp (newbie)
Date: 
Message-ID: <1124585532.586035.148190@g43g2000cwa.googlegroups.com>
Hi!

I'm trying to learn some lisp. I've been reading this newsgroup,
downloaded some books ("Practical common lisp" and "On lisp") and
tutorials, and tried to produce some minor programs. Of course, I still
don't "get it", but I'm doing my best. :-)

Now I think that I want to learn more, and I thought that one of the
best ways would be to solve one problem that I like very much: the
representation of a hypercube of 4-dimensions, and its rotations on
different planes. I base my algorithm in the article of A.K.Dewney in
"Scientific American".

I think that I just could make the first step: making the 16 vertices,
and being able to rotate it. Now, I don't know if my code is "proper
lisp" (I mean... I'm SURE it isn't :) )... would you be so kind to show
me the errors, both in style, performance, and whatever you consider
useful for me to learn?

This is the code so far:

(defvar *posicion-actual*
  (make-array '(16 4)))

(defconstant +rad+ (/ pi 180.0))

(defun iniciar-posicion () "Establish initial positions of vertices"
  (dotimes (i 16)
    (let ((binario (format nil "~4,'0b" i)))
    (dotimes (j 4)
      (setf (aref *posicion-actual* i j)
	    (if (string= "0" (aref binario j)) -1 1))))))

(defun traducir (eje)
  (cond
   ((string= eje "x") 0)
   ((string= eje "y") 1)
   ((string= eje "z") 2)
   ((string= eje "w") 3)))

(defun rotar (eje1 eje2 grados) "Gives the hypercube a rotation of
'grados' degrees in the plane 'eje1-eje2'"
  (setf eje1 (traducir eje1) eje2 (traducir eje2))
  (if (> eje1 eje2) (rotatef eje1 eje2))
  (let ((rot (make-array '(4 4)))
	(grados (* grados +rad+)))
    (dotimes (i 4)
      (setf (aref rot i i) (if (or (= i eje1) (= i eje2)
				   (cos grados))
			       1))
      (setf (aref rot eje1 eje2) (sin grados))
      (setf (aref rot eje2 eje1) (- (sin grados))))
    (multiplicar *posicion-actual* rot)))

(defun multiplicar (A B) "Multiplies matrix A by B"
  (let ((temporario (make-array '(16 4))))
    (dotimes (i 16)
      (dotimes (j 4)
	(let ((temp 0))
	  (dotimes (k 4)
	    (incf temp (* (aref A i k) (aref B k j))))
	  (setf (aref temporario i j) temp))))
    (dotimes (i 16) ; copy 'temporario' in '*posicion-actual*
      (dotimes (j 4)
	(setf (aref *posicion-actual* i j) (aref temporario i j))))))

By the way, I'm using SBCL in linux. And... I must confess: I come from
Java... :-)

Thank you!!!!!!

Luis :-)

From: Pascal Bourguignon
Subject: Re: Hypercube in lisp (newbie)
Date: 
Message-ID: <87slx45a5r.fsf@thalassa.informatimago.com>
"LuisGLopez" <············@gmail.com> writes:
> (defvar *posicion-actual*
>   (make-array '(16 4)))
>
> (defconstant +rad+ (/ pi 180.0))
>
> (defun iniciar-posicion () "Establish initial positions of vertices"
>   (dotimes (i 16)
>     (let ((binario (format nil "~4,'0b" i)))
>     (dotimes (j 4)
>       (setf (aref *posicion-actual* i j)
> 	    (if (string= "0" (aref binario j)) -1 1))))))

It works because a character is a string designator.  
I would have compared the character:

            (char= #\0 (aref binario j))

But it would be clearer and more efficient to test for the bits directly:

(defun iniciar-posicion () "Establish initial positions of vertices"
   (dotimes (i 16)
     (dotimes (j 4)
       (setf (aref *posicion-actual* i j) (if (oddp (ldb (byte 1 j) i)) -1 1)))))

And it would be better to make it a pure function:

(defun posicion-inicial ()
  "Return the initial positions of vertices"
  (let ((result (make-array '(16 4) :initial-element 0)))
    (dotimes (i 16)
      (dotimes (j 4)
        (setf (aref result i j) (if (oddp (ldb (byte 1 j) i)) -1 1))))
    result))

(defconstant +rad+ (/ pi 180.0))
(defvar *posicion-actual* (posicion-inicial))



> (defun traducir (eje)
>   (cond
>    ((string= eje "x") 0)
>    ((string= eje "y") 1)
>    ((string= eje "z") 2)
>    ((string= eje "w") 3)))

string= is case sensitive. string-equal is not.

(defun traducir (eje) 
  (if (integerp eje)
      eje ; accept a numeric eje specification.
      (position (character eje) "xyzw" :test (function char-equal))))

Note: (list (character "x") (character 'x) (character :x)) --> (#\x #\X #\X)
      (mapcar (function traducir) '( 1 :y y "y" "Y")) --> (1 1 1 1 1)

> (defun rotar (eje1 eje2 grados) "Gives the hypercube a rotation of
> 'grados' degrees in the plane 'eje1-eje2'"
>   (setf eje1 (traducir eje1) eje2 (traducir eje2))
>   (if (> eje1 eje2) (rotatef eje1 eje2))
>   (let ((rot (make-array '(4 4)))
> 	(grados (* grados +rad+)))
>     (dotimes (i 4)
>       (setf (aref rot i i) (if (or (= i eje1) (= i eje2)
> 				   (cos grados))
> 			       1))
>       (setf (aref rot eje1 eje2) (sin grados))
>       (setf (aref rot eje2 eje1) (- (sin grados))))
>     (multiplicar *posicion-actual* rot)))

It is worthwhile to use emacs and let it indent the code correctly (use M-\):

(defun rotar (eje1 eje2 grados)
  "Gives the hypercube a rotation of 'grados' degrees in the plane 'eje1-eje2'"
  (setf eje1 (traducir eje1) eje2 (traducir eje2))
  (if (> eje1 eje2) (rotatef eje1 eje2))
  (let ((rot (make-array '(4 4)))
        (grados (* grados +rad+)))
    (dotimes (i 4)
      (setf (aref rot i i) (if (or (= i eje1) (= i eje2)
                                   (cos grados))
                               1))
      (setf (aref rot eje1 eje2) (sin grados))
      (setf (aref rot eje2 eje1) (- (sin grados))))
    (multiplicar *posicion-actual* rot)))

then the errors become obvious.


You need to specify the initial-element of the arrays because the
default is implementation dependant (and you don't initialize
completely some arrays).

It's better to make multiplicar functional: let it just return the
result and let the caller decide if it must modify a global variable.

(defun multiplicar (A B)
  "Multiplies matrix A by B"
  (let ((temporario (make-array '(16 4) :initial-element 0)))
    (dotimes (i 16)
      (dotimes (j 4)
        (let ((temp 0))
          (dotimes (k 4)
            (incf temp (* (aref A i k) (aref B k j))))
          (setf (aref temporario i j) temp))))
    temporario))

Better use LET than SETF.  

Be careful with parentheses (= let emacs indent the code and check it's
correct!).  

Don't put inside a loop constant expressions.  It's useles to set 4
times (aref rot eje1 eje2) to the same value.


(defun rotar (eje1 eje2 grados)
  "Gives the hypercube a rotation of 'grados' degrees in the plane 'eje1-eje2'"
  (let ((eje1   (traducir eje1))
        (eje2   (traducir eje2))
        (rot    (make-array '(4 4) :initial-element 0))
        (grados (* grados +rad+)))
    (when (> eje1 eje2)
      (rotatef eje1 eje2))
    (dotimes (i 4)
      (setf (aref rot i i) (if (or (= i eje1) (= i eje2))
                               (cos grados)
                               1)))
    (setf (aref rot eje1 eje2) (sin grados)
          (aref rot eje2 eje1) (- (sin grados))
          *posicion-actual*    (multiplicar *posicion-actual* rot))))


It would be better to parameterize the vertices and make it a true function:

(defun rotar (vertices eje1 eje2 grados)
  "Gives the hypercube a rotation of 'grados' degrees in the plane 'eje1-eje2'"
  (let ((eje1   (traducir eje1))
        (eje2   (traducir eje2))
        (rot    (make-array '(4 4) :initial-element 0))
        (grados (* grados +rad+)))
    (when (> eje1 eje2)
      (rotatef eje1 eje2))
    (dotimes (i 4)
      (setf (aref rot i i) (if (or (= i eje1) (= i eje2))
                               (cos grados)
                               1)))
    (setf (aref rot eje1 eje2) (sin grados)
          (aref rot eje2 eje1) (- (sin grados)))
    (multiplicar vertices rot)))

(I would further leave out the multiplication and return just rotation
matrix, but let's stop here for now).


(loop repeat 10 
      for axis1 = (random 4)
      for axis2 = (loop for axis = (random 4) 
                        while (= axis axis1)
                        finally (return axis))
      for angle = (random 90)
      for vertices = (rotar (posicion-inicial) axis1 axis2 angle)
                then (rotar vertices axis1 axis2 angle)
      do (format t "~2%Rotation ~D degree around ~
                    plane (~[x~;y~;z~;w~],~[x~;y~;z~;w~])~%" angle axis1 axis2)
         (loop for i below 16
               do (loop for j below 4
                        do (format t "~8,3F " (aref vertices i j))
                        finally (format t "~%"))))
    ;; Oh oh! I didn't use *posicion-actual* ! 
    ;; There's no need for global variables ;-)
    ;; Cool, now you may rotate two or more hypercubes at the same time!


-- 
__Pascal Bourguignon__                     http://www.informatimago.com/

This is a signature virus.  Add me to your signature and help me to live
From: LuisGLopez
Subject: Re: Hypercube in lisp (newbie)
Date: 
Message-ID: <1124592544.801025.109570@g14g2000cwa.googlegroups.com>
Thank you very much!!!!! :-)

> But it would be clearer and more efficient to test for the bits directly:
>
> (defun iniciar-posicion () "Establish initial positions of vertices"
>   (dotimes (i 16)
>     (dotimes (j 4)
>       (setf (aref *posicion-actual* i j) (if (oddp (ldb (byte 1 j) i)) -1 1)))))

Hmmm... I think I'll have to read the books more carefully :) I didn't
know about 'ldb' and 'byte' (shame on me!)

> And it would be better to make it a pure function:
> (snip)
> (defvar *posicion-actual* (posicion-inicial))

I think I got a "Eureka" moment. Now I get what "pure function" means
and what it is good for!!!! THANKS!!!! :-) I was still thinking
"procedurally", wasn't I? The idea is to get any function return a
value (well, it sound obvious, but it isn't... for me)... then I can
put it in the defvar of *posicion-actual* and save not only some lines
of code, but gain clarity. I hope I can "grab" that concept and use it
from now on.

> (defun traducir (eje)
>   (if (integerp eje)
>      eje ; accept a numeric eje specification.
>     (position (character eje) "xyzw" :test (function char-equal))))

Oops: several more functions for me to read in the books.... :)
By the way, GREAT idea to check for integerp... very elegant. But I
don't understand why, if eje is integer, traducir returns its value...
> It is worthwhile to use emacs and let it indent the code correctly (use M-\):

I use it with slime; don't know why it appeared bad-indented.

> You need to specify the initial-element of the arrays because the
> default is implementation dependant (and you don't initialize
> completely some arrays).

Didn't know about... thanks!!!!

> It's better to make multiplicar functional: let it just return the
> result and let the caller decide if it must modify a global variable.

Again, now I get your point. :-)

> Better use LET than SETF.

I took note of it. :-)

> Don't put inside a loop constant expressions.  It's useles to set 4
> times (aref rot eje1 eje2) to the same value.

Shame on me! This was *indeed* obvious...

> (defun rotar (eje1 eje2 grados)
>   "Gives the hypercube a rotation of 'grados' degrees in the plane > 'eje1-eje2'"
>  (let ((eje1   (traducir eje1))
>        (eje2   (traducir eje2))
>        (rot    (make-array '(4 4) :initial-element 0))
>        (grados (* grados +rad+)))

Very elegant the style in LET (spaces, etc.); I take note of it.

>    (when (> eje1 eje2)
>      (rotatef eje1 eje2))

Why 'when' and not 'if'? Don't see the difference in this case...

>    (dotimes (i 4)
>      (setf (aref rot i i) (if (or (= i eje1) (= i eje2))
>                               (cos grados)
>                               1)))
>    (setf (aref rot eje1 eje2) (sin grados)
>          (aref rot eje2 eje1) (- (sin grados))
>          *posicion-actual*    (multiplicar *posicion-actual* rot))))
>

> It would be better to parameterize the vertices and make it a true function:

> (I would further leave out the multiplication and return just rotation
> matrix, but let's stop here for now).

Again, I'm with you in this.

The 'loop' part is too much for me (up to now); didn't read it in the
books as I should.
All in all, I can see why each part is good as you made them, but when
I try to assemble the whole in my head... oops! I'm in the verge of a
headache ;) I think I'll print it out and read in paper.
Anyway, I know that that is the right way... the lisp way. Not thinking
it like a "java-program-literally-translated-to-lisp". :-)
As I said, I will study your code more deeply, and then bother you with
more questions... THANK YOU!!!! :-)
From: Pascal Bourguignon
Subject: Re: Hypercube in lisp (newbie)
Date: 
Message-ID: <87oe7s55nd.fsf@thalassa.informatimago.com>
"LuisGLopez" <············@gmail.com> writes:
>> (defun traducir (eje)
>>   (if (integerp eje)
>>      eje ; accept a numeric eje specification.
>>     (position (character eje) "xyzw" :test (function char-equal))))
>
> Oops: several more functions for me to read in the books.... :)
> By the way, GREAT idea to check for integerp... very elegant. But I
> don't understand why, if eje is integer, traducir returns its value...

This allow to specify the axis either as indices: 0 1 2 3,
or as symbols:  'x 'y 'z 'w, 
or as keywords: :x :y :z :w,
or as strings: "x" "Y" "z" "W".

It's useful to be able to specify the axis ss indices in various situations.

In 2D, you can easily name x and y; but in nD n>2, it's often easier
to write x_0, x_1, x_2, ... x_n. 

>>    (when (> eje1 eje2)
>>      (rotatef eje1 eje2))
>
> Why 'when' and not 'if'? Don't see the difference in this case...

It's a matter of style to use the more specific construct for the job.
Since IF can work with two branches, when there's only one branch it's
less surprising to use WHEN or UNLESS.

It's more obvious when there are several statement in the one branch:

(if condition
    (progn
       sexp1
       sexp2
       ...
       sexpn))
vs.

(when condition 
   sexp1
   sexp2
   ...
   sexpn)

-- 
"By filing this bug report you have challenged the honor of my
family. Prepare to die!"
From: LuisGLopez
Subject: Re: Hypercube in lisp (newbie)
Date: 
Message-ID: <1124627359.120011.43880@g14g2000cwa.googlegroups.com>
Thanks for the explanation! It's clear to me now.

I made a minor change to your code:

(setf (aref posicion i j) (if (*evenp* (ldb (byte 1 j) i)) -1 1)))

in posicion-inicial (you used *oddp*), because I want the first vertice
to be (-1, -1, -1, -1) and the last (1, 1, 1, 1).

Everything else works just fine! :-)

Now, I'll see if I can understand the inner deeps of your flow; and
then (of course) I would love to be able to representate the hypercube
in the screen... ;) But, one step at a time.

Thank you very much,

Luis.
From: LuisGLopez
Subject: Re: Hypercube in lisp (newbie)
Date: 
Message-ID: <1124723810.315878.89150@o13g2000cwo.googlegroups.com>
Hi!

Now I think I understand the 'ldb', 'byte' and 'position'. All of them
are great; I'm sure I'll need them again in the future (thanks again,
Pascal! :-))

Now I want to be able to display de hypercube. I saw that CMUCL has CLX
as extra, so I downloaded it, and change my .emacs to reflect the
change. Then, I downloaded the CLX manual, and...er...ummm... I think
it's too much. I'll try to explain myself: I "just" want to draw black
lines on a white background... ¿Do I have to deal directly with the X
server? (When I say "just", I realice that an apparently "trivial" task
like drawing a line on screen involves a lot of stuff; it's just that I
would like them to be a little more "transparent" for me, like -say- in
java... Would it be possible?). 

Thank you!!!

Luis.
From: LuisGLopez
Subject: Re: Hypercube in lisp (newbie)
Date: 
Message-ID: <1124727936.777979.135480@f14g2000cwb.googlegroups.com>
Hi! :-)

Thanks for your reply!!!
Reading the CLX manual only I should be able to achieve the steps you
pointed me? O should I get another reference?

I'll do my best, but if you could send some snipplets, would be
absolutely great!!!!

Thanks again,

Luis.
From: Pascal Bourguignon
Subject: Re: Hypercube in lisp (newbie)
Date: 
Message-ID: <877jed7tam.fsf@thalassa.informatimago.com>
"LuisGLopez" <············@gmail.com> writes:
> Thanks for your reply!!!
> Reading the CLX manual only I should be able to achieve the steps you
> pointed me? O should I get another reference?
>
> I'll do my best, but if you could send some snipplets, would be
> absolutely great!!!!

It can be as simple as:

(open-window)
(draw-polygon (list (point 20 120) (point 120 220)
                    (point 220 120) (point 120 20)))


With the following.  It's bad because it doesn't handle X events, no
refresh, no unwind-protect to close properly the window, etc, but it's
simple and it's enough to show a few lines and points.


;;;;;;;;;;;;;;;;;;;;;;;;;;;;
;;; X11 routines

(unuse-package :XLIB)
;; (ext:without-package-lock (:posix)
;;   (defun posix::hostent-addr-type (&rest args)
;;     (apply (function posix::hostent-addrtype) args)))

(defparameter *dpy* nil)
(defparameter *win* nil)
(defparameter *gctxt* nil)

(defun get-environment-variable (string)
  #-clisp (cdr (assoc string ext:*environment-list* :test #'string=))
  #+clisp (ext:getenv string))

(defun parse-display-variable (s)
  (let* ((colon (position #\: s))
         (dot (position #\. s :start colon))
         (host-name (if (zerop colon) "localhost" (subseq s 0 colon)))
         (display-number (parse-integer s :start (1+ colon) :end dot)))
    (values host-name display-number)))

(defun open-window ()
  (multiple-value-bind (host display)
      (parse-display-variable (get-environment-variable "DISPLAY"))
    (let* ((dpy (xlib:open-display "")) ;; host :display display))
           (screen (xlib:display-default-screen dpy))
           (black (xlib:screen-black-pixel screen))
           (white (xlib:screen-white-pixel screen))
           (win (xlib:create-window :parent (xlib:screen-root screen)
                                    :background white
                                    :x 0 :y 0 :width 500 :height 500))
           (gcontext (xlib:create-gcontext :drawable win
                                           :background white
                                           :foreground black)))
      (xlib:map-window win)
      (xlib:display-force-output dpy)
      (setf *dpy* dpy
            *win* win
            *gctxt* gcontext)
      win)))


(defun rgb (r g b)
  (xlib:make-color :red r :green g :blue b))

(defun set-color (color)
  (setf (xlib:GCONTEXT-FOREGROUND *gctxt*) color))

(defun set-color-rgb (r g b)
  (setf (xlib:GCONTEXT-FOREGROUND *gctxt*)
        (xlib:make-color :red r :green g :blue b)))

(defun draw-line (x1 y1 x2 y2)
  (xlib:draw-line *win* *gctxt* (round x1) (round y1) (round x2) (round y2))
  (xlib:display-force-output *dpy*))

(defun x (pt) (car pt))
(defun y (pt) (cdr pt))
(defun point (x y) (cons x y))

(defun draw-polygon (points)
  (xlib:draw-lines *win* *gctxt* 
                   (mapcan (lambda (pt) (list (x pt) (y pt)))
                           (cons (car (last points)) points)))
  (xlib:display-force-output *dpy*))


(open-window)
(draw-polygon (list (point 20 120) (point 120 220)
                    (point 220 120) (point 120 20)))


-- 
__Pascal Bourguignon__                     http://www.informatimago.com/
Small brave carnivores
Kill pine cones and mosquitoes
Fear vacuum cleaner
From: Rob Warnock
Subject: Re: Hypercube in lisp (newbie)
Date: 
Message-ID: <Ud6dnVDoSY4a4JDeRVn-rg@speakeasy.net>
Pascal Bourguignon  <····@mouse-potato.com> wrote:
+---------------
| (defun get-environment-variable (string)
|   #-clisp (cdr (assoc string ext:*environment-list* :test #'string=))
|   #+clisp (ext:getenv string))
+---------------

I tend to use this one, which covers a few more cases:  ;-}

    ;;; GETENV -- Mostly-portable code for accessing Unix
    ;;; (or Windows?) environment variables. Morphed very slightly
    ;;; from <URL:http://cl-cookbook.sourceforge.net/os.html>
    ;;; Copyright (c) 2002 The Common Lisp Cookbook Project 
    ;;; See: <URL:http://cl-cookbook.sourceforge.net/license.html>
    (defun getenv (name &optional default)
      (or
       #+CMU (cdr (assoc name ext:*environment-list* :test #'string=))
       #+Allegro (sys:getenv name)
       #+CLISP (ext:getenv name)
       #+ECL (si:getenv name)
       #+SBCL (sb-unix::posix-getenv name)
       #+LISPWORKS (lispworks:environment-variable name)
       default))


-Rob

-----
Rob Warnock			<····@rpw3.org>
627 26th Avenue			<URL:http://rpw3.org/>
San Mateo, CA 94403		(650)572-2607
From: GP lisper
Subject: Re: Hypercube in lisp (newbie)
Date: 
Message-ID: <1124732164.4371957997d383f7a5e97de20323e959@teranews>
On 22 Aug 2005 16:37:15 +0100, <······@hexapodia.net> wrote:
>
> There might be a couple of example programs distributed with the CLX
> package.

Within CMUCL+Extras:

(load "hello")
(xlib::hello-world "localhost" :string "Click to Close")

(load "menu")
(xlib::just-say-lisp "localhost")

"beziertest" is missing the datafile.

-- 
Program A uses CLOS, Program B is implemented with structs, leading
to a fourfold increase in execution speed.  --J. B. Heimatseiten
From: Alan Crowe
Subject: Re: Hypercube in lisp (newbie)
Date: 
Message-ID: <86br3psmod.fsf@cawtech.freeserve.co.uk>
LuisGLopez wrote:
> Now I want to be able to display de hypercube. I saw that
> CMUCL has CLX as extra, so I downloaded it, and change my
> .emacs to reflect the change. Then, I downloaded the CLX
> manual, and...er...ummm... I think it's too much. I'll try
> to explain myself: I "just" want to draw black lines on a
> white background...

I've suffered that shock myself. I couldn't find a gentle
introduction to CLX on the web so I had a go at writing one
myself, despite the obvious bootstrapping problem.

  http://alan.crowe.name/clx/simple/examples.txt

It doesn't go very far. I'm still dithering about how to
write an example for redirect-override. I checked the tar ball
of source files

http://alan.crowe.name/clx/simple/source.tar

It was broken, because I forgot to set binary mode when I
uploaded it last. I've fixed it now.

Alan Crowe
Edinburgh
Scotland
From: LuisGLopez
Subject: Re: Hypercube in lisp (newbie)
Date: 
Message-ID: <1124835966.181796.265250@g14g2000cwa.googlegroups.com>
Hi!!!!

Thank you so much!!!!

GP Lisper: Didn't find the 'hello' and 'menu' programs anywhere on my
installation of CMUCL.

Pascal: I tried to run your code, but when I try to load it, I get this
error:

Reader error at 895 on #<Stream for file
"/home/luis/Documents/lisp/dibujar.lisp">:
Symbol "OPEN-DISPLAY" not found in the XLIB package.
   [Condition of type LISP::READER-PACKAGE-ERROR]

(I saved your code in 'dibujar.lisp' file).

Alan: Thank you so much for your text; I'll read it.

Luis.
From: Pascal Bourguignon
Subject: Re: Hypercube in lisp (newbie)
Date: 
Message-ID: <87slx0454f.fsf@thalassa.informatimago.com>
"LuisGLopez" <············@gmail.com> writes:
> Pascal: I tried to run your code, but when I try to load it, I get this
> error:
>
> Reader error at 895 on #<Stream for file
> "/home/luis/Documents/lisp/dibujar.lisp">:
> Symbol "OPEN-DISPLAY" not found in the XLIB package.
>    [Condition of type LISP::READER-PACKAGE-ERROR]

Then that must  be the CL curse, an  implementation specific function.
(We need a XLIB CLRFI to unify all implementations! ;-)

In the mean time, it gives you a good learning opportunity: 
- Read clisp documentation and sources to learn what open-display does.
- Read X documentation to get more information about XOpenDisplay(3x).
- Read cmucl documentation to find an equivalent function, or if none exists,
  to write it for cmucl (or port clisp's one).

If you've got a little luck, you could find it rapidly using
(apropos :display) or (apropos :open) in cmucl REPL.


-- 
__Pascal Bourguignon__                     http://www.informatimago.com/
In deep sleep hear sound,
Cat vomit hairball somewhere.
Will find in morning.
From: Marco Antoniotti
Subject: Re: Hypercube in lisp (newbie)
Date: 
Message-ID: <Bq%Oe.60$DJ5.70936@typhoon.nyu.edu>
Pascal Bourguignon wrote:
> "LuisGLopez" <············@gmail.com> writes:
> 
>>Pascal: I tried to run your code, but when I try to load it, I get this
>>error:
>>
>>Reader error at 895 on #<Stream for file
>>"/home/luis/Documents/lisp/dibujar.lisp">:
>>Symbol "OPEN-DISPLAY" not found in the XLIB package.
>>   [Condition of type LISP::READER-PACKAGE-ERROR]
> 
> 
> Then that must  be the CL curse, an  implementation specific function.
> (We need a XLIB CLRFI to unify all implementations! ;-)

Most likely he does not have a CMUCL configured with CLX.

Cheers
--
Marco
From: LuisGLopez
Subject: Re: Hypercube in lisp (newbie)
Date: 
Message-ID: <1124936221.646182.11070@g14g2000cwa.googlegroups.com>
Hi!!!

Exactly! I forgot to do (require :clx) (shame on me!)

Now a beautiful window pops up immediately, but no polygon is drawn,
although I get no error from   (draw-polygon (list (point 20 120)
(point 120 220)
		      (point 220 120) (point 120 20))))
From: Rob Warnock
Subject: Re: Hypercube in lisp (newbie)
Date: 
Message-ID: <1NednR8qtcTTHJDeRVn-tQ@speakeasy.net>
LuisGLopez <············@gmail.com> wrote:
+---------------
| Exactly! I forgot to do (require :clx) (shame on me!)
| 
| Now a beautiful window pops up immediately, but no polygon is drawn,
| although I get no error from   (draw-polygon (list (point 20 120)
| (point 120 220) (point 220 120) (point 120 20))))
+---------------

Works fine for me in CMUCL-19a. Did you somehow fail to copy the
(DISPLAY-FORCE-OUTPUT *DPY*) call in the (DEFUN DRAW-POLYGON ...)?

Or perhaps was your pop-up window covered when you did the DRAW-POLYGON
call? Remember, Pascal's example is *very* simple code, and does not
contain an exposure event handler, so if the section of window you're
drawing into is not completely visible when you call DRAW-POLYGON,
you won't see anything at all. Move the drawn-to window somewhere
so that at least the top-left quadrant is visible, and then try the
above DRAW-POLYGON call again.

[Then drag another window across part of your nice polygon, and
watch it get erased...  ;-}  ...until you call DRAW-POLYGON again.]


-Rob

-----
Rob Warnock			<····@rpw3.org>
627 26th Avenue			<URL:http://rpw3.org/>
San Mateo, CA 94403		(650)572-2607
From: http://public.xdi.org/=pf
Subject: Re: Hypercube in lisp (newbie)
Date: 
Message-ID: <m23boyrusj.fsf@mycroft.actrix.gen.nz>
On Thu, 25 Aug 2005 03:29:02 -0500, Rob Warnock wrote:

> LuisGLopez <············@gmail.com> wrote:
> +---------------
> | Exactly! I forgot to do (require :clx) (shame on me!)
> | 
> | Now a beautiful window pops up immediately, but no polygon is drawn,
> | although I get no error from   (draw-polygon (list (point 20 120)
> | (point 120 220) (point 220 120) (point 120 20))))
> +---------------

> Works fine for me in CMUCL-19a. Did you somehow fail to copy the
> (DISPLAY-FORCE-OUTPUT *DPY*) call in the (DEFUN DRAW-POLYGON ...)?

> Or perhaps was your pop-up window covered when you did the DRAW-POLYGON
> call? Remember, Pascal's example is *very* simple code, and does not
> contain an exposure event handler, so if the section of window you're
> drawing into is not completely visible when you call DRAW-POLYGON,
> you won't see anything at all. Move the drawn-to window somewhere
> so that at least the top-left quadrant is visible, and then try the
> above DRAW-POLYGON call again.

> [Then drag another window across part of your nice polygon, and
> watch it get erased...  ;-}  ...until you call DRAW-POLYGON again.]

A simple solution to that problem is just to add :BACKING-STORE T to
the CREATE-WINDOW call.

-- 
Don't worry about people stealing your ideas. If your ideas are any good,
you'll have to ram them down people's throats.
                                                          -- Howard Aiken
(setq reply-to
  (concatenate 'string "Paul Foley " "<mycroft" '(··@) "actrix.gen.nz>"))
From: Rob Warnock
Subject: Re: Hypercube in lisp (newbie)
Date: 
Message-ID: <95adndaHooa1EJPeRVn-uw@speakeasy.net>
Paul Foley  <···@below.invalid> wrote:
+---------------
| Rob Warnock wrote:
| > Or perhaps was your pop-up window covered when you did the DRAW-POLYGON
| > call? Remember, Pascal's example is *very* simple code, and does not
| > contain an exposure event handler...
| 
| > [Then drag another window across part of your nice polygon, and
| > watch it get erased...  ;-}  ...until you call DRAW-POLYGON again.]
| 
| A simple solution to that problem is just to add :BACKING-STORE T to
| the CREATE-WINDOW call.
+---------------

First, "T" doesn't appear to be a valid argument to :BACKING-STORE,
at least, not with the CLX that comes with CMUCL-19a:

	> (decribe'xlib:create-window)
	...
	(:BACKING-STORE (MEMBER NIL :NOT-USEFUL :WHEN-MAPPED :ALWAYS))
	(:BACKING-PLANES (OR NULL (UNSIGNED-BYTE 32)))
	(:BACKING-PIXEL (OR NULL (UNSIGNED-BYTE 32)))
	(:SAVE-UNDER (MEMBER NIL :ON :OFF))
	...
	> 

Next, I tried both ":BACKING-STORE :WHEN-MAPPED" and ":BACKING-STORE :ALWAYS"
and neither helped. Does something more need to be done with :BACKING-PLANES
and/or :BACKING-PIXEL and/or :SAVE-UNDER?

Note: AFAIK, an X Server is *NOT* required to honor :BACKING-STORE,
and most won't if they can't do it cheaply.

Anyway, the real solution is an XLIB:EVENT-CASE loop with an :EXPOSURE
handler...


-Rob

-----
Rob Warnock			<····@rpw3.org>
627 26th Avenue			<URL:http://rpw3.org/>
San Mateo, CA 94403		(650)572-2607
From: http://public.xdi.org/=pf
Subject: Re: Hypercube in lisp (newbie)
Date: 
Message-ID: <m2ll2orjbh.fsf@mycroft.actrix.gen.nz>
On Thu, 25 Aug 2005 22:31:52 -0500, Rob Warnock wrote:

> Paul Foley  <···@below.invalid> wrote:
> +---------------
> | Rob Warnock wrote:
> | > Or perhaps was your pop-up window covered when you did the DRAW-POLYGON
> | > call? Remember, Pascal's example is *very* simple code, and does not
> | > contain an exposure event handler...
> | 
> | > [Then drag another window across part of your nice polygon, and
> | > watch it get erased...  ;-}  ...until you call DRAW-POLYGON again.]
> | 
> | A simple solution to that problem is just to add :BACKING-STORE T to
> | the CREATE-WINDOW call.
> +---------------

> First, "T" doesn't appear to be a valid argument to :BACKING-STORE,
> at least, not with the CLX that comes with CMUCL-19a:

> 	(:BACKING-STORE (MEMBER NIL :NOT-USEFUL :WHEN-MAPPED :ALWAYS))

Oops; you're right.  It must be at least five years since I've
actually used CLX -- :ALWAYS is what I had in mind, though
:WHEN-MAPPED will do the job, too.

> Next, I tried both ":BACKING-STORE :WHEN-MAPPED" and ":BACKING-STORE :ALWAYS"
> and neither helped. Does something more need to be done with :BACKING-PLANES
> and/or :BACKING-PIXEL and/or :SAVE-UNDER?

You shouldn't need to, no.

Seems you do have to set the background, though.  I suppose that makes
sense.

> Note: AFAIK, an X Server is *NOT* required to honor :BACKING-STORE,
> and most won't if they can't do it cheaply.

It doesn't have to honour it, but I doubt you'll find one that
doesn't.  [It's not required to honour requests /not/ to keep a
backing store, either.  I'm not sure what you mean about
"cheaply"...if the window is too big, or something?]

> Anyway, the real solution is an XLIB:EVENT-CASE loop with an :EXPOSURE
> handler...

Sure (but if you use :BACKING-STORE you won't get any expose events,
except perhaps one when the window is first mapped)

-- 
Don't worry about people stealing your ideas. If your ideas are any good,
you'll have to ram them down people's throats.
                                                          -- Howard Aiken
(setq reply-to
  (concatenate 'string "Paul Foley " "<mycroft" '(··@) "actrix.gen.nz>"))
From: Rob Warnock
Subject: Re: Hypercube in lisp (newbie)
Date: 
Message-ID: <Rd6dnfpsbbALD5DeRVn-ug@speakeasy.net>
Ingvar  <······@hexapodia.net> wrote:
+---------------
| "LuisGLopez" <············@gmail.com> writes:
| > Now a beautiful window pops up immediately, but no polygon is drawn,
| > although I get no error from   (draw-polygon (list (point 20 120)
| > (point 120 220) (point 220 120) (point 120 20))))
...
| When you play around interactively, you want to issue calls to
| XLIB:DISPLAY-FORCE-OUTPUT occasionally.  In a drawing-intensive
| program, it's better to call taht function only when you're about to
| wait for X events.
+---------------

Note that Pascal's code *does* have a call to XLIB:DISPLAY-FORCE-OUTPUT
at the end of the DRAW-POLYGON routine.

[In a parallel reply, I speculated that perhaps he was being surprised
because exposure events weren't being handled...]


-Rob

-----
Rob Warnock			<····@rpw3.org>
627 26th Avenue			<URL:http://rpw3.org/>
San Mateo, CA 94403		(650)572-2607
From: Alan Crowe
Subject: Re: Hypercube in lisp (newbie)
Date: 
Message-ID: <86u0hevygb.fsf@cawtech.freeserve.co.uk>
LuisGLopez wrote:
> Now a beautiful window pops up immediately, but no polygon is drawn,

My quick guess is that the window didn't pop up quite as fast as
you think, with the consequence that the polygon was drawn before
the X server put up the window and sent the expose event.

I.E. the X serverignored stuff sent before it was ready. Then it
popped up a blank window (as always), then it sent the expose
event and waited .......

Alan Crowe
Edinburgh
Scotland
From: Andras Simon
Subject: Re: Hypercube in lisp (newbie)
Date: 
Message-ID: <vcdk6ib7apc.fsf@csusza.math.bme.hu>
"LuisGLopez" <············@gmail.com> writes:


> Pascal: I tried to run your code, but when I try to load it, I get this
> error:
> 
> Reader error at 895 on #<Stream for file
> "/home/luis/Documents/lisp/dibujar.lisp">:
> Symbol "OPEN-DISPLAY" not found in the XLIB package.
>    [Condition of type LISP::READER-PACKAGE-ERROR]

Have you REQUIREd clx (or got xlib loaded some other way)? 

CL-USER> (lisp-implementation-type)
"CMU Common Lisp"
CL-USER> (require :clx)
; Loading
#p"/home/simon/LISP/CMUCL19aPre3/lib/cmucl/lib/subsystems/clx-library.x86f".
Warning:  XLIB previously used the following packages:
  (#<The LISP package, 1780/3389 internal, 978/1227 external>)
T
CL-USER> (describe 'xlib:open-display)
OPEN-DISPLAY is an external symbol in the XLIB package.
Function: #<Function XLIB:OPEN-DISPLAY {5905DB49}>
Function arguments:
  (host &key (display 0) protocol authorization-name
authorization-data)
Its defined argument types are:
  (T &KEY (:DISPLAY INTEGER) (:PROTOCOL T) (:AUTHORIZATION-NAME T)
   (:AUTHORIZATION-DATA T))
Its result type is:
  XLIB:DISPLAY
On Wednesday, 6/30/04 07:13:47 pm [-2] it was compiled from:
target:clx/display.lisp
  Created: Friday, 8/29/03 11:17:50 am [-2]
  Comment: $Header: /project/cmucl/cvsroot/src/clx/display.lisp,v 1.11
2003/08/29 09:17:50 gerd Exp $
; No value
CL-USER> 

Andras
From: Surendra Singhi
Subject: Re: Hypercube in lisp (newbie)
Date: 
Message-ID: <r7chgmt5.fsf@netscape.net>
Pascal Bourguignon <····@mouse-potato.com> writes:

>
> (defun multiplicar (A B)
>   "Multiplies matrix A by B"
>   (let ((temporario (make-array '(16 4) :initial-element 0)))
>     (dotimes (i 16)
>       (dotimes (j 4)
>         (let ((temp 0))
>           (dotimes (k 4)
>             (incf temp (* (aref A i k) (aref B k j))))
>           (setf (aref temporario i j) temp))))
>     temporario))
>
> Better use LET than SETF.  
>

If you plan to write a program for rotating hypercubes, I will suggest
changing the above function to the one below. The above program allocates
memory on every call and then when the use of matrix is over the garbage
collector needs to recollect the array.

Whereas the one below will allocate the memory only once, and then keep using
it. 

(let ((temporario (make-array '(16 4) :initial-element 0)))
  (defun multiplicar (A B)
     "Multiplies matrix A by B"
     (dotimes (i 16)
       (dotimes (j 4)
         (let ((temp 0))
           (dotimes (k 4)
             (incf temp (* (aref A i k) (aref B k j))))
           (setf (aref temporario i j) temp))))
     temporario))

Any comments?

Thanks.

-- 
Surendra Singhi
http://www.public.asu.edu/~sksinghi/index.html

"All animals are equal, but some animals are more equal than others."
    - Orwell, Animal Farm, 1945
From: Damien Diederen
Subject: Re: Hypercube in lisp (newbie)
Date: 
Message-ID: <87fysx9dc9.fsf@keem.bcc>
Hi All,

Surendra Singhi <·········@netscape.net> writes:
[deletia]
> If you plan to write a program for rotating hypercubes, I will suggest
> changing the above function to the one below. The above program allocates
> memory on every call and then when the use of matrix is over the garbage
> collector needs to recollect the array.
>
> Whereas the one below will allocate the memory only once, and then keep using
> it. 
>
> (let ((temporario (make-array '(16 4) :initial-element 0)))
>   (defun multiplicar (A B)
>      "Multiplies matrix A by B"
>      (dotimes (i 16)
>        (dotimes (j 4)
>          (let ((temp 0))
>            (dotimes (k 4)
>              (incf temp (* (aref A i k) (aref B k j))))
>            (setf (aref temporario i j) temp))))
>      temporario))
>
> Any comments?

This will break:

 1. If the function is used by multiple threads;

 2. In the following scenario, where Y gets overwritten by the second
    call to MULTIPLICAR:

    (let ((y (multiplicar a b))
          (z (multiplicar c d)))
      ...)

Allowing the caller to pass the temporary array might be better:

(defun multiplicar (A B &optional (result (make-array '(16 4) :initial-element 0)))
  "Multiplies matrix A by B"
  (dotimes (i 16)
    (dotimes (j 4)
      (let ((temp 0))
	(dotimes (k 4)
	  (incf temp (* (aref A i k) (aref B k j))))
	(setf (aref result i j) temp))))
  result)

With the above definition,

  (multiplicar a b)

will cons a new array, while:

(let ((temporario (make-array '(16 4) :initial-element 0)))
  (dotimes (i 25)
    (multiplicar a b temporario)
    ...))

allows the caller to "safely" reuse local storage.  In some
implementations (notably SBCL), this:

(let ((temporario (make-array '(16 4) :initial-element 0)))
  (declare (dynamic-extent temporario))
  (multiplicar a b temporario)
  ...)

might cause the temporary array to be allocated on the stack.

> Thanks.

Enjoy,
Damien.

-- 
http://foobox.net/~dash/

I can resist everything except temptation.
                --Oscar Wilde
From: LuisGLopez
Subject: Re: Hypercube in lisp (newbie)
Date: 
Message-ID: <1125281228.656419.238760@z14g2000cwz.googlegroups.com>
Hi!!!

Well, I finally could (thanks to you, of course ;)) display the
hypercube. Its representation is still naive (I just use the x-y
coordinates of the hypercube, with no perspective... yet ;)). But... it
works!!!

I did just a very little (barely noticeable) change to Pascal's X11
code.

Here it is:
--------------------------------------------------------------------
(require :clx)

;;;;;;;;;;;;;;;;;;;;;;;;;;;;
;;; X11 routines

;;; THANKS, PASCAL!!!! :-)

(unuse-package :XLIB)
;; (ext:without-package-lock (:posix)
;;   (defun posix::hostent-addr-type (&rest args)
;;     (apply (function posix::hostent-addrtype) args)))

(defparameter *dpy* nil)
(defparameter *win* nil)
(defparameter *gctxt* nil)
(defparameter *window-width* 500)
(defparameter *window-height* 500)

(defun get-environment-variable (string)
  #-clisp (cdr (assoc string ext:*environment-list* :test #'string=))
  #+clisp (ext:getenv string))

(defun parse-display-variable (s)
  (let* ((colon (position #\: s))
         (dot (position #\. s :start colon))
         (host-name (if (zerop colon) "localhost" (subseq s 0 colon)))
         (display-number (parse-integer s :start (1+ colon) :end dot)))
    (values host-name display-number)))

(defun open-window ()
  (multiple-value-bind (host display)
      (parse-display-variable (get-environment-variable "DISPLAY"))
    (let* ((dpy (xlib:open-display host :display display))
           (screen (xlib:display-default-screen dpy))
           (black (xlib:screen-black-pixel screen))
           (white (xlib:screen-white-pixel screen))
           (win (xlib:create-window :parent (xlib:screen-root screen)
                                    :background white
                                    :x 0 :y 0 :width *window-width*
:height *window-height*))
           (gcontext (xlib:create-gcontext :drawable win
                                           :background white
                                           :foreground black)))
      (xlib:map-window win)
      (xlib:display-force-output dpy)
      (setf *dpy* dpy
            *win* win
            *gctxt* gcontext)
      win)))

(defun rgb (r g b)
  (xlib:make-color :red r :green g :blue b))

(defun set-color (color)
  (setf (xlib:GCONTEXT-FOREGROUND *gctxt*) color))

(defun set-color-rgb (r g b)
  (setf (xlib:GCONTEXT-FOREGROUND *gctxt*)
        (xlib:make-color :red r :green g :blue b)))

(defun draw-line (x1 y1 x2 y2)
  (xlib:draw-line *win* *gctxt* (round x1) (round y1) (round x2) (round
y2))
  (xlib:display-force-output *dpy*))

(defun clear-window ()
  (xlib:clear-area *win* :width *window-width* :height *window-height*)
  (xlib:display-force-output *dpy*))

(defun x (pt) (car pt))
(defun y (pt) (cdr pt))
(defun point (x y) (cons x y))

(defun draw-polygon (points)
  (xlib:draw-lines *win* *gctxt*
                   (mapcan (lambda (pt) (list (x pt) (y pt)))
                           (cons (car (last points)) points)))
  (xlib:display-force-output *dpy*))

;; Hipercube code

(defconstant +rad+ (/ pi 180.0))

(defun posicion-inicial ()
  "Return the initial positions of vertices"
  (let ((posicion (make-array '(16 4) :initial-element 0)))
    (dotimes (i 16)
      (dotimes (j 4)
        (setf (aref posicion i j) (if (evenp (ldb (byte 1 j) i)) -1
1))))
    posicion))

(defparameter *posicion-actual* (posicion-inicial))

(defparameter *camino*
  '(0 1 3 2 6 14 10 8 9 11 3 7 15 14 12 13 9 1 5 7 6 4 12 8 0 4 5 13 15
11 10 2 0)
  "Euler path for the vertices")

(defparameter *escala* 100
  "Factor for scaling the 'canonical' hypercube")
(defparameter *cero* 250
  "Where is the 'origin' of coordinates of the window")

(defun traducir (eje)
  (if (integerp eje)
      eje
      (position (character eje) "xyzw" :test (function char-equal))))

(defun multiplicar (A B)
  "Multiplies matrix A by B"
  (let ((posicion (make-array '(16 4) :initial-element 0)))
    (dotimes (i 16)
      (dotimes (j 4)
        (let ((temp 0))
          (dotimes (k 4)
            (incf temp (* (aref A i k) (aref B k j))))
          (setf (aref posicion i j) temp))))
    posicion))

(defun rotar (posicion eje1 eje2 grados)
  "Gives the hypercube a rotation of 'grados' degrees in the plane
'eje1-eje2'"
  (let ((eje1   (traducir eje1))
        (eje2   (traducir eje2))
        (rot    (make-array '(4 4) :initial-element 0))
        (grados (* grados +rad+)))
    (when (> eje1 eje2)
      (rotatef eje1 eje2))
    (dotimes (i 4)
      (setf (aref rot i i) (if (or (= i eje1) (= i eje2))
                               (cos grados)
                               1)))
    (setf (aref rot eje1 eje2) (sin grados)
          (aref rot eje2 eje1) (- (sin grados)))
    (multiplicar posicion rot)))

(defun escalar (vertice)
  "Changes the scale of a vertex of the 'canonic' hypercube"
  (round (+ *cero* (* *escala* vertice))))

(defun dibujar (vertices)
  "Draws a hipercube in the given position using just its x-y
coordinates (no perspective... yet ;))"
  (let ((puntos nil))
    (dolist (i *camino*)
      (push (point (escalar (aref vertices i 0)) (escalar (aref
vertices i 1))) puntos))
    (draw-polygon puntos)))

(defun correr ()
  (open-window)
  (let ((p (posicion-inicial)))
    (do ((a 0 (+ 5 a)))
	((>= a 360))
      (dibujar (rotar (rotar (rotar p 0 3 a) 0 2 40) 3 1 30))
      (sleep 0.1)
      (clear-window))))
From: LuisGLopez
Subject: Re: Hypercube in lisp (newbie)
Date: 
Message-ID: <1125365797.659826.309930@f14g2000cwb.googlegroups.com>
Hi!!!

Well... I think I did it!!!! Now I can see my beautiful hypercube
rotating in the z-w plane... I paste the code I added/changed.

I can't exagerate how much I learned from this... and enjoyed! The last
changes came fluid from my fingertips (well..almost ;)). Anyway, I'm
absolutely happy of having found lisp. :)

-------------------------------------
(defun proyectar (desde sobre)
  (let ((distancia 6))
    (* distancia (/ sobre (- distancia desde)))))

(defun transformar (posicion i j)
  "Applies scaling and perspective to vertex i j"
  (escalar (proyectar (aref posicion i 3)
		       (proyectar (aref posicion i 2)
				  (aref posicion i j)))))

(defun dibujar (posicion)
  "Draws a hipercube in the given position... with perspective!"
  (let ((puntos nil))
    (dolist (i *camino*)
      (push (point (transformar posicion i 0) (transformar posicion i
1)) puntos))
    (draw-polygon puntos)))

(defun correr ()
  "A beautiful hypercube rotating in the z-w plane...I just *love* it."
  (open-window)
  (let ((p (posicion-inicial)))
    (do ((a 0 (+ 5 a))
	 (b 0 (+ 7 b)))
	((>= a 720))
      (dibujar (rotar (rotar (rotar p 2 3 a) 1 2 30) 0 2 40))
      (sleep 0.1)
      (clear-window))))