glotzerlab / plato

Efficient visualization of particle data supporting several rendering engines.
https://plato-draw.readthedocs.io/
BSD 3-Clause "New" or "Revised" License
12 stars 4 forks source link

Add box primitive for faster drawing in plato. #18

Closed bdice closed 5 years ago

bdice commented 5 years ago

Drawing boxes is a common use case for plato. It would be nice to have a primitive that creates a box primitive from an object with attributes like Lx, Ly, Lz, xy, xz, yz. Something like the following code snippet (which uses fractional coordinates and allows freud to perform the math of converting to real space).

box_prim = draw.Lines(
    start_points=[box.makeCoordinates([0, 0, 0]),
                  box.makeCoordinates([0, 0, 0]),
                  box.makeCoordinates([0, 0, 0]),
                  box.makeCoordinates([1, 1, 0]),
                  box.makeCoordinates([1, 1, 0]),
                  box.makeCoordinates([1, 1, 0]),
                  box.makeCoordinates([0, 1, 1]),
                  box.makeCoordinates([0, 1, 1]),
                  box.makeCoordinates([0, 1, 1]),
                  box.makeCoordinates([1, 0, 1]),
                  box.makeCoordinates([1, 0, 1]),
                  box.makeCoordinates([1, 0, 1]),
                 ],
    end_points=[box.makeCoordinates([1, 0, 0]),
                box.makeCoordinates([0, 1, 0]),
                box.makeCoordinates([0, 0, 1]),
                box.makeCoordinates([1, 0, 0]),
                box.makeCoordinates([0, 1, 0]),
                box.makeCoordinates([1, 1, 1]),
                box.makeCoordinates([1, 1, 1]),
                box.makeCoordinates([0, 1, 0]),
                box.makeCoordinates([0, 0, 1]),
                box.makeCoordinates([0, 0, 1]),
                box.makeCoordinates([1, 1, 1]),
                box.makeCoordinates([1, 0, 0]),
               ],
    widths=0.2,
    colors=[0, 0, 0, 1],
)
bdice commented 5 years ago

Another related snippet from platoviz that uses itertools.product:

https://github.com/glotzerlab/platoviz/blob/0981533eb971b25d7ac3de81a556b8724b8ca742/platoviz/modules/TrajectoryViewer.py#L370-L380

        # [(0, 0, 0), (0, 0, 1), ..., (1, 1, 1)]
        fractions = np.array(list(product(*(3*[[0, 1]]))), dtype=np.float32)
        coordinates = plato.math.fractions_to_coordinates(box, fractions)

        # input pairs as sequences and reshape into two columns
        edge_indices = np.array([0, 1, 1, 3, 3, 2, 2, 0, 0, 4, 1, 5, 3, 7, 2, 6, 4,
                                 5, 5, 7, 7, 6, 6, 4], dtype=np.uint32).reshape((12, 2))
        self.box_primitive.start_points = coordinates[edge_indices[:, 0]]
        self.box_primitive.end_points = coordinates[edge_indices[:, 1]]
        self.box_primitive.widths = np.ones((12,), dtype=np.float32)*boxWidth
        self.box_primitive.colors = np.ones((12, 4), dtype=np.float32)*(0, 0, 0, 1)
klarh commented 5 years ago

I think this should be pretty straightforward to implement as a thin layer on top of Lines, much like Arrows2D uses Polygons. The triclinic box math is even already implemented in plato.math! Until we have issue #3 cleared up, though, this primitive may want to be composed of a Lines primitive and a Spheres primitive at the corners. There is an example of this inside platoviz.modules.TrajectoryViewer, in case that helps.