Skip to main content
  • Home
  • Development
  • Documentation
  • Donate
  • Operational login
  • Browse the archive

swh logo
SoftwareHeritage
Software
Heritage
Archive
Features
  • Search

  • Downloads

  • Save code now

  • Add forge now

  • Help

https://github.com/sueda/redmax
01 May 2020, 06:26:32 UTC
  • Code
  • Branches (2)
  • Releases (0)
  • Visits
Revision 3251bcb93cd91a892a8a056b6536a295b0ed80be authored by Shinjiro Sueda on 28 October 2019, 19:54:18 UTC, committed by Shinjiro Sueda on 28 October 2019, 19:54:18 UTC
Diagonalized inertia computation
1 parent 41e0991
  • Files
  • Changes
    • Branches
    • Releases
    • HEAD
    • refs/heads/mac-testing
    • refs/heads/master
    • 3251bcb93cd91a892a8a056b6536a295b0ed80be
    No releases to show
  • bee074d
  • /
  • matlab
  • /
  • testRedMaxScenes.m
Raw File Download Save again
Take a new snapshot of a software origin

If the archived software origin currently browsed is not synchronized with its upstream version (for instance when new commits have been issued), you can explicitly request Software Heritage to take a new snapshot of it.

Use the form below to proceed. Once a request has been submitted and accepted, it will be processed as soon as possible. You can then check its processing state by visiting this dedicated page.
swh spinner

Processing "take a new snapshot" request ...

To reference or cite the objects present in the Software Heritage archive, permalinks based on SoftWare Hash IDentifiers (SWHIDs) must be used.
Select below a type of object currently browsed in order to display its associated SWHID and permalink.

  • revision
  • directory
  • content
  • snapshot
origin badgerevision badge
swh:1:rev:3251bcb93cd91a892a8a056b6536a295b0ed80be
origin badgedirectory badge
swh:1:dir:fed9dd39554d3b7e9ebbe3fd875c13af6a57f3b4
origin badgecontent badge
swh:1:cnt:6480428191b0ca05a3cc170b4cc435ba997a7bcf
origin badgesnapshot badge
swh:1:snp:7a235a6948ea44d6bc90942c935c9234831ee01e

This interface enables to generate software citations, provided that the root directory of browsed objects contains a citation.cff or codemeta.json file.
Select below a type of object currently browsed in order to generate citations for them.

  • revision
  • directory
  • content
  • snapshot
(requires biblatex-software package)
Generating citation ...
(requires biblatex-software package)
Generating citation ...
(requires biblatex-software package)
Generating citation ...
(requires biblatex-software package)
Generating citation ...
Tip revision: 3251bcb93cd91a892a8a056b6536a295b0ed80be authored by Shinjiro Sueda on 28 October 2019, 19:54:18 UTC
Diagonalized inertia computation
Tip revision: 3251bcb
testRedMaxScenes.m
function scene = testRedMaxScenes(itype,sceneID)
% Creates test scenes

global RECURS_ODE45 REDMAX_ODE45 REDMAX_EULER

scene = redmax.Scene();

