512 lines
18 KiB
Java
512 lines
18 KiB
Java
|
/*
|
||
|
File: SceneMoon.java
|
||
|
|
||
|
University of Applied Science Berne,HTA-Biel/Bienne,
|
||
|
Computer Science Department.
|
||
|
|
||
|
Diploma thesis J3D Solar System Simulator
|
||
|
Originally written by Marcel Portner & Bernhard Hari (c) 2000
|
||
|
|
||
|
CVS - Information :
|
||
|
|
||
|
$Header: /var/cvsreps/projects/c450/2000/sss3d/source_diploma/sss3d/contentbranch/moons/SceneMoon.java,v 1.11 2000/12/15 02:53:18 portm Exp $
|
||
|
$Author: portm $
|
||
|
$Date: 2000/12/15 02:53:18 $
|
||
|
$State: Exp $
|
||
|
|
||
|
*/
|
||
|
package sss3d.contentbranch.moons;
|
||
|
|
||
|
import sss3d.contentbranch.*;
|
||
|
import sss3d.utils.SSS3dConstants;
|
||
|
import sss3d.utils.observer.*;
|
||
|
import sss3d.contentbranch.orbit.*;
|
||
|
import sss3d.calculations.constants.AstronomicalConstants;
|
||
|
import sss3d.utils.xmlparser.XMLConstants;
|
||
|
|
||
|
import javax.media.j3d.*;
|
||
|
import javax.vecmath.*;
|
||
|
import com.sun.j3d.utils.behaviors.interpolators.*;
|
||
|
import java.util.Date;
|
||
|
import java.awt.Color;
|
||
|
|
||
|
/**
|
||
|
* This class describe the scene of a moon.
|
||
|
*
|
||
|
* @author Marcel Portner & Bernhard Hari
|
||
|
* @version $Revision: 1.11 $
|
||
|
* @see Moon
|
||
|
*/
|
||
|
public class SceneMoon implements SceneCelestialObjects, PositionObserver, InfoObserver {
|
||
|
|
||
|
private RotPosScaleTCBSplinePathInterpolator spline;
|
||
|
private RotPosScaleTCBSplinePathInterpolator parentSpline;
|
||
|
private RotationInterpolator rotator;
|
||
|
private Alpha rotationAlpha;
|
||
|
private Alpha animAlpha;
|
||
|
private Alpha parentAnimAlpha;
|
||
|
private long alphaTime = 0;
|
||
|
private Date date;
|
||
|
|
||
|
private BranchGroup removableCoordBG;
|
||
|
private BranchGroup removableOrbitBG;
|
||
|
private TransformGroup trGrRotAndLeafs;
|
||
|
private TransformGroup trGrOrbit;
|
||
|
private TransformGroup trGrTendSpline;
|
||
|
private TransformGroup trGrParentSpline;
|
||
|
|
||
|
private ColorOrbit colorOrbit;
|
||
|
|
||
|
private Moon moon;
|
||
|
|
||
|
private ObjectsPositions objPos;
|
||
|
private ObjectsInformation objInfo;
|
||
|
private InitializationObject iniObject;
|
||
|
private CelestialObjectInfo parentObject;
|
||
|
|
||
|
/**
|
||
|
* Initializes a new ScenePlanet.
|
||
|
*
|
||
|
* @param moon a object reference of a given moon.
|
||
|
* @param objPos a reference to the concrete subject of
|
||
|
* the observer pattern positions
|
||
|
* @param objInfo a reference to the concrete subject of
|
||
|
* the observer pattern information
|
||
|
*/
|
||
|
public SceneMoon(Moon moon, ObjectsPositions objPos,
|
||
|
ObjectsInformation objInfo ) {
|
||
|
this.moon = moon;
|
||
|
this.objPos = objPos;
|
||
|
this.objPos.attach(this);
|
||
|
this.objInfo = objInfo;
|
||
|
this.objInfo.attach(this);
|
||
|
iniObject = objInfo.getInitializationObject();
|
||
|
parentObject = objInfo.getInfo(moon.getParentName());
|
||
|
}
|
||
|
|
||
|
/**
|
||
|
* Removes this object from the position observer list.
|
||
|
*/
|
||
|
public void destroy() {
|
||
|
objPos.detach(this);
|
||
|
objInfo.detach(this);
|
||
|
moon = null;
|
||
|
}
|
||
|
|
||
|
/**
|
||
|
* Updates the position state of the object.<br>
|
||
|
*
|
||
|
* @param orbitPos a reference to the list containing all position observer
|
||
|
*/
|
||
|
public void update(OrbitPositions orbitPos) {
|
||
|
|
||
|
}
|
||
|
|
||
|
/**
|
||
|
* Updates the information state of the object.<br>
|
||
|
*
|
||
|
* @param id identification of the object that has to be updated
|
||
|
* @param parameter the parameter that has been changed
|
||
|
*/
|
||
|
public void update( String id, int parameter ) {
|
||
|
|
||
|
if ( id.equals( moon.getId() ) ||
|
||
|
id.equals("all") ) {
|
||
|
|
||
|
IniData data = (IniData)objInfo.getParameter( XMLConstants.MOON, moon.getId() );
|
||
|
|
||
|
switch ( parameter ) {
|
||
|
case XMLConstants.VISIBLE :
|
||
|
// implemented inside SceneSolarSystem
|
||
|
break;
|
||
|
case XMLConstants.COORDINATESYSTEM :
|
||
|
if ( data.hasCoordinateSystem() ) {
|
||
|
addCoord();
|
||
|
} else {
|
||
|
removeCoord();
|
||
|
}
|
||
|
break;
|
||
|
case XMLConstants.ORBIT :
|
||
|
if ( data.hasOrbit() ) {
|
||
|
addOrbit();
|
||
|
} else {
|
||
|
removeOrbit();
|
||
|
}
|
||
|
break;
|
||
|
case XMLConstants.COLORORBIT :
|
||
|
setOrbitColor( data.getColorOrbit());
|
||
|
break;
|
||
|
case XMLConstants.ANIMATIONSPEED :
|
||
|
setAnimSpeed();
|
||
|
break;
|
||
|
default : break;
|
||
|
}
|
||
|
}
|
||
|
|
||
|
}
|
||
|
|
||
|
/**
|
||
|
* Returns the ID of the current moon.
|
||
|
*
|
||
|
* @return the ID to idendification the moon.
|
||
|
*/
|
||
|
public String getId() {
|
||
|
return moon.getId();
|
||
|
}
|
||
|
|
||
|
/**
|
||
|
* Returns a CelestialObjectInfo of the current moon.
|
||
|
* This object has all specific information of the current moon.
|
||
|
*
|
||
|
* @return a CelestialObjectInfo of the current moon.
|
||
|
*/
|
||
|
public CelestialObjectInfo getInfo() {
|
||
|
return moon.getInfo();
|
||
|
}
|
||
|
|
||
|
/**
|
||
|
* This method handle the animation and rotation of the celestial objects.
|
||
|
*
|
||
|
* @param animate if true, start animation; if false stop animation.
|
||
|
* @param rotate if true, start rotation; if false stop rotation.
|
||
|
*/
|
||
|
public void setAnimation(boolean animate, boolean rotate) {
|
||
|
date = new Date();
|
||
|
if(animate) {
|
||
|
/*
|
||
|
Set the stop time for the animation and rotation.
|
||
|
Without this trick, the celestial objects are jumping around.
|
||
|
Because when the animation is stop, the objects goes on
|
||
|
in the background.
|
||
|
*/
|
||
|
alphaTime = date.getTime() - alphaTime;
|
||
|
rotationAlpha.setStartTime(alphaTime);
|
||
|
animAlpha.setStartTime(alphaTime);
|
||
|
parentAnimAlpha.setStartTime(alphaTime);
|
||
|
} else {
|
||
|
// save the stop time for the animation and rotation.
|
||
|
alphaTime = animAlpha.getStartTime();
|
||
|
alphaTime = date.getTime() - alphaTime;
|
||
|
}
|
||
|
// set the animation and rotation to stop or go.
|
||
|
spline.setEnable(animate);
|
||
|
parentSpline.setEnable(animate);
|
||
|
rotator.setEnable(rotate);
|
||
|
}
|
||
|
|
||
|
/**
|
||
|
* Set the animation speed for the moon.
|
||
|
*/
|
||
|
public void setAnimSpeed() {
|
||
|
date = new Date();
|
||
|
|
||
|
AnimationSpeed animationSpeed = (AnimationSpeed)objInfo.getInitializationObject().getParameter(XMLConstants.ANIMATIONSPEED);
|
||
|
int factor = 0;
|
||
|
switch(animationSpeed.getType()) {
|
||
|
case AnimationSpeed.DAYS_PER_SECOND:
|
||
|
factor = 1;
|
||
|
break;
|
||
|
case AnimationSpeed.HOURS_PER_SECOND:
|
||
|
factor = 24;
|
||
|
break;
|
||
|
case AnimationSpeed.MINUTES_PER_SECOND:
|
||
|
factor = 24 * 60;
|
||
|
break;
|
||
|
default:
|
||
|
}
|
||
|
// moon rotation
|
||
|
int animSpeed = animationSpeed.getValue();
|
||
|
long duration = (long)(moon.getRotOwnAxis() * factor / animSpeed);
|
||
|
|
||
|
long increasing = rotationAlpha.getIncreasingAlphaDuration();
|
||
|
// delta t = t1*(1-T2/T1)
|
||
|
long diff = (long)((date.getTime() - rotationAlpha.getStartTime()) *
|
||
|
(1.0f - (float)duration / (float)increasing));
|
||
|
rotationAlpha.setIncreasingAlphaDuration(duration);
|
||
|
rotationAlpha.setStartTime(rotationAlpha.getStartTime() + diff);
|
||
|
|
||
|
// moon movement
|
||
|
duration = (long)(moon.getRotOrbit() * factor / animSpeed);
|
||
|
|
||
|
increasing = animAlpha.getIncreasingAlphaDuration();
|
||
|
// delta t = t1*(1-T2/T1)
|
||
|
diff = (long)((date.getTime() - animAlpha.getStartTime()) *
|
||
|
(1.0f - (float)duration / (float)increasing));
|
||
|
|
||
|
animAlpha.setIncreasingAlphaDuration(duration);
|
||
|
animAlpha.setStartTime(animAlpha.getStartTime() + diff);
|
||
|
|
||
|
// parent movement
|
||
|
duration = (long)(parentObject.getOrbitPeriod() * 1000 * AstronomicalConstants.DAYS *
|
||
|
factor / animSpeed);
|
||
|
|
||
|
increasing = parentAnimAlpha.getIncreasingAlphaDuration();
|
||
|
// delta t = t1*(1-T2/T1)
|
||
|
diff = (long)((date.getTime() - parentAnimAlpha.getStartTime()) *
|
||
|
(1.0f - (float)duration / (float)increasing));
|
||
|
|
||
|
parentAnimAlpha.setIncreasingAlphaDuration(duration);
|
||
|
parentAnimAlpha.setStartTime(parentAnimAlpha.getStartTime() + diff);
|
||
|
}
|
||
|
|
||
|
/**
|
||
|
* Add the coordinate system to this scenegraph.
|
||
|
*/
|
||
|
public void addCoord() {
|
||
|
trGrRotAndLeafs.addChild(removableCoordBG);
|
||
|
}
|
||
|
|
||
|
/**
|
||
|
* Remove the coordinate system from this scenegraph.
|
||
|
*/
|
||
|
public void removeCoord() {
|
||
|
removableCoordBG.detach();
|
||
|
}
|
||
|
|
||
|
/**
|
||
|
* Add the color orbit to this scenegraph.
|
||
|
*/
|
||
|
public void addOrbit() {
|
||
|
trGrOrbit.addChild(removableOrbitBG);
|
||
|
}
|
||
|
|
||
|
/**
|
||
|
* Remove the color orbit from this scenegraph.
|
||
|
*/
|
||
|
public void removeOrbit() {
|
||
|
removableOrbitBG.detach();
|
||
|
}
|
||
|
|
||
|
/**
|
||
|
* Set a new orbit color.
|
||
|
*
|
||
|
* @param orbitColor the new color of the orbit
|
||
|
*/
|
||
|
public void setOrbitColor(Color orbitColor) {
|
||
|
colorOrbit.setColor(orbitColor);
|
||
|
}
|
||
|
|
||
|
/**
|
||
|
* Get the current position of the moon.
|
||
|
*
|
||
|
* @return a Transform3D with the current position.
|
||
|
*/
|
||
|
public Transform3D getCurrentPosition() {
|
||
|
Transform3D transVec = new Transform3D();
|
||
|
Transform3D transVecParent = new Transform3D();
|
||
|
Transform3D transRot = new Transform3D();
|
||
|
trGrTendSpline.getTransform(transVec);
|
||
|
trGrRotAndLeafs.getTransform(transRot);
|
||
|
trGrParentSpline.getTransform(transVecParent);
|
||
|
Vector3d vec = new Vector3d();
|
||
|
Vector3d vecParent = new Vector3d();
|
||
|
transVec.get(vec);
|
||
|
transVecParent.get(vecParent);
|
||
|
vec.add(vecParent);
|
||
|
transRot.setTranslation(vec);
|
||
|
return transRot;
|
||
|
}
|
||
|
|
||
|
/**
|
||
|
* Returns the BranchGroup of the created moon scene.
|
||
|
* It has a moon as leaf, make a rotation to the own axis and
|
||
|
* make with a TCB-spline function a rotation around the parent planet.
|
||
|
*
|
||
|
* @return the BranchGroup of the given moon.
|
||
|
*/
|
||
|
public BranchGroup createSceneGraph() {
|
||
|
// A BoundingSphere instance as general bounding region.
|
||
|
BoundingSphere boundsGen = new BoundingSphere(new Point3d(0.0, 0.0, 0.0),
|
||
|
SSS3dConstants.BOUNDRADIUS);
|
||
|
|
||
|
// Create the first TransformGroup node trGrRotAndLeafs for the moon,
|
||
|
// the coordinate system and the rotation
|
||
|
trGrRotAndLeafs = new TransformGroup();
|
||
|
|
||
|
// With the ALLOW_TRANSFORM_READ and ALLOW_TRANSFORM_WRITE
|
||
|
// capabilities, we allow the modification of the TransformGroup's
|
||
|
// code by the Behavior's code at run time.
|
||
|
trGrRotAndLeafs.setCapability(TransformGroup.ALLOW_TRANSFORM_READ);
|
||
|
trGrRotAndLeafs.setCapability(TransformGroup.ALLOW_TRANSFORM_WRITE);
|
||
|
|
||
|
trGrRotAndLeafs.setCapability(Group.ALLOW_CHILDREN_READ);
|
||
|
trGrRotAndLeafs.setCapability(Group.ALLOW_CHILDREN_WRITE);
|
||
|
trGrRotAndLeafs.setCapability(Group.ALLOW_CHILDREN_EXTEND);
|
||
|
|
||
|
// Attach the leaf node "moon" to the TransformGroup trGrRotAndLeafs.
|
||
|
trGrRotAndLeafs.addChild(moon);
|
||
|
|
||
|
removableCoordBG = new BranchGroup();
|
||
|
|
||
|
// Create and attach a coordinate system to the TransformGroup node
|
||
|
// trGrRotAndLeafs, that is to the moon.
|
||
|
removableCoordBG.addChild(new CoordinateSystem(1.2f));
|
||
|
|
||
|
// Allow to detach the Coordinate System
|
||
|
removableCoordBG.setCapability(BranchGroup.ALLOW_DETACH);
|
||
|
|
||
|
// Set the picking of the CoordinateSystem BranchGroup to false.
|
||
|
removableCoordBG.setPickable(false);
|
||
|
|
||
|
trGrRotAndLeafs.addChild(removableCoordBG);
|
||
|
|
||
|
// Prepare the RotationInterpolator (Behavior) for the
|
||
|
// moon's rotation about its own axis.
|
||
|
Transform3D tr3Drot = new Transform3D();
|
||
|
|
||
|
float startRot, endRot;
|
||
|
if(moon.getRotOwnAxis() > 0) {
|
||
|
// Create the alpha(t) function. Positiv rotation!
|
||
|
rotationAlpha = new Alpha(-1, Alpha.INCREASING_ENABLE, 0, 0,
|
||
|
/*moon.getRotOwnAxis()*/Long.MAX_VALUE, 0, 0, 0, 0, 0);
|
||
|
startRot = 0.0f;
|
||
|
endRot = (float)(2.0 * Math.PI);
|
||
|
} else {
|
||
|
// Create the alpha(t) function. Negativ rotation!
|
||
|
rotationAlpha = new Alpha(-1, Alpha.DECREASING_ENABLE, 0, 0, 0, 0, 0,
|
||
|
/*Math.abs(moon.getRotOwnAxis())*/Long.MAX_VALUE, 0, 0);
|
||
|
startRot = (float)(2.0 * Math.PI);
|
||
|
endRot = 0.0f;
|
||
|
}
|
||
|
// Create the moon's rotation about its own axis.
|
||
|
rotator = new RotationInterpolator(rotationAlpha,
|
||
|
trGrRotAndLeafs, tr3Drot,
|
||
|
startRot, endRot);
|
||
|
rotator.setSchedulingBounds(boundsGen);
|
||
|
trGrRotAndLeafs.addChild(rotator);
|
||
|
|
||
|
// Create the second TransformGroup node trGrTendSpline for the moon's
|
||
|
// rotation around the parent planet and the tend.
|
||
|
trGrTendSpline = new TransformGroup();
|
||
|
|
||
|
// With the ALLOW_TRANSFORM_READ and ALLOW_TRANSFORM_WRITE
|
||
|
// capabilities, we allow the modification of the TransformGroup's
|
||
|
// code by the Behavior's code at run time.
|
||
|
trGrTendSpline.setCapability(TransformGroup.ALLOW_TRANSFORM_READ);
|
||
|
trGrTendSpline.setCapability(TransformGroup.ALLOW_TRANSFORM_WRITE);
|
||
|
|
||
|
// Attach trGrRotAndLeafs node to the trGrTendSpline node.
|
||
|
trGrTendSpline.addChild(trGrRotAndLeafs);
|
||
|
|
||
|
Transform3D yAxis = new Transform3D();
|
||
|
|
||
|
// Create the alpha(t) function.
|
||
|
animAlpha = new Alpha(-1, Alpha.INCREASING_ENABLE, 0, 0,
|
||
|
/*moon.getRotOrbit()*/Long.MAX_VALUE, 0, 0, 0, 0, 0);
|
||
|
|
||
|
// the animation Alpha value of the parent object
|
||
|
long duration = (long)(parentObject.getOrbitPeriod() * 1000 * AstronomicalConstants.DAYS);
|
||
|
// Create the alpha(t) function.
|
||
|
parentAnimAlpha = new Alpha(-1, Alpha.INCREASING_ENABLE, 0, 0,
|
||
|
/*duration*/Long.MAX_VALUE, 0, 0, 0, 0, 0);
|
||
|
|
||
|
// rotate the moon to his true tend
|
||
|
Matrix4d mat = new Matrix4d();
|
||
|
mat.rotX(Math.toRadians(moon.getDegree()));
|
||
|
Quat4f r = new Quat4f();
|
||
|
r.set(mat);
|
||
|
// set scale of the moon
|
||
|
float scale;
|
||
|
boolean compressed = ((Boolean)iniObject.getParameter(XMLConstants.COMPRESSED)).booleanValue();
|
||
|
if(compressed) {
|
||
|
scale = moon.getLogRadius();
|
||
|
} else {
|
||
|
scale = moon.getRadius();
|
||
|
}
|
||
|
Point3f s = new Point3f(scale, scale, scale);
|
||
|
|
||
|
Double time = (Double)iniObject.getParameter(XMLConstants.JDAY);
|
||
|
// take the positions of the moon
|
||
|
Point3f[] pos = objPos.getPositions(getInfo(), time.doubleValue(), compressed);
|
||
|
int pointNrs = pos.length;
|
||
|
float stepLength = 1 / (float)pointNrs;
|
||
|
float stepPos = 0.0f;
|
||
|
// save the positions in the TCBKeyFrame
|
||
|
TCBKeyFrame[] keyFrames = new TCBKeyFrame[pointNrs+1];
|
||
|
for(int i = 0; i < pointNrs; i++) {
|
||
|
keyFrames[i] = new TCBKeyFrame(stepPos, 0, pos[i], r, s, 0.0f, 0.0f, 0.0f);
|
||
|
stepPos += stepLength;
|
||
|
}
|
||
|
keyFrames[pointNrs] = new TCBKeyFrame(1.0f, 0, pos[0], r, s, 0.0f, 0.0f, 0.0f);
|
||
|
|
||
|
// make a spline courve of the position points
|
||
|
spline = new RotPosScaleTCBSplinePathInterpolator(animAlpha,
|
||
|
trGrTendSpline,
|
||
|
yAxis,
|
||
|
keyFrames);
|
||
|
spline.setSchedulingBounds(boundsGen);
|
||
|
trGrTendSpline.addChild(spline);
|
||
|
|
||
|
// Create the TransformGroup node trGrOrbit for the moon orbit.
|
||
|
trGrOrbit = new TransformGroup();
|
||
|
|
||
|
trGrOrbit.addChild(trGrTendSpline);
|
||
|
|
||
|
trGrOrbit.setCapability(Group.ALLOW_CHILDREN_READ);
|
||
|
trGrOrbit.setCapability(Group.ALLOW_CHILDREN_WRITE);
|
||
|
trGrOrbit.setCapability(Group.ALLOW_CHILDREN_EXTEND);
|
||
|
|
||
|
removableOrbitBG = new BranchGroup();
|
||
|
|
||
|
colorOrbit = new ColorLineOrbit(Color.red);
|
||
|
|
||
|
colorOrbit.setPositions(pos);
|
||
|
|
||
|
// Attach the leaf node "orbit" to the BranchGroup removableOrbitBG.
|
||
|
removableOrbitBG.addChild(colorOrbit);
|
||
|
|
||
|
removableOrbitBG.setCapability(BranchGroup.ALLOW_DETACH);
|
||
|
|
||
|
// Set the picking of the ColorOrbit BranchGroup to false.
|
||
|
removableOrbitBG.setPickable(false);
|
||
|
|
||
|
trGrOrbit.addChild(removableOrbitBG);
|
||
|
|
||
|
trGrParentSpline = new TransformGroup();
|
||
|
|
||
|
// With the ALLOW_TRANSFORM_READ and ALLOW_TRANSFORM_WRITE
|
||
|
// capabilities, we allow the modification of the TransformGroup's
|
||
|
// code by the Behavior's code at run time.
|
||
|
trGrParentSpline.setCapability(TransformGroup.ALLOW_TRANSFORM_READ);
|
||
|
trGrParentSpline.setCapability(TransformGroup.ALLOW_TRANSFORM_WRITE);
|
||
|
|
||
|
// Attach trGrRotAndLeafs node to the trGrTendSpline node.
|
||
|
trGrParentSpline.addChild(trGrOrbit);
|
||
|
|
||
|
r = null;
|
||
|
s = null;
|
||
|
r = new Quat4f();
|
||
|
s = new Point3f(1.0f, 1.0f, 1.0f);
|
||
|
|
||
|
// take the positions of the parent planet
|
||
|
Point3f[] parentPos = objPos.getPositions(parentObject, time.doubleValue(), compressed);
|
||
|
pointNrs = parentPos.length;
|
||
|
stepLength = 1 / (float)pointNrs;
|
||
|
stepPos = 0.0f;
|
||
|
// save the positions in the TCBKeyFrame
|
||
|
TCBKeyFrame[] parentKeyFrames = new TCBKeyFrame[pointNrs+1];
|
||
|
for(int i = 0; i < pointNrs; i++) {
|
||
|
parentKeyFrames[i] = new TCBKeyFrame(stepPos, 0, parentPos[i], r, s, 0.0f, 0.0f, 0.0f);
|
||
|
stepPos += stepLength;
|
||
|
}
|
||
|
parentKeyFrames[pointNrs] = new TCBKeyFrame(1.0f, 0, parentPos[0], r, s, 0.0f, 0.0f, 0.0f);
|
||
|
|
||
|
// make a spline courve of the position points
|
||
|
parentSpline = new RotPosScaleTCBSplinePathInterpolator(parentAnimAlpha,
|
||
|
trGrParentSpline,
|
||
|
yAxis,
|
||
|
parentKeyFrames);
|
||
|
|
||
|
parentSpline.setSchedulingBounds(boundsGen);
|
||
|
trGrParentSpline.addChild(parentSpline);
|
||
|
|
||
|
BranchGroup brGrAll = new BranchGroup();
|
||
|
brGrAll.addChild(trGrParentSpline);
|
||
|
|
||
|
brGrAll.setCapability(BranchGroup.ALLOW_DETACH);
|
||
|
|
||
|
// Return the final version of the BranchGroup node brGrAll.
|
||
|
return brGrAll;
|
||
|
}
|
||
|
}
|