osd-contiki/tools/cooja/apps/mrm/java/se/sics/mrm/AngleInterval.java

416 lines
13 KiB
Java

/*
* Copyright (c) 2006, Swedish Institute of Computer Science.
* All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions
* are met:
* 1. Redistributions of source code must retain the above copyright
* notice, this list of conditions and the following disclaimer.
* 2. Redistributions in binary form must reproduce the above copyright
* notice, this list of conditions and the following disclaimer in the
* documentation and/or other materials provided with the distribution.
* 3. Neither the name of the Institute nor the names of its contributors
* may be used to endorse or promote products derived from this software
* without specific prior written permission.
*
* THIS SOFTWARE IS PROVIDED BY THE INSTITUTE AND CONTRIBUTORS ``AS IS'' AND
* ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
* ARE DISCLAIMED. IN NO EVENT SHALL THE INSTITUTE OR CONTRIBUTORS BE LIABLE
* FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
* DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
* OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
* HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
* LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
* OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
* SUCH DAMAGE.
*
*/
package se.sics.mrm;
import java.awt.geom.Line2D;
import java.awt.geom.Point2D;
import java.util.Vector;
import org.apache.log4j.Logger;
/**
* This class represents an angle interval.
*
* @author Fredrik Osterlind
*/
class AngleInterval {
private static Logger logger = Logger.getLogger(AngleInterval.class);
// Sub intervals all between 0 and 2*PI
Vector<Interval> subIntervals;
/**
* Creates a new angle interval.
*
* @param startAngle Start angle (rad)
* @param endAngle End angle (rad) (> start angle)
*/
public AngleInterval(double startAngle, double endAngle) {
subIntervals = new Vector<Interval>();
if (endAngle < startAngle) {
} else if (endAngle - startAngle >= 2*Math.PI) {
subIntervals.add(new Interval(0, 2*Math.PI));
} else {
while (startAngle < 0)
startAngle += 2*Math.PI;
while (endAngle < 0)
endAngle += 2*Math.PI;
startAngle %= 2*Math.PI;
endAngle %= 2*Math.PI;
Interval tempInterval;
if (startAngle < endAngle) {
tempInterval = new Interval(startAngle, endAngle);
if (!tempInterval.isEmpty())
subIntervals.add(tempInterval);
} else {
tempInterval = new Interval(startAngle, 2*Math.PI);
if (!tempInterval.isEmpty())
subIntervals.add(tempInterval);
tempInterval = new Interval(0, endAngle);
if (!tempInterval.isEmpty())
subIntervals.add(tempInterval);
}
}
}
/**
* Returns new intervals consisting of this interval with the given interval removed.
* These can either be null (if entire interval was removed),
* one interval (if upper or lower part, or nothing was removed) or two intervals
* (if middle part of interval was removed).
*
* @param intervalToSubtract Other interval
* @return New intervals
*/
public Vector<AngleInterval> subtract(AngleInterval intervalToSubtract) {
Vector<Interval> afterSubtractionIntervals = new Vector<Interval>();
// Before subtraction
afterSubtractionIntervals.addAll(subIntervals);
if (intervalToSubtract == null) {
Vector<AngleInterval> ret = new Vector<AngleInterval>();
ret.add(this);
return ret;
}
// Subtract every subinterval each
for (int i=0; i < intervalToSubtract.subIntervals.size(); i++) {
Interval subIntervalToSubtract = intervalToSubtract.subIntervals.get(i);
Vector<Interval> newAfterSubtractionIntervals = new Vector<Interval>();
for (int j=0; j < afterSubtractionIntervals.size(); j++) {
Vector<Interval> tempIntervals = afterSubtractionIntervals.get(j).subtract(subIntervalToSubtract);
if (tempIntervals != null)
newAfterSubtractionIntervals.addAll(tempIntervals);
}
afterSubtractionIntervals = newAfterSubtractionIntervals;
}
Vector<AngleInterval> newAngleIntervals = new Vector<AngleInterval>();
for (int i=0; i < afterSubtractionIntervals.size(); i++) {
if (afterSubtractionIntervals.get(i) != null && !afterSubtractionIntervals.get(i).isEmpty())
newAngleIntervals.add(
new AngleInterval(afterSubtractionIntervals.get(i).getLow(), afterSubtractionIntervals.get(i).getHigh())
);
}
return newAngleIntervals;
}
/**
* Returns the intersection of this interval with
* the given.
*
* @param interval Other interval
* @return Intersection
*/
public AngleInterval intersectWith(AngleInterval interval) {
Vector<Interval> afterIntersectionIntervals = new Vector<Interval>();
// Intersect all subintervals, keep all results
for (int i=0; i < interval.subIntervals.size(); i++) {
for (int j=0; j < subIntervals.size(); j++) {
Interval temp = interval.subIntervals.get(i).intersectWith(subIntervals.get(j));
if (temp != null && !temp.isEmpty())
afterIntersectionIntervals.add(temp);
}
}
if (afterIntersectionIntervals.size() > 2) {
logger.fatal("AngleInterval.intersectWith() error!");
} else if (afterIntersectionIntervals.size() == 2) {
// The interval (y-x) is divided into:
// y -> 2*PI
// 0 -> x
Interval interval1 = afterIntersectionIntervals.get(0);
Interval interval2 = afterIntersectionIntervals.get(1);
if (interval1.getLow() == 0)
return new AngleInterval(
interval2.getLow(), interval1.getHigh() + 2*Math.PI
);
else
return new AngleInterval(
interval1.getLow(), interval2.getHigh() + 2*Math.PI
);
} else if (afterIntersectionIntervals.size() == 1) {
return new AngleInterval(
afterIntersectionIntervals.firstElement().getLow(),
afterIntersectionIntervals.firstElement().getHigh()
);
}
return null;
}
/**
* Returns start angle of this interval.
* This angle is always > 0 and < the end angle.
*
* @return Start angle
*/
public double getStartAngle() {
if (subIntervals == null || subIntervals.isEmpty()) {
logger.warn("Getting start angle of null angle interval!");
return 0;
}
if (subIntervals.size() > 2) {
logger.warn("Getting start angle of malformed angle interval!");
return 0;
}
if (subIntervals.size() == 1) {
return subIntervals.firstElement().getLow();
}
// The interval (y-x) is divided into:
// y -> 2*PI
// 0 -> x
Interval interval1 = subIntervals.get(0);
Interval interval2 = subIntervals.get(1);
if (interval1.getLow() == 0)
return interval2.getLow();
else
return interval1.getLow();
}
/**
* Returns end angle of this interval.
* This angle is always > start angle, and may be > 2*PI.
*
* @return End angle
*/
public double getEndAngle() {
if (subIntervals == null || subIntervals.isEmpty()) {
logger.warn("Getting start angle of null angle interval!");
return 0;
}
if (subIntervals.size() > 2) {
logger.warn("Getting start angle of malformed angle interval!");
return 0;
}
if (subIntervals.size() == 1) {
return subIntervals.firstElement().getHigh();
}
// The interval (y-x) is divided into:
// y -> 2*PI
// 0 -> x
Interval interval1 = subIntervals.get(0);
Interval interval2 = subIntervals.get(1);
if (interval1.getLow() == 0)
return interval1.getHigh() + 2*Math.PI;
else
return interval2.getHigh() + 2*Math.PI;
}
/**
* @return Size of interval (rad)
*/
public double getSize() {
double size = 0;
for (int i=0; i < subIntervals.size(); i++)
size += subIntervals.get(i).getSize();
return size;
}
/**
* Checks if the given interval is a subset of this interval.
*
* @param interval Other interval
* @return True if this interval contains given interval
*/
public boolean contains(AngleInterval interval) {
// Check that all parts of argument is contained by any part of this
for (int i=0; i < interval.subIntervals.size(); i++) {
boolean contained = false;
for (int j=0; j < subIntervals.size(); j++) {
if (subIntervals.get(j).contains(interval.subIntervals.get(i))) {
contained = true;
break;
}
}
if (!contained)
return false;
}
return true;
}
/**
* Checks if the two intervals intersect.
*
* @param interval Other interval
* @return True if this interval intersects given interval
*/
public boolean intersects(AngleInterval interval) {
return (intersectWith(interval) != null);
}
/**
* @return True if interval defined is of no size.
*/
public boolean isEmpty() {
if (subIntervals.isEmpty())
return true;
if (getSize() <= 0.001)
return true;
return false;
}
public String toString() {
String retString = "";
for (int i=0; i < subIntervals.size(); i++) {
if (!retString.equals(""))
retString = retString.concat(" && ");
retString = retString.concat("(");
retString = retString.concat(Math.toDegrees(subIntervals.get(i).getLow()) + " -> " + Math.toDegrees(subIntervals.get(i).getHigh()));
retString = retString.concat(")");
}
return retString;
}
/**
* Returns a line starting at given start point and
* in the given direction.
* This line may be used when calculating intersection points.
*
* @param startPoint Start point
* @param angle Line direction (rad)
* @return Line
*/
public static Line2D getDirectedLine(Point2D startPoint, double angle, double length) {
return new Line2D.Double(
startPoint.getX(),
startPoint.getY(),
startPoint.getX() + length*Math.cos(angle),
startPoint.getY() + length*Math.sin(angle)
);
}
/**
* Returns an angle interval of the given line seen from
* the given reference point.
*
* @param refPoint Reference point
* @param line Line to measure angle against
* @return Angle interval (-pi <-> pi)
*/
public static AngleInterval getAngleIntervalOfLine(Point2D refPoint, Line2D line) {
// Create angle interval of this line
double x1 = line.getX1() - refPoint.getX();
double y1 = line.getY1() - refPoint.getY();
double x2 = line.getX2() - refPoint.getX();
double y2 = line.getY2() - refPoint.getY();
double angle1 = Math.atan2(y1, x1);
double angle2 = Math.atan2(y2, x2);
// If interval is bigger than PI, line angles must wrap
if (Math.abs(angle1 - angle2) > Math.PI) {
if (angle1 < 0)
angle1 += 2*Math.PI;
else
angle2 += 2*Math.PI;
}
return new AngleInterval(
Math.min(angle1, angle2), Math.max(angle1, angle2)
);
}
public boolean equals(Object object) {
if (object == null)
return false;
AngleInterval interval = (AngleInterval) object;
return (interval.getStartAngle() == this.getStartAngle() && interval.getEndAngle() == this.getEndAngle());
}
/**
* Subtracts given interval from all intervals in given vector.
* This method never returns null (but empty vectors).
*
* @param initialIntervals Initial intervals
* @param interval Interval to subtract
* @return New intervals
*/
public static Vector<AngleInterval> subtract(Vector<AngleInterval> initialIntervals, AngleInterval interval) {
Vector<AngleInterval> newIntervals = new Vector<AngleInterval>();
for (int i=0; i < initialIntervals.size(); i++) {
Vector<AngleInterval> tempIntervals = initialIntervals.get(i).subtract(interval);
if (tempIntervals != null) {
newIntervals.addAll(tempIntervals);
}
}
return newIntervals;
}
/**
* Intersects given interval with all intervals in given vector.
* This method never returns null (but empty vectors).
*
* @param initialIntervals Initial intervals
* @param interval Interval to intersect
* @return New intervals
*/
public static Vector<AngleInterval> intersect(Vector<AngleInterval> initialIntervals, AngleInterval interval) {
Vector<AngleInterval> newIntervals = new Vector<AngleInterval>();
for (int i=0; i < initialIntervals.size(); i++) {
AngleInterval tempInterval = initialIntervals.get(i).intersectWith(interval);
if (tempInterval != null) {
newIntervals.add(tempInterval);
}
}
return newIntervals;
}
}