Esempio n. 1
0
 def render(self, screen_size):
     light = vap.LightSource([3, 3, 3], 'color', [3, 3, 3], 'parallel',
                             'point_at', [0, 0, 0])
     camera = vap.Camera('location', [0.5 * 1, -2 * 1, 3 * 1], 'look_at',
                         [0, 0, 0], 'rotate', [20, 0, 0])
     ground = vap.Plane([0, 0, 1], 0, vap.Texture('T_Stone33'))
     walls = [wall.rendered for wall in self.wall]
     robots = [bot.rendered for bot in self.robot]
     obj = self.obj.rendered
     obj_pos_str = '\"{:2.2f}, {:2.2f}, {:2.2f}\"'.format(
         *self.obj.body.getPosition())
     for ir, robot in enumerate(self.robot):
         logger.info('{} - {:2.2f}, {:2.2f}, {:2.2f} - {st}'.format(
             ir, *robot.body.getPosition(), st=self.sim_time))
     logger.info('{} - {}'.format(obj_pos_str, self.sim_time))
     # obj_pos = vap.Text('ttf', '\"timrom.ttf\"', obj_pos_str, 0.1, '0.1 * x', 'rotate',
     #                    '<100,0,10>', 'translate', '-3*x', 'finish',
     #                    '{ reflection .25 specular 1  diffuse 0.1}', 'scale', [0.25, 0.25, 0.25])
     scene = vap.Scene(
         camera,
         [light, ground,
          vap.Background('color', [0.2, 0.2, 0.3]), obj] + robots + walls,
         included=["colors.inc", "textures.inc", "glass.inc", "stones.inc"])
     return scene.render(height=screen_size,
                         width=screen_size,
                         antialiasing=0.01,
                         remove_temp=False)
Esempio n. 2
0
def povray_test():
  """ Just a purple sphere """
  scene = vapory.Scene(  vapory.Camera('location',  [0.0, 0.5, -4.0],
                         'direction', [0,0,1.5],
                         'look_at',  [0, 0, 0]),
  
                  objects = [
  
                      vapory.Background("color", [0.85, 0.75, 0.75]),
  
                      vapory.LightSource([0, 0, 0],
                                    'color',[1, 1, 1],
                                    'translate', [-5, 5, -5]),
  
                      vapory.LightSource ([0, 0, 0],
                                      'color', [0.25, 0.25, 0.25],
                                      'translate', [600, -600, -600]),
  
  
                      vapory.Box([-0.5, -0.5, -0.5], [0.5, 0.5, 0.5],
                           vapory.Texture( vapory.Pigment( 'color', [1,0,0]),
                                    vapory.Finish('specular', 0.6),
                                    vapory.Normal('agate', 0.25, 'scale', 0.5)),
                          'rotate', [45,46,47])
                 ]
  )
  # We use antialiasing. Remove this option for faster rendering.
  scene.render("cube.png", width=300, height=300, antialiasing=0.001)
Esempio n. 3
0
def make_cali_img_pairs(w,
                        h,
                        loc,
                        look_at,
                        direct1,
                        direct2,
                        rotate=[0, 0, 0],
                        trans=[0, 0, 0],
                        ctype=None,
                        rand_amount=1):
    cali_dir = os.path.join(CFD, "caliimg")
    light = vp.LightSource(
        # [2,4,-3],
        [0, 0, -10000],
        'color',
        "White",
        'rotate',
        [30, 0, 0],
        'rotate',
        [0, 88, 0])  # White light

    background = vp.Background("color", "White")  # White background

    center = np.array(look_at) + (np.random.rand(3) - 0.5) * rand_amount
    sphere = vp.Sphere(
        center,
        0.1,  # center, radius
        vp.Texture(vp.Pigment('color', "Black")))  # Black point

    l_camera = vp.Camera('location', loc, 'direction', direct1, 'up',
                         [0, 1, 0], 'right', [1 * w / h, 0, 0], 'look_at',
                         look_at)
    l_scene = vp.Scene(l_camera,
                       objects=[background, light, sphere],
                       included=["colors.inc"])
    l_img_path = os.path.join(cali_dir, "left.png")
    l_scene.render(l_img_path, width=w, height=h, auto_camera_angle=False)

    r_camera = vp.Camera('location', loc, 'direction', direct2, 'up',
                         [0, 1, 0], 'right', [1 * w / h, 0, 0], 'look_at',
                         look_at, 'rotate', rotate, 'translate', trans)
    r_scene = vp.Scene(r_camera,
                       objects=[background, light, sphere],
                       included=["colors.inc"])
    r_img_path = os.path.join(cali_dir, "right.png")
    r_scene.render(r_img_path, width=w, height=h, auto_camera_angle=False)

    with open(os.path.join(cali_dir, "left.pov"), "wb") as f:
        f.write(l_scene.__str__())

    with open(os.path.join(cali_dir, "right.pov"), "wb") as f:
        f.write(r_scene.__str__())

    return cv2.imread(l_img_path), cv2.imread(r_img_path)
