/*
* 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.util.ArrayList;
import java.util.Iterator;
import org.opensourcephysics.controls.XMLControl;
import org.opensourcephysics.display3d.core.interaction.InteractionEvent;
import org.opensourcephysics.display3d.core.interaction.InteractionListener;
import org.opensourcephysics.numerics.Matrix3DTransformation;
import org.opensourcephysics.numerics.Quaternion;
import org.opensourcephysics.numerics.Transformation;
/**
*
* <p>Title: Element</p>
*
* <p>Interaction: An Element includes the following targets:</p>
* <ul>
* <li> TARGET_POSITION : Allows the element to be repositioned
* <li> TARGET_SIZE : Allows the element to be resized
* </ul>
* The actual position (and implementation) of the target depends on the
* element.
*
* <p>Copyright: Open Source Physics project</p>
*
* @author Francisco Esquembre
* @version June 2005
*/
public abstract class Element implements org.opensourcephysics.display3d.core.Element {
static final int SENSIBILITY = 5; // The accuracy for the mouse to find a point on the screen
// Configuration variables
private boolean visible = true; // is the object visible?
private double x = 0.0, y = 0.0, z = 0.0; // position of the element
private double sizeX = 1.0, sizeY = 1.0, sizeZ = 1.0; // the size of the element in each dimension
private String name = ""; //$NON-NLS-1$
private Transformation transformation = null;
private Style style = new Style(this);
private Group group = null;
private double factorX = 1.0;
private double factorY = 1.0;
private double factorZ = 1.0;
// Implementation variables
private boolean elementChanged = true, needsToProject = true;
private DrawingPanel3D panel;
// Variables for interaction
private ArrayList<InteractionListener> listeners = new ArrayList<InteractionListener>();
protected final InteractionTarget targetPosition = new InteractionTarget(this, TARGET_POSITION);
protected final InteractionTarget targetSize = new InteractionTarget(this, TARGET_SIZE);
/**
* Returns the DrawingPanel3D in which it (or its final ancestor group)
* is displayed.
* @return DrawingPanel3D
*/
final public DrawingPanel3D getDrawingPanel3D() {
Element el = this;
while(el.group!=null) {
el = el.group;
}
return el.panel;
}
/**
* To be used internally by DrawingPanel3D only! Sets the panel for this element.
* @param _panel DrawingPanel3D
*/
void setPanel(DrawingPanel3D _panel) {
this.panel = _panel;
this.factorX = _panel.getScaleFactorX();
this.factorY = _panel.getScaleFactorY();
this.factorZ = _panel.getScaleFactorZ();
elementChanged = true;
}
/**
* Returns the group to which the element belongs
* @return Group Returns null if it doesn't belong to a group
*/
final Group getGroup() {
return group;
}
/**
* Returns the top group to which the element belongs
* @return Group Returns null if it doesn't belong to a group
*
final Group getTopGroup() {
Group gr = group;
if(gr==null) {
return null;
}
while(gr.getGroup()!=null) {
gr = gr.getGroup();
}
return gr;
}
*/
/**
* To be used internally by Group only! Sets the group of this element.
* @param _group Group
*/
void setGroup(Group _group) {
this.group = _group;
elementChanged = true;
}
protected int getAxesMode() {
if(panel!=null) {
return panel.getAxesMode();
}
return org.opensourcephysics.display3d.core.DrawingPanel3D.MODE_XYZ;
}
// ----------------------------------------
// Name of the element
// ----------------------------------------
public void setName(String aName) {
this.name = aName;
}
final public String getName() {
return this.name;
}
// ----------------------------------------
// Position of the element
// ----------------------------------------
public void setX(double x) {
switch(getAxesMode()) {
case org.opensourcephysics.display3d.core.DrawingPanel3D.MODE_YXZ :
this.y = x*this.factorX;
break;
case org.opensourcephysics.display3d.core.DrawingPanel3D.MODE_YZX :
this.z = x*this.factorZ;
break;
case org.opensourcephysics.display3d.core.DrawingPanel3D.MODE_ZXY :
this.y = x*this.factorY;
break;
case org.opensourcephysics.display3d.core.DrawingPanel3D.MODE_ZYX :
this.z = x*this.factorZ;
break;
default :
this.x = x*this.factorX;
}
elementChanged = true;
}
final public double getX() {
return this.x/this.factorX;
}
public void setY(double y) {
switch(getAxesMode()) {
case org.opensourcephysics.display3d.core.DrawingPanel3D.MODE_XZY :
this.z = y*this.factorZ;
break;
case org.opensourcephysics.display3d.core.DrawingPanel3D.MODE_YXZ :
this.x = y*this.factorX;
break;
case org.opensourcephysics.display3d.core.DrawingPanel3D.MODE_YZX :
this.x = y*this.factorX;
break;
case org.opensourcephysics.display3d.core.DrawingPanel3D.MODE_ZXY :
this.z = y*this.factorZ;
break;
default :
this.y = y*this.factorY;
}
elementChanged = true;
}
final public double getY() {
return this.y/this.factorY;
}
public void setZ(double z) {
switch(getAxesMode()) {
case org.opensourcephysics.display3d.core.DrawingPanel3D.MODE_XZY :
this.y = z*this.factorY;
break;
case org.opensourcephysics.display3d.core.DrawingPanel3D.MODE_YZX :
this.y = z*this.factorY;
break;
case org.opensourcephysics.display3d.core.DrawingPanel3D.MODE_ZXY :
this.x = z*this.factorX;
break;
case org.opensourcephysics.display3d.core.DrawingPanel3D.MODE_ZYX :
this.x = z*this.factorX;
break;
default :
this.z = z*this.factorZ;
}
elementChanged = true;
}
final public double getZ() {
return this.z/this.factorZ;
}
public void setXYZ(double x, double y, double z) {
switch(getAxesMode()) {
case org.opensourcephysics.display3d.core.DrawingPanel3D.MODE_XZY :
this.x = x*this.factorX;
this.z = y*this.factorZ;
this.y = z*this.factorY;
break;
case org.opensourcephysics.display3d.core.DrawingPanel3D.MODE_YXZ :
this.y = x;
this.x = y;
this.z = z;
break;
case org.opensourcephysics.display3d.core.DrawingPanel3D.MODE_YZX :
this.z = x*this.factorZ;
this.x = y*this.factorX;
this.y = z*this.factorY;
break;
case org.opensourcephysics.display3d.core.DrawingPanel3D.MODE_ZXY :
this.y = x*this.factorY;
this.z = y*this.factorZ;
this.x = z*this.factorX;
break;
case org.opensourcephysics.display3d.core.DrawingPanel3D.MODE_ZYX :
this.z = x*this.factorZ;
this.y = y*this.factorY;
this.x = z*this.factorX;
break;
default :
this.x = x*this.factorX;
this.y = y*this.factorY;
this.z = z*this.factorZ;
}
elementChanged = true;
}
public void setXYZ(double[] pos) {
switch(getAxesMode()) {
case org.opensourcephysics.display3d.core.DrawingPanel3D.MODE_XZY :
this.x = pos[0]*this.factorX;
this.z = pos[1]*this.factorZ;
if(pos.length>=3) {
this.y = pos[2]*this.factorY;
}
break;
case org.opensourcephysics.display3d.core.DrawingPanel3D.MODE_YXZ :
this.y = pos[0]*this.factorY;
this.x = pos[1]*this.factorX;
if(pos.length>=3) {
this.z = pos[2]*this.factorZ;
}
break;
case org.opensourcephysics.display3d.core.DrawingPanel3D.MODE_YZX :
this.z = pos[0]*this.factorZ;
this.x = pos[1]*this.factorX;
if(pos.length>=3) {
this.y = pos[2]*this.factorY;
}
break;
case org.opensourcephysics.display3d.core.DrawingPanel3D.MODE_ZXY :
this.y = pos[0]*this.factorY;
this.z = pos[1]*this.factorZ;
if(pos.length>=3) {
this.x = pos[2]*this.factorX;
}
break;
case org.opensourcephysics.display3d.core.DrawingPanel3D.MODE_ZYX :
this.z = pos[0]*this.factorZ;
this.y = pos[1]*this.factorY;
if(pos.length>=3) {
this.x = pos[2]*this.factorX;
}
break;
default :
this.x = pos[0]*this.factorX;
this.y = pos[1]*this.factorY;
if(pos.length>=3) {
this.z = pos[2]*this.factorZ;
}
}
elementChanged = true;
}
/**
* Returns the extreme points of a box that contains the element.
* @param min double[] A previously allocated double[3] array that will hold
* the minimum point
* @param max double[] A previously allocated double[3] array that will hold
* the maximum point
*/
void getExtrema(double[] min, double[] max) {
min[0] = -0.5;
max[0] = 0.5;
min[1] = -0.5;
max[1] = 0.5;
min[2] = -0.5;
max[2] = 0.5;
sizeAndToSpaceFrame(min);
sizeAndToSpaceFrame(max);
}
// ----------------------------------------
// Size of the element
// ----------------------------------------
public void setSizeX(double sizeX) {
switch(getAxesMode()) {
case org.opensourcephysics.display3d.core.DrawingPanel3D.MODE_YXZ :
this.sizeY = sizeX*this.factorY;
break;
case org.opensourcephysics.display3d.core.DrawingPanel3D.MODE_YZX :
this.sizeZ = sizeX*this.factorZ;
break;
case org.opensourcephysics.display3d.core.DrawingPanel3D.MODE_ZXY :
this.sizeY = sizeX*this.factorY;
break;
case org.opensourcephysics.display3d.core.DrawingPanel3D.MODE_ZYX :
this.sizeZ = sizeX*this.factorZ;
break;
default :
this.sizeX = sizeX*this.factorY;
}
elementChanged = true;
}
final public double getSizeX() {
return this.sizeX;
}
public void setSizeY(double sizeY) {
switch(getAxesMode()) {
case org.opensourcephysics.display3d.core.DrawingPanel3D.MODE_XZY :
this.sizeZ = sizeY*this.factorZ;
break;
case org.opensourcephysics.display3d.core.DrawingPanel3D.MODE_YXZ :
this.sizeX = sizeY*this.factorX;
break;
case org.opensourcephysics.display3d.core.DrawingPanel3D.MODE_YZX :
this.sizeX = sizeY*this.factorX;
break;
case org.opensourcephysics.display3d.core.DrawingPanel3D.MODE_ZXY :
this.sizeZ = sizeY*this.factorZ;
break;
default :
this.sizeY = sizeY*this.factorY;
}
elementChanged = true;
}
final public double getSizeY() {
return this.sizeY;
}
public void setSizeZ(double sizeZ) {
switch(getAxesMode()) {
case org.opensourcephysics.display3d.core.DrawingPanel3D.MODE_XZY :
this.sizeY = sizeZ*this.factorY;
break;
case org.opensourcephysics.display3d.core.DrawingPanel3D.MODE_YZX :
this.sizeY = sizeZ*this.factorY;
break;
case org.opensourcephysics.display3d.core.DrawingPanel3D.MODE_ZXY :
this.sizeX = sizeZ*this.factorX;
break;
case org.opensourcephysics.display3d.core.DrawingPanel3D.MODE_ZYX :
this.sizeX = sizeZ*this.factorX;
break;
default :
this.sizeZ = sizeZ*this.factorZ;
}
elementChanged = true;
}
final public double getSizeZ() {
return this.sizeZ;
}
public void setSizeXYZ(double sizeX, double sizeY, double sizeZ) {
switch(getAxesMode()) {
case org.opensourcephysics.display3d.core.DrawingPanel3D.MODE_XZY :
this.sizeX = sizeX*this.factorX;
this.sizeZ = sizeY*this.factorY;
this.sizeY = sizeZ*this.factorZ;
break;
case org.opensourcephysics.display3d.core.DrawingPanel3D.MODE_YXZ :
this.sizeY = sizeX*this.factorY;
this.sizeX = sizeY*this.factorX;
this.sizeZ = sizeZ*this.factorZ;
break;
case org.opensourcephysics.display3d.core.DrawingPanel3D.MODE_YZX :
this.sizeZ = sizeX*this.factorZ;
this.sizeX = sizeY*this.factorX;
this.sizeY = sizeZ*this.factorY;
break;
case org.opensourcephysics.display3d.core.DrawingPanel3D.MODE_ZXY :
this.sizeY = sizeX*this.factorY;
this.sizeZ = sizeY*this.factorZ;
this.sizeX = sizeZ*this.factorX;
break;
case org.opensourcephysics.display3d.core.DrawingPanel3D.MODE_ZYX :
this.sizeZ = sizeX*this.factorZ;
this.sizeY = sizeY*this.factorY;
this.sizeX = sizeZ*this.factorX;
break;
default :
this.sizeX = sizeX*this.factorX;
this.sizeY = sizeY*this.factorY;
this.sizeZ = sizeZ*this.factorZ;
}
elementChanged = true;
}
public void setSizeXYZ(double[] size) {
switch(getAxesMode()) {
case org.opensourcephysics.display3d.core.DrawingPanel3D.MODE_XZY :
this.sizeX = size[0]*this.factorX;
this.sizeZ = size[1]*this.factorZ;
if(size.length>=3) {
this.sizeY = size[2]*this.factorY;
}
break;
case org.opensourcephysics.display3d.core.DrawingPanel3D.MODE_YXZ :
this.sizeY = size[0]*this.factorY;
this.sizeX = size[1]*this.factorX;
if(size.length>=3) {
this.sizeZ = size[2]*this.factorZ;
}
break;
case org.opensourcephysics.display3d.core.DrawingPanel3D.MODE_YZX :
this.sizeZ = size[0]*this.factorZ;
this.sizeX = size[1]*this.factorX;
if(size.length>=3) {
this.sizeY = size[2]*this.factorY;
}
break;
case org.opensourcephysics.display3d.core.DrawingPanel3D.MODE_ZXY :
this.sizeY = size[0]*this.factorY;
this.sizeZ = size[1]*this.factorZ;
if(size.length>=3) {
this.sizeX = size[2]*this.factorX;
}
break;
case org.opensourcephysics.display3d.core.DrawingPanel3D.MODE_ZYX :
this.sizeZ = size[0]*this.factorZ;
this.sizeY = size[1]*this.factorY;
if(size.length>=3) {
this.sizeX = size[2]*this.factorX;
}
break;
default :
this.sizeX = size[0]*this.factorX;
this.sizeY = size[1]*this.factorY;
if(size.length>=3) {
this.sizeZ = size[2]*this.factorZ;
}
}
elementChanged = true;
}
/**
* Returns the diagonal size of the element, i.e., Math.sqrt(sizeX*sizeX+sizeY*sizeY+sizeZ*sizeZ)
* @return double
*/
final double getDiagonalSize() {
return Math.sqrt(sizeX*sizeX+sizeY*sizeY+sizeZ*sizeZ);
}
/**
* Returns whether the element has changed significantly.
* This can be used by implementing classes to help improve performance.
* @return boolean
*/
final boolean hasChanged() {
Element el = this;
while(el!=null) {
if(el.elementChanged) {
return true;
}
el = el.group;
}
return false;
}
/**
* Whether this element has changed position, size, transformation or style
* since the last time it was displayed.
* @return boolean
*/
boolean getElementChanged() {
return elementChanged;
}
/**
* Tells the element whether it has a change that demands some
* kind of computation or, the other way round, someone took
* care of all possible changes.
* Typically used by subclasses when they change something or
* make all needed computations.
* @param change Whether the element has changed
*/
final void setElementChanged(boolean change) {
elementChanged = change;
}
// -------------------------------------
// Visibility and style
// -------------------------------------
public void setVisible(boolean _visible) {
this.visible = _visible;
}
final public boolean isVisible() {
return this.visible;
}
/**
* Returns the real visibility status of the element, which will be false if
* it belongs to an invisible group
* @return boolean
*/
final protected boolean isReallyVisible() {
Element el = this.group;
while(el!=null) {
if(!el.visible) {
return false;
}
el = el.group;
}
return this.visible;
}
final public org.opensourcephysics.display3d.core.Style getStyle() {
return this.style;
}
/**
* Gets the real Style. This is more convenient and improves performance (a liiittle bit)
* @return Style
*/
final Style getRealStyle() {
return this.style;
}
/**
* Used by Style to notify possible changes.
* @param styleThatChanged int
*/
final void styleChanged(int styleThatChanged) {
elementChanged = true;
}
// ----------------------------------------
// Transformation of the element
// ----------------------------------------
public Transformation getTransformation() {
if(transformation==null) {
return null;
}
return(Transformation) transformation.clone();
}
public void setTransformation(org.opensourcephysics.numerics.Transformation transformation) {
if(transformation==null) {
this.transformation = null;
} else {
this.transformation = (Transformation) transformation.clone();
if(transformation instanceof Quaternion) {
Quaternion q = (Quaternion) this.transformation;
double[] coordsBuffer = q.getCoordinates();
//Switch the Axis Mode
switch(getAxesMode()) {
case org.opensourcephysics.display3d.core.DrawingPanel3D.MODE_XYZ :
q.setCoordinates(coordsBuffer[0], coordsBuffer[1], coordsBuffer[2], coordsBuffer[3]);
break;
case org.opensourcephysics.display3d.core.DrawingPanel3D.MODE_XZY :
q.setCoordinates(coordsBuffer[0], coordsBuffer[1], coordsBuffer[3], coordsBuffer[2]);
break;
case org.opensourcephysics.display3d.core.DrawingPanel3D.MODE_YXZ :
q.setCoordinates(coordsBuffer[0], coordsBuffer[2], coordsBuffer[1], coordsBuffer[3]);
break;
case org.opensourcephysics.display3d.core.DrawingPanel3D.MODE_YZX :
q.setCoordinates(coordsBuffer[0], coordsBuffer[2], coordsBuffer[3], coordsBuffer[1]);
break;
case org.opensourcephysics.display3d.core.DrawingPanel3D.MODE_ZXY :
q.setCoordinates(coordsBuffer[0], coordsBuffer[3], coordsBuffer[1], coordsBuffer[2]);
break;
case org.opensourcephysics.display3d.core.DrawingPanel3D.MODE_ZYX :
q.setCoordinates(coordsBuffer[0], coordsBuffer[3], coordsBuffer[2], coordsBuffer[1]);
break;
default :
q.setCoordinates(coordsBuffer[0], coordsBuffer[1], coordsBuffer[2], coordsBuffer[3]);
}
this.transformation = new Quaternion(q);
} else {
Matrix3DTransformation mt = (Matrix3DTransformation) this.transformation;
double[] q = new double[4];
mt.toQuaternion(q);
Quaternion qf = new Quaternion(q);
//Switch the Axis Mode
switch(getAxesMode()) {
case org.opensourcephysics.display3d.core.DrawingPanel3D.MODE_XYZ :
qf.setCoordinates(q[0], q[1], q[2], q[3]);
break;
case org.opensourcephysics.display3d.core.DrawingPanel3D.MODE_XZY :
qf.setCoordinates(q[0], q[1], q[3], q[2]);
break;
case org.opensourcephysics.display3d.core.DrawingPanel3D.MODE_YXZ :
qf.setCoordinates(q[0], q[2], q[1], q[3]);
break;
case org.opensourcephysics.display3d.core.DrawingPanel3D.MODE_YZX :
qf.setCoordinates(q[0], q[2], q[3], q[1]);
break;
case org.opensourcephysics.display3d.core.DrawingPanel3D.MODE_ZXY :
qf.setCoordinates(q[0], q[3], q[1], q[2]);
break;
case org.opensourcephysics.display3d.core.DrawingPanel3D.MODE_ZYX :
qf.setCoordinates(q[0], q[3], q[2], q[1]);
break;
default :
qf.setCoordinates(q[0], q[1], q[2], q[3]);
}
this.transformation = new Quaternion(qf);
}
}
elementChanged = true;
}
public double[] toSpaceFrame(double[] vector) {
if(transformation!=null) {
transformation.direct(vector);
}
vector[0] += x;
vector[1] += y;
vector[2] += z;
Element el = group;
while(el!=null) {
vector[0] *= el.sizeX;
vector[1] *= el.sizeY;
vector[2] *= el.sizeZ;
if(el.transformation!=null) {
el.transformation.direct(vector);
}
vector[0] += el.x;
vector[1] += el.y;
vector[2] += el.z;
el = el.group;
}
return vector;
}
public double[] toBodyFrame(double[] vector) throws UnsupportedOperationException {
java.util.ArrayList<Element> elList = new java.util.ArrayList<Element>();
Element el = this;
do {
elList.add(el);
el = el.group;
} while(el!=null);
for(int i = elList.size()-1; i>=0; i--) { // Done in the reverse order
el = elList.get(i);
vector[0] -= el.x;
vector[1] -= el.y;
vector[2] -= el.z;
if(el.transformation!=null) {
el.transformation.inverse(vector);
}
if(el!=this) {
if(el.sizeX!=0.0) {
vector[0] /= el.sizeX;
}
if(el.sizeY!=0.0) {
vector[1] /= el.sizeY;
}
if(el.sizeZ!=0.0) {
vector[2] /= el.sizeZ;
}
}
}
return vector;
}
/**
* Translates a point of the standard (0,0,0) to (1,1,1) element
* to its real spatial coordinate. Thus, if the point has a coordinate of 1,
* the result will be the size of the element.
* @param vector the vector to be converted
*/
final void sizeAndToSpaceFrame(double[] vector) {
vector[0] *= sizeX;
vector[1] *= sizeY;
vector[2] *= sizeZ;
toSpaceFrame(vector);
}
// ----------------------------------------------------
// Needed by the drawing mechanism
// ----------------------------------------------------
/**
* Returns an array of Objects3D to sort according to its distance and draw.
*/
abstract Object3D[] getObjects3D();
/**
* Draws a given Object3D (indicated by its index).
*/
abstract void draw(java.awt.Graphics2D g, int index);
/**
* Sketches the drawable
*/
abstract void drawQuickly(java.awt.Graphics2D g);
/**
* Tells the element whether it should reproject its points because the panel
* has changed its projection parameters. Or, the other way round,
* if someone (typically methods in subclasses) took care of this already.
*/
void setNeedToProject(boolean _need) {
needsToProject = _need;
}
/**
* Whether the element needs to project
* @return boolean
* @see #setNeedToProject(boolean)
*/
final boolean needsToProject() {
return needsToProject;
}
// ---------------------------------
// Implementation of core.InteractionSource
// ---------------------------------
public org.opensourcephysics.display3d.core.interaction.InteractionTarget getInteractionTarget(int target) {
switch(target) {
case TARGET_POSITION :
return targetPosition;
case TARGET_SIZE :
return targetSize;
}
return null;
}
public void addInteractionListener(InteractionListener listener) {
if((listener==null)||listeners.contains(listener)) {
return;
}
listeners.add(listener);
}
public void removeInteractionListener(InteractionListener listener) {
listeners.remove(listener);
}
/**
* Invokes the interactionPerformed() methods on the registered
* interaction listeners.
* @param event InteractionEvent
*/
final void invokeActions(InteractionEvent event) {
Iterator<InteractionListener> it = listeners.iterator();
while(it.hasNext()) {
it.next().interactionPerformed(event);
}
}
// TODO : make this method abstract
/**
* Gets the target that is under the (x,y) position of the screen
* @param x int
* @param y int
* @return InteractionTarget
*/
protected InteractionTarget getTargetHit(int x, int y) {
return null;
}
/**
* Returns the body coordinates of the specified hotspot
* @return double[]
*/
protected double[] getHotSpotBodyCoordinates(InteractionTarget target) {
if(target==targetPosition) {
return new double[] {0, 0, 0};
}
if(target==targetSize) {
double[] c = new double[] {1, 1, 1};
if(sizeX==0) {
c[0] = 0.0;
}
if(sizeY==0) {
c[1] = 0.0;
}
if(sizeZ==0) {
c[2] = 0.0;
}
return c;
}
return null;
}
/**
* This method returns the coordinates of the given target.
* @param target InteractionTarget
* @return double[]
*/
final double[] getHotSpot(InteractionTarget target) {
double[] coordinates = getHotSpotBodyCoordinates(target);
if(coordinates!=null) {
sizeAndToSpaceFrame(coordinates);
}
return coordinates;
}
/**
* This method updates the position or size of the element
* according to the position of the 3D cursor during the interaction.
* Notice that, for targetSize, if any of the sizes of the element
* is zero, this dimension cannot be changed.
* @param target InteractionTarget The target interacted
* @param point double[] The position of the cursor during the interaction
*/
final void updateHotSpot(InteractionTarget target, double[] point) {
Element gr = this.group; //getTopGroup();
switch(target.getType()) {
case org.opensourcephysics.display3d.core.Element.TARGET_POSITION :
if(target.getAffectsGroup()&&(gr!=null)) { // Move the whole group
double[] origin = getHotSpot(target);
gr.setXYZ(gr.x+point[0]-origin[0], gr.y+point[1]-origin[1], gr.z+point[2]-origin[2]);
} else { // Move only the element
double[] coordinates = new double[] {point[0], point[1], point[2]};
groupInverseTransformations(coordinates);
double[] origin = getHotSpotBodyCoordinates(target);
origin[0] *= sizeX;
origin[1] *= sizeY;
origin[2] *= sizeZ;
if(transformation!=null) {
transformation.direct(origin);
}
setXYZ(coordinates[0]-origin[0], coordinates[1]-origin[1], coordinates[2]-origin[2]);
}
break;
case org.opensourcephysics.display3d.core.Element.TARGET_SIZE :
if(target.getAffectsGroup()&&(gr!=null)) { // Resize the whole group
double[] coordinates = new double[] {point[0], point[1], point[2]};
coordinates[0] -= gr.x;
coordinates[1] -= gr.y;
coordinates[2] -= gr.z;
if(gr.transformation!=null) {
gr.transformation.inverse(coordinates);
}
double[] origin = getHotSpotBodyCoordinates(target);
elementDirectTransformations(origin);
// If any of the dimensions is zero, a division by zero would occur.
// Not dividing is not enough.
if(origin[0]!=0) {
coordinates[0] /= origin[0];
} else {
coordinates[0] = gr.sizeX;
}
if(origin[1]!=0) {
coordinates[1] /= origin[1];
} else {
coordinates[1] = gr.sizeY;
}
if(origin[2]!=0) {
coordinates[2] /= origin[2];
} else {
coordinates[2] = gr.sizeZ;
}
gr.setSizeXYZ(coordinates);
} else { // Resize only the element
double[] coordinates = new double[] {point[0], point[1], point[2]};
groupInverseTransformations(coordinates);
coordinates[0] -= x;
coordinates[1] -= y;
coordinates[2] -= z;
if(transformation!=null) {
transformation.inverse(coordinates);
}
double[] origin = getHotSpotBodyCoordinates(target);
for(int i = 0; i<3; i++) {
if(origin[i]!=0) {
coordinates[i] /= origin[i];
}
}
setSizeXYZ(coordinates);
}
}
}
/**
* All the inverse transformations of toBodyFrame except that of the
* element itself
* @param vector double[]
* @throws UnsupportedOperationException
*/
private final void groupInverseTransformations(double[] vector) throws UnsupportedOperationException {
java.util.ArrayList<Element> elList = new java.util.ArrayList<Element>();
Element el = this.group;
while(el!=null) {
elList.add(el);
el = el.group;
}
for(int i = elList.size()-1; i>=0; i--) { // Done in the reverse order
el = elList.get(i);
vector[0] -= el.x;
vector[1] -= el.y;
vector[2] -= el.z;
if(el.transformation!=null) {
el.transformation.inverse(vector);
}
if(el.sizeX!=0.0) {
vector[0] /= el.sizeX;
}
if(el.sizeY!=0.0) {
vector[1] /= el.sizeY;
}
if(el.sizeZ!=0.0) {
vector[2] /= el.sizeZ;
}
}
}
/**
* All the direct transformations of sizeAndToSpaceFrame except that of the
* top group
* @param vector double[]
*/
private final void elementDirectTransformations(double[] vector) {
Element el = this;
do {
if(el.sizeX!=0) {
vector[0] *= el.sizeX;
}
if(el.sizeY!=0) {
vector[1] *= el.sizeY;
}
if(el.sizeZ!=0) {
vector[2] *= el.sizeZ;
}
if(el.transformation!=null) {
el.transformation.direct(vector);
}
vector[0] += el.x;
vector[1] += el.y;
vector[2] += el.z;
el = el.group;
} while((el!=null)&&(el.group!=null));
}
// ----------------------------------------------------
// XML loader
// ----------------------------------------------------
public void loadUnmutableObjects(XMLControl control) {
style = (Style) control.getObject("style"); //$NON-NLS-1$
style.setElement(this);
elementChanged = true;
}
}
/*
* 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
*/