コード例 #1
0
ファイル: test_utils.py プロジェクト: femisan/bfieldtools
def test_mesh_utils():

    mesh1 = utils.load_example_mesh("unit_disc")
    mesh2 = utils.load_example_mesh("10x10_plane")

    mesh2.vertices += np.array([[0, 0, 20]])

    mesh3 = utils.combine_meshes((mesh1, mesh2))

    assert len(mesh3.vertices) == len(mesh1.vertices) + len(mesh2.vertices)
コード例 #2
0
def test_line_conductor_functionality():
    """
    Test LineConductor creation and functionality
    """

    lp = _fake_line_conductor()
    lp2 = _fake_line_conductor_closed()

    lp.plot_loops()

    mesh = load_example_mesh("unit_disc")
    scalars = np.ones(
        (len(mesh.vertices), )) - np.linalg.norm(mesh.vertices, axis=1)

    lp = line_conductor.LineConductor(mesh=mesh, scalars=scalars, N_contours=6)

    slp = lp.simplify()

    B = slp.magnetic_field(mesh.vertices + np.array([0, 0, 1]))
    A = slp.vector_potential(mesh.vertices + np.array([0, 0, 1]))
    U = slp.scalar_potential(mesh.vertices + np.array([0, 0, 1]))

    mesh.vertices += np.array([[0, 0, 1]])

    M = slp.mesh_mutual_inductance(mesh)
コード例 #3
0
def test_magnetic_vector_potential_coupling():

    mesh = load_example_mesh("unit_disc")

    points = mesh.vertices + np.array([0, 0, 20])

    A_1_chunk = mesh_magnetics.vector_potential_coupling(mesh,
                                                         points,
                                                         Nchunks=1)
    A_2_chunk = mesh_magnetics.vector_potential_coupling(mesh,
                                                         points,
                                                         Nchunks=2)

    assert_allclose(A_1_chunk, A_2_chunk, atol=1e-16)

    A_exact = mesh_magnetics.vector_potential_coupling(mesh,
                                                       points,
                                                       approx_far=False)
    A_approx_far = mesh_magnetics.vector_potential_coupling(mesh,
                                                            points,
                                                            approx_far=True,
                                                            margin=0.5)
    A_approx_far_clusters = mesh_magnetics.vector_potential_coupling(
        mesh, points, approx_far=True, margin=0.5, chunk_clusters=True)

    assert_allclose(A_exact,
                    A_approx_far,
                    atol=2e-3 * np.mean(np.abs(A_exact)))
    assert_allclose(A_exact,
                    A_approx_far_clusters,
                    atol=2e-3 * np.mean(np.abs(A_exact)))
コード例 #4
0
def test_triangle_potential_approximation():
    """
    test whether 1/r triangle potential approximations give close enough
    to exact (with/without planar assumption)
    """

    mesh = load_example_mesh("unit_disc")

    points = np.array([[0, 0, 1], [-0.1, 0.1, -5]])

    R = points[:, None, None, :] - mesh.vertices[mesh.faces][None, :, :, :]
    Rcenters = points[:, None, :] - mesh.triangles_center[None, :, :]

    exact = integrals.triangle_potential_uniform(R, mesh.face_normals)
    approx = integrals.triangle_potential_approx(Rcenters, mesh.area_faces)

    assert_allclose(approx, exact, rtol=5e-3)

    points = np.array([[0, 0, 0], [-0.1, 0.1, 0]])
    R = points[:, None, None, :] - mesh.vertices[mesh.faces][None, :, :, :]

    exact = integrals.triangle_potential_uniform(R, mesh.face_normals)
    exact_planar = integrals.triangle_potential_uniform(R,
                                                        mesh.face_normals,
                                                        planar=True)

    assert_allclose(exact_planar, exact)
コード例 #5
0
def test_mesh_plotting():

    mesh = load_example_mesh("unit_disc")

    v_scalars = np.random.rand(len(mesh.vertices))
    f_scalars = np.random.rand(len(mesh.faces))

    viz.plot_mesh(mesh, figure=None)
    f = mlab.figure()
    viz.plot_mesh(mesh, figure=f)

    viz.plot_data_on_faces(mesh, f_scalars)

    f = mlab.figure()
    viz.plot_data_on_faces(mesh, f_scalars, figure=f, colormap="jet")

    f_scalars = np.random.rand(len(mesh.faces)) - 0.5
    viz.plot_data_on_faces(mesh, f_scalars, figure=f, colorbar=True)

    viz.plot_data_on_vertices(mesh, v_scalars, colorbar=True)
    viz.plot_data_on_vertices(mesh, v_scalars, autoscale=True)

    v_scalars = np.random.rand(len(mesh.vertices)) - 0.5

    viz.plot_data_on_vertices(mesh, v_scalars, colormap="jet")
