diff --git a/alchemist-cognitive-agents/src/main/kotlin/it/unibo/alchemist/model/cognitiveagents/CognitiveModel.kt b/alchemist-cognitive-agents/src/main/kotlin/it/unibo/alchemist/model/cognitiveagents/CognitiveModel.kt index 4df570f83e..1e0187d847 100644 --- a/alchemist-cognitive-agents/src/main/kotlin/it/unibo/alchemist/model/cognitiveagents/CognitiveModel.kt +++ b/alchemist-cognitive-agents/src/main/kotlin/it/unibo/alchemist/model/cognitiveagents/CognitiveModel.kt @@ -40,7 +40,7 @@ interface CognitiveModel { fun update(frequency: Double) /** - * Whether or not this pedestrian intends to escape. + * Whether or not this node intends to escape. */ fun wantsToEscape(): Boolean = escapeIntention() > remainIntention() } diff --git a/alchemist-cognitive-agents/src/main/kotlin/it/unibo/alchemist/model/implementations/actions/AbstractGroupSteeringAction.kt b/alchemist-cognitive-agents/src/main/kotlin/it/unibo/alchemist/model/implementations/actions/AbstractGroupSteeringAction.kt index 03343a104e..e3b512207b 100644 --- a/alchemist-cognitive-agents/src/main/kotlin/it/unibo/alchemist/model/implementations/actions/AbstractGroupSteeringAction.kt +++ b/alchemist-cognitive-agents/src/main/kotlin/it/unibo/alchemist/model/implementations/actions/AbstractGroupSteeringAction.kt @@ -11,7 +11,7 @@ package it.unibo.alchemist.model.implementations.actions import it.unibo.alchemist.model.interfaces.Environment import it.unibo.alchemist.model.interfaces.GroupSteeringAction -import it.unibo.alchemist.model.interfaces.Pedestrian +import it.unibo.alchemist.model.interfaces.Node import it.unibo.alchemist.model.interfaces.Position import it.unibo.alchemist.model.interfaces.Reaction import it.unibo.alchemist.model.interfaces.geometry.GeometricTransformation @@ -21,21 +21,19 @@ import it.unibo.alchemist.model.interfaces.geometry.Vector * An abstract [GroupSteeringAction]. */ abstract class AbstractGroupSteeringAction( - /** - * The environment in which this action executes. - */ - protected open val env: Environment, + environment: Environment, reaction: Reaction, - pedestrian: Pedestrian -) : AbstractSteeringAction(env, reaction, pedestrian), + node: Node, +) : AbstractSteeringAction(environment, reaction, node), GroupSteeringAction - where P : Position

, P : Vector

, + where P : Position

, + P : Vector

, A : GeometricTransformation

{ /** * Computes the centroid of the [group] in absolute coordinates. */ protected fun centroid(): P = with(group()) { - map { env.getPosition(it) }.reduce { acc, pos -> acc + pos } / size.toDouble() + map { environment.getPosition(it) }.reduce { acc, pos -> acc + pos } / size.toDouble() } } diff --git a/alchemist-cognitive-agents/src/main/kotlin/it/unibo/alchemist/model/implementations/actions/AbstractLayerAction.kt b/alchemist-cognitive-agents/src/main/kotlin/it/unibo/alchemist/model/implementations/actions/AbstractLayerAction.kt index f36a0a92eb..d22c237c10 100644 --- a/alchemist-cognitive-agents/src/main/kotlin/it/unibo/alchemist/model/implementations/actions/AbstractLayerAction.kt +++ b/alchemist-cognitive-agents/src/main/kotlin/it/unibo/alchemist/model/implementations/actions/AbstractLayerAction.kt @@ -4,8 +4,7 @@ import it.unibo.alchemist.model.implementations.layers.BidimensionalGaussianLaye import it.unibo.alchemist.model.implementations.positions.Euclidean2DPosition import it.unibo.alchemist.model.interfaces.Layer import it.unibo.alchemist.model.interfaces.Molecule -import it.unibo.alchemist.model.interfaces.Pedestrian -import it.unibo.alchemist.model.interfaces.Pedestrian2D +import it.unibo.alchemist.model.interfaces.Node import it.unibo.alchemist.model.interfaces.Position import it.unibo.alchemist.model.interfaces.Reaction import it.unibo.alchemist.model.interfaces.environments.Euclidean2DEnvironment @@ -15,10 +14,10 @@ import it.unibo.alchemist.model.interfaces.geometry.euclidean2d.Euclidean2DTrans * Abstract implementation of an action influenced by the concentration of a given molecule in the environment. * * @param environment - * the environment inside which the pedestrian moves. + * the environment inside which the node moves. * @param reaction * the reaction which executes this action. - * @param pedestrian + * @param node * the owner of this action. * @param targetMolecule * the {@link Molecule} you want to know the concentration in the different positions of the environment. @@ -26,16 +25,11 @@ import it.unibo.alchemist.model.interfaces.geometry.euclidean2d.Euclidean2DTrans abstract class AbstractLayerAction( protected val environment: Euclidean2DEnvironment, reaction: Reaction, - override val pedestrian: Pedestrian2D, - protected val targetMolecule: Molecule -) : AbstractSteeringAction(environment, reaction, pedestrian) { + node: Node, + protected val targetMolecule: Molecule, +) : AbstractSteeringAction(environment, reaction, node) { - override fun cloneAction( - n: Pedestrian, - r: Reaction - ) = requireNodeTypeAndProduce, AbstractLayerAction>(n) { cloneAction(it, r) } - - protected abstract fun cloneAction(n: Pedestrian2D, r: Reaction): AbstractLayerAction + abstract override fun cloneAction(node: Node, reaction: Reaction): AbstractLayerAction /** * @returns the layer containing [targetMolecule] or fails. diff --git a/alchemist-cognitive-agents/src/main/kotlin/it/unibo/alchemist/model/implementations/actions/AbstractNavigationAction.kt b/alchemist-cognitive-agents/src/main/kotlin/it/unibo/alchemist/model/implementations/actions/AbstractNavigationAction.kt index a90349369e..9c8dee70b7 100644 --- a/alchemist-cognitive-agents/src/main/kotlin/it/unibo/alchemist/model/implementations/actions/AbstractNavigationAction.kt +++ b/alchemist-cognitive-agents/src/main/kotlin/it/unibo/alchemist/model/implementations/actions/AbstractNavigationAction.kt @@ -11,7 +11,6 @@ package it.unibo.alchemist.model.implementations.actions import it.unibo.alchemist.model.interfaces.NavigationAction import it.unibo.alchemist.model.interfaces.NavigationStrategy -import it.unibo.alchemist.model.interfaces.OrientingPedestrian import it.unibo.alchemist.model.interfaces.Position import it.unibo.alchemist.model.interfaces.Reaction import it.unibo.alchemist.model.interfaces.environments.EnvironmentWithGraph @@ -26,75 +25,88 @@ import it.unibo.alchemist.model.implementations.actions.AbstractNavigationAction import it.unibo.alchemist.model.implementations.actions.AbstractNavigationAction.NavigationState.CROSSING_DOOR import it.unibo.alchemist.model.implementations.actions.AbstractNavigationAction.NavigationState.MOVING_TO_FINAL import it.unibo.alchemist.model.implementations.actions.AbstractNavigationAction.NavigationState.ARRIVED +import it.unibo.alchemist.model.interfaces.Node +import it.unibo.alchemist.model.interfaces.properties.OccupiesSpaceProperty +import it.unibo.alchemist.model.interfaces.Node.Companion.asProperty +import it.unibo.alchemist.model.interfaces.properties.OrientingProperty /** - * An abstract [NavigationAction], taking care of properly moving the pedestrian in the + * An abstract [NavigationAction], taking care of properly moving the node in the * environment while delegating the decision on where to move it to a [NavigationStrategy]. * * @param T the concentration type. - * @param P the [Position] type and [Vector] type for the space the pedestrian is into. + * @param P the [Position] type and [Vector] type for the space the node is into. * @param A the transformations supported by the shapes in this space. - * @param L the type of landmarks of the pedestrian's cognitive map. - * @param R the type of edges of the pedestrian's cognitive map, representing the [R]elations between landmarks. + * @param L the type of landmarks of the node's cognitive map. + * @param R the type of edges of the node's cognitive map, representing the [R]elations between landmarks. * @param N the type of nodes of the navigation graph provided by the [environment]. * @param E the type of edges of the navigation graph provided by the [environment]. */ abstract class AbstractNavigationAction( override val environment: EnvironmentWithGraph<*, T, P, A, N, E>, override val reaction: Reaction, - final override val pedestrian: OrientingPedestrian -) : AbstractSteeringAction(environment, reaction, pedestrian), + node: Node, +) : AbstractSteeringAction(environment, reaction, node), NavigationAction where P : Position

, P : Vector

, A : GeometricTransformation

, L : ConvexGeometricShape, N : ConvexGeometricShape { + override val navigatingNode = node + /** * The strategy used to navigate the environment. */ protected open lateinit var strategy: NavigationStrategy /** - * The position of the [pedestrian] in the [environment], this is cached and updated + * The position of the [navigatingNode] in the [environment], this is cached and updated * every time [update] is called so as to avoid potentially costly re-computations. */ override lateinit var pedestrianPosition: P /** - * The room (= environment's area) the [pedestrian] is into, this is cached and updated + * The room (= environment's area) the [navigatingNode] is into, this is cached and updated * every time [update] is called so as to avoid potentially costly re-computations. */ override var currentRoom: N? = null /** * Minimum distance to consider a target reached. Using zero (even with fuzzy equals) may lead to some - * boundary cases in which the pedestrian remains blocked due to how the environment manage collisions - * at present. This workaround allows to specify a minimum distance which is dependent on the pedestrian + * boundary cases in which the node remains blocked due to how the environment manage collisions + * at present. This workaround allows to specify a minimum distance which is dependent on the node * shape. In the future, something better could be done. */ - protected val minDistance: Double = pedestrian.shape.diameter + protected val minDistance: Double = node.asProperty>().shape.diameter /** * @returns true if the distance to [pedestrianPosition] is smaller than or equal to [minDistance]. */ protected open fun P.isReached(): Boolean = distanceTo(pedestrianPosition) <= minDistance + /** + * The navigation state. + */ protected var state: NavigationState = START + /** - * Caches the room the pedestrian is into when he/she starts moving. When the pedestrian is crossing a door, it - * contains the room being left. When in [NavigationState.MOVING_TO_FINAL], it contains the room the pedestrian - * was (and should be) into. It's used to detect if the pedestrian ended up in an unexpected room while moving. + * Caches the room the node is into when he/she starts moving. When the node is crossing a door, it + * contains the room being left. When in [NavigationState.MOVING_TO_FINAL], it contains the room the node + * was (and should be) into. It's used to detect if the node ended up in an unexpected room while moving. */ protected var previousRoom: N? = null + /** * Defined when crossing a door. See [crossDoor]. */ protected var crossingPoints: Pair? = null + /** * Defined when crossing a door. */ protected var expectedNewRoom: N? = null + /** * Defined in [NavigationState.MOVING_TO_FINAL]. */ @@ -109,16 +121,16 @@ abstract class AbstractNavigationAction( /** * Updates [pedestrianPosition] and [currentRoom], this can be costly. - * Depending on how [ConvexGeometricShape.contains] manage points on the boundary, the pedestrian could + * Depending on how [ConvexGeometricShape.contains] manage points on the boundary, the node could * be inside two (adjacent) rooms at once. This can happen in two cases: - * - when in [NavigationState.MOVING_TO_CROSSING_POINT_1] or [NavigationState.MOVING_TO_FINAL] and the pedestrian + * - when in [NavigationState.MOVING_TO_CROSSING_POINT_1] or [NavigationState.MOVING_TO_FINAL] and the node * is moving on [previousRoom]'s boundary. In such case [previousRoom] is used. * - when crossing a door or in [NavigationState.NEW_ROOM] and [expectedNewRoom] is adjacent to [previousRoom]. * In such case [expectedNewRoom] is used. * Otherwise the first room containing [pedestrianPosition] is used. */ protected open fun updateCachedVariables() { - pedestrianPosition = environment.getPosition(pedestrian) + pedestrianPosition = environment.getPosition(navigatingNode) currentRoom = when { (state == MOVING_TO_CROSSING_POINT_1 || state == MOVING_TO_FINAL) && previousRoom.orFail().contains(pedestrianPosition) -> @@ -131,11 +143,14 @@ abstract class AbstractNavigationAction( } } + /** + * Execute on navigation start. + */ protected open fun onStart() { state = when { currentRoom != null -> NEW_ROOM /* - * If the pedestrian cannot locate itself inside any room on start, it simply won't move. + * If the node cannot locate itself inside any room on start, it simply won't move. */ else -> ARRIVED } @@ -153,7 +168,7 @@ abstract class AbstractNavigationAction( protected open val E.target: N get() = environment.graph.getEdgeTarget(this) /** - * Moves the pedestrian across the provided [door], which must be among [doorsInSight]. + * Moves the node across the provided [door], which must be among [doorsInSight]. * Since connected rooms may be non-adjacent, a pair of [crossingPoints] has to be provided: * - the first point must belong to the current room's boundary and will be reached first, * - the second point must belong to the next room's boundary and will be pursued after @@ -198,7 +213,7 @@ abstract class AbstractNavigationAction( } /** - * The position the pedestrian wants to reach. + * The position the node wants to reach. */ val desiredPosition: P get() = when (state) { MOVING_TO_CROSSING_POINT_1 -> crossingPoints.orFail().first @@ -208,18 +223,19 @@ abstract class AbstractNavigationAction( /* * Always up to date current position. */ - else -> environment.getPosition(pedestrian) + else -> environment.getPosition(navigatingNode) } /** - * Updates the internal state but does not move the pedestrian. + * Updates the internal state but does not move the node. */ open fun update() { updateCachedVariables() when (state) { START -> onStart() NEW_ROOM -> currentRoom.orFail().let { - pedestrian.registerVisit(it) + navigatingNode.asProperty>() + .registerVisit(it) strategy.inNewRoom(it) } in MOVING_TO_CROSSING_POINT_1..MOVING_TO_FINAL -> moving() @@ -242,7 +258,7 @@ abstract class AbstractNavigationAction( */ MOVING_TO_CROSSING_POINT_2, /** - * When the second crossing point [isReached] (see [crossDoor]), the pedestrian may still be outside + * When the second crossing point [isReached] (see [crossDoor]), the node may still be outside * any room. In such case it moves towards [expectedNewRoom] centroid until he/she enters a room. */ CROSSING_DOOR, diff --git a/alchemist-cognitive-agents/src/main/kotlin/it/unibo/alchemist/model/implementations/actions/AbstractSteeringAction.kt b/alchemist-cognitive-agents/src/main/kotlin/it/unibo/alchemist/model/implementations/actions/AbstractSteeringAction.kt index 6e11ad37c2..b2b007fd31 100644 --- a/alchemist-cognitive-agents/src/main/kotlin/it/unibo/alchemist/model/implementations/actions/AbstractSteeringAction.kt +++ b/alchemist-cognitive-agents/src/main/kotlin/it/unibo/alchemist/model/implementations/actions/AbstractSteeringAction.kt @@ -12,18 +12,19 @@ package it.unibo.alchemist.model.implementations.actions import it.unibo.alchemist.model.interfaces.Action import it.unibo.alchemist.model.interfaces.Environment import it.unibo.alchemist.model.interfaces.Node -import it.unibo.alchemist.model.interfaces.Pedestrian import it.unibo.alchemist.model.interfaces.Position import it.unibo.alchemist.model.interfaces.Reaction import it.unibo.alchemist.model.interfaces.SteeringAction +import it.unibo.alchemist.model.interfaces.properties.PedestrianProperty import it.unibo.alchemist.model.interfaces.geometry.GeometricTransformation import it.unibo.alchemist.model.interfaces.geometry.Vector +import it.unibo.alchemist.model.interfaces.Node.Companion.asProperty /** * A [SteeringAction] in a vector space. The implementation of [nextPosition] is left to subclasses. */ abstract class AbstractSteeringAction( - env: Environment, + environment: Environment, /** * The reaction in which this action is executed. */ @@ -31,28 +32,40 @@ abstract class AbstractSteeringAction( /** * The owner of this action. */ - protected open val pedestrian: Pedestrian -) : AbstractMoveNode(env, pedestrian), + node: Node, +) : AbstractMoveNode(environment, node), SteeringAction - where P : Position

, P : Vector

, + where P : Position

, + P : Vector

, A : GeometricTransformation

{ /** - * The maximum distance the pedestrian can walk, this is a length. + * The maximum distance the node can walk, this is a length. */ - open val maxWalk: Double get() = pedestrian.speed() / reaction.rate + open val maxWalk: Double get() = node.asProperty>().speed() / reaction.rate + /** + * @return The next position where to move, in absolute or relative coordinates depending on the + * value of isAbsolute. + */ override fun getNextPosition(): P = nextPosition() - override fun getNode(): Pedestrian = pedestrian - - override fun cloneAction(node: Node, reaction: Reaction): Action = - requireNodeTypeAndProduce, AbstractSteeringAction>(node) { - cloneAction(it, reaction) - } - - protected abstract fun cloneAction(n: Pedestrian, r: Reaction): AbstractSteeringAction + /** + * This method allows to clone this action on a new node. It may result + * useful to support runtime creation of nodes with the same reaction + * programming, e.g. for morphogenesis. + * + * @param [node] + * The node where to clone this {@link Action} + * @param [reaction] + * The reaction to which the CURRENT action is assigned + * @return the cloned action + */ + abstract override fun cloneAction(node: Node, reaction: Reaction): AbstractSteeringAction + /** + * Ensures that the passed [node] has type [N]. + */ protected inline fun , S : Action<*>> requireNodeTypeAndProduce( node: Node<*>, builder: (N) -> S diff --git a/alchemist-cognitive-agents/src/main/kotlin/it/unibo/alchemist/model/implementations/actions/AbstractSteeringActionWithTarget.kt b/alchemist-cognitive-agents/src/main/kotlin/it/unibo/alchemist/model/implementations/actions/AbstractSteeringActionWithTarget.kt index ba5f18a597..d41b396b3f 100644 --- a/alchemist-cognitive-agents/src/main/kotlin/it/unibo/alchemist/model/implementations/actions/AbstractSteeringActionWithTarget.kt +++ b/alchemist-cognitive-agents/src/main/kotlin/it/unibo/alchemist/model/implementations/actions/AbstractSteeringActionWithTarget.kt @@ -1,7 +1,7 @@ package it.unibo.alchemist.model.implementations.actions import it.unibo.alchemist.model.interfaces.Environment -import it.unibo.alchemist.model.interfaces.Pedestrian +import it.unibo.alchemist.model.interfaces.Node import it.unibo.alchemist.model.interfaces.Position import it.unibo.alchemist.model.interfaces.Reaction import it.unibo.alchemist.model.interfaces.SteeringActionWithTarget @@ -13,8 +13,8 @@ import it.unibo.alchemist.model.interfaces.movestrategies.TargetSelectionStrateg * A [SteeringActionWithTarget] in a vector space. * * @param environment - * the environment inside which the pedestrian moves. - * @param pedestrian + * the environment inside which the node moves. + * @param node * the owner of this action. * @param targetSelectionStrategy * strategy selecting the next target. @@ -22,9 +22,9 @@ import it.unibo.alchemist.model.interfaces.movestrategies.TargetSelectionStrateg abstract class AbstractSteeringActionWithTarget( environment: Environment, reaction: Reaction, - pedestrian: Pedestrian, - private val targetSelectionStrategy: TargetSelectionStrategy -) : AbstractSteeringAction(environment, reaction, pedestrian), + node: Node, + private val targetSelectionStrategy: TargetSelectionStrategy, +) : AbstractSteeringAction(environment, reaction, node), SteeringActionWithTarget where P : Position

, P : Vector

, A : GeometricTransformation

{ @@ -32,14 +32,14 @@ abstract class AbstractSteeringActionWithTarget( constructor( environment: Environment, reaction: Reaction, - pedestrian: Pedestrian, - target: P - ) : this(environment, reaction, pedestrian, TargetSelectionStrategy { target }) + node: Node, + target: P, + ) : this(environment, reaction, node, TargetSelectionStrategy { target }) override fun target(): P = targetSelectionStrategy.target /** - * @returns the next relative position. By default, the pedestrian tries to move towards its [target]. + * @returns the next relative position. By default, the node tries to move towards its [target]. */ override fun nextPosition(): P = (target() - currentPosition).coerceAtMost(maxWalk) } diff --git a/alchemist-cognitive-agents/src/main/kotlin/it/unibo/alchemist/model/implementations/actions/CognitiveAgentArrive.kt b/alchemist-cognitive-agents/src/main/kotlin/it/unibo/alchemist/model/implementations/actions/CognitiveAgentArrive.kt index 6369a2efc6..8b138c72e1 100644 --- a/alchemist-cognitive-agents/src/main/kotlin/it/unibo/alchemist/model/implementations/actions/CognitiveAgentArrive.kt +++ b/alchemist-cognitive-agents/src/main/kotlin/it/unibo/alchemist/model/implementations/actions/CognitiveAgentArrive.kt @@ -2,57 +2,67 @@ package it.unibo.alchemist.model.implementations.actions import it.unibo.alchemist.model.cognitiveagents.impact.individual.Speed import it.unibo.alchemist.model.interfaces.Environment -import it.unibo.alchemist.model.interfaces.Pedestrian +import it.unibo.alchemist.model.interfaces.Node import it.unibo.alchemist.model.interfaces.Position import it.unibo.alchemist.model.interfaces.Reaction +import it.unibo.alchemist.model.interfaces.properties.PedestrianProperty import it.unibo.alchemist.model.interfaces.geometry.GeometricTransformation import it.unibo.alchemist.model.interfaces.geometry.Vector +import it.unibo.alchemist.model.interfaces.Node.Companion.asProperty /** * Move the agent towards a target position. * It is similar to [CognitiveAgentSeek] but attempts to arrive at the target position with a zero velocity. * - * @param env - * the environment inside which the pedestrian moves. + * @param environment + * the environment inside which the node moves. * @param reaction * the reaction which executes this action. - * @param pedestrian + * @param node * the owner of this action. * @param decelerationRadius - * the distance from which the pedestrian starts to decelerate. + * the distance from which the node starts to decelerate. * @param arrivalTolerance - * the distance at which the pedestrian is considered arrived to the target. + * the distance at which the node is considered arrived to the target. * @param target - * the position the pedestrian moves towards. + * the position the node moves towards. */ open class CognitiveAgentArrive( - protected val env: Environment, + environment: Environment, reaction: Reaction, - pedestrian: Pedestrian, + node: Node, protected val decelerationRadius: Double, protected val arrivalTolerance: Double, - protected val target: P -) : AbstractSteeringActionWithTarget(env, reaction, pedestrian, target) - where P : Position

, P : Vector

, + protected val target: P, +) : AbstractSteeringActionWithTarget(environment, reaction, node, target) + where P : Position

, + P : Vector

, A : GeometricTransformation

{ constructor( - env: Environment, + environment: Environment, reaction: Reaction, - pedestrian: Pedestrian, + node: Node, decelerationRadius: Double, arrivalTolerance: Double, - vararg coordinates: Number - ) : this(env, reaction, pedestrian, decelerationRadius, arrivalTolerance, env.makePosition(*coordinates)) + vararg coordinates: Number, + ) : this( + environment, + reaction, + node, + decelerationRadius, + arrivalTolerance, + environment.makePosition(*coordinates), + ) override val maxWalk: Double get() = with((currentPosition as Vector

).distanceTo(target)) { when { this < arrivalTolerance -> 0.0 this < decelerationRadius -> Speed.default * this / decelerationRadius / reaction.rate - else -> pedestrian.speed() / reaction.rate + else -> node.asProperty>().speed() / reaction.rate } } - override fun cloneAction(n: Pedestrian, r: Reaction): CognitiveAgentArrive = - CognitiveAgentArrive(env, r, n, decelerationRadius, arrivalTolerance, target) + override fun cloneAction(node: Node, reaction: Reaction): CognitiveAgentArrive = + CognitiveAgentArrive(environment, reaction, node, decelerationRadius, arrivalTolerance, target) } diff --git a/alchemist-cognitive-agents/src/main/kotlin/it/unibo/alchemist/model/implementations/actions/CognitiveAgentAvoidLayer.kt b/alchemist-cognitive-agents/src/main/kotlin/it/unibo/alchemist/model/implementations/actions/CognitiveAgentAvoidLayer.kt index 1b7a7ba945..643424babc 100644 --- a/alchemist-cognitive-agents/src/main/kotlin/it/unibo/alchemist/model/implementations/actions/CognitiveAgentAvoidLayer.kt +++ b/alchemist-cognitive-agents/src/main/kotlin/it/unibo/alchemist/model/implementations/actions/CognitiveAgentAvoidLayer.kt @@ -1,54 +1,54 @@ package it.unibo.alchemist.model.implementations.actions -import it.unibo.alchemist.model.implementations.nodes.AbstractCognitivePedestrian import it.unibo.alchemist.model.implementations.positions.Euclidean2DPosition import it.unibo.alchemist.model.interfaces.EnvironmentWithObstacles import it.unibo.alchemist.model.interfaces.Molecule -import it.unibo.alchemist.model.interfaces.Pedestrian -import it.unibo.alchemist.model.interfaces.Pedestrian2D +import it.unibo.alchemist.model.interfaces.Node import it.unibo.alchemist.model.interfaces.Reaction import it.unibo.alchemist.model.interfaces.environments.Euclidean2DEnvironment +import it.unibo.alchemist.model.interfaces.Node.Companion.asPropertyOrNull +import it.unibo.alchemist.model.interfaces.properties.CognitiveProperty /** - * Move the pedestrian towards positions of the environment with a low concentration of the target molecule. + * Move the node towards positions of the environment with a low concentration of the target molecule. * * @param environment - * the environment inside which the pedestrian moves. + * the environment inside which the node moves. * @param reaction * the reaction which executes this action. - * @param pedestrian + * @param node * the owner of this action. * @param targetMolecule * the {@link Molecule} you want to know the concentration in the different positions of the environment. * @param viewDepth - * the depth of view of the pedestrian, defaults to infinity. + * the depth of view of the node, defaults to infinity. */ class CognitiveAgentAvoidLayer @JvmOverloads constructor( environment: Euclidean2DEnvironment, reaction: Reaction, - pedestrian: Pedestrian2D, + node: Node, targetMolecule: Molecule, - private val viewDepth: Double = Double.POSITIVE_INFINITY -) : AbstractLayerAction(environment, reaction, pedestrian, targetMolecule) { + private val viewDepth: Double = Double.POSITIVE_INFINITY, +) : AbstractLayerAction(environment, reaction, node, targetMolecule) { private val followScalarField = getLayerOrFail().let { layer -> - CognitiveAgentFollowScalarField(environment, reaction, pedestrian, layer.center()) { + CognitiveAgentFollowScalarField(environment, reaction, node, layer.center()) { /* - * Moves the pedestrian where the concentration is lower. + * Moves the node where the concentration is lower. */ -layer.concentrationIn(it) } } - override fun cloneAction(n: Pedestrian2D, r: Reaction): CognitiveAgentAvoidLayer = - CognitiveAgentAvoidLayer(environment, r, n, targetMolecule, viewDepth) + override fun cloneAction(node: Node, reaction: Reaction): CognitiveAgentAvoidLayer = + CognitiveAgentAvoidLayer(environment, reaction, node, targetMolecule, viewDepth) /** - * @returns the next relative position. The pedestrian is moved only if he/she percepts the danger + * @returns the next relative position. The node is moved only if he/she percepts the danger * (either because it is in sight or due to social contagion), otherwise a zero vector is returned. */ override fun nextPosition(): Euclidean2DPosition = when { - pedestrian.wantsToEscape() || isDangerInSight() -> followScalarField.nextPosition() + node.wantsToEscape() || isDangerInSight() -> followScalarField.nextPosition() else -> environment.origin } @@ -58,7 +58,7 @@ class CognitiveAgentAvoidLayer @JvmOverloads constructor( */ @Suppress("UNCHECKED_CAST") private fun isDangerInSight(): Boolean = getLayerOrFail().center()?.let { center -> - val currentPosition = environment.getPosition(pedestrian) + val currentPosition = environment.getPosition(node) /* * environment is euclidean, so if it has obstacles it must be an * EnvironmentWithObstacles<*, *, Euclidean2DPosition>. Since generic types can't be checked at runtime, this @@ -70,7 +70,10 @@ class CognitiveAgentAvoidLayer @JvmOverloads constructor( center.distanceTo(currentPosition) <= viewDepth && !visualTrajectoryOccluded } ?: true - private fun Pedestrian<*, *, *>.wantsToEscape(): Boolean = - this is AbstractCognitivePedestrian<*, *, *, *> && - this.danger == targetMolecule && cognitiveModel.wantsToEscape() + private fun Node.wantsToEscape(): Boolean { + val cognitiveProperty = asPropertyOrNull>() + return cognitiveProperty != null && + cognitiveProperty.danger == targetMolecule && + cognitiveProperty.cognitiveModel.wantsToEscape() + } } diff --git a/alchemist-cognitive-agents/src/main/kotlin/it/unibo/alchemist/model/implementations/actions/CognitiveAgentCohesion.kt b/alchemist-cognitive-agents/src/main/kotlin/it/unibo/alchemist/model/implementations/actions/CognitiveAgentCohesion.kt index f36b47da16..c2b1ea3bf6 100644 --- a/alchemist-cognitive-agents/src/main/kotlin/it/unibo/alchemist/model/implementations/actions/CognitiveAgentCohesion.kt +++ b/alchemist-cognitive-agents/src/main/kotlin/it/unibo/alchemist/model/implementations/actions/CognitiveAgentCohesion.kt @@ -1,33 +1,36 @@ package it.unibo.alchemist.model.implementations.actions import it.unibo.alchemist.model.interfaces.Environment -import it.unibo.alchemist.model.interfaces.Pedestrian +import it.unibo.alchemist.model.interfaces.Node import it.unibo.alchemist.model.interfaces.Position import it.unibo.alchemist.model.interfaces.Reaction import it.unibo.alchemist.model.interfaces.geometry.GeometricTransformation import it.unibo.alchemist.model.interfaces.geometry.Vector +import it.unibo.alchemist.model.interfaces.Node.Companion.asProperty +import it.unibo.alchemist.model.interfaces.properties.SocialProperty /** * Move the agent towards the other members of his group. * - * @param env - * the environment inside which the pedestrian moves. + * @param environment + * the environment inside which the node moves. * @param reaction * the reaction which executes this action. - * @param pedestrian + * @param node * the owner of this action. */ class CognitiveAgentCohesion( - env: Environment, + environment: Environment, reaction: Reaction, - pedestrian: Pedestrian -) : AbstractGroupSteeringAction(env, reaction, pedestrian) + node: Node +) : AbstractGroupSteeringAction(environment, reaction, node) where P : Position

, P : Vector

, A : GeometricTransformation

{ - override fun cloneAction(n: Pedestrian, r: Reaction) = CognitiveAgentCohesion(env, r, n) + override fun cloneAction(node: Node, reaction: Reaction): CognitiveAgentCohesion = + CognitiveAgentCohesion(environment, reaction, node) override fun nextPosition(): P = (centroid() - currentPosition).coerceAtMost(maxWalk) - override fun group(): List> = pedestrian.membershipGroup.members + override fun group() = node.asProperty>().group.members } diff --git a/alchemist-cognitive-agents/src/main/kotlin/it/unibo/alchemist/model/implementations/actions/CognitiveAgentCombineSteering.kt b/alchemist-cognitive-agents/src/main/kotlin/it/unibo/alchemist/model/implementations/actions/CognitiveAgentCombineSteering.kt index bd49cdb1e6..3328114823 100644 --- a/alchemist-cognitive-agents/src/main/kotlin/it/unibo/alchemist/model/implementations/actions/CognitiveAgentCombineSteering.kt +++ b/alchemist-cognitive-agents/src/main/kotlin/it/unibo/alchemist/model/implementations/actions/CognitiveAgentCombineSteering.kt @@ -1,7 +1,7 @@ package it.unibo.alchemist.model.implementations.actions import it.unibo.alchemist.model.interfaces.Environment -import it.unibo.alchemist.model.interfaces.Pedestrian +import it.unibo.alchemist.model.interfaces.Node import it.unibo.alchemist.model.interfaces.Position import it.unibo.alchemist.model.interfaces.Reaction import it.unibo.alchemist.model.interfaces.SteeringAction @@ -12,27 +12,27 @@ import it.unibo.alchemist.model.interfaces.geometry.Vector /** * Combination of multiple steering actions. * - * @param env - * the environment inside which the pedestrian moves. - * @param pedestrian + * @param environment + * the environment inside which the node moves. + * @param node * the owner of this action. * @param actions - * the list of actions to combine to determine the pedestrian movement. + * the list of actions to combine to determine the node movement. * @param steerStrategy * the logic according to the steering actions are combined. */ class CognitiveAgentCombineSteering( - private val env: Environment, + environment: Environment, reaction: Reaction, - pedestrian: Pedestrian, + node: Node, private val actions: List>, private val steerStrategy: SteeringStrategy -) : AbstractSteeringAction(env, reaction, pedestrian) +) : AbstractSteeringAction(environment, reaction, node) where P : Position

, P : Vector

, A : GeometricTransformation

{ - override fun cloneAction(n: Pedestrian, r: Reaction) = - CognitiveAgentCombineSteering(env, r, n, actions, steerStrategy) + override fun cloneAction(node: Node, reaction: Reaction): CognitiveAgentCombineSteering = + CognitiveAgentCombineSteering(environment, reaction, node, actions, steerStrategy) override fun nextPosition(): P = steerStrategy.computeNextPosition(actions).coerceAtMost(maxWalk) } diff --git a/alchemist-cognitive-agents/src/main/kotlin/it/unibo/alchemist/model/implementations/actions/CognitiveAgentExplore.kt b/alchemist-cognitive-agents/src/main/kotlin/it/unibo/alchemist/model/implementations/actions/CognitiveAgentExplore.kt index 1468de12b6..20fd2b463e 100644 --- a/alchemist-cognitive-agents/src/main/kotlin/it/unibo/alchemist/model/implementations/actions/CognitiveAgentExplore.kt +++ b/alchemist-cognitive-agents/src/main/kotlin/it/unibo/alchemist/model/implementations/actions/CognitiveAgentExplore.kt @@ -11,7 +11,7 @@ package it.unibo.alchemist.model.implementations.actions import it.unibo.alchemist.model.implementations.actions.navigationstrategies.Exploring import it.unibo.alchemist.model.interfaces.NavigationAction -import it.unibo.alchemist.model.interfaces.OrientingPedestrian2D +import it.unibo.alchemist.model.interfaces.Node import it.unibo.alchemist.model.interfaces.Reaction import it.unibo.alchemist.model.interfaces.environments.Euclidean2DEnvironmentWithGraph import it.unibo.alchemist.model.interfaces.geometry.euclidean2d.ConvexPolygon @@ -22,14 +22,14 @@ import it.unibo.alchemist.model.interfaces.geometry.euclidean2d.graph.Euclidean2 * A [NavigationAction] using [Exploring] navigation strategy. * * @param T the concentration type. - * @param L the type of landmarks of the pedestrian's cognitive map. - * @param R the type of edges of the pedestrian's cognitive map, representing the [R]elations between landmarks. + * @param L the type of landmarks of the node's cognitive map. + * @param R the type of edges of the node's cognitive map, representing the [R]elations between landmarks. */ class CognitiveAgentExplore( environment: Euclidean2DEnvironmentWithGraph<*, T, ConvexPolygon, Euclidean2DPassage>, reaction: Reaction, - pedestrian: OrientingPedestrian2D -) : CognitiveAgentNavigationAction2D(environment, reaction, pedestrian) { + node: Node +) : CognitiveAgentNavigationAction2D(environment, reaction, node) { init { strategy = Exploring(this) diff --git a/alchemist-cognitive-agents/src/main/kotlin/it/unibo/alchemist/model/implementations/actions/CognitiveAgentFlee.kt b/alchemist-cognitive-agents/src/main/kotlin/it/unibo/alchemist/model/implementations/actions/CognitiveAgentFlee.kt index 6fb947cc19..e776ae5a4c 100644 --- a/alchemist-cognitive-agents/src/main/kotlin/it/unibo/alchemist/model/implementations/actions/CognitiveAgentFlee.kt +++ b/alchemist-cognitive-agents/src/main/kotlin/it/unibo/alchemist/model/implementations/actions/CognitiveAgentFlee.kt @@ -1,7 +1,7 @@ package it.unibo.alchemist.model.implementations.actions import it.unibo.alchemist.model.interfaces.Environment -import it.unibo.alchemist.model.interfaces.Pedestrian +import it.unibo.alchemist.model.interfaces.Node import it.unibo.alchemist.model.interfaces.Position import it.unibo.alchemist.model.interfaces.Reaction import it.unibo.alchemist.model.interfaces.geometry.GeometricTransformation @@ -10,28 +10,28 @@ import it.unibo.alchemist.model.interfaces.geometry.Vector /** * Move the agent away from a target position. It's the opposite of [CognitiveAgentSeek]. * - * @param env - * the environment inside which the pedestrian moves. + * @param environment + * the environment inside which the node moves. * @param reaction * the reaction which executes this action. - * @param pedestrian + * @param node * the owner of this action. * @param coords - * the coordinates of the position the pedestrian moves away. + * the coordinates of the position the node moves away. */ open class CognitiveAgentFlee( - private val env: Environment, + environment: Environment, reaction: Reaction, - pedestrian: Pedestrian, + node: Node, vararg coords: Double -) : AbstractSteeringAction(env, reaction, pedestrian) +) : AbstractSteeringAction(environment, reaction, node) where P : Position

, P : Vector

, A : GeometricTransformation

{ - private val danger: P = env.makePosition(*coords.toTypedArray()) + private val danger: P = environment.makePosition(*coords.toTypedArray()) - override fun cloneAction(n: Pedestrian, r: Reaction) = - CognitiveAgentFlee(env, r, n, *danger.coordinates) + override fun cloneAction(node: Node, reaction: Reaction): CognitiveAgentFlee = + CognitiveAgentFlee(environment, reaction, node, *danger.coordinates) override fun nextPosition(): P = (currentPosition - danger).resized(maxWalk) } diff --git a/alchemist-cognitive-agents/src/main/kotlin/it/unibo/alchemist/model/implementations/actions/CognitiveAgentFollowLayer.kt b/alchemist-cognitive-agents/src/main/kotlin/it/unibo/alchemist/model/implementations/actions/CognitiveAgentFollowLayer.kt index 9b5019db2d..c61516fabe 100644 --- a/alchemist-cognitive-agents/src/main/kotlin/it/unibo/alchemist/model/implementations/actions/CognitiveAgentFollowLayer.kt +++ b/alchemist-cognitive-agents/src/main/kotlin/it/unibo/alchemist/model/implementations/actions/CognitiveAgentFollowLayer.kt @@ -2,37 +2,37 @@ package it.unibo.alchemist.model.implementations.actions import it.unibo.alchemist.model.implementations.positions.Euclidean2DPosition import it.unibo.alchemist.model.interfaces.Molecule -import it.unibo.alchemist.model.interfaces.Pedestrian2D +import it.unibo.alchemist.model.interfaces.Node import it.unibo.alchemist.model.interfaces.Reaction import it.unibo.alchemist.model.interfaces.environments.Euclidean2DEnvironment /** - * Move the pedestrian towards positions of the environment with a high concentration of the target molecule. + * Move the node towards positions of the environment with a high concentration of the target molecule. * - * @param env - * the environment inside which the pedestrian moves. + * @param euclidean + * the environment inside which the node moves. * @param reaction * the reaction which executes this action. - * @param pedestrian + * @param node * the owner of this action. * @param targetMolecule * the {@link Molecule} you want to know the concentration in the different positions of the environment. */ open class CognitiveAgentFollowLayer( - env: Euclidean2DEnvironment, + euclidean: Euclidean2DEnvironment, reaction: Reaction, - pedestrian: Pedestrian2D, + node: Node, targetMolecule: Molecule -) : AbstractLayerAction(env, reaction, pedestrian, targetMolecule) { +) : AbstractLayerAction(euclidean, reaction, node, targetMolecule) { private val followScalarField = getLayerOrFail().let { layer -> - CognitiveAgentFollowScalarField(environment, reaction, pedestrian, layer.center()) { + CognitiveAgentFollowScalarField(environment, reaction, node, layer.center()) { layer.concentrationIn(it) } } override fun nextPosition(): Euclidean2DPosition = followScalarField.nextPosition() - override fun cloneAction(n: Pedestrian2D, r: Reaction): CognitiveAgentFollowLayer = - CognitiveAgentFollowLayer(environment, r, n, targetMolecule) + override fun cloneAction(node: Node, reaction: Reaction): CognitiveAgentFollowLayer = + CognitiveAgentFollowLayer(environment, reaction, node, targetMolecule) } diff --git a/alchemist-cognitive-agents/src/main/kotlin/it/unibo/alchemist/model/implementations/actions/CognitiveAgentFollowRoute.kt b/alchemist-cognitive-agents/src/main/kotlin/it/unibo/alchemist/model/implementations/actions/CognitiveAgentFollowRoute.kt index f4f6723727..0210c2b8f2 100644 --- a/alchemist-cognitive-agents/src/main/kotlin/it/unibo/alchemist/model/implementations/actions/CognitiveAgentFollowRoute.kt +++ b/alchemist-cognitive-agents/src/main/kotlin/it/unibo/alchemist/model/implementations/actions/CognitiveAgentFollowRoute.kt @@ -11,7 +11,7 @@ package it.unibo.alchemist.model.implementations.actions import it.unibo.alchemist.model.implementations.actions.navigationstrategies.RouteFollowing import it.unibo.alchemist.model.interfaces.NavigationAction -import it.unibo.alchemist.model.interfaces.OrientingPedestrian2D +import it.unibo.alchemist.model.interfaces.Node import it.unibo.alchemist.model.interfaces.Reaction import it.unibo.alchemist.model.interfaces.environments.Euclidean2DEnvironmentWithGraph import it.unibo.alchemist.model.interfaces.geometry.euclidean2d.ConvexPolygon @@ -22,15 +22,15 @@ import it.unibo.alchemist.model.interfaces.geometry.euclidean2d.graph.Euclidean2 * A [NavigationAction] using [RouteFollowing] navigation strategy. * * @param T the concentration type. - * @param L the type of landmarks of the pedestrian's cognitive map. - * @param R the type of edges of the pedestrian's cognitive map, representing the [R]elations between landmarks. + * @param L the type of landmarks of the node's cognitive map. + * @param R the type of edges of the node's cognitive map, representing the [R]elations between landmarks. */ class CognitiveAgentFollowRoute( environment: Euclidean2DEnvironmentWithGraph<*, T, ConvexPolygon, Euclidean2DPassage>, reaction: Reaction, - pedestrian: OrientingPedestrian2D, + node: Node, vararg route: Number -) : CognitiveAgentNavigationAction2D(environment, reaction, pedestrian) { +) : CognitiveAgentNavigationAction2D(environment, reaction, node) { init { strategy = RouteFollowing(this, route.toPositions(environment)) diff --git a/alchemist-cognitive-agents/src/main/kotlin/it/unibo/alchemist/model/implementations/actions/CognitiveAgentFollowScalarField.kt b/alchemist-cognitive-agents/src/main/kotlin/it/unibo/alchemist/model/implementations/actions/CognitiveAgentFollowScalarField.kt index 53079f06e8..ade89759d0 100644 --- a/alchemist-cognitive-agents/src/main/kotlin/it/unibo/alchemist/model/implementations/actions/CognitiveAgentFollowScalarField.kt +++ b/alchemist-cognitive-agents/src/main/kotlin/it/unibo/alchemist/model/implementations/actions/CognitiveAgentFollowScalarField.kt @@ -11,7 +11,7 @@ package it.unibo.alchemist.model.implementations.actions import it.unibo.alchemist.model.interfaces.Environment import it.unibo.alchemist.model.interfaces.EnvironmentWithObstacles -import it.unibo.alchemist.model.interfaces.Pedestrian +import it.unibo.alchemist.model.interfaces.Node import it.unibo.alchemist.model.interfaces.Position2D import it.unibo.alchemist.model.interfaces.Reaction import it.unibo.alchemist.model.interfaces.environments.PhysicsEnvironment @@ -19,15 +19,12 @@ import it.unibo.alchemist.model.interfaces.geometry.GeometricTransformation import it.unibo.alchemist.model.interfaces.geometry.Vector2D /** - * Moves the pedestrian where the given scalar field is higher. + * Moves the node where the given scalar field is higher. */ class CognitiveAgentFollowScalarField( - /** - * The environment the pedestrian is into. - */ - private val env: Environment, + environment: Environment, reaction: Reaction, - pedestrian: Pedestrian, + node: Node, /** * The position of either maximum or minimum value of the scalar field, can be null if such a position doesn't * exist or isn't known. Its use is explained in [nextPosition]. @@ -36,17 +33,17 @@ class CognitiveAgentFollowScalarField( /** * A function mapping each position to a scalar value (= the scalar field). */ - private val valueIn: (P) -> Double -) : AbstractSteeringAction(env, reaction, pedestrian) + private val valueIn: (P) -> Double, +) : AbstractSteeringAction(environment, reaction, node) where P : Position2D

, P : Vector2D

, A : GeometricTransformation

{ /** - * @returns the next relative position reached by the pedestrian. The set of reachable positions is discretized + * @returns the next relative position reached by the node. The set of reachable positions is discretized * using [Vector2D.surrounding] from the current position (radius is [maxWalk]). If the scalar field has a * [center], two more positions are taken into account: one towards the center along the direction connecting the * latter to the current position, and another away from the center along the same direction. The position with - * maximum value is then selected: if its value is higher than the current one, the pedestrian moves there. + * maximum value is then selected: if its value is higher than the current one, the node moves there. * Otherwise, it doesn't move at all. */ override fun nextPosition(): P = currentPosition.let { currentPosition -> @@ -64,14 +61,18 @@ class CognitiveAgentFollowScalarField( .maxOr(currentPosition) - currentPosition } - override fun cloneAction(n: Pedestrian, r: Reaction) = - CognitiveAgentFollowScalarField(env, r, n, center, valueIn) + override fun cloneAction(node: Node, reaction: Reaction): CognitiveAgentFollowScalarField = + CognitiveAgentFollowScalarField(environment, reaction, node, center, valueIn) private fun Sequence

.enforceObstacles(currentPosition: P): Sequence

= - if (env is EnvironmentWithObstacles<*, T, P>) map { env.next(currentPosition, it) } else this + if (environment is EnvironmentWithObstacles<*, T, P>) map { + (environment as EnvironmentWithObstacles<*, T, P>).next(currentPosition, it) + } else this private fun Sequence

.enforceOthers(): Sequence

= - if (env is PhysicsEnvironment) map { env.farthestPositionReachable(pedestrian, it) } else this + if (environment is PhysicsEnvironment) map { + (environment as PhysicsEnvironment).farthestPositionReachable(node, it) + } else this private fun Sequence

.maxOr(position: P): P = maxByOrNull { valueIn(it) } diff --git a/alchemist-cognitive-agents/src/main/kotlin/it/unibo/alchemist/model/implementations/actions/CognitiveAgentGoalOrientedExplore.kt b/alchemist-cognitive-agents/src/main/kotlin/it/unibo/alchemist/model/implementations/actions/CognitiveAgentGoalOrientedExplore.kt index 06c52c8f04..d75b1039f0 100644 --- a/alchemist-cognitive-agents/src/main/kotlin/it/unibo/alchemist/model/implementations/actions/CognitiveAgentGoalOrientedExplore.kt +++ b/alchemist-cognitive-agents/src/main/kotlin/it/unibo/alchemist/model/implementations/actions/CognitiveAgentGoalOrientedExplore.kt @@ -11,7 +11,7 @@ package it.unibo.alchemist.model.implementations.actions import it.unibo.alchemist.model.implementations.actions.navigationstrategies.GoalOrientedExploring import it.unibo.alchemist.model.interfaces.NavigationAction -import it.unibo.alchemist.model.interfaces.OrientingPedestrian2D +import it.unibo.alchemist.model.interfaces.Node import it.unibo.alchemist.model.interfaces.Reaction import it.unibo.alchemist.model.interfaces.environments.Euclidean2DEnvironmentWithGraph import it.unibo.alchemist.model.interfaces.geometry.euclidean2d.ConvexPolygon @@ -22,15 +22,15 @@ import it.unibo.alchemist.model.interfaces.geometry.euclidean2d.graph.Euclidean2 * A [NavigationAction] using [GoalOrientedExploring] navigation strategy. * * @param T the concentration type. - * @param L the type of landmarks of the pedestrian's cognitive map. - * @param R the type of edges of the pedestrian's cognitive map, representing the [R]elations between landmarks. + * @param L the type of landmarks of the node's cognitive map. + * @param R the type of edges of the node's cognitive map, representing the [R]elations between landmarks. */ class CognitiveAgentGoalOrientedExplore( environment: Euclidean2DEnvironmentWithGraph<*, T, ConvexPolygon, Euclidean2DPassage>, reaction: Reaction, - pedestrian: OrientingPedestrian2D, + node: Node, vararg unknownDestinations: Number -) : CognitiveAgentNavigationAction2D(environment, reaction, pedestrian) { +) : CognitiveAgentNavigationAction2D(environment, reaction, node) { init { strategy = GoalOrientedExploring(this, unknownDestinations.toPositions(environment)) diff --git a/alchemist-cognitive-agents/src/main/kotlin/it/unibo/alchemist/model/implementations/actions/CognitiveAgentNavigationAction2D.kt b/alchemist-cognitive-agents/src/main/kotlin/it/unibo/alchemist/model/implementations/actions/CognitiveAgentNavigationAction2D.kt index 2c943fd5f0..f656b5a8ff 100644 --- a/alchemist-cognitive-agents/src/main/kotlin/it/unibo/alchemist/model/implementations/actions/CognitiveAgentNavigationAction2D.kt +++ b/alchemist-cognitive-agents/src/main/kotlin/it/unibo/alchemist/model/implementations/actions/CognitiveAgentNavigationAction2D.kt @@ -11,8 +11,7 @@ package it.unibo.alchemist.model.implementations.actions import it.unibo.alchemist.model.implementations.positions.Euclidean2DPosition import it.unibo.alchemist.model.interfaces.NavigationAction2D -import it.unibo.alchemist.model.interfaces.OrientingPedestrian2D -import it.unibo.alchemist.model.interfaces.Pedestrian +import it.unibo.alchemist.model.interfaces.Node import it.unibo.alchemist.model.interfaces.Reaction import it.unibo.alchemist.model.interfaces.environments.Euclidean2DEnvironmentWithGraph import it.unibo.alchemist.model.interfaces.geometry.euclidean2d.ConvexPolygon @@ -29,23 +28,23 @@ private typealias AbstractNavigationAction2D = * contains [ConvexPolygon]al nodes and [Euclidean2DPassage]s as edges. * * @param T the concentration type. - * @param L the type of landmarks of the pedestrian's cognitive map. - * @param R the type of edges of the pedestrian's cognitive map, representing the [R]elations between landmarks. + * @param L the type of landmarks of the node's cognitive map. + * @param R the type of edges of the node's cognitive map, representing the [R]elations between landmarks. */ open class CognitiveAgentNavigationAction2D( override val environment: Euclidean2DEnvironmentWithGraph<*, T, ConvexPolygon, Euclidean2DPassage>, reaction: Reaction, - pedestrian: OrientingPedestrian2D, + node: Node, /** - * When crossing [Euclidean2DPassage]s, the pedestrian is pushed away from the wall of + * When crossing [Euclidean2DPassage]s, the node is pushed away from the wall of * a quantity equal to (this factor * the width of the passage). This is performed to prevent - * the pedestrian from moving attached to the wall. This factor must be in [0.0, 0.5). + * the node from moving attached to the wall. This factor must be in [0.0, 0.5). */ - private val wallRepulsionFactor: Double = DEFAULT_WALL_REPULSION_FACTOR + private val wallRepulsionFactor: Double = DEFAULT_WALL_REPULSION_FACTOR, ) : AbstractNavigationAction2D( environment, reaction, - pedestrian + node ) { companion object { @@ -84,7 +83,7 @@ open class CognitiveAgentNavigationAction2D( super.moving() if (state == NavigationState.MOVING_TO_CROSSING_POINT_1) { /* - * When moving towards a door the most convenient crossing point may change depending on the pedestrian + * When moving towards a door the most convenient crossing point may change depending on the node * position. Recomputing the crossing points allows more natural movement (even though it's costly). */ if (currentRoom != null) { @@ -95,13 +94,12 @@ open class CognitiveAgentNavigationAction2D( override fun nextPosition(): Euclidean2DPosition { update() - return CognitiveAgentSeek2D(environment, reaction, pedestrian, desiredPosition).nextPosition + return CognitiveAgentSeek2D(environment, reaction, navigatingNode, desiredPosition).nextPosition } - override fun cloneAction(n: Pedestrian, r: Reaction) = - requireNodeTypeAndProduce, CognitiveAgentNavigationAction2D>(n) { - val clone = CognitiveAgentNavigationAction2D(environment, r, it, wallRepulsionFactor) - clone.strategy = this.strategy - return clone - } + override fun cloneAction(node: Node, reaction: Reaction): CognitiveAgentNavigationAction2D { + val clone = CognitiveAgentNavigationAction2D(environment, reaction, node, wallRepulsionFactor) + clone.strategy = this.strategy + return clone + } } diff --git a/alchemist-cognitive-agents/src/main/kotlin/it/unibo/alchemist/model/implementations/actions/CognitiveAgentObstacleAvoidance.kt b/alchemist-cognitive-agents/src/main/kotlin/it/unibo/alchemist/model/implementations/actions/CognitiveAgentObstacleAvoidance.kt index 5560b58b22..c8ed66dd9a 100644 --- a/alchemist-cognitive-agents/src/main/kotlin/it/unibo/alchemist/model/implementations/actions/CognitiveAgentObstacleAvoidance.kt +++ b/alchemist-cognitive-agents/src/main/kotlin/it/unibo/alchemist/model/implementations/actions/CognitiveAgentObstacleAvoidance.kt @@ -2,9 +2,8 @@ package it.unibo.alchemist.model.implementations.actions import it.unibo.alchemist.model.implementations.positions.Euclidean2DPosition import it.unibo.alchemist.model.implementations.reactions.SteeringBehavior +import it.unibo.alchemist.model.interfaces.Node import it.unibo.alchemist.model.interfaces.Obstacle2D -import it.unibo.alchemist.model.interfaces.Pedestrian -import it.unibo.alchemist.model.interfaces.Pedestrian2D import it.unibo.alchemist.model.interfaces.Reaction import it.unibo.alchemist.model.interfaces.environments.Environment2DWithObstacles import it.unibo.alchemist.model.interfaces.geometry.euclidean2d.Euclidean2DTransformation @@ -12,45 +11,44 @@ import it.unibo.alchemist.model.interfaces.geometry.euclidean2d.Euclidean2DTrans /** * Move the agent avoiding potential obstacles in its path. * - * @param env - * the environment inside which the pedestrian moves. + * @param environment + * the environment inside which the node moves. * @param reaction * the reaction which executes this action. - * @param pedestrian + * @param node * the owner of this action. * @param proximityRange - * the distance at which an obstacle is perceived by the pedestrian. + * the distance at which an obstacle is perceived by the node. */ class CognitiveAgentObstacleAvoidance, T>( - private val env: Environment2DWithObstacles, + private val environment: Environment2DWithObstacles, override val reaction: SteeringBehavior, - pedestrian: Pedestrian2D, - private val proximityRange: Double -) : AbstractSteeringAction(env, reaction, pedestrian) { + node: Node, + private val proximityRange: Double, +) : AbstractSteeringAction(environment, reaction, node) { - override fun cloneAction(n: Pedestrian, r: Reaction) = - requireNodeTypeAndProduce, CognitiveAgentObstacleAvoidance>(n) { - require(r is SteeringBehavior) { "steering behavior needed but found $reaction" } - CognitiveAgentObstacleAvoidance(env, r, it, proximityRange) - } + override fun cloneAction(node: Node, reaction: Reaction): CognitiveAgentObstacleAvoidance = + if (reaction is SteeringBehavior) + CognitiveAgentObstacleAvoidance(environment, reaction, node, proximityRange) + else throw IllegalArgumentException("steering behavior needed but found ${this.reaction}") override fun nextPosition(): Euclidean2DPosition = target().let { target -> - env.getObstaclesInRange(currentPosition, proximityRange) + environment.getObstaclesInRange(currentPosition, proximityRange) .asSequence() .map { obstacle: W -> obstacle.nearestIntersection(currentPosition, target) to obstacle.bounds2D } .minByOrNull { (intersection, _) -> currentPosition.distanceTo(intersection) } - ?.let { (intersection, bound) -> intersection to env.makePosition(bound.centerX, bound.centerY) } + ?.let { (intersection, bound) -> intersection to environment.makePosition(bound.centerX, bound.centerY) } ?.let { (intersection, center) -> (intersection - center).coerceAtMost(maxWalk) } /* * Otherwise we just don't apply any repulsion force. */ - ?: env.origin + ?: environment.origin } /** - * Computes the target of the pedestrian, delegating to [reaction].steerStrategy.computeTarget. + * Computes the target of the node, delegating to [reaction].steerStrategy.computeTarget. */ private fun target(): Euclidean2DPosition = with(reaction) { steerStrategy.computeTarget(steerActions().filterNot { it is CognitiveAgentObstacleAvoidance<*, *> }) diff --git a/alchemist-cognitive-agents/src/main/kotlin/it/unibo/alchemist/model/implementations/actions/CognitiveAgentPursue.kt b/alchemist-cognitive-agents/src/main/kotlin/it/unibo/alchemist/model/implementations/actions/CognitiveAgentPursue.kt index 38da878018..7b7513e423 100644 --- a/alchemist-cognitive-agents/src/main/kotlin/it/unibo/alchemist/model/implementations/actions/CognitiveAgentPursue.kt +++ b/alchemist-cognitive-agents/src/main/kotlin/it/unibo/alchemist/model/implementations/actions/CognitiveAgentPursue.kt @@ -11,7 +11,7 @@ package it.unibo.alchemist.model.implementations.actions import it.unibo.alchemist.model.implementations.actions.navigationstrategies.Pursuing import it.unibo.alchemist.model.interfaces.NavigationAction -import it.unibo.alchemist.model.interfaces.OrientingPedestrian2D +import it.unibo.alchemist.model.interfaces.Node import it.unibo.alchemist.model.interfaces.Reaction import it.unibo.alchemist.model.interfaces.environments.Euclidean2DEnvironmentWithGraph import it.unibo.alchemist.model.interfaces.geometry.euclidean2d.ConvexPolygon @@ -22,15 +22,15 @@ import it.unibo.alchemist.model.interfaces.geometry.euclidean2d.graph.Euclidean2 * A [NavigationAction] using [Pursuing] navigation strategy. * * @param T the concentration type. - * @param L the type of landmarks of the pedestrian's cognitive map. - * @param R the type of edges of the pedestrian's cognitive map, representing the [R]elations between landmarks. + * @param L the type of landmarks of the node's cognitive map. + * @param R the type of edges of the node's cognitive map, representing the [R]elations between landmarks. */ class CognitiveAgentPursue( environment: Euclidean2DEnvironmentWithGraph<*, T, ConvexPolygon, Euclidean2DPassage>, reaction: Reaction, - pedestrian: OrientingPedestrian2D, + node: Node, vararg destination: Number -) : CognitiveAgentNavigationAction2D(environment, reaction, pedestrian) { +) : CognitiveAgentNavigationAction2D(environment, reaction, node) { init { strategy = Pursuing(this, environment.makePosition(*destination)) diff --git a/alchemist-cognitive-agents/src/main/kotlin/it/unibo/alchemist/model/implementations/actions/CognitiveAgentReachDestination.kt b/alchemist-cognitive-agents/src/main/kotlin/it/unibo/alchemist/model/implementations/actions/CognitiveAgentReachDestination.kt index 44b78f3d0b..b9ebaa6a74 100644 --- a/alchemist-cognitive-agents/src/main/kotlin/it/unibo/alchemist/model/implementations/actions/CognitiveAgentReachDestination.kt +++ b/alchemist-cognitive-agents/src/main/kotlin/it/unibo/alchemist/model/implementations/actions/CognitiveAgentReachDestination.kt @@ -15,13 +15,15 @@ import it.unibo.alchemist.model.interfaces.NavigationAction import it.unibo.alchemist.model.implementations.positions.Euclidean2DPosition import it.unibo.alchemist.model.math.lazyMutable import it.unibo.alchemist.model.interfaces.NavigationStrategy2D -import it.unibo.alchemist.model.interfaces.OrientingPedestrian2D +import it.unibo.alchemist.model.interfaces.Node import it.unibo.alchemist.model.interfaces.Reaction import it.unibo.alchemist.model.interfaces.environments.Euclidean2DEnvironmentWithGraph import it.unibo.alchemist.model.interfaces.geometry.euclidean2d.ConvexPolygon import it.unibo.alchemist.model.interfaces.geometry.euclidean2d.Euclidean2DConvexShape import it.unibo.alchemist.model.interfaces.geometry.euclidean2d.graph.Euclidean2DPassage import org.jgrapht.Graphs +import it.unibo.alchemist.model.interfaces.Node.Companion.asProperty +import it.unibo.alchemist.model.interfaces.properties.OrientingProperty /** * A [NavigationAction] using [DestinationReaching] navigation strategy. @@ -29,27 +31,28 @@ import org.jgrapht.Graphs * known and unknown ones. * * @param T the concentration type. - * @param L the type of landmarks of the pedestrian's cognitive map. - * @param R the type of edges of the pedestrian's cognitive map, representing the [R]elations between landmarks. + * @param L the type of landmarks of the node's cognitive map. + * @param R the type of edges of the node's cognitive map, representing the [R]elations between landmarks. */ class CognitiveAgentReachDestination( environment: Euclidean2DEnvironmentWithGraph<*, T, ConvexPolygon, Euclidean2DPassage>, reaction: Reaction, - pedestrian: OrientingPedestrian2D, - vararg destinations: Number -) : CognitiveAgentNavigationAction2D(environment, reaction, pedestrian) { + node: Node, + vararg destinations: Number, +) : CognitiveAgentNavigationAction2D(environment, reaction, node) { /** - * Infers if a [destination] is known by the [pedestrian] (see [Pursuing]). A destination is considered - * to be known if the pedestrian's cognitive map contains at least one landmark located in the same + * Infers if a [destination] is known by the [navigatingNode] (see [Pursuing]). A destination is considered + * to be known if the node's cognitive map contains at least one landmark located in the same * room (= [environment]'s area) of the destination, or in an adjacent room. */ private fun inferIsKnown(destination: Euclidean2DPosition): Boolean = environment.graph.nodeContaining(destination)?.let { room -> val neighborhood = Graphs.neighborListOf(environment.graph, room) + room - pedestrian.cognitiveMap.vertexSet().any { landmark -> - neighborhood.any { it.contains(landmark.centroid) } - } + navigatingNode.asProperty>() + .cognitiveMap + .vertexSet() + .any { landmark -> neighborhood.any { it.contains(landmark.centroid) } } } ?: false override var strategy: NavigationStrategy2D by lazyMutable { diff --git a/alchemist-cognitive-agents/src/main/kotlin/it/unibo/alchemist/model/implementations/actions/CognitiveAgentReachKnownDestination.kt b/alchemist-cognitive-agents/src/main/kotlin/it/unibo/alchemist/model/implementations/actions/CognitiveAgentReachKnownDestination.kt index b723c36497..99ffba4d0f 100644 --- a/alchemist-cognitive-agents/src/main/kotlin/it/unibo/alchemist/model/implementations/actions/CognitiveAgentReachKnownDestination.kt +++ b/alchemist-cognitive-agents/src/main/kotlin/it/unibo/alchemist/model/implementations/actions/CognitiveAgentReachKnownDestination.kt @@ -13,7 +13,7 @@ import it.unibo.alchemist.model.implementations.actions.navigationstrategies.Kno import it.unibo.alchemist.model.interfaces.NavigationAction import it.unibo.alchemist.model.math.lazyMutable import it.unibo.alchemist.model.interfaces.NavigationStrategy2D -import it.unibo.alchemist.model.interfaces.OrientingPedestrian2D +import it.unibo.alchemist.model.interfaces.Node import it.unibo.alchemist.model.interfaces.Reaction import it.unibo.alchemist.model.interfaces.environments.Euclidean2DEnvironmentWithGraph import it.unibo.alchemist.model.interfaces.geometry.euclidean2d.ConvexPolygon @@ -24,15 +24,15 @@ import it.unibo.alchemist.model.interfaces.geometry.euclidean2d.graph.Euclidean2 * A [NavigationAction] using [KnownDestinationReaching] navigation strategy. * * @param T the concentration type. - * @param L the type of landmarks of the pedestrian's cognitive map. - * @param R the type of edges of the pedestrian's cognitive map, representing the [R]elations between landmarks. + * @param L the type of landmarks of the node's cognitive map. + * @param R the type of edges of the node's cognitive map, representing the [R]elations between landmarks. */ class CognitiveAgentReachKnownDestination( environment: Euclidean2DEnvironmentWithGraph<*, T, ConvexPolygon, Euclidean2DPassage>, reaction: Reaction, - pedestrian: OrientingPedestrian2D, + node: Node, vararg destinations: Number -) : CognitiveAgentNavigationAction2D(environment, reaction, pedestrian) { +) : CognitiveAgentNavigationAction2D(environment, reaction, node) { override var strategy: NavigationStrategy2D by lazyMutable { KnownDestinationReaching(this, destinations.toPositions(environment)) diff --git a/alchemist-cognitive-agents/src/main/kotlin/it/unibo/alchemist/model/implementations/actions/CognitiveAgentSeek.kt b/alchemist-cognitive-agents/src/main/kotlin/it/unibo/alchemist/model/implementations/actions/CognitiveAgentSeek.kt index a83be66ed3..e6cc5eab37 100644 --- a/alchemist-cognitive-agents/src/main/kotlin/it/unibo/alchemist/model/implementations/actions/CognitiveAgentSeek.kt +++ b/alchemist-cognitive-agents/src/main/kotlin/it/unibo/alchemist/model/implementations/actions/CognitiveAgentSeek.kt @@ -1,37 +1,38 @@ package it.unibo.alchemist.model.implementations.actions import it.unibo.alchemist.model.interfaces.Environment -import it.unibo.alchemist.model.interfaces.Pedestrian +import it.unibo.alchemist.model.interfaces.Node import it.unibo.alchemist.model.interfaces.Position import it.unibo.alchemist.model.interfaces.Reaction import it.unibo.alchemist.model.interfaces.geometry.GeometricTransformation import it.unibo.alchemist.model.interfaces.geometry.Vector /** - * Move the pedestrian towards the target position as fast as possible. + * Move the node towards the target position as fast as possible. * * @param environment - * the environment inside which the pedestrian moves. - * @param pedestrian + * the environment inside which the node moves. + * @param node * the owner of this action. * @param target - * the position the pedestrian moves towards. + * the position the node moves towards. */ open class CognitiveAgentSeek( environment: Environment, reaction: Reaction, - pedestrian: Pedestrian, + node: Node, target: P -) : CognitiveAgentArrive(environment, reaction, pedestrian, 0.0, 0.0, target) +) : CognitiveAgentArrive(environment, reaction, node, 0.0, 0.0, target) where P : Position

, P : Vector

, A : GeometricTransformation

{ constructor( environment: Environment, reaction: Reaction, - pedestrian: Pedestrian, + node: Node, vararg coordinates: Number - ) : this(environment, reaction, pedestrian, environment.makePosition(*coordinates)) + ) : this(environment, reaction, node, environment.makePosition(*coordinates)) - override fun cloneAction(n: Pedestrian, r: Reaction) = CognitiveAgentSeek(environment, r, n, target) + override fun cloneAction(node: Node, reaction: Reaction): CognitiveAgentSeek = + CognitiveAgentSeek(environment, reaction, node, target) } diff --git a/alchemist-cognitive-agents/src/main/kotlin/it/unibo/alchemist/model/implementations/actions/CognitiveAgentSeek2D.kt b/alchemist-cognitive-agents/src/main/kotlin/it/unibo/alchemist/model/implementations/actions/CognitiveAgentSeek2D.kt index f578642cb7..6107b86c73 100644 --- a/alchemist-cognitive-agents/src/main/kotlin/it/unibo/alchemist/model/implementations/actions/CognitiveAgentSeek2D.kt +++ b/alchemist-cognitive-agents/src/main/kotlin/it/unibo/alchemist/model/implementations/actions/CognitiveAgentSeek2D.kt @@ -1,7 +1,7 @@ package it.unibo.alchemist.model.implementations.actions import it.unibo.alchemist.model.interfaces.EuclideanEnvironment -import it.unibo.alchemist.model.interfaces.Pedestrian +import it.unibo.alchemist.model.interfaces.Node import it.unibo.alchemist.model.interfaces.Position2D import it.unibo.alchemist.model.interfaces.Reaction import it.unibo.alchemist.model.interfaces.SteeringActionWithTarget @@ -10,21 +10,21 @@ import it.unibo.alchemist.model.interfaces.geometry.Vector2D /** * [CognitiveAgentSeek] behavior in a bidimensional environment, delegated to [CognitiveAgentFollowScalarField] - * (this means the pedestrian tries to overtake others on its path, + * (this means the node tries to overtake others on its path, * in general its movements are more sophisticated than [CognitiveAgentSeek]). */ open class CognitiveAgentSeek2D( /** - * The environment the pedestrian is into. + * The environment the node is into. */ protected val environment: EuclideanEnvironment, reaction: Reaction, - pedestrian: Pedestrian, + node: Node, /** - * The position the pedestrian wants to reach. + * The position the node wants to reach. */ private val target: P -) : AbstractSteeringAction(environment, reaction, pedestrian), +) : AbstractSteeringAction(environment, reaction, node), SteeringActionWithTarget where P : Position2D

, P : Vector2D

, A : GeometricTransformation

{ @@ -32,12 +32,12 @@ open class CognitiveAgentSeek2D( constructor( environment: EuclideanEnvironment, reaction: Reaction, - pedestrian: Pedestrian, + node: Node, x: Number, - y: Number - ) : this(environment, reaction, pedestrian, environment.makePosition(x, y)) + y: Number, + ) : this(environment, reaction, node, environment.makePosition(x, y)) - private val followScalarField = CognitiveAgentFollowScalarField(environment, reaction, pedestrian, target) { + private val followScalarField = CognitiveAgentFollowScalarField(environment, reaction, node, target) { -it.distanceTo(target) } @@ -45,5 +45,6 @@ open class CognitiveAgentSeek2D( override fun nextPosition(): P = followScalarField.nextPosition() - override fun cloneAction(n: Pedestrian, r: Reaction) = CognitiveAgentSeek2D(environment, r, n, target) + override fun cloneAction(node: Node, reaction: Reaction): CognitiveAgentSeek2D = + CognitiveAgentSeek2D(environment, reaction, node, target) } diff --git a/alchemist-cognitive-agents/src/main/kotlin/it/unibo/alchemist/model/implementations/actions/CognitiveAgentSeparation.kt b/alchemist-cognitive-agents/src/main/kotlin/it/unibo/alchemist/model/implementations/actions/CognitiveAgentSeparation.kt index c5e935092f..4b5732973f 100644 --- a/alchemist-cognitive-agents/src/main/kotlin/it/unibo/alchemist/model/implementations/actions/CognitiveAgentSeparation.kt +++ b/alchemist-cognitive-agents/src/main/kotlin/it/unibo/alchemist/model/implementations/actions/CognitiveAgentSeparation.kt @@ -1,37 +1,36 @@ package it.unibo.alchemist.model.implementations.actions import it.unibo.alchemist.model.implementations.positions.Euclidean2DPosition -import it.unibo.alchemist.model.interfaces.Pedestrian -import it.unibo.alchemist.model.interfaces.Pedestrian2D +import it.unibo.alchemist.model.interfaces.Node import it.unibo.alchemist.model.interfaces.Reaction import it.unibo.alchemist.model.interfaces.environments.Physics2DEnvironment import it.unibo.alchemist.model.interfaces.geometry.euclidean2d.Euclidean2DTransformation +import it.unibo.alchemist.model.interfaces.Node.Companion.asProperty +import it.unibo.alchemist.model.interfaces.properties.PerceptiveProperty /** * Move the agent away from the pedestrians near to him. * - * @param env - * the environment inside which the pedestrian moves. + * @param environment + * the environment inside which the node moves. * @param reaction * the reaction which executes this action. - * @param pedestrian + * @param node * the owner of this action. */ class CognitiveAgentSeparation( - override val env: Physics2DEnvironment, + val environment: Physics2DEnvironment, reaction: Reaction, - override val pedestrian: Pedestrian2D -) : AbstractGroupSteeringAction(env, reaction, pedestrian) { + node: Node, +) : AbstractGroupSteeringAction(environment, reaction, node) { - override fun cloneAction(n: Pedestrian, r: Reaction) = - requireNodeTypeAndProduce, CognitiveAgentSeparation>(n) { - CognitiveAgentSeparation(env, r, it) - } + override fun cloneAction(node: Node, reaction: Reaction): CognitiveAgentSeparation = + CognitiveAgentSeparation(environment, reaction, node) override fun nextPosition(): Euclidean2DPosition = (currentPosition - centroid()).coerceAtMost(maxWalk) - override fun group(): List> = pedestrian.fieldOfView + override fun group(): List> = node.asProperty>() + .fieldOfView .influentialNodes() - .filterIsInstance>() - .plusElement(pedestrian) + .plusElement(node) } diff --git a/alchemist-cognitive-agents/src/main/kotlin/it/unibo/alchemist/model/implementations/actions/CognitiveAgentWander.kt b/alchemist-cognitive-agents/src/main/kotlin/it/unibo/alchemist/model/implementations/actions/CognitiveAgentWander.kt index 53b09dca53..bed028d17e 100644 --- a/alchemist-cognitive-agents/src/main/kotlin/it/unibo/alchemist/model/implementations/actions/CognitiveAgentWander.kt +++ b/alchemist-cognitive-agents/src/main/kotlin/it/unibo/alchemist/model/implementations/actions/CognitiveAgentWander.kt @@ -2,8 +2,7 @@ package it.unibo.alchemist.model.implementations.actions import it.unibo.alchemist.model.implementations.positions.Euclidean2DPosition import it.unibo.alchemist.model.interfaces.Environment -import it.unibo.alchemist.model.interfaces.Pedestrian -import it.unibo.alchemist.model.interfaces.Pedestrian2D +import it.unibo.alchemist.model.interfaces.Node import it.unibo.alchemist.model.interfaces.Position2D import it.unibo.alchemist.model.interfaces.Reaction import it.unibo.alchemist.model.interfaces.environments.Physics2DEnvironment @@ -16,36 +15,36 @@ import org.apache.commons.math3.random.RandomGenerator /** * Give the impression of a random walk through the environment targeting an ever changing pseudo-randomly point - * of a circumference at a given distance and with a given radius from the current pedestrian position. + * of a circumference at a given distance and with a given radius from the current node position. * * @param environment - * the environment inside which the pedestrian moves. - * @param pedestrian + * the environment inside which the node moves. + * @param node * the owner of this action. * @param randomGenerator * the simulation {@link RandomGenerator}. * @param offset - * the distance from the pedestrian position of the center of the circle. + * the distance from the node position of the center of the circle. * @param radius * the radius of the circle. */ open class CognitiveAgentWander( private val environment: Physics2DEnvironment, reaction: Reaction, - pedestrian: Pedestrian2D, + node: Node, protected val randomGenerator: RandomGenerator, protected val offset: Double, - protected val radius: Double + protected val radius: Double, ) : AbstractSteeringActionWithTarget( environment, reaction, - pedestrian, + node, TargetSelectionStrategy { randomGenerator.position(environment) } ) { private val heading by lazy { - environment.setHeading(pedestrian, randomGenerator.random2DVersor(environment)).let { - { environment.getHeading(pedestrian) } + environment.setHeading(node, randomGenerator.random2DVersor(environment)).let { + { environment.getHeading(node) } } } @@ -56,10 +55,8 @@ open class CognitiveAgentWander( .first() .coerceAtMost(maxWalk) - override fun cloneAction(n: Pedestrian, r: Reaction) = - requireNodeTypeAndProduce, CognitiveAgentWander>(n) { - CognitiveAgentWander(environment, r, it, randomGenerator, offset, radius) - } + override fun cloneAction(node: Node, reaction: Reaction) = + CognitiveAgentWander(environment, reaction, node, randomGenerator, offset, radius) } /** diff --git a/alchemist-cognitive-agents/src/main/kotlin/it/unibo/alchemist/model/implementations/actions/navigationstrategies/DynamicPursuing.kt b/alchemist-cognitive-agents/src/main/kotlin/it/unibo/alchemist/model/implementations/actions/navigationstrategies/DynamicPursuing.kt index d60ed16194..f1976856f8 100644 --- a/alchemist-cognitive-agents/src/main/kotlin/it/unibo/alchemist/model/implementations/actions/navigationstrategies/DynamicPursuing.kt +++ b/alchemist-cognitive-agents/src/main/kotlin/it/unibo/alchemist/model/implementations/actions/navigationstrategies/DynamicPursuing.kt @@ -14,13 +14,15 @@ import it.unibo.alchemist.model.interfaces.NavigationAction2D import it.unibo.alchemist.model.interfaces.geometry.euclidean2d.ConvexPolygon import it.unibo.alchemist.model.interfaces.geometry.euclidean2d.Euclidean2DConvexShape import it.unibo.alchemist.model.interfaces.geometry.euclidean2d.graph.Euclidean2DPassage +import it.unibo.alchemist.model.interfaces.Node.Companion.asProperty +import it.unibo.alchemist.model.interfaces.properties.OrientingProperty /** * [Pursuing] strategy allowing to dynamically change [destination]. * * @param T the concentration type. - * @param L the type of landmarks of the pedestrian's cognitive map. - * @param R the type of edges of the pedestrian's cognitive map, representing the [R]elations between landmarks. + * @param L the type of landmarks of the node's cognitive map. + * @param R the type of edges of the node's cognitive map, representing the [R]elations between landmarks. */ open class DynamicPursuing( action: NavigationAction2D, @@ -28,9 +30,9 @@ open class DynamicPursuing( ) : Pursuing(action, destination) { /** - * Changes the destination of the strategy. If [voidVolatileMemory] is true, the pedestrian's + * Changes the destination of the strategy. If [voidVolatileMemory] is true, the node's * volatile memory is set to zero. This has two effects: - * - known impasses remain stored (hence the pedestrian will keep avoiding them) + * - known impasses remain stored (hence the node will keep avoiding them) * - rooms visited while pursuing the previous destination won't be penalised (= won't be avoided) * Defaults to false. */ @@ -38,18 +40,18 @@ open class DynamicPursuing( destination = newDestination action.currentRoom?.let { /* - * If the pedestrian is inside a room, we force the re-computation of what to do. Otherwise + * If the node is inside a room, we force the re-computation of what to do. Otherwise * he/she's crossing a door and inNewRoom will be called as soon as a room is reached. */ inNewRoom(it) } if (voidVolatileMemory) { /* - * clear() would cause volatileMemory[anyArea] to be null, which in turn means the pedestrian would + * clear() would cause volatileMemory[anyArea] to be null, which in turn means the node would * forget known impasses as well (an impasse is known when volatileMemory[area] != null). Setting * volatileMemory[anyArea] to zero allows to remember known impasses. */ - pedestrian.volatileMemory.replaceAll { _, _ -> 0 } + node.asProperty>().volatileMemory.replaceAll { _, _ -> 0 } } } } diff --git a/alchemist-cognitive-agents/src/main/kotlin/it/unibo/alchemist/model/implementations/actions/navigationstrategies/Exploring.kt b/alchemist-cognitive-agents/src/main/kotlin/it/unibo/alchemist/model/implementations/actions/navigationstrategies/Exploring.kt index 8fe7da329f..12ec322c84 100644 --- a/alchemist-cognitive-agents/src/main/kotlin/it/unibo/alchemist/model/implementations/actions/navigationstrategies/Exploring.kt +++ b/alchemist-cognitive-agents/src/main/kotlin/it/unibo/alchemist/model/implementations/actions/navigationstrategies/Exploring.kt @@ -12,13 +12,15 @@ package it.unibo.alchemist.model.implementations.actions.navigationstrategies import it.unibo.alchemist.model.interfaces.NavigationAction2D import it.unibo.alchemist.model.interfaces.NavigationStrategy2D import it.unibo.alchemist.model.interfaces.NavigationStrategy -import it.unibo.alchemist.model.interfaces.OrientingPedestrian2D -import it.unibo.alchemist.model.interfaces.Pedestrian +import it.unibo.alchemist.model.interfaces.Node +import it.unibo.alchemist.model.interfaces.properties.AreaProperty import it.unibo.alchemist.model.interfaces.geometry.euclidean2d.ConvexPolygon import it.unibo.alchemist.model.interfaces.geometry.euclidean2d.Euclidean2DConvexShape import it.unibo.alchemist.model.interfaces.geometry.euclidean2d.graph.Euclidean2DPassage import kotlin.math.abs import kotlin.math.pow +import it.unibo.alchemist.model.interfaces.Node.Companion.asProperty +import it.unibo.alchemist.model.interfaces.properties.OrientingProperty /** * A [NavigationStrategy] allowing to explore the environment. @@ -47,9 +49,9 @@ open class Exploring( } /** - * Shortcut to obtain the pedestrian. + * Shortcut to obtain the node. */ - protected val pedestrian: OrientingPedestrian2D get() = action.pedestrian + protected val node: Node get() = action.navigatingNode /** * Shortcut to obtain the environment. @@ -93,7 +95,7 @@ open class Exploring( * Less visited rooms are preferred. */ protected open fun volatileMemoryFactor(head: ConvexPolygon): Double = - 2.0.pow(pedestrian.volatileMemory[head] ?: 0) + 2.0.pow(orientingCapability.volatileMemory[head] ?: 0) /** * Takes into account the congestion of [head], it is assumed that the pedestrian can estimate the @@ -118,11 +120,10 @@ open class Exploring( protected open val ConvexPolygon.congestionLevel: Double get() = environment .getNodesWithinRange(centroid, radius) .asSequence() - .filterIsInstance>() .map { environment.getPosition(it) } .filter { contains(it) } .count() - .let { it * pedestrian.area / area } + .let { it * node.area / area } .coerceAtMost(1.0) /** @@ -133,11 +134,13 @@ open class Exploring( /** * A rough estimation of the area of a [Pedestrian]. */ - protected open val Pedestrian.area: Double get() = Math.PI * shape.radius.pow(2) + protected open val Node.area: Double get() = + Math.PI * asProperty>().shape.radius.pow(2) /** * Checks if the pedestrian knows that the area is an impasse (= an area with a single door). */ - protected open fun ConvexPolygon.isKnownImpasse(): Boolean = pedestrian.volatileMemory.contains(this) && - environment.graph.outgoingEdgesOf(this).distinct().count() <= 1 + protected open fun ConvexPolygon.isKnownImpasse(): Boolean = + node.asProperty>().volatileMemory.contains(this) && + environment.graph.outgoingEdgesOf(this).distinct().count() <= 1 } diff --git a/alchemist-cognitive-agents/src/main/kotlin/it/unibo/alchemist/model/implementations/actions/navigationstrategies/GoalOrientedExploring.kt b/alchemist-cognitive-agents/src/main/kotlin/it/unibo/alchemist/model/implementations/actions/navigationstrategies/GoalOrientedExploring.kt index 5e85b9f722..d9633111fa 100644 --- a/alchemist-cognitive-agents/src/main/kotlin/it/unibo/alchemist/model/implementations/actions/navigationstrategies/GoalOrientedExploring.kt +++ b/alchemist-cognitive-agents/src/main/kotlin/it/unibo/alchemist/model/implementations/actions/navigationstrategies/GoalOrientedExploring.kt @@ -20,13 +20,13 @@ import it.unibo.alchemist.model.interfaces.geometry.euclidean2d.graph.Euclidean2 * A [NavigationStrategy] allowing to explore the environment looking for something specific whose position * is unknown. * The client can specify a list of [unknownDestinations]: these can be recognized once they're in sight, - * but the pedestrian doesn't know their position until that moment (think e.g. of exits in an evacuation + * but the node doesn't know their position until that moment (think e.g. of exits in an evacuation * scenario). More specifically, unknown destinations can be detected if located in a room adjacent to the - * room the pedestrian is into. Once a destination is detected, the pedestrian will reach it and stop. + * room the node is into. Once a destination is detected, the node will reach it and stop. * * @param T the concentration type. - * @param L the type of landmarks of the pedestrian's cognitive map. - * @param R the type of edges of the pedestrian's cognitive map, representing the [R]elations between landmarks. + * @param L the type of landmarks of the node's cognitive map. + * @param R the type of edges of the node's cognitive map, representing the [R]elations between landmarks. */ open class GoalOrientedExploring( action: NavigationAction2D, @@ -37,7 +37,7 @@ open class GoalOrientedExploring( reachUnknownDestination(newRoom, orElse = { super.inNewRoom(newRoom) }) /** - * If one or more unknown destinations are inside [newRoom] (= the room the pedestrian is into), the closest + * If one or more unknown destinations are inside [newRoom] (= the room the node is into), the closest * one is approached. Otherwise, if one or more destinations are in a room adjacent to the current one, the * related doors are weighted using [weightExit] and the one with minimum weight is crossed. [orElse] is * executed otherwise. diff --git a/alchemist-cognitive-agents/src/main/kotlin/it/unibo/alchemist/model/implementations/actions/navigationstrategies/KnownDestinationReaching.kt b/alchemist-cognitive-agents/src/main/kotlin/it/unibo/alchemist/model/implementations/actions/navigationstrategies/KnownDestinationReaching.kt index 8162e26ee4..aad1e8f50d 100644 --- a/alchemist-cognitive-agents/src/main/kotlin/it/unibo/alchemist/model/implementations/actions/navigationstrategies/KnownDestinationReaching.kt +++ b/alchemist-cognitive-agents/src/main/kotlin/it/unibo/alchemist/model/implementations/actions/navigationstrategies/KnownDestinationReaching.kt @@ -46,7 +46,7 @@ open class KnownDestinationReaching( init { route = emptyList().takeIf { destinations.isEmpty() } ?: with(action) { - val currPos = environment.getPosition(pedestrian) + val currPos = environment.getPosition(navigatingNode) val (closestDest, distanceToClosestDest) = destinations .asSequence() .map { it to it.distanceTo(currPos) } @@ -71,7 +71,7 @@ open class KnownDestinationReaching( } /** - * Finds a known path to the specified [destination]. The path is obtained from the [pedestrian]'s cognitive + * Finds a known path to the specified [destination]. The path is obtained from the [node]'s cognitive * map and consists of a list of landmarks, it is computed so as to minimize the distance between the first * landmark and the pedestrian's position and the distance between the last landmark and the destination. In * spite of this, the first landmark may be far from the pedestrian's position and the same applies to the @@ -82,9 +82,9 @@ open class KnownDestinationReaching( * from the pedestrian's position than the destination itself it's just more convenient to pursue the latter. * If the cognitive map is empty the path will be empty as well. */ - private fun findKnownPathTo(destination: Euclidean2DPosition): List = with(pedestrian.cognitiveMap) { + private fun findKnownPathTo(destination: Euclidean2DPosition): List = with(orientingCapability.cognitiveMap) { emptyList().takeIf { vertexSet().isEmpty() } ?: let { - val currPos = environment.getPosition(pedestrian) + val currPos = environment.getPosition(node) val currRoom = environment.graph.nodeContaining(currPos) val destRoom = environment.graph.nodeContaining(destination) if (currRoom == null || destRoom == null) { @@ -121,7 +121,7 @@ open class KnownDestinationReaching( */ private fun buildSequence(startRoom: ConvexPolygon, endRoom: ConvexPolygon): Sequence> { val landmarksIn: (room: ConvexPolygon) -> Sequence = { room -> - pedestrian.cognitiveMap.vertexSet().asSequence().filter { room.contains(it.centroid) } + orientingCapability.cognitiveMap.vertexSet().asSequence().filter { room.contains(it.centroid) } } val landmarksInAny: (rooms: List) -> Sequence = { rooms -> rooms.asSequence().flatMap(landmarksIn) diff --git a/alchemist-cognitive-agents/src/main/kotlin/it/unibo/alchemist/model/implementations/actions/navigationstrategies/RouteFollowing.kt b/alchemist-cognitive-agents/src/main/kotlin/it/unibo/alchemist/model/implementations/actions/navigationstrategies/RouteFollowing.kt index 22d57984f4..ad33e029ae 100644 --- a/alchemist-cognitive-agents/src/main/kotlin/it/unibo/alchemist/model/implementations/actions/navigationstrategies/RouteFollowing.kt +++ b/alchemist-cognitive-agents/src/main/kotlin/it/unibo/alchemist/model/implementations/actions/navigationstrategies/RouteFollowing.kt @@ -22,13 +22,13 @@ import it.unibo.alchemist.model.interfaces.geometry.euclidean2d.graph.Euclidean2 * other (i.e. the path leading from a waypoint to the next one may or may not be representable * as a single segment), for this reason [Pursuing] behavior is used to reach each waypoint. * In this context, a waypoint is considered reached when it's inside the current room (not when the - * pedestrian reach that exact position), apart from the last waypoint which is actually approached. - * Cuts to the route are allowed (i.e. if the pedestrian finds a waypoint which is farther than the + * node reach that exact position), apart from the last waypoint which is actually approached. + * Cuts to the route are allowed (i.e. if the node finds a waypoint which is farther than the * expected next one, he/she skips all the waypoints in between). * * @param T the concentration type. - * @param L the type of landmarks of the pedestrian's cognitive map. - * @param R the type of edges of the pedestrian's cognitive map, representing the [R]elations between landmarks. + * @param L the type of landmarks of the node's cognitive map. + * @param R the type of edges of the node's cognitive map, representing the [R]elations between landmarks. */ open class RouteFollowing constructor( action: NavigationAction2D, @@ -41,7 +41,7 @@ open class RouteFollowing constructor( private var indexOfNextWaypoint: Int = 0 /** - * When in an unexpected room the pedestrian gets back to [previousRoom] so as to continue following the + * When in an unexpected room the node gets back to [previousRoom] so as to continue following the * route correctly. */ override fun inUnexpectedNewRoom( diff --git a/alchemist-cognitive-agents/src/main/kotlin/it/unibo/alchemist/model/implementations/actions/steeringstrategies/DistanceWeighted.kt b/alchemist-cognitive-agents/src/main/kotlin/it/unibo/alchemist/model/implementations/actions/steeringstrategies/DistanceWeighted.kt index fe412a4454..8a0f4c222f 100644 --- a/alchemist-cognitive-agents/src/main/kotlin/it/unibo/alchemist/model/implementations/actions/steeringstrategies/DistanceWeighted.kt +++ b/alchemist-cognitive-agents/src/main/kotlin/it/unibo/alchemist/model/implementations/actions/steeringstrategies/DistanceWeighted.kt @@ -2,35 +2,34 @@ package it.unibo.alchemist.model.implementations.actions.steeringstrategies import it.unibo.alchemist.model.interfaces.Environment import it.unibo.alchemist.model.interfaces.Node -import it.unibo.alchemist.model.interfaces.Pedestrian2D import it.unibo.alchemist.model.interfaces.Position import it.unibo.alchemist.model.interfaces.SteeringActionWithTarget import it.unibo.alchemist.model.interfaces.environments.Euclidean2DEnvironment import it.unibo.alchemist.model.interfaces.geometry.Vector /** - * [Weighted] strategy where the weight of each steering action is the inverse of the pedestrian's distance from the + * [Weighted] strategy where the weight of each steering action is the inverse of the node's distance from the * action's target (the closer the target, the more important the action). [defaultWeight] is used for actions without * a target. * * @param environment - * the environment in which the pedestrian moves. - * @param pedestrian + * the environment in which the node moves. + * @param node * the owner of the steering action this strategy belongs to. */ class DistanceWeighted( environment: Euclidean2DEnvironment, - pedestrian: Pedestrian2D, + node: Node, /** * Default weight for steering actions without a defined target. */ private val defaultWeight: Double = 1.0 ) : Weighted( environment, - pedestrian, + node, { if (this is SteeringActionWithTarget) { - this.targetDistanceTo(pedestrian, environment).let { if (it > 0.0) 1 / it else it } + this.targetDistanceTo(node, environment).let { if (it > 0.0) 1 / it else it } } else { defaultWeight } @@ -42,5 +41,5 @@ class DistanceWeighted( */ fun SteeringActionWithTarget.targetDistanceTo( node: Node, - env: Environment -): Double where P : Position

, P : Vector

= target().distanceTo(env.getPosition(node)) + environment: Environment +): Double where P : Position

, P : Vector

= target().distanceTo(environment.getPosition(node)) diff --git a/alchemist-cognitive-agents/src/main/kotlin/it/unibo/alchemist/model/implementations/actions/steeringstrategies/Nearest.kt b/alchemist-cognitive-agents/src/main/kotlin/it/unibo/alchemist/model/implementations/actions/steeringstrategies/Nearest.kt index 4935c9cc50..e2a2416e71 100644 --- a/alchemist-cognitive-agents/src/main/kotlin/it/unibo/alchemist/model/implementations/actions/steeringstrategies/Nearest.kt +++ b/alchemist-cognitive-agents/src/main/kotlin/it/unibo/alchemist/model/implementations/actions/steeringstrategies/Nearest.kt @@ -3,43 +3,43 @@ package it.unibo.alchemist.model.implementations.actions.steeringstrategies import it.unibo.alchemist.model.implementations.positions.Euclidean2DPosition import it.unibo.alchemist.model.interfaces.Environment import it.unibo.alchemist.model.interfaces.GroupSteeringAction -import it.unibo.alchemist.model.interfaces.Pedestrian2D +import it.unibo.alchemist.model.interfaces.Node import it.unibo.alchemist.model.interfaces.SteeringAction import it.unibo.alchemist.model.interfaces.SteeringActionWithTarget import it.unibo.alchemist.model.interfaces.environments.Euclidean2DEnvironment /** * [Filtered] strategy considering only the group steering action and the non-group steering action whose targets are - * nearest to the pedestrian's position. The two actions are combined using [DistanceWeighted] strategy. + * nearest to the node's position. The two actions are combined using [DistanceWeighted] strategy. * * @param environment - * the environment in which the pedestrian moves. - * @param pedestrian + * the environment in which the node moves. + * @param node * the owner of the steering action this strategy belongs to. */ class Nearest( environment: Euclidean2DEnvironment, - pedestrian: Pedestrian2D + node: Node, ) : Filtered( - DistanceWeighted(environment, pedestrian), + DistanceWeighted(environment, node), { partition { it is GroupSteeringAction }.let { (groupActions, otherActions) -> listOfNotNull( - groupActions.pickNearestOrFirst(environment, pedestrian), - otherActions.pickNearestOrFirst(environment, pedestrian) + groupActions.pickNearestOrFirst(environment, node), + otherActions.pickNearestOrFirst(environment, node), ) } } ) /** - * Picks the [SteeringActionWithTarget] whose target is nearest to the [pedestrian]'s current position, or the first + * Picks the [SteeringActionWithTarget] whose target is nearest to the [node]'s current position, or the first * action of the list if none of them has a defined target. If the list is empty, null is returned. */ fun List>.pickNearestOrFirst( - env: Environment, - pedestrian: Pedestrian2D + environment: Environment, + node: Node, ): SteeringAction? = this .filterIsInstance>() - .minByOrNull { it.targetDistanceTo(pedestrian, env) } + .minByOrNull { it.targetDistanceTo(node, environment) } ?: firstOrNull() diff --git a/alchemist-cognitive-agents/src/main/kotlin/it/unibo/alchemist/model/implementations/actions/steeringstrategies/SinglePrevalent.kt b/alchemist-cognitive-agents/src/main/kotlin/it/unibo/alchemist/model/implementations/actions/steeringstrategies/SinglePrevalent.kt index b6de8a7be6..90cd0587c0 100644 --- a/alchemist-cognitive-agents/src/main/kotlin/it/unibo/alchemist/model/implementations/actions/steeringstrategies/SinglePrevalent.kt +++ b/alchemist-cognitive-agents/src/main/kotlin/it/unibo/alchemist/model/implementations/actions/steeringstrategies/SinglePrevalent.kt @@ -12,7 +12,7 @@ package it.unibo.alchemist.model.implementations.actions.steeringstrategies import it.unibo.alchemist.model.implementations.positions.Euclidean2DPosition import it.unibo.alchemist.model.interfaces.NavigationAction2D import it.unibo.alchemist.model.interfaces.NavigationAction -import it.unibo.alchemist.model.interfaces.Pedestrian2D +import it.unibo.alchemist.model.interfaces.Node import it.unibo.alchemist.model.interfaces.SteeringAction import it.unibo.alchemist.model.interfaces.SteeringStrategy import it.unibo.alchemist.model.interfaces.environments.Euclidean2DEnvironmentWithGraph @@ -24,19 +24,19 @@ private typealias SteeringActions = List = List( environment: Euclidean2DEnvironmentWithGraph<*, T, N, *>, - pedestrian: Pedestrian2D, + node: Node, private val prevalent: SteeringActions.() -> NavigationAction2D, /** * Tolerance angle in radians. @@ -57,11 +57,11 @@ class SinglePrevalent( */ private val alpha: Double = DEFAULT_ALPHA, /** - * Function computing the maximum distance the pedestrian can walk. + * Function computing the maximum distance the node can walk. */ private val maxWalk: () -> Double, /** - * When the pedestrian is subject to contrasting forces the resulting one may be small in magnitude. + * When the node is subject to contrasting forces the resulting one may be small in magnitude. * This parameter allows to specify a minimum magnitude for the resulting force computed as * [maxWalk] * [maxWalkRatio] */ @@ -73,7 +73,7 @@ class SinglePrevalent( * reduced to O(1) in the future. */ private val delta: Double = DEFAULT_DELTA -) : Weighted(environment, pedestrian, { 0.0 }) { +) : Weighted(environment, node, { 0.0 }) { companion object { /** diff --git a/alchemist-cognitive-agents/src/main/kotlin/it/unibo/alchemist/model/implementations/actions/steeringstrategies/TypeBased.kt b/alchemist-cognitive-agents/src/main/kotlin/it/unibo/alchemist/model/implementations/actions/steeringstrategies/TypeBased.kt index 27f583141c..e6ab725d23 100644 --- a/alchemist-cognitive-agents/src/main/kotlin/it/unibo/alchemist/model/implementations/actions/steeringstrategies/TypeBased.kt +++ b/alchemist-cognitive-agents/src/main/kotlin/it/unibo/alchemist/model/implementations/actions/steeringstrategies/TypeBased.kt @@ -1,7 +1,7 @@ package it.unibo.alchemist.model.implementations.actions.steeringstrategies import it.unibo.alchemist.model.implementations.positions.Euclidean2DPosition -import it.unibo.alchemist.model.interfaces.Pedestrian2D +import it.unibo.alchemist.model.interfaces.Node import it.unibo.alchemist.model.interfaces.SteeringAction import it.unibo.alchemist.model.interfaces.environments.Euclidean2DEnvironment import kotlin.reflect.KClass @@ -11,15 +11,15 @@ import kotlin.reflect.KClass * by the client). * * @param environment - * the environment in which the pedestrian moves. - * @param pedestrian + * the environment in which the node moves. + * @param node * the owner of the steering actions combined by this strategy. * @param typeWeights * the weight for each type of steering action. */ class TypeBased( environment: Euclidean2DEnvironment, - pedestrian: Pedestrian2D, + node: Node, typeWeights: LinkedHashMap>, Double>, - defaultWeight: Double = 0.0 -) : Weighted(environment, pedestrian, { typeWeights[this::class] ?: defaultWeight }) + defaultWeight: Double = 0.0, +) : Weighted(environment, node, { typeWeights[this::class] ?: defaultWeight }) diff --git a/alchemist-cognitive-agents/src/main/kotlin/it/unibo/alchemist/model/implementations/actions/steeringstrategies/Weighted.kt b/alchemist-cognitive-agents/src/main/kotlin/it/unibo/alchemist/model/implementations/actions/steeringstrategies/Weighted.kt index f227d67710..c2932c7653 100644 --- a/alchemist-cognitive-agents/src/main/kotlin/it/unibo/alchemist/model/implementations/actions/steeringstrategies/Weighted.kt +++ b/alchemist-cognitive-agents/src/main/kotlin/it/unibo/alchemist/model/implementations/actions/steeringstrategies/Weighted.kt @@ -2,7 +2,7 @@ package it.unibo.alchemist.model.implementations.actions.steeringstrategies import it.unibo.alchemist.model.implementations.positions.Euclidean2DPosition import it.unibo.alchemist.model.interfaces.GroupSteeringAction -import it.unibo.alchemist.model.interfaces.Pedestrian2D +import it.unibo.alchemist.model.interfaces.Node import it.unibo.alchemist.model.interfaces.SteeringAction import it.unibo.alchemist.model.interfaces.SteeringActionWithTarget import it.unibo.alchemist.model.interfaces.SteeringStrategy @@ -12,8 +12,8 @@ import it.unibo.alchemist.model.interfaces.environments.Euclidean2DEnvironment * A [SteeringStrategy] performing a weighted sum of steering actions (see [computeNextPosition]). * * @param environment - * the environment in which the pedestrian moves. - * @param pedestrian + * the environment in which the node moves. + * @param node * the owner of the steering actions combined by this strategy. * @param weight * lambda used to assign a weight to each steering action: the higher the weight, the greater the @@ -21,7 +21,7 @@ import it.unibo.alchemist.model.interfaces.environments.Euclidean2DEnvironment */ open class Weighted( private val environment: Euclidean2DEnvironment, - private val pedestrian: Pedestrian2D, + private val node: Node, private val weight: SteeringAction.() -> Double ) : SteeringStrategy { @@ -40,7 +40,7 @@ open class Weighted( * the closest target is picked. */ override fun computeTarget(actions: List>): Euclidean2DPosition = - environment.getPosition(pedestrian).let { currPos -> + environment.getPosition(node).let { currPos -> actions.filterIsInstance>() .map { it.target() } .minByOrNull { it.distanceTo(currPos) } diff --git a/alchemist-cognitive-agents/src/main/kotlin/it/unibo/alchemist/model/implementations/conditions/WantToEscape.kt b/alchemist-cognitive-agents/src/main/kotlin/it/unibo/alchemist/model/implementations/conditions/WantToEscape.kt index 64899f1259..4eec69a9bb 100644 --- a/alchemist-cognitive-agents/src/main/kotlin/it/unibo/alchemist/model/implementations/conditions/WantToEscape.kt +++ b/alchemist-cognitive-agents/src/main/kotlin/it/unibo/alchemist/model/implementations/conditions/WantToEscape.kt @@ -1,20 +1,23 @@ package it.unibo.alchemist.model.implementations.conditions -import it.unibo.alchemist.model.interfaces.CognitivePedestrian import it.unibo.alchemist.model.interfaces.Context +import it.unibo.alchemist.model.interfaces.Node import it.unibo.alchemist.model.interfaces.geometry.GeometricTransformation import it.unibo.alchemist.model.interfaces.geometry.Vector +import it.unibo.alchemist.model.interfaces.Node.Companion.asProperty +import it.unibo.alchemist.model.interfaces.properties.CognitiveProperty /** * The intention of the pedestrian to evacuate or not. */ open class WantToEscape, A : GeometricTransformation>( - private val pedestrian: CognitivePedestrian -) : AbstractCondition(pedestrian) { + node: Node +) : AbstractCondition(node) { override fun getContext(): Context = Context.LOCAL override fun getPropensityContribution(): Double = 0.0 - override fun isValid(): Boolean = pedestrian.cognitiveModel.wantsToEscape() + override fun isValid(): Boolean = + node.asProperty>().cognitiveModel.wantsToEscape() } diff --git a/alchemist-cognitive-agents/src/main/kotlin/it/unibo/alchemist/model/implementations/groups/Alone.kt b/alchemist-cognitive-agents/src/main/kotlin/it/unibo/alchemist/model/implementations/groups/Alone.kt index cc13a9b217..84f0a10fab 100644 --- a/alchemist-cognitive-agents/src/main/kotlin/it/unibo/alchemist/model/implementations/groups/Alone.kt +++ b/alchemist-cognitive-agents/src/main/kotlin/it/unibo/alchemist/model/implementations/groups/Alone.kt @@ -1,22 +1,10 @@ package it.unibo.alchemist.model.implementations.groups -import it.unibo.alchemist.model.interfaces.Pedestrian -import it.unibo.alchemist.model.interfaces.PedestrianGroup -import it.unibo.alchemist.model.interfaces.geometry.GeometricTransformation -import it.unibo.alchemist.model.interfaces.geometry.Vector - +import it.unibo.alchemist.model.interfaces.Group +import it.unibo.alchemist.model.interfaces.Node /** - * Group representing a pedestrian alone. + * Group representing a node alone. */ -class Alone, A : GeometricTransformation>( - pedestrian: Pedestrian -) : PedestrianGroup { - - override val members = listOf(pedestrian) - - override fun addMember(node: Pedestrian): PedestrianGroup = - throw UnsupportedOperationException() - - override fun removeMember(node: Pedestrian): PedestrianGroup = - throw UnsupportedOperationException() -} +class Alone( + node: Node +) : Group, MutableList> by mutableListOf(node) diff --git a/alchemist-cognitive-agents/src/main/kotlin/it/unibo/alchemist/model/implementations/groups/Family.kt b/alchemist-cognitive-agents/src/main/kotlin/it/unibo/alchemist/model/implementations/groups/Family.kt index c8069e9f59..36d5a3975e 100644 --- a/alchemist-cognitive-agents/src/main/kotlin/it/unibo/alchemist/model/implementations/groups/Family.kt +++ b/alchemist-cognitive-agents/src/main/kotlin/it/unibo/alchemist/model/implementations/groups/Family.kt @@ -1,18 +1,16 @@ package it.unibo.alchemist.model.implementations.groups import it.unibo.alchemist.model.interfaces.GroupWithLeader -import it.unibo.alchemist.model.interfaces.Pedestrian -import it.unibo.alchemist.model.interfaces.geometry.GeometricTransformation -import it.unibo.alchemist.model.interfaces.geometry.Vector +import it.unibo.alchemist.model.interfaces.Node /** * A [Family] is modeled as a group of pedestrians with a leader. */ -class Family, A : GeometricTransformation>( - comparator: Comparator> = Comparator { a, b -> a.id.compareTo(b.id) } -) : GenericGroup>(), - GroupWithLeader> { +class Family( + comparator: Comparator> = Comparator { a, b -> a.id.compareTo(b.id) } +) : GenericGroup>(), + GroupWithLeader> { - override val leader: Pedestrian = + override val leader: Node = members.minWithOrNull(comparator) ?: throw IllegalStateException("Can't determine a leader.") } diff --git a/alchemist-cognitive-agents/src/main/kotlin/it/unibo/alchemist/model/implementations/groups/Friends.kt b/alchemist-cognitive-agents/src/main/kotlin/it/unibo/alchemist/model/implementations/groups/Friends.kt index 9d5ab13826..bb3c162876 100644 --- a/alchemist-cognitive-agents/src/main/kotlin/it/unibo/alchemist/model/implementations/groups/Friends.kt +++ b/alchemist-cognitive-agents/src/main/kotlin/it/unibo/alchemist/model/implementations/groups/Friends.kt @@ -1,10 +1,8 @@ package it.unibo.alchemist.model.implementations.groups -import it.unibo.alchemist.model.interfaces.Pedestrian -import it.unibo.alchemist.model.interfaces.geometry.GeometricTransformation -import it.unibo.alchemist.model.interfaces.geometry.Vector +import it.unibo.alchemist.model.interfaces.Node /** * A generic, leaderless group of pedestrians. */ -class Friends, A : GeometricTransformation> : GenericGroup>() +class Friends : GenericGroup>() diff --git a/alchemist-cognitive-agents/src/main/kotlin/it/unibo/alchemist/model/implementations/groups/GenericGroup.kt b/alchemist-cognitive-agents/src/main/kotlin/it/unibo/alchemist/model/implementations/groups/GenericGroup.kt index 328bc9b90d..ab03034669 100644 --- a/alchemist-cognitive-agents/src/main/kotlin/it/unibo/alchemist/model/implementations/groups/GenericGroup.kt +++ b/alchemist-cognitive-agents/src/main/kotlin/it/unibo/alchemist/model/implementations/groups/GenericGroup.kt @@ -6,20 +6,21 @@ import it.unibo.alchemist.model.interfaces.Node /** * Basic implementation of a group. */ -open class GenericGroup> : Group { +open class GenericGroup>( + members: List = mutableListOf(), +) : Group, MutableList> by mutableListOf() { - private val _members: MutableList = mutableListOf() - - override val members: List - get() = _members - - override fun addMember(node: N): Group = apply { - if (!members.contains(node)) { - _members.add(node) - } + init { + members.forEach { this.addMember(it) } } - override fun removeMember(node: N): Group = apply { - _members.remove(node) - } + /** + * adds [node] to the group if not already added. + */ + fun addMember(node: N) = node !in this && add(node) + + /** + * removes, if present, [node] from the group. + */ + fun removeMember(node: N) = this.remove(node) } diff --git a/alchemist-cognitive-agents/src/main/kotlin/it/unibo/alchemist/model/implementations/groups/GroupFactory.kt b/alchemist-cognitive-agents/src/main/kotlin/it/unibo/alchemist/model/implementations/groups/GroupFactory.kt index 89a6ee706c..fe067be414 100644 --- a/alchemist-cognitive-agents/src/main/kotlin/it/unibo/alchemist/model/implementations/groups/GroupFactory.kt +++ b/alchemist-cognitive-agents/src/main/kotlin/it/unibo/alchemist/model/implementations/groups/GroupFactory.kt @@ -17,10 +17,10 @@ object GroupFactory { /** * Builds a new [Family]. */ - fun family(): Family = Family() + fun family(): Family = Family() /** * Builds a new group of [Friends]. */ - fun friends(): Friends = Friends() + fun friends(): Friends = Friends() } diff --git a/alchemist-cognitive-agents/src/main/kotlin/it/unibo/alchemist/model/implementations/nodes/AbstractCognitivePedestrian.kt b/alchemist-cognitive-agents/src/main/kotlin/it/unibo/alchemist/model/implementations/nodes/AbstractCognitivePedestrian.kt deleted file mode 100644 index c60830f040..0000000000 --- a/alchemist-cognitive-agents/src/main/kotlin/it/unibo/alchemist/model/implementations/nodes/AbstractCognitivePedestrian.kt +++ /dev/null @@ -1,61 +0,0 @@ -package it.unibo.alchemist.model.implementations.nodes - -import it.unibo.alchemist.model.cognitiveagents.CognitiveModel -import it.unibo.alchemist.model.cognitiveagents.impact.ImpactModel -import it.unibo.alchemist.model.cognitiveagents.impact.individual.Age -import it.unibo.alchemist.model.cognitiveagents.impact.individual.Gender -import it.unibo.alchemist.model.interfaces.CognitivePedestrian -import it.unibo.alchemist.model.interfaces.Molecule -import it.unibo.alchemist.model.interfaces.Node -import it.unibo.alchemist.model.interfaces.PedestrianGroup -import it.unibo.alchemist.model.interfaces.Position -import it.unibo.alchemist.model.interfaces.environments.PhysicsEnvironment -import it.unibo.alchemist.model.interfaces.geometry.GeometricShapeFactory -import it.unibo.alchemist.model.interfaces.geometry.GeometricTransformation -import it.unibo.alchemist.model.interfaces.geometry.Vector -import org.apache.commons.math3.random.RandomGenerator - -/** - * Implementation of a cognitive pedestrian. - * - * @param environment - * the environment inside which this pedestrian moves. - * @param randomGenerator - * the simulation {@link RandomGenerator}. - * @param age - * the age of this pedestrian. - * @param gender - * the gender of this pedestrian - * @param danger - * the molecule associated to danger in the environment. - */ -abstract class AbstractCognitivePedestrian @JvmOverloads constructor( - environment: PhysicsEnvironment, - randomGenerator: RandomGenerator, - backingNode: Node, - age: Age, - gender: Gender, - val danger: Molecule? = null, - group: PedestrianGroup? = null, - cognitive: CognitiveModel? = null -) : AbstractHeterogeneousPedestrian(randomGenerator, backingNode, age, gender, group), - CognitivePedestrian - where P : Position

, P : Vector

, - A : GeometricTransformation

, - F : GeometricShapeFactory { - - override val cognitiveModel: CognitiveModel by lazy { - cognitive ?: ImpactModel(pedestrianModel.compliance, ::influencialPeople) { - environment.getLayer(danger) - .map { it.getValue(environment.getPosition(this)) as Double } - .orElse(0.0) - } - } - - override fun speed() = - if (cognitiveModel.wantsToEscape()) { - runningSpeed * minOf(cognitiveModel.escapeIntention(), 1.0) - } else { - walkingSpeed * minOf(cognitiveModel.remainIntention(), 1.0) - } -} diff --git a/alchemist-cognitive-agents/src/main/kotlin/it/unibo/alchemist/model/implementations/nodes/AbstractHeterogeneousPedestrian.kt b/alchemist-cognitive-agents/src/main/kotlin/it/unibo/alchemist/model/implementations/nodes/AbstractHeterogeneousPedestrian.kt deleted file mode 100644 index 829517bc90..0000000000 --- a/alchemist-cognitive-agents/src/main/kotlin/it/unibo/alchemist/model/implementations/nodes/AbstractHeterogeneousPedestrian.kt +++ /dev/null @@ -1,50 +0,0 @@ -package it.unibo.alchemist.model.implementations.nodes - -import it.unibo.alchemist.model.HeterogeneousPedestrianModel -import it.unibo.alchemist.model.cognitiveagents.impact.individual.Age -import it.unibo.alchemist.model.cognitiveagents.impact.individual.Gender -import it.unibo.alchemist.model.cognitiveagents.impact.individual.Speed -import it.unibo.alchemist.model.interfaces.HeterogeneousPedestrian -import it.unibo.alchemist.model.interfaces.Node -import it.unibo.alchemist.model.interfaces.PedestrianGroup -import it.unibo.alchemist.model.interfaces.Position -import it.unibo.alchemist.model.interfaces.geometry.GeometricShapeFactory -import it.unibo.alchemist.model.interfaces.geometry.GeometricTransformation -import it.unibo.alchemist.model.interfaces.geometry.Vector -import org.apache.commons.math3.random.RandomGenerator - -/** - * Implementation of a heterogeneous pedestrian. - * - * @param randomGenerator - * the simulation {@link RandomGenerator}. - * @param age - * the age of this pedestrian. - * @param gender - * the gender of this pedestrian - */ -abstract class AbstractHeterogeneousPedestrian @JvmOverloads constructor( - randomGenerator: RandomGenerator, - backingNode: Node, - age: Age, - gender: Gender, - group: PedestrianGroup? = null -) : AbstractHomogeneousPedestrian(randomGenerator, backingNode, group), - HeterogeneousPedestrian - where P : Vector

, P : Position

, - A : GeometricTransformation

, - F : GeometricShapeFactory { - - final override val pedestrianModel: HeterogeneousPedestrianModel = HeterogeneousPedestrianModel( - age = age, - gender = gender, - speed = Speed(age, gender, randomGenerator), - ) - - override val walkingSpeed = pedestrianModel.speed.walking - - override val runningSpeed = pedestrianModel.speed.running - -// override fun probabilityOfHelping(toHelp: HeterogeneousPedestrian) = -// model.helpAttitude.level(toHelp.model.age, toHelp.model.gender, membershipGroup.contains(toHelp)) -} diff --git a/alchemist-cognitive-agents/src/main/kotlin/it/unibo/alchemist/model/implementations/nodes/AbstractHomogeneousPedestrian.kt b/alchemist-cognitive-agents/src/main/kotlin/it/unibo/alchemist/model/implementations/nodes/AbstractHomogeneousPedestrian.kt deleted file mode 100644 index d1f1c86116..0000000000 --- a/alchemist-cognitive-agents/src/main/kotlin/it/unibo/alchemist/model/implementations/nodes/AbstractHomogeneousPedestrian.kt +++ /dev/null @@ -1,44 +0,0 @@ -package it.unibo.alchemist.model.implementations.nodes - -import it.unibo.alchemist.model.cognitiveagents.impact.individual.Speed -import it.unibo.alchemist.model.implementations.groups.Alone -import it.unibo.alchemist.model.interfaces.Node -import it.unibo.alchemist.model.interfaces.Pedestrian -import it.unibo.alchemist.model.interfaces.PedestrianGroup -import it.unibo.alchemist.model.interfaces.Position -import it.unibo.alchemist.model.interfaces.geometry.GeometricShapeFactory -import it.unibo.alchemist.model.interfaces.geometry.GeometricTransformation -import it.unibo.alchemist.model.interfaces.geometry.Vector -import it.unibo.alchemist.nextDouble -import org.apache.commons.math3.random.RandomGenerator - -/** - * Implementation of a basic pedestrian. - * - */ -abstract class AbstractHomogeneousPedestrian @JvmOverloads constructor( - protected val randomGenerator: RandomGenerator, - protected val backingNode: Node, - group: PedestrianGroup? = null -) : Node by backingNode, Pedestrian -where -P : Position

, P : Vector

, -A : GeometricTransformation

, -F : GeometricShapeFactory { - override val membershipGroup: PedestrianGroup by lazy { - val pedestrianGroup = group?.addMember(this) as? PedestrianGroup - pedestrianGroup ?: Alone(this) - } - - /** - * The speed at which the pedestrian moves if it's walking. - */ - protected open val walkingSpeed: Double = Speed.default - - /** - * The speed at which the pedestrian moves if it's running. - */ - protected open val runningSpeed: Double = Speed.default * 3 - - override fun speed() = randomGenerator.nextDouble(walkingSpeed, runningSpeed) -} diff --git a/alchemist-cognitive-agents/src/main/kotlin/it/unibo/alchemist/model/implementations/nodes/CognitiveOrientingPedestrian2D.kt b/alchemist-cognitive-agents/src/main/kotlin/it/unibo/alchemist/model/implementations/nodes/CognitiveOrientingPedestrian2D.kt deleted file mode 100644 index 27da601594..0000000000 --- a/alchemist-cognitive-agents/src/main/kotlin/it/unibo/alchemist/model/implementations/nodes/CognitiveOrientingPedestrian2D.kt +++ /dev/null @@ -1,95 +0,0 @@ -/* - * Copyright (C) 2010-2020, Danilo Pianini and contributors - * listed in the main project's alchemist/build.gradle.kts file. - * - * This file is part of Alchemist, and is distributed under the terms of the - * GNU General Public License, with a linking exception, - * as described in the file LICENSE in the Alchemist distribution's top directory. - */ - -package it.unibo.alchemist.model.implementations.nodes - -import it.unibo.alchemist.model.HeterogeneousPedestrianModel -import it.unibo.alchemist.model.cognitiveagents.CognitiveModel -import it.unibo.alchemist.model.cognitiveagents.impact.ImpactModel -import it.unibo.alchemist.model.cognitiveagents.impact.individual.Age -import it.unibo.alchemist.model.cognitiveagents.impact.individual.Gender -import it.unibo.alchemist.model.cognitiveagents.impact.individual.Speed -import it.unibo.alchemist.model.implementations.positions.Euclidean2DPosition -import it.unibo.alchemist.model.interfaces.CognitivePedestrian -import it.unibo.alchemist.model.interfaces.Incarnation -import it.unibo.alchemist.model.interfaces.Molecule -import it.unibo.alchemist.model.interfaces.Node -import it.unibo.alchemist.model.interfaces.OrientingPedestrian -import it.unibo.alchemist.model.interfaces.PedestrianGroup2D -import it.unibo.alchemist.model.interfaces.environments.EuclideanPhysics2DEnvironmentWithGraph -import it.unibo.alchemist.model.interfaces.geometry.euclidean2d.ConvexPolygon -import it.unibo.alchemist.model.interfaces.geometry.euclidean2d.Euclidean2DTransformation -import org.apache.commons.math3.random.RandomGenerator - -/** - * A cognitive [OrientingPedestrian] in the Euclidean world. - * - * @param T the concentration type. - * @param N the type of nodes of the navigation graph provided by the environment. - * @param E the type of edges of the navigation graph provided by the environment. - */ -class CognitiveOrientingPedestrian2D @JvmOverloads constructor( - randomGenerator: RandomGenerator, - environment: EuclideanPhysics2DEnvironmentWithGraph<*, T, N, E>, - backingNode: Node, - knowledgeDegree: Double, - group: PedestrianGroup2D? = null, - age: Age, - gender: Gender, - danger: Molecule? = null, -) : HomogeneousOrientingPedestrian2D( - randomGenerator, - environment, - backingNode, - knowledgeDegree = knowledgeDegree, - group = group -), - CognitivePedestrian { - - /** - * Allows to specify age and gender with a string. - */ - @JvmOverloads constructor( - incarnation: Incarnation, - randomGenerator: RandomGenerator, - environment: EuclideanPhysics2DEnvironmentWithGraph<*, T, N, E>, - nodeCreationParameter: String? = null, - knowledgeDegree: Double, - group: PedestrianGroup2D? = null, - age: Any, - gender: String, - danger: Molecule? = null - ) : this( - randomGenerator, - environment, - incarnation.createNode(randomGenerator, environment, nodeCreationParameter), - knowledgeDegree, - group, - Age.fromAny(age), - Gender.fromString(gender), - danger - ) - - /** - * The pedestrian model, containing its age, gender, and speed. - */ - val pedestrianModel: HeterogeneousPedestrianModel = - HeterogeneousPedestrianModel(age = age, gender = gender, speed = Speed(age, gender, randomGenerator)) - - /** - * The [CognitiveModel] of the pedestrian, storing their mental state. - */ - override val cognitiveModel: CognitiveModel by lazy { - ImpactModel(pedestrianModel.compliance, ::influencialPeople) { - environment.getLayer(danger) - .map { it.getValue(environment.getPosition(this)) as Double } - .orElse(0.0) - } - } -} diff --git a/alchemist-cognitive-agents/src/main/kotlin/it/unibo/alchemist/model/implementations/nodes/CognitivePedestrian2D.kt b/alchemist-cognitive-agents/src/main/kotlin/it/unibo/alchemist/model/implementations/nodes/CognitivePedestrian2D.kt deleted file mode 100644 index ff7b056cbf..0000000000 --- a/alchemist-cognitive-agents/src/main/kotlin/it/unibo/alchemist/model/implementations/nodes/CognitivePedestrian2D.kt +++ /dev/null @@ -1,65 +0,0 @@ -package it.unibo.alchemist.model.implementations.nodes - -import it.unibo.alchemist.model.cognitiveagents.impact.individual.Age -import it.unibo.alchemist.model.cognitiveagents.impact.individual.Gender -import it.unibo.alchemist.model.implementations.positions.Euclidean2DPosition -import it.unibo.alchemist.model.interfaces.Incarnation -import it.unibo.alchemist.model.interfaces.Molecule -import it.unibo.alchemist.model.interfaces.Node -import it.unibo.alchemist.model.interfaces.Pedestrian2D -import it.unibo.alchemist.model.interfaces.PedestrianGroup2D -import it.unibo.alchemist.model.interfaces.environments.Physics2DEnvironment -import it.unibo.alchemist.model.interfaces.geometry.euclidean2d.Euclidean2DShapeFactory -import it.unibo.alchemist.model.interfaces.geometry.euclidean2d.Euclidean2DTransformation -import org.apache.commons.math3.random.RandomGenerator - -/** - * Implementation of a cognitive pedestrian in the Euclidean world. - * - * @param environment - * the environment inside which this pedestrian moves. - * @param randomGenerator - * the simulation {@link RandomGenerator}. - * @param age - * the age of this pedestrian. - * @param gender - * the gender of this pedestrian - * @param danger - * the molecule associated to danger in the environment. - */ -open class CognitivePedestrian2D constructor( - randomGenerator: RandomGenerator, - /* - * This is final for a reason, see HomogeneousPedestrian2D. - */ - final override val environment: Physics2DEnvironment, - backingNode: Node, - age: Age, - gender: Gender, - danger: Molecule? = null, - group: PedestrianGroup2D? = null -) : - Pedestrian2D, - AbstractCognitivePedestrian( - environment, randomGenerator, backingNode, age, gender, danger, group - ) { - - @JvmOverloads constructor( - incarnation: Incarnation, - randomGenerator: RandomGenerator, - environment: Physics2DEnvironment, - age: Any, - gender: String, - danger: Molecule? = null, - group: PedestrianGroup2D? = null, - nodeCreationParameter: String? = null, - ) : this( - randomGenerator, - environment, - incarnation.createNode(randomGenerator, environment, nodeCreationParameter), - Age.fromAny(age), - Gender.fromString(gender), - danger, - group - ) -} diff --git a/alchemist-cognitive-agents/src/main/kotlin/it/unibo/alchemist/model/implementations/nodes/HeterogeneousPedestrian2D.kt b/alchemist-cognitive-agents/src/main/kotlin/it/unibo/alchemist/model/implementations/nodes/HeterogeneousPedestrian2D.kt deleted file mode 100644 index d65eb342d1..0000000000 --- a/alchemist-cognitive-agents/src/main/kotlin/it/unibo/alchemist/model/implementations/nodes/HeterogeneousPedestrian2D.kt +++ /dev/null @@ -1,42 +0,0 @@ -/* - * Copyright (C) 2010-2021, Danilo Pianini and contributors - * listed, for each module, in the respective subproject's build.gradle.kts file. - * - * This file is part of Alchemist, and is distributed under the terms of the - * GNU General Public License, with a linking exception, - * as described in the file LICENSE in the Alchemist distribution's top directory. - */ - -package it.unibo.alchemist.model.implementations.nodes - -import it.unibo.alchemist.model.cognitiveagents.impact.individual.Age -import it.unibo.alchemist.model.cognitiveagents.impact.individual.Gender -import it.unibo.alchemist.model.implementations.positions.Euclidean2DPosition -import it.unibo.alchemist.model.interfaces.Incarnation -import it.unibo.alchemist.model.interfaces.Pedestrian2D -import it.unibo.alchemist.model.interfaces.PedestrianGroup2D -import it.unibo.alchemist.model.interfaces.environments.Physics2DEnvironment -import it.unibo.alchemist.model.interfaces.geometry.euclidean2d.Euclidean2DShapeFactory -import it.unibo.alchemist.model.interfaces.geometry.euclidean2d.Euclidean2DTransformation -import org.apache.commons.math3.random.RandomGenerator - -/** - * A pedestrian with heterogeneous characteristics. Requires a bidimensional [environment] with support for physics - * ([Physics2DEnvironment]). - */ -class HeterogeneousPedestrian2D @JvmOverloads constructor( - incarnation: Incarnation, - randomGenerator: RandomGenerator, - override val environment: Physics2DEnvironment, - age: Any, - gender: String, - group: PedestrianGroup2D? = null, - nodeCreationParameter: String? = null, -) : AbstractHeterogeneousPedestrian( - randomGenerator, - incarnation.createNode(randomGenerator, environment, nodeCreationParameter), - Age.fromAny(age), - Gender.fromString(gender), - group -), - Pedestrian2D diff --git a/alchemist-cognitive-agents/src/main/kotlin/it/unibo/alchemist/model/implementations/nodes/HomogeneousOrientingPedestrian2D.kt b/alchemist-cognitive-agents/src/main/kotlin/it/unibo/alchemist/model/implementations/nodes/HomogeneousOrientingPedestrian2D.kt deleted file mode 100644 index 499b3b4567..0000000000 --- a/alchemist-cognitive-agents/src/main/kotlin/it/unibo/alchemist/model/implementations/nodes/HomogeneousOrientingPedestrian2D.kt +++ /dev/null @@ -1,94 +0,0 @@ -/* - * Copyright (C) 2010-2020, Danilo Pianini and contributors - * listed in the main project's alchemist/build.gradle.kts file. - * - * This file is part of Alchemist, and is distributed under the terms of the - * GNU General Public License, with a linking exception, - * as described in the file LICENSE in the Alchemist distribution's top directory. - */ - -package it.unibo.alchemist.model.implementations.nodes - -import it.unibo.alchemist.model.implementations.geometry.euclidean2d.Ellipse -import it.unibo.alchemist.model.implementations.positions.Euclidean2DPosition -import it.unibo.alchemist.model.interfaces.Incarnation -import it.unibo.alchemist.model.interfaces.Node -import it.unibo.alchemist.model.interfaces.OrientingPedestrian -import it.unibo.alchemist.model.interfaces.Pedestrian2D -import it.unibo.alchemist.model.interfaces.PedestrianGroup2D -import it.unibo.alchemist.model.interfaces.environments.EuclideanPhysics2DEnvironmentWithGraph -import it.unibo.alchemist.model.interfaces.geometry.euclidean2d.ConvexPolygon -import it.unibo.alchemist.model.interfaces.geometry.euclidean2d.Euclidean2DShapeFactory -import it.unibo.alchemist.model.interfaces.geometry.euclidean2d.Euclidean2DTransformation -import it.unibo.alchemist.nextDouble -import org.apache.commons.math3.random.RandomGenerator -import java.awt.geom.Ellipse2D -import java.awt.geom.Rectangle2D - -private typealias Position2D = Euclidean2DPosition -private typealias Transformation2D = Euclidean2DTransformation -private typealias ShapeFactory = Euclidean2DShapeFactory - -/** - * A homogeneous [OrientingPedestrian] in the Euclidean world. Landmarks are represented as [Ellipse]s, which can - * model the human error concerning both the exact position of a landmark and the angles formed by the connections - * between them. This class accepts an environment whose graph contains [ConvexPolygon]al nodes. - * - * @param T the concentration type. - * @param N the type of nodes of the navigation graph provided by the environment. - * @param E the type of edges of the navigation graph provided by the environment. - */ -open class HomogeneousOrientingPedestrian2D @JvmOverloads constructor( - randomGenerator: RandomGenerator, - override val environment: EuclideanPhysics2DEnvironmentWithGraph<*, T, N, E>, - backingNode: Node, - knowledgeDegree: Double, - /** - * The starting width and height of the generated Ellipses will be a random quantity in - * ([minSide, maxSide] * the diameter of this pedestrian). - */ - private val minSide: Double = 30.0, - private val maxSide: Double = 60.0, - group: PedestrianGroup2D? = null -) : - Pedestrian2D, - AbstractOrientingPedestrian( - randomGenerator, - environment, - backingNode, - knowledgeDegree, - group, - ) { - - @JvmOverloads constructor( - incarnation: Incarnation, - randomGenerator: RandomGenerator, - environment: EuclideanPhysics2DEnvironmentWithGraph<*, T, N, E>, - nodeCreationParameter: String? = null, - knowledgeDegree: Double, - minSide: Double = 30.0, - maxSide: Double = 60.0, - group: PedestrianGroup2D? = null - ) : this( - randomGenerator, - environment, - incarnation.createNode(randomGenerator, environment, nodeCreationParameter), - knowledgeDegree, - minSide, - maxSide, - group, - ) - - override fun createLandmarkIn(area: N): Ellipse = with(area) { - val width = randomEllipseSide() - val height = randomEllipseSide() - val frame = Rectangle2D.Double(centroid.x, centroid.y, width, height) - while (!contains(frame)) { - frame.width /= 2 - frame.height /= 2 - } - Ellipse(Ellipse2D.Double(frame.x, frame.y, frame.width, frame.height)) - } - - private fun randomEllipseSide(): Double = randomGenerator.nextDouble(minSide, maxSide) * shape.diameter -} diff --git a/alchemist-cognitive-agents/src/main/kotlin/it/unibo/alchemist/model/implementations/nodes/HomogeneousPedestrian2D.kt b/alchemist-cognitive-agents/src/main/kotlin/it/unibo/alchemist/model/implementations/nodes/HomogeneousPedestrian2D.kt deleted file mode 100644 index 5ba9c16fbe..0000000000 --- a/alchemist-cognitive-agents/src/main/kotlin/it/unibo/alchemist/model/implementations/nodes/HomogeneousPedestrian2D.kt +++ /dev/null @@ -1,53 +0,0 @@ -package it.unibo.alchemist.model.implementations.nodes - -import it.unibo.alchemist.model.implementations.positions.Euclidean2DPosition -import it.unibo.alchemist.model.interfaces.Incarnation -import it.unibo.alchemist.model.interfaces.Node -import it.unibo.alchemist.model.interfaces.Pedestrian2D -import it.unibo.alchemist.model.interfaces.PedestrianGroup2D -import it.unibo.alchemist.model.interfaces.environments.Physics2DEnvironment -import it.unibo.alchemist.model.interfaces.geometry.euclidean2d.Euclidean2DShapeFactory -import it.unibo.alchemist.model.interfaces.geometry.euclidean2d.Euclidean2DTransformation -import org.apache.commons.math3.random.RandomGenerator - -private typealias AbstractHomogeneousPedestrian2D = - AbstractHomogeneousPedestrian - -/** - * Implementation of a homogeneous pedestrian in the Euclidean world. - * - * @param environment - * the environment inside which this pedestrian moves. - * @param randomGenerator - * the simulation {@link RandomGenerator}. - */ -open class HomogeneousPedestrian2D @JvmOverloads constructor( - randomGenerator: RandomGenerator, - /* - * This is final because otherwise instancing a subclass overriding it would throw a NullPointerException when - * computing fieldOfView (as such computation uses this property, which would not be initialised yet in the sub- - * class, a bit tricky). - */ - final override val environment: Physics2DEnvironment, - backingNode: Node, - group: PedestrianGroup2D? = null -) : AbstractHomogeneousPedestrian2D( - randomGenerator, - backingNode, - group -), - Pedestrian2D { - - @JvmOverloads constructor( - incarnation: Incarnation, - randomGenerator: RandomGenerator, - environment: Physics2DEnvironment, - nodeCreationParameter: String? = null, - group: PedestrianGroup2D? = null - ) : this( - randomGenerator, - environment, - incarnation.createNode(randomGenerator, environment, nodeCreationParameter), - group - ) -} diff --git a/alchemist-cognitive-agents/src/main/kotlin/it/unibo/alchemist/model/implementations/properties/Cognitive.kt b/alchemist-cognitive-agents/src/main/kotlin/it/unibo/alchemist/model/implementations/properties/Cognitive.kt new file mode 100644 index 0000000000..c0ff9d722a --- /dev/null +++ b/alchemist-cognitive-agents/src/main/kotlin/it/unibo/alchemist/model/implementations/properties/Cognitive.kt @@ -0,0 +1,48 @@ +/* + * Copyright (C) 2010-2022, Danilo Pianini and contributors + * listed, for each module, in the respective subproject's build.gradle.kts file. + * + * This file is part of Alchemist, and is distributed under the terms of the + * GNU General Public License, with a linking exception, + * as described in the file LICENSE in the Alchemist distribution's top directory. + */ + +package it.unibo.alchemist.model.implementations.properties + +import it.unibo.alchemist.model.cognitiveagents.CognitiveModel +import it.unibo.alchemist.model.cognitiveagents.impact.ImpactModel +import it.unibo.alchemist.model.interfaces.Molecule +import it.unibo.alchemist.model.interfaces.Node +import it.unibo.alchemist.model.interfaces.Node.Companion.asProperty +import it.unibo.alchemist.model.interfaces.Position +import it.unibo.alchemist.model.interfaces.properties.CognitiveProperty +import it.unibo.alchemist.model.interfaces.properties.HumanProperty +import it.unibo.alchemist.model.interfaces.environments.PhysicsEnvironment +import it.unibo.alchemist.model.interfaces.geometry.GeometricTransformation +import it.unibo.alchemist.model.interfaces.geometry.GeometricShapeFactory +import it.unibo.alchemist.model.interfaces.geometry.Vector + +/** + * The node's [CognitiveModel]. + */ +data class Cognitive @JvmOverloads constructor( + private val environment: PhysicsEnvironment, + override val node: Node, + override val danger: Molecule? = null, +) : CognitiveProperty +where P : Position

, + P : Vector

, + A : GeometricTransformation

, + F : GeometricShapeFactory { + override val cognitiveModel: CognitiveModel by lazy { + ImpactModel( + node.asProperty>().compliance, ::influentialPeople, + ) { + environment.getLayer(danger) + .map { it.getValue(environment.getPosition(node)) as Double } + .orElse(0.0) + } + } + + override fun cloneOnNewNode(node: Node) = Cognitive(environment, node, danger) +} diff --git a/alchemist-cognitive-agents/src/main/kotlin/it/unibo/alchemist/model/implementations/properties/Cognitive2D.kt b/alchemist-cognitive-agents/src/main/kotlin/it/unibo/alchemist/model/implementations/properties/Cognitive2D.kt new file mode 100644 index 0000000000..0d2930546d --- /dev/null +++ b/alchemist-cognitive-agents/src/main/kotlin/it/unibo/alchemist/model/implementations/properties/Cognitive2D.kt @@ -0,0 +1,31 @@ +/* + * Copyright (C) 2010-2022, Danilo Pianini and contributors + * listed, for each module, in the respective subproject's build.gradle.kts file. + * + * This file is part of Alchemist, and is distributed under the terms of the + * GNU General Public License, with a linking exception, + * as described in the file LICENSE in the Alchemist distribution's top directory. + */ + +package it.unibo.alchemist.model.implementations.properties + +import it.unibo.alchemist.model.cognitiveagents.CognitiveModel +import it.unibo.alchemist.model.interfaces.Molecule +import it.unibo.alchemist.model.interfaces.Node +import it.unibo.alchemist.model.interfaces.properties.CognitiveProperty +import it.unibo.alchemist.model.interfaces.environments.Physics2DEnvironment + +/** + * The node's [CognitiveModel]. + */ +class Cognitive2D @JvmOverloads constructor( + /** + * The environment in which the node moves. + */ + environment: Physics2DEnvironment, + node: Node, + /** + * The molecule associated with danger in the environment. + */ + danger: Molecule? = null, +) : CognitiveProperty by Cognitive(environment, node, danger) diff --git a/alchemist-cognitive-agents/src/main/kotlin/it/unibo/alchemist/model/implementations/properties/CognitivePedestrian.kt b/alchemist-cognitive-agents/src/main/kotlin/it/unibo/alchemist/model/implementations/properties/CognitivePedestrian.kt new file mode 100644 index 0000000000..d112228d4a --- /dev/null +++ b/alchemist-cognitive-agents/src/main/kotlin/it/unibo/alchemist/model/implementations/properties/CognitivePedestrian.kt @@ -0,0 +1,34 @@ +/* + * Copyright (C) 2010-2022, Danilo Pianini and contributors + * listed, for each module, in the respective subproject's build.gradle.kts file. + * + * This file is part of Alchemist, and is distributed under the terms of the + * GNU General Public License, with a linking exception, + * as described in the file LICENSE in the Alchemist distribution's top directory. + */ +package it.unibo.alchemist.model.implementations.properties + +import it.unibo.alchemist.model.interfaces.Node +import it.unibo.alchemist.model.interfaces.properties.CognitiveProperty +import it.unibo.alchemist.model.interfaces.geometry.GeometricTransformation +import it.unibo.alchemist.model.interfaces.geometry.Vector +import org.apache.commons.math3.random.RandomGenerator +import it.unibo.alchemist.model.interfaces.Node.Companion.asProperty + +/** + * A cognitive pedestrian's movement capability. + */ +class CognitivePedestrian( + randomGenerator: RandomGenerator, + node: Node, +) : HeterogeneousPedestrian(randomGenerator, node) + where S : Vector, A : GeometricTransformation { + override fun speed(): Double { + val myCognitiveModel = node.asProperty>().cognitiveModel + return if (myCognitiveModel.wantsToEscape()) { + runningSpeed * minOf(myCognitiveModel.escapeIntention(), 1.0) + } else { + walkingSpeed * minOf(myCognitiveModel.remainIntention(), 1.0) + } + } +} diff --git a/alchemist-cognitive-agents/src/main/kotlin/it/unibo/alchemist/model/implementations/properties/HeterogeneousPedestrian.kt b/alchemist-cognitive-agents/src/main/kotlin/it/unibo/alchemist/model/implementations/properties/HeterogeneousPedestrian.kt new file mode 100644 index 0000000000..983d078fa6 --- /dev/null +++ b/alchemist-cognitive-agents/src/main/kotlin/it/unibo/alchemist/model/implementations/properties/HeterogeneousPedestrian.kt @@ -0,0 +1,32 @@ +/* + * Copyright (C) 2010-2022, Danilo Pianini and contributors + * listed, for each module, in the respective subproject's build.gradle.kts file. + * + * This file is part of Alchemist, and is distributed under the terms of the + * GNU General Public License, with a linking exception, + * as described in the file LICENSE in the Alchemist distribution's top directory. + */ + +package it.unibo.alchemist.model.implementations.properties + +import it.unibo.alchemist.model.interfaces.Node +import it.unibo.alchemist.model.interfaces.properties.HumanProperty +import org.apache.commons.math3.random.RandomGenerator +import it.unibo.alchemist.model.interfaces.Node.Companion.asProperty +import it.unibo.alchemist.model.interfaces.geometry.GeometricTransformation +import it.unibo.alchemist.model.interfaces.geometry.Vector + +/** + * A heterogeneous pedestrian's movement capability. + * Note: to use this capability the node must already have a [HumanProperty]. + */ +open class HeterogeneousPedestrian ( + randomGenerator: RandomGenerator, + node: Node, +) : Pedestrian ( + randomGenerator, + node +) where S : Vector, A : GeometricTransformation { + override val walkingSpeed: Double get() = node.asProperty>().speed.walking + override val runningSpeed: Double get() = node.asProperty>().speed.running +} diff --git a/alchemist-cognitive-agents/src/main/kotlin/it/unibo/alchemist/model/implementations/properties/Human.kt b/alchemist-cognitive-agents/src/main/kotlin/it/unibo/alchemist/model/implementations/properties/Human.kt new file mode 100644 index 0000000000..1bbe3f377b --- /dev/null +++ b/alchemist-cognitive-agents/src/main/kotlin/it/unibo/alchemist/model/implementations/properties/Human.kt @@ -0,0 +1,52 @@ +/* + * Copyright (C) 2010-2022, Danilo Pianini and contributors + * listed, for each module, in the respective subproject's build.gradle.kts file. + * + * This file is part of Alchemist, and is distributed under the terms of the + * GNU General Public License, with a linking exception, + * as described in the file LICENSE in the Alchemist distribution's top directory. + */ + +package it.unibo.alchemist.model.implementations.properties + +import it.unibo.alchemist.model.cognitiveagents.impact.individual.Age +import it.unibo.alchemist.model.cognitiveagents.impact.individual.Compliance +import it.unibo.alchemist.model.cognitiveagents.impact.individual.Gender +import it.unibo.alchemist.model.cognitiveagents.impact.individual.HelpAttitude +import it.unibo.alchemist.model.cognitiveagents.impact.individual.Speed +import it.unibo.alchemist.model.interfaces.Node +import it.unibo.alchemist.model.interfaces.properties.Human2DProperty +import org.apache.commons.math3.random.RandomGenerator + +/** + * A pedestrian's individual characteristics. + */ +class Human @JvmOverloads constructor( + private val randomGenerator: RandomGenerator, + override val node: Node, + override val age: Age, + override val gender: Gender, + override val speed: Speed = Speed(age, gender, randomGenerator), + override val compliance: Double = Compliance(age, gender).level, + override val helpAttitude: HelpAttitude = HelpAttitude(age, gender), +) : Human2DProperty { + @JvmOverloads constructor( + randomGenerator: RandomGenerator, + node: Node, + age: Any, + gender: String, + speed: Speed = Speed(Age.fromAny(age), Gender.fromString(gender), randomGenerator), + compliance: Double = Compliance(Age.fromAny(age), Gender.fromString(gender)).level, + helpAttitude: HelpAttitude = HelpAttitude(Age.fromAny(age), Gender.fromString(gender)) + ) : this(randomGenerator, node, Age.fromAny(age), Gender.fromString(gender), speed, compliance, helpAttitude) + + override fun cloneOnNewNode(node: Node) = Human( + randomGenerator, + node, + age, + gender, + speed, + compliance, + helpAttitude, + ) +} diff --git a/alchemist-cognitive-agents/src/main/kotlin/it/unibo/alchemist/model/implementations/nodes/AbstractOrientingPedestrian.kt b/alchemist-cognitive-agents/src/main/kotlin/it/unibo/alchemist/model/implementations/properties/Orienting.kt similarity index 53% rename from alchemist-cognitive-agents/src/main/kotlin/it/unibo/alchemist/model/implementations/nodes/AbstractOrientingPedestrian.kt rename to alchemist-cognitive-agents/src/main/kotlin/it/unibo/alchemist/model/implementations/properties/Orienting.kt index 3a5277e9c0..d82f7b2976 100644 --- a/alchemist-cognitive-agents/src/main/kotlin/it/unibo/alchemist/model/implementations/nodes/AbstractOrientingPedestrian.kt +++ b/alchemist-cognitive-agents/src/main/kotlin/it/unibo/alchemist/model/implementations/properties/Orienting.kt @@ -1,102 +1,67 @@ /* - * Copyright (C) 2010-2020, Danilo Pianini and contributors - * listed in the main project's alchemist/build.gradle.kts file. + * Copyright (C) 2010-2022, Danilo Pianini and contributors + * listed, for each module, in the respective subproject's build.gradle.kts file. * * This file is part of Alchemist, and is distributed under the terms of the * GNU General Public License, with a linking exception, * as described in the file LICENSE in the Alchemist distribution's top directory. */ -package it.unibo.alchemist.model.implementations.nodes +package it.unibo.alchemist.model.implementations.properties import it.unibo.alchemist.model.implementations.actions.takePercentage import it.unibo.alchemist.model.implementations.geometry.euclidean2d.graph.UndirectedNavigationGraph import it.unibo.alchemist.model.implementations.geometry.euclidean2d.graph.pathExists import it.unibo.alchemist.model.interfaces.Node -import it.unibo.alchemist.model.interfaces.OrientingPedestrian -import it.unibo.alchemist.model.interfaces.PedestrianGroup import it.unibo.alchemist.model.interfaces.Position +import it.unibo.alchemist.model.interfaces.properties.OrientingProperty import it.unibo.alchemist.model.interfaces.environments.EnvironmentWithGraph import it.unibo.alchemist.model.interfaces.geometry.ConvexGeometricShape -import it.unibo.alchemist.model.interfaces.geometry.GeometricShapeFactory import it.unibo.alchemist.model.interfaces.geometry.GeometricTransformation import it.unibo.alchemist.model.interfaces.geometry.Vector -import it.unibo.alchemist.model.interfaces.geometry.euclidean2d.graph.NavigationGraph import it.unibo.alchemist.shuffled -import org.apache.commons.math3.random.RandomGenerator import org.jgrapht.alg.shortestpath.DijkstraShortestPath import org.jgrapht.alg.spanning.PrimMinimumSpanningTree import org.jgrapht.graph.AsWeightedGraph import org.jgrapht.graph.DefaultEdge +import it.unibo.alchemist.model.interfaces.Node.Companion.asProperty +import it.unibo.alchemist.model.interfaces.properties.OccupiesSpaceProperty +import it.unibo.alchemist.model.interfaces.geometry.euclidean2d.graph.NavigationGraph +import org.apache.commons.math3.random.RandomGenerator /** - * An abstract [OrientingPedestrian], contains an algorithm for the generation of a pseudo-random [cognitiveMap]. The - * creation of landmarks is left to subclasses via factory method (see [createLandmarkIn]). - * - * The [environment] must feature a navigation graph. - * - * @param T the concentration type. - * @param P the [Position] type and [Vector] type for the space this pedestrian is inside. - * @param A the transformations supported by the shapes in this space. - * @param L the type of landmarks in the pedestrian's [cognitiveMap]. - * @param N the type of nodes of the navigation graph provided by the environment. - * @param E the type of edges of the navigation graph provided by the environment. - * @param F the type of the shape factory provided by the environment. + * Base implementation of a node's [OrientingProperty]. */ -abstract class AbstractOrientingPedestrian( +abstract class Orienting @JvmOverloads constructor( /** - * The random generator to use in order to preserve reproducibility. + * The simulation [RandomGenerator]. */ - randomGenerator: RandomGenerator, - open val environment: EnvironmentWithGraph<*, T, P, A, N, E>, - backingNode: Node, - final override val knowledgeDegree: Double, - /** - * The environment this pedestrian is into. - */ - group: PedestrianGroup? = null, + val randomGenerator: RandomGenerator, + override val environment: EnvironmentWithGraph<*, T, P, A, N, DefaultEdge>, + override val node: Node, + override val knowledgeDegree: Double, /** * Environment's areas whose diameter is smaller than ([minArea] * the diameter of this pedestrian) will be * regarded as too small and discarded when generating the cognitive map (i.e. no landmark will be placed inside * them). */ private val minArea: Double = 10.0, -) : OrientingPedestrian, - AbstractHomogeneousPedestrian(randomGenerator, backingNode, group) - where -P : Position

, -P : Vector

, -A : GeometricTransformation

, -L : ConvexGeometricShape, -N : ConvexGeometricShape, -F : GeometricShapeFactory { - - init { - require(knowledgeDegree in 0.0..1.0) { "knowledge degree must be in [0,1]" } - } +) : OrientingProperty + where P : Position

, + P : Vector

, + A : GeometricTransformation

, + L : ConvexGeometricShape, + N : ConvexGeometricShape { override val volatileMemory: MutableMap, Int> = HashMap() - /** - * The cognitive map of the pedestrian. This is generated from the [environment]'s graph as follows: we randomly - * select a % of environment's areas equal to the knowledge degree of the pedestrian, we then create a landmark - * in each of them. Those landmarks will be the nodes of the cognitive map. Concerning the connections between - * them, we produce a graph in which each generated landmark is connected to any other landmark reachable from it, - * with an edge whose weight depends on the number of areas that need to be traversed. Finally, the cognitive map - * is a minimum spanning tree of the described full connected graph. - * Note that edges are plain [DefaultEdge]s, which means no extra info regarding the connection between landmarks - * is stored in the cognitive map. If two landmarks are connected, the pedestrian knows there's a path between them - * (this may be simple or not, i.e. representable as a single segment, but the pedestrian doesn't know it). If two - * landmarks are not connected, the pedestrian doesn't have info regarding any path between them, which may - * anyway exist. - */ override val cognitiveMap: NavigationGraph by lazy { val environmentGraph = environment.graph /* * The rooms in which landmarks will be placed. */ val rooms = environmentGraph.vertexSet() - .filter { it.diameter > shape.diameter * minArea } + .filter { it.diameter > node.asProperty>().shape.diameter * minArea } .shuffled(randomGenerator) .toList() .takePercentage(knowledgeDegree) @@ -141,5 +106,5 @@ F : GeometricShapeFactory { * Creates a landmark entirely contained in the given area. If such area contains one or more destinations, the * returned landmark must contain at least one of them. */ - protected abstract fun createLandmarkIn(area: N): L + abstract override fun createLandmarkIn(area: N): L } diff --git a/alchemist-cognitive-agents/src/main/kotlin/it/unibo/alchemist/model/implementations/properties/Orienting2D.kt b/alchemist-cognitive-agents/src/main/kotlin/it/unibo/alchemist/model/implementations/properties/Orienting2D.kt new file mode 100644 index 0000000000..e39b2e2551 --- /dev/null +++ b/alchemist-cognitive-agents/src/main/kotlin/it/unibo/alchemist/model/implementations/properties/Orienting2D.kt @@ -0,0 +1,75 @@ +/* + * Copyright (C) 2010-2022, Danilo Pianini and contributors + * listed, for each module, in the respective subproject's build.gradle.kts file. + * + * This file is part of Alchemist, and is distributed under the terms of the + * GNU General Public License, with a linking exception, + * as described in the file LICENSE in the Alchemist distribution's top directory. + */ + +package it.unibo.alchemist.model.implementations.properties + +import it.unibo.alchemist.model.implementations.geometry.euclidean2d.Ellipse +import it.unibo.alchemist.model.implementations.positions.Euclidean2DPosition +import it.unibo.alchemist.model.interfaces.Node +import it.unibo.alchemist.model.interfaces.Node.Companion.asProperty +import it.unibo.alchemist.model.interfaces.properties.OrientingProperty +import it.unibo.alchemist.model.interfaces.properties.AreaProperty +import it.unibo.alchemist.model.interfaces.environments.Euclidean2DEnvironmentWithGraph +import it.unibo.alchemist.model.interfaces.geometry.euclidean2d.ConvexPolygon +import it.unibo.alchemist.model.interfaces.geometry.euclidean2d.Euclidean2DShapeFactory +import it.unibo.alchemist.model.interfaces.geometry.euclidean2d.Euclidean2DTransformation +import it.unibo.alchemist.nextDouble +import org.apache.commons.math3.random.RandomGenerator +import org.jgrapht.graph.DefaultEdge +import java.awt.geom.Ellipse2D +import java.awt.geom.Rectangle2D + +private typealias Position2D = Euclidean2DPosition +private typealias Transformation2D = Euclidean2DTransformation +private typealias ShapeFactory = Euclidean2DShapeFactory + +/** + * Basic implementation of a node's [OrientingProperty] in a 2D space. + */ +class Orienting2D @JvmOverloads constructor( + override val environment: Euclidean2DEnvironmentWithGraph<*, T, N, DefaultEdge>, + randomGenerator: RandomGenerator, + override val node: Node, + override val knowledgeDegree: Double, + /** + * The starting width and height of the generated Ellipses will be a random quantity in + * ([minSide, maxSide] * the diameter of this pedestrian). + */ + private val minSide: Double = 30.0, + private val maxSide: Double = 60.0, +) : Orienting( + randomGenerator, + environment, + node, + knowledgeDegree, +) { + override fun createLandmarkIn(area: N): Ellipse = with(area) { + val width = randomEllipseSide() + val height = randomEllipseSide() + val frame = Rectangle2D.Double(centroid.x, centroid.y, width, height) + while (!contains(frame)) { + frame.width /= 2 + frame.height /= 2 + } + Ellipse(Ellipse2D.Double(frame.x, frame.y, frame.width, frame.height)) + } + + private fun randomEllipseSide(): Double = + randomGenerator.nextDouble(minSide, maxSide) * + node.asProperty>().shape.diameter + + override fun cloneOnNewNode(node: Node) = Orienting2D( + environment, + randomGenerator, + node, + knowledgeDegree, + minSide, + maxSide, + ) +} diff --git a/alchemist-cognitive-agents/src/main/kotlin/it/unibo/alchemist/model/implementations/properties/Pedestrian.kt b/alchemist-cognitive-agents/src/main/kotlin/it/unibo/alchemist/model/implementations/properties/Pedestrian.kt new file mode 100644 index 0000000000..cbffa6117f --- /dev/null +++ b/alchemist-cognitive-agents/src/main/kotlin/it/unibo/alchemist/model/implementations/properties/Pedestrian.kt @@ -0,0 +1,38 @@ +/* + * Copyright (C) 2010-2022, Danilo Pianini and contributors + * listed, for each module, in the respective subproject's build.gradle.kts file. + * + * This file is part of Alchemist, and is distributed under the terms of the + * GNU General Public License, with a linking exception, + * as described in the file LICENSE in the Alchemist distribution's top directory. + */ + +package it.unibo.alchemist.model.implementations.properties + +import it.unibo.alchemist.model.cognitiveagents.impact.individual.Speed +import it.unibo.alchemist.model.interfaces.Node +import it.unibo.alchemist.model.interfaces.properties.PedestrianProperty +import it.unibo.alchemist.model.interfaces.properties.RunningPedestrianProperty +import it.unibo.alchemist.model.interfaces.properties.WalkingPedestrianProperty +import it.unibo.alchemist.nextDouble +import org.apache.commons.math3.random.RandomGenerator + +/** + * Implementation of a basic [PedestrianProperty]. + */ +open class Pedestrian @JvmOverloads constructor( + /** + * The simulation random generator. + */ + private val randomGenerator: RandomGenerator, + override val node: Node, + override val walkingSpeed: Double = Speed.default, + override val runningSpeed: Double = Speed.default * 3, +) : PedestrianProperty, + WalkingPedestrianProperty by WalkingPedestrian(node, walkingSpeed), + RunningPedestrianProperty by RunningPedestrian(node, runningSpeed) { + + override fun speed(): Double = randomGenerator.nextDouble(walkingSpeed, runningSpeed) + + override fun cloneOnNewNode(node: Node) = Pedestrian(randomGenerator, node, walkingSpeed, runningSpeed) +} diff --git a/alchemist-cognitive-agents/src/main/kotlin/it/unibo/alchemist/model/implementations/properties/Perceptive.kt b/alchemist-cognitive-agents/src/main/kotlin/it/unibo/alchemist/model/implementations/properties/Perceptive.kt new file mode 100644 index 0000000000..24bf822119 --- /dev/null +++ b/alchemist-cognitive-agents/src/main/kotlin/it/unibo/alchemist/model/implementations/properties/Perceptive.kt @@ -0,0 +1,25 @@ +/* + * Copyright (C) 2010-2022, Danilo Pianini and contributors + * listed, for each module, in the respective subproject's build.gradle.kts file. + * + * This file is part of Alchemist, and is distributed under the terms of the + * GNU General Public License, with a linking exception, + * as described in the file LICENSE in the Alchemist distribution's top directory. + */ + +package it.unibo.alchemist.model.implementations.properties + +import it.unibo.alchemist.model.interfaces.Node +import it.unibo.alchemist.model.interfaces.properties.PerceptiveProperty +import it.unibo.alchemist.model.interfaces.geometry.InfluenceSphere + +/** + * Base implementation of a pedestrian's capability to influence each other. + */ +data class Perceptive ( + override val node: Node, + override val fieldOfView: InfluenceSphere, +) : PerceptiveProperty { + + override fun cloneOnNewNode(node: Node) = Perceptive(node, fieldOfView) +} diff --git a/alchemist-cognitive-agents/src/main/kotlin/it/unibo/alchemist/model/implementations/properties/Perceptive2D.kt b/alchemist-cognitive-agents/src/main/kotlin/it/unibo/alchemist/model/implementations/properties/Perceptive2D.kt new file mode 100644 index 0000000000..8714b56a79 --- /dev/null +++ b/alchemist-cognitive-agents/src/main/kotlin/it/unibo/alchemist/model/implementations/properties/Perceptive2D.kt @@ -0,0 +1,49 @@ +/* + * Copyright (C) 2010-2022, Danilo Pianini and contributors + * listed, for each module, in the respective subproject's build.gradle.kts file. + * + * This file is part of Alchemist, and is distributed under the terms of the + * GNU General Public License, with a linking exception, + * as described in the file LICENSE in the Alchemist distribution's top directory. + */ + +package it.unibo.alchemist.model.implementations.properties + +import it.unibo.alchemist.model.implementations.geometry.euclidean2d.FieldOfView2D +import it.unibo.alchemist.model.implementations.geometry.euclidean2d.InfluenceSphere2D +import it.unibo.alchemist.model.interfaces.Node +import it.unibo.alchemist.model.interfaces.properties.PerceptiveProperty +import it.unibo.alchemist.model.interfaces.environments.Physics2DEnvironment + +/** + * Base implementation of a pedestrian's capability to influence each other in a 2D space. + */ +class Perceptive2D @JvmOverloads constructor( + /** + * The environment where [node] is moving. + */ + val environment: Physics2DEnvironment, + override val node: Node, + override val fieldOfView: InfluenceSphere2D = + FieldOfView2D(environment, node, defaultFieldOfViewDepth, defaultFieldOfViewAperture), +) : PerceptiveProperty by Perceptive( + node, + fieldOfView, +) { + override fun cloneOnNewNode(node: Node) = Perceptive2D( + environment, + node, + FieldOfView2D(environment, node, defaultFieldOfViewDepth, defaultFieldOfViewAperture), + ) + + companion object { + /** + * Default aperture of pedestrian's [fieldOfView]. + */ + const val defaultFieldOfViewAperture = Math.PI / 180 * 80 + /** + * Default depth of pedestrian's [fieldOfView]. + */ + const val defaultFieldOfViewDepth = 10.0 + } +} diff --git a/alchemist-cognitive-agents/src/main/kotlin/it/unibo/alchemist/model/implementations/properties/RunningPedestrian.kt b/alchemist-cognitive-agents/src/main/kotlin/it/unibo/alchemist/model/implementations/properties/RunningPedestrian.kt new file mode 100644 index 0000000000..873dcc9167 --- /dev/null +++ b/alchemist-cognitive-agents/src/main/kotlin/it/unibo/alchemist/model/implementations/properties/RunningPedestrian.kt @@ -0,0 +1,24 @@ +/* + * Copyright (C) 2010-2022, Danilo Pianini and contributors + * listed, for each module, in the respective subproject's build.gradle.kts file. + * + * This file is part of Alchemist, and is distributed under the terms of the + * GNU General Public License, with a linking exception, + * as described in the file LICENSE in the Alchemist distribution's top directory. + */ + +package it.unibo.alchemist.model.implementations.properties + +import it.unibo.alchemist.model.cognitiveagents.impact.individual.Speed +import it.unibo.alchemist.model.interfaces.Node +import it.unibo.alchemist.model.interfaces.properties.RunningPedestrianProperty + +/** + * Implementation of a basic [RunningPedestrianProperty]. + */ +data class RunningPedestrian @JvmOverloads constructor( + override val node: Node, + override val runningSpeed: Double = Speed.default * 3 +) : RunningPedestrianProperty { + override fun cloneOnNewNode(node: Node) = RunningPedestrian(node, runningSpeed) +} diff --git a/alchemist-cognitive-agents/src/main/kotlin/it/unibo/alchemist/model/implementations/properties/Social.kt b/alchemist-cognitive-agents/src/main/kotlin/it/unibo/alchemist/model/implementations/properties/Social.kt new file mode 100644 index 0000000000..dfdc902c49 --- /dev/null +++ b/alchemist-cognitive-agents/src/main/kotlin/it/unibo/alchemist/model/implementations/properties/Social.kt @@ -0,0 +1,27 @@ +/* + * Copyright (C) 2010-2022, Danilo Pianini and contributors + * listed, for each module, in the respective subproject's build.gradle.kts file. + * + * This file is part of Alchemist, and is distributed under the terms of the + * GNU General Public License, with a linking exception, + * as described in the file LICENSE in the Alchemist distribution's top directory. + */ + +package it.unibo.alchemist.model.implementations.properties + +import it.unibo.alchemist.model.implementations.groups.Alone +import it.unibo.alchemist.model.implementations.groups.GenericGroup +import it.unibo.alchemist.model.interfaces.Group +import it.unibo.alchemist.model.interfaces.Node +import it.unibo.alchemist.model.interfaces.properties.SocialProperty + +/** + * Base implementation of a [SocialProperty]. + */ +data class Social @JvmOverloads constructor( + override val node: Node, + override val group: Group = Alone(node), +) : SocialProperty { + + override fun cloneOnNewNode(node: Node) = Social(node, GenericGroup(listOf(node))) +} diff --git a/alchemist-cognitive-agents/src/main/kotlin/it/unibo/alchemist/model/implementations/properties/WalkingPedestrian.kt b/alchemist-cognitive-agents/src/main/kotlin/it/unibo/alchemist/model/implementations/properties/WalkingPedestrian.kt new file mode 100644 index 0000000000..fe664f8162 --- /dev/null +++ b/alchemist-cognitive-agents/src/main/kotlin/it/unibo/alchemist/model/implementations/properties/WalkingPedestrian.kt @@ -0,0 +1,25 @@ +/* + * Copyright (C) 2010-2022, Danilo Pianini and contributors + * listed, for each module, in the respective subproject's build.gradle.kts file. + * + * This file is part of Alchemist, and is distributed under the terms of the + * GNU General Public License, with a linking exception, + * as described in the file LICENSE in the Alchemist distribution's top directory. + */ + +package it.unibo.alchemist.model.implementations.properties + +import it.unibo.alchemist.model.cognitiveagents.impact.individual.Speed +import it.unibo.alchemist.model.interfaces.Node +import it.unibo.alchemist.model.interfaces.properties.WalkingPedestrianProperty + +/** + * Implementation of a basic [WalkingPedestrianProperty]. + */ +data class WalkingPedestrian @JvmOverloads constructor( + override val node: Node, + override val walkingSpeed: Double = Speed.default +) : WalkingPedestrianProperty { + + override fun cloneOnNewNode(node: Node) = WalkingPedestrian(node, walkingSpeed) +} diff --git a/alchemist-cognitive-agents/src/main/kotlin/it/unibo/alchemist/model/implementations/reactions/BlendedSteering.kt b/alchemist-cognitive-agents/src/main/kotlin/it/unibo/alchemist/model/implementations/reactions/BlendedSteering.kt index 9d89855666..51fca55b0b 100644 --- a/alchemist-cognitive-agents/src/main/kotlin/it/unibo/alchemist/model/implementations/reactions/BlendedSteering.kt +++ b/alchemist-cognitive-agents/src/main/kotlin/it/unibo/alchemist/model/implementations/reactions/BlendedSteering.kt @@ -1,7 +1,7 @@ package it.unibo.alchemist.model.implementations.reactions import it.unibo.alchemist.model.implementations.actions.steeringstrategies.DistanceWeighted -import it.unibo.alchemist.model.interfaces.Pedestrian2D +import it.unibo.alchemist.model.interfaces.Node import it.unibo.alchemist.model.interfaces.TimeDistribution import it.unibo.alchemist.model.interfaces.environments.Euclidean2DEnvironment @@ -11,13 +11,13 @@ import it.unibo.alchemist.model.interfaces.environments.Euclidean2DEnvironment * * @param environment * the environment inside which the pedestrian moves. - * @param pedestrian + * @param node * the owner of this reaction. * @param timeDistribution * the time distribution according to this the reaction executes. */ open class BlendedSteering( environment: Euclidean2DEnvironment, - pedestrian: Pedestrian2D, + node: Node, timeDistribution: TimeDistribution -) : SteeringBehavior(environment, pedestrian, timeDistribution, DistanceWeighted(environment, pedestrian)) +) : SteeringBehavior(environment, node, timeDistribution, DistanceWeighted(environment, node)) diff --git a/alchemist-cognitive-agents/src/main/kotlin/it/unibo/alchemist/model/implementations/reactions/CognitiveBehavior.kt b/alchemist-cognitive-agents/src/main/kotlin/it/unibo/alchemist/model/implementations/reactions/CognitiveBehavior.kt index 956abaf3c3..2f2d810546 100644 --- a/alchemist-cognitive-agents/src/main/kotlin/it/unibo/alchemist/model/implementations/reactions/CognitiveBehavior.kt +++ b/alchemist-cognitive-agents/src/main/kotlin/it/unibo/alchemist/model/implementations/reactions/CognitiveBehavior.kt @@ -1,33 +1,34 @@ package it.unibo.alchemist.model.implementations.reactions -import it.unibo.alchemist.model.interfaces.CognitivePedestrian import it.unibo.alchemist.model.interfaces.Environment import it.unibo.alchemist.model.interfaces.Node import it.unibo.alchemist.model.interfaces.Time import it.unibo.alchemist.model.interfaces.TimeDistribution import it.unibo.alchemist.model.interfaces.geometry.GeometricTransformation import it.unibo.alchemist.model.interfaces.geometry.Vector +import it.unibo.alchemist.model.interfaces.Node.Companion.asProperty +import it.unibo.alchemist.model.interfaces.properties.CognitiveProperty /** * Reaction representing the cognitive behavior of a pedestrian. * - * @param pedestrian + * @param node * the owner of this reaction. * @param timeDistribution * the time distribution according to this the reaction executes. */ class CognitiveBehavior( - private val pedestrian: CognitivePedestrian, + node: Node, timeDistribution: TimeDistribution -) : AbstractReaction(pedestrian, timeDistribution) +) : AbstractReaction(node, timeDistribution) where V : Vector, A : GeometricTransformation { @Suppress("UNCHECKED_CAST") - override fun cloneOnNewNode(node: Node?, currentTime: Time?) = - CognitiveBehavior(node as CognitivePedestrian, timeDistribution) + override fun cloneOnNewNode(node: Node, currentTime: Time) = + CognitiveBehavior(node, timeDistribution) override fun getRate() = timeDistribution.rate - override fun updateInternalStatus(curTime: Time?, executed: Boolean, env: Environment?) = - pedestrian.cognitiveModel.update(rate) + override fun updateInternalStatus(curTime: Time, executed: Boolean, environment: Environment) = + node.asProperty>().cognitiveModel.update(rate) } diff --git a/alchemist-cognitive-agents/src/main/kotlin/it/unibo/alchemist/model/implementations/reactions/NavigationPrioritisedSteering.kt b/alchemist-cognitive-agents/src/main/kotlin/it/unibo/alchemist/model/implementations/reactions/NavigationPrioritisedSteering.kt index 08d8ee4bd4..26dc2eaaa8 100644 --- a/alchemist-cognitive-agents/src/main/kotlin/it/unibo/alchemist/model/implementations/reactions/NavigationPrioritisedSteering.kt +++ b/alchemist-cognitive-agents/src/main/kotlin/it/unibo/alchemist/model/implementations/reactions/NavigationPrioritisedSteering.kt @@ -12,11 +12,13 @@ package it.unibo.alchemist.model.implementations.reactions import it.unibo.alchemist.model.implementations.actions.steeringstrategies.SinglePrevalent import it.unibo.alchemist.model.implementations.positions.Euclidean2DPosition import it.unibo.alchemist.model.interfaces.NavigationAction2D -import it.unibo.alchemist.model.interfaces.Pedestrian2D +import it.unibo.alchemist.model.interfaces.Node import it.unibo.alchemist.model.interfaces.SteeringAction import it.unibo.alchemist.model.interfaces.TimeDistribution +import it.unibo.alchemist.model.interfaces.properties.PedestrianProperty import it.unibo.alchemist.model.interfaces.environments.Euclidean2DEnvironmentWithGraph import it.unibo.alchemist.model.interfaces.geometry.euclidean2d.ConvexPolygon +import it.unibo.alchemist.model.interfaces.Node.Companion.asProperty /** * A [SteeringBehavior] using [SinglePrevalent] steering strategy and accepting a collection of actions @@ -26,8 +28,8 @@ import it.unibo.alchemist.model.interfaces.geometry.euclidean2d.ConvexPolygon * @param N type of nodes of the environment's graph. */ open class NavigationPrioritisedSteering @JvmOverloads constructor( - env: Euclidean2DEnvironmentWithGraph<*, T, N, *>, - pedestrian: Pedestrian2D, + environment: Euclidean2DEnvironmentWithGraph<*, T, N, *>, + node: Node, timeDistribution: TimeDistribution, /** * Tolerance angle in degrees (see [SinglePrevalent]). @@ -36,16 +38,16 @@ open class NavigationPrioritisedSteering @JvmOverloads con /** * Alpha value for exponential smoothing (see [SinglePrevalent]). */ - alpha: Double = SinglePrevalent.DEFAULT_ALPHA + alpha: Double = SinglePrevalent.DEFAULT_ALPHA, ) : SteeringBehavior( - env, - pedestrian, + environment, + node, timeDistribution, SinglePrevalent( - env, - pedestrian, + environment, + node, prevalent = { singleNavigationAction() }, - maxWalk = { pedestrian.speed() / timeDistribution.rate }, + maxWalk = { node.asProperty>().speed() / timeDistribution.rate }, toleranceAngle = Math.toRadians(toleranceAngle), alpha = alpha ) diff --git a/alchemist-cognitive-agents/src/main/kotlin/it/unibo/alchemist/model/implementations/reactions/PrioritySteering.kt b/alchemist-cognitive-agents/src/main/kotlin/it/unibo/alchemist/model/implementations/reactions/PrioritySteering.kt index e53cb2a055..db30146237 100644 --- a/alchemist-cognitive-agents/src/main/kotlin/it/unibo/alchemist/model/implementations/reactions/PrioritySteering.kt +++ b/alchemist-cognitive-agents/src/main/kotlin/it/unibo/alchemist/model/implementations/reactions/PrioritySteering.kt @@ -1,7 +1,7 @@ package it.unibo.alchemist.model.implementations.reactions import it.unibo.alchemist.model.implementations.actions.steeringstrategies.Nearest -import it.unibo.alchemist.model.interfaces.Pedestrian2D +import it.unibo.alchemist.model.interfaces.Node import it.unibo.alchemist.model.interfaces.TimeDistribution import it.unibo.alchemist.model.interfaces.environments.Euclidean2DEnvironment @@ -10,13 +10,13 @@ import it.unibo.alchemist.model.interfaces.environments.Euclidean2DEnvironment * * @param environment * the environment inside which the pedestrian moves. - * @param pedestrian + * @param node * the owner of this reaction. * @param timeDistribution * the time distribution according to this the reaction executes. */ class PrioritySteering( environment: Euclidean2DEnvironment, - pedestrian: Pedestrian2D, - timeDistribution: TimeDistribution -) : SteeringBehavior(environment, pedestrian, timeDistribution, Nearest(environment, pedestrian)) + node: Node, + timeDistribution: TimeDistribution, +) : SteeringBehavior(environment, node, timeDistribution, Nearest(environment, node)) diff --git a/alchemist-cognitive-agents/src/main/kotlin/it/unibo/alchemist/model/implementations/reactions/SteeringBehavior.kt b/alchemist-cognitive-agents/src/main/kotlin/it/unibo/alchemist/model/implementations/reactions/SteeringBehavior.kt index 185b065f3a..2f22638073 100644 --- a/alchemist-cognitive-agents/src/main/kotlin/it/unibo/alchemist/model/implementations/reactions/SteeringBehavior.kt +++ b/alchemist-cognitive-agents/src/main/kotlin/it/unibo/alchemist/model/implementations/reactions/SteeringBehavior.kt @@ -4,7 +4,6 @@ import it.unibo.alchemist.model.implementations.actions.CognitiveAgentCombineSte import it.unibo.alchemist.model.implementations.positions.Euclidean2DPosition import it.unibo.alchemist.model.interfaces.Environment import it.unibo.alchemist.model.interfaces.Node -import it.unibo.alchemist.model.interfaces.Pedestrian2D import it.unibo.alchemist.model.interfaces.SteeringAction import it.unibo.alchemist.model.interfaces.SteeringStrategy import it.unibo.alchemist.model.interfaces.Time @@ -13,9 +12,9 @@ import it.unibo.alchemist.model.interfaces.TimeDistribution /** * Reaction representing the steering behavior of a pedestrian. * - * @param env + * @param environment * the environment inside which the pedestrian moves. - * @param pedestrian + * @param node * the owner of this reaction. * @param timeDistribution * the time distribution according to which this reaction executes. @@ -23,11 +22,11 @@ import it.unibo.alchemist.model.interfaces.TimeDistribution * the strategy used to combine steering actions. */ open class SteeringBehavior( - private val env: Environment, - private val pedestrian: Pedestrian2D, + private val environment: Environment, + node: Node, timeDistribution: TimeDistribution, open val steerStrategy: SteeringStrategy -) : AbstractReaction(pedestrian, timeDistribution) { +) : AbstractReaction(node, timeDistribution) { /** * The list of steering actions in this reaction. @@ -35,8 +34,8 @@ open class SteeringBehavior( fun steerActions(): List> = actions.filterIsInstance>() - override fun cloneOnNewNode(node: Node?, currentTime: Time?) = - SteeringBehavior(env, node as Pedestrian2D, timeDistribution, steerStrategy) + override fun cloneOnNewNode(node: Node, currentTime: Time) = + SteeringBehavior(environment, node, timeDistribution, steerStrategy) override fun getRate() = timeDistribution.rate @@ -48,6 +47,6 @@ open class SteeringBehavior( override fun execute() { (actions - steerActions()).forEach { it.execute() } - CognitiveAgentCombineSteering(env, this, pedestrian, steerActions(), steerStrategy).execute() + CognitiveAgentCombineSteering(environment, this, node, steerActions(), steerStrategy).execute() } } diff --git a/alchemist-cognitive-agents/src/main/kotlin/it/unibo/alchemist/model/interfaces/CognitivePedestrian.kt b/alchemist-cognitive-agents/src/main/kotlin/it/unibo/alchemist/model/interfaces/CognitivePedestrian.kt deleted file mode 100644 index 5464f3d8c2..0000000000 --- a/alchemist-cognitive-agents/src/main/kotlin/it/unibo/alchemist/model/interfaces/CognitivePedestrian.kt +++ /dev/null @@ -1,26 +0,0 @@ -package it.unibo.alchemist.model.interfaces - -import it.unibo.alchemist.model.cognitiveagents.CognitiveModel -import it.unibo.alchemist.model.interfaces.geometry.GeometricTransformation -import it.unibo.alchemist.model.interfaces.geometry.Vector - -/** - * A heterogeneous pedestrian with cognitive capabilities. - */ -interface CognitivePedestrian : Pedestrian where -V : Vector, -A : GeometricTransformation { - /** - * The mind model of this pedestrian. - */ - val cognitiveModel: CognitiveModel - - /** - * The mind model of all people considered influencial for this cognitive pedestrian. - */ - fun influencialPeople(): List = senses.flatMap { sphere -> - sphere.influentialNodes() - .filterIsInstance>() - .map { it.cognitiveModel } - } -} diff --git a/alchemist-cognitive-agents/src/main/kotlin/it/unibo/alchemist/model/interfaces/Group.kt b/alchemist-cognitive-agents/src/main/kotlin/it/unibo/alchemist/model/interfaces/Group.kt index 813acde499..2b7728d4c5 100644 --- a/alchemist-cognitive-agents/src/main/kotlin/it/unibo/alchemist/model/interfaces/Group.kt +++ b/alchemist-cognitive-agents/src/main/kotlin/it/unibo/alchemist/model/interfaces/Group.kt @@ -3,33 +3,10 @@ package it.unibo.alchemist.model.interfaces /** * A group of nodes. */ -interface Group> { +interface Group : MutableList> { /** * The list of pedestrians belonging to this group. */ - val members: List - - /** - * Whether a node belongs to this group or not. - * - * @param node The node to whom the membership must be checked. - */ - fun contains(node: N): Boolean = members.contains(node) - - /** - * Add a node in this group if not already part of it. - * - * @param node - * the node to add. - */ - fun addMember(node: N): Group - - /** - * Remove a node from this group. - * - * @param node - * the node to remove. - */ - fun removeMember(node: N): Group + val members: List> get() = this } diff --git a/alchemist-cognitive-agents/src/main/kotlin/it/unibo/alchemist/model/interfaces/GroupSteeringAction.kt b/alchemist-cognitive-agents/src/main/kotlin/it/unibo/alchemist/model/interfaces/GroupSteeringAction.kt index 20558dd8e2..43dc935e0e 100644 --- a/alchemist-cognitive-agents/src/main/kotlin/it/unibo/alchemist/model/interfaces/GroupSteeringAction.kt +++ b/alchemist-cognitive-agents/src/main/kotlin/it/unibo/alchemist/model/interfaces/GroupSteeringAction.kt @@ -10,5 +10,5 @@ interface GroupSteeringAction : SteeringAction where P : Position

/** * The list of pedestrians influencing this action. */ - fun group(): List> + fun group(): List> } diff --git a/alchemist-cognitive-agents/src/main/kotlin/it/unibo/alchemist/model/interfaces/GroupWithLeader.kt b/alchemist-cognitive-agents/src/main/kotlin/it/unibo/alchemist/model/interfaces/GroupWithLeader.kt index 65f2d00b4a..0f5a6d2d10 100644 --- a/alchemist-cognitive-agents/src/main/kotlin/it/unibo/alchemist/model/interfaces/GroupWithLeader.kt +++ b/alchemist-cognitive-agents/src/main/kotlin/it/unibo/alchemist/model/interfaces/GroupWithLeader.kt @@ -3,7 +3,7 @@ package it.unibo.alchemist.model.interfaces /** * A group with a special member acting as a leader. */ -interface GroupWithLeader> : Group { +interface GroupWithLeader> : Group { /** * The leader of the group. diff --git a/alchemist-cognitive-agents/src/main/kotlin/it/unibo/alchemist/model/interfaces/HeterogeneousPedestrian.kt b/alchemist-cognitive-agents/src/main/kotlin/it/unibo/alchemist/model/interfaces/HeterogeneousPedestrian.kt deleted file mode 100644 index 78f10a93a7..0000000000 --- a/alchemist-cognitive-agents/src/main/kotlin/it/unibo/alchemist/model/interfaces/HeterogeneousPedestrian.kt +++ /dev/null @@ -1,25 +0,0 @@ -/* - * Copyright (C) 2010-2021, Danilo Pianini and contributors - * listed, for each module, in the respective subproject's build.gradle.kts file. - * - * This file is part of Alchemist, and is distributed under the terms of the - * GNU General Public License, with a linking exception, - * as described in the file LICENSE in the Alchemist distribution's top directory. - */ - -package it.unibo.alchemist.model.interfaces - -import it.unibo.alchemist.model.HeterogeneousPedestrianModel -import it.unibo.alchemist.model.interfaces.geometry.GeometricTransformation -import it.unibo.alchemist.model.interfaces.geometry.Vector - -/** - * Pedestrians that can differ by age, geneder, etc, depending on their [HeterogeneousPedestrianModel]. - */ -interface HeterogeneousPedestrian, A : GeometricTransformation> : Pedestrian { - - /** - * The pedestrian model, capturing its characteristics. - */ - val pedestrianModel: HeterogeneousPedestrianModel -} diff --git a/alchemist-cognitive-agents/src/main/kotlin/it/unibo/alchemist/model/interfaces/NavigationAction.kt b/alchemist-cognitive-agents/src/main/kotlin/it/unibo/alchemist/model/interfaces/NavigationAction.kt index 0e51ddc994..75e186fdd6 100644 --- a/alchemist-cognitive-agents/src/main/kotlin/it/unibo/alchemist/model/interfaces/NavigationAction.kt +++ b/alchemist-cognitive-agents/src/main/kotlin/it/unibo/alchemist/model/interfaces/NavigationAction.kt @@ -10,21 +10,23 @@ package it.unibo.alchemist.model.interfaces import it.unibo.alchemist.model.implementations.positions.Euclidean2DPosition +import it.unibo.alchemist.model.interfaces.Node.Companion.asProperty import it.unibo.alchemist.model.interfaces.environments.EnvironmentWithGraph import it.unibo.alchemist.model.interfaces.geometry.ConvexGeometricShape import it.unibo.alchemist.model.interfaces.geometry.GeometricTransformation import it.unibo.alchemist.model.interfaces.geometry.Vector import it.unibo.alchemist.model.interfaces.geometry.euclidean2d.Euclidean2DTransformation +import it.unibo.alchemist.model.interfaces.properties.OrientingProperty /** - * A [SteeringAction] allowing a pedestrian to navigate an environment consciously (e.g. without getting stuck in + * A [SteeringAction] allowing a node to navigate an environment consciously (e.g. without getting stuck in * U-shaped obstacles). Names are inspired to indoor environments, but this interface works for outdoor ones as well. * * @param T the concentration type. - * @param P the [Position] type and [Vector] type for the space the pedestrian is into. + * @param P the [Position] type and [Vector] type for the space the node is into. * @param A the transformations supported by the shapes in this space. - * @param L the type of landmarks of the pedestrian's cognitive map. - * @param R the type of edges of the pedestrian's cognitive map, representing the [R]elations between landmarks. + * @param L the type of landmarks of the node's cognitive map. + * @param R the type of edges of the node's cognitive map, representing the [R]elations between landmarks. * @param N the type of nodes of the navigation graph provided by the [environment]. * @param E the type of edges of the navigation graph provided by the [environment]. */ @@ -35,42 +37,47 @@ interface NavigationAction : SteeringAction N : ConvexGeometricShape { /** - * The pedestrian to move. + * The owner of this action. */ - val pedestrian: OrientingPedestrian + val navigatingNode: Node /** - * The environment the [pedestrian] is into. + * The [navigatingNode]'s orientingProperty. + */ + val orientingProperty get() = navigatingNode.asProperty>() + + /** + * The environment the [navigatingNode] is into. */ val environment: EnvironmentWithGraph<*, T, P, A, N, E> /** - * The position of the [pedestrian] in the [environment]. + * The position of the [navigatingNode] in the [environment]. */ val pedestrianPosition: P /** - * The room (= environment's area) the [pedestrian] is into. + * The room (= environment's area) the [navigatingNode] is into. */ val currentRoom: N? /** - * @returns the doors (= passages/edges) the pedestrian can perceive. + * @returns the doors (= passages/edges) the node can perceive. */ fun doorsInSight(): List /** - * Moves the pedestrian across the provided [door], which must be among [doorsInSight]. + * Moves the node across the provided [door], which must be among [doorsInSight]. */ fun crossDoor(door: E) /** - * Moves the pedestrian to the given final [destination], which must be inside [currentRoom]. + * Moves the node to the given final [destination], which must be inside [currentRoom]. */ fun moveToFinal(destination: P) /** - * Stops moving the pedestrian. + * Stops moving the node. */ fun stop() = moveToFinal(pedestrianPosition) } diff --git a/alchemist-cognitive-agents/src/main/kotlin/it/unibo/alchemist/model/interfaces/NavigationStrategy.kt b/alchemist-cognitive-agents/src/main/kotlin/it/unibo/alchemist/model/interfaces/NavigationStrategy.kt index 940446f52c..0fb7080e42 100644 --- a/alchemist-cognitive-agents/src/main/kotlin/it/unibo/alchemist/model/interfaces/NavigationStrategy.kt +++ b/alchemist-cognitive-agents/src/main/kotlin/it/unibo/alchemist/model/interfaces/NavigationStrategy.kt @@ -16,15 +16,15 @@ import it.unibo.alchemist.model.interfaces.geometry.Vector import it.unibo.alchemist.model.interfaces.geometry.euclidean2d.Euclidean2DTransformation /** - * Defines what a pedestrian should do when in a new room (= environment's area), this is designed to be used jointly - * with a [NavigationAction]: the latter defines how to properly move the pedestrian, while delegating the decision on + * Defines what a node should do when in a new room (= environment's area), this is designed to be used jointly + * with a [NavigationAction]: the latter defines how to properly move the node, while delegating the decision on * where to move it to a [NavigationStrategy]. * * @param T the concentration type. - * @param P the [Position] type and [Vector] type for the space the pedestrian is into. + * @param P the [Position] type and [Vector] type for the space the node is into. * @param A the transformations supported by the shapes in this space. - * @param L the type of landmarks of the pedestrian's cognitive map. - * @param R the type of edges of the pedestrian's cognitive map, representing the [R]elations between landmarks. + * @param L the type of landmarks of the node's cognitive map. + * @param R the type of edges of the node's cognitive map, representing the [R]elations between landmarks. * @param N the type of nodes of the navigation graph provided by the environment. * @param E the type of edges of the navigation graph provided by the environment. */ @@ -40,12 +40,17 @@ interface NavigationStrategy val action: NavigationAction /** - * This is called whenever the pedestrian enters a new room. + * The node's orienting capability. + */ + val orientingCapability get() = action.orientingProperty + + /** + * This is called whenever the node enters a new room. */ fun inNewRoom(newRoom: N) /** - * This is called in place of [inNewRoom] when the pedestrian ends up in an unexpected room while moving. + * This is called in place of [inNewRoom] when the node ends up in an unexpected room while moving. * By default, unexpected rooms are treated just like expected ones. */ fun inUnexpectedNewRoom(previousRoom: N, expectedNewRoom: N, actualNewRoom: N) = inNewRoom(actualNewRoom) diff --git a/alchemist-cognitive-agents/src/main/kotlin/it/unibo/alchemist/model/interfaces/OrientingAgent.kt b/alchemist-cognitive-agents/src/main/kotlin/it/unibo/alchemist/model/interfaces/OrientingAgent.kt index e4ef8f54f5..fa6130749c 100644 --- a/alchemist-cognitive-agents/src/main/kotlin/it/unibo/alchemist/model/interfaces/OrientingAgent.kt +++ b/alchemist-cognitive-agents/src/main/kotlin/it/unibo/alchemist/model/interfaces/OrientingAgent.kt @@ -34,7 +34,7 @@ L : ConvexGeometricShape { /** * The knowledge degree of the agent concerning the environment. This is a Double value in [0, 1] describing the * percentage of environment the agent is familiar with prior to the start of the simulation (thus it does not - * take into account the knowledge the pedestrian will gain during it, namely the [volatileMemory]). + * take into account the knowledge the node will gain during it, namely the [volatileMemory]). */ val knowledgeDegree: Double diff --git a/alchemist-cognitive-agents/src/main/kotlin/it/unibo/alchemist/model/interfaces/OrientingPedestrian.kt b/alchemist-cognitive-agents/src/main/kotlin/it/unibo/alchemist/model/interfaces/OrientingPedestrian.kt deleted file mode 100644 index 7e00862c4b..0000000000 --- a/alchemist-cognitive-agents/src/main/kotlin/it/unibo/alchemist/model/interfaces/OrientingPedestrian.kt +++ /dev/null @@ -1,35 +0,0 @@ -/* - * Copyright (C) 2010-2020, Danilo Pianini and contributors - * listed in the main project's alchemist/build.gradle.kts file. - * - * This file is part of Alchemist, and is distributed under the terms of the - * GNU General Public License, with a linking exception, - * as described in the file LICENSE in the Alchemist distribution's top directory. - */ - -package it.unibo.alchemist.model.interfaces - -import it.unibo.alchemist.model.implementations.positions.Euclidean2DPosition -import it.unibo.alchemist.model.interfaces.geometry.ConvexGeometricShape -import it.unibo.alchemist.model.interfaces.geometry.GeometricTransformation -import it.unibo.alchemist.model.interfaces.geometry.Vector -import it.unibo.alchemist.model.interfaces.geometry.euclidean2d.Euclidean2DTransformation - -/** - * A pedestrian capable of orienting itself. - * - * @param T the concentration type. - * @param V the [Vector] type for the space this pedestrian is inside. - * @param A the transformations supported by the shapes in this space. - * @param L the type of landmarks stored in the pedestrian's [cognitiveMap]. - * @param R the type of edges of the [cognitiveMap], representing the [R]elations between landmarks. - */ -interface OrientingPedestrian : Pedestrian, OrientingAgent - where V : Vector, - A : GeometricTransformation, - L : ConvexGeometricShape - -/** - * An [OrientingPedestrian] in an euclidean bidimensional space. - */ -typealias OrientingPedestrian2D = OrientingPedestrian diff --git a/alchemist-cognitive-agents/src/main/kotlin/it/unibo/alchemist/model/interfaces/Pedestrian.kt b/alchemist-cognitive-agents/src/main/kotlin/it/unibo/alchemist/model/interfaces/Pedestrian.kt deleted file mode 100644 index 8797990c22..0000000000 --- a/alchemist-cognitive-agents/src/main/kotlin/it/unibo/alchemist/model/interfaces/Pedestrian.kt +++ /dev/null @@ -1,32 +0,0 @@ -package it.unibo.alchemist.model.interfaces - -import it.unibo.alchemist.model.interfaces.geometry.GeometricTransformation -import it.unibo.alchemist.model.interfaces.geometry.InfluenceSphere -import it.unibo.alchemist.model.interfaces.geometry.Vector -import it.unibo.alchemist.model.interfaces.nodes.NodeWithShape - -/** - * A plain pedestrian. - */ -interface Pedestrian, A : GeometricTransformation> : NodeWithShape { - - /** - * The list of influence spheres belonging to this pedestrian (by default, only its [fieldOfView]). - */ - val senses: List get() = listOf(fieldOfView) - - /** - * The field of view of the pedestrian. - */ - val fieldOfView: InfluenceSphere - - /** - * The group this pedestrian belongs to. - */ - val membershipGroup: PedestrianGroup - - /** - * The speed at which the pedestrian is moving. - */ - fun speed(): Double -} diff --git a/alchemist-cognitive-agents/src/main/kotlin/it/unibo/alchemist/model/interfaces/Pedestrian2D.kt b/alchemist-cognitive-agents/src/main/kotlin/it/unibo/alchemist/model/interfaces/Pedestrian2D.kt deleted file mode 100644 index 4c9e7a030c..0000000000 --- a/alchemist-cognitive-agents/src/main/kotlin/it/unibo/alchemist/model/interfaces/Pedestrian2D.kt +++ /dev/null @@ -1,46 +0,0 @@ -package it.unibo.alchemist.model.interfaces - -import it.unibo.alchemist.model.implementations.geometry.euclidean2d.FieldOfView2D -import it.unibo.alchemist.model.implementations.positions.Euclidean2DPosition -import it.unibo.alchemist.model.interfaces.environments.Physics2DEnvironment -import it.unibo.alchemist.model.interfaces.geometry.euclidean2d.Euclidean2DShape -import it.unibo.alchemist.model.interfaces.geometry.euclidean2d.Euclidean2DTransformation - -/** - * A bidimensional pedestrian. - */ -interface Pedestrian2D : Pedestrian { - - /** - * Access to the [environment]. - */ - val environment: Physics2DEnvironment - - /** - * The shape of any pedestrian in the Euclidean world. - * Implementors should override this property to prevent the continuous creation of new [Euclidean2DShape]s. - */ - override val shape: Euclidean2DShape get() = environment.shapeFactory.circle(defaultRadius) - - /** - * The field of view of a pedestrian in the Euclidean world. - * Implementors should override this property to prevent the continuous creation of new [FieldOfView2D]s. - */ - override val fieldOfView: FieldOfView2D get() = - FieldOfView2D(environment, this, defaultFieldOfViewDepth, defaultFieldOfViewAperture) - - companion object { - /** - * Default radius of pedestrian's [shape]. - */ - const val defaultRadius = 0.3 - /** - * Default aperture of pedestrian's [fieldOfView]. - */ - const val defaultFieldOfViewAperture = Math.PI / 180 * 80 - /** - * Default depth of pedestrian's [fieldOfView]. - */ - const val defaultFieldOfViewDepth = 10.0 - } -} diff --git a/alchemist-cognitive-agents/src/main/kotlin/it/unibo/alchemist/model/interfaces/PedestrianGroup.kt b/alchemist-cognitive-agents/src/main/kotlin/it/unibo/alchemist/model/interfaces/PedestrianGroup.kt deleted file mode 100644 index 51197f1072..0000000000 --- a/alchemist-cognitive-agents/src/main/kotlin/it/unibo/alchemist/model/interfaces/PedestrianGroup.kt +++ /dev/null @@ -1,16 +0,0 @@ -package it.unibo.alchemist.model.interfaces - -import it.unibo.alchemist.model.implementations.positions.Euclidean2DPosition -import it.unibo.alchemist.model.interfaces.geometry.GeometricTransformation -import it.unibo.alchemist.model.interfaces.geometry.Vector -import it.unibo.alchemist.model.interfaces.geometry.euclidean2d.Euclidean2DTransformation - -/** - * A [Group] of [Pedestrian]s. - */ -interface PedestrianGroup, A : GeometricTransformation

> : Group> - -/** - * A [PedestrianGroup] featuring bidimensional pedestrians. - */ -interface PedestrianGroup2D : PedestrianGroup diff --git a/alchemist-cognitive-agents/src/main/kotlin/it/unibo/alchemist/model/interfaces/SteeringStrategy.kt b/alchemist-cognitive-agents/src/main/kotlin/it/unibo/alchemist/model/interfaces/SteeringStrategy.kt index 2c9036985a..72f925175e 100644 --- a/alchemist-cognitive-agents/src/main/kotlin/it/unibo/alchemist/model/interfaces/SteeringStrategy.kt +++ b/alchemist-cognitive-agents/src/main/kotlin/it/unibo/alchemist/model/interfaces/SteeringStrategy.kt @@ -7,7 +7,7 @@ package it.unibo.alchemist.model.interfaces interface SteeringStrategy> { /** - * Computes the next position starting from the steering actions the pedestrian obey to, + * Computes the next position starting from the steering actions the node obey to, * in relative coordinates with respect to its current position. * * @param actions @@ -16,7 +16,7 @@ interface SteeringStrategy> { fun computeNextPosition(actions: List>): P /** - * Computes the target to reach starting from the steering actions the pedestrian obey to, + * Computes the target to reach starting from the steering actions the node obey to, * in absolute coordinates. * * @param actions diff --git a/alchemist-cognitive-agents/src/main/kotlin/it/unibo/alchemist/model/interfaces/properties/CognitiveProperty.kt b/alchemist-cognitive-agents/src/main/kotlin/it/unibo/alchemist/model/interfaces/properties/CognitiveProperty.kt new file mode 100644 index 0000000000..ed4e74b235 --- /dev/null +++ b/alchemist-cognitive-agents/src/main/kotlin/it/unibo/alchemist/model/interfaces/properties/CognitiveProperty.kt @@ -0,0 +1,39 @@ +/* + * Copyright (C) 2010-2022, Danilo Pianini and contributors + * listed, for each module, in the respective subproject's build.gradle.kts file. + * + * This file is part of Alchemist, and is distributed under the terms of the + * GNU General Public License, with a linking exception, + * as described in the file LICENSE in the Alchemist distribution's top directory. + */ + +package it.unibo.alchemist.model.interfaces.properties + +import it.unibo.alchemist.model.cognitiveagents.CognitiveModel +import it.unibo.alchemist.model.interfaces.NodeProperty +import it.unibo.alchemist.model.interfaces.Molecule +import it.unibo.alchemist.model.interfaces.Node.Companion.asProperty +import it.unibo.alchemist.model.interfaces.Node.Companion.asPropertyOrNull + +/** + * The pedestrian's cognitive capability. + */ +interface CognitiveProperty : NodeProperty { + /** + * The molecule associated with danger in the environment. + */ + val danger: Molecule? + + /** + * The pedestrian's cognitive model. + */ + val cognitiveModel: CognitiveModel + + /** + * The mind model of all people considered influential for this cognitive pedestrian. + */ + fun influentialPeople(): List = node.asProperty>() + .senses.flatMap { it.value.influentialNodes() } + .mapNotNull { it.asPropertyOrNull>() } + .map { it.cognitiveModel } +} diff --git a/alchemist-cognitive-agents/src/main/kotlin/it/unibo/alchemist/model/interfaces/properties/HumanProperty.kt b/alchemist-cognitive-agents/src/main/kotlin/it/unibo/alchemist/model/interfaces/properties/HumanProperty.kt new file mode 100644 index 0000000000..a881cddc95 --- /dev/null +++ b/alchemist-cognitive-agents/src/main/kotlin/it/unibo/alchemist/model/interfaces/properties/HumanProperty.kt @@ -0,0 +1,66 @@ +/* + * Copyright (C) 2010-2022, Danilo Pianini and contributors + * listed, for each module, in the respective subproject's build.gradle.kts file. + * + * This file is part of Alchemist, and is distributed under the terms of the + * GNU General Public License, with a linking exception, + * as described in the file LICENSE in the Alchemist distribution's top directory. + */ + +package it.unibo.alchemist.model.interfaces.properties + +import it.unibo.alchemist.model.HeterogeneousPedestrianModel +import it.unibo.alchemist.model.cognitiveagents.impact.individual.Age +import it.unibo.alchemist.model.cognitiveagents.impact.individual.Gender +import it.unibo.alchemist.model.cognitiveagents.impact.individual.HelpAttitude +import it.unibo.alchemist.model.cognitiveagents.impact.individual.Speed +import it.unibo.alchemist.model.implementations.positions.Euclidean2DPosition +import it.unibo.alchemist.model.interfaces.NodeProperty +import it.unibo.alchemist.model.interfaces.geometry.GeometricTransformation +import it.unibo.alchemist.model.interfaces.geometry.Vector +import it.unibo.alchemist.model.interfaces.geometry.euclidean2d.Euclidean2DTransformation + +/** + * A capability representing a pedestrian's individual characteristics. + */ +interface HumanProperty, A : GeometricTransformation> : NodeProperty { + + /** + * The age of this pedestrian. + */ + val age: Age + + /** + * The gender of this pedestrian. + */ + val gender: Gender + + /** + * The speed of an agent considering its age, gender and a random factor. + */ + val speed: Speed + + /** + * Value between 0 and 1 representing the attitude towards conforming to social rules of this pedestrian. + */ + val compliance: Double + + /** + * The attitude of an agent towards helping another agent. + */ + val helpAttitude: HelpAttitude + + /** + * Value between 0 and 1 representing the probability this pedestrian will help another pedestrian in difficulty. + * + * @param toHelp The pedestrian who needs help. + */ + fun probabilityOfHelping(toHelp: HeterogeneousPedestrianModel, isGroupMember: Boolean): Double = + helpAttitude.level(toHelp.age, toHelp.gender, isGroupMember) +} + +/** + * A capability representing a pedestrian's individual characteristics in a 2D space. + */ +typealias Human2DProperty = + HumanProperty diff --git a/alchemist-cognitive-agents/src/main/kotlin/it/unibo/alchemist/model/interfaces/properties/OrientingProperty.kt b/alchemist-cognitive-agents/src/main/kotlin/it/unibo/alchemist/model/interfaces/properties/OrientingProperty.kt new file mode 100644 index 0000000000..238f5995d6 --- /dev/null +++ b/alchemist-cognitive-agents/src/main/kotlin/it/unibo/alchemist/model/interfaces/properties/OrientingProperty.kt @@ -0,0 +1,66 @@ +/* + * Copyright (C) 2010-2022, Danilo Pianini and contributors + * listed, for each module, in the respective subproject's build.gradle.kts file. + * + * This file is part of Alchemist, and is distributed under the terms of the + * GNU General Public License, with a linking exception, + * as described in the file LICENSE in the Alchemist distribution's top directory. + */ + +package it.unibo.alchemist.model.interfaces.properties + +import it.unibo.alchemist.model.interfaces.NodeProperty +import it.unibo.alchemist.model.interfaces.Position +import it.unibo.alchemist.model.interfaces.environments.EnvironmentWithGraph +import it.unibo.alchemist.model.interfaces.geometry.ConvexGeometricShape +import it.unibo.alchemist.model.interfaces.geometry.GeometricTransformation +import it.unibo.alchemist.model.interfaces.geometry.Vector +import it.unibo.alchemist.model.interfaces.geometry.euclidean2d.graph.NavigationGraph + +/** + * A node's capability to orient. + */ +interface OrientingProperty : NodeProperty + where P : Position

, + P : Vector

, + A : GeometricTransformation

, + L : ConvexGeometricShape, + N : ConvexGeometricShape { + /** + * The knowledge degree of the agent concerning the environment. This is a Double value in [0, 1] describing the + * percentage of environment the agent is familiar with prior to the start of the simulation (thus it does not + * take into account the knowledge the pedestrian will gain during it, namely the [volatileMemory]). + */ + val knowledgeDegree: Double + + /** + * The environment in which the node moves. + */ + val environment: EnvironmentWithGraph<*, T, P, A, N, E> + + /** + * The cognitive map of the agent. It's a graph composed of landmarks (elements of the environment easy to + * remember due to their uniqueness) and spatial relations between them. It's modeled as a [NavigationGraph]. + */ + val cognitiveMap: NavigationGraph + + /** + * The volatile memory of the agent: it models the ability to remember areas of the environment already visited + * since the start of the simulation. Each area is paired with the number of visits. Areas are assumed to be + * represented as [ConvexGeometricShape]s, as in [NavigationGraph]s. + */ + val volatileMemory: MutableMap, Int> + + /** + * Registers a visit to the provided [area] in the agent's [volatileMemory]. + */ + fun > registerVisit(area: M) { + volatileMemory[area] = (volatileMemory[area] ?: 0) + 1 + } + + /** + * Creates a landmark entirely contained in the given area. If such area contains one or more destinations, the + * returned landmark must contain at least one of them. + */ + fun createLandmarkIn(area: N): L +} diff --git a/alchemist-cognitive-agents/src/main/kotlin/it/unibo/alchemist/model/interfaces/properties/PedestrianProperty.kt b/alchemist-cognitive-agents/src/main/kotlin/it/unibo/alchemist/model/interfaces/properties/PedestrianProperty.kt new file mode 100644 index 0000000000..666889f807 --- /dev/null +++ b/alchemist-cognitive-agents/src/main/kotlin/it/unibo/alchemist/model/interfaces/properties/PedestrianProperty.kt @@ -0,0 +1,20 @@ +/* + * Copyright (C) 2010-2022, Danilo Pianini and contributors + * listed, for each module, in the respective subproject's build.gradle.kts file. + * + * This file is part of Alchemist, and is distributed under the terms of the + * GNU General Public License, with a linking exception, + * as described in the file LICENSE in the Alchemist distribution's top directory. + */ + +package it.unibo.alchemist.model.interfaces.properties + +/** + * A node capability to move. It includes both [WalkingPedestrianProperty] and [RunningPedestrianProperty]. + */ +interface PedestrianProperty : WalkingPedestrianProperty, RunningPedestrianProperty { + /** + * The speed at which the pedestrian is moving. + */ + fun speed(): Double +} diff --git a/alchemist-cognitive-agents/src/main/kotlin/it/unibo/alchemist/model/interfaces/properties/PerceptiveProperty.kt b/alchemist-cognitive-agents/src/main/kotlin/it/unibo/alchemist/model/interfaces/properties/PerceptiveProperty.kt new file mode 100644 index 0000000000..5953cbd5c7 --- /dev/null +++ b/alchemist-cognitive-agents/src/main/kotlin/it/unibo/alchemist/model/interfaces/properties/PerceptiveProperty.kt @@ -0,0 +1,29 @@ +/* + * Copyright (C) 2010-2022, Danilo Pianini and contributors + * listed, for each module, in the respective subproject's build.gradle.kts file. + * + * This file is part of Alchemist, and is distributed under the terms of the + * GNU General Public License, with a linking exception, + * as described in the file LICENSE in the Alchemist distribution's top directory. + */ + +package it.unibo.alchemist.model.interfaces.properties + +import it.unibo.alchemist.model.interfaces.NodeProperty +import it.unibo.alchemist.model.interfaces.geometry.InfluenceSphere + +/** + * The pedestrian's capability to influence other pedestrians. + */ +interface PerceptiveProperty : NodeProperty { + + /** + * The field of view of the pedestrian. + */ + val fieldOfView: InfluenceSphere + + /** + * The list of influence spheres belonging to this pedestrian (by default, only its [fieldOfView]). + */ + val senses: Map> get() = mapOf("view" to fieldOfView) +} diff --git a/alchemist-cognitive-agents/src/main/kotlin/it/unibo/alchemist/model/interfaces/properties/RunningPedestrianProperty.kt b/alchemist-cognitive-agents/src/main/kotlin/it/unibo/alchemist/model/interfaces/properties/RunningPedestrianProperty.kt new file mode 100644 index 0000000000..f0be62f47b --- /dev/null +++ b/alchemist-cognitive-agents/src/main/kotlin/it/unibo/alchemist/model/interfaces/properties/RunningPedestrianProperty.kt @@ -0,0 +1,22 @@ +/* + * Copyright (C) 2010-2022, Danilo Pianini and contributors + * listed, for each module, in the respective subproject's build.gradle.kts file. + * + * This file is part of Alchemist, and is distributed under the terms of the + * GNU General Public License, with a linking exception, + * as described in the file LICENSE in the Alchemist distribution's top directory. + */ + +package it.unibo.alchemist.model.interfaces.properties + +import it.unibo.alchemist.model.interfaces.NodeProperty + +/** + * A node's capability to run. + */ +interface RunningPedestrianProperty : NodeProperty { + /** + * The node's running speed. + */ + val runningSpeed: Double +} diff --git a/alchemist-cognitive-agents/src/main/kotlin/it/unibo/alchemist/model/interfaces/properties/SocialProperty.kt b/alchemist-cognitive-agents/src/main/kotlin/it/unibo/alchemist/model/interfaces/properties/SocialProperty.kt new file mode 100644 index 0000000000..ca89a0741f --- /dev/null +++ b/alchemist-cognitive-agents/src/main/kotlin/it/unibo/alchemist/model/interfaces/properties/SocialProperty.kt @@ -0,0 +1,23 @@ +/* + * Copyright (C) 2010-2022, Danilo Pianini and contributors + * listed, for each module, in the respective subproject's build.gradle.kts file. + * + * This file is part of Alchemist, and is distributed under the terms of the + * GNU General Public License, with a linking exception, + * as described in the file LICENSE in the Alchemist distribution's top directory. + */ + +package it.unibo.alchemist.model.interfaces.properties + +import it.unibo.alchemist.model.interfaces.NodeProperty +import it.unibo.alchemist.model.interfaces.Group + +/** + * The pedestrian's capability for form groups. + */ +interface SocialProperty : NodeProperty { + /** + * Pedestrian's [Group]. + */ + val group: Group +} diff --git a/alchemist-cognitive-agents/src/main/kotlin/it/unibo/alchemist/model/interfaces/properties/WalkingPedestrianProperty.kt b/alchemist-cognitive-agents/src/main/kotlin/it/unibo/alchemist/model/interfaces/properties/WalkingPedestrianProperty.kt new file mode 100644 index 0000000000..0753ca04b4 --- /dev/null +++ b/alchemist-cognitive-agents/src/main/kotlin/it/unibo/alchemist/model/interfaces/properties/WalkingPedestrianProperty.kt @@ -0,0 +1,22 @@ +/* + * Copyright (C) 2010-2022, Danilo Pianini and contributors + * listed, for each module, in the respective subproject's build.gradle.kts file. + * + * This file is part of Alchemist, and is distributed under the terms of the + * GNU General Public License, with a linking exception, + * as described in the file LICENSE in the Alchemist distribution's top directory. + */ + +package it.unibo.alchemist.model.interfaces.properties + +import it.unibo.alchemist.model.interfaces.NodeProperty + +/** + * A node capability to walk. + */ +interface WalkingPedestrianProperty : NodeProperty { + /** + * The node's walking speed. + */ + val walkingSpeed: Double +} diff --git a/alchemist-cognitive-agents/src/test/kotlin/it/unibo/alchemist/test/TestFeelsTransmission.kt b/alchemist-cognitive-agents/src/test/kotlin/it/unibo/alchemist/test/TestFeelsTransmission.kt index 99020f002f..9c04322b67 100644 --- a/alchemist-cognitive-agents/src/test/kotlin/it/unibo/alchemist/test/TestFeelsTransmission.kt +++ b/alchemist-cognitive-agents/src/test/kotlin/it/unibo/alchemist/test/TestFeelsTransmission.kt @@ -7,23 +7,24 @@ import io.kotest.matchers.comparables.shouldBeLessThan import io.kotest.matchers.should import io.kotest.matchers.shouldNot import io.kotest.matchers.shouldNotBe -import it.unibo.alchemist.model.implementations.nodes.AbstractCognitivePedestrian -import it.unibo.alchemist.model.interfaces.CognitivePedestrian import it.unibo.alchemist.model.interfaces.Environment import it.unibo.alchemist.model.interfaces.EuclideanEnvironment import it.unibo.alchemist.model.interfaces.Position import it.unibo.alchemist.model.interfaces.geometry.Vector import it.unibo.alchemist.testsupport.loadYamlSimulation import it.unibo.alchemist.testsupport.startSimulation +import it.unibo.alchemist.model.interfaces.Node.Companion.asPropertyOrNull +import it.unibo.alchemist.model.interfaces.properties.CognitiveProperty class TestFeelsTransmission : StringSpec({ "danger layer affects cognitive pedestrians" { - fun Environment.perceivedDanger() = nodes.filterIsInstance>() - .sumOf { it.cognitiveModel.dangerBelief() } + fun Environment.perceivedDanger() = nodes + .mapNotNull { it.asPropertyOrNull>()?.cognitiveModel } + .sumOf { it.dangerBelief() } fun EuclideanEnvironment.dangerIsLoaded() = this.also { - nodes.filterIsInstance>().forEach { - it.danger shouldNotBe null + nodes.mapNotNull { it.asPropertyOrNull>()?.danger }.forEach { + it shouldNotBe null } } val aggregateDangerWithLayer = loadYamlSimulation("feels-transmission-with-layer.yml") diff --git a/alchemist-cognitive-agents/src/test/kotlin/it/unibo/alchemist/test/TestOrientingBehavior.kt b/alchemist-cognitive-agents/src/test/kotlin/it/unibo/alchemist/test/TestOrientingBehavior.kt index c798938a0a..5c50c5c90a 100644 --- a/alchemist-cognitive-agents/src/test/kotlin/it/unibo/alchemist/test/TestOrientingBehavior.kt +++ b/alchemist-cognitive-agents/src/test/kotlin/it/unibo/alchemist/test/TestOrientingBehavior.kt @@ -11,6 +11,7 @@ package it.unibo.alchemist.test import io.kotest.core.spec.style.StringSpec import io.kotest.matchers.collections.beEmpty +import io.kotest.matchers.collections.shouldNotBeIn import io.kotest.matchers.comparables.shouldBeLessThan import io.kotest.matchers.shouldBe import io.kotest.matchers.shouldNot @@ -18,13 +19,15 @@ import io.kotest.matchers.shouldNotBe import it.unibo.alchemist.model.interfaces.Environment import it.unibo.alchemist.model.interfaces.NavigationAction import it.unibo.alchemist.model.interfaces.NavigationStrategy -import it.unibo.alchemist.model.interfaces.OrientingPedestrian +import it.unibo.alchemist.model.interfaces.Node import it.unibo.alchemist.model.interfaces.Position +import it.unibo.alchemist.model.interfaces.properties.OrientingProperty import it.unibo.alchemist.model.interfaces.environments.Euclidean2DEnvironmentWithGraph import it.unibo.alchemist.model.interfaces.geometry.Vector import it.unibo.alchemist.testsupport.loadYamlSimulation import it.unibo.alchemist.testsupport.startSimulation import org.apache.commons.collections4.queue.CircularFifoQueue +import it.unibo.alchemist.model.interfaces.Node.Companion.asPropertyOrNull /** * Contains tests concerning [NavigationAction]s and [NavigationStrategy], such tests are @@ -32,19 +35,21 @@ import org.apache.commons.collections4.queue.CircularFifoQueue */ class TestOrientingBehavior : StringSpec({ + fun Iterable>.orienting() = filter { it.asPropertyOrNull>() != null } + /** * Asserts that the distance of each pedestrian from the target position specified * with [coords] is less than the given [tolerance]. */ fun assertPedestriansReached( - env: Environment, + environment: Environment, tolerance: Double, - vararg coords: Number + vararg coords: Number, ) { - val target = env.makePosition(*coords) - env.nodes - .filterIsInstance>() - .forEach { p -> env.getPosition(p).distanceTo(target) shouldBeLessThan tolerance } + val target = environment.makePosition(*coords) + environment.nodes + .orienting() + .forEach { p -> environment.getPosition(p).distanceTo(target) shouldBeLessThan tolerance } } /** @@ -60,7 +65,7 @@ class TestOrientingBehavior : StringSpec({ ) { loadYamlSimulation(simulation).startSimulation( onceInitialized = { it.nodes shouldNot beEmpty() }, - whenFinished = { env, _, _ -> assertPedestriansReached(env, tolerance, *coords) }, + whenFinished = { environment, _, _ -> assertPedestriansReached(environment, tolerance, *coords) }, steps = steps, ) } @@ -91,14 +96,14 @@ class TestOrientingBehavior : StringSpec({ "route following allows cuts to the route" { loadYamlSimulation("follow-route.yml").startSimulation( - atEachStep = { env: Environment, _, _, _ -> - if (env is Euclidean2DEnvironmentWithGraph<*, T, *, *>) { - val pedestrian = env.nodes.first() - val waypointToSkip = env.makePosition(70, 105) - env.graph.nodeContaining(waypointToSkip)?.contains(env.getPosition(pedestrian)) shouldBe false + atEachStep = { environment: Environment, _, _, _ -> + if (environment is Euclidean2DEnvironmentWithGraph<*, T, *, *>) { + val node = environment.nodes.first() + val waypointToSkip = environment.makePosition(70, 105) + environment.getPosition(node).shouldNotBeIn(environment.graph.nodeContaining(waypointToSkip)) } }, - whenFinished = { env, _, _ -> assertPedestriansReached(env, 1.0, 85, 80) }, + whenFinished = { environment, _, _ -> assertPedestriansReached(environment, 1.0, 85, 80) }, steps = 190 ) } @@ -132,15 +137,15 @@ class TestOrientingBehavior : StringSpec({ "pedestrian should avoid congestion" { var corridorTaken = false loadYamlSimulation("congestion-avoidance.yml").startSimulation( - atEachStep = { env: Environment, _, _, _ -> - if (env is Euclidean2DEnvironmentWithGraph<*, T, *, *> && !corridorTaken) { - val pedestrian = env.nodes.filterIsInstance>().first() - val corridorToTake = env.graph.nodeContaining(env.makePosition(35.0, 31.0)) - corridorTaken = corridorToTake?.contains(env.getPosition(pedestrian)) ?: false + atEachStep = { environment: Environment, _, _, _ -> + if (environment is Euclidean2DEnvironmentWithGraph<*, T, *, *> && !corridorTaken) { + val node = environment.nodes.orienting().first() + val corridorToTake = environment.graph.nodeContaining(environment.makePosition(35.0, 31.0)) + corridorTaken = corridorToTake?.contains(environment.getPosition(node)) ?: false } }, - whenFinished = { env, _, _ -> - assertPedestriansReached(env, 1.0, 10, 55) + whenFinished = { environment, _, _ -> + assertPedestriansReached(environment, 1.0, 10, 55) corridorTaken shouldBe true }, steps = 70 diff --git a/alchemist-cognitive-agents/src/test/kotlin/it/unibo/alchemist/test/TestPedestriansLoading.kt b/alchemist-cognitive-agents/src/test/kotlin/it/unibo/alchemist/test/TestPedestriansLoading.kt index 9af40f6cd5..f307f45b36 100644 --- a/alchemist-cognitive-agents/src/test/kotlin/it/unibo/alchemist/test/TestPedestriansLoading.kt +++ b/alchemist-cognitive-agents/src/test/kotlin/it/unibo/alchemist/test/TestPedestriansLoading.kt @@ -3,12 +3,14 @@ package it.unibo.alchemist.test import io.kotest.assertions.fail import io.kotest.assertions.throwables.shouldThrow import io.kotest.core.spec.style.StringSpec -import it.unibo.alchemist.model.interfaces.Pedestrian +import io.kotest.matchers.nulls.shouldNotBeNull import it.unibo.alchemist.model.interfaces.Position import it.unibo.alchemist.model.interfaces.geometry.GeometricTransformation import it.unibo.alchemist.model.interfaces.geometry.Vector import it.unibo.alchemist.testsupport.loadYamlSimulation import it.unibo.alchemist.testsupport.startSimulation +import it.unibo.alchemist.model.interfaces.Node.Companion.asPropertyOrNull +import it.unibo.alchemist.model.interfaces.properties.SocialProperty class TestPedestriansLoading : StringSpec({ @@ -33,10 +35,8 @@ class TestPedestriansLoading : StringSpec({ "groups of pedestrians loading" { loadYamlSimulation("groups.yml").startSimulation( - onceInitialized = { e -> - e.nodes.filterIsInstance>().forEach { - println("${it.id} -> ${it.membershipGroup}") - } + onceInitialized = { environment -> + environment.nodes.forEach { it.asPropertyOrNull>().shouldNotBeNull() } } ) } diff --git a/alchemist-cognitive-agents/src/test/kotlin/it/unibo/alchemist/test/TestSensory.kt b/alchemist-cognitive-agents/src/test/kotlin/it/unibo/alchemist/test/TestSensory.kt index 807f080f89..d141de7c13 100644 --- a/alchemist-cognitive-agents/src/test/kotlin/it/unibo/alchemist/test/TestSensory.kt +++ b/alchemist-cognitive-agents/src/test/kotlin/it/unibo/alchemist/test/TestSensory.kt @@ -12,13 +12,19 @@ package it.unibo.alchemist.test import io.kotest.core.spec.style.StringSpec import io.kotest.matchers.shouldBe import it.unibo.alchemist.SupportedIncarnations +import it.unibo.alchemist.model.implementations.properties.Pedestrian +import it.unibo.alchemist.model.implementations.properties.Perceptive2D +import it.unibo.alchemist.model.implementations.properties.Social import it.unibo.alchemist.model.implementations.environments.Continuous2DEnvironment import it.unibo.alchemist.model.implementations.linkingrules.NoLinks -import it.unibo.alchemist.model.implementations.nodes.HomogeneousPedestrian2D import it.unibo.alchemist.model.implementations.positions.Euclidean2DPosition import it.unibo.alchemist.model.implementations.geometry.euclidean2d.FieldOfView2D +import it.unibo.alchemist.model.implementations.nodes.GenericNode +import it.unibo.alchemist.model.implementations.properties.CircularArea import it.unibo.alchemist.model.interfaces.Incarnation +import it.unibo.alchemist.model.interfaces.environments.Physics2DEnvironment import org.apache.commons.math3.random.MersenneTwister +import org.apache.commons.math3.random.RandomGenerator /** * Tests that pedestrians can detect other objects in the environment. @@ -27,29 +33,42 @@ import org.apache.commons.math3.random.MersenneTwister */ class TestSensory : StringSpec({ + fun createHomogeneousPedestrian( + incarnation: Incarnation, + randomGenerator: RandomGenerator, + environment: Physics2DEnvironment, + ) = GenericNode(incarnation, environment).apply { + listOf( + Pedestrian(randomGenerator, this), + Social(this), + Perceptive2D(environment, this), + CircularArea(environment, this), + ).forEach(this::addProperty) + } + "field of view" { - val env = Continuous2DEnvironment( + val environment = Continuous2DEnvironment( SupportedIncarnations.get("protelis").orElseThrow() ) val rand = MersenneTwister(1) - env.linkingRule = NoLinks() + environment.linkingRule = NoLinks() val incarnation: Incarnation = SupportedIncarnations .get(SupportedIncarnations.getAvailableIncarnations().first()) .get() - val observed = HomogeneousPedestrian2D(incarnation, rand, env) + val observed = createHomogeneousPedestrian(incarnation, rand, environment) val origin = Euclidean2DPosition(5.0, 5.0) - env.addNode(observed, origin) + environment.addNode(observed, origin) val radius = 10.0 origin.surrounding(radius).forEach { - with(HomogeneousPedestrian2D(incarnation, rand, env)) { - env.addNode(this, it) - env.setHeading(this, origin - it) + with(createHomogeneousPedestrian(incarnation, rand, environment)) { + environment.addNode(this, it) + environment.setHeading(this, origin - it) } } - env.nodes.minusElement(observed).forEach { - with(FieldOfView2D(env, it, radius, Math.PI / 2).influentialNodes()) { - size shouldBe 1 - first() shouldBe observed + environment.nodes.minusElement(observed).forEach { + with(FieldOfView2D(environment, it, radius, Math.PI / 2)) { + influentialNodes().size shouldBe 1 + influentialNodes().first() shouldBe observed } } } diff --git a/alchemist-cognitive-agents/src/test/kotlin/it/unibo/alchemist/test/TestSteeringBehaviors.kt b/alchemist-cognitive-agents/src/test/kotlin/it/unibo/alchemist/test/TestSteeringBehaviors.kt index 186f57d927..858791ec33 100644 --- a/alchemist-cognitive-agents/src/test/kotlin/it/unibo/alchemist/test/TestSteeringBehaviors.kt +++ b/alchemist-cognitive-agents/src/test/kotlin/it/unibo/alchemist/test/TestSteeringBehaviors.kt @@ -5,8 +5,10 @@ import io.kotest.matchers.collections.shouldBeSortedWith import io.kotest.matchers.comparables.shouldBeGreaterThan import io.kotest.matchers.comparables.shouldBeLessThan import it.unibo.alchemist.model.interfaces.Node -import it.unibo.alchemist.model.interfaces.Pedestrian +import it.unibo.alchemist.model.interfaces.Node.Companion.asProperty +import it.unibo.alchemist.model.interfaces.Node.Companion.asPropertyOrNull import it.unibo.alchemist.model.interfaces.Position2D +import it.unibo.alchemist.model.interfaces.properties.SocialProperty import it.unibo.alchemist.model.interfaces.geometry.Vector2D import it.unibo.alchemist.testsupport.loadYamlSimulation import it.unibo.alchemist.testsupport.startSimulation @@ -16,6 +18,8 @@ private const val EPSILON = 0.001 class TestSteeringBehaviors : StringSpec({ + val filterSocialNode: (Node) -> Boolean = { it.asPropertyOrNull>() != null } + "nodes seeking a target must approach it" { val startDistances = mutableMapOf, Double>() val endDistances = mutableMapOf, Double>() @@ -82,8 +86,8 @@ class TestSteeringBehaviors : StringSpec({ loadYamlSimulation("cohesion.yml").startSimulation( whenFinished = { e, _, _ -> e.nodes.asSequence() - .filterIsInstance>() - .groupBy { it.membershipGroup } + .filter(filterSocialNode) + .groupBy { it.asProperty>().group } .values .forEach { for (nodePos in it.map { node -> e.getPosition(node) }) { diff --git a/alchemist-cognitive-agents/src/test/resources/arrive.yml b/alchemist-cognitive-agents/src/test/resources/arrive.yml index 968f2d9cbc..2abbfe16ff 100644 --- a/alchemist-cognitive-agents/src/test/resources/arrive.yml +++ b/alchemist-cognitive-agents/src/test/resources/arrive.yml @@ -19,6 +19,9 @@ _reactions: &behavior deployments: type: Circle parameters: [50, 0, 0, 400] - nodes: - type: HomogeneousPedestrian2D + properties: + - type: Pedestrian + - type: Social + - type: Perceptive2D + - type: CircularArea programs: *behavior diff --git a/alchemist-cognitive-agents/src/test/resources/blended-gradients.yml b/alchemist-cognitive-agents/src/test/resources/blended-gradients.yml index 8c6b8cfe07..e8b3d4d191 100644 --- a/alchemist-cognitive-agents/src/test/resources/blended-gradients.yml +++ b/alchemist-cognitive-agents/src/test/resources/blended-gradients.yml @@ -37,6 +37,9 @@ _reactions: &behavior deployments: - type: Circle parameters: [50, 0, 0, 33] - nodes: - type: HomogeneousPedestrian2D + properties: + - type: Pedestrian + - type: Social + - type: Percective2D + - type: CircularArea programs: *behavior diff --git a/alchemist-cognitive-agents/src/test/resources/blended.yml b/alchemist-cognitive-agents/src/test/resources/blended.yml index 81b8b97cfe..7889791197 100644 --- a/alchemist-cognitive-agents/src/test/resources/blended.yml +++ b/alchemist-cognitive-agents/src/test/resources/blended.yml @@ -18,13 +18,18 @@ _reactions: &behavior - type: Flee parameters: [500, -500] -deployments: +_homogeneous_pedestrian: &pedestrianInPoint - type: Point + properties: + - type: Pedestrian + - type: Social + - type: Percective + - type: CircularArea + +deployments: + - <<: *pedestrianInPoint parameters: [0, 0] - nodes: - type: HomogeneousPedestrian2D programs: *behavior - - type: Point + + - <<: *pedestrianInPoint parameters: [1000, 500] - nodes: - type: HomogeneousPedestrian2D diff --git a/alchemist-cognitive-agents/src/test/resources/cant-give-cognitive-to-heterogeneous.yml b/alchemist-cognitive-agents/src/test/resources/cant-give-cognitive-to-heterogeneous.yml index 458104500f..c49eed353f 100644 --- a/alchemist-cognitive-agents/src/test/resources/cant-give-cognitive-to-heterogeneous.yml +++ b/alchemist-cognitive-agents/src/test/resources/cant-give-cognitive-to-heterogeneous.yml @@ -15,15 +15,21 @@ _reactions: &behavior _deployment_hetero: &male-deployment type: Circle - parameters: [ 50, 0, 0, 20 ] - nodes: - type: HeterogeneousPedestrian2D + parameters: [ 50, 0, 0, 20 ] + properties: + - type: Human parameters: [ "adult", "male" ] - programs: *behavior + - type: HeterogeneousPedestrian + - type: Perceptive2D + - type: CircularArea + programs: *behavior deployments: - *male-deployment - <<: *male-deployment - nodes: - type: HeterogeneousPedestrian2D - parameters: ["adult", "female"] + properties: + - type: Human + parameters: [ "adult", "female" ] + - type: HeterogeneousPedestrian + - type: Perceptive2D + - type: CircularArea diff --git a/alchemist-cognitive-agents/src/test/resources/cognitive-pedestrians.yml b/alchemist-cognitive-agents/src/test/resources/cognitive-pedestrians.yml index 9d08900066..5c297fd030 100644 --- a/alchemist-cognitive-agents/src/test/resources/cognitive-pedestrians.yml +++ b/alchemist-cognitive-agents/src/test/resources/cognitive-pedestrians.yml @@ -13,16 +13,30 @@ _reactions: &behavior parameters: [1.0] type: CognitiveBehavior +_cognitive_properties: &properties + pedestrian: + type: CognitivePedestrian + cognitive: + type: Cognitive2D + perceptive: + type: Perceptive2D + area: + type: CircularArea + deployments: - type: Circle parameters: [50, 0, 0, 20] - nodes: - type: CognitivePedestrian2D - parameters: ["adult", "male"] + properties: + <<: *properties + human: + type: Human + parameters: ["adult", "male"] programs: *behavior - type: Circle parameters: [50, 0, 0, 20] - nodes: - type: CognitivePedestrian2D - parameters: ["adult", "female"] + properties: + <<: *properties + human: + type: Human + parameters: ["adult", "female"] programs: *behavior diff --git a/alchemist-cognitive-agents/src/test/resources/cohesion.yml b/alchemist-cognitive-agents/src/test/resources/cohesion.yml index 3f15d9b6a2..51f1fbc55c 100644 --- a/alchemist-cognitive-agents/src/test/resources/cohesion.yml +++ b/alchemist-cognitive-agents/src/test/resources/cohesion.yml @@ -40,28 +40,44 @@ _reactions: &behavior - type: CognitiveAgentAvoidLayer parameters: [*exit] +_homogeneous_properties: &homogeneous_properties + pedestrian: + type: Pedestrian + perceptive: + type: Perceptive2D + solid: + type: CircularArea + deployments: - type: Circle parameters: [8, 0, 0, 15] - nodes: - type: HomogeneousPedestrian2D - parameters: [*group1] + properties: + <<: *homogeneous_properties + social: + type: Social + parameters: [*group1] programs: *behavior - type: Circle parameters: [4, 0, 0, 15] - nodes: - type: HomogeneousPedestrian2D - parameters: [*group2] + properties: + <<: *homogeneous_properties + social: + type: Social + parameters: [*group2] programs: *behavior - type: Circle parameters: [10, 0, 0, 15] - nodes: - type: HomogeneousPedestrian2D - parameters: [*group3] + properties: + <<: *homogeneous_properties + social: + type: Social + parameters: [*group3] programs: *behavior - type: Circle parameters: [2, 0, 0, 15] - nodes: - type: HomogeneousPedestrian2D - parameters: [*group4] + properties: + <<: *homogeneous_properties + social: + type: Social + parameters: [*group4] programs: *behavior diff --git a/alchemist-cognitive-agents/src/test/resources/complete-knowledge.yml b/alchemist-cognitive-agents/src/test/resources/complete-knowledge.yml index dba1a0a048..78c56b5cf4 100644 --- a/alchemist-cognitive-agents/src/test/resources/complete-knowledge.yml +++ b/alchemist-cognitive-agents/src/test/resources/complete-knowledge.yml @@ -20,8 +20,11 @@ _reactions: &behavior deployments: - type: Point parameters: [15, 15] - nodes: - type: HomogeneousOrientingPedestrian2D - parameters: [1.0] + properties: + - type: CircularArea + - type: Orienting2D + parameters: [1.0] + - type: Pedestrian + - type: Social programs: - *behavior diff --git a/alchemist-cognitive-agents/src/test/resources/congestion-avoidance.yml b/alchemist-cognitive-agents/src/test/resources/congestion-avoidance.yml index 30a772f672..52a0ca4712 100644 --- a/alchemist-cognitive-agents/src/test/resources/congestion-avoidance.yml +++ b/alchemist-cognitive-agents/src/test/resources/congestion-avoidance.yml @@ -20,16 +20,25 @@ _reactions: &behavior deployments: - type: Point parameters: [70, 50] - nodes: - type: HomogeneousOrientingPedestrian2D - parameters: [1.0] + properties: + - type: CircularArea + - type: Orienting2D + parameters: [1.0] + - type: Pedestrian + - type: Social programs: - *behavior - type: Grid parameters: [27, 52, 56, 58, 0.5, 0.5, 0.3, 0.3] - nodes: - type: HomogeneousPedestrian2D + properties: + - type: Pedestrian + - type: Perceptive2D + - type: CircularArea + - type: Social - type: Grid parameters: [30, 40, 50, 45, 0.5, 0.5, 0.3, 0.3] - nodes: - type: HomogeneousPedestrian2D + properties: + - type: Pedestrian + - type: Perceptive2D + - type: CircularArea + - type: Social diff --git a/alchemist-cognitive-agents/src/test/resources/explore.yml b/alchemist-cognitive-agents/src/test/resources/explore.yml index 7b62c9ff3e..592e71d60e 100644 --- a/alchemist-cognitive-agents/src/test/resources/explore.yml +++ b/alchemist-cognitive-agents/src/test/resources/explore.yml @@ -19,8 +19,11 @@ _reactions: &behavior deployments: - type: Point parameters: [15, 15] - nodes: - type: HomogeneousOrientingPedestrian2D - parameters: [0.0] + properties: + - type: CircularArea + - type: Orienting2D + parameters: [0.0] + - type: Pedestrian + - type: Social programs: - *behavior diff --git a/alchemist-cognitive-agents/src/test/resources/feels-transmission-with-layer.yml b/alchemist-cognitive-agents/src/test/resources/feels-transmission-with-layer.yml index 94e64f197f..ed5cc92e38 100644 --- a/alchemist-cognitive-agents/src/test/resources/feels-transmission-with-layer.yml +++ b/alchemist-cognitive-agents/src/test/resources/feels-transmission-with-layer.yml @@ -29,20 +29,24 @@ _reactions: &behavior _deployment_males: &males type: Circle parameters: [ 50, 0, 0, 20 ] - nodes: - type: CognitivePedestrian2D - parameters: - age: "adult" - gender: "male" - danger: *danger + properties: + - type: Human + parameters: ["adult", "male"] + - type: CognitivePedestrian + - type: Cognitive2D + parameters: [*danger] + - type: Perceptive2D + - type: CircularArea programs: *behavior deployments: - *males - <<: *males - nodes: - type: CognitivePedestrian2D - parameters: - age: "adult" - gender: "female" - danger: *danger + properties: + - type: Human + parameters: ["adult", "female"] + - type: CognitivePedestrian + - type: Cognitive2D + parameters: [*danger] + - type: Perceptive2D + - type: CircularArea diff --git a/alchemist-cognitive-agents/src/test/resources/feels-transmission-without-layer.yml b/alchemist-cognitive-agents/src/test/resources/feels-transmission-without-layer.yml index 943db72f54..f6c8071bc7 100644 --- a/alchemist-cognitive-agents/src/test/resources/feels-transmission-without-layer.yml +++ b/alchemist-cognitive-agents/src/test/resources/feels-transmission-without-layer.yml @@ -20,13 +20,21 @@ _reactions: &behavior deployments: - type: Circle parameters: [50, 0, 0, 20] - nodes: - type: CognitivePedestrian2D - parameters: ["adult", "male"] + properties: + - type: Human + parameters: [ "adult", "male" ] + - type: CognitivePedestrian + - type: Cognitive2D + - type: Perceptive2D + - type: CircularArea programs: *behavior - type: Circle parameters: [50, 0, 0, 20] - nodes: - type: CognitivePedestrian2D - parameters: ["adult", "female"] - programs: *behavior \ No newline at end of file + properties: + - type: Human + parameters: [ "adult", "female" ] + - type: CognitivePedestrian + - type: Cognitive2D + - type: Perceptive2D + - type: CircularArea + programs: *behavior diff --git a/alchemist-cognitive-agents/src/test/resources/flee.yml b/alchemist-cognitive-agents/src/test/resources/flee.yml index 2e671da384..6b14169085 100644 --- a/alchemist-cognitive-agents/src/test/resources/flee.yml +++ b/alchemist-cognitive-agents/src/test/resources/flee.yml @@ -19,7 +19,10 @@ _reactions: &behavior deployments: - type: Circle parameters: [40, 0, 0, 20] - nodes: - type: HomogeneousPedestrian2D + properties: + - type: Pedestrian + - type: Social + - type: Perceptive2D + - type: CircularArea programs: - - *behavior \ No newline at end of file + - *behavior diff --git a/alchemist-cognitive-agents/src/test/resources/follow-route.yml b/alchemist-cognitive-agents/src/test/resources/follow-route.yml index c5887ac5c0..4f1e5798ab 100644 --- a/alchemist-cognitive-agents/src/test/resources/follow-route.yml +++ b/alchemist-cognitive-agents/src/test/resources/follow-route.yml @@ -20,7 +20,10 @@ _reactions: &behavior deployments: type: Point parameters: [15, 15] - nodes: - type: HomogeneousOrientingPedestrian2D - parameters: [0.0] + properties: + - type: CircularArea + - type: Orienting2D + parameters: [0.0] + - type: Pedestrian + - type: Social programs: *behavior diff --git a/alchemist-cognitive-agents/src/test/resources/goal-oriented-explore.yml b/alchemist-cognitive-agents/src/test/resources/goal-oriented-explore.yml index e3d5307bd7..7f2de01168 100644 --- a/alchemist-cognitive-agents/src/test/resources/goal-oriented-explore.yml +++ b/alchemist-cognitive-agents/src/test/resources/goal-oriented-explore.yml @@ -21,7 +21,10 @@ _reactions: &behavior deployments: type: Point parameters: [15, 15] - nodes: - type: HomogeneousOrientingPedestrian2D - parameters: [0.0] + properties: + - type: CircularArea + - type: Orienting2D + parameters: [0.0] + - type: Pedestrian + - type: Social programs: *behavior diff --git a/alchemist-cognitive-agents/src/test/resources/groups.yml b/alchemist-cognitive-agents/src/test/resources/groups.yml index acdfc57e2e..870cab82a4 100644 --- a/alchemist-cognitive-agents/src/test/resources/groups.yml +++ b/alchemist-cognitive-agents/src/test/resources/groups.yml @@ -17,14 +17,26 @@ variables: formula: *create_friends language: kotlin +_homogeneous_properties: &homogeneous_properties + pedestrian: + type: Pedestrian + perceptive: + type: Perceptive2D + solid: + type: CircularArea + deployments: - type: Circle parameters: [10, 100, 20, 10] - nodes: - type: HomogeneousPedestrian2D - parameters: [*group1] + properties: + <<: *homogeneous_properties + social: + type: Social + parameters: [*group1] - type: Circle parameters: [5, 10, 10, 10] - nodes: - type: HomogeneousPedestrian2D - parameters: [*group2] \ No newline at end of file + properties: + <<: *homogeneous_properties + social: + type: Social + parameters: [*group2] \ No newline at end of file diff --git a/alchemist-cognitive-agents/src/test/resources/heterogeneous-pedestrians.yml b/alchemist-cognitive-agents/src/test/resources/heterogeneous-pedestrians.yml index 3d62a5d39e..0702e87da6 100644 --- a/alchemist-cognitive-agents/src/test/resources/heterogeneous-pedestrians.yml +++ b/alchemist-cognitive-agents/src/test/resources/heterogeneous-pedestrians.yml @@ -10,11 +10,19 @@ seeds: deployments: - type: Circle parameters: [50, 0, 0, 20] - nodes: - type: HeterogeneousPedestrian2D - parameters: ["elderly", "female"] + properties: + - type: Human + parameters: ["elderly", "female"] + - type: HeterogeneousPedestrian + - type: Perceptive2D + - type: CircularArea + - type: Social - type: Circle parameters: [50, 0, 0, 20] - nodes: - type: HeterogeneousPedestrian2D - parameters: ["child", "male"] \ No newline at end of file + properties: + - type: Human + parameters: ["child", "male"] + - type: HeterogeneousPedestrian + - type: Perceptive2D + - type: CircularArea + - type: Social \ No newline at end of file diff --git a/alchemist-cognitive-agents/src/test/resources/homogeneous-pedestrians.yml b/alchemist-cognitive-agents/src/test/resources/homogeneous-pedestrians.yml index 4de1b23a50..b11cba520b 100644 --- a/alchemist-cognitive-agents/src/test/resources/homogeneous-pedestrians.yml +++ b/alchemist-cognitive-agents/src/test/resources/homogeneous-pedestrians.yml @@ -10,5 +10,8 @@ seeds: deployments: - type: Circle parameters: [100, 0, 0, 20] - nodes: - type: HomogeneousPedestrian2D \ No newline at end of file + properties: + - type: Pedestrian + - type: Social + - type: Perceptive2D + - type: CircularArea \ No newline at end of file diff --git a/alchemist-cognitive-agents/src/test/resources/nearest-door.yml b/alchemist-cognitive-agents/src/test/resources/nearest-door.yml index 925c955ad7..cdc30b3e62 100644 --- a/alchemist-cognitive-agents/src/test/resources/nearest-door.yml +++ b/alchemist-cognitive-agents/src/test/resources/nearest-door.yml @@ -21,8 +21,11 @@ _reactions: &behavior deployments: - type: Point parameters: [108, 88] - nodes: - type: HomogeneousOrientingPedestrian2D - parameters: [0.0] + properties: + - type: CircularArea + - type: Orienting2D + parameters: [0.0] + - type: Pedestrian + - type: Social programs: - *behavior diff --git a/alchemist-cognitive-agents/src/test/resources/no-knowledge.yml b/alchemist-cognitive-agents/src/test/resources/no-knowledge.yml index 4d29f278cd..d14ac90e8d 100644 --- a/alchemist-cognitive-agents/src/test/resources/no-knowledge.yml +++ b/alchemist-cognitive-agents/src/test/resources/no-knowledge.yml @@ -21,8 +21,11 @@ _reactions: &behavior deployments: - type: Point parameters: [15, 15] - nodes: - type: HomogeneousOrientingPedestrian2D - parameters: [0.0] + properties: + - type: CircularArea + - type: Orienting2D + parameters: [0.0] + - type: Pedestrian + - type: Social programs: - *behavior diff --git a/alchemist-cognitive-agents/src/test/resources/obstacle-avoidance.yml b/alchemist-cognitive-agents/src/test/resources/obstacle-avoidance.yml index 2676cb1211..a071cfa6e8 100644 --- a/alchemist-cognitive-agents/src/test/resources/obstacle-avoidance.yml +++ b/alchemist-cognitive-agents/src/test/resources/obstacle-avoidance.yml @@ -28,22 +28,26 @@ _reactions: &behavior - type: CognitiveAgentObstacleAvoidance parameters: [50] +_homogeous_pedestrian: &homogeneous_pedestrian + properties: + - type: Pedestrian + - type: Social + - type: Perceptive2D + - type: CircularArea + deployments: - type: Circle parameters: [20, -100, 240, 30] - nodes: - type: HomogeneousPedestrian2D + <<: *homogeneous_pedestrian programs: - *behavior - type: Circle parameters: [30, 0, 600, 50] - nodes: - type: HomogeneousPedestrian2D + <<: *homogeneous_pedestrian programs: - *behavior - type: Circle parameters: [40, 100, -60, 40] - nodes: - type: HomogeneousPedestrian2D + <<: *homogeneous_pedestrian programs: - *behavior \ No newline at end of file diff --git a/alchemist-cognitive-agents/src/test/resources/partial-knowledge.yml b/alchemist-cognitive-agents/src/test/resources/partial-knowledge.yml index d3280a34b5..1572f449cc 100644 --- a/alchemist-cognitive-agents/src/test/resources/partial-knowledge.yml +++ b/alchemist-cognitive-agents/src/test/resources/partial-knowledge.yml @@ -20,8 +20,11 @@ _reactions: &behavior deployments: - type: Point parameters: [15, 15] - nodes: - type: HomogeneousOrientingPedestrian2D - parameters: [0.3] + properties: + - type: CircularArea + - type: Orienting2D + parameters: [0.3] + - type: Pedestrian + - type: Social programs: - *behavior diff --git a/alchemist-cognitive-agents/src/test/resources/pursue.yml b/alchemist-cognitive-agents/src/test/resources/pursue.yml index ac520927ec..72a3c04066 100644 --- a/alchemist-cognitive-agents/src/test/resources/pursue.yml +++ b/alchemist-cognitive-agents/src/test/resources/pursue.yml @@ -21,7 +21,10 @@ _reactions: &behavior deployments: type: Point parameters: [15, 15] - nodes: - type: HomogeneousOrientingPedestrian2D - parameters: [0.0] + properties: + - type: CircularArea + - type: Orienting2D + parameters: [0.0] + - type: Pedestrian + - type: Social programs: *behavior diff --git a/alchemist-cognitive-agents/src/test/resources/reach-destination.yml b/alchemist-cognitive-agents/src/test/resources/reach-destination.yml index 54e287ac9e..98b4dc906f 100644 --- a/alchemist-cognitive-agents/src/test/resources/reach-destination.yml +++ b/alchemist-cognitive-agents/src/test/resources/reach-destination.yml @@ -21,8 +21,11 @@ _reactions: &behavior deployments: - type: Point parameters: [15, 15] - nodes: - type: HomogeneousOrientingPedestrian2D - parameters: [0.4] + properties: + - type: CircularArea + - type: Orienting2D + parameters: [0.4] + - type: Pedestrian + - type: Social programs: - *behavior diff --git a/alchemist-cognitive-agents/src/test/resources/seek.yml b/alchemist-cognitive-agents/src/test/resources/seek.yml index 9ab2c2409f..ecdc029c0e 100644 --- a/alchemist-cognitive-agents/src/test/resources/seek.yml +++ b/alchemist-cognitive-agents/src/test/resources/seek.yml @@ -19,7 +19,10 @@ _reactions: &behavior deployments: - type: Circle parameters: [40, 0, 0, 20] - nodes: - type: HomogeneousPedestrian2D + properties: + - type: Pedestrian + - type: Social + - type: Perceptive2D + - type: CircularArea programs: - *behavior \ No newline at end of file diff --git a/alchemist-cognitive-agents/src/test/resources/separation.yml b/alchemist-cognitive-agents/src/test/resources/separation.yml index 1c15343db3..8484e79ee5 100644 --- a/alchemist-cognitive-agents/src/test/resources/separation.yml +++ b/alchemist-cognitive-agents/src/test/resources/separation.yml @@ -19,7 +19,10 @@ _reactions: &behavior deployments: - type: Circle parameters: [30, 0, 0, 20] - nodes: - type: HomogeneousPedestrian2D + properties: + - type: Pedestrian + - type: Social + - type: Perceptive2D + - type: CircularArea programs: - - *behavior \ No newline at end of file + - *behavior diff --git a/alchemist-cognitive-agents/src/test/resources/social-contagion.yml b/alchemist-cognitive-agents/src/test/resources/social-contagion.yml index 2373322e61..7af49a7185 100644 --- a/alchemist-cognitive-agents/src/test/resources/social-contagion.yml +++ b/alchemist-cognitive-agents/src/test/resources/social-contagion.yml @@ -41,12 +41,14 @@ _reactions: &behavior - type: HeadTowardRandomDirection _nodes: &nodes - nodes: - type: CognitivePedestrian2D - parameters: - age: "adult" - gender: "male" - danger: *danger + properties: + - type: Human + parameters: [ "adult", "male" ] + - type: CognitivePedestrian + - type: Cognitive2D + parameters: [ *danger ] + - type: Perceptive2D + - type: CircularArea programs: *behavior deployments: diff --git a/alchemist-engine/src/test/java/it/unibo/alchemist/core/tests/TestConcurrency.java b/alchemist-engine/src/test/java/it/unibo/alchemist/core/tests/TestConcurrency.java index 5f22ae7559..db31e63a0b 100644 --- a/alchemist-engine/src/test/java/it/unibo/alchemist/core/tests/TestConcurrency.java +++ b/alchemist-engine/src/test/java/it/unibo/alchemist/core/tests/TestConcurrency.java @@ -16,11 +16,12 @@ import it.unibo.alchemist.core.interfaces.Status; import it.unibo.alchemist.model.implementations.environments.Continuous2DEnvironment; import it.unibo.alchemist.model.implementations.linkingrules.NoLinks; -import it.unibo.alchemist.model.implementations.nodes.AbstractNode; +import it.unibo.alchemist.model.implementations.nodes.GenericNode; import it.unibo.alchemist.model.implementations.positions.Euclidean2DPosition; import it.unibo.alchemist.model.implementations.reactions.Event; import it.unibo.alchemist.model.implementations.timedistributions.DiracComb; import it.unibo.alchemist.model.interfaces.Environment; +import it.unibo.alchemist.model.interfaces.Incarnation; import it.unibo.alchemist.model.interfaces.Node; import it.unibo.alchemist.model.interfaces.Reaction; import it.unibo.alchemist.model.interfaces.TimeDistribution; @@ -44,20 +45,22 @@ */ class TestConcurrency { - private Environment env; + private Environment environment; /** * Setup phase. */ @BeforeEach public void setUp() { - env = new Continuous2DEnvironment<>(SupportedIncarnations.get("sapere").get()); - final Node n = new DummyNode(env); - env.setLinkingRule(new NoLinks<>()); + final Incarnation incarnation = SupportedIncarnations + .get("sapere").get(); + environment = new Continuous2DEnvironment<>(incarnation); + final Node n = new GenericNode<>(incarnation, environment); + environment.setLinkingRule(new NoLinks<>()); final TimeDistribution td = new DiracComb<>(1); final Reaction r = new Event<>(n, td); n.addReaction(r); - env.addNode(n, env.makePosition(0, 0)); + environment.addNode(n, environment.makePosition(0, 0)); } /** @@ -74,7 +77,7 @@ public void setUp() { ) void testCommandInterleaving() throws InterruptedException, ExecutionException { final int inWaitCount = 100; - final Simulation sim = new Engine<>(env, 10); + final Simulation sim = new Engine<>(environment, 10); sim.pause(); final ExecutorService container = Executors.newFixedThreadPool(inWaitCount + 1); container.submit(sim); @@ -110,15 +113,4 @@ void testCommandInterleaving() throws InterruptedException, ExecutionException { assertEquals(Status.TERMINATED, sim.waitFor(Status.RUNNING, 100, TimeUnit.MILLISECONDS)); } - private static final class DummyNode extends AbstractNode { - private static final long serialVersionUID = 1L; - private DummyNode(final Environment env) { - super(env); - } - @Override - protected Object createT() { - return ""; - } - } - } diff --git a/alchemist-euclidean-geometry/src/main/kotlin/it/unibo/alchemist/model/implementations/actions/FollowAtDistance.kt b/alchemist-euclidean-geometry/src/main/kotlin/it/unibo/alchemist/model/implementations/actions/FollowAtDistance.kt index 9081f87a99..4be14e8299 100644 --- a/alchemist-euclidean-geometry/src/main/kotlin/it/unibo/alchemist/model/implementations/actions/FollowAtDistance.kt +++ b/alchemist-euclidean-geometry/src/main/kotlin/it/unibo/alchemist/model/implementations/actions/FollowAtDistance.kt @@ -26,7 +26,7 @@ import kotlin.math.sin * but keeping a [distance] from it. * * @param concentration type - * @param env the environment containing the nodes + * @param environment the environment containing the nodes * @param node the follower * @param reaction the reaction hosting this action * @param target molecule from which to read the destination to follow in the form of coordinates or a tuple @@ -36,7 +36,7 @@ import kotlin.math.sin class FollowAtDistance( node: Node, private val reaction: Reaction, - private val env: Environment, + private val environment: Environment, private val target: Molecule, private val distance: Double, private val speed: Double @@ -45,12 +45,12 @@ class FollowAtDistance( private val speedStrategy = GloballyConstantSpeed(reaction, speed) override fun cloneAction(node: Node, reaction: Reaction) = - FollowAtDistance(node, reaction, env, target, distance, speed) + FollowAtDistance(node, reaction, environment, target, distance, speed) override fun execute() { node.getConcentration(target)?.also { - val targetPosition = it.toPosition(env) - val currentPosition = env.getPosition(node) + val targetPosition = it.toPosition(environment) + val currentPosition = environment.getPosition(node) var destination = targetPosition.surroundingPointAt(currentPosition - targetPosition, distance) if (currentPosition != destination) { // avoid "bouncing" val currentSpeed = min( @@ -61,7 +61,7 @@ class FollowAtDistance( val angle = direction.asAngle destination = currentPosition + Euclidean2DPosition(currentSpeed * cos(angle), currentSpeed * sin(angle)) - env.moveNodeToPosition(node, destination) + environment.moveNodeToPosition(node, destination) } } } diff --git a/alchemist-euclidean-geometry/src/main/kotlin/it/unibo/alchemist/model/implementations/actions/Spin.kt b/alchemist-euclidean-geometry/src/main/kotlin/it/unibo/alchemist/model/implementations/actions/Spin.kt index 8a62a6f5ee..000fd4fc76 100644 --- a/alchemist-euclidean-geometry/src/main/kotlin/it/unibo/alchemist/model/implementations/actions/Spin.kt +++ b/alchemist-euclidean-geometry/src/main/kotlin/it/unibo/alchemist/model/implementations/actions/Spin.kt @@ -23,21 +23,22 @@ import org.apache.commons.math3.util.FastMath.toRadians class Spin( node: Node, private val reaction: Reaction, - private val env: Physics2DEnvironment, + private val environment: Physics2DEnvironment, private val angularSpeedDegrees: Double ) : AbstractAction(node) { private val angularSpeedRadians = toRadians(angularSpeedDegrees) - override fun cloneAction(node: Node, reaction: Reaction) = Spin(node, reaction, env, angularSpeedDegrees) + override fun cloneAction(node: Node, reaction: Reaction) = + Spin(node, reaction, environment, angularSpeedDegrees) /** * Spins the node around itself. */ override fun execute() { val realSpeed = angularSpeedRadians / reaction.timeDistribution.rate - val headingAngle = env.getHeading(node).asAngle + realSpeed - env.setHeading(node, env.makePosition(cos(headingAngle), sin(headingAngle))) + val headingAngle = environment.getHeading(node).asAngle + realSpeed + environment.setHeading(node, environment.makePosition(cos(headingAngle), sin(headingAngle))) } override fun getContext() = Context.LOCAL diff --git a/alchemist-euclidean-geometry/src/main/kotlin/it/unibo/alchemist/model/implementations/environments/Continuous2DEnvironment.kt b/alchemist-euclidean-geometry/src/main/kotlin/it/unibo/alchemist/model/implementations/environments/Continuous2DEnvironment.kt index 7633cc7ed2..058774771d 100644 --- a/alchemist-euclidean-geometry/src/main/kotlin/it/unibo/alchemist/model/implementations/environments/Continuous2DEnvironment.kt +++ b/alchemist-euclidean-geometry/src/main/kotlin/it/unibo/alchemist/model/implementations/environments/Continuous2DEnvironment.kt @@ -22,7 +22,10 @@ import it.unibo.alchemist.model.interfaces.geometry.euclidean2d.Euclidean2DShape import it.unibo.alchemist.model.interfaces.geometry.euclidean2d.Euclidean2DShapeFactory import it.unibo.alchemist.model.interfaces.geometry.euclidean2d.Euclidean2DTransformation import it.unibo.alchemist.model.interfaces.geometry.euclidean2d.Segment2D -import it.unibo.alchemist.model.interfaces.nodes.NodeWithShape +import it.unibo.alchemist.model.interfaces.Node.Companion.asProperty +import it.unibo.alchemist.model.interfaces.Node.Companion.asPropertyOrNull +import it.unibo.alchemist.model.interfaces.properties.AreaProperty +import it.unibo.alchemist.model.interfaces.properties.OccupiesSpaceProperty /** * Implementation of [Physics2DEnvironment]. @@ -58,13 +61,11 @@ open class Continuous2DEnvironment(incarnation: Incarnation): Euclidean2DShape = when (node) { - !is NodeWithShape<*, *, *> -> adimensional - else -> shapeFactory.requireCompatible(node.shape).transformed { + override fun getShape(node: Node): Euclidean2DShape = + node.asPropertyOrNull>()?.shape?.transformed { origin(getPosition(node)) rotate(getHeading(node)) - } - } + } ?: adimensional /** * Keeps track of the largest diameter of the shapes. Throws an [IllegalStateException] if the [node] can't fit @@ -72,12 +73,12 @@ open class Continuous2DEnvironment(incarnation: Incarnation, position: Euclidean2DPosition, neighborhood: Neighborhood) { super.nodeAdded(node, position, neighborhood) + val occupiesSpace = node.asPropertyOrNull>() check(node.canFit(position)) { - node as NodeWithShape "node in $position overlaps with nodes in ${node.overlappingNodes(position).map { getPosition(it) }}." } - if (node is NodeWithShape<*, *, *> && node.shape.diameter > largestShapeDiameter) { - largestShapeDiameter = node.shape.diameter + if (occupiesSpace != null && occupiesSpace.shape.diameter > largestShapeDiameter) { + largestShapeDiameter = occupiesSpace.shape.diameter } } @@ -87,9 +88,10 @@ open class Continuous2DEnvironment(incarnation: Incarnation, neighborhood: Neighborhood) = super.nodeRemoved(node, neighborhood).also { nodeToHeading.remove(node) - if (node is NodeWithShape<*, *, *> && largestShapeDiameter <= node.shape.diameter) { + val occupiesSpaceProperty = node.asPropertyOrNull>() + if (occupiesSpaceProperty != null && largestShapeDiameter <= occupiesSpaceProperty.shape.diameter) { largestShapeDiameter = nodes.asSequence() - .filterIsInstance>() + .mapNotNull { it.asPropertyOrNull>() } .map { it.shape.diameter } .maxOrNull() ?: 0.0 } @@ -100,7 +102,7 @@ open class Continuous2DEnvironment(incarnation: Incarnation, newPosition: Euclidean2DPosition) = - if (node is NodeWithShape) { + if (node.asPropertyOrNull>() != null) { super.moveNodeToPosition(node, farthestPositionReachable(node, newPosition)) } else { super.moveNodeToPosition(node, newPosition) @@ -126,7 +128,7 @@ open class Continuous2DEnvironment(incarnation: Incarnation, + node: Node, desiredPosition: Euclidean2DPosition, hitboxRadius: Double ): Euclidean2DPosition { @@ -141,7 +143,7 @@ open class Continuous2DEnvironment(incarnation: Incarnation(incarnation: Incarnation, desiredMovement: Segment2D<*>): Nodes = - with(node.shape) { + private fun nodesOnPath(node: Node, desiredMovement: Segment2D<*>): Nodes = + with(node.asProperty>().shape) { shapeFactory.rectangle(desiredMovement.length + diameter, diameter) .transformed { desiredMovement.midPoint.let { origin(it.x, it.y) } rotate(desiredMovement.toVector.asAngle) } .let { movementArea -> - getNodesWithin(movementArea).filterIsInstance>().minusElement(node) + getNodesWithin(movementArea) + .filter { it.asPropertyOrNull>() != null } + .minusElement(node) } } @@ -173,16 +177,18 @@ open class Continuous2DEnvironment(incarnation: Incarnation.canFit(position: Euclidean2DPosition): Boolean = - this !is NodeWithShape || overlappingNodes(position).isEmpty() + asPropertyOrNull>() == null || overlappingNodes(position).isEmpty() /** * @returns the nodes in this environment whose shape intersects this node's shape. The [position] of this * node must be specified as it may not have been added in the environment yet. */ - private fun NodeWithShape.overlappingNodes(position: Euclidean2DPosition): Nodes = - getNodesWithin(shapeFactory.requireCompatible(shape).transformed { origin(position) }) - .filterIsInstance>() + private fun Node.overlappingNodes(position: Euclidean2DPosition): Nodes { + val shape = asProperty>().shape + return getNodesWithin(shapeFactory.requireCompatible(shape).transformed { origin(position) }) + .filter { it.asPropertyOrNull>() != null } .minusElement(this) + } } -private typealias Nodes = List> +private typealias Nodes = List> diff --git a/alchemist-euclidean-geometry/src/main/kotlin/it/unibo/alchemist/model/implementations/geometry/euclidean2d/FieldOfView2D.kt b/alchemist-euclidean-geometry/src/main/kotlin/it/unibo/alchemist/model/implementations/geometry/euclidean2d/FieldOfView2D.kt index 4b74335425..44ce597083 100644 --- a/alchemist-euclidean-geometry/src/main/kotlin/it/unibo/alchemist/model/implementations/geometry/euclidean2d/FieldOfView2D.kt +++ b/alchemist-euclidean-geometry/src/main/kotlin/it/unibo/alchemist/model/implementations/geometry/euclidean2d/FieldOfView2D.kt @@ -15,7 +15,7 @@ import it.unibo.alchemist.model.interfaces.environments.Physics2DEnvironment /** * A sphere of influence representing the sight of a node in the Euclidean world. * - * @param env + * @param environment * the environment where this sphere of influence is. * @param owner * the node who owns this sphere of influence. @@ -25,8 +25,8 @@ import it.unibo.alchemist.model.interfaces.environments.Physics2DEnvironment * the amplitude of the field of view in radians. */ class FieldOfView2D( - env: Physics2DEnvironment, + environment: Physics2DEnvironment, owner: Node, distance: Double, aperture: Double -) : InfluenceSphere2D(env, owner, env.shapeFactory.circleSector(distance, aperture, 0.0)) +) : InfluenceSphere2D(environment, owner, environment.shapeFactory.circleSector(distance, aperture, 0.0)) diff --git a/alchemist-euclidean-geometry/src/main/kotlin/it/unibo/alchemist/model/implementations/geometry/euclidean2d/InfluenceSphere2D.kt b/alchemist-euclidean-geometry/src/main/kotlin/it/unibo/alchemist/model/implementations/geometry/euclidean2d/InfluenceSphere2D.kt index 4b38ae0a6b..c5e426c38e 100644 --- a/alchemist-euclidean-geometry/src/main/kotlin/it/unibo/alchemist/model/implementations/geometry/euclidean2d/InfluenceSphere2D.kt +++ b/alchemist-euclidean-geometry/src/main/kotlin/it/unibo/alchemist/model/implementations/geometry/euclidean2d/InfluenceSphere2D.kt @@ -8,7 +8,7 @@ import it.unibo.alchemist.model.interfaces.geometry.euclidean2d.Euclidean2DShape /** * A sphere of influence in the Euclidean world. * - * @param env + * @param environment * the environment where this sphere of influence is. * @param owner * the node who owns this sphere of influence. @@ -16,15 +16,14 @@ import it.unibo.alchemist.model.interfaces.geometry.euclidean2d.Euclidean2DShape * the shape of this sphere of influence */ open class InfluenceSphere2D( - private val env: Physics2DEnvironment, + private val environment: Physics2DEnvironment, private val owner: Node, private val shape: Euclidean2DShape -) : InfluenceSphere { - - override fun influentialNodes(): List> = env.getNodesWithin( +) : InfluenceSphere { + override fun influentialNodes(): List> = environment.getNodesWithin( shape.transformed { - origin(env.getPosition(owner)) - rotate(env.getHeading(owner)) + origin(environment.getPosition(owner)) + rotate(environment.getHeading(owner)) } ).minusElement(owner) } diff --git a/alchemist-euclidean-geometry/src/main/kotlin/it/unibo/alchemist/model/implementations/movestrategies/RandomTarget.kt b/alchemist-euclidean-geometry/src/main/kotlin/it/unibo/alchemist/model/implementations/movestrategies/RandomTarget.kt index e58152967d..8cad39f560 100644 --- a/alchemist-euclidean-geometry/src/main/kotlin/it/unibo/alchemist/model/implementations/movestrategies/RandomTarget.kt +++ b/alchemist-euclidean-geometry/src/main/kotlin/it/unibo/alchemist/model/implementations/movestrategies/RandomTarget.kt @@ -36,14 +36,20 @@ class RandomTarget( ) : ChangeTargetOnCollision(getCurrentPosition) { /** - * Handy constructor for Alchemist where the object to move is a [node] in the [env]. + * Handy constructor for Alchemist where the object to move is a [node] in the [environment]. */ constructor( - env: Environment, + environment: Environment, node: Node, directionRng: RandomGenerator, distanceDistribution: RealDistribution - ) : this(env, { env.getPosition(node) }, { x, y -> env.makePosition(x, y) }, directionRng, distanceDistribution) + ) : this( + environment, + { environment.getPosition(node) }, + { x, y -> environment.makePosition(x, y) }, + directionRng, + distanceDistribution, + ) override fun chooseTarget() = with(directionRng.nextDouble(0.0, 2 * PI)) { val distance = distanceDistribution.sample() diff --git a/alchemist-euclidean-geometry/src/main/kotlin/it/unibo/alchemist/model/implementations/nodes/CircleNode.kt b/alchemist-euclidean-geometry/src/main/kotlin/it/unibo/alchemist/model/implementations/nodes/CircleNode.kt deleted file mode 100644 index 51931bbddb..0000000000 --- a/alchemist-euclidean-geometry/src/main/kotlin/it/unibo/alchemist/model/implementations/nodes/CircleNode.kt +++ /dev/null @@ -1,35 +0,0 @@ -/* - * Copyright (C) 2010-2020, Danilo Pianini and contributors - * listed in the main project's alchemist/build.gradle.kts file. - * - * This file is part of Alchemist, and is distributed under the terms of the - * GNU General Public License, with a linking exception, - * as described in the file LICENSE in the Alchemist distribution's top directory. - */ - -package it.unibo.alchemist.model.implementations.nodes - -import it.unibo.alchemist.model.implementations.positions.Euclidean2DPosition -import it.unibo.alchemist.model.interfaces.environments.Physics2DEnvironment -import it.unibo.alchemist.model.interfaces.geometry.euclidean2d.Euclidean2DTransformation -import it.unibo.alchemist.model.interfaces.nodes.NodeWithShape - -/** - * A [it.unibo.alchemist.model.interfaces.Node] with a circle shape meant to be added to a - * [it.unibo.alchemist.model.interfaces.environments.PhysicsEnvironment]. - */ -open class CircleNode @JvmOverloads constructor( - env: Physics2DEnvironment, - radius: Double = 5.0 -) : AbstractNode(env), NodeWithShape { - - /** - * {@inheritDoc}. - */ - final override val shape = env.shapeFactory.circle(radius) - - /** - * Returns null because T is unknown. - */ - override fun createT() = null -} diff --git a/alchemist-euclidean-geometry/src/main/kotlin/it/unibo/alchemist/model/implementations/properties/CircularArea.kt b/alchemist-euclidean-geometry/src/main/kotlin/it/unibo/alchemist/model/implementations/properties/CircularArea.kt new file mode 100644 index 0000000000..a84e3493a8 --- /dev/null +++ b/alchemist-euclidean-geometry/src/main/kotlin/it/unibo/alchemist/model/implementations/properties/CircularArea.kt @@ -0,0 +1,34 @@ +/* + * Copyright (C) 2010-2022, Danilo Pianini and contributors + * listed, for each module, in the respective subproject's build.gradle.kts file. + * + * This file is part of Alchemist, and is distributed under the terms of the + * GNU General Public License, with a linking exception, + * as described in the file LICENSE in the Alchemist distribution's top directory. + */ + +package it.unibo.alchemist.model.implementations.properties + +import it.unibo.alchemist.model.implementations.positions.Euclidean2DPosition +import it.unibo.alchemist.model.interfaces.Node +import it.unibo.alchemist.model.interfaces.properties.OccupiesSpaceProperty +import it.unibo.alchemist.model.interfaces.environments.Physics2DEnvironment +import it.unibo.alchemist.model.interfaces.geometry.euclidean2d.Euclidean2DShape +import it.unibo.alchemist.model.interfaces.geometry.euclidean2d.Euclidean2DTransformation + +/** + * The [node] occupies a circular space with the provided radius. + */ +class CircularArea @JvmOverloads constructor( + /** + * The environment in witch the node moves. + */ + val environment: Physics2DEnvironment, + override val node: Node, + radius: Double = 0.3, +) : OccupiesSpaceProperty { + + override val shape: Euclidean2DShape = environment.shapeFactory.circle(radius) + + override fun cloneOnNewNode(node: Node) = CircularArea(environment, node, shape.radius) +} diff --git a/alchemist-euclidean-geometry/src/main/kotlin/it/unibo/alchemist/model/implementations/properties/RectangularArea.kt b/alchemist-euclidean-geometry/src/main/kotlin/it/unibo/alchemist/model/implementations/properties/RectangularArea.kt new file mode 100644 index 0000000000..ff5104342d --- /dev/null +++ b/alchemist-euclidean-geometry/src/main/kotlin/it/unibo/alchemist/model/implementations/properties/RectangularArea.kt @@ -0,0 +1,41 @@ +/* + * Copyright (C) 2010-2022, Danilo Pianini and contributors + * listed, for each module, in the respective subproject's build.gradle.kts file. + * + * This file is part of Alchemist, and is distributed under the terms of the + * GNU General Public License, with a linking exception, + * as described in the file LICENSE in the Alchemist distribution's top directory. + */ + +package it.unibo.alchemist.model.implementations.properties + +import it.unibo.alchemist.model.implementations.positions.Euclidean2DPosition +import it.unibo.alchemist.model.interfaces.Node +import it.unibo.alchemist.model.interfaces.environments.Physics2DEnvironment +import it.unibo.alchemist.model.interfaces.geometry.euclidean2d.Euclidean2DShape +import it.unibo.alchemist.model.interfaces.geometry.euclidean2d.Euclidean2DTransformation +import it.unibo.alchemist.model.interfaces.properties.OccupiesSpaceProperty + +/** + * The [node] occupies a rectangular width x height area. + */ +class RectangularArea( + /** + * The environment in which [node] is moving. + */ + val environment: Physics2DEnvironment, + override val node: Node, + /** + * The rectangle width. + */ + val width: Double, + /** + * The rectangle height. + */ + val height: Double, +) : OccupiesSpaceProperty { + + override val shape: Euclidean2DShape = environment.shapeFactory.rectangle(width, height) + + override fun cloneOnNewNode(node: Node) = RectangularArea(environment, node, width, height) +} diff --git a/alchemist-euclidean-geometry/src/main/kotlin/it/unibo/alchemist/model/interfaces/environments/PhysicsEnvironment.kt b/alchemist-euclidean-geometry/src/main/kotlin/it/unibo/alchemist/model/interfaces/environments/PhysicsEnvironment.kt index dd26893b45..996248a21e 100644 --- a/alchemist-euclidean-geometry/src/main/kotlin/it/unibo/alchemist/model/interfaces/environments/PhysicsEnvironment.kt +++ b/alchemist-euclidean-geometry/src/main/kotlin/it/unibo/alchemist/model/interfaces/environments/PhysicsEnvironment.kt @@ -3,11 +3,12 @@ package it.unibo.alchemist.model.interfaces.environments import it.unibo.alchemist.model.interfaces.EuclideanEnvironment import it.unibo.alchemist.model.interfaces.Node import it.unibo.alchemist.model.interfaces.Position +import it.unibo.alchemist.model.interfaces.properties.OccupiesSpaceProperty import it.unibo.alchemist.model.interfaces.geometry.GeometricShape import it.unibo.alchemist.model.interfaces.geometry.GeometricShapeFactory import it.unibo.alchemist.model.interfaces.geometry.GeometricTransformation import it.unibo.alchemist.model.interfaces.geometry.Vector -import it.unibo.alchemist.model.interfaces.nodes.NodeWithShape +import it.unibo.alchemist.model.interfaces.Node.Companion.asProperty /** * An environment supporting physics and nodes shapes. @@ -69,8 +70,8 @@ where P : Position

, * a different radius for the hitbox of the moving node. */ fun farthestPositionReachable( - node: NodeWithShape, + node: Node, desiredPosition: P, - hitboxRadius: Double = node.shape.radius + hitboxRadius: Double = node.asProperty>().shape.radius, ): P } diff --git a/alchemist-euclidean-geometry/src/main/kotlin/it/unibo/alchemist/model/interfaces/geometry/InfluenceSphere.kt b/alchemist-euclidean-geometry/src/main/kotlin/it/unibo/alchemist/model/interfaces/geometry/InfluenceSphere.kt index 2e5b1fec30..9c09877f8c 100644 --- a/alchemist-euclidean-geometry/src/main/kotlin/it/unibo/alchemist/model/interfaces/geometry/InfluenceSphere.kt +++ b/alchemist-euclidean-geometry/src/main/kotlin/it/unibo/alchemist/model/interfaces/geometry/InfluenceSphere.kt @@ -5,10 +5,9 @@ import it.unibo.alchemist.model.interfaces.Node /** * Area inside which nodes exert an influence on each other. */ -interface InfluenceSphere { - +interface InfluenceSphere { /** - * The list of nodes relevant for this sphere of influence. + * List of influential nodes. (e.g. nodes withing a field of view). */ - fun influentialNodes(): List> + fun influentialNodes(): List> } diff --git a/alchemist-cognitive-agents/src/main/kotlin/it/unibo/alchemist/model/interfaces/CognitivePedestrian2D.kt b/alchemist-euclidean-geometry/src/main/kotlin/it/unibo/alchemist/model/interfaces/properties/AreaProperty.kt similarity index 59% rename from alchemist-cognitive-agents/src/main/kotlin/it/unibo/alchemist/model/interfaces/CognitivePedestrian2D.kt rename to alchemist-euclidean-geometry/src/main/kotlin/it/unibo/alchemist/model/interfaces/properties/AreaProperty.kt index 0e5dc69eb3..330705744a 100644 --- a/alchemist-cognitive-agents/src/main/kotlin/it/unibo/alchemist/model/interfaces/CognitivePedestrian2D.kt +++ b/alchemist-euclidean-geometry/src/main/kotlin/it/unibo/alchemist/model/interfaces/properties/AreaProperty.kt @@ -1,5 +1,5 @@ /* - * Copyright (C) 2010-2021, Danilo Pianini and contributors + * Copyright (C) 2010-2022, Danilo Pianini and contributors * listed, for each module, in the respective subproject's build.gradle.kts file. * * This file is part of Alchemist, and is distributed under the terms of the @@ -7,14 +7,12 @@ * as described in the file LICENSE in the Alchemist distribution's top directory. */ -package it.unibo.alchemist.model.interfaces +package it.unibo.alchemist.model.interfaces.properties import it.unibo.alchemist.model.implementations.positions.Euclidean2DPosition import it.unibo.alchemist.model.interfaces.geometry.euclidean2d.Euclidean2DTransformation /** - * A pedestrian with cognitive characteristics that lives in a bidimensional environment. + * A node's ability to occupy space in a 2D space. */ -interface CognitivePedestrian2D : - CognitivePedestrian, - Pedestrian2D +typealias AreaProperty = OccupiesSpaceProperty diff --git a/alchemist-euclidean-geometry/src/main/kotlin/it/unibo/alchemist/model/interfaces/nodes/NodeWithShape.kt b/alchemist-euclidean-geometry/src/main/kotlin/it/unibo/alchemist/model/interfaces/properties/OccupiesSpaceProperty.kt similarity index 54% rename from alchemist-euclidean-geometry/src/main/kotlin/it/unibo/alchemist/model/interfaces/nodes/NodeWithShape.kt rename to alchemist-euclidean-geometry/src/main/kotlin/it/unibo/alchemist/model/interfaces/properties/OccupiesSpaceProperty.kt index 9aed086383..33c754bba4 100644 --- a/alchemist-euclidean-geometry/src/main/kotlin/it/unibo/alchemist/model/interfaces/nodes/NodeWithShape.kt +++ b/alchemist-euclidean-geometry/src/main/kotlin/it/unibo/alchemist/model/interfaces/properties/OccupiesSpaceProperty.kt @@ -1,25 +1,25 @@ /* - * Copyright (C) 2010-2020, Danilo Pianini and contributors - * listed in the main project's alchemist/build.gradle.kts file. + * Copyright (C) 2010-2022, Danilo Pianini and contributors + * listed, for each module, in the respective subproject's build.gradle.kts file. * * This file is part of Alchemist, and is distributed under the terms of the * GNU General Public License, with a linking exception, * as described in the file LICENSE in the Alchemist distribution's top directory. */ -package it.unibo.alchemist.model.interfaces.nodes +package it.unibo.alchemist.model.interfaces.properties -import it.unibo.alchemist.model.interfaces.Node +import it.unibo.alchemist.model.interfaces.NodeProperty import it.unibo.alchemist.model.interfaces.geometry.GeometricShape import it.unibo.alchemist.model.interfaces.geometry.GeometricTransformation import it.unibo.alchemist.model.interfaces.geometry.Vector /** - * A node with a [shape]. + * A node's ability to have a [Shape]. */ -interface NodeWithShape, A : GeometricTransformation> : Node { +interface OccupiesSpaceProperty, A : GeometricTransformation> : NodeProperty { /** - * The shape of the node. + * The node's shape. */ val shape: GeometricShape } diff --git a/alchemist-euclidean-geometry/src/test/java/it/unibo/alchemist/test/TestContinuous2DEnvironment.java b/alchemist-euclidean-geometry/src/test/java/it/unibo/alchemist/test/TestContinuous2DEnvironment.java index 7db0909ef5..acd579b0da 100644 --- a/alchemist-euclidean-geometry/src/test/java/it/unibo/alchemist/test/TestContinuous2DEnvironment.java +++ b/alchemist-euclidean-geometry/src/test/java/it/unibo/alchemist/test/TestContinuous2DEnvironment.java @@ -10,8 +10,9 @@ import it.unibo.alchemist.SupportedIncarnations; import it.unibo.alchemist.model.implementations.environments.Continuous2DEnvironment; import it.unibo.alchemist.model.implementations.linkingrules.NoLinks; -import it.unibo.alchemist.model.implementations.nodes.IntNode; +import it.unibo.alchemist.model.implementations.nodes.GenericNode; import it.unibo.alchemist.model.implementations.positions.Euclidean2DPosition; +import it.unibo.alchemist.model.interfaces.Environment; import it.unibo.alchemist.model.interfaces.Incarnation; import it.unibo.alchemist.model.interfaces.Node; import org.junit.jupiter.api.BeforeEach; @@ -47,6 +48,18 @@ public void setUp() { environment.setLinkingRule(new NoLinks<>()); } + private Node createIntNode( + final Incarnation incarnation, + final Environment environment + ) { + return new GenericNode<>(incarnation, environment) { + @Override + public Integer createT() { + return 0; + } + }; + } + /** * Test size initialization and change. */ @@ -54,11 +67,11 @@ public void setUp() { void testEnvironmentSize() { assertEquals(0, environment.getNodeCount()); assertArrayEquals(ZEROS, environment.getSize(), TOLERANCE); - environment.addNode(new IntNode(environment), new Euclidean2DPosition(P2_3)); + environment.addNode(createIntNode(INCARNATION, environment), new Euclidean2DPosition(P2_3)); assertArrayEquals(ZEROS, environment.getSize(), TOLERANCE); - environment.addNode(new IntNode(environment), new Euclidean2DPosition(P2_2)); + environment.addNode(createIntNode(INCARNATION, environment), new Euclidean2DPosition(P2_2)); assertArrayEquals(new double[]{0, 1}, environment.getSize(), TOLERANCE); - environment.addNode(new IntNode(environment), new Euclidean2DPosition(ZEROS)); + environment.addNode(createIntNode(INCARNATION, environment), new Euclidean2DPosition(ZEROS)); assertArrayEquals(P2_3, environment.getSize(), TOLERANCE); } @@ -70,11 +83,11 @@ void testEnvironmentOffset() { assertEquals(0, environment.getNodeCount()); assertTrue(Double.isNaN(environment.getOffset()[0])); assertTrue(Double.isNaN(environment.getOffset()[1])); - environment.addNode(new IntNode(environment), new Euclidean2DPosition(P2_3)); + environment.addNode(createIntNode(INCARNATION, environment), new Euclidean2DPosition(P2_3)); assertArrayEquals(P2_3, environment.getOffset(), TOLERANCE); - environment.addNode(new IntNode(environment), new Euclidean2DPosition(P2_2)); + environment.addNode(createIntNode(INCARNATION, environment), new Euclidean2DPosition(P2_2)); assertArrayEquals(P2_2, environment.getOffset(), TOLERANCE); - environment.addNode(new IntNode(environment), new Euclidean2DPosition(ZEROS)); + environment.addNode(createIntNode(INCARNATION, environment), new Euclidean2DPosition(ZEROS)); assertArrayEquals(ZEROS, environment.getOffset(), TOLERANCE); } @@ -84,7 +97,7 @@ void testEnvironmentOffset() { @Test void testNegativeRangeQuery() { assertEquals(0, environment.getNodeCount()); - final Node dummy = new IntNode(environment); + final Node dummy = createIntNode(INCARNATION, environment); environment.addNode(dummy, new Euclidean2DPosition(ZEROS)); try { environment.getNodesWithinRange(dummy, -1); @@ -100,8 +113,8 @@ void testNegativeRangeQuery() { @Test void testZeroRangeQuery() { assertEquals(0, environment.getNodeCount()); - final Node dummy = new IntNode(environment); - final Node dummy2 = new IntNode(environment); + final Node dummy = createIntNode(INCARNATION, environment); + final Node dummy2 = createIntNode(INCARNATION, environment); environment.addNode(dummy, new Euclidean2DPosition(ZEROS)); environment.addNode(dummy2, new Euclidean2DPosition(ZEROS)); assertEquals(2, environment.getNodeCount()); diff --git a/alchemist-euclidean-geometry/src/test/java/it/unibo/alchemist/test/TestContinuous2DObstacle.java b/alchemist-euclidean-geometry/src/test/java/it/unibo/alchemist/test/TestContinuous2DObstacle.java index ba9f54f776..c5be37e1ef 100644 --- a/alchemist-euclidean-geometry/src/test/java/it/unibo/alchemist/test/TestContinuous2DObstacle.java +++ b/alchemist-euclidean-geometry/src/test/java/it/unibo/alchemist/test/TestContinuous2DObstacle.java @@ -10,13 +10,16 @@ import it.unibo.alchemist.SupportedIncarnations; import it.unibo.alchemist.model.implementations.environments.Continuous2DObstacles; import it.unibo.alchemist.model.implementations.linkingrules.NoLinks; -import it.unibo.alchemist.model.implementations.nodes.IntNode; +import it.unibo.alchemist.model.implementations.nodes.GenericNode; import it.unibo.alchemist.model.implementations.obstacles.RectObstacle2D; import it.unibo.alchemist.model.implementations.positions.Euclidean2DPosition; -import org.apache.commons.math3.util.FastMath; +import it.unibo.alchemist.model.interfaces.Environment; +import it.unibo.alchemist.model.interfaces.Incarnation; +import it.unibo.alchemist.model.interfaces.Node; import org.junit.jupiter.api.Test; import static org.junit.jupiter.api.Assertions.assertEquals; +import static org.apache.commons.math3.util.FastMath.nextAfter; /** */ @@ -25,39 +28,57 @@ class TestContinuous2DObstacle { private static final RectObstacle2D R1021 = new RectObstacle2D<>(1, 0, 1, 1); private static final RectObstacle2D R0527 = new RectObstacle2D<>(0, 5, 2, -2); + private Node createIntNode( + final Incarnation incarnation, + final Environment environment + ) { + return new GenericNode<>(incarnation, environment) { + @Override + public Integer createT() { + return 0; + } + }; + } + /** * */ @Test void test() { final var incarnation = SupportedIncarnations.get("protelis").orElseThrow(); - final Continuous2DObstacles env = new Continuous2DObstacles<>(incarnation); - env.setLinkingRule(new NoLinks<>()); - env.addObstacle(R1021); - env.addObstacle(R0527); - - assertEquals(new Euclidean2DPosition(FastMath.nextAfter(1.0, 0.0), FastMath.nextAfter(1.0, 0.0)), env.next(0, 0, 1, 1)); - assertEquals(new Euclidean2DPosition(0, 0), env.next(1, 1, 0, 0)); - assertEquals(new Euclidean2DPosition(FastMath.nextAfter(1.0, 0.0), FastMath.nextAfter(0.5, 0.0)), env.next(0, 0, 2, 1)); - - env.addNode(new IntNode(env), new Euclidean2DPosition(0, 0)); - assertEquals(env.getNodeCount(), 1); - env.addNode(new IntNode(env), new Euclidean2DPosition(1, 1)); - assertEquals(env.getNodeCount(), 1); + final Continuous2DObstacles environment = new Continuous2DObstacles<>(incarnation); + environment.setLinkingRule(new NoLinks<>()); + environment.addObstacle(R1021); + environment.addObstacle(R0527); + + assertEquals( + new Euclidean2DPosition(nextAfter(1.0, 0.0), nextAfter(1.0, 0.0)), + environment.next(0, 0, 1, 1) + ); + assertEquals(new Euclidean2DPosition(0, 0), environment.next(1, 1, 0, 0)); + assertEquals( + new Euclidean2DPosition(nextAfter(1.0, 0.0), nextAfter(0.5, 0.0)), + environment.next(0, 0, 2, 1) + ); + + environment.addNode(createIntNode(incarnation, environment), new Euclidean2DPosition(0, 0)); + assertEquals(environment.getNodeCount(), 1); + environment.addNode(createIntNode(incarnation, environment), new Euclidean2DPosition(1, 1)); + assertEquals(environment.getNodeCount(), 1); // CHECKSTYLE: MagicNumber OFF - env.addNode(new IntNode(env), new Euclidean2DPosition(1.5, 0.5)); - assertEquals(env.getNodeCount(), 1); - env.addNode(new IntNode(env), new Euclidean2DPosition(1, 5)); - assertEquals(env.getNodeCount(), 1); - env.addNode(new IntNode(env), new Euclidean2DPosition(1, 2.999)); - assertEquals(env.getNodeCount(), 2); - assertEquals(env.getObstaclesInRange(0d, 0d, 100d).size(), 2); - assertEquals(env.getObstaclesInRange(0d, 0d, 1d).size(), 1); - assertEquals(env.getObstaclesInRange(0d, 0d, 1d).get(0), R1021); - assertEquals(env.getObstaclesInRange(1d, 5d, 1d).size(), 1); - assertEquals(env.getObstaclesInRange(1d, 5d, 1d).get(0), R0527); + environment.addNode(createIntNode(incarnation, environment), new Euclidean2DPosition(1.5, 0.5)); + assertEquals(environment.getNodeCount(), 1); + environment.addNode(createIntNode(incarnation, environment), new Euclidean2DPosition(1, 5)); + assertEquals(environment.getNodeCount(), 1); + environment.addNode(createIntNode(incarnation, environment), new Euclidean2DPosition(1, 2.999)); + assertEquals(environment.getNodeCount(), 2); + assertEquals(environment.getObstaclesInRange(0d, 0d, 100d).size(), 2); + assertEquals(environment.getObstaclesInRange(0d, 0d, 1d).size(), 1); + assertEquals(environment.getObstaclesInRange(0d, 0d, 1d).get(0), R1021); + assertEquals(environment.getObstaclesInRange(1d, 5d, 1d).size(), 1); + assertEquals(environment.getObstaclesInRange(1d, 5d, 1d).get(0), R0527); // CHECKSTYLE: MagicNumber ON - assertEquals(env.getObstaclesInRange(0d, 0d, 0.5d).size(), 0); + assertEquals(environment.getObstaclesInRange(0d, 0d, 0.5d).size(), 0); } } diff --git a/alchemist-euclidean-geometry/src/test/kotlin/it/unibo/alchemist/test/TestEuclideanPhysics2DEnvironment.kt b/alchemist-euclidean-geometry/src/test/kotlin/it/unibo/alchemist/test/TestEuclideanPhysics2DEnvironment.kt index ecd13f541b..9a1664a484 100644 --- a/alchemist-euclidean-geometry/src/test/kotlin/it/unibo/alchemist/test/TestEuclideanPhysics2DEnvironment.kt +++ b/alchemist-euclidean-geometry/src/test/kotlin/it/unibo/alchemist/test/TestEuclideanPhysics2DEnvironment.kt @@ -9,78 +9,93 @@ import io.kotest.matchers.shouldBe import it.unibo.alchemist.SupportedIncarnations import it.unibo.alchemist.model.implementations.environments.Continuous2DEnvironment import it.unibo.alchemist.model.implementations.linkingrules.NoLinks -import it.unibo.alchemist.model.implementations.nodes.CircleNode +import it.unibo.alchemist.model.implementations.nodes.GenericNode import it.unibo.alchemist.model.implementations.positions.Euclidean2DPosition +import it.unibo.alchemist.model.implementations.properties.CircularArea +import it.unibo.alchemist.model.interfaces.Incarnation +import it.unibo.alchemist.model.interfaces.Node import it.unibo.alchemist.model.interfaces.environments.Physics2DEnvironment -import it.unibo.alchemist.model.interfaces.geometry.euclidean2d.Euclidean2DTransformation -import it.unibo.alchemist.model.interfaces.nodes.NodeWithShape import it.unibo.alchemist.test.TestEuclidean2DShapeFactory.Companion.DEFAULT_SHAPE_SIZE import org.danilopianini.lang.MathUtils +import it.unibo.alchemist.model.interfaces.Node.Companion.asProperty +import it.unibo.alchemist.model.interfaces.properties.AreaProperty internal infix fun Double.shouldBeFuzzy(other: Double): Unit = MathUtils.fuzzyEquals(this, other) shouldBe true class TestEuclideanPhysics2DEnvironment : StringSpec() { - private lateinit var env: Physics2DEnvironment - private lateinit var node1: NodeWithShape - private lateinit var node2: NodeWithShape - private lateinit var node3: NodeWithShape + private lateinit var environment: Physics2DEnvironment + private lateinit var node1: Node + private lateinit var node2: Node + private lateinit var node3: Node + + private fun createCircleNode( + incarnation: Incarnation, + environment: Physics2DEnvironment, + radius: Double, + ) = GenericNode(incarnation, environment).apply { + addProperty(CircularArea(environment, this, radius)) + } + + private fun getNodeRadius(node: Node): Double = + node.asProperty>().shape.radius override fun beforeTest(testCase: TestCase) { super.beforeTest(testCase) - env = Continuous2DEnvironment(SupportedIncarnations.get("protelis").get()) - env.linkingRule = NoLinks() - node1 = CircleNode(env, DEFAULT_SHAPE_SIZE / 2) - node2 = CircleNode(env, DEFAULT_SHAPE_SIZE / 2) - node3 = CircleNode(env, DEFAULT_SHAPE_SIZE / 2) + environment = Continuous2DEnvironment(SupportedIncarnations.get("protelis").get()) + val incarnation = SupportedIncarnations.get("protelis").orElseThrow() + environment.linkingRule = NoLinks() + node1 = createCircleNode(incarnation, environment, DEFAULT_SHAPE_SIZE / 2) + node2 = createCircleNode(incarnation, environment, DEFAULT_SHAPE_SIZE / 2) + node3 = createCircleNode(incarnation, environment, DEFAULT_SHAPE_SIZE / 2) } init { "Cannot add overlapping nodes" { - env.addNode(node1, Euclidean2DPosition(0.0, 0.0)) - env.addNode(node2, Euclidean2DPosition(0.0, 0.0)) - env.addNode(node3, Euclidean2DPosition(3 * DEFAULT_SHAPE_SIZE, 0.0)) - env.nodes shouldContainExactlyInAnyOrder listOf(node1, node3) + environment.addNode(node1, Euclidean2DPosition(0.0, 0.0)) + environment.addNode(node2, Euclidean2DPosition(0.0, 0.0)) + environment.addNode(node3, Euclidean2DPosition(3 * DEFAULT_SHAPE_SIZE, 0.0)) + environment.nodes shouldContainExactlyInAnyOrder listOf(node1, node3) } "Cannot move into other nodes" { - env.addNode(node1, Euclidean2DPosition(0.0, 0.0)) - env.addNode(node2, Euclidean2DPosition(3 * DEFAULT_SHAPE_SIZE, 0.0)) - env.moveNodeToPosition(node2, env.getPosition(node1)) - env.getPosition(node1).distanceTo(env.getPosition(node2)) shouldBeFuzzy - node1.shape.radius + node2.shape.radius + environment.addNode(node1, Euclidean2DPosition(0.0, 0.0)) + environment.addNode(node2, Euclidean2DPosition(3 * DEFAULT_SHAPE_SIZE, 0.0)) + environment.moveNodeToPosition(node2, environment.getPosition(node1)) + val distance = environment.getPosition(node1).distanceTo(environment.getPosition(node2)) + distance shouldBeFuzzy getNodeRadius(node1) + getNodeRadius(node2) } "Get nodes within a small shape" { - env.addNode(node1, Euclidean2DPosition(0.0, 0.0)) - env.addNode(node2, Euclidean2DPosition(2 * DEFAULT_SHAPE_SIZE, 0.0)) - val shape = env.shapeFactory.rectangle(DEFAULT_SHAPE_SIZE / 2, DEFAULT_SHAPE_SIZE / 2) - env.getNodesWithin(shape) shouldContainExactly listOf(node1) + environment.addNode(node1, Euclidean2DPosition(0.0, 0.0)) + environment.addNode(node2, Euclidean2DPosition(2 * DEFAULT_SHAPE_SIZE, 0.0)) + val shape = environment.shapeFactory.rectangle(DEFAULT_SHAPE_SIZE / 2, DEFAULT_SHAPE_SIZE / 2) + environment.getNodesWithin(shape) shouldContainExactly listOf(node1) } "Get nodes within a big shape" { - env.addNode(node1, Euclidean2DPosition(0.0, 0.0)) - env.addNode(node2, Euclidean2DPosition(2 * DEFAULT_SHAPE_SIZE, 0.0)) - env.addNode(node3, Euclidean2DPosition(30 * DEFAULT_SHAPE_SIZE, 0.0)) - val shape = env.shapeFactory.rectangle(3.1 * DEFAULT_SHAPE_SIZE, DEFAULT_SHAPE_SIZE) - env.getNodesWithin(shape) shouldContainExactlyInAnyOrder listOf(node1, node2) + environment.addNode(node1, Euclidean2DPosition(0.0, 0.0)) + environment.addNode(node2, Euclidean2DPosition(2 * DEFAULT_SHAPE_SIZE, 0.0)) + environment.addNode(node3, Euclidean2DPosition(30 * DEFAULT_SHAPE_SIZE, 0.0)) + val shape = environment.shapeFactory.rectangle(3.1 * DEFAULT_SHAPE_SIZE, DEFAULT_SHAPE_SIZE) + environment.getNodesWithin(shape) shouldContainExactlyInAnyOrder listOf(node1, node2) } "Node is moved to the farthest position reachable when its path is occupied by others" { - env.addNode(node1, coords(2.0, 2.0)) - env.addNode(node2, coords(6.0, 2.0)) + environment.addNode(node1, coords(2.0, 2.0)) + environment.addNode(node2, coords(6.0, 2.0)) val target = coords(8.0, 2.0) - env.moveNodeToPosition(node1, target) - env.getPosition(node1).distanceTo(target) shouldBeFuzzy - env.getPosition(node2).distanceTo(target) + node1.shape.radius + node2.shape.radius + environment.moveNodeToPosition(node1, target) + environment.getPosition(node1).distanceTo(target) shouldBeFuzzy + environment.getPosition(node2).distanceTo(target) + getNodeRadius(node1) + getNodeRadius(node2) } "Node is moved to the farthest position reachable when its path is occupied by others 2" { - env.addNode(node1, coords(2.0, 2.0)) - env.addNode(node2, coords(8.0, 1.0)) - env.addNode(node3, coords(8.0, 2.5)) + environment.addNode(node1, coords(2.0, 2.0)) + environment.addNode(node2, coords(8.0, 1.0)) + environment.addNode(node3, coords(8.0, 2.5)) val target = coords(8.0, 2.0) - env.moveNodeToPosition(node1, target) - env.getPosition(node1).distanceTo(target) shouldBeGreaterThan node1.shape.radius + environment.moveNodeToPosition(node1, target) + environment.getPosition(node1).distanceTo(target) shouldBeGreaterThan getNodeRadius(node1) } } } diff --git a/alchemist-euclidean-geometry/src/test/kotlin/it/unibo/alchemist/test/TestNeighborhood.kt b/alchemist-euclidean-geometry/src/test/kotlin/it/unibo/alchemist/test/TestNeighborhood.kt index c83bb44cdf..59657302b9 100644 --- a/alchemist-euclidean-geometry/src/test/kotlin/it/unibo/alchemist/test/TestNeighborhood.kt +++ b/alchemist-euclidean-geometry/src/test/kotlin/it/unibo/alchemist/test/TestNeighborhood.kt @@ -10,8 +10,11 @@ package it.unibo.alchemist.test import it.unibo.alchemist.SupportedIncarnations import it.unibo.alchemist.model.implementations.environments.Continuous2DEnvironment import it.unibo.alchemist.model.implementations.neighborhoods.Neighborhoods -import it.unibo.alchemist.model.implementations.nodes.IntNode +import it.unibo.alchemist.model.implementations.nodes.GenericNode import it.unibo.alchemist.model.implementations.positions.Euclidean2DPosition +import it.unibo.alchemist.model.interfaces.Environment +import it.unibo.alchemist.model.interfaces.Incarnation +import it.unibo.alchemist.model.interfaces.Node import org.junit.jupiter.api.Assertions import org.junit.jupiter.api.Test @@ -19,6 +22,13 @@ import org.junit.jupiter.api.Test * Tests pertaining to the [it.unibo.alchemist.model.implementations.neighborhoods] package. */ class TestNeighborhood { + private fun createIntNode( + incarnation: Incarnation, + environment: Environment + ): Node = object : GenericNode(incarnation, environment) { + override fun createT(): Int = 0 + } + /** * Tests whether the clone function of the * [it.unibo.alchemist.model.implementations.neighborhoods.SimpleNeighborhood] class works as expected. @@ -26,10 +36,10 @@ class TestNeighborhood { @Test fun testClone() { val incarnation = SupportedIncarnations.get("protelis").orElseThrow() - val env = Continuous2DEnvironment(incarnation) - val n1 = IntNode(env) - val n2 = IntNode(env) - val neigh1 = Neighborhoods.make(env, n1, mutableListOf(n2)) + val environment = Continuous2DEnvironment(incarnation) + val n1 = createIntNode(incarnation, environment) + val n2 = createIntNode(incarnation, environment) + val neigh1 = Neighborhoods.make(environment, n1, mutableListOf(n2)) val neigh2 = neigh1.remove(n2) Assertions.assertEquals(0, neigh2.size()) Assertions.assertTrue(neigh1.neighbors.contains(n2)) diff --git a/alchemist-euclidean-geometry/src/test/resources/levywalk.yml b/alchemist-euclidean-geometry/src/test/resources/levywalk.yml index cf50ba1da4..1ef9816a4c 100644 --- a/alchemist-euclidean-geometry/src/test/resources/levywalk.yml +++ b/alchemist-euclidean-geometry/src/test/resources/levywalk.yml @@ -3,9 +3,9 @@ incarnation: protelis deployments: - type: Circle parameters: [50, 0, 0, 200] - nodes: - type: CircleNode - parameters: [1] + properties: + - type: CircularArea + parameters: [1] programs: - time-distribution: 1 type: ChemicalReaction diff --git a/alchemist-fxui/src/main/kotlin/it/unibo/alchemist/wormhole/implementation/LeafletMapWormhole.kt b/alchemist-fxui/src/main/kotlin/it/unibo/alchemist/wormhole/implementation/LeafletMapWormhole.kt index 24111d235a..4d4aa04d03 100644 --- a/alchemist-fxui/src/main/kotlin/it/unibo/alchemist/wormhole/implementation/LeafletMapWormhole.kt +++ b/alchemist-fxui/src/main/kotlin/it/unibo/alchemist/wormhole/implementation/LeafletMapWormhole.kt @@ -69,11 +69,11 @@ class LeafletMapWormhole( override fun optimalZoom() { zoom = CustomLeafletMapView.MAX_ZOOM_VALUE.toDouble() @Suppress("UNCHECKED_CAST") - val env = environment as Environment + val environment = environment as Environment while ( zoom > CustomLeafletMapView.MIN_ZOOM_VALUE && - !env.nodes - .map(env::getPosition) + !environment.nodes + .map(environment::getPosition) .map(::getViewPoint) .all(::isInsideView) ) { diff --git a/alchemist-grid/src/main/java/it/unibo/alchemist/grid/util/WorkingDirectory.java b/alchemist-grid/src/main/java/it/unibo/alchemist/grid/util/WorkingDirectory.java index ad08835f4e..54298d2879 100644 --- a/alchemist-grid/src/main/java/it/unibo/alchemist/grid/util/WorkingDirectory.java +++ b/alchemist-grid/src/main/java/it/unibo/alchemist/grid/util/WorkingDirectory.java @@ -14,11 +14,11 @@ import java.nio.charset.StandardCharsets; import java.util.Map; import java.util.Map.Entry; +import java.nio.file.Files; +import java.util.Objects; import org.apache.commons.io.FileUtils; -import com.google.common.io.Files; - /** * Class that manage a temp local working directory. * @@ -31,7 +31,13 @@ public final class WorkingDirectory implements AutoCloseable { * Create new local temp working directory. */ public WorkingDirectory() { - this.directory = Files.createTempDir(); + File tryDirectory; + try { + tryDirectory = Files.createTempDirectory("alchemist").toFile(); + } catch (IOException e) { + throw new IllegalStateException("An error occure while attempting to create a temporary directory", e); + } + this.directory = Objects.requireNonNull(tryDirectory); } /** diff --git a/alchemist-implementationbase/src/main/java/it/unibo/alchemist/model/implementations/actions/AbstractAction.java b/alchemist-implementationbase/src/main/java/it/unibo/alchemist/model/implementations/actions/AbstractAction.java index 186bbb6715..df3aa96254 100644 --- a/alchemist-implementationbase/src/main/java/it/unibo/alchemist/model/implementations/actions/AbstractAction.java +++ b/alchemist-implementationbase/src/main/java/it/unibo/alchemist/model/implementations/actions/AbstractAction.java @@ -60,14 +60,14 @@ protected final void declareDependencyTo(final Dependency m) { } /** - * @param m + * @param molecule * the molecule * @return An {@link Optional} with the value of concentration, or an empty * {@link Optional} if the molecule if * {@link Node#getConcentration(Molecule)} returns null */ - protected final Optional getConcentration(final Molecule m) { - return Optional.ofNullable(getNode().getConcentration(m)); + protected final Optional getConcentration(final Molecule molecule) { + return Optional.ofNullable(getNode().getConcentration(molecule)); } /** @@ -89,19 +89,19 @@ protected Node getNode() { } /** - * @param m - * the molecule - * @return true if the local node contains the molecule + * Checks if the molecule is contained in this node. + * @param molecule the molecule + * + * @return true if the local node contains the molecule. */ - protected final boolean nodeContains(final Molecule m) { - return getNode().contains(m); + protected final boolean nodeContains(final Molecule molecule) { + return getNode().contains(molecule); } /** * Deletes a molecule entirely in the local node. * - * @param molecule - * molecule + * @param molecule molecule */ protected final void removeConcentration(final Molecule molecule) { getNode().removeConcentration(Objects.requireNonNull(molecule, "The molecule can not be null")); diff --git a/alchemist-implementationbase/src/main/java/it/unibo/alchemist/model/implementations/actions/AbstractConfigurableMoveNode.java b/alchemist-implementationbase/src/main/java/it/unibo/alchemist/model/implementations/actions/AbstractConfigurableMoveNode.java index 3729297a7a..677fe3c77c 100644 --- a/alchemist-implementationbase/src/main/java/it/unibo/alchemist/model/implementations/actions/AbstractConfigurableMoveNode.java +++ b/alchemist-implementationbase/src/main/java/it/unibo/alchemist/model/implementations/actions/AbstractConfigurableMoveNode.java @@ -97,9 +97,9 @@ public final P getNextPosition() { resetRoute(); } double maxWalk = speedSelectionStrategy.getNodeMovementLength(end); - final Environment env = getEnvironment(); + final Environment environment = getEnvironment(); final Node node = getNode(); - P curPos = env.getPosition(node); + P curPos = environment.getPosition(node); if (curPos.distanceTo(end) <= maxWalk) { final P destination = end; end = targetSelectionStrategy.getTarget(); diff --git a/alchemist-implementationbase/src/main/java/it/unibo/alchemist/model/implementations/linkingrules/ClosestN.java b/alchemist-implementationbase/src/main/java/it/unibo/alchemist/model/implementations/linkingrules/ClosestN.java index fd6ac03c1f..71db910964 100644 --- a/alchemist-implementationbase/src/main/java/it/unibo/alchemist/model/implementations/linkingrules/ClosestN.java +++ b/alchemist-implementationbase/src/main/java/it/unibo/alchemist/model/implementations/linkingrules/ClosestN.java @@ -116,26 +116,26 @@ && closestN(node, environment).anyMatch(center::equals) .collect(Collectors.toCollection(LinkedHashSet::new))); } - private Stream> closestN(final Node center, final Environment env) { + private Stream> closestN(final Node center, final Environment environment) { if (!nodeIsEnabled(center)) { return Stream.empty(); } - double currentRange = getRange(env, center); + double currentRange = getRange(environment, center); Set> inRange; - final double maxRange = Doubles.max(env.getSizeInDistanceUnits()) * 2; + final double maxRange = Doubles.max(environment.getSizeInDistanceUnits()) * 2; do { - inRange = (env.getNodeCount() > n && currentRange < maxRange - ? nodesInRange(env, center, currentRange).stream() - : env.getNodes().stream()) + inRange = (environment.getNodeCount() > n && currentRange < maxRange + ? nodesInRange(environment, center, currentRange).stream() + : environment.getNodes().stream()) .filter(n -> !n.equals(center) && nodeIsEnabled(n)) .collect(Collectors.toCollection(LinkedHashSet::new)); currentRange *= 2; - } while (inRange.size() < n && inRange.size() < env.getNodeCount() - 1 && currentRange < maxRange * 2); + } while (inRange.size() < n && inRange.size() < environment.getNodeCount() - 1 && currentRange < maxRange * 2); if (inRange.isEmpty()) { return Stream.empty(); } final MinMaxPriorityQueue>> closestN = inRange.stream() - .map(node -> new Tuple2<>(env.getDistanceBetweenNodes(center, node), node)) + .map(node -> new Tuple2<>(environment.getDistanceBetweenNodes(center, node), node)) .collect(new SmallestN<>(n)); final var farthestNode = closestN.peekLast(); if (farthestNode == null) { @@ -153,13 +153,17 @@ private Stream> closestN(final Node center, final Environment e } /** - * @param env the {@link Environment} + * The set of nodes within the comunication range. + * @param environment the {@link Environment} * @param node the {@link Node} * @param range the communication range * @return the set of nodes within the communication range */ - protected final Set> nodesInRange(final Environment env, final Node node, final double range) { - return env.getNodesWithinRange(node, range); + protected final Set> nodesInRange( + final Environment environment, + final Node node, final double range + ) { + return environment.getNodesWithinRange(node, range); } /** @@ -176,28 +180,30 @@ protected boolean nodeIsEnabled(final Node node) { /** * Gets the communication range of a node. * - * @param env + * @param environment * the environment * @param center * the node * @return the communication range */ - protected final double getRange(final Environment env, final Node center) { + protected final double getRange(final Environment environment, final Node center) { try { /* * Range estimation: twice the radius of a circle with an area that * would, on average, contain the number of required devices */ return ranges().get(center, () -> { - final int nodes = env.getNodeCount(); + final int nodes = environment.getNodeCount(); if (nodes < n || nodes < 10) { return Double.MAX_VALUE; } - final double[] size = env.getSizeInDistanceUnits(); + final double[] size = environment.getSizeInDistanceUnits(); final double x = size[0]; final double y = size[1]; final double density = x * y / nodes; - return Math.max(Double.MIN_VALUE, Math.min(2 * FastMath.sqrt(density / Math.PI * n), Double.MAX_VALUE)); + return Math.max(Double.MIN_VALUE, + Math.min(2 * FastMath.sqrt(density / Math.PI * n), Double.MAX_VALUE) + ); }); } catch (ExecutionException e) { throw new IllegalStateException("Couldn't compute ranges. This is most likely a bug.", e); diff --git a/alchemist-implementationbase/src/main/java/it/unibo/alchemist/model/implementations/linkingrules/ObstaclesBreakConnection.java b/alchemist-implementationbase/src/main/java/it/unibo/alchemist/model/implementations/linkingrules/ObstaclesBreakConnection.java index fddf9bcba7..600b31bcf3 100644 --- a/alchemist-implementationbase/src/main/java/it/unibo/alchemist/model/implementations/linkingrules/ObstaclesBreakConnection.java +++ b/alchemist-implementationbase/src/main/java/it/unibo/alchemist/model/implementations/linkingrules/ObstaclesBreakConnection.java @@ -38,15 +38,21 @@ public ObstaclesBreakConnection(final Double radius) { } @Override - public Neighborhood computeNeighborhood(final Node center, final Environment env) { - Neighborhood normal = super.computeNeighborhood(center, env); - if (!normal.isEmpty() && env instanceof EnvironmentWithObstacles) { - final P cp = env.getPosition(center); - final EnvironmentWithObstacles environment = (EnvironmentWithObstacles) env; - environment.intersectsObstacle(environment.getPosition(center), environment.getPosition(center)); - normal = Neighborhoods.make(env, center, StreamSupport.stream(normal.spliterator(), false) - .filter(node -> !environment.intersectsObstacle(cp, environment.getPosition(node))) - .collect(Collectors.toList())); + public Neighborhood computeNeighborhood(final Node center, final Environment environment) { + Neighborhood normal = super.computeNeighborhood(center, environment); + if (!normal.isEmpty() && environment instanceof EnvironmentWithObstacles) { + final P centerPosition = environment.getPosition(center); + final EnvironmentWithObstacles environmentWithObstacles = + (EnvironmentWithObstacles) environment; + environmentWithObstacles.intersectsObstacle( + environmentWithObstacles.getPosition(center), + environmentWithObstacles.getPosition(center) + ); + final Iterable> neighbors = StreamSupport.stream(normal.spliterator(), false) + .filter(node -> !environmentWithObstacles + .intersectsObstacle(centerPosition, environmentWithObstacles.getPosition(node)) + ).collect(Collectors.toList()); + normal = Neighborhoods.make(environmentWithObstacles, center, neighbors); } return normal; } diff --git a/alchemist-implementationbase/src/main/java/it/unibo/alchemist/model/implementations/nodes/AbstractNode.java b/alchemist-implementationbase/src/main/java/it/unibo/alchemist/model/implementations/nodes/AbstractNode.java deleted file mode 100644 index 4fdaf74c1a..0000000000 --- a/alchemist-implementationbase/src/main/java/it/unibo/alchemist/model/implementations/nodes/AbstractNode.java +++ /dev/null @@ -1,206 +0,0 @@ -/* - * Copyright (C) 2010-2019, Danilo Pianini and contributors listed in the main project's alchemist/build.gradle file. - * - * This file is part of Alchemist, and is distributed under the terms of the - * GNU General Public License, with a linking exception, - * as described in the file LICENSE in the Alchemist distribution's top directory. - */ - -package it.unibo.alchemist.model.implementations.nodes; - -import com.google.common.collect.MapMaker; -import it.unibo.alchemist.model.interfaces.Environment; -import it.unibo.alchemist.model.interfaces.Molecule; -import it.unibo.alchemist.model.interfaces.Node; -import it.unibo.alchemist.model.interfaces.Reaction; -import it.unibo.alchemist.model.interfaces.Time; -import javax.annotation.Nonnull; - -import java.util.ArrayList; -import java.util.Collections; -import java.util.Iterator; -import java.util.LinkedHashMap; -import java.util.List; -import java.util.Map; -import java.util.NoSuchElementException; -import java.util.Objects; -import java.util.Spliterator; -import java.util.concurrent.ConcurrentMap; -import java.util.concurrent.Semaphore; -import java.util.concurrent.atomic.AtomicInteger; -import java.util.function.Consumer; - - -/** - * This class realizes an abstract node. You may extend it to realize your own - * nodes. - * - * @param concentration type - */ -public abstract class AbstractNode implements Node { - - private static final long serialVersionUID = 2496775909028222278L; - private static final ConcurrentMap, AtomicInteger> IDGENERATOR = new MapMaker() - .weakKeys().makeMap(); - private static final Semaphore MUTEX = new Semaphore(1); - private final int id; - private final List> reactions = new ArrayList<>(); - private final Map molecules = new LinkedHashMap<>(); - - private static int idFromEnv(final Environment env) { - MUTEX.acquireUninterruptibly(); - AtomicInteger idgen = IDGENERATOR.get(Objects.requireNonNull(env)); - if (idgen == null) { - idgen = new AtomicInteger(); - IDGENERATOR.put(env, idgen); - } - MUTEX.release(); - return idgen.getAndIncrement(); - } - - /** - * @param env - * the environment, used to generate sequential ids for each - * environment, always starting from 0. - */ - public AbstractNode(final Environment env) { - id = idFromEnv(env); - } - - @Override - public final void addReaction(final Reaction reactionToAdd) { - reactions.add(reactionToAdd); - } - - /** - * Default implementation fails: override correctly calling the constructor. - */ - @Override - public AbstractNode cloneNode(final Time currentTime) { - throw new UnsupportedOperationException(); - } - - @Override - public final int compareTo(@Nonnull final Node other) { - if (other instanceof AbstractNode) { - if (id > ((AbstractNode) other).id) { - return 1; - } - if (id < ((AbstractNode) other).id) { - return -1; - } - } - return 0; - } - - /** - * {@inheritDoc} - */ - @Override - public boolean contains(final Molecule molecule) { - return molecules.containsKey(molecule); - } - - /** - * @return an empty concentration - */ - protected abstract T createT(); - - @Override - public final boolean equals(final Object other) { - if (other instanceof AbstractNode) { - return ((AbstractNode) other).id == id; - } - return false; - } - - @Override - public final void forEach(final Consumer> action) { - reactions.forEach(action); - } - - /** - * {@inheritDoc} - */ - @Override - public int getMoleculeCount() { - return molecules.size(); - } - - /** - * {@inheritDoc} - */ - @Override - public T getConcentration(final Molecule molecule) { - final T res = molecules.get(molecule); - if (res == null) { - return createT(); - } - return res; - } - - /** - * {@inheritDoc} - */ - @Override - public Map getContents() { - return Collections.unmodifiableMap(molecules); - } - - @Override - public final int getId() { - return id; - } - - @Override - public final List> getReactions() { - return Collections.unmodifiableList(reactions); - } - - @Override - public final int hashCode() { - return id; - } - - @Override - public final Iterator> iterator() { - return reactions.iterator(); - } - - /** - * {@inheritDoc} - */ - @Override - public void removeConcentration(final Molecule moleculeToRemove) { - if (molecules.remove(moleculeToRemove) == null) { - throw new NoSuchElementException(moleculeToRemove + " was not present in node " + getId()); - } - } - - @Override - public final void removeReaction(final Reaction reactionToRemove) { - reactions.remove(reactionToRemove); - } - - /** - * {@inheritDoc} - */ - @Override - public void setConcentration(final Molecule molecule, final T concentration) { - molecules.put(molecule, concentration); - } - - @Override - public final Spliterator> spliterator() { - return reactions.spliterator(); - } - - /** - * {@inheritDoc} - */ - @Override - public String toString() { - return molecules.toString(); - } - -} diff --git a/alchemist-implementationbase/src/main/kotlin/it/unibo/alchemist/model/implementations/linkingrules/ConnectViaAccessPoint.kt b/alchemist-implementationbase/src/main/kotlin/it/unibo/alchemist/model/implementations/linkingrules/ConnectViaAccessPoint.kt index aec0d2a4f8..9399e7420c 100644 --- a/alchemist-implementationbase/src/main/kotlin/it/unibo/alchemist/model/implementations/linkingrules/ConnectViaAccessPoint.kt +++ b/alchemist-implementationbase/src/main/kotlin/it/unibo/alchemist/model/implementations/linkingrules/ConnectViaAccessPoint.kt @@ -26,8 +26,8 @@ class ConnectViaAccessPoint>( private val Node.isAccessPoint get() = contains(accessPointId) - private fun Neighborhood.closestAccessPoint(env: Environment): Node? = - asSequence().filter { it.isAccessPoint }.minByOrNull { env.getDistanceBetweenNodes(center, it) } + private fun Neighborhood.closestAccessPoint(environment: Environment): Node? = + asSequence().filter { it.isAccessPoint }.minByOrNull { environment.getDistanceBetweenNodes(center, it) } override fun computeNeighborhood(center: Node, environment: Environment): Neighborhood = super.computeNeighborhood(center, environment).run { diff --git a/alchemist-implementationbase/src/main/kotlin/it/unibo/alchemist/model/implementations/linkingrules/FullyConnected.kt b/alchemist-implementationbase/src/main/kotlin/it/unibo/alchemist/model/implementations/linkingrules/FullyConnected.kt index 688125fa76..0997643d1a 100644 --- a/alchemist-implementationbase/src/main/kotlin/it/unibo/alchemist/model/implementations/linkingrules/FullyConnected.kt +++ b/alchemist-implementationbase/src/main/kotlin/it/unibo/alchemist/model/implementations/linkingrules/FullyConnected.kt @@ -23,7 +23,7 @@ class FullyConnected> : LinkingRule { override fun isLocallyConsistent() = true override fun computeNeighborhood(center: Node, environment: Environment) = object : Neighborhood { - override fun contains(n: Node?) = n != center + override fun contains(node: Node?) = node != center override fun getCenter() = center diff --git a/alchemist-implementationbase/src/main/kotlin/it/unibo/alchemist/model/implementations/neighborhoods/Neighborhoods.kt b/alchemist-implementationbase/src/main/kotlin/it/unibo/alchemist/model/implementations/neighborhoods/Neighborhoods.kt index 6d448f8e43..1f0aff649e 100644 --- a/alchemist-implementationbase/src/main/kotlin/it/unibo/alchemist/model/implementations/neighborhoods/Neighborhoods.kt +++ b/alchemist-implementationbase/src/main/kotlin/it/unibo/alchemist/model/implementations/neighborhoods/Neighborhoods.kt @@ -19,16 +19,16 @@ class Neighborhoods private constructor() { /** * Creates a [SimpleNeighborhood]. * - * @param env The environment of the neighborhood. + * @param environment The environment of the neighborhood. * @param center The center of the neighborhood. * @param neighbors The neighbors in the neighborhood, defaults to empty. * * @return The newly created [SimpleNeighborhood]. */ @JvmStatic @JvmOverloads fun > make( - env: Environment, + environment: Environment, center: Node, neighbors: Iterable> = emptyList() - ) = SimpleNeighborhood(env, center, neighbors) + ) = SimpleNeighborhood(environment, center, neighbors) } } diff --git a/alchemist-implementationbase/src/main/kotlin/it/unibo/alchemist/model/implementations/neighborhoods/SimpleNeighborhood.kt b/alchemist-implementationbase/src/main/kotlin/it/unibo/alchemist/model/implementations/neighborhoods/SimpleNeighborhood.kt index 4163a0f329..54c8d84565 100644 --- a/alchemist-implementationbase/src/main/kotlin/it/unibo/alchemist/model/implementations/neighborhoods/SimpleNeighborhood.kt +++ b/alchemist-implementationbase/src/main/kotlin/it/unibo/alchemist/model/implementations/neighborhoods/SimpleNeighborhood.kt @@ -27,14 +27,14 @@ class SimpleNeighborhood> private constructor( ) : Neighborhood { internal constructor( - env: Environment, + environment: Environment, center: Node, neighbors: Iterable> - ) : this(env, center, ImmutableListSet.Builder>().addAll(neighbors).build()) + ) : this(environment, center, ImmutableListSet.Builder>().addAll(neighbors).build()) override fun clone() = SimpleNeighborhood(environment, center, ArrayListSet(neighbors)) - override fun contains(n: Node?) = neighbors.contains(n) + override fun contains(node: Node?) = neighbors.contains(node) override fun getCenter() = center diff --git a/alchemist-implementationbase/src/main/kotlin/it/unibo/alchemist/model/implementations/nodes/GenericNode.kt b/alchemist-implementationbase/src/main/kotlin/it/unibo/alchemist/model/implementations/nodes/GenericNode.kt new file mode 100644 index 0000000000..afecc79a4c --- /dev/null +++ b/alchemist-implementationbase/src/main/kotlin/it/unibo/alchemist/model/implementations/nodes/GenericNode.kt @@ -0,0 +1,141 @@ +/* + * Copyright (C) 2010-2022, Danilo Pianini and contributors + * listed, for each module, in the respective subproject's build.gradle.kts file. + * + * This file is part of Alchemist, and is distributed under the terms of the + * GNU General Public License, with a linking exception, + * as described in the file LICENSE in the Alchemist distribution's top directory. + */ +package it.unibo.alchemist.model.implementations.nodes + +import com.google.common.collect.MapMaker +import it.unibo.alchemist.model.interfaces.NodeProperty +import it.unibo.alchemist.model.interfaces.Environment +import it.unibo.alchemist.model.interfaces.Incarnation +import it.unibo.alchemist.model.interfaces.Reaction +import it.unibo.alchemist.model.interfaces.Molecule +import it.unibo.alchemist.model.interfaces.Node +import it.unibo.alchemist.model.interfaces.Time +import java.util.ArrayList +import java.util.Collections +import java.util.LinkedHashMap +import java.util.NoSuchElementException +import java.util.Spliterator +import java.util.concurrent.Semaphore +import java.util.concurrent.atomic.AtomicInteger +import java.util.function.Consumer +import javax.annotation.Nonnull + +/** + * This class realizes an abstract node. You may extend it to realize your own + * nodes. + * + * @param concentration type + */ +open class GenericNode @JvmOverloads constructor( + /** + * simulation incarnation. + */ + val incarnation: Incarnation, + /** + * The environment in which the node is places. + */ + val environment: Environment, + override val id: Int = idFromEnv(environment), + override val reactions: MutableList> = ArrayList(), + /** + * The node's molecules. + */ + val molecules: MutableMap = LinkedHashMap(), + override val capabilities: MutableList> = ArrayList(), +) : Node { + + constructor( + environment: Environment, + ) : this(environment.incarnation, environment) + + final override fun addReaction(reactionToAdd: Reaction) { + reactions.add(reactionToAdd) + } + + override fun cloneNode(currentTime: Time): Node = GenericNode(environment).also { + this.capabilities.forEach { property -> it.addProperty(property.cloneOnNewNode(it)) } + this.contents.forEach(it::setConcentration) + this.reactions.forEach { reaction -> it.addReaction(reaction.cloneOnNewNode(it, currentTime)) } + } + + final override fun compareTo(@Nonnull other: Node): Int = id.compareTo(other.id) + + override fun contains(molecule: Molecule): Boolean = molecules.containsKey(molecule) + + /** + * @return an empty concentration + */ + protected open fun createT(): T = incarnation.createConcentration() + + final override fun equals(other: Any?): Boolean = other is Node<*> && other.id == id + + /** + * Performs an [action] for every reaction. + */ + final override fun forEach(action: Consumer>) = reactions.forEach(action) + + override fun getConcentration(molecule: Molecule): T = molecules[molecule] ?: createT() + + override val contents: Map = Collections.unmodifiableMap(molecules) + + override val moleculeCount: Int get() = molecules.size + + final override fun hashCode(): Int = id // TODO: better hashing + + final override fun iterator(): Iterator> = reactions.iterator() + + final override fun removeConcentration(moleculeToRemove: Molecule) { + if (molecules.remove(moleculeToRemove) == null) { + throw NoSuchElementException("$moleculeToRemove was not present in node $id") + } + } + + final override fun removeReaction(reactionToRemove: Reaction) { + reactions.remove(reactionToRemove) + } + + override fun setConcentration(molecule: Molecule, concentration: T) { + molecules[molecule] = concentration + } + + final override fun addProperty(nodeProperty: NodeProperty) { + if (capabilities.find { it::class == nodeProperty::class } == null) + capabilities.add(nodeProperty) + else throw IllegalArgumentException( + "This node (${this.id}) already contains a property of type ${nodeProperty::class}," + + "this may lead to an inconsistent state" + ) + } + + /** + * Returns the [reactions] [Spliterator]. + */ + final override fun spliterator(): Spliterator> = reactions.spliterator() + + override fun toString(): String = molecules.toString() + + companion object { + private const val serialVersionUID = 2496775909028222278L + + private val IDGENERATOR = MapMaker().weakKeys().makeMap, AtomicInteger>() + + private val MUTEX = Semaphore(1) + + private fun idFromEnv(environment: Environment<*, *>): Int { + MUTEX.acquireUninterruptibly() + var idgen = IDGENERATOR[environment] + if (idgen == null) { + idgen = AtomicInteger() + IDGENERATOR[environment] = idgen + } + MUTEX.release() + return idgen.getAndIncrement() + } + } +} diff --git a/alchemist-implementationbase/src/main/kotlin/it/unibo/alchemist/model/implementations/nodes/IntNode.kt b/alchemist-implementationbase/src/main/kotlin/it/unibo/alchemist/model/implementations/nodes/IntNode.kt deleted file mode 100644 index 31ac5e4166..0000000000 --- a/alchemist-implementationbase/src/main/kotlin/it/unibo/alchemist/model/implementations/nodes/IntNode.kt +++ /dev/null @@ -1,17 +0,0 @@ -/* - * Copyright (C) 2010-2019, Danilo Pianini and contributors listed in the main project's alchemist/build.gradle file. - * - * This file is part of Alchemist, and is distributed under the terms of the - * GNU General Public License, with a linking exception, - * as described in the file LICENSE in the Alchemist distribution's top directory. - */ -package it.unibo.alchemist.model.implementations.nodes - -import it.unibo.alchemist.model.interfaces.Environment - -/** - * An integer node. - */ -open class IntNode(env: Environment<*, *>) : AbstractNode(env) { - override fun createT() = 0 -} diff --git a/alchemist-incarnation-biochemistry/src/main/java/it/unibo/alchemist/model/BiochemistryIncarnation.java b/alchemist-incarnation-biochemistry/src/main/java/it/unibo/alchemist/model/BiochemistryIncarnation.java index 07abce4b02..d267c6d195 100644 --- a/alchemist-incarnation-biochemistry/src/main/java/it/unibo/alchemist/model/BiochemistryIncarnation.java +++ b/alchemist-incarnation-biochemistry/src/main/java/it/unibo/alchemist/model/BiochemistryIncarnation.java @@ -10,11 +10,11 @@ import it.unibo.alchemist.model.implementations.molecules.Biomolecule; -import it.unibo.alchemist.model.implementations.nodes.CellNodeImpl; +import it.unibo.alchemist.model.implementations.nodes.GenericNode; +import it.unibo.alchemist.model.implementations.properties.CircularCell; import it.unibo.alchemist.model.implementations.reactions.BiochemicalReactionBuilder; import it.unibo.alchemist.model.implementations.timedistributions.ExponentialTime; import it.unibo.alchemist.model.interfaces.Action; -import it.unibo.alchemist.model.interfaces.CellNode; import it.unibo.alchemist.model.interfaces.Condition; import it.unibo.alchemist.model.interfaces.Environment; import it.unibo.alchemist.model.interfaces.Incarnation; @@ -42,14 +42,18 @@ public Biomolecule createMolecule(final String s) { } @Override - public CellNode

createNode( + public Node createNode( final RandomGenerator randomGenerator, final Environment environment, - final String parameter) { + final String parameter + ) { + final Node node = new GenericNode<>(this, environment); if (parameter == null || parameter.isEmpty()) { - return new CellNodeImpl<>(environment); + node.addProperty(new CircularCell

(environment, node)); + } else { + node.addProperty(new CircularCell

(environment, node, Double.parseDouble(parameter))); } - return new CellNodeImpl<>(environment, Double.parseDouble(parameter)); + return node; } @Override @@ -115,6 +119,11 @@ public Double createConcentration(final String s) { return Double.parseDouble(s); } + @Override + public Double createConcentration() { + return 0d; + } + @Override public String toString() { return getClass().getSimpleName(); diff --git a/alchemist-incarnation-biochemistry/src/main/java/it/unibo/alchemist/model/implementations/actions/AbstractNeighborAction.java b/alchemist-incarnation-biochemistry/src/main/java/it/unibo/alchemist/model/implementations/actions/AbstractNeighborAction.java index fe4ba0ac9a..8f89162fa6 100644 --- a/alchemist-incarnation-biochemistry/src/main/java/it/unibo/alchemist/model/implementations/actions/AbstractNeighborAction.java +++ b/alchemist-incarnation-biochemistry/src/main/java/it/unibo/alchemist/model/implementations/actions/AbstractNeighborAction.java @@ -23,7 +23,7 @@ public abstract class AbstractNeighborAction extends AbstractRandomizableAction { private static final long serialVersionUID = -2287346030993830896L; - private final Environment env; + private final Environment environment; private final Node node; /** @@ -39,7 +39,7 @@ protected AbstractNeighborAction( ) { super(node, randomGenerator); this.node = node; - env = environment; + this.environment = environment; } @Override @@ -50,7 +50,7 @@ protected AbstractNeighborAction( */ @Override public void execute() { - final Neighborhood neighborhood = env.getNeighborhood(node); + final Neighborhood neighborhood = environment.getNeighborhood(node); if (!neighborhood.isEmpty()) { execute(neighborhood.getNeighborByNumber(getRandomGenerator().nextInt(neighborhood.size()))); } @@ -73,7 +73,7 @@ public final Context getContext() { * @return exposes the {@link Environment} to subclasses */ protected final Environment getEnvironment() { - return env; + return environment; } } diff --git a/alchemist-incarnation-biochemistry/src/main/java/it/unibo/alchemist/model/implementations/actions/AddJunctionInCell.java b/alchemist-incarnation-biochemistry/src/main/java/it/unibo/alchemist/model/implementations/actions/AddJunctionInCell.java index b63199683f..6d491b2c7c 100644 --- a/alchemist-incarnation-biochemistry/src/main/java/it/unibo/alchemist/model/implementations/actions/AddJunctionInCell.java +++ b/alchemist-incarnation-biochemistry/src/main/java/it/unibo/alchemist/model/implementations/actions/AddJunctionInCell.java @@ -8,11 +8,11 @@ package it.unibo.alchemist.model.implementations.actions; +import it.unibo.alchemist.model.interfaces.properties.CellProperty; import org.apache.commons.math3.random.RandomGenerator; import it.unibo.alchemist.model.implementations.molecules.Junction; import it.unibo.alchemist.model.interfaces.Environment; -import it.unibo.alchemist.model.interfaces.CellNode; import it.unibo.alchemist.model.interfaces.Node; import it.unibo.alchemist.model.interfaces.Reaction; @@ -37,11 +37,12 @@ public final class AddJunctionInCell extends AbstractNeighborAction { // */ public AddJunctionInCell(final Environment e, final Node n, final Junction j, final RandomGenerator rg) { super(n, e, rg); - if (n instanceof CellNode) { + if (n.asPropertyOrNull(CellProperty.class) != null) { declareDependencyTo(j); jun = j; } else { - throw new UnsupportedOperationException("This Action can be set only in CellNodes"); + throw new UnsupportedOperationException("This Action can be set only in nodes with " + + CellProperty.class.getSimpleName()); } } @@ -64,10 +65,11 @@ public void execute() { */ @Override public void execute(final Node targetNode) { - if (targetNode instanceof CellNode) { - getNode().addJunction(jun, (CellNode) targetNode); + if (targetNode.asPropertyOrNull(CellProperty.class) != null) { + getNode().asProperty(CellProperty.class).addJunction(jun, targetNode); } else { - throw new UnsupportedOperationException("Can't add Junction in a node that it's not a CellNode"); + throw new UnsupportedOperationException("Can't add Junction in a node with no " + + CellProperty.class.getSimpleName()); } } @@ -75,9 +77,4 @@ public void execute(final Node targetNode) { public String toString() { return "add junction " + jun.toString() + " in node"; } - - @Override - public CellNode getNode() { - return (CellNode) super.getNode(); - } } diff --git a/alchemist-incarnation-biochemistry/src/main/java/it/unibo/alchemist/model/implementations/actions/AddJunctionInNeighbor.java b/alchemist-incarnation-biochemistry/src/main/java/it/unibo/alchemist/model/implementations/actions/AddJunctionInNeighbor.java index 323c1d78ab..ee2f8ed636 100644 --- a/alchemist-incarnation-biochemistry/src/main/java/it/unibo/alchemist/model/implementations/actions/AddJunctionInNeighbor.java +++ b/alchemist-incarnation-biochemistry/src/main/java/it/unibo/alchemist/model/implementations/actions/AddJunctionInNeighbor.java @@ -8,11 +8,11 @@ package it.unibo.alchemist.model.implementations.actions; import it.unibo.alchemist.model.implementations.molecules.Junction; -import it.unibo.alchemist.model.interfaces.CellNode; import it.unibo.alchemist.model.interfaces.Environment; import it.unibo.alchemist.model.interfaces.Node; import it.unibo.alchemist.model.interfaces.Position; import it.unibo.alchemist.model.interfaces.Reaction; +import it.unibo.alchemist.model.interfaces.properties.CellProperty; import org.apache.commons.math3.random.RandomGenerator; /** @@ -37,7 +37,7 @@ public final class AddJunctionInNeighbor

> extend */ public AddJunctionInNeighbor( final Environment environment, - final CellNode

node, + final Node node, final Junction junction, final RandomGenerator randomGenerator ) { @@ -49,13 +49,13 @@ public AddJunctionInNeighbor( @SuppressWarnings("unchecked") @Override public AddJunctionInNeighbor

cloneAction(final Node node, final Reaction reaction) { - if (node instanceof CellNode) { + if (node.asPropertyOrNull(CellProperty.class) != null) { return new AddJunctionInNeighbor<>( (Environment) getEnvironment(), - (CellNode

) node, + node, jun, getRandomGenerator()); } - throw new IllegalArgumentException("Node must be CellNode, found " + node + " of type: " + node.getClass()); + throw new IllegalArgumentException("Node must have a " + CellProperty.class.getSimpleName()); } /** @@ -73,10 +73,11 @@ public void execute() { @Override @SuppressWarnings("unchecked") public void execute(final Node targetNode) { - if (targetNode instanceof CellNode) { - ((CellNode

) targetNode).addJunction(jun, getNode()); + if (targetNode.asPropertyOrNull(CellProperty.class) != null) { + targetNode.asProperty(CellProperty.class).addJunction(jun, getNode()); } else { - throw new UnsupportedOperationException("Can't add Junction in a node that it's not a CellNode"); + throw new UnsupportedOperationException("Can't add Junction in a node with no " + + CellProperty.class.getSimpleName()); } } @@ -85,10 +86,4 @@ public String toString() { return "add junction " + jun.toString() + " in neighbor"; } - @Override - @SuppressWarnings("unchecked") - public CellNode

getNode() { - return (CellNode

) super.getNode(); - } - } diff --git a/alchemist-incarnation-biochemistry/src/main/java/it/unibo/alchemist/model/implementations/actions/CellMove.java b/alchemist-incarnation-biochemistry/src/main/java/it/unibo/alchemist/model/implementations/actions/CellMove.java index f240f13ded..f2ce7e9225 100644 --- a/alchemist-incarnation-biochemistry/src/main/java/it/unibo/alchemist/model/implementations/actions/CellMove.java +++ b/alchemist-incarnation-biochemistry/src/main/java/it/unibo/alchemist/model/implementations/actions/CellMove.java @@ -7,12 +7,14 @@ */ package it.unibo.alchemist.model.implementations.actions; -import it.unibo.alchemist.model.interfaces.CellNode; -import it.unibo.alchemist.model.interfaces.CellWithCircularArea; import it.unibo.alchemist.model.interfaces.Environment; import it.unibo.alchemist.model.interfaces.Node; import it.unibo.alchemist.model.interfaces.Position; import it.unibo.alchemist.model.interfaces.Reaction; +import it.unibo.alchemist.model.interfaces.properties.CellProperty; +import it.unibo.alchemist.model.interfaces.properties.CircularCellProperty; + +import java.util.Objects; /** * @@ -21,8 +23,9 @@ public final class CellMove

> extends AbstractMoveNode { private static final long serialVersionUID = 1L; - private final boolean inPer; + private final boolean inPercent; private final double delta; + private final CellProperty

cell; /** * Initialize an Action that move the cell of a given space delta, which can be expressed in percent of the cell's @@ -47,47 +50,40 @@ public CellMove( final double delta ) { super(environment, node); - this.inPer = inPercent; - if (node instanceof CellNode) { - if (inPercent) { - if (node instanceof CellWithCircularArea && ((CellWithCircularArea) node).getRadius() != 0) { - this.delta = ((CellWithCircularArea) node).getDiameter() * delta; - } else { - throw new IllegalArgumentException( - "Can't set distance in percent of the cell's diameter if cell has not a diameter" - ); - } + this.inPercent = inPercent; + cell = Objects.requireNonNull( + node.asPropertyOrNull(CellProperty.class), + "CellMove can be setted only in cells." + ); + if (inPercent) { + if (cell instanceof CircularCellProperty && ((CircularCellProperty

) cell).getRadius() != 0) { + this.delta = ((CircularCellProperty

) cell).getDiameter() * delta; } else { - this.delta = delta; + throw new IllegalArgumentException( + "Can't set distance in percent of the cell's diameter if cell has not a diameter" + ); } } else { - throw new UnsupportedOperationException("CellMove can be setted only in cells."); + this.delta = delta; } } @Override public CellMove

cloneAction(final Node node, final Reaction reaction) { - return new CellMove<>(getEnvironment(), node, inPer, delta); + return new CellMove<>(getEnvironment(), node, inPercent, delta); } @Override public P getNextPosition() { return getEnvironment().makePosition( - delta * getNode().getPolarizationVersor().getCoordinate(0), - delta * getNode().getPolarizationVersor().getCoordinate(1) + delta * cell.getPolarizationVersor().getCoordinate(0), + delta * cell.getPolarizationVersor().getCoordinate(1) ); } @Override public void execute() { super.execute(); - getNode().setPolarization(getEnvironment().makePosition(0, 0)); - } - - @Override - @SuppressWarnings("unchecked") - public CellNode

getNode() { - return (CellNode

) super.getNode(); + cell.setPolarizationVersor(getEnvironment().makePosition(0, 0)); } - } diff --git a/alchemist-incarnation-biochemistry/src/main/java/it/unibo/alchemist/model/implementations/actions/CellTensionPolarization.java b/alchemist-incarnation-biochemistry/src/main/java/it/unibo/alchemist/model/implementations/actions/CellTensionPolarization.java index 0695785427..e2102887dc 100644 --- a/alchemist-incarnation-biochemistry/src/main/java/it/unibo/alchemist/model/implementations/actions/CellTensionPolarization.java +++ b/alchemist-incarnation-biochemistry/src/main/java/it/unibo/alchemist/model/implementations/actions/CellTensionPolarization.java @@ -7,17 +7,18 @@ */ package it.unibo.alchemist.model.implementations.actions; -import it.unibo.alchemist.model.interfaces.CellWithCircularArea; -import it.unibo.alchemist.model.interfaces.CircularDeformableCell; import it.unibo.alchemist.model.interfaces.Context; import it.unibo.alchemist.model.interfaces.EnvironmentSupportingDeformableCells; import it.unibo.alchemist.model.interfaces.Node; import it.unibo.alchemist.model.interfaces.Position2D; import it.unibo.alchemist.model.interfaces.Reaction; +import it.unibo.alchemist.model.interfaces.properties.CircularCellProperty; +import it.unibo.alchemist.model.interfaces.properties.CircularDeformableCellProperty; import org.apache.commons.math3.util.FastMath; import org.danilopianini.lang.MathUtils; import java.util.List; +import java.util.Objects; import java.util.stream.Collectors; /** @@ -29,7 +30,8 @@ public final class CellTensionPolarization

> extends Abst * */ private static final long serialVersionUID = 1L; - private final EnvironmentSupportingDeformableCells

env; + private final EnvironmentSupportingDeformableCells

environment; + private final CircularDeformableCellProperty

deformableCell; /** * @@ -38,71 +40,82 @@ public final class CellTensionPolarization

> extends Abst */ public CellTensionPolarization( final EnvironmentSupportingDeformableCells

environment, - final CircularDeformableCell

node + final Node node ) { super(node); - this.env = environment; + this.environment = environment; + this.deformableCell = Objects.requireNonNull(getDeformableCell(node), + "The node must have a " + CircularDeformableCellProperty.class.getSimpleName() + ); + } + + private CircularDeformableCellProperty

getDeformableCell(final Node node) { + return node.asPropertyOrNull(CircularDeformableCellProperty.class); + } + + private CircularCellProperty

getCircularCell(final Node node) { + return node.asPropertyOrNull(CircularCellProperty.class); + } + + private boolean isDeformableCell(final Node node) { + return getDeformableCell(node) != null; } @Override public CellTensionPolarization

cloneAction(final Node node, final Reaction reaction) { - if (node instanceof CircularDeformableCell) { - return new CellTensionPolarization<>(env, (CircularDeformableCell

) node); - } - throw new IllegalArgumentException("Node must be CircularDeformableCell, found " + node - + " of type: " + node.getClass()); + return new CellTensionPolarization<>(environment, node); } @Override public void execute() { // get node position as array - final double[] nodePos = env.getPosition(getNode()).getCoordinates(); + final double[] nodePosistion = environment.getPosition(getNode()).getCoordinates(); // initializing resulting versor - final double[] resVersor = new double[nodePos.length]; + final double[] resultingVersor = new double[nodePosistion.length]; // declaring a variable for the node where this action is set, to have faster access - final CircularDeformableCell

thisNode = getNode(); + final Node thisNode = getNode(); // transforming each node around in a vector (Position) - final List

pushForces = env.getNodesWithinRange( + final List

pushForces = environment.getNodesWithinRange( thisNode, - env.getMaxDiameterAmongCircularDeformableCells()).stream() + environment.getMaxDiameterAmongCircularDeformableCells()).stream() .parallel() - .filter(n -> { // only cells overlapping this cell are selected - if (n instanceof CellWithCircularArea) { + .filter(node -> { // only cells overlapping this cell are selected + final CircularCellProperty

circularCell = getCircularCell(node); + if (!Objects.isNull(circularCell)) { // computing for each cell the max distance among which can't be overlapping - double maxDist; - if (n instanceof CircularDeformableCell) { + double maxDistance; + if (isDeformableCell(node)) { // for deformable cell is maxRad + maxRad - maxDist = thisNode.getMaxRadius() + ((CircularDeformableCell

) n).getMaxRadius(); + maxDistance = deformableCell.getMaximumRadius() + getDeformableCell(node).getMaximumRadius(); } else { // for simple cells is maxRad + rad - maxDist = thisNode.getMaxRadius() + ((CellWithCircularArea

) n).getRadius(); + maxDistance = deformableCell.getMaximumRadius() + circularCell.getRadius(); } // check - return env.getDistanceBetweenNodes(thisNode, n) < maxDist; + return environment.getDistanceBetweenNodes(thisNode, node) < maxDistance; } else { // only CellWithCircularArea are selected. return false; } }) - .map(n -> { + .map(node -> { // position of node n as array - final double[] nPos = env.getPosition(n).getCoordinates(); + final double[] nPos = environment.getPosition(node).getCoordinates(); // max radius of n final double localNodeMaxRadius; // min radius of n final double localNodeMinRadius; // max radius of this node (thisNode) - final double nodeMaxRadius = thisNode.getMaxRadius(); + final double nodeMaxRadius = deformableCell.getMaximumRadius(); // min radius of this node (thisNode) - final double nodeMinRadius = thisNode.getRadius(); + final double nodeMinRadius = deformableCell.getRadius(); // intensity of tension between n and this node (thisNode), measured as value between 0 and 1 final double intensity; - if (n instanceof CircularDeformableCell) { - final CircularDeformableCell

localNode = (CircularDeformableCell

) n; - localNodeMaxRadius = localNode.getMaxRadius(); - localNodeMinRadius = localNode.getRadius(); + if (isDeformableCell(node)) { + localNodeMaxRadius = getDeformableCell(node).getMaximumRadius(); + localNodeMinRadius = getDeformableCell(node).getRadius(); } else { - localNodeMaxRadius = ((CellWithCircularArea

) n).getRadius(); + localNodeMaxRadius = getCircularCell(node).getRadius(); localNodeMinRadius = localNodeMaxRadius; } // if both cells has no difference between maxRad and minRad intensity must be 1 @@ -112,38 +125,40 @@ public void execute() { intensity = 1; } else { final double maxRadiusSum = localNodeMaxRadius + nodeMaxRadius; - intensity = (maxRadiusSum - env.getDistanceBetweenNodes(n, thisNode)) + intensity = (maxRadiusSum - environment.getDistanceBetweenNodes(node, thisNode)) / (maxRadiusSum - localNodeMinRadius - nodeMinRadius); } if (intensity != 0) { - double[] propensityVector = {nodePos[0] - nPos[0], nodePos[1] - nPos[1]}; + double[] propensityVector = {nodePosistion[0] - nPos[0], nodePosistion[1] - nPos[1]}; final double module = FastMath.sqrt(FastMath.pow(propensityVector[0], 2) + FastMath.pow(propensityVector[1], 2)); if (module == 0) { - return env.makePosition(0, 0); + return environment.makePosition(0, 0); } propensityVector = new double[]{ intensity * (propensityVector[0] / module), intensity * (propensityVector[1] / module) }; - return env.makePosition(propensityVector[0], propensityVector[1]); + return environment.makePosition(propensityVector[0], propensityVector[1]); } else { - return env.makePosition(0, 0); + return environment.makePosition(0, 0); } }) .collect(Collectors.toList()); if (pushForces.isEmpty()) { - thisNode.addPolarization(env.makePosition(0, 0)); + deformableCell.addPolarizationVersor(environment.makePosition(0, 0)); } else { for (final P p : pushForces) { - resVersor[0] = resVersor[0] + p.getX(); - resVersor[1] = resVersor[1] + p.getY(); + resultingVersor[0] = resultingVersor[0] + p.getX(); + resultingVersor[1] = resultingVersor[1] + p.getY(); } - final double module = FastMath.sqrt(FastMath.pow(resVersor[0], 2) + FastMath.pow(resVersor[1], 2)); + final double module = FastMath.sqrt(FastMath.pow(resultingVersor[0], 2) + FastMath.pow(resultingVersor[1], 2)); if (module == 0) { - thisNode.addPolarization(env.makePosition(0, 0)); + deformableCell.addPolarizationVersor(environment.makePosition(0, 0)); } else { - thisNode.addPolarization(env.makePosition(resVersor[0] / module, resVersor[1] / module)); + deformableCell.addPolarizationVersor( + environment.makePosition(resultingVersor[0] / module, resultingVersor[1] / module) + ); } } } @@ -153,9 +168,4 @@ public Context getContext() { return Context.LOCAL; } - @Override - public CircularDeformableCell

getNode() { - return (CircularDeformableCell

) super.getNode(); - } - } diff --git a/alchemist-incarnation-biochemistry/src/main/java/it/unibo/alchemist/model/implementations/actions/ChangeBiomolConcentrationInEnv.java b/alchemist-incarnation-biochemistry/src/main/java/it/unibo/alchemist/model/implementations/actions/ChangeBiomolConcentrationInEnv.java index 28dd936020..dae9748348 100644 --- a/alchemist-incarnation-biochemistry/src/main/java/it/unibo/alchemist/model/implementations/actions/ChangeBiomolConcentrationInEnv.java +++ b/alchemist-incarnation-biochemistry/src/main/java/it/unibo/alchemist/model/implementations/actions/ChangeBiomolConcentrationInEnv.java @@ -11,12 +11,12 @@ import it.unibo.alchemist.model.implementations.molecules.Biomolecule; import it.unibo.alchemist.model.interfaces.Action; -import it.unibo.alchemist.model.interfaces.CellNode; import it.unibo.alchemist.model.interfaces.Context; import it.unibo.alchemist.model.interfaces.Environment; import it.unibo.alchemist.model.interfaces.EnvironmentNode; import it.unibo.alchemist.model.interfaces.Node; import it.unibo.alchemist.model.interfaces.Reaction; +import it.unibo.alchemist.model.interfaces.properties.CellProperty; import org.apache.commons.math3.random.RandomGenerator; import org.apache.commons.math3.util.FastMath; @@ -33,7 +33,7 @@ public final class ChangeBiomolConcentrationInEnv extends AbstractRandomizableAc private static final long serialVersionUID = 1L; private final double delta; private final Biomolecule biomolecule; - private final Environment env; + private final Environment environment; /** * Initialize a new {@link Action} that change concentration of the given @@ -53,12 +53,15 @@ public ChangeBiomolConcentrationInEnv( final RandomGenerator randomGen ) { super(node, randomGen); - if (node instanceof EnvironmentNode || node instanceof CellNode) { + if (node instanceof EnvironmentNode || node.asPropertyOrNull(CellProperty.class) != null) { this.biomolecule = biomolecule; delta = deltaCon; - env = environment; + this.environment = environment; } else { - throw new UnsupportedOperationException("This condition can be set only in EnvironmentNode and CellNode"); + throw new UnsupportedOperationException( + "This condition can be set only in Node with nodes with " + CellProperty.class.getSimpleName() + " or " + + EnvironmentNode.class.getSimpleName() + ); } } @@ -81,7 +84,7 @@ public ChangeBiomolConcentrationInEnv( @Override public Action cloneAction(final Node node, final Reaction reaction) { - return new ChangeBiomolConcentrationInEnv(node, biomolecule, env, getRandomGenerator()); + return new ChangeBiomolConcentrationInEnv(node, biomolecule, environment, getRandomGenerator()); } @Override @@ -97,7 +100,7 @@ public void execute() { } else { // if getNode() instanceof CellNode, check if all nodes are at the same distance final boolean areAllEnvNodesAtTheSameDistance = environmentNodesSurrounding.stream() - .mapToDouble(n -> env.getDistanceBetweenNodes(thisNode, n)) + .mapToDouble(n -> environment.getDistanceBetweenNodes(thisNode, n)) .distinct() .count() == 1; if (areAllEnvNodesAtTheSameDistance) { @@ -116,7 +119,8 @@ public void execute() { } } else { // else, sort the list by the distance from the node - environmentNodesSurrounding.sort(Comparator.comparingDouble(n -> env.getDistanceBetweenNodes(thisNode, n))); + environmentNodesSurrounding.sort(Comparator + .comparingDouble(n -> environment.getDistanceBetweenNodes(thisNode, n))); changeConcentrationInSortedNodes(environmentNodesSurrounding); } } @@ -132,7 +136,7 @@ public Context getContext() { * @return a list containing the environment nodes around */ protected List getEnvironmentNodesSurrounding() { - return env.getNeighborhood(getNode()).getNeighbors().stream() + return environment.getNeighborhood(getNode()).getNeighbors().stream() .parallel() .flatMap(n -> n instanceof EnvironmentNode ? Stream.of((EnvironmentNode) n) : Stream.empty()) .collect(Collectors.toList()); @@ -184,7 +188,8 @@ private void changeConcentrationInRandomNodes(final List envNod } } else { // if delta > 0, simply add delta to the first node of the list (which has been sorted randomly) - final Node target = envNodesSurrounding.get(getRandomGenerator().nextInt(envNodesSurrounding.size())); + final Node target = envNodesSurrounding + .get(getRandomGenerator().nextInt(envNodesSurrounding.size())); target.setConcentration(biomolecule, target.getConcentration(biomolecule) + delta); } } diff --git a/alchemist-incarnation-biochemistry/src/main/java/it/unibo/alchemist/model/implementations/actions/ChangeBiomolConcentrationInNeighbor.java b/alchemist-incarnation-biochemistry/src/main/java/it/unibo/alchemist/model/implementations/actions/ChangeBiomolConcentrationInNeighbor.java index daab1ae0f3..57f3001fa4 100644 --- a/alchemist-incarnation-biochemistry/src/main/java/it/unibo/alchemist/model/implementations/actions/ChangeBiomolConcentrationInNeighbor.java +++ b/alchemist-incarnation-biochemistry/src/main/java/it/unibo/alchemist/model/implementations/actions/ChangeBiomolConcentrationInNeighbor.java @@ -11,10 +11,10 @@ import java.util.ArrayList; import java.util.List; +import it.unibo.alchemist.model.interfaces.properties.CellProperty; import org.apache.commons.math3.random.RandomGenerator; import it.unibo.alchemist.model.implementations.molecules.Biomolecule; -import it.unibo.alchemist.model.interfaces.CellNode; import it.unibo.alchemist.model.interfaces.Environment; import it.unibo.alchemist.model.interfaces.Neighborhood; import it.unibo.alchemist.model.interfaces.Node; @@ -59,12 +59,12 @@ public void execute() { final List validTargetsIds = new ArrayList<>(); if (delta < 0) { neighborhood.getNeighbors().stream() - .filter(n -> n instanceof CellNode && n.getConcentration(mol) >= delta) + .filter(n -> n.asPropertyOrNull(CellProperty.class) != null && n.getConcentration(mol) >= delta) .mapToInt(Node::getId) .forEach(validTargetsIds::add); } else { neighborhood.getNeighbors().stream() - .filter(n -> n instanceof CellNode && n.getConcentration(mol) >= delta) + .filter(n -> n.asPropertyOrNull(CellProperty.class) != null && n.getConcentration(mol) >= delta) .mapToInt(Node::getId) .forEach(validTargetsIds::add); } diff --git a/alchemist-incarnation-biochemistry/src/main/java/it/unibo/alchemist/model/implementations/actions/ChemotacticPolarization.java b/alchemist-incarnation-biochemistry/src/main/java/it/unibo/alchemist/model/implementations/actions/ChemotacticPolarization.java index 16ac497aef..1667322db9 100644 --- a/alchemist-incarnation-biochemistry/src/main/java/it/unibo/alchemist/model/implementations/actions/ChemotacticPolarization.java +++ b/alchemist-incarnation-biochemistry/src/main/java/it/unibo/alchemist/model/implementations/actions/ChemotacticPolarization.java @@ -8,13 +8,13 @@ package it.unibo.alchemist.model.implementations.actions; import it.unibo.alchemist.model.implementations.molecules.Biomolecule; -import it.unibo.alchemist.model.interfaces.CellNode; import it.unibo.alchemist.model.interfaces.Context; import it.unibo.alchemist.model.interfaces.Environment; import it.unibo.alchemist.model.interfaces.EnvironmentNode; import it.unibo.alchemist.model.interfaces.Node; import it.unibo.alchemist.model.interfaces.Position2D; import it.unibo.alchemist.model.interfaces.Reaction; +import it.unibo.alchemist.model.interfaces.properties.CellProperty; import org.apache.commons.math3.util.FastMath; import java.util.Comparator; @@ -31,9 +31,10 @@ public final class ChemotacticPolarization

> extends Abst * */ private static final long serialVersionUID = 1L; - private final Environment env; - private final Biomolecule biomol; + private final Environment environment; + private final Biomolecule biomolecule; private final boolean ascend; + private final CellProperty

cell; /** * @@ -46,13 +47,17 @@ public final class ChemotacticPolarization

> extends Abst */ public ChemotacticPolarization( final Environment environment, - final CellNode

node, + final Node node, final Biomolecule biomolecule, final String ascendGrad ) { super(node); - this.env = Objects.requireNonNull(environment); - this.biomol = Objects.requireNonNull(biomolecule); + this.cell = Objects.requireNonNull( + node.asPropertyOrNull(CellProperty.class), + "This action can't be added to nodes with no " + CellProperty.class.getSimpleName() + ); + this.environment = Objects.requireNonNull(environment); + this.biomolecule = Objects.requireNonNull(biomolecule); if ("up".equalsIgnoreCase(ascendGrad)) { this.ascend = true; } else if ("down".equalsIgnoreCase(ascendGrad)) { @@ -77,62 +82,64 @@ public ChemotacticPolarization( final String biomolecule, final String ascendGrad ) { - this(environment, asCellNode(node), new Biomolecule(biomolecule), ascendGrad); + this(environment, node, new Biomolecule(biomolecule), ascendGrad); } @Override public ChemotacticPolarization

cloneAction(final Node node, final Reaction reaction) { - return new ChemotacticPolarization<>(env, node, biomol.toString(), ascend ? "up" : "down"); + return new ChemotacticPolarization<>(environment, node, biomolecule.toString(), ascend ? "up" : "down"); } @Override public void execute() { // declaring a variable for the node where this action is set, to have faster access - final CellNode

thisNode = getNode(); - final List> l = env.getNeighborhood(thisNode).getNeighbors().stream() - .filter(n -> n instanceof EnvironmentNode && n.contains(biomol)) + final Node thisNode = getNode(); + final List> l = environment.getNeighborhood(thisNode).getNeighbors().stream() + .filter(n -> n instanceof EnvironmentNode && n.contains(biomolecule)) .collect(Collectors.toList()); if (l.isEmpty()) { - thisNode.addPolarization(env.makePosition(0, 0)); + cell.addPolarizationVersor(environment.makePosition(0, 0)); } else { - final boolean isNodeOnMaxConc = env.getPosition(l.stream() - .max(Comparator.comparingDouble(n -> n.getConcentration(biomol))) - .get()).equals(env.getPosition(thisNode)); + final boolean isNodeOnMaxConc = environment.getPosition(l.stream() + .max(Comparator.comparingDouble(n -> n.getConcentration(biomolecule))) + .get()).equals(environment.getPosition(thisNode)); if (isNodeOnMaxConc) { - thisNode.addPolarization(env.makePosition(0, 0)); + cell.addPolarizationVersor(environment.makePosition(0, 0)); } else { P newPolVer = weightedAverageVectors(l, thisNode); final double newPolX = newPolVer.getX(); final double newPolY = newPolVer.getY(); final double newPolVerModule = FastMath.sqrt(newPolX * newPolX + newPolY * newPolY); if (newPolVerModule == 0) { - thisNode.addPolarization(newPolVer); + cell.addPolarizationVersor(newPolVer); } else { - newPolVer = env.makePosition(newPolVer.getX() / newPolVerModule, newPolVer.getY() / newPolVerModule); + newPolVer = environment.makePosition(newPolVer.getX() / newPolVerModule, + newPolVer.getY() / newPolVerModule); if (ascend) { - thisNode.addPolarization(newPolVer); + cell.addPolarizationVersor(newPolVer); } else { - thisNode.addPolarization(env.makePosition(-newPolVer.getX(), -newPolVer.getY())); + cell.addPolarizationVersor(environment.makePosition(-newPolVer.getX(), -newPolVer.getY())); } } } } } - private P weightedAverageVectors(final List> list, final CellNode

thisNode) { - P res = env.makePosition(0, 0); - final P thisNodePos = env.getPosition(thisNode); + private P weightedAverageVectors(final List> list, final Node thisNode) { + P res = environment.makePosition(0, 0); + final P thisNodePos = environment.getPosition(thisNode); for (final Node n : list) { - final P nPos = env.getPosition(n); - P vecTemp = env.makePosition( + final P nPos = environment.getPosition(n); + P vecTemp = environment.makePosition( nPos.getX() - thisNodePos.getX(), nPos.getY() - thisNodePos.getY()); - final double vecTempModule = FastMath.sqrt(FastMath.pow(vecTemp.getX(), 2) + FastMath.pow(vecTemp.getY(), 2)); - vecTemp = env.makePosition( - n.getConcentration(biomol) * (vecTemp.getX() / vecTempModule), - n.getConcentration(biomol) * (vecTemp.getY() / vecTempModule)); - res = env.makePosition( + final double vecTempModule = FastMath.sqrt(FastMath.pow(vecTemp.getX(), 2) + + FastMath.pow(vecTemp.getY(), 2)); + vecTemp = environment.makePosition( + n.getConcentration(biomolecule) * (vecTemp.getX() / vecTempModule), + n.getConcentration(biomolecule) * (vecTemp.getY() / vecTempModule)); + res = environment.makePosition( res.getX() + vecTemp.getX(), res.getY() + vecTemp.getY()); } @@ -144,16 +151,4 @@ public Context getContext() { return Context.LOCAL; } - @Override - public CellNode

getNode() { - return asCellNode(super.getNode()); - } - - private static

> CellNode

asCellNode(final Node node) { - if (node instanceof CellNode) { - return (CellNode

) node; - } - throw new IllegalArgumentException("CellNode required, got " + node.getClass()); - } - } diff --git a/alchemist-incarnation-biochemistry/src/main/java/it/unibo/alchemist/model/implementations/actions/RandomPolarization.java b/alchemist-incarnation-biochemistry/src/main/java/it/unibo/alchemist/model/implementations/actions/RandomPolarization.java index aef20df009..02d978dab5 100644 --- a/alchemist-incarnation-biochemistry/src/main/java/it/unibo/alchemist/model/implementations/actions/RandomPolarization.java +++ b/alchemist-incarnation-biochemistry/src/main/java/it/unibo/alchemist/model/implementations/actions/RandomPolarization.java @@ -7,15 +7,17 @@ */ package it.unibo.alchemist.model.implementations.actions; -import it.unibo.alchemist.model.interfaces.CellNode; import it.unibo.alchemist.model.interfaces.Context; import it.unibo.alchemist.model.interfaces.Environment; import it.unibo.alchemist.model.interfaces.Node; import it.unibo.alchemist.model.interfaces.Position2D; import it.unibo.alchemist.model.interfaces.Reaction; +import it.unibo.alchemist.model.interfaces.properties.CellProperty; import org.apache.commons.math3.random.RandomGenerator; import org.apache.commons.math3.util.FastMath; +import java.util.Objects; + /** * * @param

position type @@ -24,19 +26,25 @@ public final class RandomPolarization

> extends AbstractR private static final long serialVersionUID = 1L; private final Environment environment; + private final CellProperty

cell; /** * @param environment the environment * @param node the node * @param random the {@link RandomGenerator} */ - public RandomPolarization(final Environment environment, final Node node, final RandomGenerator random) { + public RandomPolarization( + final Environment environment, + final Node node, + final RandomGenerator random + ) { super(node, random); this.environment = environment; - if (!(node instanceof CellNode)) { - throw new UnsupportedOperationException("Polarization can happen only in cells, required CellNode, got " - + node.getClass()); - } + this.cell = node.asPropertyOrNull(CellProperty.class); + Objects.requireNonNull( + cell, + "Polarization can happen only in nodes with " + CellProperty.class.getSimpleName() + ); } /** @@ -59,7 +67,7 @@ public void execute() { randomVersor = environment.makePosition(x / module, y / module); } } - getNode().addPolarization(randomVersor); + cell.addPolarizationVersor(randomVersor); } /** @@ -75,10 +83,4 @@ public RandomPolarization

cloneAction(final Node node, final Reaction return new RandomPolarization<>(environment, node, getRandomGenerator()); } - @Override - @SuppressWarnings("unchecked") - public CellNode

getNode() { - return (CellNode

) super.getNode(); - } - } diff --git a/alchemist-incarnation-biochemistry/src/main/java/it/unibo/alchemist/model/implementations/actions/RemoveJunctionInCell.java b/alchemist-incarnation-biochemistry/src/main/java/it/unibo/alchemist/model/implementations/actions/RemoveJunctionInCell.java index 1db636b3ba..762693a556 100644 --- a/alchemist-incarnation-biochemistry/src/main/java/it/unibo/alchemist/model/implementations/actions/RemoveJunctionInCell.java +++ b/alchemist-incarnation-biochemistry/src/main/java/it/unibo/alchemist/model/implementations/actions/RemoveJunctionInCell.java @@ -10,13 +10,14 @@ import it.unibo.alchemist.model.implementations.molecules.Biomolecule; import it.unibo.alchemist.model.implementations.molecules.Junction; -import it.unibo.alchemist.model.interfaces.CellNode; import it.unibo.alchemist.model.interfaces.Environment; import it.unibo.alchemist.model.interfaces.Node; import it.unibo.alchemist.model.interfaces.Reaction; +import it.unibo.alchemist.model.interfaces.properties.CellProperty; import org.apache.commons.math3.random.RandomGenerator; import java.util.Map; +import java.util.Objects; /** * Represent the action of removing a junction between the current node and a neighbor. @@ -30,7 +31,8 @@ public final class RemoveJunctionInCell extends AbstractNeighborAction { private static final long serialVersionUID = 3565077605882164314L; private final Junction jun; - private final Environment env; + private final Environment environment; + private final CellProperty cell; /** * @@ -46,21 +48,21 @@ public RemoveJunctionInCell( final RandomGenerator randomGenerator ) { super(node, environment, randomGenerator); - if (node instanceof CellNode) { - declareDependencyTo(junction); - for (final Map.Entry entry : junction.getMoleculesInCurrentNode().entrySet()) { - declareDependencyTo(entry.getKey()); - } - jun = junction; - env = environment; - } else { - throw new UnsupportedOperationException("This Action can be set only in CellNodes"); + cell = Objects.requireNonNull( + node.asPropertyOrNull(CellProperty.class), + "This Action can be set only in nodes with " + CellProperty.class.getSimpleName() + ); + declareDependencyTo(junction); + for (final Map.Entry entry : junction.getMoleculesInCurrentNode().entrySet()) { + declareDependencyTo(entry.getKey()); } + jun = junction; + this.environment = environment; } @Override public RemoveJunctionInCell cloneAction(final Node node, final Reaction reaction) { - return new RemoveJunctionInCell(env, node, jun, getRandomGenerator()); + return new RemoveJunctionInCell(environment, node, jun, getRandomGenerator()); } /** @@ -74,10 +76,12 @@ public void execute() { } */ @Override public void execute(final Node targetNode) { - if (targetNode instanceof CellNode) { - getNode().removeJunction(jun, (CellNode) targetNode); + if (targetNode.asPropertyOrNull(CellProperty.class) != null) { + cell.removeJunction(jun, targetNode); } else { - throw new UnsupportedOperationException("Can't remove Junction in a node that it's not a CellNode"); + throw new UnsupportedOperationException( + "Can't remove Junction in a node with no " + CellProperty.class.getSimpleName() + ); } } @@ -85,9 +89,4 @@ public void execute(final Node targetNode) { public String toString() { return "remove junction " + jun.toString() + " in cell"; } - - @Override - public CellNode getNode() { - return (CellNode) super.getNode(); - } } diff --git a/alchemist-incarnation-biochemistry/src/main/java/it/unibo/alchemist/model/implementations/actions/RemoveJunctionInNeighbor.java b/alchemist-incarnation-biochemistry/src/main/java/it/unibo/alchemist/model/implementations/actions/RemoveJunctionInNeighbor.java index c685266973..53ec06996b 100644 --- a/alchemist-incarnation-biochemistry/src/main/java/it/unibo/alchemist/model/implementations/actions/RemoveJunctionInNeighbor.java +++ b/alchemist-incarnation-biochemistry/src/main/java/it/unibo/alchemist/model/implementations/actions/RemoveJunctionInNeighbor.java @@ -10,10 +10,10 @@ import it.unibo.alchemist.model.implementations.molecules.Biomolecule; import it.unibo.alchemist.model.implementations.molecules.Junction; -import it.unibo.alchemist.model.interfaces.CellNode; import it.unibo.alchemist.model.interfaces.Environment; import it.unibo.alchemist.model.interfaces.Node; import it.unibo.alchemist.model.interfaces.Reaction; +import it.unibo.alchemist.model.interfaces.properties.CellProperty; import org.apache.commons.math3.random.RandomGenerator; import java.util.Map; @@ -44,14 +44,16 @@ public RemoveJunctionInNeighbor( final Junction junction, final RandomGenerator randomGenerator) { super(node, environment, randomGenerator); - if (node instanceof CellNode) { + if (node.asPropertyOrNull(CellProperty.class) != null) { declareDependencyTo(junction); for (final Map.Entry entry : junction.getMoleculesInCurrentNode().entrySet()) { declareDependencyTo(entry.getKey()); } jun = junction; } else { - throw new UnsupportedOperationException("This Action can be set only in CellNodes"); + throw new UnsupportedOperationException( + "This Action can be set only in nodes with " + CellProperty.class.getSimpleName() + ); } } @@ -68,10 +70,11 @@ public void execute() { } @Override public void execute(final Node targetNode) { - if (targetNode instanceof CellNode) { - ((CellNode) targetNode).removeJunction(jun, getNode()); + if (targetNode.asPropertyOrNull(CellProperty.class) != null) { + targetNode.asProperty(CellProperty.class).removeJunction(jun, getNode()); } else { - throw new UnsupportedOperationException("Can't add Junction in a node that it's not a CellNode"); + throw new UnsupportedOperationException("Can't add Junction in a node with no " + + CellProperty.class.getSimpleName()); } } @@ -79,10 +82,4 @@ public void execute(final Node targetNode) { public String toString() { return "remove junction " + jun.toString() + " in neighbor"; } - - @Override - public CellNode getNode() { - return (CellNode) super.getNode(); - } - } diff --git a/alchemist-incarnation-biochemistry/src/main/java/it/unibo/alchemist/model/implementations/conditions/AbstractNeighborCondition.java b/alchemist-incarnation-biochemistry/src/main/java/it/unibo/alchemist/model/implementations/conditions/AbstractNeighborCondition.java index 06e83ba42b..edb9a4d8ac 100644 --- a/alchemist-incarnation-biochemistry/src/main/java/it/unibo/alchemist/model/implementations/conditions/AbstractNeighborCondition.java +++ b/alchemist-incarnation-biochemistry/src/main/java/it/unibo/alchemist/model/implementations/conditions/AbstractNeighborCondition.java @@ -28,7 +28,7 @@ public abstract class AbstractNeighborCondition extends AbstractCondition private static final long serialVersionUID = 1133243697147282024L; - private final Environment env; + private final Environment environment; /** * @@ -39,7 +39,7 @@ public abstract class AbstractNeighborCondition extends AbstractCondition */ protected AbstractNeighborCondition(final Environment environment, final Node node) { super(node); - env = environment; + this.environment = environment; } @Override @@ -54,7 +54,7 @@ public final Context getContext() { * @return allows subclasses to access the environment */ protected final Environment getEnvironment() { - return env; + return environment; } /** diff --git a/alchemist-incarnation-biochemistry/src/main/java/it/unibo/alchemist/model/implementations/conditions/BiomolPresentInEnv.java b/alchemist-incarnation-biochemistry/src/main/java/it/unibo/alchemist/model/implementations/conditions/BiomolPresentInEnv.java index acddba651c..b138a2640a 100644 --- a/alchemist-incarnation-biochemistry/src/main/java/it/unibo/alchemist/model/implementations/conditions/BiomolPresentInEnv.java +++ b/alchemist-incarnation-biochemistry/src/main/java/it/unibo/alchemist/model/implementations/conditions/BiomolPresentInEnv.java @@ -42,15 +42,17 @@ public final class BiomolPresentInEnv

> extends G * the requested concentration. * @param node * the node where this condition is located; - * @param env + * @param environment * the {@link Environment} where the node is located. */ - public BiomolPresentInEnv(final Environment env, - final Node node, - final Biomolecule biomolecule, - final Double concentration) { + public BiomolPresentInEnv( + final Environment environment, + final Node node, + final Biomolecule biomolecule, + final Double concentration + ) { super(node, biomolecule, concentration); - environment = env; + this.environment = environment; } @Override diff --git a/alchemist-incarnation-biochemistry/src/main/java/it/unibo/alchemist/model/implementations/conditions/BiomolPresentInNeighbor.java b/alchemist-incarnation-biochemistry/src/main/java/it/unibo/alchemist/model/implementations/conditions/BiomolPresentInNeighbor.java index 55aa8cadae..9a95c1ddda 100644 --- a/alchemist-incarnation-biochemistry/src/main/java/it/unibo/alchemist/model/implementations/conditions/BiomolPresentInNeighbor.java +++ b/alchemist-incarnation-biochemistry/src/main/java/it/unibo/alchemist/model/implementations/conditions/BiomolPresentInNeighbor.java @@ -9,11 +9,11 @@ package it.unibo.alchemist.model.implementations.conditions; import it.unibo.alchemist.model.implementations.molecules.Biomolecule; -import it.unibo.alchemist.model.interfaces.CellNode; import it.unibo.alchemist.model.interfaces.Environment; import it.unibo.alchemist.model.interfaces.Neighborhood; import it.unibo.alchemist.model.interfaces.Node; import it.unibo.alchemist.model.interfaces.Reaction; +import it.unibo.alchemist.model.interfaces.properties.CellProperty; import org.apache.commons.math3.util.FastMath; import java.util.Optional; @@ -28,25 +28,25 @@ public final class BiomolPresentInNeighbor extends AbstractNeighborCondition env, + final Environment environment, final Node node, final Biomolecule molecule, final Double concentration) { - super(env, node); + super(environment, node); declareDependencyOn(molecule); - mol = molecule; - conc = concentration; + this.molecule = molecule; + this.concentration = concentration; } @Override @@ -56,31 +56,31 @@ public boolean isValid() { } else { final Neighborhood neighborhood = getEnvironment().getNeighborhood(getNode()); return getValidNeighbors().entrySet().stream() - .filter(n -> n.getKey() instanceof CellNode) + .filter(n -> n.getKey().asPropertyOrNull(CellProperty.class) != null) .allMatch(n -> neighborhood.contains(n.getKey()) - && n.getKey().getConcentration(mol) >= conc); + && n.getKey().getConcentration(molecule) >= concentration); } } @Override - public BiomolPresentInNeighbor cloneCondition(final Node node, final Reaction r) { - return new BiomolPresentInNeighbor(getEnvironment(), node, mol, conc); + public BiomolPresentInNeighbor cloneCondition(final Node node, final Reaction reaction) { + return new BiomolPresentInNeighbor(getEnvironment(), node, molecule, concentration); } @Override protected double getNeighborPropensity(final Node neighbor) { // the neighbor is eligible, its propensity is computed using the concentration of the biomolecule return Optional.of(neighbor) - .filter(it -> it instanceof CellNode) - .map(it -> it.getConcentration(mol)) - .filter(it -> it >= conc) - .map(it -> binomialCoefficientDouble(it.intValue(), (int) FastMath.ceil(conc))) + .filter(it -> it.asPropertyOrNull(CellProperty.class) != null) + .map(it -> it.getConcentration(molecule)) + .filter(it -> it >= concentration) + .map(it -> binomialCoefficientDouble(it.intValue(), (int) FastMath.ceil(concentration))) .orElse(0d); } @Override public String toString() { - return mol.toString() + " >= " + conc + " in neighbor"; + return molecule.toString() + " >= " + concentration + " in neighbor"; } } diff --git a/alchemist-incarnation-biochemistry/src/main/java/it/unibo/alchemist/model/implementations/conditions/EnvPresent.java b/alchemist-incarnation-biochemistry/src/main/java/it/unibo/alchemist/model/implementations/conditions/EnvPresent.java index 9b9fcbc52f..2a443896c3 100644 --- a/alchemist-incarnation-biochemistry/src/main/java/it/unibo/alchemist/model/implementations/conditions/EnvPresent.java +++ b/alchemist-incarnation-biochemistry/src/main/java/it/unibo/alchemist/model/implementations/conditions/EnvPresent.java @@ -28,11 +28,11 @@ public final class EnvPresent extends AbstractCondition { /** * * @param node the node - * @param env the environment + * @param environment the environment */ - public EnvPresent(final Environment env, final Node node) { + public EnvPresent(final Environment environment, final Node node) { super(node); - environment = env; + this.environment = environment; } @Override diff --git a/alchemist-incarnation-biochemistry/src/main/java/it/unibo/alchemist/model/implementations/conditions/JunctionPresentInCell.java b/alchemist-incarnation-biochemistry/src/main/java/it/unibo/alchemist/model/implementations/conditions/JunctionPresentInCell.java index 8f19408bcb..4b487284cd 100644 --- a/alchemist-incarnation-biochemistry/src/main/java/it/unibo/alchemist/model/implementations/conditions/JunctionPresentInCell.java +++ b/alchemist-incarnation-biochemistry/src/main/java/it/unibo/alchemist/model/implementations/conditions/JunctionPresentInCell.java @@ -10,11 +10,12 @@ import it.unibo.alchemist.model.implementations.molecules.Junction; import it.unibo.alchemist.model.interfaces.Environment; -import it.unibo.alchemist.model.interfaces.CellNode; import it.unibo.alchemist.model.interfaces.Node; import it.unibo.alchemist.model.interfaces.Reaction; +import it.unibo.alchemist.model.interfaces.properties.CellProperty; import java.util.Collections; +import java.util.Objects; /** */ @@ -22,53 +23,49 @@ public final class JunctionPresentInCell extends AbstractNeighborCondition env; + private final Junction junction; + private final Environment environment; + private final CellProperty cell; /** * * @param junction the junction - * @param n the node - * @param e the environment + * @param node the node + * @param environment the environment */ - public JunctionPresentInCell(final Environment e, final Node n, final Junction junction) { - super(e, n); - if (n instanceof CellNode) { - declareDependencyOn(junction); - j = junction; - env = e; - } else { - throw new UnsupportedOperationException("This Condition can be set only in CellNodes"); - } + public JunctionPresentInCell(final Environment environment, final Node node, final Junction junction) { + super(environment, node); + cell = node.asPropertyOrNull(CellProperty.class); + Objects.requireNonNull( + cell, + "This Condition can be set only in node with " + CellProperty.class.getSimpleName() + ); + declareDependencyOn(junction); + this.junction = junction; + this.environment = environment; } @Override public boolean isValid() { - return getNode().containsJunction(j); + return cell.containsJunction(junction); } @Override public JunctionPresentInCell cloneCondition(final Node node, final Reaction r) { - return new JunctionPresentInCell(env, node, j); + return new JunctionPresentInCell(environment, node, junction); } @Override protected double getNeighborPropensity(final Node neighbor) { // the neighbor's propensity is computed as the number of junctions it has - //noinspection SuspiciousMethodCalls - return getNode().getJunctions() - .getOrDefault(j, Collections.emptyMap()) + return cell.getJunctions() + .getOrDefault(junction, Collections.emptyMap()) .getOrDefault(neighbor, 0); } @Override public String toString() { - return "junction " + j.toString() + " present "; - } - - @Override - public CellNode getNode() { - return (CellNode) super.getNode(); + return "junction " + junction.toString() + " present "; } } diff --git a/alchemist-incarnation-biochemistry/src/main/java/it/unibo/alchemist/model/implementations/conditions/NeighborhoodPresent.java b/alchemist-incarnation-biochemistry/src/main/java/it/unibo/alchemist/model/implementations/conditions/NeighborhoodPresent.java index 66bf007835..f2f65e938c 100644 --- a/alchemist-incarnation-biochemistry/src/main/java/it/unibo/alchemist/model/implementations/conditions/NeighborhoodPresent.java +++ b/alchemist-incarnation-biochemistry/src/main/java/it/unibo/alchemist/model/implementations/conditions/NeighborhoodPresent.java @@ -8,10 +8,10 @@ package it.unibo.alchemist.model.implementations.conditions; -import it.unibo.alchemist.model.interfaces.CellNode; import it.unibo.alchemist.model.interfaces.Environment; import it.unibo.alchemist.model.interfaces.Node; import it.unibo.alchemist.model.interfaces.Reaction; +import it.unibo.alchemist.model.interfaces.properties.CellProperty; /** * A condition is valid if the node has a neighborhood, formally if the node has at least one node @@ -40,13 +40,13 @@ public NeighborhoodPresent cloneCondition(final Node node, final Reaction< @Override protected double getNeighborPropensity(final Node neighbor) { // to be eligible (p = 1) a neighbor just needs to be instance of CellNode - return neighbor instanceof CellNode ? 1d : 0d; + return neighbor.asPropertyOrNull(CellProperty.class) != null ? 1d : 0d; } @Override public boolean isValid() { return getEnvironment().getNeighborhood(getNode()).getNeighbors().stream() - .anyMatch(n -> n instanceof CellNode); + .anyMatch(n -> n.asPropertyOrNull(CellProperty.class) != null); } @Override diff --git a/alchemist-incarnation-biochemistry/src/main/java/it/unibo/alchemist/model/implementations/conditions/TensionPresent.java b/alchemist-incarnation-biochemistry/src/main/java/it/unibo/alchemist/model/implementations/conditions/TensionPresent.java index eb83dcc85b..b7650f87e1 100644 --- a/alchemist-incarnation-biochemistry/src/main/java/it/unibo/alchemist/model/implementations/conditions/TensionPresent.java +++ b/alchemist-incarnation-biochemistry/src/main/java/it/unibo/alchemist/model/implementations/conditions/TensionPresent.java @@ -8,12 +8,12 @@ package it.unibo.alchemist.model.implementations.conditions; import edu.umd.cs.findbugs.annotations.SuppressFBWarnings; -import it.unibo.alchemist.model.interfaces.CellWithCircularArea; -import it.unibo.alchemist.model.interfaces.CircularDeformableCell; import it.unibo.alchemist.model.interfaces.Context; import it.unibo.alchemist.model.interfaces.EnvironmentSupportingDeformableCells; import it.unibo.alchemist.model.interfaces.Node; import it.unibo.alchemist.model.interfaces.Reaction; +import it.unibo.alchemist.model.interfaces.properties.CircularCellProperty; +import it.unibo.alchemist.model.interfaces.properties.CircularDeformableCellProperty; import java.util.stream.Stream; @@ -24,25 +24,25 @@ public final class TensionPresent extends AbstractCondition { private static final long serialVersionUID = 1L; - private final EnvironmentSupportingDeformableCells env; + private final EnvironmentSupportingDeformableCells environment; /** * * @param node the node - * @param env the environment + * @param environment the environment */ - public TensionPresent(final EnvironmentSupportingDeformableCells env, final CircularDeformableCell node) { + public TensionPresent(final EnvironmentSupportingDeformableCells environment, final Node node) { super(node); - this.env = env; + this.environment = environment; } @Override public TensionPresent cloneCondition(final Node node, final Reaction reaction) { - if (node instanceof CircularDeformableCell) { - return new TensionPresent(env, (CircularDeformableCell) node); + if (node.asPropertyOrNull(CircularDeformableCellProperty.class) != null) { + return new TensionPresent(environment, node); } - throw new IllegalArgumentException("Node must be CircularDeformableCell, found " + node - + " of type: " + node.getClass()); + throw new IllegalArgumentException("Node must have a " + + CircularDeformableCellProperty.class.getSimpleName()); } @Override @@ -52,26 +52,27 @@ public Context getContext() { @Override public double getPropensityContribution() { - final CircularDeformableCell thisNode = (CircularDeformableCell) getNode(); - return env.getNodesWithinRange(thisNode, env.getMaxDiameterAmongCircularDeformableCells()).stream() + final Node thisNode = getNode(); + return environment.getNodesWithinRange(thisNode, environment.getMaxDiameterAmongCircularDeformableCells()).stream() //.parallel() - .flatMap(n -> n instanceof CellWithCircularArea - ? Stream.of((CellWithCircularArea) n) + .flatMap(n -> n.asPropertyOrNull(CircularCellProperty.class) != null + ? Stream.of(n) : Stream.empty()) .mapToDouble(n -> { final double maxRn; final double minRn; - final double maxRN = thisNode.getMaxRadius(); - final double minRN = thisNode.getRadius(); - if (n instanceof CircularDeformableCell) { - final CircularDeformableCell cell = (CircularDeformableCell) n; - maxRn = cell.getMaxRadius(); - minRn = cell.getRadius(); + final double maxRN = thisNode.asProperty(CircularDeformableCellProperty.class) + .getMaximumRadius(); + final double minRN = thisNode.asProperty(CircularDeformableCellProperty.class).getRadius(); + if (n.asPropertyOrNull(CircularDeformableCellProperty.class) != null) { + final Node cell = n; + maxRn = cell.asProperty(CircularDeformableCellProperty.class).getMaximumRadius(); + minRn = cell.asProperty(CircularDeformableCellProperty.class).getRadius(); } else { - maxRn = n.getRadius(); + maxRn = n.asProperty(CircularCellProperty.class).getRadius(); minRn = maxRn; } - final double distance = env.getDistanceBetweenNodes(n, thisNode); + final double distance = environment.getDistanceBetweenNodes(n, thisNode); if (maxRn + maxRN - distance < 0) { return 0; } else { @@ -87,19 +88,22 @@ public double getPropensityContribution() { @Override public boolean isValid() { - final CircularDeformableCell thisNode = (CircularDeformableCell) getNode(); - return env.getNodesWithinRange(thisNode, env.getMaxDiameterAmongCircularDeformableCells()).stream() + final Node thisNode = getNode(); + return environment.getNodesWithinRange(thisNode, environment.getMaxDiameterAmongCircularDeformableCells()).stream() .parallel() - .flatMap(n -> n instanceof CellWithCircularArea - ? Stream.of((CellWithCircularArea) n) + .flatMap(n -> n.asPropertyOrNull(CircularCellProperty.class) != null + ? Stream.of(n) : Stream.empty()) .anyMatch(n -> { - final double maxDN = thisNode.getMaxRadius(); - if (n instanceof CircularDeformableCell) { - return env.getDistanceBetweenNodes(n, thisNode) - < (maxDN + ((CircularDeformableCell) n).getMaxRadius()); + final double maxDN = thisNode.asProperty(CircularDeformableCellProperty.class) + .getMaximumRadius(); + if (n.asPropertyOrNull(CircularDeformableCellProperty.class) != null) { + return environment.getDistanceBetweenNodes(n, thisNode) + < (maxDN + n.asProperty(CircularDeformableCellProperty.class) + .getMaximumRadius()); } else { - return env.getDistanceBetweenNodes(n, thisNode) < (maxDN + n.getRadius()); + return environment.getDistanceBetweenNodes(n, thisNode) < (maxDN + + n.asProperty(CircularCellProperty.class).getRadius()); } }); } diff --git a/alchemist-incarnation-biochemistry/src/main/java/it/unibo/alchemist/model/implementations/environments/BioRect2DEnvironment.java b/alchemist-incarnation-biochemistry/src/main/java/it/unibo/alchemist/model/implementations/environments/BioRect2DEnvironment.java index fce92eccda..b79630ff17 100644 --- a/alchemist-incarnation-biochemistry/src/main/java/it/unibo/alchemist/model/implementations/environments/BioRect2DEnvironment.java +++ b/alchemist-incarnation-biochemistry/src/main/java/it/unibo/alchemist/model/implementations/environments/BioRect2DEnvironment.java @@ -11,9 +11,9 @@ import it.unibo.alchemist.SupportedIncarnations; import it.unibo.alchemist.model.implementations.molecules.Junction; import it.unibo.alchemist.model.implementations.positions.Euclidean2DPosition; -import it.unibo.alchemist.model.interfaces.CellNode; import it.unibo.alchemist.model.interfaces.Neighborhood; import it.unibo.alchemist.model.interfaces.Node; +import it.unibo.alchemist.model.interfaces.properties.CellProperty; import org.slf4j.Logger; import org.slf4j.LoggerFactory; @@ -94,17 +94,18 @@ protected final boolean isAllowed(final Euclidean2DPosition p) { @Override @SuppressWarnings("unchecked") public final void moveNode(final Node node, final Euclidean2DPosition direction) { - if (node instanceof CellNode) { + if (node.asPropertyOrNull(CellProperty.class) != null) { super.moveNode(node, direction); - final CellNode nodeToMove = (CellNode) node; + final Node nodeToMove = node; final Neighborhood neigh = getNeighborhood(nodeToMove); - final Map, Integer>> jun = nodeToMove.getJunctions(); + final Map, Integer>> jun = nodeToMove + .asProperty(CellProperty.class).getJunctions(); jun.forEach((key, value) -> value.forEach((key1, value1) -> { if (!neigh.contains(key1)) { // there is a junction that links a node which isn't in the neighborhood after the movement for (int i = 0; i < value1; i++) { - nodeToMove.removeJunction(key, key1); - key1.removeJunction(key.reverse(), nodeToMove); + nodeToMove.asProperty(CellProperty.class).removeJunction(key, key1); + key1.asProperty(CellProperty.class).removeJunction(key.reverse(), nodeToMove); } } })); diff --git a/alchemist-incarnation-biochemistry/src/main/java/it/unibo/alchemist/model/implementations/environments/BioRect2DEnvironmentNoOverlap.java b/alchemist-incarnation-biochemistry/src/main/java/it/unibo/alchemist/model/implementations/environments/BioRect2DEnvironmentNoOverlap.java index 33d8df4993..9a72711cd2 100644 --- a/alchemist-incarnation-biochemistry/src/main/java/it/unibo/alchemist/model/implementations/environments/BioRect2DEnvironmentNoOverlap.java +++ b/alchemist-incarnation-biochemistry/src/main/java/it/unibo/alchemist/model/implementations/environments/BioRect2DEnvironmentNoOverlap.java @@ -9,11 +9,11 @@ import com.google.common.base.Optional; import it.unibo.alchemist.model.implementations.positions.Euclidean2DPosition; -import it.unibo.alchemist.model.interfaces.CellWithCircularArea; -import it.unibo.alchemist.model.interfaces.CircularDeformableCell; import it.unibo.alchemist.model.interfaces.EnvironmentSupportingDeformableCells; import it.unibo.alchemist.model.interfaces.Neighborhood; import it.unibo.alchemist.model.interfaces.Node; +import it.unibo.alchemist.model.interfaces.properties.CircularCellProperty; +import it.unibo.alchemist.model.interfaces.properties.CircularDeformableCellProperty; import org.apache.commons.math3.util.FastMath; import org.danilopianini.lang.MathUtils; import javax.annotation.Nonnull; @@ -38,8 +38,8 @@ public final class BioRect2DEnvironmentNoOverlap private static final long serialVersionUID = 1L; private static final String UNCHECKED = "unchecked"; private static final Logger L = LoggerFactory.getLogger(BioRect2DEnvironmentNoOverlap.class); - private Optional> biggestCellWithCircularArea = Optional.absent(); - private Optional> biggestCircularDeformableCell = Optional.absent(); + private Optional> biggestCellWithCircularArea = Optional.absent(); + private Optional> biggestCircularDeformableCell = Optional.absent(); /** * Returns an infinite {@link BioRect2DEnvironment}. @@ -64,18 +64,18 @@ public BioRect2DEnvironmentNoOverlap(final double minX, final double maxX, final protected boolean nodeShouldBeAdded(final Node node, final Euclidean2DPosition p) { final boolean isWithinLimits = super.nodeShouldBeAdded(node, p); if (isWithinLimits) { - if (node instanceof CellWithCircularArea) { - final CellWithCircularArea thisNode = (CellWithCircularArea) node; + if (node.asPropertyOrNull(CircularCellProperty.class) != null) { + final Node thisNode = node; double range = getMaxDiameterAmongCellWithCircularShape(); - if (thisNode.getDiameter() > range) { - range = thisNode.getDiameter(); + if (thisNode.asProperty(CircularCellProperty.class).getDiameter() > range) { + range = thisNode.asProperty(CircularCellProperty.class).getDiameter(); } - final double nodeRadius = thisNode.getRadius(); + final double nodeRadius = thisNode.asProperty(CircularCellProperty.class).getRadius(); return range <= 0 || getNodesWithinRange(p, range).stream() - .filter(n -> n instanceof CellWithCircularArea) - .map(n -> (CellWithCircularArea) n) - .noneMatch(n -> getPosition(n).distanceTo(p) < nodeRadius + n.getRadius()); + .filter(n -> n.asPropertyOrNull(CircularCellProperty.class) != null) + .noneMatch(n -> getPosition(n).distanceTo(p) < nodeRadius + + n.asProperty(CircularCellProperty.class).getRadius()); } else { return true; } @@ -90,9 +90,9 @@ public void moveNodeToPosition(final Node node, final Euclidean2DPositio final double[] cur = getPosition(node).getCoordinates(); final double[] np = newPos.getCoordinates(); final Euclidean2DPosition nextWithinLimts = super.next(cur[0], cur[1], np[0], np[1]); - if (node instanceof CellWithCircularArea) { + if (node.asPropertyOrNull(CircularCellProperty.class) != null) { final Euclidean2DPosition nextPos = findNearestFreePosition( - (CellWithCircularArea) node, + node, new Euclidean2DPosition(cur[0], cur[1]), nextWithinLimts ); @@ -107,7 +107,7 @@ public void moveNodeToPosition(final Node node, final Euclidean2DPositio */ @SuppressWarnings(UNCHECKED) private Euclidean2DPosition findNearestFreePosition( - final CellWithCircularArea nodeToMove, + final Node nodeToMove, final Euclidean2DPosition originalPos, final Euclidean2DPosition requestedPos) { // get the maximum range depending by cellular shape @@ -116,7 +116,8 @@ private Euclidean2DPosition findNearestFreePosition( if (maxDiameter == 0d || distanceToReq == 0) { return requestedPos; } - final double distanceToScan = distanceToReq + nodeToMove.getRadius() + (maxDiameter / 2); + final double distanceToScan = distanceToReq + nodeToMove + .asProperty(CircularCellProperty.class).getRadius() + (maxDiameter / 2); final double halfDistance = distanceToScan / 2; // compute position of the midpoint between originalPos and a point at distance distanceToScan final double rx = requestedPos.getX(); @@ -136,18 +137,18 @@ private Euclidean2DPosition findNearestFreePosition( double range = FastMath.sqrt(FastMath.pow(halfDistance, 2) + FastMath.pow(maxDiameter, 2)); final double newMaxDiameter = getNodesWithinRange(midPoint, range).stream() .parallel() - .filter(n -> n instanceof CellWithCircularArea) - .mapToDouble(n -> ((CellWithCircularArea) n).getDiameter()) + .filter(n -> n.asPropertyOrNull(CircularCellProperty.class) != null) + .mapToDouble(n -> n.asProperty(CircularCellProperty.class).getDiameter()) .max() .orElse(0); - final double newDistanceToScan = distanceToReq + nodeToMove.getRadius() + newMaxDiameter / 2; + final double newDistanceToScan = distanceToReq + + nodeToMove.asProperty(CircularCellProperty.class).getRadius() + newMaxDiameter / 2; final double newHalfDistance = newDistanceToScan / 2; final Euclidean2DPosition vecToMid2 = new Euclidean2DPosition(xVer * newHalfDistance, yVer * newHalfDistance); final Euclidean2DPosition newMidPoint = originalPos.plus(vecToMid2); range = FastMath.sqrt(FastMath.pow(newHalfDistance, 2) + FastMath.pow(newMaxDiameter, 2)); return getNodesWithinRange(newMidPoint, range).stream() - .filter(n -> !n.equals(nodeToMove) && n instanceof CellWithCircularArea) - .map(n -> (CellWithCircularArea) n) + .filter(n -> !n.equals(nodeToMove) && n.asPropertyOrNull(CircularCellProperty.class) != null) .filter(n -> selectNodes(n, nodeToMove, getPosition(nodeToMove), requestedPos, xVer, yVer)) .map(n -> getPositionIfNodeIsObstacle(nodeToMove, n, originalPos, oy, ox, ry, rx)) .filter(Optional::isPresent) @@ -156,8 +157,8 @@ private Euclidean2DPosition findNearestFreePosition( .orElse(requestedPos); } - private boolean selectNodes(final CellWithCircularArea node, - final CellWithCircularArea nodeToMove, + private boolean selectNodes(final Node node, + final Node nodeToMove, final Euclidean2DPosition origin, final Euclidean2DPosition requestedPos, final double xVer, final double yVer) { @@ -165,16 +166,20 @@ private boolean selectNodes(final CellWithCircularArea node final Euclidean2DPosition nodePos = getPosition(node); final Euclidean2DPosition nodeOrientationFromOrigin = new Euclidean2DPosition(nodePos.getX() - origin.getX(), nodePos.getY() - origin.getY()); - final double scalarProductResult1 = xVer * nodeOrientationFromOrigin.getX() + yVer * nodeOrientationFromOrigin.getY(); + final double scalarProductResult1 = xVer * nodeOrientationFromOrigin.getX() + + yVer * nodeOrientationFromOrigin.getY(); // testing if node is near enough to requested position to be an obstacle final Euclidean2DPosition oppositeVersor = new Euclidean2DPosition(-xVer, -yVer); - final Euclidean2DPosition nodeOrientationFromReq = new Euclidean2DPosition(nodePos.getX() - requestedPos.getX(), - nodePos.getY() - requestedPos.getY()); + final Euclidean2DPosition nodeOrientationFromReq = new Euclidean2DPosition( + nodePos.getX() - requestedPos.getX(), + nodePos.getY() - requestedPos.getY() + ); final double scalarProductResult2 = oppositeVersor.getX() * nodeOrientationFromReq.getX() + oppositeVersor.getY() * nodeOrientationFromReq.getY(); if (scalarProductResult2 <= 0) { - return nodePos.distanceTo(requestedPos) < node.getRadius() + nodeToMove.getRadius() + return nodePos.distanceTo(requestedPos) < node.asProperty(CircularCellProperty.class).getRadius() + + nodeToMove.asProperty(CircularCellProperty.class).getRadius() && scalarProductResult1 >= 0; } return scalarProductResult1 >= 0; @@ -182,8 +187,8 @@ private boolean selectNodes(final CellWithCircularArea node // returns the Optional containing the position of the node, if it's an obstacle for movement private Optional getPositionIfNodeIsObstacle( - final CellWithCircularArea nodeToMove, - final CellWithCircularArea node, + final Node nodeToMove, + final Node node, final Euclidean2DPosition originalPos, final double yo, final double xo, @@ -195,7 +200,8 @@ private Optional getPositionIfNodeIsObstacle( final double yn = possibleObstaclePosition.getY(); final double xn = possibleObstaclePosition.getX(); // cellular range - final double cellRange = node.getRadius() + nodeToMove.getRadius(); + final double cellRange = node.asProperty(CircularCellProperty.class).getRadius() + + nodeToMove.asProperty(CircularCellProperty.class).getRadius(); // compute intersection final double xIntersect; final double yIntersect; @@ -248,15 +254,17 @@ protected void nodeAdded( final @Nonnull Neighborhood neighborhood ) { super.nodeAdded(node, position, neighborhood); - if (node instanceof CellWithCircularArea) { - final CellWithCircularArea cell = (CellWithCircularArea) node; - if (cell.getDiameter() > getMaxDiameterAmongCellWithCircularShape()) { + if (node.asPropertyOrNull(CircularCellProperty.class) != null) { + final Node cell = node; + if (cell.asProperty(CircularCellProperty.class) + .getDiameter() > getMaxDiameterAmongCellWithCircularShape()) { biggestCellWithCircularArea = Optional.of(cell); } } - if (node instanceof CircularDeformableCell) { - final CircularDeformableCell cell = (CircularDeformableCell) node; - if (cell.getMaxDiameter() > getMaxDiameterAmongCircularDeformableCells()) { + if (node.asPropertyOrNull(CircularDeformableCellProperty.class) != null) { + final Node cell = node; + if (cell.asProperty(CircularDeformableCellProperty.class) + .getMaximumDiameter() > getMaxDiameterAmongCircularDeformableCells()) { biggestCircularDeformableCell = Optional.of(cell); } } @@ -265,23 +273,23 @@ protected void nodeAdded( @SuppressWarnings(UNCHECKED) @Override protected void nodeRemoved(final @Nonnull Node node, final @Nonnull Neighborhood neighborhood) { - if (node instanceof CellWithCircularArea) { + if (node.asPropertyOrNull(CircularCellProperty.class) != null) { if (biggestCircularDeformableCell.isPresent() && biggestCircularDeformableCell.get().equals(node)) { - biggestCircularDeformableCell = getBiggest(CircularDeformableCell.class) - .transform(c -> (CircularDeformableCell) c); + biggestCircularDeformableCell = getBiggest(CircularDeformableCellProperty.class) + .transform(CircularDeformableCellProperty::getNode); } if (biggestCellWithCircularArea.isPresent() && biggestCellWithCircularArea.get().equals(node)) { - biggestCellWithCircularArea = getBiggest(CellWithCircularArea.class) - .transform(c -> (CellWithCircularArea) c); + biggestCellWithCircularArea = getBiggest(CircularCellProperty.class) + .transform(CircularCellProperty::getNode); } } } private Optional getBiggest(final Class cellClass) { final boolean isDeformable; - if (cellClass.equals(CircularDeformableCell.class)) { + if (cellClass.equals(CircularDeformableCellProperty.class)) { isDeformable = true; - } else if (cellClass.equals(CellWithCircularArea.class)) { + } else if (cellClass.equals(CircularCellProperty.class)) { isDeformable = false; } else { throw new UnsupportedOperationException("Input type must be CellWithCircuolarShape or CircularDeformableCell"); @@ -342,11 +350,11 @@ public double getMaxDiameterAmongCircularDeformableCells() { return getDiameterFromCell(biggestCircularDeformableCell); } - private double getDiameterFromCell(final Optional> biggest) { + private double getDiameterFromCell(final Optional> biggest) { return biggest - .transform(n -> n instanceof CircularDeformableCell - ? ((CircularDeformableCell) n).getMaxDiameter() - : n.getDiameter()) + .transform(n -> n.asPropertyOrNull(CircularDeformableCellProperty.class) != null + ? n.asProperty(CircularDeformableCellProperty.class).getMaximumDiameter() + : n.asProperty(CircularCellProperty.class).getDiameter()) .or(0d); } } diff --git a/alchemist-incarnation-biochemistry/src/main/java/it/unibo/alchemist/model/implementations/nodes/CellNodeImpl.java b/alchemist-incarnation-biochemistry/src/main/java/it/unibo/alchemist/model/implementations/nodes/CellNodeImpl.java deleted file mode 100644 index ba5e4cbf09..0000000000 --- a/alchemist-incarnation-biochemistry/src/main/java/it/unibo/alchemist/model/implementations/nodes/CellNodeImpl.java +++ /dev/null @@ -1,195 +0,0 @@ -/* - * Copyright (C) 2010-2019, Danilo Pianini and contributors listed in the main project's alchemist/build.gradle file. - * - * This file is part of Alchemist, and is distributed under the terms of the - * GNU General Public License, with a linking exception, - * as described in the file LICENSE in the Alchemist distribution's top directory. - */ - -package it.unibo.alchemist.model.implementations.nodes; - -import it.unibo.alchemist.model.implementations.molecules.Biomolecule; -import it.unibo.alchemist.model.implementations.molecules.Junction; -import it.unibo.alchemist.model.interfaces.CellNode; -import it.unibo.alchemist.model.interfaces.CellWithCircularArea; -import it.unibo.alchemist.model.interfaces.Environment; -import it.unibo.alchemist.model.interfaces.Molecule; -import it.unibo.alchemist.model.interfaces.Position; -import it.unibo.alchemist.model.interfaces.geometry.Vector; -import org.apache.commons.math3.util.FastMath; - -import java.util.Collections; -import java.util.HashSet; -import java.util.LinkedHashMap; -import java.util.Map; -import java.util.Set; - -/** - * @param

{@link Position} type - */ -public class CellNodeImpl

& Vector

> extends DoubleNode implements CellNode

, CellWithCircularArea

{ - - private static final long serialVersionUID = 837704874534888283L; - - private final Map, Integer>> junctions = new LinkedHashMap<>(); - private final Environment environment; - private double diameter; - private P polarizationVersor; - - /** - * create a new cell node. - * - * @param env - * the environment - * @param diameter - * the diameter - */ - public CellNodeImpl(final Environment env, final double diameter) { - super(env); - environment = env; - this.polarizationVersor = env.makePosition(0, 0); - this.diameter = diameter; - } - - /** - * @param env - * the environment - */ - public CellNodeImpl(final Environment env) { - this(env, 0); - } - - @Override - public final void setConcentration(final Molecule mol, final Double c) { - if (c < 0) { - throw new IllegalArgumentException("No negative concentrations allowed (" + mol + " -> " + c + ")"); - } - if (c > 0) { - super.setConcentration(mol, c); - } else { - if (contains(mol)) { - removeConcentration(mol); - } - } - } - - @Override - public final void setPolarization(final P v) { - this.polarizationVersor = v; - } - - @Override - public final P getPolarizationVersor() { - return polarizationVersor; - } - - @Override - public final void addPolarization(final P v) { - final double[] tempCor = this.polarizationVersor.plus(v).getCoordinates(); - final double module = FastMath.sqrt(FastMath.pow(tempCor[0], 2) + FastMath.pow(tempCor[1], 2)); - this.polarizationVersor = module == 0 - ? environment.makePosition(0, 0) - : environment.makePosition(tempCor[0] / module, tempCor[1] / module); - } - - @Override - public final boolean contains(final Molecule m) { - if (m instanceof Junction) { - return containsJunction((Junction) m); - } else { - return super.contains(m); - } - } - - @Override - public final Map, Integer>> getJunctions() { - //return Collections.unmodifiableMap(junctions); - final Map, Integer>> ret = new LinkedHashMap<>(); - junctions.forEach((key, value) -> ret.put(key, new LinkedHashMap<>(value))); - return ret; - } - - @Override - public final void addJunction(final Junction j, final CellNode neighbor) { - if (junctions.containsKey(j)) { - final Map, Integer> inner = junctions.get(j); - if (inner.containsKey(neighbor)) { - inner.put(neighbor, inner.get(neighbor) + 1); - } else { - inner.put(neighbor, 1); - } - junctions.put(j, inner); - } else { - final Map, Integer> tmp = new LinkedHashMap<>(1); - tmp.put(neighbor, 1); - junctions.put(j, tmp); - } - } - - @Override - public final boolean containsJunction(final Junction j) { - return junctions.containsKey(j); - } - - @Override - public final void removeJunction(final Junction j, final CellNode neighbor) { - if (junctions.containsKey(j)) { - final Map, Integer> inner = junctions.get(j); - if (inner.containsKey(neighbor)) { - if (inner.get(neighbor) == 1) { // only one junction j with neighbor - inner.remove(neighbor); - } else { - inner.put(neighbor, inner.get(neighbor) - 1); - } - if (inner.isEmpty()) { - junctions.remove(j); - } else { - junctions.put(j, inner); - } - for (final Map.Entry e : j.getMoleculesInCurrentNode().entrySet()) { - setConcentration(e.getKey(), getConcentration(e.getKey()) + e.getValue()); - } - } - } - } - - @Override - public final Set> getNeighborsLinkWithJunction(final Junction j) { - if (junctions.get(j) == null) { - return Collections.emptySet(); - } - return new HashSet<>(junctions.get(j).keySet()); - } - - @Override - public final Set> getAllNodesLinkWithJunction() { - final Set> r = new HashSet<>(); - for (final Map.Entry, Integer>> e : junctions.entrySet()) { - r.addAll(e.getValue().keySet()); - } - return r; - } - - @Override - public final int getJunctionsCount() { - return junctions.values().stream().mapToInt(m -> m.values().stream().reduce(0, (a, b) -> a + b)).sum(); - } - - @Override - public final double getDiameter() { - return diameter; - } - - @Override - public final double getRadius() { - return getDiameter() / 2; - } - - /** - * {@inheritDoc} - */ - @Override - public String toString() { - return "Instance of CellNodeImpl with diameter = " + diameter; - } -} diff --git a/alchemist-incarnation-biochemistry/src/main/java/it/unibo/alchemist/model/implementations/nodes/CircularDeformableCellImpl.java b/alchemist-incarnation-biochemistry/src/main/java/it/unibo/alchemist/model/implementations/nodes/CircularDeformableCellImpl.java deleted file mode 100644 index 531026cd11..0000000000 --- a/alchemist-incarnation-biochemistry/src/main/java/it/unibo/alchemist/model/implementations/nodes/CircularDeformableCellImpl.java +++ /dev/null @@ -1,53 +0,0 @@ -/* - * Copyright (C) 2010-2019, Danilo Pianini and contributors listed in the main project's alchemist/build.gradle file. - * - * This file is part of Alchemist, and is distributed under the terms of the - * GNU General Public License, with a linking exception, - * as described in the file LICENSE in the Alchemist distribution's top directory. - */ -package it.unibo.alchemist.model.implementations.nodes; - -import it.unibo.alchemist.model.interfaces.CircularDeformableCell; -import it.unibo.alchemist.model.interfaces.Environment; -import it.unibo.alchemist.model.interfaces.Position; -import it.unibo.alchemist.model.interfaces.geometry.Vector; - -/** - * Implementation of a circular deformable cell. - * - * @param

{@link Position} type - */ -public final class CircularDeformableCellImpl

& Vector

> - extends CellNodeImpl

implements CircularDeformableCell

{ - - /** - * - */ - private static final long serialVersionUID = 1L; - private final double maxDiam; - - /** - * Create a circular deformable cell of maxDiam = maxDiameter and minDiam = deformability * maxDiam. - * @param env the environment - * @param maxDiameter the maximum diameter - * @param rigidity the rigidity of the cell, in ]0, 1[ - */ - public CircularDeformableCellImpl(final Environment env, final double maxDiameter, final double rigidity) { - super(env, maxDiameter * rigidity); - if (rigidity > 1 || rigidity < 0) { - throw new IllegalArgumentException("deformability must be between 0 and 1"); - } - this.maxDiam = maxDiameter; - } - - - @Override - public double getMaxDiameter() { - return maxDiam; - } - - @Override - public double getMaxRadius() { - return maxDiam / 2; - } -} diff --git a/alchemist-incarnation-biochemistry/src/main/java/it/unibo/alchemist/model/implementations/nodes/DoubleNode.java b/alchemist-incarnation-biochemistry/src/main/java/it/unibo/alchemist/model/implementations/nodes/DoubleNode.java deleted file mode 100644 index 9009d66487..0000000000 --- a/alchemist-incarnation-biochemistry/src/main/java/it/unibo/alchemist/model/implementations/nodes/DoubleNode.java +++ /dev/null @@ -1,34 +0,0 @@ -/* - * Copyright (C) 2010-2019, Danilo Pianini and contributors listed in the main project's alchemist/build.gradle file. - * - * This file is part of Alchemist, and is distributed under the terms of the - * GNU General Public License, with a linking exception, - * as described in the file LICENSE in the Alchemist distribution's top directory. - */ - -/** - * - */ -package it.unibo.alchemist.model.implementations.nodes; - -import it.unibo.alchemist.model.interfaces.Environment; - -/** - */ -public class DoubleNode extends AbstractNode { - - private static final long serialVersionUID = 3426243332828063314L; - - /** - * Builds a new DoubleNode. - * @param env the environment - */ - public DoubleNode(final Environment env) { - super(env); - } - - @Override - protected final Double createT() { - return 0d; - } -} diff --git a/alchemist-incarnation-biochemistry/src/main/java/it/unibo/alchemist/model/implementations/nodes/EnvironmentNodeImpl.java b/alchemist-incarnation-biochemistry/src/main/java/it/unibo/alchemist/model/implementations/nodes/EnvironmentNodeImpl.java index 6697a86198..4231b275c7 100644 --- a/alchemist-incarnation-biochemistry/src/main/java/it/unibo/alchemist/model/implementations/nodes/EnvironmentNodeImpl.java +++ b/alchemist-incarnation-biochemistry/src/main/java/it/unibo/alchemist/model/implementations/nodes/EnvironmentNodeImpl.java @@ -10,32 +10,46 @@ import it.unibo.alchemist.model.interfaces.Environment; import it.unibo.alchemist.model.interfaces.EnvironmentNode; +import it.unibo.alchemist.model.interfaces.Incarnation; import it.unibo.alchemist.model.interfaces.Molecule; +import javax.annotation.Nonnull; + /** - * + * A node with non-negative concentration. */ -public final class EnvironmentNodeImpl extends DoubleNode implements EnvironmentNode { +public final class EnvironmentNodeImpl extends GenericNode implements EnvironmentNode { private static final long serialVersionUID = 1L; /** * Create a new environment node. - * @param env the environment + * @param incarnation the simulation incarnation + * @param environment the environment + */ + public EnvironmentNodeImpl(final Incarnation incarnation, final Environment environment) { + super(incarnation, environment); + } + + /** + * Create a new environment node. + * @param environment the environment */ - public EnvironmentNodeImpl(final Environment env) { - super(env); + public EnvironmentNodeImpl(final Environment environment) { + super(environment.getIncarnation(), environment); } @Override - public void setConcentration(final Molecule mol, final Double c) { - if (c < 0) { - throw new IllegalArgumentException("No negative concentrations allowed (" + mol + " -> " + c + ")"); + public void setConcentration(@Nonnull final Molecule molecule, @Nonnull final Double concentration) { + if (concentration < 0) { + throw new IllegalArgumentException( + "No negative concentrations allowed (" + molecule + " -> " + concentration + ")" + ); } - if (c > 0) { - super.setConcentration(mol, c); + if (concentration > 0) { + super.setConcentration(molecule, concentration); } else { - removeConcentration(mol); + removeConcentration(molecule); } } } diff --git a/alchemist-incarnation-biochemistry/src/main/java/it/unibo/alchemist/model/implementations/nodes/IntegerNode.java b/alchemist-incarnation-biochemistry/src/main/java/it/unibo/alchemist/model/implementations/nodes/IntegerNode.java deleted file mode 100644 index 88314f5262..0000000000 --- a/alchemist-incarnation-biochemistry/src/main/java/it/unibo/alchemist/model/implementations/nodes/IntegerNode.java +++ /dev/null @@ -1,35 +0,0 @@ -/* - * Copyright (C) 2010-2019, Danilo Pianini and contributors listed in the main project's alchemist/build.gradle file. - * - * This file is part of Alchemist, and is distributed under the terms of the - * GNU General Public License, with a linking exception, - * as described in the file LICENSE in the Alchemist distribution's top directory. - */ - -package it.unibo.alchemist.model.implementations.nodes; - -import it.unibo.alchemist.model.interfaces.Environment; - -/** - */ -public abstract class IntegerNode extends AbstractNode { - - private static final long serialVersionUID = -1064026943504464379L; - - /** - * Create a new integer node. - * @param env the environment - */ - public IntegerNode(final Environment env) { - super(env); - } - - /** - * {@inheritDoc} - */ - @Override - protected Integer createT() { - return 0; - } - -} diff --git a/alchemist-incarnation-biochemistry/src/main/java/it/unibo/alchemist/model/implementations/reactions/BiochemicalReactionBuilder.java b/alchemist-incarnation-biochemistry/src/main/java/it/unibo/alchemist/model/implementations/reactions/BiochemicalReactionBuilder.java index 77d66552b1..fe80ac1279 100644 --- a/alchemist-incarnation-biochemistry/src/main/java/it/unibo/alchemist/model/implementations/reactions/BiochemicalReactionBuilder.java +++ b/alchemist-incarnation-biochemistry/src/main/java/it/unibo/alchemist/model/implementations/reactions/BiochemicalReactionBuilder.java @@ -32,7 +32,6 @@ import it.unibo.alchemist.model.implementations.molecules.Biomolecule; import it.unibo.alchemist.model.implementations.molecules.Junction; import it.unibo.alchemist.model.interfaces.Action; -import it.unibo.alchemist.model.interfaces.CellNode; import it.unibo.alchemist.model.interfaces.Condition; import it.unibo.alchemist.model.interfaces.Environment; import it.unibo.alchemist.model.interfaces.Incarnation; @@ -42,6 +41,7 @@ import it.unibo.alchemist.model.interfaces.Reaction; import it.unibo.alchemist.model.interfaces.TimeDistribution; import it.unibo.alchemist.model.interfaces.geometry.Vector; +import it.unibo.alchemist.model.interfaces.properties.CellProperty; import org.antlr.v4.runtime.ANTLRErrorListener; import org.antlr.v4.runtime.CharStreams; import org.antlr.v4.runtime.CommonTokenStream; @@ -55,9 +55,9 @@ import org.apache.commons.math3.random.RandomGenerator; import org.danilopianini.jirf.Factory; import org.danilopianini.jirf.FactoryBuilder; -import javax.annotation.Nonnull; import org.kaikikm.threadresloader.ResourceLoader; +import javax.annotation.Nonnull; import java.util.ArrayList; import java.util.BitSet; import java.util.LinkedHashMap; @@ -74,7 +74,7 @@ public class BiochemicalReactionBuilder

& Vector

> { private final BiochemistryIncarnation

incarnation; private final Node node; - private final Environment env; + private final Environment environment; private RandomGenerator rand; private TimeDistribution time; private String reactionString; @@ -93,7 +93,7 @@ public BiochemicalReactionBuilder( ) { incarnation = inc; node = currentNode; - env = environment; + this.environment = environment; } /** @@ -107,7 +107,7 @@ public Reaction build() { parser.removeErrorListeners(); parser.addErrorListener(new BiochemistryParseErrorListener(reactionString)); final ParseTree tree = parser.reaction(); - final BiochemistryDSLVisitor

eval = new BiochemistryDSLVisitor<>(rand, incarnation, time, node, env); + final BiochemistryDSLVisitor

eval = new BiochemistryDSLVisitor<>(rand, incarnation, time, node, environment); return Objects.requireNonNull(eval.visit(tree), "Unable to visit/parse " + reactionString); } @@ -162,7 +162,7 @@ private static final class BiochemistryDSLVisitor

node; - private final @Nonnull Environment env; + private final @Nonnull Environment environment; private final @Nonnull Reaction reaction; private final List> conditionList = new ArrayList<>(0); private final List> actionList = new ArrayList<>(0); @@ -180,8 +180,8 @@ private BiochemistryDSLVisitor(@Nonnull final RandomGenerator rand, @Nonnull final Environment environment) { this.rand = rand; this.node = currentNode; - env = environment; - reaction = new BiochemicalReaction(node, timeDistribution, env, rand); + this.environment = environment; + reaction = new BiochemicalReaction(node, timeDistribution, this.environment, rand); factory = new FactoryBuilder() .withAutoBoxing() .withBooleanIntConversions() @@ -244,10 +244,10 @@ public Reaction visitBiochemicalReaction(final BiochemistrydslParser.Bio * is undefined, and can lead to unwanted behavior. */ if (neighborActionPresent && biomolConditionsInNeighbor.isEmpty()) { - conditionList.add(new NeighborhoodPresent<>(env, node)); + conditionList.add(new NeighborhoodPresent<>(environment, node)); } if (envActionPresent && !envConditionPresent) { - conditionList.add(new EnvPresent(env, node)); + conditionList.add(new EnvPresent(environment, node)); } reaction.setConditions(conditionList); reaction.setActions(actionList); @@ -275,8 +275,8 @@ public Reaction visitBiochemicalReactionLeftInEnvContext( for (final BiomoleculeContext b : ctx.biomolecule()) { final Biomolecule biomol = createBiomolecule(b); final double concentration = createConcentration(b); - conditionList.add(new BiomolPresentInEnv<>(env, node, biomol, concentration)); - actionList.add(new ChangeBiomolConcentrationInEnv(node, biomol, env, rand)); + conditionList.add(new BiomolPresentInEnv<>(environment, node, biomol, concentration)); + actionList.add(new ChangeBiomolConcentrationInEnv(node, biomol, environment, rand)); envConditionPresent = true; } return reaction; @@ -290,8 +290,8 @@ public Reaction visitBiochemicalReactionLeftInNeighborContext( final Biomolecule biomol = createBiomolecule(b); final double concentration = createConcentration(b); insertInMap(biomolConditionsInNeighbor, biomol, concentration); - conditionList.add(new BiomolPresentInNeighbor(env, node, biomol, concentration)); - actionList.add(new ChangeBiomolConcentrationInNeighbor(env, node, biomol, rand, -concentration)); + conditionList.add(new BiomolPresentInNeighbor(environment, node, biomol, concentration)); + actionList.add(new ChangeBiomolConcentrationInNeighbor(environment, node, biomol, rand, -concentration)); } return reaction; } @@ -320,7 +320,7 @@ public Reaction visitBiochemicalReactionRightInEnvContext( if (re.biomolecule() != null) { final Biomolecule biomol = createBiomolecule(re.biomolecule()); final double concentration = createConcentration(re.biomolecule()); - actionList.add(new ChangeBiomolConcentrationInEnv(env, node, biomol, concentration, rand)); + actionList.add(new ChangeBiomolConcentrationInEnv(environment, node, biomol, concentration, rand)); } else if (re.javaConstructor() != null) { actionList.add(createObject(re.javaConstructor(), ACTIONS_PACKAGE)); } @@ -337,7 +337,7 @@ public Reaction visitBiochemicalReactionRightInNeighborContext( if (re.biomolecule() != null) { final Biomolecule biomol = createBiomolecule(re.biomolecule()); final double concentration = createConcentration(re.biomolecule()); - actionList.add(new ChangeBiomolConcentrationInNeighbor(env, node, biomol, rand, concentration)); + actionList.add(new ChangeBiomolConcentrationInNeighbor(environment, node, biomol, rand, concentration)); } else if (re.javaConstructor() != null) { actionList.add(createObject(re.javaConstructor(), ACTIONS_PACKAGE)); } @@ -382,12 +382,12 @@ public Reaction visitCreateJunctionJunction( ); } }); - if (node instanceof CellNode) { - actionList.add(new AddJunctionInCell(env, node, j, rand)); - actionList.add(new AddJunctionInNeighbor<>(env, (CellNode

) node, reverseJunction(j), rand)); + if (node.asPropertyOrNull(CellProperty.class) != null) { + actionList.add(new AddJunctionInCell(environment, node, j, rand)); + actionList.add(new AddJunctionInNeighbor<>(environment, node, reverseJunction(j), rand)); } else { throw new UnsupportedOperationException( - "Junctions are supported ONLY in CellNodes, not in " + node.getClass().getName() + "Junctions are supported ONLY in nodes with " + CellProperty.class.getSimpleName() ); } return reaction; @@ -410,12 +410,12 @@ public Reaction visitJunctionReaction(final BiochemistrydslParser.Juncti visit(context.customReactionType()); } junctionList.forEach(j -> { - if (node instanceof CellNode) { - actionList.add(new RemoveJunctionInCell(env, node, j, rand)); - actionList.add(new RemoveJunctionInNeighbor(env, node, reverseJunction(j), rand)); + if (node.asPropertyOrNull(CellProperty.class) != null) { + actionList.add(new RemoveJunctionInCell(environment, node, j, rand)); + actionList.add(new RemoveJunctionInNeighbor(environment, node, reverseJunction(j), rand)); } else { throw new UnsupportedOperationException( - "Junctions are supported ONLY in CellNodes, not in " + node.getClass().getName() + "Junctions are supported ONLY in node with " + CellProperty.class.getSimpleName() ); } }); @@ -445,14 +445,14 @@ public Reaction visitJunctionReactionJunction( public Reaction visitJunctionReactionJunctionCondition( final BiochemistrydslParser.JunctionReactionJunctionConditionContext context ) { - if (node instanceof CellNode) { + if (node.asPropertyOrNull(CellProperty.class) != null) { final Junction j = createJunction(context.junction()); junctionList.add(j); - conditionList.add(new JunctionPresentInCell(env, node, j)); + conditionList.add(new JunctionPresentInCell(environment, node, j)); return reaction; } else { throw new UnsupportedOperationException( - "Junctions are supported ONLY in CellNodes, not in " + node.getClass().getName() + "Junctions are supported ONLY in nodes with " + CellProperty.class.getSimpleName() ); } } diff --git a/alchemist-incarnation-biochemistry/src/main/java/it/unibo/alchemist/model/interfaces/CellNode.java b/alchemist-incarnation-biochemistry/src/main/java/it/unibo/alchemist/model/interfaces/CellNode.java deleted file mode 100644 index c7ff04d038..0000000000 --- a/alchemist-incarnation-biochemistry/src/main/java/it/unibo/alchemist/model/interfaces/CellNode.java +++ /dev/null @@ -1,87 +0,0 @@ -/* - * Copyright (C) 2010-2019, Danilo Pianini and contributors listed in the main project's alchemist/build.gradle file. - * - * This file is part of Alchemist, and is distributed under the terms of the - * GNU General Public License, with a linking exception, - * as described in the file LICENSE in the Alchemist distribution's top directory. - */ - -package it.unibo.alchemist.model.interfaces; - -import it.unibo.alchemist.model.implementations.molecules.Junction; - -import java.util.Map; -import java.util.Set; - -/** - * @param

position type - */ -public interface CellNode

> extends Node { - /** - * - * @return the map junction - node - quantity - */ - Map, Integer>> getJunctions(); - - /** - * Add a junction to the current node. - * @param j the junction - * @param neighbor the neighbor node at the other side of the junction - */ - void addJunction(Junction j, CellNode neighbor); - - /** - * Return true if a junction is present in the current node, false otherwise. - * Note: a junction is considered present if the method junction.equals(j) return true. - * @param j the junction - * @return true if the junction is present, false otherwise. - */ - boolean containsJunction(Junction j); - - /** - * Remove a junction from this node. - * @param j the junction to remove - * @param neighbor the node at the other side of the junction. - */ - void removeJunction(Junction j, CellNode neighbor); - - /** - * Returns a set of ICellNode which are linked with the current node by a junction of the type j. - * @param j the junction - * @return a set of ICellNode which are linked with the current node by a junction of the type j - */ - Set> getNeighborsLinkWithJunction(Junction j); - - /** - * - * @return The total number of junctions presents in this node - */ - int getJunctionsCount(); - - /** - * - * @return A set of nodes which are linked by a junction with the current node - */ - Set> getAllNodesLinkWithJunction(); - - /** - * set the polarization versor, e.g. a versor indicating the direction in which the cell will move the next time. - * - * @param v The {@link Position} representing the new polarization versor. - * Note: v MUST be a versor, so a vector with module = 1. - */ - void setPolarization(P v); - - /** - * - * @return the {@link Position} representing the direction of cell polarization. - */ - P getPolarizationVersor(); - - /** - * add v to the polarization versor inside the cell; useful for considering the combination of various stimuli in a cell. - * @param v polarization direction - */ - void addPolarization(P v); - -} diff --git a/alchemist-incarnation-biochemistry/src/main/java/it/unibo/alchemist/model/interfaces/CellWithCircularArea.java b/alchemist-incarnation-biochemistry/src/main/java/it/unibo/alchemist/model/interfaces/CellWithCircularArea.java deleted file mode 100644 index 96b98b01bf..0000000000 --- a/alchemist-incarnation-biochemistry/src/main/java/it/unibo/alchemist/model/interfaces/CellWithCircularArea.java +++ /dev/null @@ -1,26 +0,0 @@ -/* - * Copyright (C) 2010-2019, Danilo Pianini and contributors listed in the main project's alchemist/build.gradle file. - * - * This file is part of Alchemist, and is distributed under the terms of the - * GNU General Public License, with a linking exception, - * as described in the file LICENSE in the Alchemist distribution's top directory. - */ -package it.unibo.alchemist.model.interfaces; - -/** - * Implements a cell with a defined volume. - * @param

{@link Position} type - */ -public interface CellWithCircularArea

> extends CellNode

{ - - /** - * @return the cell's diameter. - */ - double getDiameter(); - - /** - * @return the cell's radius. - */ - double getRadius(); - -} diff --git a/alchemist-incarnation-biochemistry/src/main/java/it/unibo/alchemist/model/interfaces/CircularDeformableCell.java b/alchemist-incarnation-biochemistry/src/main/java/it/unibo/alchemist/model/interfaces/CircularDeformableCell.java deleted file mode 100644 index 3cb2b1bbd4..0000000000 --- a/alchemist-incarnation-biochemistry/src/main/java/it/unibo/alchemist/model/interfaces/CircularDeformableCell.java +++ /dev/null @@ -1,29 +0,0 @@ -/* - * Copyright (C) 2010-2019, Danilo Pianini and contributors listed in the main project's alchemist/build.gradle file. - * - * This file is part of Alchemist, and is distributed under the terms of the - * GNU General Public License, with a linking exception, - * as described in the file LICENSE in the Alchemist distribution's top directory. - */ -package it.unibo.alchemist.model.interfaces; - -/** - * Implements a circular deformable cell. - * - * @param

{@link Position} type - * - */ -public interface CircularDeformableCell

> extends CellWithCircularArea

{ - - /** - * - * @return the max diameter that this cell can have, e.g. the diameter that this cell has if no other cell is around. - */ - double getMaxDiameter(); - - /** - * - * @return the max radius that this cell can have, e.g. the radius that this cell has if no other cell is around. - */ - double getMaxRadius(); -} diff --git a/alchemist-incarnation-biochemistry/src/main/kotlin/it/unibo/alchemist/model/implementations/properties/Cell.kt b/alchemist-incarnation-biochemistry/src/main/kotlin/it/unibo/alchemist/model/implementations/properties/Cell.kt new file mode 100644 index 0000000000..c8f7981227 --- /dev/null +++ b/alchemist-incarnation-biochemistry/src/main/kotlin/it/unibo/alchemist/model/implementations/properties/Cell.kt @@ -0,0 +1,43 @@ +/* + * Copyright (C) 2010-2022, Danilo Pianini and contributors + * listed, for each module, in the respective subproject's build.gradle.kts file. + * + * This file is part of Alchemist, and is distributed under the terms of the + * GNU General Public License, with a linking exception, + * as described in the file LICENSE in the Alchemist distribution's top directory. + */ + +package it.unibo.alchemist.model.implementations.properties + +import it.unibo.alchemist.model.implementations.molecules.Junction +import it.unibo.alchemist.model.interfaces.Environment +import it.unibo.alchemist.model.interfaces.Node +import it.unibo.alchemist.model.interfaces.Position +import it.unibo.alchemist.model.interfaces.properties.CellProperty +import org.apache.commons.math3.util.FastMath + +/** + * Base implementation of a [CellProperty]. + */ +class Cell

> @JvmOverloads constructor( + /** + * The environment in which [node] is moving. + */ + val environment: Environment, + override val node: Node, + override val junctions: MutableMap, Int>> = LinkedHashMap(), +) : CellProperty

{ + + override var polarizationVersor: P = environment.makePosition(0, 0) + + override fun addPolarizationVersor(versor: P) { + val tempCor = (polarizationVersor + versor.coordinates).coordinates + val module = FastMath.hypot(tempCor[0], tempCor[1]) + polarizationVersor = if (module == 0.0) environment.makePosition(0, 0) else environment.makePosition( + tempCor[0] / module, + tempCor[1] / module, + ) + } + + override fun cloneOnNewNode(node: Node) = Cell(environment, node) +} diff --git a/alchemist-incarnation-biochemistry/src/main/kotlin/it/unibo/alchemist/model/implementations/properties/CircularCell.kt b/alchemist-incarnation-biochemistry/src/main/kotlin/it/unibo/alchemist/model/implementations/properties/CircularCell.kt new file mode 100644 index 0000000000..c66e4e4c48 --- /dev/null +++ b/alchemist-incarnation-biochemistry/src/main/kotlin/it/unibo/alchemist/model/implementations/properties/CircularCell.kt @@ -0,0 +1,27 @@ +/* + * Copyright (C) 2010-2022, Danilo Pianini and contributors + * listed, for each module, in the respective subproject's build.gradle.kts file. + * + * This file is part of Alchemist, and is distributed under the terms of the + * GNU General Public License, with a linking exception, + * as described in the file LICENSE in the Alchemist distribution's top directory. + */ + +package it.unibo.alchemist.model.implementations.properties + +import it.unibo.alchemist.model.implementations.molecules.Junction +import it.unibo.alchemist.model.interfaces.Environment +import it.unibo.alchemist.model.interfaces.Node +import it.unibo.alchemist.model.interfaces.Position +import it.unibo.alchemist.model.interfaces.properties.CellProperty +import it.unibo.alchemist.model.interfaces.properties.CircularCellProperty + +/** + * Base implementation of a [CircularCellProperty]. + */ +class CircularCell

> @JvmOverloads constructor( + environment: Environment, + override val node: Node, + override val diameter: Double = 0.0, + override val junctions: MutableMap, Int>> = LinkedHashMap(), +) : CircularCellProperty

, CellProperty

by Cell(environment, node, junctions) diff --git a/alchemist-incarnation-biochemistry/src/main/kotlin/it/unibo/alchemist/model/implementations/properties/CircularDeformableCell.kt b/alchemist-incarnation-biochemistry/src/main/kotlin/it/unibo/alchemist/model/implementations/properties/CircularDeformableCell.kt new file mode 100644 index 0000000000..e4a33c0b8a --- /dev/null +++ b/alchemist-incarnation-biochemistry/src/main/kotlin/it/unibo/alchemist/model/implementations/properties/CircularDeformableCell.kt @@ -0,0 +1,40 @@ +/* + * Copyright (C) 2010-2022, Danilo Pianini and contributors + * listed, for each module, in the respective subproject's build.gradle.kts file. + * + * This file is part of Alchemist, and is distributed under the terms of the + * GNU General Public License, with a linking exception, + * as described in the file LICENSE in the Alchemist distribution's top directory. + */ + +package it.unibo.alchemist.model.implementations.properties + +import it.unibo.alchemist.model.implementations.molecules.Junction +import it.unibo.alchemist.model.interfaces.Environment +import it.unibo.alchemist.model.interfaces.Node +import it.unibo.alchemist.model.interfaces.Position +import it.unibo.alchemist.model.interfaces.properties.CircularCellProperty +import it.unibo.alchemist.model.interfaces.properties.CircularDeformableCellProperty + +/** + * Base implementation of a [CircularCellProperty]. + */ +class CircularDeformableCell

> @JvmOverloads constructor( + environment: Environment, + override val node: Node, + override val maximumDiameter: Double, + override val rigidity: Double, + override val junctions: MutableMap, Int>> = LinkedHashMap(), +) : CircularDeformableCellProperty

, + CircularCellProperty

by CircularCell( + environment, + node, + maximumDiameter * rigidity, + junctions, + ) { + init { + require(rigidity in 0.0..1.0) { + "deformability must be between 0 and 1" + } + } +} diff --git a/alchemist-incarnation-biochemistry/src/main/kotlin/it/unibo/alchemist/model/interfaces/properties/CellProperty.kt b/alchemist-incarnation-biochemistry/src/main/kotlin/it/unibo/alchemist/model/interfaces/properties/CellProperty.kt new file mode 100644 index 0000000000..182187d461 --- /dev/null +++ b/alchemist-incarnation-biochemistry/src/main/kotlin/it/unibo/alchemist/model/interfaces/properties/CellProperty.kt @@ -0,0 +1,110 @@ +/* + * Copyright (C) 2010-2022, Danilo Pianini and contributors + * listed, for each module, in the respective subproject's build.gradle.kts file. + * + * This file is part of Alchemist, and is distributed under the terms of the + * GNU General Public License, with a linking exception, + * as described in the file LICENSE in the Alchemist distribution's top directory. + */ + +package it.unibo.alchemist.model.interfaces.properties + +import it.unibo.alchemist.model.implementations.molecules.Junction +import it.unibo.alchemist.model.interfaces.NodeProperty +import it.unibo.alchemist.model.interfaces.Node +import it.unibo.alchemist.model.interfaces.Position + +/** + * A node's capability to behave as a cell. + */ +interface CellProperty

> : NodeProperty { + + /** + * The map junction - node - quantity. + */ + val junctions: MutableMap, Int>> + + /** + * Add a junction to the current node. + * [junction] the junction. + * [neighbor] the neighbor node at the other side of the junction. + */ + fun addJunction(junction: Junction, neighbor: Node) { + if (containsJunction(junction)) { + junctions[junction]?.let { + if (it.containsKey(neighbor)) it[neighbor] = it[neighbor]?.plus(1) ?: 1 + else it[neighbor] = 1 + junctions[junction] = it + } + } else { + val tmp: MutableMap, Int> = LinkedHashMap(1) + tmp[neighbor] = 1 + junctions[junction] = tmp + } + } + + /** + * Return true if a junction is present in the current node, false otherwise. + * Note: a junction is considered present if the method junction.equals(j) return true. + * [junction] the junction. + */ + fun containsJunction(junction: Junction) = junctions.containsKey(junction) + + /** + * Removes a [junction] from this [node]. + * + * [neighbor] the node at the other side of the junction. + */ + fun removeJunction(junction: Junction, neighbor: Node) { + if (containsJunction(junction)) { + val inner: MutableMap, Int>? = junctions[junction] + inner?.let { + when (it[neighbor]) { + 1 -> it.remove(neighbor) + else -> it[neighbor]?.minus(1) + } + if (it.isEmpty()) { + junctions.remove(junction) + } else { + junctions[junction] = inner + } + junction.moleculesInCurrentNode.forEach { (biomolecule, value) -> + with(node) { + setConcentration(biomolecule, getConcentration(biomolecule) + value) + } + } + } + } + } + + /** + * Returns a set of [Node]s which are linked with the current node by a junction of the type [junction]. + */ + fun getNeighborLinkWithJunction(junction: Junction): Set> { + return junctions.getOrElse(junction) { LinkedHashMap() }.keys + } + + /** + * Returns set of [Node]s which are linked by a junction with the current [node]. + */ + fun getAllNodesLinkWithJunction(): Set> { + return junctions.values.flatMap { it.keys }.toSet() + } + + /** + * The total number of junctions presents in this [node]. + */ + val junctionsCount: Int + get() = junctions.values.flatMap { it.values }.sum() + + /** + * The polarization versor, e.g. a versor indicating the direction in which the cell will move the next time. + */ + var polarizationVersor: P + + /** + * add [versor] to the polarization versor inside the cell; useful for considering the combination of various + * stimuli in a cell. + */ + fun addPolarizationVersor(versor: P) +} diff --git a/alchemist-incarnation-biochemistry/src/main/kotlin/it/unibo/alchemist/model/interfaces/properties/CircularCellProperty.kt b/alchemist-incarnation-biochemistry/src/main/kotlin/it/unibo/alchemist/model/interfaces/properties/CircularCellProperty.kt new file mode 100644 index 0000000000..12f515c49e --- /dev/null +++ b/alchemist-incarnation-biochemistry/src/main/kotlin/it/unibo/alchemist/model/interfaces/properties/CircularCellProperty.kt @@ -0,0 +1,27 @@ +/* + * Copyright (C) 2010-2022, Danilo Pianini and contributors + * listed, for each module, in the respective subproject's build.gradle.kts file. + * + * This file is part of Alchemist, and is distributed under the terms of the + * GNU General Public License, with a linking exception, + * as described in the file LICENSE in the Alchemist distribution's top directory. + */ + +package it.unibo.alchemist.model.interfaces.properties + +import it.unibo.alchemist.model.interfaces.Position + +/** + * A node's capability to behave as a cell with a circular area. + */ +interface CircularCellProperty

> : CellProperty

{ + /** + * The diameter of the cell. + */ + val diameter: Double + + /** + * The radius of the cell. + */ + val radius: Double get() = diameter / 2 +} diff --git a/alchemist-incarnation-biochemistry/src/main/kotlin/it/unibo/alchemist/model/interfaces/properties/CircularDeformableCellProperty.kt b/alchemist-incarnation-biochemistry/src/main/kotlin/it/unibo/alchemist/model/interfaces/properties/CircularDeformableCellProperty.kt new file mode 100644 index 0000000000..e8e153de7f --- /dev/null +++ b/alchemist-incarnation-biochemistry/src/main/kotlin/it/unibo/alchemist/model/interfaces/properties/CircularDeformableCellProperty.kt @@ -0,0 +1,34 @@ +/* + * Copyright (C) 2010-2022, Danilo Pianini and contributors + * listed, for each module, in the respective subproject's build.gradle.kts file. + * + * This file is part of Alchemist, and is distributed under the terms of the + * GNU General Public License, with a linking exception, + * as described in the file LICENSE in the Alchemist distribution's top directory. + */ + +package it.unibo.alchemist.model.interfaces.properties + +import it.unibo.alchemist.model.interfaces.Position + +/** + * A node's capability to behave as a circular deformable cell. + */ +interface CircularDeformableCellProperty

> : CircularCellProperty

{ + /** + * + * The max diameter that this cell can have, e.g. the diameter that this cell has if no other cell is around. + */ + val maximumDiameter: Double + /** + * + * The max radius that this cell can have, e.g. the radius that this cell has if no other cell is around. + */ + val maximumRadius: Double + get() = maximumDiameter / 2 + + /** + * Cellular rigidity. + */ + val rigidity: Double +} diff --git a/alchemist-incarnation-biochemistry/src/test/java/it/unibo/alchemist/test/TestBioRect2DEnvironmentNoOverlap.java b/alchemist-incarnation-biochemistry/src/test/java/it/unibo/alchemist/test/TestBioRect2DEnvironmentNoOverlap.java index bc3c5c974f..970aaf219e 100644 --- a/alchemist-incarnation-biochemistry/src/test/java/it/unibo/alchemist/test/TestBioRect2DEnvironmentNoOverlap.java +++ b/alchemist-incarnation-biochemistry/src/test/java/it/unibo/alchemist/test/TestBioRect2DEnvironmentNoOverlap.java @@ -14,15 +14,15 @@ import it.unibo.alchemist.loader.LoadAlchemist; import it.unibo.alchemist.model.implementations.environments.BioRect2DEnvironmentNoOverlap; import it.unibo.alchemist.model.implementations.linkingrules.NoLinks; -import it.unibo.alchemist.model.implementations.nodes.CellNodeImpl; import it.unibo.alchemist.model.implementations.positions.Euclidean2DPosition; -import it.unibo.alchemist.model.interfaces.CellWithCircularArea; import it.unibo.alchemist.model.interfaces.Environment; import it.unibo.alchemist.model.interfaces.Node; import it.unibo.alchemist.model.interfaces.Position; import it.unibo.alchemist.model.interfaces.Reaction; import it.unibo.alchemist.model.interfaces.Time; +import it.unibo.alchemist.model.interfaces.properties.CircularCellProperty; import it.unibo.alchemist.model.interfaces.environments.Euclidean2DEnvironment; +import org.apache.commons.math3.random.MersenneTwister; import org.apache.commons.math3.util.FastMath; import org.apache.commons.math3.util.Pair; import org.jooq.lambda.fi.util.function.CheckedConsumer; @@ -32,6 +32,7 @@ import javax.annotation.Nonnull; import java.util.Collections; +import java.util.List; import java.util.Map; import java.util.function.Supplier; import java.util.stream.Collectors; @@ -107,32 +108,36 @@ class TestBioRect2DEnvironmentNoOverlap { private static final Euclidean2DPosition NODE_POS13_3 = new Euclidean2DPosition(-5, -5); private static final Euclidean2DPosition NODE_POS13_4 = new Euclidean2DPosition(5, -5); private final Euclidean2DPosition originalPos = new Euclidean2DPosition(0, 0); - private CellWithCircularArea ng1; - private CellWithCircularArea ng2; - private CellWithCircularArea ng3; - private CellWithCircularArea nm1; - private CellWithCircularArea nm2; - private CellWithCircularArea np1; - private CellWithCircularArea np2; - private CellWithCircularArea np3; + private Node ng1; + private Node ng2; + private Node ng3; + private Node nm1; + private Node nm2; + private Node np1; + private Node np2; + private Node np3; - private Euclidean2DEnvironment env; + private Euclidean2DEnvironment environment; + + private Node createNode(final double diameter) { + return environment.getIncarnation().createNode(new MersenneTwister(), environment, Double.toString(diameter)); + } /** * */ @BeforeEach void setUp() { - env = new BioRect2DEnvironmentNoOverlap(); - env.setLinkingRule(new NoLinks<>()); - ng1 = new CellNodeImpl<>(env, BIG_CELL_DIAMETER); - ng2 = new CellNodeImpl<>(env, BIG_CELL_DIAMETER); - ng3 = new CellNodeImpl<>(env, BIG_CELL_DIAMETER); - nm1 = new CellNodeImpl<>(env, MEDIUM_CELL_DIAMETER); - nm2 = new CellNodeImpl<>(env, MEDIUM_CELL_DIAMETER); - np1 = new CellNodeImpl<>(env, LITTLE_CELL_DIAMETER); - np2 = new CellNodeImpl<>(env, LITTLE_CELL_DIAMETER); - np3 = new CellNodeImpl<>(env, LITTLE_CELL_DIAMETER); + environment = new BioRect2DEnvironmentNoOverlap(); + environment.setLinkingRule(new NoLinks<>()); + ng1 = createNode(BIG_CELL_DIAMETER); + ng2 = createNode(BIG_CELL_DIAMETER); + ng3 = createNode(BIG_CELL_DIAMETER); + nm1 = createNode(MEDIUM_CELL_DIAMETER); + nm2 = createNode(MEDIUM_CELL_DIAMETER); + np1 = createNode(LITTLE_CELL_DIAMETER); + np2 = createNode(LITTLE_CELL_DIAMETER); + np3 = createNode(LITTLE_CELL_DIAMETER); } /** @@ -140,62 +145,62 @@ void setUp() { */ @Test void testAddNode() { - final CellWithCircularArea n1 = new CellNodeImpl<>(env, LITTLE_CELL_DIAMETER); - final CellWithCircularArea n2 = new CellNodeImpl<>(env, LITTLE_CELL_DIAMETER); - final CellWithCircularArea n3 = new CellNodeImpl<>(env, LITTLE_CELL_DIAMETER); - final CellWithCircularArea n4 = new CellNodeImpl<>(env, LITTLE_CELL_DIAMETER); - final CellWithCircularArea n5 = new CellNodeImpl<>(env, LITTLE_CELL_DIAMETER); - final CellWithCircularArea n6 = new CellNodeImpl<>(env, LITTLE_CELL_DIAMETER); - final CellWithCircularArea n7 = new CellNodeImpl<>(env, LITTLE_CELL_DIAMETER); - final CellWithCircularArea n8 = new CellNodeImpl<>(env, LITTLE_CELL_DIAMETER); - final CellWithCircularArea n9 = new CellNodeImpl<>(env, LITTLE_CELL_DIAMETER); - final CellWithCircularArea n10 = new CellNodeImpl<>(env, LITTLE_CELL_DIAMETER); - final CellWithCircularArea n11 = new CellNodeImpl<>(env, LITTLE_CELL_DIAMETER); + final Node n1 = createNode(LITTLE_CELL_DIAMETER); + final Node n2 = createNode(LITTLE_CELL_DIAMETER); + final Node n3 = createNode(LITTLE_CELL_DIAMETER); + final Node n4 = createNode(LITTLE_CELL_DIAMETER); + final Node n5 = createNode(LITTLE_CELL_DIAMETER); + final Node n6 = createNode(LITTLE_CELL_DIAMETER); + final Node n7 = createNode(LITTLE_CELL_DIAMETER); + final Node n8 = createNode(LITTLE_CELL_DIAMETER); + final Node n9 = createNode(LITTLE_CELL_DIAMETER); + final Node n10 = createNode(LITTLE_CELL_DIAMETER); + final Node n11 = createNode(LITTLE_CELL_DIAMETER); final Euclidean2DPosition p2 = new Euclidean2DPosition(10, 0); - env.addNode(n1, originalPos); - env.addNode(n2, p2); + environment.addNode(n1, originalPos); + environment.addNode(n2, p2); final Euclidean2DPosition p3 = new Euclidean2DPosition(0, 20); // this should be added - env.addNode(n3, p3); - assertEquals(env.getPosition(n3), p3, getFailureTestString("n3", n3, p3)); - env.removeNode(n3); + environment.addNode(n3, p3); + assertEquals(environment.getPosition(n3), p3, getFailureTestString("n3", n3, p3)); + environment.removeNode(n3); final Euclidean2DPosition p4 = new Euclidean2DPosition(0, 10); // this should be added - env.addNode(n4, p4); + environment.addNode(n4, p4); verifyAdded(n4, p4); - env.removeNode(n4); + environment.removeNode(n4); final Euclidean2DPosition p5 = new Euclidean2DPosition(0, 5); // this should not be added - env.addNode(n5, p5); + environment.addNode(n5, p5); verifyNotAdded(n5); final Euclidean2DPosition p6 = new Euclidean2DPosition(5, 0); // this should not be added - env.addNode(n6, p6); + environment.addNode(n6, p6); verifyNotAdded(n6); final Euclidean2DPosition p7 = new Euclidean2DPosition(0, 0); // this should not be added - env.addNode(n7, p7); + environment.addNode(n7, p7); verifyNotAdded(n7); final Euclidean2DPosition p8 = new Euclidean2DPosition(10, 0); // this should not be added - env.addNode(n8, p8); + environment.addNode(n8, p8); verifyNotAdded(n8); final Euclidean2DPosition p9 = new Euclidean2DPosition(20, 0); // this should be added - env.addNode(n9, p9); + environment.addNode(n9, p9); verifyAdded(n9, p9); - env.removeNode(n9); + environment.removeNode(n9); final Euclidean2DPosition p10 = new Euclidean2DPosition(2.5, 2.5); // this should not be added - env.addNode(n10, p10); + environment.addNode(n10, p10); verifyNotAdded(n10); final Euclidean2DPosition p11 = new Euclidean2DPosition(7.5, -2.5); // this should not be added - env.addNode(n11, p11); + environment.addNode(n11, p11); verifyNotAdded(n11); - env.removeNode(n1); - env.removeNode(n2); + environment.removeNode(n1); + environment.removeNode(n2); } private void verifyNotAdded(final Node node) { - assertFalse(env.getNodes().contains(node)); + assertFalse(environment.getNodes().contains(node)); } private void verifyAdded(final Node node, @Nonnull final Position expected) { - final Position position = env.getPosition(node); + final Position position = environment.getPosition(node); final Supplier message = () -> getFailureTestString("node" + node.getId(), node, position); assertEquals(expected, position, message); } @@ -205,19 +210,19 @@ private void verifyAdded(final Node node, @Nonnull final Position exp */ @Test void testMoveNode1() { - final CellWithCircularArea cellToMove1 = new CellNodeImpl<>(env, LITTLE_CELL_DIAMETER); - env.addNode(cellToMove1, originalPos); + final Node cellToMove1 = createNode(LITTLE_CELL_DIAMETER); + environment.addNode(cellToMove1, originalPos); final Euclidean2DPosition p1 = new Euclidean2DPosition(40, 0); - final CellWithCircularArea c1 = new CellNodeImpl<>(env, LITTLE_CELL_DIAMETER); - env.addNode(c1, p1); - env.moveNode(cellToMove1, POSITION_TO_MOVE1); + final Node c1 = createNode(LITTLE_CELL_DIAMETER); + environment.addNode(c1, p1); + environment.moveNode(cellToMove1, POSITION_TO_MOVE1); assertEquals( - env.getPosition(cellToMove1), + environment.getPosition(cellToMove1), EXPECTED_POS1, - "cellToMove1 is in position: " + env.getPosition(cellToMove1) + "cellToMove1 is in position: " + environment.getPosition(cellToMove1) ); - env.removeNode(cellToMove1); - env.removeNode(c1); + environment.removeNode(cellToMove1); + environment.removeNode(c1); } /** @@ -225,23 +230,23 @@ void testMoveNode1() { */ @Test void testMoveNode2() { - final CellWithCircularArea cellToMove2 = new CellNodeImpl<>(env, LITTLE_CELL_DIAMETER); - env.addNode(cellToMove2, originalPos); + final Node cellToMove2 = createNode(LITTLE_CELL_DIAMETER); + environment.addNode(cellToMove2, originalPos); final Euclidean2DPosition p2 = new Euclidean2DPosition(40, 5); final Euclidean2DPosition p3 = new Euclidean2DPosition(40, -5); - final CellWithCircularArea c2 = new CellNodeImpl<>(env, LITTLE_CELL_DIAMETER); - final CellWithCircularArea c3 = new CellNodeImpl<>(env, LITTLE_CELL_DIAMETER); - env.addNode(c2, p2); - env.addNode(c3, p3); - env.moveNode(cellToMove2, POSITION_TO_MOVE2); + final Node c2 = createNode(LITTLE_CELL_DIAMETER); + final Node c3 = createNode(LITTLE_CELL_DIAMETER); + environment.addNode(c2, p2); + environment.addNode(c3, p3); + environment.moveNode(cellToMove2, POSITION_TO_MOVE2); assertEquals( - env.getPosition(cellToMove2), + environment.getPosition(cellToMove2), EXPECTED_POS2, - "cellToMove2 is in position: " + env.getPosition(cellToMove2) + "cellToMove2 is in position: " + environment.getPosition(cellToMove2) ); - env.removeNode(cellToMove2); - env.removeNode(c2); - env.removeNode(c3); + environment.removeNode(cellToMove2); + environment.removeNode(c2); + environment.removeNode(c3); } /** @@ -249,19 +254,19 @@ void testMoveNode2() { */ @Test void testMoveNode3() { - final CellWithCircularArea cellToMove3 = new CellNodeImpl<>(env, LITTLE_CELL_DIAMETER); - env.addNode(cellToMove3, originalPos); + final Node cellToMove3 = createNode(LITTLE_CELL_DIAMETER); + environment.addNode(cellToMove3, originalPos); final Euclidean2DPosition p4 = new Euclidean2DPosition(10, 0); - final CellWithCircularArea c4 = new CellNodeImpl<>(env, LITTLE_CELL_DIAMETER); - env.addNode(c4, p4); - env.moveNode(cellToMove3, POSITION_TO_MOVE3); + final Node c4 = createNode(LITTLE_CELL_DIAMETER); + environment.addNode(c4, p4); + environment.moveNode(cellToMove3, POSITION_TO_MOVE3); assertEquals( - env.getPosition(cellToMove3), + environment.getPosition(cellToMove3), originalPos, - "cellToMove3 is in position: " + env.getPosition(cellToMove3) + "cellToMove3 is in position: " + environment.getPosition(cellToMove3) ); - env.removeNode(cellToMove3); - env.removeNode(c4); + environment.removeNode(cellToMove3); + environment.removeNode(c4); } /** @@ -269,22 +274,23 @@ void testMoveNode3() { */ @Test void testMoveNode4() { - final CellWithCircularArea cellToMove4 = new CellNodeImpl<>(env, LITTLE_CELL_DIAMETER); - env.addNode(cellToMove4, originalPos); + final Node cellToMove4 = createNode(LITTLE_CELL_DIAMETER); + environment.addNode(cellToMove4, originalPos); final Euclidean2DPosition p5 = new Euclidean2DPosition( 0.2, - FastMath.sqrt(FastMath.pow(cellToMove4.getDiameter(), 2) - FastMath.pow(0.2, 2)) + FastMath.sqrt(FastMath.pow(cellToMove4 + .asProperty(CircularCellProperty.class).getDiameter(), 2) - FastMath.pow(0.2, 2)) ); - final CellWithCircularArea c5 = new CellNodeImpl<>(env, LITTLE_CELL_DIAMETER); - env.addNode(c5, p5); - env.moveNode(cellToMove4, POSITION_TO_MOVE4); + final Node c5 = createNode(LITTLE_CELL_DIAMETER); + environment.addNode(c5, p5); + environment.moveNode(cellToMove4, POSITION_TO_MOVE4); assertNotEquals( - env.getPosition(cellToMove4), + environment.getPosition(cellToMove4), POSITION_TO_MOVE4, - "cellToMove4 is in position: " + env.getPosition(cellToMove4) + "cellToMove4 is in position: " + environment.getPosition(cellToMove4) ); - env.removeNode(cellToMove4); - env.removeNode(c5); + environment.removeNode(cellToMove4); + environment.removeNode(c5); } /** @@ -292,19 +298,19 @@ void testMoveNode4() { */ @Test void testMoveNode5() { - final CellWithCircularArea cellToMove5 = new CellNodeImpl<>(env, LITTLE_CELL_DIAMETER); - env.addNode(cellToMove5, originalPos); + final Node cellToMove5 = createNode(LITTLE_CELL_DIAMETER); + environment.addNode(cellToMove5, originalPos); final Euclidean2DPosition p6 = new Euclidean2DPosition(20, 10); - final CellWithCircularArea c6 = new CellNodeImpl<>(env, LITTLE_CELL_DIAMETER); - env.addNode(c6, p6); - env.moveNode(cellToMove5, POSITION_TO_MOVE5); + final Node c6 = createNode(LITTLE_CELL_DIAMETER); + environment.addNode(c6, p6); + environment.moveNode(cellToMove5, POSITION_TO_MOVE5); assertEquals( - env.getPosition(cellToMove5), + environment.getPosition(cellToMove5), POSITION_TO_MOVE5, - "cellToMove5 is in position: " + env.getPosition(cellToMove5) + "cellToMove5 is in position: " + environment.getPosition(cellToMove5) ); - env.removeNode(cellToMove5); - env.removeNode(c6); + environment.removeNode(cellToMove5); + environment.removeNode(c6); } /** @@ -312,19 +318,19 @@ void testMoveNode5() { */ @Test void testMoveNode6() { - final CellWithCircularArea cellToMove6 = new CellNodeImpl<>(env, LITTLE_CELL_DIAMETER); - env.addNode(cellToMove6, originalPos); + final Node cellToMove6 = createNode(LITTLE_CELL_DIAMETER); + environment.addNode(cellToMove6, originalPos); final Euclidean2DPosition p7 = new Euclidean2DPosition(-40, 0); - final CellWithCircularArea c7 = new CellNodeImpl<>(env, LITTLE_CELL_DIAMETER); - env.addNode(c7, p7); - env.moveNode(cellToMove6, POSITION_TO_MOVE6); + final Node c7 = createNode(LITTLE_CELL_DIAMETER); + environment.addNode(c7, p7); + environment.moveNode(cellToMove6, POSITION_TO_MOVE6); assertEquals( - env.getPosition(cellToMove6), + environment.getPosition(cellToMove6), EXPECTED_POS6, - "cellToMove6 is in position: " + env.getPosition(cellToMove6) + "cellToMove6 is in position: " + environment.getPosition(cellToMove6) ); - env.removeNode(cellToMove6); - env.removeNode(c7); + environment.removeNode(cellToMove6); + environment.removeNode(c7); } /** @@ -332,16 +338,16 @@ void testMoveNode6() { */ @Test void testMoveNode7() { - final CellWithCircularArea cellToMove7 = new CellNodeImpl<>(env, LITTLE_CELL_DIAMETER); - env.addNode(cellToMove7, originalPos); + final Node cellToMove7 = createNode(LITTLE_CELL_DIAMETER); + environment.addNode(cellToMove7, originalPos); final Euclidean2DPosition p8 = new Euclidean2DPosition(40, 40); - final CellWithCircularArea c8 = new CellNodeImpl<>(env, LITTLE_CELL_DIAMETER); - env.addNode(c8, p8); - env.moveNode(cellToMove7, POSITION_TO_MOVE7); - assertTrueJUnit4("cellToMove7 is in position: " + env.getPosition(cellToMove7), - EXPECTED_POS7.equals(env.getPosition(cellToMove7))); - env.removeNode(cellToMove7); - env.removeNode(c8); + final Node c8 = createNode(LITTLE_CELL_DIAMETER); + environment.addNode(c8, p8); + environment.moveNode(cellToMove7, POSITION_TO_MOVE7); + assertTrueJUnit4("cellToMove7 is in position: " + environment.getPosition(cellToMove7), + EXPECTED_POS7.equals(environment.getPosition(cellToMove7))); + environment.removeNode(cellToMove7); + environment.removeNode(c8); } /** @@ -349,16 +355,16 @@ void testMoveNode7() { */ @Test void testMoveNode8() { - final CellWithCircularArea cellToMove8 = new CellNodeImpl<>(env, LITTLE_CELL_DIAMETER); - env.addNode(cellToMove8, originalPos); + final Node cellToMove8 = createNode(LITTLE_CELL_DIAMETER); + environment.addNode(cellToMove8, originalPos); final Euclidean2DPosition p9 = new Euclidean2DPosition(-40, -40); - final CellWithCircularArea c9 = new CellNodeImpl<>(env, LITTLE_CELL_DIAMETER); - env.addNode(c9, p9); - env.moveNode(cellToMove8, POSITION_TO_MOVE8); - assertTrueJUnit4("cellToMove8 is in position: " + env.getPosition(cellToMove8), - EXPECTED_POS8.equals(env.getPosition(cellToMove8))); - env.removeNode(cellToMove8); - env.removeNode(c9); + final Node c9 = createNode(LITTLE_CELL_DIAMETER); + environment.addNode(c9, p9); + environment.moveNode(cellToMove8, POSITION_TO_MOVE8); + assertTrueJUnit4("cellToMove8 is in position: " + environment.getPosition(cellToMove8), + EXPECTED_POS8.equals(environment.getPosition(cellToMove8))); + environment.removeNode(cellToMove8); + environment.removeNode(c9); } /* @@ -370,17 +376,17 @@ void testMoveNode8() { */ @Test void testAddDifferentDiam1() { - env.addNode(ng1, originalPos); + environment.addNode(ng1, originalPos); final Euclidean2DPosition p1 = new Euclidean2DPosition(10, 0); final Euclidean2DPosition p2 = new Euclidean2DPosition(0, 10); final Euclidean2DPosition p3 = new Euclidean2DPosition(-10, -10); - env.addNode(np1, p1); - env.addNode(np2, p2); - env.addNode(np3, p3); - nodeNotInEnvironment(env, np1); - nodeNotInEnvironment(env, np2); - nodeNotInEnvironment(env, np3); + environment.addNode(np1, p1); + environment.addNode(np2, p2); + environment.addNode(np3, p3); + nodeNotInEnvironment(environment, np1); + nodeNotInEnvironment(environment, np2); + nodeNotInEnvironment(environment, np3); } /** @@ -388,24 +394,24 @@ void testAddDifferentDiam1() { */ @Test void testAddDifferentDiam2() { - env.addNode(np1, originalPos); + environment.addNode(np1, originalPos); final Euclidean2DPosition p1 = new Euclidean2DPosition(10, 0); final Euclidean2DPosition p2 = new Euclidean2DPosition(20, 0); final Euclidean2DPosition p3 = new Euclidean2DPosition(30, 0); - env.addNode(ng1, p1); - env.addNode(ng2, p2); - env.addNode(ng3, p3); - nodeNotInEnvironment(env, ng1); + environment.addNode(ng1, p1); + environment.addNode(ng2, p2); + environment.addNode(ng3, p3); + nodeNotInEnvironment(environment, ng1); assertTrueJUnit4( getFailureTestString("ng2", ng2, p2), - env.getPosition(ng2).equals(p2) + environment.getPosition(ng2).equals(p2) ); - nodeNotInEnvironment(env, ng3); - env.removeNode(ng2); - env.addNode(ng3, p3); + nodeNotInEnvironment(environment, ng3); + environment.removeNode(ng2); + environment.addNode(ng3, p3); assertTrueJUnit4( getFailureTestString("ng3", ng3, p3), - env.getPosition(ng3).equals(p3) + environment.getPosition(ng3).equals(p3) ); } @@ -414,24 +420,24 @@ void testAddDifferentDiam2() { */ @Test void testAddDifferentDiam3() { - env.addNode(np1, originalPos); + environment.addNode(np1, originalPos); final Euclidean2DPosition p1 = new Euclidean2DPosition(20, 0); final Euclidean2DPosition p2 = new Euclidean2DPosition(0, 15); final Euclidean2DPosition p3 = new Euclidean2DPosition(10, 10); - env.addNode(ng1, p1); - env.addNode(nm1, p2); - env.addNode(nm2, p3); - env.addNode(np2, p3); + environment.addNode(ng1, p1); + environment.addNode(nm1, p2); + environment.addNode(nm2, p3); + environment.addNode(np2, p3); assertTrueJUnit4( getFailureTestString("ng1", ng1, p1), - env.getPosition(ng1).equals(p1) + environment.getPosition(ng1).equals(p1) ); assertTrueJUnit4( getFailureTestString("nm1", nm1, p2), - env.getPosition(nm1).equals(p2) + environment.getPosition(nm1).equals(p2) ); - nodeNotInEnvironment(env, nm2); - nodeNotInEnvironment(env, np2); + nodeNotInEnvironment(environment, nm2); + nodeNotInEnvironment(environment, np2); } /** @@ -439,15 +445,15 @@ void testAddDifferentDiam3() { */ @Test void testMoveDifferentDiam1() { - final CellWithCircularArea cellToMove1 = np1; - env.addNode(cellToMove1, originalPos); + final Node cellToMove1 = np1; + environment.addNode(cellToMove1, originalPos); final Euclidean2DPosition pd = new Euclidean2DPosition(50, 0); final Euclidean2DPosition p1 = new Euclidean2DPosition(25, 20); - env.addNode(ng1, p1); - env.moveNode(cellToMove1, pd); - assertTrueJUnit4("cellToMove1 is in position: " + env.getPosition(cellToMove1), - env.getPosition(cellToMove1).equals(pd)); + environment.addNode(ng1, p1); + environment.moveNode(cellToMove1, pd); + assertTrueJUnit4("cellToMove1 is in position: " + environment.getPosition(cellToMove1), + environment.getPosition(cellToMove1).equals(pd)); } /** @@ -456,14 +462,14 @@ void testMoveDifferentDiam1() { @Test void testMoveDifferentDiam2() { final Euclidean2DPosition pd = new Euclidean2DPosition(50, 0); - final CellWithCircularArea cellToMove2 = np2; - env.addNode(cellToMove2, originalPos); - final CellWithCircularArea bce = new CellNodeImpl<>(env, 50); + final Node cellToMove2 = np2; + environment.addNode(cellToMove2, originalPos); + final Node bce = createNode(50); final Euclidean2DPosition p2 = new Euclidean2DPosition(25, 30); - env.addNode(bce, p2); - env.moveNode(cellToMove2, pd); - assertTrueJUnit4("cellToMove2 is in position: " + env.getPosition(cellToMove2), - env.getPosition(cellToMove2).equals(pd)); + environment.addNode(bce, p2); + environment.moveNode(cellToMove2, pd); + assertTrueJUnit4("cellToMove2 is in position: " + environment.getPosition(cellToMove2), + environment.getPosition(cellToMove2).equals(pd)); } /** @@ -472,22 +478,22 @@ void testMoveDifferentDiam2() { @Test void testMoveDifferentDiam3() { final Euclidean2DPosition pd = new Euclidean2DPosition(50, 0); - final CellWithCircularArea cellToMove3 = np3; - env.addNode(cellToMove3, originalPos); + final Node cellToMove3 = np3; + environment.addNode(cellToMove3, originalPos); final Euclidean2DPosition p1 = new Euclidean2DPosition(25, 0); - env.addNode(ng1, p1); - env.moveNode(cellToMove3, pd); + environment.addNode(ng1, p1); + environment.moveNode(cellToMove3, pd); assertTrueJUnit4( - "cellToMove3 is in position: " + env.getPosition(cellToMove3), - EXPECTED_POS_DIFFDIAM3_1.equals(env.getPosition(cellToMove3)) + "cellToMove3 is in position: " + environment.getPosition(cellToMove3), + EXPECTED_POS_DIFFDIAM3_1.equals(environment.getPosition(cellToMove3)) ); - env.removeNode(ng1); - env.addNode(nm1, p1); - env.moveNode(cellToMove3, pd); + environment.removeNode(ng1); + environment.addNode(nm1, p1); + environment.moveNode(cellToMove3, pd); assertTrueJUnit4( - "cellToMove3 is in position: " + env.getPosition(cellToMove3), - EXPECTED_POS_DIFFDIAM3_2.equals(env.getPosition(cellToMove3)) + "cellToMove3 is in position: " + environment.getPosition(cellToMove3), + EXPECTED_POS_DIFFDIAM3_2.equals(environment.getPosition(cellToMove3)) ); } @@ -497,22 +503,22 @@ void testMoveDifferentDiam3() { @Test void testMoveDifferentDiam4() { final Euclidean2DPosition pd = new Euclidean2DPosition(-50, 0); - final CellWithCircularArea cellToMove4 = np3; - env.addNode(cellToMove4, originalPos); + final Node cellToMove4 = np3; + environment.addNode(cellToMove4, originalPos); final Euclidean2DPosition p1 = new Euclidean2DPosition(-25, 0); - env.addNode(ng1, p1); - env.moveNode(cellToMove4, pd); + environment.addNode(ng1, p1); + environment.moveNode(cellToMove4, pd); assertTrueJUnit4( - "cellToMove4 is in position: " + env.getPosition(cellToMove4), - EXPECTED_POS_DIFFDIAM4_1.equals(env.getPosition(cellToMove4)) + "cellToMove4 is in position: " + environment.getPosition(cellToMove4), + EXPECTED_POS_DIFFDIAM4_1.equals(environment.getPosition(cellToMove4)) ); - env.removeNode(ng1); - env.addNode(nm1, p1); - env.moveNode(cellToMove4, pd); + environment.removeNode(ng1); + environment.addNode(nm1, p1); + environment.moveNode(cellToMove4, pd); assertTrueJUnit4( - "cellToMove4 is in position: " + env.getPosition(cellToMove4), - EXPECTED_POS_DIFFDIAM4_2.equals(env.getPosition(cellToMove4)) + "cellToMove4 is in position: " + environment.getPosition(cellToMove4), + EXPECTED_POS_DIFFDIAM4_2.equals(environment.getPosition(cellToMove4)) ); } @@ -522,24 +528,24 @@ void testMoveDifferentDiam4() { @Test void testMoveDifferentDiam5() { final Euclidean2DPosition pd = new Euclidean2DPosition(50, 50); - final CellWithCircularArea cellToMove5 = np3; - env.addNode(cellToMove5, originalPos); + final Node cellToMove5 = np3; + environment.addNode(cellToMove5, originalPos); final Euclidean2DPosition p1 = new Euclidean2DPosition(25, 25); - env.addNode(ng1, p1); - env.moveNode(cellToMove5, pd); + environment.addNode(ng1, p1); + environment.moveNode(cellToMove5, pd); assertNotEquals( - env.getPosition(cellToMove5), + environment.getPosition(cellToMove5), pd, - "cellToMove5 is in position: " + env.getPosition(cellToMove5) + "cellToMove5 is in position: " + environment.getPosition(cellToMove5) ); - env.removeNode(ng1); - env.addNode(nm1, p1); - env.moveNode(cellToMove5, pd); + environment.removeNode(ng1); + environment.addNode(nm1, p1); + environment.moveNode(cellToMove5, pd); assertNotEquals( - env.getPosition(cellToMove5), + environment.getPosition(cellToMove5), pd, - "cellToMove5 is in position: " + env.getPosition(cellToMove5) + "cellToMove5 is in position: " + environment.getPosition(cellToMove5) ); } @@ -548,18 +554,18 @@ void testMoveDifferentDiam5() { */ @Test void testMoveDifferentDiam6() { - final CellWithCircularArea cellToMove6 = np1; - env.addNode(cellToMove6, originalPos); + final Node cellToMove6 = np1; + environment.addNode(cellToMove6, originalPos); final Euclidean2DPosition pd = new Euclidean2DPosition(50, 0); final Euclidean2DPosition p1 = new Euclidean2DPosition(25, 20); - env.addNode(ng1, p1); + environment.addNode(ng1, p1); final Euclidean2DPosition p2 = new Euclidean2DPosition(-10, 0); - env.addNode(np2, p2); - env.moveNode(cellToMove6, pd); + environment.addNode(np2, p2); + environment.moveNode(cellToMove6, pd); assertTrueJUnit4( - "cellToMove6 is in position: " + env.getPosition(cellToMove6), - env.getPosition(cellToMove6).equals(pd) + "cellToMove6 is in position: " + environment.getPosition(cellToMove6), + environment.getPosition(cellToMove6).equals(pd) ); } @@ -568,17 +574,17 @@ void testMoveDifferentDiam6() { */ @Test void testMoveDifferentDiam7() { - final CellWithCircularArea cellToMove7 = np1; - env.addNode(cellToMove7, originalPos); + final Node cellToMove7 = np1; + environment.addNode(cellToMove7, originalPos); final Euclidean2DPosition pd = new Euclidean2DPosition(50, 0); final Euclidean2DPosition p1 = new Euclidean2DPosition(25, 20); - env.addNode(ng1, p1); + environment.addNode(ng1, p1); final Euclidean2DPosition p2 = new Euclidean2DPosition(60, 5); - env.addNode(np2, p2); - env.moveNode(cellToMove7, pd); - assertTrueJUnit4("cellToMove7 is in position: " + env.getPosition(cellToMove7), - env.getPosition(cellToMove7).equals(pd)); + environment.addNode(np2, p2); + environment.moveNode(cellToMove7, pd); + assertTrueJUnit4("cellToMove7 is in position: " + environment.getPosition(cellToMove7), + environment.getPosition(cellToMove7).equals(pd)); } /** @@ -586,17 +592,17 @@ void testMoveDifferentDiam7() { */ @Test void testMoveDifferentDiam8() { - final CellWithCircularArea cellToMove8 = np1; - env.addNode(cellToMove8, originalPos); + final Node cellToMove8 = np1; + environment.addNode(cellToMove8, originalPos); final Euclidean2DPosition pd = new Euclidean2DPosition(50, 0); final Euclidean2DPosition p1 = new Euclidean2DPosition(25, 20); - env.addNode(ng1, p1); + environment.addNode(ng1, p1); final Euclidean2DPosition p2 = new Euclidean2DPosition(0, 10); - env.addNode(np2, p2); - env.moveNode(cellToMove8, pd); - assertTrueJUnit4("cellToMove8 is in position: " + env.getPosition(cellToMove8), - env.getPosition(cellToMove8).equals(pd)); + environment.addNode(np2, p2); + environment.moveNode(cellToMove8, pd); + assertTrueJUnit4("cellToMove8 is in position: " + environment.getPosition(cellToMove8), + environment.getPosition(cellToMove8).equals(pd)); } /** @@ -605,17 +611,17 @@ void testMoveDifferentDiam8() { */ @Test void testMoveInTwoSteps1() { - final CellWithCircularArea c1 = np1; - env.addNode(c1, originalPos); + final Node c1 = np1; + environment.addNode(c1, originalPos); final Euclidean2DPosition pd1 = new Euclidean2DPosition(50, 0); final Euclidean2DPosition pd2 = new Euclidean2DPosition(100, 0); - final CellWithCircularArea c2 = np2; - env.addNode(c2, pd1); - env.moveNode(c1, POSITION_TO_MOVE_TWOSTEP1); - assertEquals(EXPECTED_POS_TWOSTEP1_1, env.getPosition(c1), "c1 is in pos : " + env.getPosition(c1)); - env.moveNode(c2, pd1); - env.moveNodeToPosition(c1, pd2); - assertEquals(EXPECTED_POS_TWOSTEP1_2, env.getPosition(c1), "c1 is in pos : " + env.getPosition(c1)); + final Node c2 = np2; + environment.addNode(c2, pd1); + environment.moveNode(c1, POSITION_TO_MOVE_TWOSTEP1); + assertEquals(EXPECTED_POS_TWOSTEP1_1, environment.getPosition(c1), "c1 is in pos : " + environment.getPosition(c1)); + environment.moveNode(c2, pd1); + environment.moveNodeToPosition(c1, pd2); + assertEquals(EXPECTED_POS_TWOSTEP1_2, environment.getPosition(c1), "c1 is in pos : " + environment.getPosition(c1)); } @@ -624,13 +630,13 @@ void testMoveInTwoSteps1() { */ @Test void testMoveNode9() { - final CellWithCircularArea c1 = new CellNodeImpl<>(env, 1); - final CellWithCircularArea c2 = new CellNodeImpl<>(env, 1); - env.addNode(c2, NODE_POS9); - env.addNode(c1, originalPos); + final Node c1 = createNode(1); + final Node c2 = createNode(1); + environment.addNode(c2, NODE_POS9); + environment.addNode(c1, originalPos); final Euclidean2DPosition pd = new Euclidean2DPosition(4.737000465393066, -5.0); - env.moveNode(c1, pd); - assertNotEquals(env.getPosition(c1), pd); + environment.moveNode(c1, pd); + assertNotEquals(environment.getPosition(c1), pd); } /** @@ -638,13 +644,24 @@ void testMoveNode9() { */ @Test void testMoveNode10() { - final CellWithCircularArea c1 = new CellNodeImpl<>(env, 1); - final CellWithCircularArea c2 = new CellNodeImpl<>(env, 1); - env.addNode(c2, NODE_POS10); - env.addNode(c1, originalPos); + final Node c1 = createNode(1); + final Node c2 = createNode(1); + environment.addNode(c2, NODE_POS10); + environment.addNode(c1, originalPos); final Euclidean2DPosition pd = new Euclidean2DPosition(3.122374292470004, -0.6490462479722794); - env.moveNode(c1, pd); - assertNotEquals(env.getPosition(c1), pd); + environment.moveNode(c1, pd); + assertNotEquals(environment.getPosition(c1), pd); + } + + private List> getOverlappingNodes(final Node node) { + final double diameter = node.asProperty(CircularCellProperty.class).getDiameter(); + return environment.getNodesWithinRange(node, diameter).stream() + .filter(n -> environment.getDistanceBetweenNodes(node, n) < diameter) + .collect(Collectors.toList()); + } + + private List mapToNodePositions(final List> nodes) { + return nodes.stream().map(node -> environment.getPosition(node).toString()).collect(Collectors.toList()); } /** @@ -653,21 +670,18 @@ void testMoveNode10() { @Test void testMoveNode11() { final double diameter = 1; - final CellWithCircularArea c1 = new CellNodeImpl<>(env, diameter); - final CellWithCircularArea c2 = new CellNodeImpl<>(env, diameter); - final CellWithCircularArea c3 = new CellNodeImpl<>(env, diameter); - final CellWithCircularArea c4 = new CellNodeImpl<>(env, diameter); - env.addNode(c1, NODE_POS11_1); - env.addNode(c2, NODE_POS11_2); - env.addNode(c3, NODE_POS11_3); - env.addNode(c4, NODE_POS11_4); + final Node c1 = createNode(diameter); + final Node c2 = createNode(diameter); + final Node c3 = createNode(diameter); + final Node c4 = createNode(diameter); + environment.addNode(c1, NODE_POS11_1); + environment.addNode(c2, NODE_POS11_2); + environment.addNode(c3, NODE_POS11_3); + environment.addNode(c4, NODE_POS11_4); final Euclidean2DPosition pd = new Euclidean2DPosition(5.0, -1.8431210525510544); - env.moveNodeToPosition(c1, pd); - assertTrueJUnit4("Should be empty but is : " + env.getNodesWithinRange(c1, c1.getDiameter()).stream() - .filter(n -> env.getDistanceBetweenNodes(c1, n) < diameter) - .map(n -> env.getPosition(n).toString()) - .collect(Collectors.toList()), - env.getNodesWithinRange(c1, c1.getDiameter() - DELTA).isEmpty()); + environment.moveNodeToPosition(c1, pd); + assertTrueJUnit4("Should be empty but is : " + mapToNodePositions(getOverlappingNodes(c1)), + environment.getNodesWithinRange(c1, diameter - DELTA).isEmpty()); } /** @@ -676,21 +690,20 @@ void testMoveNode11() { @Test void testMoveNode12() { final double diameter = 1; - final CellWithCircularArea c1 = new CellNodeImpl<>(env, diameter); - final CellWithCircularArea c2 = new CellNodeImpl<>(env, diameter); - final CellWithCircularArea c3 = new CellNodeImpl<>(env, diameter); - final CellWithCircularArea c4 = new CellNodeImpl<>(env, diameter); - env.addNode(c1, NODE_POS12_1); - env.addNode(c2, NODE_POS12_2); - env.addNode(c3, NODE_POS12_3); - env.addNode(c4, NODE_POS12_4); + final Node c1 = createNode(diameter); + final Node c2 = createNode(diameter); + final Node c3 = createNode(diameter); + final Node c4 = createNode(diameter); + environment.addNode(c1, NODE_POS12_1); + environment.addNode(c2, NODE_POS12_2); + environment.addNode(c3, NODE_POS12_3); + environment.addNode(c4, NODE_POS12_4); final Euclidean2DPosition pd = new Euclidean2DPosition(5.3, -1.8431210525510544); - env.moveNodeToPosition(c1, pd); - assertTrueJUnit4("Should be empty but is : " + env.getNodesWithinRange(c1, c1.getDiameter()).stream() - .filter(n -> env.getDistanceBetweenNodes(c1, n) < diameter) - .map(n -> env.getPosition(n).toString()) - .collect(Collectors.toList()), - env.getNodesWithinRange(c1, c1.getDiameter()).isEmpty()); + environment.moveNodeToPosition(c1, pd); + assertTrueJUnit4( + "Should be empty but is : " + mapToNodePositions(getOverlappingNodes(c1)), + environment.getNodesWithinRange(c1, diameter).isEmpty() + ); } /** @@ -699,24 +712,21 @@ void testMoveNode12() { @Test void testMoveNode13() { final double diameter = 1; - final CellWithCircularArea c1 = new CellNodeImpl<>(env, diameter); - final CellWithCircularArea c2 = new CellNodeImpl<>(env, diameter); - final CellWithCircularArea c3 = new CellNodeImpl<>(env, diameter); - final CellWithCircularArea c4 = new CellNodeImpl<>(env, diameter); - env.addNode(c1, NODE_POS13_1); - env.addNode(c2, NODE_POS13_2); - env.addNode(c3, NODE_POS13_3); - env.addNode(c4, NODE_POS13_4); + final Node c1 = createNode(diameter); + final Node c2 = createNode(diameter); + final Node c3 = createNode(diameter); + final Node c4 = createNode(diameter); + environment.addNode(c1, NODE_POS13_1); + environment.addNode(c2, NODE_POS13_2); + environment.addNode(c3, NODE_POS13_3); + environment.addNode(c4, NODE_POS13_4); final Euclidean2DPosition pd = new Euclidean2DPosition(10, 10); - env.moveNodeToPosition(c1, pd); - env.moveNodeToPosition(c2, pd); - env.moveNodeToPosition(c3, pd); - env.moveNodeToPosition(c4, pd); - assertTrueJUnit4("Should be empty but is : " + env.getNodesWithinRange(c1, c1.getDiameter()).stream() - .filter(n -> env.getDistanceBetweenNodes(c1, n) < diameter) - .map(n -> env.getPosition(n).toString()) - .collect(Collectors.toList()), - env.getNodesWithinRange(c1, c1.getDiameter()).isEmpty()); + environment.moveNodeToPosition(c1, pd); + environment.moveNodeToPosition(c2, pd); + environment.moveNodeToPosition(c3, pd); + environment.moveNodeToPosition(c4, pd); + assertTrueJUnit4("Should be empty but is : " + mapToNodePositions(getOverlappingNodes(c1)), + environment.getNodesWithinRange(c1, diameter).isEmpty()); } /** @@ -770,22 +780,27 @@ public void finished( assertTrue(thereIsOverlap(environment)); } - private Stream> getNodes() { + private Stream> getNodes() { return env.getNodes().stream() - .filter(n -> n instanceof CellWithCircularArea) - .map(n -> (CellWithCircularArea) n); + .filter(n -> n.asPropertyOrNull(CircularCellProperty.class) != null) + .map(n -> (Node) n); } private boolean thereIsOverlap(final Environment env) { getNodes().flatMap(n -> getNodes() .filter(c -> !c.equals(n)) - .filter(c -> env.getDistanceBetweenNodes(n, c) < n.getRadius() + c.getRadius() - DELTA) + .filter(c -> env.getDistanceBetweenNodes(n, c) + < n.asProperty(CircularCellProperty.class).getRadius() + + c.asProperty(CircularCellProperty.class).getRadius() - DELTA) .map(c -> new Pair<>(n, c))) .findAny() - .ifPresent(e -> fail("Nodes " + e.getFirst().getId() + env.getPosition(e.getFirst()) + " and " + .ifPresent(e -> fail("Nodes " + e.getFirst().getId() + + env.getPosition(e.getFirst()) + " and " + e.getSecond().getId() + env.getPosition(e.getSecond()) + " are overlapping. " + "Their distance is: " + env.getDistanceBetweenNodes(e.getFirst(), e.getSecond()) - + " but should be greater than " + (e.getFirst().getRadius() + e.getSecond().getRadius()))); + + " but should be greater than " + + (e.getFirst().asProperty(CircularCellProperty.class).getRadius() + + e.getSecond().asProperty(CircularCellProperty.class).getRadius()))); return true; } }); @@ -797,7 +812,7 @@ private boolean thereIsOverlap(final Environment en } private String getFailureTestString(final String nodeName, final Node n, final Position expected) { - return nodeName + " not in pos " + expected + "; it's in pos " + env.getPosition(n); + return nodeName + " not in pos " + expected + "; it's in pos " + environment.getPosition(n); } private static void nodeNotInEnvironment(final Environment environment, final Node node) { diff --git a/alchemist-incarnation-biochemistry/src/test/java/it/unibo/alchemist/test/TestBiomolLayer.java b/alchemist-incarnation-biochemistry/src/test/java/it/unibo/alchemist/test/TestBiomolLayer.java index 1ac8214611..51600def28 100644 --- a/alchemist-incarnation-biochemistry/src/test/java/it/unibo/alchemist/test/TestBiomolLayer.java +++ b/alchemist-incarnation-biochemistry/src/test/java/it/unibo/alchemist/test/TestBiomolLayer.java @@ -15,15 +15,14 @@ import it.unibo.alchemist.model.implementations.environments.BioRect2DEnvironment; import it.unibo.alchemist.model.implementations.layers.StepLayer; import it.unibo.alchemist.model.implementations.molecules.Biomolecule; -import it.unibo.alchemist.model.implementations.nodes.CellNodeImpl; import it.unibo.alchemist.model.implementations.positions.Euclidean2DPosition; import it.unibo.alchemist.model.implementations.timedistributions.DiracComb; import it.unibo.alchemist.model.implementations.times.DoubleTime; -import it.unibo.alchemist.model.interfaces.CellNode; import it.unibo.alchemist.model.interfaces.Environment; import it.unibo.alchemist.model.interfaces.Incarnation; import it.unibo.alchemist.model.interfaces.Layer; import it.unibo.alchemist.model.interfaces.Molecule; +import it.unibo.alchemist.model.interfaces.Node; import it.unibo.alchemist.model.interfaces.Reaction; import it.unibo.alchemist.model.interfaces.Time; import org.apache.commons.math3.random.MersenneTwister; @@ -46,26 +45,28 @@ class TestBiomolLayer { */ @Test void testBiomolStepLayer() { - final Environment env = new BioRect2DEnvironment(); + final Environment environment = new BioRect2DEnvironment(); final Biomolecule b = new Biomolecule("B"); final Layer bLayer = new StepLayer<>(10_000.0, 0d); - final CellNode cellNode = new CellNodeImpl<>(env); final MersenneTwister rand = new MersenneTwister(0); + final Node cellNode = INCARNATION.createNode(rand, environment, null); final Molecule a = new Biomolecule("A"); final Reaction underTest = INCARNATION.createReaction( - rand, env, cellNode, - INCARNATION.createTimeDistribution(rand, env, cellNode, "1"), + rand, environment, cellNode, + INCARNATION.createTimeDistribution(rand, environment, cellNode, "1"), "[B in env] --> [A]" ); cellNode.addReaction(underTest); cellNode.addReaction(INCARNATION.createReaction( - rand, env, cellNode, new DiracComb<>(100d), "[] --> [BrownianMove(10)]" + rand, environment, cellNode, new DiracComb<>(100d), "[] --> [BrownianMove(10)]" )); cellNode.setConcentration(a, 0d); - env.setLinkingRule(new it.unibo.alchemist.model.implementations.linkingrules.ConnectWithinDistance<>(2)); - env.addNode(cellNode, new Euclidean2DPosition(0, 0)); - env.addLayer(b, bLayer); - final Simulation sim = new Engine<>(env, 3000); + environment.setLinkingRule( + new it.unibo.alchemist.model.implementations.linkingrules.ConnectWithinDistance<>(2) + ); + environment.addNode(cellNode, new Euclidean2DPosition(0, 0)); + environment.addLayer(b, bLayer); + final Simulation sim = new Engine<>(environment, 3000); sim.play(); sim.addOutputMonitor(new OutputMonitor<>() { private static final long serialVersionUID = 0L; diff --git a/alchemist-incarnation-biochemistry/src/test/java/it/unibo/alchemist/test/TestChemotaxis.java b/alchemist-incarnation-biochemistry/src/test/java/it/unibo/alchemist/test/TestChemotaxis.java index 821597a781..a7e244976c 100644 --- a/alchemist-incarnation-biochemistry/src/test/java/it/unibo/alchemist/test/TestChemotaxis.java +++ b/alchemist-incarnation-biochemistry/src/test/java/it/unibo/alchemist/test/TestChemotaxis.java @@ -10,17 +10,17 @@ import it.unibo.alchemist.model.BiochemistryIncarnation; import it.unibo.alchemist.model.implementations.environments.BioRect2DEnvironmentNoOverlap; import it.unibo.alchemist.model.implementations.molecules.Biomolecule; -import it.unibo.alchemist.model.implementations.nodes.CellNodeImpl; import it.unibo.alchemist.model.implementations.nodes.EnvironmentNodeImpl; import it.unibo.alchemist.model.implementations.positions.Euclidean2DPosition; import it.unibo.alchemist.model.implementations.timedistributions.ExponentialTime; -import it.unibo.alchemist.model.interfaces.CellNode; import it.unibo.alchemist.model.interfaces.Environment; import it.unibo.alchemist.model.interfaces.EnvironmentNode; import it.unibo.alchemist.model.interfaces.Incarnation; import it.unibo.alchemist.model.interfaces.Molecule; +import it.unibo.alchemist.model.interfaces.Node; import it.unibo.alchemist.model.interfaces.Reaction; import it.unibo.alchemist.model.interfaces.TimeDistribution; +import it.unibo.alchemist.model.interfaces.properties.CircularCellProperty; import org.apache.commons.math3.random.MersenneTwister; import org.apache.commons.math3.random.RandomGenerator; import org.apache.commons.math3.util.FastMath; @@ -42,8 +42,8 @@ class TestChemotaxis { private static final int EXPECTED_NODES = 5; private static final double PRECISION = 1e-15; private static final String CELL_MOVE_REACTION = "[] --> [CellMove(false, 1)]"; - private Environment env; - private CellNode cellNode1; + private Environment environment; + private Node cellNode1; private EnvironmentNode envNode1; private EnvironmentNode envNode2; private EnvironmentNode envNode3; @@ -58,13 +58,15 @@ class TestChemotaxis { */ @BeforeEach public void setUp() { - env = new BioRect2DEnvironmentNoOverlap(); - env.setLinkingRule(new it.unibo.alchemist.model.implementations.linkingrules.ConnectWithinDistance<>(2)); - envNode1 = new EnvironmentNodeImpl(env); - envNode2 = new EnvironmentNodeImpl(env); - envNode3 = new EnvironmentNodeImpl(env); - envNode4 = new EnvironmentNodeImpl(env); - cellNode1 = new CellNodeImpl<>(env); + environment = new BioRect2DEnvironmentNoOverlap(); + environment.setLinkingRule( + new it.unibo.alchemist.model.implementations.linkingrules.ConnectWithinDistance<>(2) + ); + envNode1 = new EnvironmentNodeImpl(environment); + envNode2 = new EnvironmentNodeImpl(environment); + envNode3 = new EnvironmentNodeImpl(environment); + envNode4 = new EnvironmentNodeImpl(environment); + cellNode1 = inc.createNode(rand, environment, null); rand = new MersenneTwister(); time = new ExponentialTime<>(1, rand); } @@ -79,21 +81,23 @@ void testChemotacticPolarization1() { final Euclidean2DPosition p3 = new Euclidean2DPosition(0, 1); final Euclidean2DPosition p4 = new Euclidean2DPosition(1, 1); final Euclidean2DPosition p5 = new Euclidean2DPosition(0.5, 0.5); - env.addNode(envNode1, p1); - env.addNode(envNode2, p2); - env.addNode(envNode3, p3); - env.addNode(envNode4, p4); - env.addNode(cellNode1, p5); + environment.addNode(envNode1, p1); + environment.addNode(envNode2, p2); + environment.addNode(envNode3, p3); + environment.addNode(envNode4, p4); + environment.addNode(cellNode1, p5); envNode4.setConcentration(biomolA, CONCENTRATION2); envNode2.setConcentration(biomolA, CONCENTRATION1); envNode3.setConcentration(biomolA, CONCENTRATION1); - final Reaction r = inc.createReaction(rand, env, cellNode1, time, CHEMIOTACTIC_POLARIZATION_REACTION); + final Reaction r = inc.createReaction( + rand, environment, cellNode1, time, CHEMIOTACTIC_POLARIZATION_REACTION + ); r.execute(); - assertEquals(cellNode1.getPolarizationVersor().getX(), + assertEquals(cellNode1.asProperty(CircularCellProperty.class).getPolarizationVersor().getCoordinate(0), FastMath.sqrt(0.5), PRECISION ); - assertEquals(cellNode1.getPolarizationVersor().getY(), + assertEquals(cellNode1.asProperty(CircularCellProperty.class).getPolarizationVersor().getCoordinate(1), FastMath.sqrt(0.5), PRECISION ); @@ -109,21 +113,23 @@ void testChemotacticPolarization2() { final Euclidean2DPosition p3 = new Euclidean2DPosition(0, 1); final Euclidean2DPosition p4 = new Euclidean2DPosition(3, 3); final Euclidean2DPosition p5 = new Euclidean2DPosition(0.5, 0.5); - env.addNode(envNode1, p1); - env.addNode(envNode2, p2); - env.addNode(envNode3, p3); - env.addNode(envNode4, p4); - env.addNode(cellNode1, p5); + environment.addNode(envNode1, p1); + environment.addNode(envNode2, p2); + environment.addNode(envNode3, p3); + environment.addNode(envNode4, p4); + environment.addNode(cellNode1, p5); envNode4.setConcentration(biomolA, CONCENTRATION2); envNode2.setConcentration(biomolA, CONCENTRATION1); envNode3.setConcentration(biomolA, CONCENTRATION1); - final Reaction r = inc.createReaction(rand, env, cellNode1, time, CHEMIOTACTIC_POLARIZATION_REACTION); + final Reaction r = inc.createReaction( + rand, environment, cellNode1, time, CHEMIOTACTIC_POLARIZATION_REACTION + ); r.execute(); - assertEquals(cellNode1.getPolarizationVersor().getX(), + assertEquals(cellNode1.asProperty(CircularCellProperty.class).getPolarizationVersor().getCoordinate(0), 0, PRECISION ); - assertEquals(cellNode1.getPolarizationVersor().getY(), + assertEquals(cellNode1.asProperty(CircularCellProperty.class).getPolarizationVersor().getCoordinate(1), 0, PRECISION ); @@ -139,18 +145,20 @@ void testChemotacticPolarization3() { final Euclidean2DPosition p3 = new Euclidean2DPosition(0, 1); final Euclidean2DPosition p4 = new Euclidean2DPosition(1, 1); final Euclidean2DPosition p5 = new Euclidean2DPosition(0.5, 0.5); - env.addNode(envNode1, p1); - env.addNode(envNode2, p2); - env.addNode(envNode3, p3); - env.addNode(envNode4, p4); - env.addNode(cellNode1, p5); - final Reaction r = inc.createReaction(rand, env, cellNode1, time, CHEMIOTACTIC_POLARIZATION_REACTION); + environment.addNode(envNode1, p1); + environment.addNode(envNode2, p2); + environment.addNode(envNode3, p3); + environment.addNode(envNode4, p4); + environment.addNode(cellNode1, p5); + final Reaction r = inc.createReaction( + rand, environment, cellNode1, time, CHEMIOTACTIC_POLARIZATION_REACTION + ); r.execute(); - assertEquals(cellNode1.getPolarizationVersor().getX(), + assertEquals(cellNode1.asProperty(CircularCellProperty.class).getPolarizationVersor().getCoordinate(0), 0, PRECISION ); - assertEquals(cellNode1.getPolarizationVersor().getY(), + assertEquals(cellNode1.asProperty(CircularCellProperty.class).getPolarizationVersor().getCoordinate(1), 0, PRECISION ); @@ -162,14 +170,16 @@ void testChemotacticPolarization3() { @Test void testChemotacticPolarization4() { final Euclidean2DPosition p1 = new Euclidean2DPosition(0.5, 0.5); - env.addNode(cellNode1, p1); - final Reaction r = inc.createReaction(rand, env, cellNode1, time, CHEMIOTACTIC_POLARIZATION_REACTION); + environment.addNode(cellNode1, p1); + final Reaction r = inc.createReaction( + rand, environment, cellNode1, time, CHEMIOTACTIC_POLARIZATION_REACTION + ); r.execute(); - assertEquals(cellNode1.getPolarizationVersor().getX(), + assertEquals(cellNode1.asProperty(CircularCellProperty.class).getPolarizationVersor().getCoordinate(0), 0, PRECISION ); - assertEquals(cellNode1.getPolarizationVersor().getY(), + assertEquals(cellNode1.asProperty(CircularCellProperty.class).getPolarizationVersor().getCoordinate(1), 0, PRECISION ); @@ -183,22 +193,26 @@ void testChemotacticPolarization5() { final Euclidean2DPosition p1 = new Euclidean2DPosition(0, 0); final Euclidean2DPosition p2 = new Euclidean2DPosition(1, 0); final Euclidean2DPosition p3 = new Euclidean2DPosition(-1, 0); - env.addNode(cellNode1, p1); + environment.addNode(cellNode1, p1); final Molecule a = new Biomolecule("A"); final Molecule b = new Biomolecule("B"); envNode1.setConcentration(a, CONCENTRATION3); envNode2.setConcentration(b, CONCENTRATION3); - env.addNode(envNode1, p2); - env.addNode(envNode2, p3); - final Reaction r1 = inc.createReaction(rand, env, cellNode1, time, CHEMIOTACTIC_POLARIZATION_REACTION); - final Reaction r2 = inc.createReaction(rand, env, cellNode1, time, "[] --> [ChemotacticPolarization(B, up)]"); + environment.addNode(envNode1, p2); + environment.addNode(envNode2, p3); + final Reaction r1 = inc.createReaction( + rand, environment, cellNode1, time, CHEMIOTACTIC_POLARIZATION_REACTION + ); + final Reaction r2 = inc.createReaction( + rand, environment, cellNode1, time, "[] --> [ChemotacticPolarization(B, up)]" + ); r1.execute(); r2.execute(); - assertEquals(cellNode1.getPolarizationVersor().getX(), + assertEquals(cellNode1.asProperty(CircularCellProperty.class).getPolarizationVersor().getCoordinate(0), 0, PRECISION ); - assertEquals(cellNode1.getPolarizationVersor().getY(), + assertEquals(cellNode1.asProperty(CircularCellProperty.class).getPolarizationVersor().getCoordinate(1), 0, PRECISION ); @@ -214,20 +228,24 @@ void testChemotacticMove1() { final Euclidean2DPosition p3 = new Euclidean2DPosition(0, 1); final Euclidean2DPosition p4 = new Euclidean2DPosition(1, 1); final Euclidean2DPosition p5 = new Euclidean2DPosition(0.5, 0.5); - env.addNode(envNode1, p1); - env.addNode(envNode2, p2); - env.addNode(envNode3, p3); - env.addNode(envNode4, p4); - env.addNode(cellNode1, p5); - assertEquals(EXPECTED_NODES, env.getNodeCount()); + environment.addNode(envNode1, p1); + environment.addNode(envNode2, p2); + environment.addNode(envNode3, p3); + environment.addNode(envNode4, p4); + environment.addNode(cellNode1, p5); + assertEquals(EXPECTED_NODES, environment.getNodeCount()); envNode4.setConcentration(biomolA, CONCENTRATION2); envNode2.setConcentration(biomolA, CONCENTRATION1); envNode3.setConcentration(biomolA, CONCENTRATION1); - final Reaction r1 = inc.createReaction(rand, env, cellNode1, time, CHEMIOTACTIC_POLARIZATION_REACTION); - final Reaction r2 = inc.createReaction(rand, env, cellNode1, time, CELL_MOVE_REACTION); + final Reaction r1 = inc.createReaction( + rand, environment, cellNode1, time, CHEMIOTACTIC_POLARIZATION_REACTION + ); + final Reaction r2 = inc.createReaction(rand, environment, cellNode1, time, CELL_MOVE_REACTION); r1.execute(); r2.execute(); - assertEquals(new Euclidean2DPosition(0.5 + FastMath.sqrt(0.5), 0.5 + FastMath.sqrt(0.5)), env.getPosition(cellNode1)); + assertEquals(new Euclidean2DPosition(0.5 + FastMath.sqrt(0.5), 0.5 + FastMath.sqrt(0.5)), + environment.getPosition(cellNode1) + ); } /** @@ -240,25 +258,27 @@ void testChemotacticMove2() { final Euclidean2DPosition p3 = new Euclidean2DPosition(0, 1); final Euclidean2DPosition p4 = new Euclidean2DPosition(1, 1); final Euclidean2DPosition p5 = new Euclidean2DPosition(1, 1); - env.addNode(envNode1, p1); - env.addNode(envNode2, p2); - env.addNode(envNode3, p3); - env.addNode(envNode4, p4); - env.addNode(cellNode1, p5); - assertEquals(EXPECTED_NODES, env.getNodeCount()); + environment.addNode(envNode1, p1); + environment.addNode(envNode2, p2); + environment.addNode(envNode3, p3); + environment.addNode(envNode4, p4); + environment.addNode(cellNode1, p5); + assertEquals(EXPECTED_NODES, environment.getNodeCount()); envNode4.setConcentration(biomolA, CONCENTRATION2); envNode2.setConcentration(biomolA, CONCENTRATION1); envNode3.setConcentration(biomolA, CONCENTRATION1); - final Reaction r1 = inc.createReaction(rand, env, cellNode1, time, CHEMIOTACTIC_POLARIZATION_REACTION); - final Reaction r2 = inc.createReaction(rand, env, cellNode1, time, CELL_MOVE_REACTION); + final Reaction r1 = inc.createReaction( + rand, environment, cellNode1, time, CHEMIOTACTIC_POLARIZATION_REACTION + ); + final Reaction r2 = inc.createReaction(rand, environment, cellNode1, time, CELL_MOVE_REACTION); r1.execute(); r2.execute(); assertEquals(1, - env.getPosition(cellNode1).getX(), + environment.getPosition(cellNode1).getX(), PRECISION ); assertEquals(1, - env.getPosition(cellNode1).getY(), + environment.getPosition(cellNode1).getY(), PRECISION ); } @@ -273,27 +293,29 @@ void testChemotacticMove3() { final Euclidean2DPosition p3 = new Euclidean2DPosition(0, 1); final Euclidean2DPosition p4 = new Euclidean2DPosition(1, 1); final Euclidean2DPosition p5 = new Euclidean2DPosition(0.5, 0.5); - env.addNode(envNode1, p1); - env.addNode(envNode2, p2); - env.addNode(envNode3, p3); - env.addNode(envNode4, p4); - env.addNode(cellNode1, p5); - assertEquals(EXPECTED_NODES, env.getNodeCount()); + environment.addNode(envNode1, p1); + environment.addNode(envNode2, p2); + environment.addNode(envNode3, p3); + environment.addNode(envNode4, p4); + environment.addNode(cellNode1, p5); + assertEquals(EXPECTED_NODES, environment.getNodeCount()); envNode4.setConcentration(biomolA, CONCENTRATION2); envNode2.setConcentration(biomolA, CONCENTRATION2); envNode3.setConcentration(biomolA, CONCENTRATION2); envNode1.setConcentration(biomolA, CONCENTRATION2); - final Reaction r1 = inc.createReaction(rand, env, cellNode1, time, CHEMIOTACTIC_POLARIZATION_REACTION); - final Reaction r2 = inc.createReaction(rand, env, cellNode1, time, CELL_MOVE_REACTION); + final Reaction r1 = inc.createReaction( + rand, environment, cellNode1, time, CHEMIOTACTIC_POLARIZATION_REACTION + ); + final Reaction r2 = inc.createReaction(rand, environment, cellNode1, time, CELL_MOVE_REACTION); r1.execute(); r2.execute(); r2.execute(); assertEquals(0.5, - env.getPosition(cellNode1).getX(), + environment.getPosition(cellNode1).getX(), PRECISION ); assertEquals(0.5, - env.getPosition(cellNode1).getY(), + environment.getPosition(cellNode1).getY(), PRECISION ); } diff --git a/alchemist-incarnation-biochemistry/src/test/java/it/unibo/alchemist/test/TestDeformableCell.java b/alchemist-incarnation-biochemistry/src/test/java/it/unibo/alchemist/test/TestDeformableCell.java index f1e39d86df..10453fdd9a 100644 --- a/alchemist-incarnation-biochemistry/src/test/java/it/unibo/alchemist/test/TestDeformableCell.java +++ b/alchemist-incarnation-biochemistry/src/test/java/it/unibo/alchemist/test/TestDeformableCell.java @@ -8,15 +8,17 @@ package it.unibo.alchemist.test; import it.unibo.alchemist.model.BiochemistryIncarnation; +import it.unibo.alchemist.model.implementations.properties.CircularDeformableCell; import it.unibo.alchemist.model.implementations.environments.BioRect2DEnvironmentNoOverlap; -import it.unibo.alchemist.model.implementations.nodes.CircularDeformableCellImpl; +import it.unibo.alchemist.model.implementations.nodes.GenericNode; import it.unibo.alchemist.model.implementations.positions.Euclidean2DPosition; import it.unibo.alchemist.model.implementations.timedistributions.ExponentialTime; -import it.unibo.alchemist.model.interfaces.CircularDeformableCell; import it.unibo.alchemist.model.interfaces.Environment; import it.unibo.alchemist.model.interfaces.EnvironmentSupportingDeformableCells; import it.unibo.alchemist.model.interfaces.Incarnation; +import it.unibo.alchemist.model.interfaces.Node; import it.unibo.alchemist.model.interfaces.TimeDistribution; +import it.unibo.alchemist.model.interfaces.properties.CircularDeformableCellProperty; import org.apache.commons.math3.random.MersenneTwister; import org.apache.commons.math3.random.RandomGenerator; import org.junit.jupiter.api.BeforeEach; @@ -64,28 +66,36 @@ class TestDeformableCell { private static final Euclidean2DPosition CELL_POS_MOV1 = new Euclidean2DPosition(0, 5.75); private static final Euclidean2DPosition EXPECTED_POS_MOV1 = new Euclidean2DPosition(0, 5); private static final String CELL_TENSION_POLARIZATION = "[] --> [CellTensionPolarization()]"; - private Environment env; - private CircularDeformableCell cellNode1; - private CircularDeformableCell cellNode2; - private CircularDeformableCell cellNode3; - private CircularDeformableCell cellNode4; - private CircularDeformableCell cellNode5; + private Environment environment; + private Node cellNode1; + private Node cellNode2; + private Node cellNode3; + private Node cellNode4; + private Node cellNode5; private final Incarnation inc = new BiochemistryIncarnation<>(); private RandomGenerator rand; private TimeDistribution time; + private Node createDeformableCell(final double maxDiameter, final double rigidity) { + final Node node = new GenericNode<>(inc, environment); + node.addProperty( + new CircularDeformableCell<>(environment, node, maxDiameter, rigidity) + ); + return node; + } + /** * */ @BeforeEach public void setUp() { - env = new BioRect2DEnvironmentNoOverlap(XMIN, XMAX, YMIN, YMAX); - env.setLinkingRule(new it.unibo.alchemist.model.implementations.linkingrules.ConnectWithinDistance<>(2)); - cellNode1 = new CircularDeformableCellImpl<>(env, 1, 1); // max rigidity - cellNode2 = new CircularDeformableCellImpl<>(env, 1, 0.5); - cellNode3 = new CircularDeformableCellImpl<>(env, 2, 0.5); - cellNode4 = new CircularDeformableCellImpl<>(env, 3, 0.5); - cellNode5 = new CircularDeformableCellImpl<>(env, 2, 0.5); + environment = new BioRect2DEnvironmentNoOverlap(XMIN, XMAX, YMIN, YMAX); + environment.setLinkingRule(new it.unibo.alchemist.model.implementations.linkingrules.ConnectWithinDistance<>(2)); + cellNode1 = createDeformableCell(1, 1); // max rigidity + cellNode2 = createDeformableCell(1, 0.5); + cellNode3 = createDeformableCell(2, 0.5); + cellNode4 = createDeformableCell(3, 0.5); + cellNode5 = createDeformableCell(2, 0.5); rand = new MersenneTwister(); time = new ExponentialTime<>(1, rand); } @@ -95,14 +105,14 @@ public void setUp() { */ @Test void testAddNode1() { - env.addNode(cellNode1, CELL_POS1_1); - env.addNode(cellNode2, CELL_POS1_2); - env.addNode(cellNode3, CELL_POS1_3); - env.addNode(cellNode4, CELL_POS1_4); + environment.addNode(cellNode1, CELL_POS1_1); + environment.addNode(cellNode2, CELL_POS1_2); + environment.addNode(cellNode3, CELL_POS1_3); + environment.addNode(cellNode4, CELL_POS1_4); - assertNotNull(env.getPosition(cellNode2), "Position of cellNode2 = " + env.getPosition(cellNode2)); - assertNotNull(env.getPosition(cellNode3), "Position of cellNode3 = " + env.getPosition(cellNode3)); - assertFalse(env.getNodes().contains(cellNode4), "unexpected node in the environment"); + assertNotNull(environment.getPosition(cellNode2), "Position of cellNode2 = " + environment.getPosition(cellNode2)); + assertNotNull(environment.getPosition(cellNode3), "Position of cellNode3 = " + environment.getPosition(cellNode3)); + assertFalse(environment.getNodes().contains(cellNode4), "unexpected node in the environment"); } /** @@ -110,21 +120,23 @@ void testAddNode1() { */ @Test void testAddAndRemoveNode() { - env.addNode(cellNode1, CELL_POS2_1); - env.addNode(cellNode2, CELL_POS2_2); - env.addNode(cellNode3, CELL_POS2_3); - env.addNode(cellNode4, CELL_POS2_4); + environment.addNode(cellNode1, CELL_POS2_1); + environment.addNode(cellNode2, CELL_POS2_2); + environment.addNode(cellNode3, CELL_POS2_3); + environment.addNode(cellNode4, CELL_POS2_4); assertEquals( 3d, - ((EnvironmentSupportingDeformableCells) env).getMaxDiameterAmongCircularDeformableCells(), + ((EnvironmentSupportingDeformableCells) environment) + .getMaxDiameterAmongCircularDeformableCells(), PRECISION ); - env.removeNode(cellNode1); - env.removeNode(cellNode2); - env.removeNode(cellNode3); - env.removeNode(cellNode4); + environment.removeNode(cellNode1); + environment.removeNode(cellNode2); + environment.removeNode(cellNode3); + environment.removeNode(cellNode4); assertEquals(0d, - ((EnvironmentSupportingDeformableCells) env).getMaxDiameterAmongCircularDeformableCells(), + ((EnvironmentSupportingDeformableCells) environment) + .getMaxDiameterAmongCircularDeformableCells(), PRECISION ); } @@ -135,9 +147,9 @@ void testAddAndRemoveNode() { @Test @SuppressWarnings("CPD-START") void testTensionPresent1() { - env.addNode(cellNode1, CELL_POS_TENSPRES1_1); - env.addNode(cellNode2, CELL_POS_TENSPRES1_2); - cellNode1.addReaction(inc.createReaction(rand, env, cellNode1, time, "[] --> [A] if TensionPresent()")); + environment.addNode(cellNode1, CELL_POS_TENSPRES1_1); + environment.addNode(cellNode2, CELL_POS_TENSPRES1_2); + cellNode1.addReaction(inc.createReaction(rand, environment, cellNode1, time, "[] --> [A] if TensionPresent()")); assertFalse(cellNode1.getReactions().isEmpty()); assertTrue(cellNode1.getReactions().stream() .findFirst() @@ -148,7 +160,7 @@ void testTensionPresent1() { .orElseThrow() .getConditions().get(0).getPropensityContribution(), PRECISION); - env.moveNodeToPosition(cellNode2, new Euclidean2DPosition(0, 4)); + environment.moveNodeToPosition(cellNode2, new Euclidean2DPosition(0, 4)); assertFalse(cellNode1.getReactions().stream() .findFirst() .orElseThrow() @@ -165,9 +177,9 @@ void testTensionPresent1() { */ @Test void testTensionPresent2() { - env.addNode(cellNode1, new Euclidean2DPosition(0, 0)); - env.addNode(cellNode3, new Euclidean2DPosition(0, 1)); - cellNode1.addReaction(inc.createReaction(rand, env, cellNode1, time, "[] --> [A] if TensionPresent()")); + environment.addNode(cellNode1, new Euclidean2DPosition(0, 0)); + environment.addNode(cellNode3, new Euclidean2DPosition(0, 1)); + cellNode1.addReaction(inc.createReaction(rand, environment, cellNode1, time, "[] --> [A] if TensionPresent()")); assertFalse(cellNode1.getReactions().isEmpty()); assertTrue(cellNode1.getReactions().stream() .findFirst() @@ -178,7 +190,7 @@ void testTensionPresent2() { .orElseThrow() .getConditions().get(0).getPropensityContribution(), PRECISION); - env.moveNodeToPosition(cellNode3, MOVE_TO_POS2_1); + environment.moveNodeToPosition(cellNode3, MOVE_TO_POS2_1); assertTrue(cellNode1.getReactions().stream() .findFirst() .orElseThrow() @@ -188,7 +200,7 @@ void testTensionPresent2() { .orElseThrow() .getConditions().get(0).getPropensityContribution(), PRECISION); - env.moveNodeToPosition(cellNode3, MOVE_TO_POS2_2); + environment.moveNodeToPosition(cellNode3, MOVE_TO_POS2_2); assertFalse(cellNode1.getReactions().stream() .findFirst() .orElseThrow() @@ -205,26 +217,33 @@ void testTensionPresent2() { */ @Test void testTensionPolarization1() { - env.addNode(cellNode1, new Euclidean2DPosition(0, 0)); - env.addNode(cellNode3, new Euclidean2DPosition(0, 1)); - cellNode1.addReaction(inc.createReaction(rand, env, cellNode1, time, CELL_TENSION_POLARIZATION)); + environment.addNode(cellNode1, new Euclidean2DPosition(0, 0)); + environment.addNode(cellNode3, new Euclidean2DPosition(0, 1)); + cellNode1.addReaction(inc.createReaction(rand, environment, cellNode1, time, CELL_TENSION_POLARIZATION)); assertFalse(cellNode1.getReactions().isEmpty()); cellNode1.getReactions().stream() .findFirst() .orElseThrow().execute(); - assertEquals(new Euclidean2DPosition(0, -1), cellNode1.getPolarizationVersor()); - env.moveNodeToPosition(cellNode3, MOVE_TO_POS_TENSPOL1_1); - cellNode1.setPolarization(new Euclidean2DPosition(0, 0)); + assertEquals(new Euclidean2DPosition(0, -1), cellNode1 + .asProperty(CircularDeformableCellProperty.class).getPolarizationVersor()); + environment.moveNodeToPosition(cellNode3, MOVE_TO_POS_TENSPOL1_1); + cellNode1.asProperty(CircularDeformableCellProperty.class) + .setPolarizationVersor(new Euclidean2DPosition(0, 0)); cellNode1.getReactions().stream() .findFirst() .orElseThrow().execute(); - assertEquals(new Euclidean2DPosition(0, -1), cellNode1.getPolarizationVersor()); - env.moveNodeToPosition(cellNode3, MOVE_TO_POS_TENSPOL1_2); - cellNode1.setPolarization(new Euclidean2DPosition(0, 0)); + assertEquals(new Euclidean2DPosition(0, -1), cellNode1 + .asProperty(CircularDeformableCellProperty.class) + .getPolarizationVersor()); + environment.moveNodeToPosition(cellNode3, MOVE_TO_POS_TENSPOL1_2); + cellNode1.asProperty(CircularDeformableCellProperty.class) + .setPolarizationVersor(new Euclidean2DPosition(0, 0)); cellNode1.getReactions().stream() .findFirst() .orElseThrow().execute(); - assertEquals(new Euclidean2DPosition(0, 0), cellNode1.getPolarizationVersor()); + assertEquals(new Euclidean2DPosition(0, 0), cellNode1 + .asProperty(CircularDeformableCellProperty.class) + .getPolarizationVersor()); } /** @@ -232,25 +251,31 @@ void testTensionPolarization1() { */ @Test void testTensionPolarization2() { - env.addNode(cellNode1, new Euclidean2DPosition(0, 0)); - env.addNode(cellNode3, new Euclidean2DPosition(0, 1)); - env.addNode(cellNode2, MOVE_TO_POS_TENSPOL2_3); - cellNode1.addReaction(inc.createReaction(rand, env, cellNode1, time, CELL_TENSION_POLARIZATION)); + environment.addNode(cellNode1, new Euclidean2DPosition(0, 0)); + environment.addNode(cellNode3, new Euclidean2DPosition(0, 1)); + environment.addNode(cellNode2, MOVE_TO_POS_TENSPOL2_3); + cellNode1.addReaction(inc.createReaction(rand, environment, cellNode1, time, CELL_TENSION_POLARIZATION)); assertFalse(cellNode1.getReactions().isEmpty()); cellNode1.getReactions().stream() .findFirst() .orElseThrow().execute(); - assertEquals(new Euclidean2DPosition(0, 0), cellNode1.getPolarizationVersor()); - env.moveNodeToPosition(cellNode3, MOVE_TO_POS_TENSPOL2_1); + assertEquals(new Euclidean2DPosition(0, 0), cellNode1 + .asProperty(CircularDeformableCellProperty.class) + .getPolarizationVersor()); + environment.moveNodeToPosition(cellNode3, MOVE_TO_POS_TENSPOL2_1); cellNode1.getReactions().stream() .findFirst() .orElseThrow().execute(); - assertEquals(new Euclidean2DPosition(0, 1), cellNode1.getPolarizationVersor()); - env.moveNodeToPosition(cellNode3, MOVE_TO_POS_TENSPOL2_2); + assertEquals(new Euclidean2DPosition(0, 1), cellNode1 + .asProperty(CircularDeformableCellProperty.class) + .getPolarizationVersor()); + environment.moveNodeToPosition(cellNode3, MOVE_TO_POS_TENSPOL2_2); cellNode1.getReactions().stream() .findFirst() .orElseThrow().execute(); - assertEquals(new Euclidean2DPosition(0, 1), cellNode1.getPolarizationVersor()); + assertEquals(new Euclidean2DPosition(0, 1), cellNode1 + .asProperty(CircularDeformableCellProperty.class) + .getPolarizationVersor()); } /** @@ -258,34 +283,42 @@ void testTensionPolarization2() { */ @Test void testTensionPolarization3() { - env.addNode(cellNode1, new Euclidean2DPosition(0, 0)); - env.addNode(cellNode3, new Euclidean2DPosition(-1, 1)); - env.addNode(cellNode5, new Euclidean2DPosition(-1, -1)); - cellNode1.addReaction(inc.createReaction(rand, env, cellNode1, time, CELL_TENSION_POLARIZATION)); + environment.addNode(cellNode1, new Euclidean2DPosition(0, 0)); + environment.addNode(cellNode3, new Euclidean2DPosition(-1, 1)); + environment.addNode(cellNode5, new Euclidean2DPosition(-1, -1)); + cellNode1.addReaction(inc.createReaction(rand, environment, cellNode1, time, CELL_TENSION_POLARIZATION)); assertFalse(cellNode1.getReactions().isEmpty()); cellNode1.getReactions().stream() .findFirst() .orElseThrow().execute(); - assertEquals(new Euclidean2DPosition(1, 0), cellNode1.getPolarizationVersor()); - env.moveNodeToPosition(cellNode3, MOVE_TO_POS_TENSPOL3_1); - cellNode1.setPolarization(new Euclidean2DPosition(0, 0)); + assertEquals(new Euclidean2DPosition(1, 0), cellNode1 + .asProperty(CircularDeformableCellProperty.class) + .getPolarizationVersor()); + environment.moveNodeToPosition(cellNode3, MOVE_TO_POS_TENSPOL3_1); + cellNode1.asProperty(CircularDeformableCellProperty.class) + .setPolarizationVersor(new Euclidean2DPosition(0, 0)); cellNode1.getReactions().stream() .findFirst() .orElseThrow().execute(); assertEquals( - cellNode1.getPolarizationVersor().getCoordinate(0), - cellNode1.getPolarizationVersor().getCoordinate(0), + cellNode1.asProperty(CircularDeformableCellProperty.class) + .getPolarizationVersor().getCoordinate(0), + cellNode1.asProperty(CircularDeformableCellProperty.class) + .getPolarizationVersor().getCoordinate(0), PRECISION ); - env.moveNodeToPosition(cellNode3, new Euclidean2DPosition(-1, 1)); - env.moveNodeToPosition(cellNode5, MOVE_TO_POS_TENSPOL3_3); - cellNode1.setPolarization(new Euclidean2DPosition(0, 0)); + environment.moveNodeToPosition(cellNode3, new Euclidean2DPosition(-1, 1)); + environment.moveNodeToPosition(cellNode5, MOVE_TO_POS_TENSPOL3_3); + cellNode1.asProperty(CircularDeformableCellProperty.class) + .setPolarizationVersor(new Euclidean2DPosition(0, 0)); cellNode1.getReactions().stream() .findFirst() .orElseThrow().execute(); assertEquals( - cellNode1.getPolarizationVersor().getCoordinate(0), - -cellNode1.getPolarizationVersor().getCoordinate(1), + cellNode1.asProperty(CircularDeformableCellProperty.class) + .getPolarizationVersor().getCoordinate(0), + -cellNode1.asProperty(CircularDeformableCellProperty.class) + .getPolarizationVersor().getCoordinate(1), PRECISION ); } @@ -295,15 +328,17 @@ void testTensionPolarization3() { */ @Test void testTensionPolarization4() { - env.addNode(cellNode3, new Euclidean2DPosition(0, 0)); - env.addNode(cellNode5, new Euclidean2DPosition(-1, 0)); - env.addNode(cellNode2, CELL_POS_TENSPOL4_1); - cellNode3.addReaction(inc.createReaction(rand, env, cellNode3, time, CELL_TENSION_POLARIZATION)); + environment.addNode(cellNode3, new Euclidean2DPosition(0, 0)); + environment.addNode(cellNode5, new Euclidean2DPosition(-1, 0)); + environment.addNode(cellNode2, CELL_POS_TENSPOL4_1); + cellNode3.addReaction(inc.createReaction(rand, environment, cellNode3, time, CELL_TENSION_POLARIZATION)); assertFalse(cellNode3.getReactions().isEmpty()); cellNode3.getReactions().stream() .findFirst() .orElseThrow().execute(); - assertEquals(new Euclidean2DPosition(1, 0), cellNode3.getPolarizationVersor()); + assertEquals(new Euclidean2DPosition(1, 0), cellNode3 + .asProperty(CircularDeformableCellProperty.class) + .getPolarizationVersor()); } /** @@ -311,15 +346,17 @@ void testTensionPolarization4() { */ @Test void testTensionPolarization5() { - env.addNode(cellNode3, new Euclidean2DPosition(0, 0)); - env.addNode(cellNode5, new Euclidean2DPosition(-1, 0)); - env.addNode(cellNode2, CELL_POS_TENSPOL5_1); - cellNode3.addReaction(inc.createReaction(rand, env, cellNode3, time, CELL_TENSION_POLARIZATION)); + environment.addNode(cellNode3, new Euclidean2DPosition(0, 0)); + environment.addNode(cellNode5, new Euclidean2DPosition(-1, 0)); + environment.addNode(cellNode2, CELL_POS_TENSPOL5_1); + cellNode3.addReaction(inc.createReaction(rand, environment, cellNode3, time, CELL_TENSION_POLARIZATION)); assertFalse(cellNode3.getReactions().isEmpty()); cellNode3.getReactions().stream() .findFirst() .orElseThrow().execute(); - assertEquals(new Euclidean2DPosition(1, 0), cellNode3.getPolarizationVersor()); + assertEquals(new Euclidean2DPosition(1, 0), cellNode3 + .asProperty(CircularDeformableCellProperty.class) + .getPolarizationVersor()); } /** @@ -327,15 +364,18 @@ void testTensionPolarization5() { */ @Test void testTensionPolarization6() { - env.addNode(cellNode3, new Euclidean2DPosition(0, 0)); - env.addNode(cellNode4, CELL_POS_TENSPOL6_1); - env.addNode(cellNode2, CELL_POS_TENSPOL6_2); - cellNode3.addReaction(inc.createReaction(rand, env, cellNode3, time, CELL_TENSION_POLARIZATION)); + environment.addNode(cellNode3, new Euclidean2DPosition(0, 0)); + environment.addNode(cellNode4, CELL_POS_TENSPOL6_1); + environment.addNode(cellNode2, CELL_POS_TENSPOL6_2); + cellNode3.addReaction(inc.createReaction(rand, environment, cellNode3, time, CELL_TENSION_POLARIZATION)); assertFalse(cellNode3.getReactions().isEmpty()); cellNode3.getReactions().stream() .findFirst() .orElseThrow().execute(); - assertEquals(new Euclidean2DPosition(0, 0), cellNode3.getPolarizationVersor()); + assertEquals( + new Euclidean2DPosition(0, 0), + cellNode3.asProperty(CircularDeformableCellProperty.class).getPolarizationVersor() + ); } /** @@ -343,13 +383,13 @@ void testTensionPolarization6() { */ @Test void testMoveNode1() { - env.addNode(cellNode1, new Euclidean2DPosition(0, 0)); - env.addNode(cellNode2, CELL_POS_MOV1); - env.moveNodeToPosition(cellNode1, new Euclidean2DPosition(0, 10)); + environment.addNode(cellNode1, new Euclidean2DPosition(0, 0)); + environment.addNode(cellNode2, CELL_POS_MOV1); + environment.moveNodeToPosition(cellNode1, new Euclidean2DPosition(0, 10)); assertEquals( - env.getPosition(cellNode1), + environment.getPosition(cellNode1), EXPECTED_POS_MOV1, - "Position of cellNode1 = " + env.getPosition(cellNode1) + "Position of cellNode1 = " + environment.getPosition(cellNode1) ); } } diff --git a/alchemist-incarnation-biochemistry/src/test/java/it/unibo/alchemist/test/TestEnvironmentNodes.java b/alchemist-incarnation-biochemistry/src/test/java/it/unibo/alchemist/test/TestEnvironmentNodes.java index cfe60f7f6a..46fd3de711 100644 --- a/alchemist-incarnation-biochemistry/src/test/java/it/unibo/alchemist/test/TestEnvironmentNodes.java +++ b/alchemist-incarnation-biochemistry/src/test/java/it/unibo/alchemist/test/TestEnvironmentNodes.java @@ -13,17 +13,17 @@ import it.unibo.alchemist.model.BiochemistryIncarnation; import it.unibo.alchemist.model.implementations.environments.BioRect2DEnvironment; import it.unibo.alchemist.model.implementations.molecules.Biomolecule; -import it.unibo.alchemist.model.implementations.nodes.CellNodeImpl; import it.unibo.alchemist.model.implementations.nodes.EnvironmentNodeImpl; import it.unibo.alchemist.model.implementations.positions.Euclidean2DPosition; import it.unibo.alchemist.model.implementations.timedistributions.ExponentialTime; -import it.unibo.alchemist.model.interfaces.CellNode; import it.unibo.alchemist.model.interfaces.Environment; import it.unibo.alchemist.model.interfaces.EnvironmentNode; import it.unibo.alchemist.model.interfaces.Incarnation; import it.unibo.alchemist.model.interfaces.Molecule; import it.unibo.alchemist.model.interfaces.Node; import it.unibo.alchemist.model.interfaces.Position; +import it.unibo.alchemist.model.interfaces.properties.CellProperty; +import it.unibo.alchemist.model.interfaces.properties.CircularCellProperty; import org.apache.commons.math3.random.MersenneTwister; import org.apache.commons.math3.random.RandomGenerator; import org.junit.jupiter.api.BeforeEach; @@ -50,13 +50,13 @@ final class TestEnvironmentNodes { private static final double PRECISION = 1e-12; private static final Incarnation INCARNATION = new BiochemistryIncarnation<>(); - private Environment env; + private Environment environment; private RandomGenerator rand; private void injectReaction(final String reaction, final Node destination, final double rate) { destination.addReaction(INCARNATION.createReaction( rand, - env, + environment, destination, new ExponentialTime<>(rate, rand), reaction @@ -67,10 +67,13 @@ private void injectAInEnvReaction(final Node destination, final double r injectReaction("[A] --> [A in env]", destination, rate); } + private Node createNode() { + return INCARNATION.createNode(rand, environment, null); + } @BeforeEach public void setUp() { - env = new BioRect2DEnvironment(); + environment = new BioRect2DEnvironment(); rand = new MersenneTwister(); } @@ -79,15 +82,15 @@ public void setUp() { */ @Test void test1() { - final CellNode cellNode = new CellNodeImpl<>(env); - final EnvironmentNode envNode = new EnvironmentNodeImpl(env); + final Node cellNode = createNode(); + final EnvironmentNode envNode = new EnvironmentNodeImpl(environment); final Molecule a = new Biomolecule("A"); injectAInEnvReaction(cellNode, 1); cellNode.setConcentration(a, 1000.0); - env.setLinkingRule(new it.unibo.alchemist.model.implementations.linkingrules.ConnectWithinDistance<>(2)); - env.addNode(cellNode, new Euclidean2DPosition(0, 0)); - env.addNode(envNode, new Euclidean2DPosition(0, 1)); - final Simulation sim = new Engine<>(env, 10_000); + environment.setLinkingRule(new it.unibo.alchemist.model.implementations.linkingrules.ConnectWithinDistance<>(2)); + environment.addNode(cellNode, new Euclidean2DPosition(0, 0)); + environment.addNode(envNode, new Euclidean2DPosition(0, 1)); + final Simulation sim = new Engine<>(environment, 10_000); sim.play(); sim.run(); assertEquals(envNode.getConcentration(a), 1000, PRECISION); @@ -98,15 +101,15 @@ void test1() { */ @Test void test2() { - final EnvironmentNode envNode1 = new EnvironmentNodeImpl(env); - final EnvironmentNode envNode2 = new EnvironmentNodeImpl(env); + final EnvironmentNode envNode1 = new EnvironmentNodeImpl(environment); + final EnvironmentNode envNode2 = new EnvironmentNodeImpl(environment); final Molecule a = new Biomolecule("A"); injectAInEnvReaction(envNode1, 1); envNode1.setConcentration(a, 1000.0); - env.setLinkingRule(new it.unibo.alchemist.model.implementations.linkingrules.ConnectWithinDistance<>(2)); - env.addNode(envNode1, new Euclidean2DPosition(0, 0)); - env.addNode(envNode2, new Euclidean2DPosition(0, 1)); - final Simulation sim = new Engine<>(env, 10_000); + environment.setLinkingRule(new it.unibo.alchemist.model.implementations.linkingrules.ConnectWithinDistance<>(2)); + environment.addNode(envNode1, new Euclidean2DPosition(0, 0)); + environment.addNode(envNode2, new Euclidean2DPosition(0, 1)); + final Simulation sim = new Engine<>(environment, 10_000); sim.play(); sim.run(); assertTrue(envNode2.getConcentration(a) == 1000 && envNode1.getConcentration(a) == 0); @@ -115,28 +118,28 @@ void test2() { private Node[] populateWithNodes(final int count) { final Node[] result = new EnvironmentNodeImpl[count]; for (int i = 0; i < result.length; i++) { - result[i] = new EnvironmentNodeImpl(env); + result[i] = new EnvironmentNodeImpl(environment); } return result; } private Node[] populateSurroundingOrigin() { final Node[] nodes = populateWithNodes(4); - env.addNode(nodes[0], new Euclidean2DPosition(0, 1)); - env.addNode(nodes[1], new Euclidean2DPosition(1, 0)); - env.addNode(nodes[2], new Euclidean2DPosition(-1, 0)); - env.addNode(nodes[3], new Euclidean2DPosition(0, -1)); + environment.addNode(nodes[0], new Euclidean2DPosition(0, 1)); + environment.addNode(nodes[1], new Euclidean2DPosition(1, 0)); + environment.addNode(nodes[2], new Euclidean2DPosition(-1, 0)); + environment.addNode(nodes[3], new Euclidean2DPosition(0, -1)); return nodes; } private void testDiffusion(final Node center) { - env.setLinkingRule(new it.unibo.alchemist.model.implementations.linkingrules.ConnectWithinDistance<>(2)); - env.addNode(center, new Euclidean2DPosition(0, 0)); + environment.setLinkingRule(new it.unibo.alchemist.model.implementations.linkingrules.ConnectWithinDistance<>(2)); + environment.addNode(center, new Euclidean2DPosition(0, 0)); final Node[] nodes = populateSurroundingOrigin(); final Molecule a = new Biomolecule("A"); injectAInEnvReaction(center, 1); center.setConcentration(a, 1000.0); - final Simulation sim = new Engine<>(env, 10_000); + final Simulation sim = new Engine<>(environment, 10_000); sim.play(); sim.run(); assertEquals(0, center.getConcentration(a)); @@ -148,7 +151,7 @@ private void testDiffusion(final Node center) { */ @Test void testDiffusionWithEnvironmentNodes() { - testDiffusion(new EnvironmentNodeImpl(env)); + testDiffusion(new EnvironmentNodeImpl(environment)); } /** @@ -156,7 +159,7 @@ void testDiffusionWithEnvironmentNodes() { */ @Test void testDiffusionWithCellNodes() { - testDiffusion(new CellNodeImpl<>(env)); + testDiffusion(createNode()); } /** @@ -164,28 +167,28 @@ void testDiffusionWithCellNodes() { */ @Test void test5() { - final CellNode cellNode = new CellNodeImpl<>(env); - final EnvironmentNode envNode1 = new EnvironmentNodeImpl(env); - final EnvironmentNode envNode2 = new EnvironmentNodeImpl(env); - final EnvironmentNode envNode3 = new EnvironmentNodeImpl(env); - final EnvironmentNode envNode4 = new EnvironmentNodeImpl(env); + final Node cellNode = createNode(); + final EnvironmentNode envNode1 = new EnvironmentNodeImpl(environment); + final EnvironmentNode envNode2 = new EnvironmentNodeImpl(environment); + final EnvironmentNode envNode3 = new EnvironmentNodeImpl(environment); + final EnvironmentNode envNode4 = new EnvironmentNodeImpl(environment); final Molecule a = new Biomolecule("A"); injectAInEnvReaction(cellNode, 1); injectAInEnvReaction(envNode1, 1000); injectAInEnvReaction(envNode2, 1000); final double total = 1000.0; cellNode.setConcentration(a, 1000.0); - env.setLinkingRule(new it.unibo.alchemist.model.implementations.linkingrules.ConnectWithinDistance<>(1)); + environment.setLinkingRule(new it.unibo.alchemist.model.implementations.linkingrules.ConnectWithinDistance<>(1)); final Euclidean2DPosition pos1 = new Euclidean2DPosition(0, -0.75); final Euclidean2DPosition pos2 = new Euclidean2DPosition(0, 0.75); final Euclidean2DPosition pos3 = new Euclidean2DPosition(0, 1.5); final Euclidean2DPosition pos4 = new Euclidean2DPosition(0, -1.5); - env.addNode(cellNode, new Euclidean2DPosition(0, 0)); - env.addNode(envNode1, pos1); - env.addNode(envNode2, pos2); - env.addNode(envNode3, pos3); - env.addNode(envNode4, pos4); - final Simulation sim = new Engine<>(env, 10_000); + environment.addNode(cellNode, new Euclidean2DPosition(0, 0)); + environment.addNode(envNode1, pos1); + environment.addNode(envNode2, pos2); + environment.addNode(envNode3, pos3); + environment.addNode(envNode4, pos4); + final Simulation sim = new Engine<>(environment, 10_000); sim.play(); sim.run(); assertNotEquals(0.0, envNode3.getConcentration(a)); @@ -198,13 +201,13 @@ void test5() { */ @Test void test6() { - final CellNode cellNode = new CellNodeImpl<>(env); + final Node cellNode = createNode(); final Molecule a = new Biomolecule("A"); injectAInEnvReaction(cellNode, 1); cellNode.setConcentration(a, 1000.0); - env.setLinkingRule(new it.unibo.alchemist.model.implementations.linkingrules.ConnectWithinDistance<>(2)); - env.addNode(cellNode, new Euclidean2DPosition(0, 0)); - final Simulation sim = new Engine<>(env, 10_000); + environment.setLinkingRule(new it.unibo.alchemist.model.implementations.linkingrules.ConnectWithinDistance<>(2)); + environment.addNode(cellNode, new Euclidean2DPosition(0, 0)); + final Simulation sim = new Engine<>(environment, 10_000); sim.play(); sim.run(); assertEquals(cellNode.getConcentration(a), 1000, PRECISION); @@ -230,18 +233,18 @@ void testEnv1() { */ @Test void testEnv2() { - final Environment env = testNoVar("testEnv2.yml"); - final Node center = env.getNodes().stream() + final Environment environment = testNoVar("testEnv2.yml"); + final Node center = environment.getNodes().stream() .parallel() - .filter(n -> n instanceof CellNode) + .filter(n -> n.asPropertyOrNull(CellProperty.class) != null) .findAny() .get(); - final double conAInNearest = env.getNodes().stream() + final double conAInNearest = environment.getNodes().stream() .parallel() .filter(n -> n.getClass().equals(EnvironmentNodeImpl.class)) .min((n1, n2) -> Double.compare( - env.getPosition(n1).distanceTo(env.getPosition(center)), - env.getPosition(n2).distanceTo(env.getPosition(center)) + environment.getPosition(n1).distanceTo(environment.getPosition(center)), + environment.getPosition(n2).distanceTo(environment.getPosition(center)) )) .get().getConcentration(new Biomolecule("A")); assertEquals(conAInNearest, 1000, PRECISION); @@ -255,7 +258,7 @@ void testEnv2() { void testEnv3() { final double conAInCell = (double) testNoVar("testEnv3.yml").getNodes().stream() .parallel() - .filter(n -> n.getClass().equals(CellNodeImpl.class)) + .filter(n -> n.asPropertyOrNull(CircularCellProperty.class) != null) .findAny() .get() .getConcentration(new Biomolecule("A")); @@ -277,7 +280,7 @@ void testEnv3() { void testEnv4() { final double conAInCell = (double) testNoVar("testEnv4.yml").getNodes().stream() .parallel() - .filter(n -> n.getClass().equals(CellNodeImpl.class)) + .filter(n -> n.asPropertyOrNull(CircularCellProperty.class) != null) .findAny() .get() .getConcentration(new Biomolecule("A")); @@ -295,16 +298,16 @@ void testEnv4() { */ @Test void testEnv5() { - final Environment env = testNoVar("testEnv5.yml"); - final double conAInEnv1 = (double) env.getNodes().stream() + final Environment environment = testNoVar("testEnv5.yml"); + final double conAInEnv1 = (double) environment.getNodes().stream() .parallel() - .filter(n -> env.getPosition(n).equals(new Euclidean2DPosition(0, 0))) + .filter(n -> environment.getPosition(n).equals(new Euclidean2DPosition(0, 0))) .findAny() .get() .getConcentration(new Biomolecule("A")); - final double conAInEnv2 = (double) env.getNodes().stream() + final double conAInEnv2 = (double) environment.getNodes().stream() .parallel() - .filter(n -> env.getPosition(n).equals(new Euclidean2DPosition(1, 0))) + .filter(n -> environment.getPosition(n).equals(new Euclidean2DPosition(1, 0))) .findAny() .get() .getConcentration(new Biomolecule("A")); @@ -325,14 +328,14 @@ void testEnv6() { */ @Test void testEnv7() { - final Environment env = testNoVar("testEnv7.yml"); - final double conAInCell = (double) env.getNodes().stream() + final Environment environment = testNoVar("testEnv7.yml"); + final double conAInCell = (double) environment.getNodes().stream() .parallel() - .filter(n -> n instanceof CellNode) + .filter(n -> n.asPropertyOrNull(CellProperty.class) != null) .findAny() .get() .getConcentration(new Biomolecule("A")); - final double conAInEnv = (double) env.getNodes().stream() + final double conAInEnv = (double) environment.getNodes().stream() .parallel() .filter(n -> n instanceof EnvironmentNode) .mapToDouble(n -> n.getConcentration(new Biomolecule("A"))) @@ -347,10 +350,10 @@ void testEnv7() { */ @Test void testEnv8() { - final Environment env = testNoVar("testEnv8.yml"); - final double conAInCell = (double) env.getNodes().stream() + final Environment environment = testNoVar("testEnv8.yml"); + final double conAInCell = (double) environment.getNodes().stream() .parallel() - .filter(n -> n instanceof CellNode) + .filter(n -> n.asPropertyOrNull(CellProperty.class) != null) .findAny() .get() .getConcentration(new Biomolecule("A")); @@ -367,11 +370,11 @@ private static > Environment testLoading( ) { final var res = ResourceLoader.getResource(resource); assertNotNull(res); - final Environment env = LoadAlchemist.from(res).getWith(vars).getEnvironment(); - final Simulation sim = new Engine<>(env, 10_000); + final Environment environment = LoadAlchemist.from(res).getWith(vars).getEnvironment(); + final Simulation sim = new Engine<>(environment, 10_000); sim.play(); sim.run(); - return env; + return environment; } } diff --git a/alchemist-incarnation-biochemistry/src/test/java/it/unibo/alchemist/test/TestIncarnation.java b/alchemist-incarnation-biochemistry/src/test/java/it/unibo/alchemist/test/TestIncarnation.java index f8352bc241..13232f19ad 100644 --- a/alchemist-incarnation-biochemistry/src/test/java/it/unibo/alchemist/test/TestIncarnation.java +++ b/alchemist-incarnation-biochemistry/src/test/java/it/unibo/alchemist/test/TestIncarnation.java @@ -23,11 +23,10 @@ import it.unibo.alchemist.model.implementations.conditions.NeighborhoodPresent; import it.unibo.alchemist.model.implementations.environments.BioRect2DEnvironment; import it.unibo.alchemist.model.implementations.molecules.Biomolecule; -import it.unibo.alchemist.model.implementations.nodes.CellNodeImpl; import it.unibo.alchemist.model.implementations.positions.Euclidean2DPosition; import it.unibo.alchemist.model.implementations.timedistributions.ExponentialTime; -import it.unibo.alchemist.model.interfaces.CellNode; import it.unibo.alchemist.model.interfaces.Environment; +import it.unibo.alchemist.model.interfaces.Node; import it.unibo.alchemist.model.interfaces.Reaction; import it.unibo.alchemist.model.interfaces.TimeDistribution; import org.apache.commons.math3.random.MersenneTwister; @@ -50,8 +49,8 @@ class TestIncarnation { private static final BiochemistryIncarnation INCARNATION = new BiochemistryIncarnation<>(); - private CellNode node; - private Environment env; + private Node node; + private Environment environment; private RandomGenerator rand; private TimeDistribution time; @@ -65,8 +64,8 @@ private static Biomolecule makeMol(final String name) { */ @BeforeEach public void setUp() { - env = new BioRect2DEnvironment(); - node = new CellNodeImpl<>(env); + environment = new BioRect2DEnvironment(); + node = INCARNATION.createNode(rand, environment, null); rand = new MersenneTwister(); time = new ExponentialTime<>(1, rand); } @@ -99,7 +98,7 @@ private void testR(final String param, final int nNeighAct, final int nEnvCond, final int nEnvAct) { // TODO custom conditions and actions - final Reaction r = INCARNATION.createReaction(rand, env, node, time, param); + final Reaction r = INCARNATION.createReaction(rand, environment, node, time, param); assertNotNull(r); assertEquals(nCond, r.getConditions().size()); assertEquals(nAct, r.getActions().size()); @@ -121,7 +120,7 @@ private void testR(final String param, private void testNoR(final String param) { // used for cases like [A] + [B in neighbor] --> [junction A-C] try { - INCARNATION.createReaction(rand, env, node, time, param); + INCARNATION.createReaction(rand, environment, node, time, param); fail(); } catch (final BiochemistryParseException e) { assertFalse(e.getMessage().isEmpty()); @@ -195,7 +194,7 @@ void testCreateReaction() { */ @Test void testCreateNode() { - assertThrows(IllegalArgumentException.class, () -> INCARNATION.createNode(rand, env, "foo")); + assertThrows(IllegalArgumentException.class, () -> INCARNATION.createNode(rand, environment, "foo")); } } diff --git a/alchemist-incarnation-biochemistry/src/test/java/it/unibo/alchemist/test/TestJunction.java b/alchemist-incarnation-biochemistry/src/test/java/it/unibo/alchemist/test/TestJunction.java index 2546ade004..92ce800ab1 100644 --- a/alchemist-incarnation-biochemistry/src/test/java/it/unibo/alchemist/test/TestJunction.java +++ b/alchemist-incarnation-biochemistry/src/test/java/it/unibo/alchemist/test/TestJunction.java @@ -15,34 +15,42 @@ import java.util.Map; import edu.umd.cs.findbugs.annotations.SuppressFBWarnings; +import it.unibo.alchemist.model.BiochemistryIncarnation; +import it.unibo.alchemist.model.interfaces.Incarnation; +import it.unibo.alchemist.model.interfaces.Node; +import it.unibo.alchemist.model.interfaces.properties.CellProperty; +import org.apache.commons.math3.random.MersenneTwister; import org.junit.jupiter.api.BeforeEach; import org.junit.jupiter.api.Test; import it.unibo.alchemist.model.implementations.environments.BioRect2DEnvironment; import it.unibo.alchemist.model.implementations.molecules.Biomolecule; import it.unibo.alchemist.model.implementations.molecules.Junction; -import it.unibo.alchemist.model.implementations.nodes.CellNodeImpl; import it.unibo.alchemist.model.implementations.positions.Euclidean2DPosition; import it.unibo.alchemist.model.interfaces.Environment; -import it.unibo.alchemist.model.interfaces.CellNode; /** */ @SuppressFBWarnings("UWF_FIELD_NOT_INITIALIZED_IN_CONSTRUCTOR") class TestJunction { - private CellNode node1; - private CellNode node2; - private CellNode node3; + private Node node1; + private Node node2; + private Node node3; + + private CellProperty getCell(final Node node) { + return node.asProperty(CellProperty.class); + } /** */ @BeforeEach public void setUp() { - final Environment env = new BioRect2DEnvironment(); - node1 = new CellNodeImpl<>(env); - node2 = new CellNodeImpl<>(env); - node3 = new CellNodeImpl<>(env); + final Environment environment = new BioRect2DEnvironment(); + final Incarnation inc = new BiochemistryIncarnation<>(); + node1 = inc.createNode(new MersenneTwister(), environment, null); + node2 = inc.createNode(new MersenneTwister(), environment, null); + node3 = inc.createNode(new MersenneTwister(), environment, null); } /** @@ -56,64 +64,66 @@ void test() { map1.put(new Biomolecule("B"), 1d); final Junction jBase = new Junction("A-B", map1, map2); final Junction j1 = new Junction(jBase); - node1.addJunction(j1, node2); - assertTrue(node1.containsJunction(j1)); - assertTrue(node1.containsJunction(jBase)); // same name here - assertFalse(node2.containsJunction(j1)); // this is just for this test, normally node2 contain j1 - assertFalse(node3.containsJunction(j1)); + getCell(node1).addJunction(j1, node2); + assertTrue(getCell(node1).containsJunction(j1)); + assertTrue(getCell(node1).containsJunction(jBase)); // same name here + assertFalse(getCell(node2).containsJunction(j1)); // this is just for this test, normally node2 contain j1 + assertFalse(getCell(node3).containsJunction(j1)); - assertEquals(node1.getJunctionsCount(), 1); - assertEquals(node2.getJunctionsCount(), 0); - assertEquals(node3.getJunctionsCount(), 0); + assertEquals(getCell(node1).getJunctionsCount(), 1); + assertEquals(getCell(node2).getJunctionsCount(), 0); + assertEquals(getCell(node3).getJunctionsCount(), 0); final Junction j2 = new Junction(jBase); - node1.addJunction(j2, node3); - assertTrue(node1.containsJunction(j1)); - assertTrue(node1.containsJunction(j2)); // same name here - assertFalse(node2.containsJunction(j2)); - assertFalse(node3.containsJunction(j2)); // this is just for this test, normally node3 contains j2 + getCell(node1).addJunction(j2, node3); + assertTrue(getCell(node1).containsJunction(j1)); + assertTrue(getCell(node1).containsJunction(j2)); // same name here + assertFalse(getCell(node2).containsJunction(j2)); + assertFalse(getCell(node3).containsJunction(j2)); // this is just for this test, normally node3 contains j2 - assertEquals(node1.getJunctionsCount(), 2); - assertEquals(node2.getJunctionsCount(), 0); - assertEquals(node3.getJunctionsCount(), 0); + assertEquals(getCell(node1).getJunctionsCount(), 2); + assertEquals(getCell(node2).getJunctionsCount(), 0); + final CellProperty b = getCell(node3); + assertEquals(b.getJunctionsCount(), 0); //CHECKSTYLE:OFF magicnumber final int totJ = 123; //CHECKSTYLE:ON magicnumber for (int i = 0; i < totJ; i++) { // add many identical junction to node 2 final Junction jtmp = new Junction(jBase); - node2.addJunction(jtmp, node3); + getCell(node2).addJunction(jtmp, node3); } /* Situation Summary: * node1: 1 junction A-B with node2, 1 junction A-B with node3 * node2: totJ junction A-B with node3 * node3: nothing */ - assertEquals(node1.getJunctionsCount(), 2); - assertEquals(node2.getJunctionsCount(), totJ); - assertEquals(node3.getJunctionsCount(), 0); + assertEquals(getCell(node1).getJunctionsCount(), 2); + assertEquals(getCell(node2).getJunctionsCount(), totJ); + assertEquals(getCell(node3).getJunctionsCount(), 0); /* **** Remove junctions **** */ // TODO ? note that molecule in the junction is not placed in cell after destruction. It is not implemented yet. - node1.removeJunction(jBase, node2); // remove a junction of the type A-B which has node2 as neighbor - assertEquals(node1.getJunctionsCount(), 1); - assertEquals(node2.getJunctionsCount(), totJ); - assertEquals(node3.getJunctionsCount(), 0); - node1.removeJunction(jBase, node2); // do nothing, because node1 hasn't any junction with node2 now - assertEquals(node1.getJunctionsCount(), 1); - assertEquals(node2.getJunctionsCount(), totJ); - assertEquals(node3.getJunctionsCount(), 0); - node1.removeJunction(jBase, node3); // remove the last junction of node1 - assertEquals(node1.getJunctionsCount(), 0); - assertEquals(node2.getJunctionsCount(), totJ); - assertEquals(node3.getJunctionsCount(), 0); + getCell(node1).removeJunction(jBase, node2); // remove a junction of the type A-B which has node2 as neighbor + assertEquals(getCell(node1).getJunctionsCount(), 1); + assertEquals(getCell(node2).getJunctionsCount(), totJ); + assertEquals(getCell(node3).getJunctionsCount(), 0); + getCell(node1).removeJunction(jBase, node2); // do nothing, because node1 hasn't any junction with node2 now + assertEquals(getCell(node1).getJunctionsCount(), 1); + assertEquals(getCell(node2).getJunctionsCount(), totJ); + assertEquals(getCell(node3).getJunctionsCount(), 0); + getCell(node1).removeJunction(jBase, node3); // remove the last junction of node1 + assertEquals(getCell(node1).getJunctionsCount(), 0); + assertEquals(getCell(node2).getJunctionsCount(), totJ); + assertEquals(getCell(node3).getJunctionsCount(), 0); final Map mapD1 = new HashMap<>(1); final Map mapD2 = new HashMap<>(1); map1.put(new Biomolecule("C"), 1d); map1.put(new Biomolecule("D"), 1d); - final Junction jDiff = new Junction("C-D", mapD1, mapD2); // a new junction that is not present in any node + // a new junction that is not present in any node + final Junction jDiff = new Junction("C-D", mapD1, mapD2); - node2.removeJunction(jDiff, node3); // do nothing because node2 hasn't a junction C-D - assertEquals(node2.getJunctionsCount(), totJ); + getCell(node2).removeJunction(jDiff, node3); // do nothing because node2 hasn't a junction C-D + assertEquals(getCell(node2).getJunctionsCount(), totJ); } } diff --git a/alchemist-incarnation-biochemistry/src/test/kotlin/it/unibo/alchemist/test/TestMoleculeSwapWithinNeighborhood.kt b/alchemist-incarnation-biochemistry/src/test/kotlin/it/unibo/alchemist/test/TestMoleculeSwapWithinNeighborhood.kt index ca96d9fa1a..9fbf070da8 100644 --- a/alchemist-incarnation-biochemistry/src/test/kotlin/it/unibo/alchemist/test/TestMoleculeSwapWithinNeighborhood.kt +++ b/alchemist-incarnation-biochemistry/src/test/kotlin/it/unibo/alchemist/test/TestMoleculeSwapWithinNeighborhood.kt @@ -18,11 +18,10 @@ import it.unibo.alchemist.model.BiochemistryIncarnation import it.unibo.alchemist.model.implementations.conditions.AbstractNeighborCondition import it.unibo.alchemist.model.implementations.environments.BioRect2DEnvironment import it.unibo.alchemist.model.implementations.linkingrules.ConnectWithinDistance -import it.unibo.alchemist.model.implementations.nodes.CellNodeImpl import it.unibo.alchemist.model.implementations.positions.Euclidean2DPosition import it.unibo.alchemist.model.implementations.timedistributions.ExponentialTime -import it.unibo.alchemist.model.interfaces.CellNode import it.unibo.alchemist.model.interfaces.Environment +import it.unibo.alchemist.model.interfaces.Node import it.unibo.alchemist.model.interfaces.Reaction import kotlin.properties.Delegates import org.apache.commons.math3.random.MersenneTwister @@ -36,7 +35,7 @@ private val TIME = ExponentialTime(1.0, RANDOM) private val LINKING_RULE = ConnectWithinDistance(5.0) private val INITIAL_POSITIONS = Pair(Euclidean2DPosition(0.0, 0.0), Euclidean2DPosition(1.0, 0.0)) private var environment: Environment by Delegates.notNull() -private var nodes: Pair, CellNode> by Delegates.notNull() +private var nodes: Pair, Node> by Delegates.notNull() class TestMoleculeSwapWithinNeighborhood : StringSpec({ "send molecule to a neighbor" { @@ -58,7 +57,10 @@ class TestMoleculeSwapWithinNeighborhood : StringSpec({ }) { override fun beforeTest(testCase: TestCase) { environment = BioRect2DEnvironment() - nodes = Pair(CellNodeImpl(environment), CellNodeImpl(environment)) + nodes = Pair( + INCARNATION.createNode(RANDOM, environment, null), + INCARNATION.createNode(RANDOM, environment, null) + ) environment.linkingRule = LINKING_RULE environment.addNode(nodes.first, INITIAL_POSITIONS.first) environment.addNode(nodes.second, INITIAL_POSITIONS.second) diff --git a/alchemist-incarnation-biochemistry/src/test/kotlin/it/unibo/alchemist/test/TestNeighborhoodReactionsPropensities.kt b/alchemist-incarnation-biochemistry/src/test/kotlin/it/unibo/alchemist/test/TestNeighborhoodReactionsPropensities.kt index 8796c73385..4bae500b44 100644 --- a/alchemist-incarnation-biochemistry/src/test/kotlin/it/unibo/alchemist/test/TestNeighborhoodReactionsPropensities.kt +++ b/alchemist-incarnation-biochemistry/src/test/kotlin/it/unibo/alchemist/test/TestNeighborhoodReactionsPropensities.kt @@ -22,15 +22,15 @@ import it.unibo.alchemist.model.implementations.conditions.NeighborhoodPresent import it.unibo.alchemist.model.implementations.environments.BioRect2DEnvironment import it.unibo.alchemist.model.implementations.linkingrules.ConnectWithinDistance import it.unibo.alchemist.model.implementations.molecules.Junction -import it.unibo.alchemist.model.implementations.nodes.CellNodeImpl import it.unibo.alchemist.model.implementations.positions.Euclidean2DPosition import it.unibo.alchemist.model.implementations.timedistributions.ExponentialTime -import it.unibo.alchemist.model.interfaces.CellNode import it.unibo.alchemist.model.interfaces.Environment import it.unibo.alchemist.model.interfaces.Node +import it.unibo.alchemist.model.interfaces.properties.CellProperty import org.apache.commons.math3.random.MersenneTwister import org.apache.commons.math3.util.CombinatoricsUtils.binomialCoefficientDouble import kotlin.properties.Delegates +import it.unibo.alchemist.model.interfaces.Node.Companion.asProperty private const val BIOMOLECULE_NEEDED = 5 private const val NEIGHBORHOOD_PRESENT_REACTION = "[5 token] --> [5 token in neighbor]" @@ -46,8 +46,8 @@ private val TIME = ExponentialTime(1.0, RANDOM) private val LINKING_RULE = ConnectWithinDistance(5.0) private val POSITION = Euclidean2DPosition(0.0, 0.0) private var environment: Environment by Delegates.notNull() -private var centralNode: CellNode by Delegates.notNull() -private var neighbors: List> by Delegates.notNull() +private var centralNode: Node by Delegates.notNull() +private var neighbors: List> by Delegates.notNull() class TestNeighborhoodReactionsPropensities : StringSpec({ "test neighborhood present propensities" { @@ -55,7 +55,10 @@ class TestNeighborhoodReactionsPropensities : StringSpec({ } "test junction present propensities" { 0.rangeTo(9).forEach { - 0.rangeTo(it).forEach { _ -> centralNode.addJunction(JUNCTION, neighbors[it]) } + 0.rangeTo(it).forEach { _ -> + centralNode.asProperty>() + .addJunction(JUNCTION, neighbors[it]) + } } testSimulation(JUNCTION_PRESENT_REACTION) } @@ -66,11 +69,11 @@ class TestNeighborhoodReactionsPropensities : StringSpec({ override fun beforeTest(testCase: TestCase) { environment = BioRect2DEnvironment() environment.linkingRule = LINKING_RULE - centralNode = CellNodeImpl(environment) + centralNode = INCARNATION.createNode(RANDOM, environment, null) centralNode.setConcentration(BIOMOLECULE, 100.0) environment.addNode(centralNode, POSITION) neighbors = 1.rangeTo(10) - .map { Pair(it * 10.0, CellNodeImpl(environment)) } + .map { Pair(it * 10.0, INCARNATION.createNode(RANDOM, environment, null)) } .onEach { it.second.setConcentration(BIOMOLECULE, it.first) } .map { it.second } .onEach { environment.addNode(it, POSITION) } @@ -122,7 +125,8 @@ private val Node.neighborhoodPresentPropensity: Double private val Node.junctionPresentPropensity: Double get() = checkCellNodeAndGetPropensity { - centralNode.junctions.getOrDefault(JUNCTION, emptyMap()).getOrDefault(it, 0).toDouble() + centralNode.asProperty>() + .junctions.getOrDefault(JUNCTION, emptyMap()).getOrDefault(it, 0).toDouble() } private val Node.biomoleculeInNeighborPropensity: Double @@ -130,5 +134,5 @@ private val Node.biomoleculeInNeighborPropensity: Double binomialCoefficientDouble(it.getConcentration(BIOMOLECULE).toInt(), BIOMOLECULE_NEEDED) } -private fun Node.checkCellNodeAndGetPropensity(propensityFunction: (CellNode<*>) -> Double) = - if (this is CellNode<*>) { propensityFunction(this) } else { 0.0 } +private fun Node.checkCellNodeAndGetPropensity(propensityFunction: (Node) -> Double) = + if (this.asPropertyOrNull(CellProperty::class) != null) { propensityFunction(this) } else { 0.0 } diff --git a/alchemist-incarnation-biochemistry/src/test/resources/provaBCReaction.yml b/alchemist-incarnation-biochemistry/src/test/resources/provaBCReaction.yml index 4b15be4b17..371746deed 100644 --- a/alchemist-incarnation-biochemistry/src/test/resources/provaBCReaction.yml +++ b/alchemist-incarnation-biochemistry/src/test/resources/provaBCReaction.yml @@ -16,8 +16,6 @@ _ionic: &waterDissociation deployments: - type: Circle parameters: [20, 0, 0, 5] - nodes: - type: CellNodeImpl - parameters: [1, p] # p useless parameter + nodes: "1" programs: - *waterDissociation diff --git a/alchemist-incarnation-biochemistry/src/test/resources/provaBCReaction2.yml b/alchemist-incarnation-biochemistry/src/test/resources/provaBCReaction2.yml index fda4915018..83dbbd927c 100644 --- a/alchemist-incarnation-biochemistry/src/test/resources/provaBCReaction2.yml +++ b/alchemist-incarnation-biochemistry/src/test/resources/provaBCReaction2.yml @@ -16,29 +16,21 @@ _ionic: &waterDissociation deployments: - type: Circle parameters: [4, 0, 0, 5] - nodes: - type: CellNodeImpl - parameters: [4, p] # p useless parameter + nodes: "4" # radius of the CircularCell programs: - *waterDissociation - type: Circle parameters: [5, 0, 0, 5] - nodes: - type: CellNodeImpl - parameters: [3, p] # p useless parameter + nodes: "3" programs: - *waterDissociation - type: Circle parameters: [5, 0, 0, 5] - nodes: - type: CellNodeImpl - parameters: [2, p] # p useless parameter + nodes: "2" programs: - *waterDissociation - type: Circle parameters: [5, 0, 0, 5] - nodes: - type: CellNodeImpl - parameters: [1, p] # p useless parameter + nodes: "1" programs: - *waterDissociation diff --git a/alchemist-incarnation-biochemistry/src/test/resources/testEnv1.yml b/alchemist-incarnation-biochemistry/src/test/resources/testEnv1.yml index 56db4d132b..009f2a0e11 100644 --- a/alchemist-incarnation-biochemistry/src/test/resources/testEnv1.yml +++ b/alchemist-incarnation-biochemistry/src/test/resources/testEnv1.yml @@ -16,9 +16,6 @@ _reactions: &reactions deployments: - type: Point parameters: [0, 0] - nodes: - type: CellNodeImpl - parameters: [] contents: - molecule: A concentration: 1000 diff --git a/alchemist-incarnation-biochemistry/src/test/resources/testEnv2.yml b/alchemist-incarnation-biochemistry/src/test/resources/testEnv2.yml index 42e4298c7c..392fa1afde 100644 --- a/alchemist-incarnation-biochemistry/src/test/resources/testEnv2.yml +++ b/alchemist-incarnation-biochemistry/src/test/resources/testEnv2.yml @@ -16,9 +16,6 @@ _reactions: &reactions deployments: - type: Point parameters: [0, 0] - nodes: - type: CellNodeImpl - parameters: [] contents: - molecule: A concentration: 1000 diff --git a/alchemist-incarnation-biochemistry/src/test/resources/testEnv3.yml b/alchemist-incarnation-biochemistry/src/test/resources/testEnv3.yml index acfe82efcb..04bc0ac2f6 100644 --- a/alchemist-incarnation-biochemistry/src/test/resources/testEnv3.yml +++ b/alchemist-incarnation-biochemistry/src/test/resources/testEnv3.yml @@ -16,9 +16,6 @@ _reactions: &reactions deployments: - type: Point parameters: [0, 0] - nodes: - type: CellNodeImpl - parameters: [] programs: - *reactions - type: Point diff --git a/alchemist-incarnation-biochemistry/src/test/resources/testEnv4.yml b/alchemist-incarnation-biochemistry/src/test/resources/testEnv4.yml index 5d7ee80050..c49f8b098d 100644 --- a/alchemist-incarnation-biochemistry/src/test/resources/testEnv4.yml +++ b/alchemist-incarnation-biochemistry/src/test/resources/testEnv4.yml @@ -16,9 +16,6 @@ _reactions: &reactions deployments: - type: Point parameters: [0, 0] - nodes: - type: CellNodeImpl - parameters: [] programs: - *reactions - type: Point diff --git a/alchemist-incarnation-biochemistry/src/test/resources/testEnv6.yml b/alchemist-incarnation-biochemistry/src/test/resources/testEnv6.yml index 4b6b400d64..0b56903321 100644 --- a/alchemist-incarnation-biochemistry/src/test/resources/testEnv6.yml +++ b/alchemist-incarnation-biochemistry/src/test/resources/testEnv6.yml @@ -26,9 +26,6 @@ deployments: - *reactions - type: Point parameters: [1, 0] - nodes: - type: CellNodeImpl - parameters: [] contents: - molecule: B concentration: 10 diff --git a/alchemist-incarnation-biochemistry/src/test/resources/testEnv7.yml b/alchemist-incarnation-biochemistry/src/test/resources/testEnv7.yml index 7a99f3c6c1..20aef298a8 100644 --- a/alchemist-incarnation-biochemistry/src/test/resources/testEnv7.yml +++ b/alchemist-incarnation-biochemistry/src/test/resources/testEnv7.yml @@ -16,9 +16,6 @@ _reactions: &reactions deployments: - type: Point parameters: [0, 0] - nodes: - type: CellNodeImpl - parameters: [] contents: - molecule: A concentration: 1000 diff --git a/alchemist-incarnation-biochemistry/src/test/resources/testEnv8.yml b/alchemist-incarnation-biochemistry/src/test/resources/testEnv8.yml index d734d9e4eb..cb3c15ecb2 100644 --- a/alchemist-incarnation-biochemistry/src/test/resources/testEnv8.yml +++ b/alchemist-incarnation-biochemistry/src/test/resources/testEnv8.yml @@ -16,9 +16,6 @@ _reactions: &reactions deployments: - type: Point parameters: [0, 0] - nodes: - type: CellNodeImpl - parameters: [] contents: - molecule: A concentration: 1000 diff --git a/alchemist-incarnation-protelis/src/main/java/it/unibo/alchemist/model/ProtelisIncarnation.java b/alchemist-incarnation-protelis/src/main/java/it/unibo/alchemist/model/ProtelisIncarnation.java index e520861e09..b820450a77 100644 --- a/alchemist-incarnation-protelis/src/main/java/it/unibo/alchemist/model/ProtelisIncarnation.java +++ b/alchemist-incarnation-protelis/src/main/java/it/unibo/alchemist/model/ProtelisIncarnation.java @@ -15,17 +15,19 @@ import com.google.common.collect.Lists; import com.google.common.collect.Sets; import edu.umd.cs.findbugs.annotations.SuppressFBWarnings; +import it.unibo.alchemist.model.implementations.properties.Protelis; import it.unibo.alchemist.model.implementations.actions.RunProtelisProgram; import it.unibo.alchemist.model.implementations.actions.SendToNeighbor; import it.unibo.alchemist.model.implementations.conditions.ComputationalRoundComplete; import it.unibo.alchemist.model.implementations.molecules.SimpleMolecule; -import it.unibo.alchemist.model.implementations.nodes.ProtelisNode; +import it.unibo.alchemist.model.implementations.nodes.GenericNode; import it.unibo.alchemist.model.implementations.reactions.ChemicalReaction; import it.unibo.alchemist.model.implementations.reactions.Event; import it.unibo.alchemist.model.implementations.timedistributions.DiracComb; import it.unibo.alchemist.model.implementations.timedistributions.ExponentialTime; import it.unibo.alchemist.model.implementations.times.DoubleTime; import it.unibo.alchemist.model.interfaces.Action; +import it.unibo.alchemist.model.interfaces.NodeProperty; import it.unibo.alchemist.model.interfaces.Condition; import it.unibo.alchemist.model.interfaces.Environment; import it.unibo.alchemist.model.interfaces.Incarnation; @@ -35,9 +37,11 @@ import it.unibo.alchemist.model.interfaces.Reaction; import it.unibo.alchemist.model.interfaces.Time; import it.unibo.alchemist.model.interfaces.TimeDistribution; +import it.unibo.alchemist.model.interfaces.properties.ProtelisProperty; import org.apache.commons.lang3.StringUtils; import org.apache.commons.math3.random.MersenneTwister; import org.apache.commons.math3.random.RandomGenerator; +import org.jetbrains.annotations.NotNull; import org.protelis.lang.ProtelisLoader; import org.protelis.lang.datatype.DeviceUID; import org.protelis.vm.CodePath; @@ -77,6 +81,7 @@ public final class ProtelisIncarnation

> implements Incarna .newBuilder() .expireAfterAccess(10, TimeUnit.MINUTES) .build(new CacheLoader<>() { + @NotNull @Override public SynchronizedVM load(@Nonnull final CacheKey key) { return new SynchronizedVM(key); @@ -84,7 +89,7 @@ public SynchronizedVM load(@Nonnull final CacheKey key) { }); private static List> getIncomplete( - final ProtelisNode protelisNode, + final Node protelisNode, final List> alreadyDone ) { return protelisNode.getReactions().stream() @@ -106,6 +111,12 @@ private static List> getIncomplete( .collect(Collectors.toList()); } + private void checkIsProtelisNode(final Node node, final String exceptionMessage) { + if (node == null || node.asPropertyOrNull(ProtelisProperty.class) == null) { + throw new IllegalArgumentException(exceptionMessage); + } + } + @Override public Action createAction( final RandomGenerator randomGenerator, @@ -116,38 +127,34 @@ public Action createAction( final String additionalParameters ) { Objects.requireNonNull(additionalParameters); - if (node instanceof ProtelisNode) { - final ProtelisNode

pNode = (ProtelisNode

) node; - if ("send".equalsIgnoreCase(additionalParameters)) { - final List> alreadyDone = pNode.getReactions() - .parallelStream() - .flatMap(r -> r.getActions().parallelStream()) - .filter(a -> a instanceof SendToNeighbor) - .map(c -> ((SendToNeighbor) c).getProtelisProgram()) - .collect(Collectors.toList()); - final List> pList = getIncomplete(pNode, alreadyDone); - if (pList.isEmpty()) { - throw new IllegalStateException("There is no program requiring a " - + SendToNeighbor.class.getSimpleName() + " action"); - } - if (pList.size() > 1) { - throw new IllegalStateException("There are too many programs requiring a " - + SendToNeighbor.class.getName() + " action: " + pList); - } - return new SendToNeighbor(pNode, reaction, pList.get(0)); - } else { - try { - return new RunProtelisProgram<>(environment, pNode, reaction, randomGenerator, additionalParameters); - } catch (RuntimeException e) { // NOPMD AvoidCatchingGenericException - throw new IllegalArgumentException( - "Could not create the requested Protelis program: " + additionalParameters, - e - ); - } + checkIsProtelisNode(node, "The node must have a " + ProtelisProperty.class.getSimpleName()); + if ("send".equalsIgnoreCase(additionalParameters)) { + final List> alreadyDone = node.getReactions() + .parallelStream() + .flatMap(r -> r.getActions().parallelStream()) + .filter(a -> a instanceof SendToNeighbor) + .map(c -> ((SendToNeighbor) c).getProtelisProgram()) + .collect(Collectors.toList()); + final List> pList = getIncomplete(node, alreadyDone); + if (pList.isEmpty()) { + throw new IllegalStateException("There is no program requiring a " + + SendToNeighbor.class.getSimpleName() + " action"); + } + if (pList.size() > 1) { + throw new IllegalStateException("There are too many programs requiring a " + + SendToNeighbor.class.getName() + " action: " + pList); + } + return new SendToNeighbor(node, reaction, pList.get(0)); + } else { + try { + return new RunProtelisProgram<>(environment, node, reaction, randomGenerator, additionalParameters); + } catch (RuntimeException e) { // NOPMD AvoidCatchingGenericException + throw new IllegalArgumentException( + "Could not create the requested Protelis program: " + additionalParameters, + e + ); } } - throw new IllegalArgumentException("The node must be an instance of " + ProtelisNode.class.getSimpleName() - + ", it is a " + node.getClass().getName() + " instead"); } @Override @@ -163,6 +170,11 @@ public Object createConcentration(final String s) { } } + @Override + public Object createConcentration() { + return null; + } + @Override public Condition createCondition( final RandomGenerator randomGenerator, @@ -172,35 +184,29 @@ public Condition createCondition( final Reaction reaction, final String additionalParameters ) { - if (node instanceof ProtelisNode) { - final ProtelisNode pNode = (ProtelisNode) node; - /* - * The list of ProtelisPrograms that have already been completed with a ComputationalRoundComplete condition - */ - final List> alreadyDone = pNode.getReactions() - .stream() - .flatMap(r -> r.getConditions().stream()) - .filter(c -> c instanceof ComputationalRoundComplete) - .map(c -> ((ComputationalRoundComplete) c).getProgram()) - .collect(Collectors.toList()); - final List> pList = getIncomplete(pNode, alreadyDone); - if (pList.isEmpty()) { - throw new IllegalStateException( - "There is no program requiring a " + ComputationalRoundComplete.class.getSimpleName() + " condition" - ); - } - if (pList.size() > 1) { - throw new IllegalStateException( - "There are too many programs requiring a " + ComputationalRoundComplete.class.getSimpleName() - + " condition: " + pList - ); - } - return new ComputationalRoundComplete(pNode, pList.get(0)); + checkIsProtelisNode(node, "The node must have a " + ProtelisProperty.class.getSimpleName()); + /* + * The list of ProtelisPrograms that have already been completed with a ComputationalRoundComplete condition + */ + final List> alreadyDone = node.getReactions() + .stream() + .flatMap(r -> r.getConditions().stream()) + .filter(c -> c instanceof ComputationalRoundComplete) + .map(c -> ((ComputationalRoundComplete) c).getProgram()) + .collect(Collectors.toList()); + final List> pList = getIncomplete(node, alreadyDone); + if (pList.isEmpty()) { + throw new IllegalStateException( + "There is no program requiring a " + ComputationalRoundComplete.class.getSimpleName() + " condition" + ); + } + if (pList.size() > 1) { + throw new IllegalStateException( + "There are too many programs requiring a " + ComputationalRoundComplete.class.getSimpleName() + + " condition: " + pList + ); } - throw new IllegalArgumentException( - "The node must be an instance of " + ProtelisNode.class.getSimpleName() + ", it is a " - + node.getClass().getName() + " instead" - ); + return new ComputationalRoundComplete(node, pList.get(0)); } @Override @@ -214,7 +220,9 @@ public Node createNode( final Environment environment, final String parameter ) { - return new ProtelisNode<>(environment); + final Node node = new GenericNode<>(this, environment); + node.addProperty(new Protelis

((ProtelisIncarnation) environment.getIncarnation(), node)); + return node; } @Override @@ -343,9 +351,9 @@ public String toString() { return "Wapper over a non-ProtelisNode for an invalid DeviceUID, meant to host local-only computation."; } }; - private final Node node; + private final Node node; - private DummyContext(final Node node) { + private DummyContext(final Node node) { super(new ProtectedExecutionEnvironment(node), new NetworkManager() { @Override public Map> getNeighborState() { @@ -367,10 +375,8 @@ public Number getCurrentTime() { @Override @SuppressFBWarnings("EI_EXPOSE_REP") public DeviceUID getDeviceUID() { - if (node instanceof ProtelisNode) { - return (ProtelisNode) node; - } - return NO_NODE_ID; + final ProtelisProperty protelisProperty = node.asPropertyOrNull(ProtelisProperty.class); + return protelisProperty != null ? protelisProperty : NO_NODE_ID; } @Override @@ -510,12 +516,12 @@ public int compareTo(@Nonnull final Node o) { } @Override - public void addReaction(final Reaction r) { + public void addReaction(@NotNull final Reaction r) { notImplemented(); } @Override - public boolean contains(final Molecule mol) { + public boolean contains(@NotNull final Molecule mol) { return notImplemented(); } @@ -525,10 +531,11 @@ public int getMoleculeCount() { } @Override - public Object getConcentration(final Molecule mol) { + public Object getConcentration(@NotNull final Molecule mol) { return notImplemented(); } + @NotNull @Override public Map getContents() { return notImplemented(); @@ -539,28 +546,30 @@ public int getId() { return notImplemented(); } + @NotNull @Override public List> getReactions() { return Collections.emptyList(); } @Override - public void removeConcentration(final Molecule mol) { + public void removeConcentration(@NotNull final Molecule mol) { notImplemented(); } @Override - public void removeReaction(final Reaction r) { + public void removeReaction(@NotNull final Reaction r) { notImplemented(); } @Override - public void setConcentration(final Molecule mol, final Object c) { + public void setConcentration(@NotNull final Molecule mol, final Object c) { notImplemented(); } + @NotNull @Override - public Node cloneNode(final Time t) { + public Node cloneNode(@NotNull final Time t) { return notImplemented(); } @@ -573,6 +582,19 @@ public boolean equals(final Object obj) { public int hashCode() { return -1; } + + + @Override + public void addProperty(@NotNull final NodeProperty nodeProperty) { + notImplemented(); + } + + @NotNull + @Override + public List> getCapabilities() { + return Collections.emptyList(); + } + } } diff --git a/alchemist-incarnation-protelis/src/main/java/it/unibo/alchemist/model/implementations/actions/RunProtelisProgram.java b/alchemist-incarnation-protelis/src/main/java/it/unibo/alchemist/model/implementations/actions/RunProtelisProgram.java index db2d9dac9d..b632073dee 100644 --- a/alchemist-incarnation-protelis/src/main/java/it/unibo/alchemist/model/implementations/actions/RunProtelisProgram.java +++ b/alchemist-incarnation-protelis/src/main/java/it/unibo/alchemist/model/implementations/actions/RunProtelisProgram.java @@ -10,7 +10,6 @@ import edu.umd.cs.findbugs.annotations.SuppressFBWarnings; import it.unibo.alchemist.model.implementations.molecules.SimpleMolecule; -import it.unibo.alchemist.model.implementations.nodes.ProtelisNode; import it.unibo.alchemist.model.interfaces.Action; import it.unibo.alchemist.model.interfaces.Context; import it.unibo.alchemist.model.interfaces.Dependency; @@ -19,6 +18,7 @@ import it.unibo.alchemist.model.interfaces.Node; import it.unibo.alchemist.model.interfaces.Position; import it.unibo.alchemist.model.interfaces.Reaction; +import it.unibo.alchemist.model.interfaces.properties.ProtelisProperty; import it.unibo.alchemist.protelis.AlchemistExecutionContext; import it.unibo.alchemist.protelis.AlchemistNetworkManager; import org.apache.commons.math3.distribution.RealDistribution; @@ -47,7 +47,7 @@ public final class RunProtelisProgram

> implements Action environment; private final Molecule name; - private final ProtelisNode

node; + private final Node node; private String originalProgram = "unknown"; private final ProtelisProgram program; @SuppressFBWarnings(value = "SE_BAD_FIELD", justification = "All the random engines provided by Apache are Serializable") @@ -60,7 +60,7 @@ public final class RunProtelisProgram

> implements Action environment, - final ProtelisNode

node, + final Node node, final Reaction reaction, final RandomGenerator randomGenerator, final ProtelisProgram program, @@ -80,7 +80,7 @@ private RunProtelisProgram( random = requireNonNull(randomGenerator); this.reaction = requireNonNull(reaction); networkManager = new AlchemistNetworkManager(reaction, this, retentionTime, packetLossDistance); - this.node.addNetworkManger(this, networkManager); + this.node.asProperty(ProtelisProperty.class).addNetworkManger(this, networkManager); executionContext = new AlchemistExecutionContext<>(environment, node, reaction, randomGenerator, networkManager); vm = new ProtelisVM(program, executionContext); this.retentionTime = retentionTime; @@ -102,7 +102,7 @@ private RunProtelisProgram( */ public RunProtelisProgram( final Environment environment, - final ProtelisNode

node, + final Node node, final Reaction reaction, final RandomGenerator randomGenerator, final String program @@ -129,7 +129,7 @@ public RunProtelisProgram( */ public RunProtelisProgram( final Environment environment, - final ProtelisNode

node, + final Node node, final Reaction reaction, final RandomGenerator randomGenerator, final String program, @@ -164,7 +164,7 @@ public RunProtelisProgram( */ public RunProtelisProgram( final Environment environment, - final ProtelisNode

node, + final Node node, final Reaction reaction, final RandomGenerator randomGenerator, final String program, @@ -210,7 +210,7 @@ public RunProtelisProgram( */ public RunProtelisProgram( final Environment environment, - final ProtelisNode

node, + final Node node, final Reaction reaction, final RandomGenerator randomGenerator, final String program, @@ -257,7 +257,7 @@ public RunProtelisProgram( */ public RunProtelisProgram( final Environment environment, - final ProtelisNode

node, + final Node node, final Reaction reaction, final RandomGenerator randomGenerator, final ProtelisProgram program, @@ -302,7 +302,7 @@ public RunProtelisProgram( */ public RunProtelisProgram( final Environment environment, - final ProtelisNode

node, + final Node node, final Reaction reaction, final RandomGenerator randomGenerator, final String program, @@ -331,11 +331,11 @@ public Molecule asMolecule() { @Override @SuppressWarnings("unchecked") public RunProtelisProgram

cloneAction(final Node node, final Reaction reaction) { - if (node instanceof ProtelisNode) { + if (node.asPropertyOrNull(ProtelisProperty.class) != null) { try { - return new RunProtelisProgram<>( + return new RunProtelisProgram

( getEnvironment(), - (ProtelisNode

) node, + node, reaction, getRandomGenerator(), originalProgram, @@ -345,7 +345,7 @@ public RunProtelisProgram

cloneAction(final Node node, final Reaction throw new IllegalStateException(e); } } - throw new IllegalArgumentException("The node must be a " + ProtelisNode.class.getSimpleName()); + throw new IllegalArgumentException("The node must have an instance of " + ProtelisProperty.class.getSimpleName()); } @Override @@ -392,7 +392,7 @@ public ListSet getOutboundDependencies() { * @return the node */ @SuppressFBWarnings(value = EI_EXPOSE_REP, justification = INTENTIONAL) - public ProtelisNode

getNode() { + public Node getNode() { return node; } diff --git a/alchemist-incarnation-protelis/src/main/java/it/unibo/alchemist/model/implementations/actions/SendToNeighbor.java b/alchemist-incarnation-protelis/src/main/java/it/unibo/alchemist/model/implementations/actions/SendToNeighbor.java index e87799f59a..b8f88d6237 100644 --- a/alchemist-incarnation-protelis/src/main/java/it/unibo/alchemist/model/implementations/actions/SendToNeighbor.java +++ b/alchemist-incarnation-protelis/src/main/java/it/unibo/alchemist/model/implementations/actions/SendToNeighbor.java @@ -8,10 +8,10 @@ package it.unibo.alchemist.model.implementations.actions; import edu.umd.cs.findbugs.annotations.SuppressFBWarnings; -import it.unibo.alchemist.model.implementations.nodes.ProtelisNode; import it.unibo.alchemist.model.interfaces.Context; import it.unibo.alchemist.model.interfaces.Node; import it.unibo.alchemist.model.interfaces.Reaction; +import it.unibo.alchemist.model.interfaces.properties.ProtelisProperty; import it.unibo.alchemist.protelis.AlchemistNetworkManager; import java.util.List; @@ -35,7 +35,7 @@ public final class SendToNeighbor extends AbstractAction { * the reference {@link RunProtelisProgram} */ @SuppressFBWarnings(value = "EI_EXPOSE_REP2", justification = "This is intentional") - public SendToNeighbor(final ProtelisNode node, final Reaction reaction, final RunProtelisProgram program) { + public SendToNeighbor(final Node node, final Reaction reaction, final RunProtelisProgram program) { super(node); this.reaction = Objects.requireNonNull(reaction); this.program = Objects.requireNonNull(program); @@ -44,7 +44,7 @@ public SendToNeighbor(final ProtelisNode node, final Reaction reactio @Override public SendToNeighbor cloneAction(final Node node, final Reaction reaction) { - if (node instanceof ProtelisNode) { + if (node.asPropertyOrNull(ProtelisProperty.class) != null) { final List> possibleRefs = node.getReactions().stream() .map(Reaction::getActions) .flatMap(List::stream) @@ -52,14 +52,14 @@ public SendToNeighbor cloneAction(final Node node, final Reaction (RunProtelisProgram) a) .collect(Collectors.toList()); if (possibleRefs.size() == 1) { - return new SendToNeighbor((ProtelisNode) node, this.reaction, possibleRefs.get(0)); + return new SendToNeighbor(node, this.reaction, possibleRefs.get(0)); } throw new IllegalStateException( "There must be one and one only unconfigured " + RunProtelisProgram.class.getSimpleName() ); } - throw new IllegalStateException(getClass().getSimpleName() + " cannot get cloned on a node of type " - + node.getClass().getSimpleName()); + throw new IllegalStateException(getClass().getSimpleName() + " cannot get cloned on a node with a missing " + + ProtelisProperty.class.getSimpleName()); } @Override @@ -69,17 +69,12 @@ public Context getContext() { @Override public void execute() { - final AlchemistNetworkManager mgr = getNode().getNetworkManager(program); + final AlchemistNetworkManager mgr = getNode().asProperty(ProtelisProperty.class).getNetworkManager(program); Objects.requireNonNull(mgr); mgr.simulateMessageArrival(reaction.getTau().toDouble()); program.prepareForComputationalCycle(); } - @Override - public ProtelisNode getNode() { - return (ProtelisNode) super.getNode(); - } - /** * @return the {@link RunProtelisProgram} whose data will be sent */ diff --git a/alchemist-incarnation-protelis/src/main/java/it/unibo/alchemist/model/implementations/conditions/ComputationalRoundComplete.java b/alchemist-incarnation-protelis/src/main/java/it/unibo/alchemist/model/implementations/conditions/ComputationalRoundComplete.java index be212043c1..5c1a922ffc 100644 --- a/alchemist-incarnation-protelis/src/main/java/it/unibo/alchemist/model/implementations/conditions/ComputationalRoundComplete.java +++ b/alchemist-incarnation-protelis/src/main/java/it/unibo/alchemist/model/implementations/conditions/ComputationalRoundComplete.java @@ -8,10 +8,10 @@ package it.unibo.alchemist.model.implementations.conditions; import it.unibo.alchemist.model.implementations.actions.RunProtelisProgram; -import it.unibo.alchemist.model.implementations.nodes.ProtelisNode; import it.unibo.alchemist.model.interfaces.Context; import it.unibo.alchemist.model.interfaces.Node; import it.unibo.alchemist.model.interfaces.Reaction; +import it.unibo.alchemist.model.interfaces.properties.ProtelisProperty; import java.util.List; import java.util.stream.Collectors; @@ -30,7 +30,7 @@ public final class ComputationalRoundComplete extends AbstractCondition * @param program * the reference {@link RunProtelisProgram} */ - public ComputationalRoundComplete(final ProtelisNode node, final RunProtelisProgram program) { + public ComputationalRoundComplete(final Node node, final RunProtelisProgram program) { super(node); this.program = program; declareDependencyOn(this.program.asMolecule()); @@ -38,7 +38,7 @@ public ComputationalRoundComplete(final ProtelisNode node, final RunProtelisP @Override public ComputationalRoundComplete cloneCondition(final Node node, final Reaction reaction) { - if (node instanceof ProtelisNode) { + if (node.asPropertyOrNull(ProtelisProperty.class) != null) { final List possibleRefs = node.getReactions().stream() .map(Reaction::getActions) .flatMap(List::stream) @@ -46,13 +46,13 @@ public ComputationalRoundComplete cloneCondition(final Node node, final .map(a -> (RunProtelisProgram) a) .collect(Collectors.toList()); if (possibleRefs.size() == 1) { - return new ComputationalRoundComplete((ProtelisNode) node, possibleRefs.get(0)); + return new ComputationalRoundComplete(node, possibleRefs.get(0)); } throw new IllegalStateException("There must be one and one only unconfigured " + RunProtelisProgram.class.getSimpleName()); } - throw new IllegalStateException(getClass().getSimpleName() + " cannot get cloned on a node of type " - + node.getClass().getSimpleName()); + throw new IllegalStateException(getClass().getSimpleName() + " cannot get cloned on a node with a missing " + + ProtelisProperty.class.getSimpleName()); } @Override @@ -60,11 +60,6 @@ public Context getContext() { return Context.LOCAL; } - @Override - public ProtelisNode getNode() { - return (ProtelisNode) super.getNode(); - } - @Override public double getPropensityContribution() { return isValid() ? 1 : 0; diff --git a/alchemist-incarnation-protelis/src/main/java/it/unibo/alchemist/model/implementations/nodes/ProtelisNode.java b/alchemist-incarnation-protelis/src/main/java/it/unibo/alchemist/model/implementations/nodes/ProtelisNode.java deleted file mode 100644 index f555af2aaa..0000000000 --- a/alchemist-incarnation-protelis/src/main/java/it/unibo/alchemist/model/implementations/nodes/ProtelisNode.java +++ /dev/null @@ -1,166 +0,0 @@ -/* - * Copyright (C) 2010-2019, Danilo Pianini and contributors listed in the main project's alchemist/build.gradle file. - * - * This file is part of Alchemist, and is distributed under the terms of the - * GNU General Public License, with a linking exception, - * as described in the file LICENSE in the Alchemist distribution's top directory. - */ - -package it.unibo.alchemist.model.implementations.nodes; - -import com.google.common.collect.ImmutableSet; -import edu.umd.cs.findbugs.annotations.SuppressFBWarnings; -import it.unibo.alchemist.model.ProtelisIncarnation; -import it.unibo.alchemist.model.implementations.actions.RunProtelisProgram; -import it.unibo.alchemist.model.interfaces.Environment; -import it.unibo.alchemist.model.interfaces.Molecule; -import it.unibo.alchemist.model.interfaces.Position; -import it.unibo.alchemist.model.interfaces.Time; -import it.unibo.alchemist.protelis.AlchemistNetworkManager; -import org.protelis.lang.datatype.DeviceUID; -import org.protelis.lang.datatype.Field; -import org.protelis.vm.ExecutionEnvironment; - -import java.util.Collections; -import java.util.LinkedHashMap; -import java.util.Map; -import java.util.Objects; -import java.util.Optional; -import java.util.Set; - -/** - * - * @param

Position type - */ -public final class ProtelisNode

> - extends AbstractNode - implements DeviceUID, ExecutionEnvironment { - - private static final long serialVersionUID = 7411790948884770553L; - private final Map, AlchemistNetworkManager> networkManagers = new LinkedHashMap<>(); - private final Environment environment; - - /** - * Builds a new {@link ProtelisNode}. - * - * @param environment - * the environment - */ - @SuppressFBWarnings(value = "EI_EXPOSE_REP2", justification = "This is intentional") - public ProtelisNode(final Environment environment) { - super(environment); - this.environment = environment; - } - - @Override - protected Object createT() { - return null; - } - - @Override - public String toString() { - return Long.toString(getId()); - } - - /** - * Adds a new {@link AlchemistNetworkManager}. - * - * @param program - * the {@link RunProtelisProgram} - * @param networkManager - * the {@link AlchemistNetworkManager} - */ - public void addNetworkManger(final RunProtelisProgram program, final AlchemistNetworkManager networkManager) { - networkManagers.put(program, networkManager); - } - - /** - * @param program - * the {@link RunProtelisProgram} - * @return the {@link AlchemistNetworkManager} for this specific - * {@link RunProtelisProgram} - */ - public AlchemistNetworkManager getNetworkManager(final RunProtelisProgram program) { - Objects.requireNonNull(program); - return networkManagers.get(program); - } - - /** - * @return all the {@link AlchemistNetworkManager} in this node - */ - public Map, AlchemistNetworkManager> getNetworkManagers() { - return Collections.unmodifiableMap(networkManagers); - } - - private static

> Molecule makeMol(final String id) { - return new ProtelisIncarnation

().createMolecule(id); - } - - @Override - public boolean has(final String id) { - return contains(makeMol(id)); - } - - @Override - public Object get(final String id) { - final Molecule mid = makeMol(id); - return Optional.ofNullable(getConcentration(mid)) - .orElse(environment.getLayer(mid) - .map(it -> it.getValue(environment.getPosition(this))) - .orElse(null) - ); - } - - @Override - public Object get(final String id, final Object defaultValue) { - return Optional.ofNullable(get(id)).orElse(defaultValue); - } - - @Override - public boolean put(final String id, final Object v) { - setConcentration(makeMol(id), v); - return true; - } - - /** - * Writes a Map representation of the Field on the environment. - * - * @param id variable name - * @param v the {@link Field} - * @return true - */ - public boolean putField(final String id, final Field v) { - setConcentration(makeMol(id), v.toMap()); - return true; - } - - @Override - public Object remove(final String id) { - final Object res = get(id); - removeConcentration(makeMol(id)); - return res; - } - - @Override - public void commit() { - } - - @Override - public void setup() { - } - - @Override - public ProtelisNode

cloneNode(final Time currentTime) { - final ProtelisNode

result = new ProtelisNode<>(environment); - getContents().forEach(result::setConcentration); - getReactions().forEach(r -> result.addReaction(r.cloneOnNewNode(result, currentTime))); - return result; - } - - @Override - public Set keySet() { - return getContents().keySet().stream() - .map(Molecule::getName) - .collect(ImmutableSet.toImmutableSet()); - } -} diff --git a/alchemist-incarnation-protelis/src/main/java/it/unibo/alchemist/protelis/AlchemistExecutionContext.java b/alchemist-incarnation-protelis/src/main/java/it/unibo/alchemist/protelis/AlchemistExecutionContext.java index e32ff9b3c4..2b102425be 100644 --- a/alchemist-incarnation-protelis/src/main/java/it/unibo/alchemist/protelis/AlchemistExecutionContext.java +++ b/alchemist-incarnation-protelis/src/main/java/it/unibo/alchemist/protelis/AlchemistExecutionContext.java @@ -13,7 +13,6 @@ import com.google.common.hash.Hashing; import edu.umd.cs.findbugs.annotations.SuppressFBWarnings; import it.unibo.alchemist.model.implementations.molecules.SimpleMolecule; -import it.unibo.alchemist.model.implementations.nodes.ProtelisNode; import it.unibo.alchemist.model.implementations.positions.LatLongPosition; import it.unibo.alchemist.model.interfaces.Environment; import it.unibo.alchemist.model.interfaces.GeoPosition; @@ -23,6 +22,7 @@ import it.unibo.alchemist.model.interfaces.Position; import it.unibo.alchemist.model.interfaces.Position2D; import it.unibo.alchemist.model.interfaces.Reaction; +import it.unibo.alchemist.model.interfaces.properties.ProtelisProperty; import org.apache.commons.lang3.ArrayUtils; import org.apache.commons.lang3.NotImplementedException; import org.apache.commons.math3.random.RandomGenerator; @@ -79,15 +79,16 @@ public Double load(@Nonnull final P dest) { private int hash; private double nbrRangeTimeout; private double precalcdRoutingDistance = Double.NaN; - private final ProtelisNode

node; + private final Node node; private final RandomGenerator randomGenerator; private final Reaction reaction; + private final ProtelisProperty protelisProperty; /** * @param environment * the simulation {@link Environment} * @param localNode - * the local {@link ProtelisNode} + * the local {@link Node} * @param reaction * the {@link Reaction} hosting the program * @param random @@ -98,13 +99,14 @@ public Double load(@Nonnull final P dest) { @SuppressFBWarnings(value = "EI_EXPOSE_REP2", justification = INTENTIONAL) public AlchemistExecutionContext( final Environment environment, - final ProtelisNode

localNode, + final Node localNode, final Reaction reaction, final RandomGenerator random, final AlchemistNetworkManager networkManager) { - super(localNode, networkManager); + super(localNode.asProperty(ProtelisProperty.class), networkManager); this.environment = environment; node = localNode; + protelisProperty = node.asProperty(ProtelisProperty.class); this.reaction = reaction; randomGenerator = random; } @@ -123,8 +125,8 @@ private Field buildFieldWithPosition(final Function fun) { */ @SuppressWarnings("unchecked") public double distanceTo(final DeviceUID target) { - assert target instanceof ProtelisNode; - return environment.getDistanceBetweenNodes(node, (ProtelisNode

) target); + assert target instanceof ProtelisProperty; + return environment.getDistanceBetweenNodes(node, ((ProtelisProperty) target).getNode()); } /** @@ -136,7 +138,7 @@ public double distanceTo(final DeviceUID target) { * @return the distance */ public double distanceTo(final int target) { - return distanceTo((ProtelisNode) environment.getNodeByID(target)); + return distanceTo(environment.getNodeByID(target).asProperty(ProtelisProperty.class)); } @Override @@ -174,7 +176,7 @@ public P getDevicePosition() { @Override @SuppressFBWarnings(value = "EI_EXPOSE_REP", justification = INTENTIONAL) public DeviceUID getDeviceUID() { - return node; + return protelisProperty; } /** @@ -294,7 +296,7 @@ public double routingDistance(final Node dest) { * Computes the distance along a map. Requires a {@link MapEnvironment}. * * @param dest - * the destination, in form of {@link ProtelisNode} ID. Non + * the destination, in form of {@link Node} ID. Non * integer numbers will be cast to integers by * {@link Number#intValue()}. * @return the distance on a map diff --git a/alchemist-incarnation-protelis/src/main/java/it/unibo/alchemist/protelis/AlchemistNetworkManager.java b/alchemist-incarnation-protelis/src/main/java/it/unibo/alchemist/protelis/AlchemistNetworkManager.java index f829d8c954..3a69398965 100644 --- a/alchemist-incarnation-protelis/src/main/java/it/unibo/alchemist/protelis/AlchemistNetworkManager.java +++ b/alchemist-incarnation-protelis/src/main/java/it/unibo/alchemist/protelis/AlchemistNetworkManager.java @@ -10,9 +10,10 @@ import com.google.common.collect.ImmutableMap; import edu.umd.cs.findbugs.annotations.SuppressFBWarnings; import it.unibo.alchemist.model.implementations.actions.RunProtelisProgram; -import it.unibo.alchemist.model.implementations.nodes.ProtelisNode; import it.unibo.alchemist.model.interfaces.Environment; +import it.unibo.alchemist.model.interfaces.Node; import it.unibo.alchemist.model.interfaces.Reaction; +import it.unibo.alchemist.model.interfaces.properties.ProtelisProperty; import org.apache.commons.math3.distribution.RealDistribution; import org.protelis.lang.datatype.DeviceUID; import org.protelis.vm.CodePath; @@ -37,7 +38,7 @@ public final class AlchemistNetworkManager implements NetworkManager, Serializab private static final long serialVersionUID = 1L; private final Environment environment; - private final ProtelisNode node; + private final Node node; /** * This reaction stores the time at which the neighbor state is read. */ @@ -185,24 +186,22 @@ public void shareState(final Map toSend) { public void simulateMessageArrival(final double currentTime) { Objects.requireNonNull(toBeSent); if (!toBeSent.isEmpty()) { - final MessageInfo msg = new MessageInfo(currentTime, node, toBeSent); + final MessageInfo msg = new MessageInfo(currentTime, node.asProperty(ProtelisProperty.class), toBeSent); environment.getNeighborhood(node).forEach(n -> { - if (n instanceof ProtelisNode) { - final AlchemistNetworkManager destination = ((ProtelisNode) n).getNetworkManager(program); - if (destination != null) { - boolean packetArrives = true; - if (distanceLossDistribution != null) { - final var distance = environment.getDistanceBetweenNodes(node, n); - final var random = program.getRandomGenerator().nextDouble(); - packetArrives = random > distanceLossDistribution.cumulativeProbability(distance); - } - if (packetArrives) { - /* - * The node is running the program, and the loss model actually makes the packet arrive. - * Otherwise, the message is discarded - */ - destination.receiveMessage(msg); - } + if (n.asPropertyOrNull(ProtelisProperty.class) != null) { + final AlchemistNetworkManager destination = n.asProperty(ProtelisProperty.class).getNetworkManager(program); + boolean packetArrives = true; + if (distanceLossDistribution != null) { + final var distance = environment.getDistanceBetweenNodes(node, n); + final var random = program.getRandomGenerator().nextDouble(); + packetArrives = random > distanceLossDistribution.cumulativeProbability(distance); + } + if (packetArrives) { + /* + * The node is running the program, and the loss model actually makes the packet arrive. + * Otherwise, the message is discarded + */ + destination.receiveMessage(msg); } } }); diff --git a/alchemist-incarnation-protelis/src/main/kotlin/it/unibo/alchemist/model/implementations/properties/Protelis.kt b/alchemist-incarnation-protelis/src/main/kotlin/it/unibo/alchemist/model/implementations/properties/Protelis.kt new file mode 100644 index 0000000000..994641b567 --- /dev/null +++ b/alchemist-incarnation-protelis/src/main/kotlin/it/unibo/alchemist/model/implementations/properties/Protelis.kt @@ -0,0 +1,87 @@ +/* + * Copyright (C) 2010-2022, Danilo Pianini and contributors + * listed, for each module, in the respective subproject's build.gradle.kts file. + * + * This file is part of Alchemist, and is distributed under the terms of the + * GNU General Public License, with a linking exception, + * as described in the file LICENSE in the Alchemist distribution's top directory. + */ + +package it.unibo.alchemist.model.implementations.properties + +import it.unibo.alchemist.model.ProtelisIncarnation +import it.unibo.alchemist.model.implementations.actions.RunProtelisProgram +import it.unibo.alchemist.model.interfaces.Node +import it.unibo.alchemist.model.interfaces.Position +import it.unibo.alchemist.model.interfaces.properties.ProtelisProperty +import it.unibo.alchemist.protelis.AlchemistNetworkManager + +/** + * Base implementation of [ProtelisProperty]. + */ +class Protelis

> @JvmOverloads constructor( + override val incarnation: ProtelisIncarnation<*>, + override val node: Node, + networkManagers: Map, AlchemistNetworkManager> = mapOf() +) : ProtelisProperty { + + override var networkManagers: Map, AlchemistNetworkManager> = networkManagers + private set + + override fun addNetworkManger(program: RunProtelisProgram<*>, networkManager: AlchemistNetworkManager) { + networkManagers = networkManagers + (program to networkManager) + } + + override fun cloneOnNewNode(node: Node) = Protelis(incarnation, node) + + /** + * Returns true if node contains [id]. + */ + override fun has(id: String): Boolean = node.contains(incarnation.createMolecule(id)) + + /** + * Returns the value associated with [id]. + */ + override fun get(id: String): Any = node.getConcentration(incarnation.createMolecule(id)) + + /** + * Returns the value associated with [id]. + */ + override fun get(id: String, defaultValue: Any): Any = get(id) + + /** + * Stores the value associated with [id]. + */ + override fun put(id: String, v: Any): Boolean { + node.setConcentration(incarnation.createMolecule(id), v) + return true + } + + /** + * Removes the value associated with [id]. + */ + override fun remove(id: String): Any { + val value = get(id) + node.removeConcentration(incarnation.createMolecule(id)) + return value + } + + /** + * Return all stored variables names. + */ + override fun keySet(): Set = node.contents.keys.mapNotNull { it.name }.toSet() + + /** + * Called just after the VM is executed, to finalize information of the execution for the environment. + */ + override fun commit() { /* Nothing to do */ } + + /** + * Called just before the VM is executed, to enable and preparations needed in the environment. + */ + override fun setup() { /* Nothing to do */ } + + companion object { + private const val serialVersionUID: Long = 1L + } +} diff --git a/alchemist-incarnation-protelis/src/main/kotlin/it/unibo/alchemist/model/interfaces/properties/ProtelisProperty.kt b/alchemist-incarnation-protelis/src/main/kotlin/it/unibo/alchemist/model/interfaces/properties/ProtelisProperty.kt new file mode 100644 index 0000000000..456f30704a --- /dev/null +++ b/alchemist-incarnation-protelis/src/main/kotlin/it/unibo/alchemist/model/interfaces/properties/ProtelisProperty.kt @@ -0,0 +1,69 @@ +/* + * Copyright (C) 2010-2022, Danilo Pianini and contributors + * listed, for each module, in the respective subproject's build.gradle.kts file. + * + * This file is part of Alchemist, and is distributed under the terms of the + * GNU General Public License, with a linking exception, + * as described in the file LICENSE in the Alchemist distribution's top directory. + */ + +package it.unibo.alchemist.model.interfaces.properties + +import it.unibo.alchemist.model.ProtelisIncarnation +import it.unibo.alchemist.model.implementations.actions.RunProtelisProgram +import it.unibo.alchemist.model.interfaces.NodeProperty +import it.unibo.alchemist.protelis.AlchemistNetworkManager +import org.protelis.lang.datatype.DeviceUID +import org.protelis.lang.datatype.Field +import org.protelis.vm.ExecutionEnvironment + +/** + * + */ +interface ProtelisProperty : NodeProperty, ExecutionEnvironment, DeviceUID { + + /** + * The node's id. + */ + val id: Int get() = node.id + + /** + * An instance of a [ProtelisIncarnation]. + */ + val incarnation: ProtelisIncarnation<*> + + /** + * All the [AlchemistNetworkManager]s in this node. + */ + val networkManagers: Map, AlchemistNetworkManager> + + /** + * Adds a new [AlchemistNetworkManager]. + * + * @param program + * the [RunProtelisProgram] + * @param networkManager + * the [AlchemistNetworkManager] + */ + fun addNetworkManger(program: RunProtelisProgram<*>, networkManager: AlchemistNetworkManager) + + /** + * @param program + * the [RunProtelisProgram] + * @return the [AlchemistNetworkManager] for this specific + * [RunProtelisProgram] + */ + fun getNetworkManager(program: RunProtelisProgram<*>): AlchemistNetworkManager = + requireNotNull(networkManagers[program]) { "No network manager associated with $program" } + + /** + * Writes a Map representation of the Field on the environment. + * + * @param id variable name + * @param v the [Field] + * @return true + */ + fun putField(id: String, v: Field) = true.also { + node.setConcentration(incarnation.createMolecule(id), v.toMap()) + } +} diff --git a/alchemist-incarnation-protelis/src/test/java/it/unibo/alchemist/test/TestInSimulator.java b/alchemist-incarnation-protelis/src/test/java/it/unibo/alchemist/test/TestInSimulator.java index f945991a3d..1cc740b0b0 100644 --- a/alchemist-incarnation-protelis/src/test/java/it/unibo/alchemist/test/TestInSimulator.java +++ b/alchemist-incarnation-protelis/src/test/java/it/unibo/alchemist/test/TestInSimulator.java @@ -121,8 +121,8 @@ private static void testNoVar(final String resource) { private static > void testLoading(final String resource, final Map vars) { final var res = ResourceLoader.getResource(resource); assertNotNull(res, "Missing test resource " + resource); - final Environment env = LoadAlchemist.from(res).getWith(vars).getEnvironment(); - final Simulation sim = new Engine<>(env, 10_000); + final Environment environment = LoadAlchemist.from(res).getWith(vars).getEnvironment(); + final Simulation sim = new Engine<>(environment, 10_000); sim.play(); // if (!java.awt.GraphicsEnvironment.isHeadless()) { // it.unibo.alchemist.boundary.gui.SingleRunGUI.make(sim); diff --git a/alchemist-incarnation-protelis/src/test/java/it/unibo/alchemist/test/TestIncarnation.java b/alchemist-incarnation-protelis/src/test/java/it/unibo/alchemist/test/TestIncarnation.java index 7d9550f42e..c2f5de5b23 100644 --- a/alchemist-incarnation-protelis/src/test/java/it/unibo/alchemist/test/TestIncarnation.java +++ b/alchemist-incarnation-protelis/src/test/java/it/unibo/alchemist/test/TestIncarnation.java @@ -46,25 +46,25 @@ class TestIncarnation { @Test void testBuild() { final RandomGenerator rng = new MersenneTwister(0); - final Environment env = new Continuous2DEnvironment<>(INCARNATION); - final Node node = INCARNATION.createNode(rng, env, null); + final Environment environment = new Continuous2DEnvironment<>(INCARNATION); + final Node node = INCARNATION.createNode(rng, environment, null); assertNotNull(node); - final TimeDistribution immediately = INCARNATION.createTimeDistribution(rng, env, node, null); + final TimeDistribution immediately = INCARNATION.createTimeDistribution(rng, environment, node, null); assertNotNull(immediately); assertTrue(Double.isInfinite(immediately.getRate())); assertTrue(immediately.getRate() > 0); - final TimeDistribution standard = INCARNATION.createTimeDistribution(rng, env, node, "3"); + final TimeDistribution standard = INCARNATION.createTimeDistribution(rng, environment, node, "3"); assertNotNull(standard); assertEquals(3d, standard.getRate(), Double.MIN_VALUE); - final Reaction generic = INCARNATION.createReaction(rng, env, node, standard, null); + final Reaction generic = INCARNATION.createReaction(rng, environment, node, standard, null); assertNotNull(generic); assertTrue(generic instanceof Event); - final Reaction program = INCARNATION.createReaction(rng, env, node, standard, "nbr(1)"); + final Reaction program = INCARNATION.createReaction(rng, environment, node, standard, "nbr(1)"); testIsProtelisProgram(program); - final Reaction program2 = INCARNATION.createReaction(rng, env, node, standard, "testprotelis:test"); + final Reaction program2 = INCARNATION.createReaction(rng, environment, node, standard, "testprotelis:test"); testIsProtelisProgram(program2); try { - INCARNATION.createReaction(rng, env, node, standard, "send"); + INCARNATION.createReaction(rng, environment, node, standard, "send"); fail(); } catch (final IllegalStateException e) { assertNotNull(e.getMessage()); @@ -72,13 +72,13 @@ void testBuild() { node.addReaction(program); node.addReaction(program2); try { - INCARNATION.createReaction(rng, env, node, standard, "send"); + INCARNATION.createReaction(rng, environment, node, standard, "send"); fail(); } catch (final IllegalStateException e) { assertNotNull(e.getMessage()); } node.removeReaction(program2); - final Reaction send = INCARNATION.createReaction(rng, env, node, standard, "send"); + final Reaction send = INCARNATION.createReaction(rng, environment, node, standard, "send"); testIsSendToNeighbor(send); } diff --git a/alchemist-incarnation-protelis/src/test/java/it/unibo/alchemist/test/TestTOMACS.java b/alchemist-incarnation-protelis/src/test/java/it/unibo/alchemist/test/TestTOMACS.java index 68c9159304..3088e1920d 100644 --- a/alchemist-incarnation-protelis/src/test/java/it/unibo/alchemist/test/TestTOMACS.java +++ b/alchemist-incarnation-protelis/src/test/java/it/unibo/alchemist/test/TestTOMACS.java @@ -10,8 +10,8 @@ import it.unibo.alchemist.loader.LoadAlchemist; import it.unibo.alchemist.loader.Loader; import it.unibo.alchemist.model.implementations.actions.RunProtelisProgram; -import it.unibo.alchemist.model.implementations.nodes.ProtelisNode; import it.unibo.alchemist.model.interfaces.Reaction; +import it.unibo.alchemist.model.interfaces.properties.ProtelisProperty; import it.unibo.alchemist.protelis.AlchemistNetworkManager; import org.junit.jupiter.api.Assertions; import org.junit.jupiter.api.Test; @@ -33,13 +33,12 @@ class TestTOMACS { void testCustomRetainTimeLoading() { final Loader loader = LoadAlchemist.from(ResourceLoader.getResource("tomacs.yml")); Assertions.assertTrue(StreamSupport.stream(loader.getDefault().getEnvironment().spliterator(), false) - .map(n -> (ProtelisNode) n) .flatMap(n -> n.getReactions().stream() .map(Reaction::getActions) .flatMap(Collection::stream) .filter(a -> a instanceof RunProtelisProgram) .map(a -> (RunProtelisProgram) a) - .map(n::getNetworkManager)) + .map(a -> n.asProperty(ProtelisProperty.class).getNetworkManager(a))) .mapToDouble(AlchemistNetworkManager::getRetentionTime) .peek(d -> Assertions.assertTrue(Double.isFinite(d))) .count() > 0); diff --git a/alchemist-incarnation-protelis/src/test/kotlin/it/unibo/alchemist/test/CustomNode.kt b/alchemist-incarnation-protelis/src/test/kotlin/it/unibo/alchemist/test/CustomNode.kt index 4c2ccd0ede..e204bb7ca2 100644 --- a/alchemist-incarnation-protelis/src/test/kotlin/it/unibo/alchemist/test/CustomNode.kt +++ b/alchemist-incarnation-protelis/src/test/kotlin/it/unibo/alchemist/test/CustomNode.kt @@ -1,8 +1,8 @@ package it.unibo.alchemist.test -import it.unibo.alchemist.model.implementations.nodes.AbstractNode +import it.unibo.alchemist.model.implementations.nodes.GenericNode import it.unibo.alchemist.model.interfaces.Environment -class CustomNode(env: Environment) : AbstractNode(env) { +class CustomNode(environment: Environment) : GenericNode(environment) { override fun createT(): T = TODO() } diff --git a/alchemist-incarnation-protelis/src/test/kotlin/it/unibo/alchemist/test/TestGetPosition.kt b/alchemist-incarnation-protelis/src/test/kotlin/it/unibo/alchemist/test/TestGetPosition.kt index 0b06b46ce8..3f79e53da0 100644 --- a/alchemist-incarnation-protelis/src/test/kotlin/it/unibo/alchemist/test/TestGetPosition.kt +++ b/alchemist-incarnation-protelis/src/test/kotlin/it/unibo/alchemist/test/TestGetPosition.kt @@ -14,7 +14,6 @@ import it.unibo.alchemist.model.ProtelisIncarnation import it.unibo.alchemist.model.implementations.actions.RunProtelisProgram import it.unibo.alchemist.model.implementations.environments.Continuous2DEnvironment import it.unibo.alchemist.model.implementations.linkingrules.NoLinks -import it.unibo.alchemist.model.implementations.nodes.ProtelisNode import it.unibo.alchemist.model.implementations.positions.Euclidean2DPosition import it.unibo.alchemist.model.implementations.reactions.Event import it.unibo.alchemist.model.implementations.timedistributions.ExponentialTime @@ -30,8 +29,8 @@ import org.protelis.lang.datatype.DatatypeFactory class TestGetPosition { private val environment: Environment = Continuous2DEnvironment(ProtelisIncarnation()) - private val node = ProtelisNode(environment) private val randomGenerator = MersenneTwister(0) + private val node = ProtelisIncarnation().createNode(randomGenerator, environment, null) private val reaction = Event(node, ExponentialTime(1.0, randomGenerator)) private val action = RunProtelisProgram(environment, node, reaction, randomGenerator, "self.getCoordinates()") diff --git a/alchemist-incarnation-sapere/src/main/java/it/unibo/alchemist/model/SAPEREIncarnation.java b/alchemist-incarnation-sapere/src/main/java/it/unibo/alchemist/model/SAPEREIncarnation.java index 2473826682..366e1fb40b 100644 --- a/alchemist-incarnation-sapere/src/main/java/it/unibo/alchemist/model/SAPEREIncarnation.java +++ b/alchemist-incarnation-sapere/src/main/java/it/unibo/alchemist/model/SAPEREIncarnation.java @@ -284,7 +284,11 @@ public Action> createAction( @Override public List createConcentration(final String s) { - return Collections.emptyList(); + return createConcentration(); } + @Override + public List createConcentration() { + return Collections.emptyList(); + } } diff --git a/alchemist-incarnation-sapere/src/main/java/it/unibo/alchemist/model/implementations/actions/LsaAscendingGradientDist.java b/alchemist-incarnation-sapere/src/main/java/it/unibo/alchemist/model/implementations/actions/LsaAscendingGradientDist.java index 681fb0931e..4a8dd23815 100644 --- a/alchemist-incarnation-sapere/src/main/java/it/unibo/alchemist/model/implementations/actions/LsaAscendingGradientDist.java +++ b/alchemist-incarnation-sapere/src/main/java/it/unibo/alchemist/model/implementations/actions/LsaAscendingGradientDist.java @@ -34,7 +34,7 @@ public final class LsaAscendingGradientDist

> extends SAPER private static final ILsaMolecule MOLGRAD = new LsaMolecule("grad, req, Type, Distance, Time"); private static final ILsaMolecule MOLRESPONSE = new LsaMolecule("response, Req, Ser, MD, D"); private static final int POS = 3; - private final Environment, ?> env; + private final Environment, ?> environment; /** * @param environment environment @@ -42,13 +42,13 @@ public final class LsaAscendingGradientDist

> extends SAPER */ public LsaAscendingGradientDist(final Environment, P> environment, final ILsaNode node) { super(environment, node, MOLRESPONSE); - this.env = environment; + this.environment = environment; } @Override public void execute() { double minGrad = getLSAArgumentAsDouble(getNode().getConcentration(MOLGRAD).get(0), POS); - final Neighborhood> neigh = env.getNeighborhood(getNode()); + final Neighborhood> neigh = environment.getNeighborhood(getNode()); final List targetPositions = new ArrayList<>(); for (final Node> node : neigh.getNeighbors()) { final LsaNode n = (LsaNode) node; diff --git a/alchemist-incarnation-sapere/src/main/java/it/unibo/alchemist/model/implementations/actions/LsaChangeArgument.java b/alchemist-incarnation-sapere/src/main/java/it/unibo/alchemist/model/implementations/actions/LsaChangeArgument.java index e6c8f320be..dd22ca733e 100644 --- a/alchemist-incarnation-sapere/src/main/java/it/unibo/alchemist/model/implementations/actions/LsaChangeArgument.java +++ b/alchemist-incarnation-sapere/src/main/java/it/unibo/alchemist/model/implementations/actions/LsaChangeArgument.java @@ -25,7 +25,7 @@ public final class LsaChangeArgument extends SAPERELocalAgent { private static final long serialVersionUID = -7128058274012426458L; private static final HashString OLD = new HashString("OldType"); - private final Environment, ?> env; + private final Environment, ?> environment; @SuppressFBWarnings( value = "SE_BAD_FIELD", justification = "All provided RandomGenerator implementations are actually Serializable" @@ -62,7 +62,7 @@ public LsaChangeArgument( ) { super(node); rnd = random; - env = environment; + this.environment = environment; newTargetVar = new HashString(targetVariable); listT = Arrays.copyOf(listTarget, listTarget.length); } @@ -89,7 +89,7 @@ public String toString() { * @return the current environment */ protected Environment, ?> getEnvironment() { - return env; + return environment; } /** diff --git a/alchemist-incarnation-sapere/src/main/java/it/unibo/alchemist/model/implementations/actions/LsaCountNeighborsAction.java b/alchemist-incarnation-sapere/src/main/java/it/unibo/alchemist/model/implementations/actions/LsaCountNeighborsAction.java index 7ce6411bf8..008cac7228 100644 --- a/alchemist-incarnation-sapere/src/main/java/it/unibo/alchemist/model/implementations/actions/LsaCountNeighborsAction.java +++ b/alchemist-incarnation-sapere/src/main/java/it/unibo/alchemist/model/implementations/actions/LsaCountNeighborsAction.java @@ -28,7 +28,7 @@ public final class LsaCountNeighborsAction extends SAPERELocalAgent { private static final long serialVersionUID = -7128058274012426458L; private final HashString countVarName; - private final Environment, ?> env; + private final Environment, ?> environment; private final ILsaMolecule mol; @SuppressFBWarnings(value = "SE_BAD_FIELD", justification = "All implementations are actually serializable") private final RandomGenerator rnd; @@ -61,7 +61,7 @@ public LsaCountNeighborsAction( ) { super(node); rnd = rand; - env = environment; + this.environment = environment; countVarName = new HashString(countVar); mol = molToCount; } @@ -112,8 +112,8 @@ public LsaCountNeighborsAction cloneAction(final Node> node, public void execute() { final List l = mol.allocateVar(getMatches()); Double num = 0.0; - if (env.getNeighborhood(getNode()) != null) { - for (final Node> nod : env.getNeighborhood(getNode()).getNeighbors()) { + if (environment.getNeighborhood(getNode()) != null) { + for (final Node> nod : environment.getNeighborhood(getNode()).getNeighbors()) { if (nod.getConcentration(new LsaMolecule(l)).size() != 0) { num++; } @@ -126,7 +126,7 @@ public void execute() { * @return the current environment */ protected Environment, ?> getEnvironment() { - return env; + return environment; } /** diff --git a/alchemist-incarnation-sapere/src/main/java/it/unibo/alchemist/model/implementations/actions/LsaRandomNeighborAction.java b/alchemist-incarnation-sapere/src/main/java/it/unibo/alchemist/model/implementations/actions/LsaRandomNeighborAction.java index 7515eccffb..d2b515ab6d 100644 --- a/alchemist-incarnation-sapere/src/main/java/it/unibo/alchemist/model/implementations/actions/LsaRandomNeighborAction.java +++ b/alchemist-incarnation-sapere/src/main/java/it/unibo/alchemist/model/implementations/actions/LsaRandomNeighborAction.java @@ -27,7 +27,7 @@ */ public class LsaRandomNeighborAction extends LsaStandardAction { private static final long serialVersionUID = -7128058274012426458L; - private final Environment, ?> env; + private final Environment, ?> environment; private final MapEnvironment, ?, ?> menv; private final boolean initO, initD, initNeigh, initRoute, mapEnv; @SuppressFBWarnings( @@ -65,9 +65,9 @@ public LsaRandomNeighborAction( initD = molString.contains(LsaMolecule.SYN_D); initNeigh = molString.contains(LsaMolecule.SYN_NEIGH); initRoute = molString.contains(LsaMolecule.SYN_ROUTE); - env = environment; + this.environment = environment; mapEnv = environment instanceof MapEnvironment; - menv = mapEnv ? (MapEnvironment, ?, ?>) env : null; + menv = mapEnv ? (MapEnvironment, ?, ?>) this.environment : null; randomEngine = randomGenerator; } @@ -124,7 +124,7 @@ public final Context getContext() { * @return the current environment */ protected Environment, ?> getEnvironment() { - return env; + return environment; } /** @@ -163,7 +163,7 @@ protected void setSynthectics(final ILsaNode node) { * #NEIGH */ if (initNeigh) { - setSyntheticNeigh(env.getNeighborhood(node).getNeighbors()); + setSyntheticNeigh(environment.getNeighborhood(node).getNeighbors()); } /* * #O @@ -174,7 +174,7 @@ protected void setSynthectics(final ILsaNode node) { } private double computeDistance(final ILsaNode node) { - return env.getDistanceBetweenNodes(getNode(), node); + return environment.getDistanceBetweenNodes(getNode(), node); } /** diff --git a/alchemist-incarnation-sapere/src/main/java/it/unibo/alchemist/model/implementations/actions/SAPEREMoveLSAToAgent.java b/alchemist-incarnation-sapere/src/main/java/it/unibo/alchemist/model/implementations/actions/SAPEREMoveLSAToAgent.java index a44923795e..3594786306 100644 --- a/alchemist-incarnation-sapere/src/main/java/it/unibo/alchemist/model/implementations/actions/SAPEREMoveLSAToAgent.java +++ b/alchemist-incarnation-sapere/src/main/java/it/unibo/alchemist/model/implementations/actions/SAPEREMoveLSAToAgent.java @@ -24,43 +24,48 @@ public final class SAPEREMoveLSAToAgent extends SAPEREAgent { private static final long serialVersionUID = -8020706131248061313L; private final ILsaNode destination; - private final ILsaMolecule molTemplate; + private final ILsaMolecule moleculeTemplate; /** * This is the constructor that should be called from DSL. Dynamically * computes the destination node if an id is given. * - * @param env + * @param environment * the current environment * @param node * the source node, where this action is programmed - * @param destId + * @param destinationId * the destination node id * @param template * the template LSA to match and move */ - public SAPEREMoveLSAToAgent(final Environment env, final ILsaNode node, final int destId, final ILsaMolecule template) { - this(node, (ILsaNode) env.getNodeByID(destId), template); + public SAPEREMoveLSAToAgent( + final Environment environment, + final ILsaNode node, + final int destinationId, + final ILsaMolecule template + ) { + this(node, (ILsaNode) environment.getNodeByID(destinationId), template); } /** * @param node * the source node, where this action is programmed - * @param dest + * @param destination * the destination node, where this action will move the matched * intance * @param template * the template LSA to match and move */ - public SAPEREMoveLSAToAgent(final ILsaNode node, final ILsaNode dest, final ILsaMolecule template) { + public SAPEREMoveLSAToAgent(final ILsaNode node, final ILsaNode destination, final ILsaMolecule template) { super(node, template); - molTemplate = template; - destination = dest; + moleculeTemplate = template; + this.destination = destination; } @Override public void execute() { - final ILsaMolecule instance = allocateVarsAndBuildLSA(molTemplate); + final ILsaMolecule instance = allocateVarsAndBuildLSA(moleculeTemplate); getNode().removeConcentration(instance); destination.setConcentration(instance); } diff --git a/alchemist-incarnation-sapere/src/main/java/it/unibo/alchemist/model/implementations/actions/SAPEREMoveNodeAgent.java b/alchemist-incarnation-sapere/src/main/java/it/unibo/alchemist/model/implementations/actions/SAPEREMoveNodeAgent.java index cd43735bbc..5ad4368fbd 100644 --- a/alchemist-incarnation-sapere/src/main/java/it/unibo/alchemist/model/implementations/actions/SAPEREMoveNodeAgent.java +++ b/alchemist-incarnation-sapere/src/main/java/it/unibo/alchemist/model/implementations/actions/SAPEREMoveNodeAgent.java @@ -31,30 +31,34 @@ public abstract class SAPEREMoveNodeAgent

> exten * must be sure that your agent does not modify any molecule (e.g. an agent * that just moves a node). * - * @param env + * @param environment * The current environment * @param node * The node in which this agent stays */ - public SAPEREMoveNodeAgent(final Environment, P> env, final ILsaNode node) { + public SAPEREMoveNodeAgent(final Environment, P> environment, final ILsaNode node) { super(node); - environment = env; + this.environment = environment; } /** * Creates a new SAPERE Local Agent stub. Use this constructor if you agent * modifies a molecule (locally!) * - * @param env + * @param environment * The current environment * @param node * The node in which this agent stays - * @param m + * @param molecule * The modified molecule template */ - public SAPEREMoveNodeAgent(final Environment, P> env, final ILsaNode node, final ILsaMolecule m) { - super(node, m); - environment = env; + public SAPEREMoveNodeAgent( + final Environment, P> environment, + final ILsaNode node, + final ILsaMolecule molecule + ) { + super(node, molecule); + this.environment = environment; } /** diff --git a/alchemist-incarnation-sapere/src/main/java/it/unibo/alchemist/model/implementations/actions/SAPERENeighborAgent.java b/alchemist-incarnation-sapere/src/main/java/it/unibo/alchemist/model/implementations/actions/SAPERENeighborAgent.java index 4ee5b3f6ee..d657045155 100644 --- a/alchemist-incarnation-sapere/src/main/java/it/unibo/alchemist/model/implementations/actions/SAPERENeighborAgent.java +++ b/alchemist-incarnation-sapere/src/main/java/it/unibo/alchemist/model/implementations/actions/SAPERENeighborAgent.java @@ -33,16 +33,16 @@ public abstract class SAPERENeighborAgent

> extends SAPEREA * you must be sure that your agent only modifies molecules matching the * template passed as m1. * - * @param env + * @param environment * The current environment * @param node * The node in which this agent stays * @param m1 * The molecule template it modifies */ - public SAPERENeighborAgent(final Environment, P> env, final ILsaNode node, final ILsaMolecule m1) { + public SAPERENeighborAgent(final Environment, P> environment, final ILsaNode node, final ILsaMolecule m1) { super(node, m1); - environment = env; + this.environment = environment; } /** @@ -50,7 +50,7 @@ public SAPERENeighborAgent(final Environment, P> env, final I * sure that your agent only modifies molecules matching the template passed * as m1 and/or the template passed in m2. * - * @param env + * @param environment * The current environment * @param node * The node in which this agent stays @@ -60,13 +60,13 @@ public SAPERENeighborAgent(final Environment, P> env, final I * The second molecule template it modifies */ public SAPERENeighborAgent( - final Environment, P> env, + final Environment, P> environment, final ILsaNode node, final ILsaMolecule m1, final ILsaMolecule m2 ) { super(node, m1, m2); - environment = env; + this.environment = environment; } /** @@ -74,7 +74,7 @@ public SAPERENeighborAgent( * sure that your agent only modifies molecules matching the template passed * as m1 and/or the template passed in m2 and/or the template passed in m3. * - * @param env + * @param environment * The current environment * @param node * The node in which this agent stays @@ -86,13 +86,13 @@ public SAPERENeighborAgent( * The third molecule template it modifies */ public SAPERENeighborAgent( - final Environment, P> env, + final Environment, P> environment, final ILsaNode node, final ILsaMolecule m1, final ILsaMolecule m2, final ILsaMolecule m3 ) { super(node, m1, m2, m3); - environment = env; + this.environment = environment; } /* diff --git a/alchemist-incarnation-sapere/src/main/java/it/unibo/alchemist/model/implementations/actions/SAPEREWalkerRiseGradient.java b/alchemist-incarnation-sapere/src/main/java/it/unibo/alchemist/model/implementations/actions/SAPEREWalkerRiseGradient.java index d7519866f1..af68783c39 100644 --- a/alchemist-incarnation-sapere/src/main/java/it/unibo/alchemist/model/implementations/actions/SAPEREWalkerRiseGradient.java +++ b/alchemist-incarnation-sapere/src/main/java/it/unibo/alchemist/model/implementations/actions/SAPEREWalkerRiseGradient.java @@ -116,12 +116,12 @@ private static final class NextTargetStrategy implements TargetSelectionStrat private GeoPosition curPos; NextTargetStrategy( - final MapEnvironment, GraphHopperOptions, GraphHopperRoutingService> env, + final MapEnvironment, GraphHopperOptions, GraphHopperRoutingService> environment, final Node> n, final Molecule patt, final int pos ) { - environment = requireNonNull(env); + this.environment = requireNonNull(environment); node = requireNonNull(n); curNode = n; template = requireNonNull(ensureIsSAPERE(patt)); @@ -130,7 +130,8 @@ private static final class NextTargetStrategy implements TargetSelectionStrat @Override public GeoPosition getTarget() { - final MapEnvironment, GraphHopperOptions, GraphHopperRoutingService> env = environment; + final MapEnvironment, GraphHopperOptions, GraphHopperRoutingService> environment = + this.environment; final List matches = node.getConcentration(template); /* * If there is no gradient and: - there is no goal, or - the goal @@ -138,7 +139,7 @@ public GeoPosition getTarget() { * * then remain still. */ - final GeoPosition currentPosition = env.getPosition(node); + final GeoPosition currentPosition = environment.getPosition(node); if (matches.isEmpty()) { if (curPos == null || currentPosition.equals(curPos)) { return currentPosition; @@ -150,15 +151,15 @@ public GeoPosition getTarget() { * If current target node has moved, destination should be * re-computed. */ - final Position curNodeActualPos = env.getPosition(curNode); + final Position curNodeActualPos = environment.getPosition(curNode); if (curNode.equals(node) || !curPos.equals(curNodeActualPos) - || env.getNeighborhood(node).contains(curNode)) { + || environment.getNeighborhood(node).contains(curNode)) { /* * Update target */ - curNode = env.getNodeByID(nid); - curPos = env.getPosition(curNode); + curNode = environment.getNodeByID(nid); + curPos = environment.getPosition(curNode); } return curPos; } diff --git a/alchemist-incarnation-sapere/src/main/java/it/unibo/alchemist/model/implementations/conditions/LsaNeighborhoodCondition.java b/alchemist-incarnation-sapere/src/main/java/it/unibo/alchemist/model/implementations/conditions/LsaNeighborhoodCondition.java index 3828a70a95..351b922f62 100644 --- a/alchemist-incarnation-sapere/src/main/java/it/unibo/alchemist/model/implementations/conditions/LsaNeighborhoodCondition.java +++ b/alchemist-incarnation-sapere/src/main/java/it/unibo/alchemist/model/implementations/conditions/LsaNeighborhoodCondition.java @@ -33,7 +33,7 @@ public final class LsaNeighborhoodCondition extends LsaStandardCondition { private static final long serialVersionUID = 5472803597473997104L; - private final Environment, ?> env; + private final Environment, ?> environment; /** * @param node the node @@ -46,12 +46,12 @@ public LsaNeighborhoodCondition( final Environment, ?> environment ) { super(molecule, node); - env = environment; + this.environment = environment; } @Override public LsaNeighborhoodCondition cloneCondition(final Node> node, final Reaction> r) { - return new LsaNeighborhoodCondition((ILsaNode) node, getMolecule(), env); + return new LsaNeighborhoodCondition((ILsaNode) node, getMolecule(), environment); } @Override @@ -213,7 +213,7 @@ public Context getContext() { * @return the current environment */ protected Environment, ?> getEnvironment() { - return env; + return environment; } /* diff --git a/alchemist-incarnation-sapere/src/main/java/it/unibo/alchemist/model/implementations/nodes/LsaNode.java b/alchemist-incarnation-sapere/src/main/java/it/unibo/alchemist/model/implementations/nodes/LsaNode.java index 43c7e9184a..0581f0e377 100644 --- a/alchemist-incarnation-sapere/src/main/java/it/unibo/alchemist/model/implementations/nodes/LsaNode.java +++ b/alchemist-incarnation-sapere/src/main/java/it/unibo/alchemist/model/implementations/nodes/LsaNode.java @@ -27,17 +27,17 @@ /** * This class realizes a node with LSA concentration. */ -public final class LsaNode extends AbstractNode> implements ILsaNode { +public final class LsaNode extends GenericNode> implements ILsaNode { private static final long serialVersionUID = -2167025208984968645L; private final List instances = new ArrayList<>(); private static final ILsaMolecule ZEROMOL = new LsaMolecule("0"); /** - * @param env + * @param environment * The environment (used for safe node id computation) */ - public LsaNode(final Environment, ?> env) { - super(env); + public LsaNode(final Environment, ?> environment) { + super(environment); } @Override diff --git a/alchemist-incarnation-sapere/src/main/java/it/unibo/alchemist/model/implementations/reactions/SAPEREGradient.java b/alchemist-incarnation-sapere/src/main/java/it/unibo/alchemist/model/implementations/reactions/SAPEREGradient.java index d9251ec94e..e92fb34b59 100644 --- a/alchemist-incarnation-sapere/src/main/java/it/unibo/alchemist/model/implementations/reactions/SAPEREGradient.java +++ b/alchemist-incarnation-sapere/src/main/java/it/unibo/alchemist/model/implementations/reactions/SAPEREGradient.java @@ -164,9 +164,9 @@ public SAPEREGradient( * for compatibility with the YAML-based Alchemist loader. It should be * avoided when possible, by relying on the other constructor instead. * - * @param env + * @param environment * the current environment - * @param n + * @param node * the node where this reaction is scheduled * @param sourceTemplate * a template ILsaMolecule representing the source @@ -192,27 +192,29 @@ public SAPEREGradient( * @param gradThreshold * if the value of the gradient grows above this threshold, the * gradient evaporates - * @param td + * @param timeDistribution * Markovian Rate */ - public SAPEREGradient(final Environment, P> env, - final ILsaNode n, - final TimeDistribution> td, + public SAPEREGradient(final Environment, P> environment, + final ILsaNode node, + final TimeDistribution> timeDistribution, final String sourceTemplate, final String gradientTemplate, final int valuePosition, final String expression, final String contextTemplate, final double gradThreshold) { - this(env, - n, - new LsaMolecule(sourceTemplate), - new LsaMolecule(gradientTemplate), - valuePosition, - expression, - new LsaMolecule(contextTemplate), - gradThreshold, - td); + this( + environment, + node, + new LsaMolecule(sourceTemplate), + new LsaMolecule(gradientTemplate), + valuePosition, + expression, + new LsaMolecule(contextTemplate), + gradThreshold, + timeDistribution + ); } @Override diff --git a/alchemist-incarnation-sapere/src/test/java/it/unibo/alchemist/test/TestIncarnation.java b/alchemist-incarnation-sapere/src/test/java/it/unibo/alchemist/test/TestIncarnation.java index 5d336661db..94b906a7dc 100644 --- a/alchemist-incarnation-sapere/src/test/java/it/unibo/alchemist/test/TestIncarnation.java +++ b/alchemist-incarnation-sapere/src/test/java/it/unibo/alchemist/test/TestIncarnation.java @@ -39,9 +39,9 @@ final class TestIncarnation { private final SAPEREIncarnation incarnation = new SAPEREIncarnation<>(); private ILsaNode node; - private Environment, Euclidean2DPosition> env; - private RandomGenerator rand; - private TimeDistribution> time; + private Environment, Euclidean2DPosition> environment; + private RandomGenerator randomGenerator; + private TimeDistribution> timeDistribution; private ILsaMolecule mkMol(final String s, final int args, final boolean ground) { final ILsaMolecule res = incarnation.createMolecule(s); @@ -57,10 +57,10 @@ private ILsaMolecule mkMol(final String s, final int args, final boolean ground) */ @BeforeEach public void setUp() { - env = new Continuous2DEnvironment<>(incarnation); - node = new LsaNode(env); - rand = new MersenneTwister(); - time = new SAPEREExponentialTime("1", rand); + environment = new Continuous2DEnvironment<>(incarnation); + node = new LsaNode(environment); + randomGenerator = new MersenneTwister(); + timeDistribution = new SAPEREExponentialTime("1", randomGenerator); } /** @@ -79,7 +79,9 @@ void testCreateMolecule() { } private void testTD(final String param, final double rate, final double occurrence) { - final TimeDistribution> t0 = incarnation.createTimeDistribution(rand, env, node, param); + final TimeDistribution> t0 = incarnation.createTimeDistribution( + randomGenerator, environment, node, param + ); assertNotNull(t0); if (!Double.isNaN(rate)) { assertEquals(rate, t0.getRate(), Double.MIN_VALUE); @@ -115,7 +117,9 @@ private void testR( final int nneighact, final int nallneighact ) { - final Reaction> r = incarnation.createReaction(rand, env, node, time, param); + final Reaction> r = incarnation.createReaction( + randomGenerator, environment, node, timeDistribution, param + ); assertNotNull(r); assertEquals(ncond, r.getConditions().size()); assertEquals(nact, r.getActions().size()); @@ -126,7 +130,7 @@ private void testR( private void testNoR(final String param) { try { - incarnation.createReaction(rand, env, node, time, param); + incarnation.createReaction(randomGenerator, environment, node, timeDistribution, param); fail(); } catch (IllegalArgumentException e) { assertFalse(e.getMessage().isEmpty()); diff --git a/alchemist-incarnation-scafi/src/main/scala/it/unibo/alchemist/model/ScafiIncarnation.scala b/alchemist-incarnation-scafi/src/main/scala/it/unibo/alchemist/model/ScafiIncarnation.scala index 4f12722866..31a23caa10 100644 --- a/alchemist-incarnation-scafi/src/main/scala/it/unibo/alchemist/model/ScafiIncarnation.scala +++ b/alchemist-incarnation-scafi/src/main/scala/it/unibo/alchemist/model/ScafiIncarnation.scala @@ -84,6 +84,8 @@ sealed class ScafiIncarnation[T, P <: Position[P]] extends Incarnation[T, P]{ CachedInterpreter[AnyRef](if(doCacheValue) v else v.tail, doCacheValue).asInstanceOf[T] } + override def createConcentration(): T = createConcentration("") + override def createCondition(rand: RandomGenerator, env: Environment[T, P] , node: Node[T], time: TimeDistribution[T], reaction: Reaction[T], param: String): Condition[T] = { if(!isScafiNode(node)) { throw new IllegalArgumentException(s"The node must be an instance of ${classOf[ScafiNode[_,_]]}" diff --git a/alchemist-incarnation-scafi/src/main/scala/it/unibo/alchemist/model/implementations/nodes/ScafiNode.scala b/alchemist-incarnation-scafi/src/main/scala/it/unibo/alchemist/model/implementations/nodes/ScafiNode.scala index b9be5ca540..af3d7837c3 100644 --- a/alchemist-incarnation-scafi/src/main/scala/it/unibo/alchemist/model/implementations/nodes/ScafiNode.scala +++ b/alchemist-incarnation-scafi/src/main/scala/it/unibo/alchemist/model/implementations/nodes/ScafiNode.scala @@ -9,7 +9,7 @@ package it.unibo.alchemist.model.implementations.nodes import it.unibo.alchemist.model.interfaces.{Environment, Molecule, Position, Time} -class ScafiNode[T, P<:Position[P]](env: Environment[T, P]) extends AbstractNode[T](env) { +class ScafiNode[T, P<:Position[P]](env: Environment[T, P]) extends GenericNode[T](env) { private var lastAccessedMolecule: Molecule = null override def createT = null.asInstanceOf[T] diff --git a/alchemist-interfaces/src/main/java/it/unibo/alchemist/model/interfaces/Incarnation.java b/alchemist-interfaces/src/main/java/it/unibo/alchemist/model/interfaces/Incarnation.java index 98b8570380..8e1ba99630 100644 --- a/alchemist-interfaces/src/main/java/it/unibo/alchemist/model/interfaces/Incarnation.java +++ b/alchemist-interfaces/src/main/java/it/unibo/alchemist/model/interfaces/Incarnation.java @@ -52,6 +52,13 @@ public interface Incarnation> { */ T createConcentration(String s); + /** + * Creates a new concentration object of a specific concrete type. + * + * @return a concentration of a certain concrete type + */ + T createConcentration(); + /** * @param randomGenerator * the random engine diff --git a/alchemist-interfaces/src/main/java/it/unibo/alchemist/model/interfaces/Node.java b/alchemist-interfaces/src/main/java/it/unibo/alchemist/model/interfaces/Node.java deleted file mode 100644 index cfe9a5f1b1..0000000000 --- a/alchemist-interfaces/src/main/java/it/unibo/alchemist/model/interfaces/Node.java +++ /dev/null @@ -1,119 +0,0 @@ -/* - * Copyright (C) 2010-2019, Danilo Pianini and contributors listed in the main project's alchemist/build.gradle file. - * - * This file is part of Alchemist, and is distributed under the terms of the - * GNU General Public License, with a linking exception, - * as described in the file LICENSE in the Alchemist distribution's top directory. - */ -package it.unibo.alchemist.model.interfaces; - -import java.io.Serializable; -import java.util.List; -import java.util.Map; - -/** - * @param - * The type of the concentration - * - * This interface must be implemented in every realization of node - */ -public interface Node extends Serializable, Iterable>, Comparable> { - - /** - * Adds a reaction to this node. - * The reaction is added only in the node, - * but not in the {@link Simulation} scheduler, so it will never be executed. - * To add the reaction also in the scheduler (and start to execute it), - * you have to call also the method {@link Simulation#reactionAdded(Reaction)}. - * @param r - * the reaction to be added - */ - void addReaction(Reaction r); - - /** - * Creates a new Node which is a clone of the current Node. The new Node - * will have all the current Node's properties, such as reactions and - * molecules, but it will also have a different ID. - * - * @param currentTime - * the time at which the cloning operation happens - * - * @return A new Node which is a clone of the current one. - * - * @throws UnsupportedOperationException - * if the implementation does not support node cloning. - */ - Node cloneNode(Time currentTime); - - /** - * Tests whether a node contains a {@link Molecule}. - * - * @param mol - * the molecule to check - * @return true if the molecule is present, false otherwise - */ - boolean contains(Molecule mol); - - /** - * Calculates the concentration of a molecule. - * - * @param mol - * the molecule whose concentration will be returned - * @return the concentration of the molecule - */ - T getConcentration(Molecule mol); - - /** - * @return the molecule corresponding to the i-th position - */ - Map getContents(); - - /** - * @return an univocal id for this node in the environment - */ - int getId(); - - /** - * @return the count of different molecules in this node - */ - int getMoleculeCount(); - - /** - * This method allows to access all the reaction of the node. - * - * @return the list of rections belonging to this node - */ - List> getReactions(); - - @Override - int hashCode(); - - /** - * @param mol the molecule that should be removed - */ - void removeConcentration(Molecule mol); - - /** - * Removes a reaction from this node. - * The reaction is removed only in the node, - * but not in the {@link Simulation} scheduler, - * so the scheduler will continue to execute the reaction. - * To remove the reaction also in the scheduler (and stop to execute it), - * you have to call also the method {@link Simulation#reactionRemoved(Reaction)}. - * - * @param r - * the reaction to be removed - */ - void removeReaction(Reaction r); - - /** - * Sets the concentration of mol to c. - * - * @param mol - * the molecule you want to set the concentration - * @param c - * the concentration you want for mol - */ - void setConcentration(Molecule mol, T c); - -} diff --git a/alchemist-interfaces/src/main/kotlin/it/unibo/alchemist/model/interfaces/Node.kt b/alchemist-interfaces/src/main/kotlin/it/unibo/alchemist/model/interfaces/Node.kt new file mode 100644 index 0000000000..d6e980dede --- /dev/null +++ b/alchemist-interfaces/src/main/kotlin/it/unibo/alchemist/model/interfaces/Node.kt @@ -0,0 +1,201 @@ +/* + * Copyright (C) 2010-2022, Danilo Pianini and contributors + * listed, for each module, in the respective subproject's build.gradle.kts file. + * + * This file is part of Alchemist, and is distributed under the terms of the + * GNU General Public License, with a linking exception, + * as described in the file LICENSE in the Alchemist distribution's top directory. + */ +package it.unibo.alchemist.model.interfaces + +import java.io.Serializable +import java.lang.IllegalStateException +import kotlin.reflect.KClass +import kotlin.reflect.full.isSubclassOf +import kotlin.reflect.jvm.jvmErasure + +/** + * @param + * The type of the concentration + * + * This interface must be implemented in every realization of node + */ +interface Node : Serializable, Iterable>, Comparable> { + /** + * Adds a reaction to this node. + * The reaction is added only in the node, + * but not in the [Simulation] scheduler, so it will never be executed. + * To add the reaction also in the scheduler (and start to execute it), + * you have to call also the method [Simulation.reactionAdded]. + * @param r + * the reaction to be added + */ + fun addReaction(reactionToAdd: Reaction) + + /** + * Creates a new Node which is a clone of the current Node. The new Node + * will have all the current Node's properties, such as reactions and + * molecules, but it will also have a different ID. + * + * @param currentTime + * the time at which the cloning operation happens + * + * @return A new Node which is a clone of the current one. + * + * @throws UnsupportedOperationException + * if the implementation does not support node cloning. + */ + fun cloneNode(currentTime: Time): Node + + /** + * Tests whether a node contains a [Molecule]. + * + * @param mol + * the molecule to check + * @return true if the molecule is present, false otherwise + */ + operator fun contains(molecule: Molecule): Boolean + + /** + * Calculates the concentration of a molecule. + * + * @param mol + * the molecule whose concentration will be returned + * @return the concentration of the molecule + */ + fun getConcentration(molecule: Molecule): T + + /** + * @return the molecule corresponding to the i-th position + */ + val contents: Map + + /** + * @return an univocal id for this node in the environment + */ + val id: Int + + /** + * @return the count of different molecules in this node + */ + val moleculeCount: Int + + /** + * @return a list of the node's capabilities + */ + val capabilities: List> + + /** + * This method allows to access all the reaction of the node. + * + * @return the list of rections belonging to this node + */ + val reactions: List> + + override fun hashCode(): Int + + override fun equals(other: Any?): Boolean + + /** + * @param mol the molecule that should be removed + */ + fun removeConcentration(moleculeToRemove: Molecule) + + /** + * Removes a reaction from this node. + * The reaction is removed only in the node, + * but not in the [Simulation] scheduler, + * so the scheduler will continue to execute the reaction. + * To remove the reaction also in the scheduler (and stop to execute it), + * you have to call also the method [Simulation.reactionRemoved]. + * + * @param r + * the reaction to be removed + */ + fun removeReaction(reactionToRemove: Reaction) + + /** + * Sets the concentration of mol to c. + * + * @param mol + * the molecule you want to set the concentration + * @param c + * the concentration you want for mol + */ + fun setConcentration(molecule: Molecule, concentration: T) + + /** + * Adds a capability to the node. + * @param nodeProperty the capability you want to add to the node + */ + fun addProperty(nodeProperty: NodeProperty) + + /** + * returns a [NodeProperty] of the provided [type] [C]. + * @param [C] type of capability + * @param superType the type of capability to retrieve + * @return a capability of the provided type [C] + */ + fun > asPropertyOrNull(superType: Class): C? = asPropertyOrNull(superType.kotlin) + + /** + * returns a [NodeProperty] of the provided [type] [C]. + * @param [C] type of capability + * @param superType the type of capability to retrieve + * @return a capability of the provided type [C] + */ + @Suppress("UNCHECKED_CAST") + fun > asPropertyOrNull(superType: KClass): C? = capabilities + .asSequence() + .mapNotNull { nodeProperty: NodeProperty -> + nodeProperty::class.distanceFrom(superType)?.let { nodeProperty to it } + } + .minByOrNull { it: Pair, Int> -> it.second } + ?.first as? C + + /** + * returns a [NodeProperty] of the provided [type] [C]. + * @param [C] type of capability + * @param superType the type of capability to retrieve + * @return a capability of the provided type [C] + */ + fun > asProperty(superType: KClass): C = + asPropertyOrNull(superType).let { it } ?: throw IllegalStateException( + "A ${superType.simpleName} is required for node ${this.id} but is missing" + ) + + /** + * returns a [NodeProperty] of the provided [type] [C]. + * @param [C] type of capability + * @param superType the type of capability to retrieve + * @return a capability of the provided type [C] + */ + fun > asProperty(superType: Class): C = asProperty(superType.kotlin) + + companion object { + /** + * returns a [NodeProperty] of the provided [type] [C]. + * @param [C] type of capability + * @param superType the type of capability to retrieve + * @return a capability of the provided type [C] + */ + inline fun > Node.asProperty(): C = asProperty(C::class) + + /** + * returns a [NodeProperty] of the provided [type] [C] or [null] if the node does not have the capabiilty. + * @param [C] type of capability + * @param superType the type of capability to retrieve + * @return if present, a capability of the provided type [C] + */ + inline fun > Node.asPropertyOrNull(): C? = asPropertyOrNull(C::class) + + private fun KClass<*>.distanceFrom(superType: KClass<*>, depth: Int = 0): Int? = when { + !isSubclassOf(superType) -> null + superType == this -> depth + else -> supertypes.asSequence() + .map { it.jvmErasure } + .mapNotNull { it.distanceFrom(superType, depth + 1) } + .minOrNull() + } + } +} diff --git a/alchemist-interfaces/src/main/kotlin/it/unibo/alchemist/model/interfaces/NodeProperty.kt b/alchemist-interfaces/src/main/kotlin/it/unibo/alchemist/model/interfaces/NodeProperty.kt new file mode 100644 index 0000000000..a040481f99 --- /dev/null +++ b/alchemist-interfaces/src/main/kotlin/it/unibo/alchemist/model/interfaces/NodeProperty.kt @@ -0,0 +1,27 @@ +/* + * Copyright (C) 2010-2022, Danilo Pianini and contributors + * listed, for each module, in the respective subproject's build.gradle.kts file. + * + * This file is part of Alchemist, and is distributed under the terms of the + * GNU General Public License, with a linking exception, + * as described in the file LICENSE in the Alchemist distribution's top directory. + */ + +package it.unibo.alchemist.model.interfaces + +import java.io.Serializable + +/** + * Represents a node's capability. + */ +interface NodeProperty : Serializable { + /** + * The node to which the capability is added. + */ + val node: Node + + /** + * Clones this property to be added on a new [node]. + */ + fun cloneOnNewNode(node: Node): NodeProperty +} diff --git a/alchemist-loading/src/main/java/it/unibo/alchemist/loader/shapes/Abstract2DShape.java b/alchemist-loading/src/main/java/it/unibo/alchemist/loader/filters/Abstract2DShape.java similarity index 88% rename from alchemist-loading/src/main/java/it/unibo/alchemist/loader/shapes/Abstract2DShape.java rename to alchemist-loading/src/main/java/it/unibo/alchemist/loader/filters/Abstract2DShape.java index 365e44b218..55287a7504 100644 --- a/alchemist-loading/src/main/java/it/unibo/alchemist/loader/shapes/Abstract2DShape.java +++ b/alchemist-loading/src/main/java/it/unibo/alchemist/loader/filters/Abstract2DShape.java @@ -5,16 +5,16 @@ * GNU General Public License, with a linking exception, * as described in the file LICENSE in the Alchemist distribution's top directory. */ -package it.unibo.alchemist.loader.shapes; +package it.unibo.alchemist.loader.filters; import it.unibo.alchemist.model.interfaces.Position2D; /** - * A bidimensional Alchemist {@link Shape} that relies on AWT {@link java.awt.Shape}. + * A bidimensional Alchemist {@link Filter} that relies on AWT {@link java.awt.Shape}. * * @param

position type */ -public abstract class Abstract2DShape

> implements Shape

{ +public abstract class Abstract2DShape

> implements Filter

{ private final java.awt.Shape shape; diff --git a/alchemist-loading/src/main/java/it/unibo/alchemist/loader/shapes/Circle.java b/alchemist-loading/src/main/java/it/unibo/alchemist/loader/filters/Circle.java similarity index 96% rename from alchemist-loading/src/main/java/it/unibo/alchemist/loader/shapes/Circle.java rename to alchemist-loading/src/main/java/it/unibo/alchemist/loader/filters/Circle.java index 9766b27495..09d7baf042 100644 --- a/alchemist-loading/src/main/java/it/unibo/alchemist/loader/shapes/Circle.java +++ b/alchemist-loading/src/main/java/it/unibo/alchemist/loader/filters/Circle.java @@ -5,7 +5,7 @@ * GNU General Public License, with a linking exception, * as described in the file LICENSE in the Alchemist distribution's top directory. */ -package it.unibo.alchemist.loader.shapes; +package it.unibo.alchemist.loader.filters; import it.unibo.alchemist.model.interfaces.Position2D; diff --git a/alchemist-loading/src/main/java/it/unibo/alchemist/loader/shapes/Rectangle.java b/alchemist-loading/src/main/java/it/unibo/alchemist/loader/filters/Rectangle.java similarity index 95% rename from alchemist-loading/src/main/java/it/unibo/alchemist/loader/shapes/Rectangle.java rename to alchemist-loading/src/main/java/it/unibo/alchemist/loader/filters/Rectangle.java index 55bdc62545..45ee9deb07 100644 --- a/alchemist-loading/src/main/java/it/unibo/alchemist/loader/shapes/Rectangle.java +++ b/alchemist-loading/src/main/java/it/unibo/alchemist/loader/filters/Rectangle.java @@ -5,7 +5,7 @@ * GNU General Public License, with a linking exception, * as described in the file LICENSE in the Alchemist distribution's top directory. */ -package it.unibo.alchemist.loader.shapes; +package it.unibo.alchemist.loader.filters; import it.unibo.alchemist.model.interfaces.Position2D; diff --git a/alchemist-loading/src/main/java/it/unibo/alchemist/loader/shapes/Shape.java b/alchemist-loading/src/main/java/it/unibo/alchemist/loader/shapes/Shape.java deleted file mode 100644 index 902dc87d4c..0000000000 --- a/alchemist-loading/src/main/java/it/unibo/alchemist/loader/shapes/Shape.java +++ /dev/null @@ -1,35 +0,0 @@ -/* - * Copyright (C) 2010-2019, Danilo Pianini and contributors listed in the main project's alchemist/build.gradle file. - * - * This file is part of Alchemist, and is distributed under the terms of the - * GNU General Public License, with a linking exception, - * as described in the file LICENSE in the Alchemist distribution's top directory. - */ -package it.unibo.alchemist.loader.shapes; - -import it.unibo.alchemist.model.interfaces.Position; - -import java.util.function.Predicate; - -/** - * A Shape, representing an partition of the space where a {@link Position} may - * lie in. - * - * @param

position type - * - */ -@FunctionalInterface -public interface Shape

> extends Predicate

{ - - /** - * @param position - * the position - * @return true if the position is inside the {@link Shape}. - */ - boolean contains(P position); - - @Override - default boolean test(final P position) { - return contains(position); - } -} diff --git a/alchemist-loading/src/main/kotlin/it/unibo/alchemist/loader/export/exporters/MongoDBExporter.kt b/alchemist-loading/src/main/kotlin/it/unibo/alchemist/loader/export/exporters/MongoDBExporter.kt index 03a29b5d0a..1c0709f050 100644 --- a/alchemist-loading/src/main/kotlin/it/unibo/alchemist/loader/export/exporters/MongoDBExporter.kt +++ b/alchemist-loading/src/main/kotlin/it/unibo/alchemist/loader/export/exporters/MongoDBExporter.kt @@ -26,7 +26,7 @@ class MongoDBExporter> @JvmOverloads constructor( val uri: String, val dbName: String = DEFAULT_DATABASE, val interval: Double = DEFAULT_INTERVAL, - private val appendTime: Boolean = false + private val appendTime: Boolean = false, ) : AbstractExporter(interval) { /** @@ -52,10 +52,15 @@ class MongoDBExporter> @JvmOverloads constructor( mongoService.stopService() } - private fun convertToDocument(env: Environment, reaction: Reaction?, time: Time, step: Long): Document { + private fun convertToDocument( + environment: Environment, + reaction: Reaction?, + time: Time, + step: Long, + ): Document { val document = Document() dataExtractors.forEach { extractor -> - extractor.extractData(env, reaction, time, step).forEach { (dataLabel, dataValue) -> + extractor.extractData(environment, reaction, time, step).forEach { (dataLabel, dataValue) -> document.append(dataLabel, dataValue) } } diff --git a/alchemist-loading/src/main/kotlin/it/unibo/alchemist/loader/filters/Filter.kt b/alchemist-loading/src/main/kotlin/it/unibo/alchemist/loader/filters/Filter.kt new file mode 100644 index 0000000000..d3cc31b695 --- /dev/null +++ b/alchemist-loading/src/main/kotlin/it/unibo/alchemist/loader/filters/Filter.kt @@ -0,0 +1,38 @@ +/* + * Copyright (C) 2010-2022, Danilo Pianini and contributors + * listed, for each module, in the respective subproject's build.gradle.kts file. + * + * This file is part of Alchemist, and is distributed under the terms of the + * GNU General Public License, with a linking exception, + * as described in the file LICENSE in the Alchemist distribution's top directory. + */ + +package it.unibo.alchemist.loader.filters + +import it.unibo.alchemist.model.interfaces.Position +import java.util.function.Predicate + +/** + * Filtering condition for deployments. (e.g inject a [Module] in a node if [test] + * is satisfied). + */ +interface Filter

> : Predicate

, (P) -> Boolean { + + /** + * Checks if the [position] is inside the shape. + * @return true if the position is inside the [Filter]. + */ + operator fun contains(position: P): Boolean + + /** + * Checks if the [position] is inside the shape. + * @return true if the position is inside the [Filter]. + */ + override fun test(position: P) = contains(position) + + /** + * Checks if the [position] is inside the shape. + * @return true if the position is inside the [Filter]. + */ + override operator fun invoke(position: P): Boolean = contains(position) +} diff --git a/alchemist-loading/src/main/kotlin/it/unibo/alchemist/loader/filters/compounds/And.kt b/alchemist-loading/src/main/kotlin/it/unibo/alchemist/loader/filters/compounds/And.kt new file mode 100644 index 0000000000..cd118cc8ea --- /dev/null +++ b/alchemist-loading/src/main/kotlin/it/unibo/alchemist/loader/filters/compounds/And.kt @@ -0,0 +1,28 @@ +/* + * Copyright (C) 2010-2022, Danilo Pianini and contributors + * listed, for each module, in the respective subproject's build.gradle.kts file. + * + * This file is part of Alchemist, and is distributed under the terms of the + * GNU General Public License, with a linking exception, + * as described in the file LICENSE in the Alchemist distribution's top directory. + */ + +package it.unibo.alchemist.loader.filters.compounds + +import it.unibo.alchemist.loader.filters.Filter +import it.unibo.alchemist.model.interfaces.Position + +/** + * Check if both [filterA] and [filterB] are satisfied. + * @param filterA the first filter. + * @param filterB the second filter. + */ +data class And

> ( + val filterA: Filter

, + val filterB: Filter

, +) : Filter

{ + /** + * Returns true if both [filterA] and [filterB] are satisfied. + */ + override operator fun contains(position: P) = position in filterA && position in filterB +} diff --git a/alchemist-loading/src/main/kotlin/it/unibo/alchemist/loader/filters/compounds/Not.kt b/alchemist-loading/src/main/kotlin/it/unibo/alchemist/loader/filters/compounds/Not.kt new file mode 100644 index 0000000000..8eed72fb0d --- /dev/null +++ b/alchemist-loading/src/main/kotlin/it/unibo/alchemist/loader/filters/compounds/Not.kt @@ -0,0 +1,26 @@ +/* + * Copyright (C) 2010-2022, Danilo Pianini and contributors + * listed, for each module, in the respective subproject's build.gradle.kts file. + * + * This file is part of Alchemist, and is distributed under the terms of the + * GNU General Public License, with a linking exception, + * as described in the file LICENSE in the Alchemist distribution's top directory. + */ + +package it.unibo.alchemist.loader.filters.compounds + +import it.unibo.alchemist.loader.filters.Filter +import it.unibo.alchemist.model.interfaces.Position + +/** + * Negates the [filter]'s test. + * @param [filter] the filter to be negated. + */ +class Not

> ( + val filter: Filter

+) : Filter

{ + /** + * Returns true if [filter] is not satisfied. + */ + override operator fun contains(position: P) = position !in filter +} diff --git a/alchemist-loading/src/main/kotlin/it/unibo/alchemist/loader/filters/compounds/Or.kt b/alchemist-loading/src/main/kotlin/it/unibo/alchemist/loader/filters/compounds/Or.kt new file mode 100644 index 0000000000..b2bc758e3c --- /dev/null +++ b/alchemist-loading/src/main/kotlin/it/unibo/alchemist/loader/filters/compounds/Or.kt @@ -0,0 +1,28 @@ +/* + * Copyright (C) 2010-2022, Danilo Pianini and contributors + * listed, for each module, in the respective subproject's build.gradle.kts file. + * + * This file is part of Alchemist, and is distributed under the terms of the + * GNU General Public License, with a linking exception, + * as described in the file LICENSE in the Alchemist distribution's top directory. + */ + +package it.unibo.alchemist.loader.filters.compounds + +import it.unibo.alchemist.loader.filters.Filter +import it.unibo.alchemist.model.interfaces.Position + +/** + * Check if either [filterA] or [filterB] is satisfied. + * @param filterA the first filter. + * @param filterB the second filter. + */ +data class Or

> ( + val filterA: Filter

, + val filterB: Filter

, +) : Filter

{ + /** + * Returns true if either [filterA] or [filterB] are satisfied. + */ + override operator fun contains(position: P) = position in filterA || position in filterB +} diff --git a/alchemist-loading/src/main/kotlin/it/unibo/alchemist/loader/filters/compounds/Xor.kt b/alchemist-loading/src/main/kotlin/it/unibo/alchemist/loader/filters/compounds/Xor.kt new file mode 100644 index 0000000000..0bb28c1597 --- /dev/null +++ b/alchemist-loading/src/main/kotlin/it/unibo/alchemist/loader/filters/compounds/Xor.kt @@ -0,0 +1,28 @@ +/* + * Copyright (C) 2010-2022, Danilo Pianini and contributors + * listed, for each module, in the respective subproject's build.gradle.kts file. + * + * This file is part of Alchemist, and is distributed under the terms of the + * GNU General Public License, with a linking exception, + * as described in the file LICENSE in the Alchemist distribution's top directory. + */ + +package it.unibo.alchemist.loader.filters.compounds + +import it.unibo.alchemist.loader.filters.Filter +import it.unibo.alchemist.model.interfaces.Position + +/** + * Check if only one between [filterA] and [filterB] is satisfied. + * @param filterA the first filter. + * @param filterB the second filter. + */ +data class Xor

> ( + val filterA: Filter

, + val filterB: Filter

, +) : Filter

{ + /** + * Returns true if only one [filterA] and [filterB] is satisfied. + */ + override operator fun contains(position: P) = position in filterA != position in filterB +} diff --git a/alchemist-loading/src/main/kotlin/it/unibo/alchemist/loader/m2m/LoadingSystem.kt b/alchemist-loading/src/main/kotlin/it/unibo/alchemist/loader/m2m/LoadingSystem.kt index 231e979a8c..caae97232b 100644 --- a/alchemist-loading/src/main/kotlin/it/unibo/alchemist/loader/m2m/LoadingSystem.kt +++ b/alchemist-loading/src/main/kotlin/it/unibo/alchemist/loader/m2m/LoadingSystem.kt @@ -20,13 +20,12 @@ import it.unibo.alchemist.model.interfaces.Incarnation import it.unibo.alchemist.model.interfaces.Layer import it.unibo.alchemist.model.interfaces.LinkingRule import it.unibo.alchemist.model.interfaces.Molecule -import it.unibo.alchemist.model.interfaces.Node import it.unibo.alchemist.model.interfaces.Position -import it.unibo.alchemist.model.interfaces.Reaction import org.apache.commons.math3.random.RandomGenerator import org.danilopianini.jirf.Factory import it.unibo.alchemist.loader.m2m.LoadingSystemLogger.logger import it.unibo.alchemist.loader.m2m.syntax.DocumentRoot +import it.unibo.alchemist.model.interfaces.Node import java.lang.IllegalStateException import java.util.concurrent.Semaphore import java.util.function.Predicate @@ -77,14 +76,14 @@ internal abstract class LoadingSystem( setCurrentRandomGenerator(simulationRNG) // INCARNATION val incarnation = SimulationModel.visitIncarnation(root[DocumentRoot.incarnation]) - registerSingleton>(incarnation) + registerSingleton(incarnation) registerImplicit(incarnation::createMolecule) registerImplicit(incarnation::createConcentration) // ENVIRONMENT val environment: Environment = SimulationModel.visitEnvironment(incarnation, context, root[DocumentRoot.environment]) logger.info("Created environment: {}", environment) - registerSingleton>(environment) + registerSingleton(environment) // LAYERS val layers: List>> = SimulationModel.visitLayers(incarnation, context, root[DocumentRoot.layers]) @@ -103,7 +102,7 @@ internal abstract class LoadingSystem( val linkingRule = SimulationModel.visitLinkingRule(context, root.getOrEmptyMap(DocumentRoot.linkingRule)) environment.linkingRule = linkingRule - registerSingleton>(linkingRule) + registerSingleton(linkingRule) // DISPLACEMENTS setCurrentRandomGenerator(scenarioRNG) val displacementsSource = root.getOrEmpty(DocumentRoot.deployments) @@ -142,6 +141,61 @@ internal abstract class LoadingSystem( return EnvironmentAndExports(environment, exporters) } + private fun > loadContentsOnNode( + incarnation: Incarnation, + node: Node, + nodePosition: P, + descriptor: Map<*, *>, + ) { + SimulationModel.visitContents(incarnation, context, descriptor) + .forEach { (filters, molecule, concentrationMaker) -> + if (filters.isEmpty() || filters.any { nodePosition in it }) { + val concentration = concentrationMaker() + logger.debug("Injecting {} ==> {} in node {}", molecule, concentration, node.id) + node.setConcentration(molecule, concentration) + } + } + } + + private fun > loadPropertiesOnNode( + node: Node, + nodePosition: P, + descriptor: Map<*, *>, + ) { + SimulationModel.visitProperty(context, descriptor) + .filter { (filters, _) -> filters.isEmpty() || filters.any { nodePosition in it } } + .forEach { (_, property) -> node.addProperty(property) } + } + + private fun > loadProgramsOnNode( + randomGenerator: RandomGenerator, + incarnation: Incarnation, + environment: Environment, + node: Node, + nodePosition: P, + descriptor: Map<*, *>, + ) { + val programDescriptor = descriptor.getOrEmpty(DocumentRoot.Deployment.programs) + val programs = SimulationModel.visitRecursively( + context, + programDescriptor, + DocumentRoot.Deployment.Program, + ) { program -> + requireNotNull(program) { + "null is not a valid program in $descriptor. ${DocumentRoot.Deployment.Program.guide}" + } + (program as? Map<*, *>)?.let { + SimulationModel.visitProgram(randomGenerator, incarnation, environment, node, context, it) + ?.onSuccess { (filters, reaction) -> + if (filters.isEmpty() || filters.any { shape -> nodePosition in shape }) { + node.addReaction(reaction) + } + } + } + } + logger.debug("Programs: {}", programs) + } + private fun > populateDeployment( simulationRNG: RandomGenerator, incarnation: Incarnation, @@ -167,34 +221,15 @@ internal abstract class LoadingSystem( environment.linkingRule = composedLinkingRule registerSingleton>(composedLinkingRule) } - val contents = SimulationModel.visitContents(incarnation, context, descriptor) - val programDescriptor = descriptor.getOrEmpty(DocumentRoot.Deployment.programs) deployment.stream().forEach { position -> val node = SimulationModel.visitNode(simulationRNG, incarnation, environment, context, nodeDescriptor) - registerSingleton>(node) + registerSingleton(node) // NODE CONTENTS - contents.forEach { (shapes, molecule, concentrationMaker) -> - if (shapes.isEmpty() || shapes.any { position in it }) { - val concentration = concentrationMaker() - logger.debug("Injecting {} ==> {} in node {}", molecule, concentration, node.id) - node.setConcentration(molecule, concentration) - } - } + loadContentsOnNode(incarnation, node, position, descriptor) + // PROPERTIES + loadPropertiesOnNode(node, position, descriptor) // PROGRAMS - val programs = SimulationModel.visitRecursively>( - context, - programDescriptor, - DocumentRoot.Deployment.Program - ) { program -> - requireNotNull(program) { - "null is not a valid program in $descriptor. ${DocumentRoot.Deployment.Program.guide}" - } - (program as? Map<*, *>)?.let { - SimulationModel.visitProgram(simulationRNG, incarnation, environment, node, context, it) - ?.onSuccess(node::addReaction) - } - } - logger.debug("Programs: {}", programs) + loadProgramsOnNode(simulationRNG, incarnation, environment, node, position, descriptor) environment.addNode(node, position) logger.debug("Added node {} at {}", node.id, position) factory.deregisterSingleton(node) diff --git a/alchemist-loading/src/main/kotlin/it/unibo/alchemist/loader/m2m/SimulationModel.kt b/alchemist-loading/src/main/kotlin/it/unibo/alchemist/loader/m2m/SimulationModel.kt index ccc33537aa..3e01b07eb8 100644 --- a/alchemist-loading/src/main/kotlin/it/unibo/alchemist/loader/m2m/SimulationModel.kt +++ b/alchemist-loading/src/main/kotlin/it/unibo/alchemist/loader/m2m/SimulationModel.kt @@ -22,7 +22,7 @@ import it.unibo.alchemist.loader.m2m.LoadingSystemLogger.logger import it.unibo.alchemist.loader.m2m.syntax.DocumentRoot import it.unibo.alchemist.loader.m2m.syntax.DocumentRoot.JavaType import it.unibo.alchemist.loader.m2m.syntax.SyntaxElement -import it.unibo.alchemist.loader.shapes.Shape +import it.unibo.alchemist.loader.filters.Filter import it.unibo.alchemist.loader.variables.Constant import it.unibo.alchemist.loader.variables.DependentVariable import it.unibo.alchemist.loader.variables.JSR223Variable @@ -33,6 +33,7 @@ import it.unibo.alchemist.model.implementations.linkingrules.CombinedLinkingRule import it.unibo.alchemist.model.implementations.linkingrules.NoLinks import it.unibo.alchemist.model.implementations.positions.Euclidean2DPosition import it.unibo.alchemist.model.interfaces.Action +import it.unibo.alchemist.model.interfaces.NodeProperty import it.unibo.alchemist.model.interfaces.Condition import it.unibo.alchemist.model.interfaces.Environment import it.unibo.alchemist.model.interfaces.Incarnation @@ -274,11 +275,23 @@ internal object SimulationModel { return constant?.let { Result.success(it) } } + fun

> visitFilter( + context: Context, + element: Map<*, *>, + ): List> { + val filterKey = DocumentRoot.Deployment.Filter.filter + val filters = visitRecursively(context, element[filterKey] ?: emptyList()) { shape -> + visitBuilding>(context, shape) + } + logger.debug("Filters: {}", filters) + return filters + } + fun > visitContents( incarnation: Incarnation, context: Context, root: Map<*, *> - ): List>, Molecule, () -> T>> { + ): List>, Molecule, () -> T>> { logger.debug("Visiting contents: {}", root) val allContents = root[DocumentRoot.Deployment.contents] ?: emptyList() return visitRecursively(context, allContents) { element -> @@ -288,11 +301,7 @@ internal object SimulationModel { ?.takeIf { element.containsKey(moleculeKey) } ?.let { logger.debug("Found content descriptor: {}", it) - val shapesKey = DocumentRoot.Deployment.Contents.shapes - val shapes = visitRecursively(context, element[shapesKey] ?: emptyList()) { shape -> - visitBuilding>(context, shape) - } - logger.debug("Shapes: {}", shapes) + val filters = visitFilter

(context, element) val moleculeElement = element[moleculeKey] require(moleculeElement !is Map<*, *> && moleculeElement !is Iterable<*>) { val type = moleculeElement?.let { ": " + it::class.simpleName } ?: "" @@ -305,11 +314,30 @@ internal object SimulationModel { val concentrationMaker: () -> T = { element[concentrationKey]?.toString().let { incarnation.createConcentration(it) } } - Result.success(Triple(shapes, molecule, concentrationMaker)) + Result.success(Triple(filters, molecule, concentrationMaker)) } } } + fun > visitProperty( + context: Context, + root: Map<*, *>, + ): List>, NodeProperty>> { + logger.debug("Visiting properties: {}", root) + val capabilitiesKey = DocumentRoot.Deployment.properties + val allCapabilities = root[capabilitiesKey] ?: emptyList() + return visitRecursively(context, allCapabilities, DocumentRoot.Deployment.Property) { element -> + (element as? Map<*, *>)?.let { + val filters = visitFilter

(context, element) + val nodeProperty = visitBuilding>(context, element) + ?.getOrThrow() + ?: cantBuildWith>(root, JavaType) + logger.debug("Property: {}", nodeProperty) + Result.success(Pair(filters, nodeProperty)) + } + } + } + private fun visitDependentVariable(name: String, context: Context, root: Any?): Result>? { val descriptor = (root as? Map<*, *>)?.takeIfNotAConstant(name, context) return when { @@ -469,7 +497,7 @@ internal object SimulationModel { node: Node, context: Context, program: Map<*, *> - ): Result>? = if (ProgramSyntax.validateDescriptor(program)) { + ): Result>, Reaction>>? = if (ProgramSyntax.validateDescriptor(program)) { val timeDistribution: TimeDistribution = visitTimeDistribution( incarnation, simulationRNG, @@ -485,6 +513,7 @@ internal object SimulationModel { fun create(parameter: Any?, makeWith: ReactionComponentFunction): Result = runCatching { makeWith(simulationRNG, environment, node, timeDistribution, reaction, parameter?.toString()) } + val filters = visitFilter

(context, program) val conditions = visitRecursively>( context, program[ProgramSyntax.conditions] ?: emptyList(), @@ -513,7 +542,7 @@ internal object SimulationModel { } context.factory.deregisterSingleton(reaction) context.factory.deregisterSingleton(timeDistribution) - Result.success(reaction) + Result.success(Pair(filters, reaction)) } else { null } diff --git a/alchemist-loading/src/main/kotlin/it/unibo/alchemist/loader/m2m/syntax/Syntax.kt b/alchemist-loading/src/main/kotlin/it/unibo/alchemist/loader/m2m/syntax/Syntax.kt index 7d702f9ad5..eef26bba0f 100644 --- a/alchemist-loading/src/main/kotlin/it/unibo/alchemist/loader/m2m/syntax/Syntax.kt +++ b/alchemist-loading/src/main/kotlin/it/unibo/alchemist/loader/m2m/syntax/Syntax.kt @@ -37,22 +37,44 @@ internal object DocumentRoot : SyntaxElement { object Deployment : SyntaxElement { val contents by OwnName() val nodes by OwnName() + val properties by OwnName() val programs by OwnName() override val validDescriptors = setOf( validDescriptor { mandatory(JavaType.type) - optional(JavaType.parameters, contents, nodes, programs) - forbidden(Contents.shapes) + optional(JavaType.parameters, contents, properties, nodes, programs) + forbidden(Filter.filter) } ) + /* + * in: + * - type: FilterType + * parameters: [...] + */ + object Filter : SyntaxElement { + const val filter = "in" + override val validDescriptors = setOf( + validDescriptor { + mandatory(JavaType.type) + optional(JavaType.parameters) + } + ) + } + object Property : SyntaxElement { + override val validDescriptors = setOf( + validDescriptor { + mandatory(JavaType.type) + optional(JavaType.parameters, Filter.filter) + } + ) + } object Contents : SyntaxElement { val molecule by OwnName() val concentration by OwnName() - const val shapes = "in" override val validDescriptors = setOf( validDescriptor { mandatory(molecule, concentration) - optional(shapes) + optional(Filter.filter) } ) } @@ -64,11 +86,11 @@ internal object DocumentRoot : SyntaxElement { override val validDescriptors = setOf( validDescriptor { mandatory(JavaType.type) - optional(JavaType.parameters, conditions, timeDistribution, actions) + optional(JavaType.parameters, Filter.filter, conditions, timeDistribution, actions) }, validDescriptor { mandatory(program) - optional(timeDistribution) + optional(timeDistribution, Filter.filter) } ) } diff --git a/alchemist-loading/src/test/java/it/unibo/alchemist/test/PreventRegressions.java b/alchemist-loading/src/test/java/it/unibo/alchemist/test/PreventRegressions.java index 01138a0144..0c3aa46786 100644 --- a/alchemist-loading/src/test/java/it/unibo/alchemist/test/PreventRegressions.java +++ b/alchemist-loading/src/test/java/it/unibo/alchemist/test/PreventRegressions.java @@ -55,16 +55,16 @@ void testLoadCustomExport() { */ @Test void testLoadAndSerialize() { - final Environment env = LoadAlchemist + final Environment environment = LoadAlchemist .from(ResourceLoader.getResource("testCustomExport.yml")) .getDefault() .getEnvironment(); - assertNotNull(env.getIncarnation()); - final byte[] serialized = SerializationUtils.serialize(env); + assertNotNull(environment.getIncarnation()); + final byte[] serialized = SerializationUtils.serialize(environment); assertNotNull(serialized); - final Object deserialized = SerializationUtils.deserialize(SerializationUtils.serialize(env)); + final Object deserialized = SerializationUtils.deserialize(SerializationUtils.serialize(environment)); assertNotNull(deserialized); - assertEquals(env.getClass(), deserialized.getClass()); + assertEquals(environment.getClass(), deserialized.getClass()); assertNotNull(((Environment) deserialized).getIncarnation()); } } diff --git a/alchemist-loading/src/test/java/it/unibo/alchemist/test/TestCircle.java b/alchemist-loading/src/test/java/it/unibo/alchemist/test/TestCircleFilter.java similarity index 90% rename from alchemist-loading/src/test/java/it/unibo/alchemist/test/TestCircle.java rename to alchemist-loading/src/test/java/it/unibo/alchemist/test/TestCircleFilter.java index 01d386d1ff..ae43e62cb2 100644 --- a/alchemist-loading/src/test/java/it/unibo/alchemist/test/TestCircle.java +++ b/alchemist-loading/src/test/java/it/unibo/alchemist/test/TestCircleFilter.java @@ -7,8 +7,8 @@ */ package it.unibo.alchemist.test; -import it.unibo.alchemist.loader.shapes.Circle; -import it.unibo.alchemist.loader.shapes.Shape; +import it.unibo.alchemist.loader.filters.Circle; +import it.unibo.alchemist.loader.filters.Filter; import it.unibo.alchemist.model.implementations.positions.LatLongPosition; import it.unibo.alchemist.model.interfaces.GeoPosition; import org.junit.jupiter.api.Test; @@ -19,9 +19,9 @@ /** * */ -class TestCircle { +class TestCircleFilter { - private final Shape s = new Circle<>(0, 0, 1); + private final Filter s = new Circle<>(0, 0, 1); /** * diff --git a/alchemist-loading/src/test/java/it/unibo/alchemist/test/TestGrid.java b/alchemist-loading/src/test/java/it/unibo/alchemist/test/TestGrid.java index eaaf845967..4bae838fc0 100644 --- a/alchemist-loading/src/test/java/it/unibo/alchemist/test/TestGrid.java +++ b/alchemist-loading/src/test/java/it/unibo/alchemist/test/TestGrid.java @@ -21,11 +21,11 @@ */ class TestGrid { - private static Continuous2DEnvironment env() { + private static Continuous2DEnvironment environment() { return new Continuous2DEnvironment(SupportedIncarnations.get("protelis").get()); } - private static MersenneTwister rand() { + private static MersenneTwister randomGenerator() { return new MersenneTwister(); } @@ -36,7 +36,7 @@ private static MersenneTwister rand() { @Test void testVerticalLine() { test(9, 1, 9.9); - assertEquals(10L, new Grid(env(), rand(), 0, 0, 1, 10, 1, 1, 0, 0).stream().count()); + assertEquals(10L, new Grid(environment(), randomGenerator(), 0, 0, 1, 10, 1, 1, 0, 0).stream().count()); } /** @@ -76,7 +76,11 @@ void test10x10() { */ @Test void testbug73() { - assertEquals(20L * 20, new Grid(env(), rand(), 0, 0, 20, 20, 1, 1, 0.8, 0.8).stream().distinct().count()); + assertEquals( + 20L * 20, + new Grid(environment(), randomGenerator(), 0, 0, 20, 20, 1, 1, 0.8, 0.8) + .stream().distinct().count() + ); } /** @@ -91,7 +95,7 @@ private void test(final long expected, final double x, final double y) { assertEquals( expected, new Grid( - env(), + environment(), new MersenneTwister(), 0, 0, diff --git a/alchemist-loading/src/test/java/it/unibo/alchemist/test/TestLoadGPSTrace.java b/alchemist-loading/src/test/java/it/unibo/alchemist/test/TestLoadGPSTrace.java index ea9ebf7b0f..8cf7bac5d9 100755 --- a/alchemist-loading/src/test/java/it/unibo/alchemist/test/TestLoadGPSTrace.java +++ b/alchemist-loading/src/test/java/it/unibo/alchemist/test/TestLoadGPSTrace.java @@ -77,9 +77,9 @@ class TestLoadGPSTrace { void testLoadGPSTrace() { final var res = ResourceLoader.getResource("testgps.yml"); assertNotNull(res, "Missing test resource " + "testgps.yml"); - final Environment env = LoadAlchemist.from(res).getDefault().getEnvironment(); - assertTrue(env.getNodeCount() > 0); - env.getNodes().forEach(node -> { + final Environment environment = LoadAlchemist.from(res).getDefault().getEnvironment(); + assertTrue(environment.getNodeCount() > 0); + environment.getNodes().forEach(node -> { final var reactions = node.getReactions(); assertFalse(reactions.isEmpty()); reactions.forEach(reaction -> { @@ -87,19 +87,19 @@ void testLoadGPSTrace() { assertEquals(1, reaction.getActions().size()); }); }); - final Simulation sim = new Engine<>(env, new DoubleTime(TIME_TO_REACH)); + final Simulation sim = new Engine<>(environment, new DoubleTime(TIME_TO_REACH)); sim.addOutputMonitor(new OutputMonitor<>() { @Override public void finished( - @Nonnull final Environment env, + @Nonnull final Environment environment, @Nonnull final Time time, final long step ) { - for (final Node node : env.getNodes()) { + for (final Node node : environment.getNodes()) { final GeoPosition start = Objects.requireNonNull(NODE_START_POSITION.get(node)); final GeoPosition idealArrive = Objects.requireNonNull(START_ARRIVE_POSITION.get(start)); - final GeoPosition realArrive = Objects.requireNonNull(env.getPosition(node)); + final GeoPosition realArrive = Objects.requireNonNull(environment.getPosition(node)); assertEquals( 0.0, idealArrive.distanceTo(realArrive), @@ -111,9 +111,9 @@ public void finished( } @Override - public void initialized(@Nonnull final Environment env) { - for (final Node node : env.getNodes()) { - final GeoPosition position = env.getPosition(node); + public void initialized(@Nonnull final Environment environment) { + for (final Node node : environment.getNodes()) { + final GeoPosition position = environment.getPosition(node); /* * We don't know the actual type of position, we use LatLongPosition here, so we need to make sure * that types match, or the map won't return what we expect @@ -124,7 +124,7 @@ public void initialized(@Nonnull final Environment env) { @Override public void stepDone( - @Nonnull final Environment env, final Reaction r, + @Nonnull final Environment environment, final Reaction r, @Nonnull final Time time, final long step ) { } diff --git a/alchemist-loading/src/test/java/it/unibo/alchemist/test/TestRectangle.java b/alchemist-loading/src/test/java/it/unibo/alchemist/test/TestRectangleFilter.java similarity index 81% rename from alchemist-loading/src/test/java/it/unibo/alchemist/test/TestRectangle.java rename to alchemist-loading/src/test/java/it/unibo/alchemist/test/TestRectangleFilter.java index 8cc44174d4..ab7298cce4 100644 --- a/alchemist-loading/src/test/java/it/unibo/alchemist/test/TestRectangle.java +++ b/alchemist-loading/src/test/java/it/unibo/alchemist/test/TestRectangleFilter.java @@ -7,8 +7,8 @@ */ package it.unibo.alchemist.test; -import it.unibo.alchemist.loader.shapes.Rectangle; -import it.unibo.alchemist.loader.shapes.Shape; +import it.unibo.alchemist.loader.filters.Filter; +import it.unibo.alchemist.loader.filters.Rectangle; import it.unibo.alchemist.model.implementations.positions.Euclidean2DPosition; import it.unibo.alchemist.model.implementations.positions.LatLongPosition; import it.unibo.alchemist.model.interfaces.GeoPosition; @@ -19,14 +19,14 @@ /** * */ -class TestRectangle { +class TestRectangleFilter { /** * */ @Test void test() { - final Shape s = new Rectangle<>(12, 44, 1, 1); + final Filter s = new Rectangle<>(12, 44, 1, 1); // CHECKSTYLE: MagicNumber OFF assertTrue(s.contains(new LatLongPosition(44.132300, 12.233000))); // NOPMD // CHECKSTYLE: MagicNumber ON @@ -40,7 +40,7 @@ void rectangleWithNegativeDimension() { /* * In this rectangle the x should go from 10 to 15 and the y from 45 to 35 */ - final Shape s = new Rectangle<>(15, 45, -5, -10); + final Filter s = new Rectangle<>(15, 45, -5, -10); // CHECKSTYLE: MagicNumber OFF assertTrue(s.contains(new Euclidean2DPosition(12, 40))); // CHECKSTYLE: MagicNumber ON diff --git a/alchemist-loading/src/test/java/it/unibo/alchemist/test/TestYAMLLoader.java b/alchemist-loading/src/test/java/it/unibo/alchemist/test/TestYAMLLoader.java index ccc54d7c11..227546d30c 100644 --- a/alchemist-loading/src/test/java/it/unibo/alchemist/test/TestYAMLLoader.java +++ b/alchemist-loading/src/test/java/it/unibo/alchemist/test/TestYAMLLoader.java @@ -53,8 +53,8 @@ class TestYAMLLoader { */ @Test void testAnyRealDistribution() { - final Environment env = testNoVar("synthetic/anyrealdistribution.yml"); - env.forEach(n -> n.forEach(r -> { + final Environment environment = testNoVar("synthetic/anyrealdistribution.yml"); + environment.forEach(n -> n.forEach(r -> { assertTrue(r.getTimeDistribution() instanceof AnyRealDistribution); })); } @@ -90,8 +90,8 @@ void testISAC2016Lab() { @Test

> void testLayers() { @SuppressWarnings("unchecked") - final Environment env = (Environment) testNoVar("synthetic/testlayer.yml"); - final Set> layers = env.getLayers(); + final Environment environment = (Environment) testNoVar("synthetic/testlayer.yml"); + final Set> layers = environment.getLayers(); assertFalse(layers.isEmpty()); assertEquals(2, layers.size()); assertEquals(2L, layers.stream() @@ -100,11 +100,11 @@

> void testLayers() { final Incarnation inc = SupportedIncarnations.get("sapere").orElseThrow( () -> new IllegalStateException("No SAPERE incarnation available")); final Molecule a = inc.createMolecule("A"); - assertTrue(env.getLayer(a).isPresent()); - assertTrue(env.getLayer(a).get() instanceof StepLayer); + assertTrue(environment.getLayer(a).isPresent()); + assertTrue(environment.getLayer(a).get() instanceof StepLayer); final Molecule b = inc.createMolecule("B"); - assertTrue(env.getLayer(b).isPresent()); - assertTrue(env.getLayer(b).get() instanceof StepLayer); + assertTrue(environment.getLayer(b).isPresent()); + assertTrue(environment.getLayer(b).get() instanceof StepLayer); } /** @@ -120,8 +120,8 @@ void testLoadVariablesInLists() { */ @Test void testMultipleMolecules() { - final Environment env = testNoVar("synthetic/multiplemolecule.yml"); - env.forEach(n -> assertEquals(4, n.getMoleculeCount())); + final Environment environment = testNoVar("synthetic/multiplemolecule.yml"); + environment.forEach(n -> assertEquals(4, n.getMoleculeCount())); } /** @@ -145,9 +145,9 @@ void testVariableContentClash() { */ @Test void testScalaVar() { - final Environment env = testNoVar("synthetic/scalavar.yml"); - assertNotNull(env); - assertEquals(env.makePosition(3, 10), env.getPosition(env.getNodeByID(0))); + final Environment environment = testNoVar("synthetic/scalavar.yml"); + assertNotNull(environment); + assertEquals(environment.makePosition(3, 10), environment.getPosition(environment.getNodeByID(0))); } /** @@ -168,15 +168,16 @@ private static > Environment testLoading( final Map vars ) { assertNotNull(resource, "Missing test resource " + resource); - final Environment env = LoadAlchemist.from(resource, YamlProvider.INSTANCE).getWith(vars).getEnvironment(); - final Simulation sim = new Engine<>(env, 10_000); + final Environment environment = LoadAlchemist.from(resource, YamlProvider.INSTANCE) + .getWith(vars).getEnvironment(); + final Simulation sim = new Engine<>(environment, 10_000); sim.play(); // if (!java.awt.GraphicsEnvironment.isHeadless()) { // it.unibo.alchemist.boundary.gui.SingleRunGUI.make(sim); // } sim.run(); sim.getError().ifPresent(e -> fail(e.getMessage())); - return env; + return environment; } private static Environment testNoVar(final InputStream resource) { diff --git a/alchemist-loading/src/test/java/it/unibo/alchemist/test/util/TestNode.java b/alchemist-loading/src/test/java/it/unibo/alchemist/test/util/TestNode.java index 6803f0a4a0..b4989198d9 100644 --- a/alchemist-loading/src/test/java/it/unibo/alchemist/test/util/TestNode.java +++ b/alchemist-loading/src/test/java/it/unibo/alchemist/test/util/TestNode.java @@ -7,21 +7,21 @@ */ package it.unibo.alchemist.test.util; -import it.unibo.alchemist.model.implementations.nodes.AbstractNode; +import it.unibo.alchemist.model.implementations.nodes.GenericNode; import it.unibo.alchemist.model.interfaces.Environment; /** * Generic node for testing purposes. */ -public final class TestNode extends AbstractNode { +public final class TestNode extends GenericNode { private static final long serialVersionUID = 1L; /** - * @param env the environment + * @param environment the environment */ - public TestNode(final Environment env) { - super(env); + public TestNode(final Environment environment) { + super(environment); } @Override diff --git a/alchemist-loading/src/test/kotlin/it/unibo/alchemist/test/TestCSVExporter.kt b/alchemist-loading/src/test/kotlin/it/unibo/alchemist/test/TestCSVExporter.kt index 740759cdc0..b58a81cbad 100644 --- a/alchemist-loading/src/test/kotlin/it/unibo/alchemist/test/TestCSVExporter.kt +++ b/alchemist-loading/src/test/kotlin/it/unibo/alchemist/test/TestCSVExporter.kt @@ -62,8 +62,8 @@ class TestCSVExporter> : FreeSpec({ }) { /* common utility functions */ companion object { - fun > getCSVExporter(env: InitializedEnvironment): CSVExporter { - val exporter = env.exporters.first() + fun > getCSVExporter(environment: InitializedEnvironment): CSVExporter { + val exporter = environment.exporters.first() require(exporter is CSVExporter) { "Invalid exporter type '${exporter::class.simpleName}'" } diff --git a/alchemist-loading/src/test/kotlin/it/unibo/alchemist/test/TestGraphStreamReproducibility.kt b/alchemist-loading/src/test/kotlin/it/unibo/alchemist/test/TestGraphStreamReproducibility.kt index ce22d29a35..6be66c1c90 100644 --- a/alchemist-loading/src/test/kotlin/it/unibo/alchemist/test/TestGraphStreamReproducibility.kt +++ b/alchemist-loading/src/test/kotlin/it/unibo/alchemist/test/TestGraphStreamReproducibility.kt @@ -14,7 +14,7 @@ import io.kotest.matchers.shouldBe import it.unibo.alchemist.SupportedIncarnations import it.unibo.alchemist.loader.GraphStreamSupport import it.unibo.alchemist.model.implementations.environments.Continuous2DEnvironment -import it.unibo.alchemist.model.implementations.nodes.AbstractNode +import it.unibo.alchemist.model.implementations.nodes.GenericNode import it.unibo.alchemist.model.implementations.positions.Euclidean2DPosition import org.apache.commons.math3.random.MersenneTwister @@ -47,7 +47,7 @@ class TestGraphStreamReproducibility : FreeSpec({ environment.linkingRule = graphStream.linkingRule graphStream.deployment.forEach { environment.addNode( - object : AbstractNode(environment) { override fun createT(): Any = Any() }, + object : GenericNode(environment) { override fun createT(): Any = Any() }, it ) } diff --git a/alchemist-loading/src/test/kotlin/it/unibo/alchemist/test/TestSpecificPositions.kt b/alchemist-loading/src/test/kotlin/it/unibo/alchemist/test/TestSpecificPositions.kt index b3d6757b6b..ba6690f360 100644 --- a/alchemist-loading/src/test/kotlin/it/unibo/alchemist/test/TestSpecificPositions.kt +++ b/alchemist-loading/src/test/kotlin/it/unibo/alchemist/test/TestSpecificPositions.kt @@ -23,11 +23,11 @@ import java.util.stream.Collectors class TestSpecificPositions : StringSpec({ "Test 2D specific positions" { val coordinates = arrayOf(listOf(1.0, 3.0), listOf(15.0, 10.0), listOf(0.0, 20.0)) - val env = Continuous2DEnvironment(incarnation()) - val positions = SpecificPositions(env, *coordinates) + val environment = Continuous2DEnvironment(incarnation()) + val positions = SpecificPositions(environment, *coordinates) .stream() .collect(Collectors.toList()) - positions shouldBe coordinates.map { env.makePosition(*it.toTypedArray()) } + positions shouldBe coordinates.map { environment.makePosition(*it.toTypedArray()) } } "Wrong number of coordinates should fail" { shouldThrow { @@ -36,8 +36,8 @@ class TestSpecificPositions : StringSpec({ } "Test YAML loading with 2D env" { val loader = LoadAlchemist.from(ResourceLoader.getResource("testSpecificPositions.yml")) - val env = loader.getWith(emptyMap()).environment - env.nodes.map { env.getPosition(it) } shouldBe + val environment = loader.getWith(emptyMap()).environment + environment.nodes.map { environment.getPosition(it) } shouldBe listOf(Euclidean2DPosition(1.0, 2.0), Euclidean2DPosition(3.0, 4.0)) } }) { diff --git a/alchemist-loading/src/test/resources/testSpecificPositions.yml b/alchemist-loading/src/test/resources/testSpecificPositions.yml index 6c14a12f41..36d84fadfc 100644 --- a/alchemist-loading/src/test/resources/testSpecificPositions.yml +++ b/alchemist-loading/src/test/resources/testSpecificPositions.yml @@ -7,6 +7,6 @@ environment: deployments: type: SpecificPositions parameters: [[1,2],[3,4]] - nodes: - type: CircleNode - parameters: [1] + properties: + - type: CircularArea + parameters: [1] diff --git a/alchemist-maps/src/test/java/it/unibo/alchemist/test/TestTargetMapWalker.java b/alchemist-maps/src/test/java/it/unibo/alchemist/test/TestTargetMapWalker.java index b4ffd7d34c..053b66cdc2 100644 --- a/alchemist-maps/src/test/java/it/unibo/alchemist/test/TestTargetMapWalker.java +++ b/alchemist-maps/src/test/java/it/unibo/alchemist/test/TestTargetMapWalker.java @@ -14,7 +14,6 @@ import it.unibo.alchemist.model.implementations.environments.OSMEnvironment; import it.unibo.alchemist.model.implementations.linkingrules.NoLinks; import it.unibo.alchemist.model.implementations.molecules.SimpleMolecule; -import it.unibo.alchemist.model.implementations.nodes.AbstractNode; import it.unibo.alchemist.model.implementations.positions.LatLongPosition; import it.unibo.alchemist.model.implementations.reactions.Event; import it.unibo.alchemist.model.implementations.timedistributions.DiracComb; @@ -24,6 +23,7 @@ import it.unibo.alchemist.model.interfaces.Molecule; import it.unibo.alchemist.model.interfaces.Node; import it.unibo.alchemist.model.interfaces.Reaction; +import org.apache.commons.math3.random.MersenneTwister; import org.junit.jupiter.api.BeforeEach; import org.junit.jupiter.api.Test; @@ -58,7 +58,7 @@ class TestTargetMapWalker { * Near Montefiore */ private static final GeoPosition ENDPOSITION = new LatLongPosition(ENDLAT, ENDLON); - private MapEnvironment env; + private MapEnvironment environment; private Node node; private Reaction reaction; @@ -71,19 +71,13 @@ class TestTargetMapWalker { @BeforeEach public void setUp() throws ClassNotFoundException, IOException { try { - env = new OSMEnvironment<>(INCARNATION, TESTMAP, true, true); - env.setLinkingRule(new NoLinks<>()); - node = new AbstractNode<>(env) { - private static final long serialVersionUID = -3982001064673078159L; - @Override - protected Object createT() { - return null; - } - }; + environment = new OSMEnvironment<>(INCARNATION, TESTMAP, true, true); + environment.setLinkingRule(new NoLinks<>()); + node = INCARNATION.createNode(new MersenneTwister(), environment, null); reaction = new Event<>(node, new DiracComb<>(1)); - reaction.setActions(Lists.newArrayList(new TargetMapWalker<>(env, node, reaction, TRACK, INTERACTING))); + reaction.setActions(Lists.newArrayList(new TargetMapWalker<>(environment, node, reaction, TRACK, INTERACTING))); node.addReaction(reaction); - env.addNode(node, STARTPOSITION); + environment.addNode(node, STARTPOSITION); } catch (IllegalStateException e) { e.printStackTrace(); // NOPMD fail(e.getMessage()); @@ -93,7 +87,7 @@ protected Object createT() { private void run() { IntStream.range(0, STEPS).forEach(i -> { reaction.execute(); - reaction.update(reaction.getTau(), true, env); + reaction.update(reaction.getTau(), true, environment); }); } @@ -102,7 +96,7 @@ private void run() { */ @Test void testNoPosition() { - final GeoPosition start = env.getPosition(node); + final GeoPosition start = environment.getPosition(node); /* * Should not be more than 10 meters afar the suggested start */ @@ -111,7 +105,7 @@ void testNoPosition() { /* * Node should not move at all */ - assertEquals(start, env.getPosition(node)); + assertEquals(start, environment.getPosition(node)); } /** @@ -119,7 +113,7 @@ void testNoPosition() { */ @Test void testPosition() { - final GeoPosition start = env.getPosition(node); + final GeoPosition start = environment.getPosition(node); /* * Should not be more than 10 meters afar the suggested start */ @@ -129,7 +123,7 @@ void testPosition() { /* * Node should get to the final position */ - assertEquals(ENDPOSITION, env.getPosition(node)); + assertEquals(ENDPOSITION, environment.getPosition(node)); } /** @@ -137,7 +131,7 @@ void testPosition() { */ @Test void testIterableDouble() { - final GeoPosition start = env.getPosition(node); + final GeoPosition start = environment.getPosition(node); /* * Should not be more than 10 meters afar the suggested start */ @@ -147,7 +141,7 @@ void testIterableDouble() { /* * Node should get to the final position */ - assertEquals(ENDPOSITION, env.getPosition(node)); + assertEquals(ENDPOSITION, environment.getPosition(node)); } /** @@ -155,7 +149,7 @@ void testIterableDouble() { */ @Test void testIterableStrings() { - final GeoPosition start = env.getPosition(node); + final GeoPosition start = environment.getPosition(node); assertNotNull(start); /* * Should not be more than 10 meters afar the suggested start @@ -166,7 +160,7 @@ void testIterableStrings() { /* * Node should get to the final position */ - assertEquals(ENDPOSITION, env.getPosition(node)); + assertEquals(ENDPOSITION, environment.getPosition(node)); } /** @@ -174,7 +168,7 @@ void testIterableStrings() { */ @Test void testStrings01() { - final GeoPosition start = env.getPosition(node); + final GeoPosition start = environment.getPosition(node); /* * Should not be more than 10 meters afar the suggested start */ @@ -184,7 +178,7 @@ void testStrings01() { /* * Node should get to the final position */ - assertEquals(ENDPOSITION, env.getPosition(node)); + assertEquals(ENDPOSITION, environment.getPosition(node)); } /** @@ -192,7 +186,7 @@ void testStrings01() { */ @Test void testStrings02() { - final GeoPosition start = env.getPosition(node); + final GeoPosition start = environment.getPosition(node); /* * Should not be more than 10 meters afar the suggested start */ @@ -202,7 +196,7 @@ void testStrings02() { /* * Node should get to the final position */ - assertEquals(ENDPOSITION, env.getPosition(node)); + assertEquals(ENDPOSITION, environment.getPosition(node)); } /** @@ -210,7 +204,7 @@ void testStrings02() { */ @Test void testStrings03() { - final GeoPosition start = env.getPosition(node); + final GeoPosition start = environment.getPosition(node); /* * Should not be more than 10 meters afar the suggested start */ @@ -220,7 +214,7 @@ void testStrings03() { /* * Node should get to the final position */ - assertEquals(ENDPOSITION, env.getPosition(node)); + assertEquals(ENDPOSITION, environment.getPosition(node)); } /** @@ -228,7 +222,7 @@ void testStrings03() { */ @Test void testStrings04() { - final GeoPosition start = env.getPosition(node); + final GeoPosition start = environment.getPosition(node); /* * Should not be more than 10 meters afar the suggested start */ @@ -238,7 +232,7 @@ void testStrings04() { /* * Node should get to the final position */ - assertEquals(ENDPOSITION, env.getPosition(node)); + assertEquals(ENDPOSITION, environment.getPosition(node)); } diff --git a/alchemist-maps/src/test/kotlin/it/unibo/alchemist/test/TestClosestNOnMaps.kt b/alchemist-maps/src/test/kotlin/it/unibo/alchemist/test/TestClosestNOnMaps.kt index e9841fba10..39822d14ff 100644 --- a/alchemist-maps/src/test/kotlin/it/unibo/alchemist/test/TestClosestNOnMaps.kt +++ b/alchemist-maps/src/test/kotlin/it/unibo/alchemist/test/TestClosestNOnMaps.kt @@ -3,7 +3,7 @@ import io.kotest.core.spec.style.StringSpec import it.unibo.alchemist.SupportedIncarnations import it.unibo.alchemist.model.implementations.environments.OSMEnvironment import it.unibo.alchemist.model.implementations.linkingrules.ClosestN -import it.unibo.alchemist.model.implementations.nodes.AbstractNode +import it.unibo.alchemist.model.implementations.nodes.GenericNode import it.unibo.alchemist.model.interfaces.GeoPosition class TestClosestNOnMaps : StringSpec({ @@ -14,7 +14,7 @@ class TestClosestNOnMaps : StringSpec({ ) environment.linkingRule = ClosestN(10) environment.addNode( - object : AbstractNode(environment) { + object : GenericNode(environment) { override fun createT() = "Nothing" }, environment.makePosition(44.139169, 12.237816) diff --git a/alchemist-physical-agents/src/main/kotlin/it/unibo/alchemist/model/implementations/actions/physicalstrategies/Sum.kt b/alchemist-physical-agents/src/main/kotlin/it/unibo/alchemist/model/implementations/actions/physicalstrategies/Sum.kt index 846f646610..68078ce75c 100644 --- a/alchemist-physical-agents/src/main/kotlin/it/unibo/alchemist/model/implementations/actions/physicalstrategies/Sum.kt +++ b/alchemist-physical-agents/src/main/kotlin/it/unibo/alchemist/model/implementations/actions/physicalstrategies/Sum.kt @@ -10,23 +10,25 @@ package it.unibo.alchemist.model.implementations.actions.physicalstrategies import it.unibo.alchemist.model.implementations.positions.Euclidean2DPosition -import it.unibo.alchemist.model.interfaces.PhysicalPedestrian2D +import it.unibo.alchemist.model.interfaces.Node import it.unibo.alchemist.model.interfaces.PhysicalSteeringStrategy import it.unibo.alchemist.model.interfaces.SteeringStrategy import it.unibo.alchemist.model.interfaces.environments.Physics2DEnvironment import it.unibo.alchemist.model.interfaces.geometry.euclidean2d.Euclidean2DShapeFactory import it.unibo.alchemist.model.interfaces.geometry.euclidean2d.Euclidean2DTransformation +import it.unibo.alchemist.model.interfaces.properties.PhysicalPedestrian2DProperty +import it.unibo.alchemist.model.interfaces.Node.Companion.asProperty /** * A [PhysicalSteeringStrategy] performing a simple sum of the overall intentional force and the physical ones. */ class Sum( private val environment: Physics2DEnvironment, - override val node: PhysicalPedestrian2D, + override val node: Node, override val nonPhysicalStrategy: SteeringStrategy ) : PhysicalSteeringStrategy { override fun computeNextPosition(overallIntentionalForce: Euclidean2DPosition): Euclidean2DPosition = - (node.physicalForces(environment) + overallIntentionalForce) + (node.asProperty>().physicalForces(environment) + overallIntentionalForce) .reduce { acc, p -> acc + p } } diff --git a/alchemist-physical-agents/src/main/kotlin/it/unibo/alchemist/model/implementations/nodes/CognitiveOrientingPhysicalPedestrian2D.kt b/alchemist-physical-agents/src/main/kotlin/it/unibo/alchemist/model/implementations/nodes/CognitiveOrientingPhysicalPedestrian2D.kt deleted file mode 100644 index 045555f06c..0000000000 --- a/alchemist-physical-agents/src/main/kotlin/it/unibo/alchemist/model/implementations/nodes/CognitiveOrientingPhysicalPedestrian2D.kt +++ /dev/null @@ -1,124 +0,0 @@ -/* - * Copyright (C) 2010-2020, Danilo Pianini and contributors - * listed in the main project's alchemist/build.gradle.kts file. - * - * This file is part of Alchemist, and is distributed under the terms of the - * GNU General Public License, with a linking exception, - * as described in the file LICENSE in the Alchemist distribution's top directory. - */ - -package it.unibo.alchemist.model.implementations.nodes - -import it.unibo.alchemist.model.implementations.geometry.euclidean2d.Ellipse -import it.unibo.alchemist.model.implementations.geometry.euclidean2d.FieldOfView2D -import it.unibo.alchemist.model.implementations.positions.Euclidean2DPosition -import it.unibo.alchemist.model.interfaces.Incarnation -import it.unibo.alchemist.model.interfaces.Molecule -import it.unibo.alchemist.model.interfaces.Node -import it.unibo.alchemist.model.interfaces.OrientingPedestrian2D -import it.unibo.alchemist.model.interfaces.PedestrianGroup -import it.unibo.alchemist.model.interfaces.PedestrianGroup2D -import it.unibo.alchemist.model.interfaces.Reaction -import it.unibo.alchemist.model.interfaces.Time -import it.unibo.alchemist.model.interfaces.environments.EuclideanPhysics2DEnvironmentWithGraph -import it.unibo.alchemist.model.interfaces.geometry.euclidean2d.ConvexPolygon -import it.unibo.alchemist.model.interfaces.geometry.euclidean2d.Euclidean2DShape -import it.unibo.alchemist.model.interfaces.geometry.euclidean2d.Euclidean2DTransformation -import org.apache.commons.math3.random.RandomGenerator -import org.jgrapht.graph.DefaultEdge - -/** - * A cognitive [OrientingPedestrian2D] capable of physical interactions. - * TODO(rename it into something like "SmartPedestrian2D"?) - */ -class CognitiveOrientingPhysicalPedestrian2D @JvmOverloads constructor( - incarnation: Incarnation, - randomGenerator: RandomGenerator, - environment: EuclideanPhysics2DEnvironmentWithGraph<*, T, N, E>, - nodeCreationParameter: String? = null, - override val knowledgeDegree: Double, - group: PedestrianGroup2D? = null, - age: String, - gender: String, - danger: Molecule? = null -) : CognitivePhysicalPedestrian2D( - incarnation, - randomGenerator, - environment, - nodeCreationParameter, - age, - gender, - danger, - group, -), - OrientingPedestrian2D by HomogeneousOrientingPedestrian2D( - incarnation, - randomGenerator, - environment, - nodeCreationParameter, - knowledgeDegree, - ) { - - override val fieldOfView: FieldOfView2D - get() = super.fieldOfView - - override val membershipGroup: PedestrianGroup - get() = super.membershipGroup - - override val shape: Euclidean2DShape - get() = super.shape - - override fun addReaction(r: Reaction?) { - super.addReaction(r) - } - - override fun cloneNode(currentTime: Time?): Node = TODO() - - override fun compareTo(other: Node?): Int { - return super.compareTo(other) - } - - override fun contains(mol: Molecule?): Boolean { - return super.contains(mol) - } - - override fun getConcentration(mol: Molecule?): T { - return super.getConcentration(mol) - } - - override fun getContents(): MutableMap { - return super.getContents() - } - - override fun getId(): Int { - return super.getId() - } - - override fun getMoleculeCount(): Int { - return super.getMoleculeCount() - } - - override fun getReactions(): MutableList> { - return super.getReactions() - } - - override fun iterator(): MutableIterator> { - return super.iterator() - } - - override fun removeConcentration(mol: Molecule?) { - super.removeConcentration(mol) - } - - override fun removeReaction(r: Reaction?) { - super.removeReaction(r) - } - - override fun setConcentration(mol: Molecule?, c: T) { - super.setConcentration(mol, c) - } - - override fun speed(): Double { - return super.speed() - } -} diff --git a/alchemist-physical-agents/src/main/kotlin/it/unibo/alchemist/model/implementations/nodes/CognitivePhysicalPedestrian2D.kt b/alchemist-physical-agents/src/main/kotlin/it/unibo/alchemist/model/implementations/nodes/CognitivePhysicalPedestrian2D.kt deleted file mode 100644 index 4f9c682101..0000000000 --- a/alchemist-physical-agents/src/main/kotlin/it/unibo/alchemist/model/implementations/nodes/CognitivePhysicalPedestrian2D.kt +++ /dev/null @@ -1,72 +0,0 @@ -/* - * Copyright (C) 2010-2020, Danilo Pianini and contributors - * listed in the main project's alchemist/build.gradle.kts file. - * - * This file is part of Alchemist, and is distributed under the terms of the - * GNU General Public License, with a linking exception, - * as described in the file LICENSE in the Alchemist distribution's top directory. - */ - -package it.unibo.alchemist.model.implementations.nodes - -import it.unibo.alchemist.model.implementations.positions.Euclidean2DPosition -import it.unibo.alchemist.model.interfaces.Incarnation -import it.unibo.alchemist.model.interfaces.Molecule -import it.unibo.alchemist.model.interfaces.PedestrianGroup2D -import it.unibo.alchemist.model.interfaces.PhysicalPedestrian2D -import it.unibo.alchemist.model.interfaces.environments.Physics2DEnvironment -import it.unibo.alchemist.nextDouble -import org.apache.commons.math3.random.RandomGenerator - -/** - * A cognitive pedestrian capable of physical interactions, modeled as a [PhysicalPedestrian2D]. [comfortRay] changes - * dynamically depending on whether the pedestrian wants to evacuate or not. - */ -open class CognitivePhysicalPedestrian2D @JvmOverloads constructor( - incarnation: Incarnation, - randomGenerator: RandomGenerator, - environment: Physics2DEnvironment, - nodeCreationParameter: String? = null, - age: String, - gender: String, - danger: Molecule? = null, - group: PedestrianGroup2D? = null -) : CognitivePedestrian2D( - incarnation = incarnation, - randomGenerator = randomGenerator, - environment = environment, - nodeCreationParameter = nodeCreationParameter, - age = age, - gender = gender, - danger = danger, - group = group, -), - PhysicalPedestrian2D { - - /* - * According to [the work of Pelechano et al](https://bit.ly/3e3C7Tb) in order to bring out - * pushing behavior different nodes must have different personal space threshold. - */ - private val desiredSpaceTreshold: Double = randomGenerator.nextDouble(minimumSpaceTreshold, maximumSpaceThreshold) - - override val comfortRay: Double get() = - if (cognitiveModel.wantsToEscape()) { - desiredSpaceTreshold / 3 - } else { - desiredSpaceTreshold - } - - override val comfortArea = super.comfortArea - get() = environment.getPosition(this).let { field.transformed { origin(it) } } - - companion object { - /** - * Mimimum value for normal state [comfortRay]. - */ - const val minimumSpaceTreshold = 0.1 - /** - * Maximum value for normal state [comfortRay]. - */ - const val maximumSpaceThreshold = 1.0 - } -} diff --git a/alchemist-physical-agents/src/main/kotlin/it/unibo/alchemist/model/implementations/nodes/HomogeneousOrientingPhysicalPedestrian2D.kt b/alchemist-physical-agents/src/main/kotlin/it/unibo/alchemist/model/implementations/nodes/HomogeneousOrientingPhysicalPedestrian2D.kt deleted file mode 100644 index 626f93999b..0000000000 --- a/alchemist-physical-agents/src/main/kotlin/it/unibo/alchemist/model/implementations/nodes/HomogeneousOrientingPhysicalPedestrian2D.kt +++ /dev/null @@ -1,113 +0,0 @@ -/* - * Copyright (C) 2010-2020, Danilo Pianini and contributors - * listed in the main project's alchemist/build.gradle.kts file. - * - * This file is part of Alchemist, and is distributed under the terms of the - * GNU General Public License, with a linking exception, - * as described in the file LICENSE in the Alchemist distribution's top directory. - */ - -package it.unibo.alchemist.model.implementations.nodes - -import it.unibo.alchemist.model.implementations.geometry.euclidean2d.Ellipse -import it.unibo.alchemist.model.implementations.geometry.euclidean2d.FieldOfView2D -import it.unibo.alchemist.model.implementations.positions.Euclidean2DPosition -import it.unibo.alchemist.model.interfaces.Incarnation -import it.unibo.alchemist.model.interfaces.Molecule -import it.unibo.alchemist.model.interfaces.Node -import it.unibo.alchemist.model.interfaces.OrientingPedestrian2D -import it.unibo.alchemist.model.interfaces.PedestrianGroup -import it.unibo.alchemist.model.interfaces.PedestrianGroup2D -import it.unibo.alchemist.model.interfaces.Reaction -import it.unibo.alchemist.model.interfaces.Time -import it.unibo.alchemist.model.interfaces.environments.EuclideanPhysics2DEnvironmentWithGraph -import it.unibo.alchemist.model.interfaces.geometry.GeometricShape -import it.unibo.alchemist.model.interfaces.geometry.euclidean2d.ConvexPolygon -import it.unibo.alchemist.model.interfaces.geometry.euclidean2d.Euclidean2DTransformation -import org.apache.commons.math3.random.RandomGenerator -import org.jgrapht.graph.DefaultEdge - -/** - * A homogeneous [OrientingPedestrian2D] capable of physical interactions. - */ -@Suppress("PARAMETER_NAME_CHANGED_ON_OVERRIDE") -class HomogeneousOrientingPhysicalPedestrian2D @JvmOverloads constructor( - incarnation: Incarnation, - randomGenerator: RandomGenerator, - environment: EuclideanPhysics2DEnvironmentWithGraph<*, T, N, E>, - nodeCreationParameter: String? = null, - knowledgeDegree: Double, - group: PedestrianGroup2D? = null -) : HomogeneousPhysicalPedestrian2D(incarnation, randomGenerator, environment, nodeCreationParameter, group), - OrientingPedestrian2D by HomogeneousOrientingPedestrian2D( - incarnation = incarnation, - randomGenerator = randomGenerator, - environment = environment, - nodeCreationParameter = nodeCreationParameter, - knowledgeDegree = knowledgeDegree, - group = group, - ) { - - override val fieldOfView: FieldOfView2D - get() = super.fieldOfView - - override val membershipGroup: PedestrianGroup - get() = super.membershipGroup - - override fun addReaction(reaction: Reaction?) { - super.addReaction(reaction) - } - - override fun cloneNode(currentTime: Time?): Node = TODO() - - override fun compareTo(other: Node?): Int { - return super.compareTo(other) - } - - override fun contains(molecule: Molecule?): Boolean { - return super.contains(molecule) - } - - override fun getConcentration(molecule: Molecule?): T { - return super.getConcentration(molecule) - } - - override fun getContents(): MutableMap { - return super.getContents() - } - - override fun getId(): Int { - return super.getId() - } - - override fun getMoleculeCount(): Int { - return super.getMoleculeCount() - } - - override fun getReactions(): MutableList> { - return super.getReactions() - } - - override fun iterator(): MutableIterator> { - return super.iterator() - } - - override fun removeConcentration(mol: Molecule?) { - super.removeConcentration(mol) - } - - override fun removeReaction(r: Reaction?) { - super.removeReaction(r) - } - - override fun setConcentration(mol: Molecule?, c: T) { - super.setConcentration(mol, c) - } - - override fun speed(): Double { - return super.speed() - } - - override val shape: GeometricShape - get() = super.shape -} diff --git a/alchemist-physical-agents/src/main/kotlin/it/unibo/alchemist/model/implementations/nodes/HomogeneousPhysicalPedestrian2D.kt b/alchemist-physical-agents/src/main/kotlin/it/unibo/alchemist/model/implementations/nodes/HomogeneousPhysicalPedestrian2D.kt deleted file mode 100644 index 608b8685e9..0000000000 --- a/alchemist-physical-agents/src/main/kotlin/it/unibo/alchemist/model/implementations/nodes/HomogeneousPhysicalPedestrian2D.kt +++ /dev/null @@ -1,40 +0,0 @@ -/* - * Copyright (C) 2010-2020, Danilo Pianini and contributors - * listed in the main project's alchemist/build.gradle.kts file. - * - * This file is part of Alchemist, and is distributed under the terms of the - * GNU General Public License, with a linking exception, - * as described in the file LICENSE in the Alchemist distribution's top directory. - */ - -package it.unibo.alchemist.model.implementations.nodes - -import it.unibo.alchemist.model.implementations.positions.Euclidean2DPosition -import it.unibo.alchemist.model.interfaces.Incarnation -import it.unibo.alchemist.model.interfaces.PhysicalPedestrian2D -import it.unibo.alchemist.model.interfaces.PedestrianGroup2D -import it.unibo.alchemist.model.interfaces.environments.Physics2DEnvironment -import org.apache.commons.math3.random.RandomGenerator - -/** - * A homogeneous pedestrian capable of physical interactions, modeled as a [PhysicalPedestrian2D]. [comfortRay] is - * statically defined to be equal to its [shape] radius. - */ -open class HomogeneousPhysicalPedestrian2D @JvmOverloads constructor( - incarnation: Incarnation, - randomGenerator: RandomGenerator, - environment: Physics2DEnvironment, - nodeCreationParameter: String? = null, - group: PedestrianGroup2D? = null -) : - PhysicalPedestrian2D, - HomogeneousPedestrian2D( - incarnation, - randomGenerator, - environment, - nodeCreationParameter, - group - ) { - - override val comfortRay: Double = super.shape.radius -} diff --git a/alchemist-physical-agents/src/main/kotlin/it/unibo/alchemist/model/implementations/properties/PhysicalPedestrian.kt b/alchemist-physical-agents/src/main/kotlin/it/unibo/alchemist/model/implementations/properties/PhysicalPedestrian.kt new file mode 100644 index 0000000000..8fe2a3d9fa --- /dev/null +++ b/alchemist-physical-agents/src/main/kotlin/it/unibo/alchemist/model/implementations/properties/PhysicalPedestrian.kt @@ -0,0 +1,93 @@ +/* + * Copyright (C) 2010-2022, Danilo Pianini and contributors + * listed, for each module, in the respective subproject's build.gradle.kts file. + * + * This file is part of Alchemist, and is distributed under the terms of the + * GNU General Public License, with a linking exception, + * as described in the file LICENSE in the Alchemist distribution's top directory. + */ + +package it.unibo.alchemist.model.implementations.properties + +import it.unibo.alchemist.model.interfaces.Node +import it.unibo.alchemist.model.interfaces.Position +import it.unibo.alchemist.model.interfaces.properties.CognitiveProperty +import it.unibo.alchemist.model.interfaces.properties.PhysicalPedestrian +import it.unibo.alchemist.model.interfaces.properties.OccupiesSpaceProperty +import it.unibo.alchemist.model.interfaces.environments.PhysicsEnvironment +import it.unibo.alchemist.model.interfaces.geometry.GeometricShape +import it.unibo.alchemist.model.interfaces.geometry.GeometricShapeFactory +import it.unibo.alchemist.model.interfaces.geometry.GeometricTransformation +import it.unibo.alchemist.model.interfaces.geometry.Vector +import it.unibo.alchemist.nextDouble +import org.apache.commons.math3.random.RandomGenerator +import it.unibo.alchemist.model.interfaces.Node.Companion.asProperty +import it.unibo.alchemist.model.interfaces.Node.Companion.asPropertyOrNull + +/** + * Base implementation of a pedestrian's capability to experience physical interactions. + */ +class PhysicalPedestrian( + /** + * The simulation's [RandomGenerator]. + */ + private val randomGenerator: RandomGenerator, + /** + * The environment in which the pedestrian is moving. + */ + val environment: PhysicsEnvironment, + override val node: Node, +) : PhysicalPedestrian + where P : Position

, P : Vector

, + A : GeometricTransformation

, + F : GeometricShapeFactory { + + private val desiredSpaceTreshold: Double = randomGenerator.nextDouble(minimumSpaceTreshold, maximumSpaceThreshold) + + override val comfortRay: Double get() { + val cognitiveModel = node.asPropertyOrNull>()?.cognitiveModel + return if (cognitiveModel?.wantsToEscape() == true) { + desiredSpaceTreshold / 3 + } else { + desiredSpaceTreshold + } + } + + override fun repulsionForce(other: Node): P { + val myShape = node.asProperty>().shape + val otherShape = other.asProperty>().shape + return (myShape.centroid - otherShape.centroid).let { + val desiredDistance = myShape.radius + comfortRay + otherShape.radius + /* + * it is the vector leading from the center of other to the center of this node, it.magnitude is the + * actual distance between the two nodes. + */ + it.normalized() * (desiredDistance - it.magnitude).coerceAtLeast(0.0) / it.magnitude + } + } + + override fun physicalForces(environment: PhysicsEnvironment) = + environment.getNodesWithin(comfortArea) + .minusElement(node) + .filter { it.asPropertyOrNull>() != null } + .map { repulsionForce(it) } + /* + * Discard infinitesimal forces. + */ + .filter { it.magnitude > Double.MIN_VALUE } + + companion object { + /** + * Minimum value for normal state [comfortRay]. + */ + private const val minimumSpaceTreshold = 0.1 + /** + * Maximum value for normal state [comfortRay]. + */ + private const val maximumSpaceThreshold = 1.0 + } + + override val comfortArea: GeometricShape get() = environment.shapeFactory.adimensional() + + override fun cloneOnNewNode(node: Node) = PhysicalPedestrian(randomGenerator, environment, node) +} diff --git a/alchemist-physical-agents/src/main/kotlin/it/unibo/alchemist/model/implementations/properties/PhysicalPedestrian2D.kt b/alchemist-physical-agents/src/main/kotlin/it/unibo/alchemist/model/implementations/properties/PhysicalPedestrian2D.kt new file mode 100644 index 0000000000..0889aa79a4 --- /dev/null +++ b/alchemist-physical-agents/src/main/kotlin/it/unibo/alchemist/model/implementations/properties/PhysicalPedestrian2D.kt @@ -0,0 +1,39 @@ +/* + * Copyright (C) 2010-2022, Danilo Pianini and contributors + * listed, for each module, in the respective subproject's build.gradle.kts file. + * + * This file is part of Alchemist, and is distributed under the terms of the + * GNU General Public License, with a linking exception, + * as described in the file LICENSE in the Alchemist distribution's top directory. + */ + +package it.unibo.alchemist.model.implementations.properties + +import it.unibo.alchemist.model.implementations.positions.Euclidean2DPosition +import it.unibo.alchemist.model.interfaces.Node +import it.unibo.alchemist.model.interfaces.Node.Companion.asProperty +import it.unibo.alchemist.model.interfaces.properties.PhysicalPedestrian2DProperty +import it.unibo.alchemist.model.interfaces.properties.PhysicalPedestrian +import it.unibo.alchemist.model.interfaces.properties.AreaProperty +import it.unibo.alchemist.model.interfaces.environments.Physics2DEnvironment +import it.unibo.alchemist.model.interfaces.geometry.euclidean2d.Euclidean2DShape +import it.unibo.alchemist.model.interfaces.geometry.euclidean2d.Euclidean2DShapeFactory +import it.unibo.alchemist.model.interfaces.geometry.euclidean2d.Euclidean2DTransformation +import org.apache.commons.math3.random.RandomGenerator + +/** + * Base implementation of a pedestrian's capability to experience physical interactions in a 2D space. + */ +class PhysicalPedestrian2D( + randomGenerator: RandomGenerator, + /** + * The environment in which the node is moving. + */ + val environment: Physics2DEnvironment, + node: Node, +) : PhysicalPedestrian by +PhysicalPedestrian(randomGenerator, environment, node), + PhysicalPedestrian2DProperty { + override val comfortArea: Euclidean2DShape get() = environment + .shapeFactory.circle(node.asProperty>().shape.radius + comfortRay) +} diff --git a/alchemist-physical-agents/src/main/kotlin/it/unibo/alchemist/model/implementations/reactions/BlendedSteeringWithPhysics.kt b/alchemist-physical-agents/src/main/kotlin/it/unibo/alchemist/model/implementations/reactions/BlendedSteeringWithPhysics.kt index 5d799fbaf7..ffc53cc845 100644 --- a/alchemist-physical-agents/src/main/kotlin/it/unibo/alchemist/model/implementations/reactions/BlendedSteeringWithPhysics.kt +++ b/alchemist-physical-agents/src/main/kotlin/it/unibo/alchemist/model/implementations/reactions/BlendedSteeringWithPhysics.kt @@ -11,7 +11,7 @@ package it.unibo.alchemist.model.implementations.reactions import it.unibo.alchemist.model.implementations.actions.physicalstrategies.Sum import it.unibo.alchemist.model.implementations.positions.Euclidean2DPosition -import it.unibo.alchemist.model.interfaces.PhysicalPedestrian2D +import it.unibo.alchemist.model.interfaces.Node import it.unibo.alchemist.model.interfaces.SteeringStrategy import it.unibo.alchemist.model.interfaces.TimeDistribution import it.unibo.alchemist.model.interfaces.environments.EuclideanPhysics2DEnvironmentWithGraph @@ -22,10 +22,10 @@ import it.unibo.alchemist.model.interfaces.environments.EuclideanPhysics2DEnviro */ class BlendedSteeringWithPhysics( environment: EuclideanPhysics2DEnvironmentWithGraph<*, T, *, *>, - pedestrian: PhysicalPedestrian2D, + node: Node, timeDistribution: TimeDistribution -) : BlendedSteering(environment, pedestrian, timeDistribution) { +) : BlendedSteering(environment, node, timeDistribution) { override val steerStrategy: SteeringStrategy = - Sum(environment, pedestrian, super.steerStrategy) + Sum(environment, node, super.steerStrategy) } diff --git a/alchemist-physical-agents/src/main/kotlin/it/unibo/alchemist/model/implementations/reactions/NavigationPrioritisedSteeringWithPhysics.kt b/alchemist-physical-agents/src/main/kotlin/it/unibo/alchemist/model/implementations/reactions/NavigationPrioritisedSteeringWithPhysics.kt index 0eec1bef66..bfbe0a9b2d 100644 --- a/alchemist-physical-agents/src/main/kotlin/it/unibo/alchemist/model/implementations/reactions/NavigationPrioritisedSteeringWithPhysics.kt +++ b/alchemist-physical-agents/src/main/kotlin/it/unibo/alchemist/model/implementations/reactions/NavigationPrioritisedSteeringWithPhysics.kt @@ -12,7 +12,7 @@ package it.unibo.alchemist.model.implementations.reactions import it.unibo.alchemist.model.implementations.actions.physicalstrategies.Sum import it.unibo.alchemist.model.implementations.actions.steeringstrategies.SinglePrevalent import it.unibo.alchemist.model.implementations.positions.Euclidean2DPosition -import it.unibo.alchemist.model.interfaces.PhysicalPedestrian2D +import it.unibo.alchemist.model.interfaces.Node import it.unibo.alchemist.model.interfaces.SteeringStrategy import it.unibo.alchemist.model.interfaces.TimeDistribution import it.unibo.alchemist.model.interfaces.environments.EuclideanPhysics2DEnvironmentWithGraph @@ -23,8 +23,8 @@ import it.unibo.alchemist.model.interfaces.geometry.euclidean2d.ConvexPolygon * [Sum] strategy is used to combine steering actions and physical forces. */ class NavigationPrioritisedSteeringWithPhysics @JvmOverloads constructor( - env: EuclideanPhysics2DEnvironmentWithGraph<*, T, N, *>, - pedestrian: PhysicalPedestrian2D, + environment: EuclideanPhysics2DEnvironmentWithGraph<*, T, N, *>, + node: Node, timeDistribution: TimeDistribution, /** * Tolerance angle in degrees (see [SinglePrevalent]). @@ -34,7 +34,8 @@ class NavigationPrioritisedSteeringWithPhysics @JvmOverloa * Alpha value for exponential smoothing (see [SinglePrevalent]). */ alpha: Double = SinglePrevalent.DEFAULT_ALPHA -) : NavigationPrioritisedSteering(env, pedestrian, timeDistribution, toleranceAngle, alpha) { +) : NavigationPrioritisedSteering(environment, node, timeDistribution, toleranceAngle, alpha) { - override val steerStrategy: SteeringStrategy = Sum(env, pedestrian, super.steerStrategy) + override val steerStrategy: SteeringStrategy = + Sum(environment, node, super.steerStrategy) } diff --git a/alchemist-physical-agents/src/main/kotlin/it/unibo/alchemist/model/interfaces/PhysicalPedestrian.kt b/alchemist-physical-agents/src/main/kotlin/it/unibo/alchemist/model/interfaces/PhysicalPedestrian.kt deleted file mode 100644 index 8c49926790..0000000000 --- a/alchemist-physical-agents/src/main/kotlin/it/unibo/alchemist/model/interfaces/PhysicalPedestrian.kt +++ /dev/null @@ -1,50 +0,0 @@ -/* - * Copyright (C) 2010-2020, Danilo Pianini and contributors - * listed in the main project's alchemist/build.gradle.kts file. - * - * This file is part of Alchemist, and is distributed under the terms of the - * GNU General Public License, with a linking exception, - * as described in the file LICENSE in the Alchemist distribution's top directory. - */ - -package it.unibo.alchemist.model.interfaces - -import it.unibo.alchemist.model.interfaces.environments.PhysicsEnvironment -import it.unibo.alchemist.model.interfaces.geometry.GeometricShape -import it.unibo.alchemist.model.interfaces.geometry.GeometricShapeFactory -import it.unibo.alchemist.model.interfaces.geometry.GeometricTransformation -import it.unibo.alchemist.model.interfaces.geometry.Vector -import it.unibo.alchemist.model.interfaces.nodes.NodeWithShape - -/** - * A pedestrian capable of interacting physically with others. [PhysicalPedestrian]s have a [comfortArea]: when - * another node enters such area, this pedestrian is subject to a repulsion force. This is derived from - * the work of Pelechano et al.. Note that [PhysicalPedestrian]s don't actively push each - * other, pushing behavior emerges from the interaction of pedestrians with different comfort areas (see the article - * linked above). - */ -interface PhysicalPedestrian : PhysicalNode, Pedestrian - where P : Vector

, P : Position

, - A : GeometricTransformation

, - F : GeometricShapeFactory { - - /** - * The comfort area of this pedestrian. - */ - val comfortArea: GeometricShape - - /** - * Computes the repulsion force caused by a node that entered the [comfortArea]. - */ - fun repulsionForce(other: NodeWithShape): P - - override fun physicalForces(environment: PhysicsEnvironment): List

= - environment.getNodesWithin(comfortArea) - .minusElement(this) - .filterIsInstance>() - .map { repulsionForce(it) } - /* - * Discard infinitesimal forces. - */ - .filter { it.magnitude > Double.MIN_VALUE } -} diff --git a/alchemist-physical-agents/src/main/kotlin/it/unibo/alchemist/model/interfaces/PhysicalPedestrian2D.kt b/alchemist-physical-agents/src/main/kotlin/it/unibo/alchemist/model/interfaces/PhysicalPedestrian2D.kt deleted file mode 100644 index 3af1969f94..0000000000 --- a/alchemist-physical-agents/src/main/kotlin/it/unibo/alchemist/model/interfaces/PhysicalPedestrian2D.kt +++ /dev/null @@ -1,50 +0,0 @@ -/* - * Copyright (C) 2010-2020, Danilo Pianini and contributors - * listed in the main project's alchemist/build.gradle.kts file. - * - * This file is part of Alchemist, and is distributed under the terms of the - * GNU General Public License, with a linking exception, - * as described in the file LICENSE in the Alchemist distribution's top directory. - */ - -package it.unibo.alchemist.model.interfaces - -import it.unibo.alchemist.model.implementations.positions.Euclidean2DPosition -import it.unibo.alchemist.model.interfaces.geometry.euclidean2d.Euclidean2DShape -import it.unibo.alchemist.model.interfaces.geometry.euclidean2d.Euclidean2DShapeFactory -import it.unibo.alchemist.model.interfaces.geometry.euclidean2d.Euclidean2DTransformation -import it.unibo.alchemist.model.interfaces.nodes.NodeWithShape - -/** - * A [PhysicalPedestrian] in an euclidean bidimensional space. This pedestrian has a circular [comfortArea] of radius - * equal to its shape radius plus a [comfortRay]. - * This is derived from [the work of Pelechano et al](https://bit.ly/3e3C7Tb). - */ -interface PhysicalPedestrian2D : - PhysicalPedestrian, - Pedestrian2D { - - /** - * The comfort ray of this pedestrian, this is added to the radius of its [shape] to obtain the [comfortArea]. - */ - val comfortRay: Double - - /** - * The comfort area of this pedestrian, it's a circle of radius [shape].radius + [comfortRay]. - */ - override val comfortArea: Euclidean2DShape get() = environment.shapeFactory.circle(shape.radius + comfortRay) - - /** - * Computes the repulsion force caused by a node that entered the [comfortArea]. This is derived from the work - * of [Pelechano et al](https://bit.ly/3e3C7Tb). - */ - override fun repulsionForce(other: NodeWithShape): Euclidean2DPosition = - (shape.centroid - other.shape.centroid).let { - val desiredDistance = shape.radius + comfortRay + other.shape.radius - /* - * it is the vector leading from the center of other to the center of this node, it.magnitude is the - * actual distance between the two nodes. - */ - it.normalized() * (desiredDistance - it.magnitude).coerceAtLeast(0.0) / it.magnitude - } -} diff --git a/alchemist-physical-agents/src/main/kotlin/it/unibo/alchemist/model/interfaces/PhysicalSteeringStrategy.kt b/alchemist-physical-agents/src/main/kotlin/it/unibo/alchemist/model/interfaces/PhysicalSteeringStrategy.kt index 6c500d88db..35d6899726 100644 --- a/alchemist-physical-agents/src/main/kotlin/it/unibo/alchemist/model/interfaces/PhysicalSteeringStrategy.kt +++ b/alchemist-physical-agents/src/main/kotlin/it/unibo/alchemist/model/interfaces/PhysicalSteeringStrategy.kt @@ -27,7 +27,7 @@ interface PhysicalSteeringStrategy : SteeringStrategy /** * The node to be moved. */ - val node: PhysicalNode + val node: Node /** * The combination of intentional forces (= steering actions) and [computeTarget] are delegated to this strategy. diff --git a/alchemist-physical-agents/src/main/kotlin/it/unibo/alchemist/model/interfaces/properties/PhysicalPedestrian.kt b/alchemist-physical-agents/src/main/kotlin/it/unibo/alchemist/model/interfaces/properties/PhysicalPedestrian.kt new file mode 100644 index 0000000000..f7d429f17e --- /dev/null +++ b/alchemist-physical-agents/src/main/kotlin/it/unibo/alchemist/model/interfaces/properties/PhysicalPedestrian.kt @@ -0,0 +1,51 @@ +/* + * Copyright (C) 2010-2022, Danilo Pianini and contributors + * listed, for each module, in the respective subproject's build.gradle.kts file. + * + * This file is part of Alchemist, and is distributed under the terms of the + * GNU General Public License, with a linking exception, + * as described in the file LICENSE in the Alchemist distribution's top directory. + */ + +package it.unibo.alchemist.model.interfaces.properties + +import it.unibo.alchemist.model.implementations.positions.Euclidean2DPosition +import it.unibo.alchemist.model.interfaces.Node +import it.unibo.alchemist.model.interfaces.Position +import it.unibo.alchemist.model.interfaces.geometry.GeometricShape +import it.unibo.alchemist.model.interfaces.geometry.GeometricShapeFactory +import it.unibo.alchemist.model.interfaces.geometry.GeometricTransformation +import it.unibo.alchemist.model.interfaces.geometry.Vector +import it.unibo.alchemist.model.interfaces.geometry.euclidean2d.Euclidean2DShapeFactory +import it.unibo.alchemist.model.interfaces.geometry.euclidean2d.Euclidean2DTransformation + +/** + * A pedestrian's capability to experience physical forces. + */ +interface PhysicalPedestrian : PhysicalProperty + where P : Position

, P : Vector

, + A : GeometricTransformation

, + F : GeometricShapeFactory { + + /** + * The comfort ray of this pedestrian, this is added to the radius of its [shape] to obtain the [comfortArea]. + */ + val comfortRay: Double + + /** + * The comfort area of this pedestrian, it's a circle of radius [shape].radius + [comfortRay]. + */ + val comfortArea: GeometricShape + + /** + * Computes the repulsion force caused by a node that entered the [comfortArea]. This is derived from the work + * of [Pelechano et al](https://bit.ly/3e3C7Tb). + */ + fun repulsionForce(other: Node): P +} + +/** + * A pedestrian's capability to experience physical forces in a 2D space. + */ +interface PhysicalPedestrian2DProperty : + PhysicalPedestrian diff --git a/alchemist-physical-agents/src/main/kotlin/it/unibo/alchemist/model/interfaces/PhysicalNode.kt b/alchemist-physical-agents/src/main/kotlin/it/unibo/alchemist/model/interfaces/properties/PhysicalProperty.kt similarity index 62% rename from alchemist-physical-agents/src/main/kotlin/it/unibo/alchemist/model/interfaces/PhysicalNode.kt rename to alchemist-physical-agents/src/main/kotlin/it/unibo/alchemist/model/interfaces/properties/PhysicalProperty.kt index 002acf5367..e22ec8d498 100644 --- a/alchemist-physical-agents/src/main/kotlin/it/unibo/alchemist/model/interfaces/PhysicalNode.kt +++ b/alchemist-physical-agents/src/main/kotlin/it/unibo/alchemist/model/interfaces/properties/PhysicalProperty.kt @@ -1,25 +1,25 @@ /* - * Copyright (C) 2010-2020, Danilo Pianini and contributors - * listed in the main project's alchemist/build.gradle.kts file. + * Copyright (C) 2010-2022, Danilo Pianini and contributors + * listed, for each module, in the respective subproject's build.gradle.kts file. * * This file is part of Alchemist, and is distributed under the terms of the * GNU General Public License, with a linking exception, * as described in the file LICENSE in the Alchemist distribution's top directory. */ -package it.unibo.alchemist.model.interfaces +package it.unibo.alchemist.model.interfaces.properties +import it.unibo.alchemist.model.interfaces.NodeProperty +import it.unibo.alchemist.model.interfaces.Position import it.unibo.alchemist.model.interfaces.environments.PhysicsEnvironment import it.unibo.alchemist.model.interfaces.geometry.GeometricShapeFactory import it.unibo.alchemist.model.interfaces.geometry.GeometricTransformation import it.unibo.alchemist.model.interfaces.geometry.Vector -import it.unibo.alchemist.model.interfaces.nodes.NodeWithShape /** - * A [NodeWithShape] capable of interacting physically with others (e.g. bumping into each other). Each physical node - * is responsible for the computation of the physical forces to which it is subject. + * A node's capability to experience physical forces. */ -interface PhysicalNode : NodeWithShape +interface PhysicalProperty : NodeProperty where P : Position

, P : Vector

, A : GeometricTransformation

, F : GeometricShapeFactory { diff --git a/alchemist-smartcam/src/main/kotlin/it/unibo/alchemist/model/implementations/actions/CameraInjectVisibleNodeClosestToDistance.kt b/alchemist-smartcam/src/main/kotlin/it/unibo/alchemist/model/implementations/actions/CameraInjectVisibleNodeClosestToDistance.kt index 8f21daa62c..b79a549212 100644 --- a/alchemist-smartcam/src/main/kotlin/it/unibo/alchemist/model/implementations/actions/CameraInjectVisibleNodeClosestToDistance.kt +++ b/alchemist-smartcam/src/main/kotlin/it/unibo/alchemist/model/implementations/actions/CameraInjectVisibleNodeClosestToDistance.kt @@ -19,13 +19,13 @@ import kotlin.math.min */ class CameraInjectVisibleNodeClosestToDistance( node: Node, - private val env: Physics2DEnvironment, + private val environment: Physics2DEnvironment, private val distance: Double, private val visionMolecule: Molecule, private val targetMolecule: Molecule ) : AbstractAction(node) { override fun cloneAction(node: Node, reaction: Reaction) = - CameraInjectVisibleNodeClosestToDistance(node, env, distance, visionMolecule, targetMolecule) + CameraInjectVisibleNodeClosestToDistance(node, environment, distance, visionMolecule, targetMolecule) override fun execute() { if (node.contains(visionMolecule)) { @@ -42,8 +42,8 @@ class CameraInjectVisibleNodeClosestToDistance( "The VisibleNode contained in visionMolecule is from a different environment" } @Suppress("UNCHECKED_CAST") val nodes = visibleNodes as List> - val myPosition = env.getPosition(node).surroundingPointAt( - versor = env.getHeading(node), + val myPosition = environment.getPosition(node).surroundingPointAt( + versor = environment.getHeading(node), distance = distance ) nodes.map { it.position } diff --git a/alchemist-swingui/src/main/java/it/unibo/alchemist/boundary/gui/effects/DrawCognitiveMap.java b/alchemist-swingui/src/main/java/it/unibo/alchemist/boundary/gui/effects/DrawCognitiveMap.java index cf97bc7444..f47213f1c0 100644 --- a/alchemist-swingui/src/main/java/it/unibo/alchemist/boundary/gui/effects/DrawCognitiveMap.java +++ b/alchemist-swingui/src/main/java/it/unibo/alchemist/boundary/gui/effects/DrawCognitiveMap.java @@ -16,10 +16,10 @@ import it.unibo.alchemist.model.implementations.positions.Euclidean2DPosition; import it.unibo.alchemist.model.interfaces.Environment; import it.unibo.alchemist.model.interfaces.Node; -import it.unibo.alchemist.model.interfaces.OrientingPedestrian; import it.unibo.alchemist.model.interfaces.Position2D; import it.unibo.alchemist.model.interfaces.environments.Environment2DWithObstacles; import it.unibo.alchemist.model.interfaces.geometry.euclidean2d.graph.NavigationGraph; +import it.unibo.alchemist.model.interfaces.properties.OrientingProperty; import org.danilopianini.lang.RangedInteger; import org.danilopianini.view.ExportForGUI; import org.jgrapht.graph.DefaultEdge; @@ -34,7 +34,7 @@ import java.awt.geom.Rectangle2D; /** - * Draws an orienting pedestrian' cognitive map. + * Draws an orienting node's cognitive map. */ @SuppressFBWarnings("EI_EXPOSE_REP") public class DrawCognitiveMap extends DrawOnce { @@ -83,10 +83,10 @@ public > void apply( final Integer markerNodeID = getMarkerNodeID(); if (cognitiveMap == null && markerNodeID != null - && environment.getNodeByID(markerNodeID) instanceof OrientingPedestrian + && environment.getNodeByID(markerNodeID).asPropertyOrNull(OrientingProperty.class) != null && environment instanceof Environment2DWithObstacles && environment.makePosition(0.0, 0.0) instanceof Euclidean2DPosition) { - cognitiveMap = ((OrientingPedestrian) environment.getNodeByID(markerNodeID)).getCognitiveMap(); + cognitiveMap = environment.getNodeByID(markerNodeID).asProperty(OrientingProperty.class).getCognitiveMap(); } } diff --git a/alchemist-swingui/src/main/java/it/unibo/alchemist/boundary/gui/effects/DrawSmartcam.java b/alchemist-swingui/src/main/java/it/unibo/alchemist/boundary/gui/effects/DrawSmartcam.java index df6c955f0f..225e880e5d 100644 --- a/alchemist-swingui/src/main/java/it/unibo/alchemist/boundary/gui/effects/DrawSmartcam.java +++ b/alchemist-swingui/src/main/java/it/unibo/alchemist/boundary/gui/effects/DrawSmartcam.java @@ -10,7 +10,7 @@ import it.unibo.alchemist.model.interfaces.environments.Physics2DEnvironment; import it.unibo.alchemist.model.implementations.geometry.AwtShapeCompatible; import it.unibo.alchemist.model.interfaces.geometry.GeometricShape; -import it.unibo.alchemist.model.interfaces.nodes.NodeWithShape; +import it.unibo.alchemist.model.interfaces.properties.OccupiesSpaceProperty; import org.jooq.lambda.function.Consumer2; import org.slf4j.Logger; import org.slf4j.LoggerFactory; @@ -43,9 +43,9 @@ public > void apply( final int x = viewPoint.x; final int y = viewPoint.y; if (environment instanceof Physics2DEnvironment) { - final Physics2DEnvironment env = (Physics2DEnvironment) environment; - drawShape(graphics, node, env, zoom, x, y); - drawFieldOfView(graphics, node, env, zoom, x, y); + final Physics2DEnvironment physicsEnvironment = (Physics2DEnvironment) environment; + drawShape(graphics, node, physicsEnvironment, zoom, x, y); + drawFieldOfView(graphics, node, physicsEnvironment, zoom, x, y); } else { logOnce("DrawSmartcam only works with EuclideanPhysics2DEnvironment", Logger::warn); } @@ -64,8 +64,8 @@ private void drawShape( final int x, final int y ) { - final GeometricShape geometricShape = node instanceof NodeWithShape - ? ((NodeWithShape) node).getShape() + final GeometricShape geometricShape = node.asPropertyOrNull(OccupiesSpaceProperty.class) != null + ? node.asProperty(OccupiesSpaceProperty.class).getShape() : null; if (geometricShape instanceof AwtShapeCompatible) { final AffineTransform transform = getTransform(x, y, zoom, getRotation(node, environment)); @@ -105,8 +105,8 @@ private void drawFieldOfView( }); } - private double getRotation(final Node node, final Physics2DEnvironment env) { - final Euclidean2DPosition direction = env.getHeading(node); + private double getRotation(final Node node, final Physics2DEnvironment environment) { + final Euclidean2DPosition direction = environment.getHeading(node); return Math.atan2(direction.getY(), direction.getX()); } diff --git a/alchemist-swingui/src/main/java/it/unibo/alchemist/boundary/gui/effects/Effect.java b/alchemist-swingui/src/main/java/it/unibo/alchemist/boundary/gui/effects/Effect.java index 1923790ef7..34217238f4 100644 --- a/alchemist-swingui/src/main/java/it/unibo/alchemist/boundary/gui/effects/Effect.java +++ b/alchemist-swingui/src/main/java/it/unibo/alchemist/boundary/gui/effects/Effect.java @@ -48,17 +48,17 @@ default void apply(final Graphics2D graphic, final Node node, final int x, fi * @param

position type * @param g graphics * @param n node - * @param env environment + * @param environment environment * @param wormhole the wormhole used to map environment's coords to screen coords */ @SuppressWarnings("deprecation") default > void apply( final Graphics2D g, final Node n, - final Environment env, + final Environment environment, final Wormhole2D

wormhole ) { - final Point viewPoint = wormhole.getViewPoint(env.getPosition(n)); + final Point viewPoint = wormhole.getViewPoint(environment.getPosition(n)); apply(g, n, viewPoint.x, viewPoint.y); // preserve backward compatibility } diff --git a/alchemist-swingui/src/main/java/it/unibo/alchemist/boundary/monitors/Generic2DDisplay.java b/alchemist-swingui/src/main/java/it/unibo/alchemist/boundary/monitors/Generic2DDisplay.java index c7317acaf4..44a20ecd54 100644 --- a/alchemist-swingui/src/main/java/it/unibo/alchemist/boundary/monitors/Generic2DDisplay.java +++ b/alchemist-swingui/src/main/java/it/unibo/alchemist/boundary/monitors/Generic2DDisplay.java @@ -482,14 +482,14 @@ protected final ZoomManager getZoomManager() { return zoomManager; } - private void initAll(final Environment env) { - wormhole = new WormholeSwing<>(env, this); + private void initAll(final Environment environment) { + wormhole = new WormholeSwing<>(environment, this); wormhole.center(); wormhole.optimalZoom(); angleManager = new AngleManagerImpl(AngleManagerImpl.DEF_DEG_PER_PIXEL); zoomManager = new ExponentialZoomManager(wormhole.getZoom(), ExponentialZoomManager.DEF_BASE); - if (env instanceof Environment2DWithObstacles) { - loadObstacles(env); + if (environment instanceof Environment2DWithObstacles) { + loadObstacles(environment); } else { obstacles = null; } @@ -531,8 +531,8 @@ public final boolean isRealTime() { return realTime; } - private void loadObstacles(final Environment env) { - obstacles = ((Environment2DWithObstacles) env).getObstacles(); + private void loadObstacles(final Environment environment) { + obstacles = ((Environment2DWithObstacles) environment).getObstacles(); } /** @@ -686,22 +686,22 @@ public final void stepDone( /** * Updates parameter for correct {@code Environment} representation. * - * @param env the {@code Environment} + * @param environment the {@code Environment} * @param time the current {@code Time} of simulation */ - private void update(final Environment env, final Time time) { - if (Thread.holdsLock(env)) { - if (envHasMobileObstacles(env)) { - loadObstacles(env); + private void update(final Environment environment, final Time time) { + if (Thread.holdsLock(environment)) { + if (envHasMobileObstacles(environment)) { + loadObstacles(environment); } lastTime = time.toDouble(); - currentEnv = env; + currentEnv = environment; accessData(); positions.clear(); neighbors.clear(); - env.getNodes().parallelStream().forEach(node -> { - positions.put(node, env.getPosition(node)); - neighbors.put(node, env.getNeighborhood(node)); + environment.getNodes().parallelStream().forEach(node -> { + positions.put(node, environment.getPosition(node)); + neighbors.put(node, environment.getNeighborhood(node)); }); releaseData(); repaint(); @@ -717,13 +717,14 @@ public final void zoomTo(final P center, final double zoomLevel) { } /** - * @param env + * @param environment * the current environment * @return true if env is subclass of {@link Environment2DWithObstacles} * and has mobile obstacles */ - protected static boolean envHasMobileObstacles(final Environment env) { - return env instanceof Environment2DWithObstacles && ((Environment2DWithObstacles) env).hasMobileObstacles(); + protected static boolean envHasMobileObstacles(final Environment environment) { + return environment instanceof Environment2DWithObstacles && ((Environment2DWithObstacles) environment) + .hasMobileObstacles(); } /** diff --git a/alchemist-swingui/src/main/java/it/unibo/alchemist/boundary/wormhole/implementation/MapWormhole.java b/alchemist-swingui/src/main/java/it/unibo/alchemist/boundary/wormhole/implementation/MapWormhole.java index 77cc3e135a..fe56c06c2f 100644 --- a/alchemist-swingui/src/main/java/it/unibo/alchemist/boundary/wormhole/implementation/MapWormhole.java +++ b/alchemist-swingui/src/main/java/it/unibo/alchemist/boundary/wormhole/implementation/MapWormhole.java @@ -37,7 +37,7 @@ public final class MapWormhole extends WormholeSwing { * Initializes a new {@link MapWormhole} copying the state of the one in * input. * - * @param env + * @param environment * the {@link Environment} * @param comp * the controlled {@link Component} @@ -45,8 +45,8 @@ public final class MapWormhole extends WormholeSwing { * the {@link IMapViewPosition} */ @SuppressFBWarnings(value = "EI_EXPOSE_REP2", justification = "This is intentional") - public MapWormhole(final Environment env, final Component comp, final IMapViewPosition m) { - super(env, comp); + public MapWormhole(final Environment environment, final Component comp, final IMapViewPosition m) { + super(environment, comp); mapModel = m; super.setMode(Mode.MAP); } @@ -129,12 +129,12 @@ public void setEnvPosition(final GeoPosition ep) { public void optimalZoom() { byte zoom = MAX_ZOOM; @SuppressWarnings("unchecked") - final Environment env = (Environment) getEnvironment(); + final Environment environment = (Environment) getEnvironment(); do { setZoom(zoom); zoom--; - } while (zoom > 1 && !env.getNodes().parallelStream() - .map(env::getPosition) + } while (zoom > 1 && !environment.getNodes().parallelStream() + .map(environment::getPosition) .map(this::getViewPoint) .allMatch(this::isInsideView)); } diff --git a/alchemist-swingui/src/main/kotlin/it/unibo/alchemist/boundary/gui/effects/BidimensionalGaussianLayersMapper.kt b/alchemist-swingui/src/main/kotlin/it/unibo/alchemist/boundary/gui/effects/BidimensionalGaussianLayersMapper.kt index 86e304c783..a4387ec14a 100644 --- a/alchemist-swingui/src/main/kotlin/it/unibo/alchemist/boundary/gui/effects/BidimensionalGaussianLayersMapper.kt +++ b/alchemist-swingui/src/main/kotlin/it/unibo/alchemist/boundary/gui/effects/BidimensionalGaussianLayersMapper.kt @@ -32,7 +32,7 @@ class BidimensionalGaussianLayersMapper : LayerToFunctionMapper { override fun > prepare( effect: DrawLayersValues, toDraw: Collection>, - env: Environment, + environment: Environment, g: Graphics2D, wormhole: Wormhole2D

) { @@ -40,7 +40,7 @@ class BidimensionalGaussianLayersMapper : LayerToFunctionMapper { val maxLayerValue = toDraw.stream() .filter { l -> l is BidimensionalGaussianLayer } .map { l -> l as BidimensionalGaussianLayer } - .map { l -> l.getValue(env.makePosition(l.centerX, l.centerY)) } + .map { l -> l.getValue(environment.makePosition(l.centerX, l.centerY)) } .max { d1, d2 -> java.lang.Double.compare(d1, d2) } .orElse(minimumLayerValue) effect.minLayerValue = minimumLayerValue.toString() diff --git a/alchemist-swingui/src/main/kotlin/it/unibo/alchemist/boundary/gui/effects/LayerToFunctionMapper.kt b/alchemist-swingui/src/main/kotlin/it/unibo/alchemist/boundary/gui/effects/LayerToFunctionMapper.kt index 3aca62b757..e96fa73619 100644 --- a/alchemist-swingui/src/main/kotlin/it/unibo/alchemist/boundary/gui/effects/LayerToFunctionMapper.kt +++ b/alchemist-swingui/src/main/kotlin/it/unibo/alchemist/boundary/gui/effects/LayerToFunctionMapper.kt @@ -28,7 +28,7 @@ interface LayerToFunctionMapper : Serializable { fun > prepare( effect: DrawLayersValues, toDraw: Collection>, - env: Environment, + environment: Environment, g: Graphics2D, wormhole: Wormhole2D

) = Unit // defaults to nothing diff --git a/alchemist-ui-tooling/src/test/java/it/unibo/alchemist/test/TestWormhole2D.java b/alchemist-ui-tooling/src/test/java/it/unibo/alchemist/test/TestWormhole2D.java index bf346af36d..3b30e31570 100644 --- a/alchemist-ui-tooling/src/test/java/it/unibo/alchemist/test/TestWormhole2D.java +++ b/alchemist-ui-tooling/src/test/java/it/unibo/alchemist/test/TestWormhole2D.java @@ -29,15 +29,15 @@ class TestWormhole2D { @Test void testZeroSizeEnvironment() { final var incarnation = SupportedIncarnations.get("protelis").orElseThrow(); - final Environment env = new Continuous2DEnvironment<>(incarnation); - final AbstractWormhole2D worm = new TestPurposeWormhole<>(env); + final Environment environment = new Continuous2DEnvironment<>(incarnation); + final AbstractWormhole2D worm = new TestPurposeWormhole<>(environment); worm.center(); } private static class TestPurposeWormhole

> extends AbstractWormhole2D

{ - TestPurposeWormhole(final Environment env) { + TestPurposeWormhole(final Environment environment) { super( - env, + environment, new ViewPort() { @Override public double getWidth() { diff --git a/src/main/hugo/content/howtos/simulation/cognitive/_index.md b/src/main/hugo/content/howtos/simulation/cognitive/_index.md index cfcdd94026..37ce4d3d50 100644 --- a/src/main/hugo/content/howtos/simulation/cognitive/_index.md +++ b/src/main/hugo/content/howtos/simulation/cognitive/_index.md @@ -8,21 +8,45 @@ summary = "Agents with realistic human behavior." We recommend to read our [explanation of the cognitive agents](/explanation/cognitive) to better understand the contents of this how-to. +Different kinds of pedestrians are obtainable by attaching +{{% api class=NodeProperty %}}s +to nodes (e.g {{%api package=model.implementations.nodes class=GenericNode %}}). +Common properties concern abilities such as perceiving other nodes +({{% api package=model.interfaces.properties class=PerceptiveProperty %}}) +and occuping space in an environment +({{% api package=model.interfaces.properties class=OccupiesSpaceProperty %}}). + ## Homogeneous Pedestrian +As shown in the example below, this kind of pedestrian is obtained by attaching the +{{% api package=model.implementations.properties class=Pedestrian %}} +property. + {{< code path="src/test/resources/website-snippets/homogeneous-pedestrian.yml" >}} ## Heterogeneous Pedestrian -The age groups available are: *child*, *adult*, *elderly*; -alternatively you can specify the exact age. -The genders available are: *male*, *female*. +The age groups available are: *child*, *adult*, and *elderly*; +alternatively, if the exact age is specified, +they are assigned to one of the aforementioned groups automatically. +The genders available are: *male* and *female*. +This informations is included in the +{{%api package=model.implementations.properties class=Human %}} +property and it is used by the +{{%api package=model.implementations.properties class=HeterogeneousPedestrian %}} +property, along with the age. {{< code path="src/test/resources/website-snippets/heterogeneous-pedestrian.yml" >}} ## Cognitive Pedestrian -Cognitive pedestrians are heterogeneous pedestrians with cognitive capabilities. -They have an emotional state and are able to influence and be influenced by others with the same capabilities. As an example, cognitive pedestrians can perceive fear via social contagion (e.g. seeing other people fleeing may cause them flee as well despite they haven't directly seen the danger). +Cognitive pedestrians are heterogeneous pedestrians with cognitive capabilities given by a +{{% api package=model.interfaces.properties class=CognitiveProperty %}}. +They have an emotional state and are able to influence and be influenced by others with the same capabilities. +As an example, cognitive pedestrians can perceive fear via social contagion +(e.g. seeing other people fleeing may cause them flee as well despite they haven't directly seen the danger). +To express how a cognitive pedestrians move, based on their emotional state, attach the +{{% api package=model.implementations.properties class=CognitivePedestrian %}} +property. {{< code path="src/test/resources/website-snippets/cognitive-pedestrian.yml" >}} @@ -39,13 +63,14 @@ can be familiar with different portions of the environment. Be also aware that orienting pedestrians can only be placed in an {{% api package="model.interfaces.environments" class="EnvironmentWithGraph" %}} which is a type of environment providing a navigation graph. +In order to give a node orienting capabilities enhance a node with an +{{% api package=model.interfaces.properties class=OrientingProperty %}}. {{< code path="src/test/resources/website-snippets/homogeneous-orienting-pedestrian.yml" >}} ## Cognitive orienting pedestrian These are cognitive pedestrians equipable with a given knowledge degree of the environment. -Cognitive orienting pedestrians can be instanced providing their knowledge degree as first parameter. {{< code path="src/test/resources/website-snippets/cognitive-orienting-pedestrian.yml" >}} @@ -148,17 +173,9 @@ Here's a list of all the hardcoded parameters. ### Physical pedestrians Physical pedestrians are capable of pushing and bumping into each other. -Similarly to Orienting Pedestrians, -we have -{{% api package="model.implementations.nodes" class="HomogeneousPhysicalPedestrian2D" %}}, -and -{{% api package="model.implementations.nodes" class="CognitivePhysicalPedestrian2D" %}}. - -Whatsmore, you can have a physical pedestrian capable of orienting as well: -there are -{{% api package="model.implementations.nodes" class="HomogeneousOrientingPhysicalPedestrian2D" %}} -and -{{% api package="model.implementations.nodes" class="CognitiveOrientingPhysicalPedestrian2D" %}}. +To express those physical interactions use a +{{% api package=model.interfaces.properties class=PhysicalPedestrian %}} +property. ### Physical steering strategies @@ -173,8 +190,7 @@ and {{% api package="model.implementations.reactions" class="NavigationPrioritisedSteeringWithPhysics" %}} are available. -Here's a simple code for loading a -{{% api package="model.implementations.nodes" class="HomogeneousPhysicalPedestrian2D" %}} +Here's a simple code for loading a homogeneous pedestrian with physical properties with {{% api package="model.implementations.actions" class="CognitiveAgentSeek" %}} and diff --git a/src/main/hugo/content/howtos/simulation/program/content/_index.md b/src/main/hugo/content/howtos/simulation/program/content/_index.md index c3af06a74a..8c9f236e82 100644 --- a/src/main/hugo/content/howtos/simulation/program/content/_index.md +++ b/src/main/hugo/content/howtos/simulation/program/content/_index.md @@ -12,8 +12,14 @@ As such, they depend on the specific {{% api class="Incarnation" %}} in use. This is done by listing the contents under [`deployments.contents`](/reference/yaml/#deploymentcontents), specifying a {{% api class="Molecule" %}} name and its {{% api class="Concentration" %}}. -Unless the [type/parameter syntax](/reference/yaml/#arbitrary-class-loading-system) is used, the data is gets processed by the {{% api class="Incarnation" %}} -through the {{% api class="Incarnation" method="createMolecule" %}} and {{% api class="Incarnation" method="createConcentration" %}} methods, respectively. +Unless the [type/parameter syntax](/reference/yaml/#arbitrary-class-loading-system) is used, the data gets processed +by the +{{% api class="Incarnation" %}} +through the +{{% api class="Incarnation" method="createMolecule" %}} +and +{{% api class="Incarnation" method="createConcentration" %}} +methods, respectively. In the following example, three molecules are created and injected into all nodes deployed in the scenario: @@ -21,10 +27,12 @@ In the following example, three molecules are created and injected into all node By default, all nodes in the deployment will be injected with the required contents. It is possible, though, to select only a subset of them through the [`in`](/reference/yaml/#contentin) keyword, -which expects enough information to be able to build a {{% api package="loader.shapes" class="Shape" %}} +which expects enough information to be able to build a +{{% api package="loader.filters" class="Filter" %}} through the [arbitrary class loading system](/reference/yaml/#arbitrary-class-loading-system). -In the following example, only molecules located inside a {{% api package="loader.shapes" class="Rectangle" %}} +In the following example, only molecules located inside a +{{% api package="loader.filters" class="Rectangle" %}} get the `ball` molecule: {{}} diff --git a/src/main/hugo/content/reference/biochemistry/_index.md b/src/main/hugo/content/reference/biochemistry/_index.md index c8ae39246c..1d66fea061 100644 --- a/src/main/hugo/content/reference/biochemistry/_index.md +++ b/src/main/hugo/content/reference/biochemistry/_index.md @@ -63,8 +63,9 @@ The Biochemistry Incarnation supports cell collisions and deformations too. In order to do that, however, the environment must feature appropriate support, as for instance {{% api package="model.implementations.environments" class="BioRect2DEnvironmentNoOverlap" %}}. -The cells must support deformation as well, as, for instance, -{{% api package="model.implementations.nodes" class="CircularDeformableCellImpl" %}}. +The cells must support deformation as well, as, for instance, a node with the +{{% api package="model.implementations.properties" class="CircularDeformableCell" %}} +property. The minimum radius of the cell is so that ``min-radius = rigidity * max-radius`` and the two parameters are used to compute collisions and impacts between the cells. diff --git a/src/main/hugo/content/reference/yaml/_index.md b/src/main/hugo/content/reference/yaml/_index.md index bafaae8786..ef1d0a09d9 100644 --- a/src/main/hugo/content/reference/yaml/_index.md +++ b/src/main/hugo/content/reference/yaml/_index.md @@ -263,6 +263,10 @@ If left unspecified, nodes get created through * Creation of heterogeneous pedestrians {{}} +### `deployment.properties` + +**Type**: Traversable of [`property`](#property) + ### `deployment.programs` **Type**: Traversable of [`program`](#program) @@ -277,14 +281,14 @@ Definition of the contents ({{% api class="Molecule" %}}s and {{% api class="Con **(Multi)Spec** -| Mandatory keys | Optional keys | -|----------------------------|---------------| +| Mandatory keys | Optional keys | +|-----------------------------|---------------| | `molecule`, `concentration` | `in` | #### Examples * Three molecules injected into all nodes deployed in the scenario {{}} -* Injection of a molecule only in those nodes located inside a {{% api package="loader.shapes" class="Rectangle" %}} +* Injection of a molecule only in those nodes located inside a {{% api package="loader.filters" class="Rectangle" %}} {{}} ### `content.molecule` @@ -295,7 +299,7 @@ The name of the molecule to be injected. If a String is provided, then it is created via {{% api class="Incarnation" method="createMolecule" %}}. Otherwise, the [arbitrary class loading system](#arbitrary-class-loading-system) **SHOULD** be used. -### `content` +### `content.concentration` **Type**: String @@ -305,7 +309,29 @@ Otherwise, the [arbitrary class loading system](#arbitrary-class-loading-system) ### `content.in` -**Type**: Traversable of Shape +**Type**: Traversable of [shapeFilter](#shapefilter) + +### `property` + +**Type**: SpecMap + +**(Multi)Spec** + +| Mandatory keys | Optional keys | +|----------------|--------------------| +| `type` | `parameters`, `in` | + +### `property.type` + +Same as [type](#type) + +### `property.parameters` + +Same as [parameters](#parameters) + +### `property.in` + +**Type**: Traversable of [shapeFilter](#shapefilter) --- @@ -510,7 +536,11 @@ Same as [type](#type) **Type**: String -Passed to {{% api class="Incarnation" method="createReaction" %}} to be interepreted and +Passed to {{% api class="Incarnation" method="createReaction" %}} to be interepreted and + +### `program.in` + +**Type**: Traversable of [shapeFilter](#shapefilter) ### `program.actions` @@ -538,15 +568,15 @@ Otherwise, the [arbitrary class loading system](#arbitrary-class-loading-system) --- -### `shape` +### `shapeFilter` **Type**: SpecMap -Builds a {{% api package="loader.shapes" class="Shape" %}} +Builds a {{% api package="loader.filters" class="Filter" %}} using the [arbitrary class loading system](#arbitrary-class-loading-system). #### Examples -* Injection of a molecule only in those nodes located inside a {{% api package="loader.shapes" class="Rectangle" %}} +* Injection of a molecule only in those nodes located inside a {{% api package="loader.filters" class="Rectangle" %}} {{}} --- diff --git a/src/test/resources/website-snippets/cognitive-orienting-pedestrian.yml b/src/test/resources/website-snippets/cognitive-orienting-pedestrian.yml index 39181a3a4d..6741a86334 100644 --- a/src/test/resources/website-snippets/cognitive-orienting-pedestrian.yml +++ b/src/test/resources/website-snippets/cognitive-orienting-pedestrian.yml @@ -15,8 +15,13 @@ _reactions: &behavior deployments: - type: Point parameters: [2, 2] - nodes: - type: CognitiveOrientingPedestrian2D - parameters: [0.5, "adult", "male"] + properties: + - type: Human + parameters: ["adult", "male"] + - type: Perceptive2D + - type: CognitivePedestrian + - type: Cognitive2D + - type: Orienting2D + parameters: [0.5] programs: - *behavior \ No newline at end of file diff --git a/src/test/resources/website-snippets/cognitive-pedestrian.yml b/src/test/resources/website-snippets/cognitive-pedestrian.yml index 5998ed0290..28d4a07d97 100644 --- a/src/test/resources/website-snippets/cognitive-pedestrian.yml +++ b/src/test/resources/website-snippets/cognitive-pedestrian.yml @@ -9,15 +9,23 @@ _reactions: &behavior deployments: - type: Circle parameters: [50, 0, 0, 20] - nodes: - type: CognitivePedestrian2D - parameters: ["adult", "male"] + properties: + - type: Human + parameters: ["adult", "male"] + - type: Perceptive2D + - type: CognitivePedestrian + - type: Cognitive2D + - type: CircularArea programs: - *behavior - type: Circle parameters: [50, 0, 0, 20] - nodes: - type: CognitivePedestrian2D - parameters: ["adult", "female"] + properties: + - type: Human + parameters: ["adult", "female"] + - type: Perceptive2D + - type: CognitivePedestrian + - type: Cognitive2D + - type: CircularArea programs: - *behavior \ No newline at end of file diff --git a/src/test/resources/website-snippets/evacuation-scenarios.yml b/src/test/resources/website-snippets/evacuation-scenarios.yml index ffb905e3d7..0c351e2b04 100644 --- a/src/test/resources/website-snippets/evacuation-scenarios.yml +++ b/src/test/resources/website-snippets/evacuation-scenarios.yml @@ -24,8 +24,12 @@ _reactions: &behavior deployments: - type: Circle parameters: [100, 0, 0, 50] - nodes: - type: CognitivePedestrian2D - parameters: ["adult", "female", *danger] + properties: + - type: Human + parameters: ["adult", "female"] + - type: Perceptive2D + - type: CognitivePedestrian + - type: Cognitive2D + - type: CircularArea programs: - *behavior \ No newline at end of file diff --git a/src/test/resources/website-snippets/heterogeneous-pedestrian.yml b/src/test/resources/website-snippets/heterogeneous-pedestrian.yml index cbbf046515..4afb92e196 100644 --- a/src/test/resources/website-snippets/heterogeneous-pedestrian.yml +++ b/src/test/resources/website-snippets/heterogeneous-pedestrian.yml @@ -3,11 +3,17 @@ incarnation: protelis deployments: - type: Circle parameters: [50, 0, 0, 20] - nodes: - type: HeterogeneousPedestrian2D - parameters: ["elderly", "female"] + properties: + - type: Human + parameters: ["elderly", "female"] + - type: HeterogeneousPedestrian + - type: Perceptive2D + - type: CircularArea - type: Circle parameters: [50, 0, 0, 20] - nodes: - type: HeterogeneousPedestrian2D - parameters: ["child", "male"] \ No newline at end of file + properties: + - type: Human + parameters: ["child", "male"] + - type: HeterogeneousPedestrian + - type: Perceptive2D + - type: CircularArea \ No newline at end of file diff --git a/src/test/resources/website-snippets/homogeneous-orienting-pedestrian.yml b/src/test/resources/website-snippets/homogeneous-orienting-pedestrian.yml index aca72c0aa4..c78cce8647 100644 --- a/src/test/resources/website-snippets/homogeneous-orienting-pedestrian.yml +++ b/src/test/resources/website-snippets/homogeneous-orienting-pedestrian.yml @@ -9,6 +9,8 @@ environment: deployments: - type: Point parameters: [2, 2] - nodes: - type: HomogeneousOrientingPedestrian2D - parameters: [0.5] + properties: + - type: Pedestrian + - type: CircularArea + - type: Orienting2D + parameters: [0.5] diff --git a/src/test/resources/website-snippets/homogeneous-pedestrian.yml b/src/test/resources/website-snippets/homogeneous-pedestrian.yml index f8920777f5..e1a38e3cd0 100644 --- a/src/test/resources/website-snippets/homogeneous-pedestrian.yml +++ b/src/test/resources/website-snippets/homogeneous-pedestrian.yml @@ -3,5 +3,8 @@ incarnation: protelis deployments: - type: Circle parameters: [100, 0, 0, 20] - nodes: - type: HomogeneousPedestrian2D \ No newline at end of file + properties: + - type: Pedestrian + - type: Perceptive2D + - type: CircularArea + - type: Social \ No newline at end of file diff --git a/src/test/resources/website-snippets/pedestrian-groups.yml b/src/test/resources/website-snippets/pedestrian-groups.yml index 8dbe890565..727c2f5503 100644 --- a/src/test/resources/website-snippets/pedestrian-groups.yml +++ b/src/test/resources/website-snippets/pedestrian-groups.yml @@ -11,11 +11,17 @@ variables: deployments: - type: Circle parameters: [10, 0, 0, 20] - nodes: - type: HomogeneousPedestrian2D - parameters: [*group1] + properties: + - type: Pedestrian + - type: Perceptive2D + - type: CircularArea + - type: Social + parameters: [*group1] - type: Circle parameters: [15, 0, 0, 20] - nodes: - type: HomogeneousPedestrian2D - parameters: [*group2] \ No newline at end of file + properties: + - type: Pedestrian + - type: Perceptive2D + - type: CircularArea + - type: Social + parameters: [*group2] \ No newline at end of file diff --git a/src/test/resources/website-snippets/physical-steering-strategies.yml b/src/test/resources/website-snippets/physical-steering-strategies.yml index 7b64596526..7cb7b3b72c 100644 --- a/src/test/resources/website-snippets/physical-steering-strategies.yml +++ b/src/test/resources/website-snippets/physical-steering-strategies.yml @@ -20,7 +20,11 @@ _reactions: &behavior deployments: - type: Point parameters: [2, 2] - nodes: - type: HomogeneousPhysicalPedestrian2D + properties: + - type: Pedestrian + - type: PhysicalPedestrian2D + - type: Perceptive2D + - type: CircularArea + - type: Social programs: - *behavior \ No newline at end of file diff --git a/src/test/resources/website-snippets/steering-actions.yml b/src/test/resources/website-snippets/steering-actions.yml index ccb99dc599..299a879428 100644 --- a/src/test/resources/website-snippets/steering-actions.yml +++ b/src/test/resources/website-snippets/steering-actions.yml @@ -19,7 +19,10 @@ _reactions: &behavior deployments: - type: Circle parameters: [50, 0, 0, 25] - nodes: - type: HomogeneousPedestrian2D + properties: + - type: Pedestrian + - type: Perceptive2D + - type: CircularArea + - type: Social programs: - *behavior \ No newline at end of file diff --git a/src/test/resources/website-snippets/steering-strategies.yml b/src/test/resources/website-snippets/steering-strategies.yml index bacf0d2ccd..7a9fedba34 100644 --- a/src/test/resources/website-snippets/steering-strategies.yml +++ b/src/test/resources/website-snippets/steering-strategies.yml @@ -17,7 +17,10 @@ _reactions: &behavior deployments: - type: Point parameters: [0, 0] - nodes: - type: HomogeneousPedestrian2D + properties: + - type: Pedestrian + - type: Perceptive2D + - type: CircularArea + - type: Social programs: - *behavior \ No newline at end of file