You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@ctakes.apache.org by se...@apache.org on 2015/02/19 19:06:17 UTC

svn commit: r1660963 [16/19] - in /ctakes/sandbox/timelanes: META-INF/ edu/ edu/mayo/ edu/mayo/bmi/ edu/mayo/bmi/annotation/ edu/mayo/bmi/annotation/knowtator/ org/ org/chboston/ org/chboston/cnlp/ org/chboston/cnlp/anafora/ org/chboston/cnlp/anafora/a...

Added: ctakes/sandbox/timelanes/org/chboston/cnlp/timeline/gui/timespan/plus/DefaultTimeSpanRenderer.java
URL: http://svn.apache.org/viewvc/ctakes/sandbox/timelanes/org/chboston/cnlp/timeline/gui/timespan/plus/DefaultTimeSpanRenderer.java?rev=1660963&view=auto
==============================================================================
--- ctakes/sandbox/timelanes/org/chboston/cnlp/timeline/gui/timespan/plus/DefaultTimeSpanRenderer.java (added)
+++ ctakes/sandbox/timelanes/org/chboston/cnlp/timeline/gui/timespan/plus/DefaultTimeSpanRenderer.java Thu Feb 19 18:06:13 2015
@@ -0,0 +1,431 @@
+package org.chboston.cnlp.timeline.gui.timespan.plus;
+
+import org.chboston.cnlp.timeline.gui.timespan.TimeSpanDrawUtil;
+import org.chboston.cnlp.timeline.timespan.EndPointer;
+import org.chboston.cnlp.timeline.timespan.TimeSpan;
+import org.chboston.cnlp.timeline.timespan.plus.PointedTimeSpan;
+import org.chboston.cnlp.timeline.timespan.plus.TimeEndPoint;
+
+import java.awt.*;
+
+/**
+ * Author: SPF
+ * Affiliation: CHIP-NLP
+ * Date: 8/2/13
+ */
+public class DefaultTimeSpanRenderer extends AbstractTimeSpanRenderer {
+
+   static private final TimeSpanPainter DEFAULT_PAINTER = new DefaultTimeSpanPainter();
+
+   protected Color getPlainColor( final boolean isSelected, final boolean isFocused, final boolean isFuzzy ) {
+      return TimeSpanDrawUtil.getPlainColor( isSelected, isFocused, isFuzzy );
+   }
+
+   protected Color getPlainColor( final boolean isSelected, final boolean isFocused, final boolean isFuzzy,
+                                  final int width ) {
+      return TimeSpanDrawUtil.getPlainColor( isSelected, isFocused, isFuzzy, width );
+   }
+
+   protected TimeSpanPainter getPainter() {
+      return DEFAULT_PAINTER;
+   }
+
+
+   private void paintStart( final Graphics2D g2d, final TimeSpan timeSpan ) {
+      if ( timeSpan instanceof PointedTimeSpan ) {
+         paintRelativeStart( g2d, (PointedTimeSpan)timeSpan );
+      } else {
+         final int height = getHeight();
+         paintEdge( g2d, 0, TimeSpanDrawUtil.LINE_THINNESS, height, timeSpan.isFuzzyDate() );
+      }
+   }
+
+   private void paintEnd( final Graphics2D g2d, final TimeSpan timeSpan ) {
+      if ( timeSpan instanceof PointedTimeSpan ) {
+         paintRelativeStop( g2d, (PointedTimeSpan)timeSpan );
+      } else {
+         final int width = getWidth();
+         final int height = getHeight();
+         paintEdge( g2d, width - TimeSpanDrawUtil.LINE_THINNESS, width, height, timeSpan.isFuzzyDate() );
+      }
+   }
+
+   //   private void paintInside( final Graphics2D g2d, final TimeSpan timeSpan ) {
+   //      if ( timeSpan instanceof TimeSpanPlus ) {
+   //         paintRelativeInside( g2d, (TimeSpanPlus) timeSpan );
+   //      } else {
+   //         paintAbsoluteInside( g2d );
+   //      }
+   //   }
+
+   private void paintRelativeStart( final Graphics2D g2d, final PointedTimeSpan timeSpan ) {
+      final int startOffset = TimeSpanDrawUtil.getStartOffset( timeSpan );
+      final TimeEndPoint startTime = timeSpan.getStartTime();
+      final int height = getHeight();
+      switch ( startTime.getPointer() ) {
+         case BEFORE:
+            if ( timeSpan.isSingleDate()
+                 && timeSpan.getStopTime().getPointer() == EndPointer.EQUAL ) {
+               // ~ tlink Ends-On
+               paintOuterLine( g2d, 0, startOffset, TimeSpanDrawUtil.LINE_THINNESS, height, startTime.isFuzzy() );
+               return;
+            }
+            paintSolidTriangle( g2d, 0, startOffset, height, startTime.isFuzzy() );
+            break;
+         case AFTER:
+            if ( timeSpan.isSingleDate() ) {
+               // ~ tlink After
+               return;
+            }
+            // ~ tlink Contains
+            paintEdge( g2d, 0, TimeSpanDrawUtil.LINE_THINNESS, height, startTime.isFuzzy() );
+            break;
+         case EQUAL:
+            if ( !timeSpan.isSingleDate()
+                 || timeSpan.getStopTime().getPointer() == EndPointer.AFTER ) {
+               // Equal Start OR ~ tlink Begins-On
+               paintEdge( g2d, 0, TimeSpanDrawUtil.LINE_THINNESS, height, startTime.isFuzzy() );
+            }
+            break;
+         case OVERLAP:
+            paintOuterLine( g2d, 0, startOffset, TimeSpanDrawUtil.LINE_THICKNESS, height, startTime.isFuzzy() );
+            break;
+      }
+   }
+
+   private void paintRelativeStop( final Graphics2D g2d, final PointedTimeSpan timeSpan ) {
+      final int endOffset = TimeSpanDrawUtil.getEndOffset( timeSpan );
+      final TimeEndPoint timeEndPoint = timeSpan.getStopTime();
+      final int width = getWidth();
+      final int leftX = width - endOffset;
+      final int height = getHeight();
+      switch ( timeEndPoint.getPointer() ) {
+         case BEFORE:
+            if ( timeSpan.isSingleDate() ) {
+               // ~ tlink Before
+               return;
+            }
+            // ~ tlink Contains
+            paintEdge( g2d, width - TimeSpanDrawUtil.LINE_THINNESS, width, height, timeEndPoint.isFuzzy() );
+            break;
+         case AFTER:
+            if ( timeSpan.isSingleDate()
+                 && timeSpan.getStartTime().getPointer() == EndPointer.EQUAL ) {
+               // ~ tlink Ends-On
+               paintOuterLine( g2d, leftX, width, TimeSpanDrawUtil.LINE_THINNESS, height, timeEndPoint.isFuzzy() );
+               return;
+            }
+            paintSolidTriangle( g2d, width, leftX, height, timeEndPoint.isFuzzy() );
+            break;
+         case EQUAL:
+            if ( !timeSpan.isSingleDate()
+                 || timeSpan.getStartTime().getPointer() == EndPointer.BEFORE ) {
+               // Equal Stop OR ~ tlink Ends-On
+               paintEdge( g2d, width - TimeSpanDrawUtil.LINE_THINNESS, width, height, timeEndPoint.isFuzzy() );
+            }
+            break;
+         case OVERLAP:
+            paintOuterLine( g2d, leftX, width, TimeSpanDrawUtil.LINE_THICKNESS, height, timeEndPoint.isFuzzy() );
+            break;
+      }
+   }
+
+   protected void paintDiamond( final Graphics2D g2d ) {
+      final int width = getWidth();
+      final int halfWidth = width / 2 - 1;
+      final int height = getHeight();
+      final int halfHeight = height / 2 - 1;
+      final Color color = getPlainColor( isSelected(), isFocused(), isFuzzy() );
+      g2d.setPaint( color );
+      final int[] xPoints = { 0, halfWidth, width - 1, halfWidth, 0 };
+      final int[] yPoints = { halfHeight, 0, halfHeight, height - 1, halfHeight };
+      g2d.fillPolygon( xPoints, yPoints, xPoints.length );
+   }
+
+   //   protected void paintTriangle( final Graphics2D g2d, final int pointX, final int nockX,
+   //                                 final int fuzzyX, final int solidX, final int height ) {
+   //      final int width = Math.abs( nockX - pointX );
+   //      final int halfHeight = height / 2 - 1;
+   //      final Color insideColor = getPlainColor( isSelected(), isFuzzy(), width );
+   //      final Color outsideColor = getPlainColor( isSelected(), true, width );
+   //      final GradientPaint gradient1 = new GradientPaint( solidX, 0, insideColor, fuzzyX, 0, outsideColor, false );
+   //      g2d.setPaint( gradient1 );
+   //      final int[] xPoints = {pointX, nockX, nockX, pointX, pointX};
+   //      final int[] yPoints = {halfHeight - 1, 0, height - 1, halfHeight, halfHeight - 1};
+   //      g2d.fillPolygon( xPoints, yPoints, xPoints.length );
+   //   }
+
+   // TODO This would be faster as an image
+   protected void paintSolidTriangle( final Graphics2D g2d, final int pointX, final int nockX, final int height,
+                                      final boolean isFuzzy ) {
+      final int halfHeight = height / 2 - 1;
+      final Color color = getPlainColor( isSelected(), isFocused(), isFuzzy );
+      g2d.setPaint( color );
+      final int[] xPoints = { pointX, nockX, nockX, pointX, pointX };
+      final int[] yPoints = { halfHeight - 1, 0, height - 1, halfHeight, halfHeight - 1 };
+      g2d.fillPolygon( xPoints, yPoints, xPoints.length );
+   }
+
+   // TODO This would be faster as an image
+   protected void paintInnerEdge( final Graphics2D g2d, final int pointX, final int nockX, final int height,
+                                  final boolean isFuzzy ) {
+      final int halfHeight = height / 2 - 1;
+      final int halfThick = TimeSpanDrawUtil.LINE_THICKNESS / 2;
+      final int top = Math.max( 0, halfHeight - halfThick );
+      final int bottom = Math.min( height, top + TimeSpanDrawUtil.LINE_THICKNESS );
+      final int lineHeight = bottom - top;
+      final Color color = getPlainColor( isSelected(), isFocused(), isFuzzy );
+      g2d.setPaint( color );
+      final int[] xPoints1 = { pointX, nockX, nockX, pointX, pointX };
+      final int[] yPoints1 = { 0, top, bottom, lineHeight, 0 };
+      g2d.fillPolygon( xPoints1, yPoints1, xPoints1.length );
+      final int[] xPoints2 = { pointX, nockX, nockX, pointX, pointX };
+      final int[] yPoints2 = { height, bottom, top, height - lineHeight, height };
+      g2d.fillPolygon( xPoints2, yPoints2, xPoints2.length );
+   }
+
+   protected void paintEdge( final Graphics2D g2d, final int leftX, final int rightX, final int height,
+                             final boolean isFuzzy ) {
+      final int width = Math.min( getWidth(), Math.abs( rightX - leftX ) );
+      final Color color = getPlainColor( isSelected(), isFocused(), isFuzzy, getWidth() );
+      g2d.setPaint( color );
+      g2d.fillRect( leftX, 0, width, height );
+   }
+
+   protected void paintOuterLine( final Graphics2D g2d, final int leftX, final int rightX, final int thickness,
+                                  final int height, final boolean isFuzzy ) {
+      final int width = Math.abs( rightX - leftX );
+      final int halfHeight = height / 2 - 1;
+      final int top = halfHeight - thickness / 2;
+      final Color color = getPlainColor( isSelected(), isFocused(), isFuzzy, width );
+      g2d.setPaint( color );
+      g2d.fillRect( leftX, top, width, thickness );
+   }
+
+   protected void paintInnerLine( final Graphics2D g2d, final TimeSpan timeSpan ) {
+      if ( timeSpan instanceof PointedTimeSpan ) {
+         paintRelativeLine( g2d, (PointedTimeSpan)timeSpan );
+      } else {
+         paintAbsoluteLine( g2d, timeSpan );
+      }
+   }
+
+   //   protected void paintSolidLine( final Graphics2D g2d, final int x, final int width,
+   //                                  final int y, final int thickness ) {
+   //      final Color solidColor = getPlainColor( isSelected(), false );
+   //      g2d.setPaint( solidColor );
+   //      g2d.fillRect( x, y, width, thickness );
+   //   }
+   //
+   //
+   //   protected void paintGradientLine( final Graphics2D g2d, final int x, final int width,
+   //                                     final int solidX, final int fuzzyX,
+   //                                     final int y, final int thickness ) {
+   //      final Color solidColor = getPlainColor( isSelected(), false );
+   //      final Color fuzzyColor = getPlainColor( isSelected(), true );
+   //      final GradientPaint gradient1 = new GradientPaint( solidX, y, solidColor, fuzzyX, y, fuzzyColor, false );
+   //      g2d.setPaint( gradient1 );
+   //      g2d.fillRect( x, y, width, thickness );
+   //   }
+
+
+   protected void paintRelativeLine( final Graphics2D g2d, final PointedTimeSpan timeSpan ) {
+      final TimeEndPoint startTime = timeSpan.getStartTime();
+      final boolean isStartFuzzy = startTime.isFuzzy();
+      final EndPointer startPointer = startTime.getPointer();
+      final TimeEndPoint stopTime = timeSpan.getStopTime();
+      final boolean isStopFuzzy = stopTime.isFuzzy();
+      final EndPointer stopPointer = stopTime.getPointer();
+      final int width = getWidth();
+      final int height = getHeight();
+      final int halfHeight = height / 2 - 1;
+      final int startOffset = TimeSpanDrawUtil.getStartOffset( timeSpan )
+                              + (startPointer == EndPointer.AFTER ? TimeSpanDrawUtil.LINE_THINNESS : 0);
+      final int endOffset = TimeSpanDrawUtil.getEndOffset( timeSpan )
+                            + (stopPointer == EndPointer.BEFORE ? TimeSpanDrawUtil.LINE_THINNESS : 0);
+      final int plainColorWidth = width - startOffset - endOffset;
+      final int lineThickess = TimeSpanDrawUtil.isEndOnly( timeSpan ) ? TimeSpanDrawUtil.LINE_THINNESS
+                                                                      : TimeSpanDrawUtil.LINE_THICKNESS;
+      final int halfThick = lineThickess / 2;
+      final int top = Math.max( 0, halfHeight - halfThick );
+      final int bottom = Math.min( height, top + lineThickess );
+      final int lineHeight = bottom - top;
+      if ( TimeSpanDrawUtil.isEndOnly( timeSpan ) ) {
+         final Color color = getPlainColor( isSelected(), isFocused(), true, plainColorWidth );
+         g2d.setPaint( color );
+         g2d.fillRect( 0, top, width, lineHeight );
+         return;
+      }
+      final TimeSpanPainter painter = getPainter();
+      if ( !isStartFuzzy && !isStopFuzzy ) {
+         final Color solidColor = getPlainColor( isSelected(), isFocused(), false, plainColorWidth );
+         // paint the center
+         g2d.setPaint( solidColor );
+//         g2d.fillRect( startOffset, top, plainColorWidth, lineHeight );
+         painter.paint( g2d, startOffset, top, plainColorWidth, lineHeight );
+         return;
+      }
+      final int quarterWidth = (int)(plainColorWidth / 4.d);
+      final int halfWidth = (int)(plainColorWidth / 2.d);
+      final int threeqWidth = plainColorWidth - quarterWidth;
+      final Color solidColor = getPlainColor( isSelected(), isFocused(), false, halfWidth );
+      // paint the center
+      g2d.setPaint( solidColor );
+      if ( isStartFuzzy && !isStopFuzzy ) {
+//         g2d.fillRect( startOffset + quarterWidth, top, threeqWidth + 2, lineHeight );
+         painter.paint( g2d, startOffset + quarterWidth, top, threeqWidth + 2, lineHeight );
+      } else if ( !isStartFuzzy ) {
+//         g2d.fillRect( startOffset, top, threeqWidth + 2, lineHeight );
+         painter.paint( g2d, startOffset, top, threeqWidth + 2, lineHeight );
+      } else {
+//         g2d.fillRect( startOffset + quarterWidth, top, halfWidth + 2, lineHeight );
+         painter.paint( g2d, startOffset + quarterWidth, top, halfWidth + 2, lineHeight );
+      }
+      // paint the fuzzy sides
+      final Color fuzzyColor = getPlainColor( isSelected(), isFocused(), true, quarterWidth );
+      if ( isStartFuzzy ) {
+         final GradientPaint gradient1 = new GradientPaint( startOffset, 0, fuzzyColor,
+               startOffset + quarterWidth, 0, solidColor, false );
+         g2d.setPaint( gradient1 );
+//         g2d.fillRect( startOffset, top, quarterWidth, lineHeight );
+         painter.paint( g2d, startOffset, top, quarterWidth, lineHeight );
+      }
+      if ( isStopFuzzy ) {
+         final GradientPaint gradient2 = new GradientPaint( startOffset + threeqWidth, 0, solidColor,
+               startOffset + plainColorWidth, 0, fuzzyColor, false );
+         g2d.setPaint( gradient2 );
+//         g2d.fillRect( startOffset + threeqWidth, top, quarterWidth, lineHeight );
+         painter.paint( g2d, startOffset + threeqWidth, top, quarterWidth, lineHeight );
+      }
+   }
+
+   protected interface TimeSpanPainter {
+      public void paint( final Graphics2D g2d, final int x, final int y, final int width, final int height );
+   }
+
+   static final private class DefaultTimeSpanPainter implements TimeSpanPainter {
+      @Override
+      public void paint( final Graphics2D g2d, final int x, final int y, final int width, final int height ) {
+         g2d.fillRect( x, y, width, height );
+      }
+   }
+
+   //   protected void paintRelativeInside( final Graphics2D g2d, final TimeSpanPlus timeSpan ) {
+   //      final int width = getWidth();
+   //      final int startOffset = TimeSpanDrawUtil.getStartOffset( timeSpan );
+   //      final int endOffset = TimeSpanDrawUtil.getEndOffset( timeSpan );
+   //      final int insideWidth = width - startOffset - endOffset;
+   //      if ( insideWidth < 1 ) {
+   //         return;
+   //      }
+   //      final int height = getHeight();
+   //      final int halfHeight = height / 2 - 1;
+   //      final int plainColorWidth = width - startOffset - endOffset;
+   //      final int lineThickess = TimeSpanDrawUtil.isEndOnly( timeSpan ) ? TimeSpanDrawUtil.LINE_THINNESS
+   //                                                                      : TimeSpanDrawUtil.LINE_THICKNESS;
+   //      final int halfThick = lineThickess / 2;
+   //      final int top = Math.max( 0, halfHeight - halfThick );
+   //      final int bottom = Math.min( height, top + lineThickess );
+   //      final int lineHeight = bottom - top;
+   ////      if ( TimeSpanDrawUtil.isEndOnly( timeSpan ) ) {
+   ////         final Color color = getPlainColor( isSelected(), true, plainColorWidth );
+   ////         g2d.setPaint( color );
+   ////         g2d.fillRect( startOffset, top, insideWidth, lineHeight );
+   ////         return;
+   ////      }
+   ////      if ( timeSpan.isBeforeOverlap() ) {
+   ////         final int halfWidth = (int) (insideWidth / 2.d);
+   ////         paintGradientLine( g2d, startOffset, insideWidth, startOffset, halfWidth, top, lineHeight );
+   ////      } else if ( timeSpan.isEqual() ) {
+   ////         paintAbsoluteInside( g2d );
+   ////      } else if ( timeSpan.beginsWith() ) {
+   ////         final int halfWidth = (int) (insideWidth / 2.d);
+   ////         paintGradientLine( g2d, startOffset, insideWidth, startOffset, halfWidth, top, lineHeight );
+   ////      } else if ( timeSpan.endsWith() ) {
+   ////         final int halfWidth = (int) (insideWidth / 2.d);
+   ////         paintGradientLine( g2d, startOffset, insideWidth, halfWidth, startOffset, top, lineHeight );
+   ////      } else if ( timeSpan.contains() ) {
+   ////         paintAbsoluteInside( g2d );
+   ////      } else if ( timeSpan.isContainedBy() ) {
+   ////         paintAbsoluteInside( g2d );
+   ////      } else if ( timeSpan.isOverlapping() ) {
+   ////         paintAbsoluteInside( g2d );
+   ////      }
+   //   }
+
+   //   protected void paintAbsoluteInside( final Graphics2D g2d ) {
+   //      final int width = getWidth();
+   //      final int height = getHeight();
+   //      final int halfHeight = height / 2 - 1;
+   //      final int halfThick = TimeSpanDrawUtil.LINE_THICKNESS / 2;
+   //      final int top = Math.max( 0, halfHeight - halfThick );
+   //      final int bottom = Math.min( height, top + TimeSpanDrawUtil.LINE_THICKNESS );
+   //      final int lineHeight = bottom - top;
+   //      if ( isFuzzy() ) {
+   //         final int quarterWidth = (int) (width / 4.d);
+   //         paintGradientLine( g2d, 0, quarterWidth, quarterWidth, 0, top, lineHeight );
+   //
+   //         final int halfWidth = (int) (width / 2.d);
+   //         paintSolidLine( g2d, quarterWidth, halfWidth + 2, top, lineHeight );
+   //
+   //         final int threeqWidth = width - quarterWidth;
+   //         paintGradientLine( g2d, threeqWidth, quarterWidth, threeqWidth, width, top, lineHeight );
+   //      } else {
+   //         paintSolidLine( g2d, 0, width, top, lineHeight );
+   //      }
+   //   }
+
+
+   protected void paintAbsoluteLine( final Graphics2D g2d, final TimeSpan timeSpan ) {
+      final int width = getWidth();
+      final int height = getHeight();
+      final int halfHeight = height / 2 - 1;
+      final int halfThick = TimeSpanDrawUtil.LINE_THICKNESS / 2;
+      final int top = Math.max( 0, halfHeight - halfThick );
+      final int bottom = Math.min( height, top + TimeSpanDrawUtil.LINE_THICKNESS );
+      final int lineHeight = bottom - top;
+      if ( isFuzzy() ) {
+         final int quarterWidth = (int)(width / 4.d);
+         final int halfWidth = (int)(width / 2.d);
+         final int threeqWidth = width - quarterWidth;
+         final Color solidColor = getPlainColor( isSelected(), isFocused(), false, halfWidth );
+         // paint the center
+         g2d.setPaint( solidColor );
+         g2d.fillRect( quarterWidth, top, halfWidth + 2, lineHeight );
+         // paint the fuzzy sides
+         final Color fuzzyColor = getPlainColor( isSelected(), isFocused(), true, quarterWidth );
+         // paint the left fade section
+         final GradientPaint gradient1 = new GradientPaint( 0, 0, fuzzyColor,
+               quarterWidth, 0, solidColor, false );
+         g2d.setPaint( gradient1 );
+         g2d.fillRect( 0, top, quarterWidth, lineHeight );
+         // paint the right fade section
+         final GradientPaint gradient2 = new GradientPaint( threeqWidth, 0, solidColor,
+               width, 0, fuzzyColor, false );
+         g2d.setPaint( gradient2 );
+         g2d.fillRect( threeqWidth, top, quarterWidth, lineHeight );
+      } else {
+         final Color solidColor = getPlainColor( isSelected(), isFocused(), false, width );
+         // paint the center
+         g2d.setPaint( solidColor );
+         g2d.fillRect( 0, top, width, lineHeight );
+      }
+   }
+
+   public void paint( final Graphics g ) {
+      final TimeSpan timeSpan = getTimeSpan();
+      final Graphics2D g2d = (Graphics2D)g.create();
+      if ( isExpanded() ) {
+         paintDiamond( g2d );
+      } else {
+         paintStart( g2d, timeSpan );
+         paintEnd( g2d, timeSpan );
+         if ( !timeSpan.isSingleDate() ) {
+            paintInnerLine( g2d, timeSpan );
+         }
+      }
+      g2d.dispose();
+   }
+
+
+}