コード例 #6
0
def test_magnetic_scalar_potential_coupling():

    mesh = load_example_mesh("unit_disc")

    points = mesh.vertices + np.array([0, 0, 20])

    U_1_chunk = mesh_magnetics.scalar_potential_coupling(mesh,
                                                         points,
                                                         Nchunks=1)
    U_2_chunk = mesh_magnetics.scalar_potential_coupling(mesh,
                                                         points,
                                                         Nchunks=2)

    assert_allclose(U_1_chunk, U_2_chunk, atol=1e-16)

    U_exact = mesh_magnetics.scalar_potential_coupling(mesh,
                                                       points,
                                                       approx_far=False)
    U_approx_far = mesh_magnetics.scalar_potential_coupling(mesh,
                                                            points,
                                                            approx_far=True,
                                                            margin=0.5)

    assert_allclose(U_exact,
                    U_approx_far,
                    atol=2e-3 * np.mean(np.abs(U_exact)))
コード例 #7
0
ファイル: test_utils.py プロジェクト: femisan/bfieldtools
def test_quad_points():
    mesh = utils.load_example_mesh("unit_disc")

    w, qp = utils.get_quad_points(mesh.vertices, mesh.faces)
    w, qp = utils.get_quad_points(mesh.vertices, mesh.faces, "dunavant_02")
    w, qp = utils.get_quad_points(mesh.vertices,
                                  mesh.faces,
                                  method="lether",
                                  index=3)
コード例 #8
0
def test_d_distance():

    mesh = load_example_mesh("unit_disc")

    for z_offset in [1, 0, -1]:
        points = mesh.vertices + np.array([[0, 0, z_offset]])

        R = points[:, None, None, :] - mesh.vertices[mesh.faces][None, :, :, :]

        assert_allclose(integrals.d_distance(R, mesh.face_normals), z_offset)
コード例 #9
0
def setup_contour_input():
    """ Load example mesh and create scalars data
    """
    from bfieldtools.utils import load_example_mesh

    mesh = load_example_mesh("unit_disc")

    r = np.linalg.norm(mesh.vertices, axis=1)
    scalars = r**2

    return mesh, scalars
コード例 #10
0
def test_others():

    mesh = load_example_mesh("unit_disc")

    vals = np.ones((len(mesh.vertices),)) - np.linalg.norm(mesh.vertices, axis=1)

    G = mesh_calculus.gradient(vals, mesh, rotated=False)
    Gr = mesh_calculus.gradient(vals, mesh, rotated=True)

    D = mesh_calculus.divergence(Gr.T, mesh)
    C = mesh_calculus.curl(Gr.T, mesh)
コード例 #11
0
ファイル: test_suhtools.py プロジェクト: femisan/bfieldtools
def test_suhbasis():

    mesh = load_example_mesh("unit_disc")

    for obj in [mesh, _fake_mesh_conductor(mesh_name="unit_disc")]:
        for mag in [False]:
            for bc in ["neumann", "dirichlet"]:
                suh = suhtools.SuhBasis(obj,
                                        Nc=10,
                                        boundary_condition=bc,
                                        magnetic=mag)
                assert suh.basis.shape[1] == 10

                suh.field(suh.basis[1], np.array([[0, 0, 1]]))

                suh.plot(Nfuncs=3)

    suh = suhtools.SuhBasis(mesh,
                            Nc=10,
                            boundary_condition="dirichlet",
                            magnetic="DC")
    assert suh.basis.shape[1] == 10

    suh = suhtools.SuhBasis(mesh,
                            Nc=10,
                            boundary_condition="dirichlet",
                            magnetic="AC")
    assert suh.basis.shape[1] == 10

    suh = suhtools.SuhBasis(mesh,
                            boundary_condition="dirichlet",
                            magnetic="DC")
    suh = suhtools.SuhBasis(mesh,
                            boundary_condition="dirichlet",
                            magnetic="AC")
    suh = suhtools.SuhBasis(mesh,
                            boundary_condition="dirichlet",
                            magnetic=False)

    suh.calculate_basis(shiftinvert=False, v0=np.ones(57))

    try:
        suh = suhtools.SuhBasis(10, Nc=10)
        assert suh.basis.shape[1] == 10
    except:
        print("Caught test exception")

    f = mlab.figure()

    suh.plot([0, 2], figure=f)
    suh.plot(Nfuncs=4, Ncols=2, figure=f)
