├── aibu_screencast.gif ├── src └── si │ └── vicos │ └── annotations │ ├── editor │ ├── changes.txt │ ├── splash.png │ ├── icon-16.png │ ├── icon-64.png │ ├── annotation.ttf │ ├── fontawesome.ttf │ ├── AnnotationRenderer.java │ ├── tracking │ │ ├── ImagesProvider.java │ │ ├── RectangleAnnotationRenderer.java │ │ ├── BackgroundFigure.java │ │ ├── TextExporter.java │ │ ├── InterpolationAssistant.java │ │ ├── ValuePlot.java │ │ ├── TransferableAnnotations.java │ │ ├── FrameTagsEditor.java │ │ ├── PolygonAnnotationRenderer.java │ │ ├── UndoableAnnotatedSequence.java │ │ └── TagPlot.java │ ├── aibu.ini │ ├── defaults.ini │ ├── ThumbnailRenderer.java │ ├── annotation.ini │ ├── general.ini │ ├── ApplicationExceptionHandler.java │ ├── ToolbarToggleButton.java │ ├── ImageCache.java │ ├── AnnotationViewer.java │ ├── FontImageProvider.java │ ├── AnnotationsDocumentRenderer.java │ ├── AnnotationEditor.java │ ├── ToggleAction.java │ ├── AnnotatedImageFigure.java │ ├── ThumbnailGenerator.java │ ├── AnnotatorSplash.java │ ├── Interpolator.java │ ├── ThumbnailGridList.java │ ├── PointAnnotationEditor.java │ ├── RectangleAnnotationEditor.java │ ├── Annotator.java │ └── RotatedRectangleAnnotationEditor.java │ ├── SituatedAnnotation.java │ ├── tracking │ ├── AnnotationList.java │ ├── AnnotatedSequenceListener.java │ ├── Annotations.java │ ├── CodeAnnotation.java │ ├── Interval.java │ ├── AbstractAnnotatedSequence.java │ ├── ReversedAnnotationsProxy.java │ ├── Values.java │ ├── Trajectory.java │ └── Tags.java │ ├── ShapeAnnotation.java │ ├── AnnotationsMetadata.java │ ├── Context.java │ ├── Utils.java │ ├── LabelAnnotation.java │ ├── PointAnnotation.java │ ├── PolygonAnnotation.java │ ├── Annotation.java │ └── RectangleAnnotation.java ├── .gitignore ├── ivy.xml └── README.md /aibu_screencast.gif: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/votchallenge/aibu/HEAD/aibu_screencast.gif -------------------------------------------------------------------------------- /src/si/vicos/annotations/editor/changes.txt: -------------------------------------------------------------------------------- 1 | Development release 1 2 | - AVT format prototype 3 | - tracking annotations usability testing -------------------------------------------------------------------------------- /src/si/vicos/annotations/editor/splash.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/votchallenge/aibu/HEAD/src/si/vicos/annotations/editor/splash.png -------------------------------------------------------------------------------- /src/si/vicos/annotations/editor/icon-16.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/votchallenge/aibu/HEAD/src/si/vicos/annotations/editor/icon-16.png -------------------------------------------------------------------------------- /src/si/vicos/annotations/editor/icon-64.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/votchallenge/aibu/HEAD/src/si/vicos/annotations/editor/icon-64.png -------------------------------------------------------------------------------- /src/si/vicos/annotations/editor/annotation.ttf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/votchallenge/aibu/HEAD/src/si/vicos/annotations/editor/annotation.ttf -------------------------------------------------------------------------------- /src/si/vicos/annotations/editor/fontawesome.ttf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/votchallenge/aibu/HEAD/src/si/vicos/annotations/editor/fontawesome.ttf -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | *.class 2 | 3 | # Mobile Tools for Java (J2ME) 4 | .mtj.tmp/ 5 | 6 | # Package Files # 7 | *.jar 8 | *.war 9 | *.ear 10 | 11 | # virtual machine crash logs, see http://www.java.com/en/download/help/error_hotspot.xml 12 | hs_err_pid* 13 | 14 | build 15 | bin 16 | deploy 17 | 18 | # eclipse project 19 | .project 20 | .classpath 21 | -------------------------------------------------------------------------------- /src/si/vicos/annotations/editor/AnnotationRenderer.java: -------------------------------------------------------------------------------- 1 | package si.vicos.annotations.editor; 2 | 3 | import java.awt.Graphics2D; 4 | 5 | /** 6 | * The Interface AnnotationRenderer. 7 | */ 8 | public interface AnnotationRenderer { 9 | 10 | /** 11 | * Paint. 12 | * 13 | * @param g 14 | * the g 15 | */ 16 | public void paint(Graphics2D g); 17 | 18 | } 19 | -------------------------------------------------------------------------------- /src/si/vicos/annotations/SituatedAnnotation.java: -------------------------------------------------------------------------------- 1 | package si.vicos.annotations; 2 | 3 | import java.awt.geom.Point2D; 4 | 5 | /** 6 | * The Class SituatedAnnotation. 7 | */ 8 | public abstract class SituatedAnnotation extends Annotation { 9 | 10 | /** 11 | * Gets the center. 12 | * 13 | * @return the center 14 | */ 15 | public abstract Point2D getCenter(); 16 | 17 | } 18 | -------------------------------------------------------------------------------- /src/si/vicos/annotations/editor/tracking/ImagesProvider.java: -------------------------------------------------------------------------------- 1 | package si.vicos.annotations.editor.tracking; 2 | 3 | import java.io.File; 4 | 5 | /** 6 | * The Interface ImagesProvider. 7 | */ 8 | public interface ImagesProvider { 9 | 10 | /** 11 | * Gets the image. 12 | * 13 | * @param i 14 | * the i 15 | * @return the image 16 | */ 17 | public File getImage(int i); 18 | 19 | } -------------------------------------------------------------------------------- /src/si/vicos/annotations/editor/aibu.ini: -------------------------------------------------------------------------------- 1 | icon = fa-pencil 2 | 3 | tools-metadata = fa-list 4 | tools-tags = fa-tags 5 | 6 | keyframe = fa-map-marker 7 | 8 | interpolate = fa-expand 9 | 10 | select-start = annotation-select-start 11 | select-end = annotation-select-end 12 | 13 | settings = fa-keyboard-o 14 | 15 | annotations = fa-database 16 | value-tag = fa-tag 17 | value-numerical = fa-line-chart 18 | value-string = fa-font 19 | value-points = fa-map-marker -------------------------------------------------------------------------------- /src/si/vicos/annotations/tracking/AnnotationList.java: -------------------------------------------------------------------------------- 1 | package si.vicos.annotations.tracking; 2 | 3 | import si.vicos.annotations.Annotation; 4 | 5 | /** 6 | * The Interface AnnotationList. 7 | */ 8 | public interface AnnotationList { 9 | 10 | /** 11 | * Size. 12 | * 13 | * @return the int 14 | */ 15 | public int size(); 16 | 17 | /** 18 | * Gets the. 19 | * 20 | * @param frame 21 | * the frame 22 | * @return the annotation 23 | */ 24 | public Annotation get(int frame); 25 | 26 | } 27 | -------------------------------------------------------------------------------- /ivy.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | -------------------------------------------------------------------------------- /src/si/vicos/annotations/ShapeAnnotation.java: -------------------------------------------------------------------------------- 1 | package si.vicos.annotations; 2 | 3 | import java.awt.geom.Point2D; 4 | import java.util.List; 5 | 6 | /** 7 | * The Class ShapeAnnotation. 8 | */ 9 | public abstract class ShapeAnnotation extends SituatedAnnotation { 10 | 11 | /** 12 | * Gets the bounding box. 13 | * 14 | * @return the bounding box 15 | */ 16 | public abstract RectangleAnnotation getBoundingBox(); 17 | 18 | /** 19 | * Gets the polygon. 20 | * 21 | * @return the polygon 22 | */ 23 | public abstract List getPolygon(); 24 | 25 | } 26 | -------------------------------------------------------------------------------- /src/si/vicos/annotations/editor/defaults.ini: -------------------------------------------------------------------------------- 1 | tracking.keybindings.previous = LEFT 2 | tracking.keybindings.next = RIGHT 3 | tracking.keybindings.previous_keyframe = ctrl LEFT 4 | tracking.keybindings.next_keyframe = ctrl RIGHT 5 | tracking.keybindings.first = shift ctrl LEFT 6 | tracking.keybindings.last = shift ctrl RIGHT 7 | tracking.keybindings.reset = DELETE 8 | tracking.keybindings.save = ctrl S 9 | tracking.keybindings.quit = shift ctrl Q 10 | tracking.keybindings.select_start = shift A 11 | tracking.keybindings.select_end = shift S 12 | tracking.keybindings.undo = ctrl Z 13 | tracking.keybindings.redo = ctrl shift Z -------------------------------------------------------------------------------- /src/si/vicos/annotations/editor/ThumbnailRenderer.java: -------------------------------------------------------------------------------- 1 | package si.vicos.annotations.editor; 2 | 3 | import java.awt.image.BufferedImage; 4 | 5 | /** 6 | * The Interface ThumbnailRenderer. 7 | */ 8 | public interface ThumbnailRenderer { 9 | 10 | /** 11 | * Render. 12 | * 13 | * @param obj 14 | * the obj 15 | * @return the buffered image 16 | */ 17 | public BufferedImage render(Object obj); 18 | 19 | /** 20 | * Gets the width. 21 | * 22 | * @return the width 23 | */ 24 | public int getWidth(); 25 | 26 | /** 27 | * Gets the height. 28 | * 29 | * @return the height 30 | */ 31 | public int getHeight(); 32 | 33 | } 34 | -------------------------------------------------------------------------------- /src/si/vicos/annotations/editor/annotation.ini: -------------------------------------------------------------------------------- 1 | annotation-play=annotation:\ue385 2 | annotation-pause=annotation:\uea1d 3 | annotation-last=annotation:\uea22 4 | annotation-reverse=annotation:\uea1c 5 | annotation-stop=annotation:\uea1e 6 | annotation-select-end=annotation:\ue603 7 | annotation-first=annotation:\uea21 8 | annotation-next=annotation:\uea24 9 | annotation-forward=annotation:\uea20 10 | annotation-editor-polygon=annotation:\ue600 11 | annotation-select-start=annotation:\ue604 12 | annotation-paint-format=annotation:\ue90c 13 | annotation-editor-rectangle=annotation:\ue601 14 | annotation-previous=annotation:\uea23 15 | annotation-backward=annotation:\uea1f 16 | annotation-editor-rotated-rectangle=annotation:\ue602 17 | -------------------------------------------------------------------------------- /src/si/vicos/annotations/editor/general.ini: -------------------------------------------------------------------------------- 1 | 2 | clear = fa-eraser 3 | execute = fa-cog 4 | view = fa-eye 5 | 6 | zoom-normal = fa-search 7 | zoom-in = fa-search-plus 8 | zoom-out = fa-search-minus 9 | zoom-fit = fa-crosshairs 10 | 11 | pause = fa-pause 12 | resume = fa-play 13 | start = fa-play 14 | play = fa-play 15 | eject = fa-eject 16 | stop = fa-stop 17 | 18 | save = fa-floppy-o 19 | new = fa-file-o 20 | load = fa-folder-o 21 | open = fa-folder-o 22 | 23 | undo = fa-undo 24 | redo = fa-repeat 25 | 26 | quit = fa-power-off 27 | exit = fa-power-off 28 | 29 | remove = fa-times 30 | close = fa-times 31 | cancel = fa-times 32 | 33 | go-previous = annotation-reverse 34 | go-next = annotation-play 35 | go-previous-keyframe = fa-backward 36 | go-next-keyframe = fa-forward 37 | go-first = fa-fast-backward 38 | go-last = fa-fast-forward -------------------------------------------------------------------------------- /src/si/vicos/annotations/AnnotationsMetadata.java: -------------------------------------------------------------------------------- 1 | package si.vicos.annotations; 2 | 3 | import java.awt.Image; 4 | import java.util.Set; 5 | 6 | /** 7 | * The Interface AnnotationsMetadata. 8 | */ 9 | public interface AnnotationsMetadata { 10 | 11 | /** 12 | * Gets the keys. 13 | * 14 | * @return the keys 15 | */ 16 | public Set getKeys(); 17 | 18 | /** 19 | * Gets the metadata. 20 | * 21 | * @param name 22 | * the name 23 | * @return the metadata 24 | */ 25 | public String getMetadata(String name); 26 | 27 | /** 28 | * Gets the preview image. 29 | * 30 | * @return the preview image 31 | */ 32 | public Image getPreviewImage(); 33 | 34 | /** 35 | * Gets the preview region. 36 | * 37 | * @return the preview region 38 | */ 39 | public Annotation getPreviewRegion(); 40 | 41 | } 42 | -------------------------------------------------------------------------------- /src/si/vicos/annotations/editor/ApplicationExceptionHandler.java: -------------------------------------------------------------------------------- 1 | package si.vicos.annotations.editor; 2 | 3 | import org.coffeeshop.application.Application; 4 | import org.coffeeshop.log.Logger; 5 | 6 | /** 7 | * The Class ApplicationExceptionHandler. 8 | */ 9 | public class ApplicationExceptionHandler implements 10 | Thread.UncaughtExceptionHandler { 11 | 12 | /** The log. */ 13 | public Logger log; 14 | 15 | /** 16 | * Instantiates a new application exception handler. 17 | */ 18 | public ApplicationExceptionHandler() { 19 | 20 | this.log = Application.getApplicationLogger(); 21 | 22 | } 23 | 24 | /* 25 | * (non-Javadoc) 26 | * 27 | * @see 28 | * java.lang.Thread.UncaughtExceptionHandler#uncaughtException(java.lang 29 | * .Thread, java.lang.Throwable) 30 | */ 31 | @Override 32 | public void uncaughtException(Thread t, Throwable e) { 33 | 34 | log.report(e); 35 | 36 | } 37 | 38 | } 39 | -------------------------------------------------------------------------------- /src/si/vicos/annotations/editor/ToolbarToggleButton.java: -------------------------------------------------------------------------------- 1 | package si.vicos.annotations.editor; 2 | 3 | import javax.swing.Action; 4 | import javax.swing.JButton; 5 | import javax.swing.JToggleButton; 6 | 7 | /** 8 | * The Class ToolbarToggleButton. 9 | */ 10 | public class ToolbarToggleButton extends JToggleButton { 11 | 12 | /** The Constant serialVersionUID. */ 13 | private static final long serialVersionUID = 1L; 14 | 15 | /** 16 | * Instantiates a new toolbar toggle button. 17 | * 18 | * @param action 19 | * the action 20 | */ 21 | public ToolbarToggleButton(Action action) { 22 | super(action); 23 | 24 | if (action != null 25 | && (action.getValue(Action.SMALL_ICON) != null || action 26 | .getValue(Action.LARGE_ICON_KEY) != null)) { 27 | setHideActionText(true); 28 | } 29 | setHorizontalTextPosition(JButton.CENTER); 30 | setVerticalTextPosition(JButton.BOTTOM); 31 | 32 | } 33 | 34 | } 35 | -------------------------------------------------------------------------------- /src/si/vicos/annotations/Context.java: -------------------------------------------------------------------------------- 1 | package si.vicos.annotations; 2 | 3 | import java.awt.Image; 4 | import java.io.File; 5 | import java.io.IOException; 6 | 7 | import javax.imageio.ImageIO; 8 | 9 | /** 10 | * The Class Context. 11 | * 12 | * @param 13 | * the element type 14 | */ 15 | public abstract class Context { 16 | 17 | /** 18 | * Gets the image represented by a given UID. 19 | * 20 | * @param entry 21 | * the entry 22 | * 23 | * @return the image or null. 24 | */ 25 | public Image getImage(E entry) { 26 | 27 | File imageFile = getImageFile(entry); 28 | 29 | if (!imageFile.exists()) 30 | return null; 31 | 32 | try { 33 | 34 | Image image = ImageIO.read(imageFile); 35 | 36 | return image; 37 | 38 | } catch (IOException e) { 39 | } 40 | 41 | return null; 42 | } 43 | 44 | /** 45 | * Gets the image file. 46 | * 47 | * @param entry 48 | * the entry 49 | * @return the image file 50 | */ 51 | public abstract File getImageFile(E entry); 52 | 53 | } 54 | -------------------------------------------------------------------------------- /src/si/vicos/annotations/editor/tracking/RectangleAnnotationRenderer.java: -------------------------------------------------------------------------------- 1 | package si.vicos.annotations.editor.tracking; 2 | 3 | import java.awt.Graphics2D; 4 | import java.awt.Rectangle; 5 | import java.awt.Shape; 6 | 7 | import si.vicos.annotations.RectangleAnnotation; 8 | import si.vicos.annotations.editor.AnnotationRenderer; 9 | 10 | /** 11 | * The Class RectangleAnnotationRenderer. 12 | */ 13 | public class RectangleAnnotationRenderer implements AnnotationRenderer { 14 | 15 | /** The shape. */ 16 | private Shape shape; 17 | 18 | /** 19 | * Instantiates a new rectangle annotation renderer. 20 | * 21 | * @param annotation 22 | * the annotation 23 | */ 24 | public RectangleAnnotationRenderer(RectangleAnnotation annotation) { 25 | 26 | shape = new Rectangle((int) annotation.getX(), (int) annotation.getY(), 27 | (int) annotation.getWidth(), (int) annotation.getHeight()); 28 | 29 | } 30 | 31 | /* 32 | * (non-Javadoc) 33 | * 34 | * @see 35 | * si.vicos.annotations.editor.AnnotationRenderer#paint(java.awt.Graphics2D) 36 | */ 37 | @Override 38 | public void paint(Graphics2D g) { 39 | 40 | g.draw(shape); 41 | 42 | } 43 | 44 | } 45 | -------------------------------------------------------------------------------- /src/si/vicos/annotations/tracking/AnnotatedSequenceListener.java: -------------------------------------------------------------------------------- 1 | package si.vicos.annotations.tracking; 2 | 3 | import java.util.Set; 4 | 5 | /** 6 | * The listener interface for receiving annotatedSequence events. The class that 7 | * is interested in processing a annotatedSequence event implements this 8 | * interface, and the object created with that class is registered with a 9 | * component using the component's 10 | * addAnnotatedSequenceListener method. When 11 | * the annotatedSequence event occurs, that object's appropriate 12 | * method is invoked. 13 | * 14 | * @see AnnotatedSequenceEvent 15 | */ 16 | public interface AnnotatedSequenceListener { 17 | 18 | /** 19 | * Interval changed. 20 | * 21 | * @param sequence 22 | * the sequence 23 | * @param interval 24 | * the interval 25 | */ 26 | public void intervalChanged(AnnotatedSequence sequence, Interval interval); 27 | 28 | /** 29 | * Metadata changed. 30 | * 31 | * @param sequence 32 | * the sequence 33 | * @param key 34 | * the key 35 | */ 36 | public void metadataChanged(AnnotatedSequence sequence, Set key); 37 | 38 | } 39 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | Aibu - image sequence annotator 2 | =============================== 3 | 4 | Aibu is an image sequence annotator developed and used by the [VOT challenge](http://votchallenge.net/). User can annotate movement of a single target in an image sequence. 5 | 6 | The annotator was only used within the VOT challenge organization until now and is therefore considered an alpha level software. A lot of features are missing or are not fully polished. If you find such a case you are welcome to open an issue report on Github. 7 | 8 | Features: 9 | 10 | * input: image sequence 11 | * per-frame tags 12 | * bounding box interpolation 13 | * aligned or rotated bounding box 14 | * undo, redo 15 | 16 | ![aibu screencast](aibu_screencast.gif) 17 | 18 | Building 19 | -------- 20 | 21 | The annotator is written in Java and can be built using Ant build tool. It depends on the [coffeshop](https://github.com/lukacu/coffeeshop) library. 22 | 23 | $ # build and add coffeshop library to your classpath 24 | $ ant 25 | 26 | `deploy/aibu.jar` will be created 27 | 28 | Using 29 | ----- 30 | 31 | $ java -jar deploy/aibu.jar si.vicos.annotations.editor.Annotator 32 | 33 | Sample Annotations 34 | ------------------ 35 | 36 | Available on http://votchallenge.net/. 37 | -------------------------------------------------------------------------------- /src/si/vicos/annotations/editor/ImageCache.java: -------------------------------------------------------------------------------- 1 | package si.vicos.annotations.editor; 2 | 3 | import java.awt.image.BufferedImage; 4 | import java.io.File; 5 | import java.io.IOException; 6 | 7 | import javax.imageio.ImageIO; 8 | 9 | import org.coffeeshop.cache.DataCache; 10 | import org.coffeeshop.io.TempDirectory; 11 | 12 | /** 13 | * The Class ImageCache. 14 | */ 15 | public class ImageCache extends DataCache { 16 | 17 | /** 18 | * Instantiates a new image cache. 19 | * 20 | * @param memoryLimit 21 | * the memory limit 22 | * @param totalLimit 23 | * the total limit 24 | * @param tempDir 25 | * the temp dir 26 | * @throws IOException 27 | * Signals that an I/O exception has occurred. 28 | */ 29 | public ImageCache(long memoryLimit, long totalLimit, TempDirectory tempDir) 30 | throws IOException { 31 | super(memoryLimit, totalLimit, tempDir); 32 | } 33 | 34 | /* 35 | * (non-Javadoc) 36 | * 37 | * @see org.coffeeshop.cache.DataCache#getDataLength(java.lang.Object) 38 | */ 39 | @Override 40 | protected long getDataLength(BufferedImage object) { 41 | if (object == null) 42 | return 0; 43 | return object.getWidth() * object.getHeight() 44 | * object.getColorModel().getNumComponents(); 45 | } 46 | 47 | /* 48 | * (non-Javadoc) 49 | * 50 | * @see org.coffeeshop.cache.DataCache#readData(java.io.File, long) 51 | */ 52 | @Override 53 | protected BufferedImage readData(File file, long length) throws IOException { 54 | return ImageIO.read(file); 55 | } 56 | 57 | /* 58 | * (non-Javadoc) 59 | * 60 | * @see org.coffeeshop.cache.DataCache#writeData(java.io.File, 61 | * java.lang.Object) 62 | */ 63 | @Override 64 | protected void writeData(File file, BufferedImage data) throws IOException { 65 | ImageIO.write(data, "PNG", file); 66 | } 67 | 68 | } 69 | -------------------------------------------------------------------------------- /src/si/vicos/annotations/tracking/Annotations.java: -------------------------------------------------------------------------------- 1 | package si.vicos.annotations.tracking; 2 | 3 | import java.util.Collection; 4 | import java.util.List; 5 | import java.util.Set; 6 | 7 | import si.vicos.annotations.Annotation; 8 | 9 | /** 10 | * The Interface Annotations. 11 | */ 12 | public interface Annotations extends AnnotationList { 13 | 14 | /** 15 | * Gets the name. 16 | * 17 | * @return the name 18 | */ 19 | public abstract String getName(); 20 | 21 | /* 22 | * (non-Javadoc) 23 | * 24 | * @see si.vicos.annotations.tracking.AnnotationList#get(int) 25 | */ 26 | public abstract Annotation get(int frame); 27 | 28 | /** 29 | * Gets the tags. 30 | * 31 | * @param frame 32 | * the frame 33 | * @return the tags 34 | */ 35 | public abstract Set getTags(int frame); 36 | 37 | /** 38 | * Checks for tag. 39 | * 40 | * @param index 41 | * the index 42 | * @param tag 43 | * the tag 44 | * @return true, if successful 45 | */ 46 | public abstract boolean hasTag(int index, String tag); 47 | 48 | /** 49 | * Gets the value keys. 50 | * 51 | * @return the value keys 52 | */ 53 | public abstract Set getValueKeys(); 54 | 55 | /** 56 | * Gets the tags. 57 | * 58 | * @return the tags 59 | */ 60 | public abstract Set getTags(); 61 | 62 | /* 63 | * (non-Javadoc) 64 | * 65 | * @see si.vicos.annotations.tracking.AnnotationList#size() 66 | */ 67 | public abstract int size(); 68 | 69 | /** 70 | * Find tag. 71 | * 72 | * @param tag 73 | * the tag 74 | * @return the collection 75 | */ 76 | public abstract Collection findTag(String tag); 77 | 78 | /** 79 | * Count tag occurences. 80 | * 81 | * @param tag 82 | * the tag 83 | * @return the int 84 | */ 85 | public abstract int countTagOccurences(String tag); 86 | 87 | /** 88 | * Find values. 89 | * 90 | * @param key 91 | * the key 92 | * @return the list 93 | */ 94 | public abstract List findValues(String key); 95 | 96 | } 97 | -------------------------------------------------------------------------------- /src/si/vicos/annotations/Utils.java: -------------------------------------------------------------------------------- 1 | package si.vicos.annotations; 2 | 3 | import java.lang.reflect.Array; 4 | 5 | /** 6 | * The Class Utils. 7 | */ 8 | public final class Utils { 9 | 10 | /** 11 | * Copy of. 12 | * 13 | * @param 14 | * the generic type 15 | * @param original 16 | * the original 17 | * @param newLength 18 | * the new length 19 | * @return the t[] 20 | */ 21 | @SuppressWarnings("unchecked") 22 | // In Java 5, the java.util.Arrays class has no copyOf() members 23 | public static T[] copyOf(T[] original, int newLength) { 24 | Class type = (Class) original.getClass().getComponentType(); 25 | T[] newArr = (T[]) Array.newInstance(type, newLength); 26 | 27 | System.arraycopy(original, 0, newArr, 0, 28 | Math.min(original.length, newLength)); 29 | 30 | return newArr; 31 | } 32 | 33 | /** 34 | * Copy of. 35 | * 36 | * @param original 37 | * the original 38 | * @param newLength 39 | * the new length 40 | * @return the int[] 41 | */ 42 | public static int[] copyOf(int[] original, int newLength) { 43 | int[] newArr = new int[newLength]; 44 | 45 | System.arraycopy(original, 0, newArr, 0, 46 | Math.min(original.length, newLength)); 47 | 48 | return newArr; 49 | } 50 | 51 | /** 52 | * Copy of. 53 | * 54 | * @param original 55 | * the original 56 | * @param newLength 57 | * the new length 58 | * @return the char[] 59 | */ 60 | public static char[] copyOf(char[] original, int newLength) { 61 | char[] newArr = new char[newLength]; 62 | 63 | System.arraycopy(original, 0, newArr, 0, 64 | Math.min(original.length, newLength)); 65 | 66 | return newArr; 67 | } 68 | 69 | /** 70 | * Append. 71 | * 72 | * @param 73 | * the generic type 74 | * @param original 75 | * the original 76 | * @param add 77 | * the add 78 | * @return the t[] 79 | */ 80 | public static T[] append(T[] original, T add) { 81 | 82 | T[] newArr = copyOf(original, original.length + 1); 83 | newArr[original.length] = add; 84 | 85 | return newArr; 86 | } 87 | 88 | } 89 | -------------------------------------------------------------------------------- /src/si/vicos/annotations/tracking/CodeAnnotation.java: -------------------------------------------------------------------------------- 1 | package si.vicos.annotations.tracking; 2 | 3 | import java.text.ParseException; 4 | 5 | import si.vicos.annotations.Annotation; 6 | 7 | /** 8 | * The Class CodeAnnotation. 9 | */ 10 | public class CodeAnnotation extends Annotation { 11 | 12 | /** The code. */ 13 | private int code; 14 | 15 | /** 16 | * Instantiates a new code annotation. 17 | * 18 | * @param code 19 | * the code 20 | */ 21 | public CodeAnnotation(int code) { 22 | this.code = code; 23 | } 24 | 25 | /* 26 | * (non-Javadoc) 27 | * 28 | * @see si.vicos.annotations.Annotation#reset() 29 | */ 30 | @Override 31 | public void reset() { 32 | code = 0; 33 | } 34 | 35 | /* 36 | * (non-Javadoc) 37 | * 38 | * @see si.vicos.annotations.Annotation#pack() 39 | */ 40 | @Override 41 | public String pack() { 42 | return String.valueOf(code); 43 | } 44 | 45 | /* 46 | * (non-Javadoc) 47 | * 48 | * @see si.vicos.annotations.Annotation#unpack(java.lang.String) 49 | */ 50 | @Override 51 | public void unpack(String data) throws ParseException { 52 | 53 | code = Integer.parseInt(data); 54 | 55 | } 56 | 57 | /* 58 | * (non-Javadoc) 59 | * 60 | * @see si.vicos.annotations.Annotation#clone() 61 | */ 62 | @Override 63 | public Annotation clone() { 64 | return new CodeAnnotation(code); 65 | } 66 | 67 | /* 68 | * (non-Javadoc) 69 | * 70 | * @see 71 | * si.vicos.annotations.Annotation#validate(si.vicos.annotations.Annotation) 72 | */ 73 | @Override 74 | public boolean validate(Annotation a) { 75 | // TODO Auto-generated method stub 76 | return false; 77 | } 78 | 79 | /* 80 | * (non-Javadoc) 81 | * 82 | * @see si.vicos.annotations.Annotation#getType() 83 | */ 84 | @Override 85 | public AnnotationType getType() { 86 | // TODO Auto-generated method stub 87 | return null; 88 | } 89 | 90 | /* 91 | * (non-Javadoc) 92 | * 93 | * @see si.vicos.annotations.Annotation#isNull() 94 | */ 95 | @Override 96 | public boolean isNull() { 97 | return false; 98 | } 99 | 100 | /** 101 | * Gets the code. 102 | * 103 | * @return the code 104 | */ 105 | public int getCode() { 106 | return code; 107 | } 108 | 109 | } 110 | -------------------------------------------------------------------------------- /src/si/vicos/annotations/editor/AnnotationViewer.java: -------------------------------------------------------------------------------- 1 | package si.vicos.annotations.editor; 2 | 3 | import java.awt.Color; 4 | import java.awt.Graphics2D; 5 | import java.awt.Point; 6 | 7 | import javax.swing.JComponent; 8 | 9 | import org.coffeeshop.swing.figure.FigurePanel; 10 | 11 | import si.vicos.annotations.editor.AnnotatedImageFigure.AnnotationPeer; 12 | 13 | /** 14 | * The Class AnnotationViewer. 15 | */ 16 | public abstract class AnnotationViewer { 17 | 18 | /** 19 | * Instantiates a new annotation viewer. 20 | * 21 | * @param peer 22 | * the peer 23 | * @param color 24 | * the color 25 | */ 26 | public AnnotationViewer(AnnotationPeer peer, Color color) { 27 | this.peer = peer; 28 | this.color = color; 29 | 30 | updateGraphics(); 31 | } 32 | 33 | /** The color. */ 34 | protected Color color; 35 | 36 | /** The peer. */ 37 | protected AnnotationPeer peer; 38 | 39 | /** The selected. */ 40 | private boolean selected; 41 | 42 | /** 43 | * Notify repaint. 44 | */ 45 | protected void notifyRepaint() { 46 | peer.repaint(); 47 | } 48 | 49 | /** 50 | * Update graphics. 51 | */ 52 | public abstract void updateGraphics(); 53 | 54 | /** 55 | * Paint. 56 | * 57 | * @param g 58 | * the g 59 | */ 60 | public abstract void paint(Graphics2D g); 61 | 62 | /** 63 | * Gets the component. 64 | * 65 | * @return the component 66 | */ 67 | public abstract JComponent getComponent(); 68 | 69 | /** 70 | * Gets the tool tip. 71 | * 72 | * @param source 73 | * the source 74 | * @param position 75 | * the position 76 | * @return the tool tip 77 | */ 78 | public abstract String getToolTip(FigurePanel source, Point position); 79 | 80 | /** 81 | * Gets the color. 82 | * 83 | * @return the color 84 | */ 85 | public Color getColor() { 86 | return color; 87 | } 88 | 89 | /** 90 | * Sets the selected. 91 | * 92 | * @param selected 93 | * the new selected 94 | */ 95 | public void setSelected(boolean selected) { 96 | this.selected = selected; 97 | notifyRepaint(); 98 | } 99 | 100 | /** 101 | * Checks if is selected. 102 | * 103 | * @return true, if is selected 104 | */ 105 | public boolean isSelected() { 106 | return selected; 107 | } 108 | 109 | } 110 | -------------------------------------------------------------------------------- /src/si/vicos/annotations/LabelAnnotation.java: -------------------------------------------------------------------------------- 1 | package si.vicos.annotations; 2 | 3 | import java.text.ParseException; 4 | 5 | import org.coffeeshop.string.StringUtils; 6 | 7 | /** 8 | * The Class LabelAnnotation. 9 | */ 10 | public class LabelAnnotation extends Annotation { 11 | 12 | /** The label. */ 13 | private String label; 14 | 15 | /** 16 | * Instantiates a new label annotation. 17 | */ 18 | public LabelAnnotation() { 19 | } 20 | 21 | /** 22 | * Instantiates a new label annotation. 23 | * 24 | * @param label 25 | * the label 26 | */ 27 | public LabelAnnotation(String label) { 28 | set(label); 29 | } 30 | 31 | /* 32 | * (non-Javadoc) 33 | * 34 | * @see si.vicos.annotations.Annotation#pack() 35 | */ 36 | @Override 37 | public String pack() { 38 | return isNull() ? "" : label; 39 | } 40 | 41 | /* 42 | * (non-Javadoc) 43 | * 44 | * @see si.vicos.annotations.Annotation#reset() 45 | */ 46 | @Override 47 | public void reset() { 48 | set(""); 49 | } 50 | 51 | /* 52 | * (non-Javadoc) 53 | * 54 | * @see si.vicos.annotations.Annotation#unpack(java.lang.String) 55 | */ 56 | @Override 57 | public void unpack(String data) throws ParseException { 58 | set(data); 59 | } 60 | 61 | /* 62 | * (non-Javadoc) 63 | * 64 | * @see si.vicos.annotations.Annotation#clone() 65 | */ 66 | @Override 67 | public Annotation clone() { 68 | return new LabelAnnotation(label); 69 | } 70 | 71 | /** 72 | * Sets the. 73 | * 74 | * @param data 75 | * the data 76 | */ 77 | public void set(String data) { 78 | label = data; 79 | } 80 | 81 | /** 82 | * Gets the label. 83 | * 84 | * @return the label 85 | */ 86 | public String getLabel() { 87 | return label; 88 | } 89 | 90 | /* 91 | * (non-Javadoc) 92 | * 93 | * @see java.lang.Object#toString() 94 | */ 95 | public String toString() { 96 | return label; 97 | } 98 | 99 | /* 100 | * (non-Javadoc) 101 | * 102 | * @see 103 | * si.vicos.annotations.Annotation#validate(si.vicos.annotations.Annotation) 104 | */ 105 | @Override 106 | public boolean validate(Annotation a) { 107 | return a instanceof LabelAnnotation; 108 | } 109 | 110 | /* 111 | * (non-Javadoc) 112 | * 113 | * @see si.vicos.annotations.Annotation#getType() 114 | */ 115 | @Override 116 | public AnnotationType getType() { 117 | return AnnotationType.LABEL; 118 | } 119 | 120 | /* 121 | * (non-Javadoc) 122 | * 123 | * @see si.vicos.annotations.Annotation#isNull() 124 | */ 125 | @Override 126 | public boolean isNull() { 127 | return StringUtils.empty(label); 128 | } 129 | } -------------------------------------------------------------------------------- /src/si/vicos/annotations/editor/tracking/BackgroundFigure.java: -------------------------------------------------------------------------------- 1 | package si.vicos.annotations.editor.tracking; 2 | 3 | import java.awt.Graphics2D; 4 | import java.awt.Rectangle; 5 | import java.awt.geom.AffineTransform; 6 | import java.awt.geom.Rectangle2D; 7 | import java.util.Map; 8 | 9 | import org.coffeeshop.swing.figure.AbstractFigure; 10 | import org.coffeeshop.swing.figure.FigureObserver; 11 | import org.coffeeshop.swing.viewers.FigureViewer; 12 | import org.coffeeshop.swing.viewers.Viewable; 13 | 14 | /** 15 | * The Class BackgroundFigure. 16 | */ 17 | public abstract class BackgroundFigure extends AbstractFigure implements 18 | Viewable { 19 | 20 | /** The image to screen. */ 21 | private AffineTransform imageToScreen = new AffineTransform(); 22 | 23 | /* 24 | * (non-Javadoc) 25 | * 26 | * @see org.coffeeshop.swing.figure.Figure#paint(java.awt.Graphics2D, 27 | * java.awt.geom.Rectangle2D, java.awt.Rectangle, 28 | * org.coffeeshop.swing.figure.FigureObserver) 29 | */ 30 | public final void paint(Graphics2D g, Rectangle2D figureSize, 31 | Rectangle windowSize, FigureObserver observer) { 32 | 33 | /* 34 | * g.setClip(windowSize.x, windowSize.y, windowSize.width, 35 | * windowSize.height); 36 | */ 37 | 38 | float scale = (float) windowSize.width / (float) figureSize.getWidth(); 39 | 40 | float offsetX = (float) figureSize.getX() * scale 41 | - (float) windowSize.x; 42 | float offsetY = (float) figureSize.getY() * scale 43 | - (float) windowSize.y; 44 | 45 | imageToScreen.setTransform(scale, 0, 0, scale, -offsetX, -offsetY); 46 | 47 | AffineTransform oldT = g.getTransform(); 48 | AffineTransform newT = new AffineTransform(oldT); 49 | newT.concatenate(imageToScreen); 50 | g.setTransform(newT); 51 | 52 | paintGeometry(g, scale, (float) figureSize.getWidth(), 53 | (float) figureSize.getHeight(), observer); 54 | 55 | g.setTransform(oldT); 56 | 57 | } 58 | 59 | /** 60 | * Paint geometry. 61 | * 62 | * @param g 63 | * the g 64 | * @param scale 65 | * the scale 66 | * @param width 67 | * the width 68 | * @param height 69 | * the height 70 | * @param observer 71 | * the observer 72 | */ 73 | protected abstract void paintGeometry(Graphics2D g, float scale, 74 | float width, float height, FigureObserver observer); 75 | 76 | /* 77 | * (non-Javadoc) 78 | * 79 | * @see org.coffeeshop.swing.viewers.Viewable#view(java.util.Map) 80 | */ 81 | public boolean view(Map parameters) { 82 | String title = parameters.get("title"); 83 | if (title == null) { 84 | title = getName(); 85 | } 86 | 87 | FigureViewer viewer = new FigureViewer(title, this); 88 | viewer.setVisible(true); 89 | 90 | return true; 91 | } 92 | } 93 | -------------------------------------------------------------------------------- /src/si/vicos/annotations/tracking/Interval.java: -------------------------------------------------------------------------------- 1 | package si.vicos.annotations.tracking; 2 | 3 | import java.util.Iterator; 4 | 5 | /** 6 | * The Class Interval. 7 | */ 8 | public class Interval implements Iterable { 9 | 10 | /** The begin. */ 11 | private int begin; 12 | 13 | /** The end. */ 14 | private int end; 15 | 16 | /** 17 | * Instantiates a new interval. 18 | * 19 | * @param begin 20 | * the begin 21 | * @param end 22 | * the end 23 | */ 24 | public Interval(int begin, int end) { 25 | super(); 26 | this.begin = Math.min(begin, end); 27 | this.end = Math.max(begin, end); 28 | } 29 | 30 | /** 31 | * Instantiates a new interval. 32 | * 33 | * @param position 34 | * the position 35 | */ 36 | public Interval(int position) { 37 | this(position, position); 38 | } 39 | 40 | /** 41 | * Instantiates a new interval. 42 | * 43 | * @param interval 44 | * the interval 45 | */ 46 | public Interval(Interval interval) { 47 | 48 | this(interval.begin, interval.end); 49 | } 50 | 51 | /** 52 | * Gets the begin. 53 | * 54 | * @return the begin 55 | */ 56 | public int getBegin() { 57 | return begin; 58 | } 59 | 60 | /** 61 | * Gets the end. 62 | * 63 | * @return the end 64 | */ 65 | public int getEnd() { 66 | return end; 67 | } 68 | 69 | /** 70 | * Length. 71 | * 72 | * @return the int 73 | */ 74 | public int length() { 75 | return end - begin; 76 | } 77 | 78 | /** 79 | * Checks if is empty. 80 | * 81 | * @return true, if is empty 82 | */ 83 | public boolean isEmpty() { 84 | return begin == end; 85 | } 86 | 87 | /** 88 | * Returns true if the given interval full contains the interval . 89 | * 90 | * @param interval 91 | * the interval 92 | * @return true, if is within 93 | */ 94 | public boolean isWithin(Interval interval) { 95 | 96 | return (interval.begin <= begin && interval.end >= end); 97 | 98 | } 99 | 100 | /** 101 | * Returns true if the position is within the interval. 102 | * 103 | * @param position 104 | * the position 105 | * @return true, if successful 106 | */ 107 | public boolean contains(int position) { 108 | 109 | return (position >= begin && end >= position); 110 | 111 | } 112 | 113 | /* 114 | * (non-Javadoc) 115 | * 116 | * @see java.lang.Iterable#iterator() 117 | */ 118 | @Override 119 | public Iterator iterator() { 120 | return new Iterator() { 121 | 122 | int current = begin; 123 | 124 | @Override 125 | public void remove() { 126 | 127 | } 128 | 129 | @Override 130 | public Integer next() { 131 | 132 | if (!hasNext()) 133 | return null; 134 | 135 | return current++; 136 | } 137 | 138 | @Override 139 | public boolean hasNext() { 140 | 141 | return current <= end; 142 | 143 | } 144 | }; 145 | } 146 | 147 | /* 148 | * (non-Javadoc) 149 | * 150 | * @see java.lang.Object#toString() 151 | */ 152 | @Override 153 | public String toString() { 154 | return "[" + begin + ", " + end + "]"; 155 | } 156 | 157 | } 158 | -------------------------------------------------------------------------------- /src/si/vicos/annotations/tracking/AbstractAnnotatedSequence.java: -------------------------------------------------------------------------------- 1 | package si.vicos.annotations.tracking; 2 | 3 | import java.awt.Image; 4 | import java.io.File; 5 | import java.util.Collection; 6 | import java.util.List; 7 | import java.util.Set; 8 | 9 | import si.vicos.annotations.Annotation; 10 | import si.vicos.annotations.AnnotationsMetadata; 11 | import si.vicos.annotations.Context; 12 | 13 | /** 14 | * The Class AbstractAnnotatedSequence. 15 | */ 16 | public abstract class AbstractAnnotatedSequence extends Context 17 | implements AnnotationsMetadata, Annotations { 18 | 19 | /** 20 | * Removes the suffix. 21 | * 22 | * @param str 23 | * the str 24 | * @param suffix 25 | * the suffix 26 | * @return the string 27 | */ 28 | public static final String removeSuffix(String str, String suffix) { 29 | 30 | if (!str.endsWith(suffix)) 31 | return str; 32 | 33 | return str.substring(0, str.length() - suffix.length()); 34 | 35 | } 36 | 37 | /* 38 | * (non-Javadoc) 39 | * 40 | * @see si.vicos.annotations.tracking.Annotations#getName() 41 | */ 42 | public abstract String getName(); 43 | 44 | /* 45 | * (non-Javadoc) 46 | * 47 | * @see si.vicos.annotations.tracking.Annotations#get(int) 48 | */ 49 | public abstract Annotation get(int frame); 50 | 51 | /* 52 | * (non-Javadoc) 53 | * 54 | * @see si.vicos.annotations.tracking.Annotations#getTags(int) 55 | */ 56 | public abstract Set getTags(int frame); 57 | 58 | /* 59 | * (non-Javadoc) 60 | * 61 | * @see si.vicos.annotations.tracking.Annotations#hasTag(int, 62 | * java.lang.String) 63 | */ 64 | public abstract boolean hasTag(int index, String tag); 65 | 66 | /* 67 | * (non-Javadoc) 68 | * 69 | * @see si.vicos.annotations.tracking.Annotations#getValueKeys() 70 | */ 71 | public abstract Set getValueKeys(); 72 | 73 | /* 74 | * (non-Javadoc) 75 | * 76 | * @see si.vicos.annotations.tracking.Annotations#getTags() 77 | */ 78 | public abstract Set getTags(); 79 | 80 | /* 81 | * (non-Javadoc) 82 | * 83 | * @see si.vicos.annotations.tracking.Annotations#size() 84 | */ 85 | public abstract int size(); 86 | 87 | /** 88 | * Gets the directory. 89 | * 90 | * @return the directory 91 | */ 92 | public abstract File getDirectory(); 93 | 94 | /* 95 | * (non-Javadoc) 96 | * 97 | * @see si.vicos.annotations.AnnotationsMetadata#getPreviewImage() 98 | */ 99 | public abstract Image getPreviewImage(); 100 | 101 | /* 102 | * (non-Javadoc) 103 | * 104 | * @see si.vicos.annotations.tracking.Annotations#findTag(java.lang.String) 105 | */ 106 | public abstract Collection findTag(String tag); 107 | 108 | /* 109 | * (non-Javadoc) 110 | * 111 | * @see 112 | * si.vicos.annotations.tracking.Annotations#countTagOccurences(java.lang 113 | * .String) 114 | */ 115 | public abstract int countTagOccurences(String tag); 116 | 117 | /* 118 | * (non-Javadoc) 119 | * 120 | * @see 121 | * si.vicos.annotations.tracking.Annotations#findValues(java.lang.String) 122 | */ 123 | public abstract List findValues(String key); 124 | 125 | } 126 | -------------------------------------------------------------------------------- /src/si/vicos/annotations/editor/FontImageProvider.java: -------------------------------------------------------------------------------- 1 | package si.vicos.annotations.editor; 2 | 3 | import java.awt.Color; 4 | import java.awt.Dimension; 5 | import java.awt.Font; 6 | import java.awt.FontFormatException; 7 | import java.awt.Graphics2D; 8 | import java.awt.Image; 9 | import java.awt.RenderingHints; 10 | import java.awt.geom.AffineTransform; 11 | import java.awt.geom.Rectangle2D; 12 | import java.awt.image.BufferedImage; 13 | import java.io.IOException; 14 | 15 | import org.coffeeshop.swing.ImageStore.ImageProvider; 16 | 17 | /** 18 | * The Class FontImageProvider. 19 | */ 20 | public class FontImageProvider implements ImageProvider { 21 | 22 | /** The font. */ 23 | private Font font; 24 | 25 | /** The size. */ 26 | private Dimension size; 27 | 28 | /** The prefix. */ 29 | private String prefix; 30 | 31 | /** 32 | * Instantiates a new font image provider. 33 | * 34 | * @param prefix 35 | * the prefix 36 | * @param font 37 | * the font 38 | * @param size 39 | * the size 40 | */ 41 | public FontImageProvider(String prefix, Font font, Dimension size) { 42 | this.font = font; 43 | 44 | this.size = new Dimension(size); 45 | 46 | this.prefix = prefix; 47 | } 48 | 49 | /** 50 | * Instantiates a new font image provider. 51 | * 52 | * @param prefix 53 | * the prefix 54 | * @param base 55 | * the base 56 | * @param name 57 | * the name 58 | * @param size 59 | * the size 60 | * @throws IOException 61 | * Signals that an I/O exception has occurred. 62 | * @throws FontFormatException 63 | * the font format exception 64 | */ 65 | public FontImageProvider(String prefix, Class base, String name, 66 | Dimension size) throws IOException, FontFormatException { 67 | 68 | Font font = Font.createFont(Font.TRUETYPE_FONT, 69 | base.getResourceAsStream(name)); 70 | 71 | this.font = font; 72 | 73 | this.size = new Dimension(size); 74 | 75 | this.prefix = prefix; 76 | } 77 | 78 | /* 79 | * (non-Javadoc) 80 | * 81 | * @see 82 | * org.coffeeshop.swing.ImageStore.ImageProvider#loadImage(java.lang.String) 83 | */ 84 | @Override 85 | public Image loadImage(String name) { 86 | if (name == null || !name.startsWith(prefix)) 87 | return null; 88 | 89 | String text = name.substring(prefix.length()); 90 | 91 | BufferedImage img = new BufferedImage(size.width, size.height, 92 | BufferedImage.TYPE_INT_ARGB); 93 | 94 | Graphics2D g = img.createGraphics(); 95 | g.setFont(font); 96 | g.setBackground(new Color(0, true)); 97 | g.clearRect(0, 0, size.width, size.height); 98 | 99 | Rectangle2D bounds = font.getStringBounds(text, 100 | g.getFontRenderContext()); 101 | g.setColor(Color.BLACK); 102 | double scaling = Math.min(size.getWidth() / bounds.getWidth(), 103 | size.getHeight() / bounds.getHeight()) * 0.9; 104 | 105 | AffineTransform transform = AffineTransform.getTranslateInstance(0, 0); 106 | transform.translate(size.getWidth() / 2, size.getHeight() / 2); 107 | transform.scale(scaling, scaling); 108 | transform.translate(-bounds.getWidth() / 2, -bounds.getY() / 2); 109 | g.setTransform(transform); 110 | g.setRenderingHint(RenderingHints.KEY_TEXT_ANTIALIASING, 111 | RenderingHints.VALUE_TEXT_ANTIALIAS_ON); 112 | // System.out.println(text + " " + scaling + " " + bounds); 113 | // g.drawString(text, size.width / 2 -(int) bounds.getX() - (int) 114 | // (scaling * bounds.getWidth() / 2), -(int) bounds.getY()); 115 | // g.drawString(text, 0, (int) (bounds.getHeight() - bounds.getY())); 116 | g.drawString(text, 0, 0); 117 | return img; 118 | } 119 | 120 | } 121 | -------------------------------------------------------------------------------- /src/si/vicos/annotations/editor/tracking/TextExporter.java: -------------------------------------------------------------------------------- 1 | package si.vicos.annotations.editor.tracking; 2 | 3 | import java.io.File; 4 | import java.io.OutputStream; 5 | import java.io.PrintWriter; 6 | 7 | import javax.swing.filechooser.FileFilter; 8 | 9 | import si.vicos.annotations.Annotation; 10 | import si.vicos.annotations.RectangleAnnotation; 11 | import si.vicos.annotations.ShapeAnnotation; 12 | import si.vicos.annotations.tracking.AnnotatedSequence; 13 | 14 | /** 15 | * The Class TextExporter. 16 | */ 17 | public abstract class TextExporter extends FileFilter { 18 | 19 | /** 20 | * The Class EightPointsExporter. 21 | */ 22 | public static class EightPointsExporter extends TextExporter { 23 | 24 | /* 25 | * (non-Javadoc) 26 | * 27 | * @see javax.swing.filechooser.FileFilter#getDescription() 28 | */ 29 | @Override 30 | public String getDescription() { 31 | return "Eight points"; 32 | } 33 | 34 | /* 35 | * (non-Javadoc) 36 | * 37 | * @see 38 | * si.vicos.annotations.editor.tracking.TextExporter#export(java.io. 39 | * OutputStream, si.vicos.annotations.tracking.AnnotatedSequence) 40 | */ 41 | @Override 42 | public void export(OutputStream out, AnnotatedSequence annotations) { 43 | 44 | PrintWriter writer = new PrintWriter(out); 45 | 46 | for (int i = 0; i < annotations.size(); i++) { 47 | 48 | Annotation a = annotations.get(i); 49 | 50 | if (a == null || a.isNull() || !(a instanceof ShapeAnnotation)) { 51 | writer.println(); 52 | continue; 53 | } 54 | 55 | RectangleAnnotation ra = ((ShapeAnnotation) a).getBoundingBox(); 56 | 57 | writer.format("%.2f,%.2f,%.2f,%.2f,%.2f,%.2f,%.2f,%.2f\n", 58 | ra.getLeft(), ra.getTop(), ra.getRight(), ra.getTop(), 59 | ra.getRight(), ra.getBottom(), ra.getLeft(), 60 | ra.getBottom()); 61 | 62 | } 63 | 64 | writer.flush(); 65 | 66 | } 67 | 68 | } 69 | 70 | /** 71 | * The Class BoundingBoxExporter. 72 | */ 73 | public static class BoundingBoxExporter extends TextExporter { 74 | 75 | /* 76 | * (non-Javadoc) 77 | * 78 | * @see javax.swing.filechooser.FileFilter#getDescription() 79 | */ 80 | @Override 81 | public String getDescription() { 82 | return "Bounding box"; 83 | } 84 | 85 | /* 86 | * (non-Javadoc) 87 | * 88 | * @see 89 | * si.vicos.annotations.editor.tracking.TextExporter#export(java.io. 90 | * OutputStream, si.vicos.annotations.tracking.AnnotatedSequence) 91 | */ 92 | @Override 93 | public void export(OutputStream out, AnnotatedSequence annotations) { 94 | 95 | PrintWriter writer = new PrintWriter(out); 96 | 97 | for (int i = 0; i < annotations.size(); i++) { 98 | 99 | Annotation a = annotations.get(i); 100 | 101 | if (a == null || a.isNull() || !(a instanceof ShapeAnnotation)) { 102 | writer.println(); 103 | continue; 104 | } 105 | 106 | RectangleAnnotation ra = ((ShapeAnnotation) a).getBoundingBox(); 107 | 108 | writer.format("%.2f,%.2f,%.2f,%.2f\n", ra.getLeft(), 109 | ra.getTop(), ra.getWidth(), ra.getHeight()); 110 | 111 | } 112 | 113 | writer.flush(); 114 | 115 | } 116 | 117 | } 118 | 119 | /* 120 | * (non-Javadoc) 121 | * 122 | * @see javax.swing.filechooser.FileFilter#accept(java.io.File) 123 | */ 124 | @Override 125 | public boolean accept(File f) { 126 | 127 | if (f.isDirectory()) 128 | return true; 129 | 130 | String name = f.getName(); 131 | 132 | return name.endsWith(".csv") || name.endsWith(".txt"); 133 | } 134 | 135 | /** 136 | * Export. 137 | * 138 | * @param out 139 | * the out 140 | * @param annotations 141 | * the annotations 142 | */ 143 | public abstract void export(OutputStream out, AnnotatedSequence annotations); 144 | 145 | } 146 | -------------------------------------------------------------------------------- /src/si/vicos/annotations/editor/tracking/InterpolationAssistant.java: -------------------------------------------------------------------------------- 1 | package si.vicos.annotations.editor.tracking; 2 | 3 | import org.coffeeshop.application.Application; 4 | 5 | import si.vicos.annotations.Annotation; 6 | import si.vicos.annotations.editor.Interpolator; 7 | import si.vicos.annotations.editor.tracking.EditableAnnotatedSequence.EditList; 8 | import si.vicos.annotations.editor.tracking.EditableAnnotatedSequence.EditRegionOperation; 9 | import si.vicos.annotations.editor.tracking.EditableAnnotatedSequence.EditTagAddOperation; 10 | import si.vicos.annotations.editor.tracking.TrackingEditor.EditAssistant; 11 | 12 | /** 13 | * The Class InterpolationAssistant. 14 | */ 15 | public class InterpolationAssistant implements EditAssistant { 16 | 17 | /** The Constant instance. */ 18 | public static final InterpolationAssistant instance = new InterpolationAssistant(); 19 | 20 | /* 21 | * (non-Javadoc) 22 | * 23 | * @see 24 | * si.vicos.annotations.editor.tracking.TrackingEditor.EditAssistant#editFrame 25 | * (si.vicos.annotations.editor.tracking.EditableAnnotatedSequence, int, 26 | * si.vicos.annotations.Annotation) 27 | */ 28 | @Override 29 | public EditList editFrame(EditableAnnotatedSequence sequence, int frame, 30 | Annotation region) { 31 | 32 | EditList list = new EditList(); 33 | 34 | try { 35 | 36 | list.add(new EditRegionOperation(frame, region)); 37 | list.add(new EditTagAddOperation(frame, "keyframe")); 38 | 39 | // Interpolate forward if possible 40 | 41 | int last = 0; 42 | 43 | for (last = frame + 1; last < sequence.size(); last++) 44 | if (sequence.hasTag(last, "keyframe") 45 | && !sequence.get(last).isNull()) 46 | break; 47 | 48 | if (!(last == sequence.size() || frame > last - 2)) 49 | list.addAll(interpolate(sequence, frame, region, last, 50 | sequence.get(frame))); 51 | 52 | // Interpolate backwards if possible 53 | 54 | int first = 0; 55 | 56 | for (first = frame - 1; first >= 0; first--) 57 | if (sequence.hasTag(first, "keyframe") 58 | && !sequence.get(first).isNull()) 59 | break; 60 | 61 | if (!(first == -1 || first > frame - 2)) 62 | list.addAll(interpolate(sequence, first, sequence.get(first), 63 | frame, region)); 64 | 65 | } catch (Exception e) { 66 | 67 | Application.getApplicationLogger().report(e); 68 | 69 | } 70 | 71 | return list; 72 | } 73 | 74 | /** 75 | * Interpolate. 76 | * 77 | * @param sequence 78 | * the sequence 79 | * @param start 80 | * the start 81 | * @param startValue 82 | * the start value 83 | * @param stop 84 | * the stop 85 | * @param stopValue 86 | * the stop value 87 | * @return the edits the list 88 | */ 89 | private EditList interpolate(EditableAnnotatedSequence sequence, int start, 90 | Annotation startValue, int stop, Annotation stopValue) { 91 | 92 | if (start > stop - 2) 93 | throw new IllegalArgumentException("Interval too short"); 94 | 95 | if (startValue == null) 96 | throw new IllegalArgumentException("Start item does not exist"); 97 | 98 | if (stopValue == null) 99 | throw new IllegalArgumentException("Stop item does not exist"); 100 | 101 | if (startValue.isNull()) 102 | throw new IllegalArgumentException("Start value is not set"); 103 | 104 | if (stopValue.isNull()) 105 | throw new IllegalArgumentException("Stop value is not set"); 106 | 107 | float step = 1.0f / (float) (stop - start); 108 | float value = step; 109 | 110 | Interpolator interpolator = new Interpolator(startValue, stopValue); 111 | 112 | EditList list = new EditList(); 113 | 114 | for (int i = start + 1; i < stop; i++) { 115 | 116 | Annotation a = interpolator.interpolate(value); 117 | 118 | if (a == null) 119 | continue; 120 | 121 | list.add(new EditableAnnotatedSequence.EditRegionOperation(i, a)); 122 | 123 | value += step; 124 | } 125 | 126 | return list; 127 | 128 | } 129 | 130 | } 131 | -------------------------------------------------------------------------------- /src/si/vicos/annotations/editor/AnnotationsDocumentRenderer.java: -------------------------------------------------------------------------------- 1 | package si.vicos.annotations.editor; 2 | 3 | import java.awt.BasicStroke; 4 | import java.awt.Color; 5 | import java.awt.Graphics; 6 | import java.awt.Graphics2D; 7 | import java.awt.Image; 8 | import java.awt.geom.AffineTransform; 9 | import java.awt.geom.Rectangle2D; 10 | import java.awt.image.BufferedImage; 11 | import java.io.File; 12 | import java.io.IOException; 13 | 14 | import si.vicos.annotations.Annotation; 15 | import si.vicos.annotations.PolygonAnnotation; 16 | import si.vicos.annotations.RectangleAnnotation; 17 | import si.vicos.annotations.editor.tracking.PolygonAnnotationRenderer; 18 | import si.vicos.annotations.editor.tracking.RectangleAnnotationRenderer; 19 | import si.vicos.annotations.tracking.AnnotatedSequence.AnnotationsMetadataReader; 20 | 21 | /** 22 | * The Class AnnotationsDocumentRenderer. 23 | */ 24 | public class AnnotationsDocumentRenderer implements ThumbnailRenderer { 25 | 26 | /** The Constant THUMBNAIL_WIDTH. */ 27 | public static final int THUMBNAIL_WIDTH = 256; 28 | 29 | /** The Constant THUMBNAIL_HEIGHT. */ 30 | public static final int THUMBNAIL_HEIGHT = 256; 31 | 32 | /* 33 | * (non-Javadoc) 34 | * 35 | * @see 36 | * si.vicos.annotations.editor.ThumbnailRenderer#render(java.lang.Object) 37 | */ 38 | @Override 39 | public BufferedImage render(Object obj) { 40 | 41 | if (obj instanceof File) { 42 | 43 | try { 44 | 45 | AnnotationsMetadataReader metadata = new AnnotationsMetadataReader( 46 | (File) obj); 47 | 48 | return renderThumbnail(metadata); 49 | 50 | } catch (IOException e) { 51 | 52 | } 53 | 54 | BufferedImage img = new BufferedImage(getWidth(), getHeight(), 55 | BufferedImage.TYPE_INT_RGB); 56 | Graphics g = img.getGraphics(); 57 | 58 | g.setColor(Color.WHITE); 59 | g.fillRect(0, 0, getWidth(), getHeight()); 60 | 61 | g.setColor(Color.BLACK); 62 | 63 | String text = ((File) obj).getName(); 64 | 65 | Rectangle2D b = g.getFontMetrics().getStringBounds(text, g); 66 | 67 | g.drawString(text, (int) (getWidth() - b.getWidth()) / 2, 68 | (int) (getHeight() - b.getHeight()) / 2); 69 | 70 | return img; 71 | } 72 | 73 | return null; 74 | } 75 | 76 | /* 77 | * (non-Javadoc) 78 | * 79 | * @see si.vicos.annotations.editor.ThumbnailRenderer#getWidth() 80 | */ 81 | @Override 82 | public int getWidth() { 83 | return THUMBNAIL_WIDTH; 84 | } 85 | 86 | /* 87 | * (non-Javadoc) 88 | * 89 | * @see si.vicos.annotations.editor.ThumbnailRenderer#getHeight() 90 | */ 91 | @Override 92 | public int getHeight() { 93 | return THUMBNAIL_HEIGHT; 94 | } 95 | 96 | /** 97 | * Render thumbnail. 98 | * 99 | * @param metadata 100 | * the metadata 101 | * @return the buffered image 102 | */ 103 | private BufferedImage renderThumbnail(AnnotationsMetadataReader metadata) { 104 | 105 | try { 106 | 107 | Image original = metadata.getPreviewImage(); 108 | 109 | Annotation region = metadata.getPreviewRegion(); 110 | 111 | AnnotationRenderer renderer = null; 112 | 113 | if (region instanceof RectangleAnnotation) { 114 | renderer = new RectangleAnnotationRenderer( 115 | (RectangleAnnotation) region); 116 | } 117 | if (region instanceof PolygonAnnotation) { 118 | renderer = new PolygonAnnotationRenderer( 119 | (PolygonAnnotation) region); 120 | } 121 | 122 | float scale = Math 123 | .min((float) THUMBNAIL_WIDTH 124 | / (float) original.getWidth(null), 125 | (float) THUMBNAIL_HEIGHT 126 | / (float) original.getHeight(null)); 127 | 128 | int w = (int) ((float) original.getWidth(null) * scale); 129 | int h = (int) ((float) original.getHeight(null) * scale); 130 | 131 | BufferedImage image = new BufferedImage(w, h, 132 | BufferedImage.TYPE_INT_RGB); 133 | 134 | Graphics2D g = image.createGraphics(); 135 | 136 | g.setTransform(AffineTransform.getScaleInstance(scale, scale)); 137 | g.drawImage(original, 0, 0, null); 138 | 139 | if (renderer != null) { 140 | 141 | g.setColor(Color.RED); 142 | g.setStroke(new BasicStroke(2 * scale)); 143 | 144 | renderer.paint(g); 145 | } 146 | 147 | return image; 148 | 149 | } catch (Exception e) { 150 | return null; 151 | } 152 | 153 | } 154 | 155 | } 156 | -------------------------------------------------------------------------------- /src/si/vicos/annotations/editor/AnnotationEditor.java: -------------------------------------------------------------------------------- 1 | package si.vicos.annotations.editor; 2 | 3 | import java.awt.BasicStroke; 4 | import java.awt.Color; 5 | import java.awt.Cursor; 6 | import java.awt.Point; 7 | import java.awt.Stroke; 8 | 9 | import org.coffeeshop.swing.figure.FigurePanel; 10 | 11 | import si.vicos.annotations.Annotation; 12 | import si.vicos.annotations.PointAnnotation; 13 | import si.vicos.annotations.PolygonAnnotation; 14 | import si.vicos.annotations.RectangleAnnotation; 15 | import si.vicos.annotations.editor.AnnotatedImageFigure.AnnotationPeer; 16 | 17 | /** 18 | * This class is an abstract base of specific annotation type editing logic 19 | * class. 20 | * 21 | * @author lukacu 22 | */ 23 | public abstract class AnnotationEditor extends AnnotationViewer { 24 | 25 | /** 26 | * A factory for creating AnnotationEditor objects. 27 | * 28 | * @param 29 | * the element type 30 | */ 31 | public static interface AnnotationEditorFactory { 32 | 33 | /** 34 | * Gets the name. 35 | * 36 | * @return the name 37 | */ 38 | public String getName(); 39 | 40 | /** 41 | * Gets the editor. 42 | * 43 | * @param peer 44 | * the peer 45 | * @param color 46 | * the color 47 | * @return the editor 48 | */ 49 | public E getEditor(AnnotationPeer peer, Color color); 50 | 51 | } 52 | 53 | /** The Constant defaultCursor. */ 54 | protected static final Cursor defaultCursor = new Cursor( 55 | Cursor.DEFAULT_CURSOR); 56 | 57 | /** The Constant pointCursor. */ 58 | protected static final Cursor pointCursor = new Cursor( 59 | Cursor.CROSSHAIR_CURSOR); 60 | 61 | /** The Constant dragPossibleCursor. */ 62 | protected static final Cursor dragPossibleCursor = new Cursor( 63 | Cursor.HAND_CURSOR); 64 | 65 | /** The Constant dragCursor. */ 66 | protected static final Cursor dragCursor = new Cursor(Cursor.MOVE_CURSOR); 67 | 68 | /** The Constant selectedStroke. */ 69 | protected static final Stroke selectedStroke = new BasicStroke(2); 70 | 71 | /** The Constant subselectedStroke. */ 72 | protected static final Stroke subselectedStroke = new BasicStroke(3); 73 | 74 | /** The Constant normalStroke. */ 75 | protected static final Stroke normalStroke = new BasicStroke(1); 76 | 77 | /** 78 | * Instantiates a new annotation editor. 79 | * 80 | * @param peer 81 | * the peer 82 | * @param color 83 | * the color 84 | */ 85 | public AnnotationEditor(AnnotationPeer peer, Color color) { 86 | super(peer, color); 87 | } 88 | 89 | /** 90 | * On move. 91 | * 92 | * @param source 93 | * the source 94 | * @param from 95 | * the from 96 | * @param to 97 | * the to 98 | * @param drag 99 | * the drag 100 | * @param modifiers 101 | * the modifiers 102 | * @return the cursor 103 | */ 104 | public abstract Cursor onMove(FigurePanel source, Point from, Point to, 105 | boolean drag, int modifiers); 106 | 107 | /** 108 | * On click. 109 | * 110 | * @param source 111 | * the source 112 | * @param position 113 | * the position 114 | * @param clicks 115 | * the clicks 116 | * @param modifiers 117 | * the modifiers 118 | */ 119 | public abstract void onClick(FigurePanel source, Point position, 120 | int clicks, int modifiers); 121 | 122 | /** 123 | * Reset. 124 | */ 125 | public abstract void reset(); 126 | 127 | /** 128 | * Reset input. 129 | */ 130 | public abstract void resetInput(); 131 | 132 | /** 133 | * Gets the current. 134 | * 135 | * @return the current 136 | */ 137 | public abstract Annotation getCurrent(); 138 | 139 | /** 140 | * Gets the annotation editor. 141 | * 142 | * @param peer 143 | * the peer 144 | * @param color 145 | * the color 146 | * @return the annotation editor 147 | */ 148 | public static AnnotationEditor getAnnotationEditor(AnnotationPeer peer, 149 | Color color) { 150 | 151 | Annotation a = peer.getAnnotation(); 152 | 153 | if (a == null) 154 | return null; 155 | // if (a instanceof CodeAnnotation) return null; 156 | 157 | if (a instanceof PointAnnotation) 158 | return new PointAnnotationEditor(peer, color); 159 | if (a instanceof RectangleAnnotation) 160 | return new RectangleAnnotationEditor(peer, color); 161 | if (a instanceof PolygonAnnotation) 162 | return new PolygonAnnotationEditor(peer, color); 163 | 164 | return null; 165 | } 166 | 167 | } -------------------------------------------------------------------------------- /src/si/vicos/annotations/editor/ToggleAction.java: -------------------------------------------------------------------------------- 1 | package si.vicos.annotations.editor; 2 | 3 | import java.awt.event.ActionEvent; 4 | import java.util.Vector; 5 | 6 | import javax.swing.Action; 7 | import javax.swing.Icon; 8 | 9 | import org.coffeeshop.swing.ToolTipAction; 10 | 11 | /** 12 | * The Class ToggleAction. 13 | */ 14 | public abstract class ToggleAction extends ToolTipAction { 15 | 16 | /** 17 | * The Class ToggleActionGroup. 18 | */ 19 | public static class ToggleActionGroup { 20 | 21 | /** The actions. */ 22 | private Vector actions = new Vector(); 23 | 24 | /** 25 | * Instantiates a new toggle action group. 26 | */ 27 | public ToggleActionGroup() { 28 | 29 | } 30 | 31 | /** 32 | * Adds the. 33 | * 34 | * @param action 35 | * the action 36 | */ 37 | private void add(ToggleAction action) { 38 | 39 | actions.add(action); 40 | 41 | } 42 | 43 | /** 44 | * Select. 45 | * 46 | * @param select 47 | * the select 48 | */ 49 | public void select(String select) { 50 | 51 | if (select == null) 52 | return; 53 | 54 | for (ToggleAction action : actions) { 55 | 56 | String identifier = (String) action.getValue(Action.NAME); 57 | 58 | if (identifier != null && identifier.compareTo(select) == 0) { 59 | 60 | select(action); 61 | return; 62 | 63 | } 64 | 65 | } 66 | 67 | } 68 | 69 | /** 70 | * Select. 71 | * 72 | * @param select 73 | * the select 74 | */ 75 | public void select(ToggleAction select) { 76 | 77 | if (!actions.contains(select)) 78 | return; 79 | 80 | ActionEvent event = new ActionEvent(this, 0, "internal"); 81 | 82 | selectWithEvent(select, event); 83 | 84 | } 85 | 86 | /** 87 | * Select with event. 88 | * 89 | * @param select 90 | * the select 91 | * @param event 92 | * the event 93 | */ 94 | private void selectWithEvent(ToggleAction select, ActionEvent event) { 95 | 96 | for (ToggleAction action : actions) { 97 | 98 | if (action == select) { 99 | action.putValue(Action.SELECTED_KEY, true); 100 | action.actionSelected(event); 101 | } else { 102 | action.putValue(Action.SELECTED_KEY, false); 103 | action.actionDeselected(event); 104 | } 105 | 106 | } 107 | 108 | } 109 | 110 | } 111 | 112 | /** The Constant serialVersionUID. */ 113 | private static final long serialVersionUID = 1L; 114 | 115 | /** The group. */ 116 | private ToggleActionGroup group; 117 | 118 | /** 119 | * Instantiates a new toggle action. 120 | * 121 | * @param title 122 | * the title 123 | * @param icon 124 | * the icon 125 | * @param group 126 | * the group 127 | */ 128 | public ToggleAction(String title, Icon icon, ToggleActionGroup group) { 129 | super(title, icon); 130 | putValue(Action.SELECTED_KEY, false); 131 | 132 | this.group = group; 133 | if (this.group != null) 134 | this.group.add(this); 135 | } 136 | 137 | /** 138 | * Instantiates a new toggle action. 139 | * 140 | * @param title 141 | * the title 142 | * @param iconId 143 | * the icon id 144 | * @param group 145 | * the group 146 | */ 147 | public ToggleAction(String title, String iconId, ToggleActionGroup group) { 148 | super(title, iconId); 149 | putValue(Action.SELECTED_KEY, false); 150 | 151 | this.group = group; 152 | if (this.group != null) 153 | this.group.add(this); 154 | } 155 | 156 | /* 157 | * (non-Javadoc) 158 | * 159 | * @see 160 | * java.awt.event.ActionListener#actionPerformed(java.awt.event.ActionEvent) 161 | */ 162 | @Override 163 | public void actionPerformed(ActionEvent arg0) { 164 | 165 | boolean selected = (Boolean) getValue(Action.SELECTED_KEY); 166 | 167 | if (selected) { 168 | 169 | if (group == null) { 170 | actionSelected(arg0); 171 | } else { 172 | group.selectWithEvent(this, arg0); 173 | } 174 | 175 | } else { 176 | 177 | if (group == null) { 178 | 179 | actionDeselected(arg0); 180 | 181 | } else { 182 | 183 | // Cannot manually deselect action in group 184 | putValue(Action.SELECTED_KEY, true); 185 | 186 | } 187 | 188 | } 189 | 190 | } 191 | 192 | /** 193 | * Action selected. 194 | * 195 | * @param e 196 | * the e 197 | */ 198 | public abstract void actionSelected(ActionEvent e); 199 | 200 | /** 201 | * Action deselected. 202 | * 203 | * @param e 204 | * the e 205 | */ 206 | public abstract void actionDeselected(ActionEvent e); 207 | } 208 | -------------------------------------------------------------------------------- /src/si/vicos/annotations/editor/tracking/ValuePlot.java: -------------------------------------------------------------------------------- 1 | package si.vicos.annotations.editor.tracking; 2 | 3 | import java.awt.Color; 4 | import java.awt.Graphics2D; 5 | import java.awt.geom.AffineTransform; 6 | import java.awt.geom.Path2D; 7 | import java.awt.geom.Point2D; 8 | import java.awt.geom.Rectangle2D; 9 | import java.util.List; 10 | 11 | import org.coffeeshop.swing.figure.PlotObject; 12 | 13 | import si.vicos.annotations.tracking.Annotations; 14 | 15 | /** 16 | * The Class ValuePlot. 17 | */ 18 | public class ValuePlot implements PlotObject { 19 | 20 | /** The plot. */ 21 | private Path2D.Float plot = new Path2D.Float(); 22 | 23 | /** The values. */ 24 | private double[] values; 25 | 26 | /** The color. */ 27 | private Color color; 28 | 29 | /** The bounds. */ 30 | private Rectangle2D bounds = new Rectangle2D.Double(); 31 | 32 | /** The key. */ 33 | private String name, key; 34 | 35 | /** 36 | * Instantiates a new value plot. 37 | * 38 | * @param annotations 39 | * the annotations 40 | * @param key 41 | * the key 42 | * @param color 43 | * the color 44 | */ 45 | public ValuePlot(Annotations annotations, String key, Color color) { 46 | this(annotations, key, color, Double.MIN_VALUE, Double.MAX_VALUE); 47 | } 48 | 49 | /** 50 | * Instantiates a new value plot. 51 | * 52 | * @param annotations 53 | * the annotations 54 | * @param key 55 | * the key 56 | * @param color 57 | * the color 58 | * @param min 59 | * the min 60 | * @param max 61 | * the max 62 | */ 63 | public ValuePlot(Annotations annotations, String key, Color color, 64 | double min, double max) { 65 | 66 | this.color = color; 67 | this.name = annotations.getName() + " " + key; 68 | this.key = key; 69 | 70 | List values = annotations.findValues(key); 71 | this.values = new double[values.size()]; 72 | 73 | int frame = 0; 74 | boolean broken = true; 75 | 76 | double minValue = Double.MAX_VALUE; 77 | double maxValue = Double.MIN_VALUE; 78 | 79 | for (String value : values) { 80 | 81 | try { 82 | 83 | if (value != null) { 84 | double number = Double.parseDouble(value); 85 | 86 | number = Math.min(max, Math.max(min, number)); 87 | 88 | minValue = Math.min(minValue, number); 89 | maxValue = Math.max(maxValue, number); 90 | 91 | if (broken) 92 | plot.moveTo(frame, number); 93 | else 94 | plot.lineTo(frame, number); 95 | 96 | broken = false; 97 | 98 | this.values[frame] = number; 99 | 100 | } else { 101 | broken = true; 102 | this.values[frame] = Double.NaN; 103 | } 104 | 105 | } catch (NumberFormatException e) { 106 | broken = true; 107 | this.values[frame] = Double.NaN; 108 | } 109 | 110 | frame++; 111 | } 112 | 113 | if (min != Double.MIN_VALUE) 114 | minValue = min; 115 | if (max != Double.MAX_VALUE) 116 | maxValue = max; 117 | 118 | bounds.setRect(0, minValue, annotations.size(), maxValue - minValue); 119 | } 120 | 121 | /* 122 | * (non-Javadoc) 123 | * 124 | * @see org.coffeeshop.swing.figure.PlotObject#paint(java.awt.Graphics2D, 125 | * float, java.awt.geom.AffineTransform) 126 | */ 127 | @Override 128 | public void paint(Graphics2D g, float scale, AffineTransform pretransform) { 129 | 130 | g.setColor(color); 131 | 132 | g.draw(pretransform.createTransformedShape(plot)); 133 | 134 | } 135 | 136 | /* 137 | * (non-Javadoc) 138 | * 139 | * @see org.coffeeshop.swing.figure.PlotObject#getBounds() 140 | */ 141 | @Override 142 | public Rectangle2D getBounds() { 143 | return bounds; 144 | } 145 | 146 | /* 147 | * (non-Javadoc) 148 | * 149 | * @see org.coffeeshop.swing.figure.PlotObject#getName() 150 | */ 151 | @Override 152 | public String getName() { 153 | return name; 154 | } 155 | 156 | /** 157 | * Sets the color. 158 | * 159 | * @param color 160 | * the new color 161 | */ 162 | public void setColor(Color color) { 163 | this.color = color; 164 | } 165 | 166 | /** 167 | * Gets the color. 168 | * 169 | * @return the color 170 | */ 171 | public Color getColor() { 172 | return color; 173 | } 174 | 175 | /* 176 | * (non-Javadoc) 177 | * 178 | * @see 179 | * org.coffeeshop.swing.figure.PlotObject#getToolTip(java.awt.geom.Point2D) 180 | */ 181 | @Override 182 | public String getToolTip(Point2D point) { 183 | 184 | int frame = (int) point.getX(); 185 | 186 | if (frame < 0 || frame >= values.length) 187 | return null; 188 | 189 | if (Double.isNaN(values[frame])) 190 | return null; 191 | 192 | return String.format("%s: %.5f", name, values[frame]); 193 | } 194 | 195 | } 196 | -------------------------------------------------------------------------------- /src/si/vicos/annotations/editor/AnnotatedImageFigure.java: -------------------------------------------------------------------------------- 1 | package si.vicos.annotations.editor; 2 | 3 | import java.awt.Graphics2D; 4 | import java.awt.Image; 5 | import java.awt.Rectangle; 6 | import java.awt.RenderingHints; 7 | import java.awt.geom.AffineTransform; 8 | import java.awt.geom.Rectangle2D; 9 | import java.awt.image.BufferedImage; 10 | 11 | import org.coffeeshop.swing.figure.FigureObserver; 12 | import org.coffeeshop.swing.figure.ImageFigure; 13 | 14 | import si.vicos.annotations.Annotation; 15 | import si.vicos.annotations.RectangleAnnotation; 16 | import si.vicos.annotations.tracking.Annotations; 17 | 18 | /** 19 | * The Class AnnotatedImageFigure. 20 | */ 21 | public class AnnotatedImageFigure extends ImageFigure { 22 | 23 | /** 24 | * The Interface AnnotationPeer. 25 | */ 26 | public interface AnnotationPeer { 27 | 28 | /** 29 | * Repaint. 30 | */ 31 | public void repaint(); 32 | 33 | /** 34 | * Sets the annotation. 35 | * 36 | * @param a 37 | * the new annotation 38 | */ 39 | public void setAnnotation(Annotation a); 40 | 41 | /** 42 | * Gets the annotation. 43 | * 44 | * @return the annotation 45 | */ 46 | public Annotation getAnnotation(); 47 | 48 | } 49 | 50 | /** 51 | * The Class ReadOnlyFrameAnnotationPeer. 52 | */ 53 | public static class ReadOnlyFrameAnnotationPeer implements AnnotationPeer { 54 | 55 | /** The index. */ 56 | private int index; 57 | 58 | /** The annotations. */ 59 | private Annotations annotations; 60 | 61 | /** 62 | * Instantiates a new read only frame annotation peer. 63 | * 64 | * @param annotations 65 | * the annotations 66 | * @param index 67 | * the index 68 | */ 69 | public ReadOnlyFrameAnnotationPeer(Annotations annotations, int index) { 70 | 71 | this.index = index; 72 | this.annotations = annotations; 73 | 74 | } 75 | 76 | /* 77 | * (non-Javadoc) 78 | * 79 | * @see si.vicos.annotations.editor.AnnotatedImageFigure.AnnotationPeer# 80 | * getAnnotation() 81 | */ 82 | public Annotation getAnnotation() { 83 | 84 | if (index >= annotations.size()) 85 | return new RectangleAnnotation(); 86 | 87 | return annotations.get(index); 88 | } 89 | 90 | /* 91 | * (non-Javadoc) 92 | * 93 | * @see 94 | * si.vicos.annotations.editor.AnnotatedImageFigure.AnnotationPeer#repaint 95 | * () 96 | */ 97 | public void repaint() { 98 | 99 | } 100 | 101 | /* 102 | * (non-Javadoc) 103 | * 104 | * @see si.vicos.annotations.editor.AnnotatedImageFigure.AnnotationPeer# 105 | * setAnnotation(si.vicos.annotations.Annotation) 106 | */ 107 | @Override 108 | public void setAnnotation(Annotation a) { 109 | 110 | } 111 | 112 | } 113 | 114 | /** The Constant PLACEHOLDER. */ 115 | private static final BufferedImage PLACEHOLDER = new BufferedImage(1, 1, 116 | BufferedImage.TYPE_INT_RGB); 117 | 118 | /** The annotation viewers. */ 119 | private AnnotationViewer[] annotationViewers = null; 120 | 121 | /** The image to screen. */ 122 | private AffineTransform imageToScreen = new AffineTransform(); 123 | 124 | /** 125 | * Instantiates a new annotated image figure. 126 | * 127 | * @param image 128 | * the image 129 | * @param viewers 130 | * the viewers 131 | */ 132 | public AnnotatedImageFigure(Image image, AnnotationViewer... viewers) { 133 | 134 | super(image == null ? PLACEHOLDER : image); 135 | 136 | this.annotationViewers = viewers; 137 | 138 | } 139 | 140 | /* 141 | * (non-Javadoc) 142 | * 143 | * @see org.coffeeshop.swing.figure.ImageFigure#paint(java.awt.Graphics2D, 144 | * java.awt.geom.Rectangle2D, java.awt.Rectangle, 145 | * org.coffeeshop.swing.figure.FigureObserver) 146 | */ 147 | public void paint(Graphics2D g, Rectangle2D figureSize, 148 | Rectangle windowSize, FigureObserver observer) { 149 | 150 | super.paint(g, figureSize, windowSize, observer); 151 | 152 | g.setRenderingHint(RenderingHints.KEY_ANTIALIASING, 153 | RenderingHints.VALUE_ANTIALIAS_ON); 154 | 155 | if (annotationViewers == null || annotationViewers.length == 0) 156 | return; 157 | 158 | g.setClip(windowSize.x, windowSize.y, windowSize.width, 159 | windowSize.height); 160 | 161 | float scale = (float) windowSize.width / (float) figureSize.getWidth(); 162 | 163 | float offsetX = (float) figureSize.getX() * scale 164 | - (float) windowSize.x; 165 | float offsetY = (float) figureSize.getY() * scale 166 | - (float) windowSize.y; 167 | 168 | imageToScreen.setTransform(scale, 0, 0, scale, -offsetX, -offsetY); 169 | AffineTransform old = g.getTransform(); 170 | old.concatenate(imageToScreen); 171 | g.setTransform(old); 172 | 173 | for (int i = 0; i < annotationViewers.length; i++) { 174 | 175 | if (annotationViewers[i] == null) 176 | continue; 177 | annotationViewers[i].paint(g); 178 | } 179 | 180 | } 181 | 182 | }; -------------------------------------------------------------------------------- /src/si/vicos/annotations/editor/tracking/TransferableAnnotations.java: -------------------------------------------------------------------------------- 1 | package si.vicos.annotations.editor.tracking; 2 | 3 | import java.awt.Color; 4 | import java.awt.datatransfer.DataFlavor; 5 | import java.awt.datatransfer.Transferable; 6 | import java.awt.datatransfer.UnsupportedFlavorException; 7 | import java.util.Properties; 8 | 9 | import si.vicos.annotations.tracking.AbstractAnnotatedSequence; 10 | import si.vicos.annotations.tracking.Annotations; 11 | 12 | /** 13 | * The Class TransferableAnnotations. 14 | */ 15 | public class TransferableAnnotations implements Transferable { 16 | 17 | /** The Constant ANNOTATIONS_FLAVOR. */ 18 | protected static final DataFlavor ANNOTATIONS_FLAVOR = new DataFlavor( 19 | AbstractAnnotatedSequence.class, "Tracking Annotations"); 20 | 21 | /** The Constant ANNOTATIONS_PATH_FLAVOR. */ 22 | protected static final DataFlavor ANNOTATIONS_PATH_FLAVOR = new DataFlavor( 23 | String.class, "Tracking Annotations File Path"); 24 | 25 | /** The Constant ANNOTATIONS_PROPERTIES_FLAVOR. */ 26 | protected static final DataFlavor ANNOTATIONS_PROPERTIES_FLAVOR = new DataFlavor( 27 | Properties.class, "Tracking Annotations Properties"); 28 | 29 | /** The Constant SUPPORTED_FLAVORS. */ 30 | protected static final DataFlavor[] SUPPORTED_FLAVORS = { 31 | ANNOTATIONS_FLAVOR, ANNOTATIONS_PATH_FLAVOR, 32 | ANNOTATIONS_PROPERTIES_FLAVOR }; 33 | 34 | /** The annotations. */ 35 | private Annotations annotations; 36 | 37 | /** The ancestry. */ 38 | private String path, name, ancestry; 39 | 40 | /** The color. */ 41 | private String color; 42 | 43 | /** 44 | * Instantiates a new transferable annotations. 45 | * 46 | * @param name 47 | * the name 48 | * @param annotations 49 | * the annotations 50 | * @param color 51 | * the color 52 | * @param ancestry 53 | * the ancestry 54 | */ 55 | public TransferableAnnotations(String name, Annotations annotations, 56 | Color color, String ancestry) { 57 | this.name = name; 58 | this.color = "#" + Integer.toHexString(color.getRGB()); 59 | this.ancestry = ancestry; 60 | this.annotations = annotations; 61 | } 62 | 63 | /** 64 | * Instantiates a new transferable annotations. 65 | * 66 | * @param name 67 | * the name 68 | * @param annotations 69 | * the annotations 70 | * @param colorHint 71 | * the color hint 72 | * @param ancestry 73 | * the ancestry 74 | */ 75 | public TransferableAnnotations(String name, Annotations annotations, 76 | String colorHint, String ancestry) { 77 | this.name = name; 78 | this.color = colorHint; 79 | this.ancestry = ancestry; 80 | this.annotations = annotations; 81 | } 82 | 83 | /* 84 | * (non-Javadoc) 85 | * 86 | * @see java.awt.datatransfer.Transferable#getTransferDataFlavors() 87 | */ 88 | public DataFlavor[] getTransferDataFlavors() { 89 | return SUPPORTED_FLAVORS; 90 | } 91 | 92 | /* 93 | * (non-Javadoc) 94 | * 95 | * @see java.awt.datatransfer.Transferable#isDataFlavorSupported(java.awt. 96 | * datatransfer.DataFlavor) 97 | */ 98 | public boolean isDataFlavorSupported(DataFlavor flavor) { 99 | if (flavor.equals(ANNOTATIONS_FLAVOR)) 100 | return true; 101 | if (flavor.equals(ANNOTATIONS_PATH_FLAVOR)) 102 | return true; 103 | if (flavor.equals(ANNOTATIONS_PROPERTIES_FLAVOR)) 104 | return true; 105 | return false; 106 | } 107 | 108 | /* 109 | * (non-Javadoc) 110 | * 111 | * @see 112 | * java.awt.datatransfer.Transferable#getTransferData(java.awt.datatransfer 113 | * .DataFlavor) 114 | */ 115 | public Object getTransferData(DataFlavor flavor) 116 | throws UnsupportedFlavorException { 117 | if (flavor.equals(ANNOTATIONS_FLAVOR)) 118 | return annotations; 119 | else if (flavor.equals(ANNOTATIONS_PATH_FLAVOR)) 120 | return path; 121 | else if (flavor.equals(ANNOTATIONS_PROPERTIES_FLAVOR)) { 122 | 123 | Properties properties = new Properties(); 124 | properties.setProperty("name", name); 125 | properties.setProperty("color", color); 126 | 127 | properties.setProperty("ancestry", ancestry); 128 | 129 | return properties; 130 | } else 131 | throw new UnsupportedFlavorException(flavor); 132 | } 133 | 134 | /** 135 | * Gets the name. 136 | * 137 | * @return the name 138 | */ 139 | public String getName() { 140 | return name == null ? annotations.getName() : name; 141 | } 142 | 143 | /** 144 | * Gets the annotations. 145 | * 146 | * @return the annotations 147 | */ 148 | public Annotations getAnnotations() { 149 | return annotations; 150 | } 151 | 152 | /** 153 | * Gets the color hint. 154 | * 155 | * @return the color hint 156 | */ 157 | public String getColorHint() { 158 | return color; 159 | } 160 | 161 | /** 162 | * Gets the ancestry. 163 | * 164 | * @return the ancestry 165 | */ 166 | public String getAncestry() { 167 | return ancestry; 168 | } 169 | 170 | } -------------------------------------------------------------------------------- /src/si/vicos/annotations/editor/ThumbnailGenerator.java: -------------------------------------------------------------------------------- 1 | package si.vicos.annotations.editor; 2 | 3 | import java.awt.image.BufferedImage; 4 | import java.util.LinkedList; 5 | import java.util.concurrent.ConcurrentLinkedQueue; 6 | 7 | import org.coffeeshop.cache.Cache; 8 | 9 | /** 10 | * The Class ThumbnailGenerator. 11 | */ 12 | public class ThumbnailGenerator { 13 | 14 | /** 15 | * The Interface ThumbnailGeneratorCallback. 16 | */ 17 | public interface ThumbnailGeneratorCallback { 18 | 19 | /** 20 | * Retrieved. 21 | * 22 | * @param image 23 | * the image 24 | */ 25 | public void retrieved(BufferedImage image); 26 | 27 | /** 28 | * Failed. 29 | */ 30 | public void failed(); 31 | 32 | } 33 | 34 | /** 35 | * The Class GenerateTask. 36 | */ 37 | private class GenerateTask { 38 | 39 | /** The object. */ 40 | private Object object; 41 | 42 | /** The callback. */ 43 | private ThumbnailGeneratorCallback callback; 44 | 45 | /** 46 | * Instantiates a new generate task. 47 | * 48 | * @param object 49 | * the object 50 | * @param callback 51 | * the callback 52 | */ 53 | protected GenerateTask(Object object, 54 | ThumbnailGeneratorCallback callback) { 55 | super(); 56 | this.object = object; 57 | this.callback = callback; 58 | } 59 | 60 | } 61 | 62 | /** The cache. */ 63 | private Cache cache = null; 64 | 65 | /** The tasks. */ 66 | private ConcurrentLinkedQueue tasks = new ConcurrentLinkedQueue(); 67 | 68 | /** The fetchers. */ 69 | private LinkedList fetchers; 70 | 71 | /** 72 | * The Class FetcherThread. 73 | */ 74 | private class FetcherThread extends Thread { 75 | 76 | /* 77 | * (non-Javadoc) 78 | * 79 | * @see java.lang.Thread#run() 80 | */ 81 | @Override 82 | public void run() { 83 | 84 | while (true) { 85 | 86 | GenerateTask task = null; 87 | 88 | synchronized (tasks) { 89 | while (true) { 90 | 91 | if (tasks.isEmpty()) 92 | try { 93 | tasks.wait(); 94 | } catch (InterruptedException e) { 95 | e.printStackTrace(); 96 | break; 97 | } 98 | else 99 | break; 100 | } 101 | task = tasks.poll(); 102 | } 103 | 104 | if (task == null) 105 | break; 106 | 107 | BufferedImage cachedImage = null; 108 | 109 | if (cache != null) 110 | cachedImage = cache.query(task.object); 111 | 112 | if (cachedImage == null) { 113 | 114 | try { 115 | 116 | cachedImage = renderer.render(task.object); 117 | 118 | if (cache != null) { 119 | synchronized (cache) { 120 | cache.insert(task.object, cachedImage); 121 | } 122 | } 123 | 124 | } catch (Exception e) { 125 | 126 | task.callback.failed(); 127 | 128 | } 129 | 130 | } 131 | 132 | task.callback.retrieved(cachedImage); 133 | 134 | continue; 135 | 136 | } 137 | } 138 | } 139 | 140 | /** The renderer. */ 141 | private ThumbnailRenderer renderer; 142 | 143 | /** 144 | * Instantiates a new thumbnail generator. 145 | * 146 | * @param renderer 147 | * the renderer 148 | * @param cache 149 | * the cache 150 | */ 151 | public ThumbnailGenerator(ThumbnailRenderer renderer, 152 | Cache cache) { 153 | 154 | this.cache = cache; 155 | 156 | this.renderer = renderer; 157 | 158 | this.fetchers = new LinkedList(); 159 | for (int i = 0; i < 2; i++) { 160 | FetcherThread th = new FetcherThread(); 161 | th.setDaemon(true); 162 | th.start(); 163 | 164 | fetchers.add(th); 165 | } 166 | } 167 | 168 | /** 169 | * Generate. 170 | * 171 | * @param object 172 | * the object 173 | * @param callback 174 | * the callback 175 | * @return the buffered image 176 | */ 177 | public BufferedImage generate(Object object, 178 | ThumbnailGeneratorCallback callback) { 179 | 180 | synchronized (tasks) { 181 | 182 | synchronized (cache) { 183 | 184 | if (cache != null) { 185 | 186 | BufferedImage cached_img = cache.query(object); 187 | 188 | if (cached_img != null) { 189 | return cached_img; 190 | } 191 | } 192 | } 193 | tasks.add(new GenerateTask(object, callback)); 194 | tasks.notifyAll(); 195 | 196 | return null; 197 | } 198 | 199 | } 200 | 201 | /** 202 | * Invalidate. 203 | * 204 | * @param object 205 | * the object 206 | */ 207 | public void invalidate(Object object) { 208 | 209 | synchronized (cache) { 210 | 211 | cache.remove(object); 212 | 213 | } 214 | 215 | } 216 | 217 | /** 218 | * Gets the width. 219 | * 220 | * @return the width 221 | */ 222 | public int getWidth() { 223 | return renderer.getWidth(); 224 | } 225 | 226 | /** 227 | * Gets the height. 228 | * 229 | * @return the height 230 | */ 231 | public int getHeight() { 232 | return renderer.getHeight(); 233 | } 234 | 235 | } 236 | -------------------------------------------------------------------------------- /src/si/vicos/annotations/PointAnnotation.java: -------------------------------------------------------------------------------- 1 | package si.vicos.annotations; 2 | 3 | import java.awt.geom.Point2D; 4 | import java.text.ParseException; 5 | import java.util.NoSuchElementException; 6 | import java.util.StringTokenizer; 7 | 8 | import org.coffeeshop.string.StringUtils; 9 | 10 | /** 11 | * The Class PointAnnotation. 12 | */ 13 | public class PointAnnotation extends SituatedAnnotation { 14 | 15 | /** The y. */ 16 | private double x, y; 17 | 18 | /** 19 | * Instantiates a new point annotation. 20 | */ 21 | public PointAnnotation() { 22 | reset(); 23 | } 24 | 25 | /** 26 | * Instantiates a new point annotation. 27 | * 28 | * @param x 29 | * the x 30 | * @param y 31 | * the y 32 | */ 33 | public PointAnnotation(double x, double y) { 34 | set(x, y); 35 | } 36 | 37 | /* 38 | * (non-Javadoc) 39 | * 40 | * @see si.vicos.annotations.Annotation#pack() 41 | */ 42 | @Override 43 | public String pack() { 44 | return isNull() ? "" : String.format(SERIALIZATION_LOCALE, "%f,%f", x, 45 | y); 46 | } 47 | 48 | /* 49 | * (non-Javadoc) 50 | * 51 | * @see si.vicos.annotations.Annotation#reset() 52 | */ 53 | @Override 54 | public void reset() { 55 | x = Integer.MAX_VALUE; 56 | y = 0; 57 | } 58 | 59 | /* 60 | * (non-Javadoc) 61 | * 62 | * @see si.vicos.annotations.Annotation#unpack(java.lang.String) 63 | */ 64 | @Override 65 | public void unpack(String data) throws ParseException { 66 | 67 | if (StringUtils.empty(data)) 68 | return; 69 | 70 | StringTokenizer tokens = new StringTokenizer(data, ","); 71 | 72 | try { 73 | set(Integer.parseInt(tokens.nextToken()), 74 | Integer.parseInt(tokens.nextToken())); 75 | } catch (NoSuchElementException e) { 76 | throw new ParseException("Unable to parse", -1); 77 | } catch (NumberFormatException e) { 78 | throw new ParseException("Unable to parse", -1); 79 | } 80 | 81 | } 82 | 83 | /* 84 | * (non-Javadoc) 85 | * 86 | * @see si.vicos.annotations.Annotation#clone() 87 | */ 88 | @Override 89 | public Annotation clone() { 90 | return new PointAnnotation(x, y); 91 | } 92 | 93 | /** 94 | * Sets the. 95 | * 96 | * @param x 97 | * the x 98 | * @param y 99 | * the y 100 | */ 101 | private void set(double x, double y) { 102 | this.x = x; 103 | this.y = y; 104 | } 105 | 106 | /** 107 | * Gets the x. 108 | * 109 | * @return the x 110 | */ 111 | public double getX() { 112 | return x; 113 | } 114 | 115 | /** 116 | * Gets the y. 117 | * 118 | * @return the y 119 | */ 120 | public double getY() { 121 | return y; 122 | } 123 | 124 | /* 125 | * (non-Javadoc) 126 | * 127 | * @see si.vicos.annotations.SituatedAnnotation#getCenter() 128 | */ 129 | public Point2D getCenter() { 130 | return new Point2D.Double(x, y); 131 | } 132 | 133 | /* 134 | * (non-Javadoc) 135 | * 136 | * @see java.lang.Object#toString() 137 | */ 138 | public String toString() { 139 | return String.format("(%.2f,%.2f)", x, y); 140 | } 141 | 142 | /* 143 | * (non-Javadoc) 144 | * 145 | * @see 146 | * si.vicos.annotations.Annotation#validate(si.vicos.annotations.Annotation) 147 | */ 148 | @Override 149 | public boolean validate(Annotation a) { 150 | return a instanceof PointAnnotation; 151 | } 152 | 153 | /* 154 | * (non-Javadoc) 155 | * 156 | * @see si.vicos.annotations.Annotation#getType() 157 | */ 158 | @Override 159 | public AnnotationType getType() { 160 | return AnnotationType.POINT; 161 | } 162 | 163 | /* 164 | * (non-Javadoc) 165 | * 166 | * @see si.vicos.annotations.Annotation#canInterpolate() 167 | */ 168 | @Override 169 | public boolean canInterpolate() { 170 | return true; 171 | } 172 | 173 | /* 174 | * (non-Javadoc) 175 | * 176 | * @see si.vicos.annotations.Annotation#scale(float) 177 | */ 178 | @Override 179 | public Annotation scale(float scale) throws UnsupportedOperationException { 180 | 181 | double x = (float) this.x * scale; 182 | double y = (float) this.y * scale; 183 | 184 | return new PointAnnotation(x, y); 185 | 186 | } 187 | 188 | /* 189 | * (non-Javadoc) 190 | * 191 | * @see 192 | * si.vicos.annotations.Annotation#convert(si.vicos.annotations.Annotation) 193 | */ 194 | @Override 195 | public Annotation convert(Annotation a) 196 | throws UnsupportedOperationException { 197 | 198 | if (a instanceof PointAnnotation) 199 | return a; 200 | 201 | if (a instanceof RectangleAnnotation) { 202 | RectangleAnnotation ra = (RectangleAnnotation) a; 203 | return new PointAnnotation(ra.getCenterX(), ra.getCenterY()); 204 | } 205 | 206 | if (a instanceof PolygonAnnotation) { 207 | Point2D center = ((PolygonAnnotation) a).getCenter(); 208 | return new PointAnnotation(center.getX(), center.getY()); 209 | } 210 | 211 | return super.convert(a); 212 | } 213 | 214 | /* 215 | * (non-Javadoc) 216 | * 217 | * @see si.vicos.annotations.Annotation#isNull() 218 | */ 219 | @Override 220 | public boolean isNull() { 221 | return x == Integer.MAX_VALUE; 222 | } 223 | } -------------------------------------------------------------------------------- /src/si/vicos/annotations/tracking/ReversedAnnotationsProxy.java: -------------------------------------------------------------------------------- 1 | package si.vicos.annotations.tracking; 2 | 3 | import java.awt.Image; 4 | import java.io.File; 5 | import java.util.Collection; 6 | import java.util.Collections; 7 | import java.util.List; 8 | import java.util.Set; 9 | import java.util.Vector; 10 | 11 | import si.vicos.annotations.Annotation; 12 | 13 | /** 14 | * The Class ReversedAnnotationsProxy. 15 | */ 16 | public class ReversedAnnotationsProxy extends AbstractAnnotatedSequence { 17 | 18 | /** The parent. */ 19 | private AbstractAnnotatedSequence parent; 20 | 21 | /** 22 | * Instantiates a new reversed annotations proxy. 23 | * 24 | * @param parent 25 | * the parent 26 | */ 27 | public ReversedAnnotationsProxy(AbstractAnnotatedSequence parent) { 28 | this.parent = parent; 29 | } 30 | 31 | /* 32 | * (non-Javadoc) 33 | * 34 | * @see 35 | * si.vicos.annotations.AnnotationsMetadata#getMetadata(java.lang.String) 36 | */ 37 | public String getMetadata(String name) { 38 | return parent.getMetadata(name); 39 | } 40 | 41 | /* 42 | * (non-Javadoc) 43 | * 44 | * @see si.vicos.annotations.Context#getImage(java.lang.Object) 45 | */ 46 | public Image getImage(Integer entry) { 47 | return parent.getImage(parent.size() - entry - 1); 48 | } 49 | 50 | /* 51 | * (non-Javadoc) 52 | * 53 | * @see si.vicos.annotations.tracking.AbstractAnnotatedSequence#getName() 54 | */ 55 | public String getName() { 56 | return parent.getName() + " [reversed]"; 57 | } 58 | 59 | /* 60 | * (non-Javadoc) 61 | * 62 | * @see si.vicos.annotations.tracking.AbstractAnnotatedSequence#get(int) 63 | */ 64 | public Annotation get(int frame) { 65 | return parent.get(parent.size() - frame - 1); 66 | } 67 | 68 | /* 69 | * (non-Javadoc) 70 | * 71 | * @see si.vicos.annotations.tracking.AbstractAnnotatedSequence#getTags(int) 72 | */ 73 | public Set getTags(int frame) { 74 | return parent.getTags(parent.size() - frame - 1); 75 | } 76 | 77 | /* 78 | * (non-Javadoc) 79 | * 80 | * @see si.vicos.annotations.Context#getImageFile(java.lang.Object) 81 | */ 82 | public File getImageFile(Integer entry) { 83 | return parent.getImageFile(parent.size() - entry - 1); 84 | } 85 | 86 | /* 87 | * (non-Javadoc) 88 | * 89 | * @see si.vicos.annotations.tracking.AbstractAnnotatedSequence#hasTag(int, 90 | * java.lang.String) 91 | */ 92 | public boolean hasTag(int index, String tag) { 93 | return parent.hasTag(parent.size() - index - 1, tag); 94 | } 95 | 96 | /* 97 | * (non-Javadoc) 98 | * 99 | * @see 100 | * si.vicos.annotations.tracking.AbstractAnnotatedSequence#getDirectory() 101 | */ 102 | public File getDirectory() { 103 | return parent.getDirectory(); 104 | } 105 | 106 | /* 107 | * (non-Javadoc) 108 | * 109 | * @see 110 | * si.vicos.annotations.tracking.AbstractAnnotatedSequence#getPreviewImage() 111 | */ 112 | public Image getPreviewImage() { 113 | return parent.getPreviewImage(); 114 | } 115 | 116 | /* 117 | * (non-Javadoc) 118 | * 119 | * @see si.vicos.annotations.AnnotationsMetadata#getPreviewRegion() 120 | */ 121 | @Override 122 | public Annotation getPreviewRegion() { 123 | return parent.getPreviewRegion(); 124 | } 125 | 126 | /* 127 | * (non-Javadoc) 128 | * 129 | * @see 130 | * si.vicos.annotations.tracking.AbstractAnnotatedSequence#countTagOccurences 131 | * (java.lang.String) 132 | */ 133 | public int countTagOccurences(String tag) { 134 | return parent.countTagOccurences(tag); 135 | } 136 | 137 | /* 138 | * (non-Javadoc) 139 | * 140 | * @see 141 | * si.vicos.annotations.tracking.AbstractAnnotatedSequence#findTag(java. 142 | * lang.String) 143 | */ 144 | public Collection findTag(String tag) { 145 | Collection labels = parent.findTag(tag); 146 | 147 | if (labels == null) 148 | return null; 149 | 150 | Vector reversed = new Vector(); 151 | 152 | for (Integer i : labels) { 153 | reversed.add(parent.size() - i - 1); 154 | } 155 | 156 | return reversed; 157 | } 158 | 159 | /* 160 | * (non-Javadoc) 161 | * 162 | * @see si.vicos.annotations.tracking.AbstractAnnotatedSequence#size() 163 | */ 164 | public int size() { 165 | return parent.size(); 166 | } 167 | 168 | /* 169 | * (non-Javadoc) 170 | * 171 | * @see si.vicos.annotations.AnnotationsMetadata#getKeys() 172 | */ 173 | public Set getKeys() { 174 | return parent.getKeys(); 175 | } 176 | 177 | /* 178 | * (non-Javadoc) 179 | * 180 | * @see 181 | * si.vicos.annotations.tracking.AbstractAnnotatedSequence#getValueKeys() 182 | */ 183 | @Override 184 | public Set getValueKeys() { 185 | return parent.getValueKeys(); 186 | } 187 | 188 | /* 189 | * (non-Javadoc) 190 | * 191 | * @see si.vicos.annotations.tracking.AbstractAnnotatedSequence#getTags() 192 | */ 193 | @Override 194 | public Set getTags() { 195 | return parent.getTags(); 196 | } 197 | 198 | /* 199 | * (non-Javadoc) 200 | * 201 | * @see 202 | * si.vicos.annotations.tracking.AbstractAnnotatedSequence#findValues(java 203 | * .lang.String) 204 | */ 205 | @Override 206 | public List findValues(String key) { 207 | List result = parent.findValues(key); 208 | Collections.reverse(result); 209 | return result; 210 | } 211 | 212 | } 213 | -------------------------------------------------------------------------------- /src/si/vicos/annotations/PolygonAnnotation.java: -------------------------------------------------------------------------------- 1 | package si.vicos.annotations; 2 | 3 | import java.awt.geom.Point2D; 4 | import java.text.ParseException; 5 | import java.util.ArrayList; 6 | import java.util.Iterator; 7 | import java.util.List; 8 | import java.util.NoSuchElementException; 9 | import java.util.StringTokenizer; 10 | import java.util.Vector; 11 | 12 | import org.coffeeshop.string.StringUtils; 13 | 14 | /** 15 | * The Class PolygonAnnotation. 16 | */ 17 | public class PolygonAnnotation extends ShapeAnnotation implements 18 | Iterable { 19 | 20 | /** The points. */ 21 | private Vector points = new Vector(); 22 | 23 | /** 24 | * Instantiates a new polygon annotation. 25 | */ 26 | public PolygonAnnotation() { 27 | 28 | } 29 | 30 | /** 31 | * Instantiates a new polygon annotation. 32 | * 33 | * @param data 34 | * the data 35 | * @throws ParseException 36 | * the parse exception 37 | */ 38 | public PolygonAnnotation(String data) throws ParseException { 39 | this.unpack(data); 40 | } 41 | 42 | /** 43 | * Instantiates a new polygon annotation. 44 | * 45 | * @param points 46 | * the points 47 | */ 48 | public PolygonAnnotation(List points) { 49 | this.points.addAll(points); 50 | } 51 | 52 | /* 53 | * (non-Javadoc) 54 | * 55 | * @see si.vicos.annotations.ShapeAnnotation#getBoundingBox() 56 | */ 57 | @Override 58 | public RectangleAnnotation getBoundingBox() { 59 | double minX = Float.MAX_VALUE, minY = Float.MAX_VALUE, maxX = Float.MIN_VALUE, maxY = Float.MIN_VALUE; 60 | 61 | for (Point2D point : points) { 62 | minX = Math.min(minX, point.getX()); 63 | minY = Math.min(minY, point.getY()); 64 | maxX = Math.max(maxX, point.getX()); 65 | maxY = Math.max(maxY, point.getY()); 66 | 67 | } 68 | 69 | return new RectangleAnnotation(minX, minY, maxX - minX, maxY - minY); 70 | } 71 | 72 | /* 73 | * (non-Javadoc) 74 | * 75 | * @see si.vicos.annotations.Annotation#reset() 76 | */ 77 | @Override 78 | public void reset() { 79 | if (points != null) 80 | points.clear(); 81 | } 82 | 83 | /* 84 | * (non-Javadoc) 85 | * 86 | * @see si.vicos.annotations.Annotation#pack() 87 | */ 88 | @Override 89 | public String pack() { 90 | 91 | if (isNull()) 92 | return ""; 93 | 94 | StringBuilder builder = new StringBuilder(); 95 | 96 | for (int i = 0; i < points.size(); i++) { 97 | Point2D p = points.get(i); 98 | if (i > 0) 99 | builder.append(","); 100 | builder.append(String.format(SERIALIZATION_LOCALE, "%.3f,%.3f", 101 | p.getX(), p.getY())); 102 | } 103 | 104 | return builder.toString(); 105 | } 106 | 107 | /* 108 | * (non-Javadoc) 109 | * 110 | * @see si.vicos.annotations.Annotation#unpack(java.lang.String) 111 | */ 112 | @Override 113 | public void unpack(String data) throws ParseException { 114 | if (StringUtils.empty(data)) 115 | return; 116 | 117 | this.points.clear(); 118 | 119 | StringTokenizer tokens = new StringTokenizer(data, ","); 120 | 121 | try { 122 | while (tokens.hasMoreElements()) { 123 | 124 | double x = Float.parseFloat(tokens.nextToken()); 125 | double y = Float.parseFloat(tokens.nextToken()); 126 | points.add(new Point2D.Double(x, y)); 127 | } 128 | } catch (NoSuchElementException e) { 129 | throw new ParseException("Unable to parse", -1); 130 | } catch (NumberFormatException e) { 131 | throw new ParseException("Unable to parse", -1); 132 | } 133 | } 134 | 135 | /* 136 | * (non-Javadoc) 137 | * 138 | * @see si.vicos.annotations.Annotation#clone() 139 | */ 140 | @Override 141 | public Annotation clone() { 142 | return new PolygonAnnotation(points); 143 | } 144 | 145 | /* 146 | * (non-Javadoc) 147 | * 148 | * @see 149 | * si.vicos.annotations.Annotation#validate(si.vicos.annotations.Annotation) 150 | */ 151 | @Override 152 | public boolean validate(Annotation a) { 153 | return (a instanceof PolygonAnnotation); 154 | } 155 | 156 | /* 157 | * (non-Javadoc) 158 | * 159 | * @see si.vicos.annotations.Annotation#getType() 160 | */ 161 | @Override 162 | public AnnotationType getType() { 163 | return AnnotationType.POLYGON; 164 | } 165 | 166 | /* 167 | * (non-Javadoc) 168 | * 169 | * @see si.vicos.annotations.Annotation#isNull() 170 | */ 171 | @Override 172 | public boolean isNull() { 173 | return points.size() < 3; 174 | } 175 | 176 | /* 177 | * (non-Javadoc) 178 | * 179 | * @see java.lang.Iterable#iterator() 180 | */ 181 | @Override 182 | public Iterator iterator() { 183 | return points.iterator(); 184 | } 185 | 186 | /** 187 | * Size. 188 | * 189 | * @return the int 190 | */ 191 | public int size() { 192 | return points.size(); 193 | } 194 | 195 | /* 196 | * (non-Javadoc) 197 | * 198 | * @see si.vicos.annotations.ShapeAnnotation#getPolygon() 199 | */ 200 | @Override 201 | public List getPolygon() { 202 | return new ArrayList(points); 203 | } 204 | 205 | /* 206 | * (non-Javadoc) 207 | * 208 | * @see si.vicos.annotations.SituatedAnnotation#getCenter() 209 | */ 210 | public Point2D getCenter() { 211 | 212 | double sumX = 0; 213 | double sumY = 0; 214 | 215 | for (Point2D p : points) { 216 | sumX += p.getX(); 217 | sumY += p.getY(); 218 | } 219 | 220 | return new Point2D.Double(sumX / points.size(), sumY / points.size()); 221 | } 222 | 223 | /* 224 | * (non-Javadoc) 225 | * 226 | * @see 227 | * si.vicos.annotations.Annotation#convert(si.vicos.annotations.Annotation) 228 | */ 229 | @Override 230 | public Annotation convert(Annotation a) 231 | throws UnsupportedOperationException { 232 | 233 | if (a instanceof PolygonAnnotation) 234 | return a; 235 | 236 | if (a instanceof RectangleAnnotation) { 237 | 238 | RectangleAnnotation r = (RectangleAnnotation) a; 239 | return new PolygonAnnotation(r.getPolygon()); 240 | 241 | } 242 | 243 | return super.convert(a); 244 | } 245 | 246 | } 247 | -------------------------------------------------------------------------------- /src/si/vicos/annotations/editor/tracking/FrameTagsEditor.java: -------------------------------------------------------------------------------- 1 | package si.vicos.annotations.editor.tracking; 2 | 3 | import java.awt.BorderLayout; 4 | import java.awt.Component; 5 | import java.awt.event.ActionEvent; 6 | import java.awt.event.ActionListener; 7 | import java.util.HashSet; 8 | import java.util.Set; 9 | 10 | import javax.swing.DefaultListModel; 11 | import javax.swing.DefaultListSelectionModel; 12 | import javax.swing.JComponent; 13 | import javax.swing.JLabel; 14 | import javax.swing.JList; 15 | import javax.swing.JScrollPane; 16 | import javax.swing.JTextField; 17 | import javax.swing.ListCellRenderer; 18 | import javax.swing.event.ListSelectionEvent; 19 | import javax.swing.event.ListSelectionListener; 20 | 21 | import si.vicos.annotations.tracking.Interval; 22 | 23 | /** 24 | * The Class FrameTagsEditor. 25 | */ 26 | public class FrameTagsEditor extends JComponent { 27 | 28 | /** The Constant serialVersionUID. */ 29 | private static final long serialVersionUID = 1L; 30 | 31 | /** The tags. */ 32 | private JList tags = new JList(); 33 | 34 | /** The annotations. */ 35 | private EditableAnnotatedSequence annotations; 36 | 37 | /** The model. */ 38 | DefaultListModel model = new DefaultListModel(); 39 | 40 | /** The status. */ 41 | private JLabel status = new JLabel(); 42 | 43 | /** The interval. */ 44 | private Interval interval; 45 | 46 | /** The selection. */ 47 | private Set selection; 48 | 49 | /** The hidden. */ 50 | private Set hidden; 51 | 52 | /** The renderer. */ 53 | private ListCellRenderer renderer = new ListCellRenderer() { 54 | 55 | @Override 56 | public Component getListCellRendererComponent( 57 | JList list, String value, int index, 58 | boolean isSelected, boolean cellHasFocus) { 59 | 60 | return null; 61 | } 62 | 63 | }; 64 | 65 | /** 66 | * Instantiates a new frame tags editor. 67 | * 68 | * @param ant 69 | * the ant 70 | * @param hiddenTags 71 | * the hidden tags 72 | */ 73 | public FrameTagsEditor(EditableAnnotatedSequence ant, Set hiddenTags) { 74 | super(); 75 | setLayout(new BorderLayout(5, 5)); 76 | 77 | annotations = ant; 78 | 79 | hidden = hiddenTags; 80 | 81 | add(new JScrollPane(tags, JScrollPane.VERTICAL_SCROLLBAR_ALWAYS, 82 | JScrollPane.HORIZONTAL_SCROLLBAR_ALWAYS), BorderLayout.CENTER); 83 | 84 | add(status, BorderLayout.NORTH); 85 | 86 | updateList(); 87 | 88 | tags.setModel(model); 89 | 90 | tags.addListSelectionListener(new ListSelectionListener() { 91 | 92 | @Override 93 | public void valueChanged(ListSelectionEvent e) { 94 | 95 | if (selection == null) 96 | return; 97 | 98 | Set newSelection = new HashSet(tags 99 | .getSelectedValuesList()); 100 | 101 | Set added = new HashSet(tags 102 | .getSelectedValuesList()); 103 | 104 | Set removed = new HashSet(selection); 105 | 106 | added.removeAll(selection); 107 | removed.removeAll(newSelection); 108 | 109 | annotations.addTags(interval, added); 110 | 111 | annotations.removeTags(interval, removed); 112 | 113 | selection = newSelection; 114 | 115 | } 116 | 117 | }); 118 | 119 | tags.setSelectionModel(new DefaultListSelectionModel() { 120 | 121 | private static final long serialVersionUID = 1L; 122 | 123 | @Override 124 | public void setSelectionInterval(int index0, int index1) { 125 | if (tags.isSelectedIndex(index0)) { 126 | tags.removeSelectionInterval(index0, index1); 127 | } else { 128 | tags.addSelectionInterval(index0, index1); 129 | } 130 | } 131 | }); 132 | 133 | final JTextField add = new JTextField(); 134 | add.setToolTipText("Add new tag"); 135 | add.addActionListener(new ActionListener() { 136 | 137 | @Override 138 | public void actionPerformed(ActionEvent arg0) { 139 | 140 | String query = add.getText().trim(); 141 | 142 | if (!query.isEmpty()) { 143 | Set added = new HashSet(); 144 | added.add(query); 145 | 146 | annotations.addTags(interval, added); 147 | } 148 | 149 | add.setText(""); 150 | 151 | } 152 | }); 153 | 154 | add(add, BorderLayout.SOUTH); 155 | 156 | } 157 | 158 | /** 159 | * Update list. 160 | */ 161 | private void updateList() { 162 | 163 | // model = new DefaultListModel(); 164 | 165 | for (String tag : annotations.getTags()) { 166 | if (hidden.contains(tag)) 167 | continue; 168 | 169 | if (model.contains(tag)) 170 | continue; 171 | // if (model.g) 172 | 173 | model.addElement(tag); 174 | } 175 | 176 | // tags.setModel(model); 177 | 178 | } 179 | 180 | /** 181 | * Sets the interval. 182 | * 183 | * @param interval 184 | * the new interval 185 | */ 186 | public void setInterval(Interval interval) { 187 | 188 | updateList(); 189 | 190 | HashSet tagSet = new HashSet(); 191 | 192 | for (Integer i : interval) 193 | tagSet.addAll(annotations.getTags(i)); 194 | 195 | tagSet.removeAll(hidden); 196 | 197 | int[] selection = new int[tagSet.size()]; 198 | 199 | int i = 0; 200 | for (String tag : tagSet) 201 | selection[i++] = model.indexOf(tag); 202 | 203 | this.selection = null; // Important so that selection does not trigger 204 | // tag toggle 205 | tags.clearSelection(); 206 | tags.setSelectedIndices(selection); 207 | this.selection = tagSet; 208 | 209 | this.interval = new Interval(interval); 210 | 211 | if (interval.isEmpty()) 212 | status.setText(String.format("Tags on frame %d", 213 | interval.getBegin())); 214 | else 215 | status.setText(String.format("Tags on interval %d:%d", 216 | interval.getBegin(), interval.getEnd())); 217 | 218 | } 219 | 220 | /** 221 | * Gets the interval. 222 | * 223 | * @return the interval 224 | */ 225 | public Interval getInterval() { 226 | 227 | return interval; 228 | 229 | } 230 | 231 | } 232 | -------------------------------------------------------------------------------- /src/si/vicos/annotations/editor/AnnotatorSplash.java: -------------------------------------------------------------------------------- 1 | package si.vicos.annotations.editor; 2 | 3 | import java.awt.Dimension; 4 | import java.awt.Image; 5 | import java.awt.event.ActionEvent; 6 | import java.awt.image.BufferedImage; 7 | import java.io.File; 8 | import java.io.IOException; 9 | import java.util.Arrays; 10 | import java.util.Collection; 11 | import java.util.Vector; 12 | 13 | import javax.swing.Action; 14 | import javax.swing.BorderFactory; 15 | import javax.swing.JComponent; 16 | import javax.swing.JFileChooser; 17 | import javax.swing.JScrollPane; 18 | import javax.swing.event.ListSelectionEvent; 19 | import javax.swing.event.ListSelectionListener; 20 | import javax.swing.filechooser.FileFilter; 21 | 22 | import org.coffeeshop.application.Application; 23 | import org.coffeeshop.cache.ObjectCache; 24 | import org.coffeeshop.settings.SettingsNotFoundException; 25 | import org.coffeeshop.swing.RecentDocuments; 26 | import org.coffeeshop.swing.Splash; 27 | import org.coffeeshop.swing.ToolTipAction; 28 | 29 | import si.vicos.annotations.editor.tracking.EditableAnnotatedSequence; 30 | import si.vicos.annotations.editor.tracking.UndoableAnnotatedSequence; 31 | 32 | /** 33 | * The Class AnnotatorSplash. 34 | */ 35 | public class AnnotatorSplash extends Splash { 36 | 37 | /** The list. */ 38 | private ThumbnailGridList list; 39 | 40 | /** 41 | * Instantiates a new annotator splash. 42 | * 43 | * @param title 44 | * the title 45 | * @param image 46 | * the image 47 | * @param history 48 | * the history 49 | */ 50 | public AnnotatorSplash(String title, Image image, RecentDocuments history) { 51 | super(title, image); 52 | 53 | list = new ThumbnailGridList(history, new Dimension(128, 128), 54 | new AnnotationsDocumentRenderer(), 55 | new ObjectCache(10)); 56 | } 57 | 58 | /* 59 | * (non-Javadoc) 60 | * 61 | * @see org.coffeeshop.swing.Splash#createSidebarComponent() 62 | */ 63 | @Override 64 | protected JComponent createSidebarComponent() { 65 | 66 | list.addListSelectionListener(new ListSelectionListener() { 67 | 68 | @Override 69 | public void valueChanged(ListSelectionEvent e) { 70 | 71 | if (e.getValueIsAdjusting()) 72 | return; 73 | 74 | closeWithResult(list.getSelectedValue()); 75 | 76 | } 77 | }); 78 | 79 | JScrollPane listpane = new JScrollPane(list, 80 | JScrollPane.VERTICAL_SCROLLBAR_AS_NEEDED, 81 | JScrollPane.HORIZONTAL_SCROLLBAR_AS_NEEDED); 82 | 83 | listpane.setBorder(BorderFactory.createCompoundBorder( 84 | BorderFactory.createEmptyBorder(20, 20, 20, 20), 85 | listpane.getBorder())); 86 | 87 | listpane.setPreferredSize(new Dimension(128 * 3 + 70, 200)); 88 | 89 | return listpane; 90 | 91 | } 92 | 93 | /** 94 | * Browse. 95 | * 96 | * @return the file 97 | */ 98 | public File browse() { 99 | String path = "."; 100 | try { 101 | path = Application.getApplicationSettings() 102 | .getString("browse.path"); 103 | } catch (SettingsNotFoundException ex) { 104 | } 105 | 106 | JFileChooser chooser = new JFileChooser(path); 107 | 108 | chooser.setFileSelectionMode(JFileChooser.FILES_ONLY); 109 | 110 | chooser.setFileFilter(Annotator.FILE_FILTER); 111 | 112 | chooser.showOpenDialog(null); 113 | 114 | if (chooser.getSelectedFile() != null) { 115 | 116 | Application.getApplicationSettings().setString("browse.path", 117 | chooser.getSelectedFile().toString()); 118 | 119 | return chooser.getSelectedFile(); 120 | } 121 | 122 | return null; 123 | } 124 | 125 | /* 126 | * (non-Javadoc) 127 | * 128 | * @see org.coffeeshop.swing.Splash#createActions() 129 | */ 130 | @Override 131 | protected Collection createActions() { 132 | 133 | Collection actions = new Vector(); 134 | 135 | actions.add(new ToolTipAction("New sequence", "new") { 136 | 137 | private static final long serialVersionUID = 1L; 138 | 139 | @Override 140 | public void actionPerformed(ActionEvent arg0) { 141 | 142 | String path = "."; 143 | try { 144 | path = Application.getApplicationSettings().getString( 145 | "browse.path"); 146 | } catch (SettingsNotFoundException ex) { 147 | } 148 | 149 | JFileChooser chooser = new JFileChooser(path); 150 | 151 | chooser.setMultiSelectionEnabled(true); 152 | chooser.setFileSelectionMode(JFileChooser.FILES_ONLY); 153 | 154 | chooser.setFileFilter(new FileFilter() { 155 | 156 | @Override 157 | public String getDescription() { 158 | return "Image files"; 159 | } 160 | 161 | @Override 162 | public boolean accept(File arg0) { 163 | 164 | if (arg0.isDirectory()) 165 | return true; 166 | 167 | String name = arg0.getName(); 168 | 169 | return name.endsWith(".jpg") || name.endsWith(".png"); 170 | 171 | } 172 | }); 173 | 174 | chooser.showOpenDialog(null); 175 | 176 | if (chooser.getSelectedFile() != null) { 177 | 178 | Application.getApplicationSettings() 179 | .setString("browse.path", 180 | chooser.getSelectedFile().toString()); 181 | 182 | EditableAnnotatedSequence writer; 183 | try { 184 | File[] files = chooser.getSelectedFiles(); 185 | Arrays.sort(files); 186 | 187 | writer = new UndoableAnnotatedSequence(files); 188 | 189 | closeWithResult(writer); 190 | } catch (IOException e) { 191 | Application.getApplicationLogger().report(e); 192 | } 193 | 194 | } 195 | 196 | } 197 | }); 198 | 199 | actions.add(new ToolTipAction("Load", "load") { 200 | 201 | private static final long serialVersionUID = 1L; 202 | 203 | @Override 204 | public void actionPerformed(ActionEvent arg0) { 205 | 206 | File selection = browse(); 207 | 208 | if (selection != null) 209 | closeWithResult(selection); 210 | 211 | } 212 | }); 213 | 214 | actions.add(new ToolTipAction("Exit", "exit") { 215 | 216 | private static final long serialVersionUID = 1L; 217 | 218 | @Override 219 | public void actionPerformed(ActionEvent arg0) { 220 | 221 | closeWithResult(null); 222 | 223 | } 224 | }); 225 | 226 | return actions; 227 | } 228 | 229 | } 230 | -------------------------------------------------------------------------------- /src/si/vicos/annotations/editor/tracking/PolygonAnnotationRenderer.java: -------------------------------------------------------------------------------- 1 | package si.vicos.annotations.editor.tracking; 2 | 3 | import java.awt.Graphics2D; 4 | import java.awt.Rectangle; 5 | import java.awt.Shape; 6 | import java.awt.geom.AffineTransform; 7 | import java.awt.geom.PathIterator; 8 | import java.awt.geom.Point2D; 9 | import java.awt.geom.Rectangle2D; 10 | import java.util.List; 11 | 12 | import si.vicos.annotations.Polygon2D; 13 | import si.vicos.annotations.PolygonAnnotation; 14 | import si.vicos.annotations.editor.AnnotationRenderer; 15 | 16 | /** 17 | * The Class PolygonAnnotationRenderer. 18 | */ 19 | public class PolygonAnnotationRenderer implements AnnotationRenderer { 20 | 21 | /** The shape. */ 22 | private Shape shape; 23 | 24 | /** 25 | * The Class EmptyPathIterator. 26 | */ 27 | public static class EmptyPathIterator implements PathIterator { 28 | 29 | /* 30 | * (non-Javadoc) 31 | * 32 | * @see java.awt.geom.PathIterator#currentSegment(float[]) 33 | */ 34 | @Override 35 | public int currentSegment(float[] coords) { 36 | return 0; 37 | } 38 | 39 | /* 40 | * (non-Javadoc) 41 | * 42 | * @see java.awt.geom.PathIterator#currentSegment(double[]) 43 | */ 44 | @Override 45 | public int currentSegment(double[] coords) { 46 | return 0; 47 | } 48 | 49 | /* 50 | * (non-Javadoc) 51 | * 52 | * @see java.awt.geom.PathIterator#getWindingRule() 53 | */ 54 | @Override 55 | public int getWindingRule() { 56 | return 0; 57 | } 58 | 59 | /* 60 | * (non-Javadoc) 61 | * 62 | * @see java.awt.geom.PathIterator#isDone() 63 | */ 64 | @Override 65 | public boolean isDone() { 66 | return true; 67 | } 68 | 69 | /* 70 | * (non-Javadoc) 71 | * 72 | * @see java.awt.geom.PathIterator#next() 73 | */ 74 | @Override 75 | public void next() { 76 | } 77 | 78 | } 79 | 80 | /** 81 | * The Class EmptyShape. 82 | */ 83 | public static class EmptyShape implements Shape { 84 | 85 | /* 86 | * (non-Javadoc) 87 | * 88 | * @see java.awt.Shape#contains(java.awt.geom.Point2D) 89 | */ 90 | @Override 91 | public boolean contains(Point2D p) { 92 | return false; 93 | } 94 | 95 | /* 96 | * (non-Javadoc) 97 | * 98 | * @see java.awt.Shape#contains(java.awt.geom.Rectangle2D) 99 | */ 100 | @Override 101 | public boolean contains(Rectangle2D r) { 102 | return false; 103 | } 104 | 105 | /* 106 | * (non-Javadoc) 107 | * 108 | * @see java.awt.Shape#contains(double, double) 109 | */ 110 | @Override 111 | public boolean contains(double x, double y) { 112 | return false; 113 | } 114 | 115 | /* 116 | * (non-Javadoc) 117 | * 118 | * @see java.awt.Shape#contains(double, double, double, double) 119 | */ 120 | @Override 121 | public boolean contains(double x, double y, double w, double h) { 122 | return false; 123 | } 124 | 125 | /* 126 | * (non-Javadoc) 127 | * 128 | * @see java.awt.Shape#getBounds() 129 | */ 130 | @Override 131 | public Rectangle getBounds() { 132 | return new Rectangle(); 133 | } 134 | 135 | /* 136 | * (non-Javadoc) 137 | * 138 | * @see java.awt.Shape#getBounds2D() 139 | */ 140 | @Override 141 | public Rectangle2D getBounds2D() { 142 | return new Rectangle2D.Double(); 143 | } 144 | 145 | /* 146 | * (non-Javadoc) 147 | * 148 | * @see java.awt.Shape#getPathIterator(java.awt.geom.AffineTransform) 149 | */ 150 | @Override 151 | public PathIterator getPathIterator(AffineTransform at) { 152 | return new EmptyPathIterator(); 153 | } 154 | 155 | /* 156 | * (non-Javadoc) 157 | * 158 | * @see java.awt.Shape#getPathIterator(java.awt.geom.AffineTransform, 159 | * double) 160 | */ 161 | @Override 162 | public PathIterator getPathIterator(AffineTransform at, double flatness) { 163 | return new EmptyPathIterator(); 164 | } 165 | 166 | /* 167 | * (non-Javadoc) 168 | * 169 | * @see java.awt.Shape#intersects(java.awt.geom.Rectangle2D) 170 | */ 171 | @Override 172 | public boolean intersects(Rectangle2D r) { 173 | return false; 174 | } 175 | 176 | /* 177 | * (non-Javadoc) 178 | * 179 | * @see java.awt.Shape#intersects(double, double, double, double) 180 | */ 181 | @Override 182 | public boolean intersects(double x, double y, double w, double h) { 183 | return false; 184 | } 185 | 186 | } 187 | 188 | /** 189 | * Instantiates a new polygon annotation renderer. 190 | * 191 | * @param annotation 192 | * the annotation 193 | */ 194 | public PolygonAnnotationRenderer(PolygonAnnotation annotation) { 195 | 196 | shape = annotationToShape(annotation); 197 | 198 | } 199 | 200 | /* 201 | * (non-Javadoc) 202 | * 203 | * @see 204 | * si.vicos.annotations.editor.AnnotationRenderer#paint(java.awt.Graphics2D) 205 | */ 206 | @Override 207 | public void paint(Graphics2D g) { 208 | 209 | g.draw(shape); 210 | 211 | } 212 | 213 | /** 214 | * Annotation to shape. 215 | * 216 | * @param annotation 217 | * the annotation 218 | * @return the shape 219 | */ 220 | public static Shape annotationToShape(PolygonAnnotation annotation) { 221 | 222 | int npoints = annotation.size(); 223 | 224 | if (npoints < 1) { 225 | return new EmptyShape(); 226 | } 227 | 228 | float[] xpoints = new float[npoints]; 229 | float[] ypoints = new float[npoints]; 230 | 231 | int i = 0; 232 | for (Point2D p : annotation) { 233 | xpoints[i] = (float) p.getX(); 234 | ypoints[i] = (float) p.getY(); 235 | i++; 236 | } 237 | 238 | return new Polygon2D(xpoints, ypoints, npoints); 239 | 240 | } 241 | 242 | /** 243 | * Points to shape. 244 | * 245 | * @param points 246 | * the points 247 | * @return the shape 248 | */ 249 | public static Shape pointsToShape(List points) { 250 | 251 | if (points == null || points.size() < 1) 252 | return new EmptyShape(); 253 | 254 | int npoints = points.size(); 255 | 256 | float[] xpoints = new float[npoints]; 257 | float[] ypoints = new float[npoints]; 258 | 259 | int i = 0; 260 | for (Point2D p : points) { 261 | xpoints[i] = (float) p.getX(); 262 | ypoints[i] = (float) p.getY(); 263 | i++; 264 | } 265 | 266 | return new Polygon2D(xpoints, ypoints, npoints); 267 | 268 | } 269 | } 270 | -------------------------------------------------------------------------------- /src/si/vicos/annotations/editor/Interpolator.java: -------------------------------------------------------------------------------- 1 | package si.vicos.annotations.editor; 2 | 3 | import java.awt.geom.Point2D; 4 | import java.util.List; 5 | import java.util.Vector; 6 | 7 | import si.vicos.annotations.Annotation; 8 | import si.vicos.annotations.PointAnnotation; 9 | import si.vicos.annotations.PolygonAnnotation; 10 | import si.vicos.annotations.RectangleAnnotation; 11 | import si.vicos.annotations.ShapeAnnotation; 12 | import si.vicos.annotations.SituatedAnnotation; 13 | 14 | /** 15 | * The Class Interpolator. 16 | */ 17 | public class Interpolator { 18 | 19 | /** The end. */ 20 | private Annotation start, end; 21 | 22 | /** The polygon interpolation. */ 23 | private PolygonMorphing polygonInterpolation = null; 24 | 25 | /** 26 | * Instantiates a new interpolator. 27 | * 28 | * @param start 29 | * the start 30 | * @param end 31 | * the end 32 | */ 33 | public Interpolator(Annotation start, Annotation end) { 34 | this.start = start; 35 | this.end = end; 36 | 37 | if (!(start instanceof SituatedAnnotation)) 38 | throw new UnsupportedOperationException( 39 | "Cannot interpolate unsituated annotations"); 40 | 41 | if (!(end instanceof SituatedAnnotation)) 42 | throw new UnsupportedOperationException( 43 | "Cannot interpolate unsituated annotations"); 44 | } 45 | 46 | /** 47 | * Interpolate. 48 | * 49 | * @param factor 50 | * the factor 51 | * @return the annotation 52 | */ 53 | public Annotation interpolate(float factor) { 54 | 55 | if ((start instanceof ShapeAnnotation) 56 | && (end instanceof ShapeAnnotation)) { 57 | 58 | if ((start instanceof PolygonAnnotation) 59 | || (end instanceof PolygonAnnotation)) { 60 | 61 | if (polygonInterpolation == null) { 62 | 63 | PolygonAnnotation p1 = (start instanceof PolygonAnnotation) ? (PolygonAnnotation) start 64 | : new PolygonAnnotation( 65 | ((ShapeAnnotation) start).getPolygon()); 66 | PolygonAnnotation p2 = (end instanceof PolygonAnnotation) ? (PolygonAnnotation) end 67 | : new PolygonAnnotation( 68 | ((ShapeAnnotation) end).getPolygon()); 69 | 70 | polygonInterpolation = new PolygonMorphing(p1, p2); 71 | 72 | } 73 | 74 | return polygonInterpolation.morph(factor); 75 | 76 | } 77 | 78 | if ((start instanceof RectangleAnnotation) 79 | && (end instanceof RectangleAnnotation)) { 80 | 81 | return interpolateRectangle((RectangleAnnotation) start, 82 | (RectangleAnnotation) end, factor); 83 | 84 | } 85 | 86 | PolygonAnnotation p1 = new PolygonAnnotation( 87 | ((ShapeAnnotation) start).getPolygon()); 88 | PolygonAnnotation p2 = new PolygonAnnotation( 89 | ((ShapeAnnotation) end).getPolygon()); 90 | 91 | return interpolatePolygon(p1, p2, factor); 92 | 93 | } else { 94 | 95 | return interpolateCenter((SituatedAnnotation) start, 96 | (SituatedAnnotation) end, factor); 97 | 98 | } 99 | 100 | } 101 | 102 | /** 103 | * Interpolate center. 104 | * 105 | * @param start 106 | * the start 107 | * @param end 108 | * the end 109 | * @param factor 110 | * the factor 111 | * @return the point annotation 112 | */ 113 | public static PointAnnotation interpolateCenter(SituatedAnnotation start, 114 | SituatedAnnotation end, float factor) { 115 | 116 | Point2D p1 = start.getCenter(); 117 | Point2D p2 = start.getCenter(); 118 | 119 | double x = (p1.getX()) + ((p2.getX()) - (p1.getX())) * factor; 120 | double y = (p1.getY()) + ((p2.getY()) - (p1.getY())) * factor; 121 | 122 | return new PointAnnotation((int) x, (int) y); 123 | 124 | } 125 | 126 | /** 127 | * Interpolate rectangle. 128 | * 129 | * @param start 130 | * the start 131 | * @param end 132 | * the end 133 | * @param factor 134 | * the factor 135 | * @return the rectangle annotation 136 | */ 137 | public static RectangleAnnotation interpolateRectangle( 138 | RectangleAnnotation start, RectangleAnnotation end, float factor) { 139 | 140 | float x = ((float) start.getX()) 141 | + (((float) end.getX()) - ((float) start.getX())) * factor; 142 | float y = ((float) start.getY()) 143 | + (((float) end.getY()) - ((float) start.getY())) * factor; 144 | float w = ((float) start.getWidth()) 145 | + (((float) end.getWidth()) - ((float) start.getWidth())) 146 | * factor; 147 | float h = ((float) start.getHeight()) 148 | + (((float) end.getHeight()) - ((float) start.getHeight())) 149 | * factor; 150 | 151 | return new RectangleAnnotation(x, y, w, h); 152 | 153 | } 154 | 155 | /** 156 | * Interpolate polygon. 157 | * 158 | * @param start 159 | * the start 160 | * @param end 161 | * the end 162 | * @param factor 163 | * the factor 164 | * @return the polygon annotation 165 | */ 166 | public static PolygonAnnotation interpolatePolygon(PolygonAnnotation start, 167 | PolygonAnnotation end, float factor) { 168 | 169 | if (start.size() != end.size()) 170 | return null; 171 | 172 | List pointsFrom = end.getPolygon(); 173 | List pointsTo = start.getPolygon(); 174 | 175 | Point2D centerFrom = end.getCenter(); 176 | Point2D centerTo = start.getCenter(); 177 | 178 | List points = new Vector(); 179 | 180 | for (Point2D pointTo : pointsTo) { 181 | 182 | Point2D pointToNorm = new Point2D.Double(pointTo.getX() 183 | - centerTo.getX(), pointTo.getY() - centerTo.getY()); 184 | 185 | Point2D closest = null; 186 | double mindist = Float.MAX_VALUE; 187 | for (int i = 0; i < pointsFrom.size(); i++) { 188 | 189 | Point2D pointFrom = pointsFrom.get(i); 190 | Point2D pointFromNorm = new Point2D.Double(pointFrom.getX() 191 | - centerFrom.getX(), pointFrom.getY() 192 | - centerFrom.getY()); 193 | 194 | double distance = pointFromNorm.distance(pointToNorm); 195 | 196 | if (distance < mindist) { 197 | closest = pointFrom; 198 | mindist = distance; 199 | } 200 | 201 | } 202 | 203 | double x = (pointTo.getX()) + ((closest.getX()) - (pointTo.getX())) 204 | * factor; 205 | double y = (pointTo.getY()) + ((closest.getY()) - (pointTo.getY())) 206 | * factor; 207 | 208 | points.add(new Point2D.Double(x, y)); 209 | 210 | pointsFrom.remove(closest); 211 | } 212 | 213 | return new PolygonAnnotation(points); 214 | } 215 | } 216 | -------------------------------------------------------------------------------- /src/si/vicos/annotations/editor/ThumbnailGridList.java: -------------------------------------------------------------------------------- 1 | package si.vicos.annotations.editor; 2 | 3 | import java.awt.Component; 4 | import java.awt.Dimension; 5 | import java.awt.Graphics; 6 | import java.awt.Graphics2D; 7 | import java.awt.Insets; 8 | import java.awt.RenderingHints; 9 | import java.awt.geom.Rectangle2D; 10 | import java.awt.image.BufferedImage; 11 | 12 | import javax.swing.BorderFactory; 13 | import javax.swing.JList; 14 | import javax.swing.JPanel; 15 | import javax.swing.ListCellRenderer; 16 | import javax.swing.ListModel; 17 | import javax.swing.border.Border; 18 | import javax.swing.event.ListDataEvent; 19 | import javax.swing.event.ListDataListener; 20 | 21 | import org.coffeeshop.cache.Cache; 22 | 23 | import si.vicos.annotations.editor.ThumbnailGenerator.ThumbnailGeneratorCallback; 24 | 25 | /** 26 | * The Class ThumbnailGridList. 27 | * 28 | * @param 29 | * the element type 30 | */ 31 | public class ThumbnailGridList extends JList { 32 | 33 | /** The Constant serialVersionUID. */ 34 | private static final long serialVersionUID = 1L; 35 | 36 | /** The border. */ 37 | private Border border = BorderFactory.createEmptyBorder(5, 5, 5, 5); 38 | 39 | /** 40 | * The Class ThumbnailCell. 41 | */ 42 | private class ThumbnailCell extends JPanel implements 43 | ThumbnailGeneratorCallback { 44 | 45 | /** The Constant serialVersionUID. */ 46 | private static final long serialVersionUID = 1L; 47 | 48 | /** The index. */ 49 | private int index; 50 | 51 | /** The object. */ 52 | private Object object; 53 | 54 | /** The failure. */ 55 | private boolean failure = false; 56 | 57 | /** The focused. */ 58 | private boolean focused; 59 | 60 | /** 61 | * Instantiates a new thumbnail cell. 62 | * 63 | * @param object 64 | * the object 65 | * @param index 66 | * the index 67 | * @param selected 68 | * the selected 69 | * @param focused 70 | * the focused 71 | */ 72 | public ThumbnailCell(Object object, int index, boolean selected, 73 | boolean focused) { 74 | 75 | this.object = object; 76 | 77 | this.index = index; 78 | 79 | setToolTipText(object.toString()); 80 | 81 | setBackground(selected ? getSelectionBackground() 82 | : ThumbnailGridList.this.getBackground()); 83 | 84 | this.focused = focused; 85 | 86 | setPreferredSize(itemSize); 87 | 88 | setBorder(border); 89 | 90 | repaint(1000L); 91 | 92 | } 93 | 94 | /* 95 | * (non-Javadoc) 96 | * 97 | * @see javax.swing.JComponent#paintComponent(java.awt.Graphics) 98 | */ 99 | @Override 100 | protected void paintComponent(Graphics g) { 101 | super.paintComponent(g); 102 | 103 | if (failure) 104 | return; 105 | 106 | BufferedImage img = generator.generate(object, this); 107 | 108 | if (img != null) { 109 | 110 | Insets insets = getInsets(); 111 | 112 | float scale = Math.max( 113 | (float) (getWidth() - insets.left - insets.right) 114 | / (float) img.getWidth(), (float) (getHeight() 115 | - insets.bottom - insets.top) 116 | / (float) img.getHeight()); 117 | 118 | int w = (int) ((float) img.getWidth() * scale); 119 | int h = (int) ((float) img.getHeight() * scale); 120 | int x = (getWidth() + insets.left - w) / 2; 121 | int y = (getHeight() + insets.top - h) / 2; 122 | 123 | ((Graphics2D) g).setRenderingHint( 124 | RenderingHints.KEY_ANTIALIASING, 125 | RenderingHints.VALUE_ANTIALIAS_ON); 126 | ((Graphics2D) g).setRenderingHint( 127 | RenderingHints.KEY_INTERPOLATION, 128 | RenderingHints.VALUE_INTERPOLATION_BILINEAR); 129 | 130 | g.setClip(insets.left, insets.top, getWidth() - insets.left 131 | - insets.right, getHeight() - insets.bottom 132 | - insets.top); 133 | 134 | g.drawImage(img, x, y, w, h, 0, 0, img.getWidth(), 135 | img.getHeight(), this); 136 | 137 | } else { 138 | 139 | String text = object.toString(); 140 | 141 | Rectangle2D b = g.getFontMetrics().getStringBounds(text, g); 142 | 143 | g.drawString(text, (int) (getWidth() - b.getWidth()) / 2, 144 | (int) (getHeight() - b.getHeight()) / 2); 145 | 146 | } 147 | 148 | if (focused) { 149 | 150 | g.drawRect(0, 0, getWidth() - 1, getHeight() - 1); 151 | 152 | } 153 | 154 | } 155 | 156 | /* 157 | * (non-Javadoc) 158 | * 159 | * @see 160 | * si.vicos.annotations.editor.ThumbnailGenerator.ThumbnailGeneratorCallback 161 | * #failed() 162 | */ 163 | @Override 164 | public void failed() { 165 | ThumbnailGridList.this.repaint(getCellBounds(index, index + 1)); 166 | } 167 | 168 | /* 169 | * (non-Javadoc) 170 | * 171 | * @see 172 | * si.vicos.annotations.editor.ThumbnailGenerator.ThumbnailGeneratorCallback 173 | * #retrieved(java.awt.image.BufferedImage) 174 | */ 175 | @Override 176 | public void retrieved(BufferedImage image) { 177 | ThumbnailGridList.this.repaint(getCellBounds(index, index + 1)); 178 | } 179 | 180 | } 181 | 182 | /** The generator. */ 183 | private ThumbnailGenerator generator; 184 | 185 | /** The item size. */ 186 | private Dimension itemSize; 187 | 188 | /** 189 | * Instantiates a new thumbnail grid list. 190 | * 191 | * @param data 192 | * the data 193 | * @param itemSize 194 | * the item size 195 | * @param renderer 196 | * the renderer 197 | * @param cache 198 | * the cache 199 | */ 200 | public ThumbnailGridList(ListModel data, Dimension itemSize, 201 | ThumbnailRenderer renderer, Cache cache) { 202 | 203 | super(data); 204 | 205 | generator = new ThumbnailGenerator(renderer, cache); 206 | 207 | this.itemSize = new Dimension(itemSize); 208 | 209 | setLayoutOrientation(JList.HORIZONTAL_WRAP); 210 | setVisibleRowCount(-1); 211 | 212 | setCellRenderer(new ListCellRenderer() { 213 | 214 | @Override 215 | public Component getListCellRendererComponent(JList list, 216 | Object value, int index, boolean isSelected, 217 | boolean cellHasFocus) { 218 | return new ThumbnailCell(value, index, isSelected, cellHasFocus); 219 | } 220 | }); 221 | 222 | data.addListDataListener(new ListDataListener() { 223 | 224 | @Override 225 | public void intervalRemoved(ListDataEvent arg0) { 226 | 227 | } 228 | 229 | @Override 230 | public void intervalAdded(ListDataEvent arg0) { 231 | 232 | } 233 | 234 | @Override 235 | public void contentsChanged(ListDataEvent a) { 236 | if (a.getSource() != getModel()) 237 | return; 238 | 239 | for (int i = a.getIndex0(); i < a.getIndex1(); i++) 240 | generator.invalidate(getModel().getElementAt(i)); 241 | } 242 | }); 243 | 244 | } 245 | 246 | } 247 | -------------------------------------------------------------------------------- /src/si/vicos/annotations/tracking/Values.java: -------------------------------------------------------------------------------- 1 | package si.vicos.annotations.tracking; 2 | 3 | import java.io.BufferedReader; 4 | import java.io.File; 5 | import java.io.FileInputStream; 6 | import java.io.FileNotFoundException; 7 | import java.io.IOException; 8 | import java.io.InputStream; 9 | import java.io.InputStreamReader; 10 | import java.io.OutputStream; 11 | import java.io.PrintWriter; 12 | import java.util.HashMap; 13 | import java.util.HashSet; 14 | import java.util.List; 15 | import java.util.Map; 16 | import java.util.Set; 17 | import java.util.Vector; 18 | 19 | import org.coffeeshop.string.StringUtils; 20 | 21 | /** 22 | * The Class Values. 23 | */ 24 | public class Values { 25 | 26 | /** The values. */ 27 | protected Vector> values = new Vector>(); 28 | 29 | /** 30 | * Read value. 31 | * 32 | * @param value 33 | * the value 34 | * @param source 35 | * the source 36 | */ 37 | public void readValue(String value, File source) { 38 | 39 | try { 40 | readValue(value, new FileInputStream(source)); 41 | } catch (FileNotFoundException e) { 42 | } 43 | 44 | } 45 | 46 | /** 47 | * Read value. 48 | * 49 | * @param value 50 | * the value 51 | * @param ins 52 | * the ins 53 | */ 54 | public void readValue(String value, InputStream ins) { 55 | 56 | try { 57 | 58 | BufferedReader reader = new BufferedReader(new InputStreamReader( 59 | ins)); 60 | 61 | int frame = 0; 62 | 63 | while (true) { 64 | 65 | String line = reader.readLine(); 66 | 67 | if (line == null) 68 | break; 69 | 70 | if (!line.isEmpty()) 71 | addValue(frame, value, line); 72 | 73 | frame++; 74 | } 75 | 76 | reader.close(); 77 | 78 | } catch (IOException e) { 79 | } 80 | 81 | } 82 | 83 | /** 84 | * Write value. 85 | * 86 | * @param value 87 | * the value 88 | * @param outs 89 | * the outs 90 | * @throws IOException 91 | * Signals that an I/O exception has occurred. 92 | */ 93 | public void writeValue(String value, OutputStream outs) throws IOException { 94 | 95 | PrintWriter out = new PrintWriter(outs); 96 | 97 | for (Map ls : values) { 98 | 99 | if (ls == null || !ls.containsKey(value)) 100 | out.println(""); 101 | else 102 | out.println(ls.get(value)); 103 | 104 | } 105 | 106 | out.flush(); 107 | 108 | } 109 | 110 | /** 111 | * Append values. 112 | * 113 | * @param values 114 | * the values 115 | */ 116 | public void appendValues(Map values) { 117 | 118 | if (values == null) 119 | values = new HashMap(); 120 | 121 | addValues(size(), values); 122 | 123 | } 124 | 125 | /** 126 | * Adds the value. 127 | * 128 | * @param index 129 | * the index 130 | * @param label 131 | * the label 132 | * @param value 133 | * the value 134 | */ 135 | public void addValue(int index, String label, String value) { 136 | 137 | if (index < 0 || label == null || StringUtils.empty(label)) 138 | return; 139 | 140 | if (index >= values.size()) { 141 | 142 | for (int i = values.size(); i <= index; i++) 143 | values.add(null); 144 | 145 | } 146 | 147 | Map tmp = this.values.get(index); 148 | 149 | if (tmp == null) { 150 | tmp = new HashMap(); 151 | this.values.set(index, tmp); 152 | } 153 | 154 | tmp.put(label, value); 155 | 156 | } 157 | 158 | /** 159 | * Adds the values. 160 | * 161 | * @param index 162 | * the index 163 | * @param values 164 | * the values 165 | */ 166 | public void addValues(int index, Map values) { 167 | 168 | if (index < 0 || values == null) 169 | return; 170 | 171 | if (index >= this.values.size()) { 172 | 173 | for (int i = this.values.size(); i <= index; i++) 174 | this.values.add(null); 175 | 176 | } 177 | 178 | Map tmp = this.values.get(index); 179 | 180 | if (tmp == null) { 181 | tmp = new HashMap(); 182 | this.values.set(index, tmp); 183 | } 184 | 185 | tmp.putAll(values); 186 | 187 | } 188 | 189 | /** 190 | * Remove all values for a given frame. 191 | * 192 | * @param index 193 | * the index 194 | */ 195 | public void removeValues(int index) { 196 | 197 | if (index < 0 || index >= size()) 198 | return; 199 | 200 | Map tmp = values.elementAt(index); 201 | 202 | if (tmp == null) 203 | return; 204 | 205 | tmp.clear(); 206 | 207 | } 208 | 209 | /** 210 | * Remove specified values for a given frame. 211 | * 212 | * @param index 213 | * the index 214 | * @param keys 215 | * the keys 216 | */ 217 | public void removeValues(int index, Set keys) { 218 | 219 | if (index < 0 || index >= size()) 220 | return; 221 | 222 | Map tmp = values.elementAt(index); 223 | 224 | if (tmp == null) 225 | return; 226 | 227 | for (String key : keys) 228 | tmp.remove(key); 229 | 230 | } 231 | 232 | /** 233 | * Gets the values. 234 | * 235 | * @param index 236 | * the index 237 | * @return the values 238 | */ 239 | public Map getValues(int index) { 240 | 241 | if (index < 0 || index >= size()) 242 | return null; 243 | 244 | Map tmp = values.elementAt(index); 245 | 246 | if (tmp == null) 247 | return null; 248 | 249 | return new HashMap(tmp); 250 | 251 | } 252 | 253 | /** 254 | * Size. 255 | * 256 | * @return the int 257 | */ 258 | public int size() { 259 | 260 | return values.size(); 261 | 262 | } 263 | 264 | /** 265 | * Gets the value keys. 266 | * 267 | * @return the value keys 268 | */ 269 | public Set getValueKeys() { 270 | 271 | HashSet keys = new HashSet(); 272 | 273 | for (Map frame : values) { 274 | if (frame != null) 275 | keys.addAll(frame.keySet()); 276 | } 277 | 278 | return keys; 279 | 280 | } 281 | 282 | /** 283 | * Checks if is empty. 284 | * 285 | * @return true, if is empty 286 | */ 287 | public boolean isEmpty() { 288 | 289 | for (Map l : values) { 290 | if (l == null || l.isEmpty()) 291 | continue; 292 | return false; 293 | } 294 | return true; 295 | } 296 | 297 | /** 298 | * Find values. 299 | * 300 | * @param key 301 | * the key 302 | * @return the list 303 | */ 304 | public List findValues(String key) { 305 | 306 | Vector frames = new Vector(); 307 | 308 | for (int i = 0; i < Math.min(values.size(), size()); i++) { 309 | 310 | if (values.get(i) != null) 311 | frames.add(values.get(i).get(key)); 312 | else 313 | frames.add(null); 314 | 315 | } 316 | 317 | return frames; 318 | } 319 | } 320 | -------------------------------------------------------------------------------- /src/si/vicos/annotations/editor/PointAnnotationEditor.java: -------------------------------------------------------------------------------- 1 | package si.vicos.annotations.editor; 2 | 3 | import java.awt.Color; 4 | import java.awt.Cursor; 5 | import java.awt.Graphics2D; 6 | import java.awt.Point; 7 | 8 | import javax.swing.JComponent; 9 | 10 | import org.coffeeshop.swing.figure.FigurePanel; 11 | 12 | import si.vicos.annotations.Annotation; 13 | import si.vicos.annotations.PointAnnotation; 14 | import si.vicos.annotations.editor.AnnotatedImageFigure.AnnotationPeer; 15 | 16 | /** 17 | * The Class PointAnnotationEditor. 18 | */ 19 | public class PointAnnotationEditor extends AnnotationEditor { 20 | 21 | /** The shape. */ 22 | private Point shape; 23 | 24 | /** The working point. */ 25 | private Point workingPoint = null; 26 | 27 | /** The radius. */ 28 | private double radius; 29 | 30 | /** The Constant FACTORY. */ 31 | public static final AnnotationEditorFactory FACTORY = new AnnotationEditorFactory() { 32 | 33 | @Override 34 | public String getName() { 35 | return "Point"; 36 | } 37 | 38 | @Override 39 | public PointAnnotationEditor getEditor(AnnotationPeer peer, Color color) { 40 | return new PointAnnotationEditor(peer, color); 41 | } 42 | 43 | }; 44 | 45 | /** 46 | * Instantiates a new point annotation editor. 47 | * 48 | * @param peer 49 | * the peer 50 | * @param color 51 | * the color 52 | */ 53 | public PointAnnotationEditor(AnnotationPeer peer, Color color) { 54 | this(peer, color, 3); 55 | } 56 | 57 | /** 58 | * Instantiates a new point annotation editor. 59 | * 60 | * @param peer 61 | * the peer 62 | * @param color 63 | * the color 64 | * @param radius 65 | * the radius 66 | */ 67 | public PointAnnotationEditor(AnnotationPeer peer, Color color, double radius) { 68 | super(peer, color); 69 | this.radius = radius; 70 | } 71 | 72 | /* 73 | * (non-Javadoc) 74 | * 75 | * @see si.vicos.annotations.editor.AnnotationViewer#getComponent() 76 | */ 77 | @Override 78 | public JComponent getComponent() { 79 | return null; 80 | } 81 | 82 | /* 83 | * (non-Javadoc) 84 | * 85 | * @see 86 | * si.vicos.annotations.editor.AnnotationViewer#paint(java.awt.Graphics2D) 87 | */ 88 | @Override 89 | public void paint(Graphics2D g) { 90 | 91 | if (shape == null) 92 | return; 93 | 94 | g.setColor(color); 95 | g.setStroke(isSelected() ? selectedStroke : normalStroke); 96 | g.drawOval(shape.x - (int) radius, shape.y - (int) radius, 97 | 2 * (int) radius, 2 * (int) radius); 98 | 99 | if (isSelected() && workingPoint != null) { 100 | g.setColor(Color.BLACK); 101 | g.setStroke(selectedStroke); 102 | g.setXORMode(Color.WHITE); 103 | g.drawOval(workingPoint.x - (int) radius, workingPoint.y 104 | - (int) radius, 2 * (int) radius, 2 * (int) radius); 105 | } 106 | 107 | } 108 | 109 | /* 110 | * (non-Javadoc) 111 | * 112 | * @see si.vicos.annotations.editor.AnnotationEditor#resetInput() 113 | */ 114 | @Override 115 | public void resetInput() { 116 | 117 | workingPoint = null; 118 | notifyRepaint(); 119 | 120 | } 121 | 122 | /* 123 | * (non-Javadoc) 124 | * 125 | * @see 126 | * si.vicos.annotations.editor.AnnotationEditor#onMove(org.coffeeshop.swing 127 | * .figure.FigurePanel, java.awt.Point, java.awt.Point, boolean, int) 128 | */ 129 | public Cursor onMove(FigurePanel source, Point from, Point to, 130 | boolean drag, int modifiers) { 131 | 132 | PointAnnotation annotation = getAnnotation(); 133 | if (annotation == null) 134 | return null; 135 | 136 | if (drag) { 137 | 138 | if (workingPoint != null) { 139 | int x = to.x - from.x; 140 | int y = to.y - from.y; 141 | 142 | workingPoint 143 | .setLocation(workingPoint.x + x, workingPoint.y + y); 144 | 145 | notifyRepaint(); 146 | return dragCursor; 147 | } 148 | 149 | if (!annotation.isNull() && contains(from) && from == to) { 150 | workingPoint = new Point((int) annotation.getX(), 151 | (int) annotation.getY()); 152 | 153 | notifyRepaint(); 154 | return dragCursor; 155 | } 156 | 157 | return defaultCursor; 158 | 159 | } else { 160 | 161 | if (!annotation.isNull() && contains(to)) 162 | return dragPossibleCursor; 163 | return defaultCursor; 164 | 165 | } 166 | } 167 | 168 | /** 169 | * Contains. 170 | * 171 | * @param p 172 | * the p 173 | * @return true, if successful 174 | */ 175 | private boolean contains(Point p) { 176 | 177 | return shape.distance(p) < radius; 178 | 179 | } 180 | 181 | /* 182 | * (non-Javadoc) 183 | * 184 | * @see 185 | * si.vicos.annotations.editor.AnnotationEditor#onClick(org.coffeeshop.swing 186 | * .figure.FigurePanel, java.awt.Point, int, int) 187 | */ 188 | public void onClick(FigurePanel source, Point position, int clicks, 189 | int modifiers) { 190 | PointAnnotation annotation = getAnnotation(); 191 | 192 | if (annotation == null) 193 | return; 194 | 195 | if (position == null) { 196 | if (workingPoint != null) 197 | peer.setAnnotation(new PointAnnotation(workingPoint.x, 198 | workingPoint.y)); 199 | } else 200 | peer.setAnnotation(new PointAnnotation(position.x, position.y)); 201 | 202 | updateGraphics(); 203 | resetInput(); 204 | 205 | } 206 | 207 | /* 208 | * (non-Javadoc) 209 | * 210 | * @see si.vicos.annotations.editor.AnnotationViewer#updateGraphics() 211 | */ 212 | @Override 213 | public void updateGraphics() { 214 | PointAnnotation annotation = getAnnotation(); 215 | 216 | if (annotation == null) 217 | return; 218 | 219 | shape = new Point((int) annotation.getX(), (int) annotation.getY()); 220 | } 221 | 222 | /* 223 | * (non-Javadoc) 224 | * 225 | * @see si.vicos.annotations.editor.AnnotationEditor#reset() 226 | */ 227 | @Override 228 | public void reset() { 229 | 230 | resetInput(); 231 | 232 | } 233 | 234 | /* 235 | * (non-Javadoc) 236 | * 237 | * @see 238 | * si.vicos.annotations.editor.AnnotationViewer#getToolTip(org.coffeeshop 239 | * .swing.figure.FigurePanel, java.awt.Point) 240 | */ 241 | @Override 242 | public String getToolTip(FigurePanel source, Point position) { 243 | PointAnnotation annotation = getAnnotation(); 244 | 245 | if (annotation != null && contains(position)) 246 | return String.format("(%.1f, %.1f)", annotation.getX(), 247 | annotation.getY()); 248 | return null; 249 | } 250 | 251 | /** 252 | * Gets the annotation. 253 | * 254 | * @return the annotation 255 | */ 256 | private PointAnnotation getAnnotation() { 257 | 258 | Annotation a = peer.getAnnotation(); 259 | 260 | if (a == null || !(a instanceof PointAnnotation)) 261 | return null; 262 | 263 | return (PointAnnotation) a; 264 | } 265 | 266 | /* 267 | * (non-Javadoc) 268 | * 269 | * @see si.vicos.annotations.editor.AnnotationEditor#getCurrent() 270 | */ 271 | @Override 272 | public Annotation getCurrent() { 273 | if (workingPoint != null) 274 | return new PointAnnotation(workingPoint.x, workingPoint.y); 275 | 276 | return getAnnotation(); 277 | } 278 | 279 | } -------------------------------------------------------------------------------- /src/si/vicos/annotations/Annotation.java: -------------------------------------------------------------------------------- 1 | package si.vicos.annotations; 2 | 3 | import java.text.ParseException; 4 | import java.util.Locale; 5 | 6 | /** 7 | * The Class Annotation. 8 | * 9 | * @author lukacu 10 | */ 11 | public abstract class Annotation implements Cloneable { 12 | 13 | /** The serialization locale. */ 14 | public static Locale SERIALIZATION_LOCALE = Locale.US; 15 | 16 | /** 17 | * The Interface AnnotationSummary. 18 | * 19 | * @author lukacu 20 | * @param 21 | * the generic type 22 | */ 23 | public static interface AnnotationSummary { 24 | 25 | /** 26 | * Adds the. 27 | * 28 | * @param annotation 29 | * the annotation 30 | */ 31 | public void add(T annotation); 32 | 33 | /** 34 | * Gets the. 35 | * 36 | * @return the t 37 | */ 38 | public T get(); 39 | 40 | } 41 | 42 | /** 43 | * The Enum AnnotationType. 44 | * 45 | * @author lukacu 46 | */ 47 | public enum AnnotationType { 48 | /** The point. */ 49 | POINT, 50 | /** The label. */ 51 | LABEL, 52 | /** The rectangle. */ 53 | RECTANGLE, 54 | /** The polygon. */ 55 | POLYGON, 56 | /** The rectangles. */ 57 | RECTANGLES 58 | } 59 | 60 | /** The Constant aliases. */ 61 | private static final Object[][] aliases = { 62 | { "point", PointAnnotation.class, AnnotationType.POINT }, 63 | { "label", LabelAnnotation.class, AnnotationType.LABEL }, 64 | { "rect", RectangleAnnotation.class, AnnotationType.RECTANGLE }, 65 | { "polygon", PolygonAnnotation.class, AnnotationType.POLYGON }, }; 66 | 67 | /** 68 | * Alias to class. 69 | * 70 | * @param alias 71 | * the alias 72 | * @return the class 73 | */ 74 | @SuppressWarnings("unchecked") 75 | public static Class aliasToClass(String alias) { 76 | 77 | for (int i = 0; i < aliases.length; i++) { 78 | if (((String) aliases[i][0]).compareToIgnoreCase(alias) == 0) { 79 | return (Class) (aliases[i][1]); 80 | } 81 | } 82 | return null; 83 | } 84 | 85 | /** 86 | * Class to alias. 87 | * 88 | * @param cls 89 | * the cls 90 | * @return the string 91 | */ 92 | public static String classToAlias(Class cls) { 93 | 94 | for (int i = 0; i < aliases.length; i++) { 95 | if (aliases[i][1].equals(cls)) { 96 | return (String) (aliases[i][0]); 97 | } 98 | } 99 | return null; 100 | } 101 | 102 | /** 103 | * Class to enum. 104 | * 105 | * @param cls 106 | * the cls 107 | * @return the annotation type 108 | */ 109 | public static AnnotationType classToEnum(Class cls) { 110 | 111 | for (int i = 0; i < aliases.length; i++) { 112 | if (aliases[i][1].equals(cls)) { 113 | return (AnnotationType) (aliases[i][2]); 114 | } 115 | } 116 | return null; 117 | } 118 | 119 | /** 120 | * Alias to enum. 121 | * 122 | * @param alias 123 | * the alias 124 | * @return the annotation type 125 | */ 126 | public static AnnotationType aliasToEnum(String alias) { 127 | 128 | alias = alias.toLowerCase(); 129 | 130 | for (int i = 0; i < aliases.length; i++) { 131 | if (aliases[i][0].equals(alias)) { 132 | return (AnnotationType) (aliases[i][2]); 133 | } 134 | } 135 | return null; 136 | } 137 | 138 | /** 139 | * Enum to class. 140 | * 141 | * @param type 142 | * the type 143 | * @return the class 144 | */ 145 | @SuppressWarnings("unchecked") 146 | public static Class enumToClass(AnnotationType type) { 147 | 148 | for (int i = 0; i < aliases.length; i++) { 149 | if (aliases[i][2].equals(type)) { 150 | return (Class) (aliases[i][1]); 151 | } 152 | } 153 | return null; 154 | } 155 | 156 | /** 157 | * Enum to alias. 158 | * 159 | * @param type 160 | * the type 161 | * @return the string 162 | */ 163 | public static String enumToAlias(AnnotationType type) { 164 | 165 | for (int i = 0; i < aliases.length; i++) { 166 | if (aliases[i][2].equals(type)) { 167 | return (String) (aliases[i][0]); 168 | } 169 | } 170 | return null; 171 | } 172 | 173 | /** 174 | * Object to alias. 175 | * 176 | * @param a 177 | * the a 178 | * @return the string 179 | */ 180 | public static String objectToAlias(Annotation a) { 181 | 182 | Class acls = a.getClass(); 183 | 184 | for (int i = 0; i < aliases.length; i++) { 185 | if (aliases[i][1].equals(acls)) { 186 | return (String) (aliases[i][0]); 187 | } 188 | } 189 | return null; 190 | } 191 | 192 | /** 193 | * Gets the empty. 194 | * 195 | * @param type 196 | * the type 197 | * @return the empty 198 | */ 199 | public static Annotation getEmpty(AnnotationType type) { 200 | 201 | Class cl = enumToClass(type); 202 | 203 | if (cl == null) 204 | return null; 205 | 206 | try { 207 | return (Annotation) cl.newInstance(); 208 | } catch (InstantiationException e) { 209 | } catch (IllegalAccessException e) { 210 | } 211 | 212 | return null; 213 | } 214 | 215 | /** 216 | * Instantiates a new annotation. 217 | */ 218 | public Annotation() { 219 | this.reset(); 220 | } 221 | 222 | /** 223 | * Instantiates a new annotation. 224 | * 225 | * @param data 226 | * the data 227 | * @throws ParseException 228 | * the parse exception 229 | */ 230 | public Annotation(String data) throws ParseException { 231 | this.unpack(data); 232 | } 233 | 234 | /** 235 | * Reset. 236 | */ 237 | public abstract void reset(); 238 | 239 | /** 240 | * Pack. 241 | * 242 | * @return the string 243 | */ 244 | public abstract String pack(); 245 | 246 | /** 247 | * Unpack. 248 | * 249 | * @param data 250 | * the data 251 | * @throws ParseException 252 | * the parse exception 253 | */ 254 | public abstract void unpack(String data) throws ParseException; 255 | 256 | /* 257 | * (non-Javadoc) 258 | * 259 | * @see java.lang.Object#clone() 260 | */ 261 | public abstract Annotation clone(); 262 | 263 | /** 264 | * Validate. 265 | * 266 | * @param a 267 | * the a 268 | * @return true, if successful 269 | */ 270 | public abstract boolean validate(Annotation a); 271 | 272 | /** 273 | * Gets the type. 274 | * 275 | * @return the type 276 | */ 277 | public abstract AnnotationType getType(); 278 | 279 | /** 280 | * Can interpolate. 281 | * 282 | * @return true, if successful 283 | */ 284 | public boolean canInterpolate() { 285 | return false; 286 | } 287 | 288 | /** 289 | * Scale. 290 | * 291 | * @param scale 292 | * the scale 293 | * @return the annotation 294 | * @throws UnsupportedOperationException 295 | * the unsupported operation exception 296 | */ 297 | public Annotation scale(float scale) throws UnsupportedOperationException { 298 | throw new UnsupportedOperationException( 299 | "Scaling not supported for type " + getType()); 300 | } 301 | 302 | /** 303 | * Convert. 304 | * 305 | * @param a 306 | * annotation object 307 | * @return the annotation 308 | * @throws UnsupportedOperationException 309 | * the unsupported operation exception 310 | */ 311 | public Annotation convert(Annotation a) 312 | throws UnsupportedOperationException { 313 | throw new UnsupportedOperationException( 314 | "Conversion not supported for type " + getType()); 315 | } 316 | 317 | /** 318 | * Checks if is null. 319 | * 320 | * @return true, if is null 321 | */ 322 | public abstract boolean isNull(); 323 | 324 | } 325 | -------------------------------------------------------------------------------- /src/si/vicos/annotations/editor/RectangleAnnotationEditor.java: -------------------------------------------------------------------------------- 1 | package si.vicos.annotations.editor; 2 | 3 | import java.awt.Color; 4 | import java.awt.Cursor; 5 | import java.awt.Graphics2D; 6 | import java.awt.Point; 7 | import java.awt.Rectangle; 8 | import java.util.Vector; 9 | 10 | import javax.swing.JComponent; 11 | 12 | import org.coffeeshop.swing.figure.FigurePanel; 13 | 14 | import si.vicos.annotations.Annotation; 15 | import si.vicos.annotations.RectangleAnnotation; 16 | import si.vicos.annotations.editor.AnnotatedImageFigure.AnnotationPeer; 17 | 18 | /** 19 | * The Class RectangleAnnotationEditor. 20 | */ 21 | public class RectangleAnnotationEditor extends AnnotationEditor { 22 | 23 | /** The Constant FACTORY. */ 24 | public static final AnnotationEditorFactory FACTORY = new AnnotationEditorFactory() { 25 | 26 | @Override 27 | public String getName() { 28 | return "Rectangle"; 29 | } 30 | 31 | @Override 32 | public RectangleAnnotationEditor getEditor(AnnotationPeer peer, 33 | Color color) { 34 | return new RectangleAnnotationEditor(peer, color); 35 | } 36 | 37 | }; 38 | 39 | /** The points. */ 40 | private Vector points = new Vector(); 41 | 42 | /** The shape. */ 43 | private Rectangle shape; 44 | 45 | /** The working shape. */ 46 | private Rectangle workingShape = null; 47 | 48 | /** 49 | * Instantiates a new rectangle annotation editor. 50 | * 51 | * @param peer 52 | * the peer 53 | * @param color 54 | * the color 55 | */ 56 | public RectangleAnnotationEditor(AnnotationPeer peer, Color color) { 57 | super(peer, color); 58 | } 59 | 60 | /* 61 | * (non-Javadoc) 62 | * 63 | * @see si.vicos.annotations.editor.AnnotationViewer#getComponent() 64 | */ 65 | @Override 66 | public JComponent getComponent() { 67 | return null; 68 | } 69 | 70 | /* 71 | * (non-Javadoc) 72 | * 73 | * @see 74 | * si.vicos.annotations.editor.AnnotationViewer#paint(java.awt.Graphics2D) 75 | */ 76 | @Override 77 | public void paint(Graphics2D g) { 78 | 79 | if (shape == null) 80 | return; 81 | 82 | g.setColor(color); 83 | g.setStroke(isSelected() ? selectedStroke : normalStroke); 84 | g.draw(shape); 85 | 86 | if (isSelected() && workingShape != null) { 87 | g.setColor(Color.BLACK); 88 | g.setStroke(selectedStroke); 89 | g.setXORMode(Color.WHITE); 90 | g.draw(workingShape); 91 | } 92 | 93 | } 94 | 95 | /* 96 | * (non-Javadoc) 97 | * 98 | * @see si.vicos.annotations.editor.AnnotationEditor#resetInput() 99 | */ 100 | @Override 101 | public void resetInput() { 102 | 103 | workingShape = null; 104 | points.clear(); 105 | notifyRepaint(); 106 | 107 | } 108 | 109 | /* 110 | * (non-Javadoc) 111 | * 112 | * @see 113 | * si.vicos.annotations.editor.AnnotationEditor#onMove(org.coffeeshop.swing 114 | * .figure.FigurePanel, java.awt.Point, java.awt.Point, boolean, int) 115 | */ 116 | public Cursor onMove(FigurePanel source, Point from, Point to, 117 | boolean drag, int modifiers) { 118 | RectangleAnnotation annotation = getAnnotation(); 119 | 120 | if (annotation == null) 121 | return null; 122 | 123 | if (drag) { 124 | 125 | if (points.size() == 2) { 126 | int x = to.x - from.x; 127 | int y = to.y - from.y; 128 | points.elementAt(0).x += x; 129 | points.elementAt(0).y += y; 130 | points.elementAt(1).x += x; 131 | points.elementAt(1).y += y; 132 | 133 | Point p1 = points.elementAt(0); 134 | Point p2 = points.elementAt(1); 135 | workingShape = new Rectangle(p1.x, p1.y, p2.x - p1.x, p2.y 136 | - p1.y); 137 | 138 | notifyRepaint(); 139 | return dragCursor; 140 | } 141 | 142 | if (points.size() == 0) { 143 | 144 | if (!annotation.isNull() && shape.contains(from) && from == to) { 145 | points.add(new Point((int) annotation.getX(), 146 | (int) annotation.getY())); 147 | points.add(new Point((int) annotation.getX() 148 | + (int) annotation.getWidth(), (int) annotation 149 | .getY() + (int) annotation.getHeight())); 150 | notifyRepaint(); 151 | return dragCursor; 152 | } 153 | return defaultCursor; 154 | } 155 | 156 | return pointCursor; 157 | 158 | } else { 159 | 160 | if (points.size() == 0) { 161 | if (!annotation.isNull() && shape.contains(to)) 162 | return dragPossibleCursor; 163 | return defaultCursor; 164 | } 165 | 166 | Point p = points.elementAt(0); 167 | int x1 = Math.min(p.x, to.x); 168 | int y1 = Math.min(p.y, to.y); 169 | int x2 = Math.max(p.x, to.x); 170 | int y2 = Math.max(p.y, to.y); 171 | workingShape = new Rectangle(x1, y1, x2 - x1, y2 - y1); 172 | notifyRepaint(); 173 | return pointCursor; 174 | } 175 | } 176 | 177 | /* 178 | * (non-Javadoc) 179 | * 180 | * @see 181 | * si.vicos.annotations.editor.AnnotationEditor#onClick(org.coffeeshop.swing 182 | * .figure.FigurePanel, java.awt.Point, int, int) 183 | */ 184 | public void onClick(FigurePanel source, Point position, int clicks, 185 | int modifiers) { 186 | RectangleAnnotation annotation = getAnnotation(); 187 | 188 | if (annotation == null) 189 | return; 190 | 191 | if (points.size() < 2 && position != null) 192 | points.add(position); 193 | 194 | if (points.size() == 2) { 195 | Point p1 = points.elementAt(0); 196 | Point p2 = points.elementAt(1); 197 | annotation = new RectangleAnnotation(p1.x, p1.y, p2.x - p1.x, p2.y 198 | - p1.y); 199 | peer.setAnnotation(annotation); 200 | 201 | updateGraphics(); 202 | resetInput(); 203 | } 204 | 205 | } 206 | 207 | /* 208 | * (non-Javadoc) 209 | * 210 | * @see si.vicos.annotations.editor.AnnotationViewer#updateGraphics() 211 | */ 212 | @Override 213 | public void updateGraphics() { 214 | RectangleAnnotation annotation = getAnnotation(); 215 | 216 | if (annotation == null) 217 | return; 218 | 219 | shape = new Rectangle((int) annotation.getX(), (int) annotation.getY(), 220 | (int) annotation.getWidth(), (int) annotation.getHeight()); 221 | } 222 | 223 | /* 224 | * (non-Javadoc) 225 | * 226 | * @see si.vicos.annotations.editor.AnnotationEditor#reset() 227 | */ 228 | @Override 229 | public void reset() { 230 | 231 | peer.setAnnotation(new RectangleAnnotation()); 232 | resetInput(); 233 | } 234 | 235 | /* 236 | * (non-Javadoc) 237 | * 238 | * @see 239 | * si.vicos.annotations.editor.AnnotationViewer#getToolTip(org.coffeeshop 240 | * .swing.figure.FigurePanel, java.awt.Point) 241 | */ 242 | @Override 243 | public String getToolTip(FigurePanel source, Point position) { 244 | RectangleAnnotation annotation = getAnnotation(); 245 | 246 | if (annotation != null && shape.contains(position)) 247 | return String.format("(%.1f, %.1f) [%.1f, %.1f]", 248 | annotation.getX(), annotation.getY(), 249 | annotation.getWidth(), annotation.getHeight()); 250 | return null; 251 | } 252 | 253 | /** 254 | * Gets the annotation. 255 | * 256 | * @return the annotation 257 | */ 258 | private RectangleAnnotation getAnnotation() { 259 | 260 | Annotation a = peer.getAnnotation(); 261 | 262 | if (a == null || !(a instanceof RectangleAnnotation)) 263 | return new RectangleAnnotation(); 264 | 265 | return (RectangleAnnotation) a; 266 | } 267 | 268 | /* 269 | * (non-Javadoc) 270 | * 271 | * @see si.vicos.annotations.editor.AnnotationEditor#getCurrent() 272 | */ 273 | @Override 274 | public Annotation getCurrent() { 275 | if (points.size() == 2) { 276 | Point p1 = points.elementAt(0); 277 | Point p2 = points.elementAt(1); 278 | return new RectangleAnnotation(p1.x, p1.y, p2.x - p1.x, p2.y - p1.y); 279 | } 280 | 281 | return getAnnotation(); 282 | } 283 | 284 | } -------------------------------------------------------------------------------- /src/si/vicos/annotations/editor/tracking/UndoableAnnotatedSequence.java: -------------------------------------------------------------------------------- 1 | package si.vicos.annotations.editor.tracking; 2 | 3 | import java.io.File; 4 | import java.io.IOException; 5 | import java.util.Collection; 6 | 7 | import javax.swing.event.UndoableEditEvent; 8 | import javax.swing.event.UndoableEditListener; 9 | import javax.swing.undo.CannotRedoException; 10 | import javax.swing.undo.CannotUndoException; 11 | import javax.swing.undo.UndoableEdit; 12 | 13 | import org.coffeeshop.ReferenceCollection; 14 | 15 | /** 16 | * The Class UndoableAnnotatedSequence. 17 | */ 18 | public class UndoableAnnotatedSequence extends EditableAnnotatedSequence { 19 | 20 | /** 21 | * The Class AnnotationEdit. 22 | */ 23 | private class AnnotationEdit implements UndoableEdit { 24 | 25 | /** The edits. */ 26 | private Collection edits; 27 | 28 | /** The redo. */ 29 | private boolean redo = false; 30 | 31 | /** The state. */ 32 | private Object state; 33 | 34 | /** 35 | * Instantiates a new annotation edit. 36 | * 37 | * @param edits 38 | * the edits 39 | * @param state 40 | * the state 41 | */ 42 | public AnnotationEdit(Collection edits, Object state) { 43 | 44 | this.edits = edits; 45 | this.state = state; 46 | } 47 | 48 | /* 49 | * (non-Javadoc) 50 | * 51 | * @see 52 | * javax.swing.undo.UndoableEdit#addEdit(javax.swing.undo.UndoableEdit) 53 | */ 54 | @Override 55 | public boolean addEdit(UndoableEdit anEdit) { 56 | return false; 57 | } 58 | 59 | /* 60 | * (non-Javadoc) 61 | * 62 | * @see 63 | * javax.swing.undo.UndoableEdit#replaceEdit(javax.swing.undo.UndoableEdit 64 | * ) 65 | */ 66 | @Override 67 | public boolean replaceEdit(UndoableEdit anEdit) { 68 | return false; 69 | } 70 | 71 | /* 72 | * (non-Javadoc) 73 | * 74 | * @see javax.swing.undo.UndoableEdit#die() 75 | */ 76 | @Override 77 | public void die() { 78 | 79 | } 80 | 81 | /* 82 | * (non-Javadoc) 83 | * 84 | * @see javax.swing.undo.UndoableEdit#undo() 85 | */ 86 | @Override 87 | public void undo() throws CannotUndoException { 88 | 89 | if (redo) 90 | throw new CannotUndoException(); 91 | 92 | state = updateState(state); 93 | 94 | edits = editInternal(edits); 95 | 96 | redo = true; 97 | 98 | } 99 | 100 | /* 101 | * (non-Javadoc) 102 | * 103 | * @see javax.swing.undo.UndoableEdit#canUndo() 104 | */ 105 | @Override 106 | public boolean canUndo() { 107 | 108 | return !redo; 109 | 110 | } 111 | 112 | /* 113 | * (non-Javadoc) 114 | * 115 | * @see javax.swing.undo.UndoableEdit#redo() 116 | */ 117 | @Override 118 | public void redo() throws CannotRedoException { 119 | 120 | if (!redo) 121 | throw new CannotRedoException(); 122 | 123 | state = updateState(state); 124 | 125 | edits = editInternal(edits); 126 | 127 | redo = false; 128 | 129 | } 130 | 131 | /* 132 | * (non-Javadoc) 133 | * 134 | * @see javax.swing.undo.UndoableEdit#canRedo() 135 | */ 136 | @Override 137 | public boolean canRedo() { 138 | return redo; 139 | } 140 | 141 | /* 142 | * (non-Javadoc) 143 | * 144 | * @see javax.swing.undo.UndoableEdit#isSignificant() 145 | */ 146 | @Override 147 | public boolean isSignificant() { 148 | return true; 149 | } 150 | 151 | /* 152 | * (non-Javadoc) 153 | * 154 | * @see javax.swing.undo.UndoableEdit#getPresentationName() 155 | */ 156 | @Override 157 | public String getPresentationName() { 158 | return "Annotation edit"; 159 | } 160 | 161 | /* 162 | * (non-Javadoc) 163 | * 164 | * @see javax.swing.undo.UndoableEdit#getUndoPresentationName() 165 | */ 166 | @Override 167 | public String getUndoPresentationName() { 168 | return "Undo annotation edit"; 169 | } 170 | 171 | /* 172 | * (non-Javadoc) 173 | * 174 | * @see javax.swing.undo.UndoableEdit#getRedoPresentationName() 175 | */ 176 | @Override 177 | public String getRedoPresentationName() { 178 | return "Redo annotation edit"; 179 | }; 180 | 181 | } 182 | 183 | /** The edit listeners. */ 184 | private ReferenceCollection editListeners = new ReferenceCollection(); 185 | 186 | /** The saved state. */ 187 | private Object savedState = new Object(); 188 | 189 | /** The current state. */ 190 | private Object currentState = savedState; 191 | 192 | /** 193 | * Instantiates a new undoable annotated sequence. 194 | * 195 | * @param file 196 | * the file 197 | * @throws IOException 198 | * Signals that an I/O exception has occurred. 199 | */ 200 | public UndoableAnnotatedSequence(File file) throws IOException { 201 | super(file); 202 | } 203 | 204 | /** 205 | * Instantiates a new undoable annotated sequence. 206 | * 207 | * @param files 208 | * the files 209 | * @throws IOException 210 | * Signals that an I/O exception has occurred. 211 | */ 212 | public UndoableAnnotatedSequence(File[] files) throws IOException { 213 | super(files); 214 | } 215 | 216 | /** 217 | * Update state. 218 | * 219 | * @param state 220 | * the state 221 | * @return the object 222 | */ 223 | private Object updateState(Object state) { 224 | 225 | Object tmp = currentState; 226 | 227 | currentState = state; 228 | 229 | return tmp; 230 | 231 | } 232 | 233 | /** 234 | * Edits the internal. 235 | * 236 | * @param edits 237 | * the edits 238 | * @return the collection 239 | */ 240 | private Collection editInternal( 241 | Collection edits) { 242 | 243 | return super.edit(edits); 244 | 245 | } 246 | 247 | /* 248 | * (non-Javadoc) 249 | * 250 | * @see 251 | * si.vicos.annotations.editor.tracking.EditableAnnotatedSequence#edit(java 252 | * .util.Collection) 253 | */ 254 | @Override 255 | public synchronized Collection edit( 256 | Collection edits) { 257 | 258 | Object state = updateState(new Object()); 259 | Collection reverse = editInternal(edits); 260 | 261 | if (reverse.isEmpty()) { 262 | state = updateState(state); 263 | return null; 264 | } 265 | 266 | AnnotationEdit edit = new AnnotationEdit(reverse, state); 267 | 268 | notifyUndoableEdit(edit); 269 | 270 | return edit.edits; 271 | } 272 | 273 | /** 274 | * Notify undoable edit. 275 | * 276 | * @param edit 277 | * the edit 278 | */ 279 | private void notifyUndoableEdit(AnnotationEdit edit) { 280 | 281 | UndoableEditEvent e = new UndoableEditEvent(this, edit); 282 | 283 | for (UndoableEditListener listener : editListeners) 284 | listener.undoableEditHappened(e); 285 | 286 | } 287 | 288 | /** 289 | * Adds the edit listener. 290 | * 291 | * @param listener 292 | * the listener 293 | */ 294 | public void addEditListener(UndoableEditListener listener) { 295 | 296 | editListeners.add(listener); 297 | 298 | } 299 | 300 | /** 301 | * Removes the edit listener. 302 | * 303 | * @param listener 304 | * the listener 305 | */ 306 | public void removeEditListener(UndoableEditListener listener) { 307 | 308 | editListeners.remove(listener); 309 | 310 | } 311 | 312 | /* 313 | * (non-Javadoc) 314 | * 315 | * @see 316 | * si.vicos.annotations.editor.tracking.EditableAnnotatedSequence#write( 317 | * java.io.File) 318 | */ 319 | @Override 320 | public void write(File directory) throws IOException { 321 | 322 | super.write(directory); 323 | 324 | savedState = currentState; 325 | 326 | } 327 | 328 | /* 329 | * (non-Javadoc) 330 | * 331 | * @see 332 | * si.vicos.annotations.editor.tracking.EditableAnnotatedSequence#isModified 333 | * () 334 | */ 335 | @Override 336 | public boolean isModified() { 337 | 338 | return savedState != currentState; 339 | 340 | } 341 | 342 | } 343 | -------------------------------------------------------------------------------- /src/si/vicos/annotations/tracking/Trajectory.java: -------------------------------------------------------------------------------- 1 | package si.vicos.annotations.tracking; 2 | 3 | import java.io.BufferedReader; 4 | import java.io.File; 5 | import java.io.FileInputStream; 6 | import java.io.IOException; 7 | import java.io.InputStream; 8 | import java.io.InputStreamReader; 9 | import java.io.OutputStream; 10 | import java.io.PrintWriter; 11 | import java.text.ParseException; 12 | import java.util.Collection; 13 | import java.util.Iterator; 14 | import java.util.List; 15 | import java.util.ListIterator; 16 | import java.util.Vector; 17 | 18 | import si.vicos.annotations.Annotation; 19 | import si.vicos.annotations.PolygonAnnotation; 20 | import si.vicos.annotations.RectangleAnnotation; 21 | 22 | /** 23 | * The Class Trajectory. 24 | */ 25 | public class Trajectory implements List, AnnotationList { 26 | 27 | /** The data. */ 28 | private Vector data = new Vector(); 29 | 30 | /** 31 | * Write trajectory. 32 | * 33 | * @param out 34 | * the out 35 | */ 36 | public void writeTrajectory(OutputStream out) { 37 | 38 | PrintWriter writer = new PrintWriter(out); 39 | 40 | for (Annotation a : data) { 41 | writer.print(a.pack()); 42 | writer.print("\n"); 43 | } 44 | 45 | writer.flush(); 46 | 47 | } 48 | 49 | /** 50 | * Read regions. 51 | * 52 | * @param in 53 | * the in 54 | * @throws IOException 55 | * Signals that an I/O exception has occurred. 56 | * @throws ParseException 57 | * the parse exception 58 | */ 59 | private void readRegions(InputStream in) throws IOException, ParseException { 60 | 61 | BufferedReader reader = new BufferedReader(new InputStreamReader(in)); 62 | 63 | int lineno = 0; 64 | 65 | while (true) { 66 | 67 | String line = reader.readLine(); 68 | lineno++; 69 | 70 | if (line == null) 71 | break; 72 | 73 | Annotation region = null; 74 | 75 | try { 76 | 77 | region = parseRegion(line); 78 | 79 | add(region); 80 | 81 | } catch (ParseException e) { 82 | throw new ParseException(e.getMessage(), lineno); 83 | } 84 | 85 | } 86 | 87 | } 88 | 89 | /** 90 | * Instantiates a new trajectory. 91 | */ 92 | public Trajectory() { 93 | 94 | } 95 | 96 | /** 97 | * Instantiates a new trajectory. 98 | * 99 | * @param source 100 | * the source 101 | * @throws IOException 102 | * Signals that an I/O exception has occurred. 103 | */ 104 | public Trajectory(InputStream source) throws IOException { 105 | 106 | try { 107 | 108 | readRegions(source); 109 | 110 | } catch (ParseException pe) { 111 | 112 | throw new IOException(pe); 113 | } 114 | 115 | } 116 | 117 | /** 118 | * Instantiates a new trajectory. 119 | * 120 | * @param source 121 | * the source 122 | * @throws IOException 123 | * Signals that an I/O exception has occurred. 124 | */ 125 | public Trajectory(File source) throws IOException { 126 | try { 127 | 128 | readRegions(new FileInputStream(source)); 129 | 130 | } catch (ParseException pe) { 131 | 132 | throw new IOException(pe); 133 | } 134 | 135 | } 136 | 137 | /* 138 | * (non-Javadoc) 139 | * 140 | * @see java.util.List#add(java.lang.Object) 141 | */ 142 | @Override 143 | public boolean add(Annotation e) { 144 | return data.add(e); 145 | } 146 | 147 | /* 148 | * (non-Javadoc) 149 | * 150 | * @see java.util.List#add(int, java.lang.Object) 151 | */ 152 | @Override 153 | public void add(int index, Annotation element) { 154 | data.add(index, element); 155 | } 156 | 157 | /* 158 | * (non-Javadoc) 159 | * 160 | * @see java.util.List#addAll(java.util.Collection) 161 | */ 162 | @Override 163 | public boolean addAll(Collection c) { 164 | return data.addAll(c); 165 | } 166 | 167 | /* 168 | * (non-Javadoc) 169 | * 170 | * @see java.util.List#addAll(int, java.util.Collection) 171 | */ 172 | @Override 173 | public boolean addAll(int index, Collection c) { 174 | return data.addAll(index, c); 175 | } 176 | 177 | /* 178 | * (non-Javadoc) 179 | * 180 | * @see java.util.List#clear() 181 | */ 182 | @Override 183 | public void clear() { 184 | data.clear(); 185 | } 186 | 187 | /* 188 | * (non-Javadoc) 189 | * 190 | * @see java.util.List#contains(java.lang.Object) 191 | */ 192 | @Override 193 | public boolean contains(Object o) { 194 | return data.contains(o); 195 | } 196 | 197 | /* 198 | * (non-Javadoc) 199 | * 200 | * @see java.util.List#containsAll(java.util.Collection) 201 | */ 202 | @Override 203 | public boolean containsAll(Collection c) { 204 | return data.containsAll(c); 205 | } 206 | 207 | /* 208 | * (non-Javadoc) 209 | * 210 | * @see java.util.List#get(int) 211 | */ 212 | @Override 213 | public Annotation get(int index) { 214 | return data.get(index); 215 | } 216 | 217 | /* 218 | * (non-Javadoc) 219 | * 220 | * @see java.util.List#indexOf(java.lang.Object) 221 | */ 222 | @Override 223 | public int indexOf(Object o) { 224 | return data.indexOf(o); 225 | } 226 | 227 | /* 228 | * (non-Javadoc) 229 | * 230 | * @see java.util.List#isEmpty() 231 | */ 232 | @Override 233 | public boolean isEmpty() { 234 | return data.isEmpty(); 235 | } 236 | 237 | /* 238 | * (non-Javadoc) 239 | * 240 | * @see java.util.List#iterator() 241 | */ 242 | @Override 243 | public Iterator iterator() { 244 | return data.iterator(); 245 | } 246 | 247 | /* 248 | * (non-Javadoc) 249 | * 250 | * @see java.util.List#lastIndexOf(java.lang.Object) 251 | */ 252 | @Override 253 | public int lastIndexOf(Object o) { 254 | return data.lastIndexOf(o); 255 | } 256 | 257 | /* 258 | * (non-Javadoc) 259 | * 260 | * @see java.util.List#listIterator() 261 | */ 262 | @Override 263 | public ListIterator listIterator() { 264 | return data.listIterator(); 265 | } 266 | 267 | /* 268 | * (non-Javadoc) 269 | * 270 | * @see java.util.List#listIterator(int) 271 | */ 272 | @Override 273 | public ListIterator listIterator(int index) { 274 | return data.listIterator(index); 275 | } 276 | 277 | /* 278 | * (non-Javadoc) 279 | * 280 | * @see java.util.List#remove(java.lang.Object) 281 | */ 282 | @Override 283 | public boolean remove(Object o) { 284 | return data.remove(o); 285 | } 286 | 287 | /* 288 | * (non-Javadoc) 289 | * 290 | * @see java.util.List#remove(int) 291 | */ 292 | @Override 293 | public Annotation remove(int index) { 294 | return data.remove(index); 295 | } 296 | 297 | /* 298 | * (non-Javadoc) 299 | * 300 | * @see java.util.List#removeAll(java.util.Collection) 301 | */ 302 | @Override 303 | public boolean removeAll(Collection c) { 304 | return data.removeAll(c); 305 | } 306 | 307 | /* 308 | * (non-Javadoc) 309 | * 310 | * @see java.util.List#retainAll(java.util.Collection) 311 | */ 312 | @Override 313 | public boolean retainAll(Collection c) { 314 | return data.retainAll(c); 315 | } 316 | 317 | /* 318 | * (non-Javadoc) 319 | * 320 | * @see java.util.List#set(int, java.lang.Object) 321 | */ 322 | @Override 323 | public Annotation set(int index, Annotation element) { 324 | return data.set(index, element); 325 | } 326 | 327 | /* 328 | * (non-Javadoc) 329 | * 330 | * @see java.util.List#size() 331 | */ 332 | @Override 333 | public int size() { 334 | return data.size(); 335 | } 336 | 337 | /* 338 | * (non-Javadoc) 339 | * 340 | * @see java.util.List#subList(int, int) 341 | */ 342 | @Override 343 | public List subList(int fromIndex, int toIndex) { 344 | return data.subList(fromIndex, toIndex); 345 | } 346 | 347 | /* 348 | * (non-Javadoc) 349 | * 350 | * @see java.util.List#toArray() 351 | */ 352 | @Override 353 | public Object[] toArray() { 354 | return data.toArray(); 355 | } 356 | 357 | /* 358 | * (non-Javadoc) 359 | * 360 | * @see java.util.List#toArray(T[]) 361 | */ 362 | @Override 363 | public T[] toArray(T[] a) { 364 | return data.toArray(a); 365 | } 366 | 367 | /** 368 | * Parses the region. 369 | * 370 | * @param data 371 | * the data 372 | * @return the annotation 373 | * @throws ParseException 374 | * the parse exception 375 | */ 376 | public static Annotation parseRegion(String data) throws ParseException { 377 | 378 | if (data.isEmpty()) { 379 | 380 | return new CodeAnnotation(0); 381 | 382 | } else { 383 | 384 | String[] tokens = data.split(","); 385 | 386 | if (tokens.length == 1) { 387 | 388 | return new CodeAnnotation(Integer.parseInt(tokens[0])); 389 | 390 | } else if (tokens.length == 4) { 391 | 392 | return new RectangleAnnotation(Float.parseFloat(tokens[0]), 393 | Float.parseFloat(tokens[1]), 394 | Float.parseFloat(tokens[2]), 395 | Float.parseFloat(tokens[3])); 396 | 397 | } else if (tokens.length > 5 && tokens.length % 2 == 0) { 398 | 399 | return new PolygonAnnotation(data); 400 | 401 | } 402 | 403 | throw new ParseException("Unknown region format", -1); 404 | } 405 | 406 | } 407 | 408 | } 409 | -------------------------------------------------------------------------------- /src/si/vicos/annotations/editor/Annotator.java: -------------------------------------------------------------------------------- 1 | package si.vicos.annotations.editor; 2 | 3 | import java.awt.Dimension; 4 | import java.awt.FontFormatException; 5 | import java.awt.event.ActionEvent; 6 | import java.io.File; 7 | import java.io.IOException; 8 | import java.util.Arrays; 9 | 10 | import javax.swing.Action; 11 | import javax.swing.JFileChooser; 12 | import javax.swing.UIManager; 13 | import javax.swing.filechooser.FileFilter; 14 | 15 | import org.coffeeshop.application.Application; 16 | import org.coffeeshop.arguments.ArgumentsException; 17 | import org.coffeeshop.io.TempDirectory; 18 | import org.coffeeshop.log.Logger; 19 | import org.coffeeshop.settings.SettingsNotFoundException; 20 | import org.coffeeshop.swing.ImageStore; 21 | import org.coffeeshop.swing.PersistentWindow; 22 | import org.coffeeshop.swing.RecentDocuments; 23 | import org.coffeeshop.swing.ToolTipAction; 24 | import org.coffeeshop.swing.viewers.FigureViewer; 25 | 26 | import si.vicos.annotations.editor.tracking.EditableAnnotatedSequence; 27 | import si.vicos.annotations.editor.tracking.TrackingEditor; 28 | import si.vicos.annotations.editor.tracking.UndoableAnnotatedSequence; 29 | 30 | /** 31 | * The Class Annotator. 32 | */ 33 | public class Annotator extends Application { 34 | 35 | /** The Constant Authors. */ 36 | public static final String[][] Authors = { { "Luka \u010Cehovin", 37 | "http://www.vicos.si/lukacu" } }; 38 | 39 | /** The Constant HOMEPAGE. */ 40 | public static final String HOMEPAGE = "http://www.vicos.si/lukacu/aibu/"; 41 | 42 | /** The Constant VERSION. */ 43 | public static final String VERSION = "0.3 (alpha)"; 44 | 45 | /** The Constant FILE_FILTER. */ 46 | public static final FileFilter FILE_FILTER = new FileFilter() { 47 | 48 | @Override 49 | public String getDescription() { 50 | return "Annotations"; 51 | } 52 | 53 | @Override 54 | public boolean accept(File arg0) { 55 | 56 | if (arg0.isDirectory()) 57 | return true; 58 | 59 | String name = arg0.getName(); 60 | 61 | return name.endsWith(".avt") 62 | || name.compareTo("groundtruth.txt") == 0; 63 | 64 | } 65 | }; 66 | 67 | /** The cache. */ 68 | private static ImageCache cache; 69 | 70 | /** The history. */ 71 | private static RecentDocuments history = null; 72 | 73 | /** 74 | * Instantiates a new annotator application object. 75 | * 76 | * @param args 77 | * the arguments array 78 | * 79 | * @throws ArgumentsException 80 | * if the arguments are not recognized 81 | */ 82 | public Annotator(String[] args) throws ArgumentsException { 83 | 84 | super("Aibu", args); 85 | 86 | } 87 | 88 | /** 89 | * The main method - starting point of the application. 90 | * 91 | * @param args 92 | * the arguments array 93 | * 94 | * @throws Exception 95 | * miscellaneous booting exception 96 | */ 97 | public static void main(String[] args) throws Exception { 98 | 99 | System.setProperty("coffeeshop.application.logging", "10"); 100 | 101 | new Annotator(args); 102 | 103 | Application.getApplicationLogger().addOutputStream(System.out); 104 | Application.getApplicationLogger().enableAllChannels(); 105 | 106 | ImageStore.registerAnchorClass(Annotator.class); 107 | ImageStore.registerAnchorClass(FigureViewer.class); 108 | 109 | try { 110 | ImageStore.registerImageProvider(new FontImageProvider("fa:", 111 | Annotator.class, "fontawesome.ttf", new Dimension(16, 16))); 112 | ImageStore.registerImageProvider(new FontImageProvider( 113 | "annotation:", Annotator.class, "annotation.ttf", 114 | new Dimension(16, 16))); 115 | } catch (FontFormatException e1) { 116 | Application.getApplicationLogger().report(e1); 117 | } 118 | 119 | ImageStore.registerImageProvider(new ImageStore.AliasImageProvider( 120 | Annotator.class.getResourceAsStream("fontawesome.ini"))); 121 | 122 | ImageStore.registerImageProvider(new ImageStore.AliasImageProvider( 123 | Annotator.class.getResourceAsStream("annotation.ini"))); 124 | 125 | ImageStore.registerImageProvider(new ImageStore.AliasImageProvider( 126 | Annotator.class.getResourceAsStream("general.ini"))); 127 | 128 | ImageStore.registerImageProvider(new ImageStore.AliasImageProvider( 129 | Annotator.class.getResourceAsStream("aibu.ini"))); 130 | 131 | System.setProperty("splash.background", "#FFFFFF"); 132 | 133 | Thread.setDefaultUncaughtExceptionHandler(new ApplicationExceptionHandler()); 134 | System.setProperty("sun.awt.exception.handler", 135 | ApplicationExceptionHandler.class.getName()); 136 | 137 | history = new RecentDocuments(getApplicationSettings(), "history."); 138 | 139 | cache = new ImageCache(1024 * 1024 * 10, 1024 * 1024 * 30, 140 | new TempDirectory(Application.getApplication().getUnixName())); 141 | 142 | AnnotatorSplash splash = new AnnotatorSplash( 143 | getApplication().getName(), ImageStore.getImage("splash.png"), 144 | history); 145 | 146 | PersistentWindow.setExitOnAllClosed(true); 147 | 148 | UIManager.setLookAndFeel(UIManager.getSystemLookAndFeelClassName()); 149 | 150 | Object choice = splash.block(); 151 | 152 | if (choice == null) 153 | System.exit(0); 154 | 155 | if (choice instanceof File) { 156 | File file = (File) choice; 157 | 158 | openFile(file); 159 | 160 | } else if (choice instanceof EditableAnnotatedSequence) { 161 | 162 | TrackingEditor te = new TrackingEditor( 163 | (EditableAnnotatedSequence) choice); 164 | te.setIconImage(ImageStore.getImage("icon")); 165 | te.setVisible(true); 166 | 167 | } 168 | } 169 | 170 | /* 171 | * (non-Javadoc) 172 | * 173 | * @see 174 | * org.coffeeshop.application.Application#defineDefaults(org.coffeeshop. 175 | * application.Application.SettingsSetter) 176 | */ 177 | @Override 178 | protected void defineDefaults(SettingsSetter setter) { 179 | 180 | } 181 | 182 | /* 183 | * (non-Javadoc) 184 | * 185 | * @see org.coffeeshop.application.Application#getLongDescription() 186 | */ 187 | @Override 188 | public String getLongDescription() { 189 | return "Aibu - a generic editor for image database annotation"; 190 | } 191 | 192 | /* 193 | * (non-Javadoc) 194 | * 195 | * @see org.coffeeshop.application.Application#getShortDescription() 196 | */ 197 | @Override 198 | public String getShortDescription() { 199 | return "Aibu Annotation Editor"; 200 | } 201 | 202 | /** 203 | * Adds the recent document. 204 | * 205 | * @param file 206 | * the file 207 | */ 208 | public static void addRecentDocument(File file) { 209 | 210 | history.addDocument(file); 211 | 212 | } 213 | 214 | /** 215 | * Gets the image cache. 216 | * 217 | * @return the image cache 218 | */ 219 | public static ImageCache getImageCache() { 220 | return cache; 221 | } 222 | 223 | /** 224 | * Open file. 225 | * 226 | * @param file 227 | * the file 228 | * @throws IOException 229 | * Signals that an I/O exception has occurred. 230 | */ 231 | public static void openFile(File file) throws IOException { 232 | history.addDocument(file); 233 | 234 | Application.getApplicationLogger().report( 235 | Logger.APPLICATION_INTERNAL_1, "Loading %s", file); 236 | 237 | Annotator.getApplicationSettings().setString("browse.path", 238 | file.toString()); 239 | 240 | EditableAnnotatedSequence writer = new UndoableAnnotatedSequence(file); 241 | 242 | TrackingEditor te = new TrackingEditor(writer); 243 | te.setVisible(true); 244 | 245 | } 246 | 247 | /** The Constant newSequenceAction. */ 248 | public static final Action newSequenceAction = new ToolTipAction( 249 | "New sequence", "new") { 250 | 251 | private static final long serialVersionUID = 1L; 252 | 253 | @Override 254 | public void actionPerformed(ActionEvent arg0) { 255 | 256 | String path = "."; 257 | try { 258 | path = Application.getApplicationSettings().getString( 259 | "browse.path"); 260 | } catch (SettingsNotFoundException ex) { 261 | } 262 | 263 | JFileChooser chooser = new JFileChooser(path); 264 | 265 | chooser.setMultiSelectionEnabled(true); 266 | chooser.setFileSelectionMode(JFileChooser.FILES_ONLY); 267 | 268 | chooser.setFileFilter(new FileFilter() { 269 | 270 | @Override 271 | public String getDescription() { 272 | return "Image files"; 273 | } 274 | 275 | @Override 276 | public boolean accept(File arg0) { 277 | 278 | if (arg0.isDirectory()) 279 | return true; 280 | 281 | String name = arg0.getName(); 282 | 283 | return name.endsWith(".jpg") || name.endsWith(".png"); 284 | 285 | } 286 | }); 287 | 288 | chooser.showOpenDialog(null); 289 | 290 | if (chooser.getSelectedFile() != null) { 291 | 292 | Application.getApplicationSettings().setString("browse.path", 293 | chooser.getSelectedFile().toString()); 294 | 295 | EditableAnnotatedSequence writer; 296 | try { 297 | File[] files = chooser.getSelectedFiles(); 298 | Arrays.sort(files); 299 | 300 | writer = new EditableAnnotatedSequence(files); 301 | 302 | // closeWithResult(writer); 303 | } catch (IOException e) { 304 | Application.getApplicationLogger().report(e); 305 | } 306 | 307 | } 308 | 309 | } 310 | }; 311 | 312 | } 313 | -------------------------------------------------------------------------------- /src/si/vicos/annotations/tracking/Tags.java: -------------------------------------------------------------------------------- 1 | package si.vicos.annotations.tracking; 2 | 3 | import java.io.BufferedReader; 4 | import java.io.File; 5 | import java.io.FileInputStream; 6 | import java.io.FileNotFoundException; 7 | import java.io.IOException; 8 | import java.io.InputStream; 9 | import java.io.InputStreamReader; 10 | import java.io.OutputStream; 11 | import java.io.PrintWriter; 12 | import java.util.Collection; 13 | import java.util.Collections; 14 | import java.util.HashSet; 15 | import java.util.Set; 16 | import java.util.Vector; 17 | 18 | import org.coffeeshop.string.StringUtils; 19 | 20 | /** 21 | * The Class Tags. 22 | */ 23 | public class Tags { 24 | 25 | /** The dictionary. */ 26 | private Set dictionary = new HashSet(); 27 | 28 | /** The tags. */ 29 | private Vector> tags = new Vector>(); 30 | 31 | /** The length. */ 32 | int length = 0; 33 | 34 | /** 35 | * Instantiates a new tags. 36 | */ 37 | public Tags() { 38 | 39 | } 40 | 41 | /** 42 | * Instantiates a new tags. 43 | * 44 | * @param length 45 | * the length 46 | */ 47 | public Tags(int length) { 48 | 49 | this.length = length; 50 | 51 | } 52 | 53 | /* 54 | * (non-Javadoc) 55 | * 56 | * @see java.lang.Object#clone() 57 | */ 58 | public Tags clone() { 59 | 60 | Tags clone = new Tags(); 61 | 62 | for (Set frame : tags) { 63 | if (frame != null) { 64 | clone.tags.add(new HashSet(frame)); 65 | } else { 66 | clone.tags.add(null); 67 | } 68 | } 69 | 70 | return clone; 71 | 72 | } 73 | 74 | /** 75 | * Read. 76 | * 77 | * @param tag 78 | * the tag 79 | * @param source 80 | * the source 81 | */ 82 | public void read(String tag, File source) { 83 | 84 | try { 85 | read(tag, new FileInputStream(source)); 86 | } catch (FileNotFoundException e) { 87 | } 88 | 89 | } 90 | 91 | /** 92 | * Read. 93 | * 94 | * @param tag 95 | * the tag 96 | * @param ins 97 | * the ins 98 | */ 99 | public void read(String tag, InputStream ins) { 100 | 101 | try { 102 | 103 | BufferedReader reader = new BufferedReader(new InputStreamReader( 104 | ins)); 105 | 106 | int frame = 0; 107 | 108 | while (true) { 109 | 110 | String line = reader.readLine(); 111 | 112 | if (line == null) 113 | break; 114 | 115 | if (line.compareTo("1") == 0) 116 | addTag(frame, tag); 117 | 118 | frame++; 119 | } 120 | 121 | reader.close(); 122 | 123 | } catch (IOException e) { 124 | } 125 | 126 | dictionary.addAll(getTags()); 127 | 128 | } 129 | 130 | /** 131 | * Write. 132 | * 133 | * @param tag 134 | * the tag 135 | * @param outs 136 | * the outs 137 | * @throws IOException 138 | * Signals that an I/O exception has occurred. 139 | */ 140 | public void write(String tag, OutputStream outs) throws IOException { 141 | 142 | PrintWriter out = new PrintWriter(outs); 143 | 144 | for (Set ls : tags) { 145 | 146 | if (ls == null || !ls.contains(tag)) 147 | out.println("0"); 148 | else 149 | out.println("1"); 150 | 151 | } 152 | 153 | if (tags.size() < length) 154 | for (int i = 0; i < length - tags.size(); i++) 155 | out.println("0"); 156 | 157 | out.flush(); 158 | 159 | } 160 | 161 | /** 162 | * Append tags. 163 | * 164 | * @param tags 165 | * the tags 166 | */ 167 | public void appendTags(Set tags) { 168 | 169 | if (tags == null) 170 | tags = new HashSet(); 171 | 172 | addTags(size(), tags); 173 | 174 | } 175 | 176 | /** 177 | * Adds the tag. 178 | * 179 | * @param index 180 | * the index 181 | * @param tag 182 | * the tag 183 | */ 184 | public void addTag(int index, String tag) { 185 | 186 | if (index < 0 || tag == null || StringUtils.empty(tag)) 187 | return; 188 | 189 | if (index >= tags.size()) { 190 | 191 | for (int i = tags.size(); i <= index; i++) 192 | tags.add(null); 193 | 194 | } 195 | 196 | Set frame = this.tags.get(index); 197 | 198 | if (frame == null) { 199 | frame = new HashSet(); 200 | this.tags.set(index, frame); 201 | } 202 | 203 | frame.add(tag); 204 | dictionary.add(tag); 205 | 206 | length = Math.max(length, tags.size()); 207 | 208 | } 209 | 210 | /** 211 | * Adds the tags. 212 | * 213 | * @param index 214 | * the index 215 | * @param tags 216 | * the tags 217 | */ 218 | public void addTags(int index, Set tags) { 219 | 220 | if (index < 0 || tags == null) 221 | return; 222 | 223 | if (index >= this.tags.size()) { 224 | 225 | for (int i = this.tags.size(); i <= index; i++) 226 | this.tags.add(null); 227 | 228 | } 229 | 230 | Set tmp = this.tags.get(index); 231 | 232 | if (tmp == null) { 233 | tmp = new HashSet(); 234 | this.tags.set(index, tmp); 235 | } 236 | 237 | tmp.addAll(tags); 238 | dictionary.addAll(tags); 239 | 240 | length = Math.max(length, tags.size()); 241 | 242 | } 243 | 244 | /** 245 | * Removes the tag. 246 | * 247 | * @param index 248 | * the index 249 | * @param tag 250 | * the tag 251 | */ 252 | public void removeTag(int index, String tag) { 253 | 254 | if (index < 0 || tag == null || StringUtils.empty(tag)) 255 | return; 256 | 257 | if (index >= tags.size()) 258 | return; 259 | 260 | Set labels = this.tags.get(index); 261 | 262 | if (labels == null) 263 | return; 264 | 265 | labels.remove(tag); 266 | 267 | } 268 | 269 | /** 270 | * Removes the tags. 271 | * 272 | * @param index 273 | * the index 274 | * @param tags 275 | * the tags 276 | */ 277 | public void removeTags(int index, Set tags) { 278 | 279 | if (index < 0 || tags == null) 280 | return; 281 | 282 | if (index >= this.tags.size()) 283 | return; 284 | 285 | Set tmp = this.tags.get(index); 286 | 287 | if (tmp == null) 288 | return; 289 | 290 | tmp.removeAll(tags); 291 | 292 | } 293 | 294 | /** 295 | * Removes all tags for a given frame. 296 | * 297 | * @param index 298 | * the index 299 | */ 300 | public void removeTags(int index) { 301 | 302 | if (index < 0 || tags == null) 303 | return; 304 | 305 | if (index >= this.tags.size()) 306 | return; 307 | 308 | Set tmp = this.tags.get(index); 309 | 310 | if (tmp == null) 311 | return; 312 | 313 | tmp.clear(); 314 | 315 | } 316 | 317 | /** 318 | * Size. 319 | * 320 | * @return the int 321 | */ 322 | public int size() { 323 | 324 | return tags.size(); 325 | 326 | } 327 | 328 | /** 329 | * Checks for tag. 330 | * 331 | * @param index 332 | * the index 333 | * @param tag 334 | * the tag 335 | * @return true, if successful 336 | */ 337 | public boolean hasTag(int index, String tag) { 338 | 339 | if (index < 0 || index >= size() || tag == null 340 | || StringUtils.empty(tag)) 341 | return false; 342 | 343 | Set labels = this.tags.get(index); 344 | 345 | if (labels == null) 346 | return false; 347 | 348 | return labels.contains(tag); 349 | } 350 | 351 | /** 352 | * Gets the tags. 353 | * 354 | * @return the tags 355 | */ 356 | public Set getTags() { 357 | 358 | HashSet lbl = new HashSet(); 359 | 360 | for (Set frame : tags) { 361 | if (frame != null) 362 | lbl.addAll(frame); 363 | } 364 | 365 | return lbl; 366 | 367 | } 368 | 369 | /** 370 | * Count tag. 371 | * 372 | * @param tag 373 | * the tag 374 | * @return the int 375 | */ 376 | public int countTag(String tag) { 377 | 378 | int count = 0; 379 | 380 | for (int i = 0; i < Math.min(tags.size(), size()); i++) { 381 | 382 | if (tags.get(i) != null && tags.get(i).contains(tag)) 383 | count++; 384 | 385 | } 386 | 387 | return count; 388 | } 389 | 390 | /** 391 | * Gets the tag. 392 | * 393 | * @param frame 394 | * the frame 395 | * @return the tag 396 | */ 397 | public Set getTag(int frame) { 398 | 399 | if (tags == null || tags.isEmpty()) 400 | return new HashSet(); 401 | 402 | if (frame < 0 || frame >= tags.size()) 403 | return new HashSet(); 404 | 405 | Set l = tags.get(frame); 406 | 407 | if (l == null) 408 | return new HashSet(); 409 | else 410 | return new HashSet(l); 411 | 412 | } 413 | 414 | /** 415 | * Find. 416 | * 417 | * @param tag 418 | * the tag 419 | * @return the collection 420 | */ 421 | public Collection find(String tag) { 422 | 423 | Vector frames = new Vector(); 424 | 425 | for (int i = 0; i < Math.min(tags.size(), size()); i++) { 426 | 427 | if (tags.get(i) != null && tags.get(i).contains(tag)) 428 | frames.add(i); 429 | 430 | } 431 | 432 | return frames; 433 | } 434 | 435 | /** 436 | * Checks if is empty. 437 | * 438 | * @return true, if is empty 439 | */ 440 | public boolean isEmpty() { 441 | 442 | for (Set l : tags) { 443 | if (l == null || l.isEmpty()) 444 | continue; 445 | return false; 446 | } 447 | return true; 448 | } 449 | 450 | /** 451 | * Returns a set of all tag names that were ever used in this tags object 452 | * even if they are no longer present. 453 | * 454 | * @return an unmodifiable set of all tag names 455 | */ 456 | public Set getDictionary() { 457 | 458 | return Collections.unmodifiableSet(dictionary); 459 | 460 | } 461 | 462 | } 463 | -------------------------------------------------------------------------------- /src/si/vicos/annotations/editor/tracking/TagPlot.java: -------------------------------------------------------------------------------- 1 | package si.vicos.annotations.editor.tracking; 2 | 3 | import java.awt.Color; 4 | import java.awt.Graphics2D; 5 | import java.awt.Shape; 6 | import java.awt.geom.AffineTransform; 7 | import java.awt.geom.Ellipse2D; 8 | import java.awt.geom.Path2D; 9 | import java.awt.geom.Point2D; 10 | import java.awt.geom.Rectangle2D; 11 | import java.util.Collection; 12 | import java.util.Set; 13 | import java.util.Vector; 14 | 15 | import org.coffeeshop.awt.Colors; 16 | import org.coffeeshop.swing.figure.PlotObject; 17 | 18 | import si.vicos.annotations.tracking.AnnotatedSequence; 19 | import si.vicos.annotations.tracking.AnnotatedSequenceListener; 20 | import si.vicos.annotations.tracking.Interval; 21 | 22 | /** 23 | * The Class TagPlot. 24 | */ 25 | public abstract class TagPlot implements PlotObject { 26 | 27 | /** 28 | * The Enum MarkerShape. 29 | */ 30 | public static enum MarkerShape { 31 | /** The circle. */ 32 | CIRCLE, /** The cross. */ 33 | CROSS, /** The rectangle. */ 34 | RECTANGLE, /** The diamond. */ 35 | DIAMOND, /** The triangle. */ 36 | TRIANGLE 37 | } 38 | 39 | /** 40 | * Gets the marker. 41 | * 42 | * @param shape 43 | * the shape 44 | * @param size 45 | * the size 46 | * @return the marker 47 | */ 48 | public static Shape getMarker(MarkerShape shape, int size) { 49 | 50 | Path2D s; 51 | switch (shape) { 52 | case CIRCLE: 53 | return new Ellipse2D.Float(-size, -size, 2 * size, 2 * size); 54 | case CROSS: 55 | s = new Path2D.Float(); 56 | s.moveTo(-size, 0); 57 | s.lineTo(size, 0); 58 | s.moveTo(0, -size); 59 | s.lineTo(0, size); 60 | return s; 61 | case RECTANGLE: 62 | s = new Path2D.Float(); 63 | s.moveTo(-size, -size); 64 | s.lineTo(size, -size); 65 | s.lineTo(size, size); 66 | s.lineTo(-size, size); 67 | s.closePath(); 68 | return s; 69 | case DIAMOND: 70 | s = new Path2D.Float(); 71 | s.moveTo(-size, 0); 72 | s.lineTo(0, -size); 73 | s.lineTo(size, 0); 74 | s.lineTo(0, -size); 75 | s.closePath(); 76 | return s; 77 | case TRIANGLE: 78 | s = new Path2D.Float(); 79 | s.moveTo(-size, size / 2); 80 | s.lineTo(0, -size / 2); 81 | s.lineTo(size, size / 2); 82 | s.closePath(); 83 | return s; 84 | default: 85 | return null; 86 | } 87 | 88 | } 89 | 90 | /** 91 | * The Class SingleTagPlot. 92 | */ 93 | public static class SingleTagPlot extends TagPlot { 94 | 95 | /** The intervals. */ 96 | private Collection intervals; 97 | 98 | /** The label. */ 99 | private String label; 100 | 101 | /** The color. */ 102 | private Color color; 103 | 104 | /** The offset. */ 105 | private float offset; 106 | 107 | /** The single frame. */ 108 | private Shape singleFrame = getMarker(MarkerShape.CIRCLE, 5); 109 | 110 | /** 111 | * Instantiates a new single tag plot. 112 | * 113 | * @param annotations 114 | * the annotations 115 | * @param label 116 | * the label 117 | * @param color 118 | * the color 119 | */ 120 | public SingleTagPlot(AnnotatedSequence annotations, String label, 121 | Color color) { 122 | this(annotations, label, color, 0.5f); 123 | } 124 | 125 | /** 126 | * Instantiates a new single tag plot. 127 | * 128 | * @param annotations 129 | * the annotations 130 | * @param label 131 | * the label 132 | * @param color 133 | * the color 134 | * @param offset 135 | * the offset 136 | */ 137 | public SingleTagPlot(AnnotatedSequence annotations, String label, 138 | Color color, float offset) { 139 | 140 | super(); 141 | 142 | this.name = "Tag '" + label + "' for " + annotations.getName(); 143 | this.label = label; 144 | this.color = color; 145 | 146 | this.offset = Math.min(1, Math.max(0, offset)); 147 | 148 | update(annotations); 149 | 150 | annotations 151 | .addAnnotatedSequenceListener(new AnnotatedSequenceListener() { 152 | 153 | public void metadataChanged(AnnotatedSequence sequence, 154 | Set keys) { 155 | 156 | } 157 | 158 | public void intervalChanged(AnnotatedSequence sequence, 159 | Interval interval) { 160 | 161 | update(sequence); 162 | 163 | } 164 | 165 | }); 166 | 167 | } 168 | 169 | /* 170 | * (non-Javadoc) 171 | * 172 | * @see 173 | * org.coffeeshop.swing.figure.PlotObject#paint(java.awt.Graphics2D, 174 | * float, java.awt.geom.AffineTransform) 175 | */ 176 | @Override 177 | public void paint(Graphics2D g, float scale, 178 | AffineTransform pretransform) { 179 | 180 | g.setColor(color); 181 | 182 | paintIntervals(intervals, g, pretransform, offset); 183 | 184 | } 185 | 186 | /** 187 | * Update. 188 | * 189 | * @param annotations 190 | * the annotations 191 | */ 192 | public void update(AnnotatedSequence annotations) { 193 | 194 | bounds.setRect(0, 0, annotations.size(), 1); 195 | 196 | intervals = extractIntervals(annotations.findTag(label)); 197 | 198 | } 199 | 200 | /** 201 | * Sets the color. 202 | * 203 | * @param color 204 | * the new color 205 | */ 206 | public void setColor(Color color) { 207 | this.color = color; 208 | } 209 | 210 | /** 211 | * Gets the color. 212 | * 213 | * @return the color 214 | */ 215 | public Color getColor() { 216 | return color; 217 | } 218 | 219 | /* 220 | * (non-Javadoc) 221 | * 222 | * @see 223 | * org.coffeeshop.swing.figure.PlotObject#getToolTip(java.awt.geom.Point2D 224 | * ) 225 | */ 226 | @Override 227 | public String getToolTip(Point2D point) { 228 | 229 | return null; 230 | } 231 | 232 | /** 233 | * Paint intervals. 234 | * 235 | * @param intervals 236 | * the intervals 237 | * @param g 238 | * the g 239 | * @param pretransform 240 | * the pretransform 241 | * @param offset 242 | * the offset 243 | */ 244 | protected void paintIntervals(Collection intervals, 245 | Graphics2D g, AffineTransform pretransform, double offset) { 246 | 247 | Color border = getColor(); 248 | 249 | Color fill = Colors.changeBrightness(border, 0.9); 250 | 251 | for (Interval i : intervals) { 252 | Point2D p1 = new Point2D.Double(), p2 = new Point2D.Double(); 253 | if (i.isEmpty()) { 254 | p1.setLocation(i.getBegin(), offset); 255 | // p2.setLocation(i.getEnd()+ 1, offset - 0.1); 256 | 257 | pretransform.transform(p1, p1); 258 | // pretransform.transform(p2, p2); 259 | g.translate(p1.getX(), p1.getY()); 260 | g.setColor(fill); 261 | g.fill(singleFrame); 262 | g.setColor(border); 263 | g.draw(singleFrame); 264 | g.translate(-p1.getX(), -p1.getY()); 265 | } else { 266 | p1.setLocation(i.getBegin(), offset + 0.1); 267 | p2.setLocation(i.getEnd(), offset - 0.1); 268 | 269 | pretransform.transform(p1, p1); 270 | pretransform.transform(p2, p2); 271 | g.setColor(fill); 272 | g.fillRect((int) p1.getX(), (int) p1.getY(), 273 | (int) (p2.getX() - p1.getX()), 274 | (int) (p2.getY() - p1.getY())); 275 | g.setColor(border); 276 | g.drawRect((int) p1.getX(), (int) p1.getY(), 277 | (int) (p2.getX() - p1.getX()), 278 | (int) (p2.getY() - p1.getY())); 279 | } 280 | } 281 | 282 | } 283 | 284 | /** 285 | * Extract intervals. 286 | * 287 | * @param frames 288 | * the frames 289 | * @return the collection 290 | */ 291 | private Collection extractIntervals(Collection frames) { 292 | 293 | Vector intervals = new Vector(); 294 | 295 | int previous = Integer.MIN_VALUE; 296 | int begin = -1; 297 | int end = 0; 298 | 299 | for (int i : frames) { 300 | 301 | if (previous == i - 1) { 302 | 303 | end = i; 304 | previous = i; 305 | 306 | } else { 307 | 308 | if (begin != -1) { 309 | intervals.add(new Interval(begin, end)); 310 | } 311 | 312 | begin = i; 313 | end = i; 314 | previous = i; 315 | } 316 | 317 | } 318 | 319 | if (begin != -1) 320 | intervals.add(new Interval(begin, end)); 321 | 322 | return intervals; 323 | 324 | } 325 | 326 | } 327 | 328 | /** 329 | * Paint markers. 330 | * 331 | * @param labels 332 | * the labels 333 | * @param g 334 | * the g 335 | * @param pretransform 336 | * the pretransform 337 | * @param shape 338 | * the shape 339 | * @param offset 340 | * the offset 341 | */ 342 | protected void paintMarkers(Collection labels, Graphics2D g, 343 | AffineTransform pretransform, Shape shape, double offset) { 344 | 345 | AffineTransform old = g.getTransform(); 346 | 347 | Point2D p = new Point2D.Double(); 348 | for (Integer i : labels) { 349 | p.setLocation(i, offset); 350 | pretransform.transform(p, p); 351 | g.translate(p.getX(), p.getY()); 352 | g.draw(shape); 353 | g.setTransform(old); 354 | } 355 | 356 | } 357 | 358 | /** The name. */ 359 | protected String name; 360 | 361 | /** The bounds. */ 362 | protected Rectangle2D bounds = new Rectangle2D.Double(); 363 | 364 | /* 365 | * (non-Javadoc) 366 | * 367 | * @see org.coffeeshop.swing.figure.PlotObject#getName() 368 | */ 369 | @Override 370 | public String getName() { 371 | return name; 372 | } 373 | 374 | /* 375 | * (non-Javadoc) 376 | * 377 | * @see org.coffeeshop.swing.figure.PlotObject#getBounds() 378 | */ 379 | @Override 380 | public Rectangle2D getBounds() { 381 | return bounds; 382 | }; 383 | 384 | } 385 | -------------------------------------------------------------------------------- /src/si/vicos/annotations/RectangleAnnotation.java: -------------------------------------------------------------------------------- 1 | package si.vicos.annotations; 2 | 3 | import java.awt.geom.Point2D; 4 | import java.text.ParseException; 5 | import java.util.ArrayList; 6 | import java.util.List; 7 | import java.util.NoSuchElementException; 8 | import java.util.StringTokenizer; 9 | 10 | import org.coffeeshop.string.StringUtils; 11 | 12 | /** 13 | * The Class RectangleAnnotation. 14 | */ 15 | public class RectangleAnnotation extends ShapeAnnotation { 16 | 17 | /** 18 | * The Class AverageRectangle. 19 | */ 20 | public static class AverageRectangle implements 21 | AnnotationSummary { 22 | 23 | /** The h. */ 24 | private double x, y, w, h; 25 | 26 | /** The count. */ 27 | private int count = 0; 28 | 29 | /* 30 | * (non-Javadoc) 31 | * 32 | * @see si.vicos.annotations.Annotation.AnnotationSummary#add(si.vicos. 33 | * annotations.Annotation) 34 | */ 35 | @Override 36 | public void add(RectangleAnnotation annotation) { 37 | 38 | x += annotation.x; 39 | y += annotation.x; 40 | w += annotation.w; 41 | h += annotation.h; 42 | count++; 43 | 44 | } 45 | 46 | /* 47 | * (non-Javadoc) 48 | * 49 | * @see si.vicos.annotations.Annotation.AnnotationSummary#get() 50 | */ 51 | @Override 52 | public RectangleAnnotation get() { 53 | 54 | if (count == 0) 55 | return null; 56 | 57 | return new RectangleAnnotation(x / count, y / count, w / count, h 58 | / count); 59 | } 60 | 61 | } 62 | 63 | /** The h. */ 64 | private double x, y, w, h; 65 | 66 | /** 67 | * Instantiates a new rectangle annotation. 68 | */ 69 | public RectangleAnnotation() { 70 | this(0, 0, -1, -1); 71 | } 72 | 73 | /** 74 | * Instantiates a new rectangle annotation. 75 | * 76 | * @param box 77 | * the box 78 | */ 79 | public RectangleAnnotation(RectangleAnnotation box) { 80 | this(box.x, box.y, box.w, box.h); 81 | } 82 | 83 | /** 84 | * Instantiates a new rectangle annotation. 85 | * 86 | * @param x 87 | * the x 88 | * @param y 89 | * the y 90 | * @param w 91 | * the w 92 | * @param h 93 | * the h 94 | */ 95 | public RectangleAnnotation(double x, double y, double w, double h) { 96 | set(x, y, w, h); 97 | } 98 | 99 | /* 100 | * (non-Javadoc) 101 | * 102 | * @see si.vicos.annotations.Annotation#pack() 103 | */ 104 | @Override 105 | public String pack() { 106 | return isNull() ? "" : String.format(SERIALIZATION_LOCALE, 107 | "%f,%f,%f,%f", x, y, w, h); 108 | } 109 | 110 | /* 111 | * (non-Javadoc) 112 | * 113 | * @see si.vicos.annotations.Annotation#reset() 114 | */ 115 | @Override 116 | public void reset() { 117 | x = 0; 118 | y = 0; 119 | w = -1; 120 | h = -1; 121 | } 122 | 123 | /* 124 | * (non-Javadoc) 125 | * 126 | * @see si.vicos.annotations.Annotation#unpack(java.lang.String) 127 | */ 128 | @Override 129 | public void unpack(String data) throws ParseException { 130 | if (StringUtils.empty(data)) 131 | return; 132 | 133 | StringTokenizer tokens = new StringTokenizer(data, ","); 134 | 135 | try { 136 | double x = Double.parseDouble(tokens.nextToken()); 137 | double y = Double.parseDouble(tokens.nextToken()); 138 | double w = Double.parseDouble(tokens.nextToken()); 139 | double h = Double.parseDouble(tokens.nextToken()); 140 | 141 | if (w < 0) { 142 | x += w; 143 | w = -w; 144 | } 145 | 146 | if (h < 0) { 147 | y += h; 148 | h = -h; 149 | } 150 | 151 | set(x, y, w, h); 152 | } catch (NoSuchElementException e) { 153 | throw new ParseException("Unable to parse", -1); 154 | } catch (NumberFormatException e) { 155 | throw new ParseException("Unable to parse", -1); 156 | } 157 | } 158 | 159 | /* 160 | * (non-Javadoc) 161 | * 162 | * @see si.vicos.annotations.Annotation#clone() 163 | */ 164 | @Override 165 | public Annotation clone() { 166 | return new RectangleAnnotation(x, y, w, h); 167 | } 168 | 169 | /** 170 | * Sets the. 171 | * 172 | * @param x 173 | * the x 174 | * @param y 175 | * the y 176 | * @param w 177 | * the w 178 | * @param h 179 | * the h 180 | */ 181 | private void set(double x, double y, double w, double h) { 182 | 183 | if (w < 0) { 184 | this.x = x + w; 185 | this.w = -w; 186 | } else { 187 | this.x = x; 188 | this.w = w; 189 | } 190 | 191 | if (h < 0) { 192 | this.y = y + h; 193 | this.h = -h; 194 | } else { 195 | this.y = y; 196 | this.h = h; 197 | } 198 | 199 | } 200 | 201 | /** 202 | * Gets the x. 203 | * 204 | * @return the x 205 | */ 206 | public double getX() { 207 | return x; 208 | } 209 | 210 | /** 211 | * Gets the y. 212 | * 213 | * @return the y 214 | */ 215 | public double getY() { 216 | return y; 217 | } 218 | 219 | /** 220 | * Gets the width. 221 | * 222 | * @return the width 223 | */ 224 | public double getWidth() { 225 | return w; 226 | } 227 | 228 | /** 229 | * Gets the height. 230 | * 231 | * @return the height 232 | */ 233 | public double getHeight() { 234 | return h; 235 | } 236 | 237 | /* 238 | * (non-Javadoc) 239 | * 240 | * @see java.lang.Object#toString() 241 | */ 242 | public String toString() { 243 | return String.format("(%d,%d) [%d,%d]", x, y, w, h); 244 | } 245 | 246 | /* 247 | * (non-Javadoc) 248 | * 249 | * @see 250 | * si.vicos.annotations.Annotation#validate(si.vicos.annotations.Annotation) 251 | */ 252 | @Override 253 | public boolean validate(Annotation a) { 254 | return a instanceof RectangleAnnotation; 255 | } 256 | 257 | /* 258 | * (non-Javadoc) 259 | * 260 | * @see si.vicos.annotations.Annotation#getType() 261 | */ 262 | @Override 263 | public AnnotationType getType() { 264 | return AnnotationType.RECTANGLE; 265 | } 266 | 267 | /* 268 | * (non-Javadoc) 269 | * 270 | * @see si.vicos.annotations.Annotation#canInterpolate() 271 | */ 272 | @Override 273 | public boolean canInterpolate() { 274 | return true; 275 | } 276 | 277 | /* 278 | * (non-Javadoc) 279 | * 280 | * @see si.vicos.annotations.Annotation#scale(float) 281 | */ 282 | @Override 283 | public Annotation scale(float scale) throws UnsupportedOperationException { 284 | 285 | float x = (float) this.x * scale; 286 | float y = (float) this.y * scale; 287 | float w = (float) this.w * scale; 288 | float h = (float) this.h * scale; 289 | 290 | return new RectangleAnnotation((int) x, (int) y, (int) w, (int) h); 291 | 292 | } 293 | 294 | /* 295 | * (non-Javadoc) 296 | * 297 | * @see si.vicos.annotations.ShapeAnnotation#getBoundingBox() 298 | */ 299 | @Override 300 | public RectangleAnnotation getBoundingBox() { 301 | return this; 302 | } 303 | 304 | /* 305 | * (non-Javadoc) 306 | * 307 | * @see si.vicos.annotations.Annotation#isNull() 308 | */ 309 | @Override 310 | public boolean isNull() { 311 | return w <= 0 || h <= 0; 312 | } 313 | 314 | /** 315 | * Intersect. 316 | * 317 | * @param b1 318 | * the b1 319 | * @param b2 320 | * the b2 321 | * @return the rectangle annotation 322 | */ 323 | public static RectangleAnnotation intersect(RectangleAnnotation b1, 324 | RectangleAnnotation b2) { 325 | 326 | double x1 = Math.max(b1.x, b2.x); 327 | double y1 = Math.max(b1.y, b2.y); 328 | double x2 = Math.min(b1.x + b1.w, b2.x + b2.w); 329 | double y2 = Math.min(b1.y + b1.h, b2.y + b2.h); 330 | 331 | return new RectangleAnnotation(x1, x2, x2 - x1, y2 - y1); 332 | 333 | } 334 | 335 | /** 336 | * Union. 337 | * 338 | * @param b1 339 | * the b1 340 | * @param b2 341 | * the b2 342 | * @return the rectangle annotation 343 | */ 344 | public static RectangleAnnotation union(RectangleAnnotation b1, 345 | RectangleAnnotation b2) { 346 | 347 | double x1 = Math.min(b1.x, b2.x); 348 | double y1 = Math.min(b1.y, b2.y); 349 | double x2 = Math.max(b1.x + b1.w, b2.x + b2.w); 350 | double y2 = Math.max(b1.y + b1.h, b2.y + b2.h); 351 | 352 | return new RectangleAnnotation(x1, x2, x2 - x1, y2 - y1); 353 | 354 | } 355 | 356 | /** 357 | * Contains. 358 | * 359 | * @param a 360 | * the a 361 | * @return true, if successful 362 | */ 363 | public boolean contains(PointAnnotation a) { 364 | 365 | if (a == null || a.isNull()) 366 | return false; 367 | 368 | if (x > a.getX() || x + w < a.getX()) 369 | return false; 370 | 371 | if (y > a.getY() || y + h < a.getY()) 372 | return false; 373 | 374 | return true; 375 | 376 | } 377 | 378 | /** 379 | * Gets the top. 380 | * 381 | * @return the top 382 | */ 383 | public double getTop() { 384 | return y; 385 | } 386 | 387 | /** 388 | * Gets the left. 389 | * 390 | * @return the left 391 | */ 392 | public double getLeft() { 393 | return x; 394 | } 395 | 396 | /** 397 | * Gets the bottom. 398 | * 399 | * @return the bottom 400 | */ 401 | public double getBottom() { 402 | return y + h; 403 | } 404 | 405 | /** 406 | * Gets the right. 407 | * 408 | * @return the right 409 | */ 410 | public double getRight() { 411 | return x + w; 412 | } 413 | 414 | /** 415 | * Gets the center x. 416 | * 417 | * @return the center x 418 | */ 419 | public double getCenterX() { 420 | return (x + w) / 2; 421 | } 422 | 423 | /** 424 | * Gets the center y. 425 | * 426 | * @return the center y 427 | */ 428 | public double getCenterY() { 429 | return (y + h) / 2; 430 | } 431 | 432 | /* 433 | * (non-Javadoc) 434 | * 435 | * @see si.vicos.annotations.SituatedAnnotation#getCenter() 436 | */ 437 | @Override 438 | public Point2D getCenter() { 439 | return new Point2D.Double(getCenterX(), getCenterY()); 440 | } 441 | 442 | /* 443 | * (non-Javadoc) 444 | * 445 | * @see si.vicos.annotations.ShapeAnnotation#getPolygon() 446 | */ 447 | @Override 448 | public List getPolygon() { 449 | 450 | ArrayList points = new ArrayList(4); 451 | 452 | points.add(new Point2D.Double(getLeft(), getBottom())); 453 | points.add(new Point2D.Double(getRight(), getBottom())); 454 | points.add(new Point2D.Double(getRight(), getTop())); 455 | points.add(new Point2D.Double(getLeft(), getTop())); 456 | 457 | return points; 458 | } 459 | 460 | /* 461 | * (non-Javadoc) 462 | * 463 | * @see 464 | * si.vicos.annotations.Annotation#convert(si.vicos.annotations.Annotation) 465 | */ 466 | @Override 467 | public Annotation convert(Annotation a) 468 | throws UnsupportedOperationException { 469 | 470 | if (a instanceof RectangleAnnotation) 471 | return a; 472 | 473 | if (a instanceof ShapeAnnotation) { 474 | return ((ShapeAnnotation) a).getBoundingBox(); 475 | } 476 | 477 | return super.convert(a); 478 | } 479 | } -------------------------------------------------------------------------------- /src/si/vicos/annotations/editor/RotatedRectangleAnnotationEditor.java: -------------------------------------------------------------------------------- 1 | package si.vicos.annotations.editor; 2 | 3 | import java.awt.Color; 4 | import java.awt.Cursor; 5 | import java.awt.Graphics2D; 6 | import java.awt.Point; 7 | import java.awt.Rectangle; 8 | import java.awt.Shape; 9 | import java.awt.event.MouseEvent; 10 | import java.awt.geom.Point2D; 11 | import java.util.Vector; 12 | 13 | import javax.swing.JComponent; 14 | 15 | import org.coffeeshop.swing.figure.FigurePanel; 16 | 17 | import si.vicos.annotations.Annotation; 18 | import si.vicos.annotations.PolygonAnnotation; 19 | import si.vicos.annotations.RectangleAnnotation; 20 | import si.vicos.annotations.ShapeAnnotation; 21 | import si.vicos.annotations.editor.AnnotatedImageFigure.AnnotationPeer; 22 | import si.vicos.annotations.editor.tracking.PolygonAnnotationRenderer; 23 | 24 | /** 25 | * The Class RotatedRectangleAnnotationEditor. 26 | */ 27 | public class RotatedRectangleAnnotationEditor extends AnnotationEditor { 28 | 29 | /** 30 | * The Enum ManipulationMode. 31 | */ 32 | public enum ManipulationMode { 33 | /** The set. */ 34 | SET, /** The move. */ 35 | MOVE, /** The rotate. */ 36 | ROTATE, /** The scale. */ 37 | SCALE, /** The rotate scale. */ 38 | ROTATE_SCALE 39 | }; 40 | 41 | /** The Constant FACTORY. */ 42 | public static final AnnotationEditorFactory FACTORY = new AnnotationEditorFactory() { 43 | 44 | @Override 45 | public String getName() { 46 | return "Rotated rectangle"; 47 | } 48 | 49 | @Override 50 | public RotatedRectangleAnnotationEditor getEditor(AnnotationPeer peer, 51 | Color color) { 52 | return new RotatedRectangleAnnotationEditor(peer, color); 53 | } 54 | 55 | }; 56 | 57 | /** The points. */ 58 | private Vector points = new Vector(); 59 | 60 | /** The shape. */ 61 | private Shape shape; 62 | 63 | /** The working shape. */ 64 | private Shape workingShape = null; 65 | 66 | /** The mode. */ 67 | private ManipulationMode mode = ManipulationMode.SET; 68 | 69 | // private Point2D edited 70 | 71 | /** 72 | * Instantiates a new rotated rectangle annotation editor. 73 | * 74 | * @param peer 75 | * the peer 76 | * @param color 77 | * the color 78 | */ 79 | public RotatedRectangleAnnotationEditor(AnnotationPeer peer, Color color) { 80 | super(peer, color); 81 | } 82 | 83 | /* 84 | * (non-Javadoc) 85 | * 86 | * @see si.vicos.annotations.editor.AnnotationViewer#getComponent() 87 | */ 88 | @Override 89 | public JComponent getComponent() { 90 | return null; 91 | } 92 | 93 | /* 94 | * (non-Javadoc) 95 | * 96 | * @see 97 | * si.vicos.annotations.editor.AnnotationViewer#paint(java.awt.Graphics2D) 98 | */ 99 | @Override 100 | public void paint(Graphics2D g) { 101 | 102 | if (shape == null) 103 | return; 104 | 105 | g.setColor(color); 106 | g.setStroke(isSelected() ? selectedStroke : normalStroke); 107 | g.draw(shape); 108 | 109 | if (isSelected() && workingShape != null) { 110 | g.setColor(Color.BLACK); 111 | g.setStroke(selectedStroke); 112 | g.setXORMode(Color.WHITE); 113 | g.draw(workingShape); 114 | } 115 | 116 | } 117 | 118 | /* 119 | * (non-Javadoc) 120 | * 121 | * @see si.vicos.annotations.editor.AnnotationEditor#resetInput() 122 | */ 123 | @Override 124 | public void resetInput() { 125 | 126 | workingShape = null; 127 | points.clear(); 128 | mode = ManipulationMode.SET; 129 | notifyRepaint(); 130 | 131 | } 132 | 133 | /* 134 | * (non-Javadoc) 135 | * 136 | * @see 137 | * si.vicos.annotations.editor.AnnotationEditor#onMove(org.coffeeshop.swing 138 | * .figure.FigurePanel, java.awt.Point, java.awt.Point, boolean, int) 139 | */ 140 | public Cursor onMove(FigurePanel source, Point from, Point to, 141 | boolean drag, int modifiers) { 142 | 143 | PolygonAnnotation annotation = getAnnotation(); 144 | 145 | if (annotation == null) 146 | return null; 147 | 148 | if (drag) { 149 | 150 | if (points.size() > 2) { 151 | switch (mode) { 152 | case MOVE: { 153 | int vx = to.x - from.x; 154 | int vy = to.y - from.y; 155 | 156 | for (int i = 0; i < points.size(); i++) { 157 | Point2D p = points.elementAt(i); 158 | points.set( 159 | i, 160 | new Point2D.Double(p.getX() + vx, p.getY() + vy)); 161 | } 162 | 163 | workingShape = points.isEmpty() ? null 164 | : PolygonAnnotationRenderer.pointsToShape(points); 165 | 166 | break; 167 | } 168 | case ROTATE: { 169 | Point2D pivot = annotation.getCenter(); 170 | 171 | double angle = Math.atan2(to.getY() - pivot.getY(), 172 | to.getX() - pivot.getX()) 173 | - Math.atan2(from.getY() - pivot.getY(), 174 | from.getX() - pivot.getX()); 175 | 176 | for (int i = 0; i < points.size(); i++) { 177 | Point2D p = points.get(i); 178 | double x = ((p.getX() - pivot.getX()) * Math.cos(angle)) 179 | - ((p.getY() - pivot.getY()) * Math.sin(angle)) 180 | + pivot.getX(); 181 | double y = ((p.getX() - pivot.getX()) * Math.sin(angle)) 182 | + ((p.getY() - pivot.getY()) * Math.cos(angle)) 183 | + pivot.getY(); 184 | points.set(i, new Point2D.Double(x, y)); 185 | } 186 | 187 | workingShape = points.isEmpty() ? null 188 | : PolygonAnnotationRenderer.pointsToShape(points); 189 | 190 | break; 191 | } 192 | case SCALE: { 193 | Point2D pivot = annotation.getCenter(); 194 | double scale = pivot.distance(to) / pivot.distance(from); 195 | 196 | for (int i = 0; i < points.size(); i++) { 197 | Point2D p = points.get(i); 198 | double x = ((p.getX() - pivot.getX()) * scale) 199 | + pivot.getX(); 200 | double y = ((p.getY() - pivot.getY()) * scale) 201 | + pivot.getY(); 202 | points.set(i, new Point2D.Double(x, y)); 203 | } 204 | 205 | workingShape = points.isEmpty() ? null 206 | : PolygonAnnotationRenderer.pointsToShape(points); 207 | 208 | break; 209 | } 210 | case ROTATE_SCALE: { 211 | Point2D pivot = annotation.getCenter(); 212 | 213 | double scale = pivot.distance(to) / pivot.distance(from); 214 | double angle = Math.atan2(to.getY() - pivot.getY(), 215 | to.getX() - pivot.getX()) 216 | - Math.atan2(from.getY() - pivot.getY(), 217 | from.getX() - pivot.getX()); 218 | 219 | for (int i = 0; i < points.size(); i++) { 220 | Point2D p = points.get(i); 221 | double x = ((p.getX() - pivot.getX()) * Math.cos(angle)) 222 | - ((p.getY() - pivot.getY()) * Math.sin(angle)) 223 | + pivot.getX(); 224 | double y = ((p.getX() - pivot.getX()) * Math.sin(angle)) 225 | + ((p.getY() - pivot.getY()) * Math.cos(angle)) 226 | + pivot.getY(); 227 | x = ((x - pivot.getX()) * scale) + pivot.getX(); 228 | y = ((y - pivot.getY()) * scale) + pivot.getY(); 229 | points.set(i, new Point2D.Double(x, y)); 230 | } 231 | 232 | workingShape = points.isEmpty() ? null 233 | : PolygonAnnotationRenderer.pointsToShape(points); 234 | 235 | break; 236 | } 237 | case SET: { 238 | } 239 | } 240 | 241 | notifyRepaint(); 242 | return dragCursor; 243 | } 244 | 245 | if (points.size() == 0) { 246 | 247 | if (!annotation.isNull() && shape.contains(from) && from == to) { 248 | points.clear(); 249 | points.addAll(annotation.getPolygon()); 250 | 251 | if ((modifiers & MouseEvent.CTRL_DOWN_MASK) != 0) 252 | mode = ManipulationMode.ROTATE_SCALE; 253 | else 254 | mode = ManipulationMode.MOVE; 255 | 256 | notifyRepaint(); 257 | return dragCursor; 258 | } 259 | return defaultCursor; 260 | } 261 | 262 | return pointCursor; 263 | 264 | } else { 265 | 266 | if (points.size() == 0) { 267 | if (!annotation.isNull() && shape.contains(to)) 268 | return dragPossibleCursor; 269 | return defaultCursor; 270 | } 271 | 272 | Point2D p = points.elementAt(0); 273 | int x1 = (int) Math.min(p.getX(), to.x); 274 | int y1 = (int) Math.min(p.getY(), to.y); 275 | int x2 = (int) Math.max(p.getX(), to.x); 276 | int y2 = (int) Math.max(p.getY(), to.y); 277 | workingShape = new Rectangle(x1, y1, x2 - x1, y2 - y1); 278 | notifyRepaint(); 279 | return pointCursor; 280 | 281 | } 282 | } 283 | 284 | /* 285 | * (non-Javadoc) 286 | * 287 | * @see 288 | * si.vicos.annotations.editor.AnnotationEditor#onClick(org.coffeeshop.swing 289 | * .figure.FigurePanel, java.awt.Point, int, int) 290 | */ 291 | public void onClick(FigurePanel source, Point position, int clicks, 292 | int modifiers) { 293 | 294 | PolygonAnnotation annotation = getAnnotation(); 295 | 296 | if (annotation == null) 297 | return; 298 | 299 | if (mode == ManipulationMode.SET) { 300 | 301 | if (points.size() < 2 && position != null) 302 | points.add(position); 303 | 304 | if (points.size() == 2) { 305 | Point2D p1 = points.elementAt(0); 306 | Point2D p2 = points.elementAt(1); 307 | RectangleAnnotation a = new RectangleAnnotation(p1.getX(), 308 | p1.getY(), p2.getX() - p1.getX(), p2.getY() - p1.getY()); 309 | annotation = new PolygonAnnotation(a.getPolygon()); 310 | peer.setAnnotation(annotation); 311 | 312 | updateGraphics(); 313 | resetInput(); 314 | } 315 | 316 | } else { 317 | 318 | if (points.size() > 2) { 319 | annotation = new PolygonAnnotation(points); 320 | peer.setAnnotation(annotation); 321 | updateGraphics(); 322 | resetInput(); 323 | points.clear(); 324 | } 325 | 326 | } 327 | } 328 | 329 | /* 330 | * (non-Javadoc) 331 | * 332 | * @see si.vicos.annotations.editor.AnnotationViewer#updateGraphics() 333 | */ 334 | @Override 335 | public void updateGraphics() { 336 | PolygonAnnotation annotation = getAnnotation(); 337 | 338 | if (annotation == null) 339 | return; 340 | 341 | shape = PolygonAnnotationRenderer.annotationToShape(annotation); 342 | } 343 | 344 | /* 345 | * (non-Javadoc) 346 | * 347 | * @see si.vicos.annotations.editor.AnnotationEditor#reset() 348 | */ 349 | @Override 350 | public void reset() { 351 | 352 | peer.setAnnotation(new PolygonAnnotation()); 353 | resetInput(); 354 | 355 | } 356 | 357 | /* 358 | * (non-Javadoc) 359 | * 360 | * @see 361 | * si.vicos.annotations.editor.AnnotationViewer#getToolTip(org.coffeeshop 362 | * .swing.figure.FigurePanel, java.awt.Point) 363 | */ 364 | @Override 365 | public String getToolTip(FigurePanel source, Point position) { 366 | 367 | PolygonAnnotation annotation = getAnnotation(); 368 | 369 | if (annotation != null && shape.contains(position)) { 370 | Rectangle bounds = shape.getBounds(); 371 | 372 | return String.format("%d points [%.1f, %.1f]", annotation.size(), 373 | bounds.getWidth(), bounds.getHeight()); 374 | } 375 | return null; 376 | } 377 | 378 | /** 379 | * Gets the annotation. 380 | * 381 | * @return the annotation 382 | */ 383 | private PolygonAnnotation getAnnotation() { 384 | 385 | Annotation a = peer.getAnnotation(); 386 | 387 | if (a == null) 388 | return new PolygonAnnotation(); 389 | 390 | if (a instanceof PolygonAnnotation) 391 | return (PolygonAnnotation) a; 392 | 393 | if (a instanceof ShapeAnnotation) { 394 | 395 | return new PolygonAnnotation(((ShapeAnnotation) a).getPolygon()); 396 | 397 | } 398 | 399 | return new PolygonAnnotation(); 400 | } 401 | 402 | /* 403 | * (non-Javadoc) 404 | * 405 | * @see si.vicos.annotations.editor.AnnotationEditor#getCurrent() 406 | */ 407 | @Override 408 | public Annotation getCurrent() { 409 | // TODO Auto-generated method stub 410 | return null; 411 | } 412 | 413 | } --------------------------------------------------------------------------------