summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorBrian Cully <bjc@kublai.com>2021-02-18 13:16:56 -0500
committerBrian Cully <bjc@kublai.com>2021-02-18 13:16:56 -0500
commit737b94c65bf275ce66df24f65dfbf418d4835902 (patch)
treedab07d0c85853e1a7228e08f9ff3e2e3f51f20ba
parent4bff68dd9c421b843b5d0c8b52e40bfc5f01f147 (diff)
downloadmolsim2-737b94c65bf275ce66df24f65dfbf418d4835902.tar.gz
molsim2-737b94c65bf275ce66df24f65dfbf418d4835902.zip
Initial pass at getting codon display working.
-rw-r--r--NOTES.org24
-rw-r--r--amino-acid.mjs132
-rw-r--r--codon.mjs47
-rw-r--r--genome.mjs33
-rw-r--r--index.html2
-rw-r--r--mobile.css27
6 files changed, 177 insertions, 88 deletions
diff --git a/NOTES.org b/NOTES.org
index 6be8975..c77b460 100644
--- a/NOTES.org
+++ b/NOTES.org
@@ -20,7 +20,10 @@ Continue to mutate for 10 rounds, though not all 10 of your sequences will survi
:PROPERTIES:
:header-args: :noweb yes
:END:
- Found [[https://en.wikipedia.org/wiki/DNA_and_RNA_codon_tables#Inverse_DNA_codon_table][on Wikipedia]]:
+
+The javascript code will be primarily concerned with translating codons into amino acids, so I want to create a javascript hash table (which is really just an object) in order to do the lookup. So first I need a table that describes those translations, which I can then manipulate in code to get the desired javascript object which maps a given codon to an amino acid.
+
+[[https://en.wikipedia.org/wiki/DNA_and_RNA_codon_tables#Inverse_DNA_codon_table][This table on Wikipedia]] is a good starting point, so I copied it into the table below. I also removed the redundancies, such as =Gln or Glu=, which had more specific definitions.
#+name: amino-acid-to-codon
| Amino acid | Codon |
@@ -47,6 +50,8 @@ Continue to mutate for 10 rounds, though not all 10 of your sequences will survi
| Val | GTT GTC GTA GTG |
| STOP | TAA TGA TAG |
+Now I take the raw table and translate it into a cons cell of ~(amino-acid . (list-of-codons))~ that is more natural in lisp. Since the =Codon= column above is a single string, I need to split it on white space into multiple codons.
+
#+name: aa-table-to-form
#+begin_src elisp :var raw-data=amino-acid-to-codon
(mapcar (lambda (kvp)
@@ -77,6 +82,8 @@ Continue to mutate for 10 rounds, though not all 10 of your sequences will survi
| Val | GTT | GTC | GTA | GTG | | |
| STOP | TAA | TGA | TAG | | | |
+The last thing that needs to be done for usable output is changing the data from a list of ~(amino-acid . (list-of-codons))~ into ~((codon . amino-acid) (codon . amino-acid) …)~ because the final target of this manipulation is going to be a =json= object in the form ~{ codon: amino-acid }~.
+
#+name: aa-table-inverted
#+begin_src elisp :var raw-data=amino-acid-to-codon
(let ((codon-alist (mapcar (lambda (aa-to-codons) (cons (cdr aa-to-codons) (car aa-to-codons)))
@@ -92,6 +99,8 @@ Continue to mutate for 10 rounds, though not all 10 of your sequences will survi
#+RESULTS: aa-table-inverted
: ((GCT . Ala) (GCC . Ala) (GCA . Ala) (GCG . Ala) (CGT . Arg) (CGC . Arg) (CGA . Arg) (CGG . Arg) (AGA . Arg) (AGG . Arg) (AAT . Asn) (AAC . Asn) (GAT . Asp) (GAC . Asp) (TGT . Cys) (TGC . Cys) (CAA . Gln) (CAG . Gln) (GAA . Glu) (GAG . Glu) (GGT . Gly) (GGC . Gly) (GGA . Gly) (GGG . Gly) (CAT . His) (CAC . His) (ATT . Ile) (ATC . Ile) (ATA . Ile) (CTT . Leu) (CTC . Leu) (CTA . Leu) (CTG . Leu) (TTA . Leu) (TTG . Leu) (AAA . Lys) (AAG . Lys) (ATG . Met) (TTT . Phe) (TTC . Phe) (CCT . Pro) (CCC . Pro) (CCA . Pro) (CCG . Pro) (TCT . Ser) (TCC . Ser) (TCA . Ser) (TCG . Ser) (AGT . Ser) (AGC . Ser) (ACT . Thr) (ACC . Thr) (ACA . Thr) (ACG . Thr) (TGG . Trp) (TAT . Tyr) (TAC . Tyr) (GTT . Val) (GTC . Val) (GTA . Val) (GTG . Val) (TAA . STOP) (TGA . STOP) (TAG . STOP))
+Now that the lisp data are organized correctly, it’s a simple matter of translating the =sexp= into =json= with some simple string manipulation.
+
#+name: tbl-to-json
#+begin_src elisp :var raw-data=amino-acid-to-codon
(let ((json-map (mapcar (lambda (kvp) (format "'%s': '%s'," (car kvp) (cdr kvp)))
@@ -169,6 +178,19 @@ Continue to mutate for 10 rounds, though not all 10 of your sequences will survi
}
#+end_example
+Finally, I need the complete amino acid list for a selector, so generate one from the initial table.
+
+#+name: tbl-to-aa-list
+#+begin_src elisp :var raw-data=amino-acid-to-codon :results raw
+ (let ((aa-strings (mapcar (lambda (aalist)
+ (format "’%s’" (car aalist)))
+ raw-data)))
+ (format "[%s]" (string-join aa-strings ", ")))
+#+end_src
+
+#+RESULTS: tbl-to-aa-list
+[’Ala’, ’Arg’, ’Asn’, ’Asp’, ’Cys’, ’Gln’, ’Glu’, ’Gly’, ’His’, ’Ile’, ’Leu’, ’Lys’, ’Met’, ’Phe’, ’Pro’, ’Ser’, ’Thr’, ’Trp’, ’Tyr’, ’Val’, ’STOP’]
+
* work steps
1. group nucleotides by codon
2. add amino acid selection area to codon group
diff --git a/amino-acid.mjs b/amino-acid.mjs
index 7df50d4..cce1e0e 100644
--- a/amino-acid.mjs
+++ b/amino-acid.mjs
@@ -1,76 +1,78 @@
class AminoAcid {
// Create a protein from three nucleotides.
constructor(n1, n2, n3) {
- this.codon = n1+n2+n3
- this.value = AminoAcid.codonMap[this.codon]
+ this.value = AminoAcid.codonMap[n1+n2+n3]
+ this._boundClickHandler = this.clickHandler.bind(this)
+ }
+
+ get elt() {
+ if (this._elt === undefined) {
+ this._elt = document.createElement('div')
+ this._elt.classList.add('amino-acid')
+ }
+ return this._elt
+ }
+
+ get value() {
+ return this.elt.innerText
+ }
+
+ set value(val) {
+ this.elt.innerText = val
+ }
+
+ lock() {
+ this.elt.removeEventListener('click', this._boundClickHandler)
+ }
+
+ unlock() {
+ this.elt.addEventListener('click', this._boundClickHandler)
+ }
+
+ get onClick() {
+ if (this._onClick !== undefined) {
+ return this._onClick
+ }
+ return () => {}
+ }
+
+ set onClick(fn) {
+ this._onClick = fn
+ }
+
+ clickHandler(evt) {
+ this.onClick(this)
}
}
+AminoAcid.list = [
+ 'Ala', 'Arg', 'Asn', 'Asp', 'Cys', 'Gln', 'Glu', 'Gly', 'His', 'Ile',
+ 'Leu', 'Lys', 'Met', 'Phe', 'Pro', 'Ser', 'Thr', 'Trp', 'Tyr', 'Val',
+ 'STOP'
+]
+
AminoAcid.codonMap = {
- 'TAA': 'STOP',
- 'TGA': 'STOP',
- 'TAG': 'STOP',
- 'GTT': 'Val',
- 'GTC': 'Val',
- 'GTA': 'Val',
- 'GTG': 'Val',
- 'TAT': 'Tyr',
- 'TAC': 'Tyr',
- 'TGG': 'Trp',
- 'ACT': 'Thr',
- 'ACC': 'Thr',
- 'ACA': 'Thr',
- 'ACG': 'Thr',
- 'TCT': 'Ser',
- 'TCC': 'Ser',
- 'TCA': 'Ser',
- 'TCG': 'Ser',
- 'AGT': 'Ser',
- 'AGC': 'Ser',
- 'CCT': 'Pro',
- 'CCC': 'Pro',
- 'CCA': 'Pro',
- 'CCG': 'Pro',
- 'TTT': 'Phe',
- 'TTC': 'Phe',
+ 'GCT': 'Ala', 'GCC': 'Ala', 'GCA': 'Ala', 'GCG': 'Ala',
+ 'CGT': 'Arg', 'CGC': 'Arg', 'CGA': 'Arg', 'CGG': 'Arg', 'AGA': 'Arg', 'AGG': 'Arg',
+ 'AAT': 'Asn', 'AAC': 'Asn',
+ 'GAT': 'Asp', 'GAC': 'Asp',
+ 'TGT': 'Cys', 'TGC': 'Cys',
+ 'CAA': 'Gln', 'CAG': 'Gln',
+ 'GAA': 'Glu', 'GAG': 'Glu',
+ 'GGT': 'Gly', 'GGC': 'Gly', 'GGA': 'Gly', 'GGG': 'Gly',
+ 'CAT': 'His', 'CAC': 'His',
+ 'ATT': 'Ile', 'ATC': 'Ile', 'ATA': 'Ile',
+ 'CTT': 'Leu', 'CTC': 'Leu', 'CTA': 'Leu', 'CTG': 'Leu', 'TTA': 'Leu', 'TTG': 'Leu',
+ 'AAA': 'Lys', 'AAG': 'Lys',
'ATG': 'Met',
- 'AAA': 'Lys',
- 'AAG': 'Lys',
- 'CTT': 'Leu',
- 'CTC': 'Leu',
- 'CTA': 'Leu',
- 'CTG': 'Leu',
- 'TTA': 'Leu',
- 'TTG': 'Leu',
- 'ATT': 'Ile',
- 'ATC': 'Ile',
- 'ATA': 'Ile',
- 'CAT': 'His',
- 'CAC': 'His',
- 'GGT': 'Gly',
- 'GGC': 'Gly',
- 'GGA': 'Gly',
- 'GGG': 'Gly',
- 'GAA': 'Glu',
- 'GAG': 'Glu',
- 'CAA': 'Gln',
- 'CAG': 'Gln',
- 'TGT': 'Cys',
- 'TGC': 'Cys',
- 'GAT': 'Asp',
- 'GAC': 'Asp',
- 'AAT': 'Asn',
- 'AAC': 'Asn',
- 'CGT': 'Arg',
- 'CGC': 'Arg',
- 'CGA': 'Arg',
- 'CGG': 'Arg',
- 'AGA': 'Arg',
- 'AGG': 'Arg',
- 'GCT': 'Ala',
- 'GCC': 'Ala',
- 'GCA': 'Ala',
- 'GCG': 'Ala',
+ 'TTT': 'Phe', 'TTC': 'Phe',
+ 'CCT': 'Pro', 'CCC': 'Pro', 'CCA': 'Pro', 'CCG': 'Pro',
+ 'TCT': 'Ser', 'TCC': 'Ser', 'TCA': 'Ser', 'TCG': 'Ser', 'AGT': 'Ser', 'AGC': 'Ser',
+ 'ACT': 'Thr', 'ACC': 'Thr', 'ACA': 'Thr', 'ACG': 'Thr',
+ 'TGG': 'Trp',
+ 'TAT': 'Tyr', 'TAC': 'Tyr',
+ 'GTT': 'Val', 'GTC': 'Val', 'GTA': 'Val', 'GTG': 'Val',
+ 'TAA': 'STOP', 'TGA': 'STOP', 'TAG': 'STOP',
}
export default AminoAcid
diff --git a/codon.mjs b/codon.mjs
new file mode 100644
index 0000000..7cc5ea1
--- /dev/null
+++ b/codon.mjs
@@ -0,0 +1,47 @@
+import AminoAcid from './amino-acid.mjs'
+import Nucleotide from './nucleotide.mjs'
+
+class Codon {
+ constructor(n1, n2, n3) {
+ this.bases = [n1, n2, n3]
+ this.aminoAcid = new AminoAcid(n1, n2, n3)
+
+ const nucleotideList = document.createElement('ol')
+ this.bases = [n1, n2, n3].map(base => {
+ const n = new Nucleotide(base)
+ n.onClick = this._boundNucleotideClickedHandler
+ nucleotideList.appendChild(n.elt)
+ return n
+ })
+ this.elt.appendChild(nucleotideList)
+ this.elt.appendChild(this.aminoAcid.elt)
+ }
+
+ get elt() {
+ if (this._elt === undefined) {
+ this._elt = document.createElement('li')
+ this._elt.classList.add('codon')
+ }
+ return this._elt
+ }
+
+ get aaElt() {
+ if (this._aaElt === undefined) {
+ this._aaElt = document.createElement('div')
+ this._aaElt.classList.add('amino-acid')
+ }
+ return this._aaElt
+ }
+
+ lock() {
+ this.bases.forEach(n => n.lock())
+ this.aminoAcid.lock()
+ }
+
+ unlock() {
+ this.bases.forEach(n => n.unlock())
+ this.aminoAcid.unlock()
+ }
+}
+
+export default Codon
diff --git a/genome.mjs b/genome.mjs
index fcab4ff..c90d10a 100644
--- a/genome.mjs
+++ b/genome.mjs
@@ -1,4 +1,4 @@
-import Nucleotide from './nucleotide.mjs'
+import Codon from './codon.mjs'
import Die from './die.mjs'
import { randomItem } from './utils.mjs'
@@ -9,17 +9,23 @@ class Genome {
}
}
- constructor(gen) {
- const nucleotideList = document.createElement('ol')
+ constructor(nucleotideGenerator) {
+ const codonList = document.createElement('ol')
this._boundNucleotideClickedHandler =
this.nucleotideClickedHandler.bind(this)
- this.nucleotides = [...gen].map(base => {
- const n = new Nucleotide(base)
- n.onClick = this._boundNucleotideClickedHandler
- nucleotideList.appendChild(n.elt)
- return n
+
+ this.codons = []
+ let tmpCodon = []
+ nucleotideGenerator.forEach(base => {
+ tmpCodon.push(base)
+ if (tmpCodon.length == 3) {
+ const c = new Codon(...tmpCodon)
+ codonList.appendChild(c.elt)
+ this.codons.push(c)
+ tmpCodon = []
+ }
})
- this.elt.appendChild(nucleotideList)
+ this.elt.appendChild(codonList)
this.lock()
}
@@ -44,16 +50,16 @@ class Genome {
lock() {
this.elt.classList.add('locked')
- this.nucleotides.forEach(n => n.lock())
+ this.codons.forEach(n => n.lock())
}
unlock() {
this.elt.classList.remove('locked')
- this.nucleotides.forEach(n => n.unlock())
+ this.coons.forEach(n => n.unlock())
}
clone() {
- return new Genome(this.nucleotides.map(n => n.value))
+ return new Genome(this.codons.map(c => c.value))
}
get selectedNucleotide() {
@@ -75,6 +81,7 @@ class Genome {
this.selectedNucleotide = nucleotide
}
}
-Genome.size = 18
+// Size of the genome in codons.
+Genome.size = 6
export default Genome
diff --git a/index.html b/index.html
index bc05cad..cb6137f 100644
--- a/index.html
+++ b/index.html
@@ -18,7 +18,7 @@
</head>
<body>
- <ol id='genome-history' start='0'>
+ <ol id='genome-history' start='0'></ol>
</ol>
<div id='instructions'>
diff --git a/mobile.css b/mobile.css
index a6ddd29..0f7f87b 100644
--- a/mobile.css
+++ b/mobile.css
@@ -130,23 +130,36 @@ body {
margin: 0.5ex;
}
+.genome>ol {
+ padding: 0;
+}
+
.genome.locked {
cursor: text
}
-.genome>ol {
- display: inline-flex;
- flex-wrap: wrap;
- padding: 0;
+.genome .codon {
+ display: inline-block;
+ margin: 0 0.5ex;
border: 1px solid black;
background-color: white;
}
+.genome .codon>ol {
+ padding: 1ex 0.5em;
+}
+
+.genome .codon .amino-acid {
+ border-top: 1px solid black;
+ text-align: center;
+ padding-top: 0.5ex;
+ padding-bottom: 0.5ex;
+}
+
.genome .nucleotide {
display: inline-block;
- height: 32px;
- width: 32px;
+ width: 28px;
font-size: 18px;
text-align: center;
flex-wrap: wrap;
@@ -156,6 +169,4 @@ body {
.genome .nucleotide span {
display: inline-block;
- padding-top: 5px;
- padding-bottom: 5px;
}