Loading...
Logo
Processing Forum

Anitaliasing?

in Contributed Library Questions  •  2 years ago  
Hi,

I'm currently building an interface that allows a user to draw a waveform into a box in realtime which will be used MAX/Msp over Osc to generate sound. I've got the waveform drawing bit working more or less how I want, but I'd like to make it look much prettier than it does (which is the whole point of using Processing rather than just the standard Max interface!). At the moment the waveform edge is covered with jaggies, and I want it to be antialiased so it looks smoother. I'm no graphics programmer and I'm slightly at a loss as to how to achieve this.

At the moment I'm drawing the curve using a quad for each position of the waveform which gives some antialiasing from the smooth() function, but, since there are so many data points it's still very jaggy; I'm sure there must be a better way to do it (line 22 2nd tab). I need to store the data for the waveform in the array in this way as this is the format I need for the audio, but it could be displayed in any way. The important thing is that the waveform display changes in realtime.

Any suggestions are welcome - I'm new to Processing so I may well be missing something really obvious!

Code posted below - I've removed a lot of extraneous stuff to keep it clear.

Many thanks,

Tim

Main:

Copy code
  1. //Opens a wave drawing window that interfaces with a MAX buffer over OSC
  2.   
  3.   //general variables
  4.   int drawWidth;
  5.   int drawHeight;
  6.   WaveBox[] waves = new WaveBox[1];
  7.   
  8.   void setup ()
  9.   {
  10.     size(480, 240, P2D);
  11.     drawWidth = 440;
  12.     drawHeight = 200;
  13.     strokeWeight(1);
  14.     fill(255);
  15.     smooth();
  16.     waves[0] = new WaveBox(20,20,drawWidth, drawHeight);
  17.   }
  18.   
  19.   void draw()
  20.   {
  21.     background(255, 250, 229);
  22.     waves[0].drawWave();
  23.   }
  24.   
  25.   void mousePressed()
  26.   {
  27.     waves[0].writeMouse(false);
  28.   }
  29.   
  30.   void mouseDragged()
  31.   {
  32.     waves[0].writeMouse(true);
  33.   }
  34.   
  35.   void mouseReleased()
  36.   {
  37.     waves[0].mouseEnd();
  38.   }
WaveBox tab:

Copy code
  1. public class WaveBox {
  2.   private int x, y, bHeight, bWidth;
  3.   private float[] wave = new float[441];
  4.   private boolean inBounds = false;
  5.   
  6.   WaveBox(int xPos,int yPos,int boxWidth,int boxHeight){
  7.     x = xPos;
  8.     y = yPos;
  9.     bHeight = boxHeight;
  10.     bWidth = boxWidth;
  11.   }
  12.   
  13.   public void drawWave (){    
  14.     rect(x, y, bWidth, bHeight);    
  15.     pushMatrix();
  16.     translate(x, y + (bHeight/2));
  17.     line(0, 0, bWidth, 0);
  18.     fill(0);
  19.     
  20.     //draw quad for each wave position to use antialiasing
  21.     for (int i=0;i<440;i++){
  22.       quad(i, (wave[i]*(bHeight/2)), i, 0, i+1, 0, i+1, (wave[i+1]*(bHeight/2)));
  23.     }
  24.     popMatrix();
  25.     fill(255);
  26.   }

  27.   public void writeMouse(boolean drag)
  28.   {
  29.     int mouseDiff;
  30.     int absMouseDiff;
  31.     float mouseValDiff;
  32.     int inc, count = 0;
  33.     int adjMouseX, adjPMouseX, adjMouseY, adjPMouseY;
  34.     float yVal;

  35.     adjMouseX = mouseX - x;
  36.     adjPMouseX = pmouseX - x;
  37.     adjMouseY = mouseY - y;
  38.     adjPMouseY = pmouseY - y; 
  39.     
  40.     //if it is mousepress then write value if within bounds
  41.     if ((drag == false) && (withinBounds(mouseX, mouseY))){      
  42.       inBounds = true;
  43.       yVal = float((adjMouseY)-(drawHeight/2))/(drawHeight/2);
  44.       wave[adjMouseX] = yVal;
  45.       
  46.       //if it is a mousedrag then write however many values are needed
  47.     } else if ((drag) && (adjPMouseX != adjMouseX) && (inBounds)){
  48.       
  49.       //calc whether mouse has moved more then one pixel
  50.       mouseDiff = adjMouseX - adjPMouseX;
  51.       absMouseDiff = abs(mouseDiff);
  52.     
  53.       //determines whether the increment value is positive or negative
  54.       //i.e. whether the mouse is moving right or left
  55.       inc = mouseDiff / absMouseDiff;    
  56.       mouseValDiff = float(adjMouseY - adjPMouseY) / absMouseDiff;
  57.       
  58.       // loop through each position between the two mouse positions and
  59.       //interpolate values between them so every pixel has a value
  60.       while(count != mouseDiff){
  61.         count = count + inc;
  62.         if (((adjPMouseX + count) >= 0) && ((adjPMouseX + count) < (drawWidth))){
  63.           yVal = ((adjPMouseY + (mouseValDiff * abs(count))-(drawHeight/2))/(drawHeight/2));

  64.           //limit values between -1 and 1          
  65.           if (yVal > 1){
  66.             yVal = 1;
  67.           } else if (yVal< -1) {
  68.             yVal = -1;
  69.           }
  70.           
  71.           wave[adjPMouseX + count] = yVal;        
  72.         }
  73.       }      
  74.     }
  75.    wave[440] = wave[439]; 
  76.   }
  77.   
  78.   public void mouseEnd(){
  79.    inBounds = false;
  80.   } 
  81.   
  82.   //test if mouse position is within the box
  83.   private boolean withinBounds(int posX, int posY){
  84.     if ((posX >= x) && (posX < (x + bWidth)) && (posY >= y) && (posY < (y + bHeight))){
  85.       return true;
  86.     } else {
  87.       return false;
  88.     }
  89.   }  
  90. }

