# What's possible with ellipse()?

I play around with celestial navigation and processing to visualize things, some involve lots of trig. What I’ve used so far is shapes3d to build spheres, as in celestial sphere or earth sphere, then plot points for stars or observer location. This is great and looks cool, but… It has limitations that I’m trying to overcome, the biggest is my understanding of things…

Its certainly been a good trip and also led me to this site:

https://en.wikipedia.org/wiki/Circle_of_equal_altitude

And to this site for algorithms:

https://sites.google.com/site/navigationalalgorithms/papersnavigation

Sometimes a circle of equal altitude looks egg shaped on a map or oval or even will look like a sine wave and not a closed circle. That’s what I wonder if ellipse() function can be used to draw these circles. Ive tried it with just using a straight up circle, but that’s not correct.

Here are two images, the first shows how circles look on a flat map. The second image shows how these identical points look on a sphere. .

What I wonder is it possible to get the same oblong, egg, quasi rectangular, sine waveish type “circles” by using the processing ellipse() function?

It is possible to use `ellipse()` to put a ring around a sphere. But I don’t think it is the right function for doing it on the distorted flat map. I honestly doubt it is even possible.

Instead, I would suggest you look into using this:

https://processing.org/reference/bezierVertex_.html

I wasn’t totally clear how I am doing it right now. I’m using the algorithm from the link. Its kind of messy and so I wondered if processing had something better but accomplishes the same thing.

I’ll look at your Bezier hint and see if that has the flexibility. Thanks.

What makes me wonder about PShapes like ellipse and Bezier is they are very good at drawing, compared to the algorithm I’m using which is very good at calculating and transforming.

When I use ellipse to draw angle data coming in real time over USB in android, it works smoothly.

Could I extend PShapes to use this CoP algorithm?

Hi HackinHarry,

One way to go would be to subdivide your circle in x points.
Then you apply the transformation to each one of those points in order to know their position in the flat model.
Finally, you use curveVertex to link all your points together.
You should get a pretty good approximation of your curves

Using curveVertex allow you to draw Catmull-Rom splines. The big advantages you have compare to bezier curves is that the spline go through all of your points.

4 Likes

Yes, the aha moment. I see it. I gotta google Catmull-Rom splines, big Thanks.

That worked!! I’ll post some code soon. I tried to control it with mouseY and it worked a few times and then locked up. The “approximation” is as good as any.

Here’s the code using the @jb4x suggestion,Thanks It works now. It was locking up trying to println.

``````

float[] x = new float[3600];
float[] y = new float[3600];

// double GHA = Math.toRadians(162.3366651);
double GHA = Math.toRadians(-38.6552);
// double dec = Math.toRadians(42);
double dec = Math.toRadians(62);
//    double dec = mouseY;
double Be = Math.toRadians(35.4000);
double Le = Math.toRadians(26.452837);

double[]vv = new double[3]; //, vy[3], vyz[3];
double[]vy = new double[3];
double[]vyz = new double[3];
double[]wpt = new double[3600];
float[]WPT = new float[3600];

double[][]My = new double[3][3];

double[][]Mz = new double[3][3];

void setup() {
size(400, 300, P3D);
smooth();
noFill();

}

void draw() {
background(0);
pushMatrix();
dec = map(mouseY,height-height,height,90,-90);
println(dec);
x = new float[3600];
y = new float[3600];

//  noLoop();
getPoints();
popMatrix();

}

