public abstract class Boundary2DScanner
extends java.lang.Object
2dimensional object boundaries scanner: the class allowing to trace boundaries of objects, "drawn" at some 2dimensional bit matrix.
More precisely, let's consider some 2dimensional AlgART bit matrix
Matrix
<? extends BitArray
>.
Below we shall designate this matrix as M.
Let's define a pixel with integer coordinates (x, y)
as a set of points of the plane with such coordinates (x', y') that
Every unit element of the matrix M with coordinates
(x, y) corresponds to a pixel (x, y) at the plane.
Let's designate IM the figure (point set) consisting of all points of all pixels
(squares with the side 1.0), corresponding to unit (1) elements of our matrix M.
We can consider IM as an image (figure), "drawn" at the matrix M.
Every unit element in M represents a little square 1x1 (a pixel) in the image (figure) IM,
and the center of the pixel has integer coordinates in ranges
dimX()
−1,dimY()
−1.
Then, let's consider a connected object at the matrix M, defined in the same terms
as in ConnectedObjectScanner
class, and corresponding connected figure
in the image IM. As well as in that class, a connected object can have straightanddiagonal
connectivity (8connected object) or straight connectivity (4connected object).
The first case corresponds to a usual connected area in the image IM,
the second case — to a connected area in the figure, coming out from IM by removing
all points with halfinteger coordinates.
We define the boundary of some connected object as the geometrical boundary of the corresponding connected figure in the image IM. More precisely, the boundary of the connected object is a connected component of the full set of the boundary points (not pixels, but infinitesimal points of the plane) of the corresponding connected figure. So, the connected object can have several boundaries, if there are some "holes" in it. Any boundary is a chain of horizontal or vertical segments with the length 1.0, that separate pixels from each others. The ends of each segment have halfinteger coordinates, and the 2nd end of the last segment coincides with the 1st end of the first segment.
We define the main boundary of the connected object as its boundary containing whole this object inside it.
We define the completion of the connected object as the sets of all points lying at or inside its main boundary. In other words, the completion is the object, where all internal "holes" are filled. If the connected object has no "holes", its completion is identical to it.
Each segment with length 1.0 in any object boundary is a boundary of some pixel, belonging to the image IM. These pixels can lie 1) inside the boundary, and then it is true for all segments of the boundary, or 2) outside the boundary, and then it is true for all segments of the boundary.
In the first case we shall call the boundary as external, and in the second case we shall call it as internal. A connected object always have only one external boundary, namely, its main boundary. But a connected object can have several internal boundaries: these are boundaries of all its "holes".
This class represents a boundary scanner: an iterator allowing to trace all segments
of one boundary — in the clockwise order for external boundaries, in the anticlockwise order
for internal boundaries (if the x axis is directed rightwards and
the y axis is directed downwards). The basic method of this iterator is next()
.
In addition, this class allows to sequentially visit all boundaries or all main boundaries
of all connected objects; it is performed by the method nextBoundary()
.
The boundary scanner always has the current position. The position consists of:
x()
coordinate of the current pixel;y()
coordinate of the current pixel;side()
: index of one of 4 sides of the square 1x1,
represented by Boundary2DScanner.Side
enumeration class.There is the only exception, when the scanner has no any position —
directly after creating new instance. The first call of nextBoundary
or goTo
method sets some position.
In other words, the current position specifies some segment with length 1.0. This segment can be an element of some object boundary, but also can be a random pixel side in the image.
There are two basic methods of this class, changing the current position. The first method is
nextBoundary()
: it moves the current position to the nearest next object boundary,
according to some rules depending on a concrete kind of scanner. After calling this method you may be sure
that the current position specifies a segment of some object boundary.
The second method is next()
: it supposes that the current position specifies a segment of a boundary
and, if it's true, moves the current position to the next segment of this boundary.
So, you can find the next object boundary by nextBoundary()
method
and then scan it by sequential calls of next()
method.
Instead of manual loop of next()
calls, you can use scanBoundary(ArrayContext)
method.
We suppose that all possible positions are sorted in the following "natural" order: the position x_{1}, y_{1}, side_{1} is "less" than the position x_{2}, y_{2}, side_{2},
ordinal()
<side_{2}.ordinal()
X_MINUS
<Y_MINUS
<X_PLUS
<Y_PLUS
).We also suppose that the "undefined" position, when the scanner is newly created and
nextBoundary
or goTo
methods
were not called yet, is "less" than all other positions.
This order is used by nextBoundary()
method.
There are the following ways to create an instance of this class:
getSingleBoundaryScanner(Matrix, ConnectivityType)
,getAllBoundariesScanner(Matrix, Matrix, Matrix, ConnectivityType)
,getMainBoundariesScanner(Matrix, Matrix, ConnectivityType)
,Boundary2DWrapper
class,Boundary2DSimpleMeasurer
or Boundary2DProjectionMeasurer
.The difference between instances, created by first 3 methods, is in the behavior of
nextBoundary()
and next()
: see comments to these instantiation methods.
The Boundary2DWrapper
class and its inheritors just call some parent boundary scanner and,
maybe, do some additional work (for example, measure the objects).
The instance of this class always works with some concrete matrix and some concrete connectivity type, specified while creating the instance, and you cannot switch an instance of this class to another bit matrix. But this class is lightweight: there is no problem to create new instances for different matrices.
You must not use this instance after any modifications in the scanned matrix, performed by an external code. If you modify the matrix, you must create new instance of this class after this.
Below is a typical example of using this class:
Boundary2DScanner
scanner =Boundary2DScanner.getAllBoundariesScanner
(m, um1, um2, connectivityType);Boundary2DSimpleMeasurer
measurer =Boundary2DSimpleMeasurer.getInstance
(scanner, EnumSet.of(Boundary2DSimpleMeasurer.ObjectParameter.AREA
)); while (measurer.nextBoundary()
) { measurer.checkInterruption
(ac); measurer.updateProgress
(ac); measurer.scanBoundary
(ac); long area = measurer.area()
; // some operations with the found area }
Note: this class works much faster (in several times)
if the scanned matrix is created by SimpleMemoryModel
,
especially if its horizontal dimension dimX()
is divisible by 64
(dimX()
%64==0).
So, if the matrix is not created by SimpleMemoryModel
and is not too large,
we recommend to create its clone by SimpleMemoryModel
,
expanded by x to the nearest integer divisible by 64, and use this class for the clone.
Note: this class can process only 2dimensional matrices. An attempt to create an instance of this class for a matrix with other number of dimensions leads to IllegalArgumentException.
This class does not use multithreading optimization, unlike
Arrays.copy(ArrayContext, UpdatableArray, Array)
and similar methods.
In other words, all methods of this class are executed in the current thread.
This class is not threadsafe, but is threadcompatible and can be synchronized manually, if multithread access is necessary. Warning! Even if you use in several different threads different instances of this class, created via one of the following methods:
then you either must pass different buffer matrices in different threads, or manually synchronize all called methods. In other case, the content of buffer matrices will be unspecified and behavior of the scanning algorithm will be undefined.Modifier and Type  Class and Description 

static class 
Boundary2DScanner.Side
The pixel side.

static class 
Boundary2DScanner.Step
The step of scanning the boundary: moving from one boundary segment to the next boundary segment.

Modifier and Type  Method and Description 

abstract boolean 
boundaryFinished()
Returns true if and only if the current position (
x() , y() , side() )
is identical to the position, set by last call of nextBoundary() or
goTo(long, long, net.algart.matrices.scanning.Boundary2DScanner.Side) method. 
void 
checkInterruption(ArrayContext context)
Calls context.
checkInterruption() or
does nothing if context==null. 
abstract ConnectivityType 
connectivityType()
Returns the connectivity kind, used by this object.

abstract boolean 
coordinatesChanged()

abstract long 
currentIndexInArray()
Returns the index of the current pixel in the
underlying array of the currently
scanned matrix. 
abstract boolean 
get()
Returns the value of the current element of the currently scanned matrix.

static Boundary2DScanner 
getAllBoundariesScanner(Matrix<? extends BitArray> matrix,
Matrix<? extends UpdatablePFixedArray> buffer1,
Matrix<? extends UpdatablePFixedArray> buffer2,
ConnectivityType connectivityType)
Creates an instance of this class, allowing to sequentially trace all segments of all boundaries
at the matrix (internal and external).

static Boundary2DScanner 
getMainBoundariesScanner(Matrix<? extends BitArray> matrix,
Matrix<? extends UpdatablePFixedArray> buffer,
ConnectivityType connectivityType)
Creates an instance of this class, allowing to trace all segments of main boundaries
at the matrix and to build completions of all objects.

static Boundary2DScanner 
getSingleBoundaryScanner(Matrix<? extends BitArray> matrix,
ConnectivityType connectivityType)
Creates an instance of the simplest kind of this class,
allowing to trace all segments of a single boundary (internal or external).

abstract void 
goTo(long x,
long y,
Boundary2DScanner.Side side)
Sets the current position in the matrix to the specified coordinates and pixel side.

void 
goToSamePosition(Boundary2DScanner scanner)
Sets the current position in the matrix to the same as in the specified scanner.

abstract boolean 
isAllBoundariesScanner()
Returns true if and only if this scanner is an all boundaries scanner.

abstract boolean 
isInitialized()
Returns true if and only if this instance was positioned to some coordinates in the matrix.

boolean 
isInternalBoundary()
Returns true if and only if
side() == Side.X_PLUS 
abstract boolean 
isMainBoundariesScanner()
Returns true if and only if this scanner is a main boundaries scanner.

abstract boolean 
isMovedAlongBoundary()
Returns true if and only if this scanner is already positioned
(
isInitialized() returns true) and, in addition, next() or
scanBoundary(ArrayContext) methods were called at least once. 
abstract boolean 
isSingleBoundaryScanner()
Returns true if and only if this scanner is a single boundary scanner.

abstract Boundary2DScanner.Step 
lastStep()
Returns information about the movement of the current position, performed by the last call of
next() method. 
Matrix<? extends BitArray> 
matrix()
Returns the reference to the currently scanned matrix.

abstract long 
nestingLevel()

abstract void 
next()
Move the current position to the next segment of the currently scanned object boundary.

abstract boolean 
nextBoundary()
Finds the next vertical segment, belonging to some object boundary,
after the current position, and sets the current position to the found one.

long 
scanBoundary()
Equivalient of
scanBoundary(null) . 
long 
scanBoundary(ArrayContext context)
Scans the current boundary.

abstract Boundary2DScanner.Side 
side()
Returns the current pixel side (or throws IllegalStateException if this scanner
was not
positioned yet ). 
abstract long 
stepCount()
Returns the total number of calls of
next() method since the last call nextBoundary() or
goTo(long, long, net.algart.matrices.scanning.Boundary2DScanner.Side) method. 
java.lang.String 
toString()
Returns a brief string description of this object.

void 
updateProgress(ArrayContext context)
Calls context.
updateProgress(event)
with an event, created by the following operator:
currentIndexInArray() , matrix() .size() ) 
abstract long 
x()
Returns the current xcoordinate (or throws IllegalStateException if the scanner
was not
positioned yet ). 
abstract long 
y()
Returns the current ycoordinate (or throws IllegalStateException if this scanner
was not
positioned yet ). 
public static Boundary2DScanner getSingleBoundaryScanner(Matrix<? extends BitArray> matrix, ConnectivityType connectivityType)
In the created instance:
nextBoundary()
method finds (after the current position)
the nearest vertical segment, belonging to some object boundary,
sets the current position to the found one and does nothing else.
next()
method switches to the next segment in the current object boundary and does nothing else.
This instance does not save anywhere the fact of tracing the boundary.
So, it is not convenient for scanning all boundaries of some kind in the matrix:
nextBoundary()
method will find the same boundary many times, at least 2 times
for every horizontal line intersecting the boundary.
matrix
 the matrix that will be scanned by the created instance.connectivityType
 the connectivity kind used by the created instance.java.lang.NullPointerException
 if matrix or connectivityType argument is null.java.lang.IllegalArgumentException
 if matrix.dimCount()