Esempio n. 4
0
def gen_img_settings_quality(l):
    """ generate the general povray settings"""

    lhalf = 0.5 * l

    ### sphere radius

    sphere_radius = 0.7

    ### RESOLUTION

    img_widthpx = 1024
    img_heightpx = 1024

    ### includes and defaults

    povray_includes = ["colors.inc", "textures.inc", "shapes.inc"]
    povray_defaults = [vapory.Finish( 'ambient', 0.1, \
           'diffuse', 0.65, \
          'specular', 0.5, \
          'shininess', 0.53, \
      'opacity', 1.0)]

    ### light sources

    sun1 = vapory.LightSource([lhalf, lhalf, -1.01 * lhalf], 'color', 'White')
    sun2 = vapory.LightSource([lhalf, lhalf, -1.01 * lhalf], 'color',
                              [0.7, 0.7, 0.7])

    ### background

    background = vapory.Background('color', [1, 1, 1])

    ### camera

    povray_cam = vapory.Camera('location', [lhalf, lhalf, -1.01*lhalf], \
                               'look_at', [lhalf,lhalf,0], 'angle', 90)

    ### text
    # If desired include this in the povray_objects - array declared in the loop
    #text1 = \
    #vapory.Text( 'ttf', '"timrom.ttf"' ,'"Division:"', 0.01, 0.0, \
    #'scale', [0.5,0.5,0.5],'rotate', \
    #[0,90,0], 'translate' , [0.0 , 15.0+2.75-1 , 15.0+1.5], \
    #vapory.Pigment('Black') )

    ### render quality

    quality = 10

    return sphere_radius, img_widthpx, img_heightpx, povray_includes, \
        povray_defaults, sun1, sun2, background, povray_cam, quality
Esempio n. 5
0
def render_povray(scene, filename='ipython', width=600, height=600,
                  antialiasing=0.01):
    '''Render the scene with povray for publication.

    :param dict scene: The scene to render
    :param string filename: Output filename or 'ipython' to render in the notebook.
    :param int width: Width in pixels.
    :param int height: Height in pixels.
    '''
    if not vapory_available:
        raise Exception("To render with povray, you need to have the vapory"
                        " package installed.")

    # Camera target
    aspect = scene['camera']['aspect']
    up = np.dot(rmatrixquaternion(scene['camera']['quaternion']), [0, 1, 0])
    v_fov = scene['camera']['vfov']  / 180.0 * np.pi
    h_fov = 2.0 * np.arctan(np.tan(v_fov/2.0) * aspect) / np.pi * 180
    # Setup camera position
    camera = vp.Camera( 'location', scene['camera']['location'],
                        'direction', [0, 0, -1],
                        'sky', up,
                        'look_at', scene['camera']['target'],
                        'angle', h_fov )

    # Lights
    light_sources = [vp.LightSource( np.array([2,4,-3]) * 1000, 'color', [1,1,1] ),
                     vp.LightSource( np.array([-2,-4,3]) * 1000, 'color', [1,1,1] ),
                     vp.LightSource( np.array([-1,2,3]) * 1000, 'color', [1,1,1] ),
                     vp.LightSource( np.array([1,-2,-3]) * 1000, 'color', [1,1,1] )]

    # Background -- white for now
    background = vp.Background([1, 1, 1])

    # Things to display
    stuff = _generate_objects(scene['representations'])

    scene = vp.Scene( camera, objects = light_sources + stuff + [background])

    return scene.render(filename, width=width, height=height,
                        antialiasing = antialiasing)