Replies(10)

Re: Anitaliasing?

2 years ago
without running and reading through your whole code. you should use beginShape(), endShape() and use vertex() to draw your wave

try this :


Copy code
  1. noFill();
  2. stroke(100);
  3. beginShape();
  4.     for (int i=0;i<440;i++){
  5.       vertex(i, wave[i]);
  6.     }
  7. endShape();
not sure how it looked like, but this is the way to go i would say

Re: Anitaliasing?

2 years ago
Thanks for that - I just gave it a go and it actually seems to be slightly worse than the method I'm already using :(
Below is the code including the change I made to test it - the fill doesn't work  as a side effect of the change, but I'm really only interested in the outside edge at the moment...

Copy code
  1. public class WaveBox {
  2.   private int x, y, bHeight, bWidth;
  3.   private float[] wave = new float[441];
  4.   private boolean inBounds = false;
  5.   
  6.   WaveBox(int xPos,int yPos,int boxWidth,int boxHeight){
  7.     x = xPos;
  8.     y = yPos;
  9.     bHeight = boxHeight;
  10.     bWidth = boxWidth;
  11.   }
  12.   
  13.   public void drawWave (){    
  14.     rect(x, y, bWidth, bHeight);    
  15.     pushMatrix();
  16.     translate(x, y + (bHeight/2));
  17.     line(0, 0, bWidth, 0);
  18.     fill(0);
  19.     
  20.     //draw quad for each wave position to use antialiasing
  21. //    for (int i=0;i<440;i++){
  22. //      quad(i, (wave[i]*(bHeight/2)), i, 0, i+1, 0, i+1, (wave[i+1]*(bHeight/2)));
  23. //    }
  24.     
  25.     beginShape();
  26.     for (int i=0;i<440;i++){
  27.       vertex(i, (wave[i]*(bHeight/2)));
  28.     }
  29.     endShape();
  30.     
  31.     popMatrix();
  32.     fill(255);
  33.   }

  34.   public void writeMouse(boolean drag)
  35.   {
  36.     int mouseDiff;
  37.     int absMouseDiff;
  38.     float mouseValDiff;
  39.     int inc, count = 0;
  40.     int adjMouseX, adjPMouseX, adjMouseY, adjPMouseY;
  41.     float yVal;

  42.     adjMouseX = mouseX - x;
  43.     adjPMouseX = pmouseX - x;
  44.     adjMouseY = mouseY - y;
  45.     adjPMouseY = pmouseY - y; 
  46.     
  47.     //if it is mousepress then write value if within bounds
  48.     if ((drag == false) && (withinBounds(mouseX, mouseY))){      
  49.       inBounds = true;
  50.       yVal = float((adjMouseY)-(drawHeight/2))/(drawHeight/2);
  51.       wave[adjMouseX] = yVal;
  52.       
  53.       //if it is a mousedrag then write however many values are needed
  54.     } else if ((drag) && (adjPMouseX != adjMouseX) && (inBounds)){
  55.       
  56.       //calc whether mouse has moved more then one pixel
  57.       mouseDiff = adjMouseX - adjPMouseX;
  58.       absMouseDiff = abs(mouseDiff);
  59.     
  60.       //determines whether the increment value is positive or negative
  61.       //i.e. whether the mouse is moving right or left
  62.       inc = mouseDiff / absMouseDiff;    
  63.       mouseValDiff = float(adjMouseY - adjPMouseY) / absMouseDiff;
  64.       
  65.       // loop through each position between the two mouse positions and
  66.       //interpolate values between them so every pixel has a value
  67.       while(count != mouseDiff){
  68.         count = count + inc;
  69.         if (((adjPMouseX + count) >= 0) && ((adjPMouseX + count) < (drawWidth))){
  70.           yVal = ((adjPMouseY + (mouseValDiff * abs(count))-(drawHeight/2))/(drawHeight/2));

  71.           //limit values between -1 and 1          
  72.           if (yVal > 1){
  73.             yVal = 1;
  74.           } else if (yVal< -1) {
  75.             yVal = -1;
  76.           }
  77.           
  78.           wave[adjPMouseX + count] = yVal;        
  79.         }
  80.       }      
  81.     }
  82.    wave[440] = wave[439]; 
  83.   }
  84.   
  85.   public void mouseEnd(){
  86.    inBounds = false;
  87.   } 
  88.   
  89.   //test if mouse position is within the box
  90.   private boolean withinBounds(int posX, int posY){
  91.     if ((posX >= x) && (posX < (x + bWidth)) && (posY >= y) && (posY < (y + bHeight))){
  92.       return true;
  93.     } else {
  94.       return false;
  95.     }
  96.   }  
  97. }