is not 2.public static Boundary2DScanner getAllBoundariesScanner(Matrix<? extends BitArray> matrix, Matrix<? extends UpdatablePFixedArray> buffer1, Matrix<? extends UpdatablePFixedArray> buffer2, ConnectivityType connectivityType)
The scanner, created by this method, works with two additional matrices buffer1 and buffer2, that are used for marking already visited boundary segments. These matrices can have any fixedpoint element type (but usually it is boolean) and must have the same dimensions as the main matrix. These matrices should be zeroinitialized before using the created instance (in other case, some boundaries are possible to be skipped). One of these matrices is always current. In the state 1, the current buffer matrix is buffer1; in the state 2, the current buffer matrix is buffer2. The state 1 is default: it is chosen after creating the scanner.
While scanning boundaries, inside the next()
method, this scanner writes "brackets" in the current
buffer matrix. It means that:
current pixel side
is
X_MINUS
, the element with coordinates x()
,y()
in the current buffer matrix is set to 1 ("opening bracket"),current pixel side
is
X_PLUS
, the element with coordinates x()
+1,y()
in the current buffer matrix is set to 1 ("closing bracket"), or nothing occurs if
x()
+1>=matrix.dimX()
,current pixel side
is
Y_MINUS
or Y_PLUS
.This behavior is the same as in main boundaries scanner created by
getMainBoundariesScanner
method.
The nextBoundary()
method in this scanner finds (after the current position)
the nearest vertical segment, belonging to some object boundary,
which was not visited yet by next()
method,
and sets the current position to the found one.
"Not visited" means that no "brackets" are set for that position neither in buffer1
nor in buffer2 matrix.
(There is the only exception from this simple rule:
this method never stops at the right side of a last pixel in the horizontal line.
If the last element in the horizontal line is 1, the corresponding boundary —
its right side — is always skipped, and
nextBoundary()
method searches for the next unit element in next lines.
The only case when it can be important is calling nextBoundary()
after
direct positioning by goTo(long, long, net.algart.matrices.scanning.Boundary2DScanner.Side)
method.)
If the new found position corresponds to a left pixel side
(side()
is Side.X_MINUS
),
this method changes the current state to state 1.
It means an external boundary, if the scanning the matrix was started outside any boundaries,
in particular, if goTo
method was never called.
If the new found position corresponds to a right pixel side
(side()
is Side.X_PLUS
),
this method changes the current state to state 2
It means an internal boundary, if the scanning the matrix was started outside any boundaries,
in particular, if goTo
method was never called.
While searching the next nonvisited boundary, nextBoundary()
method counts "brackets"
in 1st and 2nd buffer matrices and corrects the current nesting level
.
It is possible to specify the same matrix as both buffer1 and buffer2 arguments.
In this case, all will work normally excepting the nesting level
,
which will be calculated incorrectly.
This instance is convenient for scanning all boundaries in the matrix. To do this, it's possible to use the following loop:
Boundary2DScanner
scanner =getAllBoundariesScanner
(m, um1, um2, connectivityType); while (scanner.nextBoundary()
) { scanner.scanBoundary
(ac); // or some more useful actions }
matrix
 the matrix that will be scanned by the created instance.buffer1
 the 1st buffer matrix for writing "brackets" (usually indicates external boundaries).buffer2
 the 2nd buffer matrix for writing "brackets" (usually indicates internal boundaries).
To save memory, you may pass here the same matrix as buffer1
and buffer2 arguments, but in this case the nestingLevel()
method
will work incorrectly.connectivityType
 the connectivity kind used by the created instance.java.lang.NullPointerException
 if matrix or connectivityType argument is null.java.lang.IllegalArgumentException
 if matrix.dimCount()
is not 2.SizeMismatchException
 if the passed matrices have different dimensions.public static Boundary2DScanner getMainBoundariesScanner(Matrix<? extends BitArray> matrix, Matrix<? extends UpdatablePFixedArray> buffer, ConnectivityType connectivityType)
The scanner, created by this method, works with the additional matrix buffer, where completions of all objects are stored as a result of the scanning. This matrix can have any fixedpoint element type (but usually it is boolean) and must have the same dimensions as the main matrix. This matrix should be zeroinitialized before using the created instance (in other case, some boundaries are possible to be skipped).
While scanning boundaries, inside the next()
method, this scanner writes "brackets" in the
buffer matrix. It means that:
current pixel side
is
X_MINUS
, the element with coordinates x()
,y()
in the buffer matrix is set to 1 ("opening bracket"),current pixel side
is
X_PLUS
, the element with coordinates x()
+1,y()
in the buffer matrix is set to 1 ("closing bracket"), or nothing occurs if
x()
+1>=matrix.dimX()
,current pixel side
is
Y_MINUS
or Y_PLUS
.This behavior is the same as in all boundaries scanner created by
getAllBoundariesScanner
method.
The nextBoundary()
method in this scanner is more complicated.
If the element of the buffer matrix at the current position is zero,
it just finds the nearest vertical segment,
belonging to some object boundary,
after the current position (alike in the simplest scanner returned by
getSingleBoundaryScanner
method).
In other case we suppose that we are at the "open bracket" (the beginning of a series of unit elements),
and nextBoundary()
method does the following:
If the next unit element was not found in the current line at step 1,
all buffer elements until the end of the horizontal line are filled by 1 —
the p index is supposed to be matrix()
.dimX()
— and the step 3 is skipped.
In fact, nextBoundary()
method skips all interior of previously scanned boundaries and fills
this interior by 1 in the buffer matrix. As a result, the buffer matrix will contain
completions
of all objects after finishing scanning the matrix.
This instance is convenient for scanning main boundaries in the matrix and, as a side effect, for calculating completions of all objects. To do this, it's possible to use the following loop:
Boundary2DScanner
scanner =getMainBoundariesScanner
(m, um, connectivityType); while (scanner.nextBoundary()
) { scanner.scanBoundary
(ac); // or some more useful actions } // now um contains the completions of all objects drawn in m // (if um was initially zerofilled)
matrix
 the matrix that will be scanned by the created instance.buffer
 the buffer matrix for writing "brackets" and filling holes.connectivityType
 the connectivity kind used by the created instance.java.lang.NullPointerException
 if matrix or connectivityType argument is null.java.lang.IllegalArgumentException
 if matrix.dimCount()
is not 2.SizeMismatchException
 if the passed matrices have different dimensions.public abstract boolean isSingleBoundaryScanner()
getSingleBoundaryScanner(net.algart.arrays.Matrix<? extends net.algart.arrays.BitArray>, net.algart.matrices.scanning.ConnectivityType)
methodBoundary2DWrapper
and this method of its parent scanner
returns true.public abstract boolean isAllBoundariesScanner()
getAllBoundariesScanner(net.algart.arrays.Matrix<? extends net.algart.arrays.BitArray>, net.algart.arrays.Matrix<? extends net.algart.arrays.UpdatablePFixedArray>, net.algart.arrays.Matrix<? extends net.algart.arrays.UpdatablePFixedArray>, net.algart.matrices.scanning.ConnectivityType)
methodBoundary2DWrapper
and this method of its parent scanner
returns true.public abstract boolean isMainBoundariesScanner()
getMainBoundariesScanner(net.algart.arrays.Matrix<? extends net.algart.arrays.BitArray>, net.algart.arrays.Matrix<? extends net.algart.arrays.UpdatablePFixedArray>, net.algart.matrices.scanning.ConnectivityType)
methodBoundary2DWrapper
and this method of its parent scanner
returns true.public final Matrix<? extends BitArray> matrix()
getSingleBoundaryScanner
,
getAllBoundariesScanner
or
getMainBoundariesScanner
,
the first argument of those methods is returned.
If this instance is Boundary2DWrapper
, the result of matrix()
method
of the parent scanner is returned.public abstract ConnectivityType connectivityType()
public abstract boolean isInitialized()
nextBoundary()
, goTo
, goToSamePosition
methods were called yet, or true in all other cases.
If this instance is Boundary2DWrapper
, the result of this method for the parent scanner is returned.
If this object is not positioned, most of methods, processing pixels in the current position,
throw IllegalStateException.nextBoundary
or
goTo
method.public abstract boolean isMovedAlongBoundary()
isInitialized()
returns true) and, in addition, next()
or
scanBoundary(ArrayContext)
methods were called at least once.
This information can be useful before calling lastStep()
method
(for example, for debugging goals): that method throws IllegalStateException
if and only if this method returns false.
next()
or scanBoundary(ArrayContext)
methods
were successfully called after creating this instance.public abstract long x()
positioned yet
).java.lang.IllegalStateException
 if this scanner was not positioned yet
