org.jtrfp.trcl.SpacePartitioningGrid.java Source code

Java tutorial

Introduction

Here is the source code for org.jtrfp.trcl.SpacePartitioningGrid.java

Source

/*******************************************************************************
 * This file is part of TERMINAL RECALL
 * Copyright (c) 2012-2014 Chuck Ritola
 * Part of the jTRFP.org project
 * All rights reserved. This program and the accompanying materials
 * are made available under the terms of the GNU Public License v3.0
 * which accompanies this distribution, and is available at
 * http://www.gnu.org/licenses/gpl.html
 * 
 * Contributors:
 *     chuck - initial API and implementation
 ******************************************************************************/
package org.jtrfp.trcl;

import java.lang.ref.WeakReference;
import java.util.ArrayList;
import java.util.Collection;
import java.util.Collections;
import java.util.List;
import java.util.Map;
import java.util.WeakHashMap;

import org.apache.commons.math3.geometry.euclidean.threed.Vector3D;
import org.jtrfp.trcl.obj.Positionable;

import com.ochafik.util.Adapter;

public abstract class SpacePartitioningGrid<E extends Positionable> {
    private double squareSize, viewingRadius;
    private int squaresX, squaresY, squaresZ;
    private final List<E> alwaysVisible = new ArrayList<E>(300);
    private WeakReference<SpacePartitioningGrid<E>> parentGrid = null;
    private Map<SpacePartitioningGrid<E>, String> branchGrids = Collections
            .synchronizedMap(new WeakHashMap<SpacePartitioningGrid<E>, String>());

    private List<E>[] elements;
    private double radiusInWorldUnits;
    private int rolloverPoint, rawDia, rawDiaX, rawDiaY, rawDiaZ, xProgression, yProgression, zProgression;
    private final Adapter<Vector3D, Integer> cubeSpaceRasterizer = new Adapter<Vector3D, Integer>() {
        @Override
        public Integer adapt(Vector3D value) {
            return (int) (value.getX() + value.getY() * squaresX + value.getZ() * squaresX * squaresY);
        }//end adapt()

        @Override
        public Vector3D reAdapt(Integer value) {
            // TODO Auto-generated method stub
            return null;
        }
    };

    private final Adapter<Vector3D, Integer> worldSpaceRasterizer = new Adapter<Vector3D, Integer>() {
        @Override
        public Integer adapt(Vector3D value) {
            return (int) ((int) absMod(Math.round(value.getX() / getSquareSize()), squaresX)
                    + (int) absMod(Math.round(value.getY() / getSquareSize()), squaresY) * squaresX
                    + (int) absMod(Math.round(value.getZ() / getSquareSize()), squaresZ) * squaresX * squaresY);
        }//end adapt()

        @Override
        public Vector3D reAdapt(Integer value) {
            // TODO Auto-generated method stub
            return null;
        }
    };

    public SpacePartitioningGrid(SpacePartitioningGrid<E> parentGrid) {
        setParentGrid(parentGrid);
    }

    public void activate() {
        if (parentGrid != null) {
            final SpacePartitioningGrid g = parentGrid.get();
            if (g != null)
                g.addBranch(this);
        }
    }//end activate()

    public Adapter<Vector3D, Integer> getCubeSpaceRasterizer() {
        return cubeSpaceRasterizer;
    }

    public Adapter<Vector3D, Integer> getWorldSpaceRasterizer() {
        return worldSpaceRasterizer;
    }

    public void deactivate() {
        if (parentGrid != null) {
            final SpacePartitioningGrid g = parentGrid.get();
            if (g != null)
                g.removeBranch(this);
        }
    }//end deactivate()

    public void notifyBranchAdded(SpacePartitioningGrid b) {
        final SpacePartitioningGrid<E> g = parentGrid.get();
        if (g != null)
            g.notifyBranchAdded(b);
    }//end notifyBranchAdded(...)

    public void notifyBranchRemoved(SpacePartitioningGrid b) {
        final SpacePartitioningGrid<E> g = parentGrid.get();
        if (g != null)
            g.notifyBranchRemoved(b);
    }//end notifyBranchRemoved(...)

    private void addBranch(SpacePartitioningGrid<E> branchToAdd) {
        if (!branchGrids.containsKey(branchToAdd)) {
            branchGrids.put(branchToAdd, "");
            if (parentGrid == null)
                return;
            final SpacePartitioningGrid<E> g = parentGrid.get();
            if (g != null)
                g.notifyBranchAdded(branchToAdd);
        } //end if(!contains)
    }//end addBranch(...)

    private void removeBranch(SpacePartitioningGrid<E> branchToRemove) {
        if (branchGrids.remove(branchToRemove) != null) {
            if (parentGrid == null)
                return;
            final SpacePartitioningGrid<E> g = parentGrid.get();
            if (g != null)
                g.notifyBranchRemoved(branchToRemove);
        } //end if(!contains)
    }//end removeBranch(...)

