/* * $Id$ * This file is a part of the Arakhne Foundation Classes, http://www.arakhne.org/afc * * Copyright (c) 2000-2012 Stephane GALLAND. * Copyright (c) 2005-10, Multiagent Team, Laboratoire Systemes et Transports, * Universite de Technologie de Belfort-Montbeliard. * Copyright (c) 2013-2016 The original authors, and other authors. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.arakhne.afc.agentmotion.common; import java.io.Serializable; import java.util.Random; import org.eclipse.xtext.xbase.lib.Pure; import org.arakhne.afc.agentmotion.AgentMotion; import org.arakhne.afc.agentmotion.FacingMotionAlgorithm; import org.arakhne.afc.agentmotion.RandomMotionAlgorithm; import org.arakhne.afc.agentmotion.SeekingMotionAlgorithm; import org.arakhne.afc.math.geometry.d2.Point2D; import org.arakhne.afc.math.geometry.d2.Vector2D; import org.arakhne.afc.math.geometry.d2.d.Point2d; import org.arakhne.afc.vmutil.asserts.AssertMessages; /** Agent is changing randomly its position and orientation. * * <p>This algorithm uses speeds or accelerations, depending on its delegates. * * @author $Author: sgalland$ * @version $FullVersion$ * @mavengroupid $GroupId$ * @mavenartifactid $ArtifactId$ * @since 14.0 */ public class RandomAlgorithm implements RandomMotionAlgorithm, Serializable, Cloneable { private static final long serialVersionUID = -8318025671219960417L; /** Distance between the agent position and the center of the virtual wheel. */ protected final double wheelDistance; /** Radius of the virtual wheel. */ protected final double wheelRadius; /** Maximal rotation of the virtual wheel. */ protected final double maxWheelRotation; /** Facing algorithm to delegate to. */ protected final FacingMotionAlgorithm facing; /** Seeking algorithm to delegate to. */ protected final SeekingMotionAlgorithm seeking; private Random random = new Random(); private double rotation; /** Constructor. * * @param wheelDistance the distance between the agent position and the center of the virtual wheel. * @param wheelRadius the radius of the virtual wheel. * @param maxWheelRotation the maximal rotation of the virtual wheel. * @param facing the facing algorithm to delegate to. * @param seeking the seeking algorithm to delegate to. */ public RandomAlgorithm(double wheelDistance, double wheelRadius, double maxWheelRotation, FacingMotionAlgorithm facing, SeekingMotionAlgorithm seeking) { assert wheelDistance >= 0. : AssertMessages.positiveOrZeroParameter(0); assert wheelRadius >= 0. : AssertMessages.positiveOrZeroParameter(1); assert maxWheelRotation >= 0. : AssertMessages.positiveOrZeroParameter(2); this.wheelDistance = wheelDistance; this.wheelRadius = wheelRadius; this.maxWheelRotation = maxWheelRotation; this.facing = facing; this.seeking = seeking; } @Pure @Override public RandomAlgorithm clone() { try { final RandomAlgorithm clone = (RandomAlgorithm) super.clone(); clone.random = new Random(); return clone; } catch (CloneNotSupportedException e) { throw new Error(e); } } @Pure @Override public boolean equals(Object obj) { if (obj == this) { return true; } if (obj != null && obj.getClass() == getClass()) { final RandomAlgorithm algo = (RandomAlgorithm) obj; return algo.wheelDistance == this.wheelDistance && algo.wheelRadius == this.wheelRadius && algo.maxWheelRotation == this.maxWheelRotation && algo.facing.equals(this.facing) && algo.seeking.equals(this.seeking); } return false; } @Pure @Override public int hashCode() { int bits = 1; bits = 31 * bits + Double.hashCode(this.wheelDistance); bits = 31 * bits + Double.hashCode(this.wheelRadius); bits = 31 * bits + Double.hashCode(this.maxWheelRotation); bits = 31 * bits + this.facing.hashCode(); bits = 31 * bits + this.seeking.hashCode(); return bits ^ (bits >> 31); } @Override public AgentMotion calculate(Point2D<?, ?> position, Vector2D<?, ?> orientation, double linearSpeed, double maxLinear, double angularSpeed, double maxAngular) { // Calculate the circle center final Vector2D<?, ?> wheelCenter = orientation.toColinearVector(this.wheelDistance); // Calculate the displacement force final Vector2D<?, ?> displacement = wheelCenter.toColinearVector(this.wheelRadius); displacement.turn(this.rotation); // Change angle just a bit, so it // won't have the same value in the // next frame. this.rotation += (this.random.nextDouble() * 2. - 1.) * this.maxWheelRotation; // Finally calculate the wander force final Point2d circleCenterPosition = new Point2d( position.getX() + wheelCenter.getX(), position.getY() + wheelCenter.getY()); final Point2d faceTarget = new Point2d( circleCenterPosition.getX() + displacement.getX(), circleCenterPosition.getY() + displacement.getY()); // Delegate final double angularMotion = this.facing.calculate(position, orientation, angularSpeed, maxAngular, faceTarget); final Vector2D<?, ?> linearMotion = this.seeking.calculate(position, linearSpeed, maxLinear, circleCenterPosition); return new AgentMotion(linearMotion, angularMotion); } }