コード例 #12
0
def test_omega():
    """
    Tests solid angle (whether sphere sums up to 4*pi)

    """

    mesh = load_example_mesh("unit_sphere")

    points = np.array([[0, 0, 0], [0.1, 0, 0.2], [-0.1, 0.1, -0.2]])

    R = points[:, None, None, :] - mesh.vertices[mesh.faces][None, :, :, :]

    solid_angle = integrals.omega(R)

    assert_allclose(np.sum(solid_angle, axis=1), 4 * np.pi)
コード例 #13
0
def _fake_streamfunction(vals=None, mesh_name="unit_disc", **kwargs):
    """
    Creates an example 'fake' StreamFunction object
    """
    if vals:
        return mesh_conductor.StreamFunction(
            vals, _fake_mesh_conductor(mesh_name, **kwargs)
        )
    else:
        mesh = load_example_mesh(mesh_name)

        vals = 1 - np.linalg.norm(mesh.vertices, axis=1)

        return mesh_conductor.StreamFunction(
            vals, _fake_mesh_conductor(mesh_name, basis_name="vertex")
        )
コード例 #14
0
ファイル: test_suhtools.py プロジェクト: femisan/bfieldtools
def test_shiftinvert():

    mesh = load_example_mesh("unit_disc")

    suh = suhtools.SuhBasis(mesh,
                            Nc=10,
                            boundary_condition="dirichlet",
                            magnetic=False)

    suh.calculate_basis(shiftinvert=True)
    shiftinvert_eigenvals = suh.eigenvals
    shiftinvert_basis = suh.basis

    suh.calculate_basis(shiftinvert=False)
    nonshiftinvert_eigenvals = suh.eigenvals
    nonshiftinvert_basis = suh.basis

    assert_allclose(shiftinvert_eigenvals, nonshiftinvert_eigenvals)
コード例 #15
0
def test_magnetic_field_coupling():

    mesh = load_example_mesh("unit_disc")

    points = mesh.vertices + np.array([0, 0, 20])

    B_1_chunk = mesh_magnetics.magnetic_field_coupling(mesh, points, Nchunks=1)
    B_2_chunk = mesh_magnetics.magnetic_field_coupling(mesh, points, Nchunks=2)

    assert_allclose(B_1_chunk, B_2_chunk, atol=1e-16)

    B_q_1 = mesh_magnetics.magnetic_field_coupling(mesh, points, quad_degree=1)
    B_q_2 = mesh_magnetics.magnetic_field_coupling(mesh, points, quad_degree=2)
    B_q_3 = mesh_magnetics.magnetic_field_coupling(mesh, points, quad_degree=3)

    assert_allclose(B_q_1, B_q_2, atol=1e-3 * np.mean(np.abs(B_q_2)))
    assert_allclose(B_q_2, B_q_3, atol=1e-3 * np.mean(np.abs(B_q_2)))

    B_a = mesh_magnetics.magnetic_field_coupling(mesh, points, analytic=True)

    assert_allclose(B_a, B_q_1, atol=1e-3 * np.mean(np.abs(B_a)))
    assert_allclose(B_a, B_q_2, atol=1e-3 * np.mean(np.abs(B_a)))
    assert_allclose(B_a, B_q_3, atol=1e-3 * np.mean(np.abs(B_a)))