Esempio n. 6
0
def renderPopSpheres():
    nb_spheres = 50
    R = 5.
    centers = np.random.randint(100, size=(nb_spheres, 3))
    radius = np.random.randn(nb_spheres) * R + R
    couleurs = np.random.randint(255, size=(nb_spheres, 4)) / 255.

    camera = vapory.Camera('location', [150, 150, 150], 'look_at', [0, 0, 0])
    bg = vapory.Background('color', [1, 1, 1])
    light = vapory.LightSource([100, 100, 100], 'color', [1, 1, 1])
    light3 = vapory.LightSource([0, 0, 0], 'color', [1, 1, 1])
    light2 = vapory.LightSource([50, 50, 50], 'color', [1, 1, 1])

    obj = [light, light2, light3, bg]
    for i in range(nb_spheres):
        sphere = vapory.Sphere(
            centers[i, ], radius[i],
            vapory.Texture(
                vapory.Finish('ambient', 0, 'reflection', 0, 'specular', 0,
                              'diffuse', 1),
                vapory.Pigment('color', couleurs[i, ])))
        obj.append(sphere)
    scene = vapory.Scene(camera, objects=obj)
    scene.render("spheres.png", width=3000, height=3000)
Esempio n. 7
0
	def getScene(self, *, cameraLocation=[100,100,50], cameraTarget=[0,0,0], lightLocation=[100,100,100],
		                    lightColor=[1,1,1], backgroundColor=[0,0,0], objectColor=[0.5,0.5,0.5],
		                    rotationAxis=None, rotationAngle=None):
		# POVRay uses a left-handed coordinate system, so we have to flip the Z axis on all geometric vectors
		cameraLocation[2] = -cameraLocation[2]
		cameraTarget[2] = -cameraTarget[2]
		lightLocation[2] = -lightLocation[2]

		vertices = self.getUniqueVertices()
		if rotationAxis and rotationAngle:
			rotationMatrix = rotation_matrix(rotationAxis, rotationAngle)
			# Z axis must be flipped in vertex coords as well, before the transform
			vertexArgs = [ len(vertices) ] + [ list(np.dot(rotationMatrix, np.array([x,y,-z]))) for x,y,z in vertices ]
		else:
			vertexArgs = [ len(vertices) ] + [ [x,y,-z] for x,y,z in vertices ] # even if there is no rotation we must flip Z axis

		triangleIndices = self.getTriangleIndicesForUniqueVertices()
		faceArgs = [ len(triangleIndices) ] + list(map(list, triangleIndices))

		normales = np.zeros((len(vertices), 3))
		npvertices = np.array(vertices)
		for v0, v1, v2 in triangleIndices:
			triangleNormale = np.cross(npvertices[v1,:]-npvertices[v0,:], npvertices[v2,:]-npvertices[v0,:])
			triangleNormale /= np.linalg.norm(triangleNormale)
			triangleArea = np.dot(npvertices[v1,:]-npvertices[v0,:], npvertices[v2,:]-npvertices[v0,:])/2
			normales[v0,:] += triangleNormale*triangleArea
			normales[v1,:] += triangleNormale*triangleArea
			normales[v2,:] += triangleNormale*triangleArea
		normales /= np.linalg.norm(normales, axis=1, keepdims=True)

#		for i in range(len(vertices)):
#			print(f'Vertex {vertices[i]} has normale {normales[i]} (product {np.dot(vertices[i], normales[i])})')
#			print(f'{np.dot(vertices[i], normales[i])}')

		if rotationAxis and rotationAngle:
			rotationMatrix = rotation_matrix(rotationAxis, rotationAngle)
			# Z axis must be flipped in vertex coords as well, before the transform
			normaleArgs = [ len(vertices) ] + [ list(np.dot(rotationMatrix, np.array([x,y,-z]))) for x,y,z in [ normales[i,:] for i in range(len(vertices)) ] ]
		else:
			# even if there is no rotation we must flip Z axis
			normaleArgs = [ len(vertices) ] + [ [x,y,-z] for x,y,z in [ normales[i,:] for i in range(len(vertices)) ] ]

