Esempio n. 1
0
def exercise():
  g = graphics_utils.make_rainbow_gradient(nbins=21)
  assert approx_equal(g[0], (0.0, 0.0, 1.0))
  assert approx_equal(g[10], (0.0, 1.0, 0.0))
  assert approx_equal(g[-1], (1.0, 0.0, 0.0))
  sel = flex.bool([True] * 10)
  sel[5] = False
  r = graphics_utils.color_rainbow(
    selection=sel,
    color_all=False)
  assert approx_equal(r[0], (0.0,0.0,1.0))
  assert approx_equal(r[-1], (1.0,0.0,0.0))
  assert approx_equal(r[4], (0.0,1.0,0.0))
  r2 = graphics_utils.color_rainbow(
    selection=sel,
    color_all=True)
  assert approx_equal(r2[0], (0.0,0.0,1.0))
  assert approx_equal(r2[-1], (1.0,0.0,0.0))
  assert approx_equal(r2[6], (2/3.,1.0,0.0))
  b = flex.double([4.0,5.2,1.7,6.9,9.5,24.3])
  c = graphics_utils.color_by_property(
    properties=b,
    selection=flex.bool(b.size(), True))
  assert approx_equal(c[2], (0.0,0.0,1.0))
  c2 = graphics_utils.scale_selected_colors(
    input_colors=c,
    selection=flex.bool(c.size(), True),
    scale=0.9)
  assert approx_equal(c2[2], (0.0,0.0,0.9))
  c3 = graphics_utils.grayscale_by_property(
    properties=b,
    selection=flex.bool(b.size(), True))
  assert approx_equal(c3[2], (0.95,0.95,0.95))
Esempio n. 2
0
def exercise () :
  g = graphics_utils.make_rainbow_gradient(nbins=21)
  assert approx_equal(g[0], (0.0, 0.0, 1.0))
  assert approx_equal(g[10], (0.0, 1.0, 0.0))
  assert approx_equal(g[-1], (1.0, 0.0, 0.0))
  sel = flex.bool([True] * 10)
  sel[5] = False
  r = graphics_utils.color_rainbow(
    selection=sel,
    color_all=False)
  assert approx_equal(r[0], (0.0,0.0,1.0))
  assert approx_equal(r[-1], (1.0,0.0,0.0))
  assert approx_equal(r[4], (0.0,1.0,0.0))
  r2 = graphics_utils.color_rainbow(
    selection=sel,
    color_all=True)
  assert approx_equal(r2[0], (0.0,0.0,1.0))
  assert approx_equal(r2[-1], (1.0,0.0,0.0))
  assert approx_equal(r2[6], (2/3.,1.0,0.0))
  b = flex.double([4.0,5.2,1.7,6.9,9.5,24.3])
  c = graphics_utils.color_by_property(
    properties=b,
    selection=flex.bool(b.size(), True))
  assert approx_equal(c[2], (0.0,0.0,1.0))
  c2 = graphics_utils.scale_selected_colors(
    input_colors=c,
    selection=flex.bool(c.size(), True),
    scale=0.9)
  assert approx_equal(c2[2], (0.0,0.0,0.9))
  c3 = graphics_utils.grayscale_by_property(
    properties=b,
    selection=flex.bool(b.size(), True))
  assert approx_equal(c3[2], (0.95,0.95,0.95))
Esempio n. 3
0
 def color_b (self) :
   cached = self._color_cache.get("b")
   if cached is not None :
     self.atom_colors = cached
   else :
     from scitbx import graphics_utils
     self.atom_colors = graphics_utils.color_by_property(
       properties=self.atoms.extract_b(),
       selection=self.visibility.atoms_visible,
       color_all=False,
       gradient_type="rainbow")
     self._color_cache["b"] = self.atom_colors
