Skip to content
Snippets Groups Projects
Commit caa8f5e4 authored by Jean-Yves Tinevez's avatar Jean-Yves Tinevez
Browse files

Region growing tool: use a coarser scale

parent d51cc5a1
No related branches found
No related tags found
No related merge requests found
......@@ -43,245 +43,286 @@ import viewer.render.SourceState;
import viewer.render.ViewerState;
import viewer.util.Affine3DHelpers;
public class Demo {
public class Demo
{
private LabelingSource< ? > overlay;
private LabelingSource overlay;
private final SpimViewer viewer;
private int nTimepoints;
private List<SourceAndConverter<?>> sources;
private List< SourceAndConverter< ? >> sources;
private SetupAssignments setupAssignments;
private final BrightnessDialog brightnessDialog;
private boolean editMode = false;
private TransformEventHandler<AffineTransform3D> transformEventHandler;
private RegionGrowingAnnotationTool<?> regionGrowingAnnotationTool;
public Demo(final File file) throws ParserConfigurationException, SAXException, IOException, InstantiationException, IllegalAccessException, ClassNotFoundException, JDOMException {
private TransformEventHandler< AffineTransform3D > transformEventHandler;
private RegionGrowingAnnotationTool< ? > regionGrowingAnnotationTool;
public Demo( final File file ) throws ParserConfigurationException, SAXException, IOException, InstantiationException, IllegalAccessException, ClassNotFoundException, JDOMException
{
/*
* Load image source
*/
prepareSources(file);
prepareSources( file );
viewer = newViewer();
initTransform(viewer, 800, 600);
initBrightness(viewer, 0, 1);
initTransform( viewer, 800, 600 );
initBrightness( viewer, 0, 1 );
/*
* Brightness
*/
brightnessDialog = new BrightnessDialog(null, setupAssignments);
brightnessDialog = new BrightnessDialog( null, setupAssignments );
}
private void prepareSources(final File dataFile) throws ParserConfigurationException, SAXException, IOException, InstantiationException, IllegalAccessException, ClassNotFoundException, JDOMException {
final SequenceViewsLoader loader = new SequenceViewsLoader(dataFile.getAbsolutePath());
@SuppressWarnings( { "rawtypes", "unchecked" } )
private void prepareSources( final File dataFile ) throws ParserConfigurationException, SAXException, IOException, InstantiationException, IllegalAccessException, ClassNotFoundException, JDOMException
{
final SequenceViewsLoader loader = new SequenceViewsLoader( dataFile.getAbsolutePath() );
final SequenceDescription seq = loader.getSequenceDescription();
nTimepoints = seq.numTimepoints();
sources = new ArrayList<SourceAndConverter<?>>();
final ArrayList<ConverterSetup> converterSetups = new ArrayList<ConverterSetup>();
for (int setup = 0; setup < seq.numViewSetups(); ++setup) {
final RealARGBColorConverter<UnsignedShortType> converter = new RealARGBColorConverter<UnsignedShortType>(0, 65535);
converter.setColor(new ARGBType(ARGBType.rgba(255, 255, 255, 255)));
sources.add(new SourceAndConverter<UnsignedShortType>(new SpimSource(loader, setup, "angle " + seq.setups.get(setup).getAngle()), converter));
sources = new ArrayList< SourceAndConverter< ? >>();
final ArrayList< ConverterSetup > converterSetups = new ArrayList< ConverterSetup >();
for ( int setup = 0; setup < seq.numViewSetups(); ++setup )
{
final RealARGBColorConverter< UnsignedShortType > converter = new RealARGBColorConverter< UnsignedShortType >( 0, 65535 );
converter.setColor( new ARGBType( ARGBType.rgba( 255, 255, 255, 255 ) ) );
sources.add( new SourceAndConverter< UnsignedShortType >( new SpimSource( loader, setup, "angle " + seq.setups.get( setup ).getAngle() ), converter ) );
final int id = setup;
converterSetups.add(new ConverterSetup() {
converterSetups.add( new ConverterSetup()
{
@Override
public void setDisplayRange(final int min, final int max) {
converter.setMin(min);
converter.setMax(max);
public void setDisplayRange( final int min, final int max )
{
converter.setMin( min );
converter.setMax( max );
requestRepaintAllViewers();
}
@Override
public void setColor(final ARGBType color) {
converter.setColor(color);
public void setColor( final ARGBType color )
{
converter.setColor( color );
requestRepaintAllViewers();
}
@Override
public int getSetupId() {
public int getSetupId()
{
return id;
}
@Override
public int getDisplayRangeMin() {
return (int) converter.getMin();
public int getDisplayRangeMin()
{
return ( int ) converter.getMin();
}
@Override
public int getDisplayRangeMax() {
return (int) converter.getMax();
public int getDisplayRangeMax()
{
return ( int ) converter.getMax();
}
@Override
public ARGBType getColor() {
public ARGBType getColor()
{
return converter.getColor();
}
});
} );
}
/*
* Create setup assignments (for managing brightness and color).
*/
setupAssignments = new SetupAssignments(converterSetups, 0, 65535);
final MinMaxGroup group = setupAssignments.getMinMaxGroups().get(0);
for (final ConverterSetup setup : setupAssignments.getConverterSetups()) {
setupAssignments.moveSetupToGroup(setup, group);
setupAssignments = new SetupAssignments( converterSetups, 0, 65535 );
final MinMaxGroup group = setupAssignments.getMinMaxGroups().get( 0 );
for ( final ConverterSetup setup : setupAssignments.getConverterSetups() )
{
setupAssignments.moveSetupToGroup( setup, group );
}
overlay = new LabelingSource(sources.get(0).getSpimSource());
sources.add(new SourceAndConverter<ARGBType>(overlay, new TypeIdentity<ARGBType>()));
// Use 2nd level
overlay = new LabelingSource( sources.get( 0 ).getSpimSource(), 2 );
sources.add( new SourceAndConverter< ARGBType >( overlay, new TypeIdentity< ARGBType >() ) );
}
private SpimViewer newViewer() {
final SpimViewer viewer = new SpimViewer(800, 600, sources, nTimepoints);
private SpimViewer newViewer()
{
final SpimViewer viewer = new SpimViewer( 800, 600, sources, nTimepoints );
viewer.addKeyAction(KeyStroke.getKeyStroke("ESCAPE"), new AbstractAction("toggle mode") {
viewer.addKeyAction( KeyStroke.getKeyStroke( "ESCAPE" ), new AbstractAction( "toggle mode" )
{
@Override
public void actionPerformed(final ActionEvent arg0) {
public void actionPerformed( final ActionEvent arg0 )
{
toggleMode();
}
private static final long serialVersionUID = 1L;
});
} );
viewer.addKeyAction(KeyStroke.getKeyStroke("S"), new AbstractAction("brightness settings") {
viewer.addKeyAction( KeyStroke.getKeyStroke( "S" ), new AbstractAction( "brightness settings" )
{
@Override
public void actionPerformed(final ActionEvent arg0) {
public void actionPerformed( final ActionEvent arg0 )
{
toggleBrightnessDialog();
}
private static final long serialVersionUID = 1L;
});
} );
return viewer;
}
public void toggleBrightnessDialog() {
brightnessDialog.setVisible(!brightnessDialog.isVisible());
public void toggleBrightnessDialog()
{
brightnessDialog.setVisible( !brightnessDialog.isVisible() );
}
private void requestRepaintAllViewers() {
if (null != viewer) {
private void requestRepaintAllViewers()
{
if ( null != viewer )
{
viewer.requestRepaint();
}
}
private void initBrightness(final SpimViewer viewer, final double cumulativeMinCutoff, final double cumulativeMaxCutoff) {
private void initBrightness( final SpimViewer viewer, final double cumulativeMinCutoff, final double cumulativeMaxCutoff )
{
final ViewerState state = viewer.getState();
final Source<?> source = state.getSources().get(state.getCurrentSource()).getSpimSource();
@SuppressWarnings({ "rawtypes", "unchecked" })
final RandomAccessibleInterval<UnsignedShortType> img = (RandomAccessibleInterval) source.getSource(state.getCurrentTimepoint(), source.getNumMipmapLevels() - 1);
final long z = (img.min(2) + img.max(2) + 1) / 2;
final Source< ? > source = state.getSources().get( state.getCurrentSource() ).getSpimSource();
@SuppressWarnings( { "rawtypes", "unchecked" } )
final RandomAccessibleInterval< UnsignedShortType > img = ( RandomAccessibleInterval ) source.getSource( state.getCurrentTimepoint(), source.getNumMipmapLevels() - 1 );
final long z = ( img.min( 2 ) + img.max( 2 ) + 1 ) / 2;
final int numBins = 6535;
final Histogram1d<UnsignedShortType> histogram = new Histogram1d<UnsignedShortType>(Views.iterable(Views.hyperSlice(img, 2, z)), new Real1dBinMapper<UnsignedShortType>(0, 65535, numBins, false));
final Histogram1d< UnsignedShortType > histogram = new Histogram1d< UnsignedShortType >( Views.iterable( Views.hyperSlice( img, 2, z ) ), new Real1dBinMapper< UnsignedShortType >( 0, 65535, numBins, false ) );
final DiscreteFrequencyDistribution dfd = histogram.dfd();
final long[] bin = new long[] { 0 };
double cumulative = 0;
int i = 0;
for (; i < numBins && cumulative < cumulativeMinCutoff; ++i) {
bin[0] = i;
cumulative += dfd.relativeFrequency(bin);
for ( ; i < numBins && cumulative < cumulativeMinCutoff; ++i )
{
bin[ 0 ] = i;
cumulative += dfd.relativeFrequency( bin );
}
final int min = i * 65535 / numBins;
for (; i < numBins && cumulative < cumulativeMaxCutoff; ++i) {
bin[0] = i;
cumulative += dfd.relativeFrequency(bin);
for ( ; i < numBins && cumulative < cumulativeMaxCutoff; ++i )
{
bin[ 0 ] = i;
cumulative += dfd.relativeFrequency( bin );
}
final int max = i * 65535 / numBins;
final MinMaxGroup minmax = setupAssignments.getMinMaxGroups().get(0);
minmax.getMinBoundedValue().setCurrentValue(min);
minmax.getMaxBoundedValue().setCurrentValue(max);
final MinMaxGroup minmax = setupAssignments.getMinMaxGroups().get( 0 );
minmax.getMinBoundedValue().setCurrentValue( min );
minmax.getMaxBoundedValue().setCurrentValue( max );
}
private void initTransform(final SpimViewer viewer, final int viewerWidth, final int viewerHeight) {
private void initTransform( final SpimViewer viewer, final int viewerWidth, final int viewerHeight )
{
final int cX = viewerWidth / 2;
final int cY = viewerHeight / 2;
final ViewerState state = viewer.getState();
final SourceState<?> source = state.getSources().get(state.getCurrentSource());
final SourceState< ? > source = state.getSources().get( state.getCurrentSource() );
final int timepoint = state.getCurrentTimepoint();
final AffineTransform3D sourceTransform = source.getSpimSource().getSourceTransform(timepoint, 0);
final AffineTransform3D sourceTransform = source.getSpimSource().getSourceTransform( timepoint, 0 );
final Interval sourceInterval = source.getSpimSource().getSource(timepoint, 0);
final double sX0 = sourceInterval.min(0);
final double sX1 = sourceInterval.max(0);
final double sY0 = sourceInterval.min(1);
final double sY1 = sourceInterval.max(1);
final double sZ0 = sourceInterval.min(2);
final double sZ1 = sourceInterval.max(2);
final double sX = (sX0 + sX1 + 1) / 2;
final double sY = (sY0 + sY1 + 1) / 2;
final double sZ = (sZ0 + sZ1 + 1) / 2;
final Interval sourceInterval = source.getSpimSource().getSource( timepoint, 0 );
final double sX0 = sourceInterval.min( 0 );
final double sX1 = sourceInterval.max( 0 );
final double sY0 = sourceInterval.min( 1 );
final double sY1 = sourceInterval.max( 1 );
final double sZ0 = sourceInterval.min( 2 );
final double sZ1 = sourceInterval.max( 2 );
final double sX = ( sX0 + sX1 + 1 ) / 2;
final double sY = ( sY0 + sY1 + 1 ) / 2;
final double sZ = ( sZ0 + sZ1 + 1 ) / 2;
final double[][] m = new double[3][4];
final double[][] m = new double[ 3 ][ 4 ];
// rotation
final double[] qSource = new double[4];
final double[] qViewer = new double[4];
Affine3DHelpers.extractApproximateRotationAffine(sourceTransform, qSource, 2);
LinAlgHelpers.quaternionInvert(qSource, qViewer);
LinAlgHelpers.quaternionToR(qViewer, m);
final double[] qSource = new double[ 4 ];
final double[] qViewer = new double[ 4 ];
Affine3DHelpers.extractApproximateRotationAffine( sourceTransform, qSource, 2 );
LinAlgHelpers.quaternionInvert( qSource, qViewer );
LinAlgHelpers.quaternionToR( qViewer, m );
// translation
final double[] centerSource = new double[] { sX, sY, sZ };
final double[] centerGlobal = new double[3];
final double[] translation = new double[3];
sourceTransform.apply(centerSource, centerGlobal);
LinAlgHelpers.quaternionApply(qViewer, centerGlobal, translation);
LinAlgHelpers.scale(translation, -1, translation);
LinAlgHelpers.setCol(3, translation, m);
final double[] centerGlobal = new double[ 3 ];
final double[] translation = new double[ 3 ];
sourceTransform.apply( centerSource, centerGlobal );
LinAlgHelpers.quaternionApply( qViewer, centerGlobal, translation );
LinAlgHelpers.scale( translation, -1, translation );
LinAlgHelpers.setCol( 3, translation, m );
final AffineTransform3D viewerTransform = new AffineTransform3D();
viewerTransform.set(m);
viewerTransform.set( m );
// scale
final double[] pSource = new double[] { sX1, sY1, sZ };
final double[] pGlobal = new double[3];
final double[] pScreen = new double[3];
sourceTransform.apply(pSource, pGlobal);
viewerTransform.apply(pGlobal, pScreen);
final double scaleX = cX / pScreen[0];
final double scaleY = cY / pScreen[1];
final double scale = Math.min(scaleX, scaleY);
viewerTransform.scale(scale);
final double[] pGlobal = new double[ 3 ];
final double[] pScreen = new double[ 3 ];
sourceTransform.apply( pSource, pGlobal );
viewerTransform.apply( pGlobal, pScreen );
final double scaleX = cX / pScreen[ 0 ];
final double scaleY = cY / pScreen[ 1 ];
final double scale = Math.min( scaleX, scaleY );
viewerTransform.scale( scale );
// window center offset
viewerTransform.set(viewerTransform.get(0, 3) + cX, 0, 3);
viewerTransform.set(viewerTransform.get(1, 3) + cY, 1, 3);
viewerTransform.set( viewerTransform.get( 0, 3 ) + cX, 0, 3 );
viewerTransform.set( viewerTransform.get( 1, 3 ) + cY, 1, 3 );
viewer.setCurrentViewerTransform(viewerTransform);
viewer.setCurrentViewerTransform( viewerTransform );
}
@SuppressWarnings({ "rawtypes", "unchecked" })
private void toggleMode() {
@SuppressWarnings( { "rawtypes", "unchecked" } )
private void toggleMode()
{
this.editMode = !editMode;
System.out.println(editMode);// DEBUG
System.out.println( editMode );// DEBUG
if (editMode) {
if ( editMode )
{
transformEventHandler = viewer.getDisplay().getTransformEventHandler();
viewer.getDisplay().removeHandler(transformEventHandler);
viewer.getDisplay().removeHandler( transformEventHandler );
final int currentSource = viewer.getState().getCurrentSource();
final int timePoint = viewer.getState().getCurrentTimepoint();
regionGrowingAnnotationTool = new RegionGrowingAnnotationTool(viewer, sources.get(currentSource).getSpimSource(), overlay, timePoint);
viewer.getDisplay().addHandler(regionGrowingAnnotationTool);
} else {
viewer.getDisplay().removeHandler(regionGrowingAnnotationTool);
viewer.getDisplay().addHandler(transformEventHandler);
regionGrowingAnnotationTool = new RegionGrowingAnnotationTool( viewer, overlay, timePoint );
viewer.getDisplay().addHandler( regionGrowingAnnotationTool );
}
else
{
viewer.getDisplay().removeHandler( regionGrowingAnnotationTool );
viewer.getDisplay().addHandler( transformEventHandler );
}
}
public static void main(final String[] args) throws ParserConfigurationException, SAXException, IOException, InstantiationException, IllegalAccessException, ClassNotFoundException, JDOMException {
ImageJ.main(args);
public static void main( final String[] args ) throws ParserConfigurationException, SAXException, IOException, InstantiationException, IllegalAccessException, ClassNotFoundException, JDOMException
{
ImageJ.main( args );
// final File file = new File(
// "/Users/tinevez/Desktop/Data/Mamut/parhyale-crop/parhyale-crop-2.xml"
// );
final File file = new File( "E:/Users/JeanYves/Desktop/Data/Celegans.xml" );
final File file = new File("/Users/tinevez/Desktop/Data/Mamut/parhyale-crop/parhyale-crop-2.xml");
new Demo(file);
new Demo( file );
}
}
......@@ -23,38 +23,49 @@ import net.imglib2.roi.IterableRegionOfInterest;
import net.imglib2.type.numeric.RealType;
import net.imglib2.type.numeric.integer.IntType;
import viewer.SpimViewer;
import viewer.render.Source;
public class RegionGrowingAnnotationTool<T extends RealType<T>> implements MouseListener, MouseMotionListener {
public class RegionGrowingAnnotationTool< T extends RealType< T >> implements MouseListener, MouseMotionListener
{
private final SpimViewer viewer;
private Map<long[], Integer> seed;
private final LabelingSource labelingSource;
private Map< long[], Integer > seed;
private final LabelingSource< T > labelingSource;
private int label = 0;
private final long[][] structuringElement;
private final GrowingMode growingMode;
private final NativeImgLabeling<Integer, IntType> labeling;
private final NativeImgLabeling< Integer, IntType > labeling;
/**
* A {@link RandomAccess} to the source image.
*/
private final RandomAccess<T> ra;
private final RandomAccess< T > ra;
/**
* The transform that maps source coordinates to mouse coordinates.
*/
private final AffineTransform3D sourceToGlobal;
public RegionGrowingAnnotationTool(final SpimViewer viewer, final Source<T> source, final LabelingSource labellingSource, final int t) {
public RegionGrowingAnnotationTool( final SpimViewer viewer, final LabelingSource< T > labelingSource, final int t )
{
this.viewer = viewer;
this.labelingSource = labellingSource;
this.labeling = labellingSource.getLabeling(t);
this.ra = source.getSource(t, 0).randomAccess();
this.sourceToGlobal = source.getSourceTransform(t, 0);
this.structuringElement = AbstractRegionGrowing.get8ConStructuringElement(3);
this.labelingSource = labelingSource;
this.labeling = labelingSource.getLabeling( t );
this.ra = labelingSource.getImgSource().getSource( t, labelingSource.getLevel() ).randomAccess();
this.sourceToGlobal = labelingSource.getImgSource().getSourceTransform( t, labelingSource.getLevel() );
this.structuringElement = AbstractRegionGrowing.get8ConStructuringElement( 3 );
this.growingMode = GrowingMode.SEQUENTIAL;
int freeLabel = 0;
for (final Integer existingLabel : labeling.getLabels()) {
if (existingLabel.intValue() > freeLabel) {
for ( final Integer existingLabel : labeling.getLabels() )
{
if ( existingLabel.intValue() > freeLabel )
{
freeLabel = existingLabel.intValue();
}
}
......@@ -62,71 +73,88 @@ public class RegionGrowingAnnotationTool<T extends RealType<T>> implements Mouse
}
@Override
public void mouseClicked(final MouseEvent e) {}
public void mouseClicked( final MouseEvent e )
{}
@Override
public void mouseEntered(final MouseEvent e) {}
public void mouseEntered( final MouseEvent e )
{}
@Override
public void mouseExited(final MouseEvent e) {}
public void mouseExited( final MouseEvent e )
{}
@Override
public void mousePressed(final MouseEvent e) {
public void mousePressed( final MouseEvent e )
{
final Point point = getLocationOnCurrentSource();
final long[] pos = new long[3];
point.localize(pos);
final long[] pos = new long[ 3 ];
point.localize( pos );
if (!labeling.getLabels().isEmpty()) {
if ( !labeling.getLabels().isEmpty() )
{
final RandomAccess<LabelingType<Integer>> randomAccess = labeling.randomAccess();
randomAccess.setPosition(point);
final RandomAccess< LabelingType< Integer >> randomAccess = labeling.randomAccess();
randomAccess.setPosition( point );
final List<Integer> existingLabels = randomAccess.get().getLabeling();
if (!existingLabels.isEmpty()) {
final List< Integer > existingLabels = randomAccess.get().getLabeling();
if ( !existingLabels.isEmpty() )
{
label = existingLabels.get(0).intValue(); // For future new annotation
label = existingLabels.get( 0 ).intValue(); // For future new
// annotation
for (final Integer existingLabel : existingLabels) {
for ( final Integer existingLabel : existingLabels )
{
final IterableRegionOfInterest roi = labeling.getIterableRegionOfInterest(existingLabel);
final IterableInterval<LabelingType<Integer>> overROI = roi.getIterableIntervalOverROI(labeling);
for (final LabelingType<Integer> labelingType : overROI) {
final ArrayList<Integer> labels = new ArrayList<Integer>(labelingType.getLabeling());
final IterableRegionOfInterest roi = labeling.getIterableRegionOfInterest( existingLabel );
final IterableInterval< LabelingType< Integer >> overROI = roi.getIterableIntervalOverROI( labeling );
for ( final LabelingType< Integer > labelingType : overROI )
{
final ArrayList< Integer > labels = new ArrayList< Integer >( labelingType.getLabeling() );
labels.clear();
labelingType.setLabeling(labels);
labelingType.setLabeling( labels );
}
}
} else {
}
else
{
label++;
}
} else {
}
else
{
label++;
}
seed = new HashMap<long[], Integer>(1);
seed.put(pos, Integer.valueOf(label));
seed = new HashMap< long[], Integer >( 1 );
seed.put( pos, Integer.valueOf( label ) );
}
@Override
public void mouseReleased(final MouseEvent e) {}
public void mouseReleased( final MouseEvent e )
{}
@Override
public void mouseDragged(final MouseEvent arg0) {
public void mouseDragged( final MouseEvent arg0 )
{
/*
* Remove old stuff
*/
if (labeling.getLabels().contains(Integer.valueOf(label))) {
if ( labeling.getLabels().contains( Integer.valueOf( label ) ) )
{
final IterableRegionOfInterest iterableRegionOfInterest = labeling.getIterableRegionOfInterest(Integer.valueOf(label));
for (final LabelingType<Integer> ll : iterableRegionOfInterest.getIterableIntervalOverROI(labeling)) {
final ArrayList<Integer> labels = new ArrayList<Integer>(ll.getLabeling());
labels.remove(Integer.valueOf(label));
ll.setLabeling(labels);
final IterableRegionOfInterest iterableRegionOfInterest = labeling.getIterableRegionOfInterest( Integer.valueOf( label ) );
for ( final LabelingType< Integer > ll : iterableRegionOfInterest.getIterableIntervalOverROI( labeling ) )
{
final ArrayList< Integer > labels = new ArrayList< Integer >( ll.getLabeling() );
labels.remove( Integer.valueOf( label ) );
ll.setLabeling( labels );
}
}
......@@ -136,10 +164,10 @@ public class RegionGrowingAnnotationTool<T extends RealType<T>> implements Mouse
*/
final Point current = getLocationOnCurrentSource();
ra.setPosition(current);
ra.setPosition( current );
final T threshold = ra.get().copy();
final ThresholdRegionGrowing<T, Integer> regionGrowing = new ThresholdRegionGrowing<T, Integer>(ra, threshold, seed, growingMode, structuringElement, labeling);
final ThresholdRegionGrowing< T, Integer > regionGrowing = new ThresholdRegionGrowing< T, Integer >( ra, threshold, seed, growingMode, structuringElement, labeling );
regionGrowing.checkInput();
regionGrowing.process();
......@@ -149,15 +177,17 @@ public class RegionGrowingAnnotationTool<T extends RealType<T>> implements Mouse
}
@Override
public void mouseMoved(final MouseEvent arg0) {}
public void mouseMoved( final MouseEvent arg0 )
{}
private Point getLocationOnCurrentSource() {
final double[] coordinates = new double[3];
final RealPoint click = RealPoint.wrap(coordinates);
viewer.getGlobalMouseCoordinates(click);
private Point getLocationOnCurrentSource()
{
final double[] coordinates = new double[ 3 ];
final RealPoint click = RealPoint.wrap( coordinates );
viewer.getGlobalMouseCoordinates( click );
final Point roundedSourcePos = new Point(3);
sourceToGlobal.applyInverse(new Round<Point>(roundedSourcePos), click);
final Point roundedSourcePos = new Point( 3 );
sourceToGlobal.applyInverse( new Round< Point >( roundedSourcePos ), click );
return roundedSourcePos;
}
}
0% Loading or .
You are about to add 0 people to the discussion. Proceed with caution.
Please register or to comment