001/*
002 * Copyright 2010-2015 Institut Pasteur.
003 * 
004 * This file is part of Icy.
005 * 
006 * Icy is free software: you can redistribute it and/or modify
007 * it under the terms of the GNU General Public License as published by
008 * the Free Software Foundation, either version 3 of the License, or
009 * (at your option) any later version.
010 * 
011 * Icy is distributed in the hope that it will be useful,
012 * but WITHOUT ANY WARRANTY; without even the implied warranty of
013 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
014 * GNU General Public License for more details.
015 * 
016 * You should have received a copy of the GNU General Public License
017 * along with Icy. If not, see <http://www.gnu.org/licenses/>.
018 */
019package icy.vtk;
020
021import java.awt.Color;
022import java.awt.Rectangle;
023import java.util.ArrayList;
024import java.util.Arrays;
025import java.util.List;
026
027import icy.canvas.Layer;
028import icy.common.exception.TooLargeArrayException;
029import icy.image.colormap.IcyColorMap;
030import icy.image.lut.LUT.LUTChannel;
031import icy.math.Scaler;
032import icy.painter.Overlay;
033import icy.painter.VtkPainter;
034import icy.roi.BooleanMask2D;
035import icy.roi.BooleanMask3D;
036import icy.roi.ROI;
037import icy.sequence.Sequence;
038import icy.type.DataType;
039import icy.type.collection.array.Array2DUtil;
040import icy.type.collection.array.ArrayUtil;
041import icy.type.rectangle.Rectangle3D;
042import icy.type.rectangle.Rectangle5D;
043import plugins.kernel.canvas.VtkCanvas;
044import plugins.kernel.roi.roi2d.ROI2DArea;
045import plugins.kernel.roi.roi3d.ROI3DArea;
046import vtk.vtkAbstractTransform;
047import vtk.vtkActor;
048import vtk.vtkActor2D;
049import vtk.vtkActor2DCollection;
050import vtk.vtkActorCollection;
051import vtk.vtkCellArray;
052import vtk.vtkCollection;
053import vtk.vtkColorTransferFunction;
054import vtk.vtkContourFilter;
055import vtk.vtkDataArray;
056import vtk.vtkDataSet;
057import vtk.vtkDataSetSurfaceFilter;
058import vtk.vtkDoubleArray;
059import vtk.vtkFloatArray;
060import vtk.vtkIdTypeArray;
061import vtk.vtkImageConstantPad;
062import vtk.vtkImageData;
063import vtk.vtkImageStencil;
064import vtk.vtkIntArray;
065import vtk.vtkLongArray;
066import vtk.vtkOBJReader;
067import vtk.vtkObject;
068import vtk.vtkObjectBase;
069import vtk.vtkPiecewiseFunction;
070import vtk.vtkPoints;
071import vtk.vtkPolyData;
072import vtk.vtkPolyDataToImageStencil;
073import vtk.vtkProp;
074import vtk.vtkPropCollection;
075import vtk.vtkRenderer;
076import vtk.vtkShortArray;
077import vtk.vtkTransform;
078import vtk.vtkTransformPolyDataFilter;
079import vtk.vtkUnsignedCharArray;
080import vtk.vtkUnsignedIntArray;
081import vtk.vtkUnsignedLongArray;
082import vtk.vtkUnsignedShortArray;
083import vtk.vtkVertexGlyphFilter;
084
085/**
086 * @author Stephane
087 */
088public class VtkUtil
089{
090    // VTK type
091    public final static int VTK_VOID = 0;
092    public final static int VTK_BIT = 1;
093    public final static int VTK_CHAR = 2;
094    public final static int VTK_SIGNED_CHAR = 15;
095    public final static int VTK_UNSIGNED_CHAR = 3;
096    public final static int VTK_SHORT = 4;
097    public final static int VTK_UNSIGNED_SHORT = 5;
098    public final static int VTK_INT = 6;
099    public final static int VTK_UNSIGNED_INT = 7;
100    public final static int VTK_LONG = 8;
101    public final static int VTK_UNSIGNED_LONG = 9;
102    public final static int VTK_FLOAT = 10;
103    public final static int VTK_DOUBLE = 11;
104    public final static int VTK_ID = 12;
105
106    // VTK interpolation
107    public final static int VTK_NEAREST_INTERPOLATION = 0;
108    public final static int VTK_LINEAR_INTERPOLATION = 1;
109    public final static int VTK_CUBIC_INTERPOLATION = 2;
110
111    // VTK bounding box
112    public final static int VTK_FLY_OUTER_EDGES = 0;
113    public final static int VTK_FLY_CLOSEST_TRIAD = 1;
114    public final static int VTK_FLY_FURTHEST_TRIAD = 2;
115    public final static int VTK_FLY_STATIC_TRIAD = 3;
116    public final static int VTK_FLY_STATIC_EDGES = 4;
117
118    public final static int VTK_TICKS_INSIDE = 0;
119    public final static int VTK_TICKS_OUTSIDE = 1;
120    public final static int VTK_TICKS_BOTH = 2;
121
122    public final static int VTK_GRID_LINES_ALL = 0;
123    public final static int VTK_GRID_LINES_CLOSEST = 1;
124    public final static int VTK_GRID_LINES_FURTHEST = 2;
125
126    /**
127     * Returns the VTK type corresponding to the specified DataType
128     */
129    public static int getVtkType(DataType type)
130    {
131        switch (type)
132        {
133            default:
134            case UBYTE:
135            case BYTE:
136                return VTK_UNSIGNED_CHAR;
137
138            // FIXME: signed char not supported by VTK java wrapper ??
139            // case BYTE:
140            // return VTK_CHAR;
141            // return VTK_SIGNED_CHAR;
142
143            case USHORT:
144                return VTK_UNSIGNED_SHORT;
145            case SHORT:
146                return VTK_SHORT;
147            case UINT:
148                return VTK_UNSIGNED_INT;
149            case INT:
150                return VTK_INT;
151            case ULONG:
152                return VTK_UNSIGNED_LONG;
153            case LONG:
154                return VTK_LONG;
155            case FLOAT:
156                return VTK_FLOAT;
157            case DOUBLE:
158                return VTK_DOUBLE;
159        }
160    }
161
162    public static vtkDataArray getVtkArray(Object array, boolean signed)
163    {
164        switch (ArrayUtil.getDataType(array))
165        {
166            case BYTE:
167                return getUCharArray((byte[]) array);
168            case SHORT:
169                if (signed)
170                    return getUShortArray((short[]) array);
171                return getShortArray((short[]) array);
172            case INT:
173                if (signed)
174                    return getUIntArray((int[]) array);
175                return getIntArray((int[]) array);
176            case LONG:
177                if (signed)
178                    return getULongArray((long[]) array);
179                return getLongArray((long[]) array);
180            case FLOAT:
181                return getFloatArray((float[]) array);
182            case DOUBLE:
183                return getDoubleArray((double[]) array);
184            default:
185                return null;
186        }
187    }
188
189    public static vtkUnsignedCharArray getUCharArray(byte[] array)
190    {
191        final vtkUnsignedCharArray result = new vtkUnsignedCharArray();
192
193        result.SetJavaArray(array);
194
195        return result;
196    }
197
198    public static vtkUnsignedShortArray getUShortArray(short[] array)
199    {
200        final vtkUnsignedShortArray result = new vtkUnsignedShortArray();
201
202        result.SetJavaArray(array);
203
204        return result;
205    }
206
207    public static vtkUnsignedIntArray getUIntArray(int[] array)
208    {
209        final vtkUnsignedIntArray result = new vtkUnsignedIntArray();
210
211        result.SetJavaArray(array);
212
213        return result;
214    }
215
216    public static vtkUnsignedLongArray getULongArray(long[] array)
217    {
218        final vtkUnsignedLongArray result = new vtkUnsignedLongArray();
219
220        result.SetJavaArray(array);
221
222        return result;
223    }
224
225    public static vtkShortArray getShortArray(short[] array)
226    {
227        final vtkShortArray result = new vtkShortArray();
228
229        result.SetJavaArray(array);
230
231        return result;
232    }
233
234    public static vtkIntArray getIntArray(int[] array)
235    {
236        final vtkIntArray result = new vtkIntArray();
237
238        result.SetJavaArray(array);
239
240        return result;
241    }
242
243    public static vtkLongArray getLongArray(long[] array)
244    {
245        final vtkLongArray result = new vtkLongArray();
246
247        result.SetJavaArray(array);
248
249        return result;
250    }
251
252    public static vtkFloatArray getFloatArray(float[] array)
253    {
254        final vtkFloatArray result = new vtkFloatArray();
255
256        result.SetJavaArray(array);
257
258        return result;
259    }
260
261    public static vtkDoubleArray getDoubleArray(double[] array)
262    {
263        final vtkDoubleArray result = new vtkDoubleArray();
264
265        result.SetJavaArray(array);
266
267        return result;
268    }
269
270    public static vtkIdTypeArray getIdTypeArray(int[] array)
271    {
272        final vtkIdTypeArray result = new vtkIdTypeArray();
273        final vtkIntArray iarray = getIntArray(array);
274
275        result.DeepCopy(iarray);
276        iarray.Delete();
277
278        return result;
279    }
280
281    /**
282     * Returns a native java array from the specified {@link vtkDataArray}.
283     */
284    public static Object getJavaArray(vtkDataArray dataArray)
285    {
286        switch (dataArray.GetDataType())
287        {
288            case VTK_UNSIGNED_CHAR:
289                return ((vtkUnsignedCharArray) dataArray).GetJavaArray();
290            case VTK_SHORT:
291                return ((vtkShortArray) dataArray).GetJavaArray();
292            case VTK_UNSIGNED_SHORT:
293                return ((vtkUnsignedShortArray) dataArray).GetJavaArray();
294            case VTK_INT:
295                return ((vtkIntArray) dataArray).GetJavaArray();
296            case VTK_UNSIGNED_INT:
297                return ((vtkUnsignedIntArray) dataArray).GetJavaArray();
298            case VTK_LONG:
299                return ((vtkLongArray) dataArray).GetJavaArray();
300            case VTK_UNSIGNED_LONG:
301                return ((vtkUnsignedLongArray) dataArray).GetJavaArray();
302            case VTK_FLOAT:
303                return ((vtkFloatArray) dataArray).GetJavaArray();
304            case VTK_DOUBLE:
305                return ((vtkDoubleArray) dataArray).GetJavaArray();
306            default:
307                return null;
308        }
309    }
310
311    public static int[] getJavaArray(vtkIdTypeArray array)
312    {
313        final vtkIntArray iarray = new vtkIntArray();
314
315        iarray.DeepCopy(array);
316
317        final int[] result = iarray.GetJavaArray();
318        iarray.Delete();
319
320        return result;
321    }
322
323    /**
324     * Transforms a vtkCollection to an array
325     */
326    public static vtkObject[] vtkCollectionToArray(vtkCollection collection)
327    {
328        final vtkObject[] result = new vtkObject[collection.GetNumberOfItems()];
329
330        collection.InitTraversal();
331        for (int i = 0; i < result.length; i++)
332            result[i] = collection.GetNextItemAsObject();
333
334        return result;
335    }
336
337    /**
338     * Get vtkPoints from double[]
339     */
340    public static vtkPoints getPoints(double[] points)
341    {
342        final vtkPoints result = new vtkPoints();
343        final vtkDoubleArray array = getDoubleArray(points);
344
345        array.SetNumberOfComponents(3);
346        result.SetData(array);
347
348        return result;
349    }
350
351    /**
352     * Get vtkPoints from double[][3]
353     */
354    public static vtkPoints getPoints(double[][] points)
355    {
356        return getPoints(Array2DUtil.toDoubleArray1D(points));
357    }
358
359    /**
360     * Get vtkPoints from float[]
361     */
362    public static vtkPoints getPoints(float[] points)
363    {
364        final vtkPoints result = new vtkPoints();
365        final vtkFloatArray array = getFloatArray(points);
366
367        array.SetNumberOfComponents(3);
368        result.SetData(array);
369
370        return result;
371    }
372
373    /**
374     * Get vtkPoints from float[][3]
375     */
376    public static vtkPoints getPoints(float[][] points)
377    {
378        return getPoints(Array2DUtil.toFloatArray1D(points));
379    }
380
381    /**
382     * Get vtkPoints from int[]
383     */
384    public static vtkPoints getPoints(int[] points)
385    {
386        final vtkPoints result = new vtkPoints();
387        final vtkIntArray array = getIntArray(points);
388
389        array.SetNumberOfComponents(3);
390        result.SetData(array);
391
392        return result;
393    }
394
395    /**
396     * Get vtkPoints from int[][3]
397     */
398    public static vtkPoints getPoints(int[][] points)
399    {
400        return getPoints(Array2DUtil.toIntArray1D(points));
401    }
402
403    /**
404     * Get vtkCellArray from a 1D prepared cells array ( {n, i1, i2, ..., n, i1, i2,...} )
405     */
406    public static vtkCellArray getCells(int numCell, int[] cells)
407    {
408        final vtkCellArray result = new vtkCellArray();
409
410        result.SetCells(numCell, getIdTypeArray(cells));
411
412        return result;
413    }
414
415    /**
416     * Returns a simple cell {@link vtkPolyData} from {@link vtkPoints}
417     */
418    public static vtkPolyData getPolyDataFromPoints(vtkPoints points)
419    {
420        final vtkPolyData tmpPolyData = new vtkPolyData();
421        final vtkVertexGlyphFilter vertexFilter = new vtkVertexGlyphFilter();
422
423        tmpPolyData.SetPoints(points);
424        vertexFilter.SetInputData(tmpPolyData);
425        vertexFilter.Update();
426
427        final vtkPolyData result = vertexFilter.GetOutput();
428
429        vertexFilter.Delete();
430
431        return result;
432    }
433
434    /**
435     * Returns a {@link vtkPolyData} from {@link vtkDataSet}
436     */
437    public static vtkPolyData getPolyDataFromDataSet(vtkDataSet dataSet)
438    {
439        // transform grid to polydata first
440        final vtkDataSetSurfaceFilter surfaceFilter = new vtkDataSetSurfaceFilter();
441        surfaceFilter.SetInputData(dataSet);
442        surfaceFilter.Update();
443
444        final vtkPolyData result = surfaceFilter.GetOutput();
445
446        surfaceFilter.Delete();
447
448        return result;
449    }
450
451    /**
452     * Returns the <i>vtkProp</i> from the specified <i>Layer</i> object.<br>
453     * Returns a 0 sized array if the specified layer is <code>null</code> or does not contains any vtkProp.
454     */
455    public static vtkProp[] getLayerProps(Layer layer)
456    {
457        if (layer != null)
458        {
459            // add painter actor from the vtk render
460            final Overlay overlay = layer.getOverlay();
461
462            if (overlay instanceof VtkPainter)
463                return ((VtkPainter) overlay).getProps();
464        }
465
466        return new vtkProp[0];
467    }
468
469    /**
470     * Returns all <i>vtkProp</i> from the specified list of <i>Layer</i> object.<br>
471     * Returns a 0 sized array if specified layers does not contains any vtkProp.
472     */
473    public static vtkProp[] getLayersProps(List<Layer> layers)
474    {
475        final List<vtkProp[]> layersProps = new ArrayList<vtkProp[]>();
476        int totalSize = 0;
477
478        for (Layer layer : layers)
479        {
480            if (layer != null)
481            {
482                // add painter actor from the vtk render
483                final Overlay overlay = layer.getOverlay();
484
485                if (overlay instanceof VtkPainter)
486                {
487                    final vtkProp[] props = ((VtkPainter) overlay).getProps();
488
489                    if (props.length > 0)
490                    {
491                        layersProps.add(props);
492                        totalSize += props.length;
493                    }
494                }
495            }
496        }
497
498        final vtkProp[] result = new vtkProp[totalSize];
499        int ind = 0;
500
501        for (vtkProp[] props : layersProps)
502        {
503            final int size = props.length;
504
505            System.arraycopy(props, 0, result, ind, size);
506            ind += size;
507        }
508
509        return result;
510    }
511
512    /**
513     * Return all actor / view prop from the specified renderer
514     */
515    public static vtkProp[] getProps(vtkRenderer renderer)
516    {
517        if (renderer == null)
518            return new vtkProp[0];
519
520        final vtkPropCollection collection = renderer.GetViewProps();
521        final vtkProp[] result = new vtkProp[collection.GetNumberOfItems()];
522
523        collection.InitTraversal();
524        for (int i = 0; i < result.length; i++)
525            result[i] = collection.GetNextProp();
526
527        return result;
528    }
529
530    /**
531     * Return true if the renderer contains the specified actor / view prop
532     */
533    public static boolean hasProp(vtkRenderer renderer, vtkProp actor)
534    {
535        if ((renderer == null) || (actor == null))
536            return false;
537
538        return renderer.HasViewProp(actor) != 0;
539    }
540
541    /**
542     * @deprecated Use {@link #hasProp(vtkRenderer, vtkProp)} instead.
543     */
544    @Deprecated
545    public static boolean findProp(vtkRenderer renderer, vtkProp actor)
546    {
547        return hasProp(renderer, actor);
548    }
549
550    /**
551     * @deprecated Use {@link #hasProp(vtkRenderer, vtkProp)} instead.
552     */
553    @Deprecated
554    public static boolean findActor(vtkRenderer renderer, vtkActor actor)
555    {
556        if ((renderer == null) || (actor == null))
557            return false;
558
559        final vtkActorCollection actors = renderer.GetActors();
560
561        actors.InitTraversal();
562        for (int i = 0; i < actors.GetNumberOfItems(); i++)
563        {
564            final vtkActor curActor = actors.GetNextActor();
565
566            // already present --> exit
567            if (curActor == actor)
568                return true;
569
570            // // search in sub actor
571            // if (findActor(curActor, actor))
572            // return true;
573        }
574
575        return false;
576    }
577
578    /**
579     * @deprecated Use {@link #hasProp(vtkRenderer, vtkProp)} instead.
580     */
581    @Deprecated
582    public static boolean findActor2D(vtkRenderer renderer, vtkActor2D actor)
583    {
584        if ((renderer == null) || (actor == null))
585            return false;
586
587        final vtkActor2DCollection actors = renderer.GetActors2D();
588
589        actors.InitTraversal();
590        for (int i = 0; i < actors.GetNumberOfItems(); i++)
591        {
592            final vtkActor2D curActor = actors.GetNextActor2D();
593
594            // already present --> exit
595            if (curActor == actor)
596                return true;
597
598            // // search in sub actor
599            // if (findActor2D(curActor, actor))
600            // return true;
601        }
602
603        return false;
604    }
605
606    /**
607     * Add an actor (vtkProp) to the specified renderer.<br>
608     * If the actor is already existing in the renderer then no operation is done.
609     */
610    public static void addProp(vtkRenderer renderer, vtkProp prop)
611    {
612        if ((renderer == null) || (prop == null))
613            return;
614
615        // actor not yet present in renderer ? --> add it
616        if (renderer.HasViewProp(prop) == 0)
617            renderer.AddViewProp(prop);
618    }
619
620    /**
621     * @deprecated Use {@link #addProp(vtkRenderer, vtkProp)} instead.
622     */
623    @Deprecated
624    public static void addActor(vtkRenderer renderer, vtkActor actor)
625    {
626        if ((renderer == null) || (actor == null))
627            return;
628
629        // actor not yet present in renderer ? --> add it
630        if (!VtkUtil.findActor(renderer, actor))
631            renderer.AddActor(actor);
632    }
633
634    /**
635     * @deprecated Use {@link #addProp(vtkRenderer, vtkProp)} instead.
636     */
637    @Deprecated
638    public static void addActor2D(vtkRenderer renderer, vtkActor2D actor)
639    {
640        if ((renderer == null) || (actor == null))
641            return;
642
643        // actor not yet present in renderer ? --> add it
644        if (!VtkUtil.findActor2D(renderer, actor))
645            renderer.AddActor2D(actor);
646    }
647
648    /**
649     * Add an array of actor (vtkProp) to the specified renderer.<br>
650     * If an actor is already existing in the renderer then nothing is done for this actor.
651     */
652    public static void addProps(vtkRenderer renderer, vtkProp[] props)
653    {
654        if ((renderer == null) || (props == null))
655            return;
656
657        for (vtkProp prop : props)
658        {
659            // actor not yet present in renderer ? --> add it
660            if (renderer.HasViewProp(prop) == 0)
661                renderer.AddViewProp(prop);
662        }
663    }
664
665    /**
666     * Remove an actor from the specified renderer.
667     */
668    public static void removeProp(vtkRenderer renderer, vtkProp actor)
669    {
670        renderer.RemoveViewProp(actor);
671    }
672
673    /**
674     * Return a 1D cells array from a 2D indexes array
675     */
676    public static int[] prepareCells(int[][] indexes)
677    {
678        final int len = indexes.length;
679
680        int total_len = 0;
681        for (int i = 0; i < len; i++)
682            total_len += indexes[i].length + 1;
683
684        final int[] result = new int[total_len];
685
686        int offset = 0;
687        for (int i = 0; i < len; i++)
688        {
689            final int[] s_cells = indexes[i];
690            final int s_len = s_cells.length;
691
692            result[offset++] = s_len;
693            for (int j = 0; j < s_len; j++)
694                result[offset++] = s_cells[j];
695        }
696
697        return result;
698    }
699
700    /**
701     * Return a 1D cells array from a 1D indexes array and num vertex per cell (polygon)
702     */
703    public static int[] prepareCells(int numVertexPerCell, int[] indexes)
704    {
705        final int num_cells = indexes.length / numVertexPerCell;
706        final int[] result = new int[num_cells * (numVertexPerCell + 1)];
707
708        int off_dst = 0;
709        int off_src = 0;
710        for (int i = 0; i < num_cells; i++)
711        {
712            result[off_dst++] = numVertexPerCell;
713
714            for (int j = 0; j < numVertexPerCell; j++)
715                result[off_dst++] = indexes[off_src + j];
716
717            off_src += numVertexPerCell;
718        }
719
720        return result;
721    }
722
723    /**
724     * Returns a {@link BooleanMask3D} from a binary (0/1 values) {@link vtkImageData}.
725     */
726    public static BooleanMask3D getBooleanMaskFromBinaryImage(vtkImageData image, boolean optimizeBounds)
727    {
728        final vtkDataArray data = image.GetPointData().GetScalars();
729        final double[] origin = image.GetOrigin();
730        final int[] dim = image.GetDimensions();
731        final int sizeX = dim[0];
732        final int sizeY = dim[1];
733        final int sizeZ = dim[2];
734        final int sizeXY = sizeX * sizeY;
735        final Rectangle bounds2D = new Rectangle((int) origin[0], (int) origin[1], sizeX, sizeY);
736        final BooleanMask2D[] masks = new BooleanMask2D[sizeZ];
737
738        // more than 200M ? use simple iterator (slower but consume less memory)
739        if ((sizeXY * sizeZ) > (200 * 1024 * 1024))
740        {
741            int off = 0;
742            for (int z = 0; z < sizeZ; z++)
743            {
744                final boolean[] mask = new boolean[sizeXY];
745
746                for (int xy = 0; xy < sizeXY; xy++)
747                    mask[xy] = (data.GetTuple1(off++) != 0d);
748
749                masks[z] = new BooleanMask2D(new Rectangle(bounds2D), mask);
750            }
751        }
752        else
753        {
754            final Object javaArray = getJavaArray(data);
755            int off = 0;
756
757            switch (ArrayUtil.getDataType(javaArray))
758            {
759                case BYTE:
760                    final byte[] javaByteArray = (byte[]) javaArray;
761
762                    for (int z = 0; z < sizeZ; z++)
763                    {
764                        final boolean[] mask = new boolean[sizeXY];
765
766                        for (int xy = 0; xy < mask.length; xy++)
767                            mask[xy] = (javaByteArray[off++] != 0);
768
769                        masks[z] = new BooleanMask2D(new Rectangle(bounds2D), mask);
770                    }
771                    break;
772
773                case SHORT:
774                    final short[] javaShortArray = (short[]) javaArray;
775
776                    for (int z = 0; z < sizeZ; z++)
777                    {
778                        final boolean[] mask = new boolean[sizeXY];
779
780                        for (int xy = 0; xy < mask.length; xy++)
781                            mask[xy] = (javaShortArray[off++] != 0);
782
783                        masks[z] = new BooleanMask2D(new Rectangle(bounds2D), mask);
784                    }
785                    break;
786
787                case INT:
788                    final int[] javaIntArray = (int[]) javaArray;
789
790                    for (int z = 0; z < sizeZ; z++)
791                    {
792                        final boolean[] mask = new boolean[sizeXY];
793
794                        for (int xy = 0; xy < mask.length; xy++)
795                            mask[xy] = (javaIntArray[off++] != 0);
796
797                        masks[z] = new BooleanMask2D(new Rectangle(bounds2D), mask);
798                    }
799                    break;
800
801                case LONG:
802                    final long[] javaLongArray = (long[]) javaArray;
803
804                    for (int z = 0; z < sizeZ; z++)
805                    {
806                        final boolean[] mask = new boolean[sizeXY];
807
808                        for (int xy = 0; xy < mask.length; xy++)
809                            mask[xy] = (javaLongArray[off++] != 0L);
810
811                        masks[z] = new BooleanMask2D(new Rectangle(bounds2D), mask);
812                    }
813                    break;
814
815                case FLOAT:
816                    final float[] javaFloatArray = (float[]) javaArray;
817
818                    for (int z = 0; z < sizeZ; z++)
819                    {
820                        final boolean[] mask = new boolean[sizeXY];
821
822                        for (int xy = 0; xy < mask.length; xy++)
823                            mask[xy] = (javaFloatArray[off++] != 0f);
824
825                        masks[z] = new BooleanMask2D(new Rectangle(bounds2D), mask);
826                    }
827                    break;
828
829                case DOUBLE:
830                    final double[] javaDoubleArray = (double[]) javaArray;
831
832                    for (int z = 0; z < sizeZ; z++)
833                    {
834                        final boolean[] mask = new boolean[sizeXY];
835
836                        for (int xy = 0; xy < mask.length; xy++)
837                            mask[xy] = (javaDoubleArray[off++] != 0d);
838
839                        masks[z] = new BooleanMask2D(new Rectangle(bounds2D), mask);
840                    }
841                    break;
842
843                default:
844                    // nothing to do here
845                    break;
846            }
847        }
848
849        final BooleanMask3D result = new BooleanMask3D(
850                new Rectangle3D.Integer(bounds2D.x, bounds2D.y, (int) origin[2], sizeX, sizeY, sizeZ), masks);
851
852        if (optimizeBounds)
853            result.optimizeBounds();
854
855        return result;
856    }
857
858    /**
859     * @deprecated Uses {@link #getBooleanMaskFromBinaryImage(vtkImageData)} then {@link ROI3DArea#ROI3DArea(BooleanMask3D)} instead.
860     */
861    public static ROI getROIFromBinaryImage(vtkImageData image, boolean force3DROI)
862    {
863        final BooleanMask3D mask = getBooleanMaskFromBinaryImage(image, true);
864
865        if ((mask.bounds.getSizeZ() > 1) || force3DROI)
866            return new ROI3DArea(mask);
867        else
868            return new ROI2DArea(mask.getMask2D(mask.bounds.z));
869    }
870
871    /**
872     * Build and return volume image data from given Sequence object.
873     * 
874     * @param sequence
875     *        the sequence object we want to get volume image data
876     * @param posT
877     *        frame index
878     * @param posC
879     *        channel index (-1 for all channel)
880     */
881    public static vtkImageData getImageData(Sequence sequence, int posT, int posC)
882            throws TooLargeArrayException, OutOfMemoryError
883    {
884        if ((sequence == null) || sequence.isEmpty())
885            return null;
886
887        final Object data;
888        final vtkImageData result;
889
890        if (posC == -1)
891        {
892            data = sequence.getDataCopyCXYZ(posT);
893            result = getImageData(data, sequence.getDataType_(), sequence.getSizeX(), sequence.getSizeY(),
894                    sequence.getSizeZ(), sequence.getSizeC());
895        }
896        else
897        {
898            data = sequence.getDataCopyXYZ(posT, posC);
899            result = getImageData(data, sequence.getDataType_(), sequence.getSizeX(), sequence.getSizeY(),
900                    sequence.getSizeZ(), 1);
901        }
902
903        return result;
904    }
905
906    /**
907     * Creates and returns a {@link vtkImageData} object from the specified 1D array data.
908     */
909    public static vtkImageData getImageData(Object data, DataType dataType, int sizeX, int sizeY, int sizeZ, int sizeC)
910    {
911        final vtkImageData result;
912        final vtkDataArray array;
913
914        // create a new image data structure
915        result = new vtkImageData();
916        result.SetDimensions(sizeX, sizeY, sizeZ);
917        result.SetExtent(0, sizeX - 1, 0, sizeY - 1, 0, sizeZ - 1);
918        // pre-allocate data
919        result.AllocateScalars(getVtkType(dataType), sizeC);
920        // get array structure
921        array = result.GetPointData().GetScalars();
922
923        switch (dataType)
924        {
925            case UBYTE:
926            case BYTE:
927                ((vtkUnsignedCharArray) array).SetJavaArray((byte[]) data);
928                break;
929            case USHORT:
930                ((vtkUnsignedShortArray) array).SetJavaArray((short[]) data);
931                break;
932            case SHORT:
933                ((vtkShortArray) array).SetJavaArray((short[]) data);
934                break;
935            case UINT:
936                ((vtkUnsignedIntArray) array).SetJavaArray((int[]) data);
937                break;
938            case INT:
939                ((vtkIntArray) array).SetJavaArray((int[]) data);
940                break;
941            case FLOAT:
942                ((vtkFloatArray) array).SetJavaArray((float[]) data);
943                break;
944            case DOUBLE:
945                ((vtkDoubleArray) array).SetJavaArray((double[]) data);
946                break;
947            default:
948                break;
949        }
950
951        return result;
952    }
953
954    /**
955     * Create a 3D surface in VTK polygon format from the input VTK image data.
956     * 
957     * @param imageData
958     *        the input image to construct surface from
959     * @param threshold
960     *        the threshold intensity value used to build the surface
961     */
962    public static vtkPolyData getSurfaceFromImage(vtkImageData imageData, double threshold)
963    {
964        // TODO: try vtkImageDataGeometryFilter
965
966        final int[] extent = imageData.GetExtent();
967        extent[0]--; // min X
968        extent[1]++; // max X
969        extent[2]--; // min Y
970        extent[3]++; // max Y
971        extent[4]--; // min Z
972        extent[5]++; // max Z
973
974        // pad on all sides to guarantee closed meshes
975        final vtkImageConstantPad pad = new vtkImageConstantPad();
976
977        pad.SetOutputWholeExtent(extent);
978        pad.SetInputData(imageData);
979        pad.Update();
980
981        final vtkImageData out = pad.GetOutput();
982        out.SetOrigin(imageData.GetOrigin());
983        // do not delete input image
984        pad.Delete();
985
986        final vtkContourFilter contourFilter = new vtkContourFilter();
987        contourFilter.SetInputData(out);
988        contourFilter.SetValue(0, threshold);
989        contourFilter.Update();
990
991        final vtkPolyData result = contourFilter.GetOutput();
992        contourFilter.GetInput().Delete();
993        contourFilter.Delete();
994
995        return result;
996    }
997
998    /**
999     * Creates and returns a 3D binary (0/1 values) {@link vtkImageData} object corresponding to the ROI 3D boolean mask
1000     * (C dimension is not considered) at specified T position.
1001     * 
1002     * @param roi
1003     *        the roi we want to retrieve the vtkImageData mask
1004     * @param sz
1005     *        the Z size to use for ROI with infinite Z dimension (if ROI has a finite Z dimension then ROI Z size is
1006     *        used).
1007     * @param t
1008     *        the T position we want to retrieve the 3D mask data
1009     */
1010    public static vtkImageData getBinaryImageData(ROI roi, int sz, int t) throws IllegalArgumentException
1011    {
1012        final vtkImageData result;
1013
1014        final Rectangle5D bounds5d = roi.getBounds5D();
1015        final int sizeX;
1016        final int sizeY;
1017        final int sizeZ;
1018        final int x;
1019        final int y;
1020        final int z;
1021        final int c;
1022
1023        x = (int) bounds5d.getX();
1024        y = (int) bounds5d.getY();
1025        sizeX = (int) (bounds5d.getMaxX() - x);
1026        sizeY = (int) (bounds5d.getMaxY() - y);
1027        if (bounds5d.isInfiniteZ())
1028        {
1029            z = 0;
1030            sizeZ = sz;
1031        }
1032        else
1033        {
1034            z = (int) bounds5d.getZ();
1035            sizeZ = (int) (bounds5d.getMaxZ() - z);
1036        }
1037        if (bounds5d.isInfiniteC())
1038            c = 0;
1039        else
1040            c = (int) bounds5d.getC();
1041
1042        long totalSize = sizeX;
1043        totalSize *= sizeY;
1044        totalSize *= sizeZ;
1045
1046        if (totalSize > Integer.MAX_VALUE)
1047            throw new IllegalArgumentException(
1048                    "VtkUtil.getBinaryImageData(ROI, ..): Input ROI is too large, can't allocate array (size > 2^31)");
1049
1050        // build java array
1051        final int sizeXY = sizeX * sizeY;
1052        final byte[] array = new byte[(int) totalSize];
1053        int offset = 0;
1054
1055        if (bounds5d.isInfiniteZ())
1056        {
1057            final boolean[] mask = roi.getBooleanMask2D(x, y, sizeX, sizeY, 0, t, c, true);
1058
1059            for (int curZ = z; curZ < (z + sizeZ); curZ++)
1060            {
1061                for (int i = 0; i < sizeXY; i++)
1062                    array[offset++] = mask[i] ? (byte) 1 : (byte) 0;
1063            }
1064        }
1065        else
1066        {
1067            for (int curZ = z; curZ < (z + sizeZ); curZ++)
1068            {
1069                final boolean[] mask = roi.getBooleanMask2D(x, y, sizeX, sizeY, curZ, t, c, true);
1070
1071                for (int i = 0; i < sizeXY; i++)
1072                    array[offset++] = mask[i] ? (byte) 1 : (byte) 0;
1073            }
1074        }
1075
1076        // create a new image data structure
1077        result = new vtkImageData();
1078        result.SetDimensions(sizeX, sizeY, sizeZ);
1079        result.SetExtent(0, sizeX - 1, 0, sizeY - 1, 0, sizeZ - 1);
1080        // pre-allocate data
1081        result.AllocateScalars(VTK_UNSIGNED_CHAR, 1);
1082        // set data
1083        ((vtkUnsignedCharArray) result.GetPointData().GetScalars()).SetJavaArray(array);
1084
1085        return result;
1086    }
1087
1088    /**
1089     * Transforms a {@link vtkPolyData} object to binary (0/1 values) {@link vtkImageData}
1090     * 
1091     * @param space
1092     *        spacing between each image point
1093     */
1094    public static vtkImageData getBinaryImageData(vtkPolyData polyData, double space[])
1095    {
1096        final vtkImageData whiteImage = new vtkImageData();
1097
1098        // get poly data bounds
1099        final double[] bounds = polyData.GetBounds();
1100        // define spacing
1101        final double[] spacing = (space == null) ? new double[] {1d, 1d, 1d} : space;
1102        // define dimensions & origin
1103        final int[] dim = new int[3];
1104        final double origin[] = new double[3];
1105
1106        // compute dimensions
1107        for (int i = 0; i < dim.length; i++)
1108            dim[i] = (int) Math.ceil((bounds[(i * 2) + 1] - bounds[(i * 2) + 0]) / spacing[i]);
1109
1110        long size = dim[0];
1111        size *= dim[1];
1112        size *= dim[2];
1113
1114        // negative value --> empty poly data
1115        if (size < 0)
1116            throw new IllegalArgumentException(
1117                    "VtkUtil.getBinaryImageData(vtkPolyData, ..): Negative object size, cannot do the conversion !");
1118        // can't allocate more than Integer.MAX_VALUE
1119        if (size > Integer.MAX_VALUE)
1120            throw new IllegalArgumentException(
1121                    "VtkUtil.getBinaryImageData(vtkPolyData, ..): Object size is too large, can't allocate array (size > 2^31) !");
1122
1123        // compute origin
1124        origin[0] = bounds[0];
1125        origin[1] = bounds[2];
1126        origin[2] = bounds[4];
1127
1128        whiteImage.SetSpacing(spacing);
1129        whiteImage.SetDimensions(dim);
1130        whiteImage.SetExtent(0, dim[0] - 1, 0, dim[1] - 1, 0, dim[2] - 1);
1131        whiteImage.SetOrigin(origin);
1132
1133        // allocate data
1134        whiteImage.AllocateScalars(VtkUtil.VTK_UNSIGNED_CHAR, 1);
1135
1136        // fill the image with foreground voxels
1137        final int len = whiteImage.GetNumberOfPoints();
1138        // allocate java array
1139        final byte[] javaArray = new byte[len];
1140        // get VTK array
1141        final vtkUnsignedCharArray vtkArray = (vtkUnsignedCharArray) whiteImage.GetPointData().GetScalars();
1142
1143        // build the java array 1 filled
1144        Arrays.fill(javaArray, (byte) 1);
1145        // set to VTK array
1146        vtkArray.SetJavaArray(javaArray);
1147
1148        // polygonal data --> image stencil
1149        final vtkPolyDataToImageStencil polyToImgStencil = new vtkPolyDataToImageStencil();
1150
1151        polyToImgStencil.SetInputData(polyData);
1152        polyToImgStencil.SetOutputOrigin(origin);
1153        polyToImgStencil.SetOutputSpacing(spacing);
1154        polyToImgStencil.SetOutputWholeExtent(whiteImage.GetExtent());
1155        // better to set tolerance to 0 (fastest and most permissive miss) for now
1156        // as more aggressive tolerance (up to 1) can add random points (known issue from VTK 6.3, maybe fixed in VTK 7.0 or >)
1157        polyToImgStencil.SetTolerance(0);
1158        polyToImgStencil.Update();
1159
1160        // cut the corresponding white image and set the background:
1161        final vtkImageStencil imageStencil = new vtkImageStencil();
1162        imageStencil.SetInputData(whiteImage);
1163        imageStencil.SetStencilConnection(polyToImgStencil.GetOutputPort());
1164        imageStencil.ReverseStencilOff();
1165        imageStencil.SetBackgroundValue(0d);
1166        imageStencil.Update();
1167
1168        final vtkImageData result = imageStencil.GetOutput();
1169
1170        // release VTK objects
1171        imageStencil.Delete();
1172        polyToImgStencil.Delete();
1173        whiteImage.GetPointData().GetScalars().Delete();
1174        whiteImage.GetPointData().Delete();
1175        whiteImage.Delete();
1176
1177        return result;
1178    }
1179
1180    /**
1181     * @deprecated Use {@link #getBinaryImageData(vtkPolyData, double[])} instead.
1182     */
1183    @Deprecated
1184    public static vtkImageData polyDataToImageData(vtkPolyData polyData, double space[])
1185    {
1186        return getBinaryImageData(polyData, space);
1187    }
1188
1189    /**
1190     * Get VTK transform object from specified transform infos
1191     */
1192    public static vtkTransform getTransform(double off[], double scale[], double rot[])
1193    {
1194        final double[] offset = (off == null) ? new double[] {0d, 0d, 0d} : off;
1195        final double[] scaling = (scale == null) ? new double[] {1d, 1d, 1d} : scale;
1196        final double[] rotation = (rot == null) ? new double[] {0d, 0d, 0d} : rot;
1197
1198        final vtkTransform result = new vtkTransform();
1199
1200        result.Translate(offset);
1201        result.Scale(scaling);
1202        result.RotateX(rotation[0]);
1203        result.RotateY(rotation[1]);
1204        result.RotateZ(rotation[2]);
1205        result.Update();
1206
1207        return result;
1208    }
1209
1210    /**
1211     * Transform a polyData using specified rotation, scaling and offset informations
1212     */
1213    public static vtkPolyData transformPolyData(vtkPolyData polyData, vtkAbstractTransform transform)
1214    {
1215        final vtkTransformPolyDataFilter transformFilter = new vtkTransformPolyDataFilter();
1216        transformFilter.SetInputData(polyData);
1217        transformFilter.SetTransform(transform);
1218        transformFilter.Update();
1219
1220        final vtkPolyData result = transformFilter.GetOutput();
1221
1222        transformFilter.Delete();
1223
1224        return result;
1225    }
1226
1227    /**
1228     * Transform a polyData using specified rotation, scaling and offset informations (3D)
1229     */
1230    public static vtkPolyData transformPolyData(vtkPolyData polyData, double off[], double scale[], double rot[])
1231    {
1232        final vtkTransform transform = getTransform(off, scale, rot);
1233        final vtkPolyData result = transformPolyData(polyData, transform);
1234        transform.Delete();
1235        return result;
1236    }
1237
1238    /**
1239     * Read a OBJ 3D model file and returns it in VTK mesh format ({@link vtkPolyData})
1240     */
1241    public static vtkPolyData getSurfaceFromOBJ(String objPath)
1242    {
1243        final vtkOBJReader reader = new vtkOBJReader();
1244
1245        reader.SetFileName(objPath);
1246        reader.Update();
1247
1248        final vtkPolyData result = reader.GetOutput();
1249
1250        reader.Delete();
1251
1252        return result;
1253    }
1254
1255    /**
1256     * Creates and returns the color map in {@link vtkColorTransferFunction} format from the
1257     * specified {@link LUTChannel}.
1258     */
1259    public static vtkColorTransferFunction getColorMap(LUTChannel lutChannel)
1260    {
1261        final IcyColorMap colorMap = lutChannel.getColorMap();
1262        final Scaler scaler = lutChannel.getScaler();
1263
1264        // SCALAR COLOR FUNCTION
1265        final vtkColorTransferFunction result = new vtkColorTransferFunction();
1266
1267        result.SetRange(scaler.getLeftIn(), scaler.getRightIn());
1268        for (int i = 0; i < IcyColorMap.SIZE; i++)
1269        {
1270            result.AddRGBPoint(scaler.unscale(i), colorMap.getNormalizedRed(i), colorMap.getNormalizedGreen(i),
1271                    colorMap.getNormalizedBlue(i));
1272        }
1273
1274        return result;
1275    }
1276
1277    /**
1278     * Creates and returns the opacity map in {@link vtkPiecewiseFunction} format from the specified {@link LUTChannel}.
1279     */
1280    public static vtkPiecewiseFunction getOpacityMap(LUTChannel lutChannel)
1281    {
1282        final IcyColorMap colorMap = lutChannel.getColorMap();
1283        final Scaler scaler = lutChannel.getScaler();
1284
1285        // SCALAR OPACITY FUNCTION
1286        final vtkPiecewiseFunction result = new vtkPiecewiseFunction();
1287
1288        if (colorMap.isEnabled())
1289        {
1290            for (int i = 0; i < IcyColorMap.SIZE; i++)
1291                result.AddPoint(scaler.unscale(i), colorMap.getNormalizedAlpha(i));
1292        }
1293        else
1294        {
1295            for (int i = 0; i < IcyColorMap.SIZE; i++)
1296                result.AddPoint(scaler.unscale(i), 0d);
1297        }
1298
1299        return result;
1300    }
1301
1302    /**
1303     * Creates and returns a binary color map in {@link vtkColorTransferFunction} format where 0 value is black and 1 is
1304     * set to specified color.
1305     */
1306    public static vtkColorTransferFunction getBinaryColorMap(Color color)
1307    {
1308        // SCALAR COLOR FUNCTION
1309        final vtkColorTransferFunction result = new vtkColorTransferFunction();
1310
1311        result.SetRange(0, 1);
1312        result.AddRGBPoint(0d, 0d, 0d, 0d);
1313        result.AddRGBPoint(1d, color.getRed() / 255d, color.getGreen() / 255d, color.getBlue() / 255d);
1314
1315        return result;
1316    }
1317
1318    /**
1319     * Creates and returns a binary opacity map in {@link vtkPiecewiseFunction} format where 0 is 100% transparent and 1
1320     * to the specified opacity value.
1321     */
1322    public static vtkPiecewiseFunction getBinaryOpacityMap(double opacity)
1323    {
1324        // SCALAR OPACITY FUNCTION
1325        final vtkPiecewiseFunction result = new vtkPiecewiseFunction();
1326
1327        result.AddPoint(0d, 1d);
1328        result.AddPoint(1d, opacity);
1329
1330        return result;
1331    }
1332
1333    /**
1334     * Set the Color of the specified {@link vtkPolyData} object.
1335     * 
1336     * @param polyData
1337     *        the vtkPolyData we want to change color
1338     * @param color
1339     *        the color to set
1340     * @param canvas
1341     *        the VtkCanvas object to lock during the color change operation for safety (can be <code>null</code> if we
1342     *        don't need to lock the VtkCanvas here)
1343     */
1344    public static void setPolyDataColor(vtkPolyData polyData, Color color, VtkCanvas canvas)
1345    {
1346        final int numPts = polyData.GetNumberOfPoints();
1347        vtkUnsignedCharArray colors = null;
1348
1349        // try to recover colors object
1350        if (polyData.GetPointData() != null)
1351        {
1352            final vtkDataArray dataArray = polyData.GetPointData().GetScalars();
1353
1354            if (dataArray instanceof vtkUnsignedCharArray)
1355                colors = (vtkUnsignedCharArray) dataArray;
1356            // delete it
1357            else if (dataArray != null)
1358                dataArray.Delete();
1359        }
1360
1361        // colors is not correctly defined ? --> reallocate
1362        if ((colors == null) || (colors.GetNumberOfTuples() != numPts) || (colors.GetNumberOfComponents() != 3))
1363        {
1364            // delete first
1365            if (colors != null)
1366                colors.Delete();
1367
1368            // and reallocate
1369            colors = new vtkUnsignedCharArray();
1370            colors.SetNumberOfComponents(3);
1371            colors.SetNumberOfTuples(numPts);
1372            // set colors array
1373            polyData.GetPointData().SetScalars(colors);
1374        }
1375
1376        final int len = numPts * 3;
1377
1378        final byte r = (byte) color.getRed();
1379        final byte g = (byte) color.getGreen();
1380        final byte b = (byte) color.getBlue();
1381        final byte[] data = new byte[len];
1382
1383        for (int i = 0; i < len; i += 3)
1384        {
1385            data[i + 0] = r;
1386            data[i + 1] = g;
1387            data[i + 2] = b;
1388        }
1389
1390        final IcyVtkPanel vtkPanel = (canvas != null) ? canvas.getVtkPanel() : null;
1391
1392        if (vtkPanel != null)
1393        {
1394            vtkPanel.lock();
1395            try
1396            {
1397                colors.SetJavaArray(data);
1398                colors.Modified();
1399            }
1400            finally
1401            {
1402                vtkPanel.unlock();
1403            }
1404        }
1405        else
1406        {
1407            colors.SetJavaArray(data);
1408            colors.Modified();
1409        }
1410    }
1411
1412    /**
1413     * Returns a cube polydata object representing the specified bounding box coordinate
1414     * 
1415     * @see #setOutlineBounds(vtkPolyData, double, double, double, double, double, double, VtkCanvas)
1416     */
1417    public static vtkPolyData getOutline(double xMin, double xMax, double yMin, double yMax, double zMin, double zMax)
1418    {
1419        final double points[][] = new double[8][3];
1420        final int indexes[][] = {{0, 2, 3, 1}, {4, 5, 7, 6}, {0, 1, 5, 4}, {1, 3, 7, 5}, {0, 4, 6, 2}, {3, 2, 6, 7}};
1421
1422        for (int i = 0; i < 8; i++)
1423        {
1424            points[i][0] = ((i & 1) == 0) ? xMin : xMax;
1425            points[i][1] = ((i & 2) == 0) ? yMin : yMax;
1426            points[i][2] = ((i & 4) == 0) ? zMin : zMax;
1427        }
1428
1429        final vtkCellArray vCells = VtkUtil.getCells(6, prepareCells(indexes));
1430        final vtkPoints vPoints = VtkUtil.getPoints(points);
1431        final vtkPolyData result = new vtkPolyData();
1432
1433        result.SetPolys(vCells);
1434        result.SetPoints(vPoints);
1435
1436        return result;
1437    }
1438
1439    /**
1440     * Set the bounds of specified outline polydata object (previously created with <i>VtkUtil.getOutline(..)</i>)
1441     * 
1442     * @param canvas
1443     *        the VtkCanvas object to lock during the color change operation for safety (can be <code>null</code> if we
1444     *        don't need to lock the VtkCanvas here)
1445     * @return <code>false</code> if the specified polydata object is not a valid outline object
1446     * @see #getOutline(double, double, double, double, double, double)
1447     */
1448    public static boolean setOutlineBounds(vtkPolyData outline, double xMin, double xMax, double yMin, double yMax,
1449            double zMin, double zMax, VtkCanvas canvas)
1450    {
1451        final vtkPoints previousPoints = outline.GetPoints();
1452
1453        // not valid
1454        if ((previousPoints != null) && (previousPoints.GetNumberOfPoints() != 8))
1455            return false;
1456
1457        final double newPoints[][] = new double[8][3];
1458        for (int i = 0; i < 8; i++)
1459        {
1460            newPoints[i][0] = ((i & 1) == 0) ? xMin : xMax;
1461            newPoints[i][1] = ((i & 2) == 0) ? yMin : yMax;
1462            newPoints[i][2] = ((i & 4) == 0) ? zMin : zMax;
1463        }
1464
1465        final vtkPoints points = getPoints(newPoints);
1466        final IcyVtkPanel vtkPanel = (canvas != null) ? canvas.getVtkPanel() : null;
1467
1468        if (vtkPanel != null)
1469        {
1470            vtkPanel.lock();
1471            try
1472            {
1473                // rebuild points
1474                outline.SetPoints(points);
1475                // changed
1476                outline.Modified();
1477                // delete previous points
1478                if (previousPoints != null)
1479                    previousPoints.Delete();
1480            }
1481            finally
1482            {
1483                vtkPanel.unlock();
1484            }
1485        }
1486        else
1487        {
1488            // rebuild points
1489            outline.SetPoints(points);
1490            // changed
1491            outline.Modified();
1492            // delete previous points
1493            if (previousPoints != null)
1494                previousPoints.Delete();
1495        }
1496
1497        return true;
1498    }
1499
1500    /**
1501     * VTK forced garbage collection
1502     */
1503    public static void vtkGC()
1504    {
1505        vtkObjectBase.JAVA_OBJECT_MANAGER.gc(false);
1506    }
1507}