Animate a Rotating Lissajous Figure

15

2

Entries for this challenge will display an animated rotating Lissajous figure. The appearance of 3d rotation occurs as the x parameter is successively phase-shifted in each frame.

Input:

The a and b parameters (as per the wikipedia article) will be specified at the command line, or read from stdin.

Output:

This is , so output will be displayed in a terminal emulator window or equivalent. Output size may be hardcoded, but the Lissajous figure must be at least big enough to fill a 80x24 window.

The animation frame rate will be approximately 50fps. Sleeping for 20ms between each frame is fine, as long as the time to calculate each frame is small compared with the fixed sleep time. If your choice of language cannot calculate quick enough on your chosen platform, then you'll have to calculate the time to sleep dynamically.

Each frame will not be displayed until all calculation for that frame is complete. There is no need to clear the screen between each frame.

The figure will make a full 2*Pi rotation approximately every 4 seconds.

For each frame, a full closed curve must be generated. At least 1000 points must be calculated along the curve. Line-drawing in between points is not necessary.

Points of the curve will be plotted as # characters. The rest of the display area will be blank/whitespace.

This is , so the shortest answer in bytes (deemed acceptable by me) will be the accepted winner one week after this posting.


Ungolfed reference answer.

Digital Trauma

Posted 2014-04-30T00:57:51.943

Reputation: 64 644

1Are we allowed to draw it using a graphics-oriented language? – TheDoctor – 2014-04-30T17:18:26.093

@TheDoctor I was torn on that, but decided to restrict to [tag:ascii-art]. Perhaps we can do a [tag:graphical-output] follow-up if this ends up popular. – Digital Trauma – 2014-04-30T17:23:09.733

1As with many rotating figures, these figures rotate different ways depending on how you look at them. For example, your answer seems to be rocking back and forth to me. But when I try hard, I can see regular rotation. – Justin – 2014-04-30T19:11:55.670

Answers

7

Perl - 177

while($d+=.1){print"\e[H\e[2J";$a=0;while(($a+=.01)<4*atan2 1,0){$x=$==40+40*cos$d+$a*$ARGV[0];$y=$==13+13*sin$d+$a*$ARGV[1];print"\e[$y;$x"."H#";}print$/;select($v,$v,$v,.03);}

The coefficients are passed via arguments. The above gif is produced from perl % 2 3

mniip

Posted 2014-04-30T00:57:51.943

Reputation: 9 396

1@DigitalTrauma I have my very own tool for recording screen in X11 – mniip – 2014-05-01T07:41:10.570

7

C (reference answer - not golfed)

Output with ./lissajous 2 3:

enter image description here

/*
 * lissajous.c
 *
 * Compile with:
 *   cc lissajous.c -lm -o lissajous
 *
 * Usage:
 *   ./lissajous a b
 *
 * a and b are the parameters as described in:
 * http://en.wikipedia.org/wiki/Lissajous_curve
 */

#include <stdio.h>
#include <stdlib.h>
#include <math.h>
#include <string.h>
#include <unistd.h>

int main (int argc, char **argv) {
    char buffer[25][80];
    double t, p;
    int x, y;
    int a, b;

    if (argc != 3) return 1;

    a = atoi(argv[1]);
    b = atoi(argv[2]);

    for (;;) {
        for (p = 0; p < 2 * M_PI; p += M_PI / 100) {
            memset(buffer, ' ', sizeof(buffer));
            /* Set 1st char of final line to nul.  Then we can printf
             * the whole 2d array as if it were one long 1d buffer.
             * Line wraps at 80 chars are assumed */
            buffer[24][0] = 0;
            for (t = 0; t < 2 * M_PI; t += M_PI / 500) {
                x = 39.5 * (1 + sin(a * t + p));
                y = 11.5 * (1 + sin(b * t)); 
                buffer[y][x] = '#';
            }
            printf("%s\n", &buffer[0][0]);
            usleep(20000);
        }
    }
    return (0);
}

C, 257 bytes

Ok, well I couldn't resist golfing it a bit myself. I think there's a lot more golfing to be done on this though:

#include<math.h>
main(int a,char**v){char x,y,b,d[25][80];double t,p,q=2*M_PI;a=atoi(v[1]);b=atoi(v[2]);for(p=0;memset(d,32,2000);p+=q/100){p=p<q?p:0;d[24][0]=0;for(t=0;t<q;y=11.5*sin(b*t)+12,d[y][x]=35,t+=q/1e3)x=39.5*sin(a*t+p)+40;puts(d);usleep(20000);}}

