/* ==============================================
* Simtools : The tools library used in JSynoptic
* ==============================================
*
* Project Info: http://jsynoptic.sourceforge.net/index.html
*
* This library is free software; you can redistribute it and/or modify it under the terms
* of the GNU Lesser General Public License as published by the Free Software Foundation;
* either version 2.1 of the License, or (at your option) any later version.
*
* This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;
* without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
* See the GNU Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public License along with this
* library; if not, write to the Free Software Foundation, Inc., 59 Temple Place, Suite 330,
* Boston, MA 02111-1307, USA.
*
* (C) Copyright 1999-2003, by :
* Corporate:
* Astrium SAS
* EADS CRC
* Individual:
* Claude Cazenave
* Nicolas Brodu
*
*
* $Id: RightAnglePath.java,v 1.2 2009/01/19 16:39:24 ogor Exp $
*
* Changes
* -------
* 08-Sep-2008 : Initial public release (RO);
*
*/
package simtools.diagram.gate;
import java.awt.Graphics2D;
import java.awt.Point;
import java.awt.geom.Line2D;
import java.awt.geom.Rectangle2D;
import java.io.Serializable;
import java.util.ArrayList;
import java.util.List;
/**
* A RightAnglePath manages right-angle paths. All segments are either vertical or horizontal
* @author zxpletran007
*
*/
public class RightAnglePath extends Path {
private static final long serialVersionUID = -4559945676352181808L;
/**
* The list of right angle segments
*/
protected List segments;
/**
* The first node position
*/
protected Point firstPoint;
/**
* Create a right angle path from 2 points
* @param firstEnd
* @param lastEnd
*/
public RightAnglePath(Point firstEnd, Point lastEnd){
super();
segments = new ArrayList();
// Create first segments
createFirstSegments(firstEnd, lastEnd);
// Path simplification
updatePath();
}
/**
* Return the number of segments
* @return
*/
public int getSegmentNumber(){
return segments.size();
}
/**
* Return the segment at given index
* @param segmentIndex
* @return
*/
public Segment getSegment(int segmentIndex){
return (Segment)segments.get(segmentIndex);
}
/**
* Initialize the list of segments with 2 segments created from 2 points.
*/
private void createFirstSegments(Point firstEnd, Point lastEnd) {
// Clear the list of segments
segments.clear();
firstPoint = firstEnd;
// Create 2 segments
boolean horizontalFirst = Math.abs(firstEnd.x-lastEnd.x) > Math.abs(firstEnd.y-lastEnd.y);
if (horizontalFirst){
segments.add(new HorizontalSegment(lastEnd.x - firstEnd.x));
segments.add(new VerticalSegment(lastEnd.y - firstEnd.y));
} else {
segments.add(new VerticalSegment(lastEnd.y - firstEnd.y));
segments.add(new HorizontalSegment(lastEnd.x - firstEnd.x));
}
}
/* (non-Javadoc)
* @see simtools.diagram.gate.ConnectionPath#translateNode(int, int, int)
*/
public int translateNode(int nodeIndex, int dx, int dy) {
if (segments.size()==1){
Segment s = (Segment)segments.get(0);
Point secondPoint = new Point(firstPoint);
if (s instanceof HorizontalSegment){
secondPoint.x += s.length;
} else {
secondPoint.y += s.length;
}
if (nodeIndex==0){
firstPoint.x += dx;
firstPoint.y += dy;
} else {
secondPoint.x += dx;
secondPoint.y += dy;
}
createFirstSegments(firstPoint, secondPoint);
if (nodeIndex == 1){
nodeIndex = 2;
}
} else {
Segment ls = nodeIndex!=0? (Segment)segments.get(nodeIndex-1) : null;
Segment ns = (nodeIndex!=segments.size())? (Segment)segments.get(nodeIndex) : null;
boolean canSlitSegments = nodeIndex!= 0 && nodeIndex!= getNodeNumber()-1;
if (ls != null){ // Translate the segment before
if (ls instanceof HorizontalSegment){
nodeIndex = translateSegment(nodeIndex-1, dy, canSlitSegments) + 1;
} else {
nodeIndex = translateSegment(nodeIndex-1, dx, canSlitSegments) + 1;
}
} else { // translate the first point
if (ns instanceof HorizontalSegment){
firstPoint.x += dx;
ns.length -= dx;
} else {
firstPoint.y += dy;
ns.length -= dy;
}
}
if (ns != null){ // Translate the segment after
if (ns instanceof HorizontalSegment){
translateSegment(nodeIndex, dy, canSlitSegments);
} else {
translateSegment(nodeIndex, dx, canSlitSegments);
}
} else { // the last point
if (ls instanceof HorizontalSegment){
ls.length += dx;
} else {
ls.length += dy;
}
}
}
return nodeIndex;
}
/* (non-Javadoc)
* @see simtools.diagram.gate.ConnectionPath#translateSegment(int, int, boolean)
*/
protected int translateSegment(int segmentIndex, int dl, boolean canSlitSegment) {
Segment s = (Segment)segments.get(segmentIndex);
boolean isHorizontal = s instanceof HorizontalSegment;
int segmentLength = s.length;
Segment ls = segmentIndex!=0? (Segment)segments.get(segmentIndex-1) : null;
Segment ns = (segmentIndex!=segments.size()-1)? (Segment)segments.get(segmentIndex+1) : null;
boolean splitFirstEnd = canSlitSegment && segmentIndex == 0 && firstNodeIsLocked;
boolean splitLastEnd = canSlitSegment && segmentIndex == segments.size()-1 && lastNodeIsLocked;
if (splitFirstEnd){
if (isHorizontal) {
segments.add(0,new HorizontalSegment(segmentLength/3));
segments.add(1,new VerticalSegment(dl));
s.length -= segmentLength/3;
} else {
segments.add(0,new VerticalSegment(segmentLength/3));
segments.add(1,new HorizontalSegment(dl));
s.length -= segmentLength/3;
}
segmentIndex += 2;
} else {
if (ls != null){
ls.length += dl; // previous segment
} else {
if (s instanceof HorizontalSegment){ // translate first point
firstPoint.y += dl;
} else {
firstPoint.x += dl;
}
}
}
if (splitLastEnd){
if (isHorizontal) {
segments.add(new VerticalSegment(-dl));
segments.add(new HorizontalSegment(segmentLength/3));
s.length -= segmentLength/3;
} else {
segments.add(new HorizontalSegment(-dl));
segments.add(new VerticalSegment(segmentLength/3));
s.length -= segmentLength/3;
}
} else {
if (ns != null){
ns.length -= dl; // next segment
}
}
return segmentIndex;
}
/* (non-Javadoc)
* @see simtools.diagram.gate.ConnectionPath#translateEnd()
*/
public void updatePath(){
while( (removeNearNodes() || merveAlignedSegments() )){
}
}
/**
* Simplifies the list of nodes, performing following operations:
* <ul>
* <li>Take off point at same position than the previous one
* </ul>
* @return true if the node list has changed
*/
private boolean removeNearNodes(){
List segmentToDelete = new ArrayList();
int segmentNumber = getSegmentNumber();
if (segmentNumber > 1){
for (int i=0; i< segmentNumber; i++){
Segment s1 = (Segment)segments.get(i);
if ( Math.abs(s1.length) <= MAX_DISTANCE_FOR_MERGING){
// Re-adjust the node after S1
if (i+2 < segmentNumber){
((Segment)segments.get(i+2)).length += s1.length;
}
// Delete S
segmentToDelete.add(s1);
}
}
for (int i=segmentToDelete.size()-1; i>=0; i--){
if (segments.size()>1){ // We keep at least one segment
segments.remove( segmentToDelete.get(i));
}
}
}
return !segmentToDelete.isEmpty();
}
/**
* Simplifies the list of nodes, performing following operations:
* <ul>
* <li>Merge consecutive segments with same direction
* </ul>
* @return true if the node list has changed
*/
private boolean merveAlignedSegments(){
List segmentToDelete = new ArrayList();
int segmentNumber = getSegmentNumber();
if (segmentNumber > 1){
for (int i=0; i< segmentNumber-1; i++){
Segment s1 = (Segment)segments.get(i);
Segment s2 = (Segment)segments.get(i+1);
if ( ((s1 instanceof HorizontalSegment && s2 instanceof HorizontalSegment)
|| (s1 instanceof VerticalSegment && s2 instanceof VerticalSegment))){
//
s1.length += s2.length;
// Delete S2
segmentToDelete.add(s2);
i++; // skip s2
}
}
for (int i=0; i< segmentToDelete.size(); i++){
segments.remove( segmentToDelete.get(i));
}
}
return !segmentToDelete.isEmpty();
}
/* (non-Javadoc)
* @see simtools.diagram.gate.ConnectionPath#getNode(int)
*/
public Point getNode(int index) {
Point res = new Point(firstPoint);
for(int i=0; i<index;i++){
Segment s = getSegment(i);
if( s instanceof HorizontalSegment){
res.x += s.length;
}else {
res.y += s.length;
}
}
return res;
}
public int getNodeNumber() {
return segments.size() + 1 ;
}
/* (non-Javadoc)
* @see simtools.diagram.gate.ConnectionPath#translatePath(int, int)
*/
public void translatePath(int dx, int dy) {
firstPoint.x += dx;
firstPoint.y += dy;
}
protected static abstract class Segment implements Cloneable, Serializable{
private static final long serialVersionUID = -7123666248196877431L;
int length;
public Segment(int length){
this.length = length;
}
public Segment cloneSegment(){
Segment clone = null;
try {
clone = (Segment)super.clone();
clone.length = length;
} catch (CloneNotSupportedException e) {
e.printStackTrace();
}
return clone;
}
}
protected static class HorizontalSegment extends Segment {
private static final long serialVersionUID = 6523450410131472274L;
public HorizontalSegment(int length) {
super(length);
}
public boolean equals(Object o){
return(o instanceof HorizontalSegment) && ( (HorizontalSegment)o).length==length;
}
}
protected static class VerticalSegment extends Segment{
private static final long serialVersionUID = 6292061906391248549L;
public VerticalSegment(int length) {
super(length);
}
public boolean equals(Object o){
return(o instanceof VerticalSegment) && ( (VerticalSegment)o).length==length;
}
}
/* (non-Javadoc)
* @see simtools.diagram.gate.ConnectionPath#drawPath(java.awt.Graphics2D)
*/
public void drawPath(Graphics2D g) {
Point p1 = new Point(firstPoint);
Point p2;
for(int i=0;i< getSegmentNumber();i++){
Segment s = getSegment(i);
p2 = new Point(p1);
if(s instanceof HorizontalSegment){
p2.x += s.length;
} else {
p2.y += s.length;
}
g.draw(new Line2D.Double(p1, p2));
p1 = p2;
}
}
/* (non-Javadoc)
* @see java.lang.Object#toString()
*/
public String toString(){
String res ="";
Point p = new Point(firstPoint);
res+= "Node 0" + " : X=" + p.x + " : Y=" + p.y + "\n";
for(int i=0;i< getSegmentNumber();i++){
Segment s = getSegment(i);
if(s instanceof HorizontalSegment){
p.x += s.length;
} else {
p.y += s.length;
}
res+= "Node " + (i+1) + " : X=" + p.x + " : Y=" + p.y + "\n";
}
return res;
}
/* (non-Javadoc)
* @see simtools.diagram.gate.ConnectionPath#cloneConnectionPath()
*/
public Path clonePath(){
RightAnglePath clone = null;
try {
clone = (RightAnglePath)super.clone();
clone.firstPoint = new Point(firstPoint);
clone.segments = new ArrayList();
for(int i=0;i<getSegmentNumber(); i++){
Segment s = getSegment(i);
clone.segments.add(s.cloneSegment());
}
} catch (CloneNotSupportedException e) {
e.printStackTrace();
}
return clone;
}
/* (non-Javadoc)
* @see simtools.diagram.gate.ConnectionPath#contains(double, double)
*/
public boolean contains(double ox, double oy){
boolean res = false;
Rectangle2D area;
Point p = new Point(firstPoint);
for(int i=0;i< getSegmentNumber() && !res;i++){
Segment s = getSegment(i);
if (s instanceof HorizontalSegment){
area = new Rectangle2D.Double(
p.x + (s.length<0? s.length : 0) - MAX_DISTANCE_FROM_SEGMENT,
p.y - MAX_DISTANCE_FROM_SEGMENT,
Math.abs(s.length) + 2* MAX_DISTANCE_FROM_SEGMENT,
2 * MAX_DISTANCE_FROM_SEGMENT
);
p.x += s.length;
} else {
area = new Rectangle2D.Double(
p.x - MAX_DISTANCE_FROM_SEGMENT,
p.y + (s.length<0? s.length : 0) - MAX_DISTANCE_FROM_SEGMENT,
2 * MAX_DISTANCE_FROM_SEGMENT,
Math.abs(s.length) + 2* MAX_DISTANCE_FROM_SEGMENT
);
p.y += s.length;
}
if (area.contains(ox, oy)){
res = true;
}
}
return res;
}
public boolean equals(Object o){
boolean res = false;
if (o instanceof RightAnglePath){
RightAnglePath otherPath = (RightAnglePath)o;
res = firstPoint.equals(otherPath.firstPoint) && getSegmentNumber()==otherPath.getSegmentNumber();
for(int i=0;i < getSegmentNumber() && res;i++){
Segment p1 = getSegment(i);
Segment p2 = otherPath.getSegment(i);
res &= p1.equals(p2);
}
}
return res;
}
}