#		print('Rendering with camera at {} and light at {}'.format(str(cameraLocation), str(lightLocation)))

		asteroid = vpr.Mesh2(vpr.VertexVectors(*vertexArgs),
		                     vpr.NormalVectors(*normaleArgs),
		                     vpr.FaceIndices(*faceArgs),
		                     vpr.Texture(vpr.Pigment('color', 'rgb', [0.5, 0.5, 0.5]),
		                                 vpr.Normal('bumps', 0.75, 'scale', 0.0125),
		                                 vpr.Finish('phong', 0.1)
		                     )
		           )

#		                     vpr.Texture(vpr.Pigment('color', objectColor)))

		return vpr.Scene( vpr.Camera('location', cameraLocation, 'look_at', cameraTarget, 'sky', [0,0,-1]),
		                  objects = [ vpr.LightSource(lightLocation, 'color', lightColor),
		                              vpr.Background('color', backgroundColor),
		                              asteroid
		                  ],
		                  included = ["colors.inc", "textures.inc"]
#		                  ,defaults = [vpr.Finish( 'ambient', 0.0, 'diffuse', 0.0)]
		                  ,global_settings = [ 'ambient_light <0,0,0>' ]
		                )
Esempio n. 8
0
    def scene(pack,
              cmap=None,
              rot=0,
              camera_height=0.7,
              camera_dist=1.5,
              angle=None,
              lightstrength=1.1,
              orthographic=False,
              pad=None,
              floater_color=(.6, .6, .6),
              bgcolor=(1, 1, 1),
              box_color=(.5, .5, .5),
              group_indexes=None,
              clip=False):
        """
        Render a 3D scene.

        Requires `vapory` package, which requires the `povray` binary.

        Parameters
        ----------
        cmap : a colormap
        box_color : Color to draw the box. 'None' => don't draw box.
        floater_color : Color for floaters. 'None' => same color as non-floaters (use cmap).
        group_indexes : a list of indexes for each "group" that should remain
                together on the same side of the box.
        clip : clip the spheres at the edge of the box.

        Returns
        -------
        scene : vapory.Scene, which can be rendered using its `.render()` method.
        """
        import vapory
        import numpy as np

        try:
            import matplotlib as mpl
            import matplotlib.cm as mcm
            vmin, vmax = min(pack.diameters), max(pack.diameters)
            sm = mcm.ScalarMappable(norm=mpl.colors.Normalize(vmin, vmax),
                                    cmap=cmap)
            cols = [sm.to_rgba(s) for s in pack.diameters]
        except ImportError:
            if not isinstance(cmap, list):
                raise ValueError(
                    "matplotlib could not be imported, and cmap not recognizeable as a list"
                )
            cols = list(cmap)
        except TypeError:
            if not isinstance(cmap, list):
                raise ValueError(
                    "matplotlib could not convert cmap to a colormap," +
                    " and cmap not recognizeable as a list")
            cols = list(cmap)

        if floater_color is not None:
            ix, _ = pack.backbone()
            ns, = np.nonzero(~ix)
            for n in ns:
                cols[n] = floater_color

        mod_add = .5 if not clip else 0.
        rs = np.remainder(pack.rs + mod_add, 1) - mod_add
        if group_indexes is not None:
            for ix in group_indexes:
                xs = pack.rs[ix, :]
                com = np.mean(xs, axis=0)
                comdiff = (np.remainder(com + mod_add, 1) - mod_add) - com
                rs[ix, :] = xs + comdiff

        if clip:
            spheres = []
            cube = vapory.Box((-.5, -.5, -.5), (.5, .5, .5))
            dxs = [-1., 0.]
            drs = np.array([(dx, dy, dz) for dx in dxs for dy in dxs
                            for dz in dxs])
            maxr = 0

            for xyz, s, col in zip(rs, pack.diameters, cols):
                for dr in drs:
                    r = dr + xyz
                    if np.any(abs(r) - s / 2. > .5):
                        # not in the box
                        continue
                    sphere = vapory.Sphere(r, s / 2.)
                    cutsphere = vapory.Intersection(
                        cube, sphere,
                        vapory.Texture(vapory.Pigment('color', col[:3])))
                    spheres.append(cutsphere)
                    if np.amax(r) > maxr:
                        maxr = np.amax(r)
        else:
            spheres = [
                vapory.Sphere(xyz, s / 2.,
                              vapory.Texture(vapory.Pigment('color', col[:3])))
                for xyz, s, col in zip(rs, pack.diameters, cols)
            ]
            maxr = np.amax(np.amax(np.abs(rs), axis=1) + pack.diameters / 2.)

        extent = (-.5, .5)
        corners = [
            np.array((x, y, z)) for x in extent for y in extent for z in extent
        ]
        pairs = [(c1, c2) for c1 in corners for c2 in corners
                 if np.allclose(np.sum((c1 - c2)**2), 1) and sum(c1 - c2) > 0]

        radius = 0.01
        cyls, caps = [], []
        if box_color is not None:
            col = vapory.Texture(vapory.Pigment('color', box_color))
            cyls = [vapory.Cylinder(c1, c2, 0.01, col) for c1, c2 in pairs]
            caps = [vapory.Sphere(c, radius, col) for c in corners]

        light_locs = [[8., 5., -3.], [-6., 6., -5.], [-6., -7., -4.],
                      [10., -5., 7.]]

        rotlocs = [[
            x * np.cos(rot) - z * np.sin(rot), y,
            z * np.cos(rot) + x * np.sin(rot)
        ] for x, y, z in light_locs]
        lights = [
            # vapory.LightSource( [2,3,5], 'color', [1,1,1] ),
            vapory.LightSource(loc, 'color', [lightstrength] * 3)
            for loc in rotlocs
        ]
        cloc = [
            np.cos(rot) * camera_dist, camera_dist * camera_height,
            np.sin(rot) * camera_dist
        ]
        # mag = sqrt(sum([d**2 for d in cloc]))
        # direction = [-v*2/mag for v in cloc]

        if angle is None:
            if pad is None:
                pad = max(pack.diameters)
            w = sqrt(2) * maxr + pad
            angle = float(np.arctan2(w, 2 * camera_dist)) * 2 * 180 / np.pi
        camera = vapory.Camera('location', cloc, 'look_at', [0, 0, 0], 'angle',
                               angle)
        # vapory.Camera('orthographic', 'location', cloc, 'direction',
        #               direction, 'up', [0,2,0], 'right', [2,0,0])

        return vapory.Scene(camera,
                            objects=(lights + spheres + cyls + caps +
                                     [vapory.Background("color", bgcolor)]))
