Package jmt.gui.jaba.graphs

Source Code of jmt.gui.jaba.graphs.Convex3DGraph$PointData

/*  
* Copyright (C) 2011, Laboratorio di Valutazione delle Prestazioni - Politecnico di Milano

* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 2 of the License, or
* (at your option) any later version.

* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
* GNU General Public License for more details.

* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software
* Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA  02110-1301  USA
*/

package jmt.gui.jaba.graphs;

import java.awt.BasicStroke;
import java.awt.Color;
import java.awt.Dimension;
import java.awt.Font;
import java.awt.Graphics;
import java.awt.Graphics2D;
import java.awt.Point;
import java.awt.Stroke;
import java.awt.event.ComponentEvent;
import java.awt.event.ComponentListener;
import java.awt.event.MouseEvent;
import java.awt.event.MouseListener;
import java.awt.event.MouseMotionListener;
import java.awt.event.MouseWheelEvent;
import java.awt.event.MouseWheelListener;
import java.security.InvalidParameterException;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.HashSet;

import javax.swing.BorderFactory;
import javax.swing.JFrame;

import jmt.engine.graphic.Matrix4;
import jmt.engine.graphic.ScreenProjector;
import jmt.engine.graphic.Vector4;
import jmt.engine.jaba.convexHull3d.HullEdge;
import jmt.engine.jaba.convexHull3d.HullFace;
import jmt.engine.jaba.convexHull3d.HullVertex;
import jmt.gui.jaba.JabaModel;
import jmt.gui.jaba.JabaWizard;

/**
* This is a panel which draws the Convex Hull diagram, given the pre-computed
* set of faces.
*
* @author Andrea Zoppi
* @author Sebastiano Spicuglia
*/

