All Downloads are FREE. Search and download functionalities are using the official Maven repository.

it.unibo.alchemist.model.physics.environments.Continuous2DObstacles Maven / Gradle / Ivy

There is a newer version: 35.0.1
Show newest version
/*
 * Copyright (C) 2010-2023, 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.physics.environments;

import com.github.davidmoten.rtree.Entry;
import com.github.davidmoten.rtree.RTree;
import com.github.davidmoten.rtree.geometry.Geometries;
import com.github.davidmoten.rtree.geometry.Rectangle;
import com.github.davidmoten.rtree.internal.EntryDefault;
import edu.umd.cs.findbugs.annotations.SuppressFBWarnings;
import it.unibo.alchemist.model.Incarnation;
import it.unibo.alchemist.model.obstacles.RectObstacle2D;
import it.unibo.alchemist.model.positions.Euclidean2DPosition;

import javax.annotation.Nonnull;
import java.io.IOException;
import java.io.ObjectInputStream;
import java.io.ObjectOutputStream;
import java.util.List;
import java.util.stream.Collectors;

/**
 * @param  concentration type
 */
public class Continuous2DObstacles extends LimitedContinuos2D
    implements EuclideanPhysics2DEnvironmentWithObstacles, T> {

    private static final double TOLERANCE_MULTIPLIER = 0.01;
    private static final long serialVersionUID = 69931743897405107L;
    private transient RTree, Rectangle> rtree = RTree.create();

    /**
     * @param incarnation the current incarnation.
     */
    public Continuous2DObstacles(final Incarnation incarnation) {
        super(incarnation);
    }

    @Override
    public final void addObstacle(@Nonnull final RectObstacle2D o) {
        rtree = rtree.add(o, toGeometry(o));
        includeObject(o.getMinX(), o.getMaxX(), o.getMinY(), o.getMaxY());
    }

    @Nonnull
    @Override
    public final List> getObstacles() {
        return rtree.entries().map(Entry::value).toList().toBlocking().single();
    }

    @Override
    @Nonnull
    public final List> getObstaclesInRange(
            @Nonnull final Euclidean2DPosition center,
            final double range
    ) {
        return getObstaclesInRange(center.getX(), center.getY(), range);
    }

    @Nonnull
    @Override
    public final List> getObstaclesInRange(
            final double centerx,
            final double centery,
            final double range
    ) {
        return rtree.search(Geometries.circle(centerx, centery, range)).map(Entry::value).toList().toBlocking().single();
    }

    /**
     * Subclasses dealing with mobile obstacles may change this.
     *
     * @return false
     */
    @Override
    public boolean hasMobileObstacles() {
        return false;
    }

    @Override
    @SuppressFBWarnings("FE_FLOATING_POINT_EQUALITY")
    public final boolean intersectsObstacle(final Euclidean2DPosition start, final Euclidean2DPosition end) {
        final double sx = start.getX();
        final double sy = start.getY();
        final double ex = end.getX();
        final double ey = end.getY();
        for (final RectObstacle2D obstacle : query(sx, sy, ex, ey, 0)) {
            final double[] coords = obstacle.nearestIntersection(start, end).getCoordinates();
            if (coords[0] != ex || coords[1] != ey || obstacle.contains(coords[0], coords[1])) {
                return true;
            }
        }
        return false;
    }

    @Override
    protected final boolean isAllowed(final Euclidean2DPosition p) {
        return rtree.search(Geometries.point(p.getX(), p.getY())).isEmpty().toBlocking().single();
    }

    @Nonnull
    @Override
    public final Euclidean2DPosition next(
            @Nonnull final Euclidean2DPosition current,
            @Nonnull final Euclidean2DPosition desired
    ) {
        return next(current.getX(), current.getY(), desired.getX(), desired.getY());
    }

    @Override
    @SuppressFBWarnings({ "FE_FLOATING_POINT_EQUALITY", "FL_FLOATS_AS_LOOP_COUNTERS" })
    public final Euclidean2DPosition next(final double ox, final double oy, final double nx, final double ny) {
        final List> l = query(ox, oy, nx, ny, TOLERANCE_MULTIPLIER);
        if (l.isEmpty()) {
            return new Euclidean2DPosition(nx, ny);
        }
        Euclidean2DPosition shortest = null;
        double fx = nx;
        double fy = ny;
        double fxCache = Double.NaN;
        double fyCache = Double.NaN;
        while (fx != fxCache || fy != fyCache) {
            fxCache = fx;
            fyCache = fy;
            for (int i = 0; i < l.size(); i++) {
                shortest = l.get(i).next(new Euclidean2DPosition(ox, oy), new Euclidean2DPosition(fx, fy));
                /*
                 * If one of the dimensions is limited, such limit must be
                 * retained!
                 */
                final double sfx = shortest.getX();
                final double sfy = shortest.getY();
                if (sfx != fx || fy != sfy) {
                    /*
                     * This obstacle has contributed already
                     */
                    fx = sfx;
                    fy = sfy;
                    l.remove(i);
                    i--;
                }
            }
        }
        return makePosition(shortest.getX(), shortest.getY());
    }

    private List> query(
            final double ox,
            final double oy,
            final double nx,
            final double ny,
            final double tolerance
    ) {
        double minx = Math.min(ox, nx);
        double miny = Math.min(oy, ny);
        double maxx = Math.max(ox, nx);
        double maxy = Math.max(oy, ny);
        final double dx = (maxx - minx) * tolerance;
        final double dy = (maxy - miny) * tolerance;
        minx -= dx;
        maxx += dx;
        miny -= dy;
        maxy += dy;
        return rtree.search(Geometries.rectangle(minx, miny, maxx, maxy))
                .map(Entry::value)
                .toList()
                .toBlocking()
                .single();
    }

    @Override
    public final boolean removeObstacle(@Nonnull final RectObstacle2D o) {
        final int initialSize = rtree.size();
        rtree = rtree.delete(o, toGeometry(o));
        return rtree.size() == initialSize - 1;
    }

    private static Rectangle toGeometry(final RectObstacle2D o) {
        return Geometries.rectangle(o.getMinX(), o.getMinY(), o.getMaxX(), o.getMaxY());
    }

    private void writeObject(final ObjectOutputStream o) throws IOException {
        o.defaultWriteObject();
        o.writeObject(getObstacles());
    }

    @SuppressWarnings("unchecked")
    private void readObject(final ObjectInputStream o) throws ClassNotFoundException, IOException {
        o.defaultReadObject();
        rtree = RTree.create();
        rtree = RTree., Rectangle>create().add(
            ((List) o.readObject()).parallelStream()
                .map(obs -> (RectObstacle2D) obs)
                .map(obs -> new EntryDefault<>(obs, toGeometry(obs)))
                .collect(Collectors.toList())
        );
    }

}




© 2015 - 2024 Weber Informatics LLC | Privacy Policy