Added: ctakes/sandbox/timelanes/org/chboston/cnlp/timeline/gui/timespan/plus/TimeSpanRenderer.java
URL: http://svn.apache.org/viewvc/ctakes/sandbox/timelanes/org/chboston/cnlp/timeline/gui/timespan/plus/TimeSpanRenderer.java?rev=1660963&view=auto
==============================================================================
--- ctakes/sandbox/timelanes/org/chboston/cnlp/timeline/gui/timespan/plus/TimeSpanRenderer.java (added)
+++ ctakes/sandbox/timelanes/org/chboston/cnlp/timeline/gui/timespan/plus/TimeSpanRenderer.java Thu Feb 19 18:06:13 2015
@@ -0,0 +1,21 @@
+package org.chboston.cnlp.timeline.gui.timespan.plus;
+
+
+import org.chboston.cnlp.timeline.gui.timeline.TimelineComponent;
+import org.chboston.cnlp.timeline.timespan.plus.PointedTimeSpan;
+
+import javax.swing.*;
+
+/**
+ * Author: SPF
+ * Affiliation: CHIP-NLP
+ * Date: 8/2/13
+ */
+public interface TimeSpanRenderer {
+
+   JComponent getTimeSpanRendererComponent( final TimelineComponent timelineComponent,
+                                            final PointedTimeSpan timeSpan,
+                                            boolean selected, boolean expanded,
+                                            boolean Focused );
+
+}