density = 1.0;
vel = (itype == REDMAX_EULER); % Velocity level integrator?
switch(sceneID)
	case -1
		scene.name = 'Simpler serial chain';
		sides = [10 1 1];
		nbodies = 2;
		scene.waxis = nbodies*5*[-1 1 -1 1 -2 0];
		scene.Hexpected(RECURS_ODE45) = -5.6531026717020723;
		scene.Hexpected(REDMAX_ODE45) = -5.6531026765951538;
		scene.Hexpected(REDMAX_EULER) = -3697.4545694454454861;
		for i = 1 : nbodies
			scene.bodies{i} = redmax.BodyCuboid(density,sides); %#ok<*SAGROW>
			if i == 1
				scene.joints{i} = redmax.JointRevolute([],scene.bodies{i},[0 1 0]');
				scene.joints{i}.setJointTransform(eye(4));
				scene.joints{i}.q(1) = pi/2;
			else
				scene.joints{i} = redmax.JointRevolute(scene.joints{i-1},scene.bodies{i},[0 1 0]');
				scene.joints{i}.setJointTransform([eye(3),[10 0 0]'; 0 0 0 1]);
				scene.joints{i}.q(1) = pi/4;
			end
			scene.bodies{i}.setBodyTransform([eye(3),[5 0 0]'; 0 0 0 1]);
		end
	case 0
		scene.name = 'Simple serial chain';
		sides = [10 1 1];
		nbodies = 5;
		scene.waxis = nbodies*5*[-1 1 -0.1 0.1 -2 0];
		scene.Hexpected(RECURS_ODE45) = -3.0971281943493523;
		scene.Hexpected(REDMAX_ODE45) = -3.0971281068341341;
		scene.Hexpected(REDMAX_EULER) = -5930.8171118834870867;
		for i = 1 : nbodies
			scene.bodies{i} = redmax.BodyCuboid(density,sides); %#ok<*SAGROW>
			if i == 1
				scene.joints{i} = redmax.JointRevolute([],scene.bodies{i},[0 1 0]');
				scene.joints{i}.setJointTransform(eye(4));
			else
				if mod(i,2) == 1
					scene.joints{i} = redmax.JointRevolute(scene.joints{i-1},scene.bodies{i},[0 1 0]');
				else
					scene.joints{i} = redmax.JointFixed(scene.joints{i-1},scene.bodies{i});
				end
				%scene.joints{i} = redmax.JointRevolute(scene.joints{i-1},scene.bodies{i},[0 1 0]');
				scene.joints{i}.setJointTransform([eye(3),[10 0 0]'; 0 0 0 1]);
			end
			scene.bodies{i}.setBodyTransform([eye(3),[5 0 0]'; 0 0 0 1]);
			if mod(i,2) == 1
				scene.joints{i}.q(1) = pi/4;
			else
				scene.joints{i}.q(1) = 0;
			end
		end
	case 1
		scene.name = 'Different revolute axes';
		sides = [10 1 1];
		scene.waxis = [-20 20 -20 20 -20 5];
		scene.Hexpected(RECURS_ODE45) = -1.9548841516880202;
		scene.Hexpected(REDMAX_ODE45) = -1.9548841526830074;
		scene.Hexpected(REDMAX_EULER) = -9423.2594023734018265;
		scene.bodies{1} = redmax.BodyCuboid(density,sides);
		scene.bodies{2} = redmax.BodyCuboid(density,sides);
		scene.bodies{3} = redmax.BodyCuboid(density,sides);
		scene.joints{1} = redmax.JointRevolute([],       scene.bodies{1},[0 0 1]');
		scene.joints{2} = redmax.JointRevolute(scene.joints{1},scene.bodies{2},[0 1 0]');
		scene.joints{3} = redmax.JointRevolute(scene.joints{2},scene.bodies{3},[0 0 1]');
		scene.bodies{1}.setBodyTransform([eye(3),[5 0 0]'; 0 0 0 1]);
		scene.bodies{2}.setBodyTransform([eye(3),[5 0 0]'; 0 0 0 1]);
		scene.bodies{3}.setBodyTransform([eye(3),[5 0 0]'; 0 0 0 1]);
		scene.joints{1}.setJointTransform(eye(4));
		scene.joints{2}.setJointTransform([eye(3),[10 0 0]'; 0 0 0 1]);
		scene.joints{3}.setJointTransform([eye(3),[10 0 0]'; 0 0 0 1]);
		scene.joints{1}.q(1) = 0;
		scene.joints{2}.q(1) = pi/2;
		scene.joints{3}.q(1) = pi/2;
	case 2
		scene.name = 'Branching';
		%      X        in YZ plane
		%      |        
		%  X---Z---Y    joint axes shown
		%  |       |
		%  |       |
		scene.waxis = [-15 15 -15 15 -10 20];
		scene.Hexpected(RECURS_ODE45) = -3.2850447686942061;
		scene.Hexpected(REDMAX_ODE45) = -3.2850447782984702;
		scene.Hexpected(REDMAX_EULER) = -1123.9825362491046690;
		scene.bodies{1} = redmax.BodyCuboid(density,[1  1 10]);
		scene.bodies{2} = redmax.BodyCuboid(density,[1 20  1]);
		scene.bodies{3} = redmax.BodyCuboid(density,[1  1 10]);
		scene.bodies{4} = redmax.BodyCuboid(density,[1  1 10]);
		scene.joints{1} = redmax.JointRevolute([],       scene.bodies{1},[1 0 0]');
		scene.joints{2} = redmax.JointRevolute(scene.joints{1},scene.bodies{2},[0 0 1]');
		scene.joints{3} = redmax.JointRevolute(scene.joints{2},scene.bodies{3},[1 0 0]');
		scene.joints{4} = redmax.JointRevolute(scene.joints{2},scene.bodies{4},[0 1 0]');
		scene.bodies{1}.setBodyTransform([eye(3),[0 0 -5]'; 0 0 0 1]);
		scene.bodies{2}.setBodyTransform([eye(3),[0 0  0]'; 0 0 0 1]);
		scene.bodies{3}.setBodyTransform([eye(3),[0 0 -5]'; 0 0 0 1]);
		scene.bodies{4}.setBodyTransform([eye(3),[0 0 -5]'; 0 0 0 1]);
		scene.joints{1}.setJointTransform([eye(3),[0   0  15]'; 0 0 0 1]);
		scene.joints{2}.setJointTransform([eye(3),[0   0 -10]'; 0 0 0 1]);
		scene.joints{3}.setJointTransform([eye(3),[0 -10   0]'; 0 0 0 1]);
		scene.joints{4}.setJointTransform([eye(3),[0  10   0]'; 0 0 0 1]);
		scene.joints{1}.q(1) = 0;
		scene.joints{2}.q(1) = 0;
		scene.joints{3}.q(1) = pi/4;
		scene.joints{4}.q(1) = pi/4;
	case 3
		scene.name = 'Spherical joint';
		if itype == REDMAX_EULER
			scene.tspan = [0.0 3.0];
		else
			% Reparameterizing with ode45 takes too much effort
			scene.tspan = [0.0 0.0];
		end
		sides = [1 1 10];
		scene.waxis = 10*[-1 1 -1 1 -1.9 0.1];
		scene.Hexpected(RECURS_ODE45) = 0;
		scene.Hexpected(REDMAX_ODE45) = 0;
		scene.Hexpected(REDMAX_EULER) = 7788.8055603543098186;
		scene.bodies{1} = redmax.BodyCuboid(density,sides);
		scene.bodies{1}.setBodyTransform([eye(3),[0 0 -5]'; 0 0 0 1]);
		scene.joints{1} = redmax.JointSphericalExp([],scene.bodies{1}); %#ok<*UNRCH>
		scene.joints{1}.setJointTransform(eye(4));
		scene.bodies{2} = redmax.BodyCuboid(density,sides);
		scene.bodies{2}.setBodyTransform([eye(3),[0 0 -5]'; 0 0 0 1]);
		scene.joints{2} = redmax.JointSphericalExp(scene.joints{1},scene.bodies{2});
		scene.joints{2}.setJointTransform([eye(3),[0 0 -10]'; 0 0 0 1]);
		scene.joints{1}.q(1) = pi/8;
		scene.joints{1}.q(3) = 0;
		scene.joints{1}.qdot(3) = 2;
		scene.joints{2}.q(1) = pi/8;
	case 4
		scene.name = 'Loop';
		scene.waxis = [-15 15 -1 1 -20 2];
		scene.Hexpected(RECURS_ODE45) = 4176.3993502426255873;
		scene.Hexpected(REDMAX_ODE45) = 4176.3993502425073530;
		scene.Hexpected(REDMAX_EULER) = 3987.2011847696289806;
		scene.bodies{1} = redmax.BodyCuboid(density,[20 1  1]);
		scene.bodies{2} = redmax.BodyCuboid(density,[ 1 1 10]);
		scene.bodies{3} = redmax.BodyCuboid(density,[ 1 1 10]);
		scene.bodies{4} = redmax.BodyCuboid(density,[20 1  1]);
		scene.bodies{5} = redmax.BodyCuboid(density,[ 1 1 10]);
		scene.joints{1} = redmax.JointFixed([],scene.bodies{1});
		scene.joints{2} = redmax.JointRevolute(scene.joints{1},scene.bodies{2},[0 1 0]');
		scene.joints{3} = redmax.JointRevolute(scene.joints{1},scene.bodies{3},[0 1 0]');
		scene.joints{4} = redmax.JointRevolute(scene.joints{2},scene.bodies{4},[0 1 0]');
		scene.joints{5} = redmax.JointRevolute(scene.joints{4},scene.bodies{5},[0 1 0]');
		scene.bodies{1}.setBodyTransform(eye(4));
		scene.bodies{2}.setBodyTransform([eye(3),[ 0 0 -5]'; 0 0 0 1]);
		scene.bodies{3}.setBodyTransform([eye(3),[ 0 0 -5]'; 0 0 0 1]);
		scene.bodies{4}.setBodyTransform([eye(3),[10 0  0]'; 0 0 0 1]);
		scene.bodies{5}.setBodyTransform([eye(3),[ 0 0 -5]'; 0 0 0 1]);
		scene.joints{1}.setJointTransform(eye(4));
		scene.joints{2}.setJointTransform([eye(3),[-10 0   0]'; 0 0 0 1]);
		scene.joints{3}.setJointTransform([eye(3),[ 10 0   0]'; 0 0 0 1]);
		scene.joints{4}.setJointTransform([eye(3),[  0 0 -10]'; 0 0 0 1]);
		scene.joints{5}.setJointTransform([eye(3),[ 10 0   0]'; 0 0 0 1]);
		scene.constraints{1} = redmax.ConstraintLoop(scene.bodies{3},scene.bodies{4});
		scene.constraints{1}.setPositions([0 0 -5]',[10 0 0]');
		scene.joints{5}.qdot(1) = 5;
	case 5
		scene.name = 'Joint torque';
		scene.tspan = [0 10];
		scene.hEuler = 5e-2;
		scene.grav = [0 0 0]';
		sides = [10 1 1];
		nbodies = 3;
		scene.waxis = [];
		scene.Hexpected(RECURS_ODE45) = 160.820781707015;
		scene.Hexpected(REDMAX_ODE45) = 160.820781710469;
		scene.Hexpected(REDMAX_EULER) = 170.5971183034905607;
		for i = 1 : nbodies
			scene.bodies{i} = redmax.BodyCuboid(density,sides); %#ok<*SAGROW>
			if i == 1
				scene.joints{i} = redmax.JointRevolute([],scene.bodies{i},[0 1 0]');
				scene.joints{i}.setJointTransform(eye(4));
			else
				scene.joints{i} = redmax.JointRevolute(scene.joints{i-1},scene.bodies{i},[0 1 0]');
				scene.joints{i}.setJointTransform([eye(3),[10 0 0]'; 0 0 0 1]);
			end
			scene.bodies{i}.setBodyTransform([eye(3),[5 0 0]'; 0 0 0 1]);
		end
		scene.sceneFcn = @sceneFcn05;
	case 6
		scene.name = 'Joint limits';
		if itype ~= REDMAX_EULER
			scene.tspan = [0 0];
		end
		sides = [10 1 1];
		nbodies = 2;
		scene.waxis = nbodies*5*[-2.5 2.5 -0.1 0.1 -2.0 0.5];
		scene.Hexpected(RECURS_ODE45) = 0;
		scene.Hexpected(REDMAX_ODE45) = 0;
		scene.Hexpected(REDMAX_EULER) = 36957.4447830002754927;
		for i = 1 : nbodies
			scene.bodies{i} = redmax.BodyCuboid(density,sides); %#ok<*SAGROW>
			if i == 1
				scene.joints{i} = redmax.JointRevolute([],scene.bodies{i},[0 1 0]');
				scene.joints{i}.setJointTransform(eye(4));
			else
				scene.joints{i} = redmax.JointRevolute(scene.joints{i-1},scene.bodies{i},[0 1 0]');
				scene.joints{i}.setJointTransform([eye(3),[10 0 0]'; 0 0 0 1]);
			end
			scene.bodies{i}.setBodyTransform([eye(3),[5 0 0]'; 0 0 0 1]);
			% NOTE: ode45 seems to get stuck if all the scene.constraints become
			% active at the same time. So, it's safer to not apply joint
			% limits to the root joint.
			if i > 1
				scene.constraints{end+1} = redmax.ConstraintJointLimit(scene.joints{i});
				scene.constraints{end}.setLimits(-pi/4,pi/4);
			end
		end
	case 7
		scene.name = 'Equality constrained angles';
		scene.hEuler = 2e-2;
		sides = [10 1 1];
		nbodies = 3;
		scene.waxis = nbodies*5*[-2.5 2.5 -1 1 -2.0 0.5];
		scene.Hexpected(RECURS_ODE45) = -229.7121594171621837;
		scene.Hexpected(REDMAX_ODE45) = -229.7121593945485074;
		scene.Hexpected(REDMAX_EULER) = 42645.1541420989669859;
		for i = 1 : nbodies
			scene.bodies{i} = redmax.BodyCuboid(density,sides); %#ok<*SAGROW>
			if i == 1
				scene.joints{i} = redmax.JointRevolute([],scene.bodies{i},[0 1 0]');
				scene.joints{i}.setJointTransform(eye(4));
			else
				scene.joints{i} = redmax.JointRevolute(scene.joints{i-1},scene.bodies{i},[0 1 0]');
				scene.joints{i}.setJointTransform([eye(3),[10 0 0]'; 0 0 0 1]);
			end
			scene.bodies{i}.setBodyTransform([eye(3),[5 0 0]'; 0 0 0 1]);
			if i > 1
				scene.constraints{end+1} = redmax.ConstraintMultQ(scene.joints{i-1},scene.joints{i});
				scene.constraints{end}.setFactor(0.5);
			end
		end
	case 8
		scene.name = 'Equality and loop';
		scene.hEuler = 2e-2;
		scene.waxis = [-15 15 -5 5 -30 1];
		scene.Hexpected(RECURS_ODE45) = 16707.8585386558770551;
		scene.Hexpected(REDMAX_ODE45) = 16707.8585386558261234;
		scene.Hexpected(REDMAX_EULER) = 14677.4348748325592169;
		scene.bodies{1} = redmax.BodyCuboid(density,[10 1  1]);
		scene.bodies{2} = redmax.BodyCuboid(density,[ 1 1 10]);
		scene.bodies{3} = redmax.BodyCuboid(density,[ 1 1 10]);
		scene.bodies{4} = redmax.BodyCuboid(density,[10 1  1]);
		scene.bodies{5} = redmax.BodyCuboid(density,[ 1 1 10]);
		scene.bodies{6} = redmax.BodyCuboid(density,[ 1 1 10]);
		scene.bodies{7} = redmax.BodyCuboid(density,[ 1 1 10]);
		scene.joints{1} = redmax.JointFixed([],scene.bodies{1});
		scene.joints{2} = redmax.JointRevolute(scene.joints{1},scene.bodies{2},[0 1 0]');
		scene.joints{3} = redmax.JointRevolute(scene.joints{2},scene.bodies{3},[0 1 0]');
		scene.joints{4} = redmax.JointRevolute(scene.joints{3},scene.bodies{4},[0 1 0]');
		scene.joints{5} = redmax.JointRevolute(scene.joints{4},scene.bodies{5},[0 1 0]');
		scene.joints{6} = redmax.JointRevolute(scene.joints{5},scene.bodies{6},[0 1 0]');
		scene.joints{7} = redmax.JointRevolute(scene.joints{4},scene.bodies{7},[0 1 0]');
		scene.bodies{1}.setBodyTransform(eye(4));
		scene.bodies{2}.setBodyTransform([eye(3),[ 0 0 -5]'; 0 0 0 1]);
		scene.bodies{3}.setBodyTransform([eye(3),[ 0 0 -5]'; 0 0 0 1]);
		scene.bodies{4}.setBodyTransform([eye(3),[-5 0  0]'; 0 0 0 1]);
		scene.bodies{5}.setBodyTransform([eye(3),[ 0 0  5]'; 0 0 0 1]);
		scene.bodies{6}.setBodyTransform([eye(3),[ 0 0  5]'; 0 0 0 1]);
		scene.bodies{7}.setBodyTransform([eye(3),[ 0 0 -5]'; 0 0 0 1]);
		scene.joints{1}.setJointTransform(eye(4));
		scene.joints{2}.setJointTransform([eye(3),[  5 0   0]'; 0 0 0 1]);
		scene.joints{3}.setJointTransform([eye(3),[  0 0 -10]'; 0 0 0 1]);
		scene.joints{4}.setJointTransform([eye(3),[  0 0 -10]'; 0 0 0 1]);
		scene.joints{5}.setJointTransform([eye(3),[-10 0   0]'; 0 0 0 1]);
		scene.joints{6}.setJointTransform([eye(3),[  0 0  10]'; 0 0 0 1]);
		scene.joints{7}.setJointTransform([eye(3),[ -5 0   0]'; 0 0 0 1]);
		scene.constraints{1} = redmax.ConstraintLoop(scene.bodies{6},scene.bodies{1});
		scene.constraints{1}.setPositions([0 0 5]',[-5 0 0]');
		scene.constraints{2} = redmax.ConstraintMultQ(scene.joints{3},scene.joints{6});
		scene.constraints{2}.setFactor(0.5);
		scene.joints{7}.qdot = 1e1;
	case 9
		% Reduced ode45 does not match redmax ode45. Ignore for now!
		scene.name = 'Hybrid dynamics';
		scene.tspan = [0 10];
		scene.hEuler = 2e-2;
		scene.grav = [0 0 0]';
		sides = [10 1 1];
		nbodies = 3;
		scene.waxis = nbodies*10*[-1 1 -0.1 0.1 -1 1];
		scene.Hexpected(RECURS_ODE45) = 27616.6067502096;
		scene.Hexpected(REDMAX_ODE45) = 29802.0819629936;
		scene.Hexpected(REDMAX_EULER) = 199570.9300431804149412;
		for i = 1 : nbodies
			scene.bodies{i} = redmax.BodyCuboid(density,sides); %#ok<*SAGROW>
			if i == 1
				scene.joints{i} = redmax.JointRevolute([],scene.bodies{i},[0 1 0]');
				scene.joints{i}.setJointTransform(eye(4));
			else
				scene.joints{i} = redmax.JointRevolute(scene.joints{i-1},scene.bodies{i},[0 1 0]');
				scene.joints{i}.setJointTransform([eye(3),[10 0 0]'; 0 0 0 1]);
			end
			scene.bodies{i}.setBodyTransform([eye(3),[5 0 0]'; 0 0 0 1]);
		end
		if itype ~= RECURS_ODE45
			scene.constraints{end+1} = redmax.ConstraintPrescJoint(scene.joints{1},vel);
		end
		scene.sceneFcn = @sceneFcn09;
	case 10
		scene.name = 'External world force';
		scene.grav = [0 0 0]';
		sides = [10 1 1];
		nbodies = 3;
		scene.waxis = nbodies*10*[-1 1 -0.1 0.1 -0.1 1];
		scene.Hexpected(RECURS_ODE45) = 1210.7099042740396726;
		scene.Hexpected(REDMAX_ODE45) = 1210.7099042740403547;
		scene.Hexpected(REDMAX_EULER) = 1088.3425711375120954;
		for i = 1 : nbodies
			scene.bodies{i} = redmax.BodyCuboid(density,sides); %#ok<*SAGROW>
			if i == 1
				scene.joints{i} = redmax.JointRevolute([],scene.bodies{i},[0 1 0]');
				scene.joints{i}.setJointTransform(eye(4));
			else
				scene.joints{i} = redmax.JointRevolute(scene.joints{i-1},scene.bodies{i},[0 1 0]');
				scene.joints{i}.setJointTransform([eye(3),[10 0 0]'; 0 0 0 1]);
			end
			scene.bodies{i}.setBodyTransform([eye(3),[5 0 0]'; 0 0 0 1]);
			scene.bodies{i}.setDamping(1e1);
		end
		scene.springs{end+1} = redmax.SpringPointDirection(scene.bodies{end},[5 0 0]');
		scene.springs{end}.setDirection([0 0 1]');
		scene.springs{end}.setStiffness(1e3);
		scene.sceneFcn = @sceneFcn10;
	case 11
		scene.name = 'Joint stiffness and damping';
		scene.tspan = [0 5];
		scene.hEuler = 2e-2;
		scene.grav = [0 0 0]';
		sides = [10 1 1];
		nbodies = 3;
		stiffness = 1e4;
		damping = 1e3;
		scene.waxis = nbodies*10*[-0.1 1 -0.1 0.1 -1 1];
		scene.Hexpected(RECURS_ODE45) = 2898.56113448227;
		scene.Hexpected(REDMAX_ODE45) = 2898.56113448227;
		scene.Hexpected(REDMAX_EULER) = 2659.7218894234238178;
		for i = 1 : nbodies
			scene.bodies{i} = redmax.BodyCuboid(density,sides); %#ok<*SAGROW>
			if i == 1
				scene.joints{i} = redmax.JointRevolute([],scene.bodies{i},[0 1 0]');
				scene.joints{i}.setJointTransform(eye(4));
			else
				scene.joints{i} = redmax.JointRevolute(scene.joints{i-1},scene.bodies{i},[0 1 0]');
				scene.joints{i}.setJointTransform([eye(3),[10 0 0]'; 0 0 0 1]);
			end
			scene.bodies{i}.setBodyTransform([eye(3),[5 0 0]'; 0 0 0 1]);
			scene.joints{i}.setStiffness(stiffness);
			scene.joints{i}.setDamping(damping);
		end
		scene.joints{1}.qdot(1) = 1;
	case 12
		% This scene will go crazy without damping!
		% This scene doesn't work with RHD!
		scene.name = 'Mass-springs';
		if itype == RECURS_ODE45
			scene.tspan = [0.0 0.0];
		else
			scene.tspan = [0.0 1.0];
		end
		scene.hEuler = 5e-3;
		sides = [10 1 1];
		nbodies = 2;
		stiffness = 1e5;
		scene.waxis = nbodies*10*[-0.5 1.5 -0.5 0.5 -1 1];
		scene.Hexpected(RECURS_ODE45) = 0;
		scene.Hexpected(REDMAX_ODE45) = -0.0345395920267038;
		scene.Hexpected(REDMAX_EULER) = -11740.4013565295099397;
		for i = 1 : nbodies
			scene.bodies{i} = redmax.BodyCuboid(density,sides); %#ok<*SAGROW>
			if i == 1
				scene.joints{i} = redmax.JointRevolute([],scene.bodies{i},[0 1 0]');
				scene.joints{i}.setJointTransform(eye(4));
			else
				scene.joints{i} = redmax.JointRevolute(scene.joints{i-1},scene.bodies{i},[0 1 0]');
				scene.joints{i}.setJointTransform([eye(3),[10 0 0]'; 0 0 0 1]);
			end
			scene.bodies{i}.setBodyTransform([eye(3),[5 0 0]'; 0 0 0 1]);
		end
		m = 0.1*prod(sides)*density;
		scene.deformables{end+1} = redmax.DeformableSpring(3);
		scene.deformables{end}.setStiffness(stiffness);
		scene.deformables{end}.setMass(m);
		scene.deformables{end}.setAttachments([],[10*nbodies+10,0,10]',scene.bodies{end},[5 0 0]');
		scene.deformables{end+1} = redmax.DeformableSpring(2);
		scene.deformables{end}.setStiffness(stiffness);
		scene.deformables{end}.setMass(m);
		scene.deformables{end}.setAttachments(scene.bodies{1},[0 0 0]',scene.bodies{end},[0 0 0]');
	case 13
		% This scene doesn't work with RHD!
		scene.name = 'Maximal hybrid dynamics';
		if itype == RECURS_ODE45
			scene.tspan = [0.0 0.0];
		else
			scene.tspan = [0.0 10.0];
		end
		scene.hEuler = 5e-2;
		scene.waxis = 15*[-0.5 1.5 -0.5 0.5 -1 0.5];
		scene.Hexpected(RECURS_ODE45) = 0;
		scene.Hexpected(REDMAX_ODE45) = 18805.7787972479818563;
		scene.Hexpected(REDMAX_EULER) = -765.6565884021354123;
		scene.bodies{1} = redmax.BodyCuboid(density,[1 1 10]);
		scene.joints{1} = redmax.JointRevolute([],scene.bodies{1},[0 1 0]');
		scene.joints{1}.setJointTransform(eye(4));
		scene.bodies{1}.setBodyTransform([eye(3),[0 0 -5]'; 0 0 0 1]);
		scene.bodies{2} = redmax.BodyCuboid(density,[10 1 1]);
		scene.joints{2} = redmax.JointRevolute(scene.joints{1},scene.bodies{2},[0 1 0]');
		scene.joints{2}.setJointTransform([eye(3),[0 0 -10]'; 0 0 0 1]);
		scene.bodies{2}.setBodyTransform([eye(3),[5 0 0]'; 0 0 0 1]);
		scene.bodies{3} = redmax.BodyCuboid(density,[1 1 10]);
		scene.joints{3} = redmax.JointRevolute(scene.joints{2},scene.bodies{3},[0 1 0]');
		scene.joints{3}.setJointTransform([eye(3),[10 0 0]'; 0 0 0 1]);
		scene.bodies{3}.setBodyTransform([eye(3),[0 0 5]'; 0 0 0 1]);
		scene.bodies{4} = redmax.BodyCuboid(density,[10 1 1]);
		scene.joints{4} = redmax.JointRevolute(scene.joints{3},scene.bodies{4},[0 1 0]');
		scene.joints{4}.setJointTransform([eye(3),[0 0 10]'; 0 0 0 1]);
		scene.bodies{4}.setBodyTransform([eye(3),[5 0 0]'; 0 0 0 1]);
		if itype ~= RECURS_ODE45
			scene.constraints{end+1} = redmax.ConstraintPrescBody(scene.bodies{end},[2 4 6],vel);
		end
		scene.sceneFcn = @sceneFcn13;
	case 14
		scene.name = 'Universal joint';
		sides = [1 1 10];
		nbodies = 3;
		scene.waxis = nbodies*10*[-0.7 0.7 -0.7 0.7 -1 0.1];
		scene.Hexpected(RECURS_ODE45) = -0.8577782593856682;
		scene.Hexpected(REDMAX_ODE45) = -0.8577782794236555;
		scene.Hexpected(REDMAX_EULER) = 9679.3365423127470422;
		for i = 1 : nbodies
			scene.bodies{i} = redmax.BodyCuboid(density,sides);
			if i == 1
				scene.joints{i} = redmax.JointUniversal([],scene.bodies{i});
				scene.joints{i}.setJointTransform(eye(4));
			else
				scene.joints{i} = redmax.JointUniversal(scene.joints{i-1},scene.bodies{i});
				scene.joints{i}.setJointTransform([eye(3),[0 0 -10]'; 0 0 0 1]);
			end
			scene.bodies{i}.setBodyTransform([eye(3),[0 0 -5]'; 0 0 0 1]);
			if mod(i,2) == 1
				scene.joints{i}.q(1) = pi/8;
			else
				scene.joints{i}.q(2) = pi/8;
			end
		end
	case 15
		scene.name = 'Prismatic joint';
		scene.waxis = 15*[-1.0 1.0 -0.2 0.2 -1 0.1];
		scene.Hexpected(RECURS_ODE45) = 2.5092171102578504;
		scene.Hexpected(REDMAX_ODE45) = 2.5092171060550754;
		scene.Hexpected(REDMAX_EULER) = -17427.8561972516035894;
		scene.bodies{1} = redmax.BodyCuboid(density,[22 1 1]);
		scene.joints{1} = redmax.JointFixed([],scene.bodies{1});
		scene.joints{1}.setJointTransform(eye(4));
		scene.bodies{1}.setBodyTransform(eye(4));
		scene.bodies{2} = redmax.BodyCuboid(density,[1 1 10]);
		scene.joints{2} = redmax.JointRevolute(scene.joints{1},scene.bodies{2},[0 1 0]');
		scene.joints{2}.setJointTransform([eye(3),[-11 0 0]'; 0 0 0 1]);
		scene.bodies{2}.setBodyTransform([eye(3),[0 0 -5]'; 0 0 0 1]);
		scene.bodies{3} = redmax.BodyCuboid(density,[22 1 1]);
		scene.joints{3} = redmax.JointPrismatic(scene.joints{2},scene.bodies{3},[1 0 0]');
		scene.joints{3}.setJointTransform([eye(3),[0 0 -10]'; 0 0 0 1]);
		scene.bodies{3}.setBodyTransform([eye(3),[11 0 0]'; 0 0 0 1]);
		scene.joints{3}.setGeometry([10 0.5 0.5]);
		scene.bodies{4} = redmax.BodyCuboid(density,[1 1 10]);
		scene.joints{4} = redmax.JointRevolute(scene.joints{3},scene.bodies{4},[0 1 0]');
		scene.joints{4}.setJointTransform([eye(3),[22 0 0]'; 0 0 0 1]);
		scene.bodies{4}.setBodyTransform([eye(3),[0 0 5]'; 0 0 0 1]);
		scene.bodies{5} = redmax.BodyCuboid(density,[1 1 10]);
		scene.joints{5} = redmax.JointRevolute(scene.joints{3},scene.bodies{5},[0 1 0]');
		scene.joints{5}.setJointTransform([eye(3),[11 0 0]'; 0 0 0 1]);
		scene.bodies{5}.setBodyTransform([eye(3),[0 0 -5]'; 0 0 0 1]);
		scene.constraints{1} = redmax.ConstraintLoop(scene.bodies{4},scene.bodies{1});
		scene.constraints{1}.setPositions([0 0 5]',[11 0 0]');
		scene.joints{5}.q(1) = 3*pi/4;
	case 16
		scene.name = 'Planar joint';
		scene.waxis = 15*[-1.0 1.0 -1 1 -1 0.1];
		scene.Hexpected(RECURS_ODE45) = -5.7644270883174613;
		scene.Hexpected(REDMAX_ODE45) = -5.7644270894088550;
		scene.Hexpected(REDMAX_EULER) = 1027.3404900101377279;
		scene.bodies{1} = redmax.BodyCuboid(density,[10 10 1]);
		scene.joints{1} = redmax.JointPlanar([],scene.bodies{1});
		scene.joints{1}.setJointTransform(eye(4));
		scene.bodies{1}.setBodyTransform(eye(4));
		scene.bodies{2} = redmax.BodyCuboid(density,[1 1 10]);
		scene.joints{2} = redmax.JointRevolute(scene.joints{1},scene.bodies{2},[0 1 0]');
		scene.joints{2}.setJointTransform([eye(3),[-5 0 0]'; 0 0 0 1]);
		scene.bodies{2}.setBodyTransform([eye(3),[0 0 -5]'; 0 0 0 1]);
		scene.bodies{3} = redmax.BodyCuboid(density,[1 1 10]);
		scene.joints{3} = redmax.JointRevolute(scene.joints{1},scene.bodies{3},[1 0 0]');
		scene.joints{3}.setJointTransform([eye(3),[0 -5 0]'; 0 0 0 1]);
		scene.bodies{3}.setBodyTransform([eye(3),[0 0 -5]'; 0 0 0 1]);
		scene.joints{2}.q(1) = pi/2;
		scene.joints{3}.q(1) = pi/4;
	case 17
		scene.name = 'Translational joint';
		scene.grav = [0 0 0]';
		scene.waxis = 20*[-1.5 0.5 -1.0 1.0 -0.5 0.5];
		scene.Hexpected(RECURS_ODE45) = 835.418079875333;
		scene.Hexpected(REDMAX_ODE45) = 835.418079875333;
		scene.Hexpected(REDMAX_EULER) = 836.2350063173605577;
		scene.bodies{1} = redmax.BodyCuboid(density,[10 10 1]);
		scene.joints{1} = redmax.JointTranslational([],scene.bodies{1});
		scene.joints{1}.setJointTransform(eye(4));
		scene.bodies{1}.setBodyTransform(eye(4));
		scene.bodies{2} = redmax.BodyCuboid(density,[1 1 10]);
		scene.joints{2} = redmax.JointRevolute(scene.joints{1},scene.bodies{2},[0 1 0]');
		scene.joints{2}.setJointTransform([eye(3),[-5 0 0]'; 0 0 0 1]);
		scene.bodies{2}.setBodyTransform([eye(3),[0 0 -5]'; 0 0 0 1]);
		scene.bodies{3} = redmax.BodyCuboid(density,[1 1 10]);
		scene.joints{3} = redmax.JointRevolute(scene.joints{1},scene.bodies{3},[1 0 0]');
		scene.joints{3}.setJointTransform([eye(3),[0 -5 0]'; 0 0 0 1]);
		scene.bodies{3}.setBodyTransform([eye(3),[0 0 -5]'; 0 0 0 1]);
		scene.joints{1}.qdot(1) = 0.0;
		scene.joints{1}.qdot(3) = 0.0;
		scene.joints{2}.qdot(1) = 2.0;
		scene.joints{3}.qdot(1) = 1.0;
	case 18
		scene.name = 'Free joint';
		scene.tspan = [0.0 7.0];
		scene.grav = [0 0 -1]';
		scene.waxis = 5*[-0.2 0.2 -0.2 0.2 -1.0 1.0];
		scene.Hexpected(RECURS_ODE45) = 4.5466342688068826;
		scene.Hexpected(REDMAX_ODE45) = 4.5466342688068924;
		scene.Hexpected(REDMAX_EULER) = 4.5116666666668817;
		scene.bodies{1} = redmax.BodyCuboid(density,[1 1 1]);
		freeType = 2;
		if freeType == 1
			scene.joints{1} = reduced.JointFree([],scene.bodies{1});
			scene.joints{1}.qdot = [0.2 0.4 0.6 0 0 3]';
		elseif freeType == 2
			% This is the best
			scene.joints{1} = reduced.JointFree3D([],scene.bodies{1});
			scene.joints{1}.qdot = [ 0 0 3 0.2 0.4 0.6]';
		elseif freeType == 3
			jointT = reduced.JointTranslational([],scene.bodies{1});
			jointS = reduced.JointSphericalExp([],scene.bodies{1});
			scene.joints{1} = reduced.JointComposite([],scene.bodies{1},jointT,jointS);
			scene.joints{1}.qdot = [0 0 3 0.2 0.4 0.6]';
		end
		scene.joints{1}.setJointTransform(eye(4));
		scene.bodies{1}.setBodyTransform(eye(4));
	case 19
		scene.name = 'Composite joint';
		scene.hEuler = 2e-2;
		scene.waxis = 5*[-0.1 2.1 -2 2 -2 2];
		scene.Hexpected(RECURS_ODE45) = -8.7962825142149086;
		scene.Hexpected(REDMAX_ODE45) = -8.7962825142917609;
		scene.Hexpected(REDMAX_EULER) = 918.5086593280602756;
		scene.bodies{1} = redmax.BodyCuboid(density,[1 1 10]);
		revolute  = redmax.JointRevolute( [],scene.bodies{1},[1 0 0]');
		prismatic = redmax.JointPrismatic([],scene.bodies{1},[1 0 0]');
		scene.joints{1} = redmax.JointComposite([],scene.bodies{1},revolute,prismatic);
		scene.joints{1}.setJointTransform(eye(4));
		scene.bodies{1}.setBodyTransform([eye(3),[0 0 5]'; 0 0 0 1]);
		scene.joints{1}.q(1) = 0.1;
		scene.joints{1}.qdot(2) = 1.0;
	case 20
		% This scene doesn't work with RHD!
		scene.name = 'Reduced/maximal hybrid dynamics';
		if itype == RECURS_ODE45
			scene.tspan = [0.0 0.0];
		else
			scene.tspan = [0.0 10.0];
		end
		scene.hEuler = 5e-2;
		scene.waxis = [];%15*[-0.5 1.5 -0.5 0.5 -1 0.5];
		scene.Hexpected(RECURS_ODE45) = 0;
		scene.Hexpected(REDMAX_ODE45) = 72822.5867580034246203;
		scene.Hexpected(REDMAX_EULER) = 50368.3587015155280824;
		scene.bodies{1} = redmax.BodyCuboid(density,[1 1 10]);
		scene.joints{1} = redmax.JointRevolute([],scene.bodies{1},[0 1 0]');
		scene.joints{1}.setJointTransform(eye(4));
		scene.bodies{1}.setBodyTransform([eye(3),[0 0 -5]'; 0 0 0 1]);
		scene.bodies{2} = redmax.BodyCuboid(density,[10 1 1]);
		scene.joints{2} = redmax.JointRevolute(scene.joints{1},scene.bodies{2},[0 1 0]');
		scene.joints{2}.setJointTransform([eye(3),[0 0 -10]'; 0 0 0 1]);
		scene.bodies{2}.setBodyTransform([eye(3),[5 0 0]'; 0 0 0 1]);
		scene.bodies{3} = redmax.BodyCuboid(density,[10 1 1]);
		scene.joints{3} = redmax.JointRevolute(scene.joints{2},scene.bodies{3},[0 1 0]');
		scene.joints{3}.setJointTransform([eye(3),[10 0 0]'; 0 0 0 1]);
		scene.bodies{3}.setBodyTransform([eye(3),[5 0 0]'; 0 0 0 1]);
		scene.bodies{4} = redmax.BodyCuboid(density,[1 1 10]);
		scene.joints{4} = redmax.JointRevolute(scene.joints{3},scene.bodies{4},[0 1 0]');
		scene.joints{4}.setJointTransform([eye(3),[10 0 0]'; 0 0 0 1]);
		scene.bodies{4}.setBodyTransform([eye(3),[0 0 5]'; 0 0 0 1]);
		scene.bodies{5} = redmax.BodyCuboid(density,[10 1 1]);
		scene.joints{5} = redmax.JointRevolute(scene.joints{4},scene.bodies{5},[0 1 0]');
		scene.joints{5}.setJointTransform([eye(3),[0 0 10]'; 0 0 0 1]);
		scene.bodies{5}.setBodyTransform([eye(3),[5 0 0]'; 0 0 0 1]);
		if itype ~= RECURS_ODE45
			scene.constraints{end+1} = redmax.ConstraintPrescBody(scene.bodies{end},[2 4 6],vel);
			scene.constraints{end+1} = redmax.ConstraintPrescJoint(scene.joints{3},vel);
		end
		scene.sceneFcn = @sceneFcn20;
	case 21
		scene.name = 'Spline curve joint';
		scene.hEuler = 5e-3;
		scene.waxis = 15*[-1.5 1.5 -0.5 0.5 -3 0.0];
		scene.Hexpected(RECURS_ODE45) = -18.5261468157405034;
		scene.Hexpected(REDMAX_ODE45) = -18.5261468464450445;
		scene.Hexpected(REDMAX_EULER) = -30627.8479814097263443;
		scene.bodies{1} = redmax.BodyCuboid(density,[1 1 10]);
		scene.joints{1} = redmax.JointRevolute([],scene.bodies{1},[0 1 0]');
		scene.joints{1}.setJointTransform(eye(4));
		scene.bodies{1}.setBodyTransform([eye(3),[0 0 -5]'; 0 0 0 1]);
		scene.bodies{2} = redmax.BodyCuboid(density,[10 1 1]);
		scene.joints{2} = redmax.JointSplineCurve(scene.joints{1},scene.bodies{2});
		scene.joints{2}.setJointTransform([eye(3),[0 0 -10]'; 0 0 0 1]);
		scene.joints{2}.addControlFrame([se3.aaToMat([0 1 0],pi),   [-10 0  0]'; 0 0 0 1]);
		scene.joints{2}.addControlFrame([se3.aaToMat([0 1 0],pi/2), [  0 0 -2]'; 0 0 0 1]);
		scene.joints{2}.addControlFrame([se3.aaToMat([0 1 0],0),    [ 10 0  0]'; 0 0 0 1]);
		scene.joints{2}.addControlFrame([se3.aaToMat([0 1 0],-pi/2),[  0 0  2]'; 0 0 0 1]);
		scene.bodies{2}.setBodyTransform([eye(3),[5 0 0]'; 0 0 0 1]);
		scene.bodies{3} = redmax.BodyCuboid(density,[10 1 1]);
		scene.joints{3} = redmax.JointRevolute(scene.joints{2},scene.bodies{3},[0 1 0]');
		scene.joints{3}.setJointTransform([eye(3),[10 0 0]'; 0 0 0 1]);
		scene.bodies{3}.setBodyTransform([eye(3),[5 0 0]'; 0 0 0 1]);
		scene.joints{2}.q(1) = 0;
		scene.joints{3}.q(1) = 15*pi/16;
	case 22
		scene.name = 'Spline surface joint';
		scene.waxis = 10*[-1.5 1.5 -1.5 1.5 -3 0];
		scene.Hexpected(RECURS_ODE45) = -1.4604474130101153;
		scene.Hexpected(REDMAX_ODE45) = -1.4604474127263529;
		scene.Hexpected(REDMAX_EULER) = 2154.9740571399888722;
		scene.bodies{1} = redmax.BodyCuboid(density,[1 1 10]);
		scene.joints{1} = redmax.JointRevolute([],scene.bodies{1},[1 0 0]');
		scene.joints{1}.setJointTransform(eye(4));
		scene.bodies{1}.setBodyTransform([eye(3),[0 0 -5]'; 0 0 0 1]);
		scene.bodies{2} = redmax.BodyCuboid(density,[1 1 10]);
		scene.joints{2} = redmax.JointSplineSurface(scene.joints{1},scene.bodies{2});
		scene.joints{2}.setJointTransform([eye(3),[0 0 -14]'; 0 0 0 1]);
		t0 = 15;
		r0 = pi/4;
		for i1 = 1 : 4
			s1 = (i1-1)/(4-1);
			x = (1-s1)*(-t0) + s1*t0;
			a = (1-s1)*(-r0) + s1*r0;
			for i2 = 1 : 4
				s2 = (i2-1)/(4-1);
				y = (1-s2)*(-t0) + s2*t0;
				z = 0.05*(x*x + y*y);
				b = (1-s1)*(-r0) + s1*r0;
				c = 0;
				scene.joints{2}.setControlFrame(i1,i2,[x y z a b c]');
			end
		end
		scene.bodies{2}.setBodyTransform([eye(3),[0 0 -5]'; 0 0 0 1]);
		scene.bodies{3} = redmax.BodyCuboid(density,[1 1 10]);
		scene.joints{3} = redmax.JointRevolute(scene.joints{2},scene.bodies{3},[0 1 0]');
		scene.joints{3}.setJointTransform([eye(3),[0 0 -10]'; 0 0 0 1]);
		scene.bodies{3}.setBodyTransform([eye(3),[0 0 -5]'; 0 0 0 1]);
		scene.joints{1}.q(1) = pi/8;
		scene.joints{2}.q(1) = 0.5;
		scene.joints{2}.q(2) = 0.5;
		scene.joints{3}.q(1) = pi/4;
	case 23
		scene.name = 'Point-to-point spring';
		sides = [10 1 1];
		nbodies = 4;
		scene.waxis = nbodies*10*[-1 1 -0.1 0.1 -1 0.1];
		scene.Hexpected(RECURS_ODE45) = -0.2671194855411159;
		scene.Hexpected(REDMAX_ODE45) = -0.2671194856266084;
		scene.Hexpected(REDMAX_EULER) = 2125.1442936080966319;
		for i = 1 : nbodies
			scene.bodies{i} = redmax.BodyCuboid(density,sides); %#ok<*SAGROW>
			if i == 1
				scene.joints{i} = redmax.JointRevolute([],scene.bodies{i},[0 1 0]');
				scene.joints{i}.setJointTransform(eye(4));
				scene.joints{i}.q(1) = pi/2;
			else
				scene.joints{i} = redmax.JointRevolute(scene.joints{i-1},scene.bodies{i},[0 1 0]');
				scene.joints{i}.setJointTransform([eye(3),[10 0 0]'; 0 0 0 1]);
				scene.joints{i}.q(1) = pi/16;
			end
			scene.bodies{i}.setBodyTransform([eye(3),[5 0 0]'; 0 0 0 1]);
			if i > 1
				scene.springs{i-1} = redmax.SpringPointPoint(scene.bodies{i-1},[-1 0 0]',scene.bodies{i},[5 0 0]');
				scene.springs{i-1}.setStiffness(1e2);
			end
		end
	case 24
		scene.name = 'Spring damper';
		if itype == RECURS_ODE45 || itype == REDMAX_ODE45
			scene.tspan = [0.0 0.0];
		end
		sides = [10 1 1];
		scene.waxis = 3*10*[-0.1 1 -0.1 0.1 -1 0.1];
		scene.Hexpected(RECURS_ODE45) = 0;
		scene.Hexpected(REDMAX_ODE45) = 0;
		scene.Hexpected(REDMAX_EULER) = -18398.2926338097677217;
		testPCG = false;
		scene.bodies{1} = redmax.BodyCuboid(density,sides);
		if testPCG
			scene.joints{1} = redmax.JointRevolute([],scene.bodies{1},[0 1 0]');
			scene.joints{1}.setStiffness(1e9);
			scene.joints{1}.setDamping(1e3);
		else
			scene.joints{1} = redmax.JointFixed([],scene.bodies{1});
		end
		scene.joints{1}.setJointTransform(eye(4));
		scene.bodies{1}.setBodyTransform([eye(3),[5 0 0]'; 0 0 0 1]);
		scene.bodies{2} = redmax.BodyCuboid(density,sides);
		scene.joints{2} = redmax.JointRevolute(scene.joints{1},scene.bodies{2},[0 1 0]');
		scene.joints{2}.setJointTransform([eye(3),[10 0 0]'; 0 0 0 1]);
		scene.joints{2}.q(1) = pi/2;
		scene.bodies{2}.setBodyTransform([eye(3),[5 0 0]'; 0 0 0 1]);
		scene.bodies{3} = redmax.BodyCuboid(density,sides);
		scene.joints{3} = redmax.JointRevolute(scene.joints{2},scene.bodies{3},[0 1 0]');
		scene.joints{3}.setJointTransform([eye(3),[10 0 0]'; 0 0 0 1]);
		scene.joints{3}.q(1) = -pi/2;
		scene.bodies{3}.setBodyTransform([eye(3),[5 0 0]'; 0 0 0 1]);
		scene.springs{1} = redmax.SpringDamper(scene.bodies{1},[-2 0 -0.5]',scene.bodies{2},[1 0.5 -0.5]');
		scene.springs{1}.setStiffness(1e6);
		scene.springs{1}.setDamping(1e3);
		if testPCG
			scene.springs{2} = redmax.SpringDamper(scene.bodies{1},[2 0 -0.5]',scene.bodies{3},[1 -0.5 0.5]');
			scene.springs{2}.setStiffness(1e6);
			scene.springs{2}.setDamping(1e3);
		end
	case 25
		scene.name = 'Composite body';
		sides = [1 1 10];
		scene.waxis = 10*[-1 1 -0.1 0.1 -1.5 0.1];
		scene.Hexpected(RECURS_ODE45) = -11.2086902929768257;
		scene.Hexpected(REDMAX_ODE45) = -11.2086902930313954;
		scene.Hexpected(REDMAX_EULER) = 1261.6057602036726166;
		if false
			% With a fixed joint
			scene.bodies{1} = redmax.BodyCuboid(density,sides);
			scene.joints{1} = redmax.JointRevolute([],scene.bodies{1},[0 1 0]');
			scene.joints{1}.setJointTransform(eye(4));
			scene.bodies{1}.setBodyTransform([eye(3),[0 0 -5]'; 0 0 0 1]);
			scene.bodies{2} = redmax.BodyCylinder(density,1.0,10.0);
			scene.joints{2} = redmax.JointFixed(scene.joints{1},scene.bodies{2});
			scene.joints{2}.setJointTransform([eye(3),[0 0 -10]'; 0 0 0 1]);
			scene.bodies{2}.setBodyTransform([se3.aaToMat([0 1 0],pi/2),[5 0 0]'; 0 0 0 1]);
		else
			% With a composite body
			body1a = redmax.BodyCuboid(density,sides);
			body1b = redmax.BodyCylinder(density,1.0,10.0);
			scene.bodies{1} = redmax.BodyComposite(density);
			scene.joints{1} = redmax.JointRevolute([],scene.bodies{1},[0 1 0]');
			scene.joints{1}.setJointTransform(eye(4));
			scene.bodies{1}.addBody(body1a,[eye(3),[0 0 -5]'; 0 0 0 1]);
			scene.bodies{1}.addBody(body1b,[se3.aaToMat([0 1 0],pi/2),[5 0 -10]'; 0 0 0 1]);
			E = scene.bodies{1}.computeInertiaFrame();
			scene.bodies{1}.setBodyTransform(E);
		end
	case 26
		scene.name = 'Obj body';
		scene.tspan = [0 1];
		scene.waxis = 4*[-1 1 -0.5 0.5 -1.9 0.1];
		scene.Hexpected(RECURS_ODE45) = -0.0441469434378234;
		scene.Hexpected(REDMAX_ODE45) = -0.0441469434412625;
		scene.Hexpected(REDMAX_EULER) = 59.8820887155682158;
		E0 = [eye(3),[0.5 0 -1.5]'; 0 0 0 1]; % body transform
		if false
			% With BodyCuboid
			scene.bodies{1} = redmax.BodyCuboid(density,[1 2 3]);
			scene.joints{1} = redmax.JointRevolute([],scene.bodies{1},[0 1 0]');
			scene.joints{1}.setJointTransform(eye(4));
			scene.bodies{1}.setBodyTransform(E0);
			scene.bodies{2} = redmax.BodyCuboid(density,[1 2 3]);
			scene.joints{2} = redmax.JointRevolute(scene.joints{1},scene.bodies{2},[0 1 0]');
			scene.joints{2}.setJointTransform([eye(3),[0 0 -3]'; 0 0 0 1]);
			scene.bodies{2}.setBodyTransform(E0);
		else
			% With BodyMeshObj
			scene.bodies{1} = redmax.BodyMeshObj(density,'cuboid.obj');
			scene.joints{1} = redmax.JointRevolute([],scene.bodies{1},[0 1 0]');
			scene.joints{1}.setJointTransform(eye(4));
			scene.bodies{1}.setBodyTransform(E0 * scene.bodies{1}.E_oi);
			scene.bodies{2} = redmax.BodyMeshObj(density,'cuboid.obj');
			scene.joints{2} = redmax.JointRevolute(scene.joints{1},scene.bodies{2},[0 1 0]');
			scene.joints{2}.setJointTransform([eye(3),[0 0 -3]'; 0 0 0 1]);
			scene.bodies{2}.setBodyTransform(E0 * scene.bodies{2}.E_oi);
		end
	case 27
		scene.name = 'Internal friction revolute';
		if itype == REDMAX_EULER
			scene.tspan = [0 1];
		else
			scene.tspan = [0 0];
		end
		scene.fric = true;
		sides = [10 1 1];
		nbodies = 2;
		angle = pi/4;
		scene.waxis = nbodies*5*[-1 1 -1 1 -2 0];
		scene.Hexpected(RECURS_ODE45) = 0;
		scene.Hexpected(REDMAX_ODE45) = 0;
		scene.Hexpected(REDMAX_EULER) = -137371.1285153437056579;
		for i = 1 : nbodies
			scene.bodies{i} = redmax.BodyCuboid(density,sides); %#ok<*SAGROW>
			if i == 1
				scene.joints{i} = redmax.JointRevolute([],scene.bodies{i},[0 0 1]');
				scene.joints{i}.setJointTransform([se3.aaToMat([1 0 0]',angle),[0 0 0]'; 0 0 0 1]);
				%scene.joints{i}.q = -pi/2;
			else
				scene.joints{i} = redmax.JointRevolute(scene.joints{i-1},scene.bodies{i},[0 0 1]');
				scene.joints{i}.setJointTransform([eye(3),[10 0 0]'; 0 0 0 1]);
			end
			scene.bodies{i}.setBodyTransform([eye(3),[5 0 0]'; 0 0 0 1]);
			scene.joints{i}.setGeometry(0.5,1);
		end
	case 28
		scene.name = 'Internal friction spherical';
		if itype == REDMAX_EULER
			scene.tspan = [0 1];
		else
			scene.tspan = [0 0];
		end
		scene.fric = true;
		scene.mu(1) = 5.0;
		sides = [10 1 1];
		nbodies = 2;
		angle = 0;
		scene.waxis = nbodies*5*[-1 1 -1 1 -2 0];
		scene.Hexpected(RECURS_ODE45) = 0;
		scene.Hexpected(REDMAX_ODE45) = 0;
		scene.Hexpected(REDMAX_EULER) = -184565.9459125697612762;
		for i = 1 : nbodies
			scene.bodies{i} = redmax.BodyCuboid(density,sides); %#ok<*SAGROW>
			if i == 1
				scene.joints{i} = redmax.JointSphericalExp([],scene.bodies{i});
				scene.joints{i}.setJointTransform([se3.aaToMat([1 0 0]',angle),[0 0 0]'; 0 0 0 1]);
			else
				scene.joints{i} = redmax.JointSphericalExp(scene.joints{i-1},scene.bodies{i});
				scene.joints{i}.setJointTransform([eye(3),[10 0 0]'; 0 0 0 1]);
				%scene.joints{i}.q = [0 0 pi/2]';
			end
			scene.bodies{i}.setBodyTransform([eye(3),[5 0 0]'; 0 0 0 1]);
			scene.joints{i}.setGeometry(1.0);
		end
	case 29
		scene.name = 'Internal friction prismatic';
		if itype == REDMAX_EULER
			scene.tspan = [0 1.0];
		else
			scene.tspan = [0 0];
		end
		scene.fric = true;
		scene.mu(1) = 0.8;
		sides = [10 1 1];
		nbodies = 2;
		angle = pi/3;
		scene.waxis = nbodies*5*[-1 1 -1 1 -2 0];
		scene.Hexpected(RECURS_ODE45) = 0;
		scene.Hexpected(REDMAX_ODE45) = 0;
		scene.Hexpected(REDMAX_EULER) = -256391.5065969563729595;
		for i = 1 : nbodies
			scene.bodies{i} = redmax.BodyCuboid(density,sides); %#ok<*SAGROW>
			if i == 1
				scene.joints{i} = redmax.JointPrismatic([],scene.bodies{i},[1 0 0]');
				scene.joints{i}.setJointTransform([se3.aaToMat([0 1 0]',angle),[0 0 0]'; 0 0 0 1]);
			else
				scene.joints{i} = redmax.JointPrismatic(scene.joints{i-1},scene.bodies{i},[1 0 0]');
				scene.joints{i}.setJointTransform([eye(3),[10 0 0]'; 0 0 0 1]);
			end
			scene.bodies{i}.setBodyTransform([eye(3),[5 0 0]'; 0 0 0 1]);
			scene.joints{i}.setGeometry([5,0.5,5]);
		end
	case 30
		% https://en.wikipedia.org/wiki/File:Grashof_Type_I_Four-Bar_Kinematic_Inversions.gif
		scene.name = 'Internal friction 4-bar linkage';
		if itype == REDMAX_EULER
			scene.tspan = [0 1];
			scene.hEuler = 5e-3;
			scene.fric = true;
			scene.mu(1) = 0.3;
			scene.baumgarte(3) = 1/scene.hEuler;
			scene.drawHz = 30;
		else
			scene.tspan = [0 1.5];
			scene.fric = false;
		end
		scene.waxis = [-10 6 -1 1 -5 15];
		scene.Hexpected(RECURS_ODE45) = -2.1968461897658926;
		scene.Hexpected(REDMAX_ODE45) = -2.1968459311251536;
		scene.Hexpected(REDMAX_EULER) = -14581.1508526040543074;
		scene.bodies{1} = redmax.BodyCuboid(density,[10 0.5 0.5]);
		scene.joints{1} = redmax.JointFixed([],scene.bodies{1});
		scene.joints{1}.setJointTransform(eye(4));
		scene.bodies{2} = redmax.BodyCuboid(density,[4 0.5 0.5]);
		scene.joints{2} = redmax.JointRevolute(scene.joints{1},scene.bodies{2},[0 1 0]');
		scene.joints{2}.setJointTransform([eye(3),[-5 0 0]'; 0 0 0 1]);
		scene.joints{2}.setGeometry(0.5,0.5);
		scene.bodies{2}.setBodyTransform([eye(3),[2 0 0]'; 0 0 0 1]);
		% https://en.wikipedia.org/wiki/Altitude_(triangle)#Altitude_in_terms_of_the_sides
		a = 6;
		b = 12;
		c = 14;
		s = 0.5*(a + b + c);
		z = 2*sqrt(s*(s-a)*(s-b)*(s-c))/a;
		x = sqrt(14*14 - z*z);
		theta = atan2(z,x);
		scene.bodies{3} = redmax.BodyCuboid(density,[14 0.5 0.5]);
		scene.joints{3} = redmax.JointRevolute(scene.joints{2},scene.bodies{3},[0 1 0]');
		scene.joints{3}.setJointTransform([eye(3),[4 0 0]'; 0 0 0 1]);
		scene.joints{3}.setGeometry(0.5,0.5);
		scene.bodies{3}.setBodyTransform([se3.aaToMat([0 1 0],-theta),[0.5*x,0,0.5*z]'; 0 0 0 1]);
		scene.bodies{4} = redmax.BodyCuboid(density,[12 0.5 0.5]);
		scene.joints{4} = redmax.JointRevolute(scene.joints{3},scene.bodies{4},[0 1 0]');
		scene.joints{4}.setJointTransform([eye(3),[x,0,z]'; 0 0 0 1]);
		scene.joints{4}.setGeometry(0.5,0.5);
		x = x - 6;
		theta = atan2(z,x);
		scene.bodies{4}.setBodyTransform([se3.aaToMat([0 1 0],-theta),[-0.5*x,0,-0.5*z]'; 0 0 0 1]);
		scene.constraints{1} = redmax.ConstraintLoop(scene.bodies{4},scene.bodies{1});
		scene.constraints{1}.setPositions([-6 0 0]',[5 0 0]');
		scene.constraints{1}.setGeometry(0.5,0.5);
	case 31
		scene.name = 'External friction';
		if itype == RECURS_ODE45 || itype == REDMAX_ODE45
			scene.tspan = [0.0 0.0];
		else
			scene.tspan = [0 2];
		end
		sides = [10 1 1];
		nbodies = 2;
		scene.waxis = nbodies*5*[-1 1 -0.1 0.1 -2 0];
		scene.fric = true;
		scene.mu = [0.1 0.2];
		scene.SPiterMax = 100;
		scene.SPconv = 1e-3;
		scene.SPathresh = 1e-10;
		scene.SPreg = 1e-6;
		scene.baumgarte(3) = 0.1/scene.hEuler;
		scene.Hexpected(RECURS_ODE45) = 0;
		scene.Hexpected(REDMAX_ODE45) = 0;
		scene.Hexpected(REDMAX_EULER) = -90558.1346001959173009;
		for i = 1 : nbodies
			scene.bodies{i} = redmax.BodyCuboid(density,sides);
			if i == 1
				scene.joints{i} = redmax.JointRevolute([],scene.bodies{i},[0 1 0]');
				scene.joints{i}.setJointTransform(eye(4));
				scene.joints{i}.q(1) = pi/4;
			else
				scene.joints{i} = redmax.JointRevolute(scene.joints{i-1},scene.bodies{i},[0 1 0]');
				scene.joints{i}.setJointTransform([eye(3),[10 0 0]'; 0 0 0 1]);
				scene.joints{i}.q(1) = -pi/4;
			end
			scene.bodies{i}.setBodyTransform([eye(3),[5 0 0]'; 0 0 0 1]);
		end
		% Add the sphere at the end
		scene.bodies{end+1} = redmax.BodySphere(density,sides(2));
		scene.joints{end+1} = redmax.JointFixed(scene.joints{end},scene.bodies{end});
		scene.joints{end}.setJointTransform([eye(3),[10 0 0]'; 0 0 0 1]);
		scene.bodies{end}.setBodyTransform(eye(4));
		% Add floor collision
		scene.constraints{end+1} = redmax.ConstraintFloor(scene.bodies{end});
		scene.constraints{end}.setTransform([eye(3),[0 0 -15]'; 0 0 0 1]);
	case 32
		% https://en.wikipedia.org/wiki/File:Grashof_Type_I_Four-Bar_Kinematic_Inversions.gif
		scene.name = 'Prescribed joint via maximal constraint';
		if itype == REDMAX_EULER
			scene.tspan = [0 1];
			scene.hEuler = 5e-3;
			scene.fric = false;
			scene.mu = [0.1 0.1];
			scene.baumgarte(3) = 0.1/scene.hEuler;
			scene.drawHz = 30;
		else
			scene.tspan = [0 0];
		end
		scene.waxis = [-8 8 -1 1 -5 15];
		scene.Hexpected(RECURS_ODE45) = 0;
		scene.Hexpected(REDMAX_ODE45) = 0;
		scene.Hexpected(REDMAX_EULER) = 4641.9162041538456833;
		scene.bodies{1} = redmax.BodyCuboid(density,[10 0.5 0.5]);
		scene.joints{1} = redmax.JointRevolute([],scene.bodies{1},[0 1 0]');
		scene.joints{1}.setJointTransform([eye(3),[0 0 10]'; 0 0 0 1]);
		scene.joints{1}.q = pi;
		scene.bodies{2} = redmax.BodyCuboid(density,[4 0.5 0.5]);
		scene.joints{2} = redmax.JointRevolute(scene.joints{1},scene.bodies{2},[0 1 0]');
		scene.joints{2}.setJointTransform([eye(3),[-5 0 0]'; 0 0 0 1]);
		scene.joints{2}.setGeometry(0.5,0.5);
		scene.bodies{2}.setBodyTransform([eye(3),[2 0 0]'; 0 0 0 1]);
		% https://en.wikipedia.org/wiki/Altitude_(triangle)#Altitude_in_terms_of_the_sides
		a = 6;
		b = 12;
		c = 14;
		s = 0.5*(a + b + c);
		z = 2*sqrt(s*(s-a)*(s-b)*(s-c))/a;
		x = sqrt(14*14 - z*z);
		theta = atan2(z,x);
		scene.bodies{3} = redmax.BodyCuboid(density,[14 0.5 0.5]);
		scene.joints{3} = redmax.JointRevolute(scene.joints{2},scene.bodies{3},[0 1 0]');
		scene.joints{3}.setJointTransform([eye(3),[4 0 0]'; 0 0 0 1]);
		scene.joints{3}.setGeometry(0.5,0.5);
		scene.bodies{3}.setBodyTransform([se3.aaToMat([0 1 0],-theta),[0.5*x,0,0.5*z]'; 0 0 0 1]);
		scene.bodies{4} = redmax.BodyCuboid(density,[12 0.5 0.5]);
		scene.joints{4} = redmax.JointRevolute(scene.joints{3},scene.bodies{4},[0 1 0]');
		scene.joints{4}.setJointTransform([eye(3),[x,0,z]'; 0 0 0 1]);
		scene.joints{4}.setGeometry(0.5,0.5);
		x = x - 6;
		theta = atan2(z,x);
		scene.bodies{4}.setBodyTransform([se3.aaToMat([0 1 0],-theta),[-0.5*x,0,-0.5*z]'; 0 0 0 1]);
		scene.constraints{1} = redmax.ConstraintLoop(scene.bodies{4},scene.bodies{1});
		scene.constraints{1}.setPositions([-6 0 0]',[5 0 0]');
		scene.constraints{1}.setGeometry(0.5,0.5);
		% Prescribe driver
		scene.constraints{end+1} = redmax.ConstraintPrescJointM(scene.joints{2},vel);
		scene.sceneFcn = @sceneFcn32;
	case 33
		% https://en.wikipedia.org/wiki/File:Grashof_Type_I_Four-Bar_Kinematic_Inversions.gif
		scene.name = 'External friction 4-bar linkage';
		if itype == REDMAX_EULER
			scene.tspan = [0 1];
			scene.hEuler = 5e-3;
			scene.fric = true;
			scene.mu = [0.8 0.8];
			scene.baumgarte(3) = 0.1/scene.hEuler;
			scene.drawHz = 30;
		else
			scene.tspan = [0 0];
		end
		scene.waxis = [-10 6 -1 1 -5 15];
		scene.Hexpected(RECURS_ODE45) = 0;
		scene.Hexpected(REDMAX_ODE45) = 0;
		scene.Hexpected(REDMAX_EULER) = 19598.8605086512579874;
		scene.bodies{1} = redmax.BodyCuboid(density,[10 0.5 0.5]);
		scene.joints{1} = redmax.JointFree([],scene.bodies{1});
		scene.joints{1}.setJointTransform([eye(3),[0 0 1]'; 0 0 0 1]);
		scene.bodies{2} = redmax.BodyCuboid(density,[4 0.5 0.5]);
		scene.joints{2} = redmax.JointRevolute(scene.joints{1},scene.bodies{2},[0 1 0]');
		scene.joints{2}.setJointTransform([eye(3),[-5 0 0]'; 0 0 0 1]);
		scene.joints{2}.setGeometry(0.5,0.5);
		scene.bodies{2}.setBodyTransform([eye(3),[2 0 0]'; 0 0 0 1]);
		% https://en.wikipedia.org/wiki/Altitude_(triangle)#Altitude_in_terms_of_the_sides
		a = 6;
		b = 12;
		c = 14;
		s = 0.5*(a + b + c);
		z = 2*sqrt(s*(s-a)*(s-b)*(s-c))/a;
		x = sqrt(14*14 - z*z);
		theta = atan2(z,x);
		scene.bodies{3} = redmax.BodyCuboid(density,[14 0.5 0.5]);
		scene.joints{3} = redmax.JointRevolute(scene.joints{2},scene.bodies{3},[0 1 0]');
		scene.joints{3}.setJointTransform([eye(3),[4 0 0]'; 0 0 0 1]);
		scene.joints{3}.setGeometry(0.5,0.5);
		scene.bodies{3}.setBodyTransform([se3.aaToMat([0 1 0],-theta),[0.5*x,0,0.5*z]'; 0 0 0 1]);
		scene.bodies{4} = redmax.BodyCuboid(density,[12 0.5 0.5]);
		scene.joints{4} = redmax.JointRevolute(scene.joints{3},scene.bodies{4},[0 1 0]');
		scene.joints{4}.setJointTransform([eye(3),[x,0,z]'; 0 0 0 1]);
		scene.joints{4}.setGeometry(0.5,0.5);
		x = x - 6;
		theta = atan2(z,x);
		scene.bodies{4}.setBodyTransform([se3.aaToMat([0 1 0],-theta),[-0.5*x,0,-0.5*z]'; 0 0 0 1]);
		scene.constraints{1} = redmax.ConstraintLoop(scene.bodies{4},scene.bodies{1});
		scene.constraints{1}.setPositions([-6 0 0]',[5 0 0]');
		scene.constraints{1}.setGeometry(0.5,0.5);		
		% Front sphere
		scene.bodies{end+1} = redmax.BodySphere(density,1.0);
		scene.joints{end+1} = redmax.JointFixed(scene.joints{1},scene.bodies{end});
		scene.bodies{end}.setBodyTransform([eye(3),[-5 0 0]'; 0 0 0 1]);
		scene.constraints{end+1} = redmax.ConstraintFloor(scene.bodies{end});
		% Back sphere
		scene.bodies{end+1} = redmax.BodySphere(density,1.0);
		scene.joints{end+1} = redmax.JointFixed(scene.joints{1},scene.bodies{end});
		scene.bodies{end}.setBodyTransform([eye(3),[5 0 0]'; 0 0 0 1]);
		scene.constraints{end+1} = redmax.ConstraintFloor(scene.bodies{end});
		% Middle cylinder
		scene.bodies{end+1} = redmax.BodyCylinder(density,0.5,5.0);
		scene.joints{end+1} = redmax.JointFixed(scene.joints{3},scene.bodies{end});
		scene.bodies{end}.setBodyTransform([se3.aaToMat([1 0 0],pi/2),[0 0 0]'; 0 0 0 1]);
		% Middle right sphere
		scene.bodies{end+1} = redmax.BodySphere(density,1.0);
		scene.joints{end+1} = redmax.JointFixed(scene.joints{end},scene.bodies{end});
		scene.bodies{end}.setBodyTransform([eye(3),[0 2.5 0]'; 0 0 0 1]);
		scene.constraints{end+1} = redmax.ConstraintFloor(scene.bodies{end});
		% Middle left sphere
		scene.bodies{end+1} = redmax.BodySphere(density,1.0);
		scene.joints{end+1} = redmax.JointFixed(scene.joints{end-1},scene.bodies{end});
		scene.bodies{end}.setBodyTransform([eye(3),[0 -2.5 0]'; 0 0 0 1]);
		scene.constraints{end+1} = redmax.ConstraintFloor(scene.bodies{end});
		% Prescribed driver
		scene.constraints{end+1} = redmax.ConstraintPrescJointM(scene.joints{2},vel);
		scene.sceneFcn = @sceneFcn33;
	case 34
		scene.name = 'Gears';
		% cm g s
		scene.tspan = [0 1];
		scene.hEuler = 1e-2;
		scene.drawHz = 100;
		rng(0);
		scene.waxis = [-7 7 -2 2 -7 3];
		scene.Hexpected(RECURS_ODE45) = -0.1839463800694148;
		scene.Hexpected(REDMAX_ODE45) = -0.1839463800738486;
		scene.Hexpected(REDMAX_EULER) = -39.5338848225347874;
		E0 = [se3.aaToMat([1 0 0],pi/2),[0 0 0]'; 0 0 0 1]; % body transform for gear
		% Main bar
		scene.bodies{1} = redmax.BodyCuboid(density,[1 1 6]);
		scene.joints{1} = redmax.JointFixed([],scene.bodies{1});
		scene.joints{1}.setJointTransform([se3.aaToMat([0 0 1],pi),[0 0 0]';0 0 0 1]);
		scene.bodies{1}.setBodyTransform(eye(4));
		scene.joints{1}.q = pi/2;
		% Top axle
		scene.bodies{2} = redmax.BodyCylinder(density,0.2,3.5);
		scene.joints{2} = redmax.JointRevolute(scene.joints{1},scene.bodies{2},[0 1 0]');
		scene.joints{2}.setGeometry(0.1,0.1);
		scene.joints{2}.setJointTransform([eye(3),[0 -0.25 2]';0 0 0 1]);
		scene.bodies{2}.setBodyTransform([se3.aaToMat([1 0 0],pi/2),[0 0 0]';0 0 0 1]);
		% Top gear
		scene.bodies{3} = redmax.BodyMeshObj(0.1*density,'gears.obj');
		scene.joints{3} = redmax.JointFixed(scene.joints{2},scene.bodies{3});
		scene.joints{3}.setJointTransform([eye(3),[0 -0.35 0]'; 0 0 0 1]);
		scene.bodies{3}.setBodyTransform(E0 * scene.bodies{3}.E_oi);
		% Bottom axle
		scene.bodies{4} = redmax.BodyCylinder(density,0.2,3.0);
		scene.joints{4} = redmax.JointRevolute(scene.joints{1},scene.bodies{4},[0 1 0]');
		scene.joints{4}.setGeometry(0.1,0.1);
		scene.joints{4}.setJointTransform([eye(3),[0 -0.5 -2]';0 0 0 1]);
		scene.bodies{4}.setBodyTransform([se3.aaToMat([1 0 0],pi/2),[0 0 0]';0 0 0 1]);
		% Bottom gear
		scene.bodies{5} = redmax.BodyMeshObj(0.1*density,'gears.obj');
		scene.joints{5} = redmax.JointFixed(scene.joints{4},scene.bodies{5});
		scene.joints{5}.setJointTransform([eye(3),[0 -0.1 0]'; 0 0 0 1]);
		scene.bodies{5}.setBodyTransform(E0 * scene.bodies{5}.E_oi);
		% Bottom bar
		scene.bodies{6} = redmax.BodyCuboid(density,[5 0.25 0.25]);
		scene.joints{6} = redmax.JointFixed(scene.joints{4},scene.bodies{6});
		scene.joints{6}.setJointTransform([eye(3),[0 0 0]'; 0 0 0 1]);
		scene.bodies{6}.setBodyTransform([eye(3),[0 1.5 0]'; 0 0 0 1]);
		% Top bar
		scene.bodies{7} = redmax.BodyCuboid(density,[7 0.25 0.25]);
		scene.joints{7} = redmax.JointFixed(scene.joints{2},scene.bodies{7});
		scene.joints{7}.setJointTransform([eye(3),[0 0 0]'; 0 0 0 1]);
		scene.bodies{7}.setBodyTransform([eye(3),[2 1.75 0]'; 0 0 0 1]);
		% Constraint
		scene.constraints{1} = redmax.ConstraintMultQ(scene.joints{2},scene.joints{4});
		scene.constraints{1}.setFactor(-1.0);
	case 35
		scene.name = '2D free joint';
		scene.waxis = [-20 20 -20 20 -1 1];
		scene.view = 2;
		scene.tspan = [0 10];
		scene.Hexpected(RECURS_ODE45) = 167.0835245643339135;
		scene.Hexpected(REDMAX_ODE45) = 167.0835245643319240;
		scene.Hexpected(REDMAX_EULER) = 166.9232451756938644;
		scene.grav = [0 -1 0]';
		l0 = 10;
		l1 = 10;
		% Parent
		scene.bodies{1} = redmax.BodyCuboid(density,[l0 1 1]);
		if false
			% Use composite joint
			revolute = redmax.JointRevolute([],scene.bodies{1},[0 0 1]');
			planar = redmax.JointPlanar([],scene.bodies{1});
			scene.joints{1} = redmax.JointComposite([],scene.bodies{1},planar,revolute);
		else
			% Use 2D free joint
			scene.joints{1} = redmax.JointFree2D([],scene.bodies{1});
		end
		scene.joints{1}.setJointTransform(eye(4));
		scene.bodies{1}.setBodyTransform(eye(4));
		% Child
		scene.bodies{2} = redmax.BodyCuboid(density,[l1 1 1]);
		scene.joints{2} = redmax.JointRevolute(scene.joints{1},scene.bodies{2},[0 0 1]');
		scene.joints{2}.setJointTransform([eye(3),[l0/2 0 0]'; 0 0 0 1]);
		scene.bodies{2}.setBodyTransform([eye(3),[l1/2 0 0]'; 0 0 0 1]);
		% Initial conditions
		scene.joints{1}.q(1:2) = [0 0]';
		scene.joints{1}.qdot(1:2) = [0 0]';
		scene.joints{1}.qdot(3) = 1;
		scene.joints{2}.qdot(1) = -1;
end

end

%%
function sceneFcn05(t,scene)
if t < 3.0
	scene.joints{1}.tau = 0;
	scene.joints{2}.tau = 0;
	scene.joints{3}.tau = 1e2;
elseif t < 6.0
	scene.joints{1}.tau = 0;
	scene.joints{2}.tau = 1e2;
	scene.joints{3}.tau = -1e2;
else
	scene.joints{1}.tau = 1e2;
	scene.joints{2}.tau = -1e2;
	scene.joints{3}.tau = 0;
end
end

%%
function sceneFcn09(t,scene)
t0 = 0.0;
t1 = 7.0;
a = 7;
b = 1.5*pi;
if t < t1
	% syms a b t t0 t1
	s = 2*((t-t0)/(t1-t0)-0.5); % -1.0 < s < 1.0
	% q(t) = b/(1+exp(-a*s))
	% dq(t) = diff(q,t);
	% ddq(t) = diff(q,t,2);
	q = b/(1+exp(-a*s));
	dq = -(2*a*b*exp(a*((2*(t - t0))/(t0 - t1) + 1)))/((t0 - t1)*(exp(a*((2*t - 2*t0)/(t0 - t1) + 1)) + 1)^2);
	ddq = (8*a^2*b*exp(2*a*((2*(t - t0))/(t0 - t1) + 1)))/((t0 - t1)^2*(exp(a*((2*t - 2*t0)/(t0 - t1) + 1)) + 1)^3) - (4*a^2*b*exp(a*((2*(t - t0))/(t0 - t1) + 1)))/((t0 - t1)^2*(exp(a*((2*t - 2*t0)/(t0 - t1) + 1)) + 1)^2);
	scene.joints{1}.presc.q = q;
	scene.joints{1}.presc.qdot = dq;
	scene.joints{1}.presc.qddot = ddq;
else
	scene.joints{1}.presc.q = b;
	scene.joints{1}.presc.qdot = 0;
	scene.joints{1}.presc.qddot = 0;
end
end

%%
function sceneFcn10(t,scene) %#ok<INUSL>
E = scene.bodies{end}.E_wi;
R = E(1:3,1:3);
G = se3.Gamma([5 0 0]');
f = [0 0 1e2]';
scene.bodies{end}.wext_i = G'*R'*f;
end

%%
function sceneFcn13(t,scene)
%
% Specifying the desired world acceleration for rotation is not
% intuitive! Let's instead specify the rotation and translation separately.
%
%    vt_w <-- target linear velocity in world space
%    wt_i <-- target angular velocity in body space
%
% Both wt_i and wtdot_i are already in body space, so they do not need to
% be transformed. Since the vt_w and vtdot_w are defined in world space, we
% must transform them.
%
%    vt_i    = R'*vt_w
%
%    vtdot_i = d/dt(R'*vt_w)
%            = R'*d/dt(vt_w) + d/dt(R')*vt_w
%            = R'*vtdot_w + ([w_i]'*R')*vt_w
%            = R'*vtdot_w + [w_i]'*vt_i
%            = R'*vtdot_w - cross(w_i,vt_i)
%
E = scene.bodies{end}.E_wi;
R = E(1:3,1:3);
phi = scene.bodies{end}.phi;
if t < 2.0
	vt_w = [0 0 0]';
	wt_i = [0 -t 0]';
	vtdot_w = [0 0 0]';
	wtdot_i = [0 -1 0]';
	% At the end of this time window, w_i = [0 -2 0]'
elseif t < 4.0
	t_ = t - 4.0; % Need to start at [0 -2 0]'
	vt_w = [0 0 0]';
	wt_i = [0 t_ 0]';
	vtdot_w = [0 0 0]';
	wtdot_i = [0 1 0]';
	% At the end of this time window, w_i = [0 0 0]'
elseif t < 6
	t_ = t - 4.0;
	vt_w = [-2*t_ 0 0]'; % Need to start at [0 0 0]'
	wt_i = [0 t_ 0]'; % Need to start at [0 0 0]'
	vtdot_w = [-2 0 0]';
	wtdot_i = [0 1 0]';
	% At the end of this time window, v_w = [-4 0 0]' and w_i = [0 2 0]'
elseif t < 8
	t_ = t - 8;
	vt_w = [2*t_ 0 0]'; % Need to start at [-4 0 0]' 
	wt_i = [0 -t_ 0]'; % Need to start at [0 2 0]'
	vtdot_w = [2 0 0]';
	wtdot_i = [0 -1 0]';
else
	vt_w = [0 0 0]';
	wt_i = [0 0 0]';
	vtdot_w = [0 0 0]';
	wtdot_i = [0 0 0]';
end
vt_i = R'*vt_w;
scene.bodies{end}.presc.qdot(1:3,1) = wt_i;
scene.bodies{end}.presc.qdot(4:6,1) = vt_i;
scene.bodies{end}.presc.qddot(1:3,1) = wtdot_i;
scene.bodies{end}.presc.qddot(4:6,1) = R'*vtdot_w - cross(phi(1:3),vt_i);
end

%%
function sceneFcn20(t,scene)
% Prescribed body
E = scene.bodies{end}.E_wi;
R = E(1:3,1:3);
phi = scene.bodies{end}.phi;
if t < 2.0
	vt_w = [0 0 0]';
	wt_i = [0 -t 0]';
	vtdot_w = [0 0 0]';
	wtdot_i = [0 -1 0]';
	% At the end of this time window, w_i = [0 -2 0]'
elseif t < 4.0
	t_ = t - 4.0; % Need to start at [0 -2 0]'
	vt_w = [0 0 0]';
	wt_i = [0 t_ 0]';
	vtdot_w = [0 0 0]';
	wtdot_i = [0 1 0]';
	% At the end of this time window, w_i = [0 0 0]'
elseif t < 6
	t_ = t - 4.0;
	vt_w = [-2*t_ 0 0]'; % Need to start at [0 0 0]'
	wt_i = [0 t_ 0]'; % Need to start at [0 0 0]'
	vtdot_w = [-2 0 0]';
	wtdot_i = [0 1 0]';
	% At the end of this time window, v_w = [-4 0 0]' and w_i = [0 2 0]'
elseif t < 8
	t_ = t - 8;
	vt_w = [2*t_ 0 0]'; % Need to start at [-4 0 0]' 
	wt_i = [0 -t_ 0]'; % Need to start at [0 2 0]'
	vtdot_w = [2 0 0]';
	wtdot_i = [0 -1 0]';
else
	vt_w = [0 0 0]';
	wt_i = [0 0 0]';
	vtdot_w = [0 0 0]';
	wtdot_i = [0 0 0]';
end
vt_i = R'*vt_w;
scene.bodies{end}.presc.qdot(1:3,1) = wt_i;
scene.bodies{end}.presc.qdot(4:6,1) = vt_i;
scene.bodies{end}.presc.qddot(1:3,1) = wtdot_i;
scene.bodies{end}.presc.qddot(4:6,1) = R'*vtdot_w - cross(phi(1:3),vt_i);
% Prescribed joint
t0 = 0.0;
t1 = 10.0;
a = 7;
b = pi/2;
% syms a b t t0 t1
s = 2*((t-t0)/(t1-t0)-0.5); % -1.0 < s < 1.0
% q(t) = b/(1+exp(-a*s))
% dq(t) = diff(q,t);
% ddq(t) = diff(q,t,2);
q = b/(1+exp(-a*s));
dq = -(2*a*b*exp(a*((2*(t - t0))/(t0 - t1) + 1)))/((t0 - t1)*(exp(a*((2*t - 2*t0)/(t0 - t1) + 1)) + 1)^2);
ddq = (8*a^2*b*exp(2*a*((2*(t - t0))/(t0 - t1) + 1)))/((t0 - t1)^2*(exp(a*((2*t - 2*t0)/(t0 - t1) + 1)) + 1)^3) - (4*a^2*b*exp(a*((2*(t - t0))/(t0 - t1) + 1)))/((t0 - t1)^2*(exp(a*((2*t - 2*t0)/(t0 - t1) + 1)) + 1)^2);
scene.joints{3}.presc.q = q;
scene.joints{3}.presc.qdot = dq;
scene.joints{3}.presc.qddot = ddq;
end

%%
function sceneFcn32(t,scene)
speed = -2*(2*pi);
q = speed*t;
scene.constraints{end}.q = q;
scene.constraints{end}.qdot = speed;
scene.constraints{end}.qddot = 0;
end

%%
function sceneFcn33(t,scene)
speed = 2*(2*pi);
q = speed*t;
scene.constraints{end}.q = q;
scene.constraints{end}.qdot = speed;
scene.constraints{end}.qddot = 0;
end
The diff you're trying to view is too large. Only the first 1000 changed files have been loaded.
Showing with 0 additions and 0 deletions (0 / 0 diffs computed)
swh spinner

Computing file changes ...

back to top

Software Heritage — Copyright (C) 2015–2026, The Software Heritage developers. License: GNU AGPLv3+.
The source code of Software Heritage itself is available on our development forge.
The source code files archived by Software Heritage are available under their own copyright and licenses.
Terms of use: Archive access, API— Content policy— Contact— JavaScript license information— Web API