void getPoints(){

Mz = Rz(Math.toRadians(360.0) - GHA, Mz);

// println(Mz[0][0] + " " + Mz[0][1] + " " + Mz[0][2]);
// println(Mz[1][0] + " " + Mz[1][1] + " " + Mz[1][2]);
// println(Mz[2][0] + " " + Mz[2][1] + " " + Mz[2][2]);

My =  Ry(Math.toRadians(90.0) - dec, My);

//  println(My[0][0] + " " + My[0][1] + " " + My[0][2]);
// println(My[1][0] + " " + My[1][1] + " " + My[1][2]);
// println(My[2][0] + " " + My[2][1] + " " + My[2][2]);

int w = 0;

for( double L0 = -180.0; L0 <= 180.0; L0 += .1 )
{
//float alt = map(mouseX,width-width,width,0,90);
vv =  VectorSpherical2Cartesian(Math.toRadians(62.5),Math.toRadians(L0) );
// vv =  VectorSpherical2Cartesian(alt,Math.toRadians(L0) );

vy =  MatrixVecProd( My, vv, vy );

vyz =  MatrixVecProd( Mz, vy, vyz );

wpt[w] = C2ELat( vyz[0], vyz[1], vyz[2]);
wpt[w+1] = C2ELon( vyz[0], vyz[1], vyz[2]);

WPT = toFloatArray(wpt);

x[w] = map(WPT[w+1],radians(-180) ,radians(180),width, width - width);
y[w] = map(WPT[w],radians(-90),(radians(90)),height,height - height);
point(x[w],y[w]);
noFill();
stroke(0,255,0);
strokeWeight(2);
beginShape();
curveVertex(x[w],y[w]);
curveVertex(x[w],y[w]);
endShape();
}
}

//Funcition to convert double[] to float[]
float[] toFloatArray(double[] arr) {
if (arr == null) return null;
int n = arr.length;
float[] ret = new float[n];
for (int i = 0; i < n; i++) {
ret[i] = (float)arr[i];
}
return ret;
}
// end of function to convert double[] to float[]

double[] VectorSpherical2Cartesian(double B, double L){

double v[] = new double[3];
v[0] = Math.cos(B) * Math.cos(L);
v[1] = Math.cos(B) * Math.sin(L);
v[2] = Math.sin(B);
//   println(B);
//   println(L);
return(v);

}

public double C2ELat( double x, double y, double z )
{
double[]res = new double[3];
res[0] = Math.sqrt( x*x+y*y+z*z);  //R
//*B = ASIN(z/(*R));
res[1] = Math.atan2( z, Math.sqrt(x*x+y*y) ); //B
res[2] = Math.atan2( y, x ); //L

//println("R:  " + (res[0]) + "  B: " + Math.toDegrees(res[1]) + "  L: " + Math.toDegrees(res[2]));

return (res[1]);
//println(R);
}

public double C2ELon( double x, double y, double z )
{
double[]res = new double[3];
res[0] = Math.sqrt( x*x+y*y+z*z);  //R
//*B = ASIN(z/(*R));
res[1] = Math.atan2( z, Math.sqrt(x*x+y*y) ); //B
res[2] = Math.atan2( y, x ); //L

//println("R:  " + (res[0]) + "  B: " + Math.toDegrees(res[1]) + "  L: " + Math.toDegrees(res[2]));

return (res[2]);
//println(R);
}

//public double[] E2C( double B, double L, double R, double x, double y, double z )

public double[] E2C( double B, double L, double R )
{
double[]res = new double[3];

res[0] = R*Math.cos((B))*Math.cos((L));
res[1] = R*Math.cos((B))*Math.sin((L));
res[2] = R*Math.sin((B));

// println(res[0] + " " + res[1] + " " + res[0]);

return(res);
}

public double[][] Rx( double a, double[][] M ){

M[0][0] = 1.0;
M[1][0] = 0.0;
M[2][0] = 0.0;
M[0][1] = 0.0;
M[1][1] = Math.cos(a); //Math.cos(Math.toRadians(a));
M[2][1] = Math.sin(a); //Math.sin(Math.toRadians(a));
M[0][2] = 0.0;
M[1][2] = -Math.sin(a); //-Math.sin(Math.toRadians(a));
M[2][2] = Math.cos(a); //Math.cos(Math.toRadians(a));

return(M);
}

public double[][] Ry( double a, double[][] M ){

M[0][0] = Math.cos(a);
M[1][0] = 0.0;
M[2][0] = -Math.sin(a);
M[0][1] = 0.0;
M[1][1] = 1.0;
M[2][1] = 0.0;
M[0][2] = Math.sin(a);
M[1][2] = 0.0;
M[2][2] = Math.cos(a);

return(M);
}

