Files
secondo/Javagui/viewer/hoese/Layer.java
2026-01-23 17:03:45 +08:00

554 lines
16 KiB
Java

//This file is part of SECONDO.
//Copyright (C) 2004, University in Hagen, Department of Computer Science,
//Database Systems for New Applications.
//SECONDO 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.
//SECONDO 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 SECONDO; if not, write to the Free Software
//Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
package viewer.hoese;
import java.awt.*;
import java.awt.event.*;
import javax.swing.*;
import java.awt.geom.*;
import java.util.*;
import javax.swing.border.*;
import viewer.HoeseViewer;
import java.awt.image.*;
import tools.Reporter;
/**
* A Swing JComponent that represent a Layer in the layerstack
*/
public class Layer extends JComponent {
/** All the graph. objects that are accociated with this Layer */
private Vector GeoObjects;
/** The object that mangages the layer */
private GraphWindow owner;
/** This butto controls the on/off status */
public JToggleButton button;
/** the world boundingbox of this layer */
Rectangle2D.Double boundsWC;
/** True if the selected object is in this layer */
boolean Selected = false;
/** the internal no. of this layer */
int LayerNo;
/**
* Default Construktor
* @see <a href="Layersrc.html#Layer1">Source</a>
*/
public Layer () {
GeoObjects = new Vector(20);
}
/**
* Construktor for a layer, which should display the objects in obj within owner gw
* @param Vector obj
* @param GraphWindow gw
* @see <a href="Layersrc.html#Layer2">Source</a>
*/
public Layer (Vector obj, GraphWindow gw) {
owner = gw;
GeoObjects = (Vector)obj.clone();
setDoubleBuffered(true);
calcBounds();
}
/** Calculates world-bounding box for this layer
* @see <a href="Layersrc.html#calcBounds">Source</a>
*/
public void calcBounds () {
boundsWC=null;
try{
ListIterator li = GeoObjects.listIterator();
while (li.hasNext()) {
Rectangle2D.Double bds = null; // null sp. entf
DsplGraph dg = ((DsplGraph)li.next());
dg.setLayer(this);
bds = dg.getBounds();
if(bds != null){ // not an empty object
if (boundsWC == null)
boundsWC = bds;
else
boundsWC = (Rectangle2D.Double)boundsWC.createUnion(bds);
}
}
// enlarge bounding boxes of size 0
double enlargeSize = 0.000001;
if(boundsWC.getWidth()<=0){
boundsWC.x -= enlargeSize;
boundsWC.width = 2*enlargeSize;
}
if(boundsWC.getHeight()<=0){
boundsWC.x -= enlargeSize;
boundsWC.height = 2*enlargeSize;
}
}
catch(Exception e){
Reporter.writeError("Exception in Layer.calcBounds "+e);
Reporter.debug(e);
}
}
/**
* Removes a graph. object from this layer
* @param dg A graph. object
* @see <a href="Layersrc.html#removeGO">Source</a>
*/
public void removeGO (DsplGraph dg) {
if(dg!=null){
GeoObjects.remove(dg);
calcBounds();
}
}
/**
*Adds a graph. object to this layer at position index
* @param index if ==-1 then dg is added at the end
* @param dg The object to add
* @see <a href="Layersrc.html#addGO">Source</a>
*/
public void addGO (int index, DsplGraph dg) {
if(dg!=null){
if ((index < 0) || (index >= GeoObjects.size()))
GeoObjects.add(dg);
else
GeoObjects.add(index, dg);
calcBounds();
}
}
/**
* Sets whether the button should be set as selected or not.
* @param b True when button should be selected
* @see <a href="Layersrc.html#setSelectedButton">Source</a>
*/
public void setSelectedButton (boolean b) {
Selected = b;
button.repaint();
}
/**
* Calculates an unique index for an object in the layerlist, so that its height is comparable to
* other objects height in the layer-stack
* @param dg A graph. object
* @return A double value
* @see <a href="Layersrc.html#getObjIndex">Source</a>
*/
public double getObjIndex (DsplGraph dg) {
int index = GeoObjects.indexOf(dg);
int size = GeoObjects.size();
if (index < 0)
index = size++;
return LayerNo + (double)index/(double)size;
}
/**
* Gets the actual transformation, which is the parent transformation
* @return Transformation
* @see <a href="Layersrc.html#getProjection">Source</a>
*/
public AffineTransform getProjection () {
return owner.getProjection();
}
/**
*
* @return the world-boundinbox of this layer.
* @see <a href="Layersrc.html#getWorldCoordBounds">Source</a>
*/
public Rectangle2D.Double getWorldCoordBounds () {
return boundsWC;
}
/**
* Creates the Layerswichbutton for turning on/off
* @param al The ActionListener that manages the layer-toggling
* @param lnr The intern layer nr
* @return A new Switchbutton
* @see <a href="Layersrc.html#CreateLayerButton">Source</a>
*/
public JToggleButton CreateLayerButton (ActionListener al, int lnr) {
LayerNo = lnr;
button = new LayerToggle();
button.setSelected(true);
button.setAlignmentX(Component.CENTER_ALIGNMENT);
//button.setBorder(new LineBorder(Color.blue,1));
button.setPreferredSize(new Dimension(10, 10));
//button.setSize(10,10);
button.addActionListener(al);
button.setActionCommand(Integer.toString(lnr));
return button;
}
/** Returns the categories of all object including the given point **/
TreeSet<Category> getCats(Point2D.Double position, double scalex, double scaley, boolean onlyFirst){
TreeSet<Category> res = new TreeSet<Category>();
boolean done = false;
double x = position.getX();
double y = position.getY();
for(int i=0;i<GeoObjects.size() && !done ;i++){
DsplGraph dg = (DsplGraph)GeoObjects.get(i);
if(dg.getVisible()) {
if(dg.contains(x,y,scalex,scaley)){
res.add(dg.getCategory());
if(onlyFirst){
done = true;
}
}
}
}
return res;
}
/**
*
* @return The list of geograph. objects in this layeer
* @see <a href="Layersrc.html#getGeoObjects">Source</a>
*/
public Vector getGeoObjects () {
return GeoObjects;
}
/**
*
* @return The application's actual time
* @see <a href="Layersrc.html#getActualTime">Source</a>
*/
public double getActualTime () {
return CurrentState.ActualTime;
}
/** paint one of the given icons to g2 */
private static void drawIcon(BufferedImage original, BufferedImage scaled,
Rectangle2D box, boolean resize, Graphics2D g,
AffineTransform at){
double x = box.getX();
double y = box.getY();
double w = box.getWidth();
double h = box.getHeight();
if(w<=0 || h<=0){
Reporter.debug("invalid value for iconbox ");
return;
}
// the current implementation ignores the scaled image
if(resize){
g.drawImage(original,(int)x,(int)y,(int)w,(int)h,null);
} else {
double dx = (w-original.getWidth())/2;
double dy = (h-original.getHeight())/2;
g.drawImage(original,(int)(x+dx),(int)(y+dy),null);
}
}
/** The draw function using the transformation from getProjection **/
public void draw(DsplGraph dg,Graphics g,double time) {
draw(dg,g,time,getProjection());
}
/**
* This method draws the RenderObject with ist viewattributes collected in the category
* @param g The graphic context to draw in.
*/
public static void draw(DsplGraph dg,Graphics g,double time, AffineTransform af2) {
if(dg==null){
Reporter.writeError("try to draw null");
return;
}
Shape sh=null; // transformed renderobject
Rectangle2D bounds=null;
Graphics2D g2 = (Graphics2D)g;
int num = dg.numberOfShapes();
Category cat = dg.getCategory();
Paint fillStyle = cat.getFillStyle(dg.getRenderAttribute(),time);
for(int i=0;i<num;i++){
Shape shp = dg.getRenderObject(i,af2);
if(shp!=null){
sh = af2.createTransformedShape(shp);
if(bounds==null){
bounds = shp.getBounds2D();
} else {
bounds.add(shp.getBounds2D());
}
setCategory(dg,g2,i);
// paint the interior
if (fillStyle != null && !dg.isLineType(i) && !cat.getIconFill()){
g2.setPaint(fillStyle);
if(sh!=null){
g2.fill(sh);
}
}
// paint an icon
if(cat.getIconFill()){
boolean resize = cat.getIconResizeToBox();
if(cat.getTextureImage()!=null){
if(sh!=null){
drawIcon(cat.getTextureImage(), cat.getResizedImage(),sh.getBounds(),resize,g2,af2);
}
} else {
Reporter.writeError("no Icon found but iconFill is choosen");
}
}
// switch line color if this object is selected
g2.setComposite(AlphaComposite.getInstance(AlphaComposite.SRC_OVER));
boolean isColorRendering = cat.getRenderMethod() == Category.RENDER_COLOR;
if(isColorRendering && dg.isLineType(i)){
cat.setRenderMethod(Category.RENDER_LINE_COLOR);
}
Color aktLineColor = cat.getLineColor(dg.getRenderAttribute(),time, dg.isLineType(i));
if (dg.getSelected()){
aktLineColor = new Color(Color.white.getRGB() ^ cat.getLineColor(dg.getRenderAttribute(),time, dg.isLineType(i)).getRGB());
}
if(isColorRendering){
cat.setRenderMethod(Category.RENDER_COLOR);
}
g2.setColor(aktLineColor);
// paint the border
if ((cat.getLineWidth(dg.getRenderAttribute(),time) > 0.0f) || (dg.getSelected())){
g2.setStroke(cat.getLineStroke(dg.getRenderAttribute(),time));
if(sh!=null){
g2.draw(sh);
}
}
}
}
if(bounds!=null){
drawLabel(dg,g2, bounds,time,af2);
}
// special treatment for non shape objects
if(dg instanceof DisplayComplex){
if (fillStyle != null ){
g2.setPaint(fillStyle);
}
((DisplayComplex)dg).draw(g,time,af2);
}
}
public static void drawLabel (DsplGraph dg, Graphics g, Rectangle2D r,
double time, AffineTransform af2) {
drawLabel(dg,g,r,time,af2,null);
}
/**
* The draw method for the label.
* @param g The graphic context to draw in.
* @param r The bounding box of the object to label.
*/
public static void drawLabel (DsplGraph dg, Graphics g, Rectangle2D r,
double time, AffineTransform af2,Color C) {
if(r==null){
Reporter.writeError("drawLabel with null-bounding box called !!");
return;
}
String LabelText = dg.getLabelText(time);
if (LabelText == null || LabelText.trim().equals("")){ // no label
return;
}
drawLabel(dg,g,r,time,af2,C,LabelText);
}
public static void drawLabel (DsplGraph dg, Graphics g, Rectangle2D r,
double time, AffineTransform af2,Color C,
String LabelText) {
Graphics2D g2 = (Graphics2D)g;
Point2D.Double p = new Point2D.Double(r.getX() + r.getWidth()/2, r.getY()
+ r.getHeight()/2);
af2.transform(p, p);
float x = (float)(p.getX()+dg.getLabPosOffset().getX());
float y = (float)(p.getY()+dg.getLabPosOffset().getY());
Rectangle2D re = g2.getFont().getStringBounds(LabelText, g2.getFontRenderContext());
x -= re.getWidth()/2.0;
y += re.getHeight()/2.0;
if (dg.getSelected()) {
g2.setPaint(new Color(255, 128, 255, 255));
g2.fill3DRect((int)x,(int)(y+re.getY()),(int)re.getWidth(), (int)re.getHeight(),
true);
}
if(C==null){
Category cat = dg.getCategory();
g2.setPaint(cat.getLineColor(null,0, false));
} else {
g2.setPaint(C);
}
g2.drawString(LabelText, x,y);
}
/**
* Sets a new Category to paint the next object to the graphics context.
* @param dg: the object to drawn
* @param g2: the graphics context which is used
* @param num: the shape wich is to drawn, an negative number means that the
* getRenderObject method is used instead of using this number as
index for the array returned by the getRenderObjects() method
**/
private static void setCategory(DsplGraph dg,Graphics2D g2, int num){
// ensure to have all required information
if(dg==null){
Reporter.writeError("Try to set a category for an object whith value null");
return;
}
if(g2==null){
Reporter.writeError("try to set a Category without a graphics context");
return;
}
Category Cat = dg.getCategory();
if(Cat==null) {
Reporter.writeError("try to set a category which has value null");
return;
}
AffineTransform af2 = CurrentState.transform;
Shape shp;
boolean isPointType;
boolean isLineType;
Shape render = dg.getRenderObject(num,af2);
if(render==null)
return;
Shape sh = af2.createTransformedShape(render);
if(sh==null) return;
if(!dg.isLineType(num)){
g2.setComposite(Cat.getAlphaStyle());
} else{
g2.setComposite(HoeseViewer.emptyStyle);
}
g2.setStroke(Cat.getLineStroke(dg.getRenderAttribute(),CurrentState.ActualTime));
Paint P = Cat.getFillStyle(dg.getRenderAttribute(),CurrentState.ActualTime);
g2.setPaint(P);
if(P==null) return;
if (dg.isPointType(num)) {
if (P instanceof TexturePaint){
BufferedImage bi = ((TexturePaint)P).getImage();
if(bi==null) return;
g2.setPaint(new TexturePaint(bi,sh.getBounds2D()));
}
else if (P instanceof GradientPaint)
g2.setPaint(new GradientPaint((float)sh.getBounds().getX(),(float)sh.getBounds().getY(),
((GradientPaint)P).getColor1(),(float)(sh.getBounds().getX()+sh.getBounds().getWidth()),
(float)(sh.getBounds().getY()+sh.getBounds().getHeight()),((GradientPaint)P).getColor2(),false));
}
}
/**
* Paints the layer with its graph. object. The selected object is not drawn here.
* @param g The graphic context
* @see <a href="Layersrc.html#paintComponent">Source</a>
*/
public void paintComponent(Graphics g)
{
Graphics2D g2 = (Graphics2D)g;
try
{
TileManager tileManager = new TileManager(GeoObjects);
if(tileManager != null)
{
tileManager.updateObjects();
}
if(Selected)
{
for(int i = 0; i < GeoObjects.size(); i++)
{
DsplGraph dg = (DsplGraph)GeoObjects.get(i);
if(dg.getVisible() == true &&
dg.getSelected() == false)
{
draw(dg, g2, CurrentState.ActualTime);
}
}
}
else
{
for(int i = 0; i < GeoObjects.size(); i++)
{
DsplGraph dg = (DsplGraph)GeoObjects.get(i);
if(dg.getVisible() == true)
{
draw(dg, g2, CurrentState.ActualTime);
}
}
}
}
catch(Exception e)
{
Reporter.writeError("Exception "+e);
Reporter.debug(e);
}
}
/** A special JToggleButton, that draws a selection area
* @see <a href="Layersrc.html#LayerToggle">Source</a>
*/
class LayerToggle extends JToggleButton {
public void paintComponent (Graphics g) {
if (isSelected())
g.setColor(Color.green);
else
g.setColor(Color.lightGray);
Insets insets = getInsets();
int currentWidth = getWidth(); //- insets.left - insets.right;
int currentHeight = getHeight(); // - insets.top - insets.bottom;
g.fillRect(0, 0, currentWidth, currentHeight);
if (Selected) {
g.setColor(Color.red);
g.fillRect(3, 3, 3, 4);
}
}
}
}