001/** 002 * Copyright (c) 2011, The University of Southampton and the individual contributors. 003 * All rights reserved. 004 * 005 * Redistribution and use in source and binary forms, with or without modification, 006 * are permitted provided that the following conditions are met: 007 * 008 * * Redistributions of source code must retain the above copyright notice, 009 * this list of conditions and the following disclaimer. 010 * 011 * * Redistributions in binary form must reproduce the above copyright notice, 012 * this list of conditions and the following disclaimer in the documentation 013 * and/or other materials provided with the distribution. 014 * 015 * * Neither the name of the University of Southampton nor the names of its 016 * contributors may be used to endorse or promote products derived from this 017 * software without specific prior written permission. 018 * 019 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND 020 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED 021 * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE 022 * DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR 023 * ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES 024 * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; 025 * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON 026 * ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT 027 * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS 028 * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 029 */ 030/** 031 * 032 */ 033package org.openimaj.vis.general; 034 035import java.util.ArrayList; 036import java.util.List; 037 038import org.openimaj.image.MBFImage; 039import org.openimaj.image.colour.ColourMap; 040import org.openimaj.image.colour.RGBColour; 041import org.openimaj.image.renderer.MBFImageRenderer; 042import org.openimaj.image.renderer.RenderHints; 043import org.openimaj.image.typography.hershey.HersheyFont; 044import org.openimaj.image.typography.hershey.HersheyFontStyle; 045import org.openimaj.math.geometry.point.Point2d; 046import org.openimaj.math.geometry.shape.Circle; 047import org.openimaj.math.geometry.shape.Rectangle; 048import org.openimaj.vis.general.DotPlotVisualisation.ColouredDot; 049import org.openimaj.vis.general.LabelledPointVisualisation.LabelledDot; 050 051/** 052 * Plots dots with a label. This can be used as a 053 * visualisation in itself or used as an {@link ItemPlotter} in other visualisations. 054 * 055 * @author David Dupplaw (dpd@ecs.soton.ac.uk) 056 * @created 3 Jun 2013 057 */ 058public class LabelledPointVisualisation extends XYPlotVisualisation<LabelledDot> 059 implements ItemPlotter<LabelledDot,Float[],MBFImage> 060{ 061 /** 062 * 063 * @author David Dupplaw (dpd@ecs.soton.ac.uk) 064 * @created 11 Jun 2013 065 */ 066 public static class LabelledDot extends ColouredDot 067 { 068 /** The label */ 069 public String label; 070 071 /** 072 * Create a labelled dot. 073 * @param l The label 074 * @param s The size 075 */ 076 public LabelledDot( final String l, final double s ) 077 { 078 super( s, RGBColour.RED ); 079 this.label = l; 080 } 081 082 /** 083 * Create a labelled dot. 084 * @param l The label 085 * @param s The size 086 * @param colour The colour 087 */ 088 public LabelledDot( final String l, final double s, final Float[] colour ) 089 { 090 super( s, colour ); 091 this.label = l; 092 } 093} 094 095 /** */ 096 private static final long serialVersionUID = 1L; 097 098 /** Whether to avoid drawing text that will overlap other text */ 099 private boolean avoidOverlaps = true; 100 101 /** The bounds of the labels that have already been drawn */ 102 private final List<Rectangle> bounds = new ArrayList<Rectangle>(); 103 104 /** 105 * Default constructor 106 */ 107 public LabelledPointVisualisation() 108 { 109 super( null ); 110 this.setItemPlotter( this ); 111 } 112 113 /** 114 * Constructor that takes the width and height of the visualisation 115 * 116 * @param width The width of the visualisation in pixels 117 * @param height The height of the visualisation in pixels 118 */ 119 public LabelledPointVisualisation( final int width, final int height ) 120 { 121 super( width, height, null ); 122 this.setItemPlotter( this ); 123 } 124 125 /** 126 * {@inheritDoc} 127 * @see org.openimaj.vis.general.ItemPlotter#renderRestarting() 128 */ 129 @Override 130 public void renderRestarting() 131 { 132 this.bounds.clear(); 133 } 134 135 /** 136 * {@inheritDoc} 137 * @see org.openimaj.vis.general.ItemPlotter#plotObject(org.openimaj.image.Image, org.openimaj.vis.general.XYPlotVisualisation.LocatedObject, org.openimaj.vis.general.AxesRenderer2D) 138 */ 139 @Override 140 public void plotObject( final MBFImage visImage, 141 final XYPlotVisualisation.LocatedObject<LabelledDot> object, 142 final AxesRenderer2D<Float[],MBFImage> renderer ) 143 { 144 // Get the position where we're going to place the dot 145 Point2d pos = renderer.calculatePosition( object.x, object.y ); 146 147 final MBFImageRenderer ir = visImage.createRenderer( RenderHints.ANTI_ALIASED ); 148 149 // Draw the dot 150 ir.drawShapeFilled( 151 new Circle( pos, 152 (float)(renderer.scaleDimensions( object.object.size, object.object.size )[0] ) ), 153 object.object.colour ); 154 155 // Get the position where we're going the place the text 156 pos = renderer.calculatePosition( object.x + object.object.size, object.y ); 157 158 // Create the font and font style 159 final HersheyFont f = HersheyFont.TIMES_MEDIUM; 160 final HersheyFontStyle<Float[]> fs = f.createStyle( visImage.createRenderer() ); 161 fs.setFontSize( 14 ); 162 163 // Calculate the bounding box of the text we're going to draw. 164 final Rectangle b = fs.getRenderer( visImage.createRenderer() ).getSize( 165 object.object.label, fs ); 166 167 // Bounding box is 0,0,width,height, so move it into position. 168 b.translate( 0, b.height ); 169 b.translate( pos.getX() + 4, pos.getY() ); 170// System.out.println( b ); 171 172 boolean overlap = false; 173 for( final Rectangle bb : this.bounds ) { 174 if( bb.isOverlapping( b ) ) { 175 overlap = true; break; 176 } 177 } 178 179 this.bounds.add( b ); 180 181 if( !overlap ) 182 ir.drawText( 183 object.object.label, (int)b.x, (int)b.y + (int)b.height, 184 f, 14, object.object.colour ); 185 186 } 187 188 /** 189 * @return the avoidOverlaps 190 */ 191 public boolean isAvoidOverlaps() 192 { 193 return this.avoidOverlaps; 194 } 195 196 /** 197 * @param avoidOverlaps the avoidOverlaps to set 198 */ 199 public void setAvoidOverlaps( final boolean avoidOverlaps ) 200 { 201 this.avoidOverlaps = avoidOverlaps; 202 } 203 204 /** 205 * Main method to demonstrate the vis. 206 * @param args command-line args (not used) 207 */ 208 public static void main( final String[] args ) 209 { 210 final LabelledPointVisualisation dpv = new LabelledPointVisualisation( 1000, 600 ); 211 dpv.getAxesRenderer().setxMajorTickSpacing( 0.2 ); 212 dpv.getAxesRenderer().setxMinorTickSpacing( 0.05 ); 213 dpv.getAxesRenderer().setyMajorTickSpacing( 0.2 ); 214 dpv.getAxesRenderer().setyMinorTickSpacing( 0.05 ); 215 216 for( int i = 0; i < 10; i++ ) 217 { 218 final double v = Math.random()/10d; 219 dpv.addPoint( (Math.random()-0.5)*2, (Math.random()-0.5)*2, 220 new LabelledDot( "Dot "+i, v, ColourMap.Cool.apply( (float)v*10f ) ) ); 221 } 222 223 dpv.updateVis(); 224 dpv.showWindow("Labelled Point Vis" ); 225 } 226}