public double[][] Rz( double a, double[][] M ){

M[0][0] = Math.cos(a); //Math.cos(a);
M[1][0] = Math.sin(a);
M[2][0] = 0.0;
M[0][1] = -Math.sin(a);
M[1][1] = Math.cos(a);
M[2][1] = 0.0;
M[0][2] = 0.0;
M[1][2] = 0.0;
M[2][2] = 1.0;

return(M);
}

public double[] MatrixVecProd( double[][] A, double[] v, double[] res ) {

int i,j;
int n = 3;

for( i=0; i<n; i++ ) {
res[i] = 0.0;
for( j=0; j<n; j++ ) {
res[i] += A[i][j]*v[j];

}
}

return (res);
}
``````
2 Likes

If you go to the getPoints() function and uncomment float alt and swap comments on vv, And set the GHA to 0 in the variable area before setup(), it looks very good and symmetrical. This allows mouseX and mouseY to control the GHA and Altitude and you can see how it changes the shape of the circle. Another thing to do is change the dimensions of the size() and see how the shapes change.

I’m currently trying to use textureSphere and mouseX and mouseY to show how the circle will move around the globe. OneDrive screwed up my preferences.txt file, so I’m trying to fix that so it will see my libraries.

Hi HackinHarry,

It seems that the mouse movement is too sensible, so the movements of your lines are not really smooth.

Where I use map() to map the mouse to the angles needed, I reduced the range of angles that get mapped to a smaller range 15 to -15 and it is smoother. Thanks @ jb4x

``````  dec = map(mouseY,height-height,height,15,-15);

float alt = map(mouseX,width-width,width,0,30);
``````

I see that this topic got moved to project guidance for good reason as this is part of a project I’ve been working on for some time. The project is to use an IMU connected by USB to android and use the pitch and yaw as inputs for GHA and altitude… It’s current state is it can read USB, use the data as input to calculate the circle of equal altitude, display those circles on a sphere. 3 of those circles intersecting is a position fix. It now does that.

Where it needs to go is allow selection of a navigational star with an altitude some nice angle… This would be preferred as the IMU is most accurate below about 80 degrees in pitch. Above that and it wants to gimbal lock and get all goofy.

Another feature that would be great is to switch between sphere display and map and zoom to see where the fix is.

As a related side note, way back when celestial navigation was new, accurate chronometers were new, somebody had the idea to use a really big globe and mark the observations and circles of equal altitude on that globe. That’s a good idea but accuracy required a ginormous globe. With a computer its now possible to realistically deploy that concept…

I used the code from above to add a circle to a rendered 3D sphere. Those funny looking circles on a flat map look like real circles on a sphere. I borrowed a slider class from some example called Kepler2012. I draw the circle using thread() and I read the warning in the thread() documentation about not using that function to draw or will get inconsistent results. I don’t know how else to do it though.

``````/*
The sphere is drawn using the example from processing/example/texturesphere
The green circle is drawn based on code from Andres Ruiz
I dont claim anything other than I fused it all together

*/

PImage texmap;
PGraphics texture;

float[] x = new float[3600];
float[] y = new float[3600];

// double GHA = Math.toRadians(162.3366651);
double GHA = Math.toRadians(100);
// double dec = Math.toRadians(42);
double dec = Math.toRadians(-32);
//    double dec = mouseY;
double Be = Math.toRadians(35.4000);
double Le = Math.toRadians(26.452837);

double[]vv = new double[3]; //, vy[3], vyz[3];
double[]vy = new double[3];
double[]vyz = new double[3];
double[]wpt = new double[3600];
float[]WPT = new float[3600];

double[][]My = new double[3][3];

double[][]Mz = new double[3][3];

int sDetail = 65;  // Sphere detail setting
float rotationX = 0;
float rotationY = 0;
float velocityX = 0;
float velocityY = 0;
//float globeRadius = 600;
float pushBack = -400;

