0 | ||| The geometry of molecules
  1 | module Geom.Mol
  2 |
  3 | import Text.Molfile
  4 |
  5 | import Geom.Angle
  6 | import Geom.Bounds
  7 | import Geom.Point
  8 | import Geom.Scale
  9 | import Geom.Vector
 10 |
 11 | %default total
 12 |
 13 | ||| Desired bond length in a .mol file in Angstrom
 14 | public export
 15 | BondLengthInAngstrom : Scale
 16 | BondLengthInAngstrom = 1.25
 17 |
 18 | ||| Desired bond length in the UI
 19 | public export
 20 | BondLengthInPixels : Scale
 21 | BondLengthInPixels = 20
 22 |
 23 | ||| Scaling factor when drawing molecules.
 24 | |||
 25 | ||| Note: This must not be applied as part of the overall transformations of
 26 | |||       the canvas when drawing the molecule, because this would also scale
 27 | |||       the bond thickness and font sizes.
 28 | |||
 29 | |||       The points corresponding to the atoms in a molecule are therefore
 30 | |||       indexed by the inverse of this scaling factor. That way, when we
 31 | |||       convert a molecule to a canvas `Scene` - where we will always
 32 | |||       require `Point Id`s for the positions - the correct scaling happens
 33 | |||       automatically.
 34 | public export
 35 | ScalingFactor : Scale
 36 | ScalingFactor = BondLengthInPixels / BondLengthInAngstrom
 37 |
 38 | ||| Describes the affine space a molecule loaded from a molfile
 39 | ||| (and after normalization, see `normalizeMol`) lives in.
 40 | |||
 41 | ||| See also @ScalingFactor for some more details.
 42 | export
 43 | Mol : AffineTransformation
 44 | Mol = AT (scaling $ inverse ScalingFactor) vzero
 45 |
 46 | public export
 47 | 0 MolPoint : Type
 48 | MolPoint = Point Mol
 49 |
 50 | public export
 51 | 0 MolVector : Type
 52 | MolVector = Vector (transform Mol)
 53 |
 54 | --------------------------------------------------------------------------------
 55 | --          Atom Position
 56 | --------------------------------------------------------------------------------
 57 |
 58 | toCoord : Double -> Maybe Coordinate
 59 | toCoord d = refineCoordinate (cast $ d * cast Precision)
 60 |
 61 | ||| Adjust the 3-D coordinates of an atom by setting the x- and y-
 62 | ||| coordinate from the given `Point Mol`.
 63 | export
 64 | toCoords : Point Mol -> Vect 3 Coordinate -> Vect 3 Coordinate
 65 | toCoords (P x y) cs@[_,_,z] =
 66 |   let Just cx := toCoord x          | Nothing => cs
 67 |       Just cy := toCoord (negate y) | Nothing => cs
 68 |    in [cx,cy,z]
 69 |
 70 | public export
 71 | GetPoint (Vect 3 Coordinate) where
 72 |   gtrans = Mol
 73 |   point [x,y,_] = P (cast x) (negate $ cast y)
 74 |
 75 | public export
 76 | ModPoint (Vect 3 Coordinate) where
 77 |   mtrans = Mol
 78 |   modPoint f cs = toCoords (f $ point cs) cs
 79 |
 80 | public export
 81 | GetPoint MolAtom where
 82 |   gtrans = Mol
 83 |   point = point . position
 84 |
 85 | public export
 86 | ModPoint MolAtom where
 87 |   mtrans = Mol
 88 |   modPoint f = {position $= modPoint f}
 89 |
 90 | public export
 91 | GetPoint MolAtomAT where
 92 |   gtrans = Mol
 93 |   point = point . position
 94 |
 95 | public export
 96 | ModPoint MolAtomAT where
 97 |   mtrans = Mol
 98 |   modPoint f = {position $= modPoint f}
 99 |
100 | public export
101 | (m : ModPoint a) => ModPoint (Graph b a) where
102 |   mtrans = mtrans @{m}
103 |   modPoint = map . modPoint
104 |
105 | public export
106 | {k : _} -> (m : ModPoint a) => ModPoint (IGraph k b a) where
107 |   mtrans   = mtrans @{m}
108 |   modPoint = map . modPoint
109 |
110 | --------------------------------------------------------------------------------
111 | --          Bond Lengths and Normalization
112 | --------------------------------------------------------------------------------
113 |
114 | ||| Calculate the length of an edge in a molecule
115 | export
116 | bondLength : GetPoint a => {k : _} -> IGraph k b a -> Edge k b -> Double
117 | bondLength g (E x y _) = distance (point $ lab g x) (point $ lab g y)
118 |
119 | ||| Calculate the average length of bonds in a molecule.
120 | export
121 | averageBondLength : GetPoint a => {k : _} -> IGraph k b a -> Maybe Double
122 | averageBondLength g = case edges g of
123 |   [] => Nothing
124 |   es => Just $ sum (bondLength g <$> es) / cast (length es)
125 |
126 | ||| Normalize a molecule to an average bond length of 1.25 Angstrom.
127 | export
128 | normalizeMol :
129 |      {auto mod : ModPoint a}
130 |   -> {auto get : GetPoint a}
131 |   -> {k : _}
132 |   -> {auto 0 mp : mtrans @{mod} === Mol}
133 |   -> {auto 0 gp : gtrans @{get} === Mol}
134 |   -> IGraph k b a
135 |   -> IGraph k b a
136 | normalizeMol g = case averageBondLength g of
137 |   Nothing => g
138 |   Just v  => scale (BondLengthInAngstrom / scale v) g
139 |