public abstract class Axes extends DefinitionalObject implements IServiceProvider
This example shows how to create a new Axes class:
// Create a new type derived from Axes. This Axes represents the axes created by rotating
// a reference axes a specified angle about a specified spin vector. The vector and the angle
// are specified as a Vector and Scalar, respectively, so both can change with time.
public static class AngularOffsetAxes extends Axes {
public AngularOffsetAxes() {}
public AngularOffsetAxes(Axes referenceAxes, Vector spinVector, Scalar rotationAngle) {
m_referenceAxes = referenceAxes;
m_spinVector = spinVector;
m_rotationAngle = rotationAngle;
}
// The copy constructor, used to make a copy of the object. Copy all of the
// fields of the 'existingInstance' to the new object. Reference types should
// be passed through a call to updateReference so that the depth of the copy
// can be controlled by the user. See the documentation of the ICloneWithContext
// interface for more information.
protected AngularOffsetAxes(AngularOffsetAxes existingInstance, CopyContext context) {
super(existingInstance, context);
m_referenceAxes = context.updateReference(existingInstance.m_referenceAxes);
m_rotationAngle = context.updateReference(existingInstance.m_rotationAngle);
m_spinVector = context.updateReference(existingInstance.m_spinVector);
}
// This is called to make a copy of the object, which it does by calling the
// copy constructor above.
@Override
public Object clone(CopyContext context) {
return new AngularOffsetAxes(this, context);
}
// This method is only called by the isSameDefinition method in the base class to
// determine if two axes are equivalent. Derived classes MUST override this method and
// check all new fields introduced by the derived class for definitional equivalence. It
// is NOT necessary to check base class fields because the base class will already have
// done that.
@Override
protected final boolean checkForSameDefinition(Axes other) {
AngularOffsetAxes o = (other instanceof AngularOffsetAxes) ? (AngularOffsetAxes) other : null;
return o != null &&
areSameDefinition(m_referenceAxes, o.m_referenceAxes) &&
areSameDefinition(m_spinVector, o.m_spinVector) &&
areSameDefinition(m_rotationAngle, o.m_rotationAngle);
}
// Called to determine a hash code for the current configuration of this object.
// Derived classes MUST override this method and compute a hash code that combines:
// a unique hash code seed, the base implementation result, and the
// hash codes of all new fields introduced by the derived class which are used
// in the checkForSameDefinition method.
@Override
protected int computeCurrentDefinitionHashCode() {
return HashCode.combine(AngularOffsetAxes.class.hashCode(),
super.computeCurrentDefinitionHashCode(),
getDefinitionHashCode(m_referenceAxes),
getDefinitionHashCode(m_rotationAngle),
getDefinitionHashCode(m_spinVector));
}
// Called to enumerate all of the other objects on which this object depends. This
// allows clients to navigate the graph of objects related to a computation.
@Override
public void enumerateDependencies(DependencyEnumerator enumerator) {
super.enumerateDependencies(enumerator);
enumerator.enumerate(m_referenceAxes);
enumerator.enumerate(m_rotationAngle);
enumerator.enumerate(m_spinVector);
}
// The reference axes that this one is defined relative to.
public final Axes getReferenceAxes() {
return m_referenceAxes;
}
// The reference axes that this one is defined relative to.
public final void setReferenceAxes(Axes value) {
m_referenceAxes = value;
}
// The vector defining the axis about which the ReferenceAxes is rotated to produce these axes.
public final Vector getSpinVector() {
return m_spinVector;
}
// The vector defining the axis about which the ReferenceAxes is rotated to produce these axes.
public final void setSpinVector(Vector value) {
m_spinVector = value;
}
// The angle to rotate the ReferenceAxes about the SpinVector to produce this axes.
public final Scalar getRotationAngle() {
return m_rotationAngle;
}
// The angle to rotate the ReferenceAxes about the SpinVector to produce this axes.
public final void setRotationAngle(Scalar value) {
m_rotationAngle = value;
}
// This method is responsible for returning an instance of the private
// Evaluator class. It should ensure that the properties are not null or
// in an invalid state, and then use the evaluator group when it constructs
// and returns an instance of the Evaluator.
@Override
public AxesEvaluator getEvaluator(EvaluatorGroup group) {
if (group == null) {
throw new ArgumentNullException("group");
}
// Ensure that the properties are not null.
if (getReferenceAxes() == null) {
throw new PropertyInvalidException("ReferenceAxes", PropertyInvalidException.PropertyCannotBeNull);
}
if (getSpinVector() == null) {
throw new PropertyInvalidException("SpinVector", PropertyInvalidException.PropertyCannotBeNull);
}
if (getRotationAngle() == null) {
throw new PropertyInvalidException("RotationAngle", PropertyInvalidException.PropertyCannotBeNull);
}
return group.createEvaluator(getEvaluatorCallback);
}
// This method will only be called if the evaluator does not
// yet exist in the group and needs to be created.
private final AxesEvaluator createEvaluator(EvaluatorGroup group) {
// The reference axes is the axes that this axes is defined in.
Axes definedIn = getReferenceAxes();
// In order to compute the orientation of this axes relative to its parent, we must observe the spin vector
// in the reference axes and compute the value of the angle scalar.
// Obtain an evaluator for the spin vector in the reference axes.
// Notice that we create this evaluator in the same EvaluatorGroup.
VectorEvaluator spinVectorEvaluator = GeometryTransformer.observeVector(getSpinVector(), getReferenceAxes(), group);
// Obtain the evaluator for the angle - again in the same EvaluatorGroup.
ScalarEvaluator angleEvaluator = getRotationAngle().getEvaluator(group);
return new Evaluator(group, definedIn, spinVectorEvaluator, angleEvaluator);
}
private final EvaluatorGroup.Callback0<AxesEvaluator> getEvaluatorCallback = EvaluatorGroup.Callback0.of(this::createEvaluator);
private Axes m_referenceAxes;
private Vector m_spinVector;
private Scalar m_rotationAngle;
// This is the definition of the Evaluator that is used to actually evaluate this Axes.
// Because it is a private, nested class, it is not visible outside of the
// AngularOffsetAxes class. This is ok, though, because once it is created users only
// interact with it via a reference to its base class: AxesEvaluator.
private static final class Evaluator extends AxesEvaluator {
// An evaluator should not store any data that the user will be able to change
// after creating the evaluator. This sometimes requires that data required by the
// evaluator be copied or frozen using the IFreezable interface.
public Evaluator(EvaluatorGroup group, Axes definedIn, VectorEvaluator spinVectorEvaluator, ScalarEvaluator angleEvaluator) {
super(group);
m_definedIn = definedIn;
m_spinVectorEvaluator = spinVectorEvaluator;
m_angleEvaluator = angleEvaluator;
}
// The Evaluator's copy constructor will be called from the Clone method.
// Don't forget to call the base class implementation!
private Evaluator(Evaluator existingInstance, CopyContext context) {
super(existingInstance, context);
// Use the updateReference method on the CopyContext to perform any updates to the
// reference that are required by the context.
m_definedIn = context.updateReference(existingInstance.m_definedIn);
// For evaluators, just assign the reference directly - we'll call updateReference later.
m_spinVectorEvaluator = existingInstance.m_spinVectorEvaluator;
m_angleEvaluator = existingInstance.m_angleEvaluator;
// Always call UpdateEvaluatorReferences at the end of the copy constructor.
// This is where references to evaluators will be updated.
updateEvaluatorReferences(context);
}
// This method is used by the EvaluatorGroup system to avoid redundant evaluations. The
// EvaluatorGroup may call it on your evaluator in order to replace your evaluator's
// reference to another evaluator with a reference to a version that caches its last
// result.
@Override
public void updateEvaluatorReferences(CopyContext context) {
m_spinVectorEvaluator = context.updateReference(m_spinVectorEvaluator);
m_angleEvaluator = context.updateReference(m_angleEvaluator);
}
// The clone method should call the copy constructor.
@Override
public Object clone(CopyContext context) {
return new Evaluator(this, context);
}
// This method determines if there is data available from this Evaluator at
// the specified date.
@Override
public boolean isAvailable(JulianDate date) {
// This evaluator is available whenever the two nested evaluators are available.
return EvaluatorHelper.allEvaluatorsAreAvailable(date, m_spinVectorEvaluator, m_angleEvaluator);
}
// This method returns a collection of time intervals when data is
// available from this Evaluator.
@Override
public TimeIntervalCollection getAvailabilityIntervals(TimeIntervalCollection consideredIntervals) {
// This evaluator is available whenever the two nested evaluators are available.
return EvaluatorHelper.getAvailabilityIntervals(consideredIntervals, m_spinVectorEvaluator, m_angleEvaluator);
}
// This property determines if this Evaluator can safely be used from multiple threads
// simultaneously. If the evaluator stores data during the evaluate call, it is not thread
// safe. Otherwise, it generally is thread safe as long as any nested evaluators it uses
// are thread safe.
@Override
public boolean getIsThreadSafe() {
// This evaluator is thread safe as long as the nested evaluators are thread safe.
return EvaluatorHelper.allEvaluatorsAreThreadSafe(m_spinVectorEvaluator, m_angleEvaluator);
}
// This property determines if this Evaluator result changes depending on the time at
// which it is evaluated.
@Override
public boolean getIsTimeVarying() {
// This evaluator is time-varying if any of the nested evaluators are time-varying.
return EvaluatorHelper.anyEvaluatorIsTimeVarying(m_spinVectorEvaluator, m_angleEvaluator);
}
// This property expresses the Axes that this Axes is defined in for various intervals.
@Override
public TimeIntervalCollection1<Axes> getDefinedInIntervals() {
// This axes is always defined in the reference axes.
return createConstantDefinedIn(m_definedIn);
}
// This is where we do the actual evaluation when only the orientation of the axes (not
// its rotational velocity, rotational acceleration, etc.) is required.
@Override
public UnitQuaternion evaluate(JulianDate date) {
Cartesian spinVector = m_spinVectorEvaluator.evaluate(date);
UnitCartesian unitSpinVector = new UnitCartesian(spinVector);
double angle = m_angleEvaluator.evaluate(date);
AngleAxisRotation rotation = new AngleAxisRotation(angle, unitSpinVector);
return new UnitQuaternion(rotation);
}
// This is where we do the actual evaluation when the rotational velocity and rotational
// acceleration of the axes are requested as well.
@Override
public Motion2<UnitQuaternion, Cartesian> evaluate(JulianDate date, int order) {
// Ideally the implementation should support computing higher derivatives here. However,
// sometimes it is not possible or difficult to produce the derivative, in which case just
// call the evaluate overload that computes the value only. Normally, the derivative would
// represent the rotational velocity (acceleration, etc) vector of the rotating axes with
// respect to the frame in which the axes are defined. In this case, the axes are defined
// in the ReferenceAxes set by the user at construction.
return new Motion2<>(evaluate(date));
}
// Override the dispose method to call the dispose() method on any nested
// evaluators or other disposable nested types.
@Override
protected void dispose(boolean disposing) {
if (disposing) {
m_angleEvaluator.dispose();
m_spinVectorEvaluator.dispose();
}
}
private final Axes m_definedIn;
private ScalarEvaluator m_angleEvaluator;
private VectorEvaluator m_spinVectorEvaluator;
}
}
Modifier | Constructor and Description |
---|---|
protected |
Axes()
Initializes a new instance.
|
protected |
Axes(Axes existingInstance,
CopyContext context)
Initializes a new instance as a copy of an existing instance.
|
Modifier and Type | Method and Description |
---|---|
protected abstract boolean |
checkForSameDefinition(Axes other)
Checks to determine if another instance has the same definition as this instance and
returns
true if it does. |
protected boolean |
checkForSameDefinition(DefinitionalObject other)
Checks to determine if another instance has the same definition as this instance and
returns
true if it does. |
protected int |
computeCurrentDefinitionHashCode()
Computes a hash code based on the current properties of this object.
|
AxesEvaluator |
getEvaluator()
Gets an evaluator that can be used to find the transformation from this axes' parent axes to
this axes at a given date.
|
abstract AxesEvaluator |
getEvaluator(EvaluatorGroup group)
Gets an evaluator that can be used to find the transformation from this axes' parent axes to
this axes represented by a
Motion <UnitQuaternion , Cartesian >
at a given JulianDate . |
static Axes |
getRoot()
Gets the root axes, which is not defined in terms of any other axes.
|
Object |
getService(Class<?> serviceType)
Gets the service object of the specified type.
|
Vector |
getVectorElement(CartesianElement element)
Creates a
Vector representing the X, Y or Z of this axes. |
Vector |
getVectorElement(CartesianElement element,
int order)
Creates a
Vector representing the X, Y or Z of this axes. |
areSameDefinition, areSameDefinition, areSameDefinition, areSameDefinition, areSameDefinition, clone, collectionItemsAreSameDefinition, collectionItemsAreSameDefinition, collectionItemsAreSameDefinition, dictionaryItemsAreSameDefinition, enumerateDependencies, freeze, freezeAggregatedObjects, getCollectionHashCode, getCollectionHashCode, getCollectionHashCode, getDefinitionHashCode, getDefinitionHashCode, getDefinitionHashCode, getDefinitionHashCode, getDefinitionHashCode, getDefinitionHashCode, getDictionaryHashCode, getIsFrozen, isSameDefinition, throwIfFrozen
protected Axes()
protected Axes(@Nonnull Axes existingInstance, @Nonnull CopyContext context)
See ICloneWithContext.clone(CopyContext)
for more information about how to implement this constructor
in a derived class.
existingInstance
- The existing instance to copy.context
- A CopyContext
that controls the depth of the copy.ArgumentNullException
- Thrown when existingInstance
or context
is null
.protected final boolean checkForSameDefinition(DefinitionalObject other)
true
if it does. Derived classes MUST override this method and check
all new fields introduced by the derived class for definitional equivalence. It is NOT necessary
to check base class fields because the base class will already have done that. When overriding this method,
you should NOT call the base implementation because it will return false
for all derived-class instances.
Derived classes should check the type of other
to preserve the symmetric nature of IEquatableDefinition.isSameDefinition(java.lang.Object)
.checkForSameDefinition
in class DefinitionalObject
other
- The other instance to compare to this one.true
if the two objects are defined equivalently; otherwise false
.protected abstract boolean checkForSameDefinition(Axes other)
true
if it does. Derived classes MUST override this method and check
all new fields introduced by the derived class for definitional equivalence. It is NOT necessary
to check base class fields because the base class will already have done that. When overriding this method,
you should NOT call the base implementation because it will return false
for all derived-class instances.
Derived classes should check the type of other
to preserve the symmetric nature of IEquatableDefinition.isSameDefinition(java.lang.Object)
.other
- The other instance to compare to this one.true
if the two objects are defined equivalently; otherwise false
.protected int computeCurrentDefinitionHashCode()
Axes.checkForSameDefinition(agi.foundation.infrastructure.DefinitionalObject)
method.computeCurrentDefinitionHashCode
in class DefinitionalObject
@Nonnull public static Axes getRoot()
@Nullable public Object getService(@Nonnull Class<?> serviceType)
getService
in interface IServiceProvider
serviceType
- An object that specifies the type of service object to get.null
if the service does not exist.ArgumentNullException
- Thrown when serviceType
is null
.@Nonnull public final Vector getVectorElement(@Nonnull CartesianElement element, int order)
Vector
representing the X, Y or Z of this axes. Note that the vector
returned will return trivial answers unless observed in some other axes. Use the
GeometryTransformer
to properly observe the vector instead of getting an evaluator
directly.element
- Specifies that the returned vector represents the X, Y or Z element
of this axes.order
- The order of derivative of the axes to represent with this objectVector
representing the X, Y or Z of this axes.@Nonnull public final Vector getVectorElement(@Nonnull CartesianElement element)
Vector
representing the X, Y or Z of this axes. Note that the vector
returned will return trivial answers unless observed in some other axes. Use the
GeometryTransformer
to properly observe the vector instead of getting an evaluator
directly.element
- Specifies that the returned vector represents the X, Y or Z element
of this axes.Vector
representing the X, Y or Z of this axes.@Nonnull public final AxesEvaluator getEvaluator()
GeometryTransformer
instead of calling this method directly.
See Axes.getEvaluator(EvaluatorGroup)
for details.@Nonnull public abstract AxesEvaluator getEvaluator(@Nonnull EvaluatorGroup group)
Motion
<UnitQuaternion
, Cartesian
>
at a given JulianDate
.
Consider using the methods of GeometryTransformer
instead of calling this method directly.group
- The group with which to associate the new evaluator. By grouping evaluators
that are often evaluated at the same Julian dates, common computations can be performed only once
for the entire group instead of multiple times for each evaluator.