.public abstract long y()
positioned yet
).java.lang.IllegalStateException
 if this scanner was not positioned yet
.public abstract Boundary2DScanner.Side side()
positioned yet
).java.lang.IllegalStateException
 if this scanner was not positioned yet
.public abstract long nestingLevel()
x()
, y()
, side()
— is located.
(Here we suppose, that if the current pixel side lies at some boundary,
then it lies inside this boundary.)
Just after creating an instance of this class the nesting level is 0.
After the first call of nextBoundary()
it becomes 1.
After finding the first internal boundary (if it exists) by nextBoundary()
the nesting level becomes 2.
After each intersection of a boundary while searching for the next boundary
the nesting level is increased by 1 or decreased by 1.
So, odd values of the nesting level correspond to external boundaries
and even values correspond to internal boundaries, excepting the case of a newly created instance
(the only case when it is 0).
Please note: the nesting level is supported only
getAllBoundariesScanner(Matrix, Matrix, Matrix, ConnectivityType)
method;goTo(long, long, net.algart.matrices.scanning.Boundary2DScanner.Side)
method was never called;Boundary2DWrapper
, the result of this method for the parent scanner
is returned.If this scanner was created via
getSingleBoundaryScanner(Matrix, ConnectivityType)
or
getMainBoundariesScanner(Matrix, Matrix, ConnectivityType)
,
the returned nesting level is always 0.
In all other cases, the result of this method is not specified.
public abstract long currentIndexInArray()
underlying array
of the currently
scanned matrix. This method is almost equivalent to
y()
* matrix()
.dimX()
+ x()
positioned yet
:
in the last case it returns 0.public abstract void goTo(long x, long y, Boundary2DScanner.Side side)
Usually this method is not necessary for scanners, created by
getAllBoundariesScanner
and
getMainBoundariesScanner
methods:
it is enough to use nextBoundary()
and next()
(or scanBoundary(ArrayContext)
)
methods to visit all object boundaries at the matrix.
But this method may be helpful if you need to scan a single boundary (for example,
that was found by another scanner).
public final void goToSamePosition(Boundary2DScanner scanner)
goTo
(scanner.x()
, scanner.y()
, scanner.side()
).scanner
 some other scanner.java.lang.NullPointerException
 if scanner argument is null.java.lang.IllegalStateException
 if the specified scanner was not positioned yet
