// License: GPL. For details, see LICENSE file.
package indoor_sweepline;
import java.util.Vector;
public class Beam {
public Beam(Vector<Double> blueprint, double blueprintOffset, CorridorPart.ReachableSide defaultSide) {
offset = blueprintOffset;
parts = new Vector<>();
setDefaultSide_(defaultSide);
if (defaultSide == CorridorPart.ReachableSide.RIGHT) {
for (int i = 1; i < blueprint.size(); i += 2) {
addCorridorPart_(true, CorridorPart.Type.WALL,
blueprint.elementAt(i).doubleValue() - blueprint.elementAt(i-1).doubleValue());
if (i+1 < blueprint.size())
addCorridorPart_(true, CorridorPart.Type.VOID,
blueprint.elementAt(i+1).doubleValue() - blueprint.elementAt(i).doubleValue());
}
} else {
for (int i = 1; i < blueprint.size(); i += 2) {
addCorridorPart_(true, CorridorPart.Type.PASSAGE,
blueprint.elementAt(i).doubleValue() - blueprint.elementAt(i-1).doubleValue());
if (i+1 < blueprint.size())
addCorridorPart_(true, CorridorPart.Type.VOID,
blueprint.elementAt(i+1).doubleValue() - blueprint.elementAt(i).doubleValue());
}
}
adjustStripCache();
}
private void setDefaultSide_(CorridorPart.ReachableSide defaultSide) {
this.defaultSide = defaultSide;
}
public void setDefaultSide(CorridorPart.ReachableSide defaultSide) {
setDefaultSide_(defaultSide);
adjustStripCache();
}
public Vector<CorridorPart> getBeamParts() {
return parts;
}
public double getBeamOffset() {
return offset;
}
public void setBeamOffset(double beamOffset) {
offset = beamOffset;
}
private void addCorridorPart_(boolean append, CorridorPart.Type type, double width) {
CorridorPart.ReachableSide side = defaultSide == CorridorPart.ReachableSide.RIGHT ?
defaultSide : CorridorPart.ReachableSide.ALL;
if (append)
parts.add(new CorridorPart(width, type, side));
else
parts.add(0, new CorridorPart(width, type, side));
}
public void addCorridorPart(boolean append, double width) {
addCorridorPart_(append,
defaultSide == CorridorPart.ReachableSide.RIGHT ? CorridorPart.Type.WALL : CorridorPart.Type.PASSAGE,
width);
adjustStripCache();
}
public void setCorridorPartWidth(int partIndex, double value) {
parts.elementAt(partIndex).width = value;
adjustStripCache();
}
public void setCorridorPartType(int partIndex, CorridorPart.Type type) {
parts.elementAt(partIndex).setType(type, defaultSide);
enforceSideCoherence();
adjustStripCache();
}
public void setCorridorPartSide(int partIndex, CorridorPart.ReachableSide side) {
parts.elementAt(partIndex).setSide(side, defaultSide);
enforceSideCoherence();
adjustStripCache();
}
private void enforceSideCoherence() {
for (int i = 1; i < parts.size(); ++i) {
if (parts.elementAt(i).getSide() != CorridorPart.ReachableSide.ALL
&& parts.elementAt(i-1).getSide() != CorridorPart.ReachableSide.ALL)
parts.elementAt(i).setSide(parts.elementAt(i-1).getSide(), defaultSide);
}
}
private boolean isVoidAbove(int i) {
return i == 0 || parts.elementAt(i-1).getType() == CorridorPart.Type.VOID
|| (parts.elementAt(i-1).getSide() == CorridorPart.ReachableSide.RIGHT
&& defaultSide == CorridorPart.ReachableSide.LEFT)
|| (parts.elementAt(i-1).getSide() == CorridorPart.ReachableSide.LEFT
&& defaultSide == CorridorPart.ReachableSide.RIGHT);
}
private boolean isVoidBelow(int i) {
return i == parts.size() || parts.elementAt(i).getType() == CorridorPart.Type.VOID
|| (parts.elementAt(i).getSide() == CorridorPart.ReachableSide.RIGHT
&& defaultSide == CorridorPart.ReachableSide.LEFT)
|| (parts.elementAt(i).getSide() == CorridorPart.ReachableSide.LEFT
&& defaultSide == CorridorPart.ReachableSide.RIGHT);
}
private boolean isPassageAbove(int i) {
return i > 0
&& parts.elementAt(i-1).getType() == CorridorPart.Type.PASSAGE
&& defaultSide == CorridorPart.ReachableSide.ALL;
}
private boolean isPassageBelow(int i) {
return i < parts.size()
&& parts.elementAt(i).getType() == CorridorPart.Type.PASSAGE
&& defaultSide == CorridorPart.ReachableSide.ALL;
}
private boolean isReachableLeft(int i) {
if (defaultSide == CorridorPart.ReachableSide.RIGHT)
return false;
if (parts.elementAt(i).getSide() == CorridorPart.ReachableSide.LEFT)
return true;
return defaultSide == CorridorPart.ReachableSide.LEFT;
}
private void connectTwoPos(StripPosition newPos, boolean toLeft) {
StripPosition other = null;
if (rhsStrips.size() > 0 && rhsStrips.elementAt(rhsStrips.size()-1).connectedTo == -1) {
newPos.connectedToSameSide = !toLeft;
newPos.connectedTo = rhsStrips.size()-1;
other = rhsStrips.elementAt(rhsStrips.size()-1);
} else {
newPos.connectedToSameSide = toLeft;
newPos.connectedTo = lhsStrips.size()-1;
other = lhsStrips.elementAt(lhsStrips.size()-1);
}
other.connectedToSameSide = newPos.connectedToSameSide;
if (toLeft) {
other.connectedTo = lhsStrips.size();
lhsStrips.add(newPos);
} else {
other.connectedTo = rhsStrips.size();
rhsStrips.add(newPos);
}
}
private class StripPosition {
StripPosition(int nodeIndex, double offset) {
this.nodeIndex = nodeIndex;
this.offset = offset;
connectedTo = -1;
connectedToSameSide = false;
}
public int nodeIndex;
public double offset;
public int connectedTo;
public boolean connectedToSameSide;
}
private double offset;
private Vector<CorridorPart> parts;
private Vector<StripPosition> lhsStrips;
private Vector<StripPosition> rhsStrips;
private void adjustStripCache() {
lhsStrips = new Vector<>();
rhsStrips = new Vector<>();
double offset = 0;
for (int i = 0; i <= parts.size(); ++i) {
if (isVoidBelow(i)) {
if (isPassageAbove(i)) {
StripPosition lhs = new StripPosition(i, offset);
StripPosition rhs = new StripPosition(i, offset);
lhs.connectedToSameSide = false;
lhs.connectedTo = rhsStrips.size();
rhs.connectedToSameSide = false;
rhs.connectedTo = lhsStrips.size();
lhsStrips.add(lhs);
rhsStrips.add(rhs);
} else if (!isVoidAbove(i))
connectTwoPos(new StripPosition(i, offset), isReachableLeft(i-1));
} else if (isPassageBelow(i)) {
if (isVoidAbove(i)) {
StripPosition lhs = new StripPosition(i, offset);
StripPosition rhs = new StripPosition(i, offset);
lhs.connectedToSameSide = false;
lhs.connectedTo = rhsStrips.size();
rhs.connectedToSameSide = false;
rhs.connectedTo = lhsStrips.size();
lhsStrips.add(lhs);
rhsStrips.add(rhs);
} else if (!isPassageAbove(i))
connectTwoPos(new StripPosition(i, offset), !isReachableLeft(i-1));
} else {
if (isVoidAbove(i)) {
if (isReachableLeft(i))
lhsStrips.add(new StripPosition(i, offset));
else
rhsStrips.add(new StripPosition(i, offset));
} else if (isPassageAbove(i)) {
if (isReachableLeft(i))
rhsStrips.add(new StripPosition(i, offset));
else
lhsStrips.add(new StripPosition(i, offset));
}
}
if (i < parts.size())
offset += parts.elementAt(i).width;
}
}
public Vector<Double> leftHandSideStrips() {
Vector<Double> offsets = new Vector<>();
for (StripPosition pos : lhsStrips) {
offsets.add(pos.offset);
}
return offsets;
}
public Vector<Double> rightHandSideStrips() {
Vector<Double> offsets = new Vector<>();
for (StripPosition pos : rhsStrips) {
offsets.add(pos.offset);
}
return offsets;
}
public int getBeamPartIndex(boolean toTheLeft, int i) {
if (toTheLeft)
return lhsStrips.elementAt(i).nodeIndex;
else
return rhsStrips.elementAt(i).nodeIndex;
}
public boolean appendNodes(IndoorSweeplineModel.SweepPolygonCursor cursor, boolean fromRight,
BeamGeography geography, String level) {
if (fromRight) {
StripPosition pos = rhsStrips.elementAt(cursor.partIndex);
StripPosition to = pos.connectedToSameSide ?
rhsStrips.elementAt(pos.connectedTo) : lhsStrips.elementAt(pos.connectedTo);
geography.appendNodes(pos.nodeIndex, to.nodeIndex, level);
if (!pos.connectedToSameSide)
--cursor.stripIndex;
cursor.partIndex = pos.connectedTo;
return !pos.connectedToSameSide;
} else {
StripPosition pos = lhsStrips.elementAt(cursor.partIndex);
StripPosition to = pos.connectedToSameSide ?
lhsStrips.elementAt(pos.connectedTo) : rhsStrips.elementAt(pos.connectedTo);
geography.appendNodes(pos.nodeIndex, to.nodeIndex, level);
if (!pos.connectedToSameSide)
++cursor.stripIndex;
cursor.partIndex = pos.connectedTo;
return pos.connectedToSameSide;
}
}
private CorridorPart.ReachableSide defaultSide;
}