Esempio n. 4
0
    def generate_view_data(self):
        from scitbx.array_family import flex
        from scitbx import graphics_utils
        settings = self.settings
        data_for_colors = data_for_radii = None
        data = self.data  #self.work_array.data()
        sigmas = self.sigmas
        if (isinstance(data, flex.double) and data.all_eq(0)):
            data = flex.double(data.size(), 1)
        if ((self.multiplicities is not None)
                and (settings.scale_colors_multiplicity)):
            data_for_colors = self.multiplicities.data().as_double()
            assert data_for_colors.size() == data.size()
        elif (settings.sqrt_scale_colors) and (isinstance(data, flex.double)):
            data_for_colors = flex.sqrt(flex.abs(data))
        elif isinstance(data, flex.complex_double):
            data_for_colors = self.radians
            # when using map coefficients the sigmas are filled with foms if provided
            #if self.sigmas:
            #  self.foms = self.sigmas
            foms_for_colours = self.foms
        elif (settings.sigma_color) and sigmas is not None:
            data_for_colors = sigmas.as_double()
        else:
            data_for_colors = flex.abs(data.deep_copy())

        uc = self.work_array.unit_cell()
        #abc = uc.parameters()[0:3]
        min_dist = min(uc.reciprocal_space_vector((1, 1, 1)))
        min_radius = 0.20 * min_dist
        max_radius = 40 * min_dist
        if ((self.multiplicities is not None)
                and (settings.scale_radii_multiplicity)):
            #data_for_radii = data.deep_copy()
            data_for_radii = self.multiplicities.data().as_double()

            if (settings.sigma_radius) and sigmas is not None:
                data_for_radii = sigmas * self.multiplicities.as_double()
                #print "sigmas: " + self.miller_array.info().label_string()
            assert data_for_radii.size() == data.size()
        #elif (settings.sqrt_scale_radii) and (isinstance(data, flex.double)):
        #  data_for_radii = flex.sqrt(flex.abs(data))
        elif (settings.sigma_radius) and sigmas is not None:
            data_for_radii = sigmas.as_double()
            #print "sigmas: " + self.miller_array.info().label_string()
        else:
            #data_for_radii = flex.abs(data.deep_copy())
            data_for_radii = nth_power_scale(data.deep_copy(),
                                             settings.nth_power_scale_radii)
        if (settings.slice_mode):
            data = data.select(self.slice_selection)
            if (not settings.keep_constant_scale):
                data_for_radii = data_for_radii.select(self.slice_selection)
                data_for_colors = data_for_colors.select(self.slice_selection)
                foms_for_colours = foms_for_colours.select(
                    self.slice_selection)
        if isinstance(data, flex.complex_double):
            if len([e for e in foms_for_colours if math.isnan(e)]) > 0:
                colors = graphics_utils.colour_by_phi_FOM(
                    data_for_colors, None)
            else:
                colors = graphics_utils.colour_by_phi_FOM(
                    data_for_colors, foms_for_colours)
        elif (settings.color_scheme in ["rainbow", "heatmap", "redblue"]):
            colors = graphics_utils.color_by_property(
                properties=data_for_colors,
                selection=flex.bool(data_for_colors.size(), True),
                color_all=False,
                gradient_type=settings.color_scheme)
        elif (settings.color_scheme == "grayscale"):
            colors = graphics_utils.grayscale_by_property(
                properties=data_for_colors,
                selection=flex.bool(data_for_colors.size(), True),
                shade_all=False,
                invert=settings.black_background)
        else:
            if (settings.black_background):
                base_color = (1.0, 1.0, 1.0)
            else:
                base_color = (0.0, 0.0, 0.0)
            colors = flex.vec3_double(data_for_colors.size(), base_color)
        if (settings.slice_mode) and (settings.keep_constant_scale):
            colors = colors.select(self.slice_selection)
            data_for_radii = data_for_radii.select(self.slice_selection)

        #if (settings.sqrt_scale_radii) and (not settings.scale_radii_multiplicity):
        #  data_for_radii = flex.sqrt(flex.abs(data_for_radii))
        if len(data_for_radii):
            max_value = flex.max(data_for_radii)
            scale = max_radius / max_value
            radii = data_for_radii * (scale * self.settings.scale)
            too_small = radii < min_radius
            if (too_small.count(True) > 0):
                radii.set_selected(too_small,
                                   flex.double(radii.size(), min_radius))
            assert radii.size() == colors.size()
        else:
            radii = flex.double()
            max_radius = 0
        self.radii = radii
        self.max_radius = max_radius
        self.colors = colors
        if isinstance(data, flex.complex_double):
            self.foms = foms_for_colours
Esempio n. 5
0
 def generate_view_data(self):
     from scitbx.array_family import flex
     #from scitbx import graphics_utils
     settings = self.settings
     data_for_colors = data_for_radii = None
     if not self.fullprocessarray:
         return
     data = self.data  #self.work_array.data()
     sigmas = self.sigmas
     if (isinstance(data, flex.double) and data.all_eq(0)):
         data = flex.double(data.size(), 1)
     if ((self.multiplicities is not None)
             and (settings.scale_colors_multiplicity)):
         data_for_colors = self.multiplicities.data().as_double()
         assert data_for_colors.size() == data.size()
     elif (settings.sqrt_scale_colors) and (isinstance(data, flex.double)):
         data_for_colors = flex.sqrt(flex.abs(data))
     elif isinstance(data, flex.complex_double):
         data_for_colors = self.radians
         foms_for_colours = self.foms
         # assuming last part of the labels indicates the phase label as in ["FCALC","PHICALC"]
         self.colourlabel = self.miller_array.info().labels[-1]
     elif (settings.sigma_color) and sigmas is not None:
         data_for_colors = sigmas.as_double()
         self.colourlabel = self.miller_array.info().labels[-1]
     else:
         data_for_colors = flex.abs(data.deep_copy())
     uc = self.work_array.unit_cell()
     self.min_dist = min(uc.reciprocal_space_vector(
         (1, 1, 1))) * self.renderscale
     min_radius = 0.05 * self.min_dist
     max_radius = 0.5 * self.min_dist
     if ((self.multiplicities is not None)
             and (settings.scale_radii_multiplicity)):
         data_for_radii = self.multiplicities.data().as_double()
         if (settings.sigma_radius) and sigmas is not None:
             data_for_radii = sigmas * self.multiplicities.as_double()
         assert data_for_radii.size() == data.size()
     elif (settings.sigma_radius) and sigmas is not None:
         data_for_radii = sigmas.as_double()
     else:
         data_for_radii, self.nth_power_scale_radii = nth_power_scale(
             flex.abs(data.deep_copy()), settings.nth_power_scale_radii)
     if (settings.slice_mode):
         data = data.select(self.slice_selection)
         if (not settings.keep_constant_scale):
             data_for_radii = data_for_radii.select(self.slice_selection)
             data_for_colors = data_for_colors.select(self.slice_selection)
             foms_for_colours = foms_for_colours.select(
                 self.slice_selection)
     if isinstance(data, flex.complex_double):
         if self.isUsingFOMs():
             colors = graphics_utils.colour_by_phi_FOM(
                 data_for_colors, foms_for_colours)
         else:
             colors = graphics_utils.colour_by_phi_FOM(
                 data_for_colors, None)
     elif (settings.color_scheme in ["rainbow", "heatmap", "redblue"]):
         colors = graphics_utils.color_by_property(
             properties=data_for_colors,
             selection=flex.bool(data_for_colors.size(), True),
             color_all=False,
             gradient_type=settings.color_scheme)
     elif (settings.color_scheme == "grayscale"):
         colors = graphics_utils.grayscale_by_property(
             properties=data_for_colors,
             selection=flex.bool(data_for_colors.size(), True),
             shade_all=False,
             invert=settings.black_background)
     else:
         if (settings.black_background):
             base_color = (1.0, 1.0, 1.0)
         else:
             base_color = (0.0, 0.0, 0.0)
         colors = flex.vec3_double(data_for_colors.size(), base_color)
     if (settings.slice_mode) and (settings.keep_constant_scale):
         colors = colors.select(self.slice_selection)
         data_for_radii = data_for_radii.select(self.slice_selection)
     #if (settings.sqrt_scale_radii) and (not settings.scale_radii_multiplicity):
     #  data_for_radii = flex.sqrt(flex.abs(data_for_radii))
     if len(data_for_radii):
         #dat2 = flex.abs(flex.double([e for e in data_for_radii if not math.isnan(e)]))
         dat2 = flex.abs(
             flex.double(graphics_utils.NoNansArray(data_for_radii, 0.1)))
         # don't divide by 0 if dealing with selection of Rfree array where all values happen to be zero
         scale = max_radius / (flex.max(dat2) + 0.001)
         radii = data_for_radii * (self.settings.scale * scale)
         assert radii.size() == colors.size()
     else:
         radii = flex.double()
         max_radius = 0
     self.radii = radii
     self.max_radius = max_radius
     self.min_radius = min_radius
     self.colors = colors
     if isinstance(data, flex.complex_double):
         self.foms = foms_for_colours
