/* CompactModel.java Representation of a 3D model as a wire frame of line segments tailored for use in a AWT drawing environment. History: 10 Jun 2008 Extracted and adapted from Model3D class in ThreeD.java. */ /** The representation of a 3D model. Used by ThreeD. @see ThreeD */ public class CompactModel extends Model3D { /** A vertex index was used which is too large to fit in the bitfield reserved for it. @param s description of the situation @see CompactModel#addLine @see CompactModel#M_INDEX_MASK @see CompactModel#M_INDEX_NBITS */ public class VertexOverflowException extends ModelException { public VertexOverflowException(String s) { super(s); } } /** Average x value (in model coordinates) for the figure. */ float m_xmean; /** Average y value (in model coordinates) for the figure. */ float m_ymean; /** Average z value (in model coordinates) for the figure. */ float m_zmean; /** Radius (in model coordinates) of the figure's bounding sphere. */ float m_radius; /** Untransformed (model) coordinates for all points. */ private float m_verts[]; /** Size of used portion of vertex array. */ private int m_nverts; /** Minimum size of vertex and line arrays. */ static final int M_MIN_BUFFSIZE = 50; /** One integer for each line. There are two bitfields indicating the indices of the endpoint vertices (0 based index), and a third bitfield indicating a type. The latter value is used when the line is painted to indicate how it should be rendered. @see #M_INDEX_MASK @see #M_INDEX_NBITS @see #M_TYPE_MASK @see #M_TYPE_NBITS */ private int m_lines[]; /** Indicates how many of the values in m_lines are valid. Incremented by addLine(). @see #m_lines @see #addLine */ private int m_nlines; /** Mask used to extract the index bitfields from an integer containing a line's description. Used by p1(), p2() and type() to decode the integers in m_lines. @see #m_lines @see #p1 @see #p2 @see #type */ private static final int M_INDEX_MASK = 0x7FFF; /** Bit count used to extract the index and type bitfields from an integer containing a line's description. Used by addLine() to encode and p1(), p2() and type() to decode the integers in m_lines. It should be set to the number of set bits in M_INDEX_MASK. @see #M_INDEX_MASK @see #m_lines @see #addLine @see #p1 @see #p2 @see #type */ private static final int M_INDEX_NBITS = 15; /** Mask used to extract the type bitfield from an integer containing a line's description. Used by addLine() to encode and p1(), p2() and type() to decode the integers in m_lines. @see #m_lines @see #addLine @see #p1 @see #p2 @see #type */ private static final int M_TYPE_MASK = 0x0003; /** Bit count used to extract the index and type bitfields from an integer containing a line's description. Used by addLine() to encode and p1(), p2() and type() to decode the integers in m_lines. It should be set to the number of set bits in M_TYPE_MASK. @see #M_TYPE_MASK @see #m_lines @see #addLine @see #p1 @see #p2 @see #type */ private static final int M_TYPE_NBITS = 2; CompactModel() { } /** Initialize the model to the empty state. */ public void reset() { m_nverts = 0; m_nlines = 0; } /** See how many vertices are in the current model -- can also be used to check if a model has been loaded. */ int nverts() { return m_nverts; } /** Get vertices for the current model. */ float[] verts() { return m_verts; } /** See how many lines are in the current model. */ int nlines() { return m_nlines; } /** Index of first end point for given line. */ int p1(int index) { return ((m_lines[index] >> (M_INDEX_NBITS + M_TYPE_NBITS)) & M_INDEX_MASK) * 3; } /** Index of second end point for given line. */ int p2(int index) { return ((m_lines[index] >> M_TYPE_NBITS) & M_INDEX_MASK) * 3; } /** Type value for given line. */ int type(int index) { return m_lines[index] & M_TYPE_MASK; } /** Add a vertex to this model. Implements abstract method. @param x x coordinate of point @param y y coordinate of point @param z z coordinate of point @exception ModelException See exception documentation for reason. */ void addPoint(double x, double y, double z) throws ModelException { int i = m_nverts; if (i >= M_INDEX_MASK) throw new VertexOverflowException(toString()); if (m_verts == null) m_verts = new float[M_MIN_BUFFSIZE * 3]; else if ((i + 1)*3 > m_verts.length) { float nv[] = new float[m_verts.length*2]; System.arraycopy(m_verts, 0, nv, 0, m_verts.length); m_verts = nv; } i *= 3; m_verts[i] = (float)x; m_verts[i + 1] = (float)y; m_verts[i + 2] = (float)z; m_nverts++; } /** Add a line from vertex p1 to vertex p2 to the model's geometry. Implements abstract method. @param p1 zero-based index of first endpoint @param p2 zero-based index of second endpoint @param type type value for this line segment @exception ModelException See exception documentation for reason. */ void addLineSegment(int p1, int p2, int val) { val = val % 8; int type = 0; if (val == 0) return; else if (val == 1) type = 1; else if (val == 2 || val == 4 || val == 6) type = 2; else if (val == 3 || val == 5) type = 3 ; else type = 0 ; if (p1 >= m_nverts || p2 >= m_nverts) return; if (m_lines == null) m_lines = new int[M_MIN_BUFFSIZE]; else if (m_nlines >= m_lines.length) { int nl[] = new int[m_lines.length*2]; System.arraycopy(m_lines, 0, nl, 0, m_lines.length); m_lines = nl; } m_lines[m_nlines] = (p1 << (M_INDEX_NBITS + M_TYPE_NBITS)) | (p2 << M_TYPE_NBITS) | (type & M_TYPE_MASK); m_nlines++; } /** Compute the bounding sphere (in model coordinates) of this model. Implements abstract method. @see #m_xmean @see #m_ymean @see #m_zmean @see #m_radius */ void computeBoundingSphere() { if (m_nverts <= 0) return; // find center m_xmean = 0; m_ymean = 0; m_zmean = 0; for (int i = (m_nverts - 1)* 3; i >= 0; i -= 3) { m_xmean += m_verts[i]; m_ymean += m_verts[i + 1]; m_zmean += m_verts[i + 2]; } m_xmean /= m_nverts; m_ymean /= m_nverts; m_zmean /= m_nverts; // find radius m_radius = 0; for (int i = (m_nverts - 1)* 3; i >= 0; i -= 3) { float dx = m_verts[i] - m_xmean; float dy = m_verts[i + 1] - m_ymean; float dz = m_verts[i + 2] - m_zmean; float radius = dx*dx + dy*dy + dz*dz; if (radius > m_radius) m_radius = radius; } m_radius = (float)Math.sqrt(m_radius); } }