Ask Your Question
0

Is it possible to determine if a line segment is present between two points on a 3d graphic object?

asked 2020-03-16 20:22:28 +0100

RyanG gravatar image

updated 2020-03-16 23:52:30 +0100

Hello everyone. I'm working on a program which plots 3D molecular structures and associated point group symmetries. I've had some issues figuring out how to fill in the remaining single bonds of a six member ring (aromatic class) after the user dictates where double bonds should be placed. These aromatics are basically 6 member cycle graphs for the purposes of my problem. Is there any way to evaluate two coordinates (atoms) on a 3d graphic object (molecule) and check if a line segment (bond) has already been added between them? By necessity any pi (double) bond should only be connected to the adjacent, (non pi-bonded) atom. So for example if there was a pi bond present between Atoms 1 and 2 of a ring then we should have single bonds between A6-A1 and A2-A3. The code below is how I am currently approaching that problem.

A1,A2,A3,A4,A6 are fixed, three dimensional coordinates associated with each position on the ring.

Bonds = str(input('Enter pi bond atomic pair in format "A1-A2" or type PLOT: '))

while (Bonds != 'PLOT'):
    Bonds2 = Bonds.split('-')
    for bond in Bonds2:
        if bond == 'A1':
            pi_list.append(A1)
        elif bond == 'A2':
            pi_list.append(A2)
        elif bond == 'A3':
            pi_list.append(A3)
        elif bond == 'A4':
            pi_list.append(A4)
        elif bond == 'A5':
            pi_list.append(A5)
        elif bond == 'A6':
            pi_list.append(A6)
    if pi_list != []:
        x += pi_bond(x,pi_list[0], pi_list[1])
    for atom in Coords:
        for pi_atom in pi_list:
        if (atom != pi_list[0]) and (atom != pi_list[1]) and (float(distance(pi_atom,atom))==float(distance(A1,A2))):
            x += single_bond(pi_atom, atom)
    pi_list.clear()

    if (Bonds == 'PLOT'):
        break
    Bonds = str(input('Enter additional pi bonds or type PLOT: '))

Can anyone think of a more elegant way to approach this?

Here is the function I am currently using to add a double bond between two points:

def pi_bond(x,Cx,Cy):
    X = 0.05
    if Cx[1] == Cy[1]:
        Ca = Cx[1]-X
        Cb = Cx[1]+X
        Cc = Cy[1]-X
        Cd = Cy[1]+X
        Cx1 = (Cx[0], Ca, Cx[2])
        Cx2 = (Cx[0], Cb, Cx[2])
        Cy1 = (Cy[0], Cc, Cy[2])
        Cy2 = (Cy[0], Cd, Cy[2])
        x += LineSegment(Cx1, Cy1, 1, color='white', axes=False, frame=False)
        x += LineSegment(Cx2, Cy2, 1, color='white', axes=False, frame=False)
       return x
    else:
        Ca = Cx[2]-X
        Cb = Cx[2]+X
        Cc = Cy[2]-X
        Cd = Cy[2]+X
        Cx1 = (Cx[0], Cx[1], Ca)
        Cx2 = (Cx[0], Cx[1], Cb)
        Cy1 = (Cy[0], Cy[1], Cc)
        Cy2 = (Cy[0], Cy[1], Cd)
        x += LineSegment(Cx1, Cy1, 1, color='white', axes=False, frame=False)
        x += LineSegment(Cx2, Cy2, 1, color='white', axes=False, frame=False)
        return x
edit retag flag offensive close merge delete

Comments

I would maintain all of the data for the molecule without doing any plotting, for example as a dictionary. Then it's easy to add and delete entries. When you're ready to plot, create the plot.

John Palmieri gravatar imageJohn Palmieri ( 2020-03-16 21:22:40 +0100 )edit

Am I understanding correctly that you're suggesting I create a default position dictionary that would like something like:

graph = { A1 : [A2, A6], A2 : [A1, A3], A3 : [A2, A4], A4: [A3, A5], A5: [A4, A6], A6: [A5, A1] }

This way when a user adds a pi bond pair ie A1-A2 I can delete the pairing from the default dictionary and add them to a second dictionary for double bonds or something to that effect. Much more elegant. Thank you for the suggestion.

RyanG gravatar imageRyanG ( 2020-03-16 21:59:10 +0100 )edit

Oh, and to try to answer your original question: I don't know of any way to determine whether a line segment is present in a graphics object. I am not an expert in the graphics part of Sage, though.

John Palmieri gravatar imageJohn Palmieri ( 2020-03-16 23:22:50 +0100 )edit

I think this will be a much better approach and I imagine any answer to that question would have taken me down a much more complicated rabbit hole.

RyanG gravatar imageRyanG ( 2020-03-16 23:31:35 +0100 )edit

I have fixed the issue and updated the code. Thank you again.

RyanG gravatar imageRyanG ( 2020-03-17 04:19:36 +0100 )edit

1 Answer

Sort by ยป oldest newest most voted
1

answered 2020-03-16 23:10:00 +0100

RyanG gravatar image

