cancel
Showing results for 
Show  only  | Search instead for 
Did you mean: 
  • JMP will suspend normal business operations for our Winter Holiday beginning on Wednesday, Dec. 24, 2025, at 5:00 p.m. ET (2:00 p.m. ET for JMP Accounts Receivable).
    Regular business hours will resume at 9:00 a.m. EST on Friday, Jan. 2, 2026.
  • We’re retiring the File Exchange at the end of this year. The JMP Marketplace is now your destination for add-ins and extensions.

Discussions

Solve problems, and share tips and tricks with other JMP users.
Choose Language Hide Translation Bar
thomasz
Level IV

Visualization in OpenGL using ArcBall with zooming and panning

I created an interactive 3D visualization of color data using the OpenGL functionality in JMP and used the ArcBall to allow the user to rotate the data, but I ran into some limitations when trying to combine this with panning and zooming.

The ArcBall itself does not have panning and zooming capability and there seems to be no way to get and set the ArcBalls orientation. So if I control panning and zooming myself, I cannot integrate the Arcball seamlessly because I have to rebuild the scene from scratch disregarding the ArcBall orientation. One possible way I have been thingking of is to capture the mouse and make the Arcball functionality myself, but in the first round this was beyond the project budget.

I have gone through all the examples, and none combine rotation of the Arcball with zooming and panning.

Any solutions/suggestions on how to make seamless panning, zooming and ArcBall-like rotation of OpenGL scenes in JSL?

Or would it be better to use the Python integration for that?

1 REPLY 1
Craige_Hales
Super User

Re: Visualization in OpenGL using ArcBall with zooming and panning

This might help.

View more...
// cobbled together from several examples...

Cube = Scene Display List( 1 );

Cube << Begin( QUAD_STRIP );
Cube << Color( 1.0, 0.0, 1.0 );
Cube << Vertex( -0.5, 0.5, 0.5 );
Cube << Color( 1.0, 0.0, 0.0 );
Cube << Vertex( -0.5, -0.5, 0.5 );
Cube << Color( 1.0, 1.0, 1.0 );
Cube << Vertex( 0.5, 0.5, 0.5 );
Cube << Color( 1.0, 1.0, 0.0 );
Cube << Vertex( 0.5, -0.5, 0.5 );
Cube << Color( 0.0, 1.0, 1.0 );
Cube << Vertex( 0.5, 0.5, -0.5 );
Cube << Color( 0.0, 1.0, 0.0 );
Cube << Vertex( 0.5, -0.5, -0.5 );
Cube << Color( 0.0, 0.0, 1.0 );
Cube << Vertex( -0.5, 0.5, -0.5 );
Cube << Color( 0.0, 0.0, 0.0 );
Cube << Vertex( -0.5, -0.5, -0.5 );
Cube << Color( 1.0, 0.0, 1.0 );
Cube << Vertex( -0.5, 0.5, 0.5 );
Cube << Color( 1.0, 0.0, 0.0 );
Cube << Vertex( -0.5, -0.5, 0.5 );
Cube << End();

cube1 = Scene Display List();
cube1 << arcball( cube, .5 ); // don't rebuild the arcballs so
cube2 = Scene Display List();
cube2 << arcball( cube, .5 ); // they remember their positions

WindowHeight = 600;
WindowWidth = 800;

mouseOriginY = WindowHeight / 2;
mouseOriginX = WindowWidth / 2;

mode = "done";
dx = dy = .;

mouseMatrix = Identity( 4 ); // <<<< this accumulates all the angle/size/scale changes