.java.lang.IndexOutOfBoundsException
 in the same situations as goTo
method
(impossible if the currently scanned matrices of this and passed scanners
have identical dimensions).public abstract boolean get()
matrix()
.array()
.getBit
(currentIndexInArray()
)positioned yet
;
in this case, it returns the value of (0,0) matrix element
(i.e. matrix()
.array()
.getBit(0)
public abstract boolean nextBoundary()
More precisely, it finds some "next" position after the current position,
in the natural order,
where the side is X_MINUS
or X_PLUS
and one from two matrix elements on the left and on the right from the specified segment (pixel side) is 1,
but another from these two elements is 0 or lies outside the matrix.
If this scanner was not positioned yet
, this method finds the first
such position.
The precise sense of the "next" term above depends on the kind of the boundary scanner.
getSingleBoundaryScanner(Matrix, ConnectivityType)
method, it is just the nearest possible position
(in the natural order).getAllBoundariesScanner(Matrix, Matrix, Matrix, ConnectivityType)
method, it is the nearest boundary segment that was not visited yet by next()
method.getMainBoundariesScanner(Matrix, Matrix, ConnectivityType)
method, it is the nearest boundary segment that was not visited yet by next()
method
and that does not lie inside some already scanned boundary.In addition to searching for the next position, this method may do something else:
see comments to methods getSingleBoundaryScanner
,
getAllBoundariesScanner
,
getMainBoundariesScanner
.
This method returns true if it can find the necessary "next" position, or false if there is no required position, i.e. if the matrix scanning is finished. In the second case, the current position is not changed.
Note that if this scanner was not positioned yet
, it becomes positioned
if this method returns true, but stays not positioned if it returns false.
public abstract void next()
If the current position does not correspond to an object boundary, the position will be changed to some unknown position near the current one (precise behavior is not specified).
In addition to switching to the next position, this method can do something else:
see comments to methods
getSingleBoundaryScanner
,
getAllBoundariesScanner
,
getMainBoundariesScanner
,
and comments to classes Boundary2DSimpleMeasurer
, Boundary2DProjectionMeasurer
.
java.lang.IllegalStateException
 if this scanner was not positioned yet
.public abstract Boundary2DScanner.Step lastStep()
next()
method.
If that method was never called (in particular, as a part of scanBoundary(ArrayContext)
),
this method throws IllegalStateException. You can check this situation with help of
isMovedAlongBoundary()
method.
next()
method.java.lang.IllegalStateException
 if next()
(or scanBoundary(ArrayContext)
) method was never
called for this instance.isMovedAlongBoundary()
public abstract boolean coordinatesChanged()
next()
method has changed x()
or y()
coordinate. Returns false if the last call of next()
method has changed only the
current pixel side
.
Equivalent to
!lastStep()
.isSamePixel()
,
but works little faster.
next()
method has changed current pixel coordinates.java.lang.IllegalStateException
 if next()
(or scanBoundary(ArrayContext)
) method was never
called for this instance.public abstract boolean boundaryFinished()
x()
, y()
, side()
)
is identical to the position, set by last call of nextBoundary()
or
goTo(long, long, net.algart.matrices.scanning.Boundary2DScanner.Side)
method.
Usually it means that the current boundary has been successfully scanned.public abstract long stepCount()
next()
method since the last call nextBoundary()
or
goTo(long, long, net.algart.matrices.scanning.Boundary2DScanner.Side)
method.next()
method and reset to zero while object creation
and while every call of nextBoundary()
or goTo(long, long, net.algart.matrices.scanning.Boundary2DScanner.Side)
method.public final boolean isInternalBoundary()
side()
== Side.X_PLUS
getAllBoundariesScanner(Matrix, Matrix, Matrix, ConnectivityType)
method.side()
== Side.X_PLUS
java.lang.IllegalStateException
 if this scanner was not positioned yet