Digital Trauma

Posted 2014-04-30T00:57:51.943

Reputation: 64 644

2funny how the reference answer is getting upvotes... – TheDoctor – 2014-04-30T17:20:06.723

@TheDoctor I know, right. Thats why I ended up adding a golfed version, as it didn't feel right to get upvotes for an ungolfed answer. – Digital Trauma – 2014-04-30T17:24:28.200

If you're usleeping for 20000ms, why not just sleep for 20s ? – user12205 – 2014-04-30T20:40:01.150

@ace usleep(20000)== 20000 micro seconds, not 20000 milli seconds – Digital Trauma – 2014-04-30T20:41:31.460

Oops, sorry, my bad. What about usleep(2e4);? – user12205 – 2014-04-30T20:47:01.393

@ace Nice idea, but for reasons beyond me, I need to #include <unistd.h> to get this form of integer literal to work with gcc. – Digital Trauma – 2014-04-30T20:59:05.533

Hmm... I guess it may be related to the fact that 2e4 is actually a float in C, not an int. But I think it will still work in your t+=q/1000 part (t+=q/1e3) – user12205 – 2014-04-30T21:04:23.973

@ace Sure - that works for 1 char ;-). Thanks. – Digital Trauma – 2014-04-30T21:18:08.147

2

Python 3 - 280

Don't have one of those fancy animated gifs for ya, sorry. Windows console is slow at printing :P

Not sure this meets the 50fps requirement though I'm not sure it's really possible with Python. You can adjust the 1000 on the second line for the amount of points to calculate (the list is output width, output height, points to find, progression per frame (pi*2/n) and starting point). Or you can remove them and specify in the input as well.

import math as m
a,b,w,h,p,r,t=list(map(int,input().split()))+[79,24,1000,100,0]
while 1:v,z=w/2,h/2;d=[[int(m.sin(a*k+t)*v+v),int(m.sin(b*k)*z+z)]for k in[m.pi*2/p*l for l in range(p)]];print('\n'.join(''.join([' ','#'][[i,j]in d]for i in range(w))for j in range(h)));t+=m.pi*2/r

MORE IMPORTANT EDIT: Input via stdin, space-separated, newline terminated. Will wait for your input.

EDIT: Screenshot. Changed height to 40 for this one.

Lissajous rendering

cjfaure

Posted 2014-04-30T00:57:51.943

Reputation: 4 213

Hmm, it just hangs for me with python 3.2.3 (and 2.7.3) on Ubuntu. I guess I need to dig up a windows VM from somewhere. Or learn python. – Digital Trauma – 2014-04-30T21:10:58.903

@DigitalTrauma Hm. I'm running 3.3.2. Strange that it doesn't work though, don't see any platform-specific procedures in my code. – cjfaure – 2014-04-30T21:20:46.063

Save as lissajous.py, then run python3 lissajous.py 2 3 should be sufficient, right? – Digital Trauma – 2014-04-30T21:22:42.693

@DigitalTrauma Oh, oh, sorry. It takes input from stdin, not the args (failed to specify that...oops). Space-separated. – cjfaure – 2014-04-30T21:23:50.153

Aha - I guess I should have seen input() and guessed that. Works fine now for me with 3.2.3. +1 – Digital Trauma – 2014-04-30T21:28:11.043

@DigitalTrauma I imported time without using it. XD Is the timing okay or should I add a delay? – cjfaure – 2014-04-30T21:32:50.387

Its a bit slower than the c reference on my VM, but I'm ok with it. – Digital Trauma – 2014-04-30T21:36:06.803

BTW under Linus (bash) you can start it all in one command thus: python3 lissajous.py <<< "2 3" – Digital Trauma – 2014-04-30T21:37:48.180

@DigitalTrauma I'll remove the import then ;D also, thanks for the +1 (phone glitch there) – cjfaure – 2014-04-30T21:38:48.417

1

C# - 360 352 (cross platform - 332 for Windows only)

Edited after micro-golfing and rounding bug fix + suggestion by Ypnypn

Not exactly a contender at that length - and it's pretty much a verbatim copy of the reference - but oh well. :)

