File: x3d.py

package info (click to toggle)
python-ase 3.21.1-2
  • links: PTS, VCS
  • area: main
  • in suites: bullseye
  • size: 13,936 kB
  • sloc: python: 122,428; xml: 946; makefile: 111; javascript: 47
file content (122 lines) | stat: -rw-r--r-- 4,024 bytes parent folder | download | duplicates (2)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
"""
Output support for X3D and X3DOM file types.
See http://www.web3d.org/x3d/specifications/
X3DOM outputs to html pages that should display 3-d manipulatable atoms in
modern web browsers.
"""

from ase.data import covalent_radii
from ase.data.colors import jmol_colors
from ase.utils import writer


@writer
def write_x3d(fd, atoms, format='X3D'):
    """Writes to html using X3DOM.

    Args:
        filename - str or file-like object, filename or output file object
        atoms - Atoms object to be rendered
        format - str, either 'X3DOM' for web-browser compatibility or 'X3D'
            to be readable by Blender. `None` to detect format based on file
            extension ('.html' -> 'X3DOM', '.x3d' -> 'X3D')"""
    X3D(atoms).write(fd, datatype=format)


@writer
def write_html(fd, atoms):
    """Writes to html using X3DOM.

    Args:
        filename - str or file-like object, filename or output file object
        atoms - Atoms object to be rendered"""
    write_x3d(fd, atoms, format='X3DOM')


class X3D:
    """Class to write either X3D (readable by open-source rendering
    programs such as Blender) or X3DOM html, readable by modern web
    browsers.
    """

    def __init__(self, atoms):
        self._atoms = atoms

    def write(self, fileobj, datatype):
        """Writes output to either an 'X3D' or an 'X3DOM' file, based on
        the extension. For X3D, filename should end in '.x3d'. For X3DOM,
        filename should end in '.html'.

        Args:
            datatype - str, output format. 'X3D' or 'X3DOM'.
        """

        # Write the header
        w = WriteToFile(fileobj)
        if datatype == 'X3DOM':
            w(0, '<html>')
            w(1, '<head>')
            w(2, '<title>ASE atomic visualization</title>')
            w(2, '<link rel="stylesheet" type="text/css"')
            w(2, ' href="https://www.x3dom.org/x3dom/release/x3dom.css">')
            w(2, '</link>')
            w(2, '<script type="text/javascript"')
            w(2, ' src="https://www.x3dom.org/x3dom/release/x3dom.js">')
            w(2, '</script>')
            w(1, '</head>')
            w(1, '<body>')
            w(2, '<X3D>')
        elif datatype == 'X3D':
            w(0, '<?xml version="1.0" encoding="UTF-8"?>')
            w(0, '<!DOCTYPE X3D PUBLIC "ISO//Web3D//DTD X3D 3.2//EN" '
              '"http://www.web3d.org/specifications/x3d-3.2.dtd">')
            w(0, '<X3D profile="Interchange" version="3.2" '
              'xmlns:xsd="http://www.w3.org/2001/XMLSchema-instance" '
              'xsd:noNamespaceSchemaLocation='
              '"http://www.web3d.org/specifications/x3d-3.2.xsd">')
        else:
            raise ValueError("datatype not supported: " + str(datatype))

        w(3, '<Scene>')

        for atom in self._atoms:
            for indent, line in atom_lines(atom):
                w(4 + indent, line)

        w(3, '</Scene>')

        if datatype == 'X3DOM':
            w(2, '</X3D>')
            w(1, '</body>')
            w(0, '</html>')
        elif datatype == 'X3D':
            w(0, '</X3D>')


class WriteToFile:
    """Creates convenience function to write to a file."""

    def __init__(self, fileobj):
        self._f = fileobj

    def __call__(self, indent, line):
        text = ' ' * indent
        print('%s%s\n' % (text, line), file=self._f)


def atom_lines(atom):
    """Generates a segment of X3D lines representing an atom."""
    x, y, z = atom.position
    lines = [(0, '<Transform translation="%.2f %.2f %.2f">' % (x, y, z))]
    lines += [(1, '<Shape>')]
    lines += [(2, '<Appearance>')]
    color = tuple(jmol_colors[atom.number])
    color = 'diffuseColor="%.3f %.3f %.3f"' % color
    lines += [(3, '<Material %s specularColor="0.5 0.5 0.5">' % color)]
    lines += [(3, '</Material>')]
    lines += [(2, '</Appearance>')]
    lines += [(2, '<Sphere radius="%.2f">' % covalent_radii[atom.number])]
    lines += [(2, '</Sphere>')]
    lines += [(1, '</Shape>')]
    lines += [(0, '</Transform>')]
    return lines