Aliasing problem on equation drawn curves / roses

Hello All,

Not sure if I’m doing this the right way, but I made this simple program i would like to improve (it’s a simple “clock” made of math roses). Each rose is drawn point by point as it’s the only way I could think of to use the rose equation. The problem is it results in a lot of aliasing.
Do you know a way I can solve this problem ?

Thank you for your help,

Pablo

//Curve Clock, pablocavero.net,curveclock.com
//A simple clock made of roses
#include "ofApp.h"
//--------------------------------------------------------------
void ofApp::setup(){
  steps=5000;//number of steps to draw roses
  th=2; // thickness of line to draw roses (has to be > 2)

  inc = 3*PI/steps;//incrementation of T radian angle at each step-- full path of the roses should never be longer than 2PI but after noticing some small cuts, this makes sure the line isn't interrupted in any case - (could be improved)
  T=0;// Teta, angle in radians (0 to 2PI), start of roses polar equation
  width=ofGetScreenWidth();
  height=ofGetScreenHeight();
  rd = width/12-(width*0.1/12); // roses radius - radius and position determine relatively to page width
  ofHideCursor();
  ofBackground(0);
  ofSetColor(255,255,255);
}

//--------------------------------------------------------------
void ofApp::update(){
  // get time values and separate tens and units
  // (small lag on IBM laptop with this method, but perfect on RPI)
  h = ofGetHours();
  ht = (h-h%10)/10;
  hu = h%10;
  m = ofGetMinutes();
  mt = (m-m%10)/10;
  mu = m%10;
  s = ofGetSeconds();
  st = (s-s%10)/10;
  su = s%10;
}

//--------------------------------------------------------------
void ofApp::draw(){
  //draws each rose at its position, relatively to screen width
  drawRose(int(width*1/12),height/2,ht);
  drawRose(int(width*3/12),height/2,hu);
  drawRose(int(width*5/12),height/2,mt);
  drawRose(int(width*7/12),height/2,mu);
  drawRose(int(width*9/12),height/2,st);
  drawRose(int(width*11/12),height/2,su);
}

void ofApp::drawRose(int xoffset, int yoffset, int indice){
  for(int i=0 ; i < steps ; i++) {
    r = cos(indice*T)*rd ; // roses equation (multiplied by radius because -1<= r <=1 ) 
    x = int(r*cos(T)) ;// conversion from polar to cartesian coordinates
    y = int(r*sin(T)) ;
    ofDrawEllipse(x+xoffset,y+yoffset,th,th) ;//draws point for this step
    T = T+inc ; //increment Teta angle for next step
  }
}

Hi, one thing to try could be to instead of drawing a hard ellipse, draw an image of a circle with a slight blur around the edges.

Another would be to do something like this (untested, so there might be typos etc) to draw a continuous line instead of points:

void ofApp::drawRose(int xoffset, int yoffset, int indice)
{
  ofMesh tmpMesh;
  tmpMesh.setMode( OF_PRIMITIVE_LINE_STRIP );
  for(int i=0 ; i < steps ; i++) 
  {
    r = cos(indice*T)*rd ; // roses equation (multiplied by radius because -1<= r <=1 ) 
    x = int(r*cos(T)) ;// conversion from polar to cartesian coordinates
    y = int(r*sin(T)) ;
    tmpMesh.addVertex( x+xoffset,y+yoffset ); 
    //ofDrawEllipse(x+xoffset,y+yoffset,th,th) ;//draws point for this step
    T = T+inc ; //increment Teta angle for next step
  }
  tmpMesh.draw();
}

If your OpenGL context is initialised with multisampling the lines will look relatively smooth.

Hi @hahakid, thanks a lot for your answer, it’s better now.

however it would be best if I could get even better results (I would like the lines to be very smooth).

How do you check that OpenGL is initialised with multisampling ?
Here is my main.cpp

#include "ofMain.h"
#include "ofApp.h"
int main( ){
  ofSetupOpenGL(1024,768,OF_FULLSCREEN); 
        ofRunApp(new ofApp());
}

I wonder if there is a way to make a pure vector drawing (e.g. with bezier curves) in OF.
If anyone has an idea, I would be very interested to know how to do it, as of coding as much as of mathematical conversion of the equation.
Theorically, I guess that would give the best possible result in terms of aliasing, wouldn’t it ?

Of your two options, I prefered trying the ofMesh way because I wanted to be able to manage every parameter in the app since it might be connected to different video projectors of different resolutions and I might want to change the thickness of the line. If I made an image, I would probably have had to make different images of circle that have different ratio of circle size/blur size.

