edu.mines.jtk.mosaic.Mosaic Maven / Gradle / Ivy
/****************************************************************************
Copyright 2004, Colorado School of Mines and others.
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
****************************************************************************/
package edu.mines.jtk.mosaic;
import java.awt.*;
import java.awt.event.*;
import static java.lang.Math.*;
import java.util.ArrayList;
import java.util.Set;
import javax.swing.*;
import edu.mines.jtk.awt.ModeManager;
/**
* A mosaic of tiles and tile axes. A mosaic lays out its tiles in a matrix,
* with a specified number of rows and columns. It manages the world and
* normalized coordinate systems of those tiles, so that tiles zoom and
* scroll consistently.
*
* For example, when the the view rectangle (in normalized coordinates)
* of a tile is set, perhaps while zooming or scrolling, then that tile's
* mosaic changes the view rectangles of any other tiles in the same row
* or column accordingly, so that they all zoom and scroll together.
*
* A mosaic can also manage axes at the top, left, bottom, and/or right
* sides of its matrix of tiles. These axes annotate the adjacent tiles,
* and mosaic ensures that they too zoom and scroll consistent with any
* changes to the view rectangles of those tiles.
*
* A mosaic also manages a horizontal scrollbar for each column and a
* vertical scrollbar for each row. The mosaic shows scrollbars for only
* those dimensions of view rectangles that are zoomed. In other words,
* scrollbars are visible and consume space only when they are needed.
*
* @author Dave Hale, Colorado School of Mines
* @version 2004.12.27
* @version 2005.12.23
*/
public class Mosaic extends IPanel {
private static final long serialVersionUID = 1L;
/**
* Placement of axes.
*/
public enum AxesPlacement {
TOP, LEFT, BOTTOM, RIGHT
}
/**
* Constructs a mosaic with the specified number of rows and columns.
* @param nrow the number of rows.
* @param ncol the number of columns.
* @param axesPlacement the placement of axes.
*/
public Mosaic(int nrow, int ncol, Set axesPlacement) {
_nrow = nrow;
_ncol = ncol;
// Tiles.
_tiles = new Tile[nrow][ncol];
_tileList = new ArrayList();
for (int irow=0; irow();
if (axesPlacement.contains(AxesPlacement.TOP)) {
_axesTop = new TileAxis[ncol];
for (int icol=0; icol0) {
int xhsb = widthMinimumAxesLeft()+wtb;
int yhsb = ytile;
if (_axesBottom!=null)
yhsb += heightMinimumAxesBottom()-wab;
for (int icol=0; icol<_ncol; ++icol) {
int whsb = wcol[icol];
_hsb[icol].setBounds(xhsb,yhsb,whsb,hhsb);
xhsb += whsb+wtb+wts+wtb;
}
}
// Vertical scroll bars.
int wvsb = widthVScrollBars();
if (wvsb>0) {
int xvsb = xtile;
if (_axesRight!=null)
xvsb += widthMinimumAxesRight()-wab;
int yvsb = heightMinimumAxesTop()+wtb;
for (int irow=0; irow<_nrow; ++irow) {
int hvsb = hrow[irow];
_vsb[irow].setBounds(xvsb,yvsb,wvsb,hvsb);
yvsb += hvsb+wtb+wts+wtb;
}
}
}
public void paintToRect(Graphics2D g2d, int x, int y, int w, int h) {
g2d = createGraphics(g2d,x,y,w,h);
// Scale factors for width and height.
double ws = (double)w/(double)getWidth();
double hs = (double)h/(double)getHeight();
// Insets and strokes for tile and axes borders.
float lineWidth = getLineWidth(g2d);
float wtb = lineWidth*(float)widthTileBorder();
float wab = lineWidth*(float)widthAxesBorder();
int itb = 1+(int)(wtb/2.0f);
int iab = 1+(int)(wab/2.0f);
BasicStroke stb = new BasicStroke(wtb);
BasicStroke sab = new BasicStroke(wab);
// Draw tile and tile axis children.
int nc = getComponentCount();
for (int ic=0; ic0.0f && ip instanceof Tile) {
Tile tile = (Tile)ip;
if (tile.countTiledViews()>0) {
g2d.setStroke(stb);
g2d.drawRect(xc-itb,yc-itb,wc+itb+itb-1,hc+itb+itb-1);
}
} else if (wab>0.0f && ip instanceof TileAxis) {
g2d.setStroke(sab);
g2d.drawRect(xc-iab,yc-iab,wc+iab+iab-1,hc+iab+iab-1);
}
}
}
g2d.dispose();
}
///////////////////////////////////////////////////////////////////////////
// protected
protected void paintComponent(Graphics g) {
super.paintComponent(g);
paintToRect((Graphics2D)g,0,0,getWidth(),getHeight());
}
///////////////////////////////////////////////////////////////////////////
// package
void alignProjectors(Tile tile) {
int jrow = tile.getRowIndex();
int jcol = tile.getColumnIndex();
Projector bhp = tile.getBestHorizontalProjector();
if (bhp!=null) {
bhp = new Projector(bhp);
for (int irow=0; irow<_nrow; ++irow) {
if (irow!=jrow)
bhp.merge(_tiles[irow][jcol].getBestHorizontalProjector());
}
}
Projector bvp = tile.getBestVerticalProjector();
if (bvp!=null) {
bvp = new Projector(bvp);
for (int icol=0; icol<_ncol; ++icol) {
if (icol!=jcol)
bvp.merge(_tiles[jrow][icol].getBestVerticalProjector());
}
}
// check to see if scales in adjacent tiles are set up right
boolean[] checkScales = checkTileScales(tile);
if(!checkScales[0] && bhp!=null)
bhp.setScale(AxisScale.LINEAR);
if(!checkScales[1] && bvp!=null)
bvp.setScale(AxisScale.LINEAR);
if (bhp!=null && bvp!=null) {
tile.setProjectors(bhp,bvp);
} else if (bhp!=null) {
tile.setHorizontalProjector(bhp);
} else if (bvp!=null) {
tile.setVerticalProjector(bvp);
}
for (int irow=0; irow<_nrow; ++irow) {
if (irow!=jrow && bhp!=null)
_tiles[irow][jcol].setHorizontalProjector(bhp);
}
for (int icol=0; icol<_ncol; ++icol) {
if (icol!=jcol && bvp!=null)
_tiles[jrow][icol].setVerticalProjector(bvp);
}
repaintAxis(_axesTop,jcol);
repaintAxis(_axesBottom,jcol);
repaintAxis(_axesLeft,jrow);
repaintAxis(_axesRight,jrow);
}
// check to see if the adjacent Tiles to tile have the appropriate
// matching axis scales
private boolean[] checkTileScales(final Tile tile) {
final int jrow = tile.getRowIndex();
final int jcol = tile.getColumnIndex();
final AxisScale hscale = tile.getHScale();
final AxisScale vscale = tile.getVScale();
boolean hcompat = true;
for (int irow=0; irow<_nrow; ++irow){
final Tile t = _tiles[irow][jcol];
hcompat &= t.getHScale()==hscale;
}
boolean vcompat = true;
for (int icol=0; icol<_ncol; ++icol){
final Tile t = _tiles[jrow][icol];
vcompat &= t.getVScale()==vscale;
}
return new boolean[] { hcompat, vcompat };
}
void setViewRect(Tile tile, DRectangle vr) {
int wvsb = widthVScrollBars();
int hhsb = heightHScrollBars();
double x = max(0.0,min(1.0,vr.x));
double y = max(0.0,min(1.0,vr.y));
double w = max(0.0,min(1.0-vr.x,vr.width));
double h = max(0.0,min(1.0-vr.y,vr.height));
DRectangle tr = new DRectangle(x,y,w,h);
tile.setViewRect(tr);
int jrow = tile.getRowIndex();
int jcol = tile.getColumnIndex();
for (int irow=0; irow<_nrow; ++irow) {
if (irow!=jrow) {
Tile ti = _tiles[irow][jcol];
DRectangle dr = ti.getViewRectangle();
dr.x = tr.x;
dr.width = tr.width;
ti.setViewRect(dr);
}
}
for (int icol=0; icol<_ncol; ++icol) {
if (icol!=jcol) {
Tile ti = _tiles[jrow][icol];
DRectangle dr = ti.getViewRectangle();
dr.y = tr.y;
dr.height = tr.height;
ti.setViewRect(dr);
}
}
repaintAxis(_axesTop,jcol);
repaintAxis(_axesBottom,jcol);
repaintAxis(_axesLeft,jrow);
repaintAxis(_axesRight,jrow);
_hsb[jcol].update();
_vsb[jrow].update();
if (wvsb!=widthVScrollBars() || hhsb!=heightHScrollBars())
revalidate();
}
int getHeightAxesTop() {
return heightMinimumAxesTop();
}
int getHeightAxesBottom() {
return heightMinimumAxesBottom();
}
int getWidthAxesLeft() {
return widthMinimumAxesLeft();
}
int getWidthAxesRight() {
return widthMinimumAxesRight();
}
///////////////////////////////////////////////////////////////////////////
// private
private int _nrow; // number of rows
private int _ncol; // number of columns
private Tile[][] _tiles; // array[nrow][ncol] of tiles
private TileAxis[] _axesTop; // array[ncol] of top axes; null, if none
private TileAxis[] _axesLeft; // array[nrow] of left axes; null, if none
private TileAxis[] _axesBottom; // array[ncol] of bottom axes; null, if none
private TileAxis[] _axesRight; // array[nrow] of right axes; null, if none
private ArrayList _tileList; // simple list of all tiles
private ArrayList _axisList; // simple list of all axes
private HScrollBar[] _hsb; // horizontal scroll bars
private VScrollBar[] _vsb; // vertical scroll bars
private int _wts = 2; // width of tile spacing, in pixels
private int[] _wm; // array[ncol] of width minimums
private int[] _we; // array[ncol] of width elastics
private int[] _hm; // array[nrow] of height minimums
private int[] _he; // array[nrow] of height elastics
private ModeManager _modeManager; // mode manager
private void repaintAxis(TileAxis[] axes, int index) {
if (axes!=null)
repaintAxis(axes[index]);
}
private void repaintAxis(TileAxis axis) {
axis.repaint();
axis.updateAxisTics();
}
private int widthAxesBorder() {
return 0; // currently hardwired
}
private int widthTileBorder() {
return 1; // currently hardwired
}
private int widthTileSpacing() {
return _wts;
}
private int widthFixed() {
int width = widthMinimumAxesLeft();
width += (_ncol-1)*widthTileSpacing();
width += 2*_ncol*widthTileBorder();
width += widthMinimumAxesRight();
width += widthVScrollBars();
return width;
}
private int widthMinimum() {
int width = widthMinimumAxesLeft();
for (int icol=0; icol<_ncol; ++icol)
width += widthMinimumColumn(icol);
width += widthMinimumAxesRight();
width += (_ncol-1)*widthTileSpacing();
width += widthMinimumVScrollBars();
return width;
}
private int widthMinimumColumn(int icol) {
int width = 0;
if (_axesTop!=null)
width = max(width,_axesTop[icol].getWidthMinimum());
width = max(width,widthMinimumTiles(icol));
if (_axesBottom!=null)
width = max(width,_axesBottom[icol].getWidthMinimum());
return width;
}
private int widthMinimumTiles(int icol) {
int width = widthTileBorder();
width += _wm[icol];
width += widthTileBorder();
return width;
}
private int widthMinimumAxesLeft() {
int width = 0;
if (_axesLeft!=null) {
for (int irow=0; irow<_nrow; ++irow)
width = max(width,_axesLeft[irow].getWidthMinimum());
width += 2*widthAxesBorder();
}
return width;
}
private int widthMinimumAxesRight() {
int width = 0;
if (_axesRight!=null) {
for (int irow=0; irow<_nrow; ++irow)
width = max(width,_axesRight[irow].getWidthMinimum());
width += 2*widthAxesBorder();
}
return width;
}
private int widthMinimumVScrollBars() {
//return _vsb[0].getMinimumSize().width;
return 0;
}
private int widthVScrollBars() {
for (int irow=0; irow<_nrow; ++irow) {
if (_vsb[irow].isVisible())
return _vsb[irow].getMinimumSize().width;
}
return 0;
}
private int heightFixed() {
int height = heightMinimumAxesTop();
height += (_nrow-1)*widthTileSpacing();
height += 2*_nrow*widthTileBorder();
height += heightMinimumAxesBottom();
height += heightHScrollBars();
return height;
}
private int heightMinimum() {
int height = heightMinimumAxesTop();
for (int irow=0; irow<_nrow; ++irow)
height += heightMinimumRow(irow);
height += heightMinimumAxesBottom();
height += (_nrow-1)*widthTileSpacing();
height += heightMinimumHScrollBars();
return height;
}
private int heightMinimumRow(int irow) {
int height = 0;
if (_axesLeft!=null)
height = max(height,_axesLeft[irow].getHeightMinimum());
height = max(height,heightMinimumTiles(irow));
if (_axesRight!=null)
height = max(height,_axesRight[irow].getHeightMinimum());
return height;
}
private int heightMinimumTiles(int irow) {
int height = widthTileBorder();
height += _hm[irow];
height += widthTileBorder();
return height;
}
private int heightMinimumAxesTop() {
int height = 0;
if (_axesTop!=null) {
for (int icol=0; icol<_ncol; ++icol)
height = max(height,_axesTop[icol].getHeightMinimum());
height += 2*widthAxesBorder();
}
return height;
}
private int heightMinimumAxesBottom() {
int height = 0;
if (_axesBottom!=null) {
for (int icol=0; icol<_ncol; ++icol)
height = max(height,_axesBottom[icol].getHeightMinimum());
height += 2*widthAxesBorder();
}
return height;
}
private int heightMinimumHScrollBars() {
//return _hsb[0].getMinimumSize().height;
return 0;
}
private int heightHScrollBars() {
for (int icol=0; icol<_ncol; ++icol) {
if (_hsb[icol].isVisible())
return _hsb[icol].getMinimumSize().height;
}
return 0;
}
private static final int SCROLL_MAX = 1000000000;
private static final double SCROLL_SCL = 1.0/SCROLL_MAX;
//private static final int HORIZONTAL = Adjustable.HORIZONTAL;
//private static final int VERTICAL = Adjustable.VERTICAL;
private class TileScrollBar extends JScrollBar {
private static final long serialVersionUID = 1L;
Tile tile;
TileScrollBar(int orientation, final Tile tile) {
super(orientation,0,SCROLL_MAX,0,SCROLL_MAX);
setVisible(false);
this.tile = tile;
addAdjustmentListener(new AdjustmentListener() {
public void adjustmentValueChanged(AdjustmentEvent ae) {
if (_settingInternal)
return;
DRectangle vr = tile.getViewRectangle();
if (getOrientation()==HORIZONTAL) {
vr.x = getV();
vr.width = getE();
} else {
vr.y = getV();
vr.height = getE();
}
tile.setViewRectangle(vr);
}
});
}
void setV(double v) {
_settingInternal = true;
setValue((int)(v*SCROLL_MAX+0.5));
_settingInternal = false;
}
void setE(double e) {
_settingInternal = true;
setVisibleAmount((int)(e*SCROLL_MAX+0.5));
setVisible(getVisibleAmount()