float globeRadius = 600;
float[] cx, cz, sphereX, sphereY, sphereZ;
float sinLUT[];
float cosLUT[];
float SINCOS_PRECISION = 0.5;

int SINCOS_LENGTH = int(360.0 / SINCOS_PRECISION);

color [] colors = new color[7];

float alt = radians(-77.79f);
//Controls controls;
boolean released = false;

Controls controls;
HorizontalControl controlX;
int showControls;
boolean draggingZoomSlider = false;

float zoom = -90.0f;
float tzoom = -100.0f;

void setup() {
size(400, 300, P3D);
//fullScreen(P3D);

smooth();
noFill();
texmap = loadImage("world32k.jpg");
//  texmap = loadImage("alphatest.png");
texture = createGraphics(texmap.width, texmap.height);
controls = new Controls();
controlX = new HorizontalControl();
showControls = 1;
initStars();

getPoints();
initializeSphere(sDetail);
}

void draw() {
background(0);

x = new float[3600];
y = new float[3600];

if (mousePressed) {
if( (showControls == 1) && (controls.isZoomSliderEvent(mouseX, mouseY))){ // || ( showControls == 1 && controlX.isZoomSliderEvent(mouseX,mouseY))) {
draggingZoomSlider = true;

zoom = controls.getZoomValue(mouseY);

//    tzoom = controlX.getZoomValue(mouseX);

// MousePress - Rotation Adjustment
}/* else if (!draggingZoomSlider) {
//  if (released != false){
velocityX += (mouseY-pmouseY) * 0.01;
velocityY -= (mouseX-pmouseX) * 0.01;
//  }
} */

else if ( showControls == 1 && controlX.isZoomSliderEvent(mouseX,mouseY)){
//  draggingZoomSlider = true;
tzoom = controlX.getZoomValue(mouseX);
// MousePress - Rotation Adjustment
} else if (!draggingZoomSlider) {
if (released = true){
velocityX += (mouseY-pmouseY) * 0.01;
velocityY -= (mouseX-pmouseX) * 0.01;
}

}

}

println(degrees((float)alt));

// initStars();
renderGlobe();
controls.updateZoomSlider(zoom);
controlX.updateZoomSlider(tzoom);
controls.render();

controlX.render();

}

void mouseReleased() {
//released = false;
draggingZoomSlider = false;
if (released == true){
alt = (map(zoom,texture.height-texture.height,texture.height,radians(-90),radians(90)));
// GHA = (map(tzoom, texture.width - texture.width, texture.width, radians(-180),radians(180)));
GHA = (map(tzoom,texture.width - texture.width,texture.width, radians(-180),radians(180)));
initStars();
initializeSphere(sDetail);
thread( "getPoints");
renderGlobe();
}
}

boolean getPoints(){
released = false;
Mz = Rz(Math.toRadians(360.0) - GHA, Mz);

My =  Ry(Math.toRadians(90.0) - dec, My);

int w = 0;

for( double L0 = -180.0; L0 <= 180.0; L0 += .1 )
{

vv =  VectorSpherical2Cartesian(alt,Math.toRadians(L0) );

vy =  MatrixVecProd( My, vv, vy );

vyz =  MatrixVecProd( Mz, vy, vyz );

wpt[w] = C2ELat( vyz[0], vyz[1], vyz[2]);
wpt[w+1] = C2ELon( vyz[0], vyz[1], vyz[2]);

WPT = toFloatArray(wpt);

x[w] = map(WPT[w+1],radians(-180) ,radians(180),texture.width, texture.width - texture.width);
y[w] = map(WPT[w],radians(-90),(radians(90)),texture.height,texture.height - texture.height);

texture.beginDraw();
texture.point(x[w],y[w]);
texture.noFill();
texture.stroke(0,255,0);
texture.strokeWeight(3);
texture.beginShape();
texture.curveVertex(x[w],y[w]);
texture.curveVertex(x[w],y[w]);
texture.endShape();
texture.endDraw();
released = true;
}
w++;
return released;
}