public class Convex3DGraph extends JabaGraph implements ComponentListener,
    MouseListener, MouseMotionListener, MouseWheelListener {
  protected static final Font FONT = new Font("Arial Narrow", Font.PLAIN, 10);

  protected static final long serialVersionUID = 1L;

  protected ScreenProjector prj = new ScreenProjector();

  protected static final Color BGCOLOR = Color.WHITE;
  private static final Color DARK_RED = new Color(128, 0, 0);
  private static final Color DARK_GREEN = new Color(0, 128, 0);
  private static final Color DARK_BLUE = new Color(0, 0, 128);


  // protected JabaModel data;//PRODUCTION SOLUTION
  // protected JabaWizard mainWin;//PRODUCTION SOLUTION
  private static final BasicStroke DOTTED = new BasicStroke(1, 1, 1, 1,
      new float[] { 2f }, 1);
  protected JFrame mainWin; // PROTOTYPE SOLUTION

  // Rendering options.
  protected boolean enableCustomZoom = false;
  protected boolean enableDebugInfo = false;
  protected boolean enableDebugEdges = false;

  protected boolean enableCoplanarEdges = true;
  protected boolean enableInternalVertices = false;
  protected boolean enableVisibleEdges = true;
  protected boolean enableHiddenEdges = false;
  protected boolean enableVisibleFaces = true;
  protected boolean enableHiddenFaces = false;
  protected boolean enableHullVerticesLabels = true;
  protected boolean enableTransparentFaces = false;

  // Viewing parameters.
  protected Vector4 axisPrjX = Vector4.createPoint3D(1.0f, 0.0f, 0.0f);
  protected Vector4 axisPrjY = Vector4.createPoint3D(0.0f, 1.0f, 0.0f);
  protected Vector4 axisPrjZ = Vector4.createPoint3D(0.0f, 0.0f, 1.0f);
  protected Vector4 axisLabelX = Vector4.createPoint3D(1.0f, 0.0f, 0.0f);
  protected Vector4 axisLabelY = Vector4.createPoint3D(0.0f, 1.0f, 0.0f);
  protected Vector4 axisLabelZ = Vector4.createPoint3D(0.0f, 0.0f, 1.0f);
  protected Vector4 originPrj = Vector4.createPoint3D(0.0f, 0.0f, 0.0f);
  protected Vector4 forward = Vector4.Z_AXIS_REV;
  protected float screenW;
  protected float screenH;
  protected float halfW;
  protected float halfH;
  protected float aspect;
  protected float rotY = (float) Math.toRadians(45.0f); // vertical rotation
                              // angle
  protected float rotX = (float) Math.toRadians(45.0f); // head pitch angle
  protected float rotYdelta = 0.0f;
  protected float rotXdelta = 0.0f;
  protected float zoomFactor = 1.0f;

  // Mouse state.
  protected boolean mouseLeft = false;
  protected boolean mouseRight = false;
  protected Point mouseStart = new Point(0, 0);
  protected Point mouseEnd = new Point(0, 0);
  protected Point mouseDelta = new Point(0, 0);
  protected MouseMode mouseMode = MouseMode.NONE;

  // Convex hull data
  protected static final float VISIBILITY_EPSILON = 0.0f; // -0.001f;
  protected static final float COPLANAR_EPSILON = 0.001f;

  private static final Color SELECTED_STATION_COLOR = Color.GREEN;

  protected HashSet<HullVertex> allVerticesSet;
  protected HullVertex[] allVerticesArray = null;
  protected ArrayList<HullFace> faces;

  // Convex hull primitives sets
  protected HashSet<HullEdge> hullEdgesSet = new HashSet<HullEdge>();
  protected HashSet<PointData> hullVerticesSet = new HashSet<PointData>();

  protected HashSet<HullFace> visibleFacesSet = new HashSet<HullFace>();
  protected HashSet<HullFace> hiddenFacesSet = new HashSet<HullFace>();
  protected HashSet<HullEdge> visibleEdgesSet = new HashSet<HullEdge>();
  protected HashSet<HullEdge> hiddenEdgesSet = new HashSet<HullEdge>();
  protected HashSet<PointData> visibleVerticesSet = new HashSet<PointData>();
  protected HashSet<PointData> hiddenVerticesSet = new HashSet<PointData>();
  protected HashSet<PointData> verticesInsideSet = new HashSet<PointData>();

  // Convex hull primitives arrays (extracted from sets)
  protected HullEdge[] hullEdgesArray = null;
  protected PointData[] hullVerticesArray = null;
  protected HullFace[] hullFacesArray = null;

  protected HullFace[] visibleFacesArray = null;
  protected HullFace[] hiddenFacesArray = null;
  protected HullEdge[] visibleEdgesArray = null;
  protected HullEdge[] hiddenEdgesArray = null;
  protected PointData[] visibleVerticesArray = null;
  protected PointData[] hiddenVerticesArray = null;
  protected PointData[] verticesInsideArray = null;

  // Sorted vertices arrays
  protected PointData[] sortedVisibleVertices;
  protected PointData[] sortedHiddenVertices;
  protected PointData[] sortedVerticesInside;

  // Information for solid centering and scaling.
  protected float maxVertexDistance = 1.0f;
  protected Vector4 centroid = Vector4.createPoint3D(0.0f, 0.0f, 0.0f);

  // Internal eye rototranslation matrix.
  protected Matrix4 eyeRT = Matrix4.createIdentity();

  private String[] classNames;

  private String selectedStation;

  /**
   * Keeps points coordinates both as world coords and as projected screen
   * coords. Each point has its own ID for further operations.
   *
   * @author Andrea Zoppi
   */
  protected static class PointData implements Comparable<PointData> {
    public Vector4 worldPos = null;
    public Vector4 screenPos = null;
    public HullVertex vertex = null;

    public PointData(Vector4 worldPos, Vector4 screenPos, HullVertex vertex) {
      this.worldPos = worldPos;
      this.screenPos = screenPos;
      this.vertex = vertex;
    }

    @Override
    public int hashCode() {
      return vertex.hashCode();
    }

    @Override
    public boolean equals(Object obj) {
      if (this == obj)
        return true;
      if (obj == null)
        return false;
      if (getClass() != obj.getClass())
        return false;
      PointData other = (PointData) obj;
      if (vertex == null) {
        if (other.vertex != null)
          return false;
      } else if (!vertex.equals(other.vertex))
        return false;
      return true;
    }

    public int compareTo(PointData o) {
      if (this.screenPos.w() > o.screenPos.w()) {
        return 1;
      } else if (this.screenPos.w() < o.screenPos.w()) {
        return -1;
      } else {
        return 0;
      }
    }
  }

  /**
   * Mouse action modes.
   */
  protected enum MouseMode {
    NONE, ROTATE_VIEW, MOVE_VERTEX;
  }

  public boolean getEnableDebugInfo() {
    return enableDebugInfo;
  }

  public void setEnableDebugInfo(boolean enableDebugInfo) {
    this.enableDebugInfo = enableDebugInfo;
  }

  public boolean getEnableDebugEdges() {
    return enableDebugEdges;
  }

  public void setEnableDebugEdges(boolean enableDebugEdges) {
    this.enableDebugEdges = enableDebugEdges;
  }

  public boolean getEnableCustomZoom() {
    return enableCustomZoom;
  }

  public void setEnableCustomZoom(boolean enableCustomZoom) {
    this.enableCustomZoom = enableCustomZoom;
  }

  public boolean getEnableInternalVertices() {
    return enableInternalVertices;
  }

  public void setEnableInternalVertices(boolean enableInternalVertices) {
    this.enableInternalVertices = enableInternalVertices;
  }

  public boolean getEnableCoplanarEdges() {
    return enableCoplanarEdges;
  }

  public void setEnableCoplanarEdges(boolean enableCoplanarEdges) {
    this.enableCoplanarEdges = enableCoplanarEdges;
  }

  public boolean getEnableVisibleEdges() {
    return enableVisibleEdges;
  }

  public void setEnableVisibleEdges(boolean enableVisibleEdges) {
    this.enableVisibleEdges = enableVisibleEdges;
  }

  public boolean getEnableHiddenEdges() {
    return enableHiddenEdges;
  }

  public void setEnableHiddenEdges(boolean enableHiddenEdges) {
    this.enableHiddenEdges = enableHiddenEdges;
  }

  public boolean getEnableVisibleFaces() {
    return enableVisibleFaces;
  }

  public void setEnableVisibleFaces(boolean enableVisibleFaces) {
    this.enableVisibleFaces = enableVisibleFaces;
  }

  public boolean getEnableHiddenFaces() {
    return enableHiddenFaces;
  }

  public void setEnableHiddenFaces(boolean enableHiddenFaces) {
    this.enableHiddenFaces = enableHiddenFaces;
  }

  public boolean getEnableHullVerticesLabels() {
    return enableHullVerticesLabels;
  }

  public void setEnableHullVerticesLabels(boolean enableHullVerticesLabels) {
    this.enableHullVerticesLabels = enableHullVerticesLabels;
  }

  public boolean getEnableTransparentFaces() {
    return enableTransparentFaces;
  }

  public void setEnableTransparentFaces(boolean enableTransparentFaces) {
    this.enableTransparentFaces = enableTransparentFaces;
  }

  public float getZoomFactor() {
    return zoomFactor;
  }

  public void setZoomFactor(float zoomFactor) {
    this.zoomFactor = zoomFactor;
  }

  /**
   * Check if the face is visible or not. Due to the convex solid property, a
   * positive dot product means that the face has the normal in the same
   * direction of the sight, thus it is not visible because there exist faces
   * which cover the sight of the analyzed one.
   *
   * @param faces
   *            Faces to split.
   * @param forward
   *            Viewing direction.
   */
  protected void splitByVisibility(Vector4 forward) {
    forward = forward.unit();
    visibleFacesSet.clear();
    hiddenFacesSet.clear();
    visibleEdgesSet.clear();
    hiddenEdgesSet.clear();
    visibleVerticesSet.clear();
    hiddenVerticesSet.clear();
    hullEdgesSet.clear();
    hullVerticesSet.clear();
    maxVertexDistance = 1.0f;
    centroid.set(Vector4.createPoint3D(0.0f, 0.0f, 0.0f));
    float[] max = { 0.0f, 0.0f, 0.0f };
    float[] min = { 0.0f, 0.0f, 0.0f };

    int size = faces.size();
    for (int i = 0; i < size; i++) {
      HullFace face = faces.get(i);

      Vector4 normal = face.getNormal();
      HullEdge[] edges = face.getEdges();
      HullVertex[] vertices = face.getVertices();

      float facing = normal.dot(forward);
      boolean isVisible = (facing <= VISIBILITY_EPSILON);
      if (isVisible) {
        visibleFacesSet.add(face);
      } else {
        hiddenFacesSet.add(face);
      }
      face.setShade(facing);

      // Create unique and visibility sets
      for (int j = 0; j < edges.length; j++) {
        HullEdge e = edges[j];
        if (isVisible) {
          visibleEdgesSet.add(e);

          // Fix edges flagged as hidden but actually visible
          if (hiddenEdgesSet.contains(e)) {
            hiddenEdgesSet.remove(e);
          }
        } else if (!visibleEdgesSet.contains(e)) {
          // Add only if no other faces have already claimed the edge
          // visibility.
          hiddenEdgesSet.add(e);
        }
        if (hullEdgesSet.add(e)) {
        }
      }
      for (int j = 0; j < vertices.length; j++) {
        HullVertex v = vertices[j];
        Vector4 worldPos = Vector4.createPoint3D(v.x(), v.y(), v.z());
        Vector4 screenPos = prj.project(worldPos);
        PointData p = new PointData(worldPos, screenPos, v);
        if (isVisible) {
          visibleVerticesSet.add(p);

          // Fix vertices flagged as hidden but actually visible
          if (hiddenVerticesSet.contains(p)) {
            hiddenVerticesSet.remove(p);
          }
        } else if (!visibleVerticesSet.contains(p)) {
          // Add only if no other faces have already claimed the
          // vertex visibility.
          hiddenVerticesSet.add(p);
        }
        if (p.vertex.getID() >= 0 && hullVerticesSet.add(p)) {
          if (v.x() > max[0]) {
            max[0] = v.x();
          }
          if (v.y() > max[1]) {
            max[1] = v.y();
          }
          if (v.z() > max[2]) {
            max[2] = v.z();
          }
          if (v.x() < min[0]) {
            min[0] = v.x();
          }
          if (v.y() < min[1]) {
            min[1] = v.y();
          }
          if (v.z() < min[2]) {
            min[2] = v.z();
          }
        }
      }
    }

    // Centering and scaling
    centroid.x((max[0] + min[0]) * 0.5f * zoomFactor);
    centroid.y((max[1] + min[1]) * 0.5f * zoomFactor);
    centroid.z((max[2] + min[2]) * 0.5f * zoomFactor);
    max[0] -= min[0];
    max[1] -= min[1];
    max[2] -= min[2];
    maxVertexDistance = 0.5f * (float) Math.sqrt(max[0] * max[0] + max[1]
        * max[1] + max[2] * max[2]);
    centroid.divSelf(maxVertexDistance * zoomFactor);

    // Build the verticesInside set.
    verticesInsideSet.clear();
    for (int i = 0; i < allVerticesArray.length; i++) {
      HullVertex v = allVerticesArray[i];
      PointData p = new PointData(null, null, v); // Dummy PointData, just
                            // to check the HashSet
      if (!hullVerticesSet.contains(p)) {
        Vector4 worldPos = Vector4.createPoint3D(v.x(), v.y(), v.z());
        Vector4 screenPos = prj.project(worldPos);
        p = new PointData(worldPos, screenPos, v);
        verticesInsideSet.add(p);
      }
    }

    // TODO: Remove useless vertices

    // Convert sets into arrays for easy modifies
    hullEdgesArray = hullEdgesSet
        .toArray(new HullEdge[hullEdgesSet.size()]);
    hullVerticesArray = hullVerticesSet
        .toArray(new PointData[hullVerticesSet.size()]);
    hullFacesArray = this.faces.toArray(new HullFace[this.faces.size()]);

    visibleFacesArray = visibleFacesSet
        .toArray(new HullFace[visibleFacesSet.size()]);
    hiddenFacesArray = hiddenFacesSet.toArray(new HullFace[hiddenFacesSet
        .size()]);
    visibleEdgesArray = visibleEdgesSet
        .toArray(new HullEdge[visibleEdgesSet.size()]);
    hiddenEdgesArray = hiddenEdgesSet.toArray(new HullEdge[hiddenEdgesSet
        .size()]);
    visibleVerticesArray = visibleVerticesSet
        .toArray(new PointData[visibleVerticesSet.size()]);
    hiddenVerticesArray = hiddenVerticesSet
        .toArray(new PointData[hiddenVerticesSet.size()]);
    verticesInsideArray = verticesInsideSet
        .toArray(new PointData[verticesInsideSet.size()]);

    // Sort sets into separate arrays
    sortedVisibleVertices = visibleVerticesArray.clone();
    Arrays.sort(sortedVisibleVertices);
    sortedHiddenVertices = hiddenVerticesArray.clone();
    Arrays.sort(sortedHiddenVertices);
    sortedVerticesInside = verticesInsideArray.clone();
    Arrays.sort(sortedVisibleVertices);

    countFaceVertices();
  }

  protected void countFaceVertices() {
    /*
     * Per ogni faccia faccia.contatore = 0; faccia.marcata = false;
     *
     * Per ogni vertice € convex hull Per ogni faccia Se vertice € faccia
     * incrementaFaccia(faccia) Per ogni faccia faccia.marcata = false
     */

    // Reset vertex counters and marks
    for (int fi = 0; fi < hullFacesArray.length; fi++) {
      HullFace f = hullFacesArray[fi];
      f.setNumCoplanarVertices(0);
    }

    // Start propagating the number of vertices per face
    for (int vi = 0; vi < hullVerticesArray.length; vi++) {
      // Cleanup marked bits
      for (int fi = 0; fi < hullFacesArray.length; fi++) {
        HullFace f = hullFacesArray[fi];
        f.setMarked(false);
      }

      for (int fi = 0; fi < hullFacesArray.length; fi++) {
        HullVertex v = hullVerticesArray[vi].vertex;
        HullFace f = hullFacesArray[fi];

        // Start color propagation
        if (v.getID() == f.getVertex(0).getID()
            || v.getID() == f.getVertex(1).getID()
            || v.getID() == f.getVertex(2).getID()) {
          if (!f.isMarked()) {
            incrementFaceCounter(f);
          }
        }
      }
    }
  }

  protected void incrementFaceCounter(HullFace face) {
    /*
     * faccia.contatore++ faccia.marcata = true
     *
     * Per ogni spigolo € faccia Per ogni faccia adiacente € spigolo Se
     * adiacente != questa && adiacente coplanare faccia &&
     * !adiacente.marcata incrementaFaccia(adiacente)
     */

    // Increment the counter and set as marked
    face.setNumCoplanarVertices(1 + face.getNumCoplanarVertices());
    face.setMarked(true);

    for (int ei = 0; ei < 3; ei++) {
      HullEdge e = face.getEdge(ei);
      for (int ai = 0; ai < 2; ai++) {
        HullFace adj = e.getFace(ai);
        /*
         * if ( face.volumeSign( adj.getVertex( 0 ) ) == 0 &&
         * face.volumeSign( adj.getVertex( 1 ) ) == 0 &&
         * face.volumeSign( adj.getVertex( 2 ) ) == 0 &&
         * !face.isMarked() ) { incrementFaceCounter( adj ); }
         */
        if (!adj.isMarked() && face.isCoplanar(adj)) {
          incrementFaceCounter(adj);
        }
      }
    }
  }

  public Convex3DGraph(JabaModel data, JabaWizard mainWin) {

    this.setBackground(BGCOLOR);

    this.addComponentListener(this);
    this.addMouseListener(this);
    this.addMouseMotionListener(this);
    this.addMouseWheelListener(this);

    this.classNames = data.getClassNames();
    this.faces = data.getResults().getFaces();
    this.mainWin = mainWin;
    this.allVerticesSet = data.getHullVertices();
    this.allVerticesArray = allVerticesSet
        .toArray(new HullVertex[allVerticesSet.size()]);

    this.setBorder(BorderFactory.createEtchedBorder());
    this.setBackground(BGCOLOR);

    repaint();
  }

  @Override
  public void paint(Graphics graphics) {
    super.paint(graphics);
    Graphics2D g = (Graphics2D) graphics;
//     g.setRenderingHint( RenderingHints.KEY_ANTIALIASING, RenderingHints.VALUE_ANTIALIAS_ON );

    updateProjection();

    // Draw 3D grids
    drawGrids(g, 1.0f, 10, false, false, false);

    g.setFont(FONT);

    // Draw hidden faces
    if (enableHiddenFaces) {
      for (int i = 0; i < hiddenFacesArray.length; i++) {
        HullFace f = hiddenFacesArray[i];
        HullVertex v = f.getVertex(0);
        Vector4 worldPos = Vector4.createPoint3D(v.x(), v.y(), v.z());
        Vector4 screenPos = prj.project(worldPos);
        PointData p1 = new PointData(worldPos, screenPos, v);

        v = f.getVertex(1);
        worldPos = Vector4.createPoint3D(v.x(), v.y(), v.z());
        screenPos = prj.project(worldPos);
        PointData p2 = new PointData(worldPos, screenPos, v);

        v = f.getVertex(2);
        worldPos = Vector4.createPoint3D(v.x(), v.y(), v.z());
        screenPos = prj.project(worldPos);
        PointData p3 = new PointData(worldPos, screenPos, v);

        drawFace(g, p1, p2, p3, f);
      }
    }

    // Draw visible faces
    if (enableVisibleFaces) {
      for (int i = 0; i < visibleFacesArray.length; i++) {
        HullFace f = visibleFacesArray[i];
        HullVertex v = f.getVertex(0);
        Vector4 worldPos = Vector4.createPoint3D(v.x(), v.y(), v.z());
        Vector4 screenPos = prj.project(worldPos);
        PointData p1 = new PointData(worldPos, screenPos, v);

        v = f.getVertex(1);
        worldPos = Vector4.createPoint3D(v.x(), v.y(), v.z());
        screenPos = prj.project(worldPos);
        PointData p2 = new PointData(worldPos, screenPos, v);

        v = f.getVertex(2);
        worldPos = Vector4.createPoint3D(v.x(), v.y(), v.z());
        screenPos = prj.project(worldPos);
        PointData p3 = new PointData(worldPos, screenPos, v);

        drawFace(g, p1, p2, p3, f);
      }
    }

    // Draw hidden edges
    if (enableHiddenEdges) {
      for (int i = 0; i < hiddenEdgesArray.length; i++) {
        HullEdge e = hiddenEdgesArray[i];
        HullVertex v = e.getEndPoint(0);
        Vector4 worldPos = Vector4.createPoint3D(v.x(), v.y(), v.z());
        Vector4 screenPos = prj.project(worldPos);
        PointData p1 = new PointData(worldPos, screenPos, v);

        v = e.getEndPoint(1);
        worldPos = Vector4.createPoint3D(v.x(), v.y(), v.z());
        screenPos = prj.project(worldPos);
        PointData p2 = new PointData(worldPos, screenPos, v);

        drawEdge(g, p1, p2, e, false);
      }
    }

    // Draw hidden hull vertices
    for (int i = 0; i < sortedHiddenVertices.length; i++) {
      PointData p = sortedHiddenVertices[i];
      if (p.vertex.getID() >= 0) {
        drawVertex(g, p, false, true);
      }
    }

    // Draws points inside
    for (int i = 0; i < sortedVerticesInside.length; i++) {
      PointData p = sortedVerticesInside[i];
      if (enableInternalVertices || p.vertex.getName().equals(selectedStation) ) {
        drawVertex(g, p, false, false);
      }
    }
   

    drawAxes(g, true);

    // Draw visible hull edges
    if (enableVisibleEdges) {
      for (int i = 0; i < visibleEdgesArray.length; i++) {
        HullEdge e = visibleEdgesArray[i];
        HullVertex v = e.getEndPoint(0);
        Vector4 worldPos = Vector4.createPoint3D(v.x(), v.y(), v.z());
        Vector4 screenPos = prj.project(worldPos);
        PointData p1 = new PointData(worldPos, screenPos, v);

        v = e.getEndPoint(1);
        worldPos = Vector4.createPoint3D(v.x(), v.y(), v.z());
        screenPos = prj.project(worldPos);
        PointData p2 = new PointData(worldPos, screenPos, v);

        drawEdge(g, p1, p2, e, true);
      }
    }

    // Draw visible hull vertices
    for (int i = 0; i < sortedVisibleVertices.length; i++) {
      PointData p = sortedVisibleVertices[i];
      if (p.vertex.getID() >= 0) {
        drawVertex(g, p, true, true);
      }
    }

    // DEBUG CODE

    if (enableDebugInfo) {
      g.setColor(Color.MAGENTA);
      if (mouseStart != null && mouseEnd != null) {
        g.drawLine(mouseStart.x, mouseStart.y, mouseEnd.x, mouseEnd.y);
      }

      printViewState(g);
    }
  }

  protected void drawAxes(Graphics2D g, boolean visible) {
    // TODO: Draw axes based on visibility
    Stroke tmp = g.getStroke();
    g.setStroke(DOTTED);
    g.setColor(DARK_RED);
    //In order to make the end of y-axis and its label as high as possible
    int yOfYAxis = 0;
    int yOfYLabel = 5;

    g.drawLine((int) originPrj.x(), (int) originPrj.y(),
        (int) axisPrjX.x(), (int) axisPrjX.y());
    drawBorderString(g, classNames[0], (int) axisLabelX.x(),
        (int) axisLabelX.y(), DARK_RED, null);
    g.setColor(DARK_GREEN);
    g.drawLine((int) originPrj.x(), (int) originPrj.y(),
        (int) axisPrjY.x(), yOfYAxis);
    drawBorderString(g, classNames[1], (int) axisLabelY.x(),
        yOfYLabel, DARK_GREEN, null);
    g.setColor(DARK_BLUE);
    g.drawLine((int) originPrj.x(), (int) originPrj.y(),
        (int) axisPrjZ.x(), (int) axisPrjZ.y());
    drawBorderString(g, classNames[2], (int) axisLabelZ.x(),
        (int) axisLabelZ.y(), DARK_BLUE, null);
    g.setStroke(tmp);
  }

  protected void drawBorderString(Graphics2D g, String s, int x, int y,
      Color textColor, Color borderColor) {
    if (borderColor != null) {
      g.setColor(borderColor);
      g.drawString(s, x + 4, y + 4);
      g.drawString(s, x + 4, y + 6);
      g.drawString(s, x + 6, y + 4);
      g.drawString(s, x + 6, y + 6);
      g.drawString(s, x + 5, y + 4);
      g.drawString(s, x + 5, y + 6);
      g.drawString(s, x + 4, y + 5);
      g.drawString(s, x + 6, y + 5);
    }
    g.setColor(textColor);
    g.drawString(s, x + 5, y + 5);
  }

  protected void drawGrids(Graphics2D g, float size, int numSegments,
      boolean xEnable, boolean yEnable, boolean zEnable) {
    // size *= 0.5f;
    float delta = size / numSegments;
    float offset, limit = size;
    numSegments *= 2;
    Vector4 p = new Vector4();
    if (xEnable) {
      final Color lineColor = new Color(1.0f, 0.75f, 0.75f);
      g.setColor(lineColor);
      offset = -limit;
      for (int i = 0; i <= numSegments; i++) {
        p.set(0.0f, offset, -limit, 1.0f);
        Vector4 s1 = prj.project(p);
        p.set(0.0f, offset, limit, 1.0f);
        Vector4 e1 = prj.project(p);
        p.set(0.0f, -limit, offset, 1.0f);
        Vector4 s2 = prj.project(p);
        p.set(0.0f, limit, offset, 1.0f);
        Vector4 e2 = prj.project(p);
        g.drawLine((int) s1.x(), (int) s1.y(), (int) e1.x(),
            (int) e1.y());
        g.drawLine((int) s2.x(), (int) s2.y(), (int) e2.x(),
            (int) e2.y());
        offset += delta;
      }
    }
    if (yEnable) {
      final Color lineColor = new Color(0.75f, 1.0f, 0.75f);
      g.setColor(lineColor);
      offset = -limit;
      for (int i = 0; i <= numSegments; i++) {
        p.set(offset, 0.0f, -limit, 1.0f);
        Vector4 s1 = prj.project(p);
        p.set(offset, 0.0f, limit, 1.0f);
        Vector4 e1 = prj.project(p);
        p.set(-limit, 0.0f, offset, 1.0f);
        Vector4 s2 = prj.project(p);
        p.set(limit, 0.0f, offset, 1.0f);
        Vector4 e2 = prj.project(p);
        g.drawLine((int) s1.x(), (int) s1.y(), (int) e1.x(),
            (int) e1.y());
        g.drawLine((int) s2.x(), (int) s2.y(), (int) e2.x(),
            (int) e2.y());
        offset += delta;
      }
    }
    if (zEnable) {
      final Color lineColor = new Color(0.75f, 0.75f, 1.0f);
      g.setColor(lineColor);
      offset = -limit;
      for (int i = 0; i <= numSegments; i++) {
        p.set(offset, -limit, 0.0f, 1.0f);
        Vector4 s1 = prj.project(p);
        p.set(offset, limit, 0.0f, 1.0f);
        Vector4 e1 = prj.project(p);
        p.set(-limit, offset, 0.0f, 1.0f);
        Vector4 s2 = prj.project(p);
        p.set(limit, offset, 0.0f, 1.0f);
        Vector4 e2 = prj.project(p);
        g.drawLine((int) s1.x(), (int) s1.y(), (int) e1.x(),
            (int) e1.y());
        g.drawLine((int) s2.x(), (int) s2.y(), (int) e2.x(),
            (int) e2.y());
        offset += delta;
      }
    }
  }

  protected void drawFace(Graphics2D g, PointData p1, PointData p2,
      PointData p3, HullFace face) {
    boolean visible = face.getShade() <= VISIBILITY_EPSILON;
    float shadeMult = (float) Math
        .sqrt((face.getShade() <= VISIBILITY_EPSILON) ? -face
            .getShade() : (1.0f - face.getShade()));
    shadeMult = 0.5f + 0.5f * shadeMult;
    int[] x = new int[3];
    int[] y = new int[3];
    x[0] = (int) p1.screenPos.x();
    y[0] = (int) p1.screenPos.y();
    x[1] = (int) p2.screenPos.x();
    y[1] = (int) p2.screenPos.y();
    x[2] = (int) p3.screenPos.x();
    y[2] = (int) p3.screenPos.y();

    float fr = face.getColor().getRed() * shadeMult * (1.0f / 255);
    float fg = face.getColor().getGreen() * shadeMult * (1.0f / 255);
    float fb = face.getColor().getBlue() * shadeMult * (1.0f / 255);
    Color colorVisible = new Color(fr, fg, fb,
        enableTransparentFaces ? 0.5f : 1.0f);
    final Color colorHidden = new Color(0.75f * shadeMult,
        0.75f * shadeMult, 0.75f * shadeMult,
        enableTransparentFaces ? 0.5f : 1.0f);
    g.setColor(visible ? colorVisible : colorHidden);
    g.fillPolygon(x, y, 3);

    if (enableDebugInfo) {
      // Draw normals
      Vector4 wc = new Vector4();
      wc.x(p1.worldPos.x() + p2.worldPos.x() + p3.worldPos.x());
      wc.y(p1.worldPos.y() + p2.worldPos.y() + p3.worldPos.y());
      wc.z(p1.worldPos.z() + p2.worldPos.z() + p3.worldPos.z());
      wc.w(1.0f);
      wc.multSelf(1.0f / 3.0f);
      Vector4 pc = prj.project(wc);
      Vector4 pn = prj.project(wc.add(face.getNormal()));
      g.setColor(Color.BLUE);
      g.drawLine((int) pc.x(), (int) pc.y(), (int) pn.x(), (int) pn.y());
    }
  }

  protected void drawEdge(Graphics2D g, PointData p1, PointData p2,
      HullEdge edge, boolean visible) {
    final Color colorVisible = new Color(0.0f, 0.0f, 0.0f);
    final Color colorHidden = new Color(0.4f, 0.4f, 0.4f);

    // Check if the two adjacent faces are coplanar
    if (enableDebugEdges || !edge.getFace(0).isCoplanar(edge.getFace(1))) {
      int x1 = (int) p1.screenPos.x();
      int y1 = (int) p1.screenPos.y();
      int x2 = (int) p2.screenPos.x();
      int y2 = (int) p2.screenPos.y();
      g.setColor(enableDebugEdges ? Color.MAGENTA
          : (visible ? colorVisible : colorHidden));
      g.drawLine(x1, y1, x2, y2);
    }
  }

  protected void drawVertex(Graphics2D g, PointData p, boolean visible,
      boolean onHull) {
    final Color colorVisible = new Color(0.0f, 0.0f, 0.0f);
    final Color colorHidden = new Color(0.4f, 0.4f, 0.4f);
    final Color bulletColor = new Color(0.0f, 0.8f, 0.0f);

    int x = (int) p.screenPos.x();
    int y = (int) p.screenPos.y();
    g.setColor(visible ? colorVisible : colorHidden);
    if (onHull) {
      g.fillOval(x - 4, y - 4, 9, 9);
      if (p.vertex.getName().equals(selectedStation)) {
        g.setColor(SELECTED_STATION_COLOR);
      } else {
        g.setColor(visible ? bulletColor : Color.LIGHT_GRAY);
      }
      g.fillOval(x - 2, y - 2, 5, 5);

      if (enableHullVerticesLabels
          || p.vertex.getName().equals(selectedStation)) {
        String s = p.vertex.getName() + " " + p.worldPos.toString3D();
        g.setColor(visible ? colorVisible : colorHidden);
        if (p.vertex.getName().equals(selectedStation)) {
          g.setColor(SELECTED_STATION_COLOR);
        } else {
          g.setColor(Color.BLACK);
        }
        g.drawString(s, x + 5, y + 5);
      }
    } else {
      g.fillOval(x - 4, y - 4, 9, 9);
      if (p.vertex.getName().equals(selectedStation)) {
        g.setColor(SELECTED_STATION_COLOR);
        String s = p.vertex.getName() + " " + p.worldPos.toString3D();
        g.setColor(visible ? colorVisible : colorHidden);
        g.setColor(SELECTED_STATION_COLOR);
        g.drawString(s, x + 5, y + 5);
      } else {
        g.setColor(Color.BLUE);
      }
      g.fillOval(x - 2, y - 2, 5, 5);
    }
  }

  /**
   * Formats a floating point value with the specified number of decimals.
   *
   * @param value
   *            Value to convert into string.
   * @param decimals
   *            Number of decimals.
   * @return
   */
  protected static String floatString(float value, int decimals) {
    String res = (value >= 0 ? " " : "") + String.valueOf(value);
    int point = res.indexOf(".") + 1;
    if (decimals < 0) {
      throw new InvalidParameterException("decimals < 0");
    } else if (decimals == 0) {
      return res.substring(0, point - 2);
    } else {
      while (res.length() - point < decimals) {
        res += "0";
      }
      if (res.length() - point > decimals) {
        res = res.substring(0, point + decimals);
      }
      return res;
    }
  }

  /**
   * Prints matrix coefficients in a human-readable manner.
   *
   * @param g
   *            Graphics context.
   * @param m
   *            Matrix to print.
   * @param x
   *            Left coordinate.
   * @param y
   *            Top coordinate.
   * @param name
   *            Displayed matrix name.
   * @return The new Y coordinate after printing text lines.
   */
  protected int printMatrix4(Graphics2D g, Matrix4 m, int x, int y,
      String name) {
    int fontSize = g.getFont().getSize();
    g.drawString(name + " =", x, y);
    y += fontSize;
    g.drawString("[", x, y);
    y += fontSize;
    for (int r = 0; r < 4; r++) {
      for (int c = 0; c < 4; c++) {
        g.drawString(floatString(m.get(r, c), 6), x + 24 + (c * 75), y);
      }
      y += fontSize;
    }
    g.drawString("]", x, y);
    return y;
  }

  /**
   * Prints useful internal data, such as rotation angles and transform
   * matrices.
   *
   * @param g
   *            Graphics context.
   */
  protected void printViewState(Graphics2D g) {
    int y = 16;
    g.setColor(Color.LIGHT_GRAY);
    g.setFont(FONT);
    // FontMetrics fm = g.getFontMetrics();
    g.drawString("rotY = " + floatString(rotY, 6) + " rad", 16, y);
    g.drawString("= " + floatString((float) (rotY * 180 / Math.PI), 6)
        + "°", 110, y);
    y += 10;
    g.drawString("rotX = " + floatString(rotX, 6) + " rad", 16, y);
    g.drawString("= " + floatString((float) (rotX * 180 / Math.PI), 6)
        + "°", 110, y);
    y += 16;

    y = printMatrix4(g, prj.getModelView(), 16, y + 16, "viewMatrix");
    y = printMatrix4(g, prj.getProjection(), 16, y + 16, "projMatrix");
    y = printMatrix4(g, eyeRT, 16, y + 16, "eyeRT");
  }

  /**
   * Keeps a value within bounds.
   *
   * @param min
   *            Minimum value.
   * @param value
   *            Value to check.
   * @param max
   *            Maximum value.
   * @return The clamped value.
   */
  protected static float clamp(float min, float value, float max) {
    if (value < min) {
      return min;
    } else if (value > max) {
      return max;
    } else {
      return value;
    }
  }

  /**
   * @return The internal ScreenProjector instance.
   */
  public ScreenProjector getScreenProjector() {
    return prj;
  }

  /**
   * Projects 3D points onto the screen and repaints the diagram.
   */
  public void updateProjection() {
    final Vector4 nx = Vector4.createPoint3D(2.0f, 0.0f, 0.0f);
    final Vector4 ny = Vector4.createPoint3D(0.0f, 2.0f, 0.0f);
    final Vector4 nz = Vector4.createPoint3D(0.0f, 0.0f, 2.0f);
    final Vector4 lx = Vector4.createPoint3D(1.7f, 0.0f, 0.0f);
    final Vector4 ly = Vector4.createPoint3D(0.0f, 1.2f, 0.0f);
    final Vector4 lz = Vector4.createPoint3D(0.0f, 0.0f, 1.7f);

    if (zoomFactor > 1.0f) {
      rotXdelta /= zoomFactor;
      rotYdelta /= zoomFactor;
    }

    // Keep pitch rotation between ±90°
    rotX = clamp((float) Math.toRadians(-89), rotX + rotXdelta,
        (float) Math.toRadians(89));

    // Yaw rotation modulo 360°
    rotY -= rotYdelta;
    if (rotY < 0) {
      rotY += (2 * Math.PI);
    }
    rotY %= 2 * Math.PI;

    rotXdelta = 0;
    rotYdelta = 0;

    Dimension size = getSize();
    if (size.width < 32 || size.height < 32) {
      return;
    }
    screenW = size.width;
    screenH = size.height;
    halfW = screenW / 2;
    halfH = screenH / 2;
    aspect = screenW / screenH;
    prj.setScreenSize(size);
    prj.updateProjection();

    Matrix4 mv = prj.getModelView();
    eyeRT = Matrix4.createScale(0.95f * (enableCustomZoom ? zoomFactor
        : 1.0f));
    eyeRT.multSelf(Matrix4.createRotationX(rotX));
    eyeRT.multSelf(Matrix4.createRotationY(rotY));
    eyeRT.multSelf(Matrix4.createTranslation(-centroid.x(), centroid.y(),
        centroid.z()));
    Vector4 eyePos = Vector4.createPoint3D(0.0f, 0.0f, 10.0f);
    prj.lookAt(eyePos, Vector4.ORIGIN, false);
    prj.push(mv.clone());
    prj.push(eyeRT.clone());
    mv.set(eyeRT.mult(mv));

    originPrj = prj.project(Vector4.ORIGIN);
    axisPrjX = prj.project(nx);
    axisPrjY = prj.project(ny);
    axisPrjZ = prj.project(nz);
    axisLabelX = prj.project(lx);
    axisLabelY = prj.project(ly);
    axisLabelZ = prj.project(lz);
    eyeRT.set(prj.pop());

    eyeRT.multSelf(Matrix4.createScale((aspect < 1.0f ? aspect : 1.0f)
        / maxVertexDistance));
    mv.set(eyeRT.mult(prj.pop()));

    // TODO: Project points here instead of inside of paint()

    forward = mv.getRow(Matrix4.Z_INDEX);
    forward.normalize();
    splitByVisibility(forward);
  }

  public void componentHidden(ComponentEvent evt) {
  }

  public void componentMoved(ComponentEvent evt) {
  }

  public void componentResized(ComponentEvent evt) {
    repaint();
  }

  public void componentShown(ComponentEvent evt) {
    repaint();
  }

  public void mouseClicked(MouseEvent evt) {
    if (evt.getButton() == MouseEvent.BUTTON2) {
      // TODO: Picking
    } else if (evt.getButton() == MouseEvent.BUTTON3) {
      rightClick(evt);
    }
    repaint();
  }

  public void mouseEntered(MouseEvent evt) {
  }

  public void mouseExited(MouseEvent evt) {
  }

  public void mousePressed(MouseEvent evt) {
    switch (evt.getButton()) {
    case MouseEvent.BUTTON1: {
      mouseLeft = true;
      break;
    }
    case MouseEvent.BUTTON2: {
      mouseRight = true;
      break;
    }
    }
    if (mouseMode == MouseMode.NONE) {
      // No action already set for mouse movements, so begin a new action.
      if (mouseLeft) {
        mouseMode = MouseMode.ROTATE_VIEW;
      } else if (mouseRight) {
        mouseMode = MouseMode.MOVE_VERTEX;
      }
      // This is the starting point for mouse tracking
      mouseStart.x = evt.getX();
      mouseStart.y = evt.getY();
      mouseEnd.x = mouseStart.x;
      mouseEnd.y = mouseStart.y;
      mouseDelta = new Point(0, 0);
    } else {
      // This is the last mouse action, so keep track of it.
      mouseEnd.x = evt.getX();
      mouseEnd.y = evt.getY();
      mouseDelta = new Point(mouseEnd.x - mouseStart.x, mouseEnd.y
          - mouseStart.y);
    }
    repaint();
  }

  public void mouseReleased(MouseEvent evt) {
    switch (evt.getButton()) {
    case MouseEvent.BUTTON1: {
      mouseLeft = false;
      break;
    }
    case MouseEvent.BUTTON2: {
      mouseRight = false;
      break;
    }
    }
    if (!mouseLeft && !mouseRight) {
      mouseMode = MouseMode.NONE;
    }
    // This is the last mouse action, so keep track of it.
    mouseEnd.x = evt.getX();
    mouseEnd.y = evt.getY();
    mouseDelta = new Point(mouseEnd.x - mouseStart.x, mouseEnd.y
        - mouseStart.y);
    repaint();
  }

  public void mouseDragged(MouseEvent evt) {
    handleMovements(evt);
  }

  public void mouseMoved(MouseEvent evt) {
    handleMovements(evt);
  }

  protected void handleMovements(MouseEvent evt) {
    if (mouseMode != MouseMode.NONE) {
      // This is the last mouse action, so keep track of it.
      mouseEnd.x = evt.getX();
      mouseEnd.y = evt.getY();
      mouseDelta = new Point(mouseEnd.x - mouseStart.x, mouseEnd.y
          - mouseStart.y);
      Dimension screenSize = prj.getScreenSize();
      switch (mouseMode) {
      case ROTATE_VIEW: {
        rotYdelta = (float) (mouseDelta.x * (Math.PI / screenSize.width));
        rotXdelta = (float) (mouseDelta.y * (Math.PI / screenSize.height));
        break;
      }
      case MOVE_VERTEX: {
        // TODO
        break;
      }
      default: {
        break;
      }
      }

      // Mouse movement was processed, so keep track of a new movement.
      mouseStart.x = mouseEnd.x;
      mouseStart.y = mouseEnd.y;

      repaint();
    }
  }

  public void selectStation(String name) {
    selectedStation = name;
    repaint();
  }

  public void mouseWheelMoved(MouseWheelEvent evt) {
    if (enableCustomZoom) {
      int n = evt.getWheelRotation();
      zoomFactor *= ((n < 0) ? (-n * 1.1f) : (n * 0.9f));
      repaint();
    } else {
      // TODO: Add super signal
    }
  }

}
TOP

Related Classes of jmt.gui.jaba.graphs.Convex3DGraph$PointData

TOP
Copyright © 2018 www.massapi.com. All rights reserved.
All source code are property of their respective owners. Java is a trademark of Sun Microsystems, Inc and owned by ORACLE Inc. Contact coftware#gmail.com.