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