//Funcition to convert double[] to float[]
float[] toFloatArray(double[] arr) {
if (arr == null) return null;
int n = arr.length;
float[] ret = new float[n];
for (int i = 0; i < n; i++) {
ret[i] = (float)arr[i];
}
return ret;
}
// end of function to convert double[] to float[]

double[] VectorSpherical2Cartesian(double B, double L){

double v[] = new double[3];
v[0] = Math.cos(B) * Math.cos(L);
v[1] = Math.cos(B) * Math.sin(L);
v[2] = Math.sin(B);

return(v);

}

public double C2ELat( double x, double y, double z )
{
double[]res = new double[3];
res[0] = Math.sqrt( x*x+y*y+z*z);  //R
//*B = ASIN(z/(*R));
res[1] = Math.atan2( z, Math.sqrt(x*x+y*y) ); //B
res[2] = Math.atan2( y, x ); //L

return (res[1]);

}

public double C2ELon( double x, double y, double z )
{
double[]res = new double[3];
res[0] = Math.sqrt( x*x+y*y+z*z);  //R
res[1] = Math.atan2( z, Math.sqrt(x*x+y*y) ); //B
res[2] = Math.atan2( y, x ); //L

return (res[2]);

}

public double[] E2C( double B, double L, double R )
{
double[]res = new double[3];

res[0] = R*Math.cos((B))*Math.cos((L));
res[1] = R*Math.cos((B))*Math.sin((L));
res[2] = R*Math.sin((B));

return(res);
}

public double[][] Rx( double a, double[][] M ){

M[0][0] = 1.0;
M[1][0] = 0.0;
M[2][0] = 0.0;
M[0][1] = 0.0;
M[1][1] = Math.cos(a); //Math.cos(Math.toRadians(a));
M[2][1] = Math.sin(a); //Math.sin(Math.toRadians(a));
M[0][2] = 0.0;
M[1][2] = -Math.sin(a); //-Math.sin(Math.toRadians(a));
M[2][2] = Math.cos(a); //Math.cos(Math.toRadians(a));

return(M);
}

public double[][] Ry( double a, double[][] M ){

M[0][0] = Math.cos(a);
M[1][0] = 0.0;
M[2][0] = -Math.sin(a);
M[0][1] = 0.0;
M[1][1] = 1.0;
M[2][1] = 0.0;
M[0][2] = Math.sin(a);
M[1][2] = 0.0;
M[2][2] = Math.cos(a);

return(M);
}

public double[][] Rz( double a, double[][] M ){

M[0][0] = Math.cos(a); //Math.cos(a);
M[1][0] = Math.sin(a);
M[2][0] = 0.0;
M[0][1] = -Math.sin(a);
M[1][1] = Math.cos(a);
M[2][1] = 0.0;
M[0][2] = 0.0;
M[1][2] = 0.0;
M[2][2] = 1.0;

return(M);
}

public double[] MatrixVecProd( double[][] A, double[] v, double[] res ) {

int i,j;
int n = 3;

for( i=0; i<n; i++ ) {
res[i] = 0.0;
for( j=0; j<n; j++ ) {
res[i] += A[i][j]*v[j];

}
}

return (res);
}

void initStars(){

texture.beginDraw();
texture.background(texmap);
texture.endDraw();
}