Esempio n. 6
0
    def render(self, canvas):
        from scitbx.array_family import flex
        from libtbx.utils import frange
        import math
        size = self.GetSize()
        border = 10
        i_rows = flex.double_range(border, size[1] - border)
        scene = self.scene
        if self.scene.settings.scale_colors_multiplicity:
            data = self.scene.multiplicities.data()
        else:
            data = self.scene.data
            if self.settings.sqrt_scale_colors:
                data = flex.sqrt(data)
        min_data = flex.min(data)
        max_data = flex.max(data)
        data_for_colors = flex.double(
            frange(max_data, min_data, -(max_data - min_data) / len(i_rows)))
        tick_step = int(math.ceil((max_data - min_data) / 10))
        i_row_ticks = []
        tick_text = []
        start_tick = math.floor(max_data)
        i_tick = 0
        for i in range(len(data_for_colors) - 1):
            tick_d = start_tick - tick_step * i_tick
            if abs(data_for_colors[i] - tick_d) < abs(data_for_colors[i + 1] -
                                                      tick_d):
                i_row_ticks.append(i_rows[i])
                tick_text.append(str(int(tick_d)))
                i_tick += 1
        tick_d = start_tick - tick_step * i_tick
        if tick_d == min_data:
            i_row_ticks.append(i_rows[-1])
            tick_text.append(str(int(tick_d)))

        from scitbx import graphics_utils
        if (self.settings.color_scheme in ["rainbow", "heatmap", "redblue"]):
            colors = graphics_utils.color_by_property(
                properties=data_for_colors,
                selection=flex.bool(data_for_colors.size(), True),
                color_all=False,
                gradient_type=self.settings.color_scheme)
        elif (self.settings.color_scheme == "grayscale"):
            colors = graphics_utils.grayscale_by_property(
                properties=data_for_colors,
                selection=flex.bool(data_for_colors.size(), True),
                shade_all=False,
                invert=self.settings.black_background)
        else:
            if (self.settings.black_background):
                base_color = (1.0, 1.0, 1.0)
            else:
                base_color = (0.0, 0.0, 0.0)
            colors = flex.vec3_double(data_for_colors.size(), base_color)

        l_padding = border
        r_padding = 4 * border

        for i_row, color in zip(i_rows, colors):
            self.draw_line(canvas,
                           l_padding,
                           i_row,
                           size[0] - r_padding,
                           i_row,
                           color=color)

        for i_row, text in zip(i_row_ticks, tick_text):
            self.draw_text(canvas, text, size[0] - 0.8 * r_padding, i_row - 5)
            self.draw_line(canvas, size[0] - r_padding - 10, i_row,
                           size[0] - r_padding, i_row)