Added: ctakes/sandbox/timelanes/org/chboston/cnlp/timeline/gui/timespan/selection/DefaultTimeSpanSelectionModel.java
URL: http://svn.apache.org/viewvc/ctakes/sandbox/timelanes/org/chboston/cnlp/timeline/gui/timespan/selection/DefaultTimeSpanSelectionModel.java?rev=1660963&view=auto
==============================================================================
--- ctakes/sandbox/timelanes/org/chboston/cnlp/timeline/gui/timespan/selection/DefaultTimeSpanSelectionModel.java (added)
+++ ctakes/sandbox/timelanes/org/chboston/cnlp/timeline/gui/timespan/selection/DefaultTimeSpanSelectionModel.java Thu Feb 19 18:06:13 2015
@@ -0,0 +1,159 @@
+package org.chboston.cnlp.timeline.gui.timespan.selection;
+
+
+import org.chboston.cnlp.nlp.annotation.classtype.SemanticClassType;
+import org.chboston.cnlp.nlp.annotation.entity.Entity;
+import org.chboston.cnlp.timeline.timespan.plus.PointedTimeSpan;
+
+import java.util.*;
+
+/**
+ * Author: SPF
+ * Affiliation: CHIP-NLP
+ * Date: 9/20/12
+ */
+final public class DefaultTimeSpanSelectionModel implements TimeSpanSelectionModel {
+
+   final private Collection<TimeSpanSelectionListener> _listeners = new HashSet<>();
+   final private Map<PointedTimeSpan, Collection<Entity>> _selectedEntities = new HashMap<>();
+   final private Set<String> _selectedTexts = new HashSet<>();
+   final private SemanticClassType _semanticType;
+   private boolean _valueIsAdjusting;
+
+
+   public DefaultTimeSpanSelectionModel() {
+      this( null );
+   }
+
+   public DefaultTimeSpanSelectionModel( final SemanticClassType semanticType ) {
+      _semanticType = semanticType;
+   }
+
+   public void setSelectedEntities( final Object source,
+                                    final Map<PointedTimeSpan, Collection<Entity>> timeSpanEntities ) {
+      if ( timeSpanEntities == null ) {
+         return;
+      }
+      _selectedEntities.clear();
+      _selectedEntities.putAll( timeSpanEntities );
+      fireValueChanged( source );
+   }
+
+   public Map<PointedTimeSpan, Collection<Entity>> getSelectedEntities() {
+      return _selectedEntities;
+   }
+
+   /**
+    * {@inheritDoc}
+    */
+   @Override
+   public void setSelectedTerms( final Object source, final Collection<String> texts ) {
+      _selectedTexts.clear();
+      for ( String text : texts ) {
+         if ( !text.trim().isEmpty() ) {
+            _selectedTexts.add( text );
+         }
+      }
+   }
+
+   /**
+    * {@inheritDoc}
+    */
+   @Override
+   public Collection<String> getSelectedTexts() {
+      return _selectedTexts;
+   }
+
+   /**
+    * {@inheritDoc}
+    */
+   @Override
+   public SemanticClassType getSemanticType() {
+      return _semanticType;
+   }
+
+   /**
+    * {@inheritDoc}
+    */
+   @Override
+   public void removeSelectedTimeSpan( final Object source, final PointedTimeSpan timeSpan ) {
+      if ( timeSpan == null ) {
+         return;
+      }
+      final Collection<Entity> removedEntities = _selectedEntities.remove( timeSpan );
+      if ( removedEntities != null ) {
+         fireValueChanged( source );
+      }
+   }
+
+   /**
+    * {@inheritDoc}
+    */
+   @Override
+   public void clearSelection( final Object source ) {
+      _selectedEntities.clear();
+      fireValueChanged( source );
+   }
+
+   /**
+    * {@inheritDoc}
+    */
+   @Override
+   public boolean isSelectionEmpty() {
+      return _selectedEntities.isEmpty();
+   }
+
+   /**
+    * {@inheritDoc}
+    */
+   @Override
+   public void setValueIsAdjusting( final Object source, final boolean valueIsAdjusting ) {
+      if ( valueIsAdjusting != _valueIsAdjusting ) {
+         _valueIsAdjusting = valueIsAdjusting;
+         fireValueChanged( source );
+      }
+   }
+
+   /**
+    * {@inheritDoc}
+    */
+   @Override
+   public boolean getValueIsAdjusting() {
+      return _valueIsAdjusting;
+   }
+
+   /**
+    * {@inheritDoc}
+    */
+   @Override
+   public void addSelectionListener( final TimeSpanSelectionListener listener ) {
+      if ( listener != null ) {
+         _listeners.add( listener );
+      }
+   }
+
+   /**
+    * {@inheritDoc}
+    */
+   @Override
+   public void removeSelectionListener( final TimeSpanSelectionListener listener ) {
+      if ( listener != null ) {
+         _listeners.remove( listener );
+      }
+   }
+
+   private void fireValueChanged( final Object source ) {
+      if ( _listeners.isEmpty() ) {
+         return;
+      }
+      final TimeSpanSelectionEvent event = new TimeSpanSelectionEvent( source,
+            _selectedEntities,
+            _selectedTexts,
+            _semanticType,
+            getValueIsAdjusting() );
+      for ( TimeSpanSelectionListener listener : _listeners ) {
+         listener.valueChanged( event );
+      }
+   }
+
+}

