Why distribution is not perfect?

Hello there, I am trying to achieve the effect in the picture using processing but somehow it is not working properly. The lines aren't getting distributed perfectly. why?

    void setup() {
      size(400, 400);
    }

    int N = 8;
    void draw() {
      background(0);
      stroke(-1);
      strokeWeight(2);
      translate(width>>1, height>>1);
      for (int j=1; j<10; j++) {
        int M = N*j; 
        float ang = 360/M;
        float R = 20*j;
        //  pushMatrix(); // trying to fix the rotation
        // rotate((j%2==0)?radians(ang/2):0); // trying to fix the rotation
        for (int i=0; i<M; i++) { 
          float x = R*cos(radians(ang)*i);
          float y = R*sin(radians(ang)*i);
          pushMatrix();
          translate(x, y);
          rotate(radians(ang)*i); 
          line(-10, 0, 10, 0); 
          popMatrix();
        }
        //  popMatrix();
      }
    }

Untitled

Answers

  • edited August 2016

    I believe the problem lies @ float ang = 360/M;. :-\"
    In Java when both operands in a division operation are of integer types, the result is also integer, w/o any fractional part! :-SS
    Here's my attempt. Still not straight lines. But it's a beginning. >-)

    // forum.Processing.org/two/discussion/17808/
    // why-distribution-is-not-perfect
    
    final int N = 8, QTY = 12, DIM = 28;
    final int X2 = DIM>>1, X1 = -X2;
    
    size(700, 700);
    noLoop();
    
    strokeWeight(2);
    stroke(-1);
    background(0);
    
    translate(width>>1, height>>1);
    
    for (int j = 1; j < QTY; ++j) {
      final int m = N*j;
      final float r = DIM*j;
      final float ang = DEG_TO_RAD*360 / m;
    
      for (int i = 0; i < m; ++i) {
        final float a = ang*i;
        final float x = r*cos(a), y = r*sin(a);
    
        pushMatrix();
        translate(x, y);
        rotate(a);
        line(X1, 0, X2, 0);
        popMatrix();
      }
    }
    
  • You are creating the rays from smaller segments which will be difficult to line up. The alternative would be to calculate the start and end of each ray then draw the ray in one piece.

    This program does that

    final int MAX_DEPTH = 10;
    
    final float MIN_RAD = 10;
    final float DELTA_RAD = 10;
    float MAX_RAD;
    
    void setup() {
      size(400, 400);
      MAX_RAD = sqrt(width * width + height * height);
      strokeWeight(2);
      stroke(255);
    }
    void draw() {
      background(0);
      translate(width>>1, height>>1);
      displayRays();
      noLoop();
    }
    
    void displayRays() {
      float start_angle = 0, step_angle = HALF_PI;
      for (int depth = 0; depth < MAX_DEPTH; depth++) {
        // Calculate the inner radius of the ray
        float inner_rad = (depth <= 1) ? MIN_RAD : MIN_RAD + DELTA_RAD * depth * depth;
        // If the ray is offscreen then we are finished no matter what the depth (d)
        if (inner_rad >= MAX_RAD)  
          break;
        if (depth > 0) {
          step_angle = PI / pow(2, depth);
          start_angle = step_angle/2;
        }
        for (float a = start_angle; a < TWO_PI; a += step_angle) {
          float sina = sin(a);
          float cosa = cos(a);
          line(inner_rad * cosa, inner_rad * sina, MAX_RAD * cosa, MAX_RAD * sina);
        }
      }
    }
    
  • edited August 2016

    @GoToLoop Thank for the effort. I am certainly gonna use your code.

    @quark Thanks for the effort but I am actually trying to implement this animation in processing ...

  • The trick here is to think in terms of 'rings' where each ring has a number of properties

    • number of lines [n]
    • the angle between lines [deltaA] ( 2π/n)
    • the inner radius [innerRad]
    • the outer radius [outerRad]

    This is enough information to draw the stationary image. To do the animation would also require

    • direction of rotation for the ring [dir] (-1 anticlockwise, 0 stationary, 1 clockwise)

    You will also need a master controller variable [t] which is used to determine the stage of the animation. The value of t will vary from 0-1 and the actual angle for each ring will be t * dir * deltaA.

    Notes

    • when dir is zero the ring will not be rotated
    • when t is zero the rings are 'at rest'
    • hanging t from 0-1 will rotate the ring by the angle deltaAng (depends on number of lines) and the direction is controlled by dir.

    The next step is to decide on how to represent the data that define the rings.

  • This animation looked challenging so I decided to have a go myself based on my previous post.

    I have managed to produce an almost exact copy of the gif animation in under 100 lines of code. I can post it here if you want but I understand that you might want to try it yourself.

  • @quark thanks for the comment. I have done the same thing as you have mentioned. I have created the ring of lines but the problem is lines are not getting distributed properly. That was my only concern. And thanks for the animation trick. I will definitely try that.

    As for now don't post the code. I want to try first :)

  • Answer ✓

    One of the problems in your original code is that you define the position of the line by its centre and then rotate about that position. Unfortunately the rotation should be about the pattern centre. It would be better if you define each line segment by its inner radius, outer radius and the angle about the pattern centre.

    Unfortunately there is no simple fix to your original code to align the segments.

    The code below is very basic and draws 3 rings showing how to align the line segments. It is only to get you started and you can ignore the code if you want but a full solution including animation will still require a lot more work on your part.

    void setup() {
      size(400, 400);
      stroke(255);
      strokeWeight(2);
    }
    
    void draw() {
      background(0);
      translate(width>>1, height>>1);
    
      drawRing(16, 40, 60, -1);
      drawRing(32, 60, 80, 0);
      drawRing(64, 80, 100, 1);
    }
    
    void drawRing(float n, float ir, float or, int dir) {
      float deltaAngle = TWO_PI / n;
      for (int i = 0; i < n; i++) {
        float a = i * deltaAngle;
        float sina = sin(a), cosa = cos(a);
        line(ir * cosa, ir * sina, or * cosa, or * sina);
      }
    }
    
  • @xenomorph -- did you ever get that full animated radial effect to align correctly? I would be interested in seeing the resulting sketch if you are willing to share it -- and/or willing to let @quark share his.

  • It seems floats are more imprecise than I thought they were.

  • floats are accurate to 6/7 digits so are good enough for most things. The errors only become apparent under certain circumstances and sometimes they can be avoided by modifying the calculation algorithm.

    The full solution simply involves adding extra calls to drawRing although I did an OO version which allows rings to have different colours. I will have a look for it when I get a chance.

  • And that's what you did.

    So will you post the solution here?

  • If I still have the solutions, then yes I will post them but not until the weekend as I am away from home.

    @quark reminder to self

  • @Lord_of_the_Galaxy

    Here is the non OO version.

    // Aniomation variable and constants
    float t = 0;
    int lastTime;
    final int ANIM_REST = 1000;
    final int ANIM_ROTATE = 800;
    final int ANIM_DURATION = ANIM_REST + ANIM_ROTATE;
    
    void setup() {
      size(400, 400);
      stroke(255);
      strokeWeight(2);
    }
    
    void draw() {
      background(0);
      translate(width / 2, height / 2);
      drawRing(t,  8, 10, 30, -1);
      drawRing(t, 16, 30, 50,  0);
      drawRing(t, 32, 50, 70,  1);
      drawRing(t, 32, 70, 90,  0);
      drawRing(t, 64, 90,110, -1);
      drawRing(t, 64,110,130,  0);
      drawRing(t, 64,130,150,  1);
      drawRing(t, 64,150,170,  0);
      drawRing(t, 64,170,190, -1);
      drawRing(t, 64,190,210,  0);
      drawRing(t,128,210,230,  1);
      drawRing(t,128,230,250,  0);
      drawRing(t,128,250,270, -1);
      drawRing(t,128,270,290,  0);
    
      t =getT();
    }
    
    void drawRing(float t, float n, float innerRad, float outerRad, int dir) {
      float deltaAng = TWO_PI / n;
      float tAngle = t * deltaAng * dir;
      for (int i = 0; i < n; i++) {
        float a = i * deltaAng + tAngle;
        float sina = sin(a), cosa = cos(a);
        line(innerRad * cosa, innerRad * sina, outerRad * cosa, outerRad * sina);
      }
    }
    
    float getT() {
      int currTime = millis();
      int deltaTime = currTime - lastTime;
      if (deltaTime >= ANIM_DURATION) {
        lastTime += ANIM_DURATION;
        deltaTime -= ANIM_DURATION;
      }
      if (deltaTime < ANIM_REST)
        return 0; // rest period
      // Rotation cycle
      deltaTime -= ANIM_REST;
      return (1.0 * deltaTime) / ANIM_ROTATE;
    }
    
  • And here is the OO version

    Ring[] rings;
    final float MIN_RAD = 10;
    final float DELTA_RAD = 26;
    float MAX_RAD;
    // Animation timings in milliseconds
    final int ANIM_REST = 1000;
    final int ANIM_ROTATE = 800;
    final int ANIM_DURATION = ANIM_REST + ANIM_ROTATE;
    int lastTime;
    
    void setup() {
      size(400, 400);
      int[] nbrInRing = {
        8, 16, 32, 32, 64, 64, 64, 64, 64, 128, 
        128, 128, 128, 128, 128, 128, 256 
      };
      MAX_RAD = sqrt(width * width + height * height);
      rings = new Ring[round((MAX_RAD + MIN_RAD) / DELTA_RAD)];
      // Create rings
      int[] dir = {-1, 0, 1, 0};
      for (int r = 0; r < rings.length; r++) {
        int noLines = (r < nbrInRing.length) ? nbrInRing[r] : nbrInRing[nbrInRing.length-1];
        float ir = r * DELTA_RAD + MIN_RAD;
        float or = ir + DELTA_RAD;
        rings[r] = new Ring(noLines, ir, or, dir[r % 4]);
      }
    }
    
    void draw() {
      float t = getT();
      background(0);
      translate(width / 2, height / 2);
      // Display in reverse order so the rings can have different background colours
      for (int r = rings.length - 1; r >= 0; r--) {
        rings[r].display(t);
      }
      // Centre black circle
      fill(0);
      noStroke();
      ellipse(0, 0, 2*MIN_RAD, 2*MIN_RAD);
    }
    
    float getT() {
      int currTime = millis();
      int deltaTime = currTime - lastTime;
      if (deltaTime >= ANIM_DURATION) {
        lastTime += ANIM_DURATION;
        deltaTime -= ANIM_DURATION;
      }
      if (deltaTime < ANIM_REST)
        return 0; // rest period
      // Rotation cycle
      deltaTime -= ANIM_REST;
      return (1.0 * deltaTime) / ANIM_ROTATE;
    }
    
    class Ring {
      int nbr;
      float deltaAng; 
      float innerRad, outerRad;
      int dir;
      int backCol, foreCol;
    
      Ring(int n, float ir, float or, int rotDir) {
        nbr = n;
        innerRad = ir;
        outerRad = or;
        deltaAng = TWO_PI / nbr;
        dir = rotDir;
        backCol = color(0);
        backCol = color(0, 0, int(random(96,160)));
        foreCol = color(255);
      }
    
      void display(float t) {
        // Draw ring background
        fill(backCol);
        noStroke();
        ellipse(0, 0, 2* outerRad, 2 * outerRad);
        noFill();
        // Draw ring lines
        strokeWeight(2);
        stroke(foreCol);
        float tAngle = t * deltaAng * dir;
        for (int r = 0; r < nbr; r++) {
          float angle = r * deltaAng + tAngle;
          float sina = sin(angle);
          float cosa = cos(angle);
          line(innerRad * cosa, innerRad * sina, outerRad * cosa, outerRad * sina);
        }
      }
    }
    
  • edited February 2017

    @quark Thanks!

  • @quark -- thank you so much for digging this up and sharing it.

Sign In or Register to comment.