    private void setParentGrid(SpacePartitioningGrid<E> parentGrid) {
        this.parentGrid = new WeakReference<SpacePartitioningGrid<E>>(parentGrid);
        setSquareSize(parentGrid.getSquareSize());
        setSquaresX(parentGrid.getSquaresX());
        setSquaresY(parentGrid.getSquaresY());
        setSquaresZ(parentGrid.getSquaresZ());
        setViewingRadius(parentGrid.getViewingRadius());

        allocateSquares();
    }//end setParentGrid(...)

    public SpacePartitioningGrid(Vector3D size, double squareSize, double viewingRadius) {
        setSquareSize(squareSize);
        setSquaresX((int) (size.getX() / squareSize));
        setSquaresY((int) (size.getY() / squareSize));
        setSquaresZ((int) (size.getZ() / squareSize));
        setViewingRadius(viewingRadius);

        allocateSquares();
    }//end constructor

    private void allocateSquares() {
        elements = new List[squaresX * squaresY * squaresZ];
        //Fudge factor to fix suddenly appearing terrain at distance
        radiusInWorldUnits = getViewingRadius() * 1.25;
        rolloverPoint = elements.length;
        rawDia = (int) ((radiusInWorldUnits * 2) / getSquareSize());
        rawDiaX = rawDia < getSquaresX() ? rawDia : getSquaresX();
        rawDiaY = rawDia < getSquaresY() ? rawDia : getSquaresY();
        rawDiaZ = rawDia < getSquaresZ() ? rawDia : getSquaresZ();
        zProgression = getSquaresX() * getSquaresY() - rawDiaY * getSquaresX();
        yProgression = getSquaresX() - rawDiaX;
        xProgression = 1;
    }//end allocateSquares()

    public synchronized void add(E objectWithPosition) {
        //Figure out where it goes
        if (objectWithPosition == null)
            throw new NullPointerException("Passed objectWithPosition is intolerably null.");
        objectWithPosition.setContainingGrid(this);
    }

    public synchronized void remove(E objectWithPosition) {
        objectWithPosition.setContainingGrid(null);
    }//end remove(...)

    private static double absMod(double value, double mod) {
        if (value >= -0.) {
            return value % mod;
        }
        value *= -1;
        value %= mod;
        if (value == 0)
            return 0;
        return mod - value;
    }//end absMod

    public void cubesWithinRadiusOf(Vector3D centerInWorldUnits, Submitter<List<E>> submitter) {
        recursiveAlwaysVisibleGridCubeSubmit(submitter);
        final double[] startPoint = centerInWorldUnits
                .subtract(new Vector3D(radiusInWorldUnits, radiusInWorldUnits, radiusInWorldUnits)).toArray();
        int startRaw = worldSpaceRasterizer.adapt(new Vector3D(startPoint[0], startPoint[1], startPoint[2]));

        final int zEnd = startRaw + getSquaresX() * getSquaresY() * rawDiaZ + (rawDiaY * getSquaresX()) + (rawDiaX);
        for (int point = startRaw; point < zEnd; point += zProgression) {//Z
            final int yEnd = point + getSquaresX() * rawDiaY;
            for (; point < yEnd; point += yProgression) {//Y
                final int xEnd = point + rawDiaX;
                for (; point < xEnd; point += xProgression) {//X
                    final int wrappedPoint = point % rolloverPoint;
                    recursiveGridCubeSubmit(submitter, wrappedPoint);
                } //end for(X)
            } //end for(Y)
        } //end for(Z)
    }//end cubesWithRadiusOf(...)

    @SuppressWarnings("unchecked")
    public void itemsWithinRadiusOf(Vector3D centerInWorldUnits, Submitter<E> submitter) {
        recursiveAlwaysVisibleSubmit(submitter);

        final double[] startPoint = centerInWorldUnits
                .subtract(new Vector3D(radiusInWorldUnits, radiusInWorldUnits, radiusInWorldUnits)).toArray();
        int startRaw = worldSpaceRasterizer.adapt(new Vector3D(startPoint[0], startPoint[1], startPoint[2]));

        final int zEnd = startRaw + getSquaresX() * getSquaresY() * rawDiaZ + (rawDiaY * getSquaresX()) + (rawDiaX);
        for (int point = startRaw; point < zEnd; point += zProgression) {//Z
            final int yEnd = point + getSquaresX() * rawDiaY;
            for (; point < yEnd; point += yProgression) {//Y
                final int xEnd = point + rawDiaX;
                for (; point < xEnd; point += xProgression) {//X
                    final int wrappedPoint = point % rolloverPoint;
                    recursiveBlockSubmit(submitter, wrappedPoint);
                } //end for(X)
            } //end for(Y)
        } //end for(Z)
    }//end itemsInRadiusOf(...)

    private void recursiveAlwaysVisibleSubmit(Submitter<E> sub) {
        sub.submit(alwaysVisible);
        synchronized (branchGrids) {
            for (SpacePartitioningGrid<E> g : branchGrids.keySet())
                g.recursiveAlwaysVisibleSubmit(sub);
        } //end sync(branchGrids)
    }// end recursiveAlwaysVisisbleSubmit(...)