void initializeSphere(int res)
{
sinLUT = new float[SINCOS_LENGTH];
cosLUT = new float[SINCOS_LENGTH];

for (int i = 0; i < SINCOS_LENGTH; i++) {
sinLUT[i] = (float) Math.sin(i * DEG_TO_RAD * SINCOS_PRECISION);
cosLUT[i] = (float) Math.cos(i * DEG_TO_RAD * SINCOS_PRECISION);
}

float delta = (float)SINCOS_LENGTH/res;
float[] cx = new float[res];
float[] cz = new float[res];

// Calc unit circle in XZ plane
for (int i = 0; i < res; i++) {
cx[i] = -cosLUT[(int) (i*delta) % SINCOS_LENGTH];
cz[i] = sinLUT[(int) (i*delta) % SINCOS_LENGTH];
}

// Computing vertexlist vertexlist starts at south pole
int vertCount = res * (res-1) + 2;
int currVert = 0;

// Re-init arrays to store vertices
sphereX = new float[vertCount];
sphereY = new float[vertCount];
sphereZ = new float[vertCount];
float angle_step = (SINCOS_LENGTH*0.5f)/res;
float angle = angle_step;

// Step along Y axis
for (int i = 1; i < res; i++) {
float curradius = sinLUT[(int) angle % SINCOS_LENGTH];
float currY = -cosLUT[(int) angle % SINCOS_LENGTH];
for (int j = 0; j < res; j++) {
sphereX[currVert] = cx[j] * curradius;
sphereY[currVert] = currY;
sphereZ[currVert++] = cz[j] * curradius;
}
angle += angle_step;
}
sDetail = res;
}

// Generic routine to draw textured sphere
void texturedSphere(float r, PGraphics t) {
int v1,v11,v2;
r = (r + 240 ) * 0.33;
beginShape(TRIANGLE_STRIP);
texture(t);
float iu=(float)(t.width-1)/(sDetail);
float iv=(float)(t.height-1)/(sDetail);
float u=0,v=iv;
for (int i = 0; i < sDetail; i++) {
vertex(0, -r, 0,u,0);
vertex(sphereX[i]*r, sphereY[i]*r, sphereZ[i]*r, u, v);
u+=iu;
}
vertex(0, -r, 0,u,0);
vertex(sphereX[0]*r, sphereY[0]*r, sphereZ[0]*r, u, v);
endShape();

// Middle rings
int voff = 0;
for(int i = 2; i < sDetail; i++) {
v1=v11=voff;
voff += sDetail;
v2=voff;
u=0;
beginShape(TRIANGLE_STRIP);
texture(t);
for (int j = 0; j < sDetail; j++) {
vertex(sphereX[v1]*r, sphereY[v1]*r, sphereZ[v1++]*r, u, v);
vertex(sphereX[v2]*r, sphereY[v2]*r, sphereZ[v2++]*r, u, v+iv);
u+=iu;
}

// Close each ring
v1=v11;
v2=voff;
vertex(sphereX[v1]*r, sphereY[v1]*r, sphereZ[v1]*r, u, v);
vertex(sphereX[v2]*r, sphereY[v2]*r, sphereZ[v2]*r, u, v+iv);
endShape();
v+=iv;
}
u=0;

// Add the northern cap
beginShape(TRIANGLE_STRIP);
texture(t);
for (int i = 0; i < sDetail; i++) {
v2 = voff + i;
vertex(sphereX[v2]*r, sphereY[v2]*r, sphereZ[v2]*r, u, v);
vertex(0, r, 0,u,v+iv);
u+=iu;
}
vertex(sphereX[voff]*r, sphereY[voff]*r, sphereZ[voff]*r, u, v);
endShape();

}

void renderGlobe() {

pushMatrix();
translate(width/2, height/2, pushBack);
pushMatrix();
noFill();
stroke(255,200);
strokeWeight(2);
smooth();
popMatrix();
lights();
pushMatrix();

rotateX( radians(-rotationX) );

rotateY( radians( - rotationY) );

fill(200);
noStroke();
textureMode(IMAGE);
texturedSphere(globeRadius, texture);
popMatrix();
popMatrix();
rotationX += velocityX;
rotationY += velocityY;
velocityX *= 0.95;
velocityY *= 0.95;

}
``````

This code below is the Control class:

``````/*

Kepler Visualization - Controls

GUI controls added by Lon Riesberg, Laboratory for Atmospheric and Space Physics
lon@ieee.org

April, 2012

Current release consists of a vertical slider for zoom control.  The slider can be toggled
on/off by pressing the 'c' key.

Slide out controls that map to the other key bindings is currently being implemented and
will be released soon.

*/