Esempio n. 7
0
 def generate_view_data(self):
     from scitbx.array_family import flex
     from scitbx import graphics_utils
     settings = self.settings
     data_for_colors = data_for_radii = None
     data = self.data  #self.work_array.data()
     if (isinstance(data, flex.double) and data.all_eq(0)):
         data = flex.double(data.size(), 1)
     if ((self.multiplicities is not None)
             and (settings.scale_colors_multiplicity)):
         data_for_colors = self.multiplicities.data().as_double()
         assert data_for_colors.size() == data.size()
     elif (settings.sqrt_scale_colors) and (isinstance(data, flex.double)):
         data_for_colors = flex.sqrt(data)
     else:
         data_for_colors = data.deep_copy()
     if ((self.multiplicities is not None)
             and (settings.scale_radii_multiplicity)):
         #data_for_radii = data.deep_copy()
         data_for_radii = self.multiplicities.data().as_double()
         assert data_for_radii.size() == data.size()
     elif (settings.sqrt_scale_radii) and (isinstance(data, flex.double)):
         data_for_radii = flex.sqrt(data)
     else:
         data_for_radii = data.deep_copy()
     if (settings.slice_mode):
         data = data.select(self.slice_selection)
         if (not settings.keep_constant_scale):
             data_for_radii = data_for_radii.select(self.slice_selection)
             data_for_colors = data_for_colors.select(self.slice_selection)
     if (settings.color_scheme in ["rainbow", "heatmap", "redblue"]):
         colors = graphics_utils.color_by_property(
             properties=data_for_colors,
             selection=flex.bool(data_for_colors.size(), True),
             color_all=False,
             gradient_type=settings.color_scheme)
     elif (settings.color_scheme == "grayscale"):
         colors = graphics_utils.grayscale_by_property(
             properties=data_for_colors,
             selection=flex.bool(data_for_colors.size(), True),
             shade_all=False,
             invert=settings.black_background)
     else:
         if (settings.black_background):
             base_color = (1.0, 1.0, 1.0)
         else:
             base_color = (0.0, 0.0, 0.0)
         colors = flex.vec3_double(data_for_colors.size(), base_color)
     if (settings.slice_mode) and (settings.keep_constant_scale):
         colors = colors.select(self.slice_selection)
         data_for_radii = data_for_radii.select(self.slice_selection)
     uc = self.work_array.unit_cell()
     abc = uc.parameters()[0:3]
     min_dist = min(uc.reciprocal_space_vector((1, 1, 1)))
     min_radius = 0.20 * min_dist
     max_radius = 40 * min_dist
     if (settings.sqrt_scale_radii) and (
             not settings.scale_radii_multiplicity):
         data_for_radii = flex.sqrt(data_for_radii)
     if len(data_for_radii):
         max_value = flex.max(data_for_radii)
         scale = max_radius / max_value
         radii = data_for_radii * scale
         too_small = radii < min_radius
         if (too_small.count(True) > 0):
             radii.set_selected(too_small,
                                flex.double(radii.size(), min_radius))
         assert radii.size() == colors.size()
     else:
         radii = flex.double()
         max_radius = 0
     self.radii = radii
     self.max_radius = max_radius
     self.colors = colors
Esempio n. 8
0
 def generate_view_data (self) :
   from scitbx.array_family import flex
   from scitbx import graphics_utils
   settings = self.settings
   data_for_colors = data_for_radii = None
   data = self.data #self.work_array.data()
   if (isinstance(data, flex.double) and data.all_eq(0)):
     data = flex.double(data.size(), 1)
   if ((self.multiplicities is not None) and
       (settings.scale_colors_multiplicity)) :
     data_for_colors = self.multiplicities.data().as_double()
     assert data_for_colors.size() == data.size()
   elif (settings.sqrt_scale_colors) and (isinstance(data, flex.double)) :
     data_for_colors = flex.sqrt(data)
   else :
     data_for_colors = data.deep_copy()
   if ((self.multiplicities is not None) and
       (settings.scale_radii_multiplicity)) :
     #data_for_radii = data.deep_copy()
     data_for_radii = self.multiplicities.data().as_double()
     assert data_for_radii.size() == data.size()
   elif (settings.sqrt_scale_radii) and (isinstance(data, flex.double)) :
     data_for_radii = flex.sqrt(data)
   else :
     data_for_radii = data.deep_copy()
   if (settings.slice_mode) :
     data = data.select(self.slice_selection)
     if (not settings.keep_constant_scale) :
       data_for_radii = data_for_radii.select(self.slice_selection)
       data_for_colors = data_for_colors.select(self.slice_selection)
   if (settings.color_scheme in ["rainbow", "heatmap", "redblue"]) :
     colors = graphics_utils.color_by_property(
       properties=data_for_colors,
       selection=flex.bool(data_for_colors.size(), True),
       color_all=False,
       gradient_type=settings.color_scheme)
   elif (settings.color_scheme == "grayscale") :
     colors = graphics_utils.grayscale_by_property(
       properties=data_for_colors,
       selection=flex.bool(data_for_colors.size(), True),
       shade_all=False,
       invert=settings.black_background)
   else :
     if (settings.black_background) :
       base_color = (1.0,1.0,1.0)
     else :
       base_color = (0.0,0.0,0.0)
     colors = flex.vec3_double(data_for_colors.size(), base_color)
   if (settings.slice_mode) and (settings.keep_constant_scale) :
     colors = colors.select(self.slice_selection)
     data_for_radii = data_for_radii.select(self.slice_selection)
   uc = self.work_array.unit_cell()
   abc = uc.parameters()[0:3]
   min_dist = min(uc.reciprocal_space_vector((1,1,1)))
   min_radius = 0.20 * min_dist
   max_radius = 40 * min_dist
   if (settings.sqrt_scale_radii) and (not settings.scale_radii_multiplicity):
     data_for_radii = flex.sqrt(data_for_radii)
   if len(data_for_radii):
     max_value = flex.max(data_for_radii)
     scale = max_radius / max_value
     radii = data_for_radii * scale
     too_small = radii < min_radius
     if (too_small.count(True) > 0) :
       radii.set_selected(too_small, flex.double(radii.size(), min_radius))
     assert radii.size() == colors.size()
   else:
     radii = flex.double()
     max_radius = 0
   self.radii = radii
   self.max_radius = max_radius
   self.colors = colors