コード例 #16
0
def test_dipole_potential_approximation():
    """
    Test whether approximations and exact solution are close enough

    """

    mesh = load_example_mesh("unit_disc")

    points = np.array([[0.1, 0, 0.2], [-0.1, 0.1, -0.2]]) * 20

    R = points[:, None, None, :] - mesh.vertices[mesh.faces][None, :, :, :]
    R_v = points[:, None, :] - mesh.vertices[None, :, :]

    vertex_areas = mass_matrix(mesh, lumped=True).diagonal()

    approx_pot_v = integrals.potential_vertex_dipoles(R_v, mesh.vertex_normals,
                                                      vertex_areas)

    approx_pot_f = integrals.potential_dipoles(R, mesh.face_normals,
                                               mesh.area_faces)
    exact_pot_f = integrals.triangle_potential_dipole_linear(
        R, mesh.face_normals, mesh.area_faces)

    # Map faces -> vertices
    Nf = len(mesh.faces)
    Nv = len(mesh.vertices)
    M0 = csc_matrix((np.ones(Nf), (np.arange(Nf), mesh.faces[:, 0])), (Nf, Nv))
    M1 = csc_matrix((np.ones(Nf), (np.arange(Nf), mesh.faces[:, 1])), (Nf, Nv))
    M2 = csc_matrix((np.ones(Nf), (np.arange(Nf), mesh.faces[:, 2])), (Nf, Nv))
    exact_pot_v = (exact_pot_f[:, :, 0] @ M0 + exact_pot_f[:, :, 1] @ M1 +
                   exact_pot_f[:, :, 2] @ M2)
    approx_pot_fv = (approx_pot_f[:, :, 0] @ M0 + approx_pot_f[:, :, 1] @ M1 +
                     approx_pot_f[:, :, 2] @ M2)

    assert_allclose(approx_pot_v, exact_pot_v, rtol=5e-2)
    assert_allclose(approx_pot_fv, exact_pot_v, rtol=1e-3)
コード例 #17
0

def field_disc(z, a):
    """Bfield z-component of streamfunction psi=r**2 z-axis
    for a disk with radius "a" on the xy plane
    """

    coeff = 1e-7
    field = -2 * (a**2 + 2 * z**2) / np.sqrt((a**2 + z**2)) + 4 * abs(z)
    field *= coeff * 2 * np.pi

    return field


# Load disc and subdivide
disc = load_example_mesh("unit_disc")
for i in range(4):
    disc = disc.subdivide()
disc.vertices -= disc.vertices.mean(axis=0)

# Stream functions
s = disc.vertices[:, 0]**2 + disc.vertices[:, 1]**2

