/* This file is part of the source code for 3D-XplorMath-J, Version 1.0 (January 2008).
* Copyright (c) 2008 The 3D-XplorMath Consortium (http://3d-xplormath.org).
* This source code is released under a BSD License, which allows redistribution
* in source and binary form, with or without modification, provided copyright
* and license information are included, and with no warranty or guarantee of
* any kind. For details, see http://3d-xplormath.org/j/source/BSDLicense.txt
*/
package vmm3d.conformalmap;
import java.awt.Color;
import org.freehep.graphics2d.VectorGraphics;
import java.awt.event.ActionEvent;
import java.awt.event.MouseEvent;
import java.awt.geom.Point2D;
import java.io.IOException;
import org.w3c.dom.Document;
import org.w3c.dom.Element;
import vmm3d.actions.AbstractActionVMM;
import vmm3d.actions.ActionList;
import vmm3d.actions.ActionRadioGroup;
import vmm3d.actions.ToggleAction;
import vmm3d.core.Animation;
import vmm3d.core.BasicMouseTask2D;
import vmm3d.core.Complex;
import vmm3d.core.Decoration;
import vmm3d.core.Display;
import vmm3d.core.Exhibit;
import vmm3d.core.I18n;
import vmm3d.core.IntegerParam;
import vmm3d.core.MouseTask;
import vmm3d.core.RealParamAnimateable;
import vmm3d.core.SaveAndRestore;
import vmm3d.core.ThreadedAnimation;
import vmm3d.core.TwoPointInput;
import vmm3d.core.Util;
import vmm3d.core.VMMSave;
import vmm3d.core.View;
import vmm3d.core3D.BasicMouseTask3D;
import vmm3d.core3D.Exhibit3D;
import vmm3d.core3D.Transform3D;
import vmm3d.core3D.Vector3D;
import vmm3d.core3D.View3D;
/**
* This class represents a map from a region in the complex plane to the
* complex plane. (Note: For most of the subclasses of this map, the map is
* conformal, but that is not in fact true in every case.)
* Note that a ConformalMap by default uses a mouse task that lets the
* user switch between a view of the domain and a view of the image by
* shift-right-clicking. Ordinarily, the image is shown. The domain is
* shown when the user shift-right-clicks until the mouse is released.
*/
abstract public class ConformalMap extends Exhibit3D {
/**
* Code for the identity function as one of the possible functions that can
* be precomposed or postcomposed with a map.
*/
public final static int IDENTITY = 0;
/**
* Code for the function 1/z as one of the possible functions that can
* be precomposed or postcomposed with a map.
*/
public final static int INVERSION = 1;
/**
* Code for the function (1-z)/(1+z) as one of the possible functions that can
* be precomposed or postcomposed with a map.
*/
public final static int FRACTIONAL = 2;
/**
* Code for the square root function as one of the possible functions that can
* be precomposed or postcomposed with a map.
*/
public final static int SQRROOT = 3;
/**
* Code for using a cartesian grid on the domain of the function.
*/
public final static int CARTESIAN = 0; // Note: the gridData array, below assumes value is 0; do not change.
/**
* Code for using a polar grid on the domain of the function.
*/
public final static int POLAR = 1; // Note: the gridData array, below assumes value is 1; do not change.
/**
* Code for using a conformal polar grid on the domain of the function.
*/
public final static int POLARCONFORMAL = 2; // Note: the gridData array, below assumes value is 2; do not change.
private double[][] gridData = new double[][] {
{ -1, 1, -1, 1, 20, 20 }, // umin, umax, vmin, vmax, ures, vres for CARTESIAN grid
{ 0, 1, 0, 2*Math.PI, 7, 42 }, // umin, umax, vmin, vmax, ures, vres for POLAR grid
{ -0.5, 0.5, 0, 2*Math.PI, 7, 42 } // umin, umax, vmin, vmax, ures, vres for POLARCONFORMAL grid
}; // See setGridType() method, where this array is used.
/**
* Minimum u-value for the domain of the function.
*/
protected RealParamAnimateable umin = new RealParamAnimateable("vmm3d.conformalmap.ConformalMap.umin", -1);
/**
* Maximum u-value for the domain of the function.
*/
protected RealParamAnimateable umax = new RealParamAnimateable("vmm3d.conformalmap.ConformalMap.umax", 1);
/**
* Minimum v-value for the domain of the function.
*/
protected RealParamAnimateable vmin = new RealParamAnimateable("vmm3d.conformalmap.ConformalMap.vmin", -1);
/**
* Maximum v-value for the domain of the function.
*/
protected RealParamAnimateable vmax = new RealParamAnimateable("vmm3d.conformalmap.ConformalMap.vmax", 1);
/**
* Number of grid lines in the grid in the u direction.
*/
protected IntegerParam ures = new IntegerParam("vmm3d.conformalmap.ConformalMap.ures", 20);
/**
* Number of grid lines in the grid in the v direction.
*/
protected IntegerParam vres = new IntegerParam("vmm3d.conformalmap.ConformalMap.vres", 20);
/**
* The actual resolutions in the u- and v- directions are obtained by multiplying the
* nominal resolutions give by the ures and vres parameters by this value. That is, there are
* this many points between each pair of neighboring grid lines.
*/
protected final static int POINTS_PER_INTERVAL = 4;
/**
* The number of points to be used for a ConformalMapFigure with a circle shape,
* when the user adds a circle to the exhibit.
* This is set here eqaul to the default from ConformalMapFigure, but can be reset
* in a subclass.
*/
protected int pointsOnCircleFigure = ConformalMapFigure.DEFAULT_POINTS_ON_CIRCLE;
/**
* The number of points to be used for a ConformalMapFigure that is a line,
* when the user adds a line to the exhibit.
* This is set here eqaul to the default from ConformalMapFigure, but can be reset
* in a subclass.
*/
protected int pointsOnLineFigure = ConformalMapFigure.DEFAULT_POINTS_ON_LINE;
/**
* The number of points to be used for a ConformalMapFigure that is a line segment,
* when the user adds a line to the exhibit.
* This is set here eqaul to the default from ConformalMapFigure, but can be reset
* in a subclass.
*/
protected int pointsOnLineSegmentFigure = ConformalMapFigure.DEFAULT_POINTS_ON_SEGMENT;
/**
* Stores all the points in the grid that covers the domain of the function.
* This is part of the basic drawing data. It is computed in the computeDrawData3D method
* and is used in the doDraw3D method.
*/
protected Complex[][]argumentGrid;
/**
* Stores the images of the points in the argumentGrid.
* This is part of the basic drawing data. It is computed in the computeDrawData3D method
* and is used in the doDraw3D method.
*/
protected Complex[][]valueGrid;
@VMMSave
private int preCompFunction =IDENTITY; // which function should be precomposed with the map.
@VMMSave
private int postCompFunction = IDENTITY; // which function should be postcomposed with the map.
@VMMSave
private int gridType = CARTESIAN; // which grid type should be used on the domain.
private ActionRadioGroup preCompSelect; // lets user select the function for precomposition
private ActionRadioGroup postCompSelect; // lets user select the function for postcomposition
protected ActionRadioGroup gridTypeSelect; // lets user select the type of grid to use on the domain
// (made "protected" so it can be disabled in Weierstrass_p.java)
private double[] defaultWindow2D = {-3,3,-3,3};
/**
* The function that is to be graphed, to
* be supplied by any concrete subclass.
*/
protected abstract Complex function(Complex argument);
public ConformalMap() {
setDefaultBackground(Color.BLACK);
addParameter(vres);
addParameter(ures);
addParameter(vmax);
addParameter(vmin);
addParameter(umax);
addParameter(umin);
vres.setMinimumValueForInput(1);
ures.setMinimumValueForInput(1);
setDefaultWindow(-1.5,1.5,-1.5,1.5);
setDefaultViewpoint(new Vector3D(10,-10,10) );
setUseFilmstripForMorphing(true);
setFramesForMorphing(25);
ActionRadioGroup group = new ActionRadioGroup() {
public void optionSelected(int selectedIndex) {
setPreCompFunction(selectedIndex);
}
};
group.addItem(I18n.tr("vmm3d.conformalmap.ConformalMap.id"));
group.addItem(I18n.tr("vmm3d.conformalmap.ConformalMap.inverse"));
group.addItem(I18n.tr("vmm3d.conformalmap.ConformalMap.fractlin"));
group.addItem(I18n.tr("vmm3d.conformalmap.ConformalMap.sqrt"));
group.setSelectedIndex(0);
preCompSelect = group;
group = new ActionRadioGroup() {
public void optionSelected(int selectedIndex) {
setPostCompFunction(selectedIndex);
}
};
group.addItem(I18n.tr("vmm3d.conformalmap.ConformalMap.id"));
group.addItem(I18n.tr("vmm3d.conformalmap.ConformalMap.inverse"));
group.addItem(I18n.tr("vmm3d.conformalmap.ConformalMap.fractlin"));
group.addItem(I18n.tr("vmm3d.conformalmap.ConformalMap.sqrt"));
group.setSelectedIndex(0);
postCompSelect = group;
group = new ActionRadioGroup() {
public void optionSelected(int selectedIndex) {
setGridType(selectedIndex);
}
};
group.addItem(I18n.tr("vmm3d.conformalmap.ConformalMap.cartesian"));
group.addItem(I18n.tr("vmm3d.conformalmap.ConformalMap.polar"));
group.addItem(I18n.tr("vmm3d.conformalmap.ConformalMap.polarconformal"));
group.setSelectedIndex(0);
gridTypeSelect = group;
}
/**
* Gets the code number for the function that is postcomposed with the basic map.
* @see #setPostCompFunction(int)
*/
public int getPostCompFunction() {
return postCompFunction;
}
/**
* Sets the function that is postcomposed with the basic map. The value should be one
* of the constants IDENTITY, INVERSION, FRACTIONAL, or SQRROOT. The default is IDENTITY.
* (This property is managed by an ActionRadioGroup, although it could also be set directly.)
*/
public void setPostCompFunction(int postCompFunction) {
if (this.postCompFunction != postCompFunction) {
this.postCompFunction = postCompFunction;
postCompSelect.setSelectedIndex(postCompFunction);
forceRedraw();
}
}
/**
* Gets the code number for the function that is precomposed with the basic map.
* @see #setPreCompFunction(int)
*/
public int getPreCompFunction() {
return preCompFunction;
}
/**
* Sets the function that is precomposed with the basic map. The value should be one
* of the constants IDENTITY, INVERSION, FRACTIONAL, or SQRROOT. The default is IDENTITY.
* (This property is managed by an ActionRadioGroup, although it could also be set directly.)
*/
public void setPreCompFunction(int preCompFunction) {
if (this.preCompFunction != preCompFunction) {
this.preCompFunction = preCompFunction;
preCompSelect.setSelectedIndex(preCompFunction);
forceRedraw();
}
}
/**
* Gets the code number for the type of grid that is used on the domain of the function.
* @see #setGridType(int)
*/
public int getGridType() {
return gridType;
}
/**
* Sets the type of grid that is used on the domain of the map. The value should be one of
* the constants CARTESIAN, POLAR, or POLARCONFORMAL; other values are ignored. The default is CARTESIAN.
* (This property is managed by an ActionRadioGroup, although it could also be set directly.)
* This method changes the values of umin, umax, vmin, vmax, ures, and vres to match the new grid type.
* Separate values for each grid type are stored for these parameters. When this method is used
* to change the grid type, the values of the paramters are set to the values saved for that
* grid type.
*/
public void setGridType(int gridType) {
if (gridType != CARTESIAN && gridType != POLAR && gridType != POLARCONFORMAL)
return;
if (this.gridType != gridType) {
// swap values of umin, umax, vmin, vmax, ures, vres for current grid type into gridData array
gridData[this.gridType][0] = umin.getValue();
gridData[this.gridType][1] = umax.getValue();
gridData[this.gridType][2] = vmin.getValue();
gridData[this.gridType][3] = vmax.getValue();
gridData[this.gridType][4] = ures.getValue();
gridData[this.gridType][5] = vres.getValue();
this.gridType = gridType;
// swap values for new grid type out of gridData array into umin, umax, vmin, vmax, ures, vres
umin.reset(gridData[this.gridType][0]);
umax.reset(gridData[this.gridType][1]);
vmin.reset(gridData[this.gridType][2]);
vmax.reset(gridData[this.gridType][3]);
ures.reset((int)gridData[this.gridType][4]);
vres.reset((int)gridData[this.gridType][5]);
gridTypeSelect.setSelectedIndex(gridType);
forceRedraw();
}
}
/**
* For use in a subclass where the gridType has to be modified without
* changing the values of umin, umax, vmin, vmax. The sets the value
* of the gridType property variable without making any other changes.
* It does change the setting of {@link #gridTypeSelect} to match the
* new grid type.
* (This was introduced specifically for use in {@link UserConformalMap}.
*/
protected void resetGridType(int gridType) {
this.gridType = gridType;
gridTypeSelect.setSelectedIndex(gridType);
}
/**
* Computes the output value for a given input argument, taking into
* account the precomposed and postcomposed functions. This is the
* function that is actually displayed.
*/
protected Complex composedFunction(Complex z){
Complex w=null;
switch (preCompFunction){
case IDENTITY:
w =z;
break;
case INVERSION:
w=z.inverse();
break;
case FRACTIONAL:
w = z.minus(1).dividedBy(z.plus(1)).times(-1);
break;
case SQRROOT:
w=z.power(0.5);
break;
}
z = function(w);
switch (postCompFunction){
case IDENTITY:
w =z;
break;
case INVERSION:
w=z.inverse();
break;
case FRACTIONAL:
w = z.minus(1).dividedBy(z.plus(1)).times(-1);
break;
case SQRROOT:
w=z.power(0.5);
break;
}
return w;
}
/**
* Returns an array of four numbers representing the default 2D window.
* @see #setDefaultWindow2D(double[])
*/
public double[] getDefaultWindow2D() {
return defaultWindow2D;
}
/**
* Sets the default 2D window for this exhibit. This is the window that is used
* when the view is in 2D mode, that is, when the domain is displayed or when the
* image is displayed in the complex plane. It is not used when the image is
* displayed on the Riemann Sphere.
* @param defaultWindow2D an array containing the default window, xmin, xmax, ymin, and ymax.
* If this parameter is null, nothing is done. If the array has fewer than four elements, an
* IllegalArgumentException is thrown.
*/
public void setDefaultWindow2D(double[] defaultWindow2D) {
if (defaultWindow2D != null) {
if (defaultWindow2D.length < 4)
throw new IllegalArgumentException("Internal Error: Array is too short.");
this.defaultWindow2D = defaultWindow2D;
}
}
/**
* Sets the default 2D window for this exhibit. This is the window that is used
* when the view is in 2D mode, that is, when the domain is displayed or when the
* image is displayed in the complex plane. It is not used when the image is
* displayed on the Riemann Sphere. This convenience method just calls
* {@link #setDefaultWindow2D(double[])}.
*/
public void setDefaultWindow2D(double xmin, double xmax, double ymin,double ymax) {
setDefaultWindow2D(new double[] { xmin, xmax, ymin, ymax });
}
/**
* Finds the input grid point corresponding to (u,v). For a Cartesian grid, this
* is just (u,v) itself. For a polar grid, it is (u*cos(v),u*sin(v)). For a
* polar conformal grid, it is (exp(u)*cos(v),exp(u)*sin(v)) which is the same
* as exp(u+iv).
*/
protected Complex gridMap(double u,double v){
Complex w = new Complex(u,v);
switch (gridType){
case CARTESIAN:
break;
case POLAR:
w.re=u*Math.cos(v);
w.im=u*Math.sin(v);
break;
case POLARCONFORMAL:
w= w.exponential();
break;
}
return w;
}
protected void computeDrawData3D(View3D view, boolean exhibitNeedsRedraw, Transform3D previousTransform3D, Transform3D newTransform3D) {
if (exhibitNeedsRedraw) {
int uCount = ures.getValue() * POINTS_PER_INTERVAL;
int vCount = vres.getValue() * POINTS_PER_INTERVAL;
valueGrid = new Complex[uCount+1][vCount+1];
argumentGrid = new Complex[uCount+1][vCount+1];
double uStart = umin.getValue();
double vStart = vmin.getValue();
double uEnd = umax.getValue();
double vEnd = vmax.getValue();
double du = (uEnd - uStart) / uCount;
double dv = (vEnd - vStart) / vCount;
for (int i = 0; i <= uCount ; i++) {
for (int j = 0; j <= vCount; j++) {
argumentGrid[i][j] = gridMap(uStart + i*du,vStart + j*dv);
valueGrid[i][j] = composedFunction(argumentGrid[i][j]);
}
}
}
}
protected void doDraw3D(VectorGraphics g, View3D view, Transform3D transform) {
int uCount = ures.getValue() * POINTS_PER_INTERVAL;
int vCount = vres.getValue() * POINTS_PER_INTERVAL;
if ( !( view instanceof ConformalMapView) || ((ConformalMapView)view).getDrawValueGrid() )
if (!view.getEnableThreeD()){
drawGrid(g,view,valueGrid,uCount,vCount);
}
else{
drawGrid3D(g,view,valueGrid,uCount,vCount);
}
else
drawGrid(g,view,argumentGrid,uCount,vCount);
}
private void drawGrid(VectorGraphics g, View3D view, Complex[][] grid, int uCount, int vCount) {
boolean whiteBackground = Color.WHITE.equals(view.getBackground());
boolean useColor = true;
int uHilite = -1;
int vHilite = -1;
Color saveColor = view.getColor();
if (view instanceof ConformalMapView) {
useColor = ((ConformalMapView)view).getUseColor();
uHilite = ((ConformalMapView)view).getUHilite();
vHilite = ((ConformalMapView)view).getVHilite();
}
boolean useHilite = uHilite >= 0 || vHilite >= 0;
for (int i = 0; i <= uCount; i = i + POINTS_PER_INTERVAL) {
Point2D[] curve = new Point2D[vCount + 1];
boolean hilited = uHilite == i;
Color c = null;
if (useColor) {
if ( !(useHilite && hilited) ) {
int brightness = useHilite && !whiteBackground? 175 : 255;
c = new Color(brightness, (int)(brightness * ((double)(uCount-i)/uCount)), 0 );
}
}
else {
if (useHilite && hilited)
c = Color.RED;
}
view.setColor(c);
for (int j = 0; j <= vCount; j++)
curve[j] = new Point2D.Double(grid[i][j].re, grid[i][j].im);
if (useHilite && hilited) {
view.setStrokeSizeMultiplier(2);
view.drawCurve(curve);
view.setStrokeSizeMultiplier(1);
}
else
view.drawCurve(curve);
}
for (int i = 0; i <= vCount; i = i + POINTS_PER_INTERVAL) {
Point2D[] curve = new Point2D[uCount + 1];
boolean hilited = vHilite == i;
Color c = null;
if (useColor) {
if ( !(useHilite && hilited)) {
int brightness = useHilite && !whiteBackground? 175 : 255;
c = new Color(brightness/3, (int)(brightness* ((double)(vCount-i)/vCount)), (int)(brightness * ((double)i/vCount)) );
}
}
else {
if (useHilite && hilited)
c = Color.RED;
}
view.setColor(c);
for (int j = 0; j <= uCount; j++)
curve[j] = new Point2D.Double(grid[j][i].re, grid[j][i].im);
if (useHilite && hilited) {
view.setStrokeSizeMultiplier(2);
view.drawCurve(curve);
view.setStrokeSizeMultiplier(1);
}
else
view.drawCurve(curve);
}
view.setColor(saveColor);
}
private void drawGrid3D(VectorGraphics g, View3D view, Complex[][] grid, int uCount, int vCount) {
boolean whiteBackground = Color.WHITE.equals(view.getBackground());
boolean useColor = true;
int uHilite = -1;
int vHilite = -1;
if (view instanceof ConformalMapView) {
useColor = ((ConformalMapView)view).getUseColor();
uHilite = ((ConformalMapView)view).getUHilite();
vHilite = ((ConformalMapView)view).getVHilite();
}
boolean useHilite = uHilite >= 0 || vHilite >= 0;
Color saveColor = view.getColor();
for (int i = 0; i <= uCount; i = i + POINTS_PER_INTERVAL) {
Vector3D[] curve = new Vector3D[vCount + 1];
boolean hilited = uHilite == i;
Color c = null;
if (useColor) {
if ( !(useHilite && hilited) ) {
int brightness = useHilite && !whiteBackground? 175 : 255;
c = new Color(brightness, (int)(brightness * ((double)(uCount-i)/uCount)), 0 );
}
}
else {
if (useHilite && hilited)
c = Color.RED;
}
view.setColor(c);
for (int j = 0; j <= vCount; j++) {
if (grid[i][j].abs2() > 1000000)
curve[j] = new Vector3D(0, 0, 1);
else {
double[] p = grid[i][j].stereographicProjection();
curve[j] = new Vector3D(p[0],p[1],p[2]);
}
}
if (useHilite && hilited) {
view.setStrokeSizeMultiplier(2);
view.drawCurve(curve);
view.setStrokeSizeMultiplier(1);
}
else
view.drawCurve(curve);
}
for (int i = 0; i <= vCount; i = i + POINTS_PER_INTERVAL) {
Vector3D[] curve = new Vector3D[uCount + 1];
boolean hilited = vHilite == i;
Color c = null;
if (useColor) {
if ( !(useHilite && hilited)) {
int brightness = useHilite && !whiteBackground? 175 : 255;
c = new Color(brightness/3, (int)(brightness* ((double)(vCount-i)/vCount)), (int)(brightness * ((double)i/vCount)) );
}
}
else {
if (useHilite && hilited)
c = Color.RED;
}
view.setColor(c);
for (int j = 0; j <= uCount; j++) {
if (grid[j][i].abs2() > 1000000)
curve[j] = new Vector3D(0, 0, 1);
else{
double[] p = grid[j][i].stereographicProjection();
curve[j] = new Vector3D(p[0],p[1],p[2]);
}
}
if (useHilite && hilited) {
view.setStrokeSizeMultiplier(2);
view.drawCurve(curve);
view.setStrokeSizeMultiplier(1);
}
else
view.drawCurve(curve);
}
view.setColor(saveColor);
}
/**
* Returns an animation that shows the domain grid for a short time, then switches
* to the image. (However, no create animation is shown in "stereograph" views,
* since it would involve changing the window size back and forth.)
*/
public Animation getCreateAnimation(View view) {
if ( ! (view instanceof ConformalMapView ) )
return null;
final ConformalMapView cmView = (ConformalMapView)view;
if (cmView.getViewStyle() != View3D.MONOCULAR_VIEW && cmView.getViewStyle() != View3D.RED_GREEN_STEREO_VIEW)
return null;
return new ThreadedAnimation() {
public void runAnimation() {
try {
int pauseTime = cmView.getShowBothArgAndValue()? 100 : 50;
int uCount = ures.getValue() * POINTS_PER_INTERVAL;
int vCount = vres.getValue() * POINTS_PER_INTERVAL;
if ( ! cmView.getShowBothArgAndValue() )
cmView.setDrawValueGrid(false);
pause(300);
for (int i = 0; i <= uCount; i += POINTS_PER_INTERVAL) {
cmView.setHilite(i,vCount+1);
pause(pauseTime);
}
cmView.setHilite(uCount + 1, vCount+1);
pause(120);
for (int i = 0; i <= vCount; i += POINTS_PER_INTERVAL) {
cmView.setHilite(uCount+1,i);
pause(pauseTime);
}
if (cmView.getShowBothArgAndValue())
return;
cmView.setHilite(-1,-1);
pause(300);
cmView.setDrawValueGrid(true);
pause(300);
pauseTime = 80;
for (int i = 0; i <= uCount; i += POINTS_PER_INTERVAL) {
cmView.setHilite(i,vCount+1);
pause(pauseTime);
}
cmView.setHilite(uCount + 1, vCount+1);
pause(120);
for (int i = 0; i <= vCount; i += POINTS_PER_INTERVAL) {
cmView.setHilite(uCount+1,i);
pause(pauseTime);
}
}
finally {
cmView.setHilite(-1,-1);
cmView.setDrawValueGrid(true);
}
}
};
}
/**
* Overridden to return a view of type ConformalMapView, defined by a nested class in this class.
*/
public View getDefaultView() {
ConformalMapView view = new ConformalMapView();
view.setShowBothArgAndValue(true);
return view;
}
public ActionList getActionsForView(final View view) {
ActionList actions = super.getActionsForView(view);
if (! (view instanceof ConformalMapView))
return actions;
actions.add(null); // separator in menu
actions.add(new AbstractActionVMM(I18n.tr("vmm3d.conformalmap.ConformalMap.GetLineSegment")) {
public void actionPerformed(ActionEvent evt) {
new GetFigure((ConformalMapView)view,ConformalMapFigure.LINE_SEGMENT,TwoPointInput.DRAW_LINE);
}
});
actions.add(new AbstractActionVMM(I18n.tr("vmm3d.conformalmap.ConformalMap.GetLine")) {
public void actionPerformed(ActionEvent evt) {
new GetFigure((ConformalMapView)view,ConformalMapFigure.LINE,TwoPointInput.DRAW_LINE);
}
});
actions.add(new AbstractActionVMM(I18n.tr("vmm3d.conformalmap.ConformalMap.GetLCircle")) {
public void actionPerformed(ActionEvent evt) {
new GetFigure((ConformalMapView)view,ConformalMapFigure.CIRCLE,TwoPointInput.DRAW_CIRCLE_FROM_RADIUS);
}
});
actions.add(new AbstractActionVMM(I18n.tr("vmm3d.conformalmap.ConformalMap.RemoveFigures")) {
public void actionPerformed(ActionEvent evt) {
((ConformalMapView)view).removeFigures();
}
});
actions.add(null);
ActionList submenu = new ActionList(I18n.tr("vmm3d.conformalmap.ConformalMap.preComp"));
submenu.add(preCompSelect);
actions.add(submenu);
submenu = new ActionList(I18n.tr("vmm3d.conformalmap.ConformalMap.postComp"));
submenu.add(postCompSelect);
actions.add(submenu);
submenu = new ActionList(I18n.tr("vmm3d.conformalmap.ConformalMap.gridChoice"));
submenu.add(gridTypeSelect);
actions.add(submenu);
return actions;
}
public void addExtraXML(Document containingDocument, Element exhibitElement) {
super.addExtraXML(containingDocument, exhibitElement);
gridData[gridType][0] = umin.getValue(); // make sure that saved gridData for current grid type agrees with u,v limits
gridData[gridType][1] = umax.getValue();
gridData[gridType][2] = vmin.getValue();
gridData[gridType][3] = vmax.getValue();
gridData[gridType][4] = ures.getValue();
gridData[gridType][5] = vres.getValue();
Element gridElement = containingDocument.createElement("gridLimits");
gridElement.setAttribute("cartesian", Util.toExternalString(gridData[0]));
gridElement.setAttribute("polar", Util.toExternalString(gridData[1]));
gridElement.setAttribute("polarconformal", Util.toExternalString(gridData[2]));
exhibitElement.appendChild(gridElement);
}
public void readExtraXML(Element exhibitInfo) throws IOException {
super.readExtraXML(exhibitInfo);
Element gridElement = SaveAndRestore.getChildElement(exhibitInfo, "gridLimits");
if (gridElement == null)
return;
String s;
s = gridElement.getAttribute("cartesian").trim();
if (s.length() == 0)
throw new IOException(I18n.tr("vmm3d.conformalmap.ConformalMap.MissingAttributeError","cartesian"));
double[] cartesian = (double[])Util.externalStringToValue(s, double[].class);
if (cartesian.length != 6)
throw new IOException(I18n.tr("vmm3d.conformalmap.ConformalMap.IncorrectArrayLength","cartesian"));
s = gridElement.getAttribute("polar").trim();
if (s.length() == 0)
throw new IOException(I18n.tr("vmm3d.conformalmap.ConformalMap.MissingAttributeError","polar"));
double[] polar = (double[])Util.externalStringToValue(s, double[].class);
if (polar.length != 6)
throw new IOException(I18n.tr("vmm3d.conformalmap.ConformalMap.IncorrectArrayLength","polar"));
s = gridElement.getAttribute("polarconformal").trim();
if (s.length() == 0)
throw new IOException(I18n.tr("vmm3d.conformalmap.ConformalMap.MissingAttributeError","polarconformal"));
double[] polarconformal = (double[])Util.externalStringToValue(s, double[].class);
if (polarconformal.length != 6)
throw new IOException(I18n.tr("vmm3d.conformalmap.ConformalMap.IncorrectArrayLength","polarconformal"));
gridData = new double[][] { cartesian, polar, polarconformal };
umin.reset(gridData[gridType][0]); // make sure u,v limikts agree with gridData for current grid type
umax.reset(gridData[gridType][1]);
vmin.reset(gridData[gridType][2]);
vmax.reset(gridData[gridType][3]);
ures.reset((int)gridData[gridType][4]);
vres.reset((int)gridData[gridType][5]);
}
/**
* Used as a replacement for BasicMouseTask2D. Responds to a Shift-right-click (or shift-command-click on Mac)
* by showing the domain grid of the map until the mouse button is released. A mouse task of
* this type is used in a ConformalMapView when 3D is disabled.
*/
private static class ConformalMapMouseTask2D extends BasicMouseTask2D {
private boolean commandShifted; // Is it this class that's handling the mouse operation?
public boolean doMouseDown(MouseEvent evt, Display display, View view, int width, int height) {
if (evt.isShiftDown() && evt.isMetaDown()) {
ConformalMapView cmView = (ConformalMapView)display.getView();
cmView.setDrawValueGrid(false);
cmView.forceRedraw();
commandShifted = true;
return true;
}
else {
commandShifted = false;
return super.doMouseDown(evt, display, view, width, height);
}
}
public void doMouseDrag(MouseEvent evt, Display display, View view, int width, int height) {
if ( ! commandShifted )
super.doMouseDrag(evt, display, view, width, height);
}
public void doMouseUp(MouseEvent evt, Display display, View view, int width, int height) {
if ( commandShifted ) {
ConformalMapView cmView = (ConformalMapView)display.getView();
cmView.setDrawValueGrid(true);
cmView.forceRedraw();
}
else
super.doMouseUp(evt, display, view, width, height);
}
}
/**
* Used as a replacement for BasicMouseTask3D. Responds to a Shift-right-click (or shift-command-click on Mac)
* by showing the domain grid of the map until the mouse button is released. A mouse task of
* this type is used in a ConformalMapView when 3D is enabled.
*/
private static class ConformalMapMouseTask3D extends BasicMouseTask3D {
private boolean commandShifted; // Is it this class that's handling the mouse operation?
public boolean doMouseDown(MouseEvent evt, Display display, View view, int width, int height) {
if (evt.isShiftDown() && evt.isMetaDown()) {
ConformalMapView cmView = (ConformalMapView)display.getView();
cmView.setDrawValueGrid(false);
cmView.forceRedraw();
commandShifted = true;
return true;
}
else {
commandShifted = false;
return super.doMouseDown(evt, display, view, width, height);
}
}
public void doMouseDrag(MouseEvent evt, Display display, View view, int width, int height) {
if ( ! commandShifted )
super.doMouseDrag(evt, display, view, width, height);
}
public void doMouseUp(MouseEvent evt, Display display,View view, int width, int height) {
if ( commandShifted ) {
ConformalMapView cmView = (ConformalMapView)display.getView();
cmView.setDrawValueGrid(true);
cmView.drawValueGrid = true;
cmView.forceRedraw();
}
else
super.doMouseUp(evt, display, view, width, height);
}
}
/**
* A one-shot mouse task that is used to add a ConformalMapFigure to a conformal map.
*/
private class GetFigure extends TwoPointInput {
boolean canceled = true;
int shape;
int pointCount;
ConformalMapView mainView;
boolean forAuxiliaryView;
GetFigure(ConformalMapView view, int shape, int whatToDraw) {
mainView = view;
view.getDisplay().stopAnimation();
view.use3DToggle.setEnabled(false);
this.shape = shape;
pointCount = shape == ConformalMapFigure.CIRCLE ? pointsOnCircleFigure :
shape == ConformalMapFigure.LINE ? pointsOnLineFigure : pointsOnLineSegmentFigure;
setFigureToDraw(whatToDraw);
setDrawColor(view.getForeground());
if (view.getShowBothArgAndValue()) {
view.getDisplay().installAuxiliaryOneShotMouseTask(this);
forAuxiliaryView = true;
}
else {
view.setDrawValueGrid(false);
view.getDisplay().installOneShotMouseTask(this);
forAuxiliaryView = false;
}
}
protected void gotPoints(Display display, View viewWhereDragged, int startX, int startY, int endX, int endY) {
canceled = false;
Point2D p1, p2;
p1 = new Point2D.Double(startX,startY);
p2 = new Point2D.Double(endX,endY);
viewWhereDragged.getTransform().viewportToWindow(p1);
viewWhereDragged.getTransform().viewportToWindow(p2);
ConformalMapFigure dec = new ConformalMapFigure(p1,p2,shape,pointCount);
if (shape == ConformalMapFigure.CIRCLE)
dec.setColor(Color.GREEN);
else
dec.setColor(Color.RED);
mainView.addDecoration(dec);
if ( ! forAuxiliaryView ) {
display.installAnimation( new ThreadedAnimation() {
protected void runAnimation() {
try {
pause(1000);
}
finally {
mainView.setDrawValueGrid(true);
}
}
});
}
}
public void finish(Display display, View view) {
if (canceled && ! forAuxiliaryView)
mainView.setDrawValueGrid(true);
mainView.use3DToggle.setEnabled(true);
}
public String getStatusText() {
return I18n.tr("vmm3d.conformalmap.ConformalMap.inputFigurePrompt");
}
}
//------------ The rest of this file defines the ConformalMapView class --------------
/**
* A class that defines the default view for a ConformalMap. This subclass of View3D uses
* the enableThreeD property of View3D -- that property was introduced to make it possible to
* switch back and forth between 2D and 3D, which is something that is needed for conformal maps
* (and possibly nowhere else). See {@link View3D#setEnableThreeD(boolean)}.
*/
public static class ConformalMapView extends View3D {
@VMMSave private boolean use3D;
@VMMSave private boolean useColor = true;
private boolean drawValueGrid = true;
private int uHilite = -1; // For create animation -- if >= 0, tells which u-curve to highlight
private int vHilite = -1; // For create animation -- if >= 0, tells which v-curve to highlight
@VMMSave private boolean showBothArgAndValue;
private ConformalMapView argView; // for when the display is split, while showing both domain and image
private boolean isArgView; // set to true if this is the argView for a main ConformalMapView
private ToggleAction useColorToggle = new ToggleAction(I18n.tr("vmm3d.conformalmap.ConformalMap.ToggleUseColor"), true) {
public void actionPerformed(ActionEvent evt) {
setUseColor( !useColor );
}
};
private ToggleAction showArgAndValueToggle = new ToggleAction(I18n.tr("vmm3d.conformalmap.ConformalMap.ToggleShowArgAndValue"), false) {
public void actionPerformed(ActionEvent evt) {
if (getDisplay() != null)
getDisplay().stopAnimation();
setShowBothArgAndValue( getState() );
}
};
protected ToggleAction use3DToggle = new ToggleAction(I18n.tr("vmm3d.conformalmap.ConformalMap.ToggleUse3D"), false) {
public void actionPerformed(ActionEvent evt) {
if (getDisplay() != null)
getDisplay().stopAnimation();
setUse3D( use3DToggle.getState() );
}
};
public ConformalMapView() {
setAntialiased(true);
setEnableThreeD(false);
setShowAxes(true);
}
public void removeFigures() {
Decoration[] decs = getDecorations();
for (int i = decs.length-1; i >= 0; i--) {
if (decs[i] instanceof ConformalMapFigure)
removeDecoration(decs[i]);
}
if (argView != null)
argView.removeFigures();
}
public void setExhibit(Exhibit exhibit) {
if (exhibit == getExhibit())
return;
super.setExhibit(exhibit);
if (exhibit != null && exhibit instanceof ConformalMap) {
ConformalMap c = (ConformalMap)exhibit;
double[] d = c.getDefaultWindow2D();
setWindowForUseWhileThreeDDisabled(d[0], d[1], d[2], d[3]);
if (showBothArgAndValue)
setUpArgView();
}
}
/**
* Tells whether grid lines are drawn in colors.
*/
public boolean getUseColor() {
return useColor;
}
/**
* Set whether to use a range of colors for the grid lines. The alternative is to draw them
* all using the view's foreground color. (This property is managed by a a checkbox.)
*/
public void setUseColor(boolean useColor) {
if (useColor == this.useColor)
return;
useColorToggle.setState(useColor); // make sure useColor property and ToggleAction are in sync
forceRedraw();
this.useColor = useColor;
if (argView != null) {
argView.useColor = useColor;
argView.forceRedraw();
}
}
/**
* Tells whether the value grid (the image of the function) is currently displayed, as opposed
* to the domain grid. (Note that the set method for this property if private.)
*/
public synchronized boolean getDrawValueGrid() {
return drawValueGrid;
}
private synchronized void setDrawValueGrid(boolean drawValues) {
if (drawValues != drawValueGrid) {
drawValueGrid = drawValues;
if (use3D)
setEnableThreeD(drawValueGrid);
forceRedraw();
}
}
/**
* Overridden to return a mouse task that lets the user switch between the view of the
* domain grid and the view of the value grid by shift-right-clicking (or shift-command-clicking
* on a Macintosh).
*/
public MouseTask getDefaultMouseTask() {
if (isArgView) {
return new BasicMouseTask2D();
}
else if (showBothArgAndValue) {
if (use3D)
return new BasicMouseTask3D();
else
return new BasicMouseTask2D();
}
else {
if (use3D)
return new ConformalMapMouseTask3D();
else
return new ConformalMapMouseTask2D();
}
}
public ActionList getActions() {
ActionList actions = super.getActions();
actions.add(showArgAndValueToggle);
actions.add(useColorToggle);
actions.add(use3DToggle);
return actions;
}
public void takeExhibit(View view, boolean shareTransform) {
super.takeExhibit(view,shareTransform);
if (shareTransform && (view instanceof ConformalMapView))
setUse3D( ((ConformalMapView)view).getUse3D());
}
public boolean getUse3D() {
return use3D;
}
/**
* Sets whether to enable 3D in the view; this method is really not meant to
* be called except by this class. This method is public only for use with
* SaveAndRestore.
*/
public void setUse3D(boolean use3D) {
if (this.use3D == use3D)
return;
this.use3D = use3D;
use3DToggle.setState(use3D);
setEnableThreeD(use3D);
if (getDisplay() != null)
getDisplay().installMouseTask(getDefaultMouseTask());
if (showBothArgAndValue)
viewStyleCommands.setEnabled(false);
}
synchronized private void setHilite(int uHilite, int vHilite) {
this.uHilite = uHilite;
this.vHilite = vHilite;
forceRedraw();
if (argView != null) {
argView.uHilite = uHilite;
argView.vHilite = vHilite;
argView.forceRedraw();
}
}
synchronized private int getUHilite() {
return uHilite;
}
synchronized private int getVHilite() {
return vHilite;
}
public boolean getShowBothArgAndValue() {
return showBothArgAndValue;
}
public void setShowBothArgAndValue(boolean showBothArgAndValue) {
if (this.showBothArgAndValue == showBothArgAndValue)
return;
this.showBothArgAndValue = showBothArgAndValue;
showArgAndValueToggle.setState(showBothArgAndValue);
if (showBothArgAndValue) {
argView = new ConformalMapView();
argView.isArgView = true;
setUpArgView();
if (getDisplay() != null) {
getDisplay().installMouseTask(getDefaultMouseTask());
getDisplay().installAuxiliaryView(this,argView);
}
}
else {
if (getDisplay() != null) {
getDisplay().installMouseTask(getDefaultMouseTask());
getDisplay().installAuxiliaryView(this, null);
}
argView = null;
if (getUse3D())
viewStyleCommands.setEnabled(true);
}
}
private void setUpArgView() {
if (argView != null) {
if (getUse3D())
setViewStyle(View3D.MONOCULAR_VIEW);
viewStyleCommands.setEnabled(false);
if (getExhibit() != null)
argView.takeExhibit(this, true);
argView.setUse3D(false);
argView.setDrawValueGrid(false);
argView.setBackground(getBackground());
argView.setShowAxes(getShowAxes());
argView.useColor = useColor;
for (Decoration d : getDecorations()) {
if (d instanceof ConformalMapFigure) {
ConformalMapFigure orig = (ConformalMapFigure)d;
ConformalMapFigure copy = new ConformalMapFigure(orig.getP1(),orig.getP2(),orig.getShape(),orig.getPointCount());
copy.setColor(orig.getColor());
argView.addDecoration(copy);
}
}
}
}
public void setDisplay(Display display) {
super.setDisplay(display);
if (display != null)
display.setStopAnimationsOnResize(false);
if (display != null && showBothArgAndValue) {
display.installAuxiliaryView(this, argView);
}
}
public void setBackground(Color c) {
super.setBackground(c);
if (argView != null)
argView.setBackground(c);
}
public void setShowAxes(boolean show) {
super.setShowAxes(show);
if (argView != null)
argView.setShowAxes(show);
}
public void addDecoration(Decoration d) {
super.addDecoration(d);
if (d instanceof ConformalMapFigure && argView != null) {
ConformalMapFigure orig = (ConformalMapFigure)d;
ConformalMapFigure copy = new ConformalMapFigure(orig.getP1(),orig.getP2(),orig.getShape(),orig.getPointCount());
copy.setColor(orig.getColor());
argView.addDecoration(copy);
}
}
public void readExtraXML(Element viewInfo) throws IOException {
super.readExtraXML(viewInfo);
if (showBothArgAndValue) {
argView.setTransform( getEnableThreeD()? getSavedAuxiliaryTransformForEnableThreeD(): getTransform());
}
}
} // end class ConformalMapView
}
Ads help maintain this website.