edu.mines.jtk.mosaic.Projector Maven / Gradle / Ivy
Show all versions of edu-mines-jtk Show documentation
/****************************************************************************
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 static edu.mines.jtk.util.MathPlus.max;
import static edu.mines.jtk.util.MathPlus.min;
import static edu.mines.jtk.util.MathPlus.signum;
import edu.mines.jtk.util.ArrayMath;
import edu.mines.jtk.util.Check;
/**
* Converts (projects) world coordinates v to/from normalized coordinates u.
* The projection is a simple scale and translation, such that specified
* world coordinates (v0,v1) correspond to specified normalized coordinates
* (u0,u1).
*
* Specifically, a projector computes u = shift+scale*v, where scale =
* (u1-u0)/(v1-v0), and shift = u0-scale*v0. The projection exists only
* for v1 != v0. However, v0 and v1 are otherwise unconstrained. v0 may
* be greater than v1.
*
* The projection from normalized coordinates u to world coordinates v is
* simply the inverse, and this inverse exists only for u1 != u0.
*
* By definition, u0 is closest to normalized coordinate u=0, and u1 is
* closest to normalized coordinate u=1. These coordinates must satisfy
* the constraints 0.0 <= u0 < u1 <= 1.0.
*
* Typically, the coordinates (v0,v1) represent bounds in world coordinate
* space. Then, the gaps in normalized coordinate space [0,u0) and (u1,1]
* represent margins, extra space needed for graphic rendering. The amount
* of extra space required varies, depending on the graphics. Accounting for
* this varying amount of extra space is a complex but important aspect of
* aligning the coordinate systems of two or more graphics.
*
* Alignment is accomplished by simply rendering all graphics using
* the same projector. We obtain this shared projector by merging the
* preferred projectors of each graphic. A preferred projector is one
* that a graphic might use if it were the only one being rendered.
*
* We assume that each graphic has a preferred projector that indicates
* the world coordinate span [v0,v1] and margins [0,u0) and (u1,1] that
* it would prefer if it were the only graphic rendered. We then merge
* two projectors into one so that the merged projector contains the
* union of the two world coordinate spans and has adequate margins.
*
* A projector has a sign, which is the sign of v1-v0. Note that this sign
* is never ambiguous, because v1 never equals v0. When merging a projector
* B into into a projector A, we preserve the sign of projector A.
*
* @author Dave Hale, Colorado School of Mines
* @version 2005.01.01
*/
public class Projector {
/**
* Constructs a projector with specified v values, u0=0, and u1=1.
* The projector will have zero margins.
* @param v0 the v coordinate that corresponds to u coordinate 0;
* v0 != v1 is required.
* @param v1 the v coordinate that corresponds to u coordinate 1;
* v0 != v1 is required.
*/
public Projector(double v0, double v1) {
this(v0,v1,0.0,1.0,AxisScale.LINEAR);
}
/**
* Constructs a projector with specified v values and Scale,
* u0=0, and u1=1. The projector will have zero margins.
* @param v0 the v coordinate that corresponds to u coordinate 0;
* v0 != v1 is required.
* @param v1 the v coordinate that corresponds to u coordinate 1;
* v0 != v1 is required.
* @param scale the AxisScale type of this projector
*/
public Projector(double v0, double v1, AxisScale scale) {
this(v0,v1,0.0,1.0,scale);
}
/**
* Constructs a projector with specified v and u values. The
* parameters u0 and u1 determine the margins of the projector.
* @param v0 the v coordinate that corresponds to u coordinate 0;
* v0 != v1 is required.
* @param v1 the v coordinate that corresponds to u coordinate 1;
* v0 != v1 is required.
* @param u0 the u coordinate closest to normalized coordinate 0;
* 0.0 <= u0 < u1 is required.
* @param u1 the u coordinate closest to normalized coordinate 1;
* u0 < u1 <= 1.0 is required.
*/
public Projector(double v0, double v1, double u0, double u1) {
this(v0,v1,u0,u1,AxisScale.LINEAR);
}
/**
* Constructs a projector with specified v and u values. The
* parameters u0 and u1 determine the margins of the projector.
* The world coordinate v0 corresponds to normalized coordinate u0;
* the world coordinate v1 corresponds to normalized coordinate u1.
* @param v0 the v coordinate that corresponds to u coordinate u0;
* v0 != v1 is required.
* @param v1 the v coordinate that corresponds to u coordinate u1;
* v0 != v1 is required.
* @param u0 the u coordinate closest to normalized coordinate 0;
* 0.0 <= u0 < u1 is required.
* @param u1 the u coordinate closest to normalized coordinate 1;
* u0 < u1 <= 1.0 is required.
* @param s the AxisScale type of this projector
*/
public Projector(double v0, double v1, double u0, double u1, AxisScale s) {
Check.argument(0.0<=u0,"0.0 <= u0");
Check.argument(u0
* This method is typically used to account for the effects of merging
* two or more projectors. For example, after merging, parameters that
* are proportional to the sizes of margins [0,u0) or (u1,1] in the
* specified projector should be scaled by this ratio before being used
* with this projector.
* @param p a projector.
* @return the scale factor.
*/
public double getScaleRatio(Projector p) {
return _vscale/p._vscale;
}
@Override
public boolean equals(Object obj) {
if (this==obj)
return true;
if (obj==null || this.getClass()!=obj.getClass())
return false;
Projector that = (Projector)obj;
return this._u0==that._u0 &&
this._u1==that._u1 &&
this._v0==that._v0 &&
this._v1==that._v1 &&
this._scaleType == that._scaleType;
}
@Override
public int hashCode() {
long u0bits = Double.doubleToLongBits(_u0);
long u1bits = Double.doubleToLongBits(_u1);
long v0bits = Double.doubleToLongBits(_v0);
long v1bits = Double.doubleToLongBits(_v1);
return (int)(u0bits^(u0bits>>>32) ^
u1bits^(u1bits>>>32) ^
v0bits^(v0bits>>>32) ^
v1bits^(v1bits>>>32));
}
@Override
public String toString() {
return "Projector("+_v0+", "+_v1+", "+_u0+", "+_u1+", " + _scaleType +")";
}
/////////////////////////////////////////////////////////////////////////
// protected
/**
* Sets the scale type
* @param s new scale type
* @return the scale type.
*/
protected Projector setScale(AxisScale s) {
if(s.isLog() && !(_v0==0.0 && _v1==0.0))
Check.argument(_v0>0 && _v1>0,"LOG scale: v0<=0 or v1<=0");
_scaleType = s;
computeShiftsAndScales();
return this;
}
///////////////////////////////////////////////////////////////////////////
// private
private double _u0,_u1;
private double _v0,_v1;
private double _ushift,_uscale;
private double _vshift,_vscale;
private AxisScale _scaleType;
private void computeShiftsAndScales() {
if (_scaleType==AxisScale.LINEAR) {
_uscale = (_v1-_v0)/(_u1-_u0);
_ushift = _v0-_uscale*_u0;
_vscale = (_u1-_u0)/(_v1-_v0);
_vshift = _u0-_vscale*_v0;
} else if (_scaleType==AxisScale.LOG10) {
_vscale = (_u1-_u0)/(ArrayMath.log10(_v1)-ArrayMath.log10(_v0));
_uscale = (ArrayMath.log10(_v1)-ArrayMath.log10(_v0))/(_u1-_u0);
_ushift = ArrayMath.log10(_v0)-_uscale*_u0;
_vshift = _u0-_vscale*ArrayMath.log10(_v0);
}
}
}