class Controls {

int barWidth;
int barX;                          // x-coordinate of zoom control
int minY, maxY;                    // y-coordinate range of zoom control
float minZoomValue, maxZoomValue;  // values that map onto zoom control
float valuePerY;                   // zoom value of each y-pixel
int sliderY;                       // y-coordinate of current slider position
float sliderValue;                 // value that corresponds to y-coordinate of slider
int sliderWidth, sliderHeight;
int sliderX;                       // x-coordinate of left-side slider edge

Controls () {

barX = 40;
barWidth = 15;

minY = 40;
maxY = minY + height/3 - sliderHeight/2;

minZoomValue = height - height;
maxZoomValue = height;   // 300 percent
valuePerY = (maxZoomValue - minZoomValue) / (maxY - minY);

sliderWidth = 25;
sliderHeight = 10;
sliderX = (barX + (barWidth/2)) - (sliderWidth/2);
sliderValue = minZoomValue;
sliderY = minY;
}

void render() {

// strokeWeight(1.5);
strokeWeight(1);
//  stroke(105, 105, 105);  // fill(0xff33ff99);
//   stroke(0xff33ff99);  // fill(0xff33ff99);  0xffff0000
stroke(0xffff0000);

// zoom control bar
fill(0, 0, 0, 0);

rect(barX, minY, barWidth, maxY-minY);

// slider
// fill(105, 105, 105); //0x3300FF00
fill(0xffff0000); // 0xff33ff99//0x3300FF00
rect(sliderX, sliderY, sliderWidth, sliderHeight);
}

float getZoomValue(int y) {
if ((y >= minY) && (y <= (maxY - sliderHeight/2))) {
sliderY = (int) (y - (sliderHeight/2));
if (sliderY < minY) {
sliderY = minY;
}
sliderValue = (y - minY) * valuePerY + minZoomValue;
}
return sliderValue;
}

void updateZoomSlider(float value) {
int tempY = (int) (value / valuePerY) + minY;
if ((tempY >= minY) && (tempY <= (maxY-sliderHeight))) {
sliderValue = value;
sliderY = tempY;
}
}

boolean isZoomSliderEvent(int x, int y) {
int slop = 50;  // number of pixels above or below slider that's acceptable.  provided for ease of use.
int sliderTop = (int) (sliderY - (sliderHeight/2)) - slop;
int sliderBottom = sliderY + sliderHeight + slop;
return ((x >= sliderX) && (x <= (sliderX    + sliderWidth)) && (y >= sliderTop)  && (y <= sliderBottom) || draggingZoomSlider );
}
}
``````

This is the HorizontalControl class. I don’t know how else to add a horizontal slider.

``````
/*
I modified this so the slider is horizontal.  That gives me a vertical for
tweaking altitude and horizontal for right ascension/longitude
*/

/*

Kepler Visualization - Controls

GUI controls added by Lon Riesberg, Laboratory for Atmospheric and Space Physics
lon@ieee.org

April, 2012

Current release consists of a vertical slider for zoom control.  The slider can be toggled
on/off by pressing the 'c' key.

Slide out controls that map to the other key bindings is currently being implemented and
will be released soon.

*/