Esempio n. 9
0
    def plot(self, state, iteration, temperature, field, save=False):
        """It is a function that creates the complete scene of the evolve of the
        states. It join the two array of the images. Also, it allows to put a text on
        the top of the scene.

        :param state:  It gets the states information from the simulation hdf file.
        :type state: list
        :param iteration:  It gets the number of iterations from the hdf file.
        :type iteration: int
        :param temperature: It gets the temperature information from the hdf file.
        :type temperature: float/list/dict
        :param field: It gets the field information from the hdf file.
        :type field: float/list/dict

        :return: It returns an array of the complete image.
        :rtype: array
        """
        camera = vapory.Camera(
            "location",
            self.location,
            "look_at",
            self.centroid,
            "sky",
            [0, 0, 1],
            "up",
            [0, 0, 1],
            "right",
            [0, 1, 0],
        )
        background = vapory.Background([1, 1, 1])
        light = vapory.LightSource(self.location, "color", [1, 1, 1])

        arrows = []
        for position, direction in zip(self.positions, state):
            color = self.get_rgb(direction, self.mode)
            arrows.append(PovrayArrow(position, direction, color))

        scene = vapory.Scene(camera, objects=[background, light, *arrows])
        scene_image = scene.render(width=self.size,
                                   height=self.size,
                                   antialiasing=0.1)

        image = PlotStates.join_images(self.colorbar_image, scene_image)

        if self.index == 1:
            title = "Initial state"
        else:
            title = f"T = {temperature:.2f}; H = {field:.2f}; iteration = {iteration}"

        draw = ImageDraw.Draw(image)
        draw.text((0.2 * image.width, 0), title, (0, 0, 0), font=self.font)

        if save:
            try:
                os.mkdir(self.output)
            except FileExistsError:
                pass

            image.save(f"{self.output}/figure_{self.index}.png")

        self.index += 1

        return numpy.array(image)