// this function is called with m==1 on mouse down, m==2 on mouse drags, and m=3 on mouse up (k is the shift/control/alt status)
// this function returns 0 or 1, on purpose, to allow arc balls to run or this code to adjust the mouseMatrix.
Click2d = Function( {x, y, m, k},
	{xt, xs, xr, yr, zr, degrees, radians, dx, dy},
	If(
		m == 1,
			mouseOriginX = x;
			mouseOriginY = y;
					
			If( Is Shift Key(),
				mode = "translate"
			);
			//		if(Is Alt Key(), // linux/virtualbox the alt key never makes it to JMP
			//			mode = "rotate";			
			//		);
			If( Is Control Key(),
				mode = "scale"
			);
			If( Is Control Key() & Is Shift Key(),
				mode = "rotate"
			);
			If( !Is Control Key() & !Is Shift Key(),
				Return( 0 ); // allow the arc ball to get control. 0 means this handler did NOT handle the click.
			);//
		, /*else if */ m == 2,
			dx = x - mouseOriginX;
			dy = y - mouseOriginY;
			If(
				mode == "translate",
					xt = Identity( 4 );
					xt[1, 4] = dx / WindowWidth;
					xt[2, 4] = -dy / WindowWidth;
					mouseMatrix = xt * mouseMatrix;//
				, /* else if */ mode == "scale",
					xs = Identity( 4 );
					xs[1, 1] = If( dx >= 0,
						(dx + WindowWidth) / WindowWidth//
					,
						WindowWidth / (WindowWidth - dx)
					);
					xs[2, 2] = If( dy >= 0,
						WindowHeight / (dy + WindowHeight)//
					,
						(WindowHeight - dy) / WindowHeight
					);
					mouseMatrix = xs * mouseMatrix;//
				, /* else if */ mode == "rotate",
					radians = 6 * dy / WindowWidth;
					xr = Identity( 4 ); // start out with an identity matrix then plug in the values to rotate about the x axis
					xr[2, 2] = Cos( radians );
					xr[2, 3] = -Sin( radians );
					xr[3, 2] = Sin( radians );
					xr[3, 3] = Cos( radians );
					yr = Identity( 4 ); // and the y axis
					radians = 6 * dx / WindowHeight;
					yr[1, 1] = Cos( radians );
					yr[1, 3] = Sin( radians );
					yr[3, 1] = -Sin( radians );
					yr[3, 3] = Cos( radians );
					zr = Identity( 4 ); // and the z axis
					mouseMatrix = xr * mouseMatrix;
					mouseMatrix = yr * mouseMatrix;
			);
			mouseOriginX = x;
			mouseOriginY = y;
			rebuild();//
		, /*else if */ m == 3,
			mode = "done";
			0;//
		, /* else */
		Throw( "unexpected m=" || Char( m ) )
	);
	//0 // because this function does NOT return 1, JMP will not call it again until the next mouse down.  Drags will act like moves and go to the Move2d function.
	1; // because it *does* return 1, m=1,2,3 for press,move(s),release
);





Scene = Scene Box( WindowWidth, WindowHeight, Click2d );
Scene << Show ArcBall( "Always" );

New Window( "Matrix rotations", V List Box( Text Box( "shift-drag: pan\!nctrl-drag: zoom\!nctrl-shift-drag: rotate\!nVanilla-drag: rotate cubes" ), Scene ) );

rebuild = Function( {},
	degrees = 45;
	radians = 3.14159 * degrees / 180; // sin and cos use radians
	Scene << clear;
	Scene << perspective( 45, 3, 7 );
	Scene << Translate( 0.0, 0.0, -4.5 );

	// apply the global translation/rotation/scaling 
	Scene << MultMatrix( mouseMatrix );

	Scene << pushmatrix;
	xt = Identity( 4 ); // translate this one left by .75
	xt[1, 4] = -.75;
	Scene << MultMatrix( xt ); 
	Scene << callList( Cube1 );
	Scene << popmatrix;
	
	Scene << pushmatrix;
	Scene << translate( .75, 0, 0 ); // translate this one right by .75
	Scene << calllist( Cube2 );
	Scene << popmatrix;

	Scene << update;
);

rebuild();

 

 

 

 

Craige

Recommended Articles