Added: ctakes/sandbox/timelanes/org/chboston/cnlp/timeline/gui/timespan/selection/SelectionForwarder.java
URL: http://svn.apache.org/viewvc/ctakes/sandbox/timelanes/org/chboston/cnlp/timeline/gui/timespan/selection/SelectionForwarder.java?rev=1660963&view=auto
==============================================================================
--- ctakes/sandbox/timelanes/org/chboston/cnlp/timeline/gui/timespan/selection/SelectionForwarder.java (added)
+++ ctakes/sandbox/timelanes/org/chboston/cnlp/timeline/gui/timespan/selection/SelectionForwarder.java Thu Feb 19 18:06:13 2015
@@ -0,0 +1,76 @@
+package org.chboston.cnlp.timeline.gui.timespan.selection;
+
+import org.chboston.cnlp.nlp.annotation.classtype.SemanticClassType;
+import org.chboston.cnlp.nlp.annotation.entity.Entity;
+import org.chboston.cnlp.timeline.timespan.plus.PointedTimeSpan;
+
+import java.util.Collection;
+import java.util.HashSet;
+import java.util.Map;
+
+/**
+ * Author: SPF
+ * Affiliation: CHIP-NLP
+ * Date: 8/16/13
+ */
+public enum SelectionForwarder implements TimeSpanSelectionListener {
+   INSTANCE;
+
+   static public SelectionForwarder getInstance() {
+      return INSTANCE;
+   }
+
+   final private Collection<TimeSpanSelectionModel> _models = new HashSet<>();
+   boolean _isForwarding;
+
+   public void addSelectionModel( final TimeSpanSelectionModel model ) {
+      if ( model != null ) {
+         _models.add( model );
+         model.addSelectionListener( this );
+      }
+   }
+
+   public void removeSelectionModel( final TimeSpanSelectionModel model ) {
+      if ( model != null ) {
+         _models.remove( model );
+         model.removeSelectionListener( this );
+      }
+   }
+
+   public void removeAllSelectionModels() {
+      for ( TimeSpanSelectionModel model : _models ) {
+         model.removeSelectionListener( this );
+      }
+      _models.clear();
+   }
+
+   private void forward( final Object source, final Map<PointedTimeSpan, Collection<Entity>> selectedEntities,
+                         final Collection<String> selectionTexts, final SemanticClassType semanticType ) {
+      for ( TimeSpanSelectionModel model : _models ) {
+         if ( model.equals( source ) ) {
+            continue;
+         }
+         final SemanticClassType modelSemanticType = model.getSemanticType();
+         if ( modelSemanticType != null && modelSemanticType != semanticType ) {
+            model.clearSelection( source );
+         }
+         // removed else 5/28/2014  spf     -- did this mess up the "add event" painting?
+//         } else {
+         model.setValueIsAdjusting( source, true );
+         model.setSelectedTerms( source, selectionTexts );
+         model.setSelectedEntities( source, selectedEntities );
+         model.setValueIsAdjusting( source, false );
+//         }
+      }
+   }
+
+
+   public void valueChanged( final TimeSpanSelectionEvent event ) {
+      if ( _isForwarding || event == null || event.getValueIsAdjusting() ) {
+         return;
+      }
+      _isForwarding = true;
+      forward( event.getSource(), event.getSelectedEntities(), event.getSelectionTexts(), event.getSemanticType() );
+      _isForwarding = false;
+   }
+}