Also in case anyone reading this is interested, in order to have the ofMesh working I had to convert coordinates with ofPoint:
tmpMesh.addVertex(ofPoint(x+xoffset,y+yoffset,0));
And I control line thickness with:
ofSetLineWidth(th);

thanks,
pablo

multisampling is enabled by default.

you can use beziers using ofPath. opengl can’t directly draw beziers so ofPath will decompose them in line segments for you with the resolution you specify which can be adjusted as many times as you need using setCurveResolution.

thank you Arturo.

I tried it but the conversion from the polar equation to beziers is complex and beyond my knowledge of geometry. I started with just 4 curves all connected in the center, but it is impossible to get the right shape this way…

I also tried the method of a blured image of a circle, but it still generates a lot of aliasing.

Thanks for your time, if anyone has another idea I’m still looking for another possibility… !
have a good day :slight_smile:

Hi pablo,
I think the main problem is that you use too many points for your shapes.

try this - it uses 10 times less steps:

void ofApp::drawRose(int xoffset, int yoffset, int indice) {
	vector<ofPoint> pts; //makes an array of points
	for (int i = 0; i < steps; i+=10) {  //notice the step size is 10
		float r = cos(indice*T)*rd; // roses equation (multiplied by radius because -1<= r <=1 ) 
		int x = int(r*cos(T));// conversion from polar to cartesian coordinates
		int y = int(r*sin(T));
		//ofDrawEllipse(x + xoffset, y + yoffset, th, th);//draws point for this step
		pts.push_back(ofPoint(x + xoffset, y + yoffset)); //puts the coordinates into the point array
		T = T + inc*10; //increment Teta angle for next step.
                //increment is multiplied by 10 to make up for the increase in step size
	}
	ofPolyline cp(pts); //makes a polyline from the points
	cp.simplify(); //mysteriously my system wouldn't render correctly after a few frames without this..
	ofSetLineWidth(2.0f); // here you set the thickness of the lines
	cp.draw(); //draws the curve
}

hope this helps,
cheers!
AA

Here’s an even better version. It prevents path overlap - which was partially responsible for the jagged look - and uses even less vertices;

void ofApp::setup(){
	ofEnableAntiAliasing();
	ofEnableSmoothing();
	steps = 50;//number of steps to draw roses
			//increment was moved to the drawrose() proceedure. preventing overlap increment was indice dependent
	th = 2; // thickness of line to draw roses (has to be > 2)
	T = 0;// Teta, angle in radians (0 to 2PI), start of roses polar equation
	width = ofGetScreenWidth();
	height = ofGetScreenHeight();
	rd = width / 12 - (width*0.1 / 12); // roses radius - radius and position determine relatively to page width
	//ofHideCursor();
	ofBackground(0);
	ofSetColor(255, 255, 255);

	ofSetFrameRate(1); //you don't really need more frames
}

void ofApp::drawRose(int xoffset, int yoffset, int indice) {
	vector<ofPoint> pts;

	// prevent self-overlaping curves in a coditional
	if (indice % 2 == 0) {	inc = 2 * PI / (indice + 1) / (steps); }
	else { inc = PI / (indice + 1) / (steps); }

	T = 0; // reset T to prevent value being inherited from previous rose
	for (int i = 0; i < steps+3; i++) { //ste[s+3 is required to close the shape with ofCurveVertices();

		float r = cos(indice*(indice + 1)*T)*rd; // roses equation (multiplied by radius because -1<= r <=1 ) 
		int x = int(r*cos((indice + 1)*T));// conversion from polar to cartesian coordinates
		int y = int(r*sin((indice + 1)*T));
		
		pts.push_back(ofPoint(x + xoffset, y + yoffset));
		T = T + inc; //increment Teta angle for next step
	}
	ofNoFill(); //draws only the lines in the following shape
	ofSetLineWidth(3.0f); //this is how thick the line is
	ofBeginShape(); //this is what really draws the roses
	     ofCurveVertices(pts);
	ofEndShape();
}

Hope this helps.
Cheers!
AA

Hi @adlarchitetto
Thank you very much.I didn’t know it was possible to draw this way. The classes ofPolyline and ofPath are very interesting with curveTo, I got to explore this a bit.
And now that it’s actual curves that are drawn, the antialiasing works, which solves my problem.
Thanks a lot, and cheers !
Pablo