So as a starting point, here's how you draw black circles with red borders for every atom (I also attached a picture of the result to compare with the previous version)

`import ovito`

import numpy as np

from PyQt5.QtCore import *

from PyQt5.QtGui import *

import ovito.vis

# This helper function projects a point from 3d space to

# 2d window coordinates.

def project_point(xyz, painter, args):

view_tm = args['view_tm'] # 3x4 matrix

proj_tm = args['proj_tm'] # 4x4 matrix

world_pos = np.append(xyz, 1) # Convert to 4-vector.

view_pos = np.dot(view_tm, world_pos) # Transform to view space.

# Check if point is behind the viewer. If yes, stop here.

if args['is_perspective'] and view_pos[2] >= 0.0: return None

# Project to screen space:

screen_pos = np.dot(proj_tm, np.append(view_pos, 1))

screen_pos[0:3] /= screen_pos[3]

win_rect = painter.window()

x = win_rect.left() + win_rect.width() * (screen_pos[0] + 1) / 2

y = win_rect.bottom() - win_rect.height() * (screen_pos[1] + 1) / 2 + 1

return (x,y)

# This helper function projects a distance or radius from 3d space to

# 2d window coordinates.

def project_radius(xyz, r, painter, args):

if args['is_perspective']:

world_pos = np.append(xyz, 1) # Convert to 4-vector.

vp = np.append(np.dot(args['view_tm'], world_pos), 1) # Transform to view space.

p1 = np.dot(args['proj_tm'], vp) # Project to screen space.

p1[0:3] /= p1[3]

vp += [0,r,0,0]

p2 = np.dot(args['proj_tm'], vp) # Project to screen space.

p2[0:3] /= p2[3]

return np.linalg.norm(p2-p1) * painter.window().height() / 2

else:

return r / args['fov'] * painter.window().height() / 2

def render(painter, **args):

# Access current particle positions.

node = ovito.dataset.selected_node

positions = node.compute().particle_properties.position.array

#Sort particles by distance viewer depending on camera view

vp = ovito.dataset.viewports.active_vp

projected_pos = np.dot(positions,vp.camera_dir)

sorted_indices = np.argsort(projected_pos)[::-1]

# Project center point of first particle.

for particle_index in sorted_indices:

xy = project_point(positions[particle_index], painter, args)

if xy is None: return

# Get particle display radius.

radius = node.source.particle_properties.position.display.radius

# Calculate screen-space size of particle in pixels.

screen_radius = project_radius(positions[0], radius, painter, args)

# Draw circles instead of particles.

pen = QPen(Qt.SolidLine)

pen.setWidth(3)

pen.setColor(QColor(255,0,0))

painter.setPen(pen)

painter.setBrush(QColor(0,0,0))

painter.drawEllipse(QPointF(xy[0], xy[1]), screen_radius, screen_radius)

If you like, copy that code and try it out in the python script overlay. I'll leave the part about drawing bonds up to you. Let me know if you have questions.

Edit: I added another 3 lines of code that sort the particles by "distance from the viewer" depending on the viewing direction of the active viewport. Otherwise the circles might not be plotted in the right order.

-Constanze