/* Copyright 2005, University of Colorado */
/*
* CVS Info -
* Filename : $Source: /cvsroot/phet/faraday/src/edu/colorado/phet/faraday/model/DipoleMagnet.java,v $
* Branch : $Name: $
* Modified by : $Author: cmalley $
* Revision : $Revision: 1.14 $
* Date modified : $Date: 2005/04/22 16:54:39 $
*/
package edu.colorado.phet.faraday.model;
import java.awt.Rectangle;
import java.awt.Shape;
import java.awt.geom.AffineTransform;
import java.awt.geom.Point2D;
import java.awt.geom.Rectangle2D;
import edu.colorado.phet.faraday.util.Vector2D;
/**
* DipoleMagnet is an abstract magnet model based on a pair of dipoles.
* The magnetic field is modeled as a pair of dipoles.
* One dipole is located at each pole of the magnet.
* This model is applicable to magnets that are cylindrical in shape.
*
* See getStrength for details on the algorithm used.
*
* @author Chris Malley (cmalley@pixelzoom.com)
* @version $Revision: 1.14 $
*/
public abstract class DipoleMagnet extends AbstractMagnet {
//----------------------------------------------------------------------------
// Class data
//----------------------------------------------------------------------------
/*
* Arbitrary positive "fudge factor".
* This should be adjusted so that transitions between inside and outside
* the magnet don't result in abrupt changes in the magnetic field.
*/
private static final double FUDGE_FACTOR = 700.0;
// Magnetic field strength drops off by this power.
private static final double DEFAULT_DISTANCE_EXPONENT = 3.0;
// Number of pixels per 1 unit of distance.
private static final double PIXELS_PER_DISTANCE = 1.0;
//----------------------------------------------------------------------------
// Instance data
//----------------------------------------------------------------------------
private AffineTransform _transform;
private Rectangle _bounds;
private Point2D _northPoint, _southPoint;
private Point2D _normalizedPoint;
private Vector2D _northVector, _southVector;
private Rectangle2D _modelShape;
//----------------------------------------------------------------------------
// Constructors
//----------------------------------------------------------------------------
public DipoleMagnet() {
super();
_transform = new AffineTransform();
_bounds = new Rectangle();
_northPoint = new Point2D.Double();
_southPoint = new Point2D.Double();
_normalizedPoint = new Point2D.Double();
_northVector = new Vector2D();
_southVector = new Vector2D();
_modelShape = new Rectangle2D.Double();
}
//----------------------------------------------------------------------------
// Accessors
//----------------------------------------------------------------------------
/**
* Gets the shape that defines this model, in this case a rectangle.
*
* @return the shape
*/
public Shape getShape() {
return _modelShape;
}
//----------------------------------------------------------------------------
// FaradayObservable overrides
//----------------------------------------------------------------------------
/*
* Respond to changes that result from calling superclass methods,
* in this case changes to the magnet's size via super.setSize.
*/
protected void notifySelf() {
double width = PIXELS_PER_DISTANCE * getWidth();
double height = PIXELS_PER_DISTANCE * getHeight() ;
_modelShape.setFrame( -width/2, -height/2, width, height );
}
//----------------------------------------------------------------------------
// AbstractMagnet implementation
//----------------------------------------------------------------------------
/*
* Gets the B-field vector at a specified point.
*/
public Vector2D getStrength( Point2D p, Vector2D outputVector /* output */ ) {
return getStrength( p, outputVector, DEFAULT_DISTANCE_EXPONENT );
}
/*
* Gets the B-field vector at a specified point.
* The caller may specify an exponent that determines how the field strength
* decreases with distance from the magnet.
*
* Algorithm courtesy of Michael Dubson (dubson@spot.colorado.edu).
*
* Assumptions made by this algorithm:
*
* - the magnet's physical center is positioned at the magnet's location
*
- the magnet's width > height
*
* Terminology:
*
* - axes oriented with +X right, +Y up
*
- origin is the center of the coil, at (0,0)
*
- (x,y) is the point of interest where we are measuring the magnetic field
*
- h is the height of the magnet
*
- w is the width of the magnet
*
- L is the distance between the dipoles
*
- C is a fudge factor
*
- rN is the distance from the north dipole to (x,y)
*
- rS is the distance from the south dipole to (x,y)
*
- B is the field vector at (x,y) due to the entire magnet
*
- BN is the field vector at (x,y) due to the north dipole
*
- BS is the field vector at (x,y) due to the south dipole
*
- e is the exponent that specifies how the field decreases with distance (3 in reality)
*
*
* The dipole locations are:
*
* - north: w/2 - h/2
*
- south: -w/2 + h/2
*
*
* Inside the magnet:
*
* - Bx = magnet strength
*
- By = 0
*
*
* Outside the magnet:
*
* - BN = ( +C / rN^e ) [ ( x - L/2 ), y ]
*
- BS = ( -C / rS^e ) [ ( x + L/2 ), y ]
*
- B = BN + BS
*
*/
public Vector2D getStrength( Point2D p, Vector2D outputVector /* output */, double distanceExponent ) {
assert( p != null );
assert( getWidth() > getHeight() );
// Result will be copied into the output parameter, if it was provided.
Vector2D fieldVector = outputVector;
if ( fieldVector == null ) {
fieldVector = new Vector2D();
}
fieldVector.zero();
// All of our calculations are based a magnet located at the origin,
// with the north pole pointing down the X-axis.
// The point we received is based on the magnet's actual location and origin.
// So transform the point accordingly, adjusting for location and rotation of the magnet.
_transform.setToIdentity();
_transform.translate( -getX(), -getY() );
_transform.rotate( -getDirection(), getX(), getY() );
_transform.transform( p, _normalizedPoint /* output */ );
// Bounds that define the "inside" of the magnet.
_bounds.setRect( -(getWidth()/2), -(getHeight()/2), getWidth(), getHeight() );
// Choose the appropriate algorithm based on
// whether the point is inside or outside the magnet.
if ( _bounds.contains( _normalizedPoint ) ) {
getStrengthInside( _normalizedPoint, fieldVector /* output */ );
}
else {
getStrengthOutside( _normalizedPoint, fieldVector /* output */, distanceExponent );
}
// Adjust the field vector to match the magnet's direction.
fieldVector.rotate( getDirection() );
// Clamp magnitude to magnet strength.
double magnetStrength = super.getStrength();
double magnitude = fieldVector.getMagnitude();
if ( magnitude > magnetStrength ) {
fieldVector.setMagnitude( magnetStrength );
//System.out.println( "DipoleMagnet.getStrength - magnitude exceeds magnet strength by " + (magnitude - magnetStrength ) ); // DEBUG
}
return fieldVector;
}
/**
* Gets the magnetic field strength at a point inside the magnet.
* This is constant for all points inside the magnet.
*
* This algorithm makes the following assumptions:
*
* - the point is guaranteed to be inside the magnet
*
- the magnet's direction is 0.0 (north pole pointing down the positive X axis)
*
*
* @param p the point
* @param outputVector write the result into this vector
*/
private void getStrengthInside( Point2D p, Vector2D outputVector /* output */ ) {
assert( p != null );
assert( outputVector != null );
outputVector.setMagnitudeAngle( getStrength(), 0 );
}
/**
* Gets the magnetic field strength at a point outside the magnet.
* The magnitude is guaranteed to be >=0 and <= the magnet strength.
*
* This algorithm makes the following assumptions:
*
* - the magnet's location is (0,0)
*
- the magnet's direction is 0.0 (north pole pointing down the positive X axis)
*
- the magnet's physical center is at (0,0)
*
- the magnet's width > height
*
- the point is guaranteed to be outside the magnet
*
- the point has been transformed so that it is relative to above magnet assumptions
*
*
* @param p the point
* @param outputVector write the result into this vector
* @param distanceExponent exponent that determines how the field strength decreases
* with the distance from the magnet
*/
private void getStrengthOutside( Point2D p, Vector2D outputVector /* output */, double distanceExponent ) {
assert( p != null );
assert( outputVector != null );
assert( getWidth() > getHeight() );
// Magnet strength.
double magnetStrength = super.getStrength();
// Dipole locations.
_northPoint.setLocation( +getWidth()/2 - getHeight()/2, 0 ); // north dipole
_southPoint.setLocation( -getWidth()/2 + getHeight()/2, 0 ); // south dipole
// Distances.
double rN = _northPoint.distance( p ) / PIXELS_PER_DISTANCE; // north dipole to point
double rS = _southPoint.distance( p ) / PIXELS_PER_DISTANCE; // south dipole to point
double L = _southPoint.distance( _northPoint ); // dipole to dipole
// Fudge factor
double C = FUDGE_FACTOR * magnetStrength;
// North dipole field strength vector.
double cN = +( C / Math.pow( rN, distanceExponent ) ); // constant multiplier
double xN = cN * ( p.getX() - ( L / 2 ) ); // X component
double yN = cN * p.getY(); // Y component
_northVector.setXY( xN, yN ); // north dipole vector
// South dipole field strength vector.
double cS = -( C / Math.pow( rS, distanceExponent ) ); // constant multiplier
double xS = cS * ( p.getX() + ( L / 2 ) ); // X component
double yS = cS * p.getY(); // Y component
_southVector.setXY( xS, yS ); // south dipole vector
// Total field strength is the vector sum.
_northVector.add( _southVector );
// Copy the result into the output parameter.
outputVector.copy( _northVector );
}
}