    private void recursiveAlwaysVisibleGridCubeSubmit(Submitter<List<E>> sub) {
        sub.submit(alwaysVisible);
        synchronized (branchGrids) {

            for (SpacePartitioningGrid<E> g : branchGrids.keySet())
                g.recursiveAlwaysVisibleGridCubeSubmit(sub);
        } //end sync(branchGrids)
    }// end recursiveAlwaysVisisbleSubmit(...)

    private void recursiveBlockSubmit(Submitter<E> sub, int blockID) {
        final List<E> elements = this.elements[blockID];
        if (elements != null) {
            synchronized (elements) {
                final int size = elements.size();
                for (int i = 0; i < size; i++) {
                    sub.submit(elements.get(i));
                } // end for(size)
            } // end sync(elements)
        } // end if(!null)
        synchronized (branchGrids) {
            for (SpacePartitioningGrid<E> g : branchGrids.keySet())
                g.recursiveBlockSubmit(sub, blockID);
        } //end sync(branchGrids)
    }// end recusiveBlockSubmit(...)

    private void recursiveGridCubeSubmit(Submitter<List<E>> sub, int blockID) {
        sub.submit(elements[blockID]);
        synchronized (branchGrids) {
            for (SpacePartitioningGrid<E> g : branchGrids.keySet())
                g.recursiveGridCubeSubmit(sub, blockID);
        } //end sync(branchGrids)
    }// end recusiveGridCubeSubmit(...)

    private Collection<E> getAlwaysVisible() {
        return alwaysVisible;
    }

    /**
     * @return the squareSize
     */
    public double getSquareSize() {
        return squareSize;
    }

    /**
     * @param squareSize the squareSize to set
     */
    public void setSquareSize(double squareSize) {
        this.squareSize = squareSize;
    }

    /**
     * @return the squaresX
     */
    public int getSquaresX() {
        return squaresX;
    }

    /**
     * @param squaresX the squaresX to set
     */
    public void setSquaresX(int squaresX) {
        if (squaresX <= 0)
            throw new RuntimeException("Invalid size: " + squaresX);
        this.squaresX = squaresX;
    }

    /**
     * @return the squaresY
     */
    public int getSquaresY() {
        return squaresY;
    }

    /**
     * @param squaresY the squaresY to set
     */
    public void setSquaresY(int squaresY) {
        if (squaresY <= 0)
            throw new RuntimeException("Invalid size: " + squaresY);
        this.squaresY = squaresY;
    }

    /**
     * @return the squaresZ
     */
    public int getSquaresZ() {
        return squaresZ;
    }

    /**
     * @param squaresZ the squaresZ to set
     */
    public void setSquaresZ(int squaresZ) {
        if (squaresZ <= 0)
            throw new RuntimeException("Invalid size: " + squaresZ);
        this.squaresZ = squaresZ;
    }

    /**
     * @return the viewingRadius
     */
    public double getViewingRadius() {
        return viewingRadius;
    }

    /**
     * @param viewingRadius the viewingRadius to set
     */
    public void setViewingRadius(double viewingRadius) {
        this.viewingRadius = viewingRadius;
    }

    public void removeDirect(int flatPos, E objectWithPosition) {
        List<E> list = elements[flatPos];
        if (list == null)
            return;
        synchronized (list) {
            if (list.remove(objectWithPosition))
                objectWithPosition.setContainingGrid(null);
            elements[flatPos].remove(objectWithPosition);
        } //end sync(list)
    }//end removeDirect(...)

    public void addDirect(int flatPos, E objectWithPosition) {
        List<E> list = elements[flatPos];
        if (list == null)
            elements[flatPos] = list = new ArrayList<E>(8);
        synchronized (list) {
            list.add(objectWithPosition);
        }
    }//end addDirect(...)

    public List<E> world2List(double x, double y, double z, boolean newListIfNull) {
        final int pos = worldSpaceRasterizer.adapt(new Vector3D(x, y, z));
        List<E> result = elements[pos];
        if (newListIfNull && result == null)
            result = elements[pos] = new ArrayList<E>(8);
        return result;
    }//end world2List

    public List<E> getAlwaysVisibleList() {
        return alwaysVisible;
    }

    public void removeAll() {
        final ArrayList<SpacePartitioningGrid> branches = new ArrayList<SpacePartitioningGrid>();
        for (SpacePartitioningGrid g : branchGrids.keySet())
            branches.add(g);
        for (SpacePartitioningGrid g : branches)
            removeBranch(g);
        final ArrayList<E> alwaysVisible = new ArrayList<E>();
        for (E e : getAlwaysVisibleList())
            alwaysVisible.add(e);
        for (E e : alwaysVisible)
            remove(e);
        final ArrayList<E> everythingElse = new ArrayList<E>();
        for (List<E> l : elements)
            if (l != null)
                for (E e : l)
                    everythingElse.add(e);
        for (E e : everythingElse)
            remove(e);
    }//end removeAll()
}//end SpacePartitionGrid