.public final long scanBoundary()
scanBoundary(null)
.java.lang.IllegalStateException
 if this scanner was not positioned yet
.public final long scanBoundary(ArrayContext context)
do { next(); } while (!boundaryFinished());
and returns stepCount()
(the number of performed iterations,
i.e. the length of the scanned boundary).
In addition, this method calls context.checkInterruption()
method from time to time (if context!=null) to allow interruption of scanning very long boundaries.
No other methods of the context are called.
Note: the number of boundary segments, returned of this method, can theoretically be incorrect if the length of the boundary is greater than Long.MAX_VALUE. It is a very exotic case, that can be practically realized only on a virtual matrix, containing almost 2^{63} bits, with special structure. In this case, this method will work during more than 10^{10} seconds (> 300 years) on a very quick computer that can perform one iteration per 1 ns.
context
 the context of execution; may be null, then it will be ignored.java.lang.IllegalStateException
 if this scanner was not positioned yet
.public final void updateProgress(ArrayContext context)
updateProgress(event)
with an event, created by the following operator:
currentIndexInArray()
, matrix()
.size()
)The method can be useful while sequentially scanning the matrix via a usual loop of
nextBoundary()
and scanBoundary(ArrayContext)
calls.
context
 the context of execution; may be null, then it will be ignored.public final void checkInterruption(ArrayContext context)
checkInterruption()
or
does nothing if context==null.
The method can be useful while sequentially scanning the matrix via a usual loop of
nextBoundary()
and scanBoundary(ArrayContext)
calls.
context
 the context of execution; may be null, then it will be ignored.public java.lang.String toString()
The result of this method may depend on implementation.
toString
in class java.lang.Object