Esempio n. 10
0
def render_povray(scene,
                  filename='ipython',
                  width=600,
                  height=600,
                  antialiasing=0.01,
                  extra_opts={}):
    '''Render the scene with povray for publication.

    :param dict scene: The scene to render
    :param string filename: Output filename or 'ipython' to render in the notebook.
    :param int width: Width in pixels.
    :param int height: Height in pixels.
    :param dict extra_opts: Dictionary to merge/override with the passed scene.
    '''
    if not vapory_available:
        raise Exception("To render with povray, you need to have the vapory"
                        " package installed.")

    # Adding extra options
    scene = normalize_scene(scene)
    scene.update(extra_opts)

    # Camera target
    aspect = scene['camera']['aspect']
    up = np.dot(rmatrixquaternion(scene['camera']['quaternion']), [0, 1, 0])
    v_fov = scene['camera']['vfov'] / 180.0 * np.pi
    h_fov = 2.0 * np.arctan(np.tan(v_fov / 2.0) * aspect) / np.pi * 180
    # Setup camera position
    camera = vp.Camera('location', scene['camera']['location'], 'direction',
                       [0, 0, -1], 'sky', up, 'look_at',
                       scene['camera']['target'], 'angle', h_fov)

    global_settings = []
    # Setup global illumination
    if scene.get('radiosity', False):
        # Global Illumination
        radiosity = vp.Radiosity(
            'brightness',
            2.0,
            'count',
            100,
            'error_bound',
            0.15,
            'gray_threshold',
            0.0,
            'low_error_factor',
            0.2,
            'minimum_reuse',
            0.015,
            'nearest_count',
            10,
            'recursion_limit',
            1,  #Docs say 1 is enough
            'adc_bailout',
            0.01,
            'max_sample',
            0.5,
            'media off',
            'normal off',
            'always_sample',
            1,
            'pretrace_start',
            0.08,
            'pretrace_end',
            0.01)

        light_sources = []
        global_settings.append(radiosity)
    else:
        # Lights
        light_sources = [
            vp.LightSource(np.array([2, 4, -3]) * 1000, 'color', [1, 1, 1]),
            vp.LightSource(np.array([-2, -4, 3]) * 1000, 'color', [1, 1, 1]),
            vp.LightSource(np.array([-1, 2, 3]) * 1000, 'color', [1, 1, 1]),
            vp.LightSource(np.array([1, -2, -3]) * 1000, 'color', [1, 1, 1])
        ]

    # Background -- white for now
    background = vp.Background([1, 1, 1])

    # Things to display
    stuff = _generate_objects(scene['representations'])

    scene = vp.Scene(camera,
                     objects=light_sources + stuff + [background],
                     global_settings=global_settings)

    return scene.render(filename,
                        width=width,
                        height=height,
                        antialiasing=antialiasing)
Esempio n. 11
0
N = 5 if debug else 5000  # number of examples
image_shape = (512, 512)  # first two dimensions of output images
num_classes = 2  # number of semantic classes
output_format = 'tfrecord'
fname = root + "two_spheres"  # tfrecord to write to
min_radius = 64  # minimum radius of either sphere
max_radius = 128  # maximum radius of either sphere

# experiment
color = lambda col: vapory.Texture(vapory.Pigment('color', col))
exp = experiment.BallExperiment(image_shape=image_shape,
                                num_classes=num_classes,
                                N=N,
                                fname=fname,
                                output_format=output_format)
exp.add_object(vapory.Background('White'))
exp.add_object(
    vapory.LightSource([0, image_shape[0], -2 * image_shape[1]], 'color',
                       [1, 1, 1]))