#%% Create evaluation points and plot them
Np = 50
z = np.linspace(-1, 10, Np) + 0.001
points = np.zeros((Np, 3))
points[:, 2] = z
mlab.triangular_mesh(*disc.vertices.T,
                     disc.faces,
                     scalars=s,
コード例 #18
0
ファイル: test_suhtools.py プロジェクト: femisan/bfieldtools
def test_suhbasis_fail3():
    mesh = load_example_mesh("unit_disc")
    suh = suhtools.SuhBasis(mesh, Nc=1000)
コード例 #19
0
ファイル: test_suhtools.py プロジェクト: femisan/bfieldtools
def test_suhbasis_fail2():
    mesh = load_example_mesh("unit_disc")
    suh = suhtools.SuhBasis(mesh, Nc=10, magnetic="foo")
コード例 #20
0
ファイル: test_suhtools.py プロジェクト: femisan/bfieldtools
def test_suhbasis_fail():
    mesh = load_example_mesh("unit_disc")
    suh = suhtools.SuhBasis(mesh, Nc=10, boundary_condition="foo")
コード例 #21
0
Head gradient coil
==================

Example showing a gradient coil designed on the surface of a MEG system helmet
"""

import numpy as np
from mayavi import mlab

from bfieldtools.mesh_conductor import MeshConductor
from bfieldtools.coil_optimize import optimize_streamfunctions
from bfieldtools.utils import load_example_mesh
from bfieldtools import sphtools

# Load simple plane mesh that is centered on the origin
helmetmesh = load_example_mesh("meg_helmet")
coil = MeshConductor(mesh_obj=helmetmesh, fix_normals=True)

#%%
# Set up target and stray field points.
# Here, the target points are on a volumetric grid within a sphere

offset = np.array([0, 0, 0.04])
center = offset

sidelength = 0.05
n = 12
xx = np.linspace(-sidelength / 2, sidelength / 2, n)
yy = np.linspace(-sidelength / 2, sidelength / 2, n)
zz = np.linspace(-sidelength / 2, sidelength / 2, n)
X, Y, Z = np.meshgrid(xx, yy, zz, indexing="ij")
コード例 #22
0
import matplotlib.pyplot as plt
from mayavi import mlab
import trimesh
import pkg_resources
from pyface.api import GUI

_gui = GUI()

from bfieldtools.mesh_magnetics import magnetic_field_coupling
from bfieldtools.mesh_magnetics import magnetic_field_coupling_analytic
from bfieldtools.mesh_magnetics import scalar_potential_coupling
from bfieldtools.sphtools import compute_sphcoeffs_mesh, basis_fields
from bfieldtools.suhtools import SuhBasis
from bfieldtools.utils import load_example_mesh

mesh = load_example_mesh("bunny_repaired")

mesh.vertices -= mesh.vertices.mean(axis=0)

mesh_field = mesh.copy()
mesh_field.vertices += 0.005 * mesh_field.vertex_normals
mesh_field = trimesh.smoothing.filter_laplacian(mesh_field, iterations=1)

Ca, Cb = basis_fields(mesh_field.vertices, 4)

bsuh = SuhBasis(mesh, 25)
Csuh = magnetic_field_coupling_analytic(mesh, mesh_field.vertices) @ bsuh.basis


#%%
def plot_basis_fields(C, comps):
コード例 #23
0
        fig = mlab.figure(bgcolor=(1, 1, 1))
        surf = s.plot(False, figure=fig)
        surf.actor.mapper.interpolate_scalars_before_mapping = True
        surf.module_manager.scalar_lut_manager.number_of_colors = 16

        if SAVE_FIGURES:
            mlab.savefig(
                SAVE_DIR + "SUH_scalp_streamfunction.png", magnification=4, figure=fig
            )

#%%
# Interpolate MEG data to the sensor surface

from bfieldtools.utils import load_example_mesh

helmet = load_example_mesh("meg_helmet", process=False)
# Bring the surface roughly to the correct place
helmet.vertices[:, 2] -= 0.05

# Reset coupling by hand
c.B_coupling.reset()
mlab.figure(bgcolor=(1, 1, 1))
B_surf = np.sum(
    c.B_coupling(helmet.vertices) * helmet.vertex_normals[:, :, None], axis=1
)

if PLOT:
    fig = mlab.quiver3d(*p.T, *n.T, mode="arrow")
    scalars = B_surf @ s
    surf = mlab.triangular_mesh(
        *helmet.vertices.T, helmet.faces, scalars=scalars, colormap="seismic"
コード例 #24
0
Example showing a basic biplanar coil producing homogeneous field in a target
region between the two coil planes. The coil planes have holes in them,

"""

import numpy as np
import trimesh

from bfieldtools.mesh_conductor import MeshConductor
from bfieldtools.coil_optimize import optimize_streamfunctions
from bfieldtools.utils import combine_meshes, load_example_mesh


# Load simple plane mesh that is centered on the origin
planemesh = load_example_mesh("plane_w_holes")

angle = np.pi / 2
rotation_matrix = np.array(
    [
        [1, 0, 0, 0],
        [0, np.cos(angle), -np.sin(angle), 0],
        [0, np.sin(angle), np.cos(angle), 0],
        [0, 0, 0, 1],
    ]
)

planemesh.apply_transform(rotation_matrix)

# Specify coil plane geometry
center_offset = np.array([0, 0, 0])
コード例 #25
0
from mayavi import mlab
import trimesh

from bfieldtools.suhtools import SuhBasis
from bfieldtools.utils import find_mesh_boundaries

from trimesh.creation import icosphere
from bfieldtools.utils import load_example_mesh

# Import meshes

# Sphere
sphere = icosphere(4, 0.1)

# Plane mesh centered on the origin
plane = load_example_mesh("10x10_plane_hires")
scaling_factor = 0.02
plane.apply_scale(scaling_factor)
# Rotate to x-plane
t = np.eye(4)
t[1:3, 1:3] = np.array([[0, 1], [-1, 0]])
plane.apply_transform(t)

# Bunny
bunny = load_example_mesh("bunny_repaired")
bunny.vertices -= bunny.vertices.mean(axis=0)

mlab.figure(bgcolor=(1, 1, 1))
for mesh in (sphere, plane, bunny):
    s = mlab.triangular_mesh(*mesh.vertices.T,
                             mesh.faces,
コード例 #26
0
"""

from bfieldtools.line_conductor import LineConductor
from bfieldtools.mesh_conductor import StreamFunction
from bfieldtools.suhtools import SuhBasis
from bfieldtools.utils import load_example_mesh

import numpy as np
from mayavi import mlab

#%%
# We create a set of wire loops by picking a single (arbitrary) surface-harmonic mode
# from a plane mesh.  Finally, we discretize the  mode into a set of wire loops, which we plot.

mesh = load_example_mesh("10x10_plane")
mesh.apply_scale(0.1)  # Downsize from 10 meters to 1 meter
N_contours = 20

sb = SuhBasis(mesh, 10)  # Construct surface-harmonics basis
sf = StreamFunction(
    sb.basis[:,
             1], sb.mesh_conductor)  # Turn single mode into a stream function
c = LineConductor(
    mesh=mesh, scalars=sf,
    N_contours=N_contours)  # Discretize the stream function into wire loops

# Plot loops for testing
c.plot_loops(origin=np.array([0, -100, 0]))

#%%
コード例 #27
0
def _fake_mesh_conductor(mesh_name="10x10_plane", **kwargs):
    """
    Creates an example 'fake' MeshConductor object
    """
    return mesh_conductor.MeshConductor(mesh_obj=load_example_mesh(mesh_name), **kwargs)
コード例 #28
0
import matplotlib.pyplot as plt
from mayavi import mlab
import trimesh
import pkg_resources
from pyface.api import GUI

_gui = GUI()

from bfieldtools.mesh_magnetics import magnetic_field_coupling
from bfieldtools.mesh_magnetics import magnetic_field_coupling_analytic
from bfieldtools.mesh_magnetics import scalar_potential_coupling
from bfieldtools.sphtools import compute_sphcoeffs_mesh, basis_fields
from bfieldtools.suhtools import SuhBasis
from bfieldtools.utils import load_example_mesh

mesh = load_example_mesh("bunny_repaired", process=True)

mesh.vertices -= mesh.vertices.mean(axis=0)

mesh_field = mesh.copy()
mesh_field.vertices += 0.001 * mesh_field.vertex_normals
# mesh_field = trimesh.smoothing.filter_laplacian(mesh_field, iterations=1)

# Ca, Cb = basis_fields(mesh_field.vertices, 4)

# bsuh = SuhBasis(mesh, 25)
# Csuh = magnetic_field_coupling_analytic(mesh, mesh_field.vertices) @ bsuh.basis

#%%
# def plot_basis_fields(C, comps):
#     d = 0.17
コード例 #29
0
Example showing a basic biplanar coil producing a high-order spherical harmonic field
in a specific target region between the two coil planes.

"""

import numpy as np
from mayavi import mlab
import trimesh

from bfieldtools.mesh_conductor import MeshConductor
from bfieldtools.coil_optimize import optimize_streamfunctions
from bfieldtools.utils import combine_meshes, load_example_mesh

# Load simple plane mesh that is centered on the origin
planemesh = load_example_mesh("10x10_plane_hires")

# Specify coil plane geometry
center_offset = np.array([0, 0, 0])
standoff = np.array([0, 3, 0])

# Create coil plane pairs
coil_plus = trimesh.Trimesh(planemesh.vertices + center_offset + standoff,
                            planemesh.faces,
                            process=False)

coil_minus = trimesh.Trimesh(planemesh.vertices + center_offset - standoff,
                             planemesh.faces,
                             process=False)

joined_planes = combine_meshes((coil_plus, coil_minus))
コード例 #30
0
for i in range(len(alpha)):
    xticknames[i] = str(M[i])

    m_l = np.arange(-L[i], L[i] + 1, step=1)

    if i == int(np.floor(len(m_l))):
        xticknames[i] += "\n" + str(L[i])

plt.figure()
plt.plot(a**2)

#%% Compute potential on the helmet mesh
from bfieldtools.utils import load_example_mesh
from bfieldtools.flatten_mesh import flatten_mesh, mesh2plane

helmet = load_example_mesh("meg_helmet", process=False)
# Bring the surface roughly to the correct place
helmet.vertices[:, 2] -= 0.045
# The helmet is slightly tilted, correct for this
# (probably the right coordinate transformation could be found from MNE)
rotmat = np.eye(3)
tt = 0.015 * np.pi
rotmat[:2, :2] = np.array([[np.cos(tt), np.sin(tt)], [-np.sin(tt),
                                                      np.cos(tt)]])
helmet.vertices = helmet.vertices @ rotmat
tt = -0.02 * np.pi
rotmat[1:, 1:] = np.array([[np.cos(tt), np.sin(tt)], [-np.sin(tt),
                                                      np.cos(tt)]])
helmet.vertices = helmet.vertices @ rotmat
helmet.vertices[:, 1] += 0.005