Author Topic: Drawing border around circles  (Read 437 times)

coffee4kepi

  • Newbie
  • *
  • Posts: 3
Drawing border around circles
« on: October 17, 2018, 09:28:22 PM »
Dear all,

I am trying to draw the atoms I have in the cell in a simple way, without any "rendering", but just as a cartoon. I can select to show them as circles instead of spheres, which is precisely what I am looking for, however I am not finding an easier way to draw a border around than duplicating the input data and making the same spheres but smaller.
Also the problem is more important with the sticks for the bonds: when atoms are circles, it seems they are considered "flat" from the bond and the sticks are sometimes drawn on top of the circles; moreover the trick for getting the border is getting awful results, because the bond itself is a 3d object and the viewport is not properly "slicing" the cylinders. You can see an example of this issue in this image: https://postimg.cc/ZWtXk3Nq
I am using ovito v. 2.9.0
Any suggestions?

Thank you in advance,

Bests

Aldo
« Last Edit: October 18, 2018, 08:51:14 AM by coffee4kepi »

Constanze Kalcher

  • Administrator
  • Full Member
  • *****
  • Posts: 113
Re: Drawing border around circles
« Reply #1 on: October 18, 2018, 12:07:19 PM »
Dear Aldo,

I think the easiest solution is to follow your strategy but repeating it one more time.
First, load in your dataset, plot the particles (as circles) and bonds (with shading mode = flat) with the "bordering" color.
Then load in the dataset one more time, create thinner bonds with your desired "inner" bond color, but this time deactivate Particles under "Display". Now also use the Affine-Transformation modifier and shift these bonds a little towards the viewing direction so that they are on top.
Finally, load the dataset one last time, shift the atoms using the Affine Transformation modifier and plot only the particles as smaller circles and use the fill color of your choice.

I attached a screenshot of an example structure. Might be that you need to add fourth step if you don't want to have "closed" circles around your atoms.
Anyways, I hope that helps. Let me know if that was what you're aiming at.

-Constanze

coffee4kepi

  • Newbie
  • *
  • Posts: 3
Re: Drawing border around circles
« Reply #2 on: October 18, 2018, 06:34:50 PM »
Dear Constanze,

yes, this result is what I was trying to reproduce. The outcome is not perfectly fine (the bonds are seen above the border), the whole procedure is toilsome and finally is limited to topview rendering. I was hoping for some flag or at least some modifier to use in a python script.
Thank you for your suggestion.

Aldo

Constanze Kalcher

  • Administrator
  • Full Member
  • *****
  • Posts: 113
Re: Drawing border around circles
« Reply #3 on: October 18, 2018, 07:06:41 PM »
Dear Aldo,

just to clarify, I created the picture I uploaded with OVITO. Depending on the "stacking sequence" of your different bonds and particles, I guess it should in principle be be possible to hide the bonds behind the "border". Also, the Affine Transformation could be adapted to follow any viewing direction. By the way, the whole procedure can be made a little bit less painful by using the "Pipeline cloning" functionality in OVITO 3, but I do get your point.

Here's another idea: Are you aware of the code examples given in the documentation about Viewport overlays? You could use a Python script overlay that's based on "Example: Viewport projection" and add a function that draws the bonds as well. The learning curve will be steep, but you'll have more flexibility there.

-Constanze

Constanze Kalcher

  • Administrator
  • Full Member
  • *****
  • Posts: 113
Re: Drawing border around circles
« Reply #4 on: October 18, 2018, 07:34:59 PM »
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)

Code: [Select]
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

« Last Edit: October 19, 2018, 11:11:31 AM by Constanze Kalcher »