argsf = lambda: ([
    np.random.randint(-image_shape[1] / 2, image_shape[1] / 2),
    np.random.randint(-image_shape[0] / 2, image_shape[0] / 2),
    np.random.randint(-max_radius, max_radius)
], np.random.randint(min_radius, max_radius + 1))

# grayscale images, but objects will result in the same class anyway
red_ball = experiment.ExperimentSphere(argsf, color('Red'))
blue_ball = experiment.ExperimentSphere(argsf, color('Blue'))

exp.add_object(red_ball)
Esempio n. 12
0
    def scene(pack,
              cmap=None,
              rot=0,
              camera_height=0.7,
              camera_dist=1.5,
              angle=None,
              lightstrength=1.1,
              orthographic=False,
              pad=None,
              floatercolor=(.6, .6, .6)):
        """
        Render a scene.
        
        Requires `vapory` package, which requires the `povray` binary.
        
        Parameters
        ----------
        cmap : a colormap 
        
        Returns
        -------
        scene : vapory.Scene, which can be rendered using its `.render()` method.
        """
        import vapory
        import numpy as np

        try:
            import matplotlib as mpl
            import matplotlib.cm as mcm
            vmin, vmax = min(pack.sigmas), max(pack.sigmas)
            sm = mcm.ScalarMappable(norm=mpl.colors.Normalize(vmin, vmax),
                                    cmap=cmap)
            cols = [sm.to_rgba(s) for s in pack.sigmas]
        except ImportError:
            if not isinstance(cmap, list):
                raise ValueError(
                    "matplotlib could not be imported, and cmap not recognizeable as a list"
                )
            cols = list(cmap)
        except TypeError:
            if not isinstance(cmap, list):
                raise ValueError(
                    "matplotlib could not convert cmap to a colormap, and cmap not recognizeable as a list"
                )
            cols = list(cmap)

        if floatercolor is not None:
            ix, _ = pack.backbone()
            ns, = np.nonzero(~ix)
            for n in ns:
                cols[n] = floatercolor
        rs = np.remainder(pack.rs + .5, 1) - .5
        spheres = [
            vapory.Sphere(xyz, s / 2.,
                          vapory.Texture(vapory.Pigment('color', col[:3])))
            for xyz, s, col in zip(rs, pack.sigmas, cols)
        ]

        extent = (-.5, .5)
        corners = [
            np.array((x, y, z)) for x in extent for y in extent for z in extent
        ]
        pairs = [(c1, c2) for c1 in corners for c2 in corners
                 if np.allclose(np.sum((c1 - c2)**2), 1) and sum(c1 - c2) > 0]

        radius = 0.01
        col = vapory.Texture(vapory.Pigment('color', [.5, .5, .5]))
        cyls = [vapory.Cylinder(c1, c2, 0.01, col) for c1, c2 in pairs]
        caps = [vapory.Sphere(c, radius, col) for c in corners]

        light_locs = [[8., 5., -3.], [-6., 6., -5.], [-6., -7., -4.],
                      [10., -5., 7.]]
        rotlocs = [[
            x * np.cos(rot) - z * np.sin(rot), y,
            z * np.cos(rot) + x * np.sin(rot)
        ] for x, y, z in light_locs]
        lights = [
            #vapory.LightSource( [2,3,5], 'color', [1,1,1] ),
            vapory.LightSource(loc, 'color', [lightstrength] * 3)
            for loc in rotlocs
        ]
        cloc = [
            np.cos(rot) * camera_dist, camera_dist * camera_height,
            np.sin(rot) * camera_dist
        ]
        mag = sqrt(sum([d**2 for d in cloc]))
        direction = [-v * 2 / mag for v in cloc]

        if angle is None:
            if pad is None: pad = max(pack.sigmas)
            w = sqrt(2) + pad
            angle = float(np.arctan2(w, 2 * camera_dist)) * 2 * 180 / np.pi
        camera = vapory.Camera('location', cloc, 'look_at', [0, 0, 0], 'angle',
                               angle)
        # vapory.Camera('orthographic', 'location', cloc, 'direction', direction, 'up', [0,2,0], 'right', [2,0,0])

        return vapory.Scene(camera,
                            objects=lights + spheres + cyls + caps +
                            [vapory.Background("color", [1, 1, 1])])