package org.das2.event; import java.awt.BasicStroke; import java.awt.Color; import java.awt.Graphics; import java.awt.Graphics2D; import java.awt.Point; import java.awt.Rectangle; import java.awt.geom.GeneralPath; import java.awt.geom.Point2D; import java.util.ArrayList; import java.util.List; import java.util.logging.Logger; import org.das2.datum.Datum; import org.das2.datum.DatumRangeUtil; import org.das2.graph.DasAxis; import org.das2.graph.DasCanvasComponent; import org.das2.qds.IDataSet; import org.das2.qds.QDataSet; import org.das2.qds.ops.Ops; import org.das2.qds.util.DataSetBuilder; import org.das2.util.LoggerManager; /** * draw a pathway of a given width including a set of waypoints. * @author jbf */ public class WaypointsDragRenderer extends AbstractDragRenderer { /** * draws a boxed region of some vertical width along a path. * @param parent */ public WaypointsDragRenderer( DasCanvasComponent parent ) { super( parent ); } Point2D.Double pointerStart=null; Point2D.Double pointerLocation= null; List<Point2D.Double> wayPoints= new ArrayList<>(); int direction=1; private int width = 20; public static final String PROP_WIDTH = "width"; private static final Logger logger= LoggerManager.getLogger("das2.gui.dmia"); /** * get the vertical width in pixels. * @return the vertical width */ public int getWidth() { return width; } /** * set the vertical width in pixels. * @param width */ public void setWidth(int width) { this.width = width; getParent().repaint(); } @Override public void clear(Graphics g) { wayPoints.clear(); pointerLocation=null; getParent().repaint(); } /** * add the current location, recorded from the last renderDrag operation, to the list of way points. */ public void addWayPoint( ) { if ( pointerLocation!=null ) { if ( direction==0 ) { if ( pointerLocation.x>this.pointerStart.x ) { direction=1; } else { direction=-1; } } if ( ( pointerLocation.x - this.pointerStart.x ) * direction > 0 ) { this.wayPoints.add( pointerLocation ); } else { logger.info( "wrong direction" ); } } else { logger.info( "no pointer location"); } } @Override public Rectangle[] renderDrag( Graphics g1, Point p1, Point p2 ) { Graphics2D g= (Graphics2D) g1; GeneralPath gen= new GeneralPath(); if ( direction>0 ) { if ( wayPoints.size()>0 ) { p2.x= Math.max( p2.x, (int)wayPoints.get(wayPoints.size()-1).x ); } else { p2.x= Math.max( p2.x, p1.x ); } } else if ( direction<0 ) { if ( wayPoints.size()>0 ) { p2.x= Math.min( p2.x, (int)wayPoints.get(wayPoints.size()-1).x ); } else { p2.x= Math.min( p2.x, p1.x ); } } gen.moveTo( p1.x, p1.y - width ); for ( int i=0; i<wayPoints.size(); i++ ) { gen.lineTo( wayPoints.get(i).x, wayPoints.get(i).y-width ); } gen.lineTo( p2.x, p2.y-width ); gen.lineTo( p2.x, p2.y+width ); for ( int i=wayPoints.size()-1; i>=0; i-- ) { gen.lineTo( wayPoints.get(i).x, wayPoints.get(i).y+width ); } gen.lineTo( p1.x, p1.y + width ); gen.closePath(); Color color0= g.getColor(); g.setColor(new Color(255,255,255,100)); g.setStroke(new BasicStroke( 3.0f, BasicStroke.CAP_ROUND, BasicStroke.JOIN_ROUND )); g.draw( gen ); g.setStroke(new BasicStroke()); g.setColor(color0); g.draw( gen ); pointerStart= new Point2D.Double( p1.x, p1.y ); pointerLocation= new Point2D.Double( p2.x, p2.y ); return new Rectangle[] { }; } /** * returns a list of indices where the rank 1 yy is within the pathway. This breaks the package.html guidance * that this not work with data and units, and may change. * @param xaxis the axis for horizontal transforms * @param yaxis the axis for vertical transforms * @param xx the rank 0 point for the data * @param yy the rank 1 data points * @return a list of indices into yy. */ protected QDataSet whereWithin( DasAxis xaxis, DasAxis yaxis, QDataSet xx, QDataSet yy ) { switch (xx.rank()) { case 0: int index= -1; double ix= xaxis.transform(xx); if ( ix<pointerStart.x ) return IDataSet.createRank1(0); for ( int i=0; i<wayPoints.size(); i++ ) { if ( ix<wayPoints.get(i).x ) { index= i; break; } } if ( index==-1 ) { if ( ix<pointerLocation.x ) { index= wayPoints.size(); } } if ( yy.rank()==0 ) { yy= Ops.join(null,yy); } if ( yy.rank()==1 ) { double alpha; // the normalized location between the two way points double y; if ( index==0 && wayPoints.isEmpty() ) { alpha= ( ix - pointerStart.x ) / ( pointerLocation.x - pointerStart.x ); y= pointerStart.y + alpha * ( pointerLocation.y - pointerStart.y ); } else if ( index==0 ) { alpha= ( ix - pointerStart.x ) / ( wayPoints.get(index).x - pointerStart.x ); y= pointerStart.y + alpha * ( wayPoints.get(index).y - pointerStart.y ); } else if ( index==wayPoints.size() ) { alpha= ( ix - wayPoints.get(index-1).x ) / ( pointerLocation.x - wayPoints.get(index-1).x ); y= wayPoints.get(index-1).y + alpha * ( pointerLocation.y - wayPoints.get(index-1).y ); } else if ( index>0 && index<wayPoints.size() ) { alpha= ( ix - wayPoints.get(index-1).x ) / ( wayPoints.get(index).x - wayPoints.get(index-1).x ); y= wayPoints.get(index-1).y + alpha * ( wayPoints.get(index).y - wayPoints.get(index-1).y ); } else { return IDataSet.createRank1(0); } double ymin= y - width; double ymax= y + width; Datum dymin= yaxis.invTransform( ymin ); Datum dymax= yaxis.invTransform( ymax ); QDataSet ww= Ops.within( yy, DatumRangeUtil.union( dymin, dymax ) ); return Ops.where( ww ); } else { throw new IllegalArgumentException("yy must be rank 1"); } case 1: DataSetBuilder dsb= new DataSetBuilder( 1,100 ); for ( int j=0; j<xx.length(); j++ ) { index= -1; ix= xaxis.transform(xx.slice(j)); if ( ix<pointerStart.x ) continue; if ( ix>pointerLocation.x ) continue; for ( int i=0; i<wayPoints.size(); i++ ) { if ( ix<wayPoints.get(i).x ) { index= i; break; } } if ( index==-1 ) { if ( ix<pointerLocation.x ) { index= wayPoints.size(); } } if ( yy.rank()==0 ) { yy= Ops.join(null,yy); } if ( yy.rank()==1 ) { double alpha; // the normalized location between the two way points double y; if ( index==0 && wayPoints.isEmpty() ) { alpha= ( ix - pointerStart.x ) / ( pointerLocation.x - pointerStart.x ); y= pointerStart.y + alpha * ( pointerLocation.y - pointerStart.y ); } else if ( index==0 ) { alpha= ( ix - pointerStart.x ) / ( wayPoints.get(index).x - pointerStart.x ); y= pointerStart.y + alpha * ( wayPoints.get(index).y - pointerStart.y ); } else if ( index==wayPoints.size() ) { alpha= ( ix - wayPoints.get(index-1).x ) / ( pointerLocation.x - wayPoints.get(index-1).x ); y= wayPoints.get(index-1).y + alpha * ( pointerLocation.y - wayPoints.get(index-1).y ); } else if ( index>0 && index<wayPoints.size() ) { alpha= ( ix - wayPoints.get(index-1).x ) / ( wayPoints.get(index).x - wayPoints.get(index-1).x ); y= wayPoints.get(index-1).y + alpha * ( wayPoints.get(index).y - wayPoints.get(index-1).y ); } else { return IDataSet.createRank1(0); } double ymin= y - width; double ymax= y + width; Datum dymin= yaxis.invTransform( ymin ); Datum dymax= yaxis.invTransform( ymax ); if ( Ops.within( yy.slice(j), DatumRangeUtil.union( dymin, dymax ) ).value()!=0 ) { dsb.nextRecord( j ); } } else { throw new IllegalArgumentException("yy must be rank 1"); } } return dsb.getDataSet(); default: throw new IllegalArgumentException("xx must be rank 0"); } } /** * return a rectangle that bounds the data. * @return */ public Rectangle getBoundingBox() { double ymin= pointerStart.y; double ymax= pointerStart.y; for ( int i=0; i<wayPoints.size(); i++ ) { Point2D.Double d= wayPoints.get(i); ymin = Math.min( ymin, d.y ); ymax = Math.max( ymax, d.y ); } ymin = Math.min( ymin, pointerLocation.y ); ymax = Math.min( ymax, pointerLocation.y ); Rectangle result= new Rectangle( (int)pointerStart.x, (int)(ymin-width), (int)(pointerLocation.x - pointerStart.x), (int)(ymax-ymin+width*2) ); return result; } }