0 | module CyBy.Draw.Internal.Label
2 | import CyBy.Draw.Internal.Atom
3 | import CyBy.Draw.Internal.CoreDims
4 | import CyBy.Draw.Internal.Graph
13 | bgRadiusFactor : Double
14 | bgRadiusFactor = 0.7
17 | lineEndRadius : Double
28 | data HPos = NoH | N | W | S | E
45 | (.h) : Text a -> Double
46 | t.h = t.dims.capHeight
49 | (.lh) : Text a -> Double
50 | t.lh = t.dims.lineHeight
53 | (.w) : Text a -> Double
54 | t.w = t.dims.txtWidth
57 | Geom.Bounds.Bounded (Text $
Point Id) where
59 | bounds (T _ "" _ _) = neutral
60 | bounds (T _ _ (P x y) (TD _ cs w)) =
61 | BS (range (x-w/2) (x+w/2)) (range (y-cs/2) (y+cs/2))
66 | noLbl = T 0 "" () (TD 0 0 0)
70 | text : (cd : CoreDims) => (sub : Bool) -> (text : String) -> Text ()
73 | let fs := if sub then cd.subscriptSize else cd.fontSize
74 | in T fs s () $
cd.measure.measure fs cd.font s
78 | radius : Text a -> Maybe Double
79 | radius (T _ "" _ _) = Nothing
80 | radius (T _ _ _ $
TD _ cs w) = Just $
max cs w * bgRadiusFactor
82 | trans : Vector Id -> Double -> Maybe Double -> Point Id -> Point Id
83 | trans v l Nothing x = x
84 | trans v l (Just r) x = translate (scale (r / l) v) x
91 | -> (rx,ry : Maybe Double)
92 | -> Maybe (Point Id, Point Id)
93 | adjEndPoints x y rx ry =
96 | True := lv > fromMaybe 0 rx + fromMaybe 0 ry | False => Nothing
97 | in Just (trans (negate v) lv rx x, trans v lv ry y)
102 | (.textPos) : Text (Point Id) -> Point Id
105 | in P x (y + l.dims.capHeight / 2.0)
110 | record AtomLabels a where
119 | labels : AtomLabels a -> List (Text a)
120 | labels (AL v w x y z) = [v,w,x,y,z]
123 | Geom.Bounds.Bounded (AtomLabels $
Point Id) where
125 | bounds = foldMap bounds . labels
128 | chargeLabel : Charge -> String
130 | chargeLabel 1 = "+"
131 | chargeLabel (-
1) = "-"
133 | if n > 0 then "\{show n.value}+" else "\{show $ abs n.value}-"
136 | massLabel : Maybe MassNr -> String
137 | massLabel = maybe "" (show . value)
140 | hlabel : HCount -> String
145 | hsubscript : HCount -> String
146 | hsubscript h = if h > 1 then show h.value else ""
153 | setPositions : HPos -> Point Id -> AtomLabels () -> AtomLabels (Point Id)
154 | setPositions x p (AL s c m h hc) =
157 | N => case c.h > 0.0 && hc.h > 0.0 of
158 | True => vid 0 (negate $
s.h + (c.h + hc.h) / 2.0)
159 | False => vid 0 (negate $
s.h + s.lh)
161 | S => vid 0 (h.h + h.lh)
165 | W => vid (negate $
(s.w + h.w) / 2 + max hc.w m.w) 0
169 | _ => vid ((s.w + h.w) / 2) 0
172 | vm := vid (negate $
(s.w + m.w) / 2) (m.h / 2 - s.h)
176 | E => vid ((s.w + c.w) / 2 + h.w) (c.h / 2 - s.h)
177 | _ => vid ((s.w + c.w) / 2) (c.h / 2 - s.h)
180 | vhc := vid ((h.w + hc.w) / 2) (h.h / 2.0)
183 | sym := {pos := p} s
188 | hyd := {pos := translate vh p} h
192 | , charge = {pos := translate vc p} c
193 | , mass = {pos := translate vm p} m
195 | , hcount = {pos := translate vhc hyd.pos} hc
203 | data AbbrPos = AE | AW
206 | abbrTextPos : CoreDims => AbbrPos -> Point Id -> Text () -> Text (Point Id)
207 | abbrTextPos @{cd} AE (P x y) t =
208 | case t.w < 2*cd.radiusAtom of
209 | True => {pos := P x y} t
210 | False => {pos := P (x + t.w / 2.0 - cd.radiusAtom) y} t
211 | abbrTextPos @{cd} AW (P x y) t =
212 | case t.w < 2*cd.radiusAtom of
213 | True => {pos := P x y} t
214 | False => {pos := P (x - t.w / 2.0 + cd.radiusAtom) y} t
220 | firstAfter : Eq a => a -> List a -> a
222 | case break (v ==) vs of
228 | getListElems : String -> List Elem
229 | getListElems s = filter (isPrefixOf s . show) values
238 | updateElem : String -> Elem -> Elem
239 | updateElem s e = firstAfter e $
sortBy (comparing show) (getListElems s)
243 | updateIsotope : String -> Isotope -> Isotope
244 | updateIsotope s i = cast $
updateElem s i.elem
247 | masses : Elem -> List (Maybe MassNr)
248 | masses e = Nothing :: sort (map (Just . massNr) $
isotopes e)
250 | isoList : Elem -> List Isotope
251 | isoList el = MkI el <$> masses el
254 | incIso : Isotope -> Isotope
255 | incIso i = firstAfter i (isoList i.elem)
258 | decIso : Isotope -> Isotope
259 | decIso i = firstAfter i (reverse $
isoList i.elem)
266 | data Label : Type where
268 | NoLabel : Point Id -> Label
269 | Abbreviation : Point Id -> Text (Point Id) -> Label
270 | Explicit : AtomLabels (Point Id) -> Label
273 | 0 Labels : Nat -> Type
274 | Labels k = IArray k Label
276 | circleBounds : (cd : CoreDims) => Point Id -> Bounds2D Id
277 | circleBounds (P x y) =
278 | let r := cd.radiusAtom in BS (range (x-r) (x+r)) (range (y-r)(y+r))
281 | (cd : CoreDims) => Geom.Bounds.Bounded Label where
283 | bounds Hidden = neutral
284 | bounds (NoLabel p) = circleBounds p
285 | bounds (Explicit x) = bounds x
286 | bounds (Abbreviation _ x) = bounds x
298 | -> Vector (transform t)
302 | trimToCircle (P x y) (V vx vy) (P cx cy) r =
305 | in case solveQuadratic (vx*vx+vy*vy) (2*(dx*vx+dy*vy)) (dx*dx+dy*dy-r*r) of
307 | Just (s1,s2) => if s1 < 0 then if s2 >= 0 then 0 else 1 else min s1 1
309 | textFactor : Point Id -> Vector Id -> Text (Point Id) -> Double
313 | Just r => trimToCircle p v t.pos (r + lineEndRadius)
315 | abbrFactor : CoreDims => Point Id -> Vector Id -> Point Id -> Double
316 | abbrFactor @{cd} p v q = trimToCircle p v q (cd.radiusAtom + 4 * lineEndRadius)
318 | factor : CoreDims => Point Id -> Vector Id -> Label -> Double
319 | factor p v Hidden = 0
320 | factor p v (NoLabel _) = 1
321 | factor p v (Abbreviation q _) = abbrFactor p v q
322 | factor p v (Explicit ls) = foldl min 1 (textFactor p v <$> labels ls)
329 | -> (x,y : Point Id)
331 | -> Maybe (Point Id, Point Id)
332 | endpoints x y (NoLabel _) (NoLabel _) = Just (x,y)
333 | endpoints x y Hidden _ = Nothing
334 | endpoints x y _ Hidden = Nothing
335 | endpoints x y lx ly =
338 | fx := factor x vx ly
339 | fy := factor y vy lx
342 | else Just ( translate (scale (1-fy) vx) x
343 | , translate (scale (1-fx) vy) y
353 | abbrPos : CDIGraph k -> Fin k -> AbbrPos
355 | let [a] := bondAngles g x | _ => AE
356 | in if a <= halfPi || a >= (negate halfPi) then AW else AE
361 | bestHPos : List Geom.Angle.Angle -> HPos
363 | if all (\x => x >= halfPi && x <= threeHalfPi) xs then E
364 | else if all (\x => x <= halfPi || x >= threeHalfPi) xs then W
365 | else if all (\x => x < angle (5 * pi / 4) || x > angle (7 * pi / 4)) xs
368 | && count (> pi) xs < 2 then N
369 | else if all (\x => x > angle (3 * pi / 4) || x < angle (pi / 4)) xs then S
377 | hpos : CDIGraph k -> Fin k -> HPos
379 | case Atom.hydrogen . atom $
lab g x of
381 | _ => bestHPos (bondAngles g x)