github.com/janelia-flyem/dvid@v1.0.0/datatype/roi/iterator.go (about)

     1  package roi
     2  
     3  import (
     4  	"fmt"
     5  	"strings"
     6  
     7  	"github.com/janelia-flyem/dvid/datastore"
     8  	"github.com/janelia-flyem/dvid/dvid"
     9  	"github.com/janelia-flyem/dvid/storage"
    10  )
    11  
    12  // Iterator is optimized for detecting whether given keys are within an ROI.
    13  // It exploits the key, and in particular IndexZYX, ordering so that checks
    14  // across a volume can be done quickly.
    15  type Iterator struct {
    16  	spans   []dvid.Span
    17  	curSpan int32
    18  }
    19  
    20  func NewIterator(roiName dvid.InstanceName, versionID dvid.VersionID, b dvid.Bounder) (*Iterator, error) {
    21  	dataservice, err := datastore.GetDataByVersionName(versionID, roiName)
    22  	if err != nil {
    23  		return nil, fmt.Errorf("Can't get ROI with name %q: %v", roiName, err)
    24  	}
    25  	data, ok := dataservice.(*Data)
    26  	if !ok {
    27  		return nil, fmt.Errorf("Data name %q was not of roi data type\n", roiName)
    28  	}
    29  
    30  	// Convert voxel extents to block Z extents
    31  	minPt := b.StartPoint().(dvid.Chunkable)
    32  	maxPt := b.EndPoint().(dvid.Chunkable)
    33  
    34  	minBlockCoord := minPt.Chunk(data.BlockSize)
    35  	maxBlockCoord := maxPt.Chunk(data.BlockSize)
    36  
    37  	minIndex := minIndexByBlockZ(minBlockCoord.Value(2))
    38  	maxIndex := maxIndexByBlockZ(maxBlockCoord.Value(2))
    39  
    40  	ctx := datastore.NewVersionedCtx(data, versionID)
    41  	it := new(Iterator)
    42  	it.spans, err = getSpans(ctx, minIndex, maxIndex)
    43  	return it, err
    44  }
    45  
    46  // ParseFilterSpec returns the specified ROI instance name and version within a FilterSpec.
    47  // Currently, only one ROI can be specified in a FilterSpec.  Multiple ROIs should use a
    48  // different FilterSpec like "intersect" instead of "roi".
    49  func ParseFilterSpec(spec storage.FilterSpec) (name dvid.InstanceName, v dvid.VersionID, found bool, err error) {
    50  	var filterval string
    51  	filterval, found = spec.GetFilterSpec("roi")
    52  	if !found {
    53  		return
    54  	}
    55  	roispec := strings.Split(filterval, ",")
    56  	if len(roispec) != 2 {
    57  		err = fmt.Errorf("bad ROI spec: %s", filterval)
    58  		return
    59  	}
    60  	name = dvid.InstanceName(roispec[0])
    61  	_, v, err = datastore.MatchingUUID(roispec[1])
    62  	return
    63  }
    64  
    65  // NewIteratorBySpec returns a ROI iterator based on a string specification of the form
    66  // "roi:<roiname>,<uuid>" where the ROI instance name and uniquely identifying string form
    67  // of uuid are given.  If the given string is not parsable, the "found" return value is false.
    68  func NewIteratorBySpec(spec storage.FilterSpec, b dvid.Bounder) (it *Iterator, v dvid.VersionID, found bool, err error) {
    69  	var name dvid.InstanceName
    70  	name, v, found, err = ParseFilterSpec(spec)
    71  	if err != nil || !found {
    72  		return
    73  	}
    74  
    75  	// Create new iterator based on spec.
    76  	it, err = NewIterator(name, v, b)
    77  	return
    78  }
    79  
    80  func (it *Iterator) Reset() {
    81  	it.curSpan = 0
    82  }
    83  
    84  // Returns true if the index is inside the ROI volume.  Note that this optimized
    85  // function maintains state and is not concurrency safe; it assumes sequential
    86  // calls where the considered indexZYX is increasing in Z, Y, and X after either
    87  // NewIterator() or Reset().
    88  func (it *Iterator) InsideFast(indexZYX dvid.IndexZYX) bool {
    89  	// Fast forward through spans to make sure we are either in span or past all
    90  	// smaller spans.
    91  	numSpans := int32(len(it.spans))
    92  	for {
    93  		if it.curSpan >= numSpans {
    94  			return false
    95  		}
    96  		span := it.spans[it.curSpan]
    97  		if span[0] > indexZYX[2] { // check z
    98  			return false
    99  		}
   100  		if span[0] < indexZYX[2] {
   101  			it.curSpan++
   102  			continue
   103  		}
   104  		if span[1] > indexZYX[1] { // check y
   105  			return false
   106  		}
   107  		if span[1] < indexZYX[1] {
   108  			it.curSpan++
   109  			continue
   110  		}
   111  		if span[2] > indexZYX[0] { // check x0
   112  			return false
   113  		}
   114  		if span[3] >= indexZYX[0] { // check x1
   115  			return true
   116  		}
   117  		// We are in correct z,y but current span is before key's coordinate, so iterate.
   118  		it.curSpan++
   119  	}
   120  }