Added: ctakes/sandbox/timelanes/org/chboston/cnlp/timeline/gui/timespan/selection/TimeSpanSelectionEvent.java
URL: http://svn.apache.org/viewvc/ctakes/sandbox/timelanes/org/chboston/cnlp/timeline/gui/timespan/selection/TimeSpanSelectionEvent.java?rev=1660963&view=auto
==============================================================================
--- ctakes/sandbox/timelanes/org/chboston/cnlp/timeline/gui/timespan/selection/TimeSpanSelectionEvent.java (added)
+++ ctakes/sandbox/timelanes/org/chboston/cnlp/timeline/gui/timespan/selection/TimeSpanSelectionEvent.java Thu Feb 19 18:06:13 2015
@@ -0,0 +1,94 @@
+package org.chboston.cnlp.timeline.gui.timespan.selection;
+
+import net.jcip.annotations.Immutable;
+import org.chboston.cnlp.nlp.annotation.classtype.SemanticClassType;
+import org.chboston.cnlp.nlp.annotation.entity.Entity;
+import org.chboston.cnlp.timeline.timespan.plus.PointedTimeSpan;
+
+import java.util.Collection;
+import java.util.EventObject;
+import java.util.Map;
+
+/**
+ * Author: SPF
+ * Affiliation: CHIP-NLP
+ * Date: 9/20/12
+ */
+@Immutable
+final public class TimeSpanSelectionEvent extends EventObject {
+
+   final private Map<PointedTimeSpan, Collection<Entity>> _timeSpanEntities;
+   private final Collection<String> _selectionTexts;
+   private final SemanticClassType _semanticType;
+   private final boolean _isAdjusting;
+
+   public TimeSpanSelectionEvent( final Object source,
+                                  final Map<PointedTimeSpan, Collection<Entity>> timeSpanEntities,
+                                  final Collection<String> selectionTexts,
+                                  final SemanticClassType semanticType,
+                                  final boolean isAdjusting ) {
+      super( source );
+      _timeSpanEntities = timeSpanEntities;
+      _selectionTexts = selectionTexts;
+      _semanticType = semanticType;
+      _isAdjusting = isAdjusting;
+   }
+
+
+   //   /**
+   //    * Represents a change in selection status between {@code firstIndex} and
+   //    * {@code lastIndex}, inclusive. {@code firstIndex} is less than or equal to
+   //    * {@code lastIndex}. The selection of at least one index within the range will
+   //    * have changed.
+   //    *
+   //    * @param selections  all selections to which this event applies
+   //    * @param isAdjusting whether or not this is one in a series of
+   //    *                    multiple events, where changes are still being made
+   //    */
+
+   public Map<PointedTimeSpan, Collection<Entity>> getSelectedEntities() {
+      return _timeSpanEntities;
+   }
+
+   //   public Collection<TimeSpanPlus> getSelectedTimeSpans() {
+   //      return _timeSpanEntities.keySet();
+   //   }
+
+   public Collection<String> getSelectionTexts() {
+      return _selectionTexts;
+   }
+
+   public SemanticClassType getSemanticType() {
+      return _semanticType;
+   }
+
+   /**
+    * Returns whether or not this is one in a series of multiple events,
+    * where changes are still being made. See the documentation for
+    * {@link javax.swing.ListSelectionModel#setValueIsAdjusting} for
+    * more details on how this is used.
+    *
+    * @return {@code true} if this is one in a series of multiple events,
+    * where changes are still being made
+    */
+   public boolean getValueIsAdjusting() {
+      return _isAdjusting;
+   }
+
+   /**
+    * Returns a {@code String} that displays and identifies this
+    * object's properties.
+    *
+    * @return a String representation of this object
+    */
+   public String toString() {
+      String properties =
+            " source=" + getSource() +
+            " selections= " + _timeSpanEntities.toString() +
+            (_semanticType == null ? "" : (" semantic type= " + _semanticType.toString())) +
+            " isAdjusting= " + _isAdjusting +
+            " ";
+      return getClass().getName() + "[" + properties + "]";
+   }
+
+}

Added: ctakes/sandbox/timelanes/org/chboston/cnlp/timeline/gui/timespan/selection/TimeSpanSelectionListener.java
URL: http://svn.apache.org/viewvc/ctakes/sandbox/timelanes/org/chboston/cnlp/timeline/gui/timespan/selection/TimeSpanSelectionListener.java?rev=1660963&view=auto
==============================================================================
--- ctakes/sandbox/timelanes/org/chboston/cnlp/timeline/gui/timespan/selection/TimeSpanSelectionListener.java (added)
+++ ctakes/sandbox/timelanes/org/chboston/cnlp/timeline/gui/timespan/selection/TimeSpanSelectionListener.java Thu Feb 19 18:06:13 2015
@@ -0,0 +1,18 @@
+package org.chboston.cnlp.timeline.gui.timespan.selection;
+
+import java.util.EventListener;
+
+/**
+ * Author: SPF
+ * Affiliation: CHIP-NLP
+ * Date: 9/20/12
+ */
+public interface TimeSpanSelectionListener extends EventListener {
+   /**
+    * Called whenever the value of the selection changes.
+    *
+    * @param event the event that characterizes the change.
+    */
+   void valueChanged( final TimeSpanSelectionEvent event );
+
+}

Added: ctakes/sandbox/timelanes/org/chboston/cnlp/timeline/gui/timespan/selection/TimeSpanSelectionModel.java
URL: http://svn.apache.org/viewvc/ctakes/sandbox/timelanes/org/chboston/cnlp/timeline/gui/timespan/selection/TimeSpanSelectionModel.java?rev=1660963&view=auto
==============================================================================
--- ctakes/sandbox/timelanes/org/chboston/cnlp/timeline/gui/timespan/selection/TimeSpanSelectionModel.java (added)
+++ ctakes/sandbox/timelanes/org/chboston/cnlp/timeline/gui/timespan/selection/TimeSpanSelectionModel.java Thu Feb 19 18:06:13 2015
@@ -0,0 +1,89 @@
+package org.chboston.cnlp.timeline.gui.timespan.selection;
+
+import org.chboston.cnlp.nlp.annotation.classtype.SemanticClassType;
+import org.chboston.cnlp.nlp.annotation.entity.Entity;
+import org.chboston.cnlp.timeline.timespan.plus.PointedTimeSpan;
+
+import java.util.Collection;
+import java.util.Map;
+
+/**
+ * Author: SPF
+ * Affiliation: CHIP-NLP
+ * Date: 9/20/12
+ */
+public interface TimeSpanSelectionModel {
+
+   void setSelectedEntities( final Object source, Map<PointedTimeSpan, Collection<Entity>> timeSpanEntities );
+
+   Map<PointedTimeSpan, Collection<Entity>> getSelectedEntities();
+
+   void setSelectedTerms( final Object source, final Collection<String> text );
+
+   Collection<String> getSelectedTexts();
+
+   SemanticClassType getSemanticType();
+
+   void removeSelectedTimeSpan( final Object source, final PointedTimeSpan timeSpan );
+
+   void clearSelection( final Object source );
+
+   /**
+    * Returns true if no TimeSpans are selected.
+    */
+   boolean isSelectionEmpty();
+
+   /**
+    * Sets the {@code valueIsAdjusting} property, which indicates whether
+    * or not upcoming selection changes should be considered part of a single
+    * change. The value of this property is used to initialize the
+    * {@code valueIsAdjusting} property of the {@code ListSelectionEvent}s that
+    * are generated.
+    * <p/>
+    * For example, if the selection is being updated in response to a user
+    * drag, this property can be set to {@code true} when the drag is initiated
+    * and set to {@code false} when the drag is finished. During the drag,
+    * listeners receive events with a {@code valueIsAdjusting} property
+    * set to {@code true}. At the end of the drag, when the change is
+    * finalized, listeners receive an event with the value set to {@code false}.
+    * Listeners can use this pattern if they wish to update only when a change
+    * has been finalized.
+    * <p/>
+    * Setting this property to {@code true} begins a series of changes that
+    * is to be considered part of a single change. When the property is changed
+    * back to {@code false}, an event is sent out characterizing the entire
+    * selection change (if there was one), with the event's
+    * {@code valueIsAdjusting} property set to {@code false}.
+    *
+    * @param valueIsAdjusting the new value of the property
+    * @see #getValueIsAdjusting
+    */
+   void setValueIsAdjusting( final Object source, boolean valueIsAdjusting );
+
+   /**
+    * Returns {@code true} if the selection is undergoing a series of changes.
+    *
+    * @return true if the selection is undergoing a series of changes
+    * @see #setValueIsAdjusting
+    */
+   boolean getValueIsAdjusting();
+
+   /**
+    * Add a listener to the list that's notified each time a change
+    * to the selection occurs.
+    *
+    * @param listener the TimeSpanSelectionListener
+    * @see #removeSelectionListener
+    */
+   void addSelectionListener( final TimeSpanSelectionListener listener );
+
+   /**
+    * Remove a listener from the list that's notified each time a
+    * change to the selection occurs.
+    *
+    * @param listener the TimeSpanSelectionListener
+    * @see #addSelectionListener
+    */
+   void removeSelectionListener( final TimeSpanSelectionListener listener );
+
+}

