public QuaternionPFDPanel() {
		super();

		projection = new LambertAzimutalEqualAreaProjection();
		// projection = PerspectiveProjection.GNOMONIC_PROJECTION;

		orientation = new Quaternion().fromAxis(0, 0, 0, 1);
		orientation.normalize();
		
		momentum = new Quaternion(0.01, new Vec3d(0, 1, 0));

		objects = new ArrayList<QuaternionPFDObject>(10);

		List<Vec3d> equator, north, south, m45, m90, m135, l45, lm45, sun;

		double inc = Math.PI / 720;

		// EQUATOR
		equator = new ArrayList<Vec3d>();
		for (double i = 0.; i < 2 * Math.PI; i += inc) {
			Vec3d q = new Vec3d(Math.cos(i), Math.sin(i), 0);
			equator.add(q);
		}
		objects.add(new QuaternionPFDObject(QuaternionPFDObject.Type.PATH_FILL, new Vec3d(0, 0, -1), equator,
				GROUND_COLOR, new BasicStroke(), Color.WHITE));
		objects.add(new QuaternionPFDObject(QuaternionPFDObject.Type.PATH_FILL, new Vec3d(0, 0, 1), equator, SKY_COLOR,
				new BasicStroke(), Color.WHITE));
		objects.add(new QuaternionPFDObject(QuaternionPFDObject.Type.PATH_STROKE, new Vec3d(0, 0, 1), equator, null,
				new BasicStroke(2.0f), Color.WHITE));

		// NORTH
		north = new ArrayList<Vec3d>();
		for (double i = -Math.PI / 2.; i <= Math.PI / 2.; i += inc) {
			Vec3d q = new Vec3d(Math.cos(i), 0, Math.sin(i));
			north.add(q);
		}
		objects.add(new QuaternionPFDObject(QuaternionPFDObject.Type.PATH_STROKE, new Vec3d(0, -1, 0), north, null,
				new BasicStroke(4.0f), Color.WHITE));
		objects.add(new QuaternionPFDObject(QuaternionPFDObject.Type.PATH_STROKE, new Vec3d(0, -1, 0), north, null,
				new BasicStroke(2.0f), Color.RED.brighter()));

		// SOUTH
		south = new ArrayList<Vec3d>();
		for (double i = -Math.PI / 2.; i <= Math.PI / 2.; i += inc) {
			Vec3d q = new Vec3d(-Math.cos(i), 0, Math.sin(i));
			south.add(q);
		}
		objects.add(new QuaternionPFDObject(QuaternionPFDObject.Type.PATH_STROKE, new Vec3d(0, -1, 0), south, null,
				new BasicStroke(2.0f), new Color(1.f, 1.f, 1.f, 1.f)));

		m45 = new ArrayList<Vec3d>();
		for (double i = 0.; i < 2 * Math.PI; i += inc) {
			Vec3d q = new Vec3d(Math.cos(Math.PI / 2.) * Math.cos(i), Math.sin(Math.PI / 2.) * Math.cos(i),
					Math.sin(i));
			m45.add(q);
		}
		objects.add(new QuaternionPFDObject(QuaternionPFDObject.Type.PATH_STROKE, new Vec3d(0, -1, 0), m45, null,
				new BasicStroke(), new Color(1.f, 1.f, 1.f, 1.f)));

		m45 = new ArrayList<Vec3d>();
		for (double i = 0.; i < 2 * Math.PI; i += inc) {
			Vec3d q = new Vec3d(Math.cos(Math.PI / 4.) * Math.cos(i), Math.sin(Math.PI / 4.) * Math.cos(i),
					Math.sin(i));
			m45.add(q);
		}
		objects.add(new QuaternionPFDObject(QuaternionPFDObject.Type.PATH_STROKE, new Vec3d(0, -1, 0), m45, null,
				new BasicStroke(), new Color(1.f, 1.f, 1.f, 0.15f)));

		m45 = new ArrayList<Vec3d>();
		for (double i = 0.; i < 2 * Math.PI; i += inc) {
			Vec3d q = new Vec3d(Math.cos(3 * Math.PI / 4.) * Math.cos(i), Math.sin(3 * Math.PI / 4.) * Math.cos(i),
					Math.sin(i));
			m45.add(q);
		}
		objects.add(new QuaternionPFDObject(QuaternionPFDObject.Type.PATH_STROKE, new Vec3d(0, -1, 0), m45, null,
				new BasicStroke(), new Color(1.f, 1.f, 1.f, 0.15f)));

		equator = new ArrayList<Vec3d>();
		for (double i = 0.; i < 2 * Math.PI; i += inc) {
			Vec3d q = new Vec3d(Math.cos(i) * Math.cos(Math.PI / 4.), Math.sin(i) * Math.cos(Math.PI / 4.),
					Math.sin(Math.PI / 4.));
			equator.add(q);
		}
		objects.add(new QuaternionPFDObject(QuaternionPFDObject.Type.PATH_STROKE, new Vec3d(0, -1, 0), equator, null,
				new BasicStroke(), new Color(1.f, 1.f, 1.f, 0.15f)));

		equator = new ArrayList<Vec3d>();
		for (double i = 0.; i < 2 * Math.PI; i += inc) {
			Vec3d q = new Vec3d(Math.cos(i) * Math.cos(-Math.PI / 4.), Math.sin(i) * Math.cos(-Math.PI / 4.),
					Math.sin(-Math.PI / 4.));
			equator.add(q);
		}
		objects.add(new QuaternionPFDObject(QuaternionPFDObject.Type.PATH_STROKE, new Vec3d(0, -1, 0), equator, null,
				new BasicStroke(), new Color(1.f, 1.f, 1.f, 0.15f)));

		// SUN
		sun = new ArrayList<Vec3d>();
		for (double i = 0.; i < 2 * Math.PI; i += inc) {
			Vec3d q = new Vec3d(0.1 * Math.cos(i), 0.1 * Math.sin(i), 1);
			sun.add(q);
		}
		objects.add(new QuaternionPFDObject(QuaternionPFDObject.Type.PATH_FILL, new Vec3d(0, 0, 1), sun, Color.WHITE,
				new BasicStroke(), Color.WHITE));

		this.setMinimumSize(new Dimension(320, 240));
		this.setPreferredSize(new Dimension(800, 600));

		HudMotionListener listener = new HudMotionListener(this);

		this.addMouseListener(listener);
		this.addMouseMotionListener(listener);
		this.addKeyListener(listener);
		this.setFocusable(true);


		QuaternionPFDPanel that = this;

		ScheduledExecutorService scheduler = Executors.newScheduledThreadPool(1);

        
		scheduler.scheduleAtFixedRate( () -> {
            that.orientation.leftmult(momentum);
            that.momentum.slerp(that.momentum, new Quaternion().fromAxis(0,1,0,0), 0.01);
            that.repaint();
        }, 200, 16, TimeUnit.MILLISECONDS);

	}