@Override
	public void paint(Graphics g) {
		// TODO Auto-generated method stub
		super.paint(g);
		Graphics2D g2 = (Graphics2D) g.create();
		g2.setRenderingHint(RenderingHints.KEY_ANTIALIASING, RenderingHints.VALUE_ANTIALIAS_ON);

		double size = Math.min(this.getBounds().getWidth(), this.getBounds().getHeight());
		double scale = size / 4.;
		double x0 = this.getBounds().getCenterX();
		double y0 = this.getBounds().getCenterY();

		// BG
		g2.setBackground(Color.BLACK);
		g2.clearRect(0, 0, this.getWidth(), this.getHeight());

		// System.out.println("paint");
		// PATH
		for (QuaternionPFDObject p : objects) {
			// System.out.println(p);

			if (p.getType() == QuaternionPFDObject.Type.PATH_FILL) {

				Path2D path = new Path2D.Double();

				Vec3d qs = p.getPath().get(0);
				Point2D pt = projection.project(orientation.rotate(qs));

				path.moveTo(pt.getX() * scale + x0 - 1, pt.getY() * scale + y0 - 1);
				for (Vec3d q : p.getPath()) {

					pt = projection.project(orientation.rotate(q));
					path.lineTo(pt.getX() * scale + x0 - 1, pt.getY() * scale + y0 - 1);
				}

				path.closePath();
				Area area = new Area(path);
				pt = projection.project(orientation.rotate(p.getPoint()));

				if (area.contains(new Point2D.Double(pt.getX() * scale + x0 - 1, pt.getY() * scale + y0 - 1))) {
					// normal paint
					g2.setPaint(p.getPaint());
					g2.fill(area);
				} else {
					// inverse paint
					Shape border = new Ellipse2D.Double(x0 - 2. * scale, y0 - 2. * scale, 4. * scale, 4. * scale);
					Area area2 = new Area(border);
					area2.subtract(area);
					g2.setPaint(p.getPaint());
					g2.fill(area2);
				}

			}

			else if (p.getType() == QuaternionPFDObject.Type.PATH_STROKE) {

				Path2D path = new Path2D.Double();

				Vec3d qs = p.getPath().get(0);
				Point2D pt = projection.project(orientation.rotate(qs));

				path.moveTo(pt.getX() * scale + x0 - 1, pt.getY() * scale + y0 - 1);
				for (Vec3d q : p.getPath()) {

					pt = projection.project(orientation.rotate(q));
					path.lineTo(pt.getX() * scale + x0 - 1, pt.getY() * scale + y0 - 1);
				}

				g2.setColor(p.getColor());
				g2.setStroke(p.getStroke());
				g2.draw(path);
			}

			else if (p.getType() == QuaternionPFDObject.Type.POINT) {

				Point2D pt = projection.project(orientation.rotate(p.getPoint()));
				g2.setColor(p.getColor());
				g2.drawOval((int) (pt.getX() * scale + x0 - 3), (int) (pt.getY() * scale + y0 - 3), 6, 6);
			}

		}

		g2.setColor(Color.WHITE);
		g2.setStroke(new BasicStroke(2.0f));
		g2.drawOval((int) (x0 - 2 * scale), (int) (y0 - 2 * scale), (int) (4 * scale), (int) (4 * scale));
		g2.drawOval((int) (x0 - 10), (int) (y0 - 10), (int) (18), (int) (18));
		g2.drawLine((int) (x0 - 1), (int) (y0 - 10), (int) (x0 - 1), (int) (y0 + 8));
		g2.drawLine((int) (x0 - 10), (int) (y0 - 1), (int) (x0 + 8), (int) (y0 - 1));
		g2.dispose();
	}