/*******************************************************************************
* Copyright 2010 Simon Mieth
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
******************************************************************************/
/*
* Created on 13.04.2005
*
*/
package org.kabeja.entities;
import java.util.ArrayList;
import java.util.Iterator;
import java.util.List;
import org.kabeja.DraftDocument;
import org.kabeja.common.Type;
import org.kabeja.math.Bounds;
import org.kabeja.math.MathUtils;
import org.kabeja.math.Point3D;
import org.kabeja.math.TransformContext;
import org.kabeja.math.Vector;
/**
* @author <a href="mailto:simon.mieth@gmx.de>Simon Mieth</a>
*
*/
public class LWPolyline extends Entity{
private final static int LAZY_INDEX_CONSTANTWIDTH=10;
private final static int LAZY_INDEX_STARTWIDTH=11;
private final static int LAZY_INDEX_ENDWIDTH=12;
private final static int LAZY_INDEX_ELEVATION=13;
private final static int BOOLEAN_BIT_CONSTANTWIDTH=10;
protected static final double QUARTER_CIRCLE_ANGLE = Math.tan(0.39269908169872414D);
protected List<LW2DVertex> vertices = new ArrayList<LW2DVertex>();
public LWPolyline() {
}
public void setConstantWidth(double width) {
if(width != 0.0){
this.lazyContainer.set(new Double(width), LAZY_INDEX_CONSTANTWIDTH);
}
}
public double getContstantWidth() {
if(this.lazyContainer.contains(LAZY_INDEX_CONSTANTWIDTH)){
return ((Double)this.lazyContainer.get(LAZY_INDEX_CONSTANTWIDTH)).doubleValue();
}
return 0.0;
}
public Type<LWPolyline>getType() {
return Type.TYPE_LWPOLYLINE;
}
public Bounds getBounds() {
Bounds bounds = new Bounds();
Iterator<LW2DVertex> i = vertices.iterator();
if (i.hasNext()) {
LW2DVertex last;
LW2DVertex first;
LW2DVertex v = null;
last = first = i.next();
bounds.addToBounds(last.getX(),last.getY(),0.0);
while (i.hasNext()) {
v=i.next();
addToBounds(last, v, bounds);
last = v;
}
if ((v != null) && (v.getBulge() != 0.0)) {
addToBounds(v, first, bounds);
}
} else {
bounds.setValid(false);
}
return bounds;
}
public void addVertex(LW2DVertex vertex) {
vertices.add(vertex);
}
public int getVertexCount() {
return this.vertices.size();
}
public List<LW2DVertex> getVertices() {
return this.vertices;
}
public void removeVertex(LW2DVertex vertex) {
// remove and check the constantwidth
this.setBit(BOOLEAN_BIT_CONSTANTWIDTH, true);
for(Iterator<LW2DVertex> i=this.vertices.iterator();i.hasNext();){
LW2DVertex v =i.next();
if (v == vertex) {
i.remove();
} else if (!v.isConstantWidth()) {
this.setBit(BOOLEAN_BIT_CONSTANTWIDTH, false);
}
}
}
public void removeVertex(int index) {
this.setBit(BOOLEAN_BIT_CONSTANTWIDTH, true);
int count=0;
for(Iterator<LW2DVertex> i=this.vertices.iterator();i.hasNext();){
LW2DVertex v =i.next();
if (count == index) {
i.remove();
} else if (!v.isConstantWidth()) {
this.setBit(BOOLEAN_BIT_CONSTANTWIDTH, false);
}
count++;
}
}
public LW2DVertex getVertex(int i) {
return (LW2DVertex) vertices.get(i);
}
/**
* Caculate the radius of a cut circle segment between 2 Vertex
*
* @param bulge
* the vertex bulge
* @param length
* the length of the circle cut
*/
public double getRadius(double bulge, double length) {
double h = (bulge * length) / 2;
double value = (h / 2) + (Math.pow(length, 2) / (8 * h));
return Math.abs(value);
}
/**
* @return Returns the endWidth.
*/
public double getEndWidth() {
if(this.lazyContainer.contains(LAZY_INDEX_ENDWIDTH)){
return ((Double)this.lazyContainer.get(LAZY_INDEX_ENDWIDTH)).doubleValue();
}
return 0.0;
}
/**
* @param endWidth
* The endWidth to set.
*/
public void setEndWidth(double endWidth) {
if(endWidth!=0.0){
this.lazyContainer.set(new Double(endWidth),LAZY_INDEX_ENDWIDTH);
}
}
/**
* @return Returns the startWidth.
*/
public double getStartWidth() {
if(this.lazyContainer.contains(LAZY_INDEX_STARTWIDTH)){
return ((Double)this.lazyContainer.get(LAZY_INDEX_STARTWIDTH)).doubleValue();
}
return 0.0;
}
/**
* @param startWidth
* The startWidth to set.
*/
public void setStartWidth(double startWidth) {
if(startWidth!=0.0){
this.lazyContainer.set(new Double(startWidth),LAZY_INDEX_STARTWIDTH);
}
}
public boolean isClosed() {
// the closed Flag
return (this.flags & 1) == 1;
}
public boolean isConstantWidth() {
//TODO review to see if the
//property is always set correct
if (!isBitEnabled(BOOLEAN_BIT_CONSTANTWIDTH)) {
return false;
} else {
setBit(BOOLEAN_BIT_CONSTANTWIDTH, true);
Iterator<LW2DVertex> i = vertices.iterator();
while (i.hasNext()) {
LW2DVertex vertex = i.next();
if (!vertex.isConstantWidth()) {
setBit(BOOLEAN_BIT_CONSTANTWIDTH, false);
return isBitEnabled(BOOLEAN_BIT_CONSTANTWIDTH);
}
}
}
return isBitEnabled(BOOLEAN_BIT_CONSTANTWIDTH);
}
protected void addToBounds(LW2DVertex start, LW2DVertex end, Bounds bounds) {
if (start.getBulge() != 0) {
// calculte the height
double l = MathUtils.distance(start.getPoint(), end.getPoint());
// double h = Math.abs(last.getBulge()) * l / 2;
double r = this.getRadius(start.getBulge(), l);
double s = l / 2;
Vector edgeDirection = MathUtils.getVector(start.getPoint(),
end.getPoint());
edgeDirection = MathUtils.normalize(edgeDirection);
Point3D centerPoint = MathUtils.getPointOfStraightLine(start.getPoint(),
edgeDirection, s);
Vector centerPointDirection = MathUtils.crossProduct(edgeDirection,
this.getExtrusion().getNormal());
centerPointDirection = MathUtils.normalize(centerPointDirection);
// double t = Math.sqrt(Math.pow(r, 2) - Math.pow(s, 2));
// double t = 0;
double h = Math.abs(start.getBulge() * l) / 2;
// if(Math.abs(start.getBulge())>=1.0){
// t = h-r;
// }else{
// //t = Math.sqrt(Math.pow(r, 2) - Math.pow(s, 2));
// t=r-h;
// }
// the center point of the arc
int startQ = 0;
int endQ = 0;
double bulge = start.getBulge();
if (bulge > 0) {
// the arc goes over the right side, but where is the center
// point?
if (bulge > 1.0) {
double t = h - r;
centerPoint = MathUtils.getPointOfStraightLine(centerPoint,
centerPointDirection, t);
} else {
double t = r - h;
centerPoint = MathUtils.getPointOfStraightLine(centerPoint,
centerPointDirection, (-1 * t));
}
endQ = MathUtils.getQuadrant(end.getPoint(), centerPoint);
startQ = MathUtils.getQuadrant(start.getPoint(), centerPoint);
} else {
// the arc goes over the left side, but where is the center
// point?
if (bulge < -1.0) {
double t = h - r;
centerPoint = MathUtils.getPointOfStraightLine(centerPoint,
centerPointDirection, (-1 * t));
} else {
double t = r - h;
centerPoint = MathUtils.getPointOfStraightLine(centerPoint,
centerPointDirection, t);
}
startQ = MathUtils.getQuadrant(end.getPoint(), centerPoint);
endQ = MathUtils.getQuadrant(start.getPoint(), centerPoint);
}
if (endQ < startQ) {
endQ += 4;
} else if ((endQ == startQ) &&
(Math.abs(start.getBulge()) > QUARTER_CIRCLE_ANGLE)) {
endQ += 4;
}
while (endQ > startQ) {
switch (startQ) {
case 0:
bounds.addToBounds(centerPoint.getX(),
centerPoint.getY() + r, centerPoint.getZ());
break;
case 1:
bounds.addToBounds(centerPoint.getX() - r,
centerPoint.getY(), centerPoint.getZ());
break;
case 2:
bounds.addToBounds(centerPoint.getX(),
centerPoint.getY() - r, centerPoint.getZ());
break;
case 3:
bounds.addToBounds(centerPoint.getX() + r,
centerPoint.getY(), centerPoint.getZ());
endQ -= 4;
startQ -= 4;
break;
}
startQ++;
}
}
bounds.addToBounds(start.getPoint());
bounds.addToBounds(end.getPoint());
}
public double getLength() {
double length = 0.0;
// a normal polyline with or without bulges
Iterator<LW2DVertex> i = this.vertices.iterator();
LW2DVertex first;
LW2DVertex last = first = i.next();
while (i.hasNext()) {
LW2DVertex v = i.next();
length += this.getSegmentLength(last, v);
last = v;
}
if (this.isClosed()) {
length += this.getSegmentLength(last, first);
}
return length;
}
protected double getSegmentLength(LW2DVertex start, LW2DVertex end) {
double l = MathUtils.distance(start.getPoint(), end.getPoint());
if (start.getBulge() == 0.0) {
return l;
} else {
double alpha = 4 * Math.atan(Math.abs(start.getBulge()));
double r = l / (2 * Math.sin(alpha / 2));
double d = (Math.PI * Math.toDegrees(alpha) * r) / 180;
return d;
}
}
public void setClosed(boolean b){
if(b){
this.flags = this.flags | 1;
}else if(this.isClosed()){
this.flags = this.flags ^ 1;
}
}
/**
* @return the elevation
*/
public Point3D getElevation() {
if (this.lazyContainer.contains(LAZY_INDEX_ELEVATION)) {
return (Point3D) this.lazyContainer.get(LAZY_INDEX_ELEVATION);
}
return new Point3D() {
public void setX(double x) {
this.addToContainer();
super.setX(x);
}
public void setY(double y) {
this.addToContainer();
super.setY(y);
}
public void setZ(double z) {
this.addToContainer();
super.setZ(z);
}
private void addToContainer() {
if (!lazyContainer.contains(LAZY_INDEX_ELEVATION)) {
lazyContainer.set(this, LAZY_INDEX_ELEVATION);
}
}
};
}
/**
* @param elevation the elevation to set
*/
public void setElevation(Point3D elevation) {
if(elevation.getX()!= 0.0 && elevation.getY()!=0.0 && elevation.getZ()!=0.0){
this.lazyContainer.set(elevation, LAZY_INDEX_ELEVATION);
}
}
public void setDocument(DraftDocument doc) {
super.setDocument(doc);
}
/**
* Not implemented yet
*/
public void transform(TransformContext context) {
}
}