f@0: /* f@0: accessPD - An accessible PD patches editor f@0: f@0: Copyright (C) 2011 Queen Mary University of London (http://ccmi.eecs.qmul.ac.uk/) f@0: Copyright (C) 2014 Fiore Martin 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: package uk.ac.qmul.eecs.ccmi.pdsupport; f@0: f@0: import java.awt.Color; f@0: import java.awt.Graphics2D; f@0: import java.awt.Shape; f@0: import java.awt.geom.Point2D; f@0: import java.awt.geom.Rectangle2D; f@0: import java.io.InputStream; 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.gui.Node; f@0: import uk.ac.qmul.eecs.ccmi.simpletemplate.MultiLineString; f@0: import uk.ac.qmul.eecs.ccmi.sound.SoundFactory; f@0: f@0: public class PdObject extends Node implements PdElement { f@0: private static final long serialVersionUID = 1L; f@0: private static final int DEFAULT_WIDTH = 100; f@0: private static final int DEFAULT_HEIGHT = 60; f@0: private static final Rectangle2D.Double minBounds = new Rectangle2D.Double(0,0,DEFAULT_WIDTH,DEFAULT_HEIGHT); f@0: f@0: f@0: private Rectangle2D.Double bounds; f@0: private static InputStream sound; f@0: private MultiLineString label; f@0: private int orderNumber; f@0: f@0: static{ f@0: sound = PdObject.class.getResourceAsStream("audio/PdObject.mp3"); f@0: SoundFactory.getInstance().loadSound(sound); f@0: } f@0: f@0: public PdObject(String type) { f@0: super(type, NodeProperties.NULL_PROPERTIES); f@0: bounds = new Rectangle2D.Double(0,0,DEFAULT_WIDTH,DEFAULT_HEIGHT); f@0: label = new MultiLineString(); f@0: label.setText(getType()); f@0: } f@0: f@0: public PdObject() { f@0: this("Object"); f@0: } f@0: f@0: @Override f@0: protected void translateImplementation(Point2D p, double dx, double dy) { f@0: bounds.setFrame(bounds.getX() + dx, f@0: bounds.getY() + dy, f@0: bounds.getWidth(), f@0: bounds.getHeight()); f@0: f@0: } f@0: f@0: @Override f@0: public boolean contains(Point2D aPoint) { f@0: return bounds.contains(aPoint); f@0: } f@0: f@0: @Override f@0: public Rectangle2D getBounds() { f@0: return bounds.getBounds2D(); f@0: } f@0: f@0: @Override f@0: public Point2D getConnectionPoint(Direction d) { f@0: double slope = bounds.getHeight() / bounds.getWidth(); f@0: double ex = d.getX(); f@0: double ey = d.getY(); f@0: double x = bounds.getCenterX(); f@0: double y = bounds.getCenterY(); f@0: f@0: if (ex != 0 && -slope <= ey / ex && ey / ex <= slope){ f@0: // intersects at left or right boundary f@0: if (ex > 0){ f@0: x = bounds.getMaxX(); f@0: y += (bounds.getWidth() / 2) * ey / ex; f@0: }else{ f@0: x = bounds.getX(); f@0: y -= (bounds.getWidth() / 2) * ey / ex; f@0: } f@0: }else if (ey != 0){ f@0: // intersects at top or bottom f@0: if (ey > 0){ f@0: x += (bounds.getHeight() / 2) * ex / ey; f@0: y = bounds.getMaxY(); f@0: }else{ f@0: x -= (bounds.getHeight() / 2) * ex / ey; f@0: y = bounds.getY(); f@0: } f@0: } f@0: f@0: return new Point2D.Double(x, y); f@0: f@0: } f@0: f@0: @Override f@0: public Shape getShape() { f@0: return getBounds(); f@0: } f@0: f@0: @Override f@0: public InputStream getSound() { f@0: return sound; f@0: } f@0: f@0: @Override f@0: public void draw(Graphics2D g2d){ f@0: Color oldColor = g2d.getColor(); f@0: g2d.setColor(Color.WHITE); f@0: g2d.fill(getBounds()); f@0: f@0: g2d.setColor(oldColor); f@0: f@0: label.draw(g2d, getBounds()); f@0: g2d.draw(bounds); f@0: } f@0: f@0: @Override f@0: public void setName(String name, Object source){ f@0: label.setText(name); f@0: super.setName(name, source); f@0: } f@0: f@0: @Override f@0: public void setId(long id){ f@0: super.setId(id); f@0: f@0: label.setText(getName()); f@0: /* when they are given an id nodes change name into "new node " * f@0: * where is the actual type of the node and is the given id * f@0: * therefore a reshape is necessary to display the new name */ f@0: Rectangle2D boundsBeforeReshape = getBounds(); f@0: f@0: bounds = calculateBounds(label.getBounds()); f@0: f@0: /* the reshape might change the bounds, so the shape is translated so that the top-left * f@0: * point is at the same position as before just to keep it more consistent */ f@0: Rectangle2D boundsAfterReshape = getBounds(); f@0: translateImplementation( f@0: new Point2D.Double(), f@0: boundsBeforeReshape.getX() - boundsAfterReshape.getX(), f@0: boundsBeforeReshape.getY() - boundsAfterReshape.getY() f@0: ); f@0: f@0: f@0: } f@0: f@0: protected Rectangle2D.Double getMinBounds(){ f@0: return minBounds; f@0: } f@0: f@0: private Rectangle2D.Double calculateBounds(Rectangle2D bounds){ f@0: return new Rectangle2D.Double(bounds.getX() , bounds.getY(), f@0: Math.max(bounds.getWidth(), getMinBounds().getWidth()), f@0: Math.max(bounds.getHeight(), getMinBounds().getHeight()) f@0: ); f@0: } f@0: f@0: @Override f@0: public Object clone(){ f@0: PdObject clone = (PdObject)super.clone(); f@0: f@0: clone.bounds = new Rectangle2D.Double(0,0,DEFAULT_WIDTH,DEFAULT_HEIGHT); f@0: clone.label = new MultiLineString(); f@0: clone.label.setText(getType()); f@0: f@0: return clone; f@0: } f@0: f@0: public String toPdFile(){ f@0: Rectangle2D bounds = getBounds(); f@0: return getChunckType() + " obj " + ((int)bounds.getX()) + f@0: ' ' + ((int)bounds.getY()) + ' ' + getName().replaceAll("\\s+",""); f@0: } f@0: f@0: @Override f@0: public String getChunckType() { f@0: return "#X"; f@0: } f@0: f@0: public void setOrderNumber(int n){ f@0: orderNumber = n; f@0: } f@0: f@0: @Override f@0: public int getOrderNumber(){ f@0: return orderNumber; f@0: } f@0: }