Added: ctakes/sandbox/timelanes/org/chboston/cnlp/timeline/oldUI/SpiralBarUIDelegate.java
URL: http://svn.apache.org/viewvc/ctakes/sandbox/timelanes/org/chboston/cnlp/timeline/oldUI/SpiralBarUIDelegate.java?rev=1660963&view=auto
==============================================================================
--- ctakes/sandbox/timelanes/org/chboston/cnlp/timeline/oldUI/SpiralBarUIDelegate.java (added)
+++ ctakes/sandbox/timelanes/org/chboston/cnlp/timeline/oldUI/SpiralBarUIDelegate.java Thu Feb 19 18:06:13 2015
@@ -0,0 +1,194 @@
+package org.chboston.cnlp.timeline.oldUI;
+
+import java.awt.*;
+import java.util.Collection;
+import java.util.Collections;
+import java.util.HashSet;
+import java.util.Set;
+
+/**
+ * Author: SPF
+ * Affiliation: CHIP-NLP
+ * Date: 8/10/12
+ */
+final public class SpiralBarUIDelegate {
+
+   private double _spokeDistance = SpiralBarUtil.MED_SPOKE_LENGTH;
+   private int _loopCount = SpiralBarUtil.MED_LOOP_COUNT;
+   private double _stopRadian = SpiralBarUtil.MED_STOP_RADIAN;
+   private double _startRadian = SpiralBarUtil.START_RADIAN;
+
+   private int _circumference = SpiralBarUtil.SPIRAL_CIRCUMFERENCE;
+   private int _trackSpacer = SpiralBarUtil.SCROLLBAR_TRACK_SPACER;
+
+
+   private Color _shadowColor = Color.GRAY;
+   private Color _highlightColor = Color.LIGHT_GRAY;
+   private Color _focusColor = Color.LIGHT_GRAY;
+   private int _scrollBarWidth;
+   private Rectangle _spiralBounds = new Rectangle();
+
+
+   //   private java.util.List<SpotSketcher> _spotSketcherList;
+   private SpiralBarUtil.SpotSketcher _spotSketcher;
+   private SpiralBarUtil.SpanSketcher _spanSketcher;
+   private Set<Integer> _spotSet;
+   private Set<Point> _spanSet;
+
+
+   public void setSpokeDistance( final double spokeDistance ) {
+      _spokeDistance = spokeDistance;
+   }
+
+   public double getSpokeDistance() {
+      return _spokeDistance;
+   }
+
+   public void setLoopCount( final int loopCount ) {
+      _loopCount = loopCount;
+   }
+
+   public int getLoopCount() {
+      return _loopCount;
+   }
+
+   public void setStopRadian( final double stopRadian ) {
+      _stopRadian = stopRadian;
+   }
+
+   public double getStopRadian() {
+      return _stopRadian;
+   }
+
+   public void setStartRadian( final double startRadian ) {
+      _startRadian = startRadian;
+   }
+
+   public double getStartRadian() {
+      return _startRadian;
+   }
+
+   public void setCircumference( final int circumference ) {
+      _circumference = circumference;
+   }
+
+   public int getCircumference() {
+      return _circumference;
+   }
+
+   public int getTrackSpacer() {
+      return _trackSpacer;
+   }
+
+   public void setTrackSpacer( final int trackSpacer ) {
+      _trackSpacer = trackSpacer;
+   }
+
+   public void setShadowColor( final Color shadowColor ) {
+      _shadowColor = shadowColor;
+   }
+
+   public Color getShadowColor() {
+      return _shadowColor;
+   }
+
+   public void setHighlightColor( final Color highlightColor ) {
+      _highlightColor = highlightColor;
+   }
+
+   public Color getHighlightColor() {
+      return _highlightColor;
+   }
+
+   public void setFocusColor( final Color focusColor ) {
+      _focusColor = focusColor;
+   }
+
+   public Color getFocusColor() {
+      return _focusColor;
+   }
+
+   public void setScrollBarWidth( final int scrollBarWidth ) {
+      if ( scrollBarWidth <= 0 ) {
+         _scrollBarWidth = 16;
+      } else {
+         _scrollBarWidth = scrollBarWidth;
+      }
+   }
+
+   public int getScrollBarWidth() {
+      return _scrollBarWidth;
+   }
+
+   public void setSpiralBounds( final Rectangle bounds ) {
+      _spiralBounds = bounds;
+   }
+
+   public void setSpiralBounds( final int x, final int y, final int width, final int height ) {
+      _spiralBounds = new Rectangle( x, y, width, height );
+   }
+
+   public Rectangle getSpiralBounds() {
+      return _spiralBounds;
+   }
+
+   //   final public void addSpotSketcher( final SpotSketcher spotSketcher ) {
+   //      if ( _spotSketcherList == null ) {
+   //         _spotSketcherList = new ArrayList<SpotSketcher>();
+   //      }
+   //      if ( !_spotSketcherList.contains( spotSketcher ) ) {
+   //         _spotSketcherList.add( spotSketcher );
+   //      }
+   //   }
+   //
+   //   final public void removeSpotSketcher( final SpotSketcher spotSketcher ) {
+   //      if ( _spotSketcherList != null ) {
+   //         _spotSketcherList.remove( spotSketcher );
+   //      }
+   //   }
+   //
+   //   final public Collection<SpotSketcher> getSpotSketchers() {
+   //      return _spotSketcherList;
+   //   }
+
+   final public void setSpotSketcher( final SpiralBarUtil.SpotSketcher spotSketcher ) {
+      _spotSketcher = spotSketcher;
+   }
+
+   final public SpiralBarUtil.SpotSketcher getSpotSketcher() {
+      return _spotSketcher;
+   }
+
+   final public void setSpanSketcher( final SpiralBarUtil.SpanSketcher spanSketcher ) {
+      _spanSketcher = spanSketcher;
+   }
+
+   final public SpiralBarUtil.SpanSketcher getSpanSketcher() {
+      return _spanSketcher;
+   }
+
+
+   final public void addSpot( final int spot ) {
+      if ( _spotSet == null ) {
+         _spotSet = new HashSet<>();
+      }
+      _spotSet.add( spot );
+   }
+
+   final public Collection<Integer> getSpots() {
+      return Collections.unmodifiableSet( _spotSet );
+   }
+
+   final public void addSpan( final int start, final int stop ) {
+      if ( _spanSet == null ) {
+         _spanSet = new HashSet<>();
+      }
+      _spanSet.add( new Point( start, stop ) );
+   }
+
+   final public Collection<Point> getSpans() {
+      return Collections.unmodifiableSet( _spanSet );
+   }
+
+
+}

