Shapes3D is beyond Coool : But can I do dynamic textures

I'm using Shapes3D with Processing 2. I downloaded a starmap and used the library to project onto an ellipsoid as a texture. If I zoom into the ellipsoid, I get a planetarium view of the stars. Now, that's so cool but what I want to do is add some arcs. I successfully connected my 9 DOF IMU and can rotate the view in pitch,roll,yaw. I basically have a sextant and I want to point at a star and get the Right Ascension and Declination. I tilted the ellipsoid to my current latitude(actually, 90-lat) and when I point at Polaris, the IMU pitch reads my latitude within .05 of a degree.

The library question is how can I draw arcs onto the ellipsoid? Do I have to "extend" or "Override" one or more of the shapes3D classes? My java skills are sketchy, but determined and growing.


  • Answer ✓

    The library does not provide methods to draw directly onto the surface but the easiest way is simple draw on the texture image.

    Assuming you have loaded an PImage called original using loadImage then create a PGraphics object which is a copy of the texture image like this.

    PImage original;
    PGraphics texture;
    Ellipsoid starBall;
    void setup(){
      size(480, 320, P3D);
      // create the star ball here
      original = loadImage("filename.png");
      texture = createGraphics(original.width, original.height);
    // Call this method to reset the star map
    void initStars(){
      texture.image(original); // draw the image into the graphics object
    // Example method showing how to modify the texture
    // could be called from mouse click or other event handler.
    void addDiagonalLine(){
      texture.stroke(255,255,0); // yellow
      texture.strokeWeight(3); // need some weight to make sure it shows
      texture.line(0,0,texture.width, texture.height);

    BEWARE: I have just typed this code into the forum so may have some syntax errors.

    OK final point, since you are viewing the star map from the inside of the ellipsoid it may look reversed. (Shapes3D draws textures as if viewed from outside the object) if this is the case then simple reverse the star map image in a graphics program, it will also affect the end points of any arcs.

    Hope this helps.

  • Thanks, I'll give drawing on the image a try. It will probably lead to more wonder and questions. I appreciate your awesome work and help. I noticed early what you say about image being reversed if viewed from inside the ellipsoid, letters were backwards. MSpaint fixed that. I originally tried projecting NASA images(reversed? who could tell) of Iapetus and Encelada from Cassini mission. That was how I accidentally realized I could zoom inside the ellipsoid and then I was off finding star maps and playing with an IMU.

    you call it starBall, ha,ha.

  • edited November 14

    OK, that worked. The more I think about what I want the more confused I get. The diagonal line function goes across the stars at an equal angle from one corner to the opposite which I think makes it an Ioxadrome. If I draw it from the top to the bottom parallel to longitude, it is a great circle. I'll have to play around with it to get what I'm after.

    Thanks, I never would have thought to draw on the original as you suggested.

  • edited December 1

    Here is a screenshot of drawing some lat and lon lines on Enceladus.

  • Next I'll add some Pvectors and points.

  • Nice one!

  • edited November 18

    In case anyone wants to do something similar, here's what I added to your addDiagonalLine() function.

    void addDiagonalLine(){
     //longitude lines
      texture.line(0,0,0, texture.height);
      texture.line(texture.width/12, 0,texture.width/12,texture.height);
      texture.line(texture.width/12 * 2, 0,texture.width/12 * 2,texture.height);
      texture.line(texture.width/12 * 3, 0,texture.width/12 * 3,texture.height);
      texture.line(texture.width/12 * 4, 0,texture.width/12 * 4,texture.height);
      texture.line(texture.width/12 * 5, 0,texture.width/12 * 5,texture.height);
      texture.line(texture.width/12 * 6, 0,texture.width/12 * 6,texture.height);
      texture.line(texture.width/12 * 7, 0,texture.width/12 * 7,texture.height);
      texture.line(texture.width/12 * 8, 0,texture.width/12 * 8,texture.height);
      texture.line(texture.width/12 * 9, 0,texture.width/12 * 9,texture.height);
      texture.line(texture.width/12 * 10, 0,texture.width/12 * 10,texture.height);
      texture.line(texture.width/12 * 11, 0,texture.width/12 * 11,texture.height);
    //latitude lines  
      texture.line(0, texture.height/2,texture.width,texture.height/2);
      texture.line(0, texture.height/2/3,texture.width,texture.height/2/3);
      texture.line(0, texture.height/2/3 * 2,texture.width,texture.height/2/3 * 2);
      texture.line(0, texture.height - texture.height/2/3 * 2,texture.width,texture.height -texture.height/2/3 * 2);
      texture.line(0, texture.height -texture.height/2/3,texture.width,texture.height -texture.height/2/3);

  • Here is another screenshot of a starmap downloaded from NASA and projected onto a Shapes3D ellipsoid. For comparison and identification, another shot of a star identification chart from the Air Almanac.

  • edited December 2

    Thanks so much for sharing this, @HarryCodes.

    Could you give an example URL of a starmap image that you downloaded, so that people could try playing with the technique?

    I'm assuming that this technique working depends on the image using a specific projection format.

  • Here'[s where I got the star map image. Make sure it uses celestial coordinates and not galactic.

    Here's another that shows a map with borders, which is typical when searching for maps. I tried a few and with varying success cropped out the borders. If not carefully cropped, you get a line where left and right sides meet. I didn't pay much attention to the map projection as long as it looks like coverage is 360 degrees.

    BTW, shapes3D does all the heavy lifting.

  • edited December 3

    I took the image from the second link above and cropped it. If you look carefully at the latitude lines, the ones I added don't quite match up with the map. This is because I didn't crop it exactly going vertically and left some slop. Plus, you can see what quark was warning about the image being reversed. This image is reversed on the outside but from inside the ellipsoid it is not.

    What I want to do now is sync the rotation using rotateBy() with system time and longitude. Then when I start the sketch, it will always be oriented according to my time and location.

  • Exciting -- thanks for sharing these examples, great progress!

    You can also reverse your image in Processing dynamically.

    For example, if you wanted to pass the camera through the sky to see it from the outside, you could draw two spheres with almost the same radius (with a small gap between them). The inside one can be textured to view from the inside, the outside image can be reversed to be viewed from the outside.

  • Others may find this discussion useful so I have modified the title to make it more likely to be hit when searching on textures.

  • edited December 4

    Two spheres works! The radii differ by 1. Enter at the north pole and bam, looking at the south pole. Except, there is something goofy. Its as if the camera immediately turns 180. For instance, I entered at the constellation Orion and should see the "antipode", Sagitarius. Instead, I see Orion again, thus the camera swung 180 on entry. Now if I zoom all the way in the opposite direction, everything flips and Sagitarius appears but Orion is now backwards when I zoom out.

    I don't think I can get the camera view correct.

  • I got it! I flipped the second texture and rotated It 180. Now I can enter at wherever and once inside see the antipode.

  • Glad it worked, @HarryCodes!

    Would you be willing to share the flip/rotation code you used that worked for you for others interested in this approach?

  • I'll post what I have soon, but I just used MSpaint to flip and flop the images and then " starBall2.rotateTo(0,radians(-180f),0) " to get the second texture right.

  • edited December 6

    I said I wanted to sync the star map with system time and longitude. I borrowed from the Processing clock example and it rotates. The problem is it only moves once a minute And the real night sky rotates faster(slower? I don't know, its different and may vary) than a clock by about 2 minutes per (day?)hour.


    float t = map(hour() + norm(minute(),0,60) ,0,24,0,TWO_PI) - radians(77.4f);


    Well, duh, I added norm(second(),0,3600) to hour() and norm minute() and it moves once a second. I guess I can just add a correction to t to speed it up, by mapping the extra 2 minutes per hour to 0 and TWO_PI. Its hard to test because I cant just let it run for 24 hours, it runs out of memory.

  • re:

    it runs out of memory.

    What do you mean? What is using up memory? What is the specific error message?

  • You can increase the memory in the settings maybe that helps

    Not sure why it runs out of memory in the first place though

  • Yeah, it stops after about 2 hours. The error says Out of Memory:Java Heap Space. I had a similar problem when I tried using a .jpg that was too big and it couldn't load it.

    As for the difference in time between a clock day and a star day, I'll pretend 15 degrees per hour is exact.

  • @HarryCodes consider sharing your code -- this could be a bug in Processing, but is it possible that you are constantly creating / loading an increasing list of new objects in your draw loop?

  • edited December 7

    Here's what I have so far:


    import shapes3d.utils.*;
    import shapes3d.animation.*;
    import shapes3d.*;
    import java.awt.event.*;
    PImage original;
    PImage original2;
    PGraphics texture;
    PGraphics texture2;
    Ellipsoid starBall;
    Ellipsoid starBall2;
    float zoom = 0.5;
    public void setup() {
      size(1275, 750, P3D);
      original = loadImage("100lightyears.jpg");
      original2 = loadImage("100lightyears2.jpg");
      frame.addMouseWheelListener(new MouseWheelInput());
      texture = createGraphics(original.width, original.height);
      texture2 = createGraphics(original2.width, original2.height);
      starBall = new Ellipsoid(this, 64, 64);
      starBall2 = new Ellipsoid(this, 64, 64);
    void initStars(){
    void addDiagonalLine(){
     //longitude lines
      texture.line(0,0,0, texture.height);
      texture.line(texture.width/12, 0,texture.width/12,texture.height);
      texture.line(texture.width/12*2, 0,texture.width/12*2,texture.height);
      texture.line(texture.width/12*3, 0,texture.width/12*3,texture.height);     
      texture.line(texture.width/12*4, 0,texture.width/12*4,texture.height);
      texture.line(texture.width/12*5, 0,texture.width/12*5,texture.height);
      texture.line(texture.width/12*6, 0,texture.width/12*6,texture.height);
      texture.line(texture.width/12*7, 0,texture.width/12*7,texture.height);
      texture.line(texture.width/12*8, 0,texture.width/12*8,texture.height);
      texture.line(texture.width/12*9, 0,texture.width/12*9,texture.height);
      texture.line(texture.width/12*10, 0,texture.width/12*10,texture.height);
      texture.line(texture.width/12*11, 0,texture.width/12*11,texture.height);
      texture.line(texture.width/12 * 11 -texture.width/12/3, 0,texture.width/12 * 11 -texture.width/12/3,texture.height);
      texture.line(texture.width/12 -texture.width/12/2, 0,texture.width/12 -texture.width/12/2,texture.height);
      texture.line(texture.width/12+texture.width/12/2, 0,texture.width/12+texture.width/12/2,texture.height);
      texture.line(texture.width/12/2+texture.width/12/2, 0,texture.width/12/2+texture.width/12/2,texture.height);
      texture.line(texture.width/12 * 2+texture.width/12/2, 0,texture.width/12 * 2+texture.width/12/2,texture.height);
      texture.line((texture.width/12 * 3)+texture.width/12/2, 0,(texture.width/12 * 3)+texture.width/12/2,texture.height);
      texture.line((texture.width/12 * 4)+texture.width/12/2, 0,(texture.width/12 * 4)+texture.width/12/2,texture.height);
      texture.line((texture.width/12 * 5)+texture.width/12/2, 0,(texture.width/12 * 5)+texture.width/12/2,texture.height);
      texture.line((texture.width/12 * 6)+texture.width/12/2, 0,(texture.width/12 * 6)+texture.width/12/2,texture.height);
      texture.line((texture.width/12 * 7)+texture.width/12/2, 0,(texture.width/12 * 7)+texture.width/12/2,texture.height);
      texture.line((texture.width/12 * 8)+texture.width/12/2+texture.width/12/3, 0,(texture.width/12 * 8)+texture.width/12/2+texture.width/12/3,texture.height);//////////////
      texture.line((texture.width/12 * 9)+texture.width/12/2, 0,(texture.width/12 * 9)+texture.width/12/2,texture.height);
      texture.line((texture.width/12 * 10)+texture.width/12/2, 0,(texture.width/12 * 10)+texture.width/12/2,texture.height);
      texture.line((texture.width/12 * 11)+texture.width/12/2, 0,(texture.width/12 * 11)+texture.width/12/2,texture.height);
    //latitude lines  
      texture.line(0, texture.height/2,texture.width,texture.height/2);
      texture.line(0, texture.height/2/3,texture.width,texture.height/2/3);
      texture.line(0, texture.height/2/3 * 2,texture.width,texture.height/2/3 * 2);
      texture.line(0, texture.height - texture.height/2/3 * 2,texture.width,texture.height -texture.height/2/3 * 2);
      texture.line(0, texture.height -texture.height/2/3,texture.width,texture.height -texture.height/2/3); 
    public void draw() {
     translate(width/2, height/2);
     float r = -0.01 * (mouseY - 500);
      float s = map(second(), 0, 60, 0, TWO_PI);
      float m = map(minute() + norm(second(), 0, 60), 0, 60, 0, TWO_PI); 
      float h = map(hour() + norm(minute(), 0, 60), 0, 24, 0, TWO_PI * 2)- HALF_PI;
      float t = map(hour() + norm(minute(),0,60)+ norm(second(), 0, 3600) ,0,24,0,TWO_PI) - radians(77.4f);
      scale(zoom, -zoom, zoom);
      starBall2.rotateTo(0,t -radians(-180f),0);
    class MouseWheelInput implements MouseWheelListener{
      void mouseWheelMoved(MouseWheelEvent e) {
        zoom -= 0.15 * e.getWheelRotation();


  • edited December 7

    Hmm. First guesses without running:

    • Is addDiagonalLine() supposed to be redrawing texture each frame, or adding more even more lines to texture each time?
    • Is System.out.println(degrees(h)) expending memory?

    You could try commenting them out and cranking up the frameRate to hopefully get a crash faster (like 1 hr, or 30 minutes). If the current bugged version shows increasing RAM use over time under inspection, then you can also see what causes that behavior to stop without waiting until the heap is full.

    ...but I'm no Java debugging expert, others may have better advice...?

  • Answer ✓

    In line 103 you have used pushStyle which will save the current drawing style but you do not have a matching popStyle, this is your memory leak.

    Also as pointed out the method addDiagonalLine only needs to be executed once because there are no changes between calls to this method.

  • edited December 8

    Thanks, I got rid of the pushStyle which came from another sketch and I moved addDiagonalLine to setup.

    I think that fixed it. I thought it crapped out again, but I was running the old version.

    I tested the orientation of the map by comparing to an online planetarium and I needed to subtract HALF_PI from t. I guess if you live east of Greenwich, then you add your longitude and still subtract Pi/2.

  • edited December 10

    Interesting -- a mismatched pushStyle() in e.g. a loop normally crashes the sketch in moments due to a matrix stack overflow. I didn't realize it could leak like that without maxing out the number of stack layers if it was open at the end of draw()

    Edit: Not accurate -- mismatched pushMatrix() crashes; mismatched pushStyle() consumes memory -- see @quark explanation below.

  • I let it run while I went to work, and it stopped when Dubhe(big dipper) was about 45 degrees from meridian. That's about 7 hours running which is a big improvement over only 2 hours but something is still using up memory. The error message is slightly different now and has hints and instructions about images being loaded thousands of times and try increasing available memory.

    In any case, I'm pretty happy with the accuracy. I used Interactive Computer Ephemeris and calculated meridian passage for a few stars and was within a minute the best I could tell.

  • Man, I am stupid. I just looked at the version I am running and the pushStyle() was in there again. I deleted and saved and running it again.

  • @jeremydouglass pushStyle() is part of the Processing API and when called creates a PStyle object which holds the current drawing variable status and then pushes it onto an expandable array (uses PApllet.expand(...)). Processing has no upper size constraint on the style array.

    It should not be confused with pushMatrix() which stores an OpenGL transformation matrix on a stack, the maximum size depends on the OpenGL implementation but all will throw an exception if we attempt to exceed it.

  • edited December 10

    Its been running for 8+ hours and no problem, honest, no errant pushStyle(). I have added something though and that is a gnomon(meridian line) at line 103 I added:

    stroke(0, 255, 0); strokeWeight(1); line(0,-500,0 , 0, 500, 0);

  • I've been studying and experimenting with this, including porting to android which is where I use my IMU connected to serial. I use Interactive Computer Ephemeris, running on a DOS Emulator, to get navigation stuff on a star like meridian passage. Then I compare this to what my star clock shows. My clock and IMU always agrees with the GHA(Greenwich Hour Angle), SHA(Sidereal Hour Angle) and declination, but the ephemeris also gives the altitude(sighting) and this is where things always differ and I don't know why. Its just a few degrees and refraction, parallax, usual corrections don't add up to the difference.

  • Is the amount a constant offset, or is it cumulative? It sounds like it is constant...?

  • I think I see it now. Heres a little experiment I set up. At 1000(GMT), ICE(ephemeris) says Dubhe will be observed at a corrected altitude(Hc) of 67 02.4' . This of course is calculated for My latitude. I observed 27.6, measured from the celestial pole and Not the horizon. 67 - 27 = 40 and that is my latitude. I thought I could subtract Hc from 90 and get 27, but that gives 23, a difference of 4. That constant difference is what puzzled me.

  • Its probably not actually constant but varies from horizon to zenith within a small range that doesn't add up to 90 degrees. If I want to do something more useful with this, I need to understand what I'm looking at.

  • Aha, I will read up on spherical trigonometry. A spherical triangle does not add up to 180. I'm probably seeing that?

  • Good detective work: a spherical triangle definitely does not add up to 180!

    You might want the equation for spherical excess:

  • edited December 15

    Awesome links, thanks. I'll work on that. My star clock is basically like the image in the first link. I am inside that sphere at (0,0,0) instead of on the surface. They mention an inner and outer triangle. I suspect I'm measuring with my IMU an angle that is part of a different triangle.

    One thing always leads to another. This link looks more like my star clock.

Sign In or Register to comment.