Re: Re: Anitaliasing?

2 years ago
would you mind posting a screenshot. of your first sketch. Its hard to guess how it should look like as we can not run it.

Re: Anitaliasing?

2 years ago
Sure - image below. I'll check the code I posted to see if I can find out why it won't work!




Re: Anitaliasing?

2 years ago
Copied and pasted the code from the first example into a new sketch and it seemed to work fine....

Re: Re: Anitaliasing?

2 years ago
sorry, you are right, it works fine.
i thought it relies on your maxMps setup. worry my fault.

Re: Anitaliasing?

2 years ago
Hi Cedric,

I've got a reply through my eMail, but it doesn't seem to have shown up in the forum - I'll reply here anyway - hope that's OK!

Thanks for the suggestions - the way it looks after implementing your changes is exactly how I want it:



Unfortunately only reading every fourth sample won't work as it needs to pick up large changes to individual samples which get missed using this method. As you can see reading every sample results in the same problems I'm already having:


I agree - the fundamental problem is that I have a lot of data points (440) and not enough pixels to spread them over! Unfortunately I can't reduce the number of points without starting to compromise the audio quality. I was hoping there might be some other solution that would enable me to antialias the line once it had been drawn or something. I'm pretty sure I need some kind of workaround rather than relying on the smooth() function working directly on a line. Perhaps I want too much!

The reason I changed renderer is because I was getting some very strange results using JAVA2D. The image below uses exactly the same code as the image posted in a previous post but with JAVA2D instead of P2D:




Re: Anitaliasing?

2 years ago
hmm strange, dont know where my post went.

but you are right. its hard to interpolate between points that are just 1 pixel away.
and i realized that changing the renderer only improves it using my approach not yours.
hmm not sure what else to test...


Re: Anitaliasing?

2 years ago
So I've been playing around with this for a while now and I've decided that the best results are to use vertex to draw the shape using strokeWidth(2) and using P2D as the renderer (it seems to antialias much better than JAVA2D in this context, although there are some strange artefacts here and there). Actually, even though it's not perfect, the results are pretty decent and will certainly do for the time being:




Many thanks for all your help Cedric, it's much appreciated!

Tim

Re: Anitaliasing?

2 years ago
you are welcome.
I think its a good result...
compared to the rest definitely the best