Added: ctakes/sandbox/timelanes/org/chboston/cnlp/timeline/oldUI/SpiralBarUtil.java
URL: http://svn.apache.org/viewvc/ctakes/sandbox/timelanes/org/chboston/cnlp/timeline/oldUI/SpiralBarUtil.java?rev=1660963&view=auto
==============================================================================
--- ctakes/sandbox/timelanes/org/chboston/cnlp/timeline/oldUI/SpiralBarUtil.java (added)
+++ ctakes/sandbox/timelanes/org/chboston/cnlp/timeline/oldUI/SpiralBarUtil.java Thu Feb 19 18:06:13 2015
@@ -0,0 +1,410 @@
+package org.chboston.cnlp.timeline.oldUI;
+
+import java.awt.*;
+import java.awt.geom.GeneralPath;
+import java.awt.geom.Point2D;
+
+/**
+ * Author: SPF
+ * Affiliation: CHIP-NLP
+ * Date: 8/10/12
+ */
+final public class SpiralBarUtil {
+
+
+   private SpiralBarUtil() {
+   }
+
+   static public final Color TRACK_COLOR = new Color( 156, 156, 156, 196 );
+   static public final Color TRACK_HIGHLIGHT = Color.LIGHT_GRAY.brighter();
+   static public final Color TRACK_SHADOW = Color.GRAY.brighter();
+
+   static public final Color THUMB_COLOR = Color.LIGHT_GRAY.brighter();
+   static public final Color THUMB_SHADOW = new Color( 64, 64, 64, 128 );
+   static public final Color THUMB_SHADOW_EDGE = new Color( 128, 128, 128, 128 );
+
+   static public final Color TIMELINE_COLOR = Color.DARK_GRAY;
+   static public final Color DATE_COLOR = Color.GREEN;
+   static public final Color DATE_HIGHLIGHT = Color.LIGHT_GRAY;
+   static public final Color DATE_SHADOW = Color.BLACK;
+   //   static public final Color DATE_SELECTED = Color.CYAN.darker().darker();
+   static public final Color DATE_SELECTED = Color.CYAN.darker();
+
+   static public final Color TIMELINE_ENDPOINT_COLOR = Color.DARK_GRAY;
+   static public final Color TIMELINE_ENDPOINT_SHADOW = Color.BLACK;
+
+   static public final Stroke STROKE_SEVEN = new BasicStroke( 7, BasicStroke.CAP_BUTT, BasicStroke.JOIN_ROUND );
+   static public final Stroke STROKE_SIX = new BasicStroke( 6, BasicStroke.CAP_BUTT, BasicStroke.JOIN_ROUND );
+   static public final Stroke STROKE_FIVE = new BasicStroke( 5, BasicStroke.CAP_BUTT, BasicStroke.JOIN_ROUND );
+   static public final Stroke STROKE_FOUR = new BasicStroke( 4, BasicStroke.CAP_BUTT, BasicStroke.JOIN_ROUND );
+   static public final Stroke STROKE_THREE = new BasicStroke( 3, BasicStroke.CAP_BUTT, BasicStroke.JOIN_ROUND );
+   static public final Stroke STROKE_TWO = new BasicStroke( 2, BasicStroke.CAP_BUTT, BasicStroke.JOIN_ROUND );
+   static public final Stroke STROKE_ONE = new BasicStroke( 1, BasicStroke.CAP_BUTT, BasicStroke.JOIN_ROUND );
+
+   static public final Stroke STROKE_ROUND_NINE = new BasicStroke( 9, BasicStroke.CAP_ROUND, BasicStroke.JOIN_ROUND );
+   static public final Stroke STROKE_ROUND_SEVEN = new BasicStroke( 7, BasicStroke.CAP_ROUND, BasicStroke.JOIN_ROUND );
+   static public final Stroke STROKE_ROUND_SIX = new BasicStroke( 6, BasicStroke.CAP_ROUND, BasicStroke.JOIN_ROUND );
+   static public final Stroke STROKE_ROUND_FIVE = new BasicStroke( 5, BasicStroke.CAP_ROUND, BasicStroke.JOIN_ROUND );
+   static public final Stroke STROKE_ROUND_FOUR = new BasicStroke( 4, BasicStroke.CAP_ROUND, BasicStroke.JOIN_ROUND );
+
+   static public final Stroke ZOOM_DRAG_STROKE_1 = new BasicStroke( 22, BasicStroke.CAP_BUTT, BasicStroke.JOIN_ROUND );
+   static public final Stroke ZOOM_DRAG_STROKE = new BasicStroke( 20, BasicStroke.CAP_BUTT, BasicStroke.JOIN_ROUND );
+   static public final Color ZOOM_DRAG_COLOR = Color.LIGHT_GRAY;
+   static public final Color ZOOM_DRAG_SHADOW = Color.GRAY;
+
+   static public final double RADIAN = Math.PI / 180; // 1 Radian
+
+   static public final double START_RADIAN = 0;
+
+   // Good combinations are 2 & 2, 1 & 4, 1.5 & 3, 0.9 & 5, 1.1 & 4,
+   static public final double SMALL_SPOKE_LENGTH = 0.9;
+   static public final int SMALL_LOOP_COUNT = 5;
+   static public final double SMALL_STOP_RADIAN = 5.75;
+
+   static public final double MED_SPOKE_LENGTH = 1.475;
+   static public final int MED_LOOP_COUNT = 3;
+   static public final double MED_STOP_RADIAN = 3.75;
+
+   static public final double LARGE_SPOKE_LENGTH = 2;
+   static public final int LARGE_LOOP_COUNT = 2;
+   static public final double LARGE_STOP_RADIAN = 2.75;
+
+   static public final int SCROLLBAR_TRACK_SPACER = 3;
+   static public final int SPIRAL_CIRCUMFERENCE = 72;
+
+
+   static public GeneralPath createCompleteSpiralPath( final SpiralBarUIDelegate spiralUI, final int width,
+                                                       final double start, final double end ) {
+      final int circumference = spiralUI.getCircumference();
+      final double centerX = circumference / 2;
+      final double centerY = circumference / 2;
+      return createCompleteSpiralPath( centerX, centerY, width - centerX, centerY, start, end,
+            spiralUI.getStopRadian(), spiralUI.getSpokeDistance() );
+   }
+
+
+   /**
+    * Archimedean Spiral.
+    *
+    * @return Graphics2D GeneralPath for the spiral
+    */
+   static public GeneralPath createCompleteSpiralPath( final double centerX1, final double centerY1,
+                                                       final double centerX2, final double centerY2,
+                                                       final double start, final double end,
+                                                       final double loops, final double spokeDistance ) {
+      final double startDistance = start * 360 * RADIAN;
+      final double stopDistance = loops * 360 * RADIAN;
+      final double endDistance = end * 360 * RADIAN;
+
+      double r = spokeDistance * startDistance;
+      double x = Math.cos( startDistance ) * r + centerX1;
+      double y = Math.sin( startDistance ) * r + centerY1;
+
+      final GeneralPath path = new GeneralPath();
+      // start at the center of the west spiral
+      path.moveTo( x, y );
+      for ( double theta = startDistance; theta < stopDistance; theta += RADIAN ) {
+         r = spokeDistance * theta; // Radius of current point
+         x = Math.cos( theta ) * r + centerX1;
+         y = Math.sin( theta ) * r + centerY1;
+         path.lineTo( x, y );
+      }
+
+      x = centerX2 - Math.cos( stopDistance ) * r;
+      y = Math.sin( stopDistance ) * r + centerY2;
+
+      // start at the turn of the east spiral
+      path.lineTo( x, y );
+      for ( double theta = stopDistance; theta >= endDistance; theta -= RADIAN ) {
+         r = spokeDistance * theta; // Radius of current point
+         x = centerX2 - Math.cos( theta ) * r;
+         y = Math.sin( theta ) * r + centerY2;
+         path.lineTo( x, y );
+      }
+      return path;
+   }
+
+
+   /**
+    * Archimedean Spiral.
+    *
+    * @return Graphics2D GeneralPath for the spiral
+    */
+   static public GeneralPath createLeftSpiralPath( final double centerX1, final double centerY1,
+                                                   final double start, final double end,
+                                                   final double spokeDistance ) {
+      final double startDistance = start * 360 * RADIAN;
+      final double stopDistance = end * 360 * RADIAN;
+
+      double r = spokeDistance * startDistance;
+      double x = Math.cos( startDistance ) * r + centerX1;
+      double y = Math.sin( startDistance ) * r + centerY1;
+
+      final GeneralPath path = new GeneralPath();
+      // start at the center of the west spiral
+      path.moveTo( x, y );
+      for ( double theta = startDistance; theta < stopDistance; theta += RADIAN ) {
+         r = spokeDistance * theta; // Radius of current point
+         x = Math.cos( theta ) * r + centerX1;
+         y = Math.sin( theta ) * r + centerY1;
+         path.lineTo( x, y );
+      }
+      return path;
+   }
+
+   /**
+    * Archimedean Spiral.
+    *
+    * @return Graphics2D GeneralPath for the spiral
+    */
+   static public double getLeftSpiralTheta( final double pointX, final double pointY,
+                                            final SpiralBarUIDelegate spiralUI ) {
+      final Point2D wantedPoint = new Point2D.Double( pointX, pointY );
+      final int circumference = spiralUI.getCircumference();
+      final int centerX1 = circumference / 2;
+      final int centerY1 = circumference / 2;
+      final double stopRadian = spiralUI.getStopRadian();
+      final double spokeDistance = spiralUI.getSpokeDistance();
+      final double startDistance = 0;
+      final double stopDistance = stopRadian * 360 * RADIAN;
+
+      double r = spokeDistance * startDistance;
+      double x = Math.cos( startDistance ) * r + centerX1;
+      double y = Math.sin( startDistance ) * r + centerY1;
+
+      Point2D.Double spiralPoint = new Point2D.Double( x, y );
+      double bestTheta = startDistance;
+      double bestDistance = wantedPoint.distance( spiralPoint );
+
+      // start at the center of the west spiral
+      for ( double theta = startDistance; theta < stopDistance; theta += RADIAN ) {
+         r = spokeDistance * theta; // Radius of current point
+         x = Math.cos( theta ) * r + centerX1;
+         y = Math.sin( theta ) * r + centerY1;
+         spiralPoint = new Point2D.Double( x, y );
+         final double distance = wantedPoint.distance( spiralPoint );
+         if ( distance <= bestDistance ) {
+            bestTheta = theta;
+            bestDistance = distance;
+         }
+      }
+      return bestTheta / 360 / RADIAN;
+   }
+
+   /**
+    * Archimedean Spiral.
+    *
+    * @return Graphics2D GeneralPath for the spiral
+    */
+   static public double getRightSpiralTheta( final double pointX, final double pointY,
+                                             final SpiralBarUIDelegate spiralUI, final int spiralWidth ) {
+      final Point2D wantedPoint = new Point2D.Double( pointX, pointY );
+      final int circumference = spiralUI.getCircumference();
+      final int centerX1 = spiralWidth - circumference / 2;
+      final int centerY1 = circumference / 2;
+      final double stopRadian = spiralUI.getStopRadian();
+      final double spokeDistance = spiralUI.getSpokeDistance();
+      final double startDistance = 0;
+      final double stopDistance = stopRadian * 360 * RADIAN;
+
+      double r = spokeDistance * stopDistance;
+      double x = centerX1 - Math.cos( stopDistance ) * r;
+      double y = Math.sin( stopDistance ) * r + centerY1;
+
+      Point2D.Double spiralPoint = new Point2D.Double( x, y );
+      double bestTheta = stopDistance;
+      double bestDistance = wantedPoint.distance( spiralPoint );
+
+      // start at the center of the west spiral
+      for ( double theta = stopDistance; theta >= startDistance; theta -= RADIAN ) {
+         r = spokeDistance * theta; // Radius of current point
+         x = centerX1 - Math.cos( theta ) * r;
+         y = Math.sin( theta ) * r + centerY1;
+         spiralPoint = new Point2D.Double( x, y );
+         final double distance = wantedPoint.distance( spiralPoint );
+         if ( distance <= bestDistance ) {
+            bestTheta = theta;
+            bestDistance = distance;
+         }
+      }
+      return bestTheta / 360 / RADIAN;
+   }
+
+
+   /**
+    * Archimedean Spiral.
+    *
+    * @return Graphics2D GeneralPath for the spiral
+    */
+   static public GeneralPath createRightSpiralPath( final double centerX1, final double centerY1,
+                                                    final double start, final double end,
+                                                    final double spokeDistance, GeneralPath path ) {
+      final double startDistance = start * 360 * RADIAN;
+      final double stopDistance = end * 360 * RADIAN;
+
+      double r = spokeDistance * stopDistance;
+      double x = centerX1 - Math.cos( stopDistance ) * r;
+      double y = Math.sin( stopDistance ) * r + centerY1;
+
+      final boolean havePath = path != null && path.getCurrentPoint() != null;
+      if ( !havePath ) {
+         path = new GeneralPath();
+         // start at the turn of the east spiral
+         path.moveTo( x, y );
+      } else {
+         path.lineTo( x, y );
+      }
+      for ( double theta = stopDistance; theta >= startDistance; theta -= RADIAN ) {
+         r = spokeDistance * theta; // Radius of current point
+         x = centerX1 - Math.cos( theta ) * r;
+         y = Math.sin( theta ) * r + centerY1;
+         path.lineTo( x, y );
+      }
+      return path;
+   }
+
+   static public double getLineLociForPoint( final double x, final double y,
+                                             final SpiralBarUIDelegate spiralUI, final int spiralWidth,
+                                             final int firstVisibleX, final int visibleWidth,
+                                             final int totalLineWidth ) {
+      final int circumference = spiralUI.getCircumference();
+      final int centerX1 = circumference / 2;
+      if ( x >= centerX1 && x <= spiralWidth - centerX1 && y <= 10 ) {
+         // Point is on horizontal line
+         final double sketchX = x - centerX1 - spiralUI.getTrackSpacer();
+         final double sketchWidth = spiralWidth - circumference;
+         final double sketchRatioX = sketchX / sketchWidth;
+         final double scaledX = sketchRatioX * visibleWidth;
+         return firstVisibleX + scaledX;
+      }
+      final double spiralledLength = totalLineWidth - visibleWidth;   // full length for spiral
+      final double stopRadian = spiralUI.getStopRadian();
+      if ( x < circumference ) {
+         // Point is on left spiral
+         final double theta = getLeftSpiralTheta( x, y, spiralUI );
+         final double lociX = theta * spiralledLength / stopRadian;
+         final double reverse = spiralledLength - lociX;
+         if ( reverse > firstVisibleX ) {
+            return firstVisibleX;
+         }
+         final double min = Math.min( firstVisibleX, reverse );
+         //         System.out.println(
+         //               "Left, Loci " + lociX + " lineW " + totalLineWidth + " spiralL " + spiralledLength + " min " + min
+         //                     + " return " + (firstVisibleX - min) );
+         return firstVisibleX - min;
+      }
+      // Point is on right spiral
+      final double theta = getRightSpiralTheta( x, y, spiralUI, spiralWidth );
+      final double lociX = theta * spiralledLength / stopRadian;
+      //      System.out.println(
+      //            "Right, Loci " + lociX + " lineW " + totalLineWidth + " spiralL " + spiralledLength + " add " + (
+      //                  firstVisibleX + visibleWidth) );
+      return Math.min( totalLineWidth, firstVisibleX + visibleWidth + lociX );
+   }
+
+   // TODO - use this for all path creation
+   static public GeneralPath getPathOnCompleteSpiral( final SpiralBarUIDelegate spiralUI, final int spiralWidth,
+                                                      final int wantedX1, final int wantedX2,
+                                                      final int firstVisibleX, final int visibleWidth,
+                                                      final int totalLineWidth ) {
+      final int circumference = spiralUI.getCircumference();
+      final int centerX1 = circumference / 2;
+      final int centerY1 = circumference / 2;
+      final double spiralledLength = totalLineWidth - visibleWidth;   // full length for spiral
+      final double stopRadian = spiralUI.getStopRadian();
+      final double spokeDistance = spiralUI.getSpokeDistance();
+      GeneralPath path = new GeneralPath();
+      if ( wantedX1 < firstVisibleX ) {
+         final double spiralX1 = firstVisibleX - wantedX1;      // length for left spiral + x on length
+         final double ratioX1 = stopRadian - stopRadian * spiralX1 / spiralledLength;
+         final double spiralX2 = wantedX2 < firstVisibleX ? firstVisibleX - wantedX2
+                                                          : 0;      // length for left spiral + x on length
+         final double ratioX2 = stopRadian - stopRadian * spiralX2 / spiralledLength;
+         path = createLeftSpiralPath( centerX1, centerY1, ratioX1, ratioX2, spokeDistance );
+         if ( wantedX2 <= firstVisibleX ) {
+            return path;
+         }
+      }
+      if ( wantedX1 >= firstVisibleX && wantedX1 <= firstVisibleX + visibleWidth ) {
+         final Point p1 = getPointOnLine( spiralUI, spiralWidth, wantedX1, firstVisibleX, visibleWidth );
+         path.moveTo( p1.x, p1.y );
+      }
+      if ( wantedX2 >= firstVisibleX && wantedX2 <= firstVisibleX + visibleWidth ) {
+         final Point p2 = getPointOnLine( spiralUI, spiralWidth, wantedX2, firstVisibleX, visibleWidth );
+         path.lineTo( p2.x, p2.y );
+         return path;
+      }
+      if ( wantedX2 > firstVisibleX + visibleWidth ) {
+         final double spiralX1 = wantedX1 < firstVisibleX + visibleWidth ? 0 : wantedX1 - firstVisibleX
+                                                                               -
+                                                                               visibleWidth;      // length for left spiral + x on length
+         final double ratioX1 = stopRadian - stopRadian * spiralX1 / spiralledLength;
+         final double spiralX2 = wantedX2 - (firstVisibleX + visibleWidth);      // length for left spiral + x on length
+         final double ratioX2 = stopRadian - stopRadian * spiralX2 / spiralledLength;
+         path = createRightSpiralPath( spiralWidth - centerX1, centerY1, ratioX2, ratioX1, spokeDistance, path );
+      }
+      return path;
+   }
+
+   // TODO remove - only used in temp2
+   static public Point getPointOnCompleteSpiral( final SpiralBarUIDelegate spiralUI, final int spiralWidth,
+                                                 final int wantedX,
+                                                 final int firstVisibleX, final int visibleWidth,
+                                                 final int totalLineWidth ) {
+      if ( wantedX >= firstVisibleX && wantedX <= firstVisibleX + visibleWidth ) {
+         return getPointOnLine( spiralUI, spiralWidth, wantedX, firstVisibleX, visibleWidth );
+      }
+      final double spiralledLength = totalLineWidth - visibleWidth;   // full length for spiral
+      final double stopRadian = spiralUI.getStopRadian();
+      if ( wantedX < firstVisibleX ) {
+         final double spiralX = firstVisibleX - wantedX;      // length for left spiral + x on length
+         final double ratioX = stopRadian * spiralX / spiralledLength;
+         return getPointOnSpiral( spiralUI, stopRadian - ratioX );
+      }
+      final double spiralX = wantedX - (firstVisibleX + visibleWidth);
+      final double ratioX = stopRadian * spiralX / spiralledLength;
+      final Point reversed = getPointOnSpiral( spiralUI, stopRadian - ratioX );
+      return new Point( spiralWidth - reversed.x, reversed.y );
+   }
+
+   static public Point getPointOnLine( final SpiralBarUIDelegate spiralUI, final int spiralWidth,
+                                       final int wantedX,
+                                       final int firstVisibleX, final int visibleWidth ) {
+      final int y = 1;
+      final int x = wantedX - firstVisibleX;
+      final int circumference = spiralUI.getCircumference();
+      final int sketchWidth = spiralWidth - circumference;
+      final double scaleX = x / (double)visibleWidth * sketchWidth;
+      return new Point( circumference / 2 + (int)scaleX, y );
+   }
+
+   // TODO remove - only used in getPointOnCompleteSpiral
+   static public Point getPointOnSpiral( final SpiralBarUIDelegate spiralUI, final double ratioX ) {
+      final double rDistance = ratioX * 360 * RADIAN;
+      final double r = rDistance * spiralUI.getSpokeDistance();
+      final double x = Math.cos( rDistance ) * r;
+      final double y = Math.sin( rDistance ) * r;
+
+      final int circumference = spiralUI.getCircumference();
+      final int centerX1 = circumference / 2;
+      final int centerY1 = circumference / 2;
+      return new Point( centerX1 + (int)x, centerY1 + (int)y );
+   }
+
+
+   //   static public interface SpotModel {
+   //      int getSpotCount();
+   //   }
+   //
+   //
+   //
+   //   // TODO replace with model, renderers
+   static public interface SpotSketcher {
+      public void sketchSpot( final Graphics2D g, final Point point );
+   }
+
+   static public interface SpanSketcher {
+      public void sketchSpan( final Graphics2D g, final Point point1, final Point point2 );
+   }
+
+}