/* * Open Source Physics software is free software as described near the bottom of this code file. * * For additional information and documentation on Open Source Physics please see: * <http://www.opensourcephysics.org/> */ package org.opensourcephysics.display3d.simple3d; import java.awt.Color; import java.awt.Graphics2D; /** * This is the basic class for all Elements which consist of a sequence * of 3D colored tiles: Ellipsoid, Cylinder, Box, ... * A tile is basically a collection of rectangles. * NOT YET FINISHED!!!!! */ public abstract class AbstractTile extends Element { // Configuration variables protected int numberOfTiles = 0; protected double corners[][][] = null; // the numberOfTiles x vertex x 3 (vertex = 4 for 4-sided tiles private boolean drawQuickInterior = false; private int interiorTransparency = 128; // private double displacementFactor = 1.0; // for z-coded colors private boolean levelBelowWhenEqual = true; private double levelx = 0.0, levely = 0.0, levelz = 0.0, leveldx = 0.0, leveldy = 0.0, leveldz = 1.0; private double[] levelZ = null; private Color[] levelColors = null; // Implementation variables private int a[][] = null, b[][] = null; private double[] pixel = new double[3]; // The output for all projections private double[] center = new double[3]; private double[] pixelOrigin = new double[3]; // The projection of the origin private Object3D[] objects = null; // ---------------------------------------------- // Configuration // See also below for everything related to the use of z-coded color // ---------------------------------------------- /** * Sets an optional displacement factor to apply to the tiles when computing * their distance to the eye. * Setting it to a number bigger that 1 (say 1.03) is useful when you want * to draw anything on top of an object. * @param factor the desired displacement factor * public void setDisplacementFactor(double factor) { this.displacementFactor = factor; } /** * Gets the displacement factor * @return the current displacement factor * @see #setDisplacementFactor() * public double getDisplacementFactor() { return this.displacementFactor; } */ /** * Draw a transparent interior when in quickDraw mode. * Default is <b>false</b> * @param draw the value desired * @param transparency the desired level of transparency (from 0=fully transparent to 255=opaque) */ public void setDrawQuickInterior(boolean draw, int transparency) { this.drawQuickInterior = draw; this.interiorTransparency = Math.max(0, Math.min(transparency, 255)); } /** * Whether a value equal to one of the thresholds should be drawn using the color * below or above * @param belowWhenEqual <b>true</b> to use the color below, <b>false</b> to use teh color above */ public void setColorBelowWhenEqual(boolean belowWhenEqual) { this.levelBelowWhenEqual = belowWhenEqual; } /** * Sets the origin and direction of the color change. * Default is (0,0,0) and (0,0,1), giving z-coded regions * * @param origin double[] * @param direction double[] */ public void setColorOriginAndDirection(double[] origin, double[] direction) { levelx = origin[0]; levely = origin[1]; levelz = origin[2]; leveldx = direction[0]; leveldy = direction[1]; leveldz = direction[2]; } /** * Set the levels and color for regional color separation * @param thresholds an array on n doubles that separate the n+1 regions. * <b>null</b> for no region separation * @param colors an array on n+1 colors, one for each of the regions */ public void setColorRegions(double thresholds[], Color colors[]) { if((thresholds==null)||(colors==null)) { levelZ = null; levelColors = null; return; } levelZ = new double[thresholds.length]; levelColors = new Color[thresholds.length+1]; for(int i = 0; i<thresholds.length; i++) { levelZ[i] = thresholds[i]; } for(int i = 0; i<thresholds.length+1; i++) { if(i<colors.length) { levelColors[i] = colors[i]; } else { levelColors[i] = colors[colors.length-1]; } } setElementChanged(true); } // ---------------------------------------------- // part of the abstract methods in Element // ---------------------------------------------- Object3D[] getObjects3D() { if(!isReallyVisible()) { return null; } if(hasChanged()) { computeCorners(); projectPoints(); } else if(needsToProject()) { projectPoints(); } if(numberOfTiles<1) { return null; } return objects; } void draw(Graphics2D _g2, int _index) { if(levelZ!=null) { drawColorCoded(_g2, _index); return; } int sides = corners[_index].length; // Allow the panel to adjust color according to depth if(getRealStyle().isDrawingFill()) { // First fill the inside _g2.setPaint(getDrawingPanel3D().projectColor(getRealStyle().getFillColor(), objects[_index].getDistance())); _g2.fillPolygon(a[_index], b[_index], sides); } if(getRealStyle().isDrawingLines()) { _g2.setStroke(getRealStyle().getLineStroke()); _g2.setColor(getDrawingPanel3D().projectColor(getRealStyle().getLineColor(), objects[_index].getDistance())); _g2.drawPolygon(a[_index], b[_index], sides); } } void drawQuickly(Graphics2D _g2) { if(!isReallyVisible()) { return; } if(hasChanged()) { computeCorners(); projectPoints(); } else if(needsToProject()) { projectPoints(); } if(numberOfTiles<1) { return; } _g2.setStroke(getRealStyle().getLineStroke()); if(getRealStyle().isDrawingFill()||drawQuickInterior) { Color fillColor = getRealStyle().getFillColor(); if(drawQuickInterior&&(fillColor.getAlpha()>interiorTransparency)) { fillColor = new Color(fillColor.getRed(), fillColor.getGreen(), fillColor.getBlue(), interiorTransparency); } _g2.setPaint(fillColor); for(int i = 0; i<numberOfTiles; i++) { _g2.fillPolygon(a[i], b[i], corners[i].length); } } if(getRealStyle().isDrawingLines()) { _g2.setColor(getRealStyle().getLineColor()); for(int i = 0; i<numberOfTiles; i++) { _g2.drawPolygon(a[i], b[i], corners[i].length); } } } // ------------------------------------- // Interaction // ------------------------------------- protected InteractionTarget getTargetHit(int x, int y) { if(!isReallyVisible()) { return null; } if(hasChanged()) { computeCorners(); projectPoints(); } else if(needsToProject()) { projectPoints(); } if(numberOfTiles<1) { return null; } if(targetPosition.isEnabled()&&(Math.abs(pixelOrigin[0]-x)<SENSIBILITY)&&(Math.abs(pixelOrigin[1]-y)<SENSIBILITY)) { return targetPosition; } return null; } // ------------------------------------- // Private or protected methods // ------------------------------------- /** * This will be used by subclasses whenever there is a need to recompute * the actual values of the corners before drawing. * Synchronization is recomended. */ abstract protected void computeCorners(); protected void setCorners(double[][][] _data) { corners = _data; if(corners==null) { numberOfTiles = 0; a = null; b = null; return; } numberOfTiles = corners.length; a = new int[numberOfTiles][]; b = new int[numberOfTiles][]; objects = new Object3D[numberOfTiles]; for(int i = 0; i<numberOfTiles; i++) { int sides = corners[i].length; a[i] = new int[sides]; b[i] = new int[sides]; objects[i] = new Object3D(this, i); } } protected void projectPoints() { for(int i = 0; i<numberOfTiles; i++) { int sides = corners[i].length; for(int k = 0; k<3; k++) { center[k] = 0.0; // Reset coordinates of the center } for(int j = 0; j<sides; j++) { getDrawingPanel3D().project(corners[i][j], pixel); // Project each corner a[i][j] = (int) pixel[0]; b[i][j] = (int) pixel[1]; for(int k = 0; k<3; k++) { center[k] += corners[i][j][k]; // Add to the coordinates of the center } } for(int k = 0; k<3; k++) { center[k] /= sides; } getDrawingPanel3D().project(center, pixel); // Project the center and take it objects[i].setDistance(pixel[2]*getStyle().getDepthFactor()); // as reference for the distance } getDrawingPanel3D().project(getHotSpot(targetPosition), pixelOrigin); setNeedToProject(false); } // ---------------------------------------------- // Everything related to the use of z-coded color // ---------------------------------------------- private double levelScalarProduct(double point[]) { return(point[0]-levelx)*leveldx+(point[1]-levely)*leveldy+(point[2]-levelz)*leveldz; } private void drawColorCoded(Graphics2D _g2, int _index) { int sides = corners[_index].length; // Compute in which region is each point int region[] = new int[sides]; if(levelBelowWhenEqual) { for(int j = 0; j<sides; j++) { region[j] = 0; double level = levelScalarProduct(corners[_index][j]); for(int k = levelZ.length-1; k>=0; k--) { // for each level if(level>levelZ[k]) { region[j] = k+1; break; } } } } else { for(int j = 0; j<sides; j++) { region[j] = levelZ.length; double level = levelScalarProduct(corners[_index][j]); for(int k = 0, l = levelZ.length; k<l; k++) { // for each level if(level<levelZ[k]) { region[j] = k; break; } } } } // Compute the subpoligon in each region int newCornersA[] = new int[sides*2]; int newCornersB[] = new int[sides*2]; for(int k = 0, l = levelZ.length; k<=l; k++) { // for each level int newCornersCounter = 0; for(int j = 0; j<sides; j++) { // for each point int next = (j+1)%sides; if((region[j]<=k)&&(region[next]>=k)) { // intersection bottom-up if(region[j]==k) { newCornersA[newCornersCounter] = a[_index][j]; newCornersB[newCornersCounter] = b[_index][j]; newCornersCounter++; } else { // It started further down double t = levelScalarProduct(corners[_index][j]); t = (levelZ[k-1]-t)/(levelScalarProduct(corners[_index][next])-t); newCornersA[newCornersCounter] = (int) Math.round(a[_index][j]+t*(a[_index][next]-a[_index][j])); newCornersB[newCornersCounter] = (int) Math.round(b[_index][j]+t*(b[_index][next]-b[_index][j])); newCornersCounter++; } if(region[next]>k) { // This segment contributes with a second point double t = levelScalarProduct(corners[_index][j]); t = (levelZ[k]-t)/(levelScalarProduct(corners[_index][next])-t); newCornersA[newCornersCounter] = (int) Math.round(a[_index][j]+t*(a[_index][next]-a[_index][j])); newCornersB[newCornersCounter] = (int) Math.round(b[_index][j]+t*(b[_index][next]-b[_index][j])); newCornersCounter++; } } else if((region[j]>=k)&&(region[next]<=k)) { // intersection top-down if(region[j]==k) { newCornersA[newCornersCounter] = a[_index][j]; newCornersB[newCornersCounter] = b[_index][j]; newCornersCounter++; } else { // It started further up double t = levelScalarProduct(corners[_index][j]); t = (levelZ[k]-t)/(levelScalarProduct(corners[_index][next])-t); newCornersA[newCornersCounter] = (int) Math.round(a[_index][j]+t*(a[_index][next]-a[_index][j])); newCornersB[newCornersCounter] = (int) Math.round(b[_index][j]+t*(b[_index][next]-b[_index][j])); newCornersCounter++; } if(region[next]<k) { // This segment contributes with a second point double t = levelScalarProduct(corners[_index][j]); t = (levelZ[k-1]-t)/(levelScalarProduct(corners[_index][next])-t); newCornersA[newCornersCounter] = (int) Math.round(a[_index][j]+t*(a[_index][next]-a[_index][j])); newCornersB[newCornersCounter] = (int) Math.round(b[_index][j]+t*(b[_index][next]-b[_index][j])); newCornersCounter++; } } } if(newCornersCounter>0) { // Draw the subpoligon Color theFillColor = levelColors[k]; // if (theFillPattern instanceof Color) theFillPattern = _panel.projectColor((Color) theFillPattern,objects[_index].distance); _g2.setPaint(theFillColor); _g2.fillPolygon(newCornersA, newCornersB, newCornersCounter); } } _g2.setColor(getDrawingPanel3D().projectColor(getRealStyle().getLineColor(), objects[_index].getDistance())); _g2.setStroke(getRealStyle().getLineStroke()); _g2.drawPolygon(a[_index], b[_index], sides); } } /* * Open Source Physics software is free software; you can redistribute * it and/or modify it under the terms of the GNU General Public License (GPL) as * published by the Free Software Foundation; either version 2 of the License, * or(at your option) any later version. * Code that uses any portion of the code in the org.opensourcephysics package * or any subpackage (subdirectory) of this package must must also be be released * under the GNU GPL license. * * This software 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; if not, write to the Free Software * Foundation, Inc., 59 Temple Place, Suite 330, Boston MA 02111-1307 USA * or view the license online at http://www.gnu.org/copyleft/gpl.html * * Copyright (c) 2007 The Open Source Physics project * http://www.opensourcephysics.org */