Esempio n. 9
0
    def DrawNGLJavaScript(self):
        if self.miller_array is None:
            self.mprint("A miller array must be selected for drawing")
            return
        self.mprint("Composing NGL JavaScript...")
        h_axis = self.scene.axes[0]
        k_axis = self.scene.axes[1]
        l_axis = self.scene.axes[2]
        nrefls = self.scene.points.size()

        Hstararrowstart = roundoff(
            [-h_axis[0] * 100, -h_axis[1] * 100, -h_axis[2] * 100])
        Hstararrowend = roundoff(
            [h_axis[0] * 100, h_axis[1] * 100, h_axis[2] * 100])
        Hstararrowtxt = roundoff(
            [h_axis[0] * 102, h_axis[1] * 102, h_axis[2] * 102])
        Kstararrowstart = roundoff(
            [-k_axis[0] * 100, -k_axis[1] * 100, -k_axis[2] * 100])
        Kstararrowend = roundoff(
            [k_axis[0] * 100, k_axis[1] * 100, k_axis[2] * 100])
        Kstararrowtxt = roundoff(
            [k_axis[0] * 102, k_axis[1] * 102, k_axis[2] * 102])
        Lstararrowstart = roundoff(
            [-l_axis[0] * 100, -l_axis[1] * 100, -l_axis[2] * 100])
        Lstararrowend = roundoff(
            [l_axis[0] * 100, l_axis[1] * 100, l_axis[2] * 100])
        Lstararrowtxt = roundoff(
            [l_axis[0] * 102, l_axis[1] * 102, l_axis[2] * 102])
        # make arrow font size roughly proportional to radius of highest resolution shell
        fontsize = str(1.0 + roundoff(
            math.pow(max(self.miller_array.index_span().max()), 1.0 / 3.0)))

        arrowstr = """
    // xyz arrows
    shape.addSphere( [0,0,0] , [ 1, 1, 1 ], 0.3, 'Origo');
    //blue-x
    shape.addArrow( %s, %s , [ 0, 0, 1 ], 0.1);
    //green-y
    shape.addArrow( %s, %s , [ 0, 1, 0 ], 0.1);
    //red-z
    shape.addArrow( %s, %s , [ 1, 0, 0 ], 0.1);

    shape.addText( %s, [ 0, 0, 1 ], %s, 'h');
    shape.addText( %s, [ 0, 1, 0 ], %s, 'k');
    shape.addText( %s, [ 1, 0, 0 ], %s, 'l');
    """ % (str(Hstararrowstart), str(Hstararrowend), str(Kstararrowstart),
           str(Kstararrowend), str(Lstararrowstart), str(Lstararrowend),
           Hstararrowtxt, fontsize, Kstararrowtxt, fontsize, Lstararrowtxt,
           fontsize)

        # Make colour gradient array used for drawing a bar of colours next to associated values on the rendered html
        mincolourscalar = self.othermindata[self.icolourcol]
        maxcolourscalar = self.othermaxdata[self.icolourcol]
        if self.settings.sigma_color:
            mincolourscalar = self.otherminsigmas[self.icolourcol]
            maxcolourscalar = self.othermaxsigmas[self.icolourcol]
        span = maxcolourscalar - mincolourscalar
        ln = 60
        incr = span / ln
        colourgradarrays = []
        val = mincolourscalar
        colourscalararray = flex.double()
        colourscalararray.append(val)
        for j, sc in enumerate(range(ln)):
            val += incr
            colourscalararray.append(val)
        if self.otherscenes[self.icolourcol].miller_array.is_complex_array():
            # When displaying phases from map coefficients together with fom values
            # compute colour map chart as a function of fom and phase values (x,y axis)
            incr = 360.0 / ln
            val = 0.0
            colourscalararray = flex.double()
            colourscalararray.append(val)
            for j in enumerate(range(ln)):
                val += incr
                colourscalararray.append(val)

            fomarrays = []
            if self.otherscenes[self.icolourcol].isUsingFOMs():
                fomln = 50
                fom = 1.0
                fomdecr = 1.0 / (fomln - 1.0)
                # make fomln fom arrays of size len(colourscalararray) when calling colour_by_phi_FOM
                for j in range(fomln):
                    fomarrays.append(flex.double(len(colourscalararray), fom))
                    fom -= fomdecr
                for j in range(fomln):
                    colourgradarrays.append(
                        graphics_utils.colour_by_phi_FOM(
                            colourscalararray *
                            (math.pi / 180.0), fomarrays[j]) * 255.0)
            else:
                fomln = 1
                fomarrays = [1.0]
                colourgradarrays.append(
                    graphics_utils.colour_by_phi_FOM(colourscalararray *
                                                     (math.pi / 180.0)) *
                    255.0)
        else:
            fomln = 1
            fomarrays = [1.0]
            colourgradarrays.append(
                graphics_utils.color_by_property(
                    properties=flex.double(colourscalararray),
                    selection=flex.bool(len(colourscalararray), True),
                    color_all=False,
                    gradient_type=self.settings.color_scheme) * 255.0)

        colors = self.otherscenes[self.icolourcol].colors
        radii = self.otherscenes[self.iradiicol].radii
        points = self.scene.points
        hkls = self.scene.indices
        dres = self.scene.dres
        colstr = self.scene.miller_array.info().label_string()
        data = self.scene.data
        colourlabel = self.otherscenes[self.icolourcol].colourlabel
        fomlabel = self.otherscenes[self.icolourcol].fomlabel
        #import code, traceback; code.interact(local=locals(), banner="".join( traceback.format_stack(limit=10) ) )
        assert (colors.size() == radii.size() == nrefls)
        colours = []
        positions = []
        radii2 = []
        spbufttips = []

        self.workingbinvals = []
        if not self.binarray == "Resolution":
            ibinarray = int(self.binarray)
            self.workingbinvals = [
                self.othermindata[ibinarray] - 0.1,
                self.othermaxdata[ibinarray] + 0.1
            ]
            self.workingbinvals.extend(self.binvals)
            self.workingbinvals.sort()
            if self.workingbinvals[0] < 0.0:
                self.workingbinvals.append(0.0)
                self.workingbinvals.sort()
            bindata = self.otherscenes[ibinarray].data
            if self.otherscenes[ibinarray].work_array.is_complex_array():
                bindata = self.otherscenes[ibinarray].ampl
        else:
            self.workingbinvals = self.binvals
            colstr = "dres"
            bindata = 1.0 / dres
        self.nbin = len(self.workingbinvals)

        for ibin in range(self.nbin):
            colours.append([])  # colours and positions are 3 x size of data()
            positions.append([])
            radii2.append([])
            spbufttips.append([])

        def data2bin(d):
            for ibin, binval in enumerate(self.workingbinvals):
                if (ibin + 1) == self.nbin:
                    return ibin
                if d > binval and d <= self.workingbinvals[ibin + 1]:
                    return ibin
            #import code, traceback; code.interact(local=locals(), banner="".join( traceback.format_stack(limit=10) ) )
            raise Sorry("Should never get here")

        #import code, traceback; code.interact(local=locals(), banner="".join( traceback.format_stack(limit=10) ) )
        for i, hklstars in enumerate(points):
            # bin currently displayed data according to the values of another miller array
            ibin = data2bin(bindata[i])
            spbufttip = 'H,K,L: %s, %s, %s' % (hkls[i][0], hkls[i][1],
                                               hkls[i][2])
            spbufttip += '\ndres: %s ' % str(roundoff(dres[i]))
            spbufttip += '\' + AA + \''
            for j, otherscene in enumerate(self.otherscenes):
                ocolstr = self.proc_arrays[j].info().label_string()
                odata = otherscene.data
                od = ""
                if self.proc_arrays[j].is_complex_array():
                    if not math.isnan(otherscene.foms[i]):
                        od = str(roundoff(otherscene.ampl[i])) + ", " + str(roundoff(otherscene.phases[i])  ) + \
                          "\' + DGR + \'" +  ", " + str(roundoff(otherscene.foms[i])  )
                    else:
                        od = str(roundoff(otherscene.ampl[i])) + ", " + str(roundoff(otherscene.phases[i])  ) + \
                          "\' + DGR + \'"
                elif self.proc_arrays[j].sigmas() is not None:
                    od = str(roundoff(odata[i])) + ", " + str(
                        roundoff(otherscene.sigmas[i]))
                else:
                    od = str(roundoff(odata[i]))
                if not (math.isnan(abs(odata[i]))
                        or odata[i] == display.inanval):
                    spbufttip += "\n%s: %s" % (ocolstr, od)
            positions[ibin].extend(roundoff(list(hklstars)))
            colours[ibin].extend(roundoff(list(colors[i]), 2))
            radii2[ibin].append(roundoff(radii[i], 2))
            spbufttips[ibin].append(spbufttip)

        spherebufferstr = """
  ttips = new Array(%d)
  positions = new Array(%d)
  colours = new Array(%d)
  radii = new Array(%d)
  spherebufs = new Array(%d)
    """ % (self.nbin, self.nbin, self.nbin, self.nbin, self.nbin)

        negativeradiistr = ""
        cntbin = 0
        self.binstrs = []
        for ibin in range(self.nbin):
            mstr = ""
            nreflsinbin = len(radii2[ibin])
            if (ibin + 1) < self.nbin and nreflsinbin > 0:
                bin1 = self.workingbinvals[ibin]
                bin2 = self.workingbinvals[ibin + 1]
                if colstr == "dres":
                    bin1 = 1.0 / self.workingbinvals[ibin]
                    bin2 = 1.0 / self.workingbinvals[ibin + 1]
                mstr= "bin:%d, %d reflections with %s in ]%2.2f; %2.2f]" %(cntbin, nreflsinbin, \
                        colstr, bin1, bin2)
                self.binstrs.append(mstr)
                self.mprint(mstr, verbose=True)
                spherebufferstr += """
  // %s
  ttips[%d] = %s
  positions[%d] = new Float32Array( %s )
  colours[%d] = new Float32Array( %s )
  radii[%d] = new Float32Array( %s )
  spherebufs[%d] = new NGL.SphereBuffer({
    position: positions[%d],
    color: colours[%d],
    radius: radii[%d],
    picking: ttips[%d],
  })
  //}, { disableImpostor: true }) // if true allows wireframe spheres but does not allow resizing spheres

  shape.addBuffer(spherebufs[%d])
      """ %(mstr, cntbin, str(spbufttips[ibin]).replace('\"', '\''), \
                 cntbin, str(positions[ibin]), cntbin, str(colours[ibin]), \
                 cntbin, str(radii2[ibin]), cntbin, cntbin, cntbin, cntbin, cntbin, cntbin )

                if self.workingbinvals[ibin] < 0.0:
                    negativeradiistr += "spherebufs[%d].setParameters({metalness: 1})\n" % cntbin
                cntbin += 1

        spherebufferstr += """
// create tooltip element and add to the viewer canvas
  tooltip = document.createElement("div");
  Object.assign(tooltip.style, {
    display: "none",
    position: "absolute",
    zIndex: 10,
    pointerEvents: "none",
    backgroundColor: "rgba(255, 255, 255, 0.75)",
    color: "black",
    padding: "0.1em",
    fontFamily: "sans-serif"
  });

  stage.viewer.container.appendChild(tooltip);
  // listen to `hovered` signal to move tooltip around and change its text
  stage.signals.hovered.add(function (pickingProxy) {
    if (pickingProxy && (Object.prototype.toString.call(pickingProxy.picker) === '[object Array]'  )){
      var sphere = pickingProxy.sphere;
      var cp = pickingProxy.canvasPosition;
      tooltip.innerText = pickingProxy.picker[pickingProxy.pid];
      tooltip.style.bottom = cp.y + 7 + "px";
      tooltip.style.left = cp.x + 8 + "px";
      tooltip.style.fontSize = "smaller";
      tooltip.style.display = "block";
    }else{
      tooltip.style.display = "none";
    }
  });

  stage.signals.clicked.add(function (pickingProxy) {
  if (pickingProxy && (Object.prototype.toString.call(pickingProxy.picker) === '[object Array]'  )){
    var innerText = pickingProxy.picker[pickingProxy.pid];
    mysocket.send( innerText);
  }
  });

    """
        colourgradstrs = "colourgradvalarray = new Array(%s)\n" % fomln
        # if displaying phases from map coefficients together with fom values then
        for g, colourgradarray in enumerate(colourgradarrays):
            self.colourgradientvalues = []
            for j, e in enumerate(colourgradarray):
                self.colourgradientvalues.append([colourscalararray[j], e])
            self.colourgradientvalues = roundoff(self.colourgradientvalues)

            fom = fomarrays[g]
            colourgradstr = []
            for j, val in enumerate(self.colourgradientvalues):
                vstr = ""
                alpha = 1.0
                gradval = "rgba(%s, %s, %s, %s)" % (val[1][0], val[1][1],
                                                    val[1][2], alpha)
                if j % 10 == 0 or j == len(self.colourgradientvalues) - 1:
                    vstr = str(roundoff(val[0], 2))
                colourgradstr.append([vstr, gradval])

            colourgradstrs += "  colourgradvalarray[%s] = %s\n" % (
                g, str(colourgradstr))

        #negativeradiistr = ""
        #for ibin in range(self.nbin):
        #  if self.workingbinvals[ibin] < 0.0:
        #    negativeradiistr += "spherebufs[%d].setParameters({metalness: 1})\n" %ibin



        self.NGLscriptstr = """
// Microsoft Edge users follow instructions on
// https://stackoverflow.com/questions/31772564/websocket-to-localhost-not-working-on-microsoft-edge
// to enable websocket connection

var pagename = location.pathname.substring(1);
var mysocket = new WebSocket('ws://127.0.0.1:7894/');

mysocket.onopen = function (e) {
  mysocket.send('%s now connected via websocket to ' + pagename + '\\n');
};

mysocket.onclose = function (e) {
  mysocket.send('%s now disconnecting from websocket ' + pagename + '\\n');
};

// Log errors to debugger of your browser
mysocket.onerror = function (error) {
  console.log('WebSocket Error ' + error);
};


window.addEventListener( 'resize', function( event ){
    stage.handleResize();
}, false );




var stage;
var shape;
var shapeComp;
var repr;
var AA = String.fromCharCode(197); // short for angstrom
var DGR = String.fromCharCode(176); // short for degree symbol

function createElement (name, properties, style) {
// utility function used in for loop over colourgradvalarray
  var el = document.createElement(name)
  Object.assign(el, properties)
  Object.assign(el.style, style)
  Object.assign(el.style, {
      display: "block",
      position: "absolute",
      color: "black",
      fontFamily: "sans-serif",
      fontSize: "smaller",
    }
  )
  return el
}


function addElement (el) {
// utility function used in for loop over colourgradvalarray
  Object.assign(el.style, {
    position: "absolute",
    zIndex: 10
  })
  stage.viewer.container.appendChild(el)
}


function addDivBox(txt, t, l, w, h, bgcolour='rgba(255.0, 255.0, 255.0, 0.0)') {
  divbox = createElement("div",
  {
    innerText: txt
  },
  {
    backgroundColor: bgcolour,
    color:  'rgba(0.0, 0.0, 0.0, 1.0)',
    top: t.toString() + "px",
    left: l.toString() + "px",
    width: w.toString() + "px",
    height: h.toString() + "px",
  }
  );
  addElement(divbox)
}


var hklscene = function () {
  shape = new NGL.Shape('shape');
  stage = new NGL.Stage('viewport', { backgroundColor: "grey", tooltip:false,
                                      fogNear: 100, fogFar: 100 });
  stage.setParameters( { cameraType: "%s" } );

  %s

  %s

  shapeComp = stage.addComponentFromObject(shape);
  repr = shapeComp.addRepresentation('buffer');
  shapeComp.autoView();
  repr.update()

  // if some radii are negative draw them with wireframe
  %s

  //colourgradvalarrays
  %s

  var ih = 3;
  var topr = 35
  var topr2 = 10
  var lp = 10
  var wp = 40
  var lp2 = lp + wp
  var gl = 3
  var wp2 = gl
  var fomlabelheight = 25
  if (colourgradvalarray.length === 1) {
    wp2 = 15
    fomlabelheight = 0
  }

  var wp3 = wp + colourgradvalarray.length * wp2 + 2

  totalheight = ih*colourgradvalarray[0].length + 35 + fomlabelheight
  // make a white box on top of which boxes with transparent background are placed
  // containing the colour values at regular intervals as well as label legend of
  // the displayed miller array
  addDivBox("", topr2, lp, wp3, totalheight, 'rgba(255.0, 255.0, 255.0, 1.0)');

  // print label of the miller array used for colouring
  addDivBox("%s", topr2, lp, wp, 20);

  if (colourgradvalarray.length > 1) {
    // print FOM label, 1, 0.5 and 0.0 values below colour chart
    fomtop = topr2 + totalheight - 18
    fomlp = lp + wp
    fomwp = wp3
    fomtop2 = fomtop - 13
    // print the 1 number
    addDivBox("1", fomtop2, fomlp, fomwp, 20)
    // print the 0.5 number
    leftp = fomlp + 0.48 * gl * colourgradvalarray.length
    addDivBox("0.5", fomtop2, leftp, fomwp, 20)
    // print the FOM label
    addDivBox("%s", fomtop, leftp, fomwp, 20);
    // print the 0 number
    leftp = fomlp + 0.96 * gl * colourgradvalarray.length
    addDivBox("0", fomtop2, leftp, fomwp, 20)
  }

  for (j = 0; j < colourgradvalarray[0].length; j++) {
    rgbcol = colourgradvalarray[0][j][1];
    val = colourgradvalarray[0][j][0]
    topv = j*ih + topr
    toptxt = topv - 5
    // print value of miller array if present in colourgradvalarray[0][j][0]
    addDivBox(val, toptxt, lp, wp, ih);
  }

  // draw the colour gradient
  for (g = 0; g < colourgradvalarray.length; g++) {
    leftp = g*gl + lp + wp

    // if FOM values are supplied draw colour gradients with decreasing
    // saturation values as stored in the colourgradvalarray[g] arrays
    for (j = 0; j < colourgradvalarray[g].length; j++) {
      rgbcol = colourgradvalarray[g][j][1];
      val = colourgradvalarray[g][j][0]
      topv = j*ih + topr
      addDivBox("", topv, leftp, wp2, ih, rgbcol);
    }
  }

}

document.addEventListener('DOMContentLoaded', function() { hklscene() }, false );


mysocket.onmessage = function (e) {
  //alert('Server: ' + e.data);
  var c
  var alpha
  var si
  mysocket.send('got ' + e.data ); // tell server what it sent us
  try {
    val = e.data.split(",")

    if (val[0] === "alpha") {
      ibin = parseInt(val[1])
      alpha = parseFloat(val[2])
      spherebufs[ibin].setParameters({opacity: alpha})
      stage.viewer.requestRender()
    }

    if (val[0] === "colour") {
      ibin = parseInt(val[1])
      si =  parseInt(val[2])
      colours[ibin][3*si] = parseFloat(val[3])
      colours[ibin][3*si+1] = parseFloat(val[4])
      colours[ibin][3*si+2] = parseFloat(val[5])
      spherebufs[ibin].setAttributes({ color: colours[ibin] })
      stage.viewer.requestRender()
    }

    if (val[0] === "Redraw") {
      stage.viewer.requestRender()
    }

    if (val[0] === "ReOrient") {
      mysocket.send( 'Reorienting ' + pagename );
      sm = new Float32Array(16);
      for (j=0; j<16; j++)
        sm[j] = parseFloat(val[j + 2]) // first 2 are "ReOrient", "NGL\\n"

      var m = new NGL.Matrix4();
      m.fromArray(sm);
      stage.viewerControls.orient(m);
      stage.viewer.requestRender();
    }

    if (val[0] === "Reload") {
    // refresh browser with the javascript file
      cvorient = stage.viewerControls.getOrientation().elements
      msg = String(cvorient)
      mysocket.send('Current vieworientation:\\n, ' + msg );

      mysocket.send( 'Refreshing ' + pagename );
      window.location.reload(true);
    }

    if (val[0] === "Testing") {
      // test something new
      mysocket.send( 'Testing something new ' + pagename );
      var newradii = radii[0].map(function(element) {
        return element*1.5;
      });

      spherebufs[0].setAttributes({
          radius: newradii
        })
      stage.viewer.requestRender()
    }

  }
  catch(err) {
    mysocket.send('error: ' + err );
  }
};




    """ % (self.__module__, self.__module__, self.cameratype, arrowstr, spherebufferstr, \
                negativeradiistr, colourgradstrs, colourlabel, fomlabel)
        if self.jscriptfname:
            with open(self.jscriptfname, "w") as f:
                f.write(self.NGLscriptstr)
        self.ReloadNGL()