class HorizontalControl {

int barHeight;
int barY;                          // y-coordinate of zoom control
int minX, maxX;                    // x-coordinate range of zoom control
float minZoomValue, maxZoomValue;  // values that map onto zoom control
float valuePerX;                   // zoom value of each y-pixel
int sliderY;                       // y-coordinate of current slider position
float sliderValue;                 // value that corresponds to y-coordinate of slider
int sliderWidth, sliderHeight;
int sliderX;                       // x-coordinate of left-side slider edge

HorizontalControl () {

barY = 15; //40;
barHeight = 40; //15;

minX = 40;
maxX = minX + width/3 - sliderWidth/2;

minZoomValue = texture.width - texture.width;
maxZoomValue = texture.width;   // 300 percent
valuePerX = (maxZoomValue - minZoomValue) / (maxX - minX);

sliderWidth = 10; //25;
sliderHeight = 25; //10;
// sliderY = (barY + (barHeight/2)) - (sliderHeight/2);
sliderY = (barY - (sliderHeight/2)) + (barHeight/2);
sliderValue = minZoomValue;
sliderX = minX;
}

void render() {
pushMatrix();

// strokeWeight(1.5);
strokeWeight(1);
//  stroke(105, 105, 105);  // fill(0xff33ff99);
//   stroke(0xff33ff99);  // fill(0xff33ff99);  0xffff0000
stroke(0xffff0000);

// zoom control bar
fill(0, 0, 0, 0);

rect(minX,barHeight + height - height/4,maxX-minX, barY );
// rect(maxX-minX, barHeight/2,minX,barY + height - height/4 );

// slider
// fill(105, 105, 105); //0x3300FF00
fill(0xffff0000); // 0xff33ff99//0x3300FF00

rect(sliderX, sliderY + height - height/4 + sliderHeight/2 , sliderWidth, sliderHeight);

popMatrix();

}

float getZoomValue(int x) {
if ((x >= minX) && (x <= (maxX - sliderWidth/2))) {
sliderX = (int) (x - (sliderWidth/2));
if (sliderX < minX) {
sliderX = minX;
}
sliderValue = (x - minX) * valuePerX + minZoomValue;
}
return sliderValue;
}

void updateZoomSlider(float value) {
int tempX = (int) (value / valuePerX) + minX;
if ((tempX >= minX) && (tempX <= (maxX+sliderWidth))) {
sliderValue = value;
sliderX = tempX;
}
}

/*   boolean isZoomSliderEvent(int x, int y) {
int slop = 50;  // number of pixels above or below slider that's acceptable.  provided for ease of use.
int sliderTop = (int) (sliderY - (sliderHeight/2)) - slop;
int sliderBottom = sliderY + sliderHeight + slop;
return ((x >= sliderX) && (x <= (sliderX    + sliderWidth)) && (y >= sliderTop)  && (y <= sliderBottom) || draggingZoomSlider );
} */

boolean isZoomSliderEvent(int x, int y) {
int slop = 50;  // number of pixels above or below slider that's acceptable.  provided for ease of use.
int sliderLeft = (int) (sliderX - (sliderWidth/2)) - slop;
int sliderRight = sliderX + sliderWidth + slop;
//  return ((y >= sliderY + height - height/4) && (y <= (sliderY + height - height/4    + sliderHeight)) && (x >= sliderLeft)  && (x <= sliderRight) || draggingZoomSlider );
return ((y >= sliderY + height - height/4 - sliderHeight/2) && (y <= (sliderY + height - height/4 + sliderHeight*2 )) && (x >= sliderLeft)  && (x <= sliderRight) || draggingZoomSlider );
}
}
``````
1 Like

We miss the `world32k.jpg` to try your code.

Is this one working? http://ggdesign1.com/wp-content/uploads/2018/04/world32k-earth-full-map.jpg

@jb4x that’s the one you link.

world32k.jpg can be found in the processing examples

C:processing-3.0-windows64\processing-3.0\modes\java\examples\Topics\Textures\TextureSphere\data\world32k.jpg

1 Like

Are you sure you need the `thread()` function? I’ve never used it but I read plenty of time that it is better to not use it (except when loading assets for example). Is your line that slow to compute that you need to do it in a separated thread?

Also, have you thought of using the controlP5 library for your sliders? It would really simplify your code!

I know about controlP5 but it doesn’t work right on android so I avoid it now. I’m pretty sure the circle needs thread() because I had a lot of trouble getting the slider to respond without thread().

Oh, if anyone tries this. Go gentle on the controls and wait for the circle to draw or it might get messy. If it does go nutty, click on the globe and it will redraw.

I fixed all that redraw madness. I had all the parts needed even the Booleans. It only redraws if the slider changes. It will not rotate if it is in the process of drawing and it wont update the slider value unless it is done drawing. I think it now respects the warning about thread().