diff --git a/org.eclipse.scanning.api/.project b/org.eclipse.scanning.api/.project index e7c943c95..580a3fb0c 100644 --- a/org.eclipse.scanning.api/.project +++ b/org.eclipse.scanning.api/.project @@ -1,45 +1,51 @@ - - - org.eclipse.scanning.api - - - - - - org.eclipse.jdt.core.javabuilder - - - - - org.eclipse.pde.ManifestBuilder - - - - - org.eclipse.pde.SchemaBuilder - - - - - org.eclipse.pde.ds.core.builder - - - - - org.zeroturnaround.eclipse.rebelXmlBuilder - - - - - org.zeroturnaround.eclipse.remoting.remotingBuilder - - - - - - org.eclipse.pde.PluginNature - org.eclipse.jdt.core.javanature - org.zeroturnaround.eclipse.jrebelNature - org.zeroturnaround.eclipse.remoting.remotingNature - - + + + org.eclipse.scanning.api + + + + + + org.python.pydev.PyDevBuilder + + + + + org.eclipse.jdt.core.javabuilder + + + + + org.eclipse.pde.ManifestBuilder + + + + + org.eclipse.pde.SchemaBuilder + + + + + org.eclipse.pde.ds.core.builder + + + + + org.zeroturnaround.eclipse.rebelXmlBuilder + + + + + org.zeroturnaround.eclipse.remoting.remotingBuilder + + + + + + org.eclipse.pde.PluginNature + org.eclipse.jdt.core.javanature + org.python.pydev.pythonNature + org.zeroturnaround.eclipse.jrebelNature + org.zeroturnaround.eclipse.remoting.remotingNature + + diff --git a/org.eclipse.scanning.api/.pydevproject b/org.eclipse.scanning.api/.pydevproject new file mode 100644 index 000000000..4a57e2e63 --- /dev/null +++ b/org.eclipse.scanning.api/.pydevproject @@ -0,0 +1,8 @@ + + +Default +jython 2.7 + +/${PROJECT_DIR_NAME}/bin + + diff --git a/org.eclipse.scanning.api/src/org/eclipse/scanning/api/points/Point.java b/org.eclipse.scanning.api/src/org/eclipse/scanning/api/points/Point.java index 3c5136b2e..63063cff1 100644 --- a/org.eclipse.scanning.api/src/org/eclipse/scanning/api/points/Point.java +++ b/org.eclipse.scanning.api/src/org/eclipse/scanning/api/points/Point.java @@ -16,6 +16,8 @@ import java.util.LinkedHashMap; import java.util.List; import java.util.Map; +import java.util.regex.Matcher; +import java.util.regex.Pattern; import org.eclipse.scanning.api.annotation.UiHidden; @@ -133,4 +135,23 @@ public Map getIndices() { } return indices; } + + private static final String VERTEX = "([a-zA-Z0-9_])+\\((\\d+)\\)=([-+]?[0-9]*\\.?[0-9]+)"; + private static final Pattern POSITION = Pattern.compile(VERTEX+", "+VERTEX); + /** + * Parse a point from the toString() method into an instance of Point + * + * y(0)=2.397560627408689, x(4)=5.266805527444651 + * @param asString + * @return + */ + public static Point parse(String asString) { + + Matcher m = POSITION.matcher(asString); + if (m.matches()) { + return new Point(m.group(4), Integer.parseInt(m.group(5)), Double.parseDouble(m.group(6)), + m.group(1), Integer.parseInt(m.group(2)), Double.parseDouble(m.group(3))); + } + throw new RuntimeException("Unparsable string "+asString); + } } diff --git a/org.eclipse.scanning.points/.pydevproject b/org.eclipse.scanning.points/.pydevproject index 40e9f40a0..4a57e2e63 100644 --- a/org.eclipse.scanning.points/.pydevproject +++ b/org.eclipse.scanning.points/.pydevproject @@ -1,5 +1,8 @@ - - -Default -python 2.7 - + + +Default +jython 2.7 + +/${PROJECT_DIR_NAME}/bin + + diff --git a/org.eclipse.scanning.points/META-INF/MANIFEST.MF b/org.eclipse.scanning.points/META-INF/MANIFEST.MF index 0fc0400d7..1346eab9c 100644 --- a/org.eclipse.scanning.points/META-INF/MANIFEST.MF +++ b/org.eclipse.scanning.points/META-INF/MANIFEST.MF @@ -9,11 +9,10 @@ Require-Bundle: org.eclipse.scanning.api;bundle-version="1.0.0", org.eclipse.january;bundle-version="1.0.0", org.eclipse.dawnsci.analysis.api;bundle-version="1.1.0", org.eclipse.dawnsci.analysis.dataset;bundle-version="1.0.0", - org.eclipse.core.runtime;bundle-version="3.8.0", com.fasterxml.jackson.core.jackson-annotations;bundle-version="2.2.0", com.fasterxml.jackson.core.jackson-core;bundle-version="2.2.0", com.fasterxml.jackson.core.jackson-databind;bundle-version="2.2.0", - org.apache.commons.math3 + org.eclipse.core.runtime Export-Package: org.eclipse.scanning.points, org.eclipse.scanning.points.classregistry, org.eclipse.scanning.points.mutators, @@ -23,8 +22,8 @@ Service-Component: OSGI-INF/*.xml Bundle-ActivationPolicy: lazy Bundle-ClassPath: . Import-Package: Jama;version="1.0.3", + org.osgi.framework;version="1.8.0", org.osgi.service.component;version="1.2.2", org.python.core, - org.python.util, org.slf4j;version="1.7.2" Bundle-Activator: org.eclipse.scanning.points.PointsActivator diff --git a/org.eclipse.scanning.points/scripts/scanpointgenerator/core/compoundgenerator.py b/org.eclipse.scanning.points/scripts/scanpointgenerator/core/compoundgenerator.py index 5ed1947b2..e57ab3f25 100644 --- a/org.eclipse.scanning.points/scripts/scanpointgenerator/core/compoundgenerator.py +++ b/org.eclipse.scanning.points/scripts/scanpointgenerator/core/compoundgenerator.py @@ -13,12 +13,6 @@ ### import logging -import sys -if sys.platform.startswith('java'): - Lock = object # Workaround for GDA -else: - from threading import Lock - from scanpointgenerator.compat import range_, np from scanpointgenerator.core.generator import Generator from scanpointgenerator.core.point import Point @@ -28,11 +22,12 @@ from scanpointgenerator.generators import LineGenerator -@Generator.register_subclass("scanpointgenerator:generator/CompoundGenerator:1.0") -class CompoundGenerator(Generator): +class CompoundGenerator(object): """Nest N generators, apply exclusion regions to relevant generator pairs and apply any mutators before yielding points""" + typeid = "scanpointgenerator:generator/CompoundGenerator:1.0" + def __init__(self, generators, excluders, mutators): """ Args: @@ -45,13 +40,11 @@ def __init__(self, generators, excluders, mutators): self.mutators = mutators self.axes = [] self.position_units = {} - self.axes_points = {} - self.axes_points_lower = {} - self.axes_points_upper = {} + self.index_dims = [] self.dimensions = [] - self.alternate_direction = [g.alternate_direction for g in generators] self.num = 1 - + self.dim_meta = {} + self.alternate_direction = [g.alternate_direction for g in generators] for generator in generators: logging.debug("Generator passed to Compound init") logging.debug(generator.to_dict()) @@ -67,8 +60,15 @@ def __init__(self, generators, excluders, mutators): self.generator_dim_scaling = {} def prepare(self): - self.num = 1 + """ + Prepare data structures and masks required for point generation. + Must be called before get_point or iterator are called. + """ self.dimensions = [] + self.index_dims = [] + self.dim_meta = {} + self.generator_dim_scaling = {} + # we're going to mutate these structures excluders = list(self.excluders) generators = list(self.generators) @@ -86,16 +86,18 @@ def prepare(self): continue if isinstance(gen_1, LineGenerator) \ and isinstance(gen_2, LineGenerator): - gen_1.produce_points() - gen_2.produce_points() + gen_1.prepare_positions() + gen_2.prepare_positions() valid = np.full(gen_1.num, True, dtype=np.int8) - valid &= gen_1.points[axis_1] <= rect.roi.width + rect.roi.start[0] - valid &= gen_1.points[axis_1] >= rect.roi.start[0] - points_1 = gen_1.points[axis_1][valid.astype(np.bool)] + valid &= gen_1.positions[axis_1] \ + <= rect.roi.width + rect.roi.start[0] + valid &= gen_1.positions[axis_1] >= rect.roi.start[0] + points_1 = gen_1.positions[axis_1][valid.astype(np.bool)] valid = np.full(gen_2.num, True, dtype=np.int8) - valid &= gen_2.points[axis_2] <= rect.roi.height + rect.roi.start[1] - valid &= gen_2.points[axis_2] >= rect.roi.start[1] - points_2 = gen_2.points[axis_2][valid.astype(np.bool)] + valid &= gen_2.positions[axis_2] \ + <= rect.roi.height + rect.roi.start[1] + valid &= gen_2.positions[axis_2] >= rect.roi.start[1] + points_2 = gen_2.positions[axis_2][valid.astype(np.bool)] new_gen1 = LineGenerator( gen_1.name, gen_1.units, points_1[0], points_1[-1], len(points_1), gen_1.alternate_direction) @@ -107,24 +109,13 @@ def prepare(self): excluders.remove(rect) for generator in generators: - generator.produce_points() - self.axes_points.update(generator.points) - self.axes_points_lower.update(generator.points_lower) - self.axes_points_upper.update(generator.points_upper) - self.num *= generator.num - - dim = {"size":generator.num, - "axes":list(generator.axes), - "generators":[generator], - "masks":[], - "tile":1, - "repeat":1, - "alternate":generator.alternate_direction} - self.dimensions.append(dim) + generator.prepare_positions() + self.dimensions.append(Dimension(generator)) + # only the inner-most generator needs to have bounds calculated + generators[-1].prepare_bounds() for excluder in excluders: axis_1, axis_2 = excluder.scannables - # ensure axis_1 is "outer" axis (if separate generators) gen_1 = [g for g in generators if axis_1 in g.axes][0] gen_2 = [g for g in generators if axis_2 in g.axes][0] gen_diff = generators.index(gen_1) \ @@ -133,27 +124,16 @@ def prepare(self): raise ValueError( "Excluders must be defined on axes that are adjacent in " \ "generator order") - if gen_diff == 1: - gen_1, gen_2 = gen_2, gen_1 - axis_1, axis_2 = axis_2, axis_1 - gen_diff = -1 - - ##### - # first check if region spans two dimensions - merge if so - ##### - dim_1 = [i for i in self.dimensions if axis_1 in i["axes"]][0] - dim_2 = [i for i in self.dimensions if axis_2 in i["axes"]][0] + # merge dimensions if region spans two + dim_1 = [i for i in self.dimensions if axis_1 in i.axes][0] + dim_2 = [i for i in self.dimensions if axis_2 in i.axes][0] dim_diff = self.dimensions.index(dim_1) \ - self.dimensions.index(dim_2) - if dim_diff < -1 or dim_diff > 1: - raise ValueError( - "Excluders must be defined on axes that are adjacent in " \ - "generator order") if dim_diff == 1: dim_1, dim_2 = dim_2, dim_1 dim_diff = -1 - if dim_1["alternate"] != dim_2["alternate"] \ + if dim_1.alternate != dim_2.alternate \ and dim_1 is not self.dimensions[0]: raise ValueError( "Generators tied by regions must have the same " \ @@ -161,175 +141,120 @@ def prepare(self): # merge "inner" into "outer" if dim_diff == -1: # dim_1 is "outer" - preserves axis ordering - - # need to appropriately scale the existing masks - # masks are "tiled" by the size of generators "below" them - # and their elements are "repeated" by the size of generators - # above them, so: - # |mask| * duplicates * repeates == |generators in index| - scale = 1 - for g in dim_2["generators"]: - scale *= g.num - for m in dim_1["masks"]: - m["repeat"] *= scale - scale = 1 - for g in dim_1["generators"]: - scale *= g.num - for m in dim_2["masks"]: - m["tile"] *= scale - dim_1["masks"] += dim_2["masks"] - dim_1["axes"] += dim_2["axes"] - dim_1["generators"] += dim_2["generators"] - dim_1["size"] *= dim_2["size"] - dim_1["alternate"] |= dim_2["alternate"] - self.dimensions.remove(dim_2) - dim = dim_1 - - ##### - # generate the mask for this region - ##### - # if gen_1 and gen_2 are different then the outer axis will have to - # have its elements repeated and the inner axis will have to have - # itself repeated - gen_1 is always inner axis - - points_1 = self.axes_points[axis_1] - points_2 = self.axes_points[axis_2] - - doubled_mask = False # used for some cases of alternating generators - - if gen_1 is gen_2 and dim["alternate"]: - # run *both* axes backwards - # but our mask will be a factor of 2 too big - doubled_mask = True - points_1 = np.append(points_1, points_1[::-1]) - points_2 = np.append(points_2, points_2[::-1]) - elif dim["alternate"]: - doubled_mask = True - points_1 = np.append(points_1, points_1[::-1]) - points_2 = np.append(points_2, points_2[::-1]) - points_2 = np.tile(points_2, gen_1.num) - points_1 = np.repeat(points_1, gen_2.num) - elif gen_1 is not gen_2: - points_1 = np.repeat(points_1, gen_2.num) - points_2 = np.tile(points_2, gen_1.num) - else: - # copy the points arrays anyway so the regions can - # safely perform any array operations in place - # this is advantageous in the cases above - points_1 = np.copy(points_1) - points_2 = np.copy(points_2) - - - if axis_1 == excluder.scannables[0]: - mask = excluder.create_mask(points_1, points_2) + new_dim = Dimension.merge_dimensions(dim_1, dim_2) + self.dimensions[self.dimensions.index(dim_1)] = new_dim + + # Having this line in appears to break MScanServletTest.testGridWithROIScan() + # self.dimensions.remove(dim_2) + dim = new_dim else: - mask = excluder.create_mask(points_2, points_1) + dim = dim_1 - ##### - # Add new mask to index - ##### - tile = 0.5 if doubled_mask else 1 - repeat = 1 - found_axis = False - # tile by product of generators "before" - # repeat by product of generators "after" - for g in dim["generators"]: - if axis_1 in g.axes or axis_2 in g.axes: - found_axis = True - else: - if found_axis: - repeat *= g.num - else: - tile *= g.num - m = {"repeat":repeat, "tile":tile, "mask":mask} - dim["masks"].append(m) - # end for excluder in excluders - ##### + dim.apply_excluder(excluder) - tile = 1 - repeat = 1 - ##### - # Generate full index mask and "apply" - ##### + self.num = 1 for dim in self.dimensions: - mask = np.full(dim["size"], True, dtype=np.int8) - for m in dim["masks"]: - assert len(m["mask"]) * m["repeat"] * m["tile"] == len(mask), \ - "Mask lengths are not consistent" - expanded = np.repeat(m["mask"], m["repeat"]) - if m["tile"] % 1 != 0: - ex = np.tile(expanded, int(m["tile"])) - expanded = np.append(ex, expanded[:len(expanded)//2]) - else: - expanded = np.tile(expanded, int(m["tile"])) - mask &= expanded - dim["mask"] = mask - dim["indicies"] = np.nonzero(mask)[0] - if len(dim["indicies"]) == 0: + self.dim_meta[dim] = {} + mask = dim.create_dimension_mask() + indicies = np.arange(mask.shape[0]) + if len(indicies) == 0: raise ValueError("Regions would exclude entire scan") - repeat *= len(dim["indicies"]) - self.num = repeat + self.num *= len(indicies) + self.dim_meta[dim]["mask"] = mask + self.dim_meta[dim]["indicies"] = indicies + self.index_dims.append(len(indicies)) + + repeat = self.num + tile = 1 for dim in self.dimensions: - l = len(dim["indicies"]) - repeat /= l - dim["tile"] = tile - dim["repeat"] = repeat - tile *= l + dim_length = len(self.dim_meta[dim]["indicies"]) + repeat /= dim_length + self.dim_meta[dim]["tile"] = tile + self.dim_meta[dim]["repeat"] = repeat + tile *= dim_length for dim in self.dimensions: tile = 1 repeat = 1 - for g in dim["generators"]: + for g in dim.generators: repeat *= g.num - for g in dim["generators"]: + for g in dim.generators: repeat /= g.num d = {"tile":tile, "repeat":repeat} tile *= g.num self.generator_dim_scaling[g] = d def iterator(self): + """ + Iterator yielding generator positions at each scan point + + Yields: + Point: The next point + """ it = (self.get_point(n) for n in range_(self.num)) - for m in self.mutators: - it = m.mutate(it) for p in it: yield p def get_point(self, n): + """ + Retrieve the desired point from the generator + + Args: + n (int): point to be generated + Returns: + Point: The requested point + """ + if n >= self.num: raise IndexError("Requested point is out of range") - p = Point() + point = Point() - # need to know how far along each index we are + # need to know how far along each dimension we are # and, in the case of alternating indicies, how # many times we've run through them - kc = 0 # the "cumulative" k for each index + kc = 0 # the "cumulative" k for each dimension for dim in self.dimensions: - indicies = dim["indicies"] - i = n // dim["repeat"] - r = i // len(indicies) + indicies = self.dim_meta[dim]["indicies"] + i = int(n // self.dim_meta[dim]["repeat"]) i %= len(indicies) k = indicies[i] dim_reverse = False - if dim["alternate"] and kc % 2 == 1: + if dim.alternate and kc % 2 == 1: i = len(indicies) - i - 1 dim_reverse = True kc *= len(indicies) kc += k k = indicies[i] - # need point k along each generator in index + # need point k along each generator in dimension # in alternating case, need to sometimes go backward - p.indexes.append(i) - for g in dim["generators"]: - j = k // self.generator_dim_scaling[g]["repeat"] - gr = j // g.num + point.indexes.append(i) + for g in dim.generators: + j = int(k // self.generator_dim_scaling[g]["repeat"]) + r = int(j // g.num) j %= g.num - if dim["alternate"] and g is not dim["generators"][0] and gr % 2 == 1: + j_lower = j + j_upper = j + 1 + if dim.alternate and g is not dim.generators[0] and r % 2 == 1: + # the top level generator's direction is handled by + # the fact that the reverse direction was appended j = g.num - j - 1 + j_lower = j + 1 + j_upper = j + elif dim_reverse and g is dim.generators[0]: + # top level generator is running in reverse, + # so bounds are swapped + j_lower, j_upper = j_upper, j_lower for axis in g.axes: - p.positions[axis] = g.points[axis][j] - p.lower[axis] = g.points_lower[axis][j] - p.upper[axis] = g.points_upper[axis][j] - return p + point.positions[axis] = g.positions[axis][j] + if g is self.generators[-1]: + point.lower[axis] = g.bounds[axis][j_lower] + point.upper[axis] = g.bounds[axis][j_upper] + else: + point.lower[axis] = g.positions[axis][j] + point.upper[axis] = g.positions[axis][j] + for m in self.mutators: + point = m.mutate(point, n) + return point def to_dict(self): """Convert object attributes into a dictionary""" @@ -344,6 +269,7 @@ def to_dict(self): def from_dict(cls, d): """ Create a CompoundGenerator instance from a serialised dictionary + Args: d(dict): Dictionary of attributes Returns: @@ -353,3 +279,113 @@ def from_dict(cls, d): excluders = [Excluder.from_dict(e) for e in d['excluders']] mutators = [Mutator.from_dict(m) for m in d['mutators']] return cls(generators, excluders, mutators) + +class Dimension(object): + """A collapsed set of generators joined by excluders""" + def __init__(self, generator): + self.axes = list(generator.axes) + self.generators = [generator] + self.size = generator.num + self.masks = [] + self.alternate = generator.alternate_direction + + def apply_excluder(self, excluder): + """Apply an excluder with axes matching some axes in the dimension to + produce an internal mask""" + axis_inner = excluder.scannables[0] + axis_outer = excluder.scannables[1] + gen_inner = [g for g in self.generators if axis_inner in g.axes][0] + gen_outer = [g for g in self.generators if axis_outer in g.axes][0] + points_x = gen_inner.positions[axis_inner] + points_y = gen_outer.positions[axis_outer] + if self.generators.index(gen_inner) > self.generators.index(gen_outer): + gen_inner, gen_outer = gen_outer, gen_inner + axis_inner, axis_outer = axis_outer, axis_inner + points_x, points_y = points_y, points_x + + if gen_inner is gen_outer and self.alternate: + points_x = np.append(points_x, points_x[::-1]) + points_y = np.append(points_y, points_y[::-1]) + elif self.alternate: + points_x = np.append(points_x, points_x[::-1]) + points_x = np.repeat(points_x, gen_outer.num) + points_y = np.append(points_y, points_y[::-1]) + points_y = np.tile(points_y, gen_inner.num) + elif gen_inner is not gen_outer: + points_x = np.repeat(points_x, gen_outer.num) + points_y = np.tile(points_y, gen_inner.num) + else: + # copy the point arrays so the excluders can perform + # array operations in place (advantageous in the other cases) + points_x = np.copy(points_x) + points_y = np.copy(points_y) + + if axis_inner == excluder.scannables[0]: + mask = excluder.create_mask(points_x, points_y) + else: + mask = excluder.create_mask(points_y, points_x) + tile = 0.5 if self.alternate else 1 + repeat = 1 + found_axis = False + for g in self.generators: + if axis_inner in g.axes or axis_outer in g.axes: + found_axis = True + else: + if found_axis: + repeat *= g.num + else: + tile *= g.num + + m = {"repeat":repeat, "tile":tile, "mask":mask} + self.masks.append(m) + + def create_dimension_mask(self): + """ + Create and return a mask for every point in the dimension + + e.g. (with [y1, y2, y3] and [x1, x2, x3] both alternating) + y: y1, y1, y1, y2, y2, y2, y3, y3, y3 + x: x1, x2, x3, x3, x2, x1, x1, x2, x3 + mask: m1, m2, m3, m4, m5, m6, m7, m8, m9 + + Returns: + np.array(int8): One dimensional mask array + """ + mask = np.full(self.size, True, dtype=np.int8) + for m in self.masks: + assert len(m["mask"]) * m["repeat"] * m["tile"] == len(mask), \ + "Mask lengths are not consistent" + expanded = np.repeat(m["mask"], m["repeat"]) + if m["tile"] % 1 != 0: + ex = np.tile(expanded, int(m["tile"])) + expanded = np.append(ex, expanded[:int(len(expanded)//2)]) + else: + expanded = np.tile(expanded, int(m["tile"])) + mask &= expanded + return mask + + @staticmethod + def merge_dimensions(outer, inner): + """Collapse two dimensions into one, appropriate scaling structures""" + dim = Dimension(outer.generators[0]) + # masks in the inner generator are tiled by the size of + # outer generators and outer generators have their elements + # repeated by the size of inner generators + inner_masks = [m.copy() for m in inner.masks] + outer_masks = [m.copy() for m in outer.masks] + scale = 1 + for g in inner.generators: + scale *= g.num + for m in outer_masks: + m["repeat"] *= scale + scale = 1 + for g in outer.generators: + scale *= g.num + for m in inner_masks: + m["tile"] *= scale + dim.masks = outer_masks + inner_masks + dim.axes = outer.axes + inner.axes + dim.generators = outer.generators + inner.generators + dim.alternate = outer.alternate or inner.alternate + dim.size = outer.size * inner.size + return dim diff --git a/org.eclipse.scanning.points/scripts/scanpointgenerator/core/generator.py b/org.eclipse.scanning.points/scripts/scanpointgenerator/core/generator.py index fab9a0646..f68826ca5 100644 --- a/org.eclipse.scanning.points/scripts/scanpointgenerator/core/generator.py +++ b/org.eclipse.scanning.points/scripts/scanpointgenerator/core/generator.py @@ -30,21 +30,48 @@ class Generator(object): position_units = None index_dims = None index_names = None + positions = None + bounds = None + num = 0 # Lookup table for generator subclasses _generator_lookup = {} axes = [] - def iterator(self): - """An iterator yielding positions at each scan point + def prepare_arrays(self, index_array): + """ + Abstract method to create position or bounds array from provided index + array. index_array will be np.arange(self.num) for positions and + np.arange(self.num + 1) - 0.5 for bounds. - Yields: - Point: The next scan :class:`Point` + Args: + index_array (np.array): Index array to produce parameterised points + + Returns: + Positions: Dictionary of axis names to position/bounds arrays """ raise NotImplementedError + def prepare_positions(self): + self.positions = self.prepare_arrays(np.arange(self.num)) + + def prepare_bounds(self): + self.bounds = self.prepare_arrays(np.arange(self.num + 1) - 0.5) + def to_dict(self): """Abstract method to convert object attributes into a dictionary""" raise NotImplementedError + + def iterator(self): + """ + Iterator yielding generator positions at each scan point + + Yields: + Point: The next point + """ + from scanpointgenerator.core.compoundgenerator import CompoundGenerator + gen = CompoundGenerator([self], [], []) + gen.prepare() + return gen.iterator() @classmethod def from_dict(cls, d): diff --git a/org.eclipse.scanning.points/scripts/scanpointgenerator/core/mutator.py b/org.eclipse.scanning.points/scripts/scanpointgenerator/core/mutator.py index 7c26ccad4..68127853f 100644 --- a/org.eclipse.scanning.points/scripts/scanpointgenerator/core/mutator.py +++ b/org.eclipse.scanning.points/scripts/scanpointgenerator/core/mutator.py @@ -19,16 +19,17 @@ class Mutator(object): # Lookup table for mutator subclasses _mutator_lookup = {} - def mutate(self, iterator): + def mutate(self, point, index): """ - Abstract method to take each point from the given iterator, apply a - mutation and then yield the new point + Abstract method to take a point, apply a mutation and then return the + new point Args: - iterator(iter): Iterator to mutate + Point: point to mutate + Index: one-dimensional linear index of point - Yields: - Point: Mutated points from generator + Returns: + Point: Mutated point """ raise NotImplementedError diff --git a/org.eclipse.scanning.points/scripts/scanpointgenerator/generators/linegenerator.py b/org.eclipse.scanning.points/scripts/scanpointgenerator/generators/linegenerator.py index 774d47a34..891287a95 100644 --- a/org.eclipse.scanning.points/scripts/scanpointgenerator/generators/linegenerator.py +++ b/org.eclipse.scanning.points/scripts/scanpointgenerator/generators/linegenerator.py @@ -13,7 +13,6 @@ ### from scanpointgenerator.compat import range_, np from scanpointgenerator.core import Generator -from scanpointgenerator.core import Point def to_list(value): @@ -34,7 +33,7 @@ def __init__(self, name, units, start, stop, num, alternate_direction=False): units (str): The scannable units. E.g. "mm" start (float/list(float)): The first position to be generated. e.g. 1.0 or [1.0, 2.0] - stop (float or list(float)): The first position to be generated. + stop (float or list(float)): The final position to be generated. e.g. 5.0 or [5.0, 10.0] num (int): The number of points to generate. E.g. 5 alternate_direction(bool): Specifier to reverse direction if @@ -45,9 +44,6 @@ def __init__(self, name, units, start, stop, num, alternate_direction=False): self.start = to_list(start) self.stop = to_list(stop) self.alternate_direction = alternate_direction - self.points = None - self.points_lower = None - self.points_upper = None self.units = units if len(self.name) != len(set(self.name)): @@ -85,48 +81,17 @@ def __init__(self, name, units, start, stop, num, alternate_direction=False): self.axes = self.name # For GDA - def produce_points(self): - self.points = {} - self.points_lower = {} - self.points_upper = {} - for axis in range_(self.num_axes): - axis_name = self.name[axis] - start = self.start[axis] - stop = self.stop[axis] + def prepare_arrays(self, index_array): + arrays = {} + for axis, start, stop in zip(self.name, self.start, self.stop): d = stop - start - if self.num == 1: - self.points[axis_name] = np.array([start]) - self.points_upper[axis_name] = np.array([start + 0.5 * d]) - self.points_lower[axis_name] = np.array([start - 0.5 * d]) - else: - n = self.num - 1. - s = d / n - upper_start = start + 0.5 * d / n - upper_stop = stop + 0.5 * d / n - lower_start = start - 0.5 * d / n - lower_stop = stop - 0.5 * d / n - self.points[axis_name] = np.linspace( - float(start), float(stop), self.num) - self.points_upper[axis_name] = np.linspace( - float(upper_start), float(upper_stop), self.num) - self.points_lower[axis_name] = np.linspace( - float(lower_start), float(lower_stop), self.num) - - def iterator(self): - for i in range_(self.num): - point = Point() - - for axis_index in range_(self.num_axes): - axis_name = self.name[axis_index] - start = self.start[axis_index] - step = self.step[axis_index] - - point.positions[axis_name] = start + i * step - point.lower[axis_name] = start + (i - 0.5) * step - point.upper[axis_name] = start + (i + 0.5) * step - - point.indexes = [i] - yield point + step = float(d) + # if self.num == 1 then single point case + if self.num > 1: + step /= (self.num - 1) + f = lambda t: (t * step) + start + arrays[axis] = f(index_array) + return arrays def to_dict(self): """Convert object attributes into a dictionary""" diff --git a/org.eclipse.scanning.points/scripts/scanpointgenerator/generators/lissajousgenerator.py b/org.eclipse.scanning.points/scripts/scanpointgenerator/generators/lissajousgenerator.py index 8bb1bad03..114de7864 100644 --- a/org.eclipse.scanning.points/scripts/scanpointgenerator/generators/lissajousgenerator.py +++ b/org.eclipse.scanning.points/scripts/scanpointgenerator/generators/lissajousgenerator.py @@ -38,9 +38,6 @@ def __init__(self, names, units, box, num_lobes, self.names = names self.units = units - self.points = None - self.points_lower = None - self.points_upper = None self.alternate_direction = alternate_direction if len(self.names) != len(set(self.names)): @@ -72,47 +69,17 @@ def __init__(self, names, units, box, num_lobes, self.axes = self.names # For GDA - def _calc_arrays(self, offset): + def prepare_arrays(self, index_array): + arrays = {} x0, y0 = self.centre[0], self.centre[1] A, B = self.x_max, self.y_max a, b = self.x_freq, self.y_freq d = self.phase_diff - f = lambda t: y0 + A * np.sin(a * 2 * m.pi * (t+offset)/self.num + d) - x = f(np.arange(self.num)) - f = lambda t: B * np.sin(b * 2 * m.pi * (t+offset)/self.num) - y = f(np.arange(self.num)) - return x, y - - def produce_points(self): - self.points = {} - self.points_lower = {} - self.points_upper = {} - - x = self.names[0] - y = self.names[1] - self.points[x], self.points[y] = self._calc_arrays(0) - self.points_upper[x], self.points_upper[y] = self._calc_arrays(0.5) - self.points_lower[x], self.points_lower[y] = self._calc_arrays(-0.5) - - def _calc(self, i): - """Calculate the coordinate for a given index""" - x = self.centre[0] + \ - self.x_max * m.sin(self.x_freq * i * self.increment + - self.phase_diff) - y = self.centre[1] + \ - self.y_max * m.sin(self.y_freq * i * self.increment) - - return x, y - - def iterator(self): - for i in range_(self.num): - p = Point() - p.positions[self.names[0]], p.positions[self.names[1]] = self._calc(i) - p.lower[self.names[0]], p.lower[self.names[1]] = self._calc(i - 0.5) - p.upper[self.names[0]], p.upper[self.names[1]] = self._calc(i + 0.5) - p.indexes = [i] - - yield p + fx = lambda t: x0 + A * np.sin(a * 2*m.pi * t/self.num + d) + fy = lambda t: y0 + B * np.sin(b * 2*m.pi * t/self.num) + arrays[self.names[0]] = fx(index_array) + arrays[self.names[1]] = fy(index_array) + return arrays def to_dict(self): """Convert object attributes into a dictionary""" diff --git a/org.eclipse.scanning.points/scripts/scanpointgenerator/generators/spiralgenerator.py b/org.eclipse.scanning.points/scripts/scanpointgenerator/generators/spiralgenerator.py index 651e518a5..4e4dac164 100644 --- a/org.eclipse.scanning.points/scripts/scanpointgenerator/generators/spiralgenerator.py +++ b/org.eclipse.scanning.points/scripts/scanpointgenerator/generators/spiralgenerator.py @@ -42,20 +42,13 @@ def __init__(self, names, units, centre, radius, scale=1.0, self.radius = radius self.scale = scale self.alternate_direction = alternate_direction - self.points = None - self.points_lower = None - self.points_upper = None if len(self.names) != len(set(self.names)): raise ValueError("Axis names cannot be duplicated; given %s" % names) - self.alpha = m.sqrt(4 * m.pi) # Theta scale factor - self.beta = scale / (2 * m.pi) # Radius scale factor - self.num = self._end_point(self.radius) + 1 self.position_units = {names[0]: units, names[1]: units} - self.index_dims = [self._end_point(self.radius)] gen_name = "Spiral" for axis_name in self.names[::-1]: gen_name = axis_name + "_" + gen_name @@ -63,58 +56,29 @@ def __init__(self, names, units, centre, radius, scale=1.0, self.axes = self.names # For GDA - def _calc_arrays(self, offset): # spiral equation : r = b * phi # scale = 2 * pi * b # parameterise phi with approximation: # phi(t) = k * sqrt(t) (for some k) # number of possible t is solved by sqrt(t) = max_r / b*k - b = self.scale / (2 * m.pi) - k = m.sqrt(4 * m.pi) # magic scaling factor for our angle steps - size = (self.radius) / (b * k) - size *= size - size = int(size) + 1 # TODO: Why the +1 ??? - phi_t = lambda t: k * np.sqrt(t + offset) - phi = phi_t(np.arange(size)) + self.alpha = m.sqrt(4 * m.pi) # Theta scale factor = k + self.beta = scale / (2 * m.pi) # Radius scale factor = b + self.num = int((self.radius / (self.alpha * self.beta)) ** 2) + 1 + + def prepare_arrays(self, index_array): + arrays = {} + b = self.beta + k = self.alpha + size = self.num + # parameterise phi with approximation: + # phi(t) = k * sqrt(t) (for some k) + phi_t = lambda t: k * np.sqrt(t + 0.5) + phi = phi_t(index_array) x = self.centre[0] + b * phi * np.sin(phi) y = self.centre[1] + b * phi * np.cos(phi) - return x, y - - def produce_points(self): - self.points = {} - self.points_lower = {} - self.points_upper = {} - x = self.names[0] - y = self.names[1] - self.points_lower[x], self.points_lower[y] = self._calc_arrays(0) - self.points[x], self.points[y] = self._calc_arrays(0.5) - self.points_upper[x], self.points_upper[y] = self._calc_arrays(1.) - - def _calc(self, i): - """Calculate the coordinate for a given index""" - theta = self.alpha * m.sqrt(i) - radius = self.beta * theta - x = self.centre[0] + radius * m.sin(theta) - y = self.centre[1] + radius * m.cos(theta) - - return x, y - - def _end_point(self, radius): - """Calculate the index of the final point contained by circle""" - return int((radius / (self.alpha * self.beta)) ** 2) - - def iterator(self): - for i in range_(0, self._end_point(self.radius) + 1): - p = Point() - p.indexes = [i] - - i += 0.5 # Offset so lower bound of first point is not less than 0 - - p.positions[self.names[0]], p.positions[self.names[1]] = self._calc(i) - p.upper[self.names[0]], p.upper[self.names[1]] = self._calc(i + 0.5) - p.lower[self.names[0]], p.lower[self.names[1]] = self._calc(i - 0.5) - - yield p + arrays[self.names[0]] = x + arrays[self.names[1]] = y + return arrays def to_dict(self): """Convert object attributes into a dictionary""" diff --git a/org.eclipse.scanning.points/scripts/scanpointgenerator/mutators/fixeddurationmutator.py b/org.eclipse.scanning.points/scripts/scanpointgenerator/mutators/fixeddurationmutator.py index da91f6cec..72c66ee5d 100644 --- a/org.eclipse.scanning.points/scripts/scanpointgenerator/mutators/fixeddurationmutator.py +++ b/org.eclipse.scanning.points/scripts/scanpointgenerator/mutators/fixeddurationmutator.py @@ -24,20 +24,20 @@ def __init__(self, duration): """ self.duration = duration - def mutate(self, iterator): + def mutate(self, point, index): """ Applies duration to points in the given iterator, yielding them Args: - iterator: Iterator to mutate + Point: Point to mutate + Index: one-dimensional index of point - Yields: - Point: Mutated points + Returns: + Point: Mutated point """ - for p in iterator: - p.duration = self.duration - yield p + point.duration = self.duration + return point def to_dict(self): return {"typeid": self.typeid, "duration": self.duration} diff --git a/org.eclipse.scanning.points/scripts/scanpointgenerator/mutators/randomoffsetmutator.py b/org.eclipse.scanning.points/scripts/scanpointgenerator/mutators/randomoffsetmutator.py index 686e04edc..d5e9920a4 100644 --- a/org.eclipse.scanning.points/scripts/scanpointgenerator/mutators/randomoffsetmutator.py +++ b/org.eclipse.scanning.points/scripts/scanpointgenerator/mutators/randomoffsetmutator.py @@ -12,7 +12,6 @@ # ### from scanpointgenerator.core import Mutator -from scanpointgenerator.core import random @Mutator.register_subclass("scanpointgenerator:mutator/RandomOffsetMutator:1.0") @@ -31,92 +30,49 @@ def __init__(self, seed, axes, max_offset): """ self.seed = seed - self.RNG = random.Random(seed) self.axes = axes self.max_offset = max_offset - def get_random_number(self): - """ - Return a random number between -1.0 and 1.0 with Gaussian distribution - - Returns: - Float: Random number - """ - random_number = 2.0 - while abs(random_number) > 1.0: - random_number = self.RNG.random() - - return random_number - - def apply_offset(self, point): - """ - Apply a random offset to the Point - - Args: - point(Point): Point to apply random offset to - - Returns: - bool: Whether point was changed - """ - - changed = False + def calc_offset(self, axis, idx): + m = self.max_offset[axis] + x = (idx << 4) + (0 if len(axis) == 0 else ord(axis[0])) + x ^= (self.seed << 12) + # Apply hash algorithm to x for pseudo-randomness + # Robert Jenkins 32 bit hash (avalanches well) + x = (x + 0x7ED55D16) + (x << 12) + x &= 0xFFFFFFFF # act as 32 bit unsigned before doing any right-shifts + x = (x ^ 0xC761C23C) ^ (x >> 19) + x = (x + 0x165667B1) + (x << 5) + x = (x + 0xD3A2646C) ^ (x << 9) + x = (x + 0xFD7046C5) + (x << 3) + x &= 0xFFFFFFFF + x = (x ^ 0xB55A4F09) ^ (x >> 16) + x &= 0xFFFFFFFF + r = float(x) / float(0xFFFFFFFF) # r in interval [0, 1] + r = r * 2 - 1 # r in [-1, 1] + return m * r + + def mutate(self, point, idx): + inner_meta = None + point_offset = None for axis in self.axes: - offset = self.max_offset[axis] - if offset == 0.0: - pass - else: - random_offset = self.get_random_number() * offset - point.positions[axis] += random_offset - changed = True - - return changed - - @staticmethod - def calculate_new_bounds(current_point, next_point): - """ - Take two adjacent points and recalculate their shared bound - - Args: - next_point(Point): Next point - current_point(Point): Current point - """ - - for axis in current_point.positions.keys(): - new_bound = (current_point.positions[axis] + - next_point.positions[axis]) / 2 - - current_point.upper[axis] = new_bound - next_point.lower[axis] = new_bound - - def mutate(self, iterator): - """ - An iterator that takes another iterator, applies a random offset to - each point and then yields it - - Args: - iterator: Iterator to mutate - - Yields: - Point: Mutated points - """ - - next_point = current_point = None - - for next_point in iterator: - changed = self.apply_offset(next_point) - - if current_point is not None: - if changed: - # If point wasn't changed don't update bounds - if next_point.lower == current_point.upper: - # If leaving and re-entering ROI don't update bounds - self.calculate_new_bounds(current_point, next_point) - - yield current_point - - current_point = next_point - - yield next_point + offset = self.calc_offset(axis, idx) + point.positions[axis] += offset + if axis in point.lower and axis in point.upper: + inner_axis = axis + point_offset = offset + if inner_axis is not None: + # recalculate lower bounds + idx -= 1 + prev_offset = self.calc_offset(inner_axis, idx) + offset = (point_offset + prev_offset) / 2 + point.lower[inner_axis] += offset + # recalculate upper bounds + idx += 2 + next_offset = self.calc_offset(inner_axis, idx) + offset = (point_offset + next_offset) / 2 + point.upper[inner_axis] += offset + return point def to_dict(self): """Convert object attributes into a dictionary""" diff --git a/org.eclipse.scanning.points/scripts/scanpointgenerator/numjy/__init__.py b/org.eclipse.scanning.points/scripts/scanpointgenerator/numjy/__init__.py index b12989680..25d43c412 100644 --- a/org.eclipse.scanning.points/scripts/scanpointgenerator/numjy/__init__.py +++ b/org.eclipse.scanning.points/scripts/scanpointgenerator/numjy/__init__.py @@ -16,6 +16,10 @@ import os -from jycore import * -from jymaths import * -from jycomparisons import * +''' +We import jcore, jymaths and jycomparisons only on jython +''' +if os.name == 'java': + from jycore import * + from jymaths import * + from jycomparisons import * diff --git a/org.eclipse.scanning.points/scripts/scanpointgenerator/numjy/jycore.py b/org.eclipse.scanning.points/scripts/scanpointgenerator/numjy/jycore.py index 400a5634f..d310aa67f 100644 --- a/org.eclipse.scanning.points/scripts/scanpointgenerator/numjy/jycore.py +++ b/org.eclipse.scanning.points/scripts/scanpointgenerator/numjy/jycore.py @@ -291,6 +291,9 @@ def asarray(data, dtype=None): asanyarray = asarray +def copy(a, order='K'): + return ndarray(buffer=a._jdataset(), copy=True) + @_wrap def asfarray(data, dtype=None): jdata = __cvt_jobj(data, copy=False, force=True) diff --git a/org.eclipse.scanning.points/scripts/scanpointgenerator/plotgenerator.py b/org.eclipse.scanning.points/scripts/scanpointgenerator/plotgenerator.py index 8fba653cc..f3b282326 100644 --- a/org.eclipse.scanning.points/scripts/scanpointgenerator/plotgenerator.py +++ b/org.eclipse.scanning.points/scripts/scanpointgenerator/plotgenerator.py @@ -1,3 +1,4 @@ + ### # Copyright (c) 2016 Diamond Light Source Ltd. # @@ -11,6 +12,7 @@ # Charles Mita - initial API and implementation and/or initial documentation # ### + MARKER_SIZE = 10 @@ -23,12 +25,16 @@ def plot_generator(gen, excluder=None, show_indexes=True): if excluder is not None: roi = excluder.roi overlay = plt.subplot(111, aspect='equal') - if roi.name == "Rectangle": - lower_left = (roi.centre[0] - roi.width/2, roi.centre[1] - roi.height/2) - overlay.add_patch(Rectangle(lower_left, roi.width, roi.height, fill=False)) - if roi.name == "Circle": + if isinstance(roi, RectangularROI): + overlay.add_patch(Rectangle(roi.start, roi.width, roi.height, fill=False)) + if isinstance(roi, CircularROI): overlay.add_patch(Circle(roi.centre, roi.radius, fill=False)) + if not isinstance(gen, CompoundGenerator): + excluders = [] if excluder is None else [excluder] + gen = CompoundGenerator([gen], excluders, []) + gen.prepare() + # points for spline generation x, y = [], [] # capture points and indexes @@ -113,6 +119,6 @@ def plot_generator(gen, excluder=None, show_indexes=True): for i, x, y in zip(capi, capx, capy): plt.annotate(i, (x, y), xytext=(MARKER_SIZE/2, MARKER_SIZE/2), textcoords='offset points') - indexes = ["%s (size %d)" % z for z in zip(gen.index_names, gen.index_dims)] - plt.title("Dataset: [%s]" % (", ".join(indexes))) + #indexes = ["%s (size %d)" % z for z in zip(gen.index_names, gen.index_dims)] + #plt.title("Dataset: [%s]" % (", ".join(indexes))) plt.show() diff --git a/org.eclipse.scanning.points/scripts/scanpointgenerator/version.py b/org.eclipse.scanning.points/scripts/scanpointgenerator/version.py index 8f7ffee32..cf7ada57f 100644 --- a/org.eclipse.scanning.points/scripts/scanpointgenerator/version.py +++ b/org.eclipse.scanning.points/scripts/scanpointgenerator/version.py @@ -1,3 +1,4 @@ + ### # Copyright (c) 2016 Diamond Light Source Ltd. # @@ -12,3 +13,4 @@ # ### __version__ = '1.6.1' + diff --git a/org.eclipse.scanning.points/src/org/eclipse/scanning/points/CompoundSpgIterator.java b/org.eclipse.scanning.points/src/org/eclipse/scanning/points/CompoundSpgIterator.java index 92800740d..ff3cc3ac6 100644 --- a/org.eclipse.scanning.points/src/org/eclipse/scanning/points/CompoundSpgIterator.java +++ b/org.eclipse.scanning.points/src/org/eclipse/scanning/points/CompoundSpgIterator.java @@ -14,7 +14,6 @@ import java.util.Collection; import java.util.Iterator; import java.util.LinkedList; -import java.util.List; import org.eclipse.dawnsci.analysis.dataset.roi.CircularROI; import org.eclipse.dawnsci.analysis.dataset.roi.EllipticalROI; @@ -70,7 +69,7 @@ public CompoundSpgIterator(CompoundGenerator gen) throws GeneratorException { JythonObjectFactory compoundGeneratorFactory = ScanPointGeneratorFactory.JCompoundGeneratorFactory(); - Object[] excluders = {}; //getExcluders(gen.getModel().getRegions()); TODO put back in when excluders are fixed in Python + Object[] excluders = getExcluders(gen.getModel().getRegions()); Object[] mutators = getMutators(gen.getModel().getMutators()); @SuppressWarnings("unchecked") diff --git a/org.eclipse.scanning.points/src/org/eclipse/scanning/points/PointGeneratorService.java b/org.eclipse.scanning.points/src/org/eclipse/scanning/points/PointGeneratorService.java index f54af1f41..b115a168e 100644 --- a/org.eclipse.scanning.points/src/org/eclipse/scanning/points/PointGeneratorService.java +++ b/org.eclipse.scanning.points/src/org/eclipse/scanning/points/PointGeneratorService.java @@ -220,11 +220,6 @@ public boolean containsPoint(IPosition pos) { } return ret; } - - @Override - public IPointGenerator createCompoundGenerator(IPointGenerator... generators) throws GeneratorException { - return new CompoundGenerator(generators); - } @Override public Collection getRegisteredGenerators() { @@ -247,6 +242,11 @@ public IPointGenerator createGenerator(String id) } } + @Override + public IPointGenerator createCompoundGenerator(IPointGenerator... generators) throws GeneratorException { + return new CompoundGenerator(generators); + } + @Override public IPointGenerator createCompoundGenerator(CompoundModel cmodel) throws GeneratorException { diff --git a/org.eclipse.scanning.server/src/org/eclipse/scanning/server/servlet/ScanProcess.java b/org.eclipse.scanning.server/src/org/eclipse/scanning/server/servlet/ScanProcess.java index f484565a8..b00db2ad6 100644 --- a/org.eclipse.scanning.server/src/org/eclipse/scanning/server/servlet/ScanProcess.java +++ b/org.eclipse.scanning.server/src/org/eclipse/scanning/server/servlet/ScanProcess.java @@ -291,7 +291,7 @@ private IDeviceController createRunnableDevice(ScanBean bean, IPointGenerator IPointGenerator generator = getGenerator(req); scanModel.setPositionIterable(generator); - ScanEstimator estimator = new ScanEstimator(Services.getGeneratorService(), bean.getScanRequest()); + ScanEstimator estimator = new ScanEstimator(gen, req.getDetectors(), 0); bean.setSize(estimator.getSize()); scanModel.setFilePath(bean.getFilePath()); diff --git a/org.eclipse.scanning.test/src/org/eclipse/scanning/test/malcolm/real/ExampleMalcolmDeviceTest.java b/org.eclipse.scanning.test/src/org/eclipse/scanning/test/malcolm/real/ExampleMalcolmDeviceTest.java index 3a9474fff..c49fe0881 100644 --- a/org.eclipse.scanning.test/src/org/eclipse/scanning/test/malcolm/real/ExampleMalcolmDeviceTest.java +++ b/org.eclipse.scanning.test/src/org/eclipse/scanning/test/malcolm/real/ExampleMalcolmDeviceTest.java @@ -332,8 +332,7 @@ public void configureAndRunDummyMalcolm() throws Exception { crUnionArray[1] = PVDataFactory.getPVDataCreate().createPVUnion(union); crUnionArray[1].set(expectedExcluder2PVStructure); - // TODO Put back in when excluders are fixed in python - //configurePVStructure.getUnionArrayField("generator.excluders").put(0, crUnionArray.length, crUnionArray, 0); + configurePVStructure.getUnionArrayField("generator.excluders").put(0, crUnionArray.length, crUnionArray, 0); PVString fileDirVal = configurePVStructure.getSubField(PVString.class, "fileDir"); fileDirVal.put("/TestFile/Dir"); diff --git a/org.eclipse.scanning.test/src/org/eclipse/scanning/test/points/RandomOffsetGridTest.java b/org.eclipse.scanning.test/src/org/eclipse/scanning/test/points/RandomOffsetGridTest.java index 9d4abcfd4..3b2040cab 100644 --- a/org.eclipse.scanning.test/src/org/eclipse/scanning/test/points/RandomOffsetGridTest.java +++ b/org.eclipse.scanning.test/src/org/eclipse/scanning/test/points/RandomOffsetGridTest.java @@ -12,15 +12,14 @@ package org.eclipse.scanning.test.points; -import static org.junit.Assert.assertEquals; +import static org.junit.Assert.assertNotEquals; -import java.util.List; +import java.util.Iterator; import org.eclipse.scanning.api.points.IPointGenerator; import org.eclipse.scanning.api.points.IPointGeneratorService; -import org.eclipse.scanning.api.points.IPosition; -import org.eclipse.scanning.api.points.Point; import org.eclipse.scanning.api.points.models.BoundingBox; +import org.eclipse.scanning.api.points.models.GridModel; import org.eclipse.scanning.api.points.models.RandomOffsetGridModel; import org.eclipse.scanning.points.PointGeneratorService; import org.junit.Before; @@ -44,22 +43,25 @@ public void testSimpleBox() throws Exception { box.setFastAxisLength(5); box.setSlowAxisLength(10); - RandomOffsetGridModel model = new RandomOffsetGridModel("x", "y"); - model.setSlowAxisPoints(5); - model.setFastAxisPoints(5); - model.setBoundingBox(box); - model.setSeed(10); - model.setOffset(25); + RandomOffsetGridModel rmodel = new RandomOffsetGridModel("x", "y"); + rmodel.setSlowAxisPoints(5); + rmodel.setFastAxisPoints(5); + rmodel.setBoundingBox(box); + rmodel.setSeed(10); + rmodel.setOffset(25); - IPointGenerator gen = service.createGenerator(model); - List pointList = gen.createPoints(); + IPointGenerator genWithRandom = service.createGenerator(rmodel); + GeneratorUtil.testGeneratorPoints(genWithRandom, 5, 5); + + GridModel gmodel = new GridModel("x", "y"); + gmodel.setSlowAxisPoints(5); + gmodel.setFastAxisPoints(5); + gmodel.setBoundingBox(box); + IPointGenerator gen = service.createGenerator(gmodel); - assertEquals(new Point("x", 0, 0.012403455250000084,"y", 0, 0.09924303325000006), pointList.get(0)); - assertEquals(new Point("x", 1, 0.837235318,"y", 0, 0.1643560529999999), pointList.get(1)); - assertEquals(new Point("x", 2, 2.20470153075,"y", 0, 0.018593022749999966), pointList.get(2)); - assertEquals(new Point("x", 3, 3.057925353,"y", 0, -0.024424061750000003), pointList.get(3)); - assertEquals(new Point("x", 4, 3.78130160075,"y", 0, 0.021858763000000003), pointList.get(4)); - assertEquals(new Point("x", 0, 0.09698760274999996,"y", 1, 1.83863665575), pointList.get(5)); + for (Iterator it1 = genWithRandom.iterator(), it2 = gen.iterator(); it1.hasNext() && it2.hasNext();) { + assertNotEquals(it1.next(), it2.next()); + } } } diff --git a/org.eclipse.scanning.test/src/org/eclipse/scanning/test/points/ScanPointGeneratorFactoryTest.java b/org.eclipse.scanning.test/src/org/eclipse/scanning/test/points/ScanPointGeneratorFactoryTest.java index beb9be824..3aeee3dcc 100644 --- a/org.eclipse.scanning.test/src/org/eclipse/scanning/test/points/ScanPointGeneratorFactoryTest.java +++ b/org.eclipse.scanning.test/src/org/eclipse/scanning/test/points/ScanPointGeneratorFactoryTest.java @@ -173,7 +173,7 @@ public void testJLissajousGeneratorFactory() { expected_points.add(new Point("X", 2, 0.03768323863482717, "Y", 2, 0.05649510414594954, false)); expected_points.add(new Point("X", 3, 0.05649510414594954, "Y", 3, 0.08464228865511125, false)); expected_points.add(new Point("X", 4, 0.07527128613841116, "Y", 4, 0.1126691918405678, false)); - expected_points.add(new Point("X", 5, 0.0939999251732282, "Y", 5, 0.14053598593929348, false)); + expected_points.add(new Point("X", 5, 0.0939999251732282, "Y", 5, 0.14053598593929345, false)); int index = 0; while (iterator.hasNext() && index < 6){ // Just get first few points @@ -263,26 +263,19 @@ public void testJCompoundGeneratorFactoryWithMutatedRaster() { generators, excluders, mutators); List expected_points = new ArrayList(); -// expected_points.add(new MapPosition("x:0:1.1984860665000001, y:0:2.0248069105")); -// expected_points.add(new MapPosition("x:1:2.328712106, y:0:1.674470636")); -// expected_points.add(new MapPosition("x:2:3.0371860455, y:0:2.4094030615")); -// expected_points.add(new MapPosition("x:3:3.9511518765, y:0:2.115850706")); -// expected_points.add(new MapPosition("x:4:5.043717526, y:0:1.5626032015")); -// expected_points.add(new MapPosition("x:0:0.6772733115, y:1:4.1939752055")); -// expected_points.add(new MapPosition("x:1:1.5828061555000001, y:1:3.9489767459999996")); -// expected_points.add(new MapPosition("x:2:3.3888981960000004, y:1:3.661987452")); -// expected_points.add(new MapPosition("x:3:3.9093635265, y:1:4.2730717205")); -// expected_points.add(new MapPosition("x:4:4.554744956, y:1:3.8436031415")); - expected_points.add(new Point("x", 0, 1.0248069105000002, "y", 0, 2.1984860665)); - expected_points.add(new Point("x", 1, 1.674470636, "y", 0, 2.328712106)); - expected_points.add(new Point("x", 2, 3.4094030615, "y", 0, 2.0371860455)); - expected_points.add(new Point("x", 3, 4.115850706, "y", 0, 1.9511518765)); - expected_points.add(new Point("x", 4, 4.5626032015, "y", 0, 2.043717526)); - expected_points.add(new Point("x", 0, 1.1939752055, "y", 1, 3.6772733115)); - expected_points.add(new Point("x", 1, 1.9489767459999996, "y", 1, 3.5828061555000001)); - expected_points.add(new Point("x", 2, 2.661987452, "y", 1, 4.3888981960000004)); - expected_points.add(new Point("x", 3, 4.2730717205, "y", 1, 3.9093635265)); - expected_points.add(new Point("x", 4, 4.8436031415, "y", 1, 3.554744956)); + + // This list of values can be regenerated by running the iterator and printing + // the points. The toString() method is then inverted to a Point using parse. + expected_points.add(Point.parse("y(0)=2.3467273793292063, x(0)=1.3423125841287693")); + expected_points.add(Point.parse("y(0)=1.9389526430608128, x(1)=2.386072452619223")); + expected_points.add(Point.parse("y(0)=2.2767811479924203, x(2)=3.2131047487987914")); + expected_points.add(Point.parse("y(0)=1.585269318447744, x(3)=3.9674925148644236")); + expected_points.add(Point.parse("y(0)=2.397560627408689, x(4)=5.266805527444651")); + expected_points.add(Point.parse("y(1)=3.5015289536680863, x(0)=1.3197151394141176")); + expected_points.add(Point.parse("y(1)=4.341493416307841, x(1)=2.150155718356873")); + expected_points.add(Point.parse("y(1)=4.480657927454603, x(2)=3.377400373778632")); + expected_points.add(Point.parse("y(1)=4.1811883078611425, x(3)=4.067826935687993")); + expected_points.add(Point.parse("y(1)=4.308295570036465, x(4)=4.779800611147611")); int index = 0; while (iterator.hasNext() && index < 10){ // Just get first few points diff --git a/org.eclipse.scanning.test/src/org/eclipse/scanning/test/scan/servlet/ScanServletTest.java b/org.eclipse.scanning.test/src/org/eclipse/scanning/test/scan/servlet/ScanServletTest.java index 080ea7972..32da42fc2 100644 --- a/org.eclipse.scanning.test/src/org/eclipse/scanning/test/scan/servlet/ScanServletTest.java +++ b/org.eclipse.scanning.test/src/org/eclipse/scanning/test/scan/servlet/ScanServletTest.java @@ -1,102 +1,103 @@ -/*- - ******************************************************************************* - * Copyright (c) 2011, 2016 Diamond Light Source Ltd. - * All rights reserved. This program and the accompanying materials - * are made available under the terms of the Eclipse Public License v1.0 - * which accompanies this distribution, and is available at - * http://www.eclipse.org/legal/epl-v10.html - * - * Contributors: - * Matthew Gerring - initial API and implementation and/or initial documentation - *******************************************************************************/ -package org.eclipse.scanning.test.scan.servlet; - -import static org.junit.Assert.assertEquals; - -import java.net.URISyntaxException; -import java.util.List; - -import org.eclipse.scanning.api.event.EventException; -import org.eclipse.scanning.api.event.scan.ScanBean; -import org.eclipse.scanning.api.event.scan.ScanRequest; -import org.eclipse.scanning.api.points.models.StepModel; -import org.eclipse.scanning.server.servlet.AbstractConsumerServlet; -import org.eclipse.scanning.server.servlet.ScanServlet; -import org.junit.Test; - -public class ScanServletTest extends AbstractServletTest { - - protected AbstractConsumerServlet createServlet() throws EventException, URISyntaxException { - - ScanServlet servlet = new ScanServlet(); - servlet.setBroker(uri.toString()); - servlet.setSubmitQueue("org.eclipse.scanning.test.servlet.submitQueue"); - servlet.setStatusSet("org.eclipse.scanning.test.servlet.statusSet"); - servlet.setStatusTopic("org.eclipse.scanning.test.servlet.statusTopic"); - servlet.setPauseOnStart(false); - servlet.connect(); // Gets called by Spring automatically - - return servlet; - } - - /** - * This test mimiks a client submitting a scan. - * - * @throws Exception - */ - @Test - public void testStepScan() throws Exception { - - ScanBean bean = createStepScan(); - runAndCheck(bean, 60); - } - - /** - * This test mimiks a client submitting a scan. - * - * @throws Exception - */ - @Test - public void testStepScanProcessing() throws Exception { - - System.setProperty("org.eclipse.scanning.api.preprocessor.name", "example"); - try { - ScanBean bean = createStepScan(); - List beans = runAndCheck(bean, 20); - // We now check that they all had xfred set. - for (ScanBean scanBean : beans) { - ScanRequest req = scanBean.getScanRequest(); - - StepModel step = (StepModel)req.getCompoundModel().getModels().toArray()[0]; - assertEquals("fred", step.getName()); - } - } finally { - System.setProperty("org.eclipse.scanning.api.preprocessor.name", ""); - } - - } - - - @Test - public void testGridScan() throws Exception { - - ScanBean bean = createGridScan(); - runAndCheck(bean, 20); - - } - - @Test - public void testStepGridScanNested1() throws Exception { - - ScanBean bean = createStepGridScan(1); - runAndCheck(bean, 100); - } - - @Test - public void testStepGridScanNested5() throws Exception { - - ScanBean bean = createStepGridScan(5); - runAndCheck(bean, 500); - } - -} +/*- + ******************************************************************************* + * Copyright (c) 2011, 2016 Diamond Light Source Ltd. + * All rights reserved. This program and the accompanying materials + * are made available under the terms of the Eclipse Public License v1.0 + * which accompanies this distribution, and is available at + * http://www.eclipse.org/legal/epl-v10.html + * + * Contributors: + * Matthew Gerring - initial API and implementation and/or initial documentation + *******************************************************************************/ +package org.eclipse.scanning.test.scan.servlet; + +import static org.junit.Assert.assertEquals; + +import java.net.URISyntaxException; +import java.util.List; + +import org.eclipse.scanning.api.event.EventException; +import org.eclipse.scanning.api.event.scan.ScanBean; +import org.eclipse.scanning.api.event.scan.ScanRequest; +import org.eclipse.scanning.api.points.models.StepModel; +import org.eclipse.scanning.server.servlet.AbstractConsumerServlet; +import org.eclipse.scanning.server.servlet.ScanServlet; +import org.junit.Test; + +public class ScanServletTest extends AbstractServletTest { + + protected AbstractConsumerServlet createServlet() throws EventException, URISyntaxException { + + ScanServlet servlet = new ScanServlet(); + servlet.setBroker(uri.toString()); + servlet.setSubmitQueue("org.eclipse.scanning.test.servlet.submitQueue"); + servlet.setStatusSet("org.eclipse.scanning.test.servlet.statusSet"); + servlet.setStatusTopic("org.eclipse.scanning.test.servlet.statusTopic"); + servlet.setPauseOnStart(false); + servlet.connect(); // Gets called by Spring automatically + + return servlet; + } + + /** + * This test mimiks a client submitting a scan. + * + * @throws Exception + */ + @Test + public void testStepScan() throws Exception { + + ScanBean bean = createStepScan(); + runAndCheck(bean, 60); + } + + /** + * This test mimiks a client submitting a scan. + * + * @throws Exception + */ + @Test + public void testStepScanProcessing() throws Exception { + + System.setProperty("org.eclipse.scanning.api.preprocessor.name", "example"); + try { + ScanBean bean = createStepScan(); + List beans = runAndCheck(bean, 20); + // We now check that they all had xfred set. + for (ScanBean scanBean : beans) { + ScanRequest req = scanBean.getScanRequest(); + + StepModel step = (StepModel)req.getCompoundModel().getModels().toArray()[0]; + assertEquals("fred", step.getName()); + } + } finally { + System.setProperty("org.eclipse.scanning.api.preprocessor.name", ""); + } + + } + + + @Test + public void testGridScan() throws Exception { + + ScanBean bean = createGridScan(); + runAndCheck(bean, 20); + + } + + @Test + public void testStepGridScanNested1() throws Exception { + + ScanBean bean = createStepGridScan(1); + runAndCheck(bean, 100); + } + + @Test + public void testStepGridScanNested5() throws Exception { + + ScanBean bean = createStepGridScan(5); + runAndCheck(bean, 500); + } + +} +