namespace System{class P{static int Main(string[]m){double p=Math.PI*2,a=int.Parse(m[0]),b=int.Parse(m[1]),q,t;for(;;)for(q=0;q<p;q+=p/200){var s=new string(' ',1920).ToCharArray();for(t=0;t<p;t+=p/1000)s[(int)(39.5*Math.Sin(a*t+q)+40)+(int)(11.5*Math.Sin(b*t)+12)*80]='#';Console.SetCursorPosition(0,0);Console.Write(s);Threading.Thread.Sleep(20);}}}}

Memory hog, creating a new array for every refresh - originally (re)used a StringBuilder, but sacrificed it for shortness. But at least the refresh takes less than 1 ms on my old Core2.

After removing some - now length-hurtful - old golfing, thereby reducing it by 8 characters, I tried to get it back to the "poetic" 360 by reverting to double rather than int parsing, and returning to 80*24 instead of 1920. That's still only 359, though - and no other single character addition that I can think of really adds any value to the code. So we'll just stick with 352. :-)

Unrolled (lost the pre-golf code):

namespace System
{
    class P
    {
        static int Main(string[] m)
        {
            double p = Math.PI * 2,
                   a = int.Parse(m[0]),
                   b = int.Parse(m[1]),
                   q, t;

            for (;;)
            {
                for (q = 0; q < p; q += p/200)
                {
                    var s = new string(' ', 1920).ToCharArray();
                    // Alternative - Windows console only:
                    // var s = new char[1920];

                    for (t = 0; t < p; t += p/1000)
                    {
                        s[
                            (int) (39.5*Math.Sin(a * t + q) + 40)
                          + (int) (11.5*Math.Sin(b * t) + 12) * 80
                        ] = '#';
                    }
                    Console.SetCursorPosition(0, 0);
                    Console.Write(s);
                    Threading.Thread.Sleep(20);
                }
            }
        }
    }
}

The Windows console actually accepts outputting a lot of null chars resulting in output that's (graphically) identical to using an actual space character - which allows a few characters less to initialize the character array.

No fancy animation, sorry :-)

JimmiTh

Posted 2014-04-30T00:57:51.943

Reputation: 441

Windows console actually accepts outputting a lot of null chars. Ah perhaps that explains why it doesn't so well with mono on Ubuntu. I don't have windows/.net handy right now, so I'll take your word for it that it works. – Digital Trauma – 2014-04-30T19:14:05.143

Just added a screenshot - should make it cross platform, but considering the non-contender status it has already - and the rather "poetic" number of characters, maybe it should just be left as is. :-) – JimmiTh – 2014-04-30T19:19:39.257

No need for all answers to be cross-platform IMO. If answers are platform-specific, its good to mention the platform, though C# is so well-known that its already obvious. – Digital Trauma – 2014-04-30T19:25:25.003

Does using C = Console really save any characters? – Ypnypn – 2014-05-01T02:21:12.777

@Ypnypn - not after invading the System namespace, nope. Didn't change it, or look for it, because the self-stated goal was to get to the same 360 characters while still using "proper" char array initialization. Thanks. :-) – JimmiTh – 2014-05-01T06:28:07.817

1

Python 2.7 - 214

I think I'm going to have another look at this. I have a feeling this can be brought down even further, but it will be hard to hit the Perl byte count. The math seems to be my biggest limitation here.

Warning: may crash whatever terminal you are using. I tested this on the Windows command prompt with lissajous.py 2 3. Because of the quick writing to the command prompt, expect the frames to jump a little bit. This can be mostly resolved (at the cost of speed) by using a greater s in the range(s) and t=2*pi*i.

I don't use \r or \b here purposely because I'm running it on Windows and it would cost extra characters.

from math import*;import sys;a,b=sys.argv[1:];p=s=1920
while p:
 c = [" "]*s
 for i in range(s):
    t=2*pi*i/s;c[int(round((39.5*(1+sin(eval(a)*t+p))))+round(11.5*(1+sin(eval(b)*t)))*80)]="#"
 print ''.join(c)
 p+=.1

grovesNL

Posted 2014-04-30T00:57:51.943

Reputation: 6 736

+1 Works on Ubuntu, though the output is a bit jumpy – Digital Trauma – 2014-05-01T14:38:45.453

@DigitalTrauma Yeah the jumpiness is caused by this being a cross platform solution (ie. to work on Windows command prompt). – grovesNL – 2014-05-01T16:28:48.387