Mercurial > hg > accesspd
diff java/src/uk/ac/qmul/eecs/ccmi/simpletemplate/TriangularNode.java @ 0:78b7fc5391a2
first import, outcome of NIME 2014 hackaton
author | Fiore Martin <f.martin@qmul.ac.uk> |
---|---|
date | Tue, 08 Jul 2014 16:28:59 +0100 |
parents | |
children |
line wrap: on
line diff
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/java/src/uk/ac/qmul/eecs/ccmi/simpletemplate/TriangularNode.java Tue Jul 08 16:28:59 2014 +0100 @@ -0,0 +1,213 @@ +/* + CCmI Editor - A Collaborative Cross-Modal Diagram Editing Tool + + Copyright (C) 2011 Queen Mary University of London (http://ccmi.eecs.qmul.ac.uk/) + + This program is free software: you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation, either version 3 of the License, or + (at your option) any later version. + + This program 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 General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program. If not, see <http://www.gnu.org/licenses/>. +*/ + +package uk.ac.qmul.eecs.ccmi.simpletemplate; + +import java.awt.Shape; +import java.awt.geom.AffineTransform; +import java.awt.geom.GeneralPath; +import java.awt.geom.Path2D; +import java.awt.geom.Point2D; +import java.awt.geom.Rectangle2D; +import java.awt.geom.Rectangle2D.Double; +import java.io.InputStream; +import java.util.List; + +import uk.ac.qmul.eecs.ccmi.diagrammodel.NodeProperties; +import uk.ac.qmul.eecs.ccmi.gui.Direction; +import uk.ac.qmul.eecs.ccmi.sound.SoundFactory; + +/** + * + * A triangular shaped diagram node. + * + */ +@SuppressWarnings("serial") +public class TriangularNode extends SimpleShapeNode { + + + public TriangularNode(String typeName, NodeProperties properties) { + super(typeName, properties); + Rectangle2D dataBounds = getMinBounds(); + dataDisplayBounds.setFrame(dataBounds); + tShape = getOutShape(dataBounds); + /* by building the shape around dataBounds which was at (0,0) the new bounds */ + /* are now negative, so we need to bring the new bounds back at (0,0) */ + Rectangle2D bounds = getBounds(); + translateImplementation(new Point2D.Double(),0-bounds.getX(),0-bounds.getY()); + } + + @Override + protected Rectangle2D getMinBounds(){ + Rectangle2D minBounds = super.getMinBounds(); + return new Rectangle2D.Double(minBounds.getX(),minBounds.getY(),minBounds.getWidth()/2,minBounds.getHeight()/2); + } + + @Override + public ShapeType getShapeType() { + return ShapeType.Triangle; + } + + @Override + protected void translateImplementation(Point2D p, double dx, double dy){ + /* if we clicked on a property node, just move that one */ + for(List<PropertyNode> pnList : propertyNodesMap.values()) + for(PropertyNode pn : pnList) + if(pn.contains(p)){ + pn.translate(dx, dy); + return; + } + super.translateImplementation(p,dx, dy); + tShape.transform(AffineTransform.getTranslateInstance(dx, dy)); + } + + public static Path2D.Double getOutShape(Rectangle2D r){ + Path2D.Double triangle = new Path2D.Double(GeneralPath.WIND_EVEN_ODD,3); + double minEdge = Math.min(r.getWidth(), r.getHeight()); + triangle.moveTo(r.getCenterX(), r.getY()-minEdge); + + double angle = Math.atan(minEdge/(r.getWidth()/2)); + double w = r.getHeight()/ Math.tan(angle); + triangle.lineTo(r.getX()-w, r.getMaxY()); + triangle.lineTo(r.getMaxX()+w, r.getMaxY()); + triangle.closePath(); + return triangle; + } + + @Override + protected void reshapeInnerProperties(List<String> insidePropertyTypes){ + nameLabel = new MultiLineString(); + nameLabel.setText(getName().isEmpty() ? " " : getName()); + nameLabel.setBold(true); + + if(!super.anyInsideProperties()){ + dataDisplayBounds.setFrame(dataDisplayBounds.getX(), + dataDisplayBounds.getY(), + nameLabel.getBounds().getWidth(), + nameLabel.getBounds().getHeight()); + Rectangle2D minBounds = getMinBounds(); + dataDisplayBounds.add(new Rectangle2D.Double(dataDisplayBounds.getX(), dataDisplayBounds.getY(), minBounds.getWidth(),minBounds.getHeight())); + tShape = getOutShape(dataDisplayBounds); + }else { + Rectangle2D r = nameLabel.getBounds(); + + for(int i=0; i<insidePropertyTypes.size();i++){ + propertyLabels[i] = new MultiLineString(); + if(getProperties().getValues(insidePropertyTypes.get(i)).size() == 0){ + propertyLabels[i].setText(" "); + }else{ + propertyLabels[i].setJustification(MultiLineString.LEFT); + String[] a = new String[getProperties().getValues(insidePropertyTypes.get(i)).size()]; + propertyLabels[i].setText(getProperties().getValues(insidePropertyTypes.get(i)).toArray(a), getProperties().getModifiers(insidePropertyTypes.get(i))); + } + r.add(new Rectangle2D.Double(r.getX(),r.getMaxY(),propertyLabels[i].getBounds().getWidth(),propertyLabels[i].getBounds().getHeight())); + } + /* set a gap to uniformly distribute the extra space among property rectangles to reach the minimum bound's height */ + boundsGap = 0; + Rectangle2D.Double minBounds = (Rectangle2D.Double)getMinBounds(); + if(r.getHeight() < minBounds.height){ + boundsGap = minBounds.height - r.getHeight(); + boundsGap /= insidePropertyTypes.size(); + } + r.add(minBounds); + dataDisplayBounds.setFrame(new Rectangle2D.Double(dataDisplayBounds.x,dataDisplayBounds.y,r.getWidth(),r.getHeight())); + + tShape = getOutShape(dataDisplayBounds); + } + + } + + @Override + public Double getBounds() { + return (Double)tShape.getBounds2D(); + } + + @Override + public InputStream getSound(){ + return sound; + } + + @Override + public Point2D getConnectionPoint(Direction d) { + return calculateConnectionPoint(d,getBounds()); + } + + public static Point2D calculateConnectionPoint(Direction d, Rectangle2D bounds) { + if(d.getX() == 0){ + return new Point2D.Double(bounds.getCenterX(), + d.getY() > 0 ? bounds.getY() : bounds.getMaxY()); + } + + boolean left = false; + boolean right = false; + double dirTan = d.getY()/d.getX(); + double boundsTan = bounds.getHeight()/bounds.getWidth(); + double alfa = Math.atan(dirTan); + double alfaDegrees = Math.toDegrees(alfa); + + if(d.getY() < 0){ //from the top + if(alfaDegrees < 0) + right = true; + else + left = true; + }else{ //from the bottom + if(dirTan < boundsTan && d.getX() > 0) + right = true; + else if(dirTan > -boundsTan && d.getX() < 0) + left = true; + } + + if(right){ + double beta = Math.atan(bounds.getHeight()/(bounds.getWidth()/2)); + double py = bounds.getHeight()/2; + double x = py/ (Math.tan(alfa)-Math.tan(beta)); + double y = x * Math.tan(alfa); + return new Point2D.Double(bounds.getCenterX()-x, bounds.getCenterY()-y); + } + else if(left){ + double beta = - Math.atan(bounds.getHeight()/(bounds.getWidth()/2)); + double py = bounds.getHeight()/2; + double x = py/ (Math.tan(alfa)-Math.tan(beta)); + double y = x * Math.tan(alfa); + return new Point2D.Double(bounds.getCenterX()-x, bounds.getCenterY()-y); + } + else{ + return new Point2D.Double( + bounds.getCenterX() + ((bounds.getHeight()/2) * (d.getX()/d.getY()) ), + bounds.getMaxY()); + } + } + + @Override + public Shape getShape() { + return tShape; + } + + public Object clone(){ + return new TriangularNode(getType(),(NodeProperties)getProperties().clone()); + } + + private Path2D.Double tShape; + private static InputStream sound; + + static { + sound = TriangularNode.class.getResourceAsStream("audio/Triangle.mp3"); + SoundFactory.getInstance().loadSound(sound); + } +}