index.html
luaa year ago
<!DOCTYPE html><html><head><meta name="viewport" content="width=device-width, initial-scale=1.0"><meta charset="UTF-8">
<title>mini map aide - display protein electron density maps on mobile phone browsers - PDB and CCP4 map file viewer for proteins, nucleotides and ligands</title>
<script src='https://minimapai.de/fengari_web.js' type="text/javascript" async></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/three.js/0.155.0/three.min.js"></script>
<script src="https://minimapai.de/surfacenets.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/mathjs/10.1.0/math.min.js"></script>
<style>
#topbar {background-color:dodgerblue; position:relative; line-height:30px; font-size:22px; text-align:center; color:white;}
body {margin:auto; font-size:16px; font-family:arial; color:white; background-color:#0C0A0A;}
p {margin:0px;}
abbr{border-bottom:none !important; cursor:inherit !important; text-decoration:none !important;}
button:active {color:gray;}
</style></head><body>
<div id="topbar"><p>
<abbr title="Link to help file."><a href="minimaphelp.html" target="_blank"><button>About Mini Map Aide</button></a></abbr>
<abbr title="The start point in the sequence. This is an internal index for the residues and is not necessarily the same as the residue number in the PDB file."><label for="position" style="margin-left: 10px;">
Go to: </label><input type="number" min="-999" max="9999" step="1" value="1" id="position" name="position" size="4"></abbr>
<abbr title="The contour level in sigma or rms units. Change sign with +/- button."><label for="densign" style="margin-left: 10px;">
Contour level: <button id="densign" style="margin-left: 10px;">+/-</button></label><input type="number" min="-20" max="20" step="0.1" value="1" id="contour" name="contour" size="4"></abbr>
<abbr title="Select the PDB file."><span style="display:inline-block;"><label for="pdbInput" style="margin-left: 10px;">PDB: </label><input type="file" id="pdbInput" style="width:180px;"></span></abbr>
<abbr title="Select CCP4 map or MTZ file from refinement."><span style="display:inline-block;"><label for="mapInput" style="margin-left: 10px;">Map: </label><input type="file" id="mapInput" style="width:180px;" disabled></span></abbr>
<span style="display:inline-block;"><abbr title="Click to view outer part of sidechain or nucleotide base."><button id="viewsidechain" style="margin-left: 10px;" disabled>Side chain</button></abbr>
<abbr title="Swap between animation and scrolling."><button id="stop" style="margin-left: 10px;" disabled>Scroll</button></abbr>
<abbr title="Move to previous residue."><button id="back" style="margin-left: 10px;" disabled>Last residue</button></abbr>
<abbr title="Move to next residue."><button id="step" style="margin-left: 10px;" disabled>Next residue</button></abbr></span></div>
<div id="graphics" style="text-align: center">
<div><span id="info">Select a PDB file</span>
<span style="position: absolute; left: 0;"><abbr title="Tick this box to display symmetry mates. You need to click the Update button in order for this to take effect. The first time this option is used will be slow as the symmetry mates are calculated but it will be quicker after that."><input type="checkbox" id="symmetry" style="margin-top:20px; margin-left: 5px;" disabled>Symmetry</abbr>
<abbr title="Mutate."><select name="mutate" id="mutate" style="margin-top:20px; margin-left: 5px;" disabled><option value="">Mutate</option><option value="ALA">ALA</option><option value="ARG">ARG</option><option value="ASN">ASN</option><option value="ASP">ASP</option><option value="CYS">CYS</option><option value="GLN">GLN</option><option value="GLU">GLU</option><option value="GLY">GLY</option><option value="HIS">HIS</option><option value="ILE">ILE</option><option value="LEU">LEU</option><option value="LYS">LYS</option><option value="MET">MET</option><option value="PHE">PHE</option><option value="PRO">PRO</option><option value="SER">SER</option><option value="THR">THR</option><option value="TRP">TRP</option><option value="TYR">TYR</option><option value="VAL">VAL</option></select></abbr></span>
<span style="position: absolute; right: 0;"><abbr title="Click to move atoms in the residue at the centre of the display. The moveable atoms will then appear green and can be clicked and dragged. The selected atom is enlarged momentarily to indicate which one is moving."><button id="move" style="margin-top:20px; margin-right: 5px;" disabled>Move</button></abbr><abbr title="Tidy up the geometry of the residue after the atoms have been moved. This is a bit approximate and does not use the electron density map at all. It may be repeated if necessary."><button id="tidy" style="margin-right: 5px;" disabled>Tidy</button></abbr><abbr title="Undo the move and tidy operations on this residue."><button id="undo" style="margin-right: 5px;" disabled>Undo</button></abbr><abbr title="Save the changes in a new PDB file. It is *important* to do this before closing the browser tab if any of the changes made during the session are still wanted for future use!"><button id="save" style="margin-right: 5px;" disabled>Save</button></abbr></span></div>
<div id="myCanvas" style="padding: 0; max-width:100%; height:auto;"></div></div>
<script type="application/lua">
-- |d|e|f|a|u|l|t|s| |f|o|r| |m|i|n|i| |m|a|p| |a|i|d|e| v11feb25
mapcolpos="dodgerblue" mapcolneg="red" bondcol="#FFD633" altbondcol="#0FFF50" maxbondlen=2.0 maxssbondlen=3.0 boxrad=6.5 maprad=4.2
local js = require "js"
local window = js.global
local threejs = window.THREE
local mathjs = window.math
local document = window.document
canvas=document:getElementById("myCanvas")
el1=mathjs:index(0) el2=mathjs:index(1) el3=mathjs:index(2)
width=window.innerWidth
height=window.innerHeight
ratio=width/height
renderer=js.new(threejs.WebGLRenderer)
camera=js.new(threejs.PerspectiveCamera,20,ratio,30,50)
--camera.position:set(0,0,10)
camera.position.z=40
--camp=camera.position print(camp.x,camp.y,camp.z)
scene=js.new(threejs.Scene)
scene:add(camera)
renderer:setSize(width,height)
canvas:appendChild(renderer.domElement)
material=js.new(threejs.LineBasicMaterial)
material.color:set(bondcol)
material.linewidth=2
altmat=js.new(threejs.LineBasicMaterial)
altmat.color:set(altbondcol)
altmat.linewidth=2
matermap=js.new(threejs.LineBasicMaterial)
matermap.linewidth=1
light=js.new(threejs.PointLight,0xFFFFFF)
light.position:set(50, 50, 50);
scene:add(light)
xworld = js.new(threejs.Vector3, 1, 0, 0 )
yworld = js.new(threejs.Vector3, 0, 1, 0 )
mouseVector = js.new(threejs.Vector2)
projector = js.new(threejs.Raycaster)
mouse_x,mouse_y,twang=0,0,0 moving=false spin=true viewSideCh=false foundSideCen=false sideAtGrk={"E","D","G","B"} --ncyc=0 maxdiff=100 --last 2 normally commented out
function mouseDown(event)
MouseDown = true
mouseX = event.offsetX
mouseY = event.offsetY
get_moving_atom()
end
function touchStart(event)
event:preventDefault() -- needed to stop browser reading swipe events
MouseDown = true
mouseX = event.touches[0].pageX - event.touches[0].target.offsetLeft -- custom event.offsetX,Y needed for mobile
mouseY = event.touches[0].pageY - event.touches[0].target.offsetTop
get_moving_atom()
end
function mouseMove(event)
if MouseDown==true then
deltaX = (event.offsetX - mouseX)
deltaY = (event.offsetY - mouseY)
if moving and intersect[0]~=nil then
mouse_x=5*(deltaX)/width
mouse_y=5*(-deltaY)/height
dist=math.sqrt(mouse_x^2+mouse_y^2)
if dist>0.1 then update_orig_coords() mouseX=event.offsetX mouseY=event.offsetY end
else
group:rotateOnWorldAxis(xworld,deltaY/20000) --Swapped deliberately. Rotating the group on world axes gives more predictable mouse/touch behaviour.
group:rotateOnWorldAxis(yworld,deltaX/20000)
end
renderer:render(scene, camera)
end
end
function touchMove(event)
if MouseDown==true then
event:preventDefault() -- needed to stop browser reading swipe events
deltaX = (event.touches[0].pageX - event.touches[0].target.offsetLeft - mouseX)/200 -- custom event.offsetX,Y needed for mobile
deltaY = (event.touches[0].pageY - event.touches[0].target.offsetTop - mouseY)/200
if moving and intersect[0]~=nil then
mouse_x=500*(deltaX)/width
mouse_y=500*(-deltaY)/height
dist=math.sqrt(mouse_x^2+mouse_y^2)
if dist>0.1 then update_orig_coords() mouseX=event.touches[0].pageX-event.touches[0].target.offsetLeft mouseY=event.touches[0].pageY-event.touches[0].target.offsetTop end
else
group:rotateOnWorldAxis(xworld,deltaY/40) -- swapped deliberately
group:rotateOnWorldAxis(yworld,deltaX/40)
end
renderer:render(scene, camera)
end
end
function mouseUp(event)
MouseDown = false
if moving and intersect[0]~=nil then update_orig_coords() end
end
function touchEnd(event) mouseUp(event) end
function get_moving_atom()
mouseVector.x = 2 * (mouseX / width) - 1
mouseVector.y = 1 - 2 * ( mouseY / height )
--print(mouseVector.x,mouseVector.y)
if moving then
projector:setFromCamera( mouseVector, camera )
intersect = projector:intersectObject( moveables, true ) --only moveables are clickable
if intersect[0]~=nil then
--print("intersect object x:",intersect[0].object.position.x)
movatoms=intersect[0].object
movatom=movatoms.name --print(movatom)
--print("movatoms:",window.console:log(movatoms))
--movatoms.material.color:set("white")
movatoms.material.size=20
end
end
end
function startmove()
moving=true
document:getElementById("tidy").disabled=false
document:getElementById("undo").disabled=false
document:getElementById("save").disabled=false
resisafe={} for i,j in pairs(protein[startpos]) do resisafe[i]={} for k,l in pairs(j) do resisafe[i][k]=l end end
move()
end
function move()
moveables = js.new(threejs.Group)
vecmov=js.new(threejs.Vector3,0,0,0)
geomov=js.new(threejs.BufferGeometry)
for k,l in pairs(protein[startpos]) do
matermov=js.new(threejs.PointsMaterial)
matermov.size=8
matermov.sizeAttenuation=false
matermov.color:set("#0FFF50")
vecmove=window:Array(vecmov:set(l.x-xcent,l.y-ycent,l.z-zcent))
geomove=geomov:clone()
geomove:setFromPoints(vecmove)
meshmove=js.new(threejs.Points,geomove,matermov)
meshmove.name=k
moveables:add(meshmove)
end
group:add(moveables)
renderer:render(scene, camera)
geomov:dispose() geomove:dispose() matermov:dispose() --RAM housekeeping
end
function update_orig_coords()
rotamat=group.matrix --grab the group rotation matrix
rotmat=rotamat.elements --N.B. three.js uses the transposed rotation matrix.
invrot1=window:Array(rotmat[0],rotmat[1],rotmat[2])
invrot2=window:Array(rotmat[4],rotmat[5],rotmat[6])
invrot3=window:Array(rotmat[8],rotmat[9],rotmat[10])
invrot=window:Array(invrot1,invrot2,invrot3)
transprot=js.new(mathjs.matrix,invrot)
mouarr=window:Array(mouse_x,mouse_y,0)
munrot=mathjs:multiply(transprot,mouarr)
munrotx=(mathjs:subset(munrot,el1))
munroty=(mathjs:subset(munrot,el2))
munrotz=(mathjs:subset(munrot,el3))
--print("Unrotated shifts:",munrotx,munroty,munrotz)
protein[startpos][movatom].x=protein[startpos][movatom].x+munrotx
protein[startpos][movatom].y=protein[startpos][movatom].y+munroty
protein[startpos][movatom].z=protein[startpos][movatom].z+munrotz
group:remove(moveables)
move()
end
function save()
format_pdb_normal="%s%5i %-4s%3s%2s%-4s %8.3f%8.3f%8.3f%s\n"
format_pdb_metals="%s%5i %-4s %3s%2s%-4s %8.3f%8.3f%8.3f%s\n"
pdbOutputText=crystline
nAt=1
for i,res in ipairs(protein) do
for j,atom in pairs(res) do
if #j==3 then format_pdb=format_pdb_normal else format_pdb=format_pdb_metals end --handle left shift of metals
pdbOutputLine=string.format(format_pdb,"ATOM ",nAt,j,atom.res,atom.chain,atom.num,atom.x,atom.y,atom.z," 1.00 10.00")
pdbOutputText=pdbOutputText..pdbOutputLine
nAt=nAt+1
end
end
a=document:createElement("a") --create imaginary download button
content=window:Array(pdbOutputText)
contentType=js.new(window.Object)
contentType["type"]="text/plain"
savefile=js.new(window.Blob,content,contentType)
a.href=window.URL:createObjectURL(savefile)
a.download=pdbinput.files[0].name
a:click()
--print("a:",window.console:log(a))
window.URL:revokeObjectURL(a.href)
moving=false
rotamat=group.matrix --grab the group rotation matrix
scene:remove(group)
readform()
getBoxAtoms()
getMapBox()
group:add(meshmap)
renderer:render(scene, camera)
document:getElementById("save").disabled=true
end
function undo()
protein[startpos]=resisafe
run()
end
function run() --redraw
document:getElementById("info").innerHTML="Please wait"
if group~=nil then window:setTimeout(function() run2() end, 1) end --1 msec delay to let the DOM display the message
end
function run2()
rotamat=group.matrix --grab the group rotation matrix
scene:remove(group)
readform()
getBoxAtoms()
if minimap~=nil then getMapBox() end
document:getElementById("mutate").disabled=false
document:getElementById("move").disabled=false
document:getElementById("tidy").disabled=true
document:getElementById("undo").disabled=true
document:getElementById("save").disabled=true
end
function rerun() --redraw after tidy
rotamat=group.matrix --grab the group rotation matrix
scene:remove(group)
readform()
getBoxAtoms()
if minimap~=nil then getMapBox() end
document:getElementById("save").disabled=false
end
function step(sign)
rotamat=group.matrix --grab the group rotation matrix
scene:remove(group)
document:getElementById("tidy").disabled=true
document:getElementById("undo").disabled=true
document:getElementById("save").disabled=true
oldstartpos=startpos
startpos=startpos+sign*1
if startpos<1 or startpos>#protein then startpos=oldstartpos end
startRes=protein[startpos]
if startRes~=nil then _,firstAtom=next(startRes)
firstAtomResNum=string.sub(firstAtom.num,1,-2) firstAtomResNum=tonumber(firstAtomResNum) --remove insertion code so resnum is a number
position.value=firstAtomResNum
chainbox.value=firstAtom.chain
end
getBoxAtoms()
if minimap~=nil then getMapBox() end
end
function stop() --stop/start animation
if spin==true then
spin=false
renderer:setAnimationLoop()
document:getElementById("stop").innerHTML="Roll"
else
spin=true
renderer:setAnimationLoop(twirl)
document:getElementById("stop").innerHTML="Scroll"
end
end
function readfile()
scene:remove(group)
pdbfile=pdbinput.files[0]
--print(pdbfile.name)
reader=js.new(window.FileReader)
--reader.onload=function() print(reader.result) end
reader.onload=readatoms
reader:readAsText(pdbfile)
end
function readcell()
a=crystline:sub(7,15) a=tonumber(a)
b=crystline:sub(16,24) b=tonumber(b)
c=crystline:sub(25,33) c=tonumber(c)
alpha_deg=crystline:sub(34,40) alpha_deg=tonumber(alpha_deg)
beta_deg=crystline:sub(41,47) beta_deg=tonumber(beta_deg)
gamma_deg=crystline:sub(48,54) gamma_deg=tonumber(gamma_deg)
if a==nil or b==nil or c==nil or alpha_deg==nil or beta_deg==nil or gamma_deg==nil then print("Incomplete unit cell parameters.") readcell_ok=false --^&
else
sg=crystline:sub(56,65)
print("Space group: "..sg.."Unit cell: a="..a..", b="..b..", c="..c..", alpha="..alpha_deg..", beta="..beta_deg..", gamma="..gamma_deg)
if sg=="P 1 " then
symmetry_ok()
else
symmetry=symops[sg]
if symmetry==nil then print("Space group not recognised.") readcell_ok=false
else
sg_rotations=symmetry.rotas
sg_translations=symmetry.trans
symmetry_ok() end
end
end --^&
end
function symmetry_ok() fractorth() readcell_ok=true document:getElementById("symmetry").disabled=false end
function fractorth()
alpha=math.rad(alpha_deg) beta=math.rad(beta_deg) gamma=math.rad(gamma_deg)
cos_alpha=math.cos(alpha) cos_beta=math.cos(beta) cos_gamma=math.cos(gamma)
cos_alpha_star=(cos_beta*cos_gamma-cos_alpha)/(math.sin(beta)*math.sin(gamma))
sin_alpha_star=math.sqrt(1-cos_alpha_star^2)
cos_delta=-math.sin(beta)*cos_alpha_star
cos_epsilon=math.sin(beta)*sin_alpha_star
L1=window:Array(a,b*cos_gamma,c*cos_beta)
L2=window:Array(0,b*math.sin(gamma),-c*math.sin(beta)*cos_alpha_star)
L3=window:Array(0,0,c*math.sin(beta)*sin_alpha_star)
La=window:Array(L1, L2, L3)
L=js.new(mathjs.matrix,La)
frorthlog="Orthogonalisation matrix\n"
for i=0,2 do for j=0,2 do frorthlog=frorthlog.." "..string.format("%14.9f",L:subset(mathjs:index(i, j))) end frorthlog=frorthlog.."\n" end
Linv=mathjs:inv(L) --fractionalisation matrix
frorthlog=frorthlog.."Fractionalisation matrix\n"
for i=0,2 do for j=0,2 do frorthlog=frorthlog.." "..string.format("%14.9f",Linv:subset(mathjs:index(i, j))) end frorthlog=frorthlog.."\n" end
print(frorthlog)
end
function readatoms() --reads all atoms into table protein={res1,res2,..}
nAt,nCA,oldresnum,xtot,ytot,ztot=0,0,0,0,0,0 protein,residue,chains={},{},{} chainold="" --residue={["CA "]={["res"]=resnam,["num"]=resnum,["x"]=3.1,["y"]=..},["CB "]={},..}
Xmin,Ymin,Zmin,Xmax,Ymax,Zmax=math.huge,math.huge,math.huge,-math.huge,-math.huge,-math.huge
filetext=reader.result
for line in filetext:gmatch("[^\r\n]+") do
if line:sub(1,6)=="CRYST1" then crystline=line.."\n" readcell()
elseif line:sub(1,6)=="ATOM " or line:sub(1,6)=="HETATM" then
atnam=line:sub(14,16)
altgrp=line:sub(17,17) --print(altgrp)
attype=atnam:sub(1,1)
metal=line:sub(13,13) if metal~=" " then if metal=="C" then metal="X" end atnam=metal..atnam attype=metal..attype end --remove left shift of heavier elements and long H-atom names, replace "C" in metals with "X" to allow atom drawing
--if attype~="H" and attype~="G" and attype~="D" and attype~="E" and attype~="Z" then --omit H's and long H-atom names
if attype~="H" then --omit H atoms
resnam=line:sub(18,20)
chain=line:sub(21,22)
resnum=line:sub(23,27)
if resnum~=oldresnum or chain~=chainold then oldresnum=resnum if nAt>0 then table.insert(protein,residue) residue={} end end
if chain~=chainold then
uniqch=true
for i,j in ipairs(chains) do if chain==j then uniqch=false end end
if uniqch then table.insert(chains,chain) print("Chain:", chain) end
chainold=chain
end
x_i=tonumber(line:sub(28,38))
y_i=tonumber(line:sub(39,46))
z_i=tonumber(line:sub(47,54))
x_o=x_i y_o=y_i z_o=z_i
if atnam=="CA " then xtot=xtot+x_o ytot=ytot+y_o ztot=ztot+z_o nCA=nCA+1 end
if x_o>Xmax then Xmax=x_o end if y_o>Ymax then Ymax=y_o end if z_o>Zmax then Zmax=z_o end
if x_o<Xmin then Xmin=x_o end if y_o<Ymin then Ymin=y_o end if z_o<Zmin then Zmin=z_o end
atom={["res"]=resnam,["num"]=resnum,["chain"]=chain,["x"]=x_o,["y"]=y_o,["z"]=z_o,["nsymop"]={0},["postrot_shift"]={0,0,0}}
if altgrp~=" " then atnam=atnam..altgrp end
residue[atnam]=atom
nAt=nAt+1
if nAt==1 then firstAtomResNum=resnum:sub(1,-2) firstAtomResNum=tonumber(firstAtomResNum) end
end
end
end
table.insert(protein,residue) -- this line only applies to last residue
--print("Number of atoms read: ", n," Last residue number: ",resnum,"\n")
document:getElementById("stop").disabled=false
document:getElementById("step").disabled=false
document:getElementById("back").disabled=false
document:getElementById("viewsidechain").disabled=false
document:getElementById("mapInput").disabled=false
--document:getElementById("info").innerHTML="Select CCP4 map"
position:insertAdjacentHTML("beforebegin","<input id=\"chain\" name=\"chain\" size=\"2\">")
chainbox=document:getElementById("chain")
chainbox:addEventListener("change", run) --add callback to the chain box
--document:getElementById("position").value=firstAtomResNum
--for i,res in ipairs(protein) do print("Residue:",i) for j,k in pairs(res) do print("Atom:",j) for l,m in pairs(k) do print(l,m) end end end
--for i,res in ipairs(protein) do _,atm=next(res) print(atm.chain,atm.num) end
getBoxAtoms()
end
function getCentre()
print("Num residues, current index:",#protein,startpos)
if protein[startpos]==nil then getStartPos() end
_,firstAtom=next(protein[startpos])
firstAtomResNum=string.sub(firstAtom.num,1,-2) firstAtomResNum=tonumber(firstAtomResNum) --remove insertion code so resnum is a number
position.value=firstAtomResNum
chainbox.value=firstAtom.chain
if protein[startpos]["CA "]~= nil then centrat=protein[startpos]["CA "]
--else for i,j in pairs(protein[startpos]) do centrat=protein[startpos][i] break end -- if no CA, iterator takes first atom
else centrat=firstAtom -- if no CA, next takes first atom
end
if viewSideCh==true then
for m,n in ipairs(sideAtGrk) do
for i,j in pairs(protein[startpos]) do
if i:sub(2,2)==n or i:sub(2,3)=="5 " then -- go up to 4 steps along a side chain or find atom 5 of base
centrat=protein[startpos][i]
foundSideCen=true
break
end
end
if foundSideCen==true then break end
end
end
--print(centrat.x,centrat.y,centrat.z)
xcent,ycent,zcent=centrat.x,centrat.y,centrat.z
xmin,xmax,ymin,ymax,zmin,zmax=centrat.x-boxrad,centrat.x+boxrad,centrat.y-boxrad,centrat.y+boxrad,centrat.z-boxrad,centrat.z+boxrad
document:getElementById("info").innerHTML=centrat.res.." "..centrat.num..centrat.chain
end
function getBonds(thing)
box={}
for i,res in ipairs(thing) do --##
residue={}
for j,atom in pairs(res) do
--print(j,atom.x)
if xmin<=atom.x and atom.x<=xmax and ymin<=atom.y and atom.y<=ymax and zmin<=atom.z and atom.z<=zmax then
--print("Inside box:",atom.res,atom.num,j)
residue[j]=atom
end
end
if next(residue)~=nil then table.insert(box,residue) end
end
extrabox={} -- now only for Cys SG atoms
for i,res in ipairs(box) do --## gets the usual bonds
bonds={} cys={} jj=0
for j,atom in pairs(res) do
jj=jj+1 kk=0 --kk must be reset here (bug found 11aug23)
if j=="SG " then cys["SG "]=atom table.insert(extrabox,cys) end -- SG's added to extrabox
for k,atom2 in pairs(res) do
other_part=false
kk=kk+1
if kk>jj then
--print("j=",j,"k=",k,"jj,kk",jj,kk)
--if #k==4 and #j==4 then print("kj=",k[4],j[4]) if string.sub(k,4)~=string.sub(j,4) then other_part=true end end
if #k==4 and #j==4 then if string.sub(k,4)~=string.sub(j,4) then other_part=true end end
d=math.sqrt((atom.x-atom2.x)^2+(atom.y-atom2.y)^2+(atom.z-atom2.z)^2)
if not other_part and d<maxbondlen then
bond={{atom.x,atom.y,atom.z},{atom2.x,atom2.y,atom2.z}}
table.insert(bonds,bond)
end
end
end
end
if res["C "]~=nil and i<#box then if box[i+1]["N "]~=nil then
if math.sqrt((res["C "].x-box[i+1]["N "].x)^2+(res["C "].y-box[i+1]["N "].y)^2+(res["C "].z-box[i+1]["N "].z)^2) < maxbondlen then
peptide={{res["C "].x,res["C "].y,res["C "].z},{box[i+1]["N "].x,box[i+1]["N "].y,box[i+1]["N "].z}}
table.insert(bonds,peptide)
end end
end
if res["O3'"]~=nil and i<#box then if box[i+1]["P "]~=nil then
if math.sqrt((res["O3'"].x-box[i+1]["P "].x)^2+(res["O3'"].y-box[i+1]["P "].y)^2+(res["O3'"].z-box[i+1]["P "].z)^2) < maxbondlen then
phospho={{res["O3'"].x,res["O3'"].y,res["O3'"].z},{box[i+1]["P "].x,box[i+1]["P "].y,box[i+1]["P "].z}}
table.insert(bonds,phospho)
end end
end
if #bonds>0 then res["bonds"]=bonds end
end --##
if thing==protein then drawbonds(box) else drawbonds(box,true) end -- draws the normal aa bonds, true for symm_box
nextra=#extrabox -- gets SS bonds
if nextra>1 then
extrabonds={}
for j,atom in ipairs(extrabox) do
for m,n in pairs(atom) do at1=m end --get atom key
for k,atom2 in ipairs(extrabox) do
if k>j then
for o,p in pairs(atom2) do at2=o end --ditto
d=math.sqrt((extrabox[j][at1].x-extrabox[k][at2].x)^2+(extrabox[j][at1].y-extrabox[k][at2].y)^2+(extrabox[j][at1].z-extrabox[k][at2].z)^2)
if d<maxssbondlen then
bond={{extrabox[j][at1].x,extrabox[j][at1].y,extrabox[j][at1].z},{extrabox[k][at2].x,extrabox[k][at2].y,extrabox[k][at2].z}}
table.insert(extrabonds,bond)
end
end
end
end
extraboxbond={{["ssbrs"]={},["bonds"]=extrabonds}} --dummy residue containing SS bonds
drawbonds(extraboxbond,true) -- draws any SSBRs
end
--for i,j in ipairs(extraboxbond) do for k,l in pairs(j) do print(k) if k=="bonds" then for m,n in ipairs(l) do print(n[1][1],n[1][2],n[1][3],n[2][1],n[2][2],n[2][3]) end end end end
end
function getBoxAtoms() --££
group = js.new(threejs.Group)
if rotamat~=nil then group:applyMatrix4(rotamat) end
getCentre()
getBonds(protein)
drawatoms()
if gen_symm==true then
if symm_box_filled~=true then get_centroid() end
getBonds(symm_box)
drawatoms()
end
end --££
function drawbonds(abox,alt)
if alt==true then matter=altmat alt=false else matter=material end
vector1=js.new(threejs.Vector3,0,0,0)
vector2=js.new(threejs.Vector3,0,0,0)
geobond = js.new(threejs.BufferGeometry)
--for i,res in ipairs(protein) do
for i,res in ipairs(abox) do
if res["bonds"]~=nil then
for j,bond in ipairs(res["bonds"]) do
vector1:set(bond[1][1]-xcent,bond[1][2]-ycent,bond[1][3]-zcent)
vector2:set(bond[2][1]-xcent,bond[2][2]-ycent,bond[2][3]-zcent)
vectors=window:Array(vector1,vector2)
geometry1=geobond:clone()
geometry1:setFromPoints(vectors)
mesh1 = js.new(threejs.Line, geometry1, matter)
group:add(mesh1)
end
end
end
--for i,res in ipairs(box) do print("Residue:",i) for j,k in pairs(res) do print(j) end end
end
function drawatoms()
vectoratom=js.new(threejs.Vector3,0,0,0)
atgeom=js.new(threejs.BufferGeometry)
for i,j in ipairs(box) do
for k,l in pairs(j) do
attype=k:sub(1,1)
--if attype=="N" or attype=="O" or attype=="S" or attype=="P" then
if attype~="C" and attype~="b" and attype~="s" then --"b" and "s" are the dummy atom types for the bonds and SSBR's
if attype=="N" then atcol="blue" elseif attype=="O" then atcol="red" elseif attype=="S" then atcol="#0FFF50" else atcol="#0FFF50" end
--print(attype,atcol,l.x-xcent,l.y-ycent,l.z-zcent)
materat=js.new(threejs.PointsMaterial)
materat.size=3
materat.sizeAttenuation=false
materat.color:set(atcol)
vectorat=window:Array(vectoratom:set(l.x-xcent,l.y-ycent,l.z-zcent))
geomat=atgeom:clone()
geomat:setFromPoints(vectorat)
meshat=js.new(threejs.Points,geomat,materat)
group:add(meshat)
end
end
end
--Next lines not needed in map version
scene:add(group)
if spin==true then renderer:setAnimationLoop(twirl) else renderer:setAnimationLoop() renderer:render(scene, camera) end
document:getElementById("mutate").disabled=false
document:getElementById("move").disabled=false
end
function twirl()
theta=0.02
twang=twang+theta
group:rotateOnWorldAxis(xworld,theta*math.cos(twang))
group:rotateOnWorldAxis(yworld,theta*math.sin(twang))
renderer:render(scene, camera)
end
function uploadMTZ()
document:getElementById("mtztips").innerHTML="Please wait for uploads to complete!"
--document:getElementById("mapdownload").disabled=true
diffneeded=document:getElementById("diffmap").checked
formData=js.new(window.FormData)
formData:append("pdbfile",pdbfile)
formData:append("mtzfile",mapfile)
formData:append("diffneeded",diffneeded)
fetchObject=js.new(window.FormData)
fetchObject.method="POST"
fetchObject.body=formData
fetchpromise=window:fetch("/cgi-bin/run_gemmi.py",fetchObject)
fetchpromise["then"](fetchpromise, getMapBack) --override lua's own if/then command
end
function getMapBack()
document:getElementById("mtztips").innerHTML="Please wait for MAP file to download."
if diffneeded~=true then getmap=nameparts[1]..".map" else getmap=nameparts[1].."_diff.map" end
mapURI="/temp/"..getmap
mapdownload=document:createElement("a")
mapdownload.href=mapURI
mapdownload.download=getmap
mapdownload:click()
document:getElementById("mtztips").innerHTML="Please use map file chooser button to load it."
end
function readmapfile()
mapfile=mapinput.files[0]
name1=pdbfile.name name2=mapfile.name print(name1, name2)
nameparts={} for word in string.gmatch(mapfile.name,"[^.]+") do table.insert(nameparts,word) end --print(nameparts[1],nameparts[2])
ref_filetype=string.lower(nameparts[2])
if ref_filetype=="mtz" and nAt>0 then
--mtztext="Tick for difference map: <input type=\"checkbox\" id=\"diffmap\"> <button id=\"uploadmtz\">Make map from MTZ file</button> <button id=\"mapdownload\" disabled>Download map file</button> <p> <small><span id=\"mtztips\"></span></small>"
mtztext="Tick for difference map: <input type=\"checkbox\" id=\"diffmap\"> <button id=\"uploadmtz\">Make map from MTZ file</button><p><small><span id=\"mtztips\"></span></small>"
document:getElementById("info").innerHTML=mtztext
mtzin=document:getElementById("uploadmtz")
mtzin:addEventListener("click", uploadMTZ)
else
mapreader=js.new(window.FileReader)
mapreader.onload=readmap
mapreader:readAsArrayBuffer(mapfile)
document:getElementById("info").innerHTML="Please wait"
end
end
function readmap()
map_fills_cell=false
map=mapreader.result
viewer=js.new(window.DataView,map)
print("Grid:")
grid={} for g=1,10 do grid[g]=viewer:getInt32((g-1)*4,true) print(grid[g]) end --up to 40. Grid is num col, row, sect, mode, start col, row, sect, num div on a, b, c. Mode is 2 for 32 bit floats
na=grid[8] nb=grid[9] nc=grid[10]
print("Cell:")
cell={} for c=1,6 do cell[c]=viewer:getFloat32(40+(c-1)*4,true) print(cell[c]) end --up to 64
print("Axis order:")
order={} for o=1,3 do order[o]=viewer:getInt32(64+(o-1)*4,true) print(order[o]) end --2,1,3 z sections (ncode 1), 3,1,2 y sections (ncode3), 1,2,3 for gemmi, 3,2,1 for EM
print("Min, max, mean density:")
mimameden={} for m=1,3 do mimameden[m]=viewer:getFloat32(76+(m-1)*4,true) print(mimameden[m]) end mean=mimameden[3]
print("Symm:")
sym={} for s=1,3 do sym[s]=viewer:getInt32(88+(s-1)*4,true) print(sym[s]) end denoffset=1024+sym[2] -- sym[1]=sg num, sym[2] is length of string of symops (not used)
rms=viewer:getFloat32(216,true) print("rms:",rms)
nlbl=viewer:getInt32(220,true) --print(nlbl)
lblbufarr=map:slice(224,1023)
lblvwr=js.new(window.TextDecoder,"utf-8")
labels=lblvwr:decode(lblbufarr) --print(labels)
symbufarr=map:slice(1024,denoffset);
symvwr=js.new(window.TextDecoder,"utf-8")
symops=symvwr:decode(symbufarr) --print(symops)
--den={} for d=1,6 do den[d]=viewer:getFloat32(denoffset+(d-1)*4,true) print(den[d]) end
if order[1]==2 and order[2]==1 and order[3]==3 then print("Old normal CCP4 map") ncode=1
amin=grid[6] numona=grid[2] bmin=grid[5] numonb=grid[1] cmin=grid[7] numonc=grid[3]
elseif order[1]==3 and order[2]==1 and order[3]==2 then print("Old Mono- or tri-clinic CCP4 map") ncode=3
amin=grid[6] numona=grid[2] bmin=grid[7] numonb=grid[3] cmin=grid[5] numonc=grid[1]
elseif order[1]==1 and order[2]==2 and order[3]==3 then print("Gemmi map") ncode="gemmi"
amin=grid[5] numona=grid[1] bmin=grid[6] numonb=grid[2] cmin=grid[7] numonc=grid[3]
elseif order[1]==3 and order[2]==2 and order[3]==1 then print("CCPEM map") ncode="ccpem"
amin=grid[7] numona=grid[3] bmin=grid[6] numonb=grid[2] cmin=grid[5] numonc=grid[1]
else document:getElementById("info").innerHTML="Bad fast medium slow axis order" return
end
a,b,c,alpha_deg,beta_deg,gamma_deg=cell[1],cell[2],cell[3],cell[4],cell[5],cell[6]
if amin==0 and bmin==0 and cmin==0 and numona==na and numonb==nb and numonc==nc then map_fills_cell=true print("Map covers unit cell rather than molecule") end --occurs with some EBI PDBe maps
fractorth()
getMapBox()
end
function getMapBox()
document:getElementById("info").innerHTML=centrat.res.." "..centrat.num..centrat.chain
if foundSideCen==false then
if protein[startpos]["CB "]~=nil then mcenat=protein[startpos]["CB "] elseif protein[startpos]["CA "]~=nil then mcenat=protein[startpos]["CA "] else mcenat=centrat end
else
mcenat=centrat
foundSideCen=false
end
print("Map centre:",mcenat.x,mcenat.y,mcenat.z)
xmin=mcenat.x-maprad xmax=mcenat.x+maprad ymin=mcenat.y-maprad ymax=mcenat.y+maprad zmin=mcenat.z-maprad zmax=mcenat.z+maprad
clo=window:Array(xmin,ymin,zmin)
far=window:Array(xmax,ymax,zmax)
fclo=mathjs:multiply(Linv,clo)
ffar=mathjs:multiply(Linv,far)
--print(fclo,ffar)
xminf=(mathjs:subset(fclo,el1)) yminf=(mathjs:subset(fclo,el2)) zminf=(mathjs:subset(fclo,el3))
xmaxf=(mathjs:subset(ffar,el1)) ymaxf=(mathjs:subset(ffar,el2)) zmaxf=(mathjs:subset(ffar,el3))
--print(xminf,xmaxf,yminf,ymaxf,zminf,zmaxf)
mapamin=math.ceil(xminf*na) intamin=mapamin-amin mapamax=math.ceil(xmaxf*na) intamax=mapamax-amin ndiva=(mapamax-mapamin) --math.abs maybe needed!! for -ve on axis
mapbmin=math.ceil(yminf*nb) intbmin=mapbmin-bmin mapbmax=math.ceil(ymaxf*nb) intbmax=mapbmax-bmin ndivb=(mapbmax-mapbmin)
mapcmin=math.ceil(zminf*nc) intcmin=mapcmin-cmin mapcmax=math.ceil(zmaxf*nc) intcmax=mapcmax-cmin ndivc=(mapcmax-mapcmin)
--print(intamin,intamax,intbmin,intbmax,intcmin,intcmax,"ndivs",ndiva,ndivb,ndivc)
minimap={}
for cc=intcmin,intcmax do
if map_fills_cell==true then cc=cc%nc if cc<0 then cc=nc-cc end end
minimapsection={}
for aa=intamin,intamax do
if map_fills_cell==true then aa=aa%na if aa<0 then aa=na-aa end end
minimaprow={}
for bb=intbmin,intbmax do
if map_fills_cell==true then bb=bb%nb if bb<0 then bb=nb-bb end end
if ncode==1 then
posi=denoffset+cc*numona*numonb*4+aa*numonb*4+bb*4
elseif ncode==3 then
posi=denoffset+bb*numona*numonc*4+aa*numonc*4+cc*4
elseif ncode=="gemmi" then
posi=denoffset+cc*numona*numonb*4+bb*numona*4+aa*4
elseif ncode=="ccpem" then
posi=denoffset+aa*numonc*numonb*4+bb*numonc*4+cc*4
end
dens=viewer:getFloat32(posi,true)
normdens=(dens-mean)/rms
table.insert(minimaprow,normdens)
end table.insert(minimapsection,minimaprow) end table.insert(minimap,minimapsection) end
--for i,j in ipairs(minimap) do for k,l in ipairs(j) do for m,n in ipairs(l) do print(n) end end end
ndiv=window:Array(ndiva,ndivb,ndivc)
start=window:Array(0,0,0)
bounds=window:Array(start,ndiv)
isomesh=window:surfaceNets(ndiv, function(_,x,y,z) return denread(x,y,z) end, bounds)
window.console:log(isomesh)
posis=isomesh.positions
vertices={}
for i=0,#posis-1 do
xcontf=(mapamin+posis[i][0])/na ycontf=(mapbmin+posis[i][1])/nb zcontf=(mapcmin+posis[i][2])/nc
minimapxyzf=window:Array(xcontf,ycontf,zcontf)
minimapxyzo=mathjs:multiply(L,minimapxyzf)
minixo=mathjs:subset(minimapxyzo,el1) miniyo=mathjs:subset(minimapxyzo,el2) minizo=mathjs:subset(minimapxyzo,el3)
table.insert(vertices,{minixo-xcent,miniyo-ycent,minizo-zcent})
end
--scene:add(group)
vector1=js.new(threejs.Vector3,0,0,0)
vector2=js.new(threejs.Vector3,0,0,0)
vector3=js.new(threejs.Vector3,0,0,0)
geomap = js.new(threejs.BufferGeometry)
triangs=isomesh.cells
for i=0,#triangs-1 do
verts=triangs[i]
vert1=verts[0]+1 vert2=verts[1]+1 vert3=verts[2]+1 -- +1 to convert to lua indices in vertices table
--print("\n",vert1,vert2,vert3)
vector1:set(vertices[vert1][1],vertices[vert1][2],vertices[vert1][3])
vector2:set(vertices[vert2][1],vertices[vert2][2],vertices[vert2][3])
vector3:set(vertices[vert3][1],vertices[vert3][2],vertices[vert3][3])
vectors=window:Array(vector1,vector2,vector3)
mapgeo=geomap:clone()
mapgeo:setFromPoints(vectors)
meshmap = js.new(threejs.LineLoop, mapgeo, matermap)
group:add(meshmap)
end
scene:add(group)
--if spin==true then renderer:setAnimationLoop(function() twirl() end) else offsetX,offsetY=0,0 renderer:setAnimationLoop() renderer:render(scene, camera) document:getElementById("move").disabled=false end
if spin==true then renderer:setAnimationLoop(twirl) else renderer:setAnimationLoop() renderer:render(scene, camera) end
document:getElementById("mutate").disabled=false
document:getElementById("move").disabled=false
geobond:dispose() geometry1:dispose() atgeom:dispose() geomat:dispose() geomap:dispose() --RAM housekeeping
material:dispose() altmat:dispose() matermap:dispose() matter:dispose() materat:dispose()
end
--Dictionaries
dictable={ --target distances are read from dist table and are grouped to be within about 0.1 Angstroms. *prev/next*=ignored restraints
["N "]={["CA "]=4,["CB "]=9,["C "]=9}, --*prev*["C "],*prev*["CA "],*prev*["O "]
["CA "]={["N "]=4,["C "]=4,["CB "]=4,["O "]=8,["CG "]=9,["OG "]=8,["OG1"]=8,["CG1"]=9,["CG2"]=9,["SG "]=10}, --*prev*["C "],*next*["N "]
["C "]={["CA "]=4,["CB "]=9,["N "]=9,["O "]=1}, --*next*["N "],*next*["CA "],*next pro*["CD "]
["O "]={["C "]=1,["CA "]=8}, --*next*["N "]
["CB "]={["CA "]=4,["N "]=9,["C "]=9,["CG "]=4,["OG "]=3,["OG1"]=3,["CG1"]=4,["CG2"]=4,["SG "]=5,["CD "]=9,["CD1"]=9,["CD2"]=9,["OD1"]=8,["OD2"]=8,["ND2"]=8,["ND1"]=9,["SD "]=10},
["CG "]={["CA "]=9,["CB "]=4,["CD1"]=4,["CD2"]=4,["CD "]=4,["SD "]=5,["OD1"]=1,["OD2"]=1,["ND2"]=2,["ND1"]=2,["OE1"]=8,["OE2"]=8,["NE1"]=6,["NE2"]=8,["NE "]=9,["CE1"]=8,["CE2"]=8,["CE3"]=9,["CE "]=10}, --Met/Lys CG/CE sorted in code
["OG "]={["CB "]=3,["CA "]=8},
["SG "]={["CB "]=5,["CA "]=10},
["OG1"]={["CB "]=3,["CA "]=8,["CG2"]=8},
["CG1"]={["CB "]=4,["CD1"]=4,["CA "]=9,["CG2"]=9},
["CG2"]={["CB "]=4,["CA "]=9,["OG1"]=8,["CG1"]=9},
["CD "]={["CG "]=4,["CB "]=9,["OE1"]=1,["OE2"]=1,["NE2"]=2,["CE "]=4,["NE "]=4,["CZ "]=9,["NZ "]=9}, --*pro prev*["C "]
["CD1"]={["CG "]=4,["CG1"]=4,["CB "]=9,["CZ "]=8,["CD2"]=8,["CE1"]=3,["NE1"]=3}, --cd1/2 aliphatic/aromatic simplified
["CD2"]={["CG "]=4,["CE2"]=3,["CB "]=9,["CZ "]=8,["CE3"]=3,["NE1"]=6,["CZ2"]=9,["CZ3"]=8,["NE2"]=3,["ND1"]=6,["CD1"]=8},
["OD1"]={["CG "]=1,["CB "]=8,["OD2"]=6,["ND2"]=6},
["OD2"]={["CG "]=1,["CB "]=8,["OD1"]=6},
["ND1"]={["CG "]=3,["CB "]=9,["CE1"]=2,["CD2"]=6,["NE2"]=6},
["ND2"]={["CG "]=2,["CB "]=8,["OD1"]=6},
["SD "]={["CG "]=5,["CE "]=5,["CB "]=10},
["CE "]={["SD "]=5,["NZ "]=4,["CD "]=4,["CG "]=10},
["CE1"]={["CD1"]=3,["CG "]=8,["CZ "]=3,["OH "]=8,["ND1"]=2,["NE2"]=2,["CE2"]=8}, --ce1/2 same as cd1/2
["CE2"]={["CD2"]=3,["CG "]=8,["CZ "]=3,["OH "]=8,["NE1"]=3,["CZ2"]=3,["CH2"]=8,["CE3"]=8,["CE1"]=8},
["CE3"]={["CD2"]=3,["CG "]=9,["CZ3"]=3,["CH2"]=8,["CE2"]=8},
["OE1"]={["CD "]=1,["CG "]=8,["OE2"]=6,["NE2"]=6},
["OE2"]={["CD "]=1,["CG "]=8,["OE1"]=6},
["NE "]={["CD "]=4,["CG "]=9,["CZ "]=2,["NH1"]=7,["NH2"]=7},
["NE1"]={["CD1"]=3,["CG "]=6,["CE2"]=3,["CD2"]=6,["CZ2"]=9},
["NE2"]={["CD "]=2,["CG "]=8,["OE1"]=6,["CD2"]=2,["CE1"]=2,["ND1"]=6},
["CZ "]={["CE1"]=3,["CE2"]=3,["CD1"]=8,["CD2"]=8,["OH "]=3,["NE "]=2,["CD "]=9,["NH1"]=2,["NH2"]=2},
["NZ "]={["CE "]=4,["CD "]=9},
["CZ2"]={["CE2"]=3,["CD2"]=9,["NE1"]=9,["CH2"]=3,["CZ3"]=8},
["CZ3"]={["CE3"]=3,["CH2"]=3,["CD2"]=8,["CZ2"]=8},
["CH2"]={["CZ2"]=3,["CZ3"]=3,["CE2"]=8,["CE3"]=8},
["NH1"]={["CZ "]=2,["NH2"]=7,["NE "]=7},
["NH2"]={["CZ "]=2,["NH1"]=7,["NE "]=7},
["OH "]={["CZ "]=3,["CE1"]=8,["CE2"]=8}
}
protable={ --target distances for proline
["N "]={["CA "]=4,["CB "]=8,["C "]=9,["CG "]=8,["CD "]=4}, --*prev*["C "],*prev*["CA "],*prev*["O "]
["CA "]={["N "]=4,["C "]=4,["CB "]=4,["O "]=8,["CG "]=8,["CD "]=9}, --*prev*["C "],*next*["N "]
["C "]={["CA "]=4,["CB "]=9,["N "]=9,["O "]=1}, --*next*["N "],*next*["CA "],*next pro*["CD "]
["O "]={["C "]=1,["CA "]=8}, --*next*["N "]
["CB "]={["CA "]=4,["N "]=8,["C "]=9,["CG "]=4,["CD "]=9},
["CG "]={["CA "]=8,["CB "]=4,["CD "]=4,["N "]=8},
["CD "]={["CG "]=4,["CB "]=9,["N "]=4,["CA "]=9} --*pro prev*["C "]
}
tetrigtable={ --tetragonal or trigonal carbon centres, neighbours and distance from centroid of neighbours. CA and Thr/Ile CB chirality not checked.
["ASP"]={["CG "]={"CB ","OD1","OD2",0.1}},["ASN"]={["CG "]={"CB ","OD1","ND2",0.1}},
["GLU"]={["CD "]={"CG ","OE1","OE2",0.1}},["GLN"]={["CD "]={"CG ","OE1","NE2",0.1}},
["PHE"]={["CG "]={"CB ","CD1","CD2",0.0}},
["TYR"]={["CG "]={"CB ","CD1","CD2",0.0},["CZ "]={"CE1","CE2","OH ",0.0}},
["TRP"]={["CG "]={"CB ","CD1","CD2",0.07},["CD2"]={"CG ","CE2","CE3",0.1},["CE2"]={"CD2","NE1","CZ2",0.1}},
["LEU"]={["CG "]={"CB ","CD1","CD2",0.5}},["VAL"]={["CB "]={"CA ","CG1","CG2",0.5}},
["ILE"]={["CB "]={"CA ","CG1","CG2",0.5}},["THR"]={["CB "]={"CA ","OG1","CG2",0.5}},
["ARG"]={["CZ "]={"NE ","NH1","NH2",0.0}},["HIS"]={["CG "]={"CB ","ND1","CD2",0.04}}}
planetable={ --restrain some interatomic vectors to be parallel e.g. CD1-CE1 bond is parallel to CB-CG in Phe/Tyr but shorter by factor of 0.91.
["PHE"]={["CE1"]={"CB ","CG ","CD1",0.91},["CE2"]={"CB ","CG ","CD2",0.91},["CZ "]={"CG ","CD1","CE2",1.0},["CZ "]={"CG ","CD2","CE1",1.0}},
["TYR"]={["CE1"]={"CB ","CG ","CD1",0.91},["CE2"]={"CB ","CG ","CD2",0.91},["CZ "]={"CG ","CD1","CE2",1.0},["CZ "]={"CG ","CD2","CE1",1.0},["OH "]={"CB ","CG ","CZ ",0.93}},
["TRP"]={["CH2"]={"CD2","CE2","CZ3",1.0},["CH2"]={"CD2","CE3","CZ2",1.0},["CZ3"]={"CE2","CZ2","CE3",1.0},["CZ2"]={"CG ","NE1","CE3",1.26},["CH2"]={"CG ","NE1","CZ3",0.63}},
["ARG"]={["NH2"]={"CD ","NE ","CZ ",1.0}}}
function addTetrig() --calculate distance of central atom from centroid of neighbours and the diff from ideal
ctat=residue[centat] nbr1=residue[nayb1] nbr2=residue[nayb2] nbr3=residue[nayb3]
xcentet=(nbr1.x+nbr2.x+nbr3.x)/3
ycentet=(nbr1.y+nbr2.y+nbr3.y)/3
zcentet=(nbr1.z+nbr2.z+nbr3.z)/3
dx=ctat.x-xcentet dy=ctat.y-ycentet dz=ctat.z-zcentet
dcalc=math.sqrt(dx^2+dy^2+dz^2)
diff=dtarg-dcalc
if dcalc~=0 then
table.insert(atDeltas,{["diff"]=diff,["at1"]="tetrig",["at2"]=centat,["delx"]=diff*dx/dcalc,["dely"]=diff*dy/dcalc,["delz"]=diff*dz/dcalc})
end
--print("In addTetrig for:", centat, nayb1, nayb2, nayb3,"dcalc:",dcalc)
--print(ctat.x,ctat.y,ctat.z,nbr1.x,nbr1.y,nbr1.z,nbr2.x,nbr2.y,nbr2.z,nbr3.x,nbr3.y,nbr3.z)
end
function addPlane() --calculate distance of atom from ideal position that would make the vectors parallel
planx=residue[tail2].x+(residue[tip1].x-residue[tail1].x)*dscale
plany=residue[tail2].y+(residue[tip1].y-residue[tail1].y)*dscale
planz=residue[tail2].z+(residue[tip1].z-residue[tail1].z)*dscale
dx=residue[planat].x-planx dy=residue[planat].y-plany dz=residue[planat].z-planz
dcalc=math.sqrt(dx^2+dy^2+dz^2)
diff=dtarg-dcalc
if dcalc~=0 then
table.insert(atDeltas,{["diff"]=diff,["at1"]="plane",["at2"]=planat,["delx"]=diff*dx/dcalc,["dely"]=diff*dy/dcalc,["delz"]=diff*dz/dcalc})
end
--print("In addPlane")
end
function addTrpPlane() --calculate distance of CD1 from its ideal position on the line bisecting CH2-CZ3 and CD2-CE2 bonds. Helps to make the Trp flat.
dum1x=(residue["CZ3"].x+residue["CH2"].x)/2 dum1y=(residue["CZ3"].y+residue["CH2"].y)/2 dum1z=(residue["CZ3"].z+residue["CH2"].z)/2
dum2x=(residue["CD2"].x+residue["CE2"].x)/2 dum2y=(residue["CD2"].y+residue["CE2"].y)/2 dum2z=(residue["CD2"].z+residue["CE2"].z)/2
planx=dum1x+(dum2x-dum1x)*1.89 plany=dum1y+(dum2y-dum1y)*1.89 planz=dum1z+(dum2z-dum1z)*1.89
dx=residue["CD1"].x-planx dy=residue["CD1"].y-plany dz=residue["CD1"].z-planz
dcalc=math.sqrt(dx^2+dy^2+dz^2)
diff=dtarg-dcalc
if dcalc~=0 then
table.insert(atDeltas,{["diff"]=diff,["at1"]="plane",["at2"]="CD1",["delx"]=diff*dx/dcalc,["dely"]=diff*dy/dcalc,["delz"]=diff*dz/dcalc})
end
--print("In addTrpPlane")
end
function checkChir() --calculate the chiral volume
chrat=residue[chirat] nbr1=residue[nayb1] nbr2=residue[nayb2] nbr3=residue[nayb3]
detRow1=window:Array(nbr1.x-chrat.x, nbr1.y-chrat.y, nbr1.z-chrat.z)
detRow2=window:Array(nbr2.x-chrat.x, nbr2.y-chrat.y, nbr2.z-chrat.z)
detRow3=window:Array(nbr3.x-chrat.x, nbr3.y-chrat.y, nbr3.z-chrat.z)
chirDet=window:Array(detRow1,detRow2,detRow3)
chivdet=js.new(mathjs.matrix,chirDet)
chiv=mathjs:det(chivdet)
print("In checkChir Chiv:",chiv,"for:",chirat,nayb1,nayb2,nayb3)
--print(chrat.x,chrat.y,chrat.z,nbr1.x,nbr1.y,nbr1.z,nbr2.x,nbr2.y,nbr2.z,nbr3.x,nbr3.y,nbr3.z)
--if chiv<0 and math.abs(chiv)>0.1 then correctChir() end --invert only if |chiv| is big enough to avoid divide by zero error
if chiv<0 then correctChir() end
end
function correctChir()
xcentet=(nbr1.x+nbr2.x+nbr3.x)/3 ycentet=(nbr1.y+nbr2.y+nbr3.y)/3 zcentet=(nbr1.z+nbr2.z+nbr3.z)/3
mov1=chrat mov2=nbr3
dx=mov1.x-xcentet dy=mov1.y-ycentet dz=mov1.z-zcentet dcalc=math.sqrt(dx^2+dy^2+dz^2) diff=-0.5 --diff=dtarg-dcalc
if dcalc~=0 then
delx=diff*dx/dcalc dely=diff*dy/dcalc delz=diff*dz/dcalc
mov1.x=mov1.x+delx mov1.y=mov1.y+dely mov1.z=mov1.z+delz
mov2.x=mov2.x-delx mov2.y=mov2.y-dely mov2.z=mov2.z-delz
end
--print("In correctChir")
end
function mmm() --massively minimal minimiser (mmm) mainly for side chains (pat not pending)
if ncyc>0 and ncyc%20==0 and resnam~="GLY" then chirat="CA " nayb1="N " nayb2="C " nayb3="CB " checkChir() end --Check the main chain chiral volume at intervals
if resnam=="THR" or resnam=="ILE" then --Check the side chain chiral volume at bigger intervals
if ncyc>0 and ncyc%33==0 then
chirAtObjs=tetrigtable[resnam]
for k,l in pairs(chirAtObjs) do --$$
--print(k,l)
chirat=k nayb1=l[1] nayb2=l[2] nayb3=l[3] checkChir()
end --$$
end
end
dist={1.2,1.3,1.4,1.5,1.8,2.2,2.3,2.4,2.5,2.8}
-- 1 2 3 4 5 6 7 8 9 10 simplified distances
atDeltas={}
--for k,l in ipairs(lookup1) do print(l) end
kk=0
for k,l in pairs(residue) do
kk=kk+1 mm=0
for m,n in pairs(residue) do
mm=mm+1
if mm>kk then --calculate the deviations from target distances (diff)
--print(k,l.x,l.y,l.z)
if dictable[k]~=nil and dictable[m]~=nil then
if resnam~="PRO" then dtarg=dist[dictable[k][m]] else dtarg=dist[protable[k][m]] end
if resnam=="LYS" then
if k=="CG " and m=="CE " then dtarg=2.5
elseif k=="CE " and m=="CG " then dtarg=2.5
end
end
if dtarg~=nil then --print(k,m)
dx=n.x-l.x
dy=n.y-l.y
dz=n.z-l.z
dcalc=math.sqrt(dx^2+dy^2+dz^2)
diff=dtarg-dcalc
table.insert(atDeltas,{["diff"]=diff,["at1"]=k,["at2"]=m})
end
end
end
end
end
--Calculate the tetragonal and trigonal target distances
if resnam~="GLY" then centat="CA " nayb1="N " nayb2="CB " nayb3="C " dtarg=0.5 addTetrig() end
if tetrigtable[resnam]~=nil then --@@
centAtObjs=tetrigtable[resnam]
for k,l in pairs(centAtObjs) do --$$
centat=k nayb1=l[1] nayb2=l[2] nayb3=l[3] dtarg=l[4] addTetrig()
end --$$
end --@@
if ncyc>0 and maxdiff<0.3 and ncyc%3==0 then --trickle in the planarity targets
if planetable[resnam]~=nil then --@@
planeAtObjs=planetable[resnam]
for k,l in pairs(planeAtObjs) do --$$
planat=k tail1=l[1] tip1=l[2] tail2=l[3] dscale=l[4] addPlane()
end --$$
end --@@
if resnam=="TRP" then addTrpPlane() end --extra planarity target for CD1 of Trp
end
ncyc=ncyc+1
--print("Cycle number:",ncyc)
--for i,j in ipairs(atDeltas) do for k,l in pairs(j) do print(l) end end
maxdiff=-100
for i,j in ipairs(atDeltas) do absdiff=math.abs(j.diff) --find the worst distance violation
if absdiff>maxdiff then
maxdiff=absdiff
worstPair={j.at1,j.at2}
if j.at1=="tetrig" or j.at1=="plane" then delx=j.delx dely=j.dely delz=j.delz
end
end
end
if ncyc>=100 and maxdiff<0.1 then sorted=true end
--for i,j in ipairs(worstPair) do print(i,j) end
at1=worstPair[1]
at2=worstPair[2]
--print("The worst pair (atom name) with delta:",at1,at2,maxdiff)
if at1=="tetrig" or at1=="plane" then --##
mov=residue[at2]
--print(at1)
else
--print("d")
sporad1=math.random(1,2) --decide at random which atom to move for atom-atom distance violations.
if sporad1==1 then fixed=at1 moving=at2 else fixed=at2 moving=at1 end
if moving=="N " then moving=fixed fixed="N " end --main chain N and C fixed
if moving=="C " then moving=fixed fixed="C " end
--print("final decision fixed:",fixed,"moving:",moving)
fix=residue[fixed]
mov=residue[moving]
dx=mov.x-fix.x
dy=mov.y-fix.y
dz=mov.z-fix.z
dcalc=math.sqrt(dx^2+dy^2+dz^2)
if resnam~="PRO" then dtarg=dist[dictable[fixed][moving]] else dtarg=dist[protable[fixed][moving]] end
if resnam=="LYS" then
if fixed=="CG " and moving=="CE " then dtarg=2.5
elseif fixed=="CE " and moving=="CG " then dtarg=2.5
end
end
diff=dtarg-dcalc
delx=diff*dx/dcalc dely=diff*dy/dcalc delz=diff*dz/dcalc
end --##
mov.x=mov.x+delx/2 mov.y=mov.y+dely/2 mov.z=mov.z+delz/2 -- Dampen the shifts
--mov.x=mov.x+delx mov.y=mov.y+dely mov.z=mov.z+delz
end --mmm
function tidy()
document:getElementById("info").innerHTML="Please wait"
window:setTimeout(function() tidy2() end, 10) --10 msec delay to let the DOM display the message
end
function tidy2()
residue=protein[startpos]
resnam=string.sub(residue["CA "].res,1,3)
ncyc=0 --normally needed
sorted=false
--mmm() --normally commented out
--repeat mmm() until sorted or ncyc>400
repeat mmm() until sorted or ncyc>200
print("Residue:",resnam,startpos,"max delta:",maxdiff,"after", ncyc, "cycles.")
rerun() --needed normally
end
function denread(xiso,yiso,ziso)
--print("denread:",xiso,yiso,ziso)
return minimap[ziso+1][xiso+1][yiso+1]-contour_level
end
function getStartPos() --if startpos < 1st resnum go to 1st res in chain, if startpos > last resnum go to last res in chain
for i,res in ipairs(protein) do
_,firstAtom=next(res) --print(firstAtom.chain,firstAtom.num)
firstAtomResNum=string.sub(firstAtom.num,1,-2) firstAtomResNum=tonumber(firstAtomResNum) --remove insertion code so resnum is a number
if chain==firstAtom.chain then
lastInChain=i
if startpos<=firstAtomResNum then wantedpos=i break else wantedpos=lastInChain end
end
end
startpos=wantedpos print("Actual res num, index:",firstAtomResNum,wantedpos)
position.value=firstAtomResNum --send resnum to the form ignoring insertion code
chainbox.value=firstAtom.chain --send chain to the form
end
function readform()
document:getElementById("info").innerHTML="Please wait"
startpos=tonumber(position.value)
chainbox=document:getElementById("chain")
if chainbox~=js.null then chain=chainbox.value chain=string.upper(chain) chain=string.format("%2s",chain) getStartPos() end
contour=document:getElementById("contour")
contour_level=tonumber(contour.value) if contour_level<0 then mapcol=mapcolneg else mapcol=mapcolpos end
matermap.color:set(mapcol)
moving=false
if document:getElementById("symmetry").checked==true then gen_symm=true else gen_symm=false end
end
function swapdensign()
contour_level=tonumber(document:getElementById("contour").value)
contour_level=-contour_level
document:getElementById("contour").value=contour_level
--print(document:getElementById("contour").value)
if minimap~=nil then if #(minimap)>0 then run() end end
end
sidechain_atoms={
["ALA"]={"CB "},
["ARG"]={"CB ", "CG ", "CD ", "NE ", "CZ ", "NH1", "NH2"},
["ASN"]={"CB ", "CG ", "OD1", "ND2"},
["ASP"]={"CB ", "CG ", "OD1", "OD2"},
["CYS"]={"CB ", "SG "},
["GLN"]={"CB ", "CG ", "CD ", "OE1", "NE2"},
["GLU"]={"CB ", "CG ", "CD ", "OE1", "OE2"},
["GLY"]={},
["HIS"]={"CB ", "CG ", "ND1", "CD2", "CE1", "NE2"},
["ILE"]={"CB ", "CG1", "CG2", "CD1"},
["LEU"]={"CB ", "CG ", "CD1", "CD2"},
["LYS"]={"CB ", "CG ", "CD ", "CE ", "NZ "},
["MET"]={"CB ", "CG ", "SD ", "CE "},
["PHE"]={"CB ", "CG ", "CD1", "CD2", "CE1", "CE2", "CZ "},
["PRO"]={"CB ", "CG ", "CD "},
["SER"]={"CB ", "OG "},
["THR"]={"CB ", "OG1", "CG2"},
["TRP"]={"CB ", "CG ", "CD1", "CD2", "NE1", "CE2", "CE3", "CZ2", "CZ3", "CH2"},
["TYR"]={"CB ", "CG ", "CD1", "CD2", "CE1", "CE2", "CZ ", "OH "},
["VAL"]={"CB ", "CG1", "CG2"}
}
function mutate()
aa_chosen=document:getElementById("mutate").value
--print("New amino acid chosen:",aa_chosen)
--for k,l in ipairs(sidechain_atoms[aa_chosen]) do print(l) end
residue=protein[startpos]
resnam=string.sub(residue["CA "].res,1,3)
--print("Old amino acid:",resnam)
new_residue={}
for k,l in pairs(residue) do if k=="N " or k=="CA " or k=="C " or k=="O " or (k=="CB " and aa_chosen~="GLY") then new_residue[k]=l end end --copy N, CA, C, O and CB to new residue; exclude CB with mutations to GLY
--for m,n in pairs(new_residue) do print(m, "copied to new residue") end
aa_chosen=document:getElementById("mutate").value
--print("New amino acid chosen:",aa_chosen)
--for k,l in ipairs(sidechain_atoms[aa_chosen]) do print(l) end
car=residue["C "]
cal=residue["CA "]
dx=cal.x-car.x
dy=cal.y-car.y
dz=cal.z-car.z
if resnam~="GLY" then cb=residue["CB "] else cb=residue["CA "] end
for k,l in ipairs(sidechain_atoms[aa_chosen]) do
--print(l)
if l~="CB " then
newat={["x"]=cb.x+(k-1)*dx/1.5, ["y"]=cb.y+(k-1)*dy/1.5, ["z"]=cb.z+(k-1)*dz/1.5}
new_residue[l]=newat
elseif resnam=="GLY" then
nit=residue["N "]
meanx=(nit.x+car.x)/2 meany=(nit.y+car.y)/2 meanz=(nit.z+car.z)/2 --adds CB to a GLY in plane with CA on opposite side of N and C (tetrig and checkChir should sort it).
dmx=cal.x-meanx dmy=cal.y-meany dmz=cal.z-meanz
newat={["x"]=cb.x+dmx*2, ["y"]=cb.y+dmy*2, ["z"]=cb.z+dmz*2}
new_residue[l]=newat
end
end
for j,k in pairs(new_residue) do
--print("Atom:",j)
k["res"]=aa_chosen
k["num"]=cal.num
k["chain"]=cal.chain
k["nsymop"]={0}
k["postrot_shift"]={0,0,0}
--for l,m in pairs(k) do print(l,m) end
end
--for i,j in pairs(new_residue) do print(i) for k,l in pairs(j) do print(k,l) end end
residue=new_residue
resnam=string.sub(residue["CA "].res,1,3)
ncyc=0 --normally needed
sorted=false
repeat mmm() until sorted or ncyc>600
print("Residue:",resnam,startpos,"max delta:",maxdiff,"after", ncyc, "cycles.")
protein[startpos]=residue
document:getElementById("mutate").selectedIndex=0
startmove()
rerun() --needed normally
end
sidechbutton=document:getElementById("viewsidechain")
sidechbutton:addEventListener("click", function() if viewSideCh==false then sidechbutton.innerHTML="Main chain" viewSideCh=true run() else viewSideCh=false sidechbutton.innerHTML="Side chain" run() end end)
stepbutton=document:getElementById("step")
stepbutton:addEventListener("click", function() step(1) end)
stepbutton=document:getElementById("back")
stepbutton:addEventListener("click", function() step(-1) end)
stopbutton=document:getElementById("stop")
stopbutton:addEventListener("click", stop)
mutatebutton=document:getElementById("mutate")
mutatebutton:addEventListener("change", mutate)
movebutton=document:getElementById("move")
movebutton:addEventListener("click", startmove)
savebutton=document:getElementById("tidy")
savebutton:addEventListener("click", tidy)
undobutton=document:getElementById("undo")
undobutton:addEventListener("click", undo)
savebutton=document:getElementById("save")
savebutton:addEventListener("click", save)
pdbinput=document:getElementById("pdbInput")
pdbinput:addEventListener("change", function() readform() readfile() end)
mapinput=document:getElementById("mapInput")
mapinput:addEventListener("change", function() readform() readmapfile() end)
posneg=document:getElementById("densign")
posneg:addEventListener("click", swapdensign)
symtick=document:getElementById("symmetry") --add callbacks to the text- and tick-boxes.
symtick:addEventListener("change", run)
position=document:getElementById("position")
position:addEventListener("change", run)
mapcon=document:getElementById("contour")
mapcon:addEventListener("change", function() if minimap~=nil then if #(minimap)>0 then run() end end end)
canvas:addEventListener("mousedown", function(_,event) mouseDown(event) end)
canvas:addEventListener("mousemove", function(_,event) mouseMove(event) end)
canvas:addEventListener("mouseup", function(_,event) mouseUp(event) end)
canvas:addEventListener("touchstart", function(_,event) touchStart(event) end) -- add ",false" after "end" to stop browser reading swipe events?
canvas:addEventListener("touchmove", function(_,event) touchMove(event) end) -- ditto
canvas:addEventListener("touchend", function(_,event) touchEnd(event) end) -- ditto
-- Functions for drawing symmetry mates
function get_distance(x1,y1,z1,x2,y2,z2)
dx=x2-x1
dy=y2-y1
dz=z2-z1
d1=(a*dx)^2+(b*dy)^2+(c*dz)^2
d2=2*dx*dy*a*b*cos_gamma
d3=2*dy*dz*b*c*cos_alpha
d4=2*dz*dx*c*a*cos_beta
d=math.sqrt(d1+d2+d3+d4)
end
function get_centroid()
if readcell_ok and nCA~=0 then
centx,centy,centz=xtot/nCA,ytot/nCA,ztot/nCA --orthogonal
cent_coord=window:Array(centx,centy,centz)
fract_cent_coord=mathjs:multiply(Linv,cent_coord) --fractional
fcentx,fcenty,fcentz=fract_cent_coord:subset(el1),fract_cent_coord:subset(el2),fract_cent_coord:subset(el3)
originx,originy,originz=math.floor(fcentx),math.floor(fcenty),math.floor(fcentz)
oricoord=window:Array(originx,originy,originz)
centroid=window:Array(fcentx,fcenty,fcentz)
readlog="No. CA atoms read: "..nCA.."Orthogonal coordinates\n"
readlog=readlog.."Min x,y,z:"..string.format("%12.7f %12.7f %12.7f\n",Xmin,Ymin,Zmin)
readlog=readlog.."Max x,y,z:"..string.format("%12.7f %12.7f %12.7f\n",Xmax,Ymax,Zmax)
readlog=readlog.."Centroid: "..string.format("%12.7f %12.7f %12.7f\n",centx,centy,centz)
print(readlog)
fill_symm_box()
elseif readcell_ok then print("No amino acids found.")
else print("No unit cell and symmetry found i.e. the CRYST1 line is missing.") end
end
function fill_symm_box()
if sg~="P 1 " then notP1=true else notP1=false end
rotbox,symm_box,tranlist={},{},{}
wideborder=20 border=boxrad --border on orthogonal axes in Angstroms
nshift=0 chainout=0
steps=1 --tested fine with steps=1 for all 67 space groups below
for ashift=-steps,steps do for bshift=-steps,steps do for cshift=-steps,steps do table.insert(tranlist,{ashift,bshift,cshift}) end end end
total_steps=#(tranlist)
--print("Total number of steps:",total_steps)
if notP1 then
rotations()
else
translations(protein) --with P1 just do translations
end
end
function rotations() --apply SG rotation and translation, get new centroid and new origin.
numrot=#(sg_rotations)
print("Number of rotations:",numrot)
window:setTimeout(function() document:getElementById("info").innerHTML="Please wait <progress value=\"0\" max=\"100\" id=\"progress\" style=\"width:100px;\"></progress>" end, 0)
rotations2(1)
end
function rotations2(k) --rewritten as a recursion to get progress bar to work
rotop=sg_rotations[k]
rotrow1=window:Array(rotop[1][1],rotop[1][2],rotop[1][3])
rotrow2=window:Array(rotop[2][1],rotop[2][2],rotop[2][3])
rotrow3=window:Array(rotop[3][1],rotop[3][2],rotop[3][3])
rotrows=window:Array(rotrow1, rotrow2, rotrow3)
rotat=js.new(mathjs.matrix,rotrows)
trans=sg_translations[k]
trans_array=window:Array(trans[1],trans[2],trans[3])
orthrot=mathjs:chain(L):multiply(rotat):multiply(Linv):done() --order is right
orthtrans=mathjs:multiply(L,trans_array)
newcent=mathjs:chain(rotat):multiply(centroid):add(trans_array):done() --new centroid fractional coords
newxc,newyc,newzc=newcent:subset(el1),newcent:subset(el2),newcent:subset(el3)
newxo,newyo,newzo=math.floor(newxc),math.floor(newyc),math.floor(newzc) --new origin
xshift=originx-newxo yshift=originy-newyo zshift=originz-newzo --shift to move new molecule close to original
newnewxc=newxc+xshift newnewyc=newyc+yshift newnewzc=newzc+zshift --new fractional centroid shifted close to original
dmin=math.huge
for oa=-2,2 do --125 unit cell translations to get the "optimum" position (on a,b,c) of the symmetry mate (i.e. centroid is closest to the original)
for ob=-2,2 do
for oc=-2,2 do
testxc=newnewxc+oa testyc=newnewyc+ob testzc=newnewzc+oc
get_distance(testxc,testyc,testzc,fcentx,fcenty,fcentz)
if d<dmin then dmin=d postrot_shift={xshift+oa,yshift+ob,zshift+oc} end --postrot_shift is always fractional
end
end
end
fract_postrot_shift=window:Array(postrot_shift[1],postrot_shift[2],postrot_shift[3]) --need to orthogonalise the shifts
orth_post_rot_shift=mathjs:multiply(L,fract_postrot_shift)
postRotShiftx=orth_post_rot_shift:subset(el1) postRotShifty=orth_post_rot_shift:subset(el2) postRotShiftz=orth_post_rot_shift:subset(el3)
print("Orthogonal shifts needed after rotation:",k,"are:",postRotShiftx,postRotShifty,postRotShiftz)
for i,res in ipairs(protein) do --&&
residue={}
for j,atom in pairs(res) do
coord=window:Array(atom.x,atom.y,atom.z)
res=atom.res num=atom.num chain=atom.chain --copy out or protein table gets modified
outcoord=mathjs:chain(orthrot):multiply(coord):add(orthtrans):add(orth_post_rot_shift):done()
newx,newy,newz=outcoord:subset(el1),outcoord:subset(el2),outcoord:subset(el3)
newatom={["res"]=res,["num"]=num,["chain"]=chain,["x"]=newx,["y"]=newy,["z"]=newz,["nsymop"]={k},["postrot_shift"]=postrot_shift}
residue[j]=newatom
end
if next(residue)~=nil then table.insert(rotbox,residue) end
end --&&
window:setTimeout(function() --££
prog=math.floor(k/numrot*100)
progbar=document:getElementById("progress")
progbar.value=tostring(prog)
if k<numrot then --##
rotations2(k+1)
else
--Note that no centroid juggling is done for the molecules generated by lattice centering (yet).
if sg:sub(1,1)=="C" then centred_box={} censhift={f12,f12,0} centext={"+(½,½,0)"} addcentre(protein,1) addcentre(rotbox,1) for l,m in ipairs(centred_box) do table.insert(rotbox,m) end end
if sg:sub(1,1)=="I" then centred_box={} censhift={f12,f12,f12} centext={"+(½,½,½)"} addcentre(protein,1) addcentre(rotbox,1) for l,m in ipairs(centred_box) do table.insert(rotbox,m) end end
if sg:sub(1,1)=="F" then centred_box={} fcen={{f12,f12,0},{0,f12,f12},{f12,0,f12}} centext={"+(½,½,0)","+(0,½,½)","+(½,0,½)"} for n,o in ipairs(fcen) do censhift=o addcentre(protein,n) addcentre(rotbox,n) end for l,m in ipairs(centred_box) do table.insert(rotbox,m) end end
translations(protein)
translations(rotbox)
end --##
end, 0) --££
end
function addcentre(thing,ncen)
centring_shift=window:Array(censhift[1],censhift[2],censhift[3]) --orthogonalise the centring shifts
orth_cen_shift=mathjs:multiply(L,centring_shift)
orth_cen_shift_x,orth_cen_shift_y,orth_cen_shift_z=orth_cen_shift:subset(el1),orth_cen_shift:subset(el2),orth_cen_shift:subset(el3)
for i,res in ipairs(thing) do
residue={}
for j,atom in pairs(res) do
newx,newy,newz=atom.x+orth_cen_shift_x,atom.y+orth_cen_shift_y,atom.z+orth_cen_shift_z
res=atom.res num=atom.num chain=atom.chain nsymop=atom.nsymop[1] postrot_shift=atom.postrot_shift --print(nsymop) --copy out or protein table gets modified
newatom={["res"]=res,["num"]=num,["chain"]=chain,["x"]=newx,["y"]=newy,["z"]=newz,["nsymop"]={nsymop,ncen},["postrot_shift"]=postrot_shift}
residue[j]=newatom
end
if next(residue)~=nil then table.insert(centred_box,residue) end
end
end
function translations(thing)
window:setTimeout(function() document:getElementById("info").innerHTML="Please wait <progress value=\"0\" max=\"100\" id=\"progress\" style=\"width:100px;\"></progress>" end, 0)
translations2(thing,1)
end
function translations2(thing,num_tran) --rewritten as a recursion to get out of progress bar hell
ashift,bshift,cshift=tranlist[num_tran][1],tranlist[num_tran][2],tranlist[num_tran][3]
--print(ashift,bshift,cshift)
abc_shift=window:Array(ashift,bshift,cshift)
orth_abc_shift=mathjs:multiply(L,abc_shift)
orth_abc_shift_x,orth_abc_shift_y,orth_abc_shift_z=orth_abc_shift:subset(el1),orth_abc_shift:subset(el2),orth_abc_shift:subset(el3)
nshift=nshift+1
--print("nshift",nshift)
if thing==protein and ashift==0 and bshift==0 and cshift==0 then do end else --#
for i,res in ipairs(thing) do --$
residue={}
_,fastatom=next(res) --first (i.e. random) atom in residue must be close to the border (within wideborder) before doing atom-by-atom scan
newx=fastatom.x+orth_abc_shift_x newy=fastatom.y+orth_abc_shift_y newz=fastatom.z+orth_abc_shift_z
if Xmin-wideborder<=newx and newx<=Xmax+wideborder and Ymin-wideborder<=newy and newy<=Ymax+wideborder and Zmin-wideborder<=newz and newz<=Zmax+wideborder then --!"
for j,atom in pairs(res) do --£
newx=atom.x+orth_abc_shift_x newy=atom.y+orth_abc_shift_y newz=atom.z+orth_abc_shift_z
if Xmin-border<=newx and newx<=Xmax+border and Ymin-border<=newy and newy<=Ymax+border and Zmin-border<=newz and newz<=Zmax+border then
res=atom.res num=atom.num chain=atom.chain nsymop=atom.nsymop postrot_shift=atom.postrot_shift --copy out or protein table gets modified
new_postrot_shift={atom.postrot_shift[1]+ashift, atom.postrot_shift[2]+bshift, atom.postrot_shift[3]+cshift}
--print("Inside box:",atom.res,atom.num,ashift,bshift,cshift)
--if oldnshift~=nshift then oldnshift=nshift chainout=chainout+1 print("chainout",chainout) end
newatom={["res"]=res,["num"]=num,["chain"]=chain,["x"]=newx,["y"]=newy,["z"]=newz,["nsymop"]=nsymop,["postrot_shift"]=new_postrot_shift}
residue[j]=newatom
end
end --£
if next(residue)~=nil then table.insert(symm_box,residue) end
end --!"
end --$
end --#
prog=math.floor(num_tran/total_steps*100)
if num_tran<total_steps then
window:setTimeout(function()
progbar=document:getElementById("progress")
progbar.value=tostring(prog)
translations2(thing,num_tran+1)
end, 0)
elseif thing==rotbox or notP1==false then
symm_box_filled=true
run() --bit ugly to run all of run() but seems necessary when things running asynchronously - more progress bar hell
end
end
f16=1/6 f14=1/4 f13=1/3 f12=1/2 f23=2/3 f34=3/4 f56=5/6
frac_lookup={[f16]="⅙",[f14]="¼",[f13]="⅓",[f12]="½",[f23]="⅔",[f34]="¾",[f56]="⅚"}
symops={
["P 1 2 1 "]={rotas={{{-1,0,0},{0,1,0},{0,0,-1}}},trans={{0,0,0}}},
["P 1 21 1 "]={rotas={{{-1,0,0},{0,1,0},{0,0,-1}}},trans={{0,f12,0}}},
["C 1 2 1 "]={rotas={{{-1,0,0},{0,1,0},{0,0,-1}}},trans={{0,0,0}}},
["I 1 2 1 "]={rotas={{{-1,0,0},{0,1,0},{0,0,-1}}},trans={{0,0,0}}},
["P 21 21 21"]={rotas={{{1,0,0},{0,-1,0},{0,0,-1}},{{-1,0,0},{0,1,0},{0,0,-1}},{{-1,0,0},{0,-1,0},{0,0,1}}},trans={{f12,f12,0},{0,f12,f12},{f12,0,f12}}},
["P 21 21 2 "]={rotas={{{1,0,0},{0,-1,0},{0,0,-1}},{{-1,0,0},{0,1,0},{0,0,-1}},{{-1,0,0},{0,-1,0},{0,0,1}}},trans={{f12,f12,0},{f12,f12,0},{0,0,0}}},
["P 2 21 21 "]={rotas={{{1,0,0},{0,-1,0},{0,0,-1}},{{-1,0,0},{0,1,0},{0,0,-1}},{{-1,0,0},{0,-1,0},{0,0,1}}},trans={{0,0,0},{0,f12,f12},{0,f12,f12}}},
["P 2 2 2 "]={rotas={{{1,0,0},{0,-1,0},{0,0,-1}},{{-1,0,0},{0,1,0},{0,0,-1}},{{-1,0,0},{0,-1,0},{0,0,1}}},trans={{0,0,0},{0,0,0},{0,0,0}}},
["P 2 2 21 "]={rotas={{{1,0,0},{0,-1,0},{0,0,-1}},{{-1,0,0},{0,1,0},{0,0,-1}},{{-1,0,0},{0,-1,0},{0,0,1}}},trans={{0,0,0},{0,0,f12},{0,0,f12}}},
["P 21 2 21 "]={rotas={{{1,0,0},{0,-1,0},{0,0,-1}},{{-1,0,0},{0,1,0},{0,0,-1}},{{-1,0,0},{0,-1,0},{0,0,1}}},trans={{f12,0,f12},{0,0,0},{f12,0,f12}}},
["C 2 2 21 "]={rotas={{{1,0,0},{0,-1,0},{0,0,-1}},{{-1,0,0},{0,1,0},{0,0,-1}},{{-1,0,0},{0,-1,0},{0,0,1}}},trans={{0,0,0},{0,0,f12},{0,0,f12}}},
["C 2 2 2 "]={rotas={{{1,0,0},{0,-1,0},{0,0,-1}},{{-1,0,0},{0,1,0},{0,0,-1}},{{-1,0,0},{0,-1,0},{0,0,1}}},trans={{0,0,0},{0,0,0},{0,0,0}}},
["I 2 2 2 "]={rotas={{{1,0,0},{0,-1,0},{0,0,-1}},{{-1,0,0},{0,1,0},{0,0,-1}},{{-1,0,0},{0,-1,0},{0,0,1}}},trans={{0,0,0},{0,0,0},{0,0,0}}},
["I 21 21 21"]={rotas={{{1,0,0},{0,-1,0},{0,0,-1}},{{-1,0,0},{0,1,0},{0,0,-1}},{{-1,0,0},{0,-1,0},{0,0,1}}},trans={{f12,f12,0},{0,f12,f12},{f12,0,f12}}},
["F 2 2 2 "]={rotas={{{1,0,0},{0,-1,0},{0,0,-1}},{{-1,0,0},{0,1,0},{0,0,-1}},{{-1,0,0},{0,-1,0},{0,0,1}}},trans={{0,0,0},{0,0,0},{0,0,0}}},
["P 4 "]={rotas={{{0,-1,0},{1,0,0},{0,0,1}},{{-1,0,0},{0,-1,0},{0,0,1}},{{0,1,0},{-1,0,0},{0,0,1}}},trans={{0,0,0},{0,0,0},{0,0,0}}},
["P 41 "]={rotas={{{0,-1,0},{1,0,0},{0,0,1}},{{-1,0,0},{0,-1,0},{0,0,1}},{{0,1,0},{-1,0,0},{0,0,1}}},trans={{0,0,f14},{0,0,f12},{0,0,f34}}},
["P 42 "]={rotas={{{0,-1,0},{1,0,0},{0,0,1}},{{-1,0,0},{0,-1,0},{0,0,1}},{{0,1,0},{-1,0,0},{0,0,1}}},trans={{0,0,f12},{0,0,0},{0,0,f12}}},
["P 43 "]={rotas={{{0,-1,0},{1,0,0},{0,0,1}},{{-1,0,0},{0,-1,0},{0,0,1}},{{0,1,0},{-1,0,0},{0,0,1}}},trans={{0,0,f34},{0,0,f12},{0,0,f14}}},
["P 4 2 2 "]={rotas={{{0,-1,0},{1,0,0},{0,0,1}},{{-1,0,0},{0,-1,0},{0,0,1}},{{0,1,0},{-1,0,0},{0,0,1}},{{0,1,0},{1,0,0},{0,0,-1}},{{1,0,0},{0,-1,0},{0,0,-1}},{{0,-1,0},{-1,0,0},{0,0,-1}},{{-1,0,0},{0,1,0},{0,0,-1}}},trans={{0,0,0},{0,0,0},{0,0,0},{0,0,0},{0,0,0},{0,0,0},{0,0,0}}},
["P 41 2 2 "]={rotas={{{0,-1,0},{1,0,0},{0,0,1}},{{-1,0,0},{0,-1,0},{0,0,1}},{{0,1,0},{-1,0,0},{0,0,1}},{{0,1,0},{1,0,0},{0,0,-1}},{{1,0,0},{0,-1,0},{0,0,-1}},{{0,-1,0},{-1,0,0},{0,0,-1}},{{-1,0,0},{0,1,0},{0,0,-1}}},trans={{0,0,f14},{0,0,f12},{0,0,f34},{0,0,f34},{0,0,f12},{0,0,f14},{0,0,0}}},
["P 42 2 2 "]={rotas={{{0,-1,0},{1,0,0},{0,0,1}},{{-1,0,0},{0,-1,0},{0,0,1}},{{0,1,0},{-1,0,0},{0,0,1}},{{0,1,0},{1,0,0},{0,0,-1}},{{1,0,0},{0,-1,0},{0,0,-1}},{{0,-1,0},{-1,0,0},{0,0,-1}},{{-1,0,0},{0,1,0},{0,0,-1}}},trans={{0,0,f12},{0,0,0},{0,0,f12},{0,0,f12},{0,0,0},{0,0,f12},{0,0,0}}},
["P 43 2 2 "]={rotas={{{0,-1,0},{1,0,0},{0,0,1}},{{-1,0,0},{0,-1,0},{0,0,1}},{{0,1,0},{-1,0,0},{0,0,1}},{{0,1,0},{1,0,0},{0,0,-1}},{{1,0,0},{0,-1,0},{0,0,-1}},{{0,-1,0},{-1,0,0},{0,0,-1}},{{-1,0,0},{0,1,0},{0,0,-1}}},trans={{0,0,f34},{0,0,f12},{0,0,f14},{0,0,f14},{0,0,f12},{0,0,f34},{0,0,0}}},
["P 4 21 2 "]={rotas={{{0,-1,0},{1,0,0},{0,0,1}},{{-1,0,0},{0,-1,0},{0,0,1}},{{0,1,0},{-1,0,0},{0,0,1}},{{0,1,0},{1,0,0},{0,0,-1}},{{1,0,0},{0,-1,0},{0,0,-1}},{{0,-1,0},{-1,0,0},{0,0,-1}},{{-1,0,0},{0,1,0},{0,0,-1}}},trans={{f12,f12,0},{0,0,0},{f12,f12,0},{0,0,0},{f12,f12,0},{0,0,0},{f12,f12,0}}},
["P 41 21 2 "]={rotas={{{0,-1,0},{1,0,0},{0,0,1}},{{-1,0,0},{0,-1,0},{0,0,1}},{{0,1,0},{-1,0,0},{0,0,1}},{{0,1,0},{1,0,0},{0,0,-1}},{{1,0,0},{0,-1,0},{0,0,-1}},{{0,-1,0},{-1,0,0},{0,0,-1}},{{-1,0,0},{0,1,0},{0,0,-1}}},trans={{f12,f12,f14},{0,0,f12},{f12,f12,f34},{0,0,0},{f12,f12,f34},{0,0,f12},{f12,f12,f14}}},
["P 42 21 2 "]={rotas={{{0,-1,0},{1,0,0},{0,0,1}},{{-1,0,0},{0,-1,0},{0,0,1}},{{0,1,0},{-1,0,0},{0,0,1}},{{0,1,0},{1,0,0},{0,0,-1}},{{1,0,0},{0,-1,0},{0,0,-1}},{{0,-1,0},{-1,0,0},{0,0,-1}},{{-1,0,0},{0,1,0},{0,0,-1}}},trans={{f12,f12,f12},{0,0,0},{f12,f12,f12},{0,0,0},{f12,f12,f12},{0,0,0},{f12,f12,f12}}},
["P 43 21 2 "]={rotas={{{0,-1,0},{1,0,0},{0,0,1}},{{-1,0,0},{0,-1,0},{0,0,1}},{{0,1,0},{-1,0,0},{0,0,1}},{{0,1,0},{1,0,0},{0,0,-1}},{{1,0,0},{0,-1,0},{0,0,-1}},{{0,-1,0},{-1,0,0},{0,0,-1}},{{-1,0,0},{0,1,0},{0,0,-1}}},trans={{f12,f12,f34},{0,0,f12},{f12,f12,f14},{0,0,0},{f12,f12,f14},{0,0,f12},{f12,f12,f34}}},
["I 4 "]={rotas={{{0,-1,0},{1,0,0},{0,0,1}},{{-1,0,0},{0,-1,0},{0,0,1}},{{0,1,0},{-1,0,0},{0,0,1}}},trans={{0,0,0},{0,0,0},{0,0,0}}},
["I 41 "]={rotas={{{0,-1,0},{1,0,0},{0,0,1}},{{-1,0,0},{0,-1,0},{0,0,1}},{{0,1,0},{-1,0,0},{0,0,1}}},trans={{0,f12,f14},{f12,f12,f12},{f12,0,f34}}},
["I 4 2 2 "]={rotas={{{0,-1,0},{1,0,0},{0,0,1}},{{-1,0,0},{0,-1,0},{0,0,1}},{{0,1,0},{-1,0,0},{0,0,1}},{{0,1,0},{1,0,0},{0,0,-1}},{{1,0,0},{0,-1,0},{0,0,-1}},{{0,-1,0},{-1,0,0},{0,0,-1}},{{-1,0,0},{0,1,0},{0,0,-1}}},trans={{0,0,0},{0,0,0},{0,0,0},{0,0,0},{0,0,0},{0,0,0},{0,0,0}}},
["I 41 2 2 "]={rotas={{{0,-1,0},{1,0,0},{0,0,1}},{{-1,0,0},{0,-1,0},{0,0,1}},{{0,1,0},{-1,0,0},{0,0,1}},{{0,1,0},{1,0,0},{0,0,-1}},{{1,0,0},{0,-1,0},{0,0,-1}},{{0,-1,0},{-1,0,0},{0,0,-1}},{{-1,0,0},{0,1,0},{0,0,-1}}},trans={{0,f12,f14},{f12,f12,f12},{f12,0,f34},{0,0,0},{f12,0,f34},{f12,f12,f12},{0,f12,f14}}},
["P 6 "]={rotas={{{1,-1,0},{1,0,0},{0,0,1}},{{0,-1,0},{1,-1,0},{0,0,1}},{{-1,0,0},{0,-1,0},{0,0,1}},{{-1,1,0},{-1,0,0},{0,0,1}},{{0,1,0},{-1,1,0},{0,0,1}}},trans={{0,0,0},{0,0,0},{0,0,0},{0,0,0},{0,0,0}}},
["P 61 "]={rotas={{{1,-1,0},{1,0,0},{0,0,1}},{{0,-1,0},{1,-1,0},{0,0,1}},{{-1,0,0},{0,-1,0},{0,0,1}},{{-1,1,0},{-1,0,0},{0,0,1}},{{0,1,0},{-1,1,0},{0,0,1}}},trans={{0,0,f16},{0,0,f13},{0,0,f12},{0,0,f23},{0,0,f56}}},
["P 62 "]={rotas={{{1,-1,0},{1,0,0},{0,0,1}},{{0,-1,0},{1,-1,0},{0,0,1}},{{-1,0,0},{0,-1,0},{0,0,1}},{{-1,1,0},{-1,0,0},{0,0,1}},{{0,1,0},{-1,1,0},{0,0,1}}},trans={{0,0,f13},{0,0,f23},{0,0,0},{0,0,f13},{0,0,f23}}},
["P 63 "]={rotas={{{1,-1,0},{1,0,0},{0,0,1}},{{0,-1,0},{1,-1,0},{0,0,1}},{{-1,0,0},{0,-1,0},{0,0,1}},{{-1,1,0},{-1,0,0},{0,0,1}},{{0,1,0},{-1,1,0},{0,0,1}}},trans={{0,0,f12},{0,0,0},{0,0,f12},{0,0,0},{0,0,f12}}},
["P 64 "]={rotas={{{1,-1,0},{1,0,0},{0,0,1}},{{0,-1,0},{1,-1,0},{0,0,1}},{{-1,0,0},{0,-1,0},{0,0,1}},{{-1,1,0},{-1,0,0},{0,0,1}},{{0,1,0},{-1,1,0},{0,0,1}}},trans={{0,0,f23},{0,0,f13},{0,0,0},{0,0,f23},{0,0,f13}}},
["P 65 "]={rotas={{{1,-1,0},{1,0,0},{0,0,1}},{{0,-1,0},{1,-1,0},{0,0,1}},{{-1,0,0},{0,-1,0},{0,0,1}},{{-1,1,0},{-1,0,0},{0,0,1}},{{0,1,0},{-1,1,0},{0,0,1}}},trans={{0,0,f56},{0,0,f23},{0,0,f12},{0,0,f13},{0,0,f16}}},
["P 6 2 2 "]={rotas={{{1,-1,0},{1,0,0},{0,0,1}},{{0,-1,0},{1,-1,0},{0,0,1}},{{-1,0,0},{0,-1,0},{0,0,1}},{{-1,1,0},{-1,0,0},{0,0,1}},{{0,1,0},{-1,1,0},{0,0,1}},{{0,1,0},{1,0,0},{0,0,-1}},{{1,0,0},{1,-1,0},{0,0,-1}},{{1,-1,0},{0,-1,0},{0,0,-1}},{{0,-1,0},{-1,0,0},{0,0,-1}},{{-1,0,0},{-1,1,0},{0,0,-1}},{{-1,1,0},{0,1,0},{0,0,-1}}},trans={{0,0,0},{0,0,0},{0,0,0},{0,0,0},{0,0,0},{0,0,0},{0,0,0},{0,0,0},{0,0,0},{0,0,0},{0,0,0}}},
["P 61 2 2 "]={rotas={{{1,-1,0},{1,0,0},{0,0,1}},{{0,-1,0},{1,-1,0},{0,0,1}},{{-1,0,0},{0,-1,0},{0,0,1}},{{-1,1,0},{-1,0,0},{0,0,1}},{{0,1,0},{-1,1,0},{0,0,1}},{{0,1,0},{1,0,0},{0,0,-1}},{{1,0,0},{1,-1,0},{0,0,-1}},{{1,-1,0},{0,-1,0},{0,0,-1}},{{0,-1,0},{-1,0,0},{0,0,-1}},{{-1,0,0},{-1,1,0},{0,0,-1}},{{-1,1,0},{0,1,0},{0,0,-1}}},trans={{0,0,f16},{0,0,f13},{0,0,f12},{0,0,f23},{0,0,f56},{0,0,f13},{0,0,f16},{0,0,0},{0,0,f56},{0,0,f23},{0,0,f12}}},
["P 62 2 2 "]={rotas={{{1,-1,0},{1,0,0},{0,0,1}},{{0,-1,0},{1,-1,0},{0,0,1}},{{-1,0,0},{0,-1,0},{0,0,1}},{{-1,1,0},{-1,0,0},{0,0,1}},{{0,1,0},{-1,1,0},{0,0,1}},{{0,1,0},{1,0,0},{0,0,-1}},{{1,0,0},{1,-1,0},{0,0,-1}},{{1,-1,0},{0,-1,0},{0,0,-1}},{{0,-1,0},{-1,0,0},{0,0,-1}},{{-1,0,0},{-1,1,0},{0,0,-1}},{{-1,1,0},{0,1,0},{0,0,-1}}},trans={{0,0,f13},{0,0,f23},{0,0,0},{0,0,f13},{0,0,f23},{0,0,f23},{0,0,f13},{0,0,0},{0,0,f23},{0,0,f13},{0,0,0}}},
["P 63 2 2 "]={rotas={{{1,-1,0},{1,0,0},{0,0,1}},{{0,-1,0},{1,-1,0},{0,0,1}},{{-1,0,0},{0,-1,0},{0,0,1}},{{-1,1,0},{-1,0,0},{0,0,1}},{{0,1,0},{-1,1,0},{0,0,1}},{{0,1,0},{1,0,0},{0,0,-1}},{{1,0,0},{1,-1,0},{0,0,-1}},{{1,-1,0},{0,-1,0},{0,0,-1}},{{0,-1,0},{-1,0,0},{0,0,-1}},{{-1,0,0},{-1,1,0},{0,0,-1}},{{-1,1,0},{0,1,0},{0,0,-1}}},trans={{0,0,f12},{0,0,0},{0,0,f12},{0,0,0},{0,0,f12},{0,0,0},{0,0,f12},{0,0,0},{0,0,f12},{0,0,0},{0,0,f12}}},
["P 64 2 2 "]={rotas={{{1,-1,0},{1,0,0},{0,0,1}},{{0,-1,0},{1,-1,0},{0,0,1}},{{-1,0,0},{0,-1,0},{0,0,1}},{{-1,1,0},{-1,0,0},{0,0,1}},{{0,1,0},{-1,1,0},{0,0,1}},{{0,1,0},{1,0,0},{0,0,-1}},{{1,0,0},{1,-1,0},{0,0,-1}},{{1,-1,0},{0,-1,0},{0,0,-1}},{{0,-1,0},{-1,0,0},{0,0,-1}},{{-1,0,0},{-1,1,0},{0,0,-1}},{{-1,1,0},{0,1,0},{0,0,-1}}},trans={{0,0,f23},{0,0,f13},{0,0,0},{0,0,f23},{0,0,f13},{0,0,f13},{0,0,f23},{0,0,0},{0,0,f13},{0,0,f23},{0,0,0}}},
["P 65 2 2 "]={rotas={{{1,-1,0},{1,0,0},{0,0,1}},{{0,-1,0},{1,-1,0},{0,0,1}},{{-1,0,0},{0,-1,0},{0,0,1}},{{-1,1,0},{-1,0,0},{0,0,1}},{{0,1,0},{-1,1,0},{0,0,1}},{{0,1,0},{1,0,0},{0,0,-1}},{{1,0,0},{1,-1,0},{0,0,-1}},{{1,-1,0},{0,-1,0},{0,0,-1}},{{0,-1,0},{-1,0,0},{0,0,-1}},{{-1,0,0},{-1,1,0},{0,0,-1}},{{-1,1,0},{0,1,0},{0,0,-1}}},trans={{0,0,f56},{0,0,f23},{0,0,f12},{0,0,f13},{0,0,f16},{0,0,f23},{0,0,f56},{0,0,0},{0,0,f16},{0,0,f13},{0,0,f12}}},
["P 3 "]={rotas={{{0,-1,0},{1,-1,0},{0,0,1}},{{-1,1,0},{-1,0,0},{0,0,1}}},trans={{0,0,0},{0,0,0}}},
["P 31 "]={rotas={{{0,-1,0},{1,-1,0},{0,0,1}},{{-1,1,0},{-1,0,0},{0,0,1}}},trans={{0,0,f13},{0,0,f23}}},
["P 32 "]={rotas={{{0,-1,0},{1,-1,0},{0,0,1}},{{-1,1,0},{-1,0,0},{0,0,1}}},trans={{0,0,f23},{0,0,f13}}},
["P 3 2 1 "]={rotas={{{0,-1,0},{1,-1,0},{0,0,1}},{{-1,1,0},{-1,0,0},{0,0,1}},{{0,1,0},{1,0,0},{0,0,-1}},{{1,-1,0},{0,-1,0},{0,0,-1}},{{-1,0,0},{-1,1,0},{0,0,-1}}},trans={{0,0,0},{0,0,0},{0,0,0},{0,0,0},{0,0,0}}},
["P 31 2 1 "]={rotas={{{0,-1,0},{1,-1,0},{0,0,1}},{{-1,1,0},{-1,0,0},{0,0,1}},{{0,1,0},{1,0,0},{0,0,-1}},{{1,-1,0},{0,-1,0},{0,0,-1}},{{-1,0,0},{-1,1,0},{0,0,-1}}},trans={{0,0,f13},{0,0,f23},{0,0,0},{0,0,f23},{0,0,f13}}},
["P 32 2 1 "]={rotas={{{0,-1,0},{1,-1,0},{0,0,1}},{{-1,1,0},{-1,0,0},{0,0,1}},{{0,1,0},{1,0,0},{0,0,-1}},{{1,-1,0},{0,-1,0},{0,0,-1}},{{-1,0,0},{-1,1,0},{0,0,-1}}},trans={{0,0,f23},{0,0,f13},{0,0,0},{0,0,f13},{0,0,f23}}},
["P 3 1 2 "]={rotas={{{0,-1,0},{1,-1,0},{0,0,1}},{{-1,1,0},{-1,0,0},{0,0,1}},{{0,-1,0},{-1,0,0},{0,0,-1}},{{-1,1,0},{0,1,0},{0,0,-1}},{{1,0,0},{1,-1,0},{0,0,-1}}},trans={{0,0,0},{0,0,0},{0,0,0},{0,0,0},{0,0,0}}},
["P 31 1 2 "]={rotas={{{0,-1,0},{1,-1,0},{0,0,1}},{{-1,1,0},{-1,0,0},{0,0,1}},{{0,-1,0},{-1,0,0},{0,0,-1}},{{-1,1,0},{0,1,0},{0,0,-1}},{{1,0,0},{1,-1,0},{0,0,-1}}},trans={{0,0,f13},{0,0,f23},{0,0,f23},{0,0,f13},{0,0,0}}},
["P 32 1 2 "]={rotas={{{0,-1,0},{1,-1,0},{0,0,1}},{{-1,1,0},{-1,0,0},{0,0,1}},{{0,-1,0},{-1,0,0},{0,0,-1}},{{-1,1,0},{0,1,0},{0,0,-1}},{{1,0,0},{1,-1,0},{0,0,-1}}},trans={{0,0,f23},{0,0,f13},{0,0,f13},{0,0,f23},{0,0,0}}},
["H 3 "]={rotas={{{0,-1,0},{1,-1,0},{0,0,1}},{{-1,1,0},{-1,0,0},{0,0,1}}, {{1,0,0},{0,1,0},{0,0,1}},{{0,-1,0},{1,-1,0},{0,0,1}},{{-1,1,0},{-1,0,0},{0,0,1}}, {{1,0,0},{0,1,0},{0,0,1}},{{0,-1,0},{1,-1,0},{0,0,1}},{{-1,1,0},{-1,0,0},{0,0,1}}},trans={{0,0,0},{0,0,0},{f23,f13,f13},{f23,f13,f13},{f23,f13,f13}, {f13,f23,f23},{f13,f23,f23},{f13,f23,f23} }},
["H 3 2 "]={rotas={{{0,-1,0},{1,-1,0},{0,0,1}},{{-1,1,0},{-1,0,0},{0,0,1}},{{0,1,0},{1,0,0},{0,0,-1}},{{1,-1,0},{0,-1,0},{0,0,-1}},{{-1,0,0},{-1,1,0},{0,0,-1}},{{1,0,0},{0,1,0},{0,0,1}},{{0,-1,0},{1,-1,0},{0,0,1}},{{-1,1,0},{-1,0,0},{0,0,1}},{{0,1,0},{1,0,0},{0,0,-1}},{{1,-1,0},{0,-1,0},{0,0,-1}},{{-1,0,0},{-1,1,0},{0,0,-1}},{{1,0,0},{0,1,0},{0,0,1}},{{0,-1,0},{1,-1,0},{0,0,1}},{{-1,1,0},{-1,0,0},{0,0,1}},{{0,1,0},{1,0,0},{0,0,-1}},{{1,-1,0},{0,-1,0},{0,0,-1}},{{-1,0,0},{-1,1,0},{0,0,-1}}},trans={{0,0,0},{0,0,0},{0,0,0},{0,0,0},{0,0,0},{f23,f13,f13},{f23,f13,f13},{f23,f13,f13},{f23,f13,f13},{f23,f13,f13},{f23,f13,f13},{f13,f23,f23},{f13,f23,f23},{f13,f23,f23},{f13,f23,f23},{f13,f23,f23},{f13,f23,f23}}},
["P 2 3 "]={rotas={{{1,0,0},{0,-1,0},{0,0,-1}},{{-1,0,0},{0,1,0},{0,0,-1}},{{-1,0,0},{0,-1,0},{0,0,1}},{{0,1,0},{0,0,1},{1,0,0}},{{0,-1,0},{0,0,-1},{1,0,0}},{{0,1,0},{0,0,-1},{-1,0,0}},{{0,-1,0},{0,0,1},{-1,0,0}},{{0,0,1},{1,0,0},{0,1,0}},{{0,0,-1},{1,0,0},{0,-1,0}},{{0,0,-1},{-1,0,0},{0,1,0}},{{0,0,1},{-1,0,0},{0,-1,0}}},trans={{0,0,0},{0,0,0},{0,0,0},{0,0,0},{0,0,0},{0,0,0},{0,0,0},{0,0,0},{0,0,0},{0,0,0},{0,0,0}}},
["P 21 3 "]={rotas={{{1,0,0},{0,-1,0},{0,0,-1}},{{-1,0,0},{0,1,0},{0,0,-1}},{{-1,0,0},{0,-1,0},{0,0,1}},{{0,1,0},{0,0,1},{1,0,0}},{{0,-1,0},{0,0,-1},{1,0,0}},{{0,1,0},{0,0,-1},{-1,0,0}},{{0,-1,0},{0,0,1},{-1,0,0}},{{0,0,1},{1,0,0},{0,1,0}},{{0,0,-1},{1,0,0},{0,-1,0}},{{0,0,-1},{-1,0,0},{0,1,0}},{{0,0,1},{-1,0,0},{0,-1,0}}},trans={{f12,f12,0},{0,f12,f12},{f12,0,f12},{0,0,0},{f12,0,f12},{f12,f12,0},{0,f12,f12},{0,0,0},{0,f12,f12},{f12,0,f12},{f12,f12,0}}},
["I 2 3 "]={rotas={{{1,0,0},{0,-1,0},{0,0,-1}},{{-1,0,0},{0,1,0},{0,0,-1}},{{-1,0,0},{0,-1,0},{0,0,1}},{{0,1,0},{0,0,1},{1,0,0}},{{0,-1,0},{0,0,-1},{1,0,0}},{{0,1,0},{0,0,-1},{-1,0,0}},{{0,-1,0},{0,0,1},{-1,0,0}},{{0,0,1},{1,0,0},{0,1,0}},{{0,0,-1},{1,0,0},{0,-1,0}},{{0,0,-1},{-1,0,0},{0,1,0}},{{0,0,1},{-1,0,0},{0,-1,0}}},trans={{0,0,0},{0,0,0},{0,0,0},{0,0,0},{0,0,0},{0,0,0},{0,0,0},{0,0,0},{0,0,0},{0,0,0},{0,0,0}}},
["I 21 3 "]={rotas={{{1,0,0},{0,-1,0},{0,0,-1}},{{-1,0,0},{0,1,0},{0,0,-1}},{{-1,0,0},{0,-1,0},{0,0,1}},{{0,1,0},{0,0,1},{1,0,0}},{{0,-1,0},{0,0,-1},{1,0,0}},{{0,1,0},{0,0,-1},{-1,0,0}},{{0,-1,0},{0,0,1},{-1,0,0}},{{0,0,1},{1,0,0},{0,1,0}},{{0,0,-1},{1,0,0},{0,-1,0}},{{0,0,-1},{-1,0,0},{0,1,0}},{{0,0,1},{-1,0,0},{0,-1,0}}},trans={{f12,f12,0},{0,f12,f12},{f12,0,f12},{0,0,0},{f12,0,f12},{f12,f12,0},{0,f12,f12},{0,0,0},{0,f12,f12},{f12,0,f12},{f12,f12,0}}},
["F 2 3 "]={rotas={{{1,0,0},{0,-1,0},{0,0,-1}},{{-1,0,0},{0,1,0},{0,0,-1}},{{-1,0,0},{0,-1,0},{0,0,1}},{{0,1,0},{0,0,1},{1,0,0}},{{0,-1,0},{0,0,-1},{1,0,0}},{{0,1,0},{0,0,-1},{-1,0,0}},{{0,-1,0},{0,0,1},{-1,0,0}},{{0,0,1},{1,0,0},{0,1,0}},{{0,0,-1},{1,0,0},{0,-1,0}},{{0,0,-1},{-1,0,0},{0,1,0}},{{0,0,1},{-1,0,0},{0,-1,0}}},trans={{0,0,0},{0,0,0},{0,0,0},{0,0,0},{0,0,0},{0,0,0},{0,0,0},{0,0,0},{0,0,0},{0,0,0},{0,0,0}}},
["P 4 3 2 "]={rotas={{{1,0,0},{0,-1,0},{0,0,-1}},{{-1,0,0},{0,1,0},{0,0,-1}},{{-1,0,0},{0,-1,0},{0,0,1}},{{0,1,0},{0,0,1},{1,0,0}},{{0,-1,0},{0,0,-1},{1,0,0}},{{0,1,0},{0,0,-1},{-1,0,0}},{{0,-1,0},{0,0,1},{-1,0,0}},{{0,0,1},{1,0,0},{0,1,0}},{{0,0,-1},{1,0,0},{0,-1,0}},{{0,0,-1},{-1,0,0},{0,1,0}},{{0,0,1},{-1,0,0},{0,-1,0}},
{{0,1,0},{1,0,0},{0,0,-1}},{{0,-1,0},{1,0,0},{0,0,1}},{{0,1,0},{-1,0,0},{0,0,1}},{{0,-1,0},{-1,0,0},{0,0,-1}},{{0,0,1},{0,1,0},{-1,0,0}},{{0,0,-1},{0,-1,0},{-1,0,0}},{{0,0,-1},{0,1,0},{1,0,0}},{{0,0,1},{0,-1,0},{1,0,0}},{{1,0,0},{0,0,1},{0,-1,0}},{{1,0,0},{0,0,-1},{0,1,0}},{{-1,0,0},{0,0,-1},{0,-1,0}},{{-1,0,0},{0,0,1},{0,1,0}}},
trans={{0,0,0},{0,0,0},{0,0,0},{0,0,0},{0,0,0},{0,0,0},{0,0,0},{0,0,0},{0,0,0},{0,0,0},{0,0,0},{0,0,0},{0,0,0},{0,0,0},{0,0,0},{0,0,0},{0,0,0},{0,0,0},{0,0,0},{0,0,0},{0,0,0},{0,0,0},{0,0,0}}},
["P 41 3 2 "]={rotas={{{1,0,0},{0,-1,0},{0,0,-1}},{{-1,0,0},{0,1,0},{0,0,-1}},{{-1,0,0},{0,-1,0},{0,0,1}},{{0,1,0},{0,0,1},{1,0,0}},{{0,-1,0},{0,0,-1},{1,0,0}},{{0,1,0},{0,0,-1},{-1,0,0}},{{0,-1,0},{0,0,1},{-1,0,0}},{{0,0,1},{1,0,0},{0,1,0}},{{0,0,-1},{1,0,0},{0,-1,0}},{{0,0,-1},{-1,0,0},{0,1,0}},{{0,0,1},{-1,0,0},{0,-1,0}},
{{0,1,0},{1,0,0},{0,0,-1}},{{0,-1,0},{1,0,0},{0,0,1}},{{0,1,0},{-1,0,0},{0,0,1}},{{0,-1,0},{-1,0,0},{0,0,-1}},{{0,0,1},{0,1,0},{-1,0,0}},{{0,0,-1},{0,-1,0},{-1,0,0}},{{0,0,-1},{0,1,0},{1,0,0}},{{0,0,1},{0,-1,0},{1,0,0}},{{1,0,0},{0,0,1},{0,-1,0}},{{1,0,0},{0,0,-1},{0,1,0}},{{-1,0,0},{0,0,-1},{0,-1,0}},{{-1,0,0},{0,0,1},{0,1,0}}},
trans={{f12,f12,0},{0,f12,f12},{f12,0,f12},{0,0,0},{f12,0,f12},{f12,f12,0},{0,f12,f12},{0,0,0},{0,f12,f12},{f12,0,f12},{f12,f12,0},{f34,f14,f14},{f14,f34,f14},{f14,f14,f34},{f34,f34,f34},{f34,f14,f14},{f34,f34,f34},{f14,f34,f14},{f14,f14,f34},{f34,f14,f14},{f14,f14,f34},{f34,f34,f34},{f14,f34,f14}}},
["P 42 3 2 "]={rotas={{{1,0,0},{0,-1,0},{0,0,-1}},{{-1,0,0},{0,1,0},{0,0,-1}},{{-1,0,0},{0,-1,0},{0,0,1}},{{0,1,0},{0,0,1},{1,0,0}},{{0,-1,0},{0,0,-1},{1,0,0}},{{0,1,0},{0,0,-1},{-1,0,0}},{{0,-1,0},{0,0,1},{-1,0,0}},{{0,0,1},{1,0,0},{0,1,0}},{{0,0,-1},{1,0,0},{0,-1,0}},{{0,0,-1},{-1,0,0},{0,1,0}},{{0,0,1},{-1,0,0},{0,-1,0}},
{{0,1,0},{1,0,0},{0,0,-1}},{{0,-1,0},{1,0,0},{0,0,1}},{{0,1,0},{-1,0,0},{0,0,1}},{{0,-1,0},{-1,0,0},{0,0,-1}},{{0,0,1},{0,1,0},{-1,0,0}},{{0,0,-1},{0,-1,0},{-1,0,0}},{{0,0,-1},{0,1,0},{1,0,0}},{{0,0,1},{0,-1,0},{1,0,0}},{{1,0,0},{0,0,1},{0,-1,0}},{{1,0,0},{0,0,-1},{0,1,0}},{{-1,0,0},{0,0,-1},{0,-1,0}},{{-1,0,0},{0,0,1},{0,1,0}}},
trans={{0,0,0},{0,0,0},{0,0,0},{0,0,0},{0,0,0},{0,0,0},{0,0,0},{0,0,0},{0,0,0},{0,0,0},{0,0,0},{f12,f12,f12},{f12,f12,f12},{f12,f12,f12},{f12,f12,f12},{f12,f12,f12},{f12,f12,f12},{f12,f12,f12},{f12,f12,f12},{f12,f12,f12},{f12,f12,f12},{f12,f12,f12},{f12,f12,f12}}},
["P 43 3 2 "]={rotas={{{1,0,0},{0,-1,0},{0,0,-1}},{{-1,0,0},{0,1,0},{0,0,-1}},{{-1,0,0},{0,-1,0},{0,0,1}},{{0,1,0},{0,0,1},{1,0,0}},{{0,-1,0},{0,0,-1},{1,0,0}},{{0,1,0},{0,0,-1},{-1,0,0}},{{0,-1,0},{0,0,1},{-1,0,0}},{{0,0,1},{1,0,0},{0,1,0}},{{0,0,-1},{1,0,0},{0,-1,0}},{{0,0,-1},{-1,0,0},{0,1,0}},{{0,0,1},{-1,0,0},{0,-1,0}},
{{0,1,0},{1,0,0},{0,0,-1}},{{0,-1,0},{1,0,0},{0,0,1}},{{0,1,0},{-1,0,0},{0,0,1}},{{0,-1,0},{-1,0,0},{0,0,-1}},{{0,0,1},{0,1,0},{-1,0,0}},{{0,0,-1},{0,-1,0},{-1,0,0}},{{0,0,-1},{0,1,0},{1,0,0}},{{0,0,1},{0,-1,0},{1,0,0}},{{1,0,0},{0,0,1},{0,-1,0}},{{1,0,0},{0,0,-1},{0,1,0}},{{-1,0,0},{0,0,-1},{0,-1,0}},{{-1,0,0},{0,0,1},{0,1,0}}},
trans={{f12,f12,0},{0,f12,f12},{f12,0,f12},{0,0,0},{f12,0,f12},{f12,f12,0},{0,f12,f12},{0,0,0},{0,f12,f12},{f12,0,f12},{f12,f12,0},{f14,f34,f34},{f34,f14,f34},{f34,f34,f14},{f14,f14,f14},{f14,f34,f34},{f14,f14,f14},{f34,f14,f34},{f34,f34,f14},{f14,f34,f34},{f34,f34,f14},{f14,f14,f14},{f34,f14,f34}}},
["I 4 3 2 "]={rotas={{{1,0,0},{0,-1,0},{0,0,-1}},{{-1,0,0},{0,1,0},{0,0,-1}},{{-1,0,0},{0,-1,0},{0,0,1}},{{0,1,0},{0,0,1},{1,0,0}},{{0,-1,0},{0,0,-1},{1,0,0}},{{0,1,0},{0,0,-1},{-1,0,0}},{{0,-1,0},{0,0,1},{-1,0,0}},{{0,0,1},{1,0,0},{0,1,0}},{{0,0,-1},{1,0,0},{0,-1,0}},{{0,0,-1},{-1,0,0},{0,1,0}},{{0,0,1},{-1,0,0},{0,-1,0}},
{{0,1,0},{1,0,0},{0,0,-1}},{{0,-1,0},{1,0,0},{0,0,1}},{{0,1,0},{-1,0,0},{0,0,1}},{{0,-1,0},{-1,0,0},{0,0,-1}},{{0,0,1},{0,1,0},{-1,0,0}},{{0,0,-1},{0,-1,0},{-1,0,0}},{{0,0,-1},{0,1,0},{1,0,0}},{{0,0,1},{0,-1,0},{1,0,0}},{{1,0,0},{0,0,1},{0,-1,0}},{{1,0,0},{0,0,-1},{0,1,0}},{{-1,0,0},{0,0,-1},{0,-1,0}},{{-1,0,0},{0,0,1},{0,1,0}}},
trans={{0,0,0},{0,0,0},{0,0,0},{0,0,0},{0,0,0},{0,0,0},{0,0,0},{0,0,0},{0,0,0},{0,0,0},{0,0,0},{0,0,0},{0,0,0},{0,0,0},{0,0,0},{0,0,0},{0,0,0},{0,0,0},{0,0,0},{0,0,0},{0,0,0},{0,0,0},{0,0,0}}},
["I 41 3 2 "]={rotas={{{1,0,0},{0,-1,0},{0,0,-1}},{{-1,0,0},{0,1,0},{0,0,-1}},{{-1,0,0},{0,-1,0},{0,0,1}},{{0,1,0},{0,0,1},{1,0,0}},{{0,-1,0},{0,0,-1},{1,0,0}},{{0,1,0},{0,0,-1},{-1,0,0}},{{0,-1,0},{0,0,1},{-1,0,0}},{{0,0,1},{1,0,0},{0,1,0}},{{0,0,-1},{1,0,0},{0,-1,0}},{{0,0,-1},{-1,0,0},{0,1,0}},{{0,0,1},{-1,0,0},{0,-1,0}},
{{0,1,0},{1,0,0},{0,0,-1}},{{0,-1,0},{1,0,0},{0,0,1}},{{0,1,0},{-1,0,0},{0,0,1}},{{0,-1,0},{-1,0,0},{0,0,-1}},{{0,0,1},{0,1,0},{-1,0,0}},{{0,0,-1},{0,-1,0},{-1,0,0}},{{0,0,-1},{0,1,0},{1,0,0}},{{0,0,1},{0,-1,0},{1,0,0}},{{1,0,0},{0,0,1},{0,-1,0}},{{1,0,0},{0,0,-1},{0,1,0}},{{-1,0,0},{0,0,-1},{0,-1,0}},{{-1,0,0},{0,0,1},{0,1,0}}},
trans={{f12,f12,0},{0,f12,f12},{f12,0,f12},{0,0,0},{f12,0,f12},{f12,f12,0},{0,f12,f12},{0,0,0},{0,f12,f12},{f12,0,f12},{f12,f12,0},{f14,f34,f34},{f34,f14,f34},{f34,f34,f14},{f14,f14,f14},{f14,f34,f34},{f14,f14,f14},{f34,f14,f34},{f34,f34,f14},{f14,f34,f34},{f34,f34,f14},{f14,f14,f14},{f34,f14,f34}}},
["F 4 3 2 "]={rotas={{{1,0,0},{0,-1,0},{0,0,-1}},{{-1,0,0},{0,1,0},{0,0,-1}},{{-1,0,0},{0,-1,0},{0,0,1}},{{0,1,0},{0,0,1},{1,0,0}},{{0,-1,0},{0,0,-1},{1,0,0}},{{0,1,0},{0,0,-1},{-1,0,0}},{{0,-1,0},{0,0,1},{-1,0,0}},{{0,0,1},{1,0,0},{0,1,0}},{{0,0,-1},{1,0,0},{0,-1,0}},{{0,0,-1},{-1,0,0},{0,1,0}},{{0,0,1},{-1,0,0},{0,-1,0}},
{{0,1,0},{1,0,0},{0,0,-1}},{{0,-1,0},{1,0,0},{0,0,1}},{{0,1,0},{-1,0,0},{0,0,1}},{{0,-1,0},{-1,0,0},{0,0,-1}},{{0,0,1},{0,1,0},{-1,0,0}},{{0,0,-1},{0,-1,0},{-1,0,0}},{{0,0,-1},{0,1,0},{1,0,0}},{{0,0,1},{0,-1,0},{1,0,0}},{{1,0,0},{0,0,1},{0,-1,0}},{{1,0,0},{0,0,-1},{0,1,0}},{{-1,0,0},{0,0,-1},{0,-1,0}},{{-1,0,0},{0,0,1},{0,1,0}}},
trans={{0,0,0},{0,0,0},{0,0,0},{0,0,0},{0,0,0},{0,0,0},{0,0,0},{0,0,0},{0,0,0},{0,0,0},{0,0,0},{0,0,0},{0,0,0},{0,0,0},{0,0,0},{0,0,0},{0,0,0},{0,0,0},{0,0,0},{0,0,0},{0,0,0},{0,0,0},{0,0,0}}},
["F 41 3 2 "]={rotas={{{1,0,0},{0,-1,0},{0,0,-1}},{{-1,0,0},{0,1,0},{0,0,-1}},{{-1,0,0},{0,-1,0},{0,0,1}},{{0,1,0},{0,0,1},{1,0,0}},{{0,-1,0},{0,0,-1},{1,0,0}},{{0,1,0},{0,0,-1},{-1,0,0}},{{0,-1,0},{0,0,1},{-1,0,0}},{{0,0,1},{1,0,0},{0,1,0}},{{0,0,-1},{1,0,0},{0,-1,0}},{{0,0,-1},{-1,0,0},{0,1,0}},{{0,0,1},{-1,0,0},{0,-1,0}},
{{0,1,0},{1,0,0},{0,0,-1}},{{0,-1,0},{1,0,0},{0,0,1}},{{0,1,0},{-1,0,0},{0,0,1}},{{0,-1,0},{-1,0,0},{0,0,-1}},{{0,0,1},{0,1,0},{-1,0,0}},{{0,0,-1},{0,-1,0},{-1,0,0}},{{0,0,-1},{0,1,0},{1,0,0}},{{0,0,1},{0,-1,0},{1,0,0}},{{1,0,0},{0,0,1},{0,-1,0}},{{1,0,0},{0,0,-1},{0,1,0}},{{-1,0,0},{0,0,-1},{0,-1,0}},{{-1,0,0},{0,0,1},{0,1,0}}},
trans={{0,0,0},{0,0,0},{0,0,0},{0,0,0},{0,0,0},{0,0,0},{0,0,0},{0,0,0},{0,0,0},{0,0,0},{0,0,0},{f14,f14,f14},{f14,f14,f14},{f14,f14,f14},{f14,f14,f14},{f14,f14,f14},{f14,f14,f14},{f14,f14,f14},{f14,f14,f14},{f14,f14,f14},{f14,f14,f14},{f14,f14,f14},{f14,f14,f14}}},
}
</script>
</body>
</html>
minimaphelp.html
htmla year ago
<html>
<head><meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>mini map aide help</title>
<style>
body {margin:auto; font-size:16px; font-family:arial; color:dodgerblue; background-color:#0C0A0A;}
p {margin:10px;}
</style>
</head>
<body>
<p>
<a href="index.html"><b style="color:#FFD633;">Mini Map Aide</b></a> allows the fit of a protein structure, in PDB format, to its electron density map, in CCP4 format, to be assessed using a web browser on mobile devices. It seems to work with nucleotides and small ligands, too.
<p>
It is meant to be a completely minimal map-checking aid that works on a residue-by-residue basis and could be used for checking a structure in the final refinement rounds whenever the user has no computer access. The bad regions could be recorded, e.g. by taking screen-shots, for later attention with <a href="https://www2.mrc-lmb.cam.ac.uk/personal/pemsley/coot/">Coot</a>. Despite its limitations, the aim is for it to be useful in research or as a teaching aid.
<p>
If you have a PDB file and the corresponding map, the display calculations are all done by a client-side script in the browser and neither file is uploaded to the server.
<p>
The map must be in CCP4 format and <u>must</u> either cover the protein molecule or fill an entire unit cell. On a computer with CCP4 installed, this can be achieved most easily from refinement MTZ files with the following command: <p style="color:#FFD633;">gemmi sf2map -s3 --mapmask=my.pdb my.mtz my.map</p><p> or requires running MAPMASK in the old CCP4i GUI or using the Phenix Maps tab. With gemmi the -s number can be increased for finer grid sampling. Use the <span style="color:#FFD633;">-d</span> option to make a difference map from a refmac MTZ file. Note, there are some <a href="https://gemmi.readthedocs.io/en/latest/utils.html#sf2map">constraints</a> on the expected MTZ column labels.
<p>
CCP4 maps for most proteins can be obtained from <a href="https://www.ebi.ac.uk/pdbe/">EBI PDBe</a> in "Downloads" as "EDS map" and "EDS difference map."
<p>
<span style="color:#FFD633;">Mini Map Aide can also calculate the map from an MTZ file.</span> For this just use the map file chooser to pick the MTZ file instead. The PDB and reflection files will then be uploaded to the server to make the map file which you can then download and save. The map must then be read back into the program in order for it to be displayed. The files on the server will be deleted a few minutes after they are created.
<p>
The contour level can be adjusted and is in units of the map sigma or RMSD. If a negative value is used, the contour lines become red.
<p>
More help is available in the tooltips which appear on a PC browser when you hover the mouse over any of the text.
<p>
If it is too slow, try increasing the contour level to reduce the size of the mesh.
<p>
Things it does do (I hope):
<ul>
<li>Makes maps from MTZ files using Gemmi by Marcin Wojdyr (CCP4/Global Phasing).</li>
<li>Handles mono- and triclinic maps, where <b>b</b> is along Z (ncode 3).</li>
<li>Shows negative contours in red, so one can look at difference maps. </li>
<li>Draws multiple conformations and disulphides (the latter in pale green).</li>
<li>Displays <i>small</i> EM maps from <a href="https://www.ebi.ac.uk/emdb">EMDB</a>.</li>
<li>Allows small changes to the amino acids to be made.</li>
<li>Basic regularisation of amino acid geometry, including Cα and Cβ chirality.</li>
<li>Allows amino acid mutations to be made.</li>
<li>Saving a new PDB file.</li>
<li>Draws symmetry mates.</li>
</ul>
<p>
Things it <b>cannot</b> do:
<ul>
<li>Display more than one map at a time.</li>
</ul>
<p>
<b>Input fields</b>
<p>
<b>Go to:</b> The current position in the sequence. As soon as the PDB file is read, this will allow the user to choose which chain and residue number to centre on. Click redraw to allow any changes to take effect. If a large positive number is entered for a given chain, e.g. A 99999, the last residue in that chain will be displayed, which might be a a water molecule. Likewise, a negative number e.g. A -9999, would cause the program to jump back to the first residue in that chain, e.g. A -7. If the residue chosen is actually missing, e.g. in a disordered loop, the program will centre on the next available amino acid. Each residue will be displayed with its CA atom at the centre of rotation and its label at the top of the graphics window. If the residue possesses a CB atom, the electron density map will be centred on this, instead of the CA, to aid viewing the side chain fit. With nucleotides, the default centering is on the P atom.
<p>
<b>Contour level:</b> The contour level in sigma or rms units. Change sign with +/- button.
<p>
<b>PDB:</b> and <b>Map:</b> are file choosers for the coordinates and map or MTZ files.
<p>
<b>Side chain:</b> Click this button to centre on the outer parts of amino acid side chains or on nucleotide bases. Clicking toggles between centering on the side chain and the main chain Cα.
<p>
<b>Roll / Scroll:</b> This switches between animation and scrolling of the molecule.
<p>
<b>Last residue:</b> Move to preceding residue in the sequence.
<p>
<b>Next residue:</b> Move to next residue in the sequence.
<p>
<b>Symmetry:</b> Ticking this checkbox causes the nearby parts of symmetry-related molecules to be shown as green sticks. It can take a while to calculate all the symmetry mates the first time this option is selected, so a progress bar or two might appear and patience is needed, but it will be quicker thereafter. It works for nearly all of the 65 enantiomorphic space groups and a few other non-standard ones that occur in the PDB.
<p>
<b>Mutate:</b> This drop-down menu allows the amino acid at the centre of the display to be mutated. The side chain of the new amino acid will probably require rebuilding by hand using the Move and Tidy options. If the outcome is satisfactory, the Save option will be needed to write the coordinates to a new local PDB file.
<p>
<b>Move:</b> This allows atoms in the residue at the centre of the display to be moved. The moveable atoms will appear green and can be clicked and dragged. The atom selected for movement is enlarged momentarily to indicate which one is actually moving. The user might find that just moving some of the side chain atoms in the right direction followed by use of the Tidy function may give an improved fit to the map. Once the Move button is clicked, the following 3 buttons become active.
<p>
<b>Tidy:</b> This should improve the geometry of an amino acid after any of the atoms have been moved and redraw the bonds. It is rather approximate although the final bond lengths and interatomic distances defining the bond angles should be correct to within about 0.1 <span>Å</span>. It does not use the electron density map at all but it may be repeated as many times as required since each run can give a slightly different, but not necessarily better, fit. Note that in order to retain reasonable main chain geometry it does not move the backbone N and C atoms, so these are probably best not moved too far by the user.
<p>
<b>Undo:</b> This should completely undo the move and tidy operations. Note that unlike, Word, Coot, <i>etc</i>, the undo operations are not chained <i>i.e.</i> only the current round of changes made to the residue at the centre of the screen can be undone.
<p>
<b>Save:</b> This allows the changes to be saved in a new PDB file. It is recommended to do this at least once before closing the browser tab if the results are wanted for future use! Note that if no changes have been made to the residue currently at the screen centre, making the Save button active would require clicking the Move button first, although it would not be necessary to move any of the atoms.
<p>
The script (which can be downloaded within the html of the main page) is written in <a href="https://www.lua.org/">Lua</a> and uses <a href="http://fengari.io/">Fengari</a> to communicate with <a href="https://mathjs.org/">Math.js</a> and <a href="https://threejs.org/">Three.js</a>. The contouring algorithm (surfacenets.js), by Mikola Lysenko, is described <a href="https://www.merl.com/publications/docs/TR99-24.pdf">here</a> and was obtained from <a href="https://github.com/mikolalysenko/isosurface">here</a>. Help from the <a href="https://www.reddit.com/r/lua/">Lua reddit</a> group was invaluable.
<p>
I am very grateful to Marcin Wodjyr (CCP4/Global Phasing) for allowing me to use <a href="https://gemmi.readthedocs.io/en/latest/utils.html#sf2map">gemmi sf2map</a>.
<p>
Please report any problems or corrections to jbcooper_at_fastmail_dot_net who will be glad to help.crontab file for minimapai.de
plain_text2 years ago
*/5 * * * * find /var/www/html/temp -type f -mmin +5 -exec rm "{}" \;
0 0 * * * certbot --webroot renew Server script (run_gemmi.py) for minimapai.de
python4 years ago
#!/usr/bin/python3
import cgi
import os
import cgitb
import subprocess
print("Content-type:text/html\r\n\r\n") # needed
cgitb.enable()
form = cgi.FieldStorage()
pdbfile = form["pdbfile"]
pdbname = os.path.basename(pdbfile.filename)
open("/var/www/html/temp/" + pdbname, "wb").write(pdbfile.file.read())
#message = "<p>The file " + pdbname + " was uploaded successfully</p>"
#print(message)
mtzfile = form["mtzfile"]
mtzname = os.path.basename(mtzfile.filename)
open("/var/www/html/temp/" + mtzname, "wb").write(mtzfile.file.read())
#message = "<p>The file " + mtzname + " was uploaded successfully</p>"
#print(message)
diffneeded = form.getvalue("diffneeded")
#print(diffneeded)
mapname=mtzname.split(".",1)[0]
if diffneeded=="true": gemmiproc=["gemmi","sf2map","-d","-s3","--mapmask=../temp/"+pdbname,"../temp/"+mtzname,"../temp/"+mapname+"_diff.map"]
else: gemmiproc=["gemmi","sf2map","-s3","--mapmask=../temp/"+pdbname,"../temp/"+mtzname,"../temp/"+mapname+".map"]
#print("<p>",gemmiproc,"</p>")
gemmirun=subprocess.run(gemmiproc)- Total 4 snippets
- 1