fiore@0: /* fiore@0: CCmI Editor - A Collaborative Cross-Modal Diagram Editing Tool fiore@0: fiore@0: Copyright (C) 2011 Queen Mary University of London (http://ccmi.eecs.qmul.ac.uk/) fiore@0: fiore@0: This program is free software: you can redistribute it and/or modify fiore@0: it under the terms of the GNU General Public License as published by fiore@0: the Free Software Foundation, either version 3 of the License, or fiore@0: (at your option) any later version. fiore@0: fiore@0: This program is distributed in the hope that it will be useful, fiore@0: but WITHOUT ANY WARRANTY; without even the implied warranty of fiore@0: MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the fiore@0: GNU General Public License for more details. fiore@0: fiore@0: You should have received a copy of the GNU General Public License fiore@0: along with this program. If not, see . fiore@0: */ fiore@0: fiore@0: package uk.ac.qmul.eecs.ccmi.simpletemplate; fiore@0: fiore@0: import java.awt.Shape; fiore@0: import java.awt.geom.AffineTransform; fiore@0: import java.awt.geom.GeneralPath; fiore@0: import java.awt.geom.Path2D; fiore@0: import java.awt.geom.Point2D; fiore@0: import java.awt.geom.Rectangle2D; fiore@0: import java.awt.geom.Rectangle2D.Double; fiore@0: import java.io.InputStream; fiore@0: import java.util.List; fiore@0: fiore@0: import uk.ac.qmul.eecs.ccmi.diagrammodel.NodeProperties; fiore@0: import uk.ac.qmul.eecs.ccmi.gui.Direction; fiore@0: import uk.ac.qmul.eecs.ccmi.sound.SoundFactory; fiore@0: fiore@0: /** fiore@0: * fiore@0: * A triangular shaped diagram node. fiore@0: * fiore@0: */ fiore@0: @SuppressWarnings("serial") fiore@0: public class TriangularNode extends SimpleShapeNode { fiore@0: fiore@0: fiore@0: public TriangularNode(String typeName, NodeProperties properties) { fiore@0: super(typeName, properties); fiore@0: Rectangle2D dataBounds = getMinBounds(); fiore@0: dataDisplayBounds.setFrame(dataBounds); fiore@0: tShape = getOutShape(dataBounds); fiore@0: /* by building the shape around dataBounds which was at (0,0) the new bounds */ fiore@0: /* are now negative, so we need to bring the new bounds back at (0,0) */ fiore@0: Rectangle2D bounds = getBounds(); fiore@0: translateImplementation(new Point2D.Double(),0-bounds.getX(),0-bounds.getY()); fiore@0: } fiore@0: fiore@0: @Override fiore@0: protected Rectangle2D getMinBounds(){ fiore@0: Rectangle2D minBounds = super.getMinBounds(); fiore@0: return new Rectangle2D.Double(minBounds.getX(),minBounds.getY(),minBounds.getWidth()/2,minBounds.getHeight()/2); fiore@0: } fiore@0: fiore@0: @Override fiore@0: public ShapeType getShapeType() { fiore@0: return ShapeType.Triangle; fiore@0: } fiore@0: fiore@0: @Override fiore@0: protected void translateImplementation(Point2D p, double dx, double dy){ fiore@0: /* if we clicked on a property node, just move that one */ fiore@0: for(List pnList : propertyNodesMap.values()) fiore@0: for(PropertyNode pn : pnList) fiore@0: if(pn.contains(p)){ fiore@0: pn.translate(dx, dy); fiore@0: return; fiore@0: } fiore@0: super.translateImplementation(p,dx, dy); fiore@0: tShape.transform(AffineTransform.getTranslateInstance(dx, dy)); fiore@0: } fiore@0: fiore@0: public static Path2D.Double getOutShape(Rectangle2D r){ fiore@0: Path2D.Double triangle = new Path2D.Double(GeneralPath.WIND_EVEN_ODD,3); fiore@0: double minEdge = Math.min(r.getWidth(), r.getHeight()); fiore@0: triangle.moveTo(r.getCenterX(), r.getY()-minEdge); fiore@0: fiore@0: double angle = Math.atan(minEdge/(r.getWidth()/2)); fiore@0: double w = r.getHeight()/ Math.tan(angle); fiore@0: triangle.lineTo(r.getX()-w, r.getMaxY()); fiore@0: triangle.lineTo(r.getMaxX()+w, r.getMaxY()); fiore@0: triangle.closePath(); fiore@0: return triangle; fiore@0: } fiore@0: fiore@0: @Override fiore@0: protected void reshapeInnerProperties(List insidePropertyTypes){ fiore@0: nameLabel = new MultiLineString(); fiore@0: nameLabel.setText(getName().isEmpty() ? " " : getName()); fiore@0: nameLabel.setBold(true); fiore@0: fiore@0: if(!super.anyInsideProperties()){ fiore@0: dataDisplayBounds.setFrame(dataDisplayBounds.getX(), fiore@0: dataDisplayBounds.getY(), fiore@0: nameLabel.getBounds().getWidth(), fiore@0: nameLabel.getBounds().getHeight()); fiore@0: Rectangle2D minBounds = getMinBounds(); fiore@0: dataDisplayBounds.add(new Rectangle2D.Double(dataDisplayBounds.getX(), dataDisplayBounds.getY(), minBounds.getWidth(),minBounds.getHeight())); fiore@0: tShape = getOutShape(dataDisplayBounds); fiore@0: }else { fiore@0: Rectangle2D r = nameLabel.getBounds(); fiore@0: fiore@0: for(int i=0; i 0 ? bounds.getY() : bounds.getMaxY()); fiore@0: } fiore@0: fiore@0: boolean left = false; fiore@0: boolean right = false; fiore@0: double dirTan = d.getY()/d.getX(); fiore@0: double boundsTan = bounds.getHeight()/bounds.getWidth(); fiore@0: double alfa = Math.atan(dirTan); fiore@0: double alfaDegrees = Math.toDegrees(alfa); fiore@0: fiore@0: if(d.getY() < 0){ //from the top fiore@0: if(alfaDegrees < 0) fiore@0: right = true; fiore@0: else fiore@0: left = true; fiore@0: }else{ //from the bottom fiore@0: if(dirTan < boundsTan && d.getX() > 0) fiore@0: right = true; fiore@0: else if(dirTan > -boundsTan && d.getX() < 0) fiore@0: left = true; fiore@0: } fiore@0: fiore@0: if(right){ fiore@0: double beta = Math.atan(bounds.getHeight()/(bounds.getWidth()/2)); fiore@0: double py = bounds.getHeight()/2; fiore@0: double x = py/ (Math.tan(alfa)-Math.tan(beta)); fiore@0: double y = x * Math.tan(alfa); fiore@0: return new Point2D.Double(bounds.getCenterX()-x, bounds.getCenterY()-y); fiore@0: } fiore@0: else if(left){ fiore@0: double beta = - Math.atan(bounds.getHeight()/(bounds.getWidth()/2)); fiore@0: double py = bounds.getHeight()/2; fiore@0: double x = py/ (Math.tan(alfa)-Math.tan(beta)); fiore@0: double y = x * Math.tan(alfa); fiore@0: return new Point2D.Double(bounds.getCenterX()-x, bounds.getCenterY()-y); fiore@0: } fiore@0: else{ fiore@0: return new Point2D.Double( fiore@0: bounds.getCenterX() + ((bounds.getHeight()/2) * (d.getX()/d.getY()) ), fiore@0: bounds.getMaxY()); fiore@0: } fiore@0: } fiore@0: fiore@0: @Override fiore@0: public Shape getShape() { fiore@0: return tShape; fiore@0: } fiore@0: fiore@0: public Object clone(){ fiore@0: return new TriangularNode(getType(),(NodeProperties)getProperties().clone()); fiore@0: } fiore@0: fiore@0: private Path2D.Double tShape; fiore@0: private static InputStream sound; fiore@0: fiore@0: static { fiore@0: sound = TriangularNode.class.getResourceAsStream("audio/Triangle.mp3"); fiore@0: SoundFactory.getInstance().loadSound(sound); fiore@0: } fiore@0: }