updated 2020-03-17 04:40:13 +0100

Using John Palmieri's suggestion I created a default dictionary of single bonds and an empty dictionary for pi bonds:

When a pi bond pair is added to Pi_Dict, the pairing from Sigma_Dict is deleted

I was not aware that lists could be entered into dictionaries and this will make my work much easier.

Updated code:

import sys
from sage.plot.plot3d.shapes import LineSegment, Sphere
from sage.plot.colors import rainbow
from sage.plot.plot3d.implicit_plot3d import implicit_plot3d
from sage.plot.plot3d.shapes2 import text3d
from sage.repl.rich_output.pretty_print import show

Radii = {'O': 0.73, 'N': 0.75, 'C': 0.77, 'He': 0.32, 'H': 0.37, 'S': 1.02, 'Cl': 0.99, 
    'F': 0.71, 'Xe': 1.30, 'Si': 1.11, 'B': 0.82, 'P': 1.06, 'Br': 1.14}

Valency = {'O': 6, 'N': 5, 'C': 4, 'He': 0, 'H': 1, 'S': 6, 'Cl': 7, 
    'F': 7, 'Xe': 8, 'Si': 4, 'B': 3, 'P': 5, 'Br': 7}

Colors = {'O': 'red', 'N': 'green', 'C': 'black', 'He': 'cyan', 'H': 'white', 'S': 'yellow',
    'Si': 'purple', 'Xe': 'pink', 'F': 'blue', 'Cl': 'green', 'B': 'pink', 'P': 'orange',
    'Br': 'red'}

X = int(input('Enter x axis shift or press enter for default: '))

def distance(A,B):
    D = float(((B[0]-A[0])**2 + (B[1]-A[1])**2 + (B[2]-A[2])**2)**(1/2))
    return D
def Normalize(x):
    x = (float(x)/0.77)*0.20
    return x

O = (X,0,0)

A2 = (X, 1, 0.559)
A3 = (X, 1, -0.559)
A5 = (X, -1, -0.559)
A6 = (X, -1, 0.559)
A1 = (X, 0, distance(A2,A3))
A4 = (X, 0,-1*distance(A2,A3))

Sigma_Dict = { 'A1-A2' : [A1,A2], 'A2-A3' : [A2,A3], 'A3-A4' : [A3,A4], 'A4-A5' : [A4,A5], 'A5-A6' : [A5,A6], 'A1-A6' : [A1, A6] }

Pi_Dict = { 'A1-A2' : [A1,A2], 'A5-A6' : [A5,A6], 'A3-A4': [A3,A4] }

Atomic_Position = { A1: 'Br', A2: 'C', A3: 'Cl', A4: 'C', A5: 'B', A6: 'C' }

def bond_split(AxAy):
    x = AxAy[0]
    y = AxAy[1]
    return(x,y)

def pi_bond(Cx,Cy):
    X = 0.05
    if Cx[1] == Cy[1]:
        Ca = Cx[1]-X
        Cb = Cx[1]+X
        Cc = Cy[1]-X
        Cd = Cy[1]+X
        Cx1 = (Cx[0], Ca, Cx[2])
        Cx2 = (Cx[0], Cb, Cx[2])
        Cy1 = (Cy[0], Cc, Cy[2])
        Cy2 = (Cy[0], Cd, Cy[2])
        D = LineSegment(Cx1, Cy1, 1, color='white', axes=False, frame=False)
        D += LineSegment(Cx2, Cy2, 1, color='white', axes=False, frame=False)
        return (D)
    else:
        Ca = Cx[2]-X
        Cb = Cx[2]+X
        Cc = Cy[2]-X
        Cd = Cy[2]+X
        Cx1 = (Cx[0], Cx[1], Ca)
        Cx2 = (Cx[0], Cx[1], Cb)
        Cy1 = (Cy[0], Cy[1], Cc)
        Cy2 = (Cy[0], Cy[1], Cd)
        D = LineSegment(Cx1, Cy1, 1, color='white', axes=False, frame=False)
        D += LineSegment(Cx2, Cy2, 1, color='white', axes=False, frame=False)
        return (D)

Benzene = Sphere(.000001, color='white').translate(O)

for atom in Atomic_Position:
    Benzene += Sphere(Normalize(Radii[Atomic_Position[atom]]), color=Colors[Atomic_Position[atom]]).translate(atom)

for pi in Pi_Dict:
    if pi in Sigma_Dict:
        del Sigma_Dict[pi]

for pi in Pi_Dict:
    Benzene += pi_bond(bond_split(Pi_Dict[pi])[0],bond_split(Pi_Dict[pi])[1])

for bond in Sigma_Dict:
    Benzene += LineSegment(bond_split(Sigma_Dict[bond])[0],bond_split(Sigma_Dict[bond])[1], 1, color= 'white' )

show(Benzene)
edit flag offensive delete link more

Your Answer

Please start posting anonymously - your entry will be published after you log in or create a new account.

Add Answer

Question Tools

1 follower

Stats

Asked: 2020-03-16 20:22:28 +0